Rewrite of the authentication and authorization system

This commit is contained in:
Frederic Guillot
2015-12-05 20:31:27 -05:00
parent 346b8312e5
commit e9fedf3e5c
255 changed files with 14114 additions and 9820 deletions

121
app/Core/Http/OAuth2.php Normal file
View File

@@ -0,0 +1,121 @@
<?php
namespace Kanboard\Core\Http;
use Kanboard\Core\Base;
/**
* OAuth2 Client
*
* @package http
* @author Frederic Guillot
*/
class OAuth2 extends Base
{
private $clientId;
private $secret;
private $callbackUrl;
private $authUrl;
private $tokenUrl;
private $scopes;
private $tokenType;
private $accessToken;
/**
* Create OAuth2 service
*
* @access public
* @param string $clientId
* @param string $secret
* @param string $callbackUrl
* @param string $authUrl
* @param string $tokenUrl
* @param array $scopes
* @return OAuth2
*/
public function createService($clientId, $secret, $callbackUrl, $authUrl, $tokenUrl, array $scopes)
{
$this->clientId = $clientId;
$this->secret = $secret;
$this->callbackUrl = $callbackUrl;
$this->authUrl = $authUrl;
$this->tokenUrl = $tokenUrl;
$this->scopes = $scopes;
return $this;
}
/**
* Get authorization url
*
* @access public
* @return string
*/
public function getAuthorizationUrl()
{
$params = array(
'response_type' => 'code',
'client_id' => $this->clientId,
'redirect_uri' => $this->callbackUrl,
'scope' => implode(' ', $this->scopes),
);
return $this->authUrl.'?'.http_build_query($params);
}
/**
* Get authorization header
*
* @access public
* @return string
*/
public function getAuthorizationHeader()
{
if (strtolower($this->tokenType) === 'bearer') {
return 'Authorization: Bearer '.$this->accessToken;
}
return '';
}
/**
* Get access token
*
* @access public
* @param string $code
* @return string
*/
public function getAccessToken($code)
{
if (empty($this->accessToken) && ! empty($code)) {
$params = array(
'code' => $code,
'client_id' => $this->clientId,
'client_secret' => $this->secret,
'redirect_uri' => $this->callbackUrl,
'grant_type' => 'authorization_code',
);
$response = json_decode($this->httpClient->postForm($this->tokenUrl, $params, array('Accept: application/json')), true);
$this->tokenType = isset($response['token_type']) ? $response['token_type'] : '';
$this->accessToken = isset($response['access_token']) ? $response['access_token'] : '';
}
return $this->accessToken;
}
/**
* Set access token
*
* @access public
* @param string $token
* @param string $type
* @return string
*/
public function setAccessToken($token, $type = 'bearer')
{
$this->accessToken = $token;
$this->tokenType = $type;
}
}

View File

@@ -0,0 +1,120 @@
<?php
namespace Kanboard\Core\Http;
use Kanboard\Core\Base;
/**
* Remember Me Cookie
*
* @package http
* @author Frederic Guillot
*/
class RememberMeCookie extends Base
{
/**
* Cookie name
*
* @var string
*/
const COOKIE_NAME = 'KB_RM';
/**
* Encode the cookie
*
* @access public
* @param string $token Session token
* @param string $sequence Sequence token
* @return string
*/
public function encode($token, $sequence)
{
return implode('|', array($token, $sequence));
}
/**
* Decode the value of a cookie
*
* @access public
* @param string $value Raw cookie data
* @return array
*/
public function decode($value)
{
list($token, $sequence) = explode('|', $value);
return array(
'token' => $token,
'sequence' => $sequence,
);
}
/**
* Return true if the current user has a RememberMe cookie
*
* @access public
* @return bool
*/
public function hasCookie()
{
return $this->request->getCookie(self::COOKIE_NAME) !== '';
}
/**
* Write and encode the cookie
*
* @access public
* @param string $token Session token
* @param string $sequence Sequence token
* @param string $expiration Cookie expiration
* @return boolean
*/
public function write($token, $sequence, $expiration)
{
return setcookie(
self::COOKIE_NAME,
$this->encode($token, $sequence),
$expiration,
$this->helper->url->dir(),
null,
$this->request->isHTTPS(),
true
);
}
/**
* Read and decode the cookie
*
* @access public
* @return mixed
*/
public function read()
{
$cookie = $this->request->getCookie(self::COOKIE_NAME);
if (empty($cookie)) {
return false;
}
return $this->decode($cookie);
}
/**
* Remove the cookie
*
* @access public
* @return boolean
*/
public function remove()
{
return setcookie(
self::COOKIE_NAME,
'',
time() - 3600,
$this->helper->url->dir(),
null,
$this->request->isHTTPS(),
true
);
}
}

View File

