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