Subversion Repositories oidplus

Rev

Rev 496 | Go to most recent revision | Blame | Last modification | View Log | RSS feed

  1. <?php
  2.  
  3. /*
  4.  * OIDplus 2.0
  5.  * Copyright 2019 - 2021 Daniel Marschall, ViaThinkSoft
  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.  
  20. if (!defined('INSIDE_OIDPLUS')) die();
  21.  
  22. class OIDplusPagePublicLoginLdap extends OIDplusPagePluginPublic {
  23.  
  24.         protected function ldapAuthByEMail($email, $password) {
  25.                 $cfg_ldap_server      = OIDplus::baseConfig()->getValue('LDAP_SERVER');
  26.                 $cfg_ldap_port        = OIDplus::baseConfig()->getValue('LDAP_PORT', 389);
  27.                 $cfg_ldap_base_dn     = OIDplus::baseConfig()->getValue('LDAP_BASE_DN');
  28.                 $cfg_ldap_rdn         = OIDplus::baseConfig()->getValue('LDAP_CONTROLUSER_RDN');
  29.                 $cfg_ldap_password    = OIDplus::baseConfig()->getValue('LDAP_CONTROLUSER_PASSWORD');
  30.                 $cfg_ldap_user_filter = OIDplus::baseConfig()->getValue('LDAP_USER_FILTER', '(&(objectClass=user)(cn=*))');
  31.  
  32.                 // Connect to the server
  33.                 if (!empty($cfg_ldap_port)) {
  34.                         if (!($ldapconn = @ldap_connect($cfg_ldap_server, $cfg_ldap_port))) throw new OIDplusException(_L('Cannot connect to LDAP server'));
  35.                 } else {
  36.                         if (!($ldapconn = @ldap_connect($cfg_ldap_server))) throw new OIDplusException(_L('Cannot connect to LDAP server'));
  37.                 }
  38.                 ldap_set_option($ldapconn, LDAP_OPT_PROTOCOL_VERSION, 3);
  39.                 ldap_set_option($ldapconn, LDAP_OPT_REFERRALS, 0);
  40.  
  41.                 // Login in order to search for the user
  42.                 if (!empty($cfg_ldap_rdn)) {
  43.                         if (!empty($cfg_ldap_password)) {
  44.                                 if (!($ldapbind = @ldap_bind($ldapconn, $cfg_ldap_rdn, $cfg_ldap_password))) throw new OIDplusException(_L('System cannot login to LDAP in order to search the user'));
  45.                         } else {
  46.                                 if (!($ldapbind = @ldap_bind($ldapconn, $cfg_ldap_rdn))) throw new OIDplusException(_L('System cannot login to LDAP in order to search the user'));
  47.                         }
  48.                 } else {
  49.                         if (!($ldapbind = @ldap_bind($ldapconn))) throw new OIDplusException(_L('System cannot login to LDAP in order to search the user'));
  50.                 }
  51.  
  52.                 // Search the user using the email address
  53.                 if (!($result = @ldap_search($ldapconn,$cfg_ldap_base_dn, $cfg_ldap_user_filter))) throw new OIDplusException(_L('Error in search query: %1', ldap_error($ldapconn)));
  54.                 $data = ldap_get_entries($ldapconn, $result);
  55.                 $found_username = null;
  56.                 for ($i=0; $i<$data['count']; $i++) {
  57.                         if ((isset($data[$i]['mail'][0])) && ($data[$i]['mail'][0] == $email)) {
  58.                                 $found_username = $data[$i]['userprincipalname'][0];
  59.                                 $ldap_userinfo = array();
  60.                                 foreach ($data[$i] as $x => $y) {
  61.                                         if (is_int($x)) continue;
  62.                                         if (!is_array($y)) continue;
  63.                                         $ldap_userinfo[$x] = $y[0];
  64.                                 }
  65.                         }
  66.                 }
  67.                 if (is_null($found_username)) return false;
  68.  
  69.                 // Login as the new user in order to check the credentials
  70.                 //ldap_unbind($ldapconn); // commented out because ldap_unbind() kills the link descriptor
  71.                 if ($ldapbind = @ldap_bind($ldapconn, $found_username, $password)) {
  72.                         //ldap_unbind($ldapconn);
  73.                         ldap_close($ldapconn);
  74.                         return $ldap_userinfo;
  75.                 } else {
  76.                         return false;
  77.                 }
  78.         }
  79.  
  80.         private function registerRA($ra, $ldap_userinfo) {
  81.                 $email = $ra->raEmail();
  82.  
  83.                 $ra->register_ra(null); // create a user account without password
  84.  
  85.                 /*
  86.                 OIDplus DB Field          ActiveDirectory field
  87.                 ------------------------------------------------
  88.                 ra_name                   cn
  89.                 personal_name             displayname (or: givenname + " " + sn)
  90.                 organization              company
  91.                 office                    physicaldeliveryofficename or department
  92.                 street                    streetaddress
  93.                 zip_town                  postalcode + " " + l
  94.                 country                   co (human-readable) or c (ISO country code)
  95.                 phone                     telephonenumber or homephone
  96.                 mobile                    mobile
  97.                 fax                       facsimiletelephonenumber
  98.                 (none)                    wwwhomepage
  99.                 */
  100.  
  101.                 if (!isset($ldap_userinfo['cn']))                         $ldap_userinfo['cn'] = '';
  102.                 if (!isset($ldap_userinfo['displayname']))                $ldap_userinfo['displayname'] = '';
  103.                 if (!isset($ldap_userinfo['givenname']))                  $ldap_userinfo['givenname'] = '';
  104.                 if (!isset($ldap_userinfo['sn']))                         $ldap_userinfo['sn'] = '';
  105.                 if (!isset($ldap_userinfo['company']))                    $ldap_userinfo['company'] = '';
  106.                 if (!isset($ldap_userinfo['physicaldeliveryofficename'])) $ldap_userinfo['physicaldeliveryofficename'] = '';
  107.                 if (!isset($ldap_userinfo['department']))                 $ldap_userinfo['department'] = '';
  108.                 if (!isset($ldap_userinfo['streetaddress']))              $ldap_userinfo['streetaddress'] = '';
  109.                 if (!isset($ldap_userinfo['postalcode']))                 $ldap_userinfo['postalcode'] = '';
  110.                 if (!isset($ldap_userinfo['l']))                          $ldap_userinfo['l'] = '';
  111.                 if (!isset($ldap_userinfo['co']))                         $ldap_userinfo['co'] = '';
  112.                 if (!isset($ldap_userinfo['c']))                          $ldap_userinfo['c'] = '';
  113.                 if (!isset($ldap_userinfo['telephonenumber']))            $ldap_userinfo['telephonenumber'] = '';
  114.                 if (!isset($ldap_userinfo['homephone']))                  $ldap_userinfo['homephone'] = '';
  115.                 if (!isset($ldap_userinfo['mobile']))                     $ldap_userinfo['mobile'] = '';
  116.                 if (!isset($ldap_userinfo['facsimiletelephonenumber']))   $ldap_userinfo['facsimiletelephonenumber'] = '';
  117.                 //if (!isset($ldap_userinfo['wwwhomepage']))                $ldap_userinfo['wwwhomepage'] = '';
  118.  
  119.                 $opuserdata = array();
  120.                 $opuserdata['ra_name'] = $ldap_userinfo['cn'];
  121.                 if (!empty($ldap_userinfo['displayname'])) {
  122.                         $opuserdata['personal_name'] = $ldap_userinfo['displayname'];
  123.                 } else {
  124.                         $opuserdata['personal_name'] = trim($ldap_userinfo['givenname'].' '.$ldap_userinfo['sn']);
  125.                 }
  126.                 $opuserdata['organization'] = $ldap_userinfo['company'];
  127.                 if (!empty($ldap_userinfo['physicaldeliveryofficename'])) {
  128.                         $opuserdata['office'] = $ldap_userinfo['physicaldeliveryofficename'];
  129.                 } else {
  130.                         $opuserdata['office'] = $ldap_userinfo['department'];
  131.                 }
  132.                 $opuserdata['street'] = $ldap_userinfo['streetaddress'];
  133.                 $opuserdata['zip_town'] = trim($ldap_userinfo['postalcode'].' '.$ldap_userinfo['l']);
  134.                 $opuserdata['country'] = $ldap_userinfo['co']; // ISO country code: $ldap_userinfo['c']
  135.                 $opuserdata['phone'] = $ldap_userinfo['telephonenumber']; // homephone for private phone number
  136.                 $opuserdata['mobile'] = $ldap_userinfo['mobile'];
  137.                 $opuserdata['fax'] = $ldap_userinfo['facsimiletelephonenumber'];
  138.  
  139.                 foreach ($opuserdata as $dbfield => $val) {
  140.                         if (!empty($val)) {
  141.                                 OIDplus::db()->query("update ###ra set ".$dbfield." = ? where email = ?", array($val, $email));
  142.                         }
  143.                 }
  144.         }
  145.  
  146.         public function action($actionID, $params) {
  147.                 if ($actionID == 'ra_login_ldap') {
  148.                         if (!OIDplus::baseConfig()->getValue('LDAP_ENABLED', false)) {
  149.                                 throw new OIDplusException(_L('LDAP authentication is disabled on this system.'));
  150.                         }
  151.  
  152.                         if (!function_exists('ldap_connect')) throw new OIDplusConfigInitializationException(_L('PHP extension "%1" not installed','LDAP'));
  153.  
  154.                         if (OIDplus::baseConfig()->getValue('RECAPTCHA_ENABLED', false)) {
  155.                                 $secret=OIDplus::baseConfig()->getValue('RECAPTCHA_PRIVATE', '');
  156.                                 $response=$params["captcha"];
  157.                                 $verify=file_get_contents("https://www.google.com/recaptcha/api/siteverify?secret={$secret}&response={$response}");
  158.                                 $captcha_success=json_decode($verify);
  159.                                 if ($captcha_success->success==false) {
  160.                                         throw new OIDplusException(_L('CAPTCHA not successfully verified'));
  161.                                 }
  162.                         }
  163.  
  164.                         $email = $params['email'];
  165.                         $password = $params['password'];
  166.  
  167.                         if (empty($email)) {
  168.                                 throw new OIDplusException(_L('Please enter a valid email address'));
  169.                         }
  170.  
  171.                         if (!($ldap_userinfo = $this->ldapAuthByEMail($email, $password))) {
  172.                                 if (OIDplus::config()->getValue('log_failed_ra_logins', false)) {
  173.                                         OIDplus::logger()->log("[WARN]A!", "Failed login to RA account '$email' using LDAP");
  174.                                 }
  175.                                 throw new OIDplusException(_L('Wrong password or user not registered'));
  176.                         }
  177.  
  178.                         $ra = new OIDplusRA($email);
  179.                         if (!$ra->existing()) {
  180.                                 $this->registerRA($ra, $ldap_userinfo);
  181.                                 OIDplus::logger()->log("[INFO]RA($email)!", "RA '$email' was created because of successful LDAP login");
  182.                         }
  183.  
  184.                         OIDplus::logger()->log("[OK]RA($email)!", "RA '$email' logged in via LDAP");
  185.                         OIDplus::authUtils()::raLogin($email);
  186.  
  187.                         OIDplus::db()->query("UPDATE ###ra set last_login = ".OIDplus::db()->sqlDate()." where email = ?", array($email));
  188.  
  189.                         return array("status" => 0);
  190.                 } else {
  191.                         throw new OIDplusException(_L('Unknown action ID'));
  192.                 }
  193.         }
  194.  
  195.         public function init($html=true) {
  196.                 // Nothing
  197.         }
  198.  
  199.         public function gui($id, &$out, &$handled) {
  200.                 if ($id === 'oidplus:login_ldap') {
  201.                         $handled = true;
  202.                         $out['title'] = _L('Login using LDAP / ActiveDirectory');
  203.                         $out['icon']  = OIDplus::webpath(__DIR__).'icon_big.png';
  204.  
  205.                         if (!OIDplus::baseConfig()->getValue('LDAP_ENABLED', false)) {
  206.                                 $out['icon'] = 'img/error_big.png';
  207.                                 $out['text'] = _L('LDAP authentication is disabled on this system.');
  208.                                 return;
  209.                         }
  210.  
  211.                         if (!function_exists('ldap_connect')) {
  212.                                 $out['icon'] = 'img/error_big.png';
  213.                                 $out['text'] = _L('PHP extension "%1" not installed','LDAP');
  214.                                 return;
  215.                         }
  216.  
  217.                         $out['text'] = '';
  218.  
  219.                         $out['text'] .= '<noscript>';
  220.                         $out['text'] .= '<p>'._L('You need to enable JavaScript to use the login area.').'</p>';
  221.                         $out['text'] .= '</noscript>';
  222.  
  223.                         $out['text'] .= '<div id="loginLdapArea" style="visibility: hidden">';
  224.                         $out['text'] .= (OIDplus::baseConfig()->getValue('RECAPTCHA_ENABLED', false) ?
  225.                                         '<script> grecaptcha.render(document.getElementById("g-recaptcha"), { "sitekey" : "'.OIDplus::baseConfig()->getValue('RECAPTCHA_PUBLIC', '').'" }); </script>'.
  226.                                         '<p>'._L('Before logging in, please solve the following CAPTCHA').'</p>'.
  227.                                         '<div id="g-recaptcha" class="g-recaptcha" data-sitekey="'.OIDplus::baseConfig()->getValue('RECAPTCHA_PUBLIC', '').'"></div>' : '');
  228.                         $out['text'] .= '<br>';
  229.  
  230.                         $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>';
  231.  
  232.                         $out['text'] .= '<h2>'._L('Login as RA').'</h2>';
  233.  
  234.                         $login_list = OIDplus::authUtils()->loggedInRaList();
  235.                         if (count($login_list) > 0) {
  236.                                 foreach ($login_list as $x) {
  237.                                         $out['text'] .= '<p>'._L('You are logged in as %1','<b>'.$x->raEmail().'</b>').' (<a href="#" onclick="return raLogout('.js_escape($x->raEmail()).');">'._L('Logout').'</a>)</p>';
  238.                                 }
  239.                                 $out['text'] .= '<p>'._L('If you have more accounts, you can log in with another account here.').'</p>';
  240.                         } else {
  241.                                 $out['text'] .= '<p>'._L('Enter your email address and your password to log in as Registration Authority.').'</p>';
  242.                         }
  243.                         $out['text'] .= '<form onsubmit="return raLoginLdapOnSubmit(this);">';
  244.                         $out['text'] .= '<div><label class="padding_label">'._L('E-Mail').':</label><input type="text" name="email" value="" id="raLoginLdapEMail"></div>';
  245.                         $out['text'] .= '<div><label class="padding_label">'._L('Password').':</label><input type="password" name="password" value="" id="raLoginLdapPassword"></div>';
  246.                         $out['text'] .= '<br><input type="submit" value="'._L('Login').'"><br><br>';
  247.                         $out['text'] .= '</form>';
  248.  
  249.                         $invitePlugin = OIDplus::getPluginByOid('1.3.6.1.4.1.37476.2.5.2.4.2.92'); // OIDplusPageRaInvite
  250.                         $out['text'] .= '<p><abbr title="'._L('You don\'t need to register. Just enter your Windows/Company credentials.').'">'._L('How to register?').'</abbr></p>';
  251.  
  252.                         $mins = ceil(OIDplus::baseConfig()->getValue('SESSION_LIFETIME', 30*60)/60);
  253.                         $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.', $mins);
  254.                         $privacy_document_file = 'OIDplus/privacy_documentation.html';
  255.                         $resourcePlugin = OIDplus::getPluginByOid('1.3.6.1.4.1.37476.2.5.2.4.1.500'); // OIDplusPagePublicResources
  256.                         if (!is_null($resourcePlugin) && file_exists(OIDplus::localpath().'res/'.$privacy_document_file)) {
  257.                                 $out['text'] .= ' <a '.OIDplus::gui()->link('oidplus:resources$'.$privacy_document_file.'#cookies').'>'._L('More information about the cookies used').'</a>';
  258.                         }
  259.                         $out['text'] .= '</font></p></div>';
  260.  
  261.                         $out['text'] .= '<script>document.getElementById("loginLdapArea").style.visibility = "visible";</script>';
  262.                 }
  263.         }
  264.  
  265.         public function publicSitemap(&$out) {
  266.                 $out[] = 'oidplus:login_ldap';
  267.         }
  268.  
  269.         public function tree(&$json, $ra_email=null, $nonjs=false, $req_goto='') {
  270.                 return true;
  271.         }
  272.  
  273.         public function tree_search($request) {
  274.                 return false;
  275.         }
  276.  
  277.         public function implementsFeature($id) {
  278.                 if (strtolower($id) == '1.3.6.1.4.1.37476.2.5.2.3.5') return true; // alternativeLoginMethods
  279.                 return false;
  280.         }
  281.  
  282.         public function alternativeLoginMethods() {
  283.                 $logins = array();
  284.                 if (OIDplus::baseConfig()->getValue('LDAP_ENABLED', false)) {
  285.                         $logins[] = array(
  286.                                 'oidplus:login_ldap',
  287.                                 _L('Login using LDAP / ActiveDirectory'),
  288.                                 OIDplus::webpath(__DIR__).'treeicon.png'
  289.                         );
  290.                 }
  291.                 return $logins;
  292.         }
  293. }
  294.