]>
Commit | Line | Data |
---|---|---|
2c3a6c0a DM |
1 | package PVE::API2::User; |
2 | ||
3 | use strict; | |
4 | use warnings; | |
37d45deb | 5 | use PVE::Exception qw(raise raise_perm_exc); |
2c3a6c0a DM |
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.", | |
930dcfc8 | 40 | permissions => { user => 'all' }, |
2c3a6c0a DM |
41 | parameters => { |
42 | additionalProperties => 0, | |
cb6f2f93 DM |
43 | properties => { |
44 | enabled => { | |
45 | type => 'boolean', | |
46 | description => "Optional filter for enable property.", | |
47 | optional => 1, | |
48 | } | |
49 | }, | |
2c3a6c0a DM |
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 | ||
930dcfc8 | 64 | my $rpcenv = PVE::RPCEnvironment::get(); |
37d45deb | 65 | my $usercfg = $rpcenv->{user_cfg}; |
930dcfc8 DM |
66 | my $authuser = $rpcenv->get_user(); |
67 | ||
2c3a6c0a DM |
68 | my $res = []; |
69 | ||
12683df7 | 70 | my $privs = [ 'User.Modify', 'User.Delete' ]; |
37d45deb DM |
71 | |
72 | my $canUserMod = $rpcenv->check_any($authuser, "/access", $privs, 1); | |
b9180ed2 | 73 | my $groups = $rpcenv->filter_groups($authuser, $privs, 1); |
37d45deb DM |
74 | my $allowed_users = $rpcenv->group_member_join([keys %$groups]); |
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 ({ | |
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'), | |
37d45deb DM |
106 | password => { |
107 | description => "Initial password.", | |
108 | type => 'string', | |
109 | optional => 1, | |
110 | minLength => 5, | |
111 | maxLength => 64 | |
112 | }, | |
2c3a6c0a DM |
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}) | |
fdb30a4c | 147 | if defined($param->{password}); |
2c3a6c0a DM |
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"); | |
2c3a6c0a | 204 | |
37d45deb DM |
205 | my $data = PVE::AccessControl::check_user_exist($usercfg, $username); |
206 | ||
2c3a6c0a DM |
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'), | |
2c3a6c0a DM |
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) = @_; | |
37d45deb DM |
246 | |
247 | my ($username, $ruid, $realm) = | |
248 | PVE::AccessControl::verify_username($param->{userid}); | |
2c3a6c0a DM |
249 | |
250 | PVE::AccessControl::lock_user_config( | |
251 | sub { | |
2c3a6c0a DM |
252 | |
253 | my $usercfg = cfs_read_file("user.cfg"); | |
254 | ||
37d45deb | 255 | PVE::AccessControl::check_user_exist($usercfg, $username); |
2c3a6c0a DM |
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) | |
e6521738 | 262 | if (!$param->{append} && defined($param->{groups})); |
2c3a6c0a DM |
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.", | |
12683df7 DM |
291 | permissions => { |
292 | check => ['userid-group', ['User.Delete']], | |
293 | }, | |
2c3a6c0a DM |
294 | parameters => { |
295 | additionalProperties => 0, | |
296 | properties => { | |
297 | userid => get_standard_option('userid'), | |
298 | } | |
299 | }, | |
300 | returns => { type => 'null' }, | |
301 | code => sub { | |
302 | my ($param) = @_; | |
37d45deb DM |
303 | |
304 | my $rpcenv = PVE::RPCEnvironment::get(); | |
305 | my $authuser = $rpcenv->get_user(); | |
306 | ||
307 | my ($userid, $ruid, $realm) = | |
308 | PVE::AccessControl::verify_username($param->{userid}); | |
2c3a6c0a DM |
309 | |
310 | PVE::AccessControl::lock_user_config( | |
311 | sub { | |
312 | ||
2c3a6c0a DM |
313 | my $usercfg = cfs_read_file("user.cfg"); |
314 | ||
37d45deb | 315 | delete ($usercfg->{users}->{$userid}); |
2c3a6c0a DM |
316 | |
317 | PVE::AccessControl::delete_shadow_password($ruid) if $realm eq 'pve'; | |
37d45deb DM |
318 | |
319 | PVE::AccessControl::delete_user_group($userid, $usercfg); | |
320 | PVE::AccessControl::delete_user_acl($userid, $usercfg); | |
2c3a6c0a DM |
321 | |
322 | cfs_write_file("user.cfg", $usercfg); | |
323 | }, "delete user failed"); | |
324 | ||
325 | return undef; | |
326 | }}); | |
327 | ||
328 | 1; |