Issues (37)

src/Provider/Provider.php (1 issue)

Labels
Severity
1
<?php
2
3
namespace Parroauth2\Client\Provider;
4
5
use Jose\Component\Core\JWKSet;
6
use Parroauth2\Client\ClientConfig;
7
use Parroauth2\Client\ClientInterface;
8
use Parroauth2\Client\Exception\OAuthServerException;
9
use Parroauth2\Client\Exception\Parroauth2Exception;
10
use Parroauth2\Client\Exception\UnsupportedServerOperation;
11
use Parroauth2\Client\Factory\ClientFactoryInterface;
12
use Psr\Http\Client\ClientInterface as PsrClientInterface;
13
use Psr\Http\Message\RequestFactoryInterface;
14
use Psr\Http\Message\RequestInterface;
15
use Psr\Http\Message\ResponseInterface;
16
use Psr\Http\Message\StreamFactoryInterface;
17
use Psr\Http\Message\StreamInterface;
18
19
/**
20
 * The authorization provider
21
 *
22
 * Handle the HTTP operations, and create clients
23
 */
24
final class Provider implements ProviderInterface
25
{
26
    /**
27
     * @var ClientFactoryInterface
28
     */
29
    private $clientFactory;
30
31
    /**
32
     * @var PsrClientInterface
33
     */
34
    private $httpClient;
35
36
    /**
37
     * @var RequestFactoryInterface
38
     */
39
    private $requestFactory;
40
41
    /**
42
     * @var StreamFactoryInterface
43
     */
44
    private $streamFactory;
45
46
    /**
47
     * @var ProviderConfig
48
     */
49
    private $config;
50
51
52
    /**
53
     * Provider constructor.
54
     *
55
     * @param ClientFactoryInterface $clientFactory
56
     * @param PsrClientInterface $httpClient
57
     * @param RequestFactoryInterface $requestFactory
58
     * @param StreamFactoryInterface $streamFactory
59
     * @param ProviderConfig $config
60
     */
61 194
    public function __construct(ClientFactoryInterface $clientFactory, PsrClientInterface $httpClient, RequestFactoryInterface $requestFactory, StreamFactoryInterface $streamFactory, ProviderConfig $config)
62
    {
63 194
        $this->clientFactory = $clientFactory;
64 194
        $this->httpClient = $httpClient;
65 194
        $this->requestFactory = $requestFactory;
66 194
        $this->streamFactory = $streamFactory;
67 194
        $this->config = $config;
68 194
    }
69
70
    /**
71
     * {@inheritdoc}
72
     */
73 132
    public function openid(): bool
74
    {
75 132
        return $this->config->openid();
76
    }
77
78
    /**
79
     * {@inheritdoc}
80
     */
81 26
    public function issuer(): string
82
    {
83 26
        return $this->metadata('issuer') ?: $this->config->url();
84
    }
85
86
    /**
87
     * {@inheritdoc}
88
     */
89 74
    public function metadata(string $parameter, $default = null)
90
    {
91 74
        return $this->config[$parameter] ?? $default;
92
    }
93
94
    /**
95
     * {@inheritdoc}
96
     */
97 167
    public function supportsEndpoint(string $name): bool
98
    {
99 167
        return isset($this->config[$name . '_endpoint']);
100
    }
101
102
    /**
103
     * {@inheritdoc}
104
     */
105 95
    public function uri(string $name, array $queryParameters = []): string
106
    {
107 95
        if (!$this->supportsEndpoint($name)) {
108 1
            throw new UnsupportedServerOperation(
109 1
                'The endpoint "' . $name . '" is not supported by the authorization provider'
110
            );
111
        }
112
113 94
        $baseUri = $this->config[$name . '_endpoint'];
114
115 94
        if (empty($queryParameters)) {
116 59
            return $baseUri;
117
        }
118
119 58
        return $baseUri . (strpos($baseUri, '?') === false ? '?' : '&') . http_build_query($queryParameters);
120
    }
121
122
    /**
123
     * {@inheritdoc}
124
     */
125 56
    public function request(string $method, string $endpoint, array $queryParameters = [], $body = null): RequestInterface
126
    {
127 56
        if (is_array($body) || (is_object($body) && !$body instanceof StreamInterface)) {
128 42
            $body = http_build_query($body);
129
        }
130
131 56
        $request = $this->requestFactory->createRequest($method, $this->uri($endpoint, $queryParameters));
132
133 56
        if (!$body) {
134 22
            return $request;
135
        }
136
137 41
        foreach ($this->metadata('default_headers', []) as $headerName => $value) {
138 1
            $request = $request->withHeader($headerName, $value);
139
        }
140
141 41
        if (is_string($body)) {
142 41
            return $request->withBody($this->streamFactory->createStream($body));
143
        }
144
145
        if (is_resource($body)) {
146
            return $request->withBody($this->streamFactory->createStreamFromResource($body));
147
        }
148
149
        /** @var StreamInterface $body */
150
        return $request->withBody($body);
151
    }
152
153
    /**
154
     * {@inheritdoc}
155
     *
156
     * @throws Parroauth2Exception When a server error occurs
157
     */
158 56
    public function sendRequest(RequestInterface $request): ResponseInterface
159
    {
160 56
        $response = $this->httpClient->sendRequest($request);
161
162 56
        if (!in_array(intdiv($response->getStatusCode(), 100), [4, 5])) {
163 50
            return $response;
164
        }
165
166 9
        $body = json_decode((string) $response->getBody(), true);
167
168 9
        if (!$body) {
169 1
            throw new Parroauth2Exception('An error has occurred:' . PHP_EOL . $response->getBody());
170
        }
171
172 8
        if (is_string($body)) {
173 1
            throw new Parroauth2Exception($body);
174
        }
175
176 7
        if (is_array($body) && isset($body['error'])) {
177 6
            if (!is_string($body['error'])) {
178 1
                throw new Parroauth2Exception('An error has occurred:' . PHP_EOL . print_r($body['error'], true));
0 ignored issues
show
Are you sure print_r($body['error'], true) of type string|true can be used in concatenation? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

178
                throw new Parroauth2Exception('An error has occurred:' . PHP_EOL . /** @scrutinizer ignore-type */ print_r($body['error'], true));
Loading history...
179
            } else {
180 5
                throw OAuthServerException::create(
181 5
                    $body['error'],
182 5
                    $body['error_description'] ?? null,
183 5
                    $body['hint'] ?? null
184
                );
185
            }
186
        }
187
188 1
        throw new Parroauth2Exception('An error has occurred');
189
    }
190
191
    /**
192
     * {@inheritdoc}
193
     */
194 142
    public function client(ClientConfig $config): ClientInterface
195
    {
196 142
        return $this->clientFactory->create($this, $config);
197
    }
198
199
    /**
200
     * {@inheritdoc}
201
     */
202 42
    public function keySet(): JWKSet
203
    {
204 42
        if (isset($this->config['jwks'])) {
205 22
            return $this->config['jwks'];
206
        }
207
208 24
        if (!isset($this->config['jwks_uri'])) {
209
            throw new \LogicException('Cannot get key set : neither jwks nor jwks_uri are configured');
210
        }
211
212 24
        $response = $this->sendRequest($this->requestFactory->createRequest('GET', $this->config['jwks_uri']));
213
214 24
        return $this->config['jwks'] = JWKSet::createFromJson((string) $response->getBody());
215
    }
216
}
217