allow dots in access paths
[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;