Subversion Repositories oidplus

Rev

Rev 1131 | Rev 1166 | 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 string $actionID
30
         * @param array $params
1143 daniel-mar 31
         * @return array
1116 daniel-mar 32
         * @throws OIDplusException
33
         */
34
        public function action(string $actionID, array $params): array {
635 daniel-mar 35
                // === RA LOGIN/LOGOUT ===
36
 
37
                if ($actionID == 'ra_login') {
702 daniel-mar 38
                        OIDplus::getActiveCaptchaPlugin()->captchaVerify($params, 'captcha');
635 daniel-mar 39
 
40
                        _CheckParamExists($params, 'email');
41
                        _CheckParamExists($params, 'password');
42
 
43
                        $email = $params['email'];
44
                        $ra = new OIDplusRA($email);
45
 
46
                        if (empty($email)) {
47
                                throw new OIDplusException(_L('Please enter a valid email address'));
48
                        }
49
 
50
                        if ($ra->checkPassword($params['password'])) {
51
                                $remember_me = isset($params['remember_me']) && ($params['remember_me']);
52
                                OIDplus::authUtils()->raLoginEx($email, $remember_me, 'Regular login');
53
 
1108 daniel-mar 54
                                $authInfo = OIDplus::authUtils()->raGeneratePassword($params['password']);
635 daniel-mar 55
 
1108 daniel-mar 56
                                // Rehash, so that we always have the latest default auth plugin and params
57
                                // Note that we do it every time (unlike PHPs recommended password_needs_rehash),
58
                                // because we are not sure which auth plugin created the hash (there might be multiple
59
                                // auth plugins that can verify this hash). So we just rehash on every login!
60
                                $new_authkey = $authInfo->getAuthKey();
61
 
62
                                OIDplus::db()->query("UPDATE ###ra set last_login = ".OIDplus::db()->sqlDate().", authkey = ? where email = ?", array($new_authkey, $email));
63
 
635 daniel-mar 64
                                return array("status" => 0);
65
                        } else {
66
                                if (OIDplus::config()->getValue('log_failed_ra_logins', false)) {
67
                                        if ($ra->existing()) {
68
                                                OIDplus::logger()->log("[WARN]A!", "Failed login to RA account '$email' (wrong password)");
69
                                        } else {
70
                                                OIDplus::logger()->log("[WARN]A!", "Failed login to RA account '$email' (RA not existing)");
71
                                        }
72
                                }
73
                                throw new OIDplusException(_L('Wrong password or user not registered'));
74
                        }
75
 
76
                } else if ($actionID == 'ra_logout') {
77
 
78
                        _CheckParamExists($params, 'email');
79
 
80
                        $email = $params['email'];
81
 
82
                        OIDplus::authUtils()->raLogoutEx($email);
83
 
84
                        return array("status" => 0);
85
                }
86
 
87
                // === ADMIN LOGIN/LOGOUT ===
88
 
89
                else if ($actionID == 'admin_login') {
702 daniel-mar 90
                        OIDplus::getActiveCaptchaPlugin()->captchaVerify($params, 'captcha');
635 daniel-mar 91
 
92
                        _CheckParamExists($params, 'password');
93
                        if (OIDplus::authUtils()->adminCheckPassword($params['password'])) {
94
                                $remember_me = isset($params['remember_me']) && ($params['remember_me']);
95
                                OIDplus::authUtils()->adminLoginEx($remember_me, 'Regular login');
96
 
97
                                // TODO: Write a "last login" entry in config table?
98
 
99
                                return array("status" => 0);
100
                        } else {
101
                                if (OIDplus::config()->getValue('log_failed_admin_logins', false)) {
102
                                        OIDplus::logger()->log("[WARN]A!", "Failed login to admin account");
103
                                }
104
                                throw new OIDplusException(_L('Wrong password'));
105
                        }
106
                }
107
                else if ($actionID == 'admin_logout') {
108
                        OIDplus::authUtils()->adminLogoutEx();
109
 
110
                        return array("status" => 0);
111
                }
112
                else {
1116 daniel-mar 113
                        return parent::action($actionID, $params);
635 daniel-mar 114
                }
115
        }
