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