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
 
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 string $email
87
         * @param array $ldap_userinfo
1116 daniel-mar 88
         * @return void
89
         * @throws OIDplusException
90
         */
1305 daniel-mar 91
        private function doLoginRA(string $email, array $ldap_userinfo) {
635 daniel-mar 92
                $ra = new OIDplusRA($email);
93
                if (!$ra->existing()) {
94
                        $this->registerRA($ra, $ldap_userinfo);
1267 daniel-mar 95
                        OIDplus::logger()->log("V2:[INFO]RA(%1)", "RA '%1' was created because of successful LDAP login", $email);
635 daniel-mar 96
                }
97
 
1305 daniel-mar 98
                OIDplus::authUtils()->raLoginEx($email, 'LDAP');
635 daniel-mar 99
 
100
                OIDplus::db()->query("UPDATE ###ra set last_login = ".OIDplus::db()->sqlDate()." where email = ?", array($email));
101
        }
102
 
1116 daniel-mar 103
        /**
1130 daniel-mar 104
         * @param string $upn
1116 daniel-mar 105
         * @return int
106
         * @throws OIDplusException
107
         */
1130 daniel-mar 108
        private function getDomainNumber(string $upn): int {
635 daniel-mar 109
                $numDomains = OIDplus::baseConfig()->getValue('LDAP_NUM_DOMAINS', 1);
110
                for ($i=1; $i<=$numDomains; $i++) {
111
                        $cfgSuffix = $i == 1 ? '' : "__$i";
112
                        $upnSuffix = OIDplus::baseConfig()->getValue('LDAP_UPN_SUFFIX'.$cfgSuffix, '');
113
                        if (str_ends_with($upn, $upnSuffix)) return $i;
114
                }
115
                return -1;
116
        }
117
 
1116 daniel-mar 118
        /**
119
         * @param array $params
1143 daniel-mar 120
         * @return array
1116 daniel-mar 121
         * @throws OIDplusConfigInitializationException
122
         * @throws OIDplusException
123
         */
