]> git.proxmox.com Git - pmg-api.git/blame - PMG/API2/AccessControl.pm
correctly use PMG::RESTEnvironment->get()
[pmg-api.git] / PMG / API2 / AccessControl.pm
CommitLineData
1360e6f0
DM
1package PMG::API2::AccessControl;
2
3use strict;
4use warnings;
5
6use PVE::Exception qw(raise raise_perm_exc);
7use PVE::SafeSyslog;
9d82c6bc 8use PMG::RESTEnvironment;
1360e6f0
DM
9use PVE::RESTHandler;
10use PVE::JSONSchema qw(get_standard_option);
11
62ebb4bc
DM
12use PMG::Utils;
13use PMG::UserConfig;
1360e6f0 14use PMG::AccessControl;
4b0c6af3 15use PMG::API2::Users;
1360e6f0
DM
16
17use Data::Dumper;
18
19use base qw(PVE::RESTHandler);
20
4b0c6af3
DM
21__PACKAGE__->register_method ({
22 subclass => "PMG::API2::Users",
23 path => 'users',
24});
1360e6f0
DM
25
26__PACKAGE__->register_method ({
27 name => 'index',
28 path => '',
29 method => 'GET',
30 description => "Directory index.",
31 permissions => {
32 user => 'all',
33 },
34 parameters => {
35 additionalProperties => 0,
36 properties => {},
37 },
38 returns => {
39 type => 'array',
40 items => {
41 type => "object",
42 properties => {
43 subdir => { type => 'string' },
44 },
45 },
46 links => [ { rel => 'child', href => "{subdir}" } ],
47 },
48 code => sub {
49 my ($param) = @_;
50
4b0c6af3
DM
51 my $res = [
52 { subdir => 'ticket' },
53 { subdir => 'password' },
54 { subdir => 'users' },
55 ];
1360e6f0
DM
56
57 return $res;
58 }});
59
60
61my $create_ticket = sub {
62 my ($rpcenv, $username, $pw_or_ticket, $otp) = @_;
63
64 my $ticketuser;
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
68 } else {
69 $username = PMG::AccessControl::authenticate_user($username, $pw_or_ticket, $otp);
70 }
71
72 my $ticket = PMG::Ticket::assemble_ticket($username);
73 my $csrftoken = PMG::Ticket::assemble_csrf_prevention_token($username);
74
75 return {
76 ticket => $ticket,
77 username => $username,
78 CSRFPreventionToken => $csrftoken,
79 };
80};
81
82
83__PACKAGE__->register_method ({
84 name => 'get_ticket',
85 path => 'ticket',
86 method => 'GET',
87 permissions => { user => 'world' },
88 description => "Dummy. Useful for formaters which want to priovde a login page.",
89 parameters => {
90 additionalProperties => 0,
91 },
92 returns => { type => "null" },
93 code => sub { return undef; }});
94
95__PACKAGE__->register_method ({
96 name => 'create_ticket',
97 path => 'ticket',
98 method => 'POST',
99 permissions => {
100 description => "You need to pass valid credientials.",
101 user => 'world'
102 },
103 protected => 1, # else we can't access shadow files
104 description => "Create or verify authentication ticket.",
105 parameters => {
106 additionalProperties => 0,
107 properties => {
108 username => {
109 description => "User name",
110 type => 'string',
111 maxLength => 64,
112 },
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>.",
115 optional => 1,
116 }),
117 password => {
118 description => "The secret password. This can also be a valid ticket.",
119 type => 'string',
120 },
121 otp => {
122 description => "One-time password for Two-factor authentication.",
123 type => 'string',
124 optional => 1,
125 },
126 }
127 },
128 returns => {
129 type => "object",
130 properties => {
131 username => { type => 'string' },
132 ticket => { type => 'string', optional => 1},
133 CSRFPreventionToken => { type => 'string', optional => 1 },
d3d13358 134 role => { type => 'string', optional => 1},
1360e6f0
DM
135 }
136 },
137 code => sub {
138 my ($param) = @_;
139
140 my $username = $param->{username};
141 $username .= "\@$param->{realm}" if $param->{realm};
142
f1c29260 143 my $rpcenv = PMG::RESTEnvironment->get();
1360e6f0 144
1360e6f0
DM
145 my $res;
146 eval {
d3d13358 147 my $role = PMG::AccessControl::check_user_enabled($username);
1360e6f0 148 $res = &$create_ticket($rpcenv, $username, $param->{password}, $param->{otp});
d3d13358 149 $res->{role} = $role;
1360e6f0
DM
150 };
151 if (my $err = $@) {
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);
156 }
157
158 syslog('info', "successful auth for user '$username'");
159
160 return $res;
161 }});
162
163__PACKAGE__->register_method ({
164 name => 'change_passsword',
165 path => 'password',
166 method => 'PUT',
167 protected => 1, # else we can't access shadow files
2a19e199
DM
168 permissions => {
169 description => "Each user is allowed to change his own password. Only root can change the password of another user.",
170 user => 'all',
171 },
1360e6f0
DM
172 description => "Change user password.",
173 parameters => {
174 additionalProperties => 0,
175 properties => {
176 userid => get_standard_option('userid'),
177 password => {
178 description => "The new password.",
179 type => 'string',
180 minLength => 5,
181 maxLength => 64,
182 },
183 }
184 },
185 returns => { type => "null" },
186 code => sub {
187 my ($param) = @_;
188
f1c29260 189 my $rpcenv = PMG::RESTEnvironment->get();
1360e6f0
DM
190 my $authuser = $rpcenv->get_user();
191
62ebb4bc 192 my ($userid, $ruid, $realm) = PMG::Utils::verify_username($param->{userid});
1360e6f0
DM
193
194 if ($authuser eq 'root@pam') {
195 # OK - root can change anything
196 } else {
197 if ($authuser eq $userid) {
62ebb4bc
DM
198 # OK - each enable user can change its own password
199 PMG::AccessControl::check_user_enabled($userid);
1360e6f0
DM
200 } else {
201 raise_perm_exc();
202 }
203 }
204
be06bc52 205 PMG::AccessControl::set_user_password($userid, $param->{password});
1360e6f0
DM
206
207 syslog('info', "changed password for user '$userid'");
208
209 return undef;
210 }});
211
2121;