Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 43
0.00% covered (danger)
0.00%
0 / 7
CRAP
0.00% covered (danger)
0.00%
0 / 1
Provider
0.00% covered (danger)
0.00%
0 / 43
0.00% covered (danger)
0.00%
0 / 7
182
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
2
 setHttpClient
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 createResourceOwner
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getResourceOwnerData
0.00% covered (danger)
0.00%
0 / 13
0.00% covered (danger)
0.00%
0 / 1
30
 getOptionsFromJsonFile
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
2
 getBasicOptionsFromJsonFile
0.00% covered (danger)
0.00%
0 / 10
0.00% covered (danger)
0.00%
0 / 1
2
 readKeycloakConfig
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
12
1<?php
2
3namespace BO\Slim\Middleware\OAuth\Keycloak;
4
5use Stevenmaguire\OAuth2\Client\Provider\Keycloak;
6use BO\Zmsclient\OAuthService;
7use BO\Zmsclient\Psr7\Client;
8use League\OAuth2\Client\Token\AccessToken;
9use BO\Zmsentities\Useraccount;
10
11/**
12 * @SuppressWarnings(PHPMD)
13 * @psalm-suppress PropertyNotSetInConstructor Parent Keycloak options are assigned in the parent constructor.
14 */
15class Provider extends Keycloak
16{
17    const string PROVIDERNAME = 'keycloak';
18
19    protected OAuthService $oauthService;
20
21    public function __construct(mixed $client = null, ?OAuthService $oauthService = null)
22    {
23        $this->oauthService = $oauthService ?? new OAuthService(\App::$http, \App::CONFIG_SECURE_TOKEN);
24        $client = $client ?? new Client();
25        $options = $this->getOptionsFromJsonFile();
26        parent::__construct($options, ['httpClient' => $client]);
27    }
28
29    #[\Override]
30    public function setHttpClient($client)
31    {
32        $this->httpClient = $client;
33        return $this;
34    }
35
36    #[\Override]
37    protected function createResourceOwner(array $response, AccessToken $token): ResourceOwner
38    {
39        return new ResourceOwner($response);
40    }
41
42    public function getResourceOwnerData(AccessToken $token): Useraccount
43    {
44        $resourceOwner = $this->getResourceOwner($token);
45        $config = $this->oauthService->readConfig();
46        $ownerData = [
47            'username' => ($resourceOwner->getName() ?? '') . '@' . static::PROVIDERNAME,
48        ];
49        if (1 == $config->getPreference('oidc', 'onlyVerifiedMail')) {
50            $email = $resourceOwner->getEmail();
51            $ownerArray = $resourceOwner->toArray();
52            $emailVerified = $ownerArray['email_verified'] ?? false;
53            if (is_string($email) && $email !== '' && $emailVerified === true) {
54                $ownerData['email'] = $email;
55            }
56        } else {
57            $ownerData['email'] = $resourceOwner->getEmail();
58        }
59        return new Useraccount($ownerData);
60    }
61
62    private function getOptionsFromJsonFile(): array
63    {
64        $configData = $this->readKeycloakConfig();
65        $realmData = $this->getBasicOptionsFromJsonFile();
66        $realmData['clientSecret'] = $configData['credentials']['secret'] ?? '';
67        $realmData['authServerUrl'] = $configData['auth-server-url'] ?? '';
68        $realmData['verify'] = $configData['ssl-verify'] ?? true;
69        return $realmData;
70    }
71
72    public function getBasicOptionsFromJsonFile(): array
73    {
74        $configData = $this->readKeycloakConfig();
75        return [
76            'realm' => $configData['realm'] ?? '',
77            'clientId' => $configData['clientId'] ?? '',
78            'clientName' => $configData['clientName'] ?? '',
79            'redirectUri' => $configData['auth-redirect-url'] ?? '',
80            'logoutUri' => $configData['logout-redirect-url'] ?? '',
81            'version' => $configData['version'] ?? '',
82            'accessRole' => $configData['access-role'] ?? '',
83        ];
84    }
85
86    /**
87     * @return array<string, mixed>
88     */
89    private function readKeycloakConfig(): array
90    {
91        $configJson = file_get_contents(\App::APP_PATH . '/' . static::PROVIDERNAME . '.json');
92        if (!is_string($configJson)) {
93            throw new \RuntimeException('Unable to read keycloak.json');
94        }
95        $configData = json_decode($configJson, true);
96        if (!is_array($configData)) {
97            throw new \RuntimeException('Invalid keycloak.json');
98        }
99        return $configData;
100    }
101}