PDF rausgenommen
This commit is contained in:
199
msd2/tracking/piwik/core/Session/SaveHandler/DbTable.php
Normal file
199
msd2/tracking/piwik/core/Session/SaveHandler/DbTable.php
Normal file
@ -0,0 +1,199 @@
|
||||
<?php
|
||||
/**
|
||||
* Piwik - free/libre analytics platform
|
||||
*
|
||||
* @link http://piwik.org
|
||||
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
|
||||
*
|
||||
*/
|
||||
|
||||
namespace Piwik\Session\SaveHandler;
|
||||
|
||||
use Piwik\Common;
|
||||
use Piwik\Db;
|
||||
use Piwik\DbHelper;
|
||||
use Exception;
|
||||
use Piwik\Updater\Migration;
|
||||
use Zend_Session;
|
||||
use Zend_Session_SaveHandler_Interface;
|
||||
|
||||
/**
|
||||
* Database-backed session save handler
|
||||
*
|
||||
*/
|
||||
class DbTable implements Zend_Session_SaveHandler_Interface
|
||||
{
|
||||
protected $config;
|
||||
protected $maxLifetime;
|
||||
|
||||
const TABLE_NAME = 'session';
|
||||
|
||||
/**
|
||||
* @param array $config
|
||||
*/
|
||||
public function __construct($config)
|
||||
{
|
||||
$this->config = $config;
|
||||
$this->maxLifetime = ini_get('session.gc_maxlifetime');
|
||||
}
|
||||
|
||||
/**
|
||||
* Destructor
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __destruct()
|
||||
{
|
||||
Zend_Session::writeClose();
|
||||
}
|
||||
|
||||
/**
|
||||
* Open Session - retrieve resources
|
||||
*
|
||||
* @param string $save_path
|
||||
* @param string $name
|
||||
* @return boolean
|
||||
*/
|
||||
public function open($save_path, $name)
|
||||
{
|
||||
Db::get()->getConnection();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Close Session - free resources
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function close()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read session data
|
||||
*
|
||||
* @param string $id
|
||||
* @return string
|
||||
*/
|
||||
public function read($id)
|
||||
{
|
||||
$sql = 'SELECT ' . $this->config['dataColumn'] . ' FROM ' . $this->config['name']
|
||||
. ' WHERE ' . $this->config['primary'] . ' = ?'
|
||||
. ' AND ' . $this->config['modifiedColumn'] . ' + ' . $this->config['lifetimeColumn'] . ' >= ?';
|
||||
|
||||
$result = $this->fetchOne($sql, array($id, time()));
|
||||
|
||||
if (!$result) {
|
||||
$result = '';
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
private function fetchOne($sql, $bind)
|
||||
{
|
||||
try {
|
||||
$result = Db::get()->fetchOne($sql, $bind);
|
||||
} catch (Exception $e) {
|
||||
if (Db::get()->isErrNo($e, Migration\Db::ERROR_CODE_TABLE_NOT_EXISTS)) {
|
||||
$this->migrateToDbSessionTable();
|
||||
$result = Db::get()->fetchOne($sql, $bind);
|
||||
} else {
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
private function query($sql, $bind)
|
||||
{
|
||||
try {
|
||||
$result = Db::get()->query($sql, $bind);
|
||||
} catch (Exception $e) {
|
||||
if (Db::get()->isErrNo($e, Migration\Db::ERROR_CODE_TABLE_NOT_EXISTS)) {
|
||||
$this->migrateToDbSessionTable();
|
||||
$result = Db::get()->query($sql, $bind);
|
||||
} else {
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write Session - commit data to resource
|
||||
*
|
||||
* @param string $id
|
||||
* @param mixed $data
|
||||
* @return boolean
|
||||
*/
|
||||
public function write($id, $data)
|
||||
{
|
||||
$sql = 'INSERT INTO ' . $this->config['name']
|
||||
. ' (' . $this->config['primary'] . ','
|
||||
. $this->config['modifiedColumn'] . ','
|
||||
. $this->config['lifetimeColumn'] . ','
|
||||
. $this->config['dataColumn'] . ')'
|
||||
. ' VALUES (?,?,?,?)'
|
||||
. ' ON DUPLICATE KEY UPDATE '
|
||||
. $this->config['modifiedColumn'] . ' = ?,'
|
||||
. $this->config['lifetimeColumn'] . ' = ?,'
|
||||
. $this->config['dataColumn'] . ' = ?';
|
||||
|
||||
$this->query($sql, array($id, time(), $this->maxLifetime, $data, time(), $this->maxLifetime, $data));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroy Session - remove data from resource for
|
||||
* given session id
|
||||
*
|
||||
* @param string $id
|
||||
* @return boolean
|
||||
*/
|
||||
public function destroy($id)
|
||||
{
|
||||
$sql = 'DELETE FROM ' . $this->config['name'] . ' WHERE ' . $this->config['primary'] . ' = ?';
|
||||
|
||||
$this->query($sql, array($id));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Garbage Collection - remove old session data older
|
||||
* than $maxlifetime (in seconds)
|
||||
*
|
||||
* @param int $maxlifetime timestamp in seconds
|
||||
* @return bool always true
|
||||
*/
|
||||
public function gc($maxlifetime)
|
||||
{
|
||||
$sql = 'DELETE FROM ' . $this->config['name']
|
||||
. ' WHERE ' . $this->config['modifiedColumn'] . ' + ' . $this->config['lifetimeColumn'] . ' < ?';
|
||||
|
||||
$this->query($sql, array(time()));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private function migrateToDbSessionTable()
|
||||
{
|
||||
// happens when updating from Piwik 1.4 or earlier to Matomo 3.7+
|
||||
// in this case on update it will change the session handler to dbtable, but it hasn't performed
|
||||
// the DB updates just yet which means the session table won't be available as it was only added in
|
||||
// Piwik 1.5 => results in a sql error the session table does not exist
|
||||
try {
|
||||
$sql = DbHelper::getTableCreateSql(self::TABLE_NAME);
|
||||
Db::query($sql);
|
||||
} catch (Exception $e) {
|
||||
if (!Db::get()->isErrNo($e, Migration\Db::ERROR_CODE_TABLE_EXISTS)) {
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
189
msd2/tracking/piwik/core/Session/SessionAuth.php
Normal file
189
msd2/tracking/piwik/core/Session/SessionAuth.php
Normal file
@ -0,0 +1,189 @@
|
||||
<?php
|
||||
/**
|
||||
* Piwik - free/libre analytics platform
|
||||
*
|
||||
* @link http://piwik.org
|
||||
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
|
||||
*
|
||||
*/
|
||||
|
||||
namespace Piwik\Session;
|
||||
|
||||
use Piwik\Auth;
|
||||
use Piwik\AuthResult;
|
||||
use Piwik\Config;
|
||||
use Piwik\Date;
|
||||
use Piwik\Plugins\UsersManager\Model as UsersModel;
|
||||
use Piwik\Session;
|
||||
|
||||
/**
|
||||
* Validates already authenticated sessions.
|
||||
*
|
||||
* See {@link \Piwik\Session\SessionFingerprint} for more info.
|
||||
*/
|
||||
class SessionAuth implements Auth
|
||||
{
|
||||
/**
|
||||
* For tests, since there's no actual session there.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
private $shouldDestroySession;
|
||||
|
||||
/**
|
||||
* @var UsersModel
|
||||
*/
|
||||
private $userModel;
|
||||
|
||||
/**
|
||||
* Set internally so it can be queried in FrontController.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $user;
|
||||
|
||||
public function __construct(UsersModel $userModel = null, $shouldDestroySession = true)
|
||||
{
|
||||
$this->userModel = $userModel ?: new UsersModel();
|
||||
$this->shouldDestroySession = $shouldDestroySession;
|
||||
}
|
||||
|
||||
public function getName()
|
||||
{
|
||||
// empty
|
||||
}
|
||||
|
||||
public function setTokenAuth($token_auth)
|
||||
{
|
||||
// empty
|
||||
}
|
||||
|
||||
public function getLogin()
|
||||
{
|
||||
return $this->user['login'];
|
||||
}
|
||||
|
||||
public function getTokenAuthSecret()
|
||||
{
|
||||
// empty
|
||||
}
|
||||
|
||||
public function setLogin($login)
|
||||
{
|
||||
// empty
|
||||
}
|
||||
|
||||
public function setPassword($password)
|
||||
{
|
||||
// empty
|
||||
}
|
||||
|
||||
public function setPasswordHash($passwordHash)
|
||||
{
|
||||
// empty
|
||||
}
|
||||
|
||||
public function authenticate()
|
||||
{
|
||||
$sessionFingerprint = new SessionFingerprint();
|
||||
$userModel = $this->userModel;
|
||||
|
||||
$userForSession = $sessionFingerprint->getUser();
|
||||
if (empty($userForSession)) {
|
||||
return $this->makeAuthFailure();
|
||||
}
|
||||
|
||||
$user = $userModel->getUser($userForSession);
|
||||
if (empty($user)
|
||||
|| $user['login'] !== $userForSession // sanity check in case there's a bug in getUser()
|
||||
) {
|
||||
return $this->makeAuthFailure();
|
||||
}
|
||||
|
||||
$tsPasswordModified = !empty($user['ts_password_modified']) ? $user['ts_password_modified'] : null;
|
||||
if ($this->isSessionStartedBeforePasswordChange($sessionFingerprint, $tsPasswordModified)) {
|
||||
$this->destroyCurrentSession($sessionFingerprint);
|
||||
return $this->makeAuthFailure();
|
||||
}
|
||||
|
||||
if ($sessionFingerprint->isRemembered()) {
|
||||
$this->updateSessionExpireTime();
|
||||
}
|
||||
|
||||
return $this->makeAuthSuccess($user);
|
||||
}
|
||||
|
||||
private function isSessionStartedBeforePasswordChange(SessionFingerprint $sessionFingerprint, $tsPasswordModified)
|
||||
{
|
||||
// sanity check, make sure users can still login if the ts_password_modified column does not exist
|
||||
if ($tsPasswordModified === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// if the session start time doesn't exist for some reason, log the user out
|
||||
$sessionStartTime = $sessionFingerprint->getSessionStartTime();
|
||||
if (empty($sessionStartTime)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return $sessionStartTime < Date::factory($tsPasswordModified)->getTimestampUTC();
|
||||
}
|
||||
|
||||
private function makeAuthFailure()
|
||||
{
|
||||
return new AuthResult(AuthResult::FAILURE, null, null);
|
||||
}
|
||||
|
||||
private function makeAuthSuccess($user)
|
||||
{
|
||||
$this->user = $user;
|
||||
|
||||
$isSuperUser = (int) $user['superuser_access'];
|
||||
$code = $isSuperUser ? AuthResult::SUCCESS_SUPERUSER_AUTH_CODE : AuthResult::SUCCESS;
|
||||
|
||||
return new AuthResult($code, $user['login'], $user['token_auth']);
|
||||
}
|
||||
|
||||
protected function initNewBlankSession(SessionFingerprint $sessionFingerprint)
|
||||
{
|
||||
// this user should be using a different session, so generate a new ID
|
||||
// NOTE: Zend_Session cannot be used since it will destroy the old
|
||||
// session.
|
||||
if ($this->shouldDestroySession) {
|
||||
session_regenerate_id();
|
||||
}
|
||||
|
||||
// regenerating the ID will create a new session w/ a new ID, but will
|
||||
// copy over the existing session data. we want the new session for the
|
||||
// unauthorized user to be different, so we clear the session fingerprint.
|
||||
$sessionFingerprint->clear();
|
||||
}
|
||||
|
||||
protected function destroyCurrentSession(SessionFingerprint $sessionFingerprint)
|
||||
{
|
||||
// Note: Piwik will attempt to create another session in the LoginController
|
||||
// when rendering the login form (the nonce for the form is stored in the session).
|
||||
// So we can't use Session::destroy() since Zend prohibits starting a new session
|
||||
// after session_destroy() is called. Instead we clear the session fingerprint for
|
||||
// the existing session and generate a new session. Both the old session &
|
||||
// new session should have no stored data.
|
||||
$sessionFingerprint->clear();
|
||||
if ($this->shouldDestroySession) {
|
||||
Session::regenerateId();
|
||||
}
|
||||
}
|
||||
|
||||
public function getTokenAuth()
|
||||
{
|
||||
return $this->user['token_auth'];
|
||||
}
|
||||
|
||||
private function updateSessionExpireTime()
|
||||
{
|
||||
$sessionParams = session_get_cookie_params();
|
||||
|
||||
$sessionCookieLifetime = Config::getInstance()->General['login_cookie_expire'];
|
||||
setcookie(session_name(), session_id(), time() + $sessionCookieLifetime, $sessionParams['path'],
|
||||
$sessionParams['domain'], $sessionParams['secure'], $sessionParams['httponly']);
|
||||
}
|
||||
}
|
108
msd2/tracking/piwik/core/Session/SessionFingerprint.php
Normal file
108
msd2/tracking/piwik/core/Session/SessionFingerprint.php
Normal file
@ -0,0 +1,108 @@
|
||||
<?php
|
||||
/**
|
||||
* Piwik - free/libre analytics platform
|
||||
*
|
||||
* @link http://piwik.org
|
||||
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
|
||||
*
|
||||
*/
|
||||
|
||||
namespace Piwik\Session;
|
||||
|
||||
use Piwik\Date;
|
||||
|
||||
/**
|
||||
* Manages session information that is used to identify who the session
|
||||
* is for.
|
||||
*
|
||||
* Once a session is authenticated using either a user name & password or
|
||||
* token auth, some information about the user is stored in the session.
|
||||
* This info includes the user name and the user agent
|
||||
* string of the user's client, and a random session secret.
|
||||
*
|
||||
* In subsequent requests that use this session, we use the above information
|
||||
* to verify that the session is allowed to be used by the person sending the
|
||||
* request.
|
||||
*
|
||||
* This is accomplished by checking the request's user agent
|
||||
* against what is stored in the session. If it doesn't then this is a
|
||||
* session hijacking attempt.
|
||||
*
|
||||
* We also check that a hash in the piwik_auth cookie matches the hash
|
||||
* of the time the user last changed their password + the session secret.
|
||||
* If they don't match, the password has been changed since this session
|
||||
* started, and is no longer valid.
|
||||
*/
|
||||
class SessionFingerprint
|
||||
{
|
||||
const USER_NAME_SESSION_VAR_NAME = 'user.name';
|
||||
const SESSION_INFO_SESSION_VAR_NAME = 'session.info';
|
||||
const SESSION_INFO_TWO_FACTOR_AUTH_VERIFIED = 'twofactorauth.verified';
|
||||
|
||||
public function getUser()
|
||||
{
|
||||
if (isset($_SESSION[self::USER_NAME_SESSION_VAR_NAME])) {
|
||||
return $_SESSION[self::USER_NAME_SESSION_VAR_NAME];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public function getUserInfo()
|
||||
{
|
||||
if (isset($_SESSION[self::SESSION_INFO_SESSION_VAR_NAME])) {
|
||||
return $_SESSION[self::SESSION_INFO_SESSION_VAR_NAME];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public function hasVerifiedTwoFactor()
|
||||
{
|
||||
if (isset($_SESSION[self::SESSION_INFO_TWO_FACTOR_AUTH_VERIFIED])) {
|
||||
return !empty($_SESSION[self::SESSION_INFO_TWO_FACTOR_AUTH_VERIFIED]);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public function setTwoFactorAuthenticationVerified()
|
||||
{
|
||||
$_SESSION[self::SESSION_INFO_TWO_FACTOR_AUTH_VERIFIED] = 1;
|
||||
}
|
||||
|
||||
public function initialize($userName, $isRemembered = false, $time = null)
|
||||
{
|
||||
$_SESSION[self::USER_NAME_SESSION_VAR_NAME] = $userName;
|
||||
$_SESSION[self::SESSION_INFO_TWO_FACTOR_AUTH_VERIFIED] = 0;
|
||||
$_SESSION[self::SESSION_INFO_SESSION_VAR_NAME] = [
|
||||
'ts' => $time ?: Date::now()->getTimestampUTC(),
|
||||
'remembered' => $isRemembered,
|
||||
];
|
||||
}
|
||||
|
||||
public function clear()
|
||||
{
|
||||
unset($_SESSION[self::USER_NAME_SESSION_VAR_NAME]);
|
||||
unset($_SESSION[self::SESSION_INFO_SESSION_VAR_NAME]);
|
||||
unset($_SESSION[self::SESSION_INFO_TWO_FACTOR_AUTH_VERIFIED]);
|
||||
}
|
||||
|
||||
public function getSessionStartTime()
|
||||
{
|
||||
$userInfo = $this->getUserInfo();
|
||||
if (empty($userInfo)
|
||||
|| empty($userInfo['ts'])
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $userInfo['ts'];
|
||||
}
|
||||
|
||||
public function isRemembered()
|
||||
{
|
||||
$userInfo = $this->getUserInfo();
|
||||
return !empty($userInfo['remembered']);
|
||||
}
|
||||
}
|
117
msd2/tracking/piwik/core/Session/SessionInitializer.php
Normal file
117
msd2/tracking/piwik/core/Session/SessionInitializer.php
Normal file
@ -0,0 +1,117 @@
|
||||
<?php
|
||||
/**
|
||||
* Piwik - free/libre analytics platform
|
||||
*
|
||||
* @link http://piwik.org
|
||||
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
|
||||
*
|
||||
*/
|
||||
namespace Piwik\Session;
|
||||
|
||||
use Exception;
|
||||
use Piwik\Auth as AuthInterface;
|
||||
use Piwik\AuthResult;
|
||||
use Piwik\Piwik;
|
||||
use Piwik\Session;
|
||||
|
||||
/**
|
||||
* Initializes authenticated sessions using an Auth implementation.
|
||||
*/
|
||||
class SessionInitializer
|
||||
{
|
||||
/**
|
||||
* Authenticates the user and, if successful, initializes an authenticated session.
|
||||
*
|
||||
* @param \Piwik\Auth $auth The Auth implementation to use.
|
||||
* @throws Exception If authentication fails or the user is not allowed to login for some reason.
|
||||
*/
|
||||
public function initSession(AuthInterface $auth)
|
||||
{
|
||||
$this->regenerateSessionId();
|
||||
|
||||
$authResult = $this->doAuthenticateSession($auth);
|
||||
|
||||
if (!$authResult->wasAuthenticationSuccessful()) {
|
||||
|
||||
Piwik::postEvent('Login.authenticate.failed', array($auth->getLogin()));
|
||||
|
||||
$this->processFailedSession();
|
||||
} else {
|
||||
|
||||
Piwik::postEvent('Login.authenticate.successful', array($auth->getLogin()));
|
||||
|
||||
$this->processSuccessfulSession($authResult);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Authenticates the user.
|
||||
*
|
||||
* Derived classes can override this method to customize authentication logic or impose
|
||||
* extra requirements on the user trying to login.
|
||||
*
|
||||
* @param AuthInterface $auth The Auth implementation to use when authenticating.
|
||||
* @return AuthResult
|
||||
*/
|
||||
protected function doAuthenticateSession(AuthInterface $auth)
|
||||
{
|
||||
Piwik::postEvent(
|
||||
'Login.authenticate',
|
||||
array(
|
||||
$auth->getLogin(),
|
||||
)
|
||||
);
|
||||
|
||||
return $auth->authenticate();
|
||||
}
|
||||
|
||||
/**
|
||||
* Executed when the session could not authenticate.
|
||||
*
|
||||
* @throws Exception always.
|
||||
*/
|
||||
protected function processFailedSession()
|
||||
{
|
||||
throw new Exception(Piwik::translate('Login_LoginPasswordNotCorrect'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Executed when the session was successfully authenticated.
|
||||
*
|
||||
* @param AuthResult $authResult The successful authentication result.
|
||||
*/
|
||||
protected function processSuccessfulSession(AuthResult $authResult)
|
||||
{
|
||||
$sessionIdentifier = new SessionFingerprint();
|
||||
$sessionIdentifier->initialize($authResult->getIdentity(), $this->isRemembered());
|
||||
|
||||
/**
|
||||
* @ignore
|
||||
*/
|
||||
Piwik::postEvent('Login.authenticate.processSuccessfulSession.end', array($authResult->getIdentity()));
|
||||
}
|
||||
|
||||
protected function regenerateSessionId()
|
||||
{
|
||||
Session::regenerateId();
|
||||
}
|
||||
|
||||
/**
|
||||
* Accessor to compute the hashed authentication token.
|
||||
*
|
||||
* @param string $login user login
|
||||
* @param string $token_auth authentication token
|
||||
* @return string hashed authentication token
|
||||
* @deprecated
|
||||
*/
|
||||
public static function getHashTokenAuth($login, $token_auth)
|
||||
{
|
||||
return md5($login . $token_auth);
|
||||
}
|
||||
|
||||
private function isRemembered()
|
||||
{
|
||||
$cookieParams = session_get_cookie_params();
|
||||
return $cookieParams['lifetime'] > 0;
|
||||
}
|
||||
}
|
37
msd2/tracking/piwik/core/Session/SessionNamespace.php
Normal file
37
msd2/tracking/piwik/core/Session/SessionNamespace.php
Normal file
@ -0,0 +1,37 @@
|
||||
<?php
|
||||
/**
|
||||
* Piwik - free/libre analytics platform
|
||||
*
|
||||
* @link http://piwik.org
|
||||
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
|
||||
*
|
||||
*/
|
||||
namespace Piwik\Session;
|
||||
|
||||
use Piwik\Common;
|
||||
use Piwik\Session;
|
||||
use Zend_Session_Namespace;
|
||||
|
||||
/**
|
||||
* Session namespace.
|
||||
*
|
||||
*/
|
||||
class SessionNamespace extends Zend_Session_Namespace
|
||||
{
|
||||
/**
|
||||
* @param string $namespace
|
||||
* @param bool $singleInstance
|
||||
*/
|
||||
public function __construct($namespace = 'Default', $singleInstance = false)
|
||||
{
|
||||
if (Common::isPhpCliMode()) {
|
||||
self::$_readable = true;
|
||||
self::$_writable = true;
|
||||
return;
|
||||
}
|
||||
|
||||
Session::start();
|
||||
|
||||
parent::__construct($namespace, $singleInstance);
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user