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