Failed Conditions
Pull Request — master (#29)
by Helene
03:02
created

MockOAuth2Server::mockApiCall()   B

Complexity

Conditions 6
Paths 3

Size

Total Lines 19
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 3
Bugs 1 Features 1
Metric Value
c 3
b 1
f 1
dl 0
loc 19
rs 8.8571
cc 6
eloc 10
nc 3
nop 1
1
<?php
2
3
namespace CommerceGuys\Guzzle\Oauth2\Tests;
4
5
6
use GuzzleHttp\Exception\RequestException;
7
use GuzzleHttp\Handler\MockHandler;
8
use GuzzleHttp\HandlerStack;
9
use GuzzleHttp\Psr7\Response;
10
use Psr\Http\Message\RequestInterface;
11
12
class MockOAuth2Server
13
{
14
    const KEY_TOKEN_EXPIRES_IN = 'tokenExpiresIn';
15
    const KEY_TOKEN_PATH = 'tokenPath';
16
    const KEY_TOKEN_INVALID_COUNT = 'tokenInvalidCount';
17
    const KEY_EXPECTED_QUERY_COUNT = 'expectedQueryCount';
18
19
    /** @var array */
20
    protected $options;
21
22
    /**
23
     * @var MockHandler
24
     */
25
    private $handlerStack;
26
27
    /**
28
     * @var int
29
     */
30
    private $tokenInvalidCount = 0;
31
32
    /**
33
     * @param array $options
34
     */
35
    public function __construct(array $options = [])
36
    {
37
        $defaults = [
38
            self::KEY_TOKEN_EXPIRES_IN => 3600,
39
            self::KEY_TOKEN_PATH => '/oauth2/token',
40
            self::KEY_EXPECTED_QUERY_COUNT => 1
41
        ];
42
43
        $this->options = $options + $defaults;
44
45
        $handler = new MockHandler(
46
            $this->options[self::KEY_EXPECTED_QUERY_COUNT] > 0 ?
47
                array_fill(
48
                    0,
49
                    $this->options[self::KEY_EXPECTED_QUERY_COUNT],
50
                    function (RequestInterface $request, array $options) {
51
                        return $this->getResult($request, $options);
52
                    }
53
                )
54
                : []
55
        );
56
57
        $this->handlerStack = HandlerStack::create($handler);
0 ignored issues
show
Documentation Bug introduced by
It seems like \GuzzleHttp\HandlerStack::create($handler) of type object<GuzzleHttp\HandlerStack> is incompatible with the declared type object<GuzzleHttp\Handler\MockHandler> of property $handlerStack.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
58
    }
59
60
    /**
61
     * @return HandlerStack
62
     */
63
    public function getHandlerStack()
64
    {
65
        return $this->handlerStack;
66
    }
67
68
    /**
69
     * @param RequestInterface $request
70
     * @param array            $options
71
     *
72
     * @return array
73
     */
74
    protected function getResult(RequestInterface $request, array $options)
75
    {
76
        if ($request->getUri()->getPath() === $this->options[self::KEY_TOKEN_PATH]) {
77
            $response = $this->oauth2Token($request, $options);
78
        } elseif (strpos($request->getUri()->getPath(), '/api/') !== false) {
79
            $response = $this->mockApiCall($request);
80
        }
81
        if (!isset($response)) {
82
            throw new \RuntimeException("Mock server cannot handle given request URI");
83
        }
84
85
        return $response;
86
    }
87
88
    /**
89
     * @param RequestInterface $request
90
     * @param array            $options
91
     *
92
     * @return array
93
     */
94
    protected function oauth2Token(RequestInterface $request, $options)
95
    {
96
        $body = $request->getBody()->__toString();
97
        $requestBody = [];
98
        parse_str($body, $requestBody);
99
        $grantType = $requestBody['grant_type'];
100
        switch ($grantType) {
101
            case 'password':
102
                return $this->grantTypePassword($requestBody);
103
104
            case 'client_credentials':
105
                return $this->grantTypeClientCredentials($options);
106
107
            case 'refresh_token':
108
                return $this->grantTypeRefreshToken($requestBody);
109
110
            case 'urn:ietf:params:oauth:grant-type:jwt-bearer':
111
                return $this->grantTypeJwtBearer($requestBody);
112
        }
113
        throw new \RuntimeException("Test grant type not implemented: $grantType");
114
    }
115
116
    /**
117
     * @return array
118
     */
119
    protected function validTokenResponse()
120
    {
121
        $token = [
122
            'access_token' => 'token',
123
            'refresh_token' => 'refreshToken',
124
            'token_type' => 'bearer',
125
        ];
126
127
        if (isset($this->options[self::KEY_TOKEN_INVALID_COUNT])) {
128
            $token['access_token'] = 'tokenInvalid';
129
        } elseif (isset($this->options[self::KEY_TOKEN_EXPIRES_IN])) {
130
            $token['expires_in'] = $this->options[self::KEY_TOKEN_EXPIRES_IN];
131
        }
132
133
        return new Response(200, [], json_encode($token));
134
    }
135
136
    /**
137
     * @param array  $requestBody
138
     *
139
     * @return array
140
     *   The response as expected by the MockHandler.
141
     */
142
    protected function grantTypePassword(array $requestBody)
143
    {
144
        if ($requestBody['username'] != 'validUsername' || $requestBody['password'] != 'validPassword') {
145
            // @todo correct response headers
146
            return new Response(401);
147
        }
148
149
        return $this->validTokenResponse();
150
    }
151
152
    /**
153
     * @param array  $options
154
     *
155
     * @return array
156
     *   The response as expected by the MockHandler.
157
     */
158
    protected function grantTypeClientCredentials(array $options)
159
    {
160
        if (!isset($options['auth']) || !isset($options['auth'][1]) || $options['auth'][1] != 'testSecret') {
161
            // @todo correct response headers
162
            return new Response(401);
163
        }
164
165
        return $this->validTokenResponse();
166
    }
167
168
    /**
169
     * @param array  $requestBody
170
     *
171
     * @return array
172
     */
173
    protected function grantTypeRefreshToken(array $requestBody)
174
    {
175
        if ($requestBody['refresh_token'] == 'refreshTokenInvalid') {
176
            return new Response(401);
177
        }
178
179
        return $this->validTokenResponse();
180
    }
181
182
    /**
183
     * @param array  $requestBody
184
     *
185
     * @return array
186
     */
187
    protected function grantTypeJwtBearer(array $requestBody)
188
    {
189
        if (!array_key_exists('assertion', $requestBody)) {
190
            return new Response(401);
191
        }
192
193
        return $this->validTokenResponse();
194
    }
195
196
    /**
197
     * @param RequestInterface $request
198
     *
199
     * @return array
200
     */
201
    protected function mockApiCall(RequestInterface $request)
202
    {
203
        if (
204
            empty($request->getHeader('Authorization')) ||
205
            (
206
                $request->getHeader('Authorization')[0] == 'Bearer tokenInvalid' &&
207
                isset($this->options[self::KEY_TOKEN_INVALID_COUNT]) &&
208
                $this->tokenInvalidCount < $this->options[self::KEY_TOKEN_INVALID_COUNT]
209
            )
210
        ) {
211
            if ($request->getHeader('Authorization')[0] == 'Bearer tokenInvalid') {
212
                ++$this->tokenInvalidCount;
213
            }
214
215
            return new Response(401);
216
        }
217
218
        return new Response(200, [], json_encode('Hello World!'));
219
    }
220
}
221