Skip to content
Snippets Groups Projects
User.php 11.2 KiB
Newer Older
/**
 * Personal info
 * @property $LastName string
 * @property $FirstName string
 * @property $SecondName string
 * @property $FacultyID int
 * @property $FacultyName string
 * @property $FacultyAbbr string
 *
 * Account info
 * @property $ID int
 * @property $Login string
 * @property $EMail string
 * @property $Type string  teacher / student
 * @property $Role string  description
 * @property $RoleMark int
 * @property $IsEnabled bool
 * @property $Code
 * @property $UserAgent
 *
 * Session
 * @property-read $SemesterID int
 * @property-read $last_active int
 * @property-read $LoggedIn bool
 * @property-read $UserHash string
 * @property-read $PasswordHash string
 * @property-read $start_time int
class User implements ArrayAccess
xamgore's avatar
xamgore committed
{
PavelBegunkov's avatar
PavelBegunkov committed
    protected static $_flag;
     * todo: mark deprecated as singleton is an anti-pattern!
xamgore's avatar
xamgore committed
     * @return self class instance (singleton-pattern)
     */
PavelBegunkov's avatar
PavelBegunkov committed
    public static function instance($state = false) {
        self::$_flag = $state;
xamgore's avatar
xamgore committed
        if (!isset(self::$_instance)) {
xamgore's avatar
xamgore committed


    private function __construct($config = array()) {
        $this->_config = $config;
        $this->_session = Session::instance();
        $this->_userInfo['RoleMark'] = (int)1;
PavelBegunkov's avatar
PavelBegunkov committed

        $this->_config['hash_key'] = Model_Account::getHashKey();
PavelBegunkov's avatar
PavelBegunkov committed
        $isSignedIn = $this->isSignedIn();
xamgore's avatar
xamgore committed
        if ($isSignedIn) {
PavelBegunkov's avatar
PavelBegunkov committed
            $this->_userInfo = $this->_session->get('UserInfo');
xamgore's avatar
xamgore committed

PavelBegunkov's avatar
PavelBegunkov committed
            if (self::$_flag != true) {
                $this->_session->regenerate();
                $this->_session->set('start_time', time());
            }
xamgore's avatar
xamgore committed


    const RIGHTS_ANYBODY = 1;
    const RIGHTS_STUDENT = 2;
    const RIGHTS_TEACHER = 4;
    const RIGHTS_ADMIN = 8;
    const RIGHTS_DEAN = 16;

    /**
     * @param $mask
     * @throws LogicException
     */
    public function checkAccess($mask) {
        $goodBoy = $this->RoleMark & $mask;
        if (!$goodBoy) throw new LogicException(Error::ACCESS_DENIED);
    }

    public function isDean() {
        return (bool) ($this->RoleMark & self::RIGHTS_DEAN);
    }

    public function isAdmin() {
        return (bool) ($this->RoleMark & self::RIGHTS_ADMIN);
    }

    public function isTeacher() {
        return (bool) ($this->RoleMark & self::RIGHTS_TEACHER);
    }

    public function isStudent() {
        return (bool) ($this->RoleMark & self::RIGHTS_DEAN);
    }

    public function isAuthorized() {
        return (bool) ($this->RoleMark & ~self::RIGHTS_ANYBODY);
    }

xamgore's avatar
xamgore committed
     * Проверяет корректность кода активации и существование
     * аккаунтов с такими же авторизационными данными.
xamgore's avatar
xamgore committed
     * @param string $code   Код активации
     * @param string $email  E-Mail адрес
xamgore's avatar
xamgore committed
     * @param string $login
     * @param string $password
     * @return array  Пару вида <tt>(is_ok, err_msg)</tt>
     */
    public function signUp($code, $email, $login, $password) {
PavelBegunkov's avatar
PavelBegunkov committed
        $account = Account::instance();
xamgore's avatar
xamgore committed

        $isValid = Model_Account::isActivationCodeValid($code);
PavelBegunkov's avatar
PavelBegunkov committed
        if (!$isValid) {
PavelBegunkov's avatar
PavelBegunkov committed

        $isLogin = $account->doesLoginExist($login);
        $isMail = $account->isMailValid($email);
xamgore's avatar
xamgore committed

PavelBegunkov's avatar
PavelBegunkov committed
        if ($isLogin) {
            return array(false, 'login_exists');
xamgore's avatar
xamgore committed
        } else {
            if ($isMail) {
                return array(false, 'mail_exists');
            }
PavelBegunkov's avatar
PavelBegunkov committed
        }
xamgore's avatar
xamgore committed

        $id = Model_Account::activateAccount($login, $password, $email, $code);
PavelBegunkov's avatar
PavelBegunkov committed
        $this->completeSignIn($id, $this->hash($password));
        return array(true, 'ok');
xamgore's avatar
xamgore committed
     * Проверяет корректность авторизационных данных.
xamgore's avatar
xamgore committed
     * @param string $login
     * @param string $password
     * @return bool  true, если авторизация прошла успешно,
     * и false, если данные являются некорректными.
     */
        $id = (int)Model_Account::checkAuth($login, $password);
PavelBegunkov's avatar
PavelBegunkov committed
        if ($id === -1) {
PavelBegunkov's avatar
PavelBegunkov committed
        } else {
Andrew Rudenets's avatar
Andrew Rudenets committed
            return $this->completeSignIn($id, $this->hash($password));
PavelBegunkov's avatar
PavelBegunkov committed
        }
xamgore's avatar
xamgore committed

    protected function completeSignIn($id, $passHash) {
xamgore's avatar
xamgore committed
        $source = $id . Request::$user_agent . Request::$client_ip;
        $userHash = $this->hash($source) . $this->_config['hash_key'];
        $passwordHash = $this->hash($passHash . $this->_config['hash_key']);
PavelBegunkov's avatar
PavelBegunkov committed
        Cookie::set('userhash', $passwordHash);
        $userInfo = Model_Account::getUserInfo($id);
        $this->_session->set('UserInfo', $userInfo);
        $this->_session->regenerate();
        $this->_session->set('ID', $id);
        $this->_session->set('LoggedIn', true);
        $this->_session->set('UserHash', $this->hash($userHash));
PavelBegunkov's avatar
PavelBegunkov committed
        $this->_session->set('PasswordHash', $passwordHash);
PavelBegunkov's avatar
PavelBegunkov committed
        $this->_session->set('start_time', time());
        $this->_session->set('SemesterID', $userInfo['SemesterID']);
xamgore's avatar
xamgore committed
        return true;
    }
xamgore's avatar
xamgore committed
     * Проверяет авторизационный статус пользователя и, если
     * пользователь имеет UserAgent и IP, отличные от хранимых
     * в сессии, осуществляет выход из текущего сеанса.
xamgore's avatar
xamgore committed
     * @return bool  true, если пользователь авторизован
     */
    public function isSignedIn() {
PavelBegunkov's avatar
PavelBegunkov committed
        $session = &$this->_session;
xamgore's avatar
xamgore committed
        if ($session->get('LoggedIn') && !$this->checkHash()) {
PavelBegunkov's avatar
PavelBegunkov committed
            $this->completeSignOut();
xamgore's avatar
xamgore committed

    protected function checkHash() {
xamgore's avatar
xamgore committed
        $source = $id . Request::$user_agent . Request::$client_ip;
        $userHash = $this->hash($source) . $this->_config['hash_key'];
        $userCheck = $this->_session->get('UserHash') == $this->hash($userHash);
        $passCheck = Cookie::get('userhash') == $this->_session->get('PasswordHash');
        return $userCheck AND $passCheck;
xamgore's avatar
xamgore committed
    }

xamgore's avatar
xamgore committed
     * @return bool
     */
    public function signOut() {
        if ($this->isSignedIn()) {
xamgore's avatar
xamgore committed
        return false;
xamgore's avatar
xamgore committed

    protected function completeSignOut() {
        $this->_session
            ->set('ID', false)
            ->set('LoggedIn', false)
            ->set('UserHash', false);

xamgore's avatar
xamgore committed
        return true;
    }

    /**
     * Проверяет корректность данного пароля для текущего пользователя.
     *
xamgore's avatar
xamgore committed
     * @param string $password
     * @return bool
     */
xamgore's avatar
xamgore committed
        if (!$this->isSignedIn())
            return false;

        $passHash = $this->hash($password);
        $computed = $this->hash($passHash . $this->_config['hash_key']);
        return $computed === $this->_session->get('PasswordHash');
xamgore's avatar
xamgore committed

    public function changePassword($old, $new) {
        if (!$this->checkPassword($old))
            return false;

        Model_Account::changeAccountData($this->ID, $new, 'password');
xamgore's avatar
xamgore committed
        $passhash = $this->hash($this->hash($new) . $this->_config['hash_key']);
xamgore's avatar
xamgore committed
        Cookie::set('userhash', $passhash);
        return true;
xamgore's avatar
xamgore committed

    public function changeLogin($login) {
        if (!$this->isSignedIn() || Account::instance()->doesLoginExist($login))
xamgore's avatar
xamgore committed
            return false;

        Model_Account::changeAccountData($this->ID, $login, 'login');
xamgore's avatar
xamgore committed
        return true;
    }

    public function changeMail($email) {
        if (!$this->isSignedIn() || Account::instance()->isMailValid($email))
xamgore's avatar
xamgore committed
            return false;

        $token = md5(time() . $this->EMail . $email);
xamgore's avatar
xamgore committed
    }

    public function completeChangeMail($token) {
        if ($token == $this->_session->get('NewMail_Token') AND !Account::instance()->isMailValid($email)) {
            Model_Account::changeAccountData($this->ID, $email, 'email');
xamgore's avatar
xamgore committed
        } else {
xamgore's avatar
xamgore committed

    public function changeProfile($data) {
        if ($this->Type == 'teacher') {
            Model_Account::changeTeacherInfo($this['TeacherID'], $data['lastName'], $data['firstName'], $data['secondName'], $data['jobPositionID'], $data['departmentID']);
xamgore's avatar
xamgore committed
        }
    }

    /* Info */

    /**
     * Возвращает массив, содержащий пользовательские данные.
     *
     * @return  array
     */
    public function toArray() {
xamgore's avatar
xamgore committed
        if ($this->isSignedIn()) {
            return $this->_userInfo + $this->_session->as_array();
            // fixme: nobody knows what _session contains!
xamgore's avatar
xamgore committed
        } else {
PavelBegunkov's avatar
PavelBegunkov committed
            return array();
xamgore's avatar
xamgore committed
        }
xamgore's avatar
xamgore committed

    /* Fields access */

    function __set($name, $value) {
        $this->offsetSet($name, $value);
    }

    function __get($name) {
        return $this->offsetGet($name);
    }
xamgore's avatar
xamgore committed

        if ($this->_userInfo && array_key_exists($offset, $this->_userInfo)) {
            $this->_userInfo[$offset] = $value;
        } elseif (isset($offset, $this->_session)) {
            $this->_session[$offset] = $value;
        } else { // TODO: _userInfo may be null
            $this->_userInfo[$offset] = $value;
        if ($this->_userInfo && array_key_exists($offset, $this->_userInfo))
        else if (isset($offset, $this->_session))
            return $this->_session[$offset];

        throw new ErrorException('No such field');
xamgore's avatar
xamgore committed

        if (array_key_exists($offset, $this->_userInfo))
        else unset($this->_session[$offset]);
xamgore's avatar
xamgore committed

        return array_key_exists($offset, $this->_userInfo) ||
            isset($this->_session[$offset]);
xamgore's avatar
xamgore committed

xamgore's avatar
xamgore committed
     * @param   string $str string to hash
xamgore's avatar
xamgore committed
    protected function hash($str) {
        if (!$this->_config['hash_key']) {
            $this->_config['hash_key'] = $key = md5(time() . Request::$client_ip);
            Model_Account::setHashKey($key);
xamgore's avatar
xamgore committed
    }