]> git.proxmox.com Git - pve-access-control.git/blame - PVE/API2/User.pm
Whitespace fixes
[pve-access-control.git] / PVE / API2 / User.pm
CommitLineData
2c3a6c0a
DM
1package PVE::API2::User;
2
3use strict;
4use warnings;
37d45deb 5use PVE::Exception qw(raise raise_perm_exc);
2c3a6c0a
DM
6use PVE::Cluster qw (cfs_read_file cfs_write_file);
7use PVE::Tools qw(split_list);
8use PVE::AccessControl;
9use PVE::JSONSchema qw(get_standard_option);
10
11use PVE::SafeSyslog;
12
2c3a6c0a
DM
13use PVE::RESTHandler;
14
15use base qw(PVE::RESTHandler);
16
17my $extract_user_data = sub {
18 my ($data, $full) = @_;
19
20 my $res = {};
21
96f8ebd6 22 foreach my $prop (qw(enable expire firstname lastname email comment keys)) {
2c3a6c0a
DM
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 ({
0a6e09fd
PA
34 name => 'index',
35 path => '',
2c3a6c0a
DM
36 method => 'GET',
37 description => "User index.",
0a6e09fd 38 permissions => {
82b63965 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.",
96919234
DM
40 user => 'all',
41 },
2c3a6c0a
DM
42 parameters => {
43 additionalProperties => 0,
cb6f2f93
DM
44 properties => {
45 enabled => {
46 type => 'boolean',
47 description => "Optional filter for enable property.",
48 optional => 1,
49 }
50 },
2c3a6c0a
DM
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) = @_;
0a6e09fd 64
930dcfc8 65 my $rpcenv = PVE::RPCEnvironment::get();
37d45deb 66 my $usercfg = $rpcenv->{user_cfg};
930dcfc8
DM
67 my $authuser = $rpcenv->get_user();
68
2c3a6c0a
DM
69 my $res = [];
70
82b63965
DM
71 my $privs = [ 'User.Modify', 'Sys.Audit' ];
72 my $canUserMod = $rpcenv->check_any($authuser, "/access/groups", $privs, 1);
b9180ed2 73 my $groups = $rpcenv->filter_groups($authuser, $privs, 1);
0a6e09fd 74 my $allowed_users = $rpcenv->group_member_join([keys %$groups]);
37d45deb 75
2c3a6c0a 76 foreach my $user (keys %{$usercfg->{users}}) {
37d45deb
DM
77
78 if (!($canUserMod || $user eq $authuser)) {
79 next if !$allowed_users->{$user};
80 }
930dcfc8 81
2c3a6c0a 82 my $entry = &$extract_user_data($usercfg->{users}->{$user});
cb6f2f93
DM
83
84 if (defined($param->{enabled})) {
85 next if $entry->{enable} && !$param->{enabled};
86 next if !$entry->{enable} && $param->{enabled};
87 }
88
2c3a6c0a
DM
89 $entry->{userid} = $user;
90 push @$res, $entry;
91 }
92
93 return $res;
94 }});
95
96__PACKAGE__->register_method ({
0a6e09fd 97 name => 'create_user',
2c3a6c0a 98 protected => 1,
0a6e09fd 99 path => '',
2c3a6c0a 100 method => 'POST',
0a6e09fd 101 permissions => {
82b63965
DM
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 ],
96919234 107 },
2c3a6c0a
DM
108 description => "Create new user.",
109 parameters => {
0a6e09fd 110 additionalProperties => 0,
2c3a6c0a
DM
111 properties => {
112 userid => get_standard_option('userid'),
37d45deb
DM
113 password => {
114 description => "Initial password.",
0a6e09fd
PA
115 type => 'string',
116 optional => 1,
117 minLength => 5,
118 maxLength => 64
37d45deb 119 },
3e5bfdf6
DM
120 groups => {
121 type => 'string', format => 'pve-groupid-list',
122 optional => 1,
123 completion => \&PVE::AccessControl::complete_group,
124 },
2c3a6c0a
DM
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 },
96f8ebd6
DM
129 keys => {
130 description => "Keys for two factor auth (yubico).",
0a6e09fd 131 type => 'string',
96f8ebd6
DM
132 optional => 1,
133 },
0a6e09fd 134 expire => {
2c3a6c0a 135 description => "Account expiration date (seconds since epoch). '0' means no expiration date.",
0a6e09fd 136 type => 'integer',
2c3a6c0a
DM
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 {
0a6e09fd 154
2c3a6c0a 155 my ($username, $ruid, $realm) = PVE::AccessControl::verify_username($param->{userid});
0a6e09fd 156
2c3a6c0a
DM
157 my $usercfg = cfs_read_file("user.cfg");
158
0a6e09fd 159 die "user '$username' already exists\n"
2c3a6c0a 160 if $usercfg->{users}->{$username};
0a6e09fd 161
2c3a6c0a 162 PVE::AccessControl::domain_set_password($realm, $ruid, $param->{password})
fdb30a4c 163 if defined($param->{password});
2c3a6c0a
DM
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};
96f8ebd6 183 $usercfg->{users}->{$username}->{keys} = $param->{keys} if $param->{keys};
2c3a6c0a
DM
184
185 cfs_write_file("user.cfg", $usercfg);
186 }, "create user failed");
187
188 return undef;
189 }});
190
191__PACKAGE__->register_method ({
0a6e09fd
PA
192 name => 'read_user',
193 path => '{userid}',
2c3a6c0a
DM
194 method => 'GET',
195 description => "Get user configuration.",
0a6e09fd 196 permissions => {
82b63965 197 check => ['userid-group', ['User.Modify', 'Sys.Audit']],
96919234 198 },
2c3a6c0a 199 parameters => {
0a6e09fd 200 additionalProperties => 0,
2c3a6c0a
DM
201 properties => {
202 userid => get_standard_option('userid'),
203 },
204 },
205 returns => {
0a6e09fd 206 additionalProperties => 0,
2c3a6c0a
DM
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 },
0a6e09fd
PA
213 comment => { type => 'string', optional => 1 },
214 keys => { type => 'string', optional => 1 },
2c3a6c0a
DM
215 groups => { type => 'array' },
216 }
217 },
218 code => sub {
219 my ($param) = @_;
220
0a6e09fd 221 my ($username, undef, $domain) =
2c3a6c0a
DM
222 PVE::AccessControl::verify_username($param->{userid});
223
224 my $usercfg = cfs_read_file("user.cfg");
2c3a6c0a 225
37d45deb 226 my $data = PVE::AccessControl::check_user_exist($usercfg, $username);
0a6e09fd 227
2c3a6c0a
DM
228 return &$extract_user_data($data, 1);
229 }});
230
231__PACKAGE__->register_method ({
0a6e09fd 232 name => 'update_user',
2c3a6c0a 233 protected => 1,
0a6e09fd 234 path => '{userid}',
2c3a6c0a 235 method => 'PUT',
0a6e09fd 236 permissions => {
96919234
DM
237 check => ['userid-group', ['User.Modify'], groups_param => 1 ],
238 },
2c3a6c0a
DM
239 description => "Update user configuration.",
240 parameters => {
0a6e09fd 241 additionalProperties => 0,
2c3a6c0a 242 properties => {
3e5bfdf6
DM
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 },
0a6e09fd
PA
251 append => {
252 type => 'boolean',
2c3a6c0a
DM
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 },
96f8ebd6
DM
265 keys => {
266 description => "Keys for two factor auth (yubico).",
0a6e09fd 267 type => 'string',
96f8ebd6
DM
268 optional => 1,
269 },
0a6e09fd 270 expire => {
2c3a6c0a 271 description => "Account expiration date (seconds since epoch). '0' means no expiration date.",
0a6e09fd 272 type => 'integer',
2c3a6c0a 273 minimum => 0,
0a6e09fd 274 optional => 1
2c3a6c0a
DM
275 },
276 },
277 },
278 returns => { type => 'null' },
279 code => sub {
280 my ($param) = @_;
37d45deb 281
0a6e09fd 282 my ($username, $ruid, $realm) =
37d45deb 283 PVE::AccessControl::verify_username($param->{userid});
0a6e09fd 284
2c3a6c0a
DM
285 PVE::AccessControl::lock_user_config(
286 sub {
0a6e09fd 287
2c3a6c0a
DM
288 my $usercfg = cfs_read_file("user.cfg");
289
37d45deb 290 PVE::AccessControl::check_user_exist($usercfg, $username);
2c3a6c0a
DM
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
0a6e09fd 296 PVE::AccessControl::delete_user_group($username, $usercfg)
e6521738 297 if (!$param->{append} && defined($param->{groups}));
2c3a6c0a
DM
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});
96f8ebd6 313 $usercfg->{users}->{$username}->{keys} = $param->{keys} if defined($param->{keys});
2c3a6c0a
DM
314
315 cfs_write_file("user.cfg", $usercfg);
316 }, "update user failed");
0a6e09fd 317
2c3a6c0a
DM
318 return undef;
319 }});
320
321__PACKAGE__->register_method ({
0a6e09fd 322 name => 'delete_user',
2c3a6c0a 323 protected => 1,
0a6e09fd 324 path => '{userid}',
2c3a6c0a
DM
325 method => 'DELETE',
326 description => "Delete user.",
0a6e09fd 327 permissions => {
82b63965
DM
328 check => [ 'and',
329 [ 'userid-param', 'Realm.AllocateUser'],
330 [ 'userid-group', ['User.Modify']],
331 ],
12683df7 332 },
2c3a6c0a 333 parameters => {
0a6e09fd 334 additionalProperties => 0,
2c3a6c0a 335 properties => {
3e5bfdf6
DM
336 userid => get_standard_option('userid', {
337 completion => \&PVE::AccessControl::complete_username,
338 }),
2c3a6c0a
DM
339 }
340 },
341 returns => { type => 'null' },
342 code => sub {
343 my ($param) = @_;
0a6e09fd 344
37d45deb
DM
345 my $rpcenv = PVE::RPCEnvironment::get();
346 my $authuser = $rpcenv->get_user();
347
0a6e09fd 348 my ($userid, $ruid, $realm) =
37d45deb 349 PVE::AccessControl::verify_username($param->{userid});
2c3a6c0a
DM
350
351 PVE::AccessControl::lock_user_config(
352 sub {
353
2c3a6c0a
DM
354 my $usercfg = cfs_read_file("user.cfg");
355
5bb4e06a
DM
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 }
2c3a6c0a 361
5bb4e06a 362 delete $usercfg->{users}->{$userid};
37d45deb
DM
363
364 PVE::AccessControl::delete_user_group($userid, $usercfg);
365 PVE::AccessControl::delete_user_acl($userid, $usercfg);
2c3a6c0a
DM
366
367 cfs_write_file("user.cfg", $usercfg);
368 }, "delete user failed");
0a6e09fd 369
2c3a6c0a
DM
370 return undef;
371 }});
372
3731;