Subversion Repositories oidplus

Rev

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