116
 
1116 daniel-mar 117
        /**
118
         * @param bool $html
119
         * @return void
120
         * @throws OIDplusException
121
         */
122
        public function init(bool $html=true) {
635 daniel-mar 123
                OIDplus::config()->prepareConfigKey('log_failed_ra_logins', 'Log failed RA logins', '0', OIDplusConfig::PROTECTION_EDITABLE, function($value) {
124
                        if (!is_numeric($value) || (($value != 0) && (($value != 1)))) {
125
                                throw new OIDplusException(_L('Valid values: 0 (off) or 1 (on).'));
126
                        }
127
                });
128
                OIDplus::config()->prepareConfigKey('log_failed_admin_logins', 'Log failed Admin logins', '0', OIDplusConfig::PROTECTION_EDITABLE, function($value) {
129
                        if (!is_numeric($value) || (($value != 0) && (($value != 1)))) {
130
                                throw new OIDplusException(_L('Valid values: 0 (off) or 1 (on).'));
131
                        }
132
                });
133
        }
134
 
1116 daniel-mar 135
        /**
136
         * @param string $id
137
         * @param array $out
138
         * @param bool $handled
139
         * @return void
140
         * @throws OIDplusException
141
         */
142
        public function gui(string $id, array &$out, bool &$handled) {
635 daniel-mar 143
                $ary = explode('$', $id);
144
                $desired_ra = '';
145
                if (isset($ary[1])) {
146
                        $id = $ary[0];
147
                        $tab = $ary[1];
148
                        if (isset($ary[2])) {
149
                                $desired_ra = $ary[2];
150
                        }
151
                } else {
152
                        $tab = 'ra';
153
                }
154
                if ($id === 'oidplus:login') {
155
                        $handled = true;
156
                        $out['title'] = _L('Login');
801 daniel-mar 157
                        $out['icon']  = OIDplus::webpath(__DIR__,OIDplus::PATH_RELATIVE).'img/login_icon.png';
635 daniel-mar 158
 
1116 daniel-mar 159
                        $out['text']  = '<noscript>';
635 daniel-mar 160
                        $out['text'] .= '<p>'._L('You need to enable JavaScript to use the login area.').'</p>';
161
                        $out['text'] .= '</noscript>';
162
 
163
                        $out['text'] .= '<div id="loginArea" style="visibility: hidden"><div id="loginTab" class="container" style="width:100%;">';
702 daniel-mar 164
 
165
                        $out['text'] .= OIDplus::getActiveCaptchaPlugin()->captchaGenerate(_L('Before logging in, please solve the following CAPTCHA'));
635 daniel-mar 166
                        $out['text'] .= '<br>';
167
 
168
                        // ---------------- Tab control
169
                        $out['text'] .= OIDplus::gui()->tabBarStart();
170
                        $out['text'] .= OIDplus::gui()->tabBarElement('ra',    _L('Login as RA'),            $tab === 'ra');
171
                        $out['text'] .= OIDplus::gui()->tabBarElement('admin', _L('Login as administrator'), $tab === 'admin');
172
                        $out['text'] .= OIDplus::gui()->tabBarEnd();
173
                        $out['text'] .= OIDplus::gui()->tabContentStart();
174
                        // ---------------- "RA" tab
175
                        $tabcont = '<h2>'._L('Login as RA').'</h2>';
176
                        $login_list = OIDplus::authUtils()->loggedInRaList();
177
                        if (count($login_list) > 0) {
178
                                foreach ($login_list as $x) {
179
                                        $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>';
180
                                }
181
                                $tabcont .= '<p>'._L('If you have more accounts, you can log in with another account here.').'</p>';
182
                        } else {
183
                                $tabcont .= '<p>'._L('Enter your email address and your password to log in as Registration Authority.').'</p>';
184
                        }
185
                        $tabcont .= '<form action="javascript:void(0);" onsubmit="return OIDplusPagePublicLogin.raLoginOnSubmit(this);">';
186
                        $tabcont .= '<div><label class="padding_label">'._L('E-Mail').':</label><input type="text" name="email" value="'.htmlentities($desired_ra).'" id="raLoginEMail"></div>';
187
                        $tabcont .= '<div><label class="padding_label">'._L('Password').':</label><input type="password" name="password" value="" id="raLoginPassword"></div>';
188
                        if (OIDplus::baseConfig()->getValue('JWT_ALLOW_LOGIN_USER', true)) {
189
                                if ((OIDplus::authUtils()->getAuthMethod() === OIDplusAuthContentStoreJWT::class)) {
190
                                        if (OIDplus::authUtils()->getExtendedAttribute('oidplus_generator',-1) === OIDplusAuthContentStoreJWT::JWT_GENERATOR_LOGIN) {
191
                                                $att = 'disabled checked';
192
                                        } else {
193
                                                $att = 'disabled';
194
                                        }
195
                                } else if ((OIDplus::authUtils()->getAuthMethod() === OIDplusAuthContentStoreSession::class)) {
196
                                        $att = 'disabled';
197
                                } else {
198
                                        $att = '';
199
                                }
200
                                $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>';
201
                        }
202
                        $tabcont .= '<br><input type="submit" value="'._L('Login').'"><br><br>';
203
                        $tabcont .= '</form>';
204
                        $tabcont .= '<p><a '.OIDplus::gui()->link('oidplus:forgot_password').'>'._L('Forgot password?').'</a><br>';
205
 
206
                        $invitePlugin = OIDplus::getPluginByOid('1.3.6.1.4.1.37476.2.5.2.4.2.92'); // OIDplusPageRaInvite
207
                        if (!is_null($invitePlugin) && OIDplus::config()->getValue('ra_invitation_enabled')) {
208
                                $tabcont .= '<abbr title="'._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.').'">'._L('How to register?').'</abbr></p>';
209
                        } else {
210
                                $tabcont .= '<abbr title="'._L('Since invitations are disabled at this OIDplus system, the system administrator needs to create your account manually in the administrator control panel.').'">'._L('How to register?').'</abbr></p>';
211
                        }
212
 
213
                        if ($tab === 'ra') {
214
                                $alt_logins_html = array();
1005 daniel-mar 215
                                foreach (OIDplus::getAllPlugins() as $plugin) {
1131 daniel-mar 216
                                        if ($plugin instanceof INTF_OID_1_3_6_1_4_1_37476_2_5_2_3_5) {
217
                                                $logins = $plugin->alternativeLoginMethods();
635 daniel-mar 218
                                                foreach ($logins as $data) {
219
                                                        if (isset($data[2]) && !empty($data[2])) {
220
                                                                $img = '<img src="'.$data[2].'" alt="'.htmlentities($data[1]).'"> ';
221
                                                        } else {
222
                                                                $img = '';
223
                                                        }
224
                                                        $alt_logins_html[] = $img.'<a '.OIDplus::gui()->link($data[0]).'>'.htmlentities($data[1]).'</a>';
225
                                                }
226
                                        }
227
                                }
228
                                if (count($alt_logins_html) > 0) {
229
                                        $tabcont .= '<p>'._L('Alternative login methods').':<br>';
230
                                        foreach ($alt_logins_html as $alt_login) {
231
                                                $tabcont .= $alt_login.'<br>';
232
                                        }
233
                                        $tabcont .= '</p>';
234
                                }
235
                        }
236
 
237
                        $out['text'] .= OIDplus::gui()->tabContentPage('ra', $tabcont, $tab === 'ra');
238
                        // ---------------- "Administrator" tab
239
                        $tabcont = '<h2>'._L('Login as administrator').'</h2>';
240
                        if (OIDplus::authUtils()->isAdminLoggedIn()) {
241
                                $tabcont .= '<p>'._L('You are logged in as administrator.').'</p>';
242
                                $tabcont .= '<a href="#" onclick="return OIDplusPagePublicLogin.adminLogout();">'._L('Logout').'</a>';
243
                        } else {
244
                                $tabcont .= '<form action="javascript:void(0);" onsubmit="return OIDplusPagePublicLogin.adminLoginOnSubmit(this);">';
245
                                $tabcont .= '<div><label class="padding_label">'._L('Password').':</label><input type="password" name="password" value="" id="adminLoginPassword"></div>';
246
                                if (OIDplus::baseConfig()->getValue('JWT_ALLOW_LOGIN_ADMIN', true)) {
247
                                        if ((OIDplus::authUtils()->getAuthMethod() === OIDplusAuthContentStoreJWT::class)) {
248
                                                if (OIDplus::authUtils()->getExtendedAttribute('oidplus_generator',-1) === OIDplusAuthContentStoreJWT::JWT_GENERATOR_LOGIN) {
249
                                                        $att = 'disabled checked';
250
                                                } else {
251
                                                        $att = 'disabled';
252
                                                }
253
                                        } else if ((OIDplus::authUtils()->getAuthMethod() === OIDplusAuthContentStoreSession::class)) {
254
                                                $att = 'disabled';
255
                                        } else {
256
                                                $att = '';
257
                                        }
258
                                        $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>';
259
                                }
260
                                $tabcont .= '<br><input type="submit" value="'._L('Login').'"><br><br>';
261
                                $tabcont .= '</form>';
262
                                $tabcont .= '<p><a '.OIDplus::gui()->link('oidplus:forgot_password_admin').'>'._L('Forgot password?').'</a><br>';
263
                        }
264
                        $out['text'] .= OIDplus::gui()->tabContentPage('admin', $tabcont, $tab === 'admin');
265
                        $out['text'] .= OIDplus::gui()->tabContentEnd();
266
                        // ---------------- Tab control END
267
 
268
                        $out['text'] .= '</div><br>';
269
 
270
                        $mins = ceil(OIDplus::baseConfig()->getValue('SESSION_LIFETIME', 30*60)/60);
271
                        $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);
272
                        $privacy_document_file = 'OIDplus/privacy_documentation.html';
273
                        $resourcePlugin = OIDplus::getPluginByOid('1.3.6.1.4.1.37476.2.5.2.4.1.500'); // OIDplusPagePublicResources
274
                        if (!is_null($resourcePlugin) && file_exists(OIDplus::localpath().'res/'.$privacy_document_file)) {
275
                                $out['text'] .= ' <a '.OIDplus::gui()->link('oidplus:resources$'.$privacy_document_file.'#cookies').'>'._L('More information about the cookies used').'</a>';
276
                        }
277
                        $out['text'] .= '</font></p></div>';
278
 
279
                        $out['text'] .= '<script>$("#loginArea")[0].style.visibility = "visible";</script>';
280
                }
