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