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
 
1131 daniel-mar 26
class OIDplusPagePublicLoginLdap extends OIDplusPagePluginPublic
27
        implements INTF_OID_1_3_6_1_4_1_37476_2_5_2_3_5, /* alternativeLoginMethods */
28
                   INTF_OID_1_3_6_1_4_1_37476_2_5_2_3_8  /* getNotifications */
29
{
635 daniel-mar 30
 
1116 daniel-mar 31
        /**
1130 daniel-mar 32
         * @param OIDplusRA $ra
33
         * @param array $ldap_userinfo
1116 daniel-mar 34
         * @return void
35
         * @throws OIDplusException
36
         */
1130 daniel-mar 37
        private function registerRA(OIDplusRA $ra, array $ldap_userinfo) {
635 daniel-mar 38
                $email = $ra->raEmail();
39
 
40
                $ra->register_ra(null); // create a user account without password
41
 
42
                /*
43
                OIDplus DB Field          ActiveDirectory field
44
                ------------------------------------------------
45
                ra_name                   cn
46
                personal_name             displayname (or: givenname + " " + sn)
47
                organization              company
48
                office                    physicaldeliveryofficename or department
49
                street                    streetaddress
50
                zip_town                  postalcode + " " + l
51
                country                   co (human-readable) or c (ISO country code)
52
                phone                     telephonenumber or homephone
53
                mobile                    mobile
54
                fax                       facsimiletelephonenumber
55
                (none)                    wwwhomepage
56
                */
57
 
58
                $opuserdata = array();
1050 daniel-mar 59
                $opuserdata['ra_name'] = \VtsLDAPUtils::getString($ldap_userinfo,'cn');
60
                if (!empty(\VtsLDAPUtils::getString($ldap_userinfo,'displayname'))) {
61
                        $opuserdata['personal_name'] = \VtsLDAPUtils::getString($ldap_userinfo,'displayname');
635 daniel-mar 62
                } else {
1050 daniel-mar 63
                        $opuserdata['personal_name'] = trim(\VtsLDAPUtils::getString($ldap_userinfo,'givenname').' '.\VtsLDAPUtils::getString($ldap_userinfo,'sn'));
635 daniel-mar 64
                }
1050 daniel-mar 65
                $opuserdata['organization'] = \VtsLDAPUtils::getString($ldap_userinfo,'company');
66
                if (!empty(\VtsLDAPUtils::getString($ldap_userinfo,'physicaldeliveryofficename'))) {
67
                        $opuserdata['office'] = \VtsLDAPUtils::getString($ldap_userinfo,'physicaldeliveryofficename');
635 daniel-mar 68
                } else {
1050 daniel-mar 69
                        $opuserdata['office'] = \VtsLDAPUtils::getString($ldap_userinfo,'department');
635 daniel-mar 70
                }
1050 daniel-mar 71
                $opuserdata['street'] = \VtsLDAPUtils::getString($ldap_userinfo,'streetaddress');
72
                $opuserdata['zip_town'] = trim(\VtsLDAPUtils::getString($ldap_userinfo,'postalcode').' '.\VtsLDAPUtils::getString($ldap_userinfo,'l'));
73
                $opuserdata['country'] = \VtsLDAPUtils::getString($ldap_userinfo,'co'); // ISO country code: \VtsLDAPUtils::getString($ldap_userinfo,'c')
74
                $opuserdata['phone'] = \VtsLDAPUtils::getString($ldap_userinfo,'telephonenumber'); // homephone for private phone number
75
                $opuserdata['mobile'] = \VtsLDAPUtils::getString($ldap_userinfo,'mobile');
76
                $opuserdata['fax'] = \VtsLDAPUtils::getString($ldap_userinfo,'facsimiletelephonenumber');
635 daniel-mar 77
 
78
                foreach ($opuserdata as $dbfield => $val) {
79
                        if (!empty($val)) {
80
                                OIDplus::db()->query("update ###ra set ".$dbfield." = ? where email = ?", array($val, $email));
81
                        }
82
                }
83
        }
84
 
1116 daniel-mar 85
        /**
1130 daniel-mar 86
         * @param bool $remember_me
87
         * @param string $email
88
         * @param array $ldap_userinfo
1116 daniel-mar 89
         * @return void
90
         * @throws OIDplusException
91
         */
1130 daniel-mar 92
        private function doLoginRA(bool $remember_me, string $email, array $ldap_userinfo) {
635 daniel-mar 93
                $ra = new OIDplusRA($email);
94
                if (!$ra->existing()) {
95
                        $this->registerRA($ra, $ldap_userinfo);
1267 daniel-mar 96
                        OIDplus::logger()->log("V2:[INFO]RA(%1)", "RA '%1' was created because of successful LDAP login", $email);
635 daniel-mar 97
                }
98
 
99
                OIDplus::authUtils()->raLoginEx($email, $remember_me, 'LDAP');
100
 
101
                OIDplus::db()->query("UPDATE ###ra set last_login = ".OIDplus::db()->sqlDate()." where email = ?", array($email));
102
        }
103
 
1116 daniel-mar 104
        /**
1130 daniel-mar 105
         * @param string $upn
1116 daniel-mar 106
         * @return int
107
         * @throws OIDplusException
108
         */
1130 daniel-mar 109
        private function getDomainNumber(string $upn): int {
635 daniel-mar 110
                $numDomains = OIDplus::baseConfig()->getValue('LDAP_NUM_DOMAINS', 1);
111
                for ($i=1; $i<=$numDomains; $i++) {
112
                        $cfgSuffix = $i == 1 ? '' : "__$i";
113
                        $upnSuffix = OIDplus::baseConfig()->getValue('LDAP_UPN_SUFFIX'.$cfgSuffix, '');
114
                        if (str_ends_with($upn, $upnSuffix)) return $i;
115
                }
116
                return -1;
117
        }
118
 
1116 daniel-mar 119
        /**
120
         * @param array $params
1143 daniel-mar 121
         * @return array
1116 daniel-mar 122
         * @throws OIDplusConfigInitializationException
123
         * @throws OIDplusException
124
         */
1293 daniel-mar 125
        private function action_Login(array $params): array {
126
                if (!OIDplus::baseConfig()->getValue('LDAP_ENABLED', false)) {
127
                        throw new OIDplusException(_L('LDAP authentication is disabled on this system.'));
128
                }
635 daniel-mar 129
 
1293 daniel-mar 130
                if (!function_exists('ldap_connect')) throw new OIDplusConfigInitializationException(_L('PHP extension "%1" not installed','LDAP'));
635 daniel-mar 131
 
1293 daniel-mar 132
                OIDplus::getActiveCaptchaPlugin()->captchaVerify($params, 'captcha');
635 daniel-mar 133
 
1293 daniel-mar 134
                _CheckParamExists($params, 'email');
135
                _CheckParamExists($params, 'password');
635 daniel-mar 136
 
1293 daniel-mar 137
                $upn = $params['email'];
138
                $password = $params['password'];
635 daniel-mar 139
 
1293 daniel-mar 140
                $domainNumber = $this->getDomainNumber($upn);
141
                if ($domainNumber <= 0) {
142
                        throw new OIDplusException(_L('The server is not configured to handle this domain (the part behind the at-sign)'));
143
                }
144
                $cfgSuffix = $domainNumber == 1 ? '' : "__$domainNumber";
635 daniel-mar 145
 
1293 daniel-mar 146
                if (empty($upn)) {
147
                        throw new OIDplusException(_L('Please enter a valid username'));
148
                }
635 daniel-mar 149
 
1293 daniel-mar 150
                $ldap = new \VtsLDAPUtils();
635 daniel-mar 151
 
1293 daniel-mar 152
                try {
635 daniel-mar 153
 
1293 daniel-mar 154
                        $cfg_ldap_server      = OIDplus::baseConfig()->getValue('LDAP_SERVER'.$cfgSuffix);
155
                        $cfg_ldap_port        = OIDplus::baseConfig()->getValue('LDAP_PORT'.$cfgSuffix, 389);
156
                        $cfg_ldap_base_dn     = OIDplus::baseConfig()->getValue('LDAP_BASE_DN'.$cfgSuffix);
635 daniel-mar 157
 
1293 daniel-mar 158
                        // Note: Will throw an Exception if connect fails
159
                        $ldap->connect($cfg_ldap_server, $cfg_ldap_port);
635 daniel-mar 160
 
1293 daniel-mar 161
                        if (!$ldap->login($upn, $password)) {
162
                                if (OIDplus::config()->getValue('log_failed_ra_logins', false)) {
163
                                        OIDplus::logger()->log("V2:[WARN]A", "Failed login to RA account '%1' using LDAP", $upn);
635 daniel-mar 164
                                }
1293 daniel-mar 165
                                throw new OIDplusException(_L('Wrong password or user not registered'));
166
                        }
635 daniel-mar 167
 
1293 daniel-mar 168
                        $ldap_userinfo = $ldap->getUserInfo($upn, $cfg_ldap_base_dn);
635 daniel-mar 169
 
1293 daniel-mar 170
                        if (!$ldap_userinfo) {
171
                                throw new OIDplusException(_L('The LDAP login was successful, but the own user %1 cannot be found. Please check the base configuration setting %2 and %3', $upn, "LDAP_BASE_DN$cfgSuffix", "LDAP_UPN_SUFFIX$cfgSuffix"));
172
                        }
635 daniel-mar 173
 
1293 daniel-mar 174
                        $foundSomething = false;
635 daniel-mar 175
 
1293 daniel-mar 176
                        // ---
635 daniel-mar 177
 
1293 daniel-mar 178
                        $cfgAdminGroup = OIDplus::baseConfig()->getValue('LDAP_ADMIN_GROUP'.$cfgSuffix,'');
179
                        if (!empty($cfgAdminGroup)) {
180
                                $isAdmin = $ldap->isMemberOfRec($ldap_userinfo, $cfgAdminGroup);
181
                        } else {
182
                                $isAdmin = false;
183
                        }
184
                        if ($isAdmin) {
185
                                $foundSomething = true;
186
                                $remember_me = isset($params['remember_me']) && ($params['remember_me']);
187
                                OIDplus::authUtils()->adminLoginEx($remember_me, 'LDAP login');
188
                        }
189
 
190
                        // ---
191
 
192
                        $cfgRaGroup = OIDplus::baseConfig()->getValue('LDAP_RA_GROUP'.$cfgSuffix,'');
193
                        if (!empty($cfgRaGroup)) {
194
                                $isRA = $ldap->isMemberOfRec($ldap_userinfo, $cfgRaGroup);
195
                        } else {
196
                                $isRA = true;
197
                        }
198
                        if ($isRA) {
199
                                if (OIDplus::baseConfig()->getValue('LDAP_AUTHENTICATE_UPN'.$cfgSuffix,true)) {
200
                                        $mail = \VtsLDAPUtils::getString($ldap_userinfo, 'userprincipalname');
635 daniel-mar 201
                                        $foundSomething = true;
202
                                        $remember_me = isset($params['remember_me']) && ($params['remember_me']);
1293 daniel-mar 203
                                        $this->doLoginRA($remember_me, $mail, $ldap_userinfo);
635 daniel-mar 204
                                }
1293 daniel-mar 205
                                if (OIDplus::baseConfig()->getValue('LDAP_AUTHENTICATE_EMAIL'.$cfgSuffix,false)) {
206
                                        $mails = \VtsLDAPUtils::getArray($ldap_userinfo, 'mail');
207
                                        foreach ($mails as $mail) {
635 daniel-mar 208
                                                $foundSomething = true;
209
                                                $remember_me = isset($params['remember_me']) && ($params['remember_me']);
210
                                                $this->doLoginRA($remember_me, $mail, $ldap_userinfo);
211
                                        }
212
                                }
213
                        }
214
 
1293 daniel-mar 215
                } finally {
216
                        $ldap->disconnect();
217
                        $ldap = null;
218
                }
635 daniel-mar 219
 
1293 daniel-mar 220
                if (!$foundSomething) {
221
                        throw new OIDplusException(_L("Error: These credentials cannot be used with OIDplus. Please check the base configuration."));
222
                }
223
 
224
                return array("status" => 0);
225
        }
226
 
227
        /**
228
         * @param string $actionID
229
         * @param array $params
230
         * @return array
231
         * @throws OIDplusConfigInitializationException
232
         * @throws OIDplusException
233
         */
234
        public function action(string $actionID, array $params): array {
235
                if ($actionID == 'ra_login_ldap') {
236
                        return $this->action_Login($params);
635 daniel-mar 237
                } else {
1116 daniel-mar 238
                        return parent::action($actionID, $params);
635 daniel-mar 239
                }
240
        }
241
 
1116 daniel-mar 242
        /**
243
         * @param bool $html
244
         * @return void
245
         */
246
        public function init(bool $html=true) {
635 daniel-mar 247
                // Nothing
248
        }
249
 
1116 daniel-mar 250
        /**
251
         * @param string $id
252
         * @param array $out
253
         * @param bool $handled
254
         * @return void
255
         * @throws OIDplusException
256
         */
257
        public function gui(string $id, array &$out, bool &$handled) {
635 daniel-mar 258
                if ($id === 'oidplus:login_ldap') {
259
                        $handled = true;
260
                        $out['title'] = _L('Login using LDAP / ActiveDirectory');
801 daniel-mar 261
                        $out['icon']  = OIDplus::webpath(__DIR__,OIDplus::PATH_RELATIVE).'img/main_icon.png';
635 daniel-mar 262
 
263
                        if (!OIDplus::baseConfig()->getValue('LDAP_ENABLED', false)) {
1206 daniel-mar 264
                                throw new OIDplusException(_L('LDAP authentication is disabled on this system.'), $out['title']);
635 daniel-mar 265
                        }
266
 
267
                        if (!function_exists('ldap_connect')) {
1206 daniel-mar 268
                                throw new OIDplusException(_L('PHP extension "%1" not installed','LDAP'), $out['title']);
635 daniel-mar 269
                        }
270
 
1116 daniel-mar 271
                        $out['text']  = '<noscript>';
1288 daniel-mar 272
                        $out['text'] .= '<p><font color="red">'._L('You need to enable JavaScript to use the login area.').'</font></p>';
635 daniel-mar 273
                        $out['text'] .= '</noscript>';
274
 
275
                        $out['text'] .= '<div id="loginLdapArea" style="visibility: hidden">';
702 daniel-mar 276
 
277
                        $out['text'] .= OIDplus::getActiveCaptchaPlugin()->captchaGenerate(_L('Before logging in, please solve the following CAPTCHA'));
635 daniel-mar 278
                        $out['text'] .= '<br>';
279
 
280
                        $out['text'] .= '<p><a '.OIDplus::gui()->link('oidplus:login').'><img src="img/arrow_back.png" width="16" alt="'._L('Go back').'"> '._L('Regular login method').'</a></p>';
281
 
282
                        $out['text'] .= '<h2>'._L('Login as RA').'</h2>';
283
 
284
                        $login_list = OIDplus::authUtils()->loggedInRaList();
285
                        if (count($login_list) > 0) {
286
                                foreach ($login_list as $x) {
287
                                        $out['text'] .= '<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>';
288
                                }
289
                                $out['text'] .= '<p>'._L('If you have more accounts, you can log in with another account here.').'</p>';
290
                        } else {
291
                                $out['text'] .= '<p>'._L('Enter your domain username and your password to log in as Registration Authority.').'</p>';
292
                        }
293
                        $out['text'] .= '<form onsubmit="return OIDplusPagePublicLoginLDAP.raLoginLdapOnSubmit(this);">';
294
                        $out['text'] .= '<div><label class="padding_label">'._L('Username').':</label><input type="text" name="username" value="" id="raLoginLdapUsername">';
295
                        $out['text'] .= '&nbsp;&nbsp;';
296
                        $out['text'] .= '<select id="ldapUpnSuffix" name="upnSuffix">';
297
 
298
                        $numDomains = OIDplus::baseConfig()->getValue('LDAP_NUM_DOMAINS', 1);
299
                        for ($i=1; $i<=$numDomains; $i++) {
300
                                $cfgSuffix = $i == 1 ? '' : "__$i";
301
                                $upnSuffix = OIDplus::baseConfig()->getValue('LDAP_UPN_SUFFIX'.$cfgSuffix, '');
302
                                if ($upnSuffix == '') throw new OIDplusException(_L('Invalid base configuration setting: %1 is missing or empty', 'LDAP_UPN_SUFFIX'.$cfgSuffix));
303
                                $out['text'] .= '<option value="'.htmlentities($upnSuffix).'">'.htmlentities($upnSuffix).'</option>';
304
                        }
305
 
306
                        $out['text'] .= '</select>';
307
                        $out['text'] .= '</div>';
308
                        $out['text'] .= '<div><label class="padding_label">'._L('Password').':</label><input type="password" name="password" value="" id="raLoginLdapPassword"></div>';
309
                        if (OIDplus::baseConfig()->getValue('JWT_ALLOW_LOGIN_USER', true)) {
310
                                if ((OIDplus::authUtils()->getAuthMethod() === OIDplusAuthContentStoreJWT::class)) {
311
                                        if (OIDplus::authUtils()->getExtendedAttribute('oidplus_generator',-1) === OIDplusAuthContentStoreJWT::JWT_GENERATOR_LOGIN) {
312
                                                $att = 'disabled checked';
313
                                        } else {
314
                                                $att = 'disabled';
315
                                        }
316
                                } else if ((OIDplus::authUtils()->getAuthMethod() === OIDplusAuthContentStoreSession::class)) {
317
                                        $att = 'disabled';
318
                                } else {
319
                                        $att = '';
320
                                }
321
                                $out['text'] .= '<div><input '.$att.' type="checkbox" value="1" id="remember_me_ldap" name="remember_me_ldap"> <label for="remember_me_ldap">'._L('Remember me').'</label></div>';
322
                        }
323
                        $out['text'] .= '<br><input type="submit" value="'._L('Login').'"><br><br>';
324
                        $out['text'] .= '</form>';
325
 
326
                        $invitePlugin = OIDplus::getPluginByOid('1.3.6.1.4.1.37476.2.5.2.4.2.92'); // OIDplusPageRaInvite
1166 daniel-mar 327
                        $out['text'] .= '<p><b>'._L('How to register?').'</b> '._L('You don\'t need to register. Just enter your Windows/Company credentials.').'</p>';
635 daniel-mar 328
 
329
                        $mins = ceil(OIDplus::baseConfig()->getValue('SESSION_LIFETIME', 30*60)/60);
330
                        $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);
331
                        $privacy_document_file = 'OIDplus/privacy_documentation.html';
332
                        $resourcePlugin = OIDplus::getPluginByOid('1.3.6.1.4.1.37476.2.5.2.4.1.500'); // OIDplusPagePublicResources
333
                        if (!is_null($resourcePlugin) && file_exists(OIDplus::localpath().'res/'.$privacy_document_file)) {
334
                                $out['text'] .= ' <a '.OIDplus::gui()->link('oidplus:resources$'.$privacy_document_file.'#cookies').'>'._L('More information about the cookies used').'</a>';
335
                        }
336
                        $out['text'] .= '</font></p></div>';
337
 
338
                        $out['text'] .= '<script>$("#loginLdapArea")[0].style.visibility = "visible";</script>';
339
                }
340
        }
