最終ログイン時間から○分過ぎたら勝手にログアウトするようにする

phpのセッションタイムアウトは正確にその時間にセッションが切れるわけではないので、その部分を自作してみた。

<?php
/**
 * original auth component
 *
 */

/**
 * ログイン制限時間(s) ※設定していない場合はcakeのSecurity.levelに応じる
 */
define('LOGIN_LIMIT_TIME',30 * 60);

App::import('Component', 'Auth');

class AppAuthComponent extends AuthComponent {

	var $name = 'AppAuth';

	/**
	 * override AuthComponent::startup
	 *
	 * 
	 */
	public function startup(&$controller) {
		$isErrorOrTests = (
			strtolower($controller->name) == 'cakeerror' ||
			(strtolower($controller->name) == 'tests' && Configure::read() > 0)
		);
		if ($isErrorOrTests) {
			return true;
		}

		$methods = array_flip($controller->methods);
		$action = strtolower($controller->params['action']);
		$isMissingAction = (
			$controller->scaffold === false &&
			!isset($methods[$action])
		);

		if ($isMissingAction) {
			return true;
		}

		if (!$this->__setDefaults()) {
			return false;
		}

		$this->data = $controller->data = $this->hashPasswords($controller->data);
		$url = '';

		if (isset($controller->params['url']['url'])) {
			$url = $controller->params['url']['url'];
		}
		$url = Router::normalize($url);
		$loginAction = Router::normalize($this->loginAction);

		$allowedActions = array_map('strtolower', $this->allowedActions);
		$isAllowed = (
			$this->allowedActions == array('*') ||
			in_array($action, $allowedActions)
		);

		if ($loginAction != $url && $isAllowed) {
			return true;
		}

		if ($loginAction == $url) {
			$model =& $this->getModel();

			if (empty($controller->data) || !isset($controller->data[$model->alias])) {
				if (!$this->Session->check('Auth.redirect') && !$this->loginRedirect && env('HTTP_REFERER')) {
					$this->Session->write('Auth.redirect', $controller->referer(null, true));
				}
				return false;
			}

			$isValid = !empty($controller->data[$model->alias][$this->fields['username']]) &&
				!empty($controller->data[$model->alias][$this->fields['password']]);
			if ($isValid) {
				$username = $controller->data[$model->alias][$this->fields['username']];
				$password = $controller->data[$model->alias][$this->fields['password']];

				$data = array(
					$model->alias . '.' . $this->fields['username'] => $username,
					$model->alias . '.' . $this->fields['password'] => $password
				);

				if ($this->login($data)) {
					if ($this->autoRedirect) {
						$controller->redirect($this->redirect(), null, true);
					}
					return true;
				}
			}
			$this->Session->setFlash($this->loginError, $this->flashElement, array(), 'auth');
			$controller->data[$model->alias][$this->fields['password']] = null;
			return false;
		} else {
			$this->__disableConnectLogin();

			if (!$this->user()) {
				if (!$this->RequestHandler->isAjax()) {
					$this->Session->setFlash($this->authError, $this->flashElement, array(), 'auth');
					if (!empty($controller->params['url']) && count($controller->params['url']) >= 2) {
						$query = $controller->params['url'];
						unset($query['url'], $query['ext']);
						$url .= Router::queryString($query, array());
					}
					$this->Session->write('Auth.redirect', $url);
					$controller->redirect($loginAction);
					return false;
				} elseif (!empty($this->ajaxLogin)) {
					$controller->viewPath = 'elements';
					echo $controller->render($this->ajaxLogin, $this->RequestHandler->ajaxLayout);
					$this->_stop();
					return false;
				} else {
					$controller->redirect(null, 403);
				}
			}
		}
		if (!$this->authorize) {
			return true;
		}

		extract($this->__authType());
		switch ($type) {
			case 'controller':
				$this->object =& $controller;
			break;
			case 'crud':
			case 'actions':
				if (isset($controller->Acl)) {
					$this->Acl =& $controller->Acl;
				} else {
					trigger_error(__('Could not find AclComponent. Please include Acl in Controller::$components.', true), E_USER_WARNING);
				}
			break;
			case 'model':
				if (!isset($object)) {
					$hasModel = (
						isset($controller->{$controller->modelClass}) &&
						is_object($controller->{$controller->modelClass})
					);
					$isUses = (
						!empty($controller->uses) && isset($controller->{$controller->uses[0]}) &&
						is_object($controller->{$controller->uses[0]})
					);

					if ($hasModel) {
						$object = $controller->modelClass;
					} elseif ($isUses) {
						$object = $controller->uses[0];
					}
				}
				$type = array('model' => $object);
			break;
		}

		if ($this->isAuthorized($type)) {
			return true;
		}

		$this->Session->setFlash($this->authError, $this->flashElement, array(), 'auth');
		$controller->redirect($controller->referer(), null, true);
		return false;
	}

	/**
	 * override AuthComponent::login
	 *
	 * ・ログイン時間を設定することで正しくシャットダウンできるようにする
	 */
	public function login($data = null) {
		$this->__setDefaults();
		$this->_loggedIn = false;

		if (empty($data)) {
			$data = $this->data;
		}
		if ($user = $this->identify($data)) {
			// ログイン時間の登録
			$user['login_time'] = time();
			$this->Session->write($this->sessionKey, $user);
			$this->_loggedIn = true;
		}
		return $this->_loggedIn;
	}


	/**
	 * アクセスから○時間後のログイン判定を正確に行う
	 *   ・LOGIN_LIMIT_TIMEを設定していない場合はcakeのSecurity.levelに応じる
	 *   ・接続時間内の場合は接続制限時間を更新する
	 */
	private function __disableConnectLogin() {
		$loginSession = $this->Session->read($this->sessionKey);

		$loginLimitTime = 0;
		if (!defined('LOGIN_LIMIT_TIME')) {
			$loginLimitTime = Configure::read('Session.timeout') * (Security::inactiveMins() * 60);
		}
		else {
			$loginLimitTime = LOGIN_LIMIT_TIME;
		}
		if (($loginSession['login_time'] + $loginLimitTime) < BASE_TIME) {
			$this->Session->delete($this->sessionKey);
		}
		else {
			$loginSession['login_time'] = BASE_TIME;
			$this->Session->write($this->sessionKey,$loginSession);
		}
	}
}
?>

基本的な部分はAuthComponentに任せて、時間判定の部分のみオーバーライドしてみた。
一応期待通りの動作はしているので問題ないかな。