1 //! Manage Access Control Lists
3 use anyhow
::{bail, Error}
;
5 use proxmox
::api
::{api, Router, RpcEnvironment, Permission}
;
6 use proxmox
::tools
::fs
::open_file_locked
;
8 use crate::api2
::types
::*;
9 use crate::config
::acl
;
10 use crate::config
::acl
::{Role, PRIV_SYS_AUDIT, PRIV_PERMISSIONS_MODIFY}
;
11 use crate::config
::cached_user_info
::CachedUserInfo
;
13 fn extract_acl_node_data(
14 node
: &acl
::AclTreeNode
,
16 list
: &mut Vec
<AclListItem
>,
18 token_user
: &Option
<Authid
>,
20 // tokens can't have tokens, so we can early return
21 if let Some(token_user
) = token_user
{
22 if token_user
.is_token() {
27 for (user
, roles
) in &node
.users
{
28 if let Some(token_user
) = token_user
{
30 || user
.user() != token_user
.user() {
35 for (role
, propagate
) in roles
{
36 list
.push(AclListItem
{
37 path
: if path
.is_empty() { String::from("/") }
else { path.to_string() }
,
38 propagate
: *propagate
,
39 ugid_type
: String
::from("user"),
40 ugid
: user
.to_string(),
41 roleid
: role
.to_string(),
45 for (group
, roles
) in &node
.groups
{
46 if token_user
.is_some() {
50 for (role
, propagate
) in roles
{
51 list
.push(AclListItem
{
52 path
: if path
.is_empty() { String::from("/") }
else { path.to_string() }
,
53 propagate
: *propagate
,
54 ugid_type
: String
::from("group"),
55 ugid
: group
.to_string(),
56 roleid
: role
.to_string(),
63 for (comp
, child
) in &node
.children
{
64 let new_path
= format
!("{}/{}", path
, comp
);
65 extract_acl_node_data(child
, &new_path
, list
, exact
, token_user
);
73 schema
: ACL_PATH_SCHEMA
,
77 description
: "If set, returns only ACL for the exact path.",
85 description
: "ACL entry list.",
92 permission
: &Permission
::Anybody
,
93 description
: "Returns all ACLs if user has Sys.Audit on '/access/acl', or just the ACLs containing the user's API tokens.",
96 /// Read Access Control List (ACLs).
100 mut rpcenv
: &mut dyn RpcEnvironment
,
101 ) -> Result
<Vec
<AclListItem
>, Error
> {
102 let auth_id
= rpcenv
.get_auth_id().unwrap().parse()?
;
104 let user_info
= CachedUserInfo
::new()?
;
106 let top_level_privs
= user_info
.lookup_privs(&auth_id
, &["access", "acl"]);
107 let auth_id_filter
= if (top_level_privs
& PRIV_SYS_AUDIT
) == 0 {
113 let (mut tree
, digest
) = acl
::config()?
;
115 let mut list
: Vec
<AclListItem
> = Vec
::new();
116 if let Some(path
) = &path
{
117 if let Some(node
) = &tree
.find_node(path
) {
118 extract_acl_node_data(&node
, path
, &mut list
, exact
, &auth_id_filter
);
121 extract_acl_node_data(&tree
.root
, "", &mut list
, exact
, &auth_id_filter
);
124 rpcenv
["digest"] = proxmox
::tools
::digest_to_hex(&digest
).into();
134 schema
: ACL_PATH_SCHEMA
,
141 schema
: ACL_PROPAGATE_SCHEMA
,
149 schema
: PROXMOX_GROUP_ID_SCHEMA
,
153 description
: "Remove permissions (instead of adding it).",
158 schema
: PROXMOX_CONFIG_DIGEST_SCHEMA
,
163 permission
: &Permission
::Anybody
,
164 description
: "Requires Permissions.Modify on '/access/acl', limited to updating ACLs of the user's API tokens otherwise."
167 /// Update Access Control List (ACLs).
171 propagate
: Option
<bool
>,
172 auth_id
: Option
<Authid
>,
173 group
: Option
<String
>,
174 delete
: Option
<bool
>,
175 digest
: Option
<String
>,
176 rpcenv
: &mut dyn RpcEnvironment
,
177 ) -> Result
<(), Error
> {
178 let current_auth_id
: Authid
= rpcenv
.get_auth_id().unwrap().parse()?
;
180 let user_info
= CachedUserInfo
::new()?
;
182 let top_level_privs
= user_info
.lookup_privs(¤t_auth_id
, &["access", "acl"]);
183 if top_level_privs
& PRIV_PERMISSIONS_MODIFY
== 0 {
185 bail
!("Unprivileged users are not allowed to create group ACL item.");
190 if current_auth_id
.is_token() {
191 bail
!("Unprivileged API tokens can't set ACL items.");
192 } else if !auth_id
.is_token() {
193 bail
!("Unprivileged users can only set ACL items for API tokens.");
194 } else if auth_id
.user() != current_auth_id
.user() {
195 bail
!("Unprivileged users can only set ACL items for their own API tokens.");
198 None
=> { bail!("Unprivileged user needs to provide auth_id to update ACL item."); }
,
202 let _lock
= open_file_locked(acl
::ACL_CFG_LOCKFILE
, std
::time
::Duration
::new(10, 0), true)?
;
204 let (mut tree
, expected_digest
) = acl
::config()?
;
206 if let Some(ref digest
) = digest
{
207 let digest
= proxmox
::tools
::hex_to_digest(digest
)?
;
208 crate::tools
::detect_modified_configuration_file(&digest
, &expected_digest
)?
;
211 let propagate
= propagate
.unwrap_or(true);
213 let delete
= delete
.unwrap_or(false);
215 if let Some(ref _group
) = group
{
216 bail
!("parameter 'group' - groups are currently not supported.");
217 } else if let Some(ref auth_id
) = auth_id
{
218 if !delete
{ // Note: we allow to delete non-existent users
219 let user_cfg
= crate::config
::user
::cached_config()?
;
220 if user_cfg
.sections
.get(&auth_id
.to_string()).is_none() {
221 bail
!(format
!("no such {}.",
222 if auth_id
.is_token() { "API token" }
else { "user" }
));
226 bail
!("missing 'userid' or 'group' parameter.");
229 if !delete
{ // Note: we allow to delete entries with invalid path
230 acl
::check_acl_path(&path
)?
;
233 if let Some(auth_id
) = auth_id
{
235 tree
.delete_user_role(&path
, &auth_id
, &role
);
237 tree
.insert_user_role(&path
, &auth_id
, &role
, propagate
);
239 } else if let Some(group
) = group
{
241 tree
.delete_group_role(&path
, &group
, &role
);
243 tree
.insert_group_role(&path
, &group
, &role
, propagate
);
247 acl
::save_config(&tree
)?
;
252 pub const ROUTER
: Router
= Router
::new()
253 .get(&API_METHOD_READ_ACL
)
254 .put(&API_METHOD_UPDATE_ACL
);