1 //! Manage Access Control Lists
3 use anyhow
::{bail, Error}
;
5 use proxmox
::api
::{api, Router, RpcEnvironment, Permission}
;
8 Authid
, AclListItem
, Role
,
9 ACL_PATH_SCHEMA
, PROXMOX_CONFIG_DIGEST_SCHEMA
, PROXMOX_GROUP_ID_SCHEMA
,
10 ACL_PROPAGATE_SCHEMA
, PRIV_SYS_AUDIT
, PRIV_PERMISSIONS_MODIFY
,
13 use pbs_config
::acl
::AclTreeNode
;
15 use crate::config
::cached_user_info
::CachedUserInfo
;
17 fn extract_acl_node_data(
20 list
: &mut Vec
<AclListItem
>,
22 auth_id_filter
: &Option
<Authid
>,
24 // tokens can't have tokens, so we can early return
25 if let Some(auth_id_filter
) = auth_id_filter
{
26 if auth_id_filter
.is_token() {
31 for (user
, roles
) in &node
.users
{
32 if let Some(auth_id_filter
) = auth_id_filter
{
34 || user
.user() != auth_id_filter
.user() {
39 for (role
, propagate
) in roles
{
40 list
.push(AclListItem
{
41 path
: if path
.is_empty() { String::from("/") }
else { path.to_string() }
,
42 propagate
: *propagate
,
43 ugid_type
: String
::from("user"),
44 ugid
: user
.to_string(),
45 roleid
: role
.to_string(),
49 for (group
, roles
) in &node
.groups
{
50 if auth_id_filter
.is_some() {
54 for (role
, propagate
) in roles
{
55 list
.push(AclListItem
{
56 path
: if path
.is_empty() { String::from("/") }
else { path.to_string() }
,
57 propagate
: *propagate
,
58 ugid_type
: String
::from("group"),
59 ugid
: group
.to_string(),
60 roleid
: role
.to_string(),
67 for (comp
, child
) in &node
.children
{
68 let new_path
= format
!("{}/{}", path
, comp
);
69 extract_acl_node_data(child
, &new_path
, list
, exact
, auth_id_filter
);
77 schema
: ACL_PATH_SCHEMA
,
81 description
: "If set, returns only ACL for the exact path.",
89 description
: "ACL entry list.",
96 permission
: &Permission
::Anybody
,
97 description
: "Returns all ACLs if user has Sys.Audit on '/access/acl', or just the ACLs containing the user's API tokens.",
100 /// Read Access Control List (ACLs).
102 path
: Option
<String
>,
104 mut rpcenv
: &mut dyn RpcEnvironment
,
105 ) -> Result
<Vec
<AclListItem
>, Error
> {
106 let auth_id
= rpcenv
.get_auth_id().unwrap().parse()?
;
108 let user_info
= CachedUserInfo
::new()?
;
110 let top_level_privs
= user_info
.lookup_privs(&auth_id
, &["access", "acl"]);
111 let auth_id_filter
= if (top_level_privs
& PRIV_SYS_AUDIT
) == 0 {
117 let (mut tree
, digest
) = pbs_config
::acl
::config()?
;
119 let mut list
: Vec
<AclListItem
> = Vec
::new();
120 if let Some(path
) = &path
{
121 if let Some(node
) = &tree
.find_node(path
) {
122 extract_acl_node_data(&node
, path
, &mut list
, exact
, &auth_id_filter
);
125 extract_acl_node_data(&tree
.root
, "", &mut list
, exact
, &auth_id_filter
);
128 rpcenv
["digest"] = proxmox
::tools
::digest_to_hex(&digest
).into();
138 schema
: ACL_PATH_SCHEMA
,
145 schema
: ACL_PROPAGATE_SCHEMA
,
153 schema
: PROXMOX_GROUP_ID_SCHEMA
,
157 description
: "Remove permissions (instead of adding it).",
162 schema
: PROXMOX_CONFIG_DIGEST_SCHEMA
,
167 permission
: &Permission
::Anybody
,
168 description
: "Requires Permissions.Modify on '/access/acl', limited to updating ACLs of the user's API tokens otherwise."
171 /// Update Access Control List (ACLs).
172 #[allow(clippy::too_many_arguments)]
176 propagate
: Option
<bool
>,
177 auth_id
: Option
<Authid
>,
178 group
: Option
<String
>,
179 delete
: Option
<bool
>,
180 digest
: Option
<String
>,
181 rpcenv
: &mut dyn RpcEnvironment
,
182 ) -> Result
<(), Error
> {
183 let current_auth_id
: Authid
= rpcenv
.get_auth_id().unwrap().parse()?
;
185 let user_info
= CachedUserInfo
::new()?
;
187 let top_level_privs
= user_info
.lookup_privs(¤t_auth_id
, &["access", "acl"]);
188 if top_level_privs
& PRIV_PERMISSIONS_MODIFY
== 0 {
190 bail
!("Unprivileged users are not allowed to create group ACL item.");
195 if current_auth_id
.is_token() {
196 bail
!("Unprivileged API tokens can't set ACL items.");
197 } else if !auth_id
.is_token() {
198 bail
!("Unprivileged users can only set ACL items for API tokens.");
199 } else if auth_id
.user() != current_auth_id
.user() {
200 bail
!("Unprivileged users can only set ACL items for their own API tokens.");
203 None
=> { bail!("Unprivileged user needs to provide auth_id to update ACL item."); }
,
207 let _lock
= pbs_config
::acl
::lock_config()?
;
209 let (mut tree
, expected_digest
) = pbs_config
::acl
::config()?
;
211 if let Some(ref digest
) = digest
{
212 let digest
= proxmox
::tools
::hex_to_digest(digest
)?
;
213 crate::tools
::detect_modified_configuration_file(&digest
, &expected_digest
)?
;
216 let propagate
= propagate
.unwrap_or(true);
218 let delete
= delete
.unwrap_or(false);
220 if let Some(ref _group
) = group
{
221 bail
!("parameter 'group' - groups are currently not supported.");
222 } else if let Some(ref auth_id
) = auth_id
{
223 if !delete
{ // Note: we allow to delete non-existent users
224 let user_cfg
= crate::config
::user
::cached_config()?
;
225 if user_cfg
.sections
.get(&auth_id
.to_string()).is_none() {
226 bail
!(format
!("no such {}.",
227 if auth_id
.is_token() { "API token" }
else { "user" }
));
231 bail
!("missing 'userid' or 'group' parameter.");
234 if !delete
{ // Note: we allow to delete entries with invalid path
235 pbs_config
::acl
::check_acl_path(&path
)?
;
238 if let Some(auth_id
) = auth_id
{
240 tree
.delete_user_role(&path
, &auth_id
, &role
);
242 tree
.insert_user_role(&path
, &auth_id
, &role
, propagate
);
244 } else if let Some(group
) = group
{
246 tree
.delete_group_role(&path
, &group
, &role
);
248 tree
.insert_group_role(&path
, &group
, &role
, propagate
);
252 pbs_config
::acl
::save_config(&tree
)?
;
257 pub const ROUTER
: Router
= Router
::new()
258 .get(&API_METHOD_READ_ACL
)
259 .put(&API_METHOD_UPDATE_ACL
);