pws-auth/src/MetaTech/PwsAuth/Authenticator.php

324 lines
9.4 KiB
PHP

<?php
/*
* This file is part of the pws-auth package.
*
* (c) meta-tech.academy
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace MetaTech\PwsAuth;
use MetaTech\Util\Tool;
use MetaTech\PwsAuth\Token;
use MetaTech\PwsAuth\AuthenticateException;
/*!
* a simple class to authenticate access throught webservices using Pluie\Auth\Token
* and PwsAuth Protocol
*
* @package MetaTech\PwsAuth
* @class Authenticator
* @author a-Sansara
* @date 2016-05-02 13:08:01 CET
*
*/
class Authenticator
{
/*! @constant DATE_FORMAT */
const DATE_FORMAT = 'smHdiy';
/*! @constant DATE_LENGTH */
const DATE_LENGTH = 12;
/*! @constant DATE_LENGTH */
const DEFAULT_ALGO = 'sha256';
/*! @protected @var [assoc] $config */
protected $config;
/*!
* @constructor
* @public
*/
public function __construct($config)
{
$this->config = $config;
}
/*!
* check if specified Token is a valid token
*
* @method isValid
* @public
* @param MetaTech\PwsAtuh\Token $token
* @return bool
*/
public function isValid(Token $token = null)
{
return !is_null($token) && $token->getType() == $this->config['type'] && $this->checkObfuscatePart($token);
}
/*!
* generate a unique signature at given time for specifyed user
*
* @method sign
* @public
* @param str $dtime given time in sqldatetime format
* @param str $login the user login
* @param str $key the user key
* @return str
*/
public function sign($dtime, $login, $key, $length=null)
{
$str = Tool::concat($this->config['hash']['sep'], [$dtime, $login, $this->getUserSalt($login), $key]);
return substr(hash($this->config['hash']['algo'], $str), is_null($length) ? - $this->config['hash']['length'] : - $length);
}
/*!
* generate the salt for a specific user
*
* @method getUserSalt
* @public
* @param str $login the user login
* @return str
*/
public function getUserSalt($login)
{
return substr(
hash(self::DEFAULT_ALGO, $login . $this->config['salt']['common']),
$this->config['salt']['user.index'],
$this->config['salt']['user.length']
);
}
/*!
* generate noise to obfuscate token
*
* @method obfuscate
* @orivate
* @param str $data
* @return str
*/
private function obfuscate($data, $date)
{
return substr(
hash(self::DEFAULT_ALGO, $date . $data . $this->config['salt']['common']),
- $this->config['hash']['session.index']
);
}
/*!
* @method formatDate
* @private
* @param str $date sqldatetime
* @return str
*/
private function formatDate($date)
{
return Tool::formatDate($date, Tool::TIMESTAMP_SQLDATETIME, self::DATE_FORMAT);
}
/*!
* @method formatDate
* @private
* @param str $formated DATE_FORMAT
* @return str
*/
private function unformatDate($formated)
{
return Tool::formatDate($formated, self::DATE_FORMAT, Tool::TIMESTAMP_SQLDATETIME);
}
/*!
* check valid noise obfuscation
*
* @method checkObfuscatePart
* @public
* @param MetaTech\PwsAtuh\Token $token
* @return bool
*/
public function checkObfuscatePart(Token $token)
{
$tokenValue = $token->getValue();
return substr($tokenValue, 0, $this->config['hash']['session.index']) == $this->obfuscate($this->deobfuscate($tokenValue), $token->getDate());
}
/*!
* deoffuscate token
*
* @method deobfuscate
* @orivate
* @param str $data
* @return str
*/
private function deobfuscate($data)
{
return substr($data, $this->config['hash']['session.index']);
}
/*!
* @method getSessionId
* @orivate
* @param MetaTech\PwsAtuh\Token $token
* @return str
*/
public function getSessionId(Token $token)
{
return $this->deobfuscate($token->getValue());
}
/*!
* check validity of Token
*
* @mehtod check
* @public
* @param MetaTech\PwsAtuh\Token $token
* @param str $login
* @return bool
*/
public function check(Token $token = null, $login = '')
{
return !is_null($token) && !empty($login) && $this->deobfuscate($token->getValue()) == $this->sign($token->getDate(), $login, $token->getIdent());
}
/*!
* @method generateNoise
* @public
* @param str $data
* @return str
*/
public function generateNoise($data)
{
return substr(hash(self::DEFAULT_ALGO, str_shuffle($data)), - $this->config['hash']['noise.length']);
}
/*!
* @method generateToken
* @public
* @param str $login
* @param str $key
* @param str $sessid|null
* @return MetaTech\PwsAuth\Token
*/
public function generateToken($login, $key, $sessid=null)
{
$date = Tool::now();
$sessid = is_null($sessid) ? $this->sign($date, $login, $key) : $sessid;
$dt = $this->formatDate($date);
$tokenValue = $this->obfuscate($sessid, $date) . $sessid;
$noise = $this->generateNoise($tokenValue);
return new Token($this->config['type'], $key, $date, $tokenValue, $noise);
}
/*!
* @method generateHeader
* @public
* @param str $login
* @param str $key
* @param str $sessid
* @return []
*/
public function generateHeader($login, $key, $sessid=null)
{
$token = $this->generateToken($login, $key, $sessid);
$ndate = $this->formatDate($token->getDate());
return array(
$this->config['header']['auth'] .': ' . $token->getType() . ' ' . $ndate . $token->getValue() . $token->getNoise(),
$this->config['header']['ident'].': ' . $token->getIdent()
);
}
/*!
* @method generatePostVars
* @public
* @param str $login
* @param str $key
* @param str $tokenName
* @param str $keyName
* @return []
*/
public function generatePostVars($login, $key, $tokenName='apitkn', $keyName='apikey')
{
$token = $this->generateToken($login, $key, null);
$ndate = $this->formatDate($token->getDate());
return array(
$tokenName => $ndate . $token->getValue() . $token->getNoise(),
$keyName => $key
);
}
/*!
* get token from specified $noisedToken for specified key.
*
* @method getTokenFromString
* @public
* @param str $noisedToken
* @param str $key
* @return MetaTech\PwsAuth\Token
*/
public function getTokenFromString($noisedToken, $key)
{
$date = substr($noisedToken, 0, self::DATE_LENGTH);
$tokenValue = substr($noisedToken, self::DATE_LENGTH, -$this->config['hash']['noise.length']);
$noise = substr($noisedToken, -$this->config['hash']['noise.length']);
return new Token($this->config['type'], $key, $this->unformatDate($date), $tokenValue, $noise);
}
/*!
* get token from specified $header or request headers.
*
* @method getToken
* @public
* @param [assoc] $headers
* @throw MetaTech\PwsAuth\AuthenticateException
* @return MetaTech\PwsAuth\Token
*/
public function getToken($headers = null)
{
$token = null;
try {
if (is_null($headers)) {
$headers = apache_request_headers();
}
if (isset($headers[$this->config['header']['auth']]) && isset($headers[$this->config['header']['ident']])) {
$tokenValue = $headers[$this->config['header']['auth']];
$ident = $headers[$this->config['header']['ident']];
if (preg_match('/(?P<type>[a-z\d]+) (?P<noised>.*)/i', $tokenValue, $rs)) {
$token = $this->getTokenFromString($rs['noised'], $ident);
if ($token->getType() != $rs['type']) {
throw new \Exception('wrong type');
}
}
}
else {
throw new \Exception('missing required headers');
}
}
catch(\Exception $e) {
throw new AuthenticateException("invalid authentication protocol : ".$e->getMessage());
}
return $token;
}
/*!
* read header generate by generateHeader
*
* @method readHeader
* @public
* @param [str] $arrHeaders
* @return [assoc]
*/
public function readHeader($arrHeaders)
{
$headers = [];
if (is_array($arrHeaders)) {
foreach($arrHeaders as $h) {
$rs = preg_split('/:/', $h);
if (count($rs)==2) {
$headers[$rs[0]] = trim($rs[1]);
}
}
}
return $headers;
}
}