Subversion Repositories oidplus

Rev

Rev 1288 | Rev 1305 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
635 daniel-mar 1
<?php
2
 
3
/*
4
 * OIDplus 2.0
1086 daniel-mar 5
 * Copyright 2019 - 2023 Daniel Marschall, ViaThinkSoft
635 daniel-mar 6
 *
7
 * Licensed under the Apache License, Version 2.0 (the "License");
8
 * you may not use this file except in compliance with the License.
9
 * You may obtain a copy of the License at
10
 *
11
 *     http://www.apache.org/licenses/LICENSE-2.0
12
 *
13
 * Unless required by applicable law or agreed to in writing, software
14
 * distributed under the License is distributed on an "AS IS" BASIS,
15
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
 * See the License for the specific language governing permissions and
17
 * limitations under the License.
18
 */
19
 
1050 daniel-mar 20
namespace ViaThinkSoft\OIDplus;
635 daniel-mar 21
 
1086 daniel-mar 22
// phpcs:disable PSR1.Files.SideEffects
23
\defined('INSIDE_OIDPLUS') or die;
24
// phpcs:enable PSR1.Files.SideEffects
25
 
635 daniel-mar 26
class OIDplusPagePublicLogin extends OIDplusPagePluginPublic {
27
 
1116 daniel-mar 28
        /**
29
         * @param array $params
1143 daniel-mar 30
         * @return array
1116 daniel-mar 31
         * @throws OIDplusException
32
         */
1293 daniel-mar 33
        private function action_RaLogin(array $params): array {
34
                OIDplus::getActiveCaptchaPlugin()->captchaVerify($params, 'captcha');
635 daniel-mar 35
 
1293 daniel-mar 36
                _CheckParamExists($params, 'email');
37
                _CheckParamExists($params, 'password');
635 daniel-mar 38
 
1293 daniel-mar 39
                $email = $params['email'];
40
                $ra = new OIDplusRA($email);
635 daniel-mar 41
 
1293 daniel-mar 42
                if (empty($email)) {
43
                        throw new OIDplusException(_L('Please enter a valid email address'));
44
                }
635 daniel-mar 45
 
1293 daniel-mar 46
                if ($ra->checkPassword($params['password'])) {
47
                        $remember_me = isset($params['remember_me']) && ($params['remember_me']);
48
                        OIDplus::authUtils()->raLoginEx($email, $remember_me, 'Regular login');
635 daniel-mar 49
 
1293 daniel-mar 50
                        $authInfo = OIDplus::authUtils()->raGeneratePassword($params['password']);
635 daniel-mar 51
 
1293 daniel-mar 52
                        // Rehash, so that we always have the latest default auth plugin and params
53
                        // Note that we do it every time (unlike PHPs recommended password_needs_rehash),
54
                        // because we are not sure which auth plugin created the hash (there might be multiple
55
                        // auth plugins that can verify this hash). So we just rehash on every login!
56
                        $new_authkey = $authInfo->getAuthKey();
635 daniel-mar 57
 
1293 daniel-mar 58
                        OIDplus::db()->query("UPDATE ###ra set last_login = ".OIDplus::db()->sqlDate().", authkey = ? where email = ?", array($new_authkey, $email));
1108 daniel-mar 59
 
1293 daniel-mar 60
                        return array("status" => 0);
61
                } else {
62
                        if (OIDplus::config()->getValue('log_failed_ra_logins', false)) {
63
                                if ($ra->existing()) {
64
                                        OIDplus::logger()->log("V2:[WARN]A", "Failed login to RA account '%1' (wrong password)", $email);
65
                                } else {
66
                                        OIDplus::logger()->log("V2:[WARN]A", "Failed login to RA account '%1' (RA not existing)", $email);
635 daniel-mar 67
                                }
68
                        }
1293 daniel-mar 69
                        throw new OIDplusException(_L('Wrong password or user not registered'));
70
                }
71
        }
635 daniel-mar 72
 
1293 daniel-mar 73
        /**
74
         * @param array $params
75
         * @return array
76
         * @throws OIDplusException
77
         */
78
        private function action_RaLogout(array $params): array {
79
                _CheckParamExists($params, 'email');
635 daniel-mar 80
 
1293 daniel-mar 81
                $email = $params['email'];
635 daniel-mar 82
 
1293 daniel-mar 83
                OIDplus::authUtils()->raLogoutEx($email);
635 daniel-mar 84
 
1293 daniel-mar 85
                return array("status" => 0);
86
        }
635 daniel-mar 87
 
1293 daniel-mar 88
        /**
89
         * @param array $params
90
         * @return array
91
         * @throws OIDplusException
92
         */
93
        private function action_AdminLogin(array $params): array {
94
                OIDplus::getActiveCaptchaPlugin()->captchaVerify($params, 'captcha');
635 daniel-mar 95
 
1293 daniel-mar 96
                _CheckParamExists($params, 'password');
97
                if (OIDplus::authUtils()->adminCheckPassword($params['password'])) {
98
                        $remember_me = isset($params['remember_me']) && ($params['remember_me']);
99
                        OIDplus::authUtils()->adminLoginEx($remember_me, 'Regular login');
635 daniel-mar 100
 
1293 daniel-mar 101
                        // TODO: Write a "last login" entry in config table?
635 daniel-mar 102
 
1293 daniel-mar 103
                        return array("status" => 0);
104
                } else {
105
                        if (OIDplus::config()->getValue('log_failed_admin_logins', false)) {
106
                                OIDplus::logger()->log("V2:[WARN]A", "Failed login to admin account");
635 daniel-mar 107
                        }
1293 daniel-mar 108
                        throw new OIDplusException(_L('Wrong password'));
635 daniel-mar 109
                }
1293 daniel-mar 110
        }
635 daniel-mar 111
 
1293 daniel-mar 112
        /**
113
         * @param array $params
114
         * @return array
115
         * @throws OIDplusException
116
         */
117
        private function action_AdminLogout(array $params): array {
118
                OIDplus::authUtils()->adminLogoutEx();
119
 
120
                return array("status" => 0);
121
        }
122
 
123
        /**
124
         * @param string $actionID
125
         * @param array $params
126
         * @return array
127
         * @throws OIDplusException
128
         */
129
        public function action(string $actionID, array $params): array {
130
                if ($actionID == 'ra_login') {
131
                        return $this->action_RaLogin($params);
132
                } else if ($actionID == 'ra_logout') {
133
                        return $this->action_RaLogout($params);
134
                } else if ($actionID == 'admin_login') {
135
                        return $this->action_AdminLogin($params);
136
                }  else if ($actionID == 'admin_logout') {
137
                        return $this->action_AdminLogout($params);
138
                } else {
1116 daniel-mar 139
                        return parent::action($actionID, $params);
635 daniel-mar 140
                }
141
        }
142
 
1116 daniel-mar 143
        /**
144
         * @param bool $html
145
         * @return void
146
         * @throws OIDplusException
147
         */
148
        public function init(bool $html=true) {
635 daniel-mar 149
                OIDplus::config()->prepareConfigKey('log_failed_ra_logins', 'Log failed RA logins', '0', OIDplusConfig::PROTECTION_EDITABLE, function($value) {
150
                        if (!is_numeric($value) || (($value != 0) && (($value != 1)))) {
151
                                throw new OIDplusException(_L('Valid values: 0 (off) or 1 (on).'));
152
                        }
153
                });
154
                OIDplus::config()->prepareConfigKey('log_failed_admin_logins', 'Log failed Admin logins', '0', OIDplusConfig::PROTECTION_EDITABLE, function($value) {
155
                        if (!is_numeric($value) || (($value != 0) && (($value != 1)))) {
156
                                throw new OIDplusException(_L('Valid values: 0 (off) or 1 (on).'));
157
                        }
158
                });
159
        }
160
 
1116 daniel-mar 161
        /**
162
         * @param string $id
163
         * @param array $out
164
         * @param bool $handled
165
         * @return void
166
         * @throws OIDplusException
167
         */
168
        public function gui(string $id, array &$out, bool &$handled) {
635 daniel-mar 169
                $ary = explode('$', $id);
170
                $desired_ra = '';
171
                if (isset($ary[1])) {
172
                        $id = $ary[0];
173
                        $tab = $ary[1];
174
                        if (isset($ary[2])) {
175
                                $desired_ra = $ary[2];
176
                        }
177
                } else {
178
                        $tab = 'ra';
179
                }
180
                if ($id === 'oidplus:login') {
181
                        $handled = true;
182
                        $out['title'] = _L('Login');
801 daniel-mar 183
                        $out['icon']  = OIDplus::webpath(__DIR__,OIDplus::PATH_RELATIVE).'img/login_icon.png';
635 daniel-mar 184
 
1116 daniel-mar 185
                        $out['text']  = '<noscript>';
1288 daniel-mar 186
                        $out['text'] .= '<p><font color="red">'._L('You need to enable JavaScript to use the login area.').'</font></p>';
635 daniel-mar 187
                        $out['text'] .= '</noscript>';
188
 
189
                        $out['text'] .= '<div id="loginArea" style="visibility: hidden"><div id="loginTab" class="container" style="width:100%;">';
702 daniel-mar 190
 
191
                        $out['text'] .= OIDplus::getActiveCaptchaPlugin()->captchaGenerate(_L('Before logging in, please solve the following CAPTCHA'));
635 daniel-mar 192
                        $out['text'] .= '<br>';
193
 
194
                        // ---------------- Tab control
195
                        $out['text'] .= OIDplus::gui()->tabBarStart();
196
                        $out['text'] .= OIDplus::gui()->tabBarElement('ra',    _L('Login as RA'),            $tab === 'ra');
197
                        $out['text'] .= OIDplus::gui()->tabBarElement('admin', _L('Login as administrator'), $tab === 'admin');
198
                        $out['text'] .= OIDplus::gui()->tabBarEnd();
199
                        $out['text'] .= OIDplus::gui()->tabContentStart();
200
                        // ---------------- "RA" tab
201
                        $tabcont = '<h2>'._L('Login as RA').'</h2>';
202
                        $login_list = OIDplus::authUtils()->loggedInRaList();
203
                        if (count($login_list) > 0) {
204
                                foreach ($login_list as $x) {
205
                                        $tabcont .= '<p>'._L('You are logged in as %1','<b>'.$x->raEmail().'</b>').' (<a href="#" onclick="return OIDplusPagePublicLogin.raLogout('.js_escape($x->raEmail()).');">'._L('Logout').'</a>)</p>';
206
                                }
207
                                $tabcont .= '<p>'._L('If you have more accounts, you can log in with another account here.').'</p>';
208
                        } else {
209
                                $tabcont .= '<p>'._L('Enter your email address and your password to log in as Registration Authority.').'</p>';
210
                        }
211
                        $tabcont .= '<form action="javascript:void(0);" onsubmit="return OIDplusPagePublicLogin.raLoginOnSubmit(this);">';
212
                        $tabcont .= '<div><label class="padding_label">'._L('E-Mail').':</label><input type="text" name="email" value="'.htmlentities($desired_ra).'" id="raLoginEMail"></div>';
213
                        $tabcont .= '<div><label class="padding_label">'._L('Password').':</label><input type="password" name="password" value="" id="raLoginPassword"></div>';
214
                        if (OIDplus::baseConfig()->getValue('JWT_ALLOW_LOGIN_USER', true)) {
215
                                if ((OIDplus::authUtils()->getAuthMethod() === OIDplusAuthContentStoreJWT::class)) {
216
                                        if (OIDplus::authUtils()->getExtendedAttribute('oidplus_generator',-1) === OIDplusAuthContentStoreJWT::JWT_GENERATOR_LOGIN) {
217
                                                $att = 'disabled checked';
218
                                        } else {
219
                                                $att = 'disabled';
220
                                        }
221
                                } else if ((OIDplus::authUtils()->getAuthMethod() === OIDplusAuthContentStoreSession::class)) {
222
                                        $att = 'disabled';
223
                                } else {
224
                                        $att = '';
225
                                }
226
                                $tabcont .= '<div><input '.$att.' type="checkbox" value="1" id="remember_me_ra" name="remember_me_ra"> <label for="remember_me_ra">'._L('Remember me').'</label></div>';
227
                        }
228
                        $tabcont .= '<br><input type="submit" value="'._L('Login').'"><br><br>';
229
                        $tabcont .= '</form>';
230
                        $tabcont .= '<p><a '.OIDplus::gui()->link('oidplus:forgot_password').'>'._L('Forgot password?').'</a><br>';
231
 
232
                        $invitePlugin = OIDplus::getPluginByOid('1.3.6.1.4.1.37476.2.5.2.4.2.92'); // OIDplusPageRaInvite
233
                        if (!is_null($invitePlugin) && OIDplus::config()->getValue('ra_invitation_enabled')) {
1166 daniel-mar 234
                                $tabcont .= '<p><b>'._L('How to register?').'</b> '._L('To receive login data, the superior RA needs to send you an invitation. After creating or updating your OID, the system will ask them if they want to send you an invitation. If they accept, you will receive an email with an activation link. Alternatively, the system admin can create your account manually in the administrator control panel.').'</p>';
635 daniel-mar 235
                        } else {
1166 daniel-mar 236
                                $tabcont .= '<p><b>'._L('How to register?').'</b> '._L('Since invitations are disabled at this OIDplus system, the system administrator needs to create your account manually in the administrator control panel.').'</p>';
635 daniel-mar 237
                        }
238
 
239
                        if ($tab === 'ra') {
240
                                $alt_logins_html = array();
1005 daniel-mar 241
                                foreach (OIDplus::getAllPlugins() as $plugin) {
1131 daniel-mar 242
                                        if ($plugin instanceof INTF_OID_1_3_6_1_4_1_37476_2_5_2_3_5) {
243
                                                $logins = $plugin->alternativeLoginMethods();
635 daniel-mar 244
                                                foreach ($logins as $data) {
245
                                                        if (isset($data[2]) && !empty($data[2])) {
246
                                                                $img = '<img src="'.$data[2].'" alt="'.htmlentities($data[1]).'"> ';
247
                                                        } else {
248
                                                                $img = '';
249
                                                        }
250
                                                        $alt_logins_html[] = $img.'<a '.OIDplus::gui()->link($data[0]).'>'.htmlentities($data[1]).'</a>';
251
                                                }
252
                                        }
253
                                }
254
                                if (count($alt_logins_html) > 0) {
255
                                        $tabcont .= '<p>'._L('Alternative login methods').':<br>';
256
                                        foreach ($alt_logins_html as $alt_login) {
257
                                                $tabcont .= $alt_login.'<br>';
258
                                        }
259
                                        $tabcont .= '</p>';
260
                                }
261
                        }
262
 
263
                        $out['text'] .= OIDplus::gui()->tabContentPage('ra', $tabcont, $tab === 'ra');
264
                        // ---------------- "Administrator" tab
265
                        $tabcont = '<h2>'._L('Login as administrator').'</h2>';
266
                        if (OIDplus::authUtils()->isAdminLoggedIn()) {
267
                                $tabcont .= '<p>'._L('You are logged in as administrator.').'</p>';
268
                                $tabcont .= '<a href="#" onclick="return OIDplusPagePublicLogin.adminLogout();">'._L('Logout').'</a>';
269
                        } else {
270
                                $tabcont .= '<form action="javascript:void(0);" onsubmit="return OIDplusPagePublicLogin.adminLoginOnSubmit(this);">';
271
                                $tabcont .= '<div><label class="padding_label">'._L('Password').':</label><input type="password" name="password" value="" id="adminLoginPassword"></div>';
272
                                if (OIDplus::baseConfig()->getValue('JWT_ALLOW_LOGIN_ADMIN', true)) {
273
                                        if ((OIDplus::authUtils()->getAuthMethod() === OIDplusAuthContentStoreJWT::class)) {
274
                                                if (OIDplus::authUtils()->getExtendedAttribute('oidplus_generator',-1) === OIDplusAuthContentStoreJWT::JWT_GENERATOR_LOGIN) {
275
                                                        $att = 'disabled checked';
276
                                                } else {
277
                                                        $att = 'disabled';
278
                                                }
279
                                        } else if ((OIDplus::authUtils()->getAuthMethod() === OIDplusAuthContentStoreSession::class)) {
280
                                                $att = 'disabled';
281
                                        } else {
282
                                                $att = '';
283
                                        }
284
                                        $tabcont .= '<div><input '.$att.' type="checkbox" value="1" id="remember_me_admin" name="remember_me_admin"> <label for="remember_me_admin">'._L('Remember me').'</label></div>';
285
                                }
286
                                $tabcont .= '<br><input type="submit" value="'._L('Login').'"><br><br>';
287
                                $tabcont .= '</form>';
288
                                $tabcont .= '<p><a '.OIDplus::gui()->link('oidplus:forgot_password_admin').'>'._L('Forgot password?').'</a><br>';
289
                        }
290
                        $out['text'] .= OIDplus::gui()->tabContentPage('admin', $tabcont, $tab === 'admin');
291
                        $out['text'] .= OIDplus::gui()->tabContentEnd();
292
                        // ---------------- Tab control END
293
 
294
                        $out['text'] .= '</div><br>';
295
 
296
                        $mins = ceil(OIDplus::baseConfig()->getValue('SESSION_LIFETIME', 30*60)/60);
297
                        $out['text'] .= '<p><font size="-1">'._L('<i>Privacy information</i>: By using the login functionality, you are accepting that a "session cookie" is temporarily stored in your browser. The session cookie is a small text file that is sent to this website every time you visit it, to identify you as an already logged in user. It does not track any of your online activities outside OIDplus. The cookie will be destroyed when you log out or after an inactivity of %1 minutes (except if the "Remember me" option is used).', $mins);
298
                        $privacy_document_file = 'OIDplus/privacy_documentation.html';
299
                        $resourcePlugin = OIDplus::getPluginByOid('1.3.6.1.4.1.37476.2.5.2.4.1.500'); // OIDplusPagePublicResources
300
                        if (!is_null($resourcePlugin) && file_exists(OIDplus::localpath().'res/'.$privacy_document_file)) {
301
                                $out['text'] .= ' <a '.OIDplus::gui()->link('oidplus:resources$'.$privacy_document_file.'#cookies').'>'._L('More information about the cookies used').'</a>';
302
                        }
303
                        $out['text'] .= '</font></p></div>';
304
 
305
                        $out['text'] .= '<script>$("#loginArea")[0].style.visibility = "visible";</script>';
306
                }
307
        }
308
 
1116 daniel-mar 309
        /**
310
         * @param array $out
311
         * @return void
312
         */
313
        public function publicSitemap(array &$out) {
635 daniel-mar 314
                $out[] = 'oidplus:login';
315
        }
316
 
1116 daniel-mar 317
        /**
318
         * @param array $json
319
         * @param string|null $ra_email
320
         * @param bool $nonjs
321
         * @param string $req_goto
322
         * @return bool
323
         * @throws OIDplusConfigInitializationException
324
         * @throws OIDplusException
325
         */
326
        public function tree(array &$json, string $ra_email=null, bool $nonjs=false, string $req_goto=''): bool {
635 daniel-mar 327
                $loginChildren = array();
328
 
329
                if (OIDplus::authUtils()->isAdminLoggedIn()) {
330
                        $ra_roots = array();
331
 
332
                        foreach (OIDplus::getPagePlugins() as $plugin) {
333
                                if (is_subclass_of($plugin, OIDplusPagePluginAdmin::class)) {
334
                                        $plugin->tree($ra_roots);
335
                                }
336
                        }
337
 
338
                        $ra_roots[] = array(
339
                                'id'       => 'oidplus:logout$admin',
801 daniel-mar 340
                                'icon'     => OIDplus::webpath(__DIR__,OIDplus::PATH_RELATIVE).'img/logout_icon16.png',
635 daniel-mar 341
                                'conditionalselect' => 'OIDplusPagePublicLogin.adminLogout(); false;',
342
                                'text'     => _L('Log out')
343
                        );
344
                        $loginChildren[] = array(
345
                                'id'       => 'oidplus:dummy$'.md5((string)rand()),
346
                                'text'     => _L("Logged in as <b>admin</b>"),
801 daniel-mar 347
                                'icon'     => OIDplus::webpath(__DIR__,OIDplus::PATH_RELATIVE).'img/admin_icon16.png',
635 daniel-mar 348
                                'conditionalselect' => 'false', // dummy node that can't be selected
349
                                'state'    => array("opened" => true),
350
                                'children' => $ra_roots
351
                        );
352
                }
353
 
354
                foreach (OIDplus::authUtils()->loggedInRaList() as $ra) {
355
                        $ra_email = $ra->raEmail();
356
                        $ra_roots = array();
357
 
358
                        foreach (OIDplus::getPagePlugins() as $plugin) {
359
                                if (is_subclass_of($plugin, OIDplusPagePluginRa::class)) {
360
                                        $plugin->tree($ra_roots, $ra_email);
361
                                }
362
                        }
363
 
364
                        $ra_roots[] = array(
365
                                'id'       => 'oidplus:logout$'.$ra_email,
366
                                'conditionalselect' => 'OIDplusPagePublicLogin.raLogout('.js_escape($ra_email).'); false;',
801 daniel-mar 367
                                'icon'     => OIDplus::webpath(__DIR__,OIDplus::PATH_RELATIVE).'img/logout_icon16.png',
635 daniel-mar 368
                                'text'     => _L('Log out')
369
                        );
370
                        foreach (OIDplusObject::getRaRoots($ra_email) as $loc_root) {
371
                                $ico = $loc_root->getIcon();
372
                                $ra_roots[] = array(
373
                                        'id' => 'oidplus:raroot$'.$loc_root->nodeId(),
374
                                        'text' => _L('Jump to RA root %1',$loc_root->objectTypeTitleShort().' '.$loc_root->crudShowId(OIDplusObject::parse($loc_root::root()))),
375
                                        'conditionalselect' => 'openOidInPanel('.js_escape($loc_root->nodeId()).', true); false;',
801 daniel-mar 376
                                        'icon' => !is_null($ico) ? $ico : OIDplus::webpath(__DIR__,OIDplus::PATH_RELATIVE).'img/link_icon16.png'
635 daniel-mar 377
                                );
378
                        }
379
                        $ra_email_or_name = (new OIDplusRA($ra_email))->raName();
380
                        if ($ra_email_or_name == '') {
381
                                $ra_email_html = htmlentities($ra_email);
382
                                $ra_email_or_name = '<b>'.$ra_email_html.'</b>';
383
                        } else {
384
                                $ra_email_html = htmlentities($ra_email);
385
                                $ra_email_or_name_html = htmlentities($ra_email_or_name);
386
                                $ra_email_or_name = "<b>$ra_email_or_name_html</b> ($ra_email_html)";
387
                        }
388
                        $loginChildren[] = array(
389
                                'id'       => 'oidplus:dummy$'.md5((string)rand()),
390
                                'text'     => _L('Logged in as %1',$ra_email_or_name),
801 daniel-mar 391
                                'icon'     => OIDplus::webpath(__DIR__,OIDplus::PATH_RELATIVE).'img/ra_icon16.png',
635 daniel-mar 392
                                'conditionalselect' => 'false', // dummy node that can't be selected
393
                                'state'    => array("opened" => true),
394
                                'children' => $ra_roots
395
                        );
396
                }
397
 
398
                $json[] = array(
399
                        'id'       => 'oidplus:login',
801 daniel-mar 400
                        'icon'     => OIDplus::webpath(__DIR__,OIDplus::PATH_RELATIVE).'img/login_icon16.png',
635 daniel-mar 401
                        'text'     => _L('Login'),
402
                        'state'    => array("opened" => count($loginChildren)>0),
403
                        'children' => $loginChildren
404
                );
405
 
406
                return true;
407
        }
408
 
1116 daniel-mar 409
        /**
410
         * @param string $request
411
         * @return array|false
412
         */
413
        public function tree_search(string $request) {
635 daniel-mar 414
                return false;
415
        }
416
}