AbstractRpTestCase::simulateAuthRedirect()   A
last analyzed

Complexity

Conditions 3
Paths 4

Size

Total Lines 20
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 3
eloc 12
nc 4
nop 1
dl 0
loc 20
rs 9.8666
c 1
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Facile\OpenIDClient\ConformanceTest;
6
7
use Http\Discovery\Psr17FactoryDiscovery;
8
use Http\Discovery\Psr18ClientDiscovery;
9
use PHPUnit\Framework\TestCase;
10
use Psr\Container\ContainerInterface;
11
use Psr\Http\Client\ClientInterface as HttpClient;
12
use Psr\Http\Message\RequestFactoryInterface;
13
use Psr\Http\Message\ServerRequestInterface;
14
use ReflectionFunction;
15
use Facile\OpenIDClient\Client\ClientBuilder;
16
use Facile\OpenIDClient\Client\ClientInterface;
17
use Facile\OpenIDClient\Client\Metadata\ClientMetadata;
18
use Facile\OpenIDClient\Issuer\IssuerBuilder;
19
use Laminas\Diactoros\RequestFactory;
20
use Laminas\Diactoros\ServerRequestFactory;
21
use Laminas\Diactoros\Uri;
22
use RuntimeException;
23
use Facile\OpenIDClient\Service\RegistrationService;
24
25
abstract class AbstractRpTestCase extends TestCase
26
{
27
    /** @var ContainerInterface */
28
    protected static $container;
29
30
    public static function setUpBeforeClass(): void
31
    {
32
        parent::setUpBeforeClass();
33
34
        self::$container = require __DIR__ . '/../config/container.php';
35
    }
36
37
    public function getRootTestUri(): string
38
    {
39
        return 'https://rp.certification.openid.net:8080/';
40
    }
41
42
    public function getRpID(): string
43
    {
44
        return 'tmv_php-openid-client';
45
    }
46
47
    public function getRpUri(): string
48
    {
49
        return $this->getRootTestUri() . $this->getRpID() . '/';
50
    }
51
52
    public function getTestUri(string $testId): string
53
    {
54
        return $this->getRpUri() . $testId;
55
    }
56
57
    public function getLogTestUri(string $testId): string
58
    {
59
        return $this->getRootTestUri() . 'log/' . $this->getRpID() . '/' . $testId . '.txt';
60
    }
61
62
    public function getRedirectUri(): string
63
    {
64
        return 'https://' . $this->getRpID() . '.dev/callback';
65
    }
66
67
    protected function simulateAuthRedirect(string $uri): ServerRequestInterface
68
    {
69
        /** @var HttpClient $client */
70
        $httpClient = $this->getContainer()->has(HttpClient::class)
71
            ? $this->getContainer()->get(HttpClient::class)
72
            : Psr18ClientDiscovery::find();
73
        $requestFactory = $this->getContainer()->has(RequestFactoryInterface::class)
74
            ? $this->getContainer()->get(RequestFactoryInterface::class)
75
            : Psr17FactoryDiscovery::findRequestFactory();
76
77
        $request = $requestFactory->createRequest('GET', $uri);
78
        $response = $httpClient->sendRequest($request);
79
80
        $serverRequestFactory = new ServerRequestFactory();
81
82
        /** @var string $location */
83
        $location = $response->getHeader('location')[0] ?? null;
84
        $this->assertIsString($location);
85
86
        return $serverRequestFactory->createServerRequest('GET', $location);
87
    }
88
89
    protected function parseQueryParams(string $uri): array
90
    {
91
        $uri = new Uri($uri);
92
        \parse_str($uri->getQuery(), $query);
93
94
        return $query;
95
    }
96
97
    protected function executeRpTest(string $profile, string $testName, callable $callback): void
98
    {
99
        echo $this->getClosureDump($callback);
100
101
        $testUtil = $this->getContainer()->get(RpTestUtil::class);
0 ignored issues
show
Unused Code introduced by
The assignment to $testUtil is dead and can be removed.
Loading history...
102
103
        try {
104
            $callback($profile, $testName);
105
        } catch (\Throwable $e) {
106
            throw $e;
107
        } finally {
108
            $this->getAndSaveTestLog($profile, $testName);
109
        }
110
    }
111
112
    public function registerClient(string $testName, array $metadata = []): ClientInterface
113
    {
114
        $registrationService = new RegistrationService();
0 ignored issues
show
Bug introduced by
The call to Facile\OpenIDClient\Serv...nService::__construct() has too few arguments starting with client. ( Ignorable by Annotation )

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

114
        $registrationService = /** @scrutinizer ignore-call */ new RegistrationService();

This check compares calls to functions or methods with their respective definitions. If the call has less arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
115
116
        $issuerBuilder = new IssuerBuilder();
117
        $issuer = $issuerBuilder->build($this->getTestUri($testName) . '/.well-known/openid-configuration');
118
119
        $clientMetadata = ClientMetadata::fromArray($registrationService->register($issuer, \array_merge([
120
            'redirect_uris' => [$this->getRedirectUri()],
121
            'contacts' => [
122
                '[email protected]',
123
            ],
124
        ], $metadata)));
125
126
        return (new ClientBuilder())
127
            ->setClientMetadata($clientMetadata)
128
            ->build();
129
    }
130
131
    protected function getClosureDump(callable $closure)
132
    {
133
        $str = 'function (';
134
        $r = new ReflectionFunction($closure);
135
        $params = [];
136
        foreach($r->getParameters() as $p) {
137
            $s = '';
138
            if($p->isArray()) {
139
                $s .= 'array ';
140
            } else if($p->getClass()) {
141
                $s .= $p->getClass()->name . ' ';
142
            }
143
            if($p->isPassedByReference()){
144
                $s .= '&';
145
            }
146
            $s .= '$' . $p->name;
147
            if($p->isOptional()) {
148
                $s .= ' = ' . var_export($p->getDefaultValue(), TRUE);
149
            }
150
            $params []= $s;
151
        }
152
        $str .= implode(', ', $params);
153
        $str .= '){' . PHP_EOL;
154
        $lines = file($r->getFileName());
155
        for ($l = $r->getStartLine(); $l < $r->getEndLine(); $l++) {
156
            $str .= $lines[$l];
157
        }
158
        return $str;
159
    }
160
161
    public function getAndSaveTestLog(string $profile, string $testName): string
162
    {
163
        /** @var HttpClient $httpClient */
164
        $httpClient = $this->getContainer()->get('httplug.clients.default');
165
        $request = (new RequestFactory())->createRequest('GET', $this->getLogTestUri($testName));
166
167
        $response = $httpClient->sendRequest($request);
168
169
        if (200 !== $response->getStatusCode()) {
170
            throw new \RuntimeException('Invalid log response status code');
171
        }
172
173
        $log = (string) $response->getBody();
174
175
        $logFilePath = __DIR__ . '/../log/' . ltrim($profile, '@') . '/' . $testName . '.txt';
176
        $dirname = \dirname($logFilePath);
177
178
        if (! \file_exists($dirname) && ! mkdir($concurrentDirectory = $dirname, 0777, true) && ! is_dir($concurrentDirectory)) {
179
            throw new \RuntimeException(sprintf('Directory "%s" was not created', $concurrentDirectory));
180
        }
181
182
        \file_put_contents($logFilePath, $log);
183
184
        return $log;
185
    }
186
187
    public function getRpProfile(): ?string
188
    {
189
        $value = $this->getAnnotations()['method']['rp-profile'][0] ?? null;
0 ignored issues
show
Bug introduced by
The method getAnnotations() does not exist on Facile\OpenIDClient\Conf...Test\AbstractRpTestCase. ( Ignorable by Annotation )

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

189
        $value = $this->/** @scrutinizer ignore-call */ getAnnotations()['method']['rp-profile'][0] ?? null;

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
190
191
        if (! $value) {
192
            throw new RuntimeException('No rp-profile annotation set');
193
        }
194
195
        return $value;
196
    }
197
198
    public function getRpTestId(): ?string
199
    {
200
        $value = $this->getAnnotations()['method']['rp-test-id'][0] ?? null;
201
202
        if (! $value) {
203
            throw new RuntimeException('No rp-test-id annotation set');
204
        }
205
206
        return $value;
207
    }
208
209
    /**
210
     * @return ContainerInterface
211
     */
212
    public function getContainer(): ContainerInterface
213
    {
214
        return static::$container;
215
    }
216
}
217