blob: 391bec508dffd6d8e42ce62915825f7a5ee74819 [file] [log] [blame]
<?php
/**
* File containing the ezcAuthenticationSession class.
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
* @license http://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0
* @filesource
* @package Authentication
* @version //autogen//
*/
/**
* Support for session authentication and saving of authentication information
* between requests.
*
* Contains the methods:
* - start - starts the session, calling the PHP function session_start()
* - load - returns the information stored in the session key ezcAuth_id
* - save - saves information in the session key ezcAuth_id and also saves
* the current timestamp in the session key ezcAuth_timestamp
* - destroy - deletes the information stored in the session keys ezcAuth_id
* and ezcAuth_timestamp
* - regenerateId - regenerates the PHPSESSID value
*
* Example of use (combined with the Htpasswd filter):
* <code>
* // no headers should be sent before calling $session->start()
* $session = new ezcAuthenticationSession();
* $session->start();
*
* // retrieve the POST request information
* $user = isset( $_POST['user'] ) ? $_POST['user'] : $session->load();
* $password = isset( $_POST['password'] ) ? $_POST['password'] : null;
* $credentials = new ezcAuthenticationPasswordCredentials( $user, $password );
* $authentication = new ezcAuthentication( $credentials );
* $authentication->session = $session;
* $authentication->addFilter( new ezcAuthenticationHtpasswdFilter( '/etc/htpasswd' ) );
* // add other filters if needed
* if ( !$authentication->run() )
* {
* // authentication did not succeed, so inform the user
* $status = $authentication->getStatus();
* $err = array(
* 'ezcAuthenticationHtpasswdFilter' => array(
* ezcAuthenticationHtpasswdFilter::STATUS_USERNAME_INCORRECT => 'Incorrect username',
* ezcAuthenticationHtpasswdFilter::STATUS_PASSWORD_INCORRECT => 'Incorrect password'
* ),
* 'ezcAuthenticationSession' => array(
* ezcAuthenticationSession::STATUS_EMPTY => '',
* ezcAuthenticationSession::STATUS_EXPIRED => 'Session expired'
* )
* );
* foreach ( $status as $line )
* {
* list( $key, $value ) = each( $line );
* echo $err[$key][$value] . "\n";
* }
* }
* else
* {
* // authentication succeeded, so allow the user to see his content
* }
* </code>
*
* See {@link ezcAuthenticationSessionOptions} for options you can set to
* session objects.
*
* @package Authentication
* @version //autogen//
* @mainclass
*/
class ezcAuthenticationSession
{
/**
* Successful authentication; normal behaviour is to skip the other filters.
*
* This should be the same value as ezcAuthenticationFilter::STATUS_OK.
*/
const STATUS_OK = 0;
/**
* The session is empty; normal behaviour is to continue with the other filters.
*/
const STATUS_EMPTY = 1;
/**
* The session expired; normal behaviour is to regenerate the session ID.
*/
const STATUS_EXPIRED = 2;
/**
* Options for authentication filters.
*
* @var ezcAuthenticationFilterOptions
*/
protected $options;
/**
* Creates a new object of this class.
*
* @param ezcAuthenticationSessionOptions $options Options for this class
*/
public function __construct( ezcAuthenticationSessionOptions $options = null )
{
$this->options = ( $options === null ) ? new ezcAuthenticationSessionOptions() : $options;
}
/**
* Runs through the session and returns a status code when finished.
*
* @param ezcAuthenticationCredentials $credentials Authentication credentials
* @return int
*/
public function run( $credentials )
{
$this->start();
$now = $_SERVER["REQUEST_TIME"];
// inactivity
if ( isset( $_SESSION[$this->options->lastActivityTimestampKey] ) &&
$now - $_SESSION[$this->options->lastActivityTimestampKey] >= $this->options->idleTimeout
)
{
$this->destroy();
$this->regenerateId();
return self::STATUS_EXPIRED;
}
// max session timeout
if ( isset( $_SESSION[$this->options->timestampKey] ) &&
$now - $_SESSION[$this->options->timestampKey] >= $this->options->validity
)
{
$this->destroy();
$this->regenerateId();
return self::STATUS_EXPIRED;
}
if ( $this->load() !== null )
{
$_SESSION[$this->options->lastActivityTimestampKey] = time();
return self::STATUS_OK;
}
return self::STATUS_EMPTY;
}
/**
* Runs through the session and returns true if the session is correct.
*
* When using the session, it is often desirable to take advantage of the
* fact that the authenticated state of the user is kept in the session and
* not create and initialize the other filters (which might slow things
* down on every request).
*
* The application can be structured like this:
* <code>
* $session = new ezcAuthenticationSession();
* $session->start();
*
* $credentials = new ezcAuthenticationPasswordCredentials( $user, $pass );
*
* $authenticated = false;
* if ( !$session->isValid( $credentials ) )
* {
* // create the authentication object
* $authentication = new ezcAuthentication( $credentials );
* $authentication->session = $session;
*
* // create filters and add them to the authentication object
* $authentication->addFilter( new ezcAuthenticationOpenidFilter() );
*
* // run the authentication object
* if ( !$authentication->run() )
* {
* $status = $authentication->getStatus();
* // build an error message based on $status
* }
* else
* {
* $authenticated = true;
* }
* }
* else
* {
* $authenticated = true;
* }
*
* if ( $authenticated )
* {
* // the authentication succeeded and the user can see his content
* }
* else
* {
* // inform the user that the authentication failed (with the error
* // message that was created earlier)
* }
* </code>
*
* In this way, the creation and initialization of the authentication
* filters is not performed if the credentials are stored in the session.
*
* @param ezcAuthenticationCredentials $credentials Authentication credentials
* @return bool
*/
public function isValid( $credentials )
{
return ( $this->run( $credentials ) === self::STATUS_OK );
}
/**
* Starts the session.
*
* This function must be called before sending any headers to the client.
*/
public function start()
{
if ( session_id() === '' && PHP_SAPI !== 'cli' )
{
session_start();
}
}
/**
* Loads the authenticated username from the session or null if it doesn't exist.
*
* @return string
*/
public function load()
{
return isset( $_SESSION[$this->options->idKey] ) ? $_SESSION[$this->options->idKey] :
null;
}
/**
* Saves the authenticated username and the current timestamp in the session
* variables.
*
* @param string $data Information to save in the session, usually username
*/
public function save( $data )
{
$_SESSION[$this->options->idKey] = $data;
$_SESSION[$this->options->timestampKey] = time();
$_SESSION[$this->options->lastActivityTimestampKey] = time();
}
/**
* Removes the variables used by this class from the session variables.
*/
public function destroy()
{
unset( $_SESSION[$this->options->idKey] );
unset( $_SESSION[$this->options->timestampKey] );
unset( $_SESSION[$this->options->lastActivityTimestampKey] );
}
/**
* Regenerates the session ID.
*/
public function regenerateId()
{
if ( !headers_sent() )
{
// ???? seems that PHPSESSID is not regenerated if session is destroyed first????
// session_destroy();
session_regenerate_id();
}
}
/**
* Sets the options of this class to $options.
*
* @param ezcAuthenticationSessionOptions $options Options for this class
*/
public function setOptions( ezcAuthenticationSessionOptions $options )
{
$this->options = $options;
}
/**
* Returns the options of this class.
*
* @return ezcAuthenticationSessionOptions
*/
public function getOptions()
{
return $this->options;
}
}
?>