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