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