-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #13 from rtckit/v0.4.0
v0.4.0
- Loading branch information
Showing
9 changed files
with
673 additions
and
25 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,273 @@ | ||
<?php | ||
/** | ||
* RTCKit\SIP\AuthHeader Class | ||
*/ | ||
declare(strict_types = 1); | ||
|
||
namespace RTCKit\SIP\Header; | ||
|
||
use RTCKit\SIP\Response; | ||
use RTCKit\SIP\Exception\InvalidHeaderLineException; | ||
use RTCKit\SIP\Exception\InvalidHeaderParameter; | ||
use RTCKit\SIP\Exception\InvalidHeaderValue; | ||
|
||
/** | ||
* Authentication/Authorization Header Class | ||
*/ | ||
class AuthHeader | ||
{ | ||
public const DIGEST_SCHEME = 'digest'; | ||
|
||
/** @var list<AuthValue> Authentication/Authorization value(s) */ | ||
public array $values = []; | ||
|
||
final public function __construct() {} | ||
|
||
/** | ||
* Authentication/Authorization field value parser | ||
* | ||
* @param list<string> $hbody Header body | ||
* @throws InvalidHeaderLineException | ||
* @throws InvalidHeaderParameter | ||
* @return AuthHeader | ||
*/ | ||
public static function parse(array $hbody): AuthHeader | ||
{ | ||
$ret = new static; | ||
|
||
foreach ($hbody as $hline) { | ||
$hparts = explode(' ', trim($hline), 2); | ||
|
||
if (count($hparts) !== 2) { | ||
throw new InvalidHeaderLineException('Invalid Auth header, missing scheme', Response::BAD_REQUEST); | ||
} | ||
|
||
$val = new AuthValue; | ||
$val->scheme = $hparts[0]; | ||
|
||
$params = ltrim($hparts[1]); | ||
|
||
if (strtolower($val->scheme) === self::DIGEST_SCHEME) { | ||
while (strlen($params)) { | ||
$pos = strpos($params, '='); | ||
|
||
if ($pos === false) { | ||
throw new InvalidHeaderLineException('Invalid Auth header, valueless parameter', Response::BAD_REQUEST); | ||
} | ||
|
||
$pk = rtrim(substr($params, 0, $pos)); | ||
$pv = ''; | ||
$params = ltrim(substr($params, $pos + 1)); | ||
|
||
if (!isset($params[0])) { | ||
throw new InvalidHeaderLineException('Invalid Auth header, valueless parameter', Response::BAD_REQUEST); | ||
} | ||
|
||
if ($params[0] === '"') { | ||
$offset = 1; | ||
$escQuotes = false; | ||
|
||
while (true) { | ||
$pos = strpos($params, '"', $offset); | ||
|
||
if ($pos === false) { | ||
throw new InvalidHeaderLineException('Invalid Auth header, unmatched parameter value enclosing', Response::BAD_REQUEST); | ||
} | ||
|
||
if ($params[$pos - 1] !== '\\') { | ||
break; | ||
} | ||
|
||
$escQuotes = true; | ||
$offset = $pos + 1; | ||
} | ||
|
||
$pv = substr($params, 1, $pos - 1); | ||
|
||
if ($escQuotes) { | ||
$pv = str_replace('\"', '"', $pv); | ||
} | ||
|
||
$params = ltrim(substr($params, $pos + 1)); | ||
|
||
if (isset($params[0])) { | ||
if ($params[0] !== ',') { | ||
throw new InvalidHeaderLineException('Invalid Auth header, invalid parameter value enclosing', Response::BAD_REQUEST); | ||
} | ||
|
||
$params = ltrim(substr($params, 1)); | ||
} | ||
} else { | ||
$pos = strpos($params, ','); | ||
|
||
if ($pos !== false) { | ||
$pv = rtrim(substr($params, 0, $pos)); | ||
$params = ltrim(substr($params, $pos + 1)); | ||
} else { | ||
$pv = rtrim($params); | ||
$params = ''; | ||
} | ||
} | ||
|
||
switch ($pk) { | ||
case 'username': | ||
$val->username = $pv; | ||
break; | ||
|
||
case 'realm': | ||
$val->realm = $pv; | ||
break; | ||
|
||
case 'domain': | ||
$val->domain = $pv; | ||
break; | ||
|
||
case 'nonce': | ||
$val->nonce = $pv; | ||
break; | ||
|
||
case 'uri': | ||
$val->uri = $pv; | ||
break; | ||
|
||
case 'response': | ||
$val->response = $pv; | ||
break; | ||
|
||
case 'stale': | ||
$pv = strtolower($pv); | ||
|
||
if ($pv === 'true') { | ||
$val->stale = true; | ||
} else if ($pv === 'false') { | ||
$val->stale = false; | ||
} else { | ||
throw new InvalidHeaderParameter('Invalid Auth header, non-boolean stale parameter', Response::BAD_REQUEST); | ||
} | ||
break; | ||
|
||
case 'algorithm': | ||
$val->algorithm = $pv; | ||
break; | ||
|
||
case 'cnonce': | ||
$val->cnonce = $pv; | ||
break; | ||
|
||
case 'qop': | ||
$val->qop = $pv; | ||
break; | ||
|
||
case 'nc': | ||
if (!ctype_digit($pv)) { | ||
throw new InvalidHeaderParameter('Invalid Auth header, non-integer nc parameter', Response::BAD_REQUEST); | ||
} | ||
|
||
$val->nc = (int)$pv; | ||
break; | ||
|
||
case 'opaque': | ||
$val->opaque = $pv; | ||
break; | ||
|
||
default: | ||
$val->params[$pk] = $pv; | ||
break; | ||
} | ||
} | ||
} else { | ||
$val->credentials = $params; | ||
} | ||
|
||
$ret->values[] = $val; | ||
} | ||
|
||
return $ret; | ||
} | ||
|
||
/** | ||
* Authentication/Authorization header field value renderer | ||
* | ||
* @param string $hname Header field name | ||
* @throws InvalidHeaderValue | ||
* @return string | ||
*/ | ||
public function render(string $hname): string | ||
{ | ||
$ret = ''; | ||
|
||
foreach ($this->values as $key => $value) { | ||
if (!isset($value->scheme)) { | ||
throw new InvalidHeaderValue('Malformed auth header, missing scheme'); | ||
} | ||
|
||
$params = []; | ||
|
||
if (strtolower($value->scheme) === self::DIGEST_SCHEME) { | ||
if (isset($value->username)) { | ||
$params[] = "username=\"{$value->username}\""; | ||
} | ||
|
||
if (isset($value->realm)) { | ||
$params[] = "realm=\"{$value->realm}\""; | ||
} | ||
|
||
if (isset($value->domain)) { | ||
$params[] = "domain=\"{$value->domain}\""; | ||
} | ||
|
||
if (isset($value->nonce)) { | ||
$params[] = "nonce=\"{$value->nonce}\""; | ||
} | ||
|
||
if (isset($value->uri)) { | ||
$params[] = "uri=\"{$value->uri}\""; | ||
} | ||
|
||
if (isset($value->response)) { | ||
$params[] = "response=\"{$value->response}\""; | ||
} | ||
|
||
if (isset($value->stale)) { | ||
$params[] = 'stale=' . ($value->stale ? 'TRUE' : 'FALSE'); | ||
} | ||
|
||
if (isset($value->algorithm)) { | ||
$params[] = "algorithm=\"{$value->algorithm}\""; | ||
} | ||
|
||
if (isset($value->cnonce)) { | ||
$params[] = "cnonce=\"{$value->cnonce}\""; | ||
} | ||
|
||
if (isset($value->qop)) { | ||
$params[] = "qop=\"{$value->qop}\""; | ||
} | ||
|
||
if (isset($value->nc)) { | ||
$params[] = sprintf('ns=%08d', $value->nc); | ||
} | ||
|
||
if (isset($value->opaque)) { | ||
$params[] = "opaque=\"{$value->opaque}\""; | ||
} | ||
} else { | ||
if (!isset($value->credentials)) { | ||
throw new InvalidHeaderValue('Malformed auth header, missing credentials'); | ||
} | ||
|
||
$params[] = $value->credentials; | ||
} | ||
|
||
foreach ($value->params as $pk => $pv) { | ||
$params[] = "{$pk}={$pv}"; | ||
} | ||
|
||
$paramStr = implode(',', $params); | ||
|
||
$ret .= "{$hname}: {$value->scheme} {$paramStr}\r\n"; | ||
} | ||
|
||
return $ret; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
<?php | ||
/** | ||
* RTCKit\SIP\AuthValue Class | ||
*/ | ||
declare(strict_types = 1); | ||
|
||
namespace RTCKit\SIP\Header; | ||
|
||
/** | ||
* Authentication/Authorization Header Field Value Class | ||
*/ | ||
class AuthValue | ||
{ | ||
/** @var string Authentication scheme */ | ||
public string $scheme; | ||
|
||
/** @var string Authentication user name */ | ||
public string $username; | ||
|
||
/** @var string Authentication realm */ | ||
public string $realm; | ||
|
||
/** @var string SIP domain */ | ||
public string $domain; | ||
|
||
/** @var string Server's number once */ | ||
public string $nonce; | ||
|
||
/** @var string SIP URI */ | ||
public string $uri; | ||
|
||
/** @var string Response hash */ | ||
public string $response; | ||
|
||
/** @var bool Stale response flag */ | ||
public bool $stale; | ||
|
||
/** @var string Digest algorithm */ | ||
public string $algorithm; | ||
|
||
/** @var string Client's number once */ | ||
public string $cnonce; | ||
|
||
/** @var string Quality of protection */ | ||
public string $qop; | ||
|
||
/** @var int Number once count */ | ||
public int $nc; | ||
|
||
/** @var string Server's opaque data blob */ | ||
public string $opaque; | ||
|
||
/** @var string Generic credentials when attributes aren't used (e.g. Basic) */ | ||
public string $credentials; | ||
|
||
/** @var array<string, string> Additional parameters */ | ||
public array $params = []; | ||
} |
Oops, something went wrong.