Rev 1107 | Rev 1130 | Go to most recent revision | Show entire file | Regard whitespace | Details | Blame | Last modification | View Log | RSS feed
Rev 1107 | Rev 1116 | ||
---|---|---|---|
Line 25... | Line 25... | ||
25 | 25 | ||
26 | class OIDplusAuthUtils extends OIDplusBaseClass { |
26 | class OIDplusAuthUtils extends OIDplusBaseClass { |
27 | 27 | ||
28 | // Useful functions |
28 | // Useful functions |
29 | 29 | ||
- | 30 | /** |
|
- | 31 | * @param string $password |
|
- | 32 | * @return string |
|
- | 33 | * @throws OIDplusException |
|
- | 34 | */ |
|
30 | private static function raPepperProcessing(string $password): string { |
35 | private static function raPepperProcessing(string $password): string { |
31 | // Additional feature: Pepper |
36 | // Additional feature: Pepper |
32 | // The pepper is stored inside the base configuration file |
37 | // The pepper is stored inside the base configuration file |
33 | // It prevents that an attacker with SQL write rights can |
38 | // It prevents that an attacker with SQL write rights can |
34 | // create accounts. |
39 | // create accounts. |
Line 41... | Line 46... | ||
41 | if (strtolower($algo) === 'sha3-512') { |
46 | if (strtolower($algo) === 'sha3-512') { |
42 | $hmac = sha3_512_hmac($password, $pepper); |
47 | $hmac = sha3_512_hmac($password, $pepper); |
43 | } else { |
48 | } else { |
44 | $hmac = hash_hmac($algo, $password, $pepper); |
49 | $hmac = hash_hmac($algo, $password, $pepper); |
45 | } |
50 | } |
46 | if ($hmac === false) throw new OIDplusException(_L('HMAC failed')); |
51 | if ($hmac === "") throw new OIDplusException(_L('HMAC failed')); |
47 | return $hmac; |
52 | return $hmac; |
48 | } else { |
53 | } else { |
49 | return $password; |
54 | return $password; |
50 | } |
55 | } |
51 | } |
56 | } |
52 | 57 | ||
53 | // Content provider |
58 | // Content provider |
54 | 59 | ||
- | 60 | /** |
|
- | 61 | * @return string |
|
- | 62 | * @throws OIDplusException |
|
- | 63 | */ |
|
55 | public function getAuthMethod() { |
64 | public function getAuthMethod() { |
56 | $acs = $this->getAuthContentStore(); |
65 | $acs = $this->getAuthContentStore(); |
57 | if (is_null($acs)) return 'null'; |
66 | if (is_null($acs)) return 'null'; |
58 | return get_class($acs); |
67 | return get_class($acs); |
59 | } |
68 | } |
60 | 69 | ||
- | 70 | /** |
|
- | 71 | * @return OIDplusAuthContentStore|null |
|
- | 72 | * @throws OIDplusException |
|
- | 73 | */ |
|
61 | protected function getAuthContentStore() { |
74 | protected function getAuthContentStore()/*: ?OIDplusAuthContentStore*/ { |
62 | // Logged in via JWT |
75 | // Logged in via JWT |
63 | $tmp = OIDplusAuthContentStoreJWT::getActiveProvider(); |
76 | $tmp = OIDplusAuthContentStoreJWT::getActiveProvider(); |
64 | if ($tmp) return $tmp; |
77 | if ($tmp) return $tmp; |
65 | 78 | ||
66 | // Normal login via web-browser |
79 | // Normal login via web-browser |
Line 70... | Line 83... | ||
70 | 83 | ||
71 | // No active session and no JWT token available. User is not logged in. |
84 | // No active session and no JWT token available. User is not logged in. |
72 | return null; |
85 | return null; |
73 | } |
86 | } |
74 | 87 | ||
- | 88 | /** |
|
- | 89 | * @param string $name |
|
- | 90 | * @param mixed|null $default |
|
- | 91 | * @return mixed |
|
- | 92 | * @throws OIDplusException |
|
- | 93 | */ |
|
75 | public function getExtendedAttribute($name, $default=NULL) { |
94 | public function getExtendedAttribute(string $name, $default=NULL) { |
76 | $acs = $this->getAuthContentStore(); |
95 | $acs = $this->getAuthContentStore(); |
77 | if (is_null($acs)) return $default; |
96 | if (is_null($acs)) return $default; |
78 | return $acs->getValue($name, $default); |
97 | return $acs->getValue($name, $default); |
79 | } |
98 | } |
80 | 99 | ||
81 | // RA authentication functions |
100 | // RA authentication functions |
82 | 101 | ||
- | 102 | /** |
|
- | 103 | * @param string $email |
|
- | 104 | * @return void |
|
- | 105 | * @throws OIDplusException |
|
- | 106 | */ |
|
83 | public function raLogin($email) { |
107 | public function raLogin(string $email) { |
84 | $acs = $this->getAuthContentStore(); |
108 | $acs = $this->getAuthContentStore(); |
85 | if (is_null($acs)) return; |
109 | if (is_null($acs)) return; |
86 | return $acs->raLogin($email); |
110 | $acs->raLogin($email); |
87 | } |
111 | } |
88 | 112 | ||
- | 113 | /** |
|
- | 114 | * @param string $email |
|
- | 115 | * @return void |
|
- | 116 | * @throws OIDplusException |
|
- | 117 | */ |
|
89 | public function raLogout($email) { |
118 | public function raLogout(string $email) { |
90 | $acs = $this->getAuthContentStore(); |
119 | $acs = $this->getAuthContentStore(); |
91 | if (is_null($acs)) return; |
120 | if (is_null($acs)) return; |
92 | return $acs->raLogout($email); |
121 | $acs->raLogout($email); |
93 | } |
122 | } |
94 | 123 | ||
- | 124 | /** |
|
- | 125 | * @param string $ra_email |
|
- | 126 | * @param string $password |
|
- | 127 | * @return bool |
|
- | 128 | * @throws OIDplusException |
|
- | 129 | */ |
|
95 | public function raCheckPassword($ra_email, $password) { |
130 | public function raCheckPassword(string $ra_email, string $password): bool { |
96 | $ra = new OIDplusRA($ra_email); |
131 | $ra = new OIDplusRA($ra_email); |
97 | 132 | ||
98 | // Get RA info from RA |
133 | // Get RA info from RA |
99 | $authInfo = $ra->getAuthInfo(); |
134 | $authInfo = $ra->getAuthInfo(); |
100 | if (!$authInfo) return false; // user not found |
135 | if (!$authInfo) return false; // user not found |
Line 109... | Line 144... | ||
109 | } |
144 | } |
110 | 145 | ||
111 | return false; |
146 | return false; |
112 | } |
147 | } |
113 | 148 | ||
- | 149 | /** |
|
- | 150 | * @return int |
|
- | 151 | * @throws OIDplusException |
|
- | 152 | */ |
|
114 | public function raNumLoggedIn() { |
153 | public function raNumLoggedIn(): int { |
115 | $acs = $this->getAuthContentStore(); |
154 | $acs = $this->getAuthContentStore(); |
116 | if (is_null($acs)) return 0; |
155 | if (is_null($acs)) return 0; |
117 | return $acs->raNumLoggedIn(); |
156 | return $acs->raNumLoggedIn(); |
118 | } |
157 | } |
119 | 158 | ||
- | 159 | /** |
|
- | 160 | * @return OIDplusRA[] |
|
- | 161 | * @throws OIDplusException |
|
- | 162 | */ |
|
120 | public function loggedInRaList() { |
163 | public function loggedInRaList(): array { |
121 | if ($this->forceAllLoggedOut()) { |
164 | if ($this->forceAllLoggedOut()) { |
122 | return array(); |
165 | return array(); |
123 | } else { |
166 | } else { |
124 | $acs = $this->getAuthContentStore(); |
167 | $acs = $this->getAuthContentStore(); |
125 | if (is_null($acs)) return array(); |
168 | if (is_null($acs)) return array(); |
126 | return $acs->loggedInRaList(); |
169 | return $acs->loggedInRaList(); |
127 | } |
170 | } |
128 | } |
171 | } |
129 | 172 | ||
- | 173 | /** |
|
- | 174 | * @param string $email |
|
- | 175 | * @return bool |
|
- | 176 | * @throws OIDplusException |
|
- | 177 | */ |
|
130 | public function isRaLoggedIn($email) { |
178 | public function isRaLoggedIn(string $email): bool { |
131 | $acs = $this->getAuthContentStore(); |
179 | $acs = $this->getAuthContentStore(); |
132 | if (is_null($acs)) return false; |
180 | if (is_null($acs)) return false; |
133 | return $acs->isRaLoggedIn($email); |
181 | return $acs->isRaLoggedIn($email); |
134 | } |
182 | } |
135 | 183 | ||
136 | // "High level" function including logging and checking for valid JWT alternations |
184 | // "High level" function including logging and checking for valid JWT alternations |
- | 185 | ||
- | 186 | /** |
|
- | 187 | * @param string $email |
|
- | 188 | * @param bool $remember_me |
|
- | 189 | * @param string $origin |
|
- | 190 | * @return void |
|
- | 191 | * @throws OIDplusException |
|
- | 192 | */ |
|
137 | public function raLoginEx($email, $remember_me, $origin='') { |
193 | public function raLoginEx(string $email, bool $remember_me, string $origin='') { |
138 | $loginfo = ''; |
194 | $loginfo = ''; |
139 | $acs = $this->getAuthContentStore(); |
195 | $acs = $this->getAuthContentStore(); |
140 | if (!is_null($acs)) { |
196 | if (!is_null($acs)) { |
141 | $acs->raLoginEx($email, $loginfo); |
197 | $acs->raLoginEx($email, $loginfo); |
142 | $acs->activate(); |
198 | $acs->activate(); |
Line 161... | Line 217... | ||
161 | if ($origin != '') $logmsg .= " via $origin"; |
217 | if ($origin != '') $logmsg .= " via $origin"; |
162 | if ($loginfo != '') $logmsg .= " ($loginfo)"; |
218 | if ($loginfo != '') $logmsg .= " ($loginfo)"; |
163 | OIDplus::logger()->log("[OK]RA($email)!", $logmsg); |
219 | OIDplus::logger()->log("[OK]RA($email)!", $logmsg); |
164 | } |
220 | } |
165 | 221 | ||
- | 222 | /** |
|
- | 223 | * @param string $email |
|
- | 224 | * @return void |
|
- | 225 | * @throws OIDplusException |
|
- | 226 | */ |
|
166 | public function raLogoutEx($email) { |
227 | public function raLogoutEx(string $email) { |
167 | $loginfo = ''; |
228 | $loginfo = ''; |
168 | 229 | ||
169 | $acs = $this->getAuthContentStore(); |
230 | $acs = $this->getAuthContentStore(); |
170 | if (is_null($acs)) return; |
231 | if (is_null($acs)) return; |
171 | $res = $acs->raLogoutEx($email, $loginfo); |
232 | $acs->raLogoutEx($email, $loginfo); |
172 | 233 | ||
173 | OIDplus::logger()->log("[OK]RA($email)!", "RA '$email' logged out ($loginfo)"); |
234 | OIDplus::logger()->log("[OK]RA($email)!", "RA '$email' logged out ($loginfo)"); |
174 | 235 | ||
175 | if (($this->raNumLoggedIn() == 0) && (!$this->isAdminLoggedIn())) { |
236 | if (($this->raNumLoggedIn() == 0) && (!$this->isAdminLoggedIn())) { |
176 | // Nobody logged in anymore. Destroy session cookie to make GDPR people happy |
237 | // Nobody logged in anymore. Destroy session cookie to make GDPR people happy |
177 | $acs->destroySession(); |
238 | $acs->destroySession(); |
178 | } else { |
239 | } else { |
179 | // Get a new token for the remaining users |
240 | // Get a new token for the remaining users |
180 | $acs->activate(); |
241 | $acs->activate(); |
181 | } |
242 | } |
182 | - | ||
183 | return $res; |
- | |
184 | } |
243 | } |
185 | 244 | ||
186 | // Admin authentication functions |
245 | // Admin authentication functions |
187 | 246 | ||
- | 247 | /** |
|
- | 248 | * @return void |
|
- | 249 | * @throws OIDplusException |
|
- | 250 | */ |
|
188 | public function adminLogin() { |
251 | public function adminLogin() { |
189 | $acs = $this->getAuthContentStore(); |
252 | $acs = $this->getAuthContentStore(); |
190 | if (is_null($acs)) return; |
253 | if (is_null($acs)) return; |
191 | return $acs->adminLogin(); |
254 | $acs->adminLogin(); |
192 | } |
255 | } |
193 | 256 | ||
- | 257 | /** |
|
- | 258 | * @return void |
|
- | 259 | * @throws OIDplusException |
|
- | 260 | */ |
|
194 | public function adminLogout() { |
261 | public function adminLogout() { |
195 | $acs = $this->getAuthContentStore(); |
262 | $acs = $this->getAuthContentStore(); |
196 | if (is_null($acs)) return; |
263 | if (is_null($acs)) return; |
197 | return $acs->adminLogout(); |
264 | $acs->adminLogout(); |
198 | } |
265 | } |
199 | 266 | ||
- | 267 | /** |
|
- | 268 | * @param string $password |
|
- | 269 | * @return bool |
|
- | 270 | * @throws OIDplusException |
|
- | 271 | */ |
|
200 | public function adminCheckPassword($password) { |
272 | public function adminCheckPassword(string $password): bool { |
201 | $cfgData = OIDplus::baseConfig()->getValue('ADMIN_PASSWORD', ''); |
273 | $cfgData = OIDplus::baseConfig()->getValue('ADMIN_PASSWORD', ''); |
202 | if (empty($cfgData)) { |
274 | if (empty($cfgData)) { |
203 | throw new OIDplusException(_L('No admin password set in %1','userdata/baseconfig/config.inc.php')); |
275 | throw new OIDplusException(_L('No admin password set in %1','userdata/baseconfig/config.inc.php')); |
204 | } |
276 | } |
205 | 277 | ||
Line 225... | Line 297... | ||
225 | } |
297 | } |
226 | 298 | ||
227 | return false; |
299 | return false; |
228 | } |
300 | } |
229 | 301 | ||
- | 302 | /** |
|
- | 303 | * @return bool |
|
- | 304 | * @throws OIDplusException |
|
- | 305 | */ |
|
230 | public function isAdminLoggedIn() { |
306 | public function isAdminLoggedIn(): bool { |
231 | if ($this->forceAllLoggedOut()) { |
307 | if ($this->forceAllLoggedOut()) { |
232 | return false; |
308 | return false; |
233 | } else { |
309 | } else { |
234 | $acs = $this->getAuthContentStore(); |
310 | $acs = $this->getAuthContentStore(); |
235 | if (is_null($acs)) return false; |
311 | if (is_null($acs)) return false; |
236 | return $acs->isAdminLoggedIn(); |
312 | return $acs->isAdminLoggedIn(); |
237 | } |
313 | } |
238 | } |
314 | } |
239 | 315 | ||
- | 316 | /** |
|
240 | // "High level" function including logging and checking for valid JWT alternations |
317 | * "High level" function including logging and checking for valid JWT alternations |
- | 318 | * @param bool $remember_me |
|
- | 319 | * @param string $origin |
|
- | 320 | * @return void |
|
- | 321 | * @throws OIDplusException |
|
- | 322 | */ |
|
241 | public function adminLoginEx($remember_me, $origin='') { |
323 | public function adminLoginEx(bool $remember_me, string $origin='') { |
242 | $loginfo = ''; |
324 | $loginfo = ''; |
243 | $acs = $this->getAuthContentStore(); |
325 | $acs = $this->getAuthContentStore(); |
244 | if (!is_null($acs)) { |
326 | if (!is_null($acs)) { |
245 | $acs->adminLoginEx($loginfo); |
327 | $acs->adminLoginEx($loginfo); |
246 | $acs->activate(); |
328 | $acs->activate(); |
Line 265... | Line 347... | ||
265 | if ($origin != '') $logmsg .= " via $origin"; |
347 | if ($origin != '') $logmsg .= " via $origin"; |
266 | if ($loginfo != '') $logmsg .= " ($loginfo)"; |
348 | if ($loginfo != '') $logmsg .= " ($loginfo)"; |
267 | OIDplus::logger()->log("[OK]A!", $logmsg); |
349 | OIDplus::logger()->log("[OK]A!", $logmsg); |
268 | } |
350 | } |
269 | 351 | ||
- | 352 | /** |
|
- | 353 | * @return void |
|
- | 354 | * @throws OIDplusException |
|
- | 355 | */ |
|
270 | public function adminLogoutEx() { |
356 | public function adminLogoutEx() { |
271 | $loginfo = ''; |
357 | $loginfo = ''; |
272 | 358 | ||
273 | $acs = $this->getAuthContentStore(); |
359 | $acs = $this->getAuthContentStore(); |
274 | if (is_null($acs)) return; |
360 | if (is_null($acs)) return; |
275 | $res = $acs->adminLogoutEx($loginfo); |
361 | $acs->adminLogoutEx($loginfo); |
276 | 362 | ||
277 | if ($this->raNumLoggedIn() == 0) { |
363 | if ($this->raNumLoggedIn() == 0) { |
278 | // Nobody here anymore. Destroy the cookie to make GDPR people happy |
364 | // Nobody here anymore. Destroy the cookie to make GDPR people happy |
279 | $acs->destroySession(); |
365 | $acs->destroySession(); |
280 | } else { |
366 | } else { |
281 | // Get a new token for the remaining users |
367 | // Get a new token for the remaining users |
282 | $acs->activate(); |
368 | $acs->activate(); |
283 | } |
369 | } |
284 | 370 | ||
285 | OIDplus::logger()->log("[OK]A!", "Admin logged out ($loginfo)"); |
371 | OIDplus::logger()->log("[OK]A!", "Admin logged out ($loginfo)"); |
286 | return $res; |
- | |
287 | } |
372 | } |
288 | 373 | ||
289 | // Authentication keys for validating arguments (e.g. sent by mail) |
374 | // Authentication keys for validating arguments (e.g. sent by mail) |
290 | 375 | ||
- | 376 | /** |
|
- | 377 | * @param string $data |
|
- | 378 | * @return string |
|
- | 379 | * @throws OIDplusException |
|
- | 380 | */ |
|
291 | public static function makeAuthKey($data) { |
381 | public static function makeAuthKey(string $data): string { |
292 | return sha3_512_hmac($data, 'authkey:'.OIDplus::baseConfig()->getValue('SERVER_SECRET'), false); |
382 | return sha3_512_hmac($data, 'authkey:'.OIDplus::baseConfig()->getValue('SERVER_SECRET'), false); |
293 | } |
383 | } |
294 | 384 | ||
- | 385 | /** |
|
- | 386 | * @param string $data |
|
- | 387 | * @param string $auth_key |
|
- | 388 | * @return bool |
|
- | 389 | * @throws OIDplusException |
|
- | 390 | */ |
|
295 | public static function validateAuthKey($data, $auth_key) { |
391 | public static function validateAuthKey(string $data, string $auth_key): bool { |
296 | return hash_equals(self::makeAuthKey($data), $auth_key); |
392 | return hash_equals(self::makeAuthKey($data), $auth_key); |
297 | } |
393 | } |
298 | 394 | ||
299 | // "Veto" functions to force logout state |
395 | // "Veto" functions to force logout state |
300 | 396 | ||
- | 397 | /** |
|
- | 398 | * @return bool |
|
- | 399 | */ |
|
301 | protected function forceAllLoggedOut() { |
400 | protected function forceAllLoggedOut(): bool { |
302 | if (isset($_SERVER['SCRIPT_FILENAME']) && (basename($_SERVER['SCRIPT_FILENAME']) == 'sitemap.php')) { |
401 | if (isset($_SERVER['SCRIPT_FILENAME']) && (basename($_SERVER['SCRIPT_FILENAME']) == 'sitemap.php')) { |
303 | // The sitemap may not contain any confidential information, |
402 | // The sitemap may not contain any confidential information, |
304 | // even if the user is logged in, because the admin could |
403 | // even if the user is logged in, because the admin could |
305 | // accidentally copy-paste the sitemap to a |
404 | // accidentally copy-paste the sitemap to a |
306 | // search engine control panel while they are logged in |
405 | // search engine control panel while they are logged in |
Line 312... | Line 411... | ||
312 | 411 | ||
313 | // CSRF functions |
412 | // CSRF functions |
314 | 413 | ||
315 | private $enable_csrf = true; |
414 | private $enable_csrf = true; |
316 | 415 | ||
- | 416 | /** |
|
- | 417 | * @return void |
|
- | 418 | */ |
|
317 | public function enableCSRF() { |
419 | public function enableCSRF() { |
318 | $this->enable_csrf = true; |
420 | $this->enable_csrf = true; |
319 | } |
421 | } |
320 | 422 | ||
- | 423 | /** |
|
- | 424 | * @return void |
|
- | 425 | */ |
|
321 | public function disableCSRF() { |
426 | public function disableCSRF() { |
322 | $this->enable_csrf = false; |
427 | $this->enable_csrf = false; |
323 | } |
428 | } |
324 | 429 | ||
- | 430 | /** |
|
- | 431 | * @return string |
|
- | 432 | * @throws \Random\RandomException |
|
- | 433 | */ |
|
325 | public function genCSRFToken() { |
434 | public function genCSRFToken(): string { |
326 | return random_bytes_ex(64, false, false); |
435 | return random_bytes_ex(64, false, false); |
327 | } |
436 | } |
328 | 437 | ||
- | 438 | /** |
|
- | 439 | * @return void |
|
- | 440 | * @throws OIDplusException |
|
- | 441 | */ |
|
329 | public function checkCSRF() { |
442 | public function checkCSRF() { |
330 | if (!$this->enable_csrf) return; |
443 | if (!$this->enable_csrf) return; |
331 | 444 | ||
332 | $request_token = isset($_REQUEST['csrf_token']) ? $_REQUEST['csrf_token'] : ''; |
445 | $request_token = isset($_REQUEST['csrf_token']) ? $_REQUEST['csrf_token'] : ''; |
333 | $cookie_token = isset($_COOKIE['csrf_token']) ? $_COOKIE['csrf_token'] : ''; |
446 | $cookie_token = isset($_COOKIE['csrf_token']) ? $_COOKIE['csrf_token'] : ''; |
Line 344... | Line 457... | ||
344 | } |
457 | } |
345 | } |
458 | } |
346 | 459 | ||
347 | // Generate RA passwords |
460 | // Generate RA passwords |
348 | 461 | ||
- | 462 | /** |
|
- | 463 | * @param string $password |
|
- | 464 | * @return OIDplusRAAuthInfo |
|
- | 465 | * @throws OIDplusException |
|
- | 466 | */ |
|
349 | public static function raGeneratePassword($password): OIDplusRAAuthInfo { |
467 | public static function raGeneratePassword(string $password): OIDplusRAAuthInfo { |
350 | $plugin = OIDplus::getDefaultRaAuthPlugin(true); |
468 | $plugin = OIDplus::getDefaultRaAuthPlugin(true); |
351 | return $plugin->generate(self::raPepperProcessing($password)); |
469 | return $plugin->generate(self::raPepperProcessing($password)); |
352 | } |
470 | } |
353 | 471 | ||
354 | // Generate admin password |
472 | // Generate admin password |