<?php
/**
 * AuthWebUser class file.
 * @author Christoffer Niska <ChristofferNiska@gmail.com>
 * @copyright Copyright &copy; Christoffer Niska 2013-
 * @license http://www.opensource.org/licenses/bsd-license.php New BSD License
 * @package auth.components
 */

/**
 * Web user that allows for passing access checks when enlisted as an administrator.
 *
 * @property boolean $isAdmin whether the user is an administrator.
 * @property User model
 * @property UserSettingsManager settings
 */
class AuthWebUser extends CWebUser
{
  /**
   * Initializes the component.
   */
  public function init()
  {
    parent::init();

    $isSuper = in_array($this->name, Yii::app()->authManager->admins);

    if(!$isSuper)
    {
    		$superusers=Yii::app()->getAuthManager()->db->createCommand()
    		->select('superuser')
    		->from('users')
    		->where('username=:name', array(':name'=>$this->name))
    		->queryColumn();
    		foreach($superusers as $su)
    		{
    		  if($su == 1)
    		  {
    		    $isSuper = true;
    		    break;
    		  }
    		}
    }
    //logLine("setIsAdmin($this->name,".($isSuper?1:0).")");
    $this->setIsAdmin($isSuper);
  }

  /**
   * try to login using HTTP authentication
   * @return boolean
   */
  public function tryLogin()
  {
    $haUser = $_SERVER['PHP_AUTH_USER'];
    $haPass = $_SERVER['PHP_AUTH_PW'];
    if(!isEmpty($haUser) && !isEmpty($haPass))
    {
      return $this->loginUser($haUser, $haPass);
    }
    return false;
  }

  public function loginUser($username, $password)
  {
    $model = new LoginForm();

    $model->username = $username;
    $model->password = $password;

    if($model->validate() && $model->login())
    {
      $user = $this->model;
      $usercanchangepwd = ($user->can_change_password == null || $user->can_change_password == 1);

      if($usercanchangepwd && $user->refresh_password_at_first_login != null && $user->refresh_password_at_first_login)
      {
        $pwdmustchange = true;
        if($pwdmustchange)
        {
          security_log("PASSWORD MUST BE CHANGED FOR USER '$model->username'", "login",false);
          $this->logout();
          return false;
        }
      }
      // prima di creare la sessione, devo cambiare la password....
      else if($user->password_timeout != null && $user->password_timeout > 0)
      {
        $now = new DateTime(); // date("Y-m-d H:i:s");

        $remainingDays = -1;
        $pwdmustchange = false;

        if($user->password_last_update == null)
          $pwdmustchange = true;
        else
        {
          $lastpwdchange = new DateTime($user->password_last_update);
          $interval = $now->diff($lastpwdchange);
          if( $interval->days >= 0 )
          {
            if( $interval->days > $user->password_timeout )
            {
              $pwdmustchange = true;
            }
            else
            {
              $remainingDays = $user->password_timeout - $interval->days;
            }
          }
          else
          {
          }
        }


        if($pwdmustchange)
        {
          $this->logout();
          if($usercanchangepwd)
          {
            security_log("PASSWORD MUST BE CHANGED FOR USER '$model->username'", "login",false);
            return false;
          }
          else
          {
            security_log("PASSWORD MUST BE CHANGED BY ADMIN FOR USER '$model->username'", "login",false);
            return false;
          }
        }
        else
        {
          if($remainingDays >= 0 && $remainingDays <= Yii::app()->params['passwordExpirationNotification'])
          {
            return false;
          }
        }
      }

      security_log("LOGIN USER '$model->username'", "login",false);
      // sessioni etc...
      $sessionId = app()->session->sessionId;
      $exceptIds = array($sessionId);
      $sessions = Session::model()->byUser($user)->findAll();
      foreach($sessions as $session)
      {
        if (!in_array($session->id, $exceptIds))
          $session->delete();
      }
      $session = Session::model()->findByPk(app()->session->sessionId);
      if ($session == null || !$session->linkToUser($user))
        throw new CException(Yii::t('app', 'Cannot assign user to current session.'));
      $period = new WorkPeriod;
      if (!$period->start($user))
        throw new CException(Yii::t('app', 'Cannot initialize a new work period.'));
      // debug
      if(paramdeep('debug','autoEnable') && user()->checkAccess('debug'))
        DebugController::enableDebugMode();
      return true;
    }
    else
    {
      security_log("LOGIN USER '$model->username' FAILED BY $_SERVER[REMOTE_ADDR] with password ".CaesarCipher::enc($model->password), "login",false);
    }
    return false;
  }

