Test Failed
Push — issue#666 ( f5ce0d...ce3976 )
by Guilherme
28:08
created

RemoteClaimFetcher   A

Complexity

Total Complexity 19

Size/Duplication

Total Lines 154
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 14

Test Coverage

Coverage 98.33%

Importance

Changes 0
Metric Value
dl 0
loc 154
ccs 59
cts 60
cp 0.9833
rs 10
c 0
b 0
f 0
wmc 19
lcom 1
cbo 14

6 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 11 1
A fetchRemoteClaim() 0 16 2
B discoverClaimUri() 0 30 6
B getRemoteClaim() 0 24 4
A findClaimProvider() 0 10 3
A getExistingClaimProvider() 0 16 3
1
<?php
2
/**
3
 * This file is part of the login-cidadao project or it's bundles.
4
 *
5
 * (c) Guilherme Donato <guilhermednt on github>
6
 *
7
 * For the full copyright and license information, please view the LICENSE
8
 * file that was distributed with this source code.
9
 */
10
11
namespace LoginCidadao\RemoteClaimsBundle\Fetcher;
12
13
use Doctrine\ORM\EntityManagerInterface;
14
use GuzzleHttp\Client;
15
use LoginCidadao\OAuthBundle\Entity\ClientRepository;
16
use LoginCidadao\OAuthBundle\Model\ClientInterface;
17
use LoginCidadao\RemoteClaimsBundle\Entity\RemoteClaim;
18
use LoginCidadao\RemoteClaimsBundle\Entity\RemoteClaimRepository;
19
use LoginCidadao\RemoteClaimsBundle\Exception\ClaimProviderNotFoundException;
20
use LoginCidadao\RemoteClaimsBundle\Model\ClaimProviderInterface;
21
use LoginCidadao\RemoteClaimsBundle\Model\HttpUri;
22
use LoginCidadao\RemoteClaimsBundle\Model\RemoteClaimFetcherInterface;
23
use LoginCidadao\RemoteClaimsBundle\Model\RemoteClaimInterface;
24
use LoginCidadao\RemoteClaimsBundle\Model\TagUri;
25
use LoginCidadao\RemoteClaimsBundle\Parser\RemoteClaimParser;
26
use LoginCidadao\OAuthBundle\Entity\Client as ClaimProvider;
27
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
28
29
class RemoteClaimFetcher implements RemoteClaimFetcherInterface
30
{
31
    /** @var  Client */
32
    private $httpClient;
33
34
    /** @var RemoteClaimRepository */
35
    private $claimRepo;
36
37
    /** @var ClientRepository */
38
    private $clientRepo;
39
40
    /** @var EntityManagerInterface */
41
    private $em;
42
43
    /**
44
     * RemoteClaimFetcher constructor.
45
     * @param Client $httpClient
46
     * @param EntityManagerInterface $em
47
     * @param RemoteClaimRepository $claimRepository
48
     * @param ClientRepository $clientRepository
49
     */
50 8
    public function __construct(
51
        Client $httpClient,
52
        EntityManagerInterface $em,
53
        RemoteClaimRepository $claimRepository,
54
        ClientRepository $clientRepository
55
    ) {
56 8
        $this->em = $em;
57 8
        $this->httpClient = $httpClient;
58 8
        $this->claimRepo = $claimRepository;
59 8
        $this->clientRepo = $clientRepository;
60 8
    }
61
62 7
    public function fetchRemoteClaim($claimUri)
63
    {
64
        try {
65 7
            $uri = HttpUri::createFromString($claimUri);
66 2
        } catch (\Exception $e) {
67 2
            $claimName = TagUri::createFromString($claimUri);
68 2
            $uri = $this->discoverClaimUri($claimName);
69
        }
70
71 6
        $response = $this->httpClient->get($uri);
72 6
        $body = $response->getBody()->__toString();
73
74 6
        $remoteClaim = RemoteClaimParser::parseClaim($body, new RemoteClaim(), new ClaimProvider());
75
76 6
        return $remoteClaim;
77
    }
78
79
    /**
80
     * @param TagUri $claimName
81
     * @return string
82
     */
83 2
    public function discoverClaimUri($claimName)
84
    {
85 2
        if (!$claimName instanceof TagUri) {
86
            $claimName = TagUri::createFromString($claimName);
87
        }
88
89 2
        $uri = HttpUri::createFromComponents([
90 2
            'scheme' => 'https',
91 2
            'host' => $claimName->getAuthorityName(),
92 2
            'path' => '/.well-known/webfinger',
93 2
            'query' => http_build_query([
94 2
                'resource' => $claimName->__toString(),
95 2
                'rel' => 'http://openid.net/specs/connect/1.0/claim',
96
            ]),
97
        ]);
98
99 2
        $response = $this->httpClient->get($uri->__toString());
100 2
        $json = json_decode($response->getBody());
101
102 2
        if (property_exists($json, 'links')) {
103 1
            foreach ($json->links as $link) {
104 1
                if ($link->rel === 'http://openid.net/specs/connect/1.0/claim'
105 1
                    && $json->subject === $claimName->__toString()) {
106 1
                    return $link->href;
107
                }
108
            }
109
        }
110
111 1
        throw new NotFoundHttpException("Couldn't find the Claim's URI");
112
    }
113
114
    /**
115
     * Fetches a RemoteClaimInterface via <code>fetchRemoteClaim</code>, persisting and returning the result.
116
     * @param TagUri|string $claimUri
117
     * @return RemoteClaimInterface
118
     * @throws ClaimProviderNotFoundException
119
     */
120 4
    public function getRemoteClaim($claimUri)
121
    {
122 4
        $remoteClaim = $this->fetchRemoteClaim($claimUri);
123
124 4
        $existingClaim = $this->claimRepo->findOneBy(['name' => $remoteClaim->getName()]);
125 4
        if ($existingClaim instanceof RemoteClaimInterface) {
126 1
            $remoteClaim = $existingClaim;
127 1
            $newClaim = false;
128
        } else {
129 3
            $newClaim = true;
130
        }
131
132 4
        $provider = $this->getExistingClaimProvider($remoteClaim->getProvider());
133 2
        if ($provider instanceof ClaimProviderInterface) {
134 2
            $remoteClaim->setProvider($provider);
135
        }
136
137 2
        if ($newClaim) {
138 1
            $this->em->persist($remoteClaim);
139 1
            $this->em->flush();
140
        }
141
142 2
        return $remoteClaim;
143
    }
144
145
    /**
146
     * @param string[] $redirectUris
147
     * @return ClientInterface
148
     */
149 4
    private function findClaimProvider($redirectUris)
150
    {
151 4
        $clients = $this->clientRepo->findByRedirectUris($redirectUris);
152
153 4
        if (count($clients) > 1) {
154 1
            throw new \InvalidArgumentException('Ambiguous redirect_uris. More than one Relying Party found.');
155
        }
156
157 3
        return empty($clients) ? null : reset($clients);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The expression empty($clients) ? null : reset($clients); of type LoginCidadao\OAuthBundle...l\ClientInterface|false adds false to the return on line 157 which is incompatible with the return type documented by LoginCidadao\RemoteClaim...cher::findClaimProvider of type LoginCidadao\OAuthBundle\Model\ClientInterface. It seems like you forgot to handle an error condition.
Loading history...
158
    }
159
160
    /**
161
     * Gets the persisted/ORM-attached ClaimProvider
162
     * @param ClaimProviderInterface|null $provider
163
     * @return ClientInterface|null
164
     * @throws ClaimProviderNotFoundException
165
     */
166 4
    private function getExistingClaimProvider(ClaimProviderInterface $provider = null)
167
    {
168 4
        $existingProvider = null;
169 4
        if ($provider instanceof ClaimProviderInterface) {
170 4
            $existingProvider = $this->findClaimProvider($provider->getRedirectUris());
171
        }
172
173 3
        if ($existingProvider instanceof ClaimProviderInterface) {
174 2
            return $existingProvider;
175
        }
176
177
        // No pre-existing provider was found. Throw Exception!
178 1
        throw new ClaimProviderNotFoundException(
179 1
            "A Claim Provider was not found. This Identity Provider does NOT support Dynamic Claim Provider registration, so make sure it is already registered."
180
        );
181
    }
182
}
183