1293 daniel-mar 124
        private function action_Login(array $params): array {
125
                if (!OIDplus::baseConfig()->getValue('LDAP_ENABLED', false)) {
126
                        throw new OIDplusException(_L('LDAP authentication is disabled on this system.'));
127
                }
635 daniel-mar 128
 
1293 daniel-mar 129
                if (!function_exists('ldap_connect')) throw new OIDplusConfigInitializationException(_L('PHP extension "%1" not installed','LDAP'));
635 daniel-mar 130
 
1293 daniel-mar 131
                OIDplus::getActiveCaptchaPlugin()->captchaVerify($params, 'captcha');
635 daniel-mar 132
 
1293 daniel-mar 133
                _CheckParamExists($params, 'email');
134
                _CheckParamExists($params, 'password');
635 daniel-mar 135
 
1293 daniel-mar 136
                $upn = $params['email'];
137
                $password = $params['password'];
635 daniel-mar 138
 
1293 daniel-mar 139
                $domainNumber = $this->getDomainNumber($upn);
140
                if ($domainNumber <= 0) {
141
                        throw new OIDplusException(_L('The server is not configured to handle this domain (the part behind the at-sign)'));
142
                }
143
                $cfgSuffix = $domainNumber == 1 ? '' : "__$domainNumber";
635 daniel-mar 144
 
1293 daniel-mar 145
                if (empty($upn)) {
146
                        throw new OIDplusException(_L('Please enter a valid username'));
147
                }
635 daniel-mar 148
 
1293 daniel-mar 149
                $ldap = new \VtsLDAPUtils();
635 daniel-mar 150
 
1293 daniel-mar 151
                try {
635 daniel-mar 152
 
1293 daniel-mar 153
                        $cfg_ldap_server      = OIDplus::baseConfig()->getValue('LDAP_SERVER'.$cfgSuffix);
154
                        $cfg_ldap_port        = OIDplus::baseConfig()->getValue('LDAP_PORT'.$cfgSuffix, 389);
155
                        $cfg_ldap_base_dn     = OIDplus::baseConfig()->getValue('LDAP_BASE_DN'.$cfgSuffix);
635 daniel-mar 156
 
1293 daniel-mar 157
                        // Note: Will throw an Exception if connect fails
158
                        $ldap->connect($cfg_ldap_server, $cfg_ldap_port);
635 daniel-mar 159
 
1293 daniel-mar 160
                        if (!$ldap->login($upn, $password)) {
161
                                if (OIDplus::config()->getValue('log_failed_ra_logins', false)) {
162
                                        OIDplus::logger()->log("V2:[WARN]A", "Failed login to RA account '%1' using LDAP", $upn);
635 daniel-mar 163
                                }
1293 daniel-mar 164
                                throw new OIDplusException(_L('Wrong password or user not registered'));
165
                        }
635 daniel-mar 166
 
1293 daniel-mar 167
                        $ldap_userinfo = $ldap->getUserInfo($upn, $cfg_ldap_base_dn);
635 daniel-mar 168
 
1293 daniel-mar 169
                        if (!$ldap_userinfo) {
170
                                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"));
171
                        }
635 daniel-mar 172
 
1293 daniel-mar 173
                        $foundSomething = false;
635 daniel-mar 174
 
1293 daniel-mar 175
                        // ---
635 daniel-mar 176
 
1293 daniel-mar 177
                        $cfgAdminGroup = OIDplus::baseConfig()->getValue('LDAP_ADMIN_GROUP'.$cfgSuffix,'');
178
                        if (!empty($cfgAdminGroup)) {
179
                                $isAdmin = $ldap->isMemberOfRec($ldap_userinfo, $cfgAdminGroup);
180
                        } else {
181
                                $isAdmin = false;
182
                        }
183
                        if ($isAdmin) {
184
                                $foundSomething = true;
1305 daniel-mar 185
                                OIDplus::authUtils()->adminLoginEx('LDAP login');
1293 daniel-mar 186
                        }
187
 
188
                        // ---
189
 
190
                        $cfgRaGroup = OIDplus::baseConfig()->getValue('LDAP_RA_GROUP'.$cfgSuffix,'');
191
                        if (!empty($cfgRaGroup)) {
192
                                $isRA = $ldap->isMemberOfRec($ldap_userinfo, $cfgRaGroup);
193
                        } else {
194
                                $isRA = true;
195
                        }
196
                        if ($isRA) {
197
                                if (OIDplus::baseConfig()->getValue('LDAP_AUTHENTICATE_UPN'.$cfgSuffix,true)) {
198
                                        $mail = \VtsLDAPUtils::getString($ldap_userinfo, 'userprincipalname');
635 daniel-mar 199
                                        $foundSomething = true;
1305 daniel-mar 200
                                        $this->doLoginRA($mail, $ldap_userinfo);
635 daniel-mar 201
                                }
1293 daniel-mar 202
                                if (OIDplus::baseConfig()->getValue('LDAP_AUTHENTICATE_EMAIL'.$cfgSuffix,false)) {
203
                                        $mails = \VtsLDAPUtils::getArray($ldap_userinfo, 'mail');
204
                                        foreach ($mails as $mail) {
635 daniel-mar 205
                                                $foundSomething = true;
1305 daniel-mar 206
                                                $this->doLoginRA($mail, $ldap_userinfo);
635 daniel-mar 207
                                        }
208
                                }
209
                        }
210
 
1293 daniel-mar 211
                } finally {
212
                        $ldap->disconnect();
213
                        $ldap = null;
214
                }
635 daniel-mar 215
 
1293 daniel-mar 216
                if (!$foundSomething) {
217
                        throw new OIDplusException(_L("Error: These credentials cannot be used with OIDplus. Please check the base configuration."));
218
                }
219
 
220
                return array("status" => 0);
221
        }
222
 
223
        /**
224
         * @param string $actionID
225
         * @param array $params
226
         * @return array
227
         * @throws OIDplusConfigInitializationException
228
         * @throws OIDplusException
229
         */