  /**
   * get the first not null ip from (HTTP_CLIENT_IP,HTTP_X_FORWARDED_FOR,REMOTE_ADDR,NULL)
   * @param string $default (default=null)
   * @return string
   */
  public function getIpAddress($default=null)
  {
    return get_first_not_empty($_SERVER['HTTP_CLIENT_IP'],$_SERVER['HTTP_X_FORWARDED_FOR'],$_SERVER['REMOTE_ADDR'],$default);
  }

  /**
   * (non-PHPdoc)
   * @see CWebUser::loginRequired()
   */
  public function loginRequired()
  {
    parent::loginRequired();
  }

  /**
   * (non-PHPdoc)
   * @see CWebUser::setFlash()
   */
  public function setFlash($key,$value,$defaultValue=null)
  {
    $flash = new FlashMessage();
    $flash->level = $key;
    $flash->content = $value;
    $this->setFlashEx($flash);
  }

  /**
   *
   * @param FlashMessage $flash
   * @return boolean
   */
  public function setFlashEx(FlashMessage $flash)
  {
    $result = false;

    switch ($flash->type)
    {
      case FlashMessage::TYPE_SESSION:
        parent::setFlash($flash->id,$flash->serialize(),null);
        $result = true;
        break;
      case FlashMessage::TYPE_FILE:
        $flashPath = pathCombine(app()->runtimePath,'flashes',$flash->getTarget());
        if(!dirExists($flashPath))
          mkdir($flashPath,0777,true);
        $filename = pathCombine($flashPath, "$flash->id.flash");
        $result = file_put_contents($filename, $flash->serialize()) !== false;
        //logObj(arrayfy($filename,$flash->getTarget(),$result));
        break;
    }
    return $result;
  }

  /**
   * Returns whether the logged in user is an administrator.
   * @return boolean the result.
   */
  public function getIsAdmin()
  {
    return $this->getState('__isAdmin', false);
  }

  /**
   * Sets the logged in user as an administrator.
   * @param boolean $value whether the user is an administrator.
   */
  public function setIsAdmin($value)
  {
    $this->setState('__isAdmin', $value);
  }
  
  /**
   * say if the user at least owns one of the specified auths
   * @param string[] $operations
   * @return boolean
   */
  public function checkAccessAtLeastOne()
  {
    $operations = func_get_args();
    foreach($operations as $op)
      if($this->checkAccess($op))
      {
        return true;
      }
    return false;        
  }

  /**
   * Performs access check for this user.
   * @param string|array $operation the name of the operation that need access check.
   * @param array $params name-value pairs that would be passed to business rules associated
   * with the tasks and roles assigned to the user.
   * @param boolean $allowCaching whether to allow caching the result of access check.
   * @return boolean whether the operations can be performed by this user.
   */
  public function checkAccess($operation, $params = array(), $allowCaching = true)
  {
    if(is_array($operation))
    {
      foreach($operation as $op)
      if(!$this->checkAccess($op, $params, $allowCaching))
        return false;
      return true;
    }
     
    //if($operation == 'debug')logLine("checkAccess($operation,"._2str($this->getIsAdmin()).")");
    $accessOk = false;
    AuthItem::ensureExists($operation);
    if ($this->getIsAdmin())
      $accessOk = true;
    else
      $accessOk = parent::checkAccess($operation, $params, $allowCaching);
    return $accessOk;
  }

  /**
   * @return CAuthAssignment[]
   */
  public function getAuthAssignments()
  {
    if($this->isGuest)
      return array();
    return Yii::app()->getAuthManager()->getAuthAssignments($this->getId());
  }

