Subversion Repositories oidplus

Rev

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