230
        public function action(string $actionID, array $params): array {
231
                if ($actionID == 'ra_login_ldap') {
232
                        return $this->action_Login($params);
635 daniel-mar 233
                } else {
1116 daniel-mar 234
                        return parent::action($actionID, $params);
635 daniel-mar 235
                }
236
        }
237
 
1116 daniel-mar 238
        /**
239
         * @param bool $html
240
         * @return void
241
         */
242
        public function init(bool $html=true) {
635 daniel-mar 243
                // Nothing
244
        }
245
 
1116 daniel-mar 246
        /**
247
         * @param string $id
248
         * @param array $out
249
         * @param bool $handled
250
         * @return void
251
         * @throws OIDplusException
252
         */
253
        public function gui(string $id, array &$out, bool &$handled) {
635 daniel-mar 254
                if ($id === 'oidplus:login_ldap') {
255
                        $handled = true;
256
                        $out['title'] = _L('Login using LDAP / ActiveDirectory');
801 daniel-mar 257
                        $out['icon']  = OIDplus::webpath(__DIR__,OIDplus::PATH_RELATIVE).'img/main_icon.png';
635 daniel-mar 258
 
259
                        if (!OIDplus::baseConfig()->getValue('LDAP_ENABLED', false)) {
1206 daniel-mar 260
                                throw new OIDplusException(_L('LDAP authentication is disabled on this system.'), $out['title']);
635 daniel-mar 261
                        }
262
 
263
                        if (!function_exists('ldap_connect')) {
1206 daniel-mar 264
                                throw new OIDplusException(_L('PHP extension "%1" not installed','LDAP'), $out['title']);
635 daniel-mar 265
                        }
266
 
1116 daniel-mar 267
                        $out['text']  = '<noscript>';
1359 daniel-mar 268
                        $out['text'] .= '<p><font color="red">'._L('You need to enable JavaScript to use this feature.').'</font></p>';
635 daniel-mar 269
                        $out['text'] .= '</noscript>';
270
 
271
                        $out['text'] .= '<div id="loginLdapArea" style="visibility: hidden">';
702 daniel-mar 272
 
273
                        $out['text'] .= OIDplus::getActiveCaptchaPlugin()->captchaGenerate(_L('Before logging in, please solve the following CAPTCHA'));
635 daniel-mar 274
                        $out['text'] .= '<br>';
275
 
276
                        $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>';
277
 
278
                        $out['text'] .= '<h2>'._L('Login as RA').'</h2>';
279
 
280
                        $login_list = OIDplus::authUtils()->loggedInRaList();
281
                        if (count($login_list) > 0) {
282
                                foreach ($login_list as $x) {
283
                                        $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>';
284
                                }
285
                                $out['text'] .= '<p>'._L('If you have more accounts, you can log in with another account here.').'</p>';
286
                        } else {
287
                                $out['text'] .= '<p>'._L('Enter your domain username and your password to log in as Registration Authority.').'</p>';
288
                        }
289
                        $out['text'] .= '<form onsubmit="return OIDplusPagePublicLoginLDAP.raLoginLdapOnSubmit(this);">';
290
                        $out['text'] .= '<div><label class="padding_label">'._L('Username').':</label><input type="text" name="username" value="" id="raLoginLdapUsername">';
291
                        $out['text'] .= '&nbsp;&nbsp;';
292
                        $out['text'] .= '<select id="ldapUpnSuffix" name="upnSuffix">';
293
 
294
                        $numDomains = OIDplus::baseConfig()->getValue('LDAP_NUM_DOMAINS', 1);
295
                        for ($i=1; $i<=$numDomains; $i++) {
296
                                $cfgSuffix = $i == 1 ? '' : "__$i";
297
                                $upnSuffix = OIDplus::baseConfig()->getValue('LDAP_UPN_SUFFIX'.$cfgSuffix, '');
298
                                if ($upnSuffix == '') throw new OIDplusException(_L('Invalid base configuration setting: %1 is missing or empty', 'LDAP_UPN_SUFFIX'.$cfgSuffix));
299
                                $out['text'] .= '<option value="'.htmlentities($upnSuffix).'">'.htmlentities($upnSuffix).'</option>';
300
                        }
301
 
302
                        $out['text'] .= '</select>';
303
                        $out['text'] .= '</div>';
304
                        $out['text'] .= '<div><label class="padding_label">'._L('Password').':</label><input type="password" name="password" value="" id="raLoginLdapPassword"></div>';
305
                        $out['text'] .= '<br><input type="submit" value="'._L('Login').'"><br><br>';
306
                        $out['text'] .= '</form>';
307
 
308
                        $invitePlugin = OIDplus::getPluginByOid('1.3.6.1.4.1.37476.2.5.2.4.2.92'); // OIDplusPageRaInvite
1166 daniel-mar 309
                        $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 310
 
1305 daniel-mar 311
                        $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 312
                        $privacy_document_file = 'OIDplus/privacy_documentation.html';
313
                        $resourcePlugin = OIDplus::getPluginByOid('1.3.6.1.4.1.37476.2.5.2.4.1.500'); // OIDplusPagePublicResources
314
                        if (!is_null($resourcePlugin) && file_exists(OIDplus::localpath().'res/'.$privacy_document_file)) {
315
                                $out['text'] .= ' <a '.OIDplus::gui()->link('oidplus:resources$'.$privacy_document_file.'#cookies').'>'._L('More information about the cookies used').'</a>';
316
                        }
317
                        $out['text'] .= '</font></p></div>';
318
 
319
                        $out['text'] .= '<script>$("#loginLdapArea")[0].style.visibility = "visible";</script>';
320
                }
321
        }
