6272cadbb1d366816ab86cec30e3ff6411cb1714
[pve-access-control.git] / PVE / API2 / User.pm
1 package PVE::API2::User;
2
3 use strict;
4 use warnings;
5 use PVE::Cluster qw (cfs_read_file cfs_write_file);
6 use PVE::Tools qw(split_list);
7 use PVE::AccessControl;
8 use PVE::JSONSchema qw(get_standard_option);
9
10 use PVE::SafeSyslog;
11
12 use Data::Dumper; # fixme: remove
13
14 use PVE::RESTHandler;
15
16 use base qw(PVE::RESTHandler);
17
18 my $extract_user_data = sub {
19     my ($data, $full) = @_;
20
21     my $res = {};
22
23     foreach my $prop (qw(enable expire firstname lastname email comment)) {
24         $res->{$prop} = $data->{$prop} if defined($data->{$prop});
25     }
26
27     return $res if !$full;
28
29     $res->{groups} = $data->{groups} ? [ keys %{$data->{groups}} ] : [];
30
31     return $res;
32 };
33
34 __PACKAGE__->register_method ({
35     name => 'index', 
36     path => '', 
37     method => 'GET',
38     description => "User index.",
39     parameters => {
40         additionalProperties => 0,
41         properties => {
42             enabled => {
43                 type => 'boolean',
44                 description => "Optional filter for enable property.",
45                 optional => 1,
46             }
47         },
48     },
49     returns => {
50         type => 'array',
51         items => {
52             type => "object",
53             properties => {
54                 userid => { type => 'string' },
55             },
56         },
57         links => [ { rel => 'child', href => "{userid}" } ],
58     },
59     code => sub {
60         my ($param) = @_;
61     
62         my $res = [];
63
64         my $usercfg = cfs_read_file("user.cfg");
65  
66         foreach my $user (keys %{$usercfg->{users}}) {
67             next if $user eq 'root';
68             
69             my $entry = &$extract_user_data($usercfg->{users}->{$user});
70
71             if (defined($param->{enabled})) {
72                 next if $entry->{enable} && !$param->{enabled};
73                 next if !$entry->{enable} && $param->{enabled};
74             }
75
76             $entry->{userid} = $user;
77             push @$res, $entry;
78         }
79
80         return $res;
81     }});
82
83 __PACKAGE__->register_method ({
84     name => 'create_user', 
85     protected => 1,
86     path => '', 
87     method => 'POST',
88     description => "Create new user.",
89     parameters => {
90         additionalProperties => 0,
91         properties => {
92             userid => get_standard_option('userid'),
93             password => { type => 'string', optional => 1 },
94             groups => { type => 'string', optional => 1, format => 'pve-groupid-list'},
95             firstname => { type => 'string', optional => 1 },
96             lastname => { type => 'string', optional => 1 },
97             email => { type => 'string', optional => 1, format => 'email-opt' },
98             comment => { type => 'string', optional => 1 },
99             expire => { 
100                 description => "Account expiration date (seconds since epoch). '0' means no expiration date.",
101                 type => 'integer', 
102                 minimum => 0,
103                 optional => 1,
104             },
105             enable => {
106                 description => "Enable the account (default). You can set this to '0' to disable the accout",
107                 type => 'boolean',
108                 optional => 1,
109                 default => 1,
110             },
111         },
112     },
113     returns => { type => 'null' },
114     code => sub {
115         my ($param) = @_;
116
117         PVE::AccessControl::lock_user_config(
118             sub {
119                         
120                 my ($username, $ruid, $realm) = PVE::AccessControl::verify_username($param->{userid});
121         
122                 my $usercfg = cfs_read_file("user.cfg");
123
124                 die "user '$username' already exists\n" 
125                     if $usercfg->{users}->{$username};
126                          
127                 PVE::AccessControl::domain_set_password($realm, $ruid, $param->{password})
128                     if $param->{password};
129
130                 my $enable = defined($param->{enable}) ? $param->{enable} : 1;
131                 $usercfg->{users}->{$username} = { enable => $enable };
132                 $usercfg->{users}->{$username}->{expire} = $param->{expire} if $param->{expire};
133
134                 if ($param->{groups}) {
135                     foreach my $group (split_list($param->{groups})) {
136                         if ($usercfg->{groups}->{$group}) {
137                             PVE::AccessControl::add_user_group($username, $usercfg, $group);
138                         } else {
139                             die "no such group '$group'\n";
140                         }
141                     }
142                 }
143
144                 $usercfg->{users}->{$username}->{firstname} = $param->{firstname} if $param->{firstname};
145                 $usercfg->{users}->{$username}->{lastname} = $param->{lastname} if $param->{lastname};
146                 $usercfg->{users}->{$username}->{email} = $param->{email} if $param->{email};
147                 $usercfg->{users}->{$username}->{comment} = $param->{comment} if $param->{comment};
148
149                 cfs_write_file("user.cfg", $usercfg);
150             }, "create user failed");
151
152         return undef;
153     }});
154
155 __PACKAGE__->register_method ({
156     name => 'read_user', 
157     path => '{userid}', 
158     method => 'GET',
159     description => "Get user configuration.",
160     parameters => {
161         additionalProperties => 0,
162         properties => {
163             userid => get_standard_option('userid'),
164         },
165     },
166     returns => {
167         additionalProperties => 0,
168         properties => {
169             enable => { type => 'boolean' },
170             expire => { type => 'integer', optional => 1 },
171             firstname => { type => 'string', optional => 1 },
172             lastname => { type => 'string', optional => 1 },
173             email => { type => 'string', optional => 1 },
174             comment => { type => 'string', optional => 1 },    
175             groups => { type => 'array' },
176         }
177     },
178     code => sub {
179         my ($param) = @_;
180
181         my ($username, undef, $domain) = 
182             PVE::AccessControl::verify_username($param->{userid});
183
184         my $usercfg = cfs_read_file("user.cfg");
185  
186         my $data = $usercfg->{users}->{$username};
187
188         die "user '$username' does not exist\n" if !$data;
189
190         return &$extract_user_data($data, 1);
191     }});
192
193 __PACKAGE__->register_method ({
194     name => 'update_user', 
195     protected => 1,
196     path => '{userid}', 
197     method => 'PUT',
198     description => "Update user configuration.",
199     parameters => {
200         additionalProperties => 0,
201         properties => {
202             userid => get_standard_option('userid'),
203             password => { type => 'string', optional => 1 },
204             groups => { type => 'string', optional => 1,  format => 'pve-groupid-list'  },
205             append => { 
206                 type => 'boolean', 
207                 optional => 1,
208                 requires => 'groups',
209             },
210             enable => {
211                 description => "Enable/disable the account.",
212                 type => 'boolean',
213                 optional => 1,
214             },
215             firstname => { type => 'string', optional => 1 },
216             lastname => { type => 'string', optional => 1 },
217             email => { type => 'string', optional => 1, format => 'email-opt' },
218             comment => { type => 'string', optional => 1 },
219             expire => { 
220                 description => "Account expiration date (seconds since epoch). '0' means no expiration date.",
221                 type => 'integer', 
222                 minimum => 0,
223                 optional => 1 
224             },
225         },
226     },
227     returns => { type => 'null' },
228     code => sub {
229         my ($param) = @_;
230         
231         PVE::AccessControl::lock_user_config(
232             sub {
233
234                 my ($username, $ruid, $realm) = 
235                     PVE::AccessControl::verify_username($param->{userid});
236         
237                 my $usercfg = cfs_read_file("user.cfg");
238
239                 die "user '$username' does not exist\n" 
240                     if !$usercfg->{users}->{$username};
241
242                 PVE::AccessControl::domain_set_password($realm, $ruid, $param->{password})
243                     if $param->{password};
244
245                 $usercfg->{users}->{$username}->{enable} = $param->{enable} if defined($param->{enable});
246
247                 $usercfg->{users}->{$username}->{expire} = $param->{expire} if defined($param->{expire});
248
249                 PVE::AccessControl::delete_user_group($username, $usercfg) 
250                     if (!$param->{append} && $param->{groups});
251
252                 if ($param->{groups}) {
253                     foreach my $group (split_list($param->{groups})) {
254                         if ($usercfg->{groups}->{$group}) {
255                             PVE::AccessControl::add_user_group($username, $usercfg, $group);
256                         } else {
257                             die "no such group '$group'\n";
258                         }
259                     }
260                 }
261
262                 $usercfg->{users}->{$username}->{firstname} = $param->{firstname} if defined($param->{firstname});
263                 $usercfg->{users}->{$username}->{lastname} = $param->{lastname} if defined($param->{lastname});
264                 $usercfg->{users}->{$username}->{email} = $param->{email} if defined($param->{email});
265                 $usercfg->{users}->{$username}->{comment} = $param->{comment} if defined($param->{comment});
266
267                 cfs_write_file("user.cfg", $usercfg);
268             }, "update user failed");
269         
270         return undef;
271     }});
272
273 __PACKAGE__->register_method ({
274     name => 'delete_user', 
275     protected => 1,
276     path => '{userid}', 
277     method => 'DELETE',
278     description => "Delete user.",
279     parameters => {
280         additionalProperties => 0,
281         properties => {
282             userid => get_standard_option('userid'),
283         }
284     },
285     returns => { type => 'null' },
286     code => sub {
287         my ($param) = @_;
288
289         PVE::AccessControl::lock_user_config(
290             sub {
291
292                 my ($username, $ruid, $realm) = 
293                     PVE::AccessControl::verify_username($param->{userid});
294
295                 my $usercfg = cfs_read_file("user.cfg");
296
297                 die "user '$username' does not exist\n" 
298                     if !$usercfg->{users}->{$username};
299
300                 delete ($usercfg->{users}->{$username});
301
302                 PVE::AccessControl::delete_shadow_password($ruid) if $realm eq 'pve';
303                 PVE::AccessControl::delete_user_group($username, $usercfg);
304                 PVE::AccessControl::delete_user_acl($username, $usercfg);
305
306                 cfs_write_file("user.cfg", $usercfg);
307             }, "delete user failed");
308         
309         return undef;
310     }});
311
312 1;