341
 
1116 daniel-mar 342
        /**
343
         * @param array $out
344
         * @return void
345
         */
346
        public function publicSitemap(array &$out) {
635 daniel-mar 347
                $out[] = 'oidplus:login_ldap';
348
        }
349
 
1116 daniel-mar 350
        /**
351
         * @param array $json
352
         * @param string|null $ra_email
353
         * @param bool $nonjs
354
         * @param string $req_goto
355
         * @return bool
356
         */
357
        public function tree(array &$json, string $ra_email=null, bool $nonjs=false, string $req_goto=''): bool {
635 daniel-mar 358
                return true;
359
        }
360
 
1116 daniel-mar 361
        /**
362
         * @param string $request
363
         * @return array|false
364
         */
365
        public function tree_search(string $request) {
635 daniel-mar 366
                return false;
367
        }
368
 
1116 daniel-mar 369
        /**
1131 daniel-mar 370
         * Implements interface INTF_OID_1_3_6_1_4_1_37476_2_5_2_3_5
1116 daniel-mar 371
         * @return array
372
         * @throws OIDplusException
373
         */
1130 daniel-mar 374
        public function alternativeLoginMethods(): array {
635 daniel-mar 375
                $logins = array();
376
                if (OIDplus::baseConfig()->getValue('LDAP_ENABLED', false)) {
377
                        $logins[] = array(
378
                                'oidplus:login_ldap',
379
                                _L('Login using LDAP / ActiveDirectory'),
801 daniel-mar 380
                                OIDplus::webpath(__DIR__,OIDplus::PATH_RELATIVE).'img/main_icon16.png'
635 daniel-mar 381
                        );
382
                }
383
                return $logins;
384
        }
1000 daniel-mar 385
 
1116 daniel-mar 386
        /**
1131 daniel-mar 387
         * Implements interface INTF_OID_1_3_6_1_4_1_37476_2_5_2_3_8
1130 daniel-mar 388
         * @param string|null $user
1116 daniel-mar 389
         * @return array
390
         * @throws OIDplusException
391
         */
1130 daniel-mar 392
        public function getNotifications(string $user=null): array {
1000 daniel-mar 393
                $notifications = array();
394
                if ((!$user || ($user == 'admin')) && OIDplus::authUtils()->isAdminLoggedIn()) {
395
                        if (OIDplus::baseConfig()->getValue('LDAP_ENABLED', false)) {
396
                                if (!function_exists('ldap_connect')) {
397
                                        $title = _L('LDAP Login');
1189 daniel-mar 398
                                        $notifications[] = new OIDplusNotification('ERR', _L('OIDplus plugin "%1" is enabled, but the required PHP extension "%2" is not installed.', htmlentities($title), 'php_ldap'));
1000 daniel-mar 399
                                }
400
                        }
401
                }
402
                return $notifications;
403
        }
404
 
635 daniel-mar 405
}