]> git.proxmox.com Git - pve-access-control.git/blob - PVE/API2/User.pm
bump version to 5.1-1
[pve-access-control.git] / PVE / API2 / User.pm
1 package PVE::API2::User;
2
3 use strict;
4 use warnings;
5 use PVE::Exception qw(raise raise_perm_exc);
6 use PVE::Cluster qw (cfs_read_file cfs_write_file);
7 use PVE::Tools qw(split_list);
8 use PVE::AccessControl;
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('user-enable', {
18 description => "Enable the account (default). You can set this to '0' to disable the account",
19 type => 'boolean',
20 optional => 1,
21 default => 1,
22 });
23 register_standard_option('user-expire', {
24 description => "Account expiration date (seconds since epoch). '0' means no expiration date.",
25 type => 'integer',
26 minimum => 0,
27 optional => 1,
28 });
29 register_standard_option('user-firstname', { type => 'string', optional => 1 });
30 register_standard_option('user-lastname', { type => 'string', optional => 1 });
31 register_standard_option('user-email', { type => 'string', optional => 1, format => 'email-opt' });
32 register_standard_option('user-comment', { type => 'string', optional => 1 });
33 register_standard_option('user-keys', {
34 description => "Keys for two factor auth (yubico).",
35 type => 'string',
36 optional => 1,
37 });
38 register_standard_option('group-list', {
39 type => 'string', format => 'pve-groupid-list',
40 optional => 1,
41 completion => \&PVE::AccessControl::complete_group,
42 });
43
44 my $extract_user_data = sub {
45 my ($data, $full) = @_;
46
47 my $res = {};
48
49 foreach my $prop (qw(enable expire firstname lastname email comment keys)) {
50 $res->{$prop} = $data->{$prop} if defined($data->{$prop});
51 }
52
53 return $res if !$full;
54
55 $res->{groups} = $data->{groups} ? [ keys %{$data->{groups}} ] : [];
56
57 return $res;
58 };
59
60 __PACKAGE__->register_method ({
61 name => 'index',
62 path => '',
63 method => 'GET',
64 description => "User index.",
65 permissions => {
66 description => "The returned list is restricted to users where you have 'User.Modify' or 'Sys.Audit' permissions on '/access/groups' or on a group the user belongs too. But it always includes the current (authenticated) user.",
67 user => 'all',
68 },
69 parameters => {
70 additionalProperties => 0,
71 properties => {
72 enabled => {
73 type => 'boolean',
74 description => "Optional filter for enable property.",
75 optional => 1,
76 }
77 },
78 },
79 returns => {
80 type => 'array',
81 items => {
82 type => "object",
83 properties => {
84 userid => get_standard_option('userid-completed'),
85 enable => get_standard_option('user-enable'),
86 expire => get_standard_option('user-expire'),
87 firstname => get_standard_option('user-firstname'),
88 lastname => get_standard_option('user-lastname'),
89 email => get_standard_option('user-email'),
90 comment => get_standard_option('user-comment'),
91 keys => get_standard_option('user-keys'),
92 },
93 },
94 links => [ { rel => 'child', href => "{userid}" } ],
95 },
96 code => sub {
97 my ($param) = @_;
98
99 my $rpcenv = PVE::RPCEnvironment::get();
100 my $usercfg = $rpcenv->{user_cfg};
101 my $authuser = $rpcenv->get_user();
102
103 my $res = [];
104
105 my $privs = [ 'User.Modify', 'Sys.Audit' ];
106 my $canUserMod = $rpcenv->check_any($authuser, "/access/groups", $privs, 1);
107 my $groups = $rpcenv->filter_groups($authuser, $privs, 1);
108 my $allowed_users = $rpcenv->group_member_join([keys %$groups]);
109
110 foreach my $user (keys %{$usercfg->{users}}) {
111
112 if (!($canUserMod || $user eq $authuser)) {
113 next if !$allowed_users->{$user};
114 }
115
116 my $entry = &$extract_user_data($usercfg->{users}->{$user});
117
118 if (defined($param->{enabled})) {
119 next if $entry->{enable} && !$param->{enabled};
120 next if !$entry->{enable} && $param->{enabled};
121 }
122
123 $entry->{userid} = $user;
124 push @$res, $entry;
125 }
126
127 return $res;
128 }});
129
130 __PACKAGE__->register_method ({
131 name => 'create_user',
132 protected => 1,
133 path => '',
134 method => 'POST',
135 permissions => {
136 description => "You need 'Realm.AllocateUser' on '/access/realm/<realm>' on the realm of user <userid>, and 'User.Modify' permissions to '/access/groups/<group>' for any group specified (or 'User.Modify' on '/access/groups' if you pass no groups.",
137 check => [ 'and',
138 [ 'userid-param', 'Realm.AllocateUser'],
139 [ 'userid-group', ['User.Modify'], groups_param => 1],
140 ],
141 },
142 description => "Create new user.",
143 parameters => {
144 additionalProperties => 0,
145 properties => {
146 userid => get_standard_option('userid-completed'),
147 enable => get_standard_option('user-enable'),
148 expire => get_standard_option('user-expire'),
149 firstname => get_standard_option('user-firstname'),
150 lastname => get_standard_option('user-lastname'),
151 email => get_standard_option('user-email'),
152 comment => get_standard_option('user-comment'),
153 keys => get_standard_option('user-keys'),
154 password => {
155 description => "Initial password.",
156 type => 'string',
157 optional => 1,
158 minLength => 5,
159 maxLength => 64
160 },
161 groups => get_standard_option('group-list'),
162 },
163 },
164 returns => { type => 'null' },
165 code => sub {
166 my ($param) = @_;
167
168 PVE::AccessControl::lock_user_config(
169 sub {
170
171 my ($username, $ruid, $realm) = PVE::AccessControl::verify_username($param->{userid});
172
173 my $usercfg = cfs_read_file("user.cfg");
174
175 die "user '$username' already exists\n"
176 if $usercfg->{users}->{$username};
177
178 PVE::AccessControl::domain_set_password($realm, $ruid, $param->{password})
179 if defined($param->{password});
180
181 my $enable = defined($param->{enable}) ? $param->{enable} : 1;
182 $usercfg->{users}->{$username} = { enable => $enable };
183 $usercfg->{users}->{$username}->{expire} = $param->{expire} if $param->{expire};
184
185 if ($param->{groups}) {
186 foreach my $group (split_list($param->{groups})) {
187 if ($usercfg->{groups}->{$group}) {
188 PVE::AccessControl::add_user_group($username, $usercfg, $group);
189 } else {
190 die "no such group '$group'\n";
191 }
192 }
193 }
194
195 $usercfg->{users}->{$username}->{firstname} = $param->{firstname} if $param->{firstname};
196 $usercfg->{users}->{$username}->{lastname} = $param->{lastname} if $param->{lastname};
197 $usercfg->{users}->{$username}->{email} = $param->{email} if $param->{email};
198 $usercfg->{users}->{$username}->{comment} = $param->{comment} if $param->{comment};
199 $usercfg->{users}->{$username}->{keys} = $param->{keys} if $param->{keys};
200
201 cfs_write_file("user.cfg", $usercfg);
202 }, "create user failed");
203
204 return undef;
205 }});
206
207 __PACKAGE__->register_method ({
208 name => 'read_user',
209 path => '{userid}',
210 method => 'GET',
211 description => "Get user configuration.",
212 permissions => {
213 check => ['userid-group', ['User.Modify', 'Sys.Audit']],
214 },
215 parameters => {
216 additionalProperties => 0,
217 properties => {
218 userid => get_standard_option('userid-completed'),
219 },
220 },
221 returns => {
222 additionalProperties => 0,
223 properties => {
224 enable => get_standard_option('user-enable'),
225 expire => get_standard_option('user-expire'),
226 firstname => get_standard_option('user-firstname'),
227 lastname => get_standard_option('user-lastname'),
228 email => get_standard_option('user-email'),
229 comment => get_standard_option('user-comment'),
230 keys => get_standard_option('user-keys'),
231 groups => { type => 'array' },
232 },
233 type => "object"
234 },
235 code => sub {
236 my ($param) = @_;
237
238 my ($username, undef, $domain) =
239 PVE::AccessControl::verify_username($param->{userid});
240
241 my $usercfg = cfs_read_file("user.cfg");
242
243 my $data = PVE::AccessControl::check_user_exist($usercfg, $username);
244
245 return &$extract_user_data($data, 1);
246 }});
247
248 __PACKAGE__->register_method ({
249 name => 'update_user',
250 protected => 1,
251 path => '{userid}',
252 method => 'PUT',
253 permissions => {
254 check => ['userid-group', ['User.Modify'], groups_param => 1 ],
255 },
256 description => "Update user configuration.",
257 parameters => {
258 additionalProperties => 0,
259 properties => {
260 userid => get_standard_option('userid-completed'),
261 enable => get_standard_option('user-enable'),
262 expire => get_standard_option('user-expire'),
263 firstname => get_standard_option('user-firstname'),
264 lastname => get_standard_option('user-lastname'),
265 email => get_standard_option('user-email'),
266 comment => get_standard_option('user-comment'),
267 keys => get_standard_option('user-keys'),
268 groups => get_standard_option('group-list'),
269 append => {
270 type => 'boolean',
271 optional => 1,
272 requires => 'groups',
273 },
274 },
275 },
276 returns => { type => 'null' },
277 code => sub {
278 my ($param) = @_;
279
280 my ($username, $ruid, $realm) =
281 PVE::AccessControl::verify_username($param->{userid});
282
283 PVE::AccessControl::lock_user_config(
284 sub {
285
286 my $usercfg = cfs_read_file("user.cfg");
287
288 PVE::AccessControl::check_user_exist($usercfg, $username);
289
290 $usercfg->{users}->{$username}->{enable} = $param->{enable} if defined($param->{enable});
291
292 $usercfg->{users}->{$username}->{expire} = $param->{expire} if defined($param->{expire});
293
294 PVE::AccessControl::delete_user_group($username, $usercfg)
295 if (!$param->{append} && defined($param->{groups}));
296
297 if ($param->{groups}) {
298 foreach my $group (split_list($param->{groups})) {
299 if ($usercfg->{groups}->{$group}) {
300 PVE::AccessControl::add_user_group($username, $usercfg, $group);
301 } else {
302 die "no such group '$group'\n";
303 }
304 }
305 }
306
307 $usercfg->{users}->{$username}->{firstname} = $param->{firstname} if defined($param->{firstname});
308 $usercfg->{users}->{$username}->{lastname} = $param->{lastname} if defined($param->{lastname});
309 $usercfg->{users}->{$username}->{email} = $param->{email} if defined($param->{email});
310 $usercfg->{users}->{$username}->{comment} = $param->{comment} if defined($param->{comment});
311 $usercfg->{users}->{$username}->{keys} = $param->{keys} if defined($param->{keys});
312
313 cfs_write_file("user.cfg", $usercfg);
314 }, "update user failed");
315
316 return undef;
317 }});
318
319 __PACKAGE__->register_method ({
320 name => 'delete_user',
321 protected => 1,
322 path => '{userid}',
323 method => 'DELETE',
324 description => "Delete user.",
325 permissions => {
326 check => [ 'and',
327 [ 'userid-param', 'Realm.AllocateUser'],
328 [ 'userid-group', ['User.Modify']],
329 ],
330 },
331 parameters => {
332 additionalProperties => 0,
333 properties => {
334 userid => get_standard_option('userid-completed'),
335 }
336 },
337 returns => { type => 'null' },
338 code => sub {
339 my ($param) = @_;
340
341 my $rpcenv = PVE::RPCEnvironment::get();
342 my $authuser = $rpcenv->get_user();
343
344 my ($userid, $ruid, $realm) =
345 PVE::AccessControl::verify_username($param->{userid});
346
347 PVE::AccessControl::lock_user_config(
348 sub {
349
350 my $usercfg = cfs_read_file("user.cfg");
351
352 my $domain_cfg = cfs_read_file('domains.cfg');
353 if (my $cfg = $domain_cfg->{ids}->{$realm}) {
354 my $plugin = PVE::Auth::Plugin->lookup($cfg->{type});
355 $plugin->delete_user($cfg, $realm, $ruid);
356 }
357
358 delete $usercfg->{users}->{$userid};
359
360 PVE::AccessControl::delete_user_group($userid, $usercfg);
361 PVE::AccessControl::delete_user_acl($userid, $usercfg);
362
363 cfs_write_file("user.cfg", $usercfg);
364 }, "delete user failed");
365
366 return undef;
367 }});
368
369 1;