Failed Conditions
Push — master ( 8f957f...3e56ea )
by Zbigniew
05:35
created

SlackAuthenticator   A

Complexity

Total Complexity 12

Size/Duplication

Total Lines 132
Duplicated Lines 0 %

Coupling/Cohesion

Components 2
Dependencies 17

Test Coverage

Coverage 12.5%

Importance

Changes 0
Metric Value
wmc 12
lcom 2
cbo 17
dl 0
loc 132
ccs 7
cts 56
cp 0.125
rs 10
c 0
b 0
f 0

8 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 9 1
A start() 0 6 1
A supports() 0 4 2
A getCredentials() 0 4 1
A getUser() 0 44 4
A onAuthenticationFailure() 0 14 1
A onAuthenticationSuccess() 0 10 1
A getSlackClient() 0 7 1
1
<?php
2
3
/** @noinspection DuplicatedCode */
4
5
declare(strict_types=1);
6
7
/*
8
 * This file is part of the zibios/sharep.
9
 *
10
 * (c) Zbigniew Ślązak
11
 */
12
13
namespace App\Security\GuardAuthenticator;
14
15
use AdamPaterson\OAuth2\Client\Provider\SlackResourceOwner;
16
use App\Entity\Access\SlackIdentity;
17
use App\Entity\Access\User;
18
use App\Entity\Parking\Member;
19
use App\Enum\Functional\RoleEnum;
20
use Doctrine\ORM\EntityManagerInterface;
21
use KnpU\OAuth2ClientBundle\Client\ClientRegistry;
22
use KnpU\OAuth2ClientBundle\Client\Provider\SlackClient;
23
use KnpU\OAuth2ClientBundle\Security\Authenticator\SocialAuthenticator;
24
use League\OAuth2\Client\Token\AccessToken;
25
use Symfony\Component\HttpFoundation\JsonResponse;
26
use Symfony\Component\HttpFoundation\RedirectResponse;
27
use Symfony\Component\HttpFoundation\Request;
28
use Symfony\Component\HttpFoundation\Response;
29
use Symfony\Component\Routing\Router;
30
use Symfony\Component\Routing\RouterInterface;
31
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
32
use Symfony\Component\Security\Core\Exception\AuthenticationException;
33
use Symfony\Component\Security\Core\User\UserProviderInterface;
34
35
class SlackAuthenticator extends SocialAuthenticator
36
{
37
    /**
38
     * @var ClientRegistry
39
     */
40
    protected $clientRegistry;
41
42
    /**
43
     * @var EntityManagerInterface
44
     */
45
    protected $entityManager;
46
    /**
47
     * @var Router
48
     */
49
    protected $router;
50
51 21
    public function __construct(
52
        ClientRegistry $clientRegistry,
53
        EntityManagerInterface $entityManager,
54
        RouterInterface $router
55
    ) {
56 21
        $this->clientRegistry = $clientRegistry;
57 21
        $this->entityManager = $entityManager;
58 21
        $this->router = $router;
0 ignored issues
show
Documentation Bug introduced by
$router is of type object<Symfony\Component\Routing\RouterInterface>, but the property $router was declared to be of type object<Symfony\Component\Routing\Router>. Are you sure that you always receive this specific sub-class here, or does it make sense to add an instanceof check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a given class or a super-class is assigned to a property that is type hinted more strictly.

Either this assignment is in error or an instanceof check should be added for that assignment.

class Alien {}

class Dalek extends Alien {}

class Plot
{
    /** @var  Dalek */
    public $villain;
}

$alien = new Alien();
$plot = new Plot();
if ($alien instanceof Dalek) {
    $plot->villain = $alien;
}
Loading history...
59 21
    }
60
61
    /**
62
     * @param Request                 $request       The request that resulted in an AuthenticationException
63
     * @param AuthenticationException $authException The exception that started the authentication process
0 ignored issues
show
Documentation introduced by
Should the type for parameter $authException not be null|AuthenticationException?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
64
     */
65
    public function start(
66
        Request $request,
67
        AuthenticationException $authException = null
68
    ): Response {
69
        return new Response('No authorized!', 403);
70
    }
71
72 21
    public function supports(Request $request): bool
73
    {
74 21
        return 'oauth2_slack_check' === $request->attributes->get('_route') && $request->isMethod('GET');
75
    }
76
77
    public function getCredentials(Request $request): AccessToken
78
    {
79
        return $this->fetchAccessToken($this->getSlackClient());
80
    }
81
82
    /**
83
     * @param AccessToken $credentials
84
     */
85
    public function getUser($credentials, UserProviderInterface $userProvider): ?User
86
    {
87
        /** @var SlackResourceOwner $slackUser */
88
        $slackUser = $this->getSlackClient()->fetchUserFromToken($credentials);
89
90
        /** @var User $user */
91
        $user = $this->entityManager
92
            ->getRepository(User::class)
93
            ->findOneBy(['email' => $slackUser->getEmail()]);
94
95
        if (!$user) {
96
            return null;
97
        }
98
99
        $slackIdentity = $user->getSlackIdentity();
100
        if (!$slackIdentity) {
101
            $slackIdentity = new SlackIdentity($user, (string) $slackUser->getId(), (string) $slackUser->getEmail());
102
        }
103
        $slackIdentity->setTeamId((string) $slackUser->toArray()['team_id']);
104
        $slackIdentity->setName((string) $slackUser->toArray()['name']);
105
        $slackIdentity->setIsDeleted((bool) $slackUser->toArray()['deleted']);
106
        $slackIdentity->setColor((string) $slackUser->toArray()['color']);
107
        $slackIdentity->setRealName((string) $slackUser->toArray()['real_name']);
108
        $slackIdentity->setTz((string) $slackUser->toArray()['tz']);
109
        $slackIdentity->setTzLabel((string) $slackUser->toArray()['tz_label']);
110
        $slackIdentity->setTzOffset((int) $slackUser->toArray()['tz_offset']);
111
        $slackIdentity->setIsAdmin((bool) $slackUser->toArray()['is_admin']);
112
        $slackIdentity->setIsBot((bool) $slackUser->toArray()['is_bot']);
113
        $slackIdentity->setUpdated((int) $slackUser->toArray()['updated']);
114
        $slackIdentity->setIsAppUser((bool) $slackUser->toArray()['is_app_user']);
115
116
        $member = $user->getMember();
117
        if (!$member) {
118
            $member = new Member($slackIdentity->getName(), RoleEnum::MEMBER(), $user);
119
        }
120
        $member->setName($slackIdentity->getName());
121
122
        $this->entityManager->persist($slackIdentity);
123
        $this->entityManager->persist($member);
124
        $this->entityManager->persist($user);
125
        $this->entityManager->flush();
126
127
        return $user;
128
    }
129
130
    public function onAuthenticationFailure(
131
        Request $request,
132
        AuthenticationException $exception
133
    ): JsonResponse {
134
        return new JsonResponse(
135
            [
136
                'message' => strtr(
137
                    $exception->getMessageKey(),
138
                    $exception->getMessageData()
139
                ),
140
            ],
141
            JsonResponse::HTTP_FORBIDDEN
142
        );
143
    }
144
145
    /**
146
     * @param string $providerKey The provider (i.e. firewall) key
147
     */
148
    public function onAuthenticationSuccess(
149
        Request $request,
150
        TokenInterface $token,
151
        $providerKey
152
    ): ?Response {
153
        return new RedirectResponse(
154
            $this->router->generate('site_page_contact'),
155
            Response::HTTP_TEMPORARY_REDIRECT
156
        );
157
    }
158
159
    private function getSlackClient(): SlackClient
160
    {
161
        $client = $this->clientRegistry->getClient('slack');
162
        assert($client instanceof SlackClient);
163
164
        return $client;
165
    }
166
}
167