Passed
Pull Request — master (#190)
by AD
05:46 queued 03:08
created

Keycloak::getIdentity()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 23
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 1.0109

Importance

Changes 1
Bugs 0 Features 1
Metric Value
cc 1
eloc 15
nc 1
nop 1
dl 0
loc 23
ccs 7
cts 9
cp 0.7778
crap 1.0109
rs 9.7666
c 1
b 0
f 1
1
<?php
2
3
declare(strict_types=1);
4
5
namespace SocialConnect\OpenIDConnect\Provider;
6
7
use SocialConnect\Common\ArrayHydrator;
8
use SocialConnect\Common\Entity\User;
9
use SocialConnect\Common\Exception\InvalidArgumentException;
10
use SocialConnect\Common\HttpStack;
11
use SocialConnect\OpenIDConnect\AbstractProvider;
12
use SocialConnect\OpenIDConnect\AccessToken;
13
use SocialConnect\Provider\AccessTokenInterface;
14
use SocialConnect\Provider\Session\SessionInterface;
15
16
class Keycloak extends AbstractProvider
17
{
18
    const NAME = 'keycloak';
19
20
    protected $name;
21
22
    protected $baseUrl;
23
24
    protected $realm;
25
26
    protected $protocol = 'openid-connect';
27
28
    protected $hydrateMapper = [];
29
30 8
    public function __construct(HttpStack $httpStack, SessionInterface $session, array $parameters)
31
    {
32 8
        parent::__construct($httpStack, $session, $parameters);
33
34 8
        $this->name = isset($parameters['name']) ? $parameters['name'] : self::NAME;
35
36 8
        $this->baseUrl = rtrim($this->getRequiredStringParameter('baseUrl', $parameters), '/') . '/';
37
38 8
        $this->realm = $this->getRequiredStringParameter('realm', $parameters);
39
40 8
        if (isset($parameters['protocol'])) {
41
            $this->protocol = $parameters['protocol'];
42
        }
43
44 8
        if (isset($parameters['hydrateMapper'])) {
45
            $this->hydrateMapper = $parameters['hydrateMapper'];
46
        }
47
    }
48
49 5
    public function getBaseUri()
50
    {
51 5
        return $this->baseUrl;
52
    }
53
54 1
    public function getName()
55
    {
56 1
        return $this->name;
57
    }
58
59 1
    public function prepareRequest(string $method, string $uri, array &$headers, array &$query, ?AccessTokenInterface $accessToken = null): void
60
    {
61 1
        if ($accessToken) {
62 1
            $headers['Authorization'] = 'Bearer ' . $accessToken->getToken();
63
        }
64
    }
65
66 1
    public function getIdentity(AccessTokenInterface $accessToken)
67
    {
68 1
        $url = sprintf('realms/%s/protocol/%s/userinfo', $this->realm, $this->protocol);
69
70 1
        $response = $this->request('GET', $url, [], $accessToken, []);
71
72 1
        $hydrator = new ArrayHydrator($this->hydrateMapper + [
73
                'sub' => 'id',
74
                'preferred_username' => 'username',
75
                'given_name' => 'firstname',
76
                'family_name' => 'lastname',
77
                'email' => 'email',
78
                'email_verified' => 'emailVerified',
79
                'name' => 'fullname',
80 1
                'gender' => static function ($value, User $user) {
81
                    $user->setSex($value);
82
                },
83 1
                'birthdate' => static function ($value, User $user) {
84
                    $user->setBirthday(date_create($value, new \DateTimeZone('UTC')));
85
                },
86
            ]);
87
88 1
        return $hydrator->hydrate(new User(), $response);
89
    }
90
91 1
    public function getAuthorizeUri()
92
    {
93 1
        return $this->getBaseUri() . sprintf('realms/%s/protocol/%s/auth', $this->realm, $this->protocol);
94
    }
95
96 1
    public function getRequestTokenUri()
97
    {
98 1
        return $this->getBaseUri() . sprintf('realms/%s/protocol/%s/token', $this->realm, $this->protocol);
99
    }
100
101 1
    public function getOpenIdUrl()
102
    {
103 1
        return $this->getBaseUri() . sprintf('realms/%s/.well-known/openid-configuration', $this->realm);
104
    }
105
106
    public function extractIdentity(AccessTokenInterface $accessToken)
107
    {
108
        if (!$accessToken instanceof AccessToken) {
109
            throw new InvalidArgumentException(
110
                '$accessToken must be instance AccessToken'
111
            );
112
        }
113
114
        $jwt = $accessToken->getJwt();
115
116
        $hydrator = new ArrayHydrator($this->hydrateMapper + [
117
            'sub' => 'id',
118
            'preferred_username' => 'username',
119
            'given_name' => 'firstname',
120
            'family_name' => 'lastname',
121
            'email' => 'email',
122
            'email_verified' => 'emailVerified',
123
            'name' => 'fullname',
124
            'gender' => static function ($value, User $user) {
125
                $user->setSex($value);
126
            },
127
            'birthdate' => static function ($value, User $user) {
128
                $user->setBirthday(date_create($value, new \DateTimeZone('UTC')));
129
            },
130
        ]);
131
132
        /** @var User $user */
133
        $user = $hydrator->hydrate(new User(), $jwt->getPayload());
134
135
        return $user;
136
    }
137
138
    public function getScopeInline()
139
    {
140
        $scopes = $this->scope;
141
142
        if (!in_array('openid', $scopes)) {
143
            array_unshift($scopes, 'openid');
144
        }
145
146
        return implode(' ', $scopes);
147
    }
148
}
149