File indexing completed on 2024-06-09 05:54:53

0001 <?php
0002 
0003 /**
0004  *  ocs-webserver
0005  *
0006  *  Copyright 2016 by pling GmbH.
0007  *
0008  *    This file is part of ocs-webserver.
0009  *
0010  *    This program is free software: you can redistribute it and/or modify
0011  *    it under the terms of the GNU Affero General Public License as
0012  *    published by the Free Software Foundation, either version 3 of the
0013  *    License, or (at your option) any later version.
0014  *
0015  *    This program is distributed in the hope that it will be useful,
0016  *    but WITHOUT ANY WARRANTY; without even the implied warranty of
0017  *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0018  *    GNU Affero General Public License for more details.
0019  *
0020  *    You should have received a copy of the GNU Affero General Public License
0021  *    along with this program.  If not, see <http://www.gnu.org/licenses/>.
0022  **/
0023 class Local_Auth_Adapter_Ocs implements Local_Auth_Adapter_Interface
0024 {
0025 
0026     const MD5 = 'enc01';
0027     const SHA = 'enc02';
0028     const PASSWORDSALT = 'ghdfklsdfgjkldfghdklgioerjgiogkldfgndfohgfhhgfhgfhgfhgfhfghfgnndf';
0029     const USER_DEACTIVATED = '_double';
0030     const EMAIL_DEACTIVATED = '_double';
0031 
0032     protected $_db;
0033     protected $_tableName;
0034     protected $_identity;
0035     protected $_credential;
0036     protected $_encryption;
0037     protected $_resultRow;
0038 
0039     /**
0040      * __construct() - Sets configuration options
0041      *
0042      * @param Zend_Db_Adapter_Abstract $dbAdapter If null, default database adapter assumed
0043      * @param string                   $tableName
0044      *
0045      * @throws Zend_Auth_Adapter_Exception
0046      */
0047     public function __construct(Zend_Db_Adapter_Abstract $dbAdapter = null, $tableName = null)
0048     {
0049         $this->_db = $dbAdapter;
0050         if (empty($this->_db)) {
0051             $this->_db = Zend_Db_Table_Abstract::getDefaultAdapter();
0052             if (empty($this->_db)) {
0053                 throw new Zend_Auth_Adapter_Exception('No database adapter present');
0054             }
0055         }
0056 
0057         $this->_tableName = $tableName;
0058     }
0059 
0060     /**
0061      * @param string $password
0062      * @param int    $passwordType
0063      * @return string
0064      */
0065     public static function getEncryptedPassword($password, $passwordType)
0066     {
0067         return $passwordType == Default_Model_DbTable_Member::PASSWORD_TYPE_HIVE
0068             ? sha1((self::PASSWORDSALT . $password . self::PASSWORDSALT))
0069             : md5($password);
0070     }
0071 
0072     /**
0073      * @param string $password
0074      * @return string
0075      */
0076     public static function getEncryptedLdapPass($password)
0077     {
0078         return '{MD5}' . base64_encode(md5($password, true));
0079     }
0080 
0081     /**
0082      * Performs an authentication attempt
0083      *
0084      * @return Zend_Auth_Result
0085      * @throws Zend_Exception
0086      */
0087     public function authenticate()
0088     {
0089         $validator = new Zend_Validate_EmailAddress();
0090         if ($validator->isValid($this->_identity)) {
0091             $resultSet = $this->fetchUserByEmail();
0092         } else {
0093             $resultSet = $this->fetchUserByUsername();
0094         }
0095 
0096         if (count($resultSet) == 0) {
0097             return $this->createAuthResult(Zend_Auth_Result::FAILURE_IDENTITY_NOT_FOUND, $this->_identity,
0098                 array('A record with the supplied identity could not be found.'));
0099         }
0100 
0101         if (count($resultSet) > 1) {
0102             return $this->createAuthResult(Zend_Auth_Result::FAILURE_IDENTITY_AMBIGUOUS, $this->_identity,
0103                 array('More than one record matches the supplied identity.'));
0104         }
0105 
0106         if (empty($resultSet[0]['email_checked'])) {
0107             return $this->createAuthResult(Local_Auth_Result::MAIL_ADDRESS_NOT_VALIDATED, $resultSet[0]['member_id'],
0108                 array('Mail address not validated.'));
0109         }
0110 
0111         if ($resultSet[0]['is_active'] == 0) {
0112             return $this->createAuthResult(Local_Auth_Result::ACCOUNT_INACTIVE, $this->_identity,
0113                 array('User account is inactive.'));
0114         }
0115 
0116         $this->_resultRow = array_shift($resultSet);
0117 
0118         return $this->createAuthResult(Zend_Auth_Result::SUCCESS, $this->_identity,
0119             array('Authentication successful.'));
0120     }
0121 
0122     /**
0123      * @return array
0124      * @throws Zend_Exception
0125      */
0126     private function fetchUserByEmail()
0127     {
0128         $sql = "
0129             SELECT `m`.*, `member_email`.`email_verification_value`, `member_email`.`email_checked`, `mei`.`external_id` 
0130             FROM `member` AS `m`
0131             JOIN `member_email` ON `m`.`member_id` = `member_email`.`email_member_id` AND `member_email`.`email_primary` = 1 AND `member_email`.`email_deleted` = 0
0132             LEFT JOIN `member_external_id` AS `mei` ON `mei`.`member_id` = `m`.`member_id`
0133             WHERE  
0134             `m`.`is_active` = :active AND
0135             `m`.`is_deleted` = :deleted AND 
0136             `m`.`login_method` = :login AND 
0137             (LOWER(`m`.`mail`) = LOWER(:mail) OR
0138             LOWER(`m`.`mail`) = CONCAT(LOWER(:mail),:user_deactivated)
0139             ) AND
0140             `m`.`password` = :pwd";
0141 
0142         $this->_db->getProfiler()->setEnabled(true);
0143         $resultSet = $this->_db->fetchAll($sql, array(
0144             'active'           => Default_Model_DbTable_Member::MEMBER_ACTIVE,
0145             'deleted'          => Default_Model_DbTable_Member::MEMBER_NOT_DELETED,
0146             'login'            => Default_Model_DbTable_Member::MEMBER_LOGIN_LOCAL,
0147             'mail'             => $this->_identity,
0148             'user_deactivated' => $this::EMAIL_DEACTIVATED,
0149             'pwd'              => $this->_credential
0150         ));
0151 
0152         $sql = str_replace(':active', Default_Model_DbTable_Member::MEMBER_ACTIVE, $sql);
0153         $sql = str_replace(':deleted', Default_Model_DbTable_Member::MEMBER_NOT_DELETED, $sql);
0154         $sql = str_replace(':login', "'" . Default_Model_DbTable_Member::MEMBER_LOGIN_LOCAL . "'", $sql);
0155         $sql = str_replace(':mail', "'" . $this->_identity . "'", $sql);
0156         $sql = str_replace(':pwd', "'" . $this->_credential . "'", $sql);
0157 
0158         Zend_Registry::get('logger')->debug(__METHOD__ . ' - SQL: ' . $sql . ' - sql take seconds: ' . $this->_db->getProfiler()
0159                                                                                                                  ->getLastQueryProfile()
0160                                                                                                                  ->getElapsedSecs());
0161         $this->_db->getProfiler()->setEnabled(false);
0162 
0163         return $resultSet;
0164     }
0165 
0166     /**
0167      * Fetches a user by username, username ist not case sensitve
0168      *
0169      * @return array
0170      * @throws Zend_Exception
0171      */
0172     private function fetchUserByUsername()
0173     {
0174         $sql = "
0175             SELECT `m`.*, `member_email`.`email_verification_value`, `member_email`.`email_checked`, `mei`.`external_id` 
0176             FROM `member` AS `m`
0177             JOIN `member_email` ON `m`.`member_id` = `member_email`.`email_member_id` AND `member_email`.`email_primary` = 1 AND `member_email`.`email_deleted` = 0
0178             LEFT JOIN `member_external_id` AS `mei` ON `mei`.`member_id` = `m`.`member_id`
0179             WHERE  
0180             `m`.`is_active` = :active AND 
0181             `m`.`is_deleted` = :deleted AND 
0182             `m`.`login_method` = :login AND 
0183             (LOWER(`m`.`username`) = LOWER(:username) OR
0184             LOWER(`m`.`username`) = CONCAT(LOWER(:username),:user_deactivated)
0185             ) 
0186             AND 
0187             `m`.`password` = :pwd";
0188 
0189         $this->_db->getProfiler()->setEnabled(true);
0190         $resultSet = $this->_db->fetchAll($sql, array(
0191             'active'           => Default_Model_DbTable_Member::MEMBER_ACTIVE,
0192             'deleted'          => Default_Model_DbTable_Member::MEMBER_NOT_DELETED,
0193             'login'            => Default_Model_DbTable_Member::MEMBER_LOGIN_LOCAL,
0194             'username'         => $this->_identity,
0195             'user_deactivated' => $this::USER_DEACTIVATED,
0196             'pwd'              => $this->_credential
0197         ));
0198 
0199         $sql = str_replace(':active', Default_Model_DbTable_Member::MEMBER_ACTIVE, $sql);
0200         $sql = str_replace(':deleted', Default_Model_DbTable_Member::MEMBER_NOT_DELETED, $sql);
0201         $sql = str_replace(':login', "'" . Default_Model_DbTable_Member::MEMBER_LOGIN_LOCAL . "'", $sql);
0202         $sql = str_replace(':username', "'" . $this->_identity . "'", $sql);
0203         $sql = str_replace(':user_deactivated', "'" . $this::USER_DEACTIVATED . "'", $sql);
0204         $sql = str_replace(':pwd', "'" . $this->_credential . "'", $sql);
0205 
0206         Zend_Registry::get('logger')->debug(__METHOD__ . ' - SQL: ' . $sql . ' - sql take seconds: ' . $this->_db->getProfiler()
0207                                                                                                                  ->getLastQueryProfile()
0208                                                                                                                  ->getElapsedSecs());
0209         $this->_db->getProfiler()->setEnabled(false);
0210 
0211         return $resultSet;
0212     }
0213 
0214     protected function createAuthResult($code, $identity, $messages)
0215     {
0216         return new Local_Auth_Result($code, $identity, $messages);
0217     }
0218 
0219     /**
0220      * @param string $identity
0221      *
0222      * @return Local_Auth_Adapter_Ocs
0223      */
0224     public function setIdentity($identity)
0225     {
0226         $this->_identity = $identity;
0227 
0228         return $this;
0229     }
0230 
0231     /**
0232      * @param string $credential
0233      *
0234      * @return Local_Auth_Adapter_Ocs
0235      * @throws Zend_Exception
0236      */
0237     public function setCredential($credential)
0238     {
0239         switch ($this->_encryption) {
0240             case self::MD5 :
0241                 $this->_credential = md5($credential);
0242                 break;
0243             case self::SHA :
0244                 $this->_credential = sha1((self::PASSWORDSALT . $credential . self::PASSWORDSALT));
0245                 break;
0246             default:
0247                 throw new Zend_Exception('There is no default case for credential encryption.');
0248         }
0249 
0250         return $this;
0251     }
0252 
0253     /**
0254      * @param mixed $encryption
0255      *
0256      * @return Local_Auth_Adapter_Ocs
0257      */
0258     public function setEncryption($encryption)
0259     {
0260         $this->_encryption = $encryption;
0261 
0262         return $this;
0263     }
0264 
0265     /**
0266      * getResultRowObject() - Returns the result row as a stdClass object
0267      *
0268      * @param string|array $returnColumns
0269      * @param string|array $omitColumns
0270      *
0271      * @return stdClass|boolean
0272      */
0273     public function getResultRowObject($returnColumns = null, $omitColumns = null)
0274     {
0275         if (!$this->_resultRow) {
0276             return false;
0277         }
0278 
0279         $returnObject = new stdClass();
0280 
0281         if (null !== $returnColumns) {
0282 
0283             $availableColumns = array_keys($this->_resultRow);
0284             foreach ((array)$returnColumns as $returnColumn) {
0285                 if (in_array($returnColumn, $availableColumns)) {
0286                     $returnObject->{$returnColumn} = $this->_resultRow[$returnColumn];
0287                 }
0288             }
0289 
0290             return $returnObject;
0291         } else {
0292             if (null !== $omitColumns) {
0293 
0294                 $omitColumns = (array)$omitColumns;
0295                 foreach ($this->_resultRow as $resultColumn => $resultValue) {
0296                     if (!in_array($resultColumn, $omitColumns)) {
0297                         $returnObject->{$resultColumn} = $resultValue;
0298                     }
0299                 }
0300 
0301                 return $returnObject;
0302             } else {
0303 
0304                 foreach ($this->_resultRow as $resultColumn => $resultValue) {
0305                     $returnObject->{$resultColumn} = $resultValue;
0306                 }
0307 
0308                 return $returnObject;
0309             }
0310         }
0311     }
0312 
0313 }