implement helper to check if we can modify permission
[pve-access-control.git] / PVE / API2 / ACL.pm
1 package PVE::API2::ACL;
2
3 use strict;
4 use warnings;
5 use PVE::Cluster qw (cfs_read_file cfs_write_file);
6 use PVE::Tools qw(split_list);
7 use PVE::AccessControl;
8 use PVE::Exception qw(raise_param_exc);
9
10 use PVE::SafeSyslog;
11
12 use Data::Dumper; # fixme: remove
13
14 use PVE::RESTHandler;
15
16 use base qw(PVE::RESTHandler);
17
18 __PACKAGE__->register_method ({
19     name => 'read_acl', 
20     path => '', 
21     method => 'GET',
22     description => "Get Access Control List (ACLs).",
23     permissions => { 
24         user => 'all',
25     },
26     parameters => {
27         additionalProperties => 0,
28         properties => {},
29     },
30     returns => {
31         type => 'array',
32         items => {
33             type => "object",
34             additionalProperties => 0,
35             properties => {
36                 path => { type => 'string' },
37                 type => { type => 'string', enum => ['user', 'group'] },
38                 ugid => { type => 'string' },
39                 roleid => { type => 'string' },
40                 propagate => { type => 'boolean' },
41             },
42         },
43     },
44     code => sub {
45         my ($param) = @_;
46     
47         my $rpcenv = PVE::RPCEnvironment::get();
48         my $authuser = $rpcenv->get_user();
49         my $res = [];
50
51         my $usercfg = $rpcenv->{user_cfg};
52         if (!$usercfg || !$usercfg->{acl}) {
53             return {};
54         }
55
56         my $audit = $rpcenv->check($authuser, '/access', ['Sys.Audit'], 1);
57
58         my $acl = $usercfg->{acl};
59         foreach my $path (keys %$acl) {
60             foreach my $type (qw(users groups)) {
61                 my $d = $acl->{$path}->{$type};
62                 next if !$d;
63                 next if !($audit || $rpcenv->check_perm_modify($authuser, $path, 1));
64                 foreach my $id (keys %$d) {
65                     foreach my $role (keys %{$d->{$id}}) {
66                         my $propagate = $d->{$id}->{$role};
67                         push @$res, {
68                             path => $path,
69                             type => $type eq 'groups' ? 'group' : 'user',
70                             ugid => $id,
71                             roleid => $role,
72                             propagate => $propagate,
73                         };
74                     }
75                 }
76             }
77         }
78
79         return $res;
80     }});
81
82 __PACKAGE__->register_method ({
83     name => 'update_acl', 
84     protected => 1,
85     path => '', 
86     method => 'PUT',
87     permissions => { 
88         check => ['perm-modify', '{path}'],
89     },
90     description => "Update Access Control List (add or remove permissions).",
91     parameters => {
92         additionalProperties => 0,
93         properties => {
94             path => {
95                 description => "Access control path",
96                 type => 'string',
97             },
98             users => { 
99                 description => "List of users.",
100                 type => 'string',  format => 'pve-userid-list',  
101                 optional => 1,
102             },
103             groups => { 
104                 description => "List of groups.",
105                 type => 'string', format => 'pve-groupid-list',
106                 optional => 1,  
107             },
108             roles => { 
109                 description => "List of roles.",
110                 type => 'string', format => 'pve-roleid-list',
111             },
112             propagate => { 
113                 description => "Allow to propagate (inherit) permissions.",
114                 type => 'boolean', 
115                 optional => 1,
116             },
117             delete => {
118                 description => "Remove permissions (instead of adding it).",
119                 type => 'boolean', 
120                 optional => 1,
121             },
122         },
123     },
124     returns => { type => 'null' },
125     code => sub {
126         my ($param) = @_;
127
128         if (!($param->{users} || $param->{groups})) {
129             raise_param_exc({ 
130                 users => "either 'users' or 'groups' is required.", 
131                 groups => "either 'users' or 'groups' is required." });
132         }
133
134         my $path = PVE::AccessControl::normalize_path($param->{path});
135         raise_param_exc({ path => "invalid ACL path '$param->{path}'" }) if !$path;
136
137         PVE::AccessControl::lock_user_config(
138             sub {
139                         
140                 my $cfg = cfs_read_file("user.cfg");
141
142                 my $propagate = $param->{propagate} ? 1 : 0;
143
144                 foreach my $role (split_list($param->{roles})) {
145                     die "role '$role' does not exist\n" 
146                         if !$cfg->{roles}->{$role};
147
148                     foreach my $group (split_list($param->{groups})) {
149
150                         die "group '$group' does not exist\n"
151                             if !$cfg->{groups}->{$group};
152
153                         if ($param->{delete}) {
154                             delete($cfg->{acl}->{$path}->{groups}->{$group}->{$role});
155                         } else {
156                             $cfg->{acl}->{$path}->{groups}->{$group}->{$role} = $propagate;
157                         }
158                     }
159
160                     foreach my $userid (split_list($param->{users})) {
161                         my $username = PVE::AccessControl::verify_username($userid);
162
163                         die "user '$username' does not exist\n"
164                             if !$cfg->{users}->{$username};
165
166                         if ($param->{delete}) {
167                             delete($cfg->{acl}->{$path}->{users}->{$username}->{$role});
168                         } else {
169                             $cfg->{acl}->{$path}->{users}->{$username}->{$role} = $propagate;
170                         } 
171                     }
172                 }
173
174                 cfs_write_file("user.cfg", $cfg);
175             }, "ACL update failed");
176
177         return undef;
178     }});
179
180 1;