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