1 package PMG
::API2
::AccessControl
;
6 use PVE
::Exception
qw(raise raise_perm_exc);
8 use PMG
::RESTEnvironment
;
10 use PVE
::JSONSchema
qw(get_standard_option);
14 use PMG
::AccessControl
;
19 use base
qw(PVE::RESTHandler);
21 __PACKAGE__-
>register_method ({
22 subclass
=> "PMG::API2::Users",
26 __PACKAGE__-
>register_method ({
30 description
=> "Directory index.",
35 additionalProperties
=> 0,
43 subdir
=> { type
=> 'string' },
46 links
=> [ { rel
=> 'child', href
=> "{subdir}" } ],
52 { subdir
=> 'ticket' },
53 { subdir
=> 'password' },
54 { subdir
=> 'users' },
61 my $create_ticket = sub {
62 my ($rpcenv, $username, $pw_or_ticket, $otp) = @_;
65 if (($ticketuser = PMG
::Ticket
::verify_ticket
($pw_or_ticket, 1)) &&
66 ($ticketuser eq 'root@pam' || $ticketuser eq $username)) {
67 # valid ticket. Note: root@pam can create tickets for other users
69 $username = PMG
::AccessControl
::authenticate_user
($username, $pw_or_ticket, $otp);
72 my $ticket = PMG
::Ticket
::assemble_ticket
($username);
73 my $csrftoken = PMG
::Ticket
::assemble_csrf_prevention_token
($username);
77 username
=> $username,
78 CSRFPreventionToken
=> $csrftoken,
83 __PACKAGE__-
>register_method ({
87 permissions
=> { user
=> 'world' },
88 description
=> "Dummy. Useful for formaters which want to priovde a login page.",
90 additionalProperties
=> 0,
92 returns
=> { type
=> "null" },
93 code
=> sub { return undef; }});
95 __PACKAGE__-
>register_method ({
96 name
=> 'create_ticket',
100 description
=> "You need to pass valid credientials.",
103 protected
=> 1, # else we can't access shadow files
104 description
=> "Create or verify authentication ticket.",
106 additionalProperties
=> 0,
109 description
=> "User name",
113 realm
=> get_standard_option
('realm', {
114 description
=> "You can optionally pass the realm using this parameter. Normally the realm is simply added to the username <username>\@<relam>.",
118 description
=> "The secret password. This can also be a valid ticket.",
122 description
=> "One-time password for Two-factor authentication.",
131 username
=> { type
=> 'string' },
132 ticket
=> { type
=> 'string', optional
=> 1},
133 CSRFPreventionToken
=> { type
=> 'string', optional
=> 1 },
134 role => { type
=> 'string', optional
=> 1},
140 my $username = $param->{username
};
141 $username .= "\@$param->{realm}" if $param->{realm
};
143 my $rpcenv = PMG
::RESTEnvironment-
>get();
147 my $role = PMG
::AccessControl
::check_user_enabled
($username);
148 $res = &$create_ticket($rpcenv, $username, $param->{password
}, $param->{otp
});
149 $res->{role} = $role;
152 my $clientip = $rpcenv->get_client_ip() || '';
153 syslog
('err', "authentication failure; rhost=$clientip user=$username msg=$err");
154 # do not return any info to prevent user enumeration attacks
155 die PVE
::Exception-
>new("authentication failure\n", code
=> 401);
158 syslog
('info', "successful auth for user '$username'");
163 __PACKAGE__-
>register_method ({
164 name
=> 'change_passsword',
167 protected
=> 1, # else we can't access shadow files
169 description
=> "Each user is allowed to change his own password. Only root can change the password of another user.",
172 description
=> "Change user password.",
174 additionalProperties
=> 0,
176 userid
=> get_standard_option
('userid'),
178 description
=> "The new password.",
185 returns
=> { type
=> "null" },
189 my $rpcenv = PMG
::RESTEnvironment-
>get();
190 my $authuser = $rpcenv->get_user();
192 my ($userid, $ruid, $realm) = PMG
::Utils
::verify_username
($param->{userid
});
194 if ($authuser eq 'root@pam') {
195 # OK - root can change anything
197 if ($authuser eq $userid) {
198 # OK - each enable user can change its own password
199 PMG
::AccessControl
::check_user_enabled
($userid);
205 PMG
::AccessControl
::set_user_password
($userid, $param->{password
});
207 syslog
('info', "changed password for user '$userid'");