Subversion Repositories oidplus

Rev

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