Skip to content

Commit e058fa1

Browse files
authored
Merge pull request #5 from CorpusPHP/feature/AuthorizationHeaderPartParser
Adds Authorization Header Parser "AuthorizationPartParser"
2 parents 19caab3 + 134527e commit e058fa1

File tree

7 files changed

+312
-2
lines changed

7 files changed

+312
-2
lines changed

.mddoc.xml.dist

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,10 @@
1616
</section>
1717

1818
<section title="Documentation">
19-
<recursiveDirectory name="src"/>
19+
<recursiveDirectory name="src" file-filter="/.*
20+
(?&lt;!Factory\.php)
21+
(?&lt;!AuthorizationParts\.php)
22+
$/x"/>
2023
</section>
2124
</section>
2225
</docpage>

README.md

Lines changed: 92 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
[![Latest Stable Version](https://poser.pugx.org/corpus/http-message-utils/version)](https://packagist.org/packages/corpus/http-message-utils)
44
[![License](https://poser.pugx.org/corpus/http-message-utils/license)](https://packagist.org/packages/corpus/http-message-utils)
5-
[![Build Status](https://github.com/CorpusPHP/HttpMessageUtils/workflows/CI/badge.svg?)](https://github.com/CorpusPHP/HttpMessageUtils/actions?query=workflow%3ACI)
5+
[![CI](https://github.com/CorpusPHP/HttpMessageUtils/workflows/CI/badge.svg?)](https://github.com/CorpusPHP/HttpMessageUtils/actions?query=workflow%3ACI)
66

77

88
Utilities for working with [PSR-7 Http Message](https://www.php-fig.org/psr/psr-7/) objects.
@@ -22,6 +22,97 @@ composer require 'corpus/http-message-utils'
2222

2323
## Documentation
2424

25+
### Class: \Corpus\HttpMessageUtils\Authorization\AuthorizationHeaderParser
26+
27+
Utility to split an Authorization header into `<type>` and `<credentials>` ala:
28+
`Authorization: <type> <credentials>`
29+
30+
The parser itself is authorization type agnostic and works with any RFC7235
31+
conforming authorization type.
32+
33+
### Example:
34+
35+
```php
36+
$serverRequest = \GuzzleHttp\Psr7\ServerRequest::fromGlobals();
37+
$parsedAuth = (new \Corpus\HttpMessageUtils\Authorization\AuthorizationHeaderParser)->parseServerRequest($serverRequest);
38+
39+
if( $parsedAuth ) {
40+
echo 'type: ' . $parsedAuth->getType();
41+
echo 'cred: ' . $parsedAuth->getCredentials();
42+
}
43+
```
44+
45+
#### Method: AuthorizationHeaderParser->__construct
46+
47+
```php
48+
function __construct([ ?\Corpus\HttpMessageUtils\Authorization\AuthorizationPartsFactory $factory = null])
49+
```
50+
51+
##### Parameters:
52+
53+
- ***\Corpus\HttpMessageUtils\Authorization\AuthorizationPartsFactory*** | ***null*** `$factory` - Optional factory for construction of result objects
54+
55+
---
56+
57+
#### Method: AuthorizationHeaderParser->parseString
58+
59+
```php
60+
function parseString(string $headerValue) : ?\Corpus\HttpMessageUtils\Authorization\AuthorizationPartsInterface
61+
```
62+
63+
Parses an Authorization header into `<type>` and `<credentials>`
64+
65+
##### Parameters:
66+
67+
- ***string*** `$headerValue` - The header value to parse
68+
69+
##### Returns:
70+
71+
- ***\Corpus\HttpMessageUtils\Authorization\AuthorizationPartsInterface*** | ***null*** - AuthorizationParts on success, null on failure.
72+
Reasons for failure include empty string and non-RFC7235 compliant header values.
73+
74+
---
75+
76+
#### Method: AuthorizationHeaderParser->parseServerRequest
77+
78+
```php
79+
function parseServerRequest(\Psr\Http\Message\ServerRequestInterface $request [, string $headerName = self::DEFAULT_HEADER]) : ?\Corpus\HttpMessageUtils\Authorization\AuthorizationPartsInterface
80+
```
81+
82+
Helper to easily parse from a PSR ServerRequestInterface
83+
84+
##### Parameters:
85+
86+
- ***\Psr\Http\Message\ServerRequestInterface*** `$request` - The PSR ServerRequestInterface to read from
87+
- ***string*** `$headerName` - Optional header name to parse. Defaults to Authorization.
88+
89+
##### Returns:
90+
91+
- ***\Corpus\HttpMessageUtils\Authorization\AuthorizationPartsInterface*** | ***null*** - AuthorizationParts on success, null on failure.
92+
93+
### Class: \Corpus\HttpMessageUtils\Authorization\AuthorizationPartsInterface
94+
95+
Representation of the parts of an Authorization Header:
96+
`Authorization: <type> <credentials>`
97+
98+
#### Method: AuthorizationPartsInterface->getType
99+
100+
```php
101+
function getType() : string
102+
```
103+
104+
The specified authorization type
105+
106+
---
107+
108+
#### Method: AuthorizationPartsInterface->getCredentials
109+
110+
```php
111+
function getCredentials() : string
112+
```
113+
114+
The specified authorization credentials
115+
25116
### Class: \Corpus\HttpMessageUtils\ProxyAwareSchemer
26117

27118
Utility to map a Uri or ServerRequestInterface's Uri to the external scheme
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
<?php
2+
3+
namespace Corpus\HttpMessageUtils\Authorization;
4+
5+
use Psr\Http\Message\ServerRequestInterface;
6+
7+
/**
8+
* Utility to split an Authorization header into `<type>` and `<credentials>` ala:
9+
* `Authorization: <type> <credentials>`
10+
*
11+
* The parser itself is authorization type agnostic and works with any RFC7235
12+
* conforming authorization type.
13+
*
14+
* ### Example:
15+
*
16+
* ```php
17+
* $serverRequest = \GuzzleHttp\Psr7\ServerRequest::fromGlobals();
18+
* $parsedAuth = (new \Corpus\HttpMessageUtils\Authorization\AuthorizationHeaderParser)->parseServerRequest($serverRequest);
19+
*
20+
* if( $parsedAuth ) {
21+
* echo 'type: ' . $parsedAuth->getType();
22+
* echo 'cred: ' . $parsedAuth->getCredentials();
23+
* }
24+
* ```
25+
*/
26+
class AuthorizationHeaderParser {
27+
28+
private const DEFAULT_HEADER = 'authorization';
29+
/** @var AuthorizationPartsFactory */
30+
private $factory;
31+
32+
/**
33+
* @param AuthorizationPartsFactory|null $factory Optional factory for construction of result objects
34+
*/
35+
public function __construct(
36+
?AuthorizationPartsFactory $factory = null
37+
) {
38+
if( !$factory ) {
39+
$factory = new AuthorizationPartsFactory;
40+
}
41+
42+
$this->factory = $factory;
43+
}
44+
45+
/**
46+
* Parses an Authorization header into `<type>` and `<credentials>`
47+
*
48+
* @param string $headerValue The header value to parse
49+
* @return AuthorizationPartsInterface|null AuthorizationParts on success, null on failure.
50+
* Reasons for failure include empty string and non-RFC7235 compliant header values.
51+
*/
52+
public function parseString(
53+
string $headerValue
54+
) : ?AuthorizationPartsInterface {
55+
$headerValue = trim($headerValue);
56+
if( !$headerValue ) {
57+
return null;
58+
}
59+
60+
$result = preg_split('/\s+/', $headerValue, 2, PREG_SPLIT_NO_EMPTY);
61+
if( count($result) === 2 ) {
62+
return $this->factory->make($result[0], $result[1]);
63+
}
64+
65+
return null;
66+
}
67+
68+
/**
69+
* Helper to easily parse from a PSR ServerRequestInterface
70+
*
71+
* @param ServerRequestInterface $request The PSR ServerRequestInterface to read from
72+
* @param string $headerName Optional header name to parse. Defaults to Authorization.
73+
* @return AuthorizationPartsInterface|null AuthorizationParts on success, null on failure.
74+
* @see self::parseString
75+
*/
76+
public function parseServerRequest(
77+
ServerRequestInterface $request,
78+
string $headerName = self::DEFAULT_HEADER
79+
) : ?AuthorizationPartsInterface {
80+
$header = $request->getHeaderLine($headerName);
81+
82+
return $this->parseString($header);
83+
}
84+
85+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<?php
2+
3+
namespace Corpus\HttpMessageUtils\Authorization;
4+
5+
class AuthorizationParts implements AuthorizationPartsInterface {
6+
7+
/** @var string */
8+
private $type;
9+
/** @var string */
10+
private $credentials;
11+
12+
public function __construct( string $type, string $credentials ) {
13+
$this->type = $type;
14+
$this->credentials = $credentials;
15+
}
16+
17+
public function getType() : string {
18+
return $this->type;
19+
}
20+
21+
public function getCredentials() : string {
22+
return $this->credentials;
23+
}
24+
25+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<?php
2+
3+
namespace Corpus\HttpMessageUtils\Authorization;
4+
5+
class AuthorizationPartsFactory {
6+
7+
public function make( string $type, string $credentials ) : AuthorizationPartsInterface {
8+
return new AuthorizationParts($type, $credentials);
9+
}
10+
11+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<?php
2+
3+
namespace Corpus\HttpMessageUtils\Authorization;
4+
5+
/**
6+
* Representation of the parts of an Authorization Header:
7+
* `Authorization: <type> <credentials>`
8+
*/
9+
interface AuthorizationPartsInterface {
10+
11+
/**
12+
* The specified authorization type
13+
*/
14+
public function getType() : string;
15+
16+
/**
17+
* The specified authorization credentials
18+
*/
19+
public function getCredentials() : string;
20+
21+
}
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
<?php
2+
3+
namespace Corpus\HttpMessageUtils\Authorization;
4+
5+
use GuzzleHttp\Psr7\ServerRequest;
6+
use PHPUnit\Framework\TestCase;
7+
8+
class AuthorizationHeaderParserTest extends TestCase {
9+
10+
/**
11+
* @dataProvider authorizationProvider
12+
*/
13+
public function testParseString(
14+
string $header,
15+
?string $type,
16+
?string $credentials = null
17+
) : void {
18+
$parser = new AuthorizationHeaderParser;
19+
$result = $parser->parseString($header);
20+
21+
if( $type === null ) {
22+
$this->assertNull($result);
23+
24+
return;
25+
}
26+
27+
$this->assertSame($type, $result->getType());
28+
$this->assertSame($credentials, $result->getCredentials());
29+
}
30+
31+
/**
32+
* @dataProvider authorizationProvider
33+
*/
34+
public function testParseServerRequest(
35+
string $header,
36+
?string $type,
37+
?string $credentials = null
38+
) : void {
39+
$parser = new AuthorizationHeaderParser;
40+
$request = new ServerRequest("POST", "https://example.com", [
41+
'Authorization' => $header,
42+
]);
43+
44+
$result = $parser->parseServerRequest($request);
45+
46+
if( $type === null ) {
47+
$this->assertNull($result);
48+
49+
return;
50+
}
51+
52+
$this->assertSame($type, $result->getType());
53+
$this->assertSame($credentials, $result->getCredentials());
54+
}
55+
56+
public function testParseServerRequest_noHeader() : void {
57+
$parser = new AuthorizationHeaderParser;
58+
$request = new ServerRequest("GET", "https://example.com");
59+
60+
$result = $parser->parseServerRequest($request);
61+
$this->assertNull($result);
62+
}
63+
64+
public function authorizationProvider() : \Generator {
65+
yield [ '', null ];
66+
yield [ 'Foo', null ];
67+
yield [ 'Foo bar', 'Foo', 'bar' ];
68+
yield [ ' Foo bar ', 'Foo', 'bar' ];
69+
yield [ 'Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==', 'Basic', 'QWxhZGRpbjpvcGVuIHNlc2FtZQ==' ];
70+
yield [ 'QueryAuth foo=bar baz=qux', 'QueryAuth', 'foo=bar baz=qux' ];
71+
yield [ ' OtherAuth foo=bar baz=qux ', 'OtherAuth', 'foo=bar baz=qux' ];
72+
}
73+
74+
}

0 commit comments

Comments
 (0)