Projekt

Obecné

Profil

Stáhnout (5.43 KB) Statistiky
| Větev: | Tag: | Revize:
74a7dbca Michal Kliment
<?php

/*
* This file is a part of PHP-HTTP-Auth-server library, released under terms
* of GPL-3.0 licence. Copyright (c) 2014, UnArt Slavičín, o.s. All rights
* reserved.
*/

namespace phphttpauthserver;

/**
* The "DigestHttpAuth" class provides implementation of Digest HTTP
* Authentication server side mechanism.
*
* This implementation requires mod_auth_digest to be enabled on server.
*
* This implementation is based on http://php.net/manual/en/features.http-auth.php.
*
* @author Ondřej Fibich
*/
class DigestHttpAuth extends HttpAuth {
/**
* Creates HTTP auth handler in given realm with account given by passsed
* manager.
*
* @param IAccountManager $accountManager Account manager
* @param string $realmName Realm name
* @throws \InvalidArgumentException on empty realm name or account manager
*/
public function __construct(IAccountManager $accountManager, $realmName) {
parent::__construct($accountManager, $realmName);
}

/**
* Get nonce - protection again replay attacks.
*
* @return string
*/
protected function generateNonce() {
return uniqid();
}
/**
* Get opaque - server identificator.
*
* @return string
*/
protected function getOpaque() {
return md5($this->realm);
}

/**
* Get digest auth HTTP header from php server variables.
*
* @return string|null header or null
*/
private static function getAuthDigestHeader() {
if (isset($_SERVER['PHP_AUTH_DIGEST'])) {
return $_SERVER['PHP_AUTH_DIGEST'];
} else if (isset($_SERVER['HTTP_AUTHORIZATION'])) {
$httpAuth = $_SERVER['HTTP_AUTHORIZATION'];
if (strncasecmp($httpAuth, 'digest', 6) === 0) {
return substr($httpAuth, 7);
}
}
return NULL;
}
/**
* Performs HTTP Digest auth using server PHP_AUTH_DIGEST and REQUEST_METHOD
* variables.
*
* @return HttpAuthResponse response object
*/
public function auth() {
$httpDigest = self::getAuthDigestHeader();
$requestMethod = $_SERVER['REQUEST_METHOD'];
$response = new HttpAuthResponse();
// no digest header sended
if (empty($httpDigest)) {
$wa = sprintf('Digest realm="%s",qop="auth",nonce="%s",opaque="%s"',
$this->realm, $this->generateNonce(), $this->getOpaque());
return $response->setPassed(FALSE)
->addHeader('WWW-Authenticate', $wa);
}
// no request method?
if (empty($requestMethod)) {
return $response->addError('Request method empty');
}
// analyze the PHP_AUTH_DIGEST variable
try {
$data = self::httpDigestParse($httpDigest);
} catch (\InvalidArgumentException $ex) {
return $response->addError('Invalid HTTP Auth Digest header: '
. $ex->getMessage());
}
// get user
$userPassword = $this->accountManager->getUserPassword(
$data['username']);
// user not exists?
if ($userPassword === FALSE) {
return $response->addError('Wrong Credentials');
}
// generate the valid response
$validResponse = $this->calculateValidResponse(
$data, $requestMethod, $userPassword);
// check client response
if ($data['response'] != $validResponse) {
echo $requestMethod . ' ' . $data['response'] . '!=' . $validResponse . "\n";
return $response->addError('Wrong Credentials');
}
// auth success
return $response->setUsername($data['username']);
}
/**
* Calculate valid response that should be received from client.
*
* @param array $data
* @param string $requestMethod
* @param string $password
* @return string
*/
private function calculateValidResponse($data, $requestMethod, $password) {
$A1 = md5($data['username'] . ':' . $this->realm . ':' . $password);
$A2 = md5($requestMethod . ':' . $data['uri']);
return md5($A1 . ':' . $data['nonce'] . ':' . $data['nc']
. ':' . $data['cnonce'] . ':' . $data['qop'] . ':' . $A2);
}
/**
* Parses HTTP auth header sended by client.
*
* @param string $headerStrValue
* @return array parsed client data asassociative array with key: nonce,
* nc, cnonce, qop, username, uri, response
* @throws \InvalidArgumentException if not all required fields were provided
*/
private static function httpDigestParse($headerStrValue) {
// protect against missing data
$neededParts = array(
'nonce' => 1, 'nc' => 1, 'cnonce' => 1, 'qop' => 1, 'username' => 1,
'uri' => 1, 'response' => 1
);
$data = array();
$keys = implode('|', array_keys($neededParts));

$matches = array();
preg_match_all('@(' . $keys . ')=(?:([\'"])([^\2]+?)\2|([^\s,]+))@',
$headerStrValue, $matches, PREG_SET_ORDER);

foreach ($matches as $m) {
$data[$m[1]] = $m[3] ? $m[3] : $m[4];
unset($neededParts[$m[1]]);
}

if ($neededParts) {
$npStr = implode(', ', array_keys($neededParts));
throw new \InvalidArgumentException('Missing fields: ' . $npStr);
}
return $data;
}

}