322
 
1116 daniel-mar 323
        /**
324
         * @param array $out
325
         * @return void
326
         */
327
        public function publicSitemap(array &$out) {
635 daniel-mar 328
                $out[] = 'oidplus:login_ldap';
329
        }
330
 
1116 daniel-mar 331
        /**
332
         * @param array $json
333
         * @param string|null $ra_email
334
         * @param bool $nonjs
335
         * @param string $req_goto
336
         * @return bool
337
         */
338
        public function tree(array &$json, string $ra_email=null, bool $nonjs=false, string $req_goto=''): bool {
635 daniel-mar 339
                return true;
340
        }
341
 
1116 daniel-mar 342
        /**
343
         * @param string $request
344
         * @return array|false
345
         */
346
        public function tree_search(string $request) {
635 daniel-mar 347
                return false;
348
        }
349
 
1116 daniel-mar 350
        /**
1131 daniel-mar 351
         * Implements interface INTF_OID_1_3_6_1_4_1_37476_2_5_2_3_5
1116 daniel-mar 352
         * @return array
353
         * @throws OIDplusException
354
         */
1130 daniel-mar 355
        public function alternativeLoginMethods(): array {
635 daniel-mar 356
                $logins = array();
357
                if (OIDplus::baseConfig()->getValue('LDAP_ENABLED', false)) {
358
                        $logins[] = array(
359
                                'oidplus:login_ldap',
360
                                _L('Login using LDAP / ActiveDirectory'),
801 daniel-mar 361
                                OIDplus::webpath(__DIR__,OIDplus::PATH_RELATIVE).'img/main_icon16.png'
635 daniel-mar 362
                        );
363
                }
364
                return $logins;
365
        }
1000 daniel-mar 366
 
1116 daniel-mar 367
        /**
1131 daniel-mar 368
         * Implements interface INTF_OID_1_3_6_1_4_1_37476_2_5_2_3_8
1130 daniel-mar 369
         * @param string|null $user
1116 daniel-mar 370
         * @return array
371
         * @throws OIDplusException
372
         */
1130 daniel-mar 373
        public function getNotifications(string $user=null): array {
1000 daniel-mar 374
                $notifications = array();
375
                if ((!$user || ($user == 'admin')) && OIDplus::authUtils()->isAdminLoggedIn()) {
376
                        if (OIDplus::baseConfig()->getValue('LDAP_ENABLED', false)) {
377
                                if (!function_exists('ldap_connect')) {
378
                                        $title = _L('LDAP Login');
1189 daniel-mar 379
                                        $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 380
                                }
381
                        }
382
                }
383
                return $notifications;
384
        }
385
 
635 daniel-mar 386
}