]> git.proxmox.com Git - pve-access-control.git/blob - PVE/API2/User.pm
add description
[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 Data::Dumper; # fixme: remove
14
15 use PVE::RESTHandler;
16
17 use base qw(PVE::RESTHandler);
18
19 my $extract_user_data = sub {
20 my ($data, $full) = @_;
21
22 my $res = {};
23
24 foreach my $prop (qw(enable expire firstname lastname email comment)) {
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.",
40 permissions => {
41 description => "The returned list is restricted to users where you have 'User.Modify' or 'User.Allocate' permissions on '/access' or on a group the user belongs too. But it always includes the current (authenticated) user.",
42 user => 'all',
43 },
44 parameters => {
45 additionalProperties => 0,
46 properties => {
47 enabled => {
48 type => 'boolean',
49 description => "Optional filter for enable property.",
50 optional => 1,
51 }
52 },
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
67 my $rpcenv = PVE::RPCEnvironment::get();
68 my $usercfg = $rpcenv->{user_cfg};
69 my $authuser = $rpcenv->get_user();
70
71 my $res = [];
72
73 my $privs = [ 'User.Modify', 'User.Allocate' ];
74
75 my $canUserMod = $rpcenv->check_any($authuser, "/access", $privs, 1);
76 my $groups = $rpcenv->filter_groups($authuser, $privs, 1);
77 my $allowed_users = $rpcenv->group_member_join([keys %$groups]);
78
79 foreach my $user (keys %{$usercfg->{users}}) {
80
81 if (!($canUserMod || $user eq $authuser)) {
82 next if !$allowed_users->{$user};
83 }
84
85 my $entry = &$extract_user_data($usercfg->{users}->{$user});
86
87 if (defined($param->{enabled})) {
88 next if $entry->{enable} && !$param->{enabled};
89 next if !$entry->{enable} && $param->{enabled};
90 }
91
92 $entry->{userid} = $user;
93 push @$res, $entry;
94 }
95
96 return $res;
97 }});
98
99 __PACKAGE__->register_method ({
100 name => 'create_user',
101 protected => 1,
102 path => '',
103 method => 'POST',
104 permissions => {
105 description => "You need 'User.Allocate' permissions to '/access/groups/<group>' for any group specified, or 'User.Allocate' on '/access' if you pass no groups.",
106 check => ['userid-group', ['User.Allocate'], groups_param => 1],
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 => { type => 'string', optional => 1, format => 'pve-groupid-list'},
121 firstname => { type => 'string', optional => 1 },
122 lastname => { type => 'string', optional => 1 },
123 email => { type => 'string', optional => 1, format => 'email-opt' },
124 comment => { type => 'string', optional => 1 },
125 expire => {
126 description => "Account expiration date (seconds since epoch). '0' means no expiration date.",
127 type => 'integer',
128 minimum => 0,
129 optional => 1,
130 },
131 enable => {
132 description => "Enable the account (default). You can set this to '0' to disable the accout",
133 type => 'boolean',
134 optional => 1,
135 default => 1,
136 },
137 },
138 },
139 returns => { type => 'null' },
140 code => sub {
141 my ($param) = @_;
142
143 PVE::AccessControl::lock_user_config(
144 sub {
145
146 my ($username, $ruid, $realm) = PVE::AccessControl::verify_username($param->{userid});
147
148 my $usercfg = cfs_read_file("user.cfg");
149
150 die "user '$username' already exists\n"
151 if $usercfg->{users}->{$username};
152
153 PVE::AccessControl::domain_set_password($realm, $ruid, $param->{password})
154 if defined($param->{password});
155
156 my $enable = defined($param->{enable}) ? $param->{enable} : 1;
157 $usercfg->{users}->{$username} = { enable => $enable };
158 $usercfg->{users}->{$username}->{expire} = $param->{expire} if $param->{expire};
159
160 if ($param->{groups}) {
161 foreach my $group (split_list($param->{groups})) {
162 if ($usercfg->{groups}->{$group}) {
163 PVE::AccessControl::add_user_group($username, $usercfg, $group);
164 } else {
165 die "no such group '$group'\n";
166 }
167 }
168 }
169
170 $usercfg->{users}->{$username}->{firstname} = $param->{firstname} if $param->{firstname};
171 $usercfg->{users}->{$username}->{lastname} = $param->{lastname} if $param->{lastname};
172 $usercfg->{users}->{$username}->{email} = $param->{email} if $param->{email};
173 $usercfg->{users}->{$username}->{comment} = $param->{comment} if $param->{comment};
174
175 cfs_write_file("user.cfg", $usercfg);
176 }, "create user failed");
177
178 return undef;
179 }});
180
181 __PACKAGE__->register_method ({
182 name => 'read_user',
183 path => '{userid}',
184 method => 'GET',
185 description => "Get user configuration.",
186 permissions => {
187 check => ['userid-group', ['User.Modify']],
188 },
189 parameters => {
190 additionalProperties => 0,
191 properties => {
192 userid => get_standard_option('userid'),
193 },
194 },
195 returns => {
196 additionalProperties => 0,
197 properties => {
198 enable => { type => 'boolean' },
199 expire => { type => 'integer', optional => 1 },
200 firstname => { type => 'string', optional => 1 },
201 lastname => { type => 'string', optional => 1 },
202 email => { type => 'string', optional => 1 },
203 comment => { type => 'string', optional => 1 },
204 groups => { type => 'array' },
205 }
206 },
207 code => sub {
208 my ($param) = @_;
209
210 my ($username, undef, $domain) =
211 PVE::AccessControl::verify_username($param->{userid});
212
213 my $usercfg = cfs_read_file("user.cfg");
214
215 my $data = PVE::AccessControl::check_user_exist($usercfg, $username);
216
217 return &$extract_user_data($data, 1);
218 }});
219
220 __PACKAGE__->register_method ({
221 name => 'update_user',
222 protected => 1,
223 path => '{userid}',
224 method => 'PUT',
225 permissions => {
226 check => ['userid-group', ['User.Modify'], groups_param => 1 ],
227 },
228 description => "Update user configuration.",
229 parameters => {
230 additionalProperties => 0,
231 properties => {
232 userid => get_standard_option('userid'),
233 groups => { type => 'string', optional => 1, format => 'pve-groupid-list' },
234 append => {
235 type => 'boolean',
236 optional => 1,
237 requires => 'groups',
238 },
239 enable => {
240 description => "Enable/disable the account.",
241 type => 'boolean',
242 optional => 1,
243 },
244 firstname => { type => 'string', optional => 1 },
245 lastname => { type => 'string', optional => 1 },
246 email => { type => 'string', optional => 1, format => 'email-opt' },
247 comment => { type => 'string', optional => 1 },
248 expire => {
249 description => "Account expiration date (seconds since epoch). '0' means no expiration date.",
250 type => 'integer',
251 minimum => 0,
252 optional => 1
253 },
254 },
255 },
256 returns => { type => 'null' },
257 code => sub {
258 my ($param) = @_;
259
260 my ($username, $ruid, $realm) =
261 PVE::AccessControl::verify_username($param->{userid});
262
263 PVE::AccessControl::lock_user_config(
264 sub {
265
266 my $usercfg = cfs_read_file("user.cfg");
267
268 PVE::AccessControl::check_user_exist($usercfg, $username);
269
270 $usercfg->{users}->{$username}->{enable} = $param->{enable} if defined($param->{enable});
271
272 $usercfg->{users}->{$username}->{expire} = $param->{expire} if defined($param->{expire});
273
274 PVE::AccessControl::delete_user_group($username, $usercfg)
275 if (!$param->{append} && defined($param->{groups}));
276
277 if ($param->{groups}) {
278 foreach my $group (split_list($param->{groups})) {
279 if ($usercfg->{groups}->{$group}) {
280 PVE::AccessControl::add_user_group($username, $usercfg, $group);
281 } else {
282 die "no such group '$group'\n";
283 }
284 }
285 }
286
287 $usercfg->{users}->{$username}->{firstname} = $param->{firstname} if defined($param->{firstname});
288 $usercfg->{users}->{$username}->{lastname} = $param->{lastname} if defined($param->{lastname});
289 $usercfg->{users}->{$username}->{email} = $param->{email} if defined($param->{email});
290 $usercfg->{users}->{$username}->{comment} = $param->{comment} if defined($param->{comment});
291
292 cfs_write_file("user.cfg", $usercfg);
293 }, "update user failed");
294
295 return undef;
296 }});
297
298 __PACKAGE__->register_method ({
299 name => 'delete_user',
300 protected => 1,
301 path => '{userid}',
302 method => 'DELETE',
303 description => "Delete user.",
304 permissions => {
305 check => ['userid-group', ['User.Allocate']],
306 },
307 parameters => {
308 additionalProperties => 0,
309 properties => {
310 userid => get_standard_option('userid'),
311 }
312 },
313 returns => { type => 'null' },
314 code => sub {
315 my ($param) = @_;
316
317 my $rpcenv = PVE::RPCEnvironment::get();
318 my $authuser = $rpcenv->get_user();
319
320 my ($userid, $ruid, $realm) =
321 PVE::AccessControl::verify_username($param->{userid});
322
323 PVE::AccessControl::lock_user_config(
324 sub {
325
326 my $usercfg = cfs_read_file("user.cfg");
327
328 delete ($usercfg->{users}->{$userid});
329
330 PVE::AccessControl::delete_shadow_password($ruid) if $realm eq 'pve';
331
332 PVE::AccessControl::delete_user_group($userid, $usercfg);
333 PVE::AccessControl::delete_user_acl($userid, $usercfg);
334
335 cfs_write_file("user.cfg", $usercfg);
336 }, "delete user failed");
337
338 return undef;
339 }});
340
341 1;