281
        }
282
 
1116 daniel-mar 283
        /**
284
         * @param array $out
285
         * @return void
286
         */
287
        public function publicSitemap(array &$out) {
635 daniel-mar 288
                $out[] = 'oidplus:login';
289
        }
290
 
1116 daniel-mar 291
        /**
292
         * @param array $json
293
         * @param string|null $ra_email
294
         * @param bool $nonjs
295
         * @param string $req_goto
296
         * @return bool
297
         * @throws OIDplusConfigInitializationException
298
         * @throws OIDplusException
299
         */
300
        public function tree(array &$json, string $ra_email=null, bool $nonjs=false, string $req_goto=''): bool {
635 daniel-mar 301
                $loginChildren = array();
302
 
303
                if (OIDplus::authUtils()->isAdminLoggedIn()) {
304
                        $ra_roots = array();
305
 
306
                        foreach (OIDplus::getPagePlugins() as $plugin) {
307
                                if (is_subclass_of($plugin, OIDplusPagePluginAdmin::class)) {
308
                                        $plugin->tree($ra_roots);
309
                                }
310
                        }
311
 
312
                        $ra_roots[] = array(
313
                                'id'       => 'oidplus:logout$admin',
801 daniel-mar 314
                                'icon'     => OIDplus::webpath(__DIR__,OIDplus::PATH_RELATIVE).'img/logout_icon16.png',
635 daniel-mar 315
                                'conditionalselect' => 'OIDplusPagePublicLogin.adminLogout(); false;',
316
                                'text'     => _L('Log out')
317
                        );
318
                        $loginChildren[] = array(
319
                                'id'       => 'oidplus:dummy$'.md5((string)rand()),
320
                                'text'     => _L("Logged in as <b>admin</b>"),
801 daniel-mar 321
                                'icon'     => OIDplus::webpath(__DIR__,OIDplus::PATH_RELATIVE).'img/admin_icon16.png',
635 daniel-mar 322
                                'conditionalselect' => 'false', // dummy node that can't be selected
323
                                'state'    => array("opened" => true),
324
                                'children' => $ra_roots
325
                        );
326
                }
327
 
328
                foreach (OIDplus::authUtils()->loggedInRaList() as $ra) {
329
                        $ra_email = $ra->raEmail();
330
                        $ra_roots = array();
331
 
332
                        foreach (OIDplus::getPagePlugins() as $plugin) {
333
                                if (is_subclass_of($plugin, OIDplusPagePluginRa::class)) {
334
                                        $plugin->tree($ra_roots, $ra_email);
335
                                }
336
                        }
337
 
338
                        $ra_roots[] = array(
339
                                'id'       => 'oidplus:logout$'.$ra_email,
340
                                'conditionalselect' => 'OIDplusPagePublicLogin.raLogout('.js_escape($ra_email).'); false;',
801 daniel-mar 341
                                'icon'     => OIDplus::webpath(__DIR__,OIDplus::PATH_RELATIVE).'img/logout_icon16.png',
635 daniel-mar 342
                                'text'     => _L('Log out')
343
                        );
344
                        foreach (OIDplusObject::getRaRoots($ra_email) as $loc_root) {
345
                                $ico = $loc_root->getIcon();
346
                                $ra_roots[] = array(
347
                                        'id' => 'oidplus:raroot$'.$loc_root->nodeId(),
348
                                        'text' => _L('Jump to RA root %1',$loc_root->objectTypeTitleShort().' '.$loc_root->crudShowId(OIDplusObject::parse($loc_root::root()))),
349
                                        'conditionalselect' => 'openOidInPanel('.js_escape($loc_root->nodeId()).', true); false;',
801 daniel-mar 350
                                        'icon' => !is_null($ico) ? $ico : OIDplus::webpath(__DIR__,OIDplus::PATH_RELATIVE).'img/link_icon16.png'
635 daniel-mar 351
                                );
352
                        }
353
                        $ra_email_or_name = (new OIDplusRA($ra_email))->raName();
354
                        if ($ra_email_or_name == '') {
355
                                $ra_email_html = htmlentities($ra_email);
356
                                $ra_email_or_name = '<b>'.$ra_email_html.'</b>';
357
                        } else {
358
                                $ra_email_html = htmlentities($ra_email);
359
                                $ra_email_or_name_html = htmlentities($ra_email_or_name);
360
                                $ra_email_or_name = "<b>$ra_email_or_name_html</b> ($ra_email_html)";
361
                        }
362
                        $loginChildren[] = array(
363
                                'id'       => 'oidplus:dummy$'.md5((string)rand()),
364
                                'text'     => _L('Logged in as %1',$ra_email_or_name),
801 daniel-mar 365
                                'icon'     => OIDplus::webpath(__DIR__,OIDplus::PATH_RELATIVE).'img/ra_icon16.png',
635 daniel-mar 366
                                'conditionalselect' => 'false', // dummy node that can't be selected
367
                                'state'    => array("opened" => true),
368
                                'children' => $ra_roots
369
                        );
370
                }
371
 
372
                $json[] = array(
373
                        'id'       => 'oidplus:login',
801 daniel-mar 374
                        'icon'     => OIDplus::webpath(__DIR__,OIDplus::PATH_RELATIVE).'img/login_icon16.png',
635 daniel-mar 375
                        'text'     => _L('Login'),
376
                        'state'    => array("opened" => count($loginChildren)>0),
377
                        'children' => $loginChildren
378
                );
379
 
380
                return true;
381
        }
382
 
1116 daniel-mar 383
        /**
384
         * @param string $request
385
         * @return array|false
386
         */
387
        public function tree_search(string $request) {
635 daniel-mar 388
                return false;
389
        }
390
}