]> git.proxmox.com Git - pve-access-control.git/blob - PVE/API2/User.pm
new API to change password
[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 => { user => 'all' },
41 parameters => {
42 additionalProperties => 0,
43 properties => {
44 enabled => {
45 type => 'boolean',
46 description => "Optional filter for enable property.",
47 optional => 1,
48 }
49 },
50 },
51 returns => {
52 type => 'array',
53 items => {
54 type => "object",
55 properties => {
56 userid => { type => 'string' },
57 },
58 },
59 links => [ { rel => 'child', href => "{userid}" } ],
60 },
61 code => sub {
62 my ($param) = @_;
63
64 my $rpcenv = PVE::RPCEnvironment::get();
65 my $usercfg = $rpcenv->{user_cfg};
66 my $authuser = $rpcenv->get_user();
67
68 my $res = [];
69
70 my $privs = [ 'Sys.UserMod', 'Sys.UserAdd' ];
71
72 my $canUserMod = $rpcenv->check_any($authuser, "/access", $privs, 1);
73 my $groups = $rpcenv->filter_groups($authuser, sub { return "/access/groups/" . shift; }, $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 description => "Create new user.",
102 parameters => {
103 additionalProperties => 0,
104 properties => {
105 userid => get_standard_option('userid'),
106 password => {
107 description => "Initial password.",
108 type => 'string',
109 optional => 1,
110 minLength => 5,
111 maxLength => 64
112 },
113 groups => { type => 'string', optional => 1, format => 'pve-groupid-list'},
114 firstname => { type => 'string', optional => 1 },
115 lastname => { type => 'string', optional => 1 },
116 email => { type => 'string', optional => 1, format => 'email-opt' },
117 comment => { type => 'string', optional => 1 },
118 expire => {
119 description => "Account expiration date (seconds since epoch). '0' means no expiration date.",
120 type => 'integer',
121 minimum => 0,
122 optional => 1,
123 },
124 enable => {
125 description => "Enable the account (default). You can set this to '0' to disable the accout",
126 type => 'boolean',
127 optional => 1,
128 default => 1,
129 },
130 },
131 },
132 returns => { type => 'null' },
133 code => sub {
134 my ($param) = @_;
135
136 PVE::AccessControl::lock_user_config(
137 sub {
138
139 my ($username, $ruid, $realm) = PVE::AccessControl::verify_username($param->{userid});
140
141 my $usercfg = cfs_read_file("user.cfg");
142
143 die "user '$username' already exists\n"
144 if $usercfg->{users}->{$username};
145
146 PVE::AccessControl::domain_set_password($realm, $ruid, $param->{password})
147 if defined($param->{password});
148
149 my $enable = defined($param->{enable}) ? $param->{enable} : 1;
150 $usercfg->{users}->{$username} = { enable => $enable };
151 $usercfg->{users}->{$username}->{expire} = $param->{expire} if $param->{expire};
152
153 if ($param->{groups}) {
154 foreach my $group (split_list($param->{groups})) {
155 if ($usercfg->{groups}->{$group}) {
156 PVE::AccessControl::add_user_group($username, $usercfg, $group);
157 } else {
158 die "no such group '$group'\n";
159 }
160 }
161 }
162
163 $usercfg->{users}->{$username}->{firstname} = $param->{firstname} if $param->{firstname};
164 $usercfg->{users}->{$username}->{lastname} = $param->{lastname} if $param->{lastname};
165 $usercfg->{users}->{$username}->{email} = $param->{email} if $param->{email};
166 $usercfg->{users}->{$username}->{comment} = $param->{comment} if $param->{comment};
167
168 cfs_write_file("user.cfg", $usercfg);
169 }, "create user failed");
170
171 return undef;
172 }});
173
174 __PACKAGE__->register_method ({
175 name => 'read_user',
176 path => '{userid}',
177 method => 'GET',
178 description => "Get user configuration.",
179 parameters => {
180 additionalProperties => 0,
181 properties => {
182 userid => get_standard_option('userid'),
183 },
184 },
185 returns => {
186 additionalProperties => 0,
187 properties => {
188 enable => { type => 'boolean' },
189 expire => { type => 'integer', optional => 1 },
190 firstname => { type => 'string', optional => 1 },
191 lastname => { type => 'string', optional => 1 },
192 email => { type => 'string', optional => 1 },
193 comment => { type => 'string', optional => 1 },
194 groups => { type => 'array' },
195 }
196 },
197 code => sub {
198 my ($param) = @_;
199
200 my ($username, undef, $domain) =
201 PVE::AccessControl::verify_username($param->{userid});
202
203 my $usercfg = cfs_read_file("user.cfg");
204
205 my $data = PVE::AccessControl::check_user_exist($usercfg, $username);
206
207 return &$extract_user_data($data, 1);
208 }});
209
210 __PACKAGE__->register_method ({
211 name => 'update_user',
212 protected => 1,
213 path => '{userid}',
214 method => 'PUT',
215 description => "Update user configuration.",
216 parameters => {
217 additionalProperties => 0,
218 properties => {
219 userid => get_standard_option('userid'),
220 groups => { type => 'string', optional => 1, format => 'pve-groupid-list' },
221 append => {
222 type => 'boolean',
223 optional => 1,
224 requires => 'groups',
225 },
226 enable => {
227 description => "Enable/disable the account.",
228 type => 'boolean',
229 optional => 1,
230 },
231 firstname => { type => 'string', optional => 1 },
232 lastname => { type => 'string', optional => 1 },
233 email => { type => 'string', optional => 1, format => 'email-opt' },
234 comment => { type => 'string', optional => 1 },
235 expire => {
236 description => "Account expiration date (seconds since epoch). '0' means no expiration date.",
237 type => 'integer',
238 minimum => 0,
239 optional => 1
240 },
241 },
242 },
243 returns => { type => 'null' },
244 code => sub {
245 my ($param) = @_;
246
247 my ($username, $ruid, $realm) =
248 PVE::AccessControl::verify_username($param->{userid});
249
250 PVE::AccessControl::lock_user_config(
251 sub {
252
253 my $usercfg = cfs_read_file("user.cfg");
254
255 PVE::AccessControl::check_user_exist($usercfg, $username);
256
257 $usercfg->{users}->{$username}->{enable} = $param->{enable} if defined($param->{enable});
258
259 $usercfg->{users}->{$username}->{expire} = $param->{expire} if defined($param->{expire});
260
261 PVE::AccessControl::delete_user_group($username, $usercfg)
262 if (!$param->{append} && defined($param->{groups}));
263
264 if ($param->{groups}) {
265 foreach my $group (split_list($param->{groups})) {
266 if ($usercfg->{groups}->{$group}) {
267 PVE::AccessControl::add_user_group($username, $usercfg, $group);
268 } else {
269 die "no such group '$group'\n";
270 }
271 }
272 }
273
274 $usercfg->{users}->{$username}->{firstname} = $param->{firstname} if defined($param->{firstname});
275 $usercfg->{users}->{$username}->{lastname} = $param->{lastname} if defined($param->{lastname});
276 $usercfg->{users}->{$username}->{email} = $param->{email} if defined($param->{email});
277 $usercfg->{users}->{$username}->{comment} = $param->{comment} if defined($param->{comment});
278
279 cfs_write_file("user.cfg", $usercfg);
280 }, "update user failed");
281
282 return undef;
283 }});
284
285 __PACKAGE__->register_method ({
286 name => 'delete_user',
287 protected => 1,
288 path => '{userid}',
289 method => 'DELETE',
290 description => "Delete user.",
291 permissions => { user => 'all' },
292 parameters => {
293 additionalProperties => 0,
294 properties => {
295 userid => get_standard_option('userid'),
296 }
297 },
298 returns => { type => 'null' },
299 code => sub {
300 my ($param) = @_;
301
302 my $rpcenv = PVE::RPCEnvironment::get();
303 my $authuser = $rpcenv->get_user();
304
305 my ($userid, $ruid, $realm) =
306 PVE::AccessControl::verify_username($param->{userid});
307
308 PVE::AccessControl::lock_user_config(
309 sub {
310
311 my $usercfg = cfs_read_file("user.cfg");
312
313 PVE::AccessControl::check_user_exist($usercfg, $userid);
314
315 my $privs = [ 'Sys.UserAdd' ]; # there is no Sys.UserDel
316 if (!$rpcenv->check($authuser, "/access", $privs, 1)) {
317 my $groups = $rpcenv->filter_groups($authuser, sub { return "/access/groups/" . shift; }, $privs, 1);
318 my $allowed_users = $rpcenv->group_member_join([keys %$groups]);
319 raise_perm_exc() if !$allowed_users->{$userid};
320 }
321
322 delete ($usercfg->{users}->{$userid});
323
324 PVE::AccessControl::delete_shadow_password($ruid) if $realm eq 'pve';
325
326 PVE::AccessControl::delete_user_group($userid, $usercfg);
327 PVE::AccessControl::delete_user_acl($userid, $usercfg);
328
329 cfs_write_file("user.cfg", $usercfg);
330 }, "delete user failed");
331
332 return undef;
333 }});
334
335 1;