Subversion Repositories oidplus

Rev

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