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