  /**
   * @param string $like
   * @return bool
   */
  public function checkAccessLike($like)
  {
    if ($this->getIsAdmin())
      return true;

    foreach($this->getAllAssignments() as $item)
    if(fnmatch($like, $item->name))
      return true;
    return false;
  }

  /**
   * @return bool[]
   */
  public function getAccessRules()
  {
    $names = AuthItem::getAllNames('name');
    $accessRules = array();
    foreach($names as $name)
      $accessRules[$name] = $this->checkAccess($name);
    return $accessRules;
  }

  /**
   * @return AuthItem[]
   */
  public function getAllAssignments()
  {
    if ($this->isGuest)
      return array();
     
    $all = $this->getAuthAssignments();
    $names = new CQueue();
    foreach($all as $aa)
      $names->enqueue($aa->itemName);
     
    $assignments = array();
    while( $names->count() > 0 )
    {
      $itemName = $names->dequeue();
      $item = AuthItem::findItem($itemName);
      if($item != null)
        $assignments[] = $item;
       
      $childs = AuthItemChild::model()->findAll('parent=:prnt',array(':prnt'=>$itemName));
      foreach($childs as $chld)
        $names->enqueue($chld->child);
    }
     
    return $assignments;
  }

  public function getIsAuthenticated()
  {
    return !$this->isGuest;
  }

  /**
   * @return User
   */
  public function getModel()
  {
    if (!$this->isGuest)
      return User::model()->findByPk($this->id);
    return null;
  }
  /**
   * @return UserSettingsManager
   */
  public function getSettings()
  {
    $m = $this->getModel();
    if ($m != null)
      return $m->settings;
    return null;
  }

  /**
   * (non-PHPdoc)
   * @see CWebUser::changeIdentity()
   */
  protected function changeIdentity($id,$name,$states)
  {
    //logObj(arrayfy($id,$name,$states));
    return parent::changeIdentity($id,$name,$states);
  }

  public function impersonate($id,$username)
  {
    return $this->changeIdentity($id,$username,array());
  }

  /**
   * @param bool $delete
   * @return FlashMessage[]
   */
  public function getFlashes($delete=true)
  {
    $rawFlashes = parent::getFlashes($delete);
    $flashes = self::getFlashesFile($delete);
    foreach($rawFlashes as $content)
      $flashes[] = FlashMessage::deserialize($content);
    return $flashes;
  }

  /**
   * @param bool $delete
   * @return FlashMessage[]
   */
  public function getFlashesFile($delete)
  {
    $fileFlashes = array();

    // globali
    $flashPath = pathCombine(app()->runtimePath,'flashes','global');
    if(dirExists($flashPath))
    {
      foreach(getFiles($flashPath,"*.flash") as $file)
      {
      		$fileFlashes[] = $file;
      }
    }

    // per utente
    $flashPath = pathCombine(app()->runtimePath,'flashes','user_'.user()->id);
    if(dirExists($flashPath))
    {
      foreach(getFiles($flashPath,"*.flash") as $file)
      {
      		$fileFlashes[] = $file;
      }
    }

    // per ip
    $flashPath = pathCombine(app()->runtimePath,'flashes','ip_'.$_SERVER['REMOTE_ADDR']);
    if(dirExists($flashPath))
    {
      foreach(getFiles($flashPath,"*.flash") as $file)
      {
      		$fileFlashes[] = $file;
      }
    }

    $flashes = array();

    // per ogni file flash
    foreach($fileFlashes as $file)
    {
      if(fileExists($file))
      {
        $flash = FlashMessage::deserializeFile($file);
        $flashes[] = $flash;
        if($delete && !$flash->permanent)
     	    unlink($file);
      }
    }

    return $flashes;
  }

  /**
   *
   * @param string $key success notice error
   * @param string $value html code / text
   * @param string $target user/remote_host/global... (default 'user')
   * @param boolean $permanent (default false)
   * @return boolean
   */
  public function setFlashFile($key, $value, $target = 'user', $permanent = false)
  {
    $flash = new FlashMessage();

    $flash->content = $value;
    $flash->permanent = $permanent;
    $flash->type = FlashMessage::TYPE_FILE;
    $flash->level = $key;
    $flash->target = $target;

    return $this->setFlashEx($flash);
  }

}