@@ -2,6 +2,7 @@
namespace Kanboard\Core\Http;
use Pimple\Container;
use Kanboard\Core\Base;
/**
@@ -13,7 +14,35 @@ use Kanboard\Core\Base;
class Request extends Base
{
/**
* Get URL string parameter
* Pointer to PHP environment variables
*
* @access private
* @var array
*/
private $server;
private $get;
private $post;
private $files;
private $cookies;
/**
* Constructor
*
* @access public
* @param \Pimple\Container $container
*/
public function __construct(Container $container, array $server = array(), array $get = array(), array $post = array(), array $files = array(), array $cookies = array())
{
parent::__construct($container);
$this->server = empty($server) ? $_SERVER : $server;
$this->get = empty($get) ? $_GET : $get;
$this->post = empty($post) ? $_POST : $post;
$this->files = empty($files) ? $_FILES : $files;
$this->cookies = empty($cookies) ? $_COOKIE : $cookies;
}
/**
* Get query string string parameter
*
* @access public
* @param string $name Parameter name
@@ -22,11 +51,11 @@ class Request extends Base
*/
public function getStringParam($name, $default_value = '')
{
return isset($_GET[$name]) ? $_GET[$name] : $default_value;
return isset($this->get[$name]) ? $this->get[$name] : $default_value;
}
/**
* Get URL integer parameter
* Get query string integer parameter
*
* @access public
* @param string $name Parameter name
@@ -35,7 +64,7 @@ class Request extends Base
*/
public function getIntegerParam($name, $default_value = 0)
{
return isset($_GET[$name]) && ctype_digit($_GET[$name]) ? (int) $_GET[$name] : $default_value;
return isset($this->get[$name]) && ctype_digit($this->get[$name]) ? (int) $this->get[$name] : $default_value;
}
/**
@@ -59,9 +88,9 @@ class Request extends Base
*/
public function getValues()
{
if (! empty($_POST) && ! empty($_POST['csrf_token']) && $this->token->validateCSRFToken($_POST['csrf_token'])) {
unset($_POST['csrf_token']);
return $_POST;
if (! empty($this->post) && ! empty($this->post['csrf_token']) && $this->token->validateCSRFToken($this->post['csrf_token'])) {
unset($this->post['csrf_token']);
return $this->post;
}
return array();
@@ -98,8 +127,8 @@ class Request extends Base
*/
public function getFileContent($name)
{
if (isset($_FILES[$name])) {
return file_get_contents($_FILES[$name]['tmp_name']);
if (isset($this->files[$name]['tmp_name'])) {
return file_get_contents($this->files[$name]['tmp_name']);
}
return '';
@@ -114,7 +143,7 @@ class Request extends Base
*/
public function getFilePath($name)
{
return isset($_FILES[$name]['tmp_name']) ? $_FILES[$name]['tmp_name'] : '';
return isset($this->files[$name]['tmp_name']) ? $this->files[$name]['tmp_name'] : '';
}
/**
@@ -125,7 +154,7 @@ class Request extends Base
*/
public function isPost()
{
return isset($_SERVER['REQUEST_METHOD']) && $_SERVER['REQUEST_METHOD'] === 'POST';
return isset($this->server['REQUEST_METHOD']) && $this->server['REQUEST_METHOD'] === 'POST';
}
/**
@@ -144,13 +173,24 @@ class Request extends Base
*
* Note: IIS return the value 'off' and other web servers an empty value when it's not HTTPS
*
* @static
* @access public
* @return boolean
*/
public static function isHTTPS()
public function isHTTPS()
{
return isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== '' && $_SERVER['HTTPS'] !== 'off';
return isset($this->server['HTTPS']) && $this->server['HTTPS'] !== '' && $this->server['HTTPS'] !== 'off';
}
/**
* Get cookie value
*
* @access public
* @param string $name
* @return string
*/
public function getCookie($name)
{
return isset($this->cookies[$name]) ? $this->cookies[$name] : '';
}
/**
@@ -163,7 +203,18 @@ class Request extends Base
public function getHeader($name)
{
$name = 'HTTP_'.str_replace('-', '_', strtoupper($name));
return isset($_SERVER[$name]) ? $_SERVER[$name] : '';
return isset($this->server[$name]) ? $this->server[$name] : '';
}
/**
* Get remote user
*
* @access public
* @return string
*/
public function getRemoteUser()
{
return isset($this->server[REVERSE_PROXY_USER_HEADER]) ? $this->server[REVERSE_PROXY_USER_HEADER] : '';
}
/**
@@ -174,41 +225,38 @@ class Request extends Base
*/
public function getQueryString()
{
return isset($_SERVER['QUERY_STRING']) ? $_SERVER['QUERY_STRING'] : '';
return isset($this->server['QUERY_STRING']) ? $this->server['QUERY_STRING'] : '';
}
/**
* Returns uri
* Return URI
*
* @access public
* @return string
*/
public function getUri()
{
return isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : '';
return isset($this->server['REQUEST_URI']) ? $this->server['REQUEST_URI'] : '';
}
/**
* Get the user agent
*
* @static
* @access public
* @return string
*/
public static function getUserAgent()
public function getUserAgent()
{
return empty($_SERVER['HTTP_USER_AGENT']) ? t('Unknown') : $_SERVER['HTTP_USER_AGENT'];
return empty($this->server['HTTP_USER_AGENT']) ? t('Unknown') : $this->server['HTTP_USER_AGENT'];
}
/**
* Get the real IP address of the user
* Get the IP address of the user
*
* @static
* @access public
* @param bool $only_public Return only public IP address
* @return string
*/
public static function getIpAddress($only_public = false)
public function getIpAddress()
{
$keys = array(
'HTTP_CLIENT_IP',
@@ -221,23 +269,24 @@ class Request extends Base
);
foreach ($keys as $key) {
if (isset($_SERVER[$key])) {
foreach (explode(',', $_SERVER[$key]) as $ip_address) {
$ip_address = trim($ip_address);
if ($only_public) {
// Return only public IP address
if (filter_var($ip_address, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE) !== false) {
return $ip_address;
}
} else {
return $ip_address;
}
if (! empty($this->server[$key])) {
foreach (explode(',', $this->server[$key]) as $ipAddress) {
return trim($ipAddress);
}
}
}
return t('Unknown');
}
/**
* Get start time
*
* @access public
* @return float
*/
public function getStartTime()
{
return isset($this->server['REQUEST_TIME_FLOAT']) ? $this->server['REQUEST_TIME_FLOAT'] : 0;
}
}

View File

@@ -257,7 +257,7 @@ class Response extends Base
*/
public function hsts()
{
if (Request::isHTTPS()) {
if ($this->request->isHTTPS()) {
header('Strict-Transport-Security: max-age=31536000');
}
}