diff options
Diffstat (limited to 'vendor/sabre/http/lib/Auth')
-rw-r--r-- | vendor/sabre/http/lib/Auth/AWS.php | 234 | ||||
-rw-r--r-- | vendor/sabre/http/lib/Auth/AbstractAuth.php | 73 | ||||
-rw-r--r-- | vendor/sabre/http/lib/Auth/Basic.php | 63 | ||||
-rw-r--r-- | vendor/sabre/http/lib/Auth/Bearer.php | 56 | ||||
-rw-r--r-- | vendor/sabre/http/lib/Auth/Digest.php | 231 |
5 files changed, 657 insertions, 0 deletions
diff --git a/vendor/sabre/http/lib/Auth/AWS.php b/vendor/sabre/http/lib/Auth/AWS.php new file mode 100644 index 000000000..5e176646a --- /dev/null +++ b/vendor/sabre/http/lib/Auth/AWS.php @@ -0,0 +1,234 @@ +<?php + +namespace Sabre\HTTP\Auth; + +use Sabre\HTTP\Util; + +/** + * HTTP AWS Authentication handler + * + * Use this class to leverage amazon's AWS authentication header + * + * @copyright Copyright (C) fruux GmbH (https://fruux.com/) + * @author Evert Pot (http://evertpot.com/) + * @license http://sabre.io/license/ Modified BSD License + */ +class AWS extends AbstractAuth { + + /** + * The signature supplied by the HTTP client + * + * @var string + */ + private $signature = null; + + /** + * The accesskey supplied by the HTTP client + * + * @var string + */ + private $accessKey = null; + + /** + * An error code, if any + * + * This value will be filled with one of the ERR_* constants + * + * @var int + */ + public $errorCode = 0; + + const ERR_NOAWSHEADER = 1; + const ERR_MD5CHECKSUMWRONG = 2; + const ERR_INVALIDDATEFORMAT = 3; + const ERR_REQUESTTIMESKEWED = 4; + const ERR_INVALIDSIGNATURE = 5; + + /** + * Gathers all information from the headers + * + * This method needs to be called prior to anything else. + * + * @return bool + */ + function init() { + + $authHeader = $this->request->getHeader('Authorization'); + $authHeader = explode(' ', $authHeader); + + if ($authHeader[0] != 'AWS' || !isset($authHeader[1])) { + $this->errorCode = self::ERR_NOAWSHEADER; + return false; + } + + list($this->accessKey, $this->signature) = explode(':', $authHeader[1]); + + return true; + + } + + /** + * Returns the username for the request + * + * @return string + */ + function getAccessKey() { + + return $this->accessKey; + + } + + /** + * Validates the signature based on the secretKey + * + * @param string $secretKey + * @return bool + */ + function validate($secretKey) { + + $contentMD5 = $this->request->getHeader('Content-MD5'); + + if ($contentMD5) { + // We need to validate the integrity of the request + $body = $this->request->getBody(); + $this->request->setBody($body); + + if ($contentMD5 != base64_encode(md5($body, true))) { + // content-md5 header did not match md5 signature of body + $this->errorCode = self::ERR_MD5CHECKSUMWRONG; + return false; + } + + } + + if (!$requestDate = $this->request->getHeader('x-amz-date')) + $requestDate = $this->request->getHeader('Date'); + + if (!$this->validateRFC2616Date($requestDate)) + return false; + + $amzHeaders = $this->getAmzHeaders(); + + $signature = base64_encode( + $this->hmacsha1($secretKey, + $this->request->getMethod() . "\n" . + $contentMD5 . "\n" . + $this->request->getHeader('Content-type') . "\n" . + $requestDate . "\n" . + $amzHeaders . + $this->request->getUrl() + ) + ); + + if ($this->signature != $signature) { + + $this->errorCode = self::ERR_INVALIDSIGNATURE; + return false; + + } + + return true; + + } + + + /** + * Returns an HTTP 401 header, forcing login + * + * This should be called when username and password are incorrect, or not supplied at all + * + * @return void + */ + function requireLogin() { + + $this->response->addHeader('WWW-Authenticate', 'AWS'); + $this->response->setStatus(401); + + } + + /** + * Makes sure the supplied value is a valid RFC2616 date. + * + * If we would just use strtotime to get a valid timestamp, we have no way of checking if a + * user just supplied the word 'now' for the date header. + * + * This function also makes sure the Date header is within 15 minutes of the operating + * system date, to prevent replay attacks. + * + * @param string $dateHeader + * @return bool + */ + protected function validateRFC2616Date($dateHeader) { + + $date = Util::parseHTTPDate($dateHeader); + + // Unknown format + if (!$date) { + $this->errorCode = self::ERR_INVALIDDATEFORMAT; + return false; + } + + $min = new \DateTime('-15 minutes'); + $max = new \DateTime('+15 minutes'); + + // We allow 15 minutes around the current date/time + if ($date > $max || $date < $min) { + $this->errorCode = self::ERR_REQUESTTIMESKEWED; + return false; + } + + return $date; + + } + + /** + * Returns a list of AMZ headers + * + * @return string + */ + protected function getAmzHeaders() { + + $amzHeaders = []; + $headers = $this->request->getHeaders(); + foreach ($headers as $headerName => $headerValue) { + if (strpos(strtolower($headerName), 'x-amz-') === 0) { + $amzHeaders[strtolower($headerName)] = str_replace(["\r\n"], [' '], $headerValue[0]) . "\n"; + } + } + ksort($amzHeaders); + + $headerStr = ''; + foreach ($amzHeaders as $h => $v) { + $headerStr .= $h . ':' . $v; + } + + return $headerStr; + + } + + /** + * Generates an HMAC-SHA1 signature + * + * @param string $key + * @param string $message + * @return string + */ + private function hmacsha1($key, $message) { + + if (function_exists('hash_hmac')) { + return hash_hmac('sha1', $message, $key, true); + } + + $blocksize = 64; + if (strlen($key) > $blocksize) { + $key = pack('H*', sha1($key)); + } + $key = str_pad($key, $blocksize, chr(0x00)); + $ipad = str_repeat(chr(0x36), $blocksize); + $opad = str_repeat(chr(0x5c), $blocksize); + $hmac = pack('H*', sha1(($key ^ $opad) . pack('H*', sha1(($key ^ $ipad) . $message)))); + return $hmac; + + } + +} diff --git a/vendor/sabre/http/lib/Auth/AbstractAuth.php b/vendor/sabre/http/lib/Auth/AbstractAuth.php new file mode 100644 index 000000000..ae45b3ee2 --- /dev/null +++ b/vendor/sabre/http/lib/Auth/AbstractAuth.php @@ -0,0 +1,73 @@ +<?php + +namespace Sabre\HTTP\Auth; + +use Sabre\HTTP\RequestInterface; +use Sabre\HTTP\ResponseInterface; + +/** + * HTTP Authentication base class. + * + * This class provides some common functionality for the various base classes. + * + * @copyright Copyright (C) fruux GmbH (https://fruux.com/) + * @author Evert Pot (http://evertpot.com/) + * @license http://sabre.io/license/ Modified BSD License + */ +abstract class AbstractAuth { + + /** + * Authentication realm + * + * @var string + */ + protected $realm; + + /** + * Request object + * + * @var RequestInterface + */ + protected $request; + + /** + * Response object + * + * @var ResponseInterface + */ + protected $response; + + /** + * Creates the object + * + * @param string $realm + * @return void + */ + function __construct($realm = 'SabreTooth', RequestInterface $request, ResponseInterface $response) { + + $this->realm = $realm; + $this->request = $request; + $this->response = $response; + + } + + /** + * This method sends the needed HTTP header and statuscode (401) to force + * the user to login. + * + * @return void + */ + abstract function requireLogin(); + + /** + * Returns the HTTP realm + * + * @return string + */ + function getRealm() { + + return $this->realm; + + } + +} diff --git a/vendor/sabre/http/lib/Auth/Basic.php b/vendor/sabre/http/lib/Auth/Basic.php new file mode 100644 index 000000000..60633b957 --- /dev/null +++ b/vendor/sabre/http/lib/Auth/Basic.php @@ -0,0 +1,63 @@ +<?php + +namespace Sabre\HTTP\Auth; + +/** + * HTTP Basic authentication utility. + * + * This class helps you setup basic auth. The process is fairly simple: + * + * 1. Instantiate the class. + * 2. Call getCredentials (this will return null or a user/pass pair) + * 3. If you didn't get valid credentials, call 'requireLogin' + * + * @copyright Copyright (C) fruux GmbH (https://fruux.com/) + * @author Evert Pot (http://evertpot.com/) + * @license http://sabre.io/license/ Modified BSD License + */ +class Basic extends AbstractAuth { + + /** + * This method returns a numeric array with a username and password as the + * only elements. + * + * If no credentials were found, this method returns null. + * + * @return null|array + */ + function getCredentials() { + + $auth = $this->request->getHeader('Authorization'); + + if (!$auth) { + return null; + } + + if (strtolower(substr($auth, 0, 6)) !== 'basic ') { + return null; + } + + $credentials = explode(':', base64_decode(substr($auth, 6)), 2); + + if (2 !== count($credentials)) { + return null; + } + + return $credentials; + + } + + /** + * This method sends the needed HTTP header and statuscode (401) to force + * the user to login. + * + * @return void + */ + function requireLogin() { + + $this->response->addHeader('WWW-Authenticate', 'Basic realm="' . $this->realm . '"'); + $this->response->setStatus(401); + + } + +} diff --git a/vendor/sabre/http/lib/Auth/Bearer.php b/vendor/sabre/http/lib/Auth/Bearer.php new file mode 100644 index 000000000..eefdf11ee --- /dev/null +++ b/vendor/sabre/http/lib/Auth/Bearer.php @@ -0,0 +1,56 @@ +<?php + +namespace Sabre\HTTP\Auth; + +/** + * HTTP Bearer authentication utility. + * + * This class helps you setup bearer auth. The process is fairly simple: + * + * 1. Instantiate the class. + * 2. Call getToken (this will return null or a token as string) + * 3. If you didn't get a valid token, call 'requireLogin' + * + * @copyright Copyright (C) fruux GmbH (https://fruux.com/) + * @author François Kooman (fkooman@tuxed.net) + * @license http://sabre.io/license/ Modified BSD License + */ +class Bearer extends AbstractAuth { + + /** + * This method returns a string with an access token. + * + * If no token was found, this method returns null. + * + * @return null|string + */ + function getToken() { + + $auth = $this->request->getHeader('Authorization'); + + if (!$auth) { + return null; + } + + if (strtolower(substr($auth, 0, 7)) !== 'bearer ') { + return null; + } + + return substr($auth, 7); + + } + + /** + * This method sends the needed HTTP header and statuscode (401) to force + * authentication. + * + * @return void + */ + function requireLogin() { + + $this->response->addHeader('WWW-Authenticate', 'Bearer realm="' . $this->realm . '"'); + $this->response->setStatus(401); + + } + +} diff --git a/vendor/sabre/http/lib/Auth/Digest.php b/vendor/sabre/http/lib/Auth/Digest.php new file mode 100644 index 000000000..4b3f0746f --- /dev/null +++ b/vendor/sabre/http/lib/Auth/Digest.php @@ -0,0 +1,231 @@ +<?php + +namespace Sabre\HTTP\Auth; + +use Sabre\HTTP\RequestInterface; +use Sabre\HTTP\ResponseInterface; + +/** + * HTTP Digest Authentication handler + * + * Use this class for easy http digest authentication. + * Instructions: + * + * 1. Create the object + * 2. Call the setRealm() method with the realm you plan to use + * 3. Call the init method function. + * 4. Call the getUserName() function. This function may return null if no + * authentication information was supplied. Based on the username you + * should check your internal database for either the associated password, + * or the so-called A1 hash of the digest. + * 5. Call either validatePassword() or validateA1(). This will return true + * or false. + * 6. To make sure an authentication prompt is displayed, call the + * requireLogin() method. + * + * @copyright Copyright (C) fruux GmbH (https://fruux.com/) + * @author Evert Pot (http://evertpot.com/) + * @license http://sabre.io/license/ Modified BSD License + */ +class Digest extends AbstractAuth { + + /** + * These constants are used in setQOP(); + */ + const QOP_AUTH = 1; + const QOP_AUTHINT = 2; + + protected $nonce; + protected $opaque; + protected $digestParts; + protected $A1; + protected $qop = self::QOP_AUTH; + + /** + * Initializes the object + */ + function __construct($realm = 'SabreTooth', RequestInterface $request, ResponseInterface $response) { + + $this->nonce = uniqid(); + $this->opaque = md5($realm); + parent::__construct($realm, $request, $response); + + } + + /** + * Gathers all information from the headers + * + * This method needs to be called prior to anything else. + * + * @return void + */ + function init() { + + $digest = $this->getDigest(); + $this->digestParts = $this->parseDigest($digest); + + } + + /** + * Sets the quality of protection value. + * + * Possible values are: + * Sabre\HTTP\DigestAuth::QOP_AUTH + * Sabre\HTTP\DigestAuth::QOP_AUTHINT + * + * Multiple values can be specified using logical OR. + * + * QOP_AUTHINT ensures integrity of the request body, but this is not + * supported by most HTTP clients. QOP_AUTHINT also requires the entire + * request body to be md5'ed, which can put strains on CPU and memory. + * + * @param int $qop + * @return void + */ + function setQOP($qop) { + + $this->qop = $qop; + + } + + /** + * Validates the user. + * + * The A1 parameter should be md5($username . ':' . $realm . ':' . $password); + * + * @param string $A1 + * @return bool + */ + function validateA1($A1) { + + $this->A1 = $A1; + return $this->validate(); + + } + + /** + * Validates authentication through a password. The actual password must be provided here. + * It is strongly recommended not store the password in plain-text and use validateA1 instead. + * + * @param string $password + * @return bool + */ + function validatePassword($password) { + + $this->A1 = md5($this->digestParts['username'] . ':' . $this->realm . ':' . $password); + return $this->validate(); + + } + + /** + * Returns the username for the request + * + * @return string + */ + function getUsername() { + + return $this->digestParts['username']; + + } + + /** + * Validates the digest challenge + * + * @return bool + */ + protected function validate() { + + $A2 = $this->request->getMethod() . ':' . $this->digestParts['uri']; + + if ($this->digestParts['qop'] == 'auth-int') { + // Making sure we support this qop value + if (!($this->qop & self::QOP_AUTHINT)) return false; + // We need to add an md5 of the entire request body to the A2 part of the hash + $body = $this->request->getBody($asString = true); + $this->request->setBody($body); + $A2 .= ':' . md5($body); + } else { + + // We need to make sure we support this qop value + if (!($this->qop & self::QOP_AUTH)) return false; + } + + $A2 = md5($A2); + + $validResponse = md5("{$this->A1}:{$this->digestParts['nonce']}:{$this->digestParts['nc']}:{$this->digestParts['cnonce']}:{$this->digestParts['qop']}:{$A2}"); + + return $this->digestParts['response'] == $validResponse; + + + } + + /** + * Returns an HTTP 401 header, forcing login + * + * This should be called when username and password are incorrect, or not supplied at all + * + * @return void + */ + function requireLogin() { + + $qop = ''; + switch ($this->qop) { + case self::QOP_AUTH : + $qop = 'auth'; + break; + case self::QOP_AUTHINT : + $qop = 'auth-int'; + break; + case self::QOP_AUTH | self::QOP_AUTHINT : + $qop = 'auth,auth-int'; + break; + } + + $this->response->addHeader('WWW-Authenticate', 'Digest realm="' . $this->realm . '",qop="' . $qop . '",nonce="' . $this->nonce . '",opaque="' . $this->opaque . '"'); + $this->response->setStatus(401); + + } + + + /** + * This method returns the full digest string. + * + * It should be compatibile with mod_php format and other webservers. + * + * If the header could not be found, null will be returned + * + * @return mixed + */ + function getDigest() { + + return $this->request->getHeader('Authorization'); + + } + + + /** + * Parses the different pieces of the digest string into an array. + * + * This method returns false if an incomplete digest was supplied + * + * @param string $digest + * @return mixed + */ + protected function parseDigest($digest) { + + // protect against missing data + $needed_parts = ['nonce' => 1, 'nc' => 1, 'cnonce' => 1, 'qop' => 1, 'username' => 1, 'uri' => 1, 'response' => 1]; + $data = []; + + preg_match_all('@(\w+)=(?:(?:")([^"]+)"|([^\s,$]+))@', $digest, $matches, PREG_SET_ORDER); + + foreach ($matches as $m) { + $data[$m[1]] = $m[2] ? $m[2] : $m[3]; + unset($needed_parts[$m[1]]); + } + + return $needed_parts ? false : $data; + + } + +} |