Completed
Push — master ( 2cc184...c6f8e7 )
by Paweł
14s
created

TokenAuthenticator   A

Complexity

Total Complexity 18

Size/Duplication

Total Lines 165
Duplicated Lines 0 %

Coupling/Cohesion

Components 3
Dependencies 15

Test Coverage

Coverage 69.23%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 18
c 1
b 0
f 0
lcom 3
cbo 15
dl 0
loc 165
rs 9.1666
ccs 27
cts 39
cp 0.6923

10 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 11 1
A checkCredentials() 0 13 3
A onAuthenticationFailure() 0 9 1
A start() 0 9 1
A supportsRememberMe() 0 4 1
A getCredentials() 0 18 3
B getUser() 0 30 4
A onAuthenticationSuccess() 0 9 2
A getIntention() 0 4 1
A getToken() 0 4 1
1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * This file is part of the Superdesk Web Publisher Core Bundle.
7
 *
8
 * Copyright 2015 Sourcefabric z.u. and contributors.
9
 *
10
 * For the full copyright and license information, please see the
11
 * AUTHORS and LICENSE files distributed with this source code.
12
 *
13
 * @copyright 2015 Sourcefabric z.ú
14
 * @license http://www.superdesk.org/license
15
 */
16
17
namespace SWP\Bundle\CoreBundle\Security\Authenticator;
18
19
use SWP\Bundle\CoreBundle\EventListener\ActivateLivesiteEditorListener;
20
use SWP\Bundle\CoreBundle\Model\ApiKeyInterface;
21
use SWP\Bundle\CoreBundle\Model\UserInterface as CoreUserInterface;
22
use SWP\Bundle\CoreBundle\Repository\ApiKeyRepository;
23
use SWP\Bundle\MultiTenancyBundle\MultiTenancyEvents;
24
use SWP\Component\MultiTenancy\Context\TenantContextInterface;
25
use SWP\Component\MultiTenancy\Repository\TenantRepositoryInterface;
26
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
27
use Symfony\Component\HttpFoundation\Request;
28
use Symfony\Component\HttpFoundation\JsonResponse;
29
use Symfony\Component\Security\Core\User\UserInterface;
30
use Symfony\Component\Security\Guard\AbstractGuardAuthenticator;
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 TokenAuthenticator extends AbstractGuardAuthenticator
36
{
37
    const INTENTION_LIVESITE_EDITOR = 'livesite_editor';
38
39
    /**
40
     * @var ApiKeyRepository
41
     */
42
    protected $apiKeyRepository;
43
44
    /**
45
     * @var TenantContextInterface
46
     */
47
    protected $tenantContext;
48
49
    /**
50
     * @var TenantRepositoryInterface
51
     */
52
    protected $tenantRepository;
53 101
54
    /**
55
     * @var EventDispatcherInterface
56
     */
57
    protected $eventDispatcher;
58 101
59 101
    /**
60 101
     * TokenAuthenticator constructor.
61 101
     *
62
     * @param ApiKeyRepository          $apiKeyRepository
63
     * @param TenantContextInterface    $tenantContext
64
     * @param TenantRepositoryInterface $tenantRepository
65
     * @param EventDispatcherInterface  $eventDispatcher
66
     */
67 78
    public function __construct(
68
        ApiKeyRepository $apiKeyRepository,
69 78
        TenantContextInterface $tenantContext,
70
        TenantRepositoryInterface $tenantRepository,
71
        EventDispatcherInterface $eventDispatcher
72
    ) {
73
        $this->apiKeyRepository = $apiKeyRepository;
74
        $this->tenantContext = $tenantContext;
75
        $this->tenantRepository = $tenantRepository;
76 78
        $this->eventDispatcher = $eventDispatcher;
77
    }
78
79
    /**
80 78
     * Called on every request. Return whatever credentials you want,
81
     * or null to stop authentication.
82 78
     */
83 78
    public function getCredentials(Request $request)
84 78
    {
85 78
        if (!$token = $this->getToken($request)) {
86
            // no token? Return null and no other methods will be called
87 78
            return;
88
        }
89
90
        $data = [
91
            'token' => $token,
92 78
        ];
93 78
94
        if (self::INTENTION_LIVESITE_EDITOR === $this->getIntention($request)) {
95
            $data['intention'] = self::INTENTION_LIVESITE_EDITOR;
96 78
        }
97 78
98
        // What you return here will be passed to getUser() as $credentials
99 78
        return $data;
100
    }
101
102 78
    public function getUser($credentials, UserProviderInterface $userProvider)
103
    {
104 78
        $this->eventDispatcher->dispatch(MultiTenancyEvents::TENANTABLE_DISABLE);
105 78
106 78
        /** @var ApiKeyInterface $apiKey */
107
        $apiKey = $this->apiKeyRepository
108 78
            ->getValidToken(str_replace('Basic ', '', $credentials['token']))
109 78
            ->getQuery()
110
            ->getOneOrNullResult();
111
112
        $this->eventDispatcher->dispatch(MultiTenancyEvents::TENANTABLE_ENABLE);
113
114
        if (null === $apiKey) {
115
            return;
116 78
        }
117
118
        // extend valid time after login
119 78
        $apiKey->extendValidTo();
120
121
        /** @var CoreUserInterface $user */
122
        $user = $apiKey->getUser();
123
        $user->addRole('ROLE_INTERNAL_API');
124
        $this->apiKeyRepository->flush();
125
126
        if (array_key_exists('intention', $credentials) && self::INTENTION_LIVESITE_EDITOR === $credentials['intention']) {
127
            $user->addRole('ROLE_LIVESITE_EDITOR');
128
        }
129
130
        return $user;
131
    }
132
133
    public function checkCredentials($credentials, UserInterface $user)
134
    {
135
        if ($user instanceof CoreUserInterface) {
136
            $currentOrganization = $this->tenantContext->getTenant()->getOrganization();
137
            $userOrganization = $user->getOrganization();
138
139
            if ($currentOrganization->getId() === $userOrganization->getId()) {
140
                return true;
141
            }
142
        }
143
144
        return false;
145
    }
146
147
    public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey)
148
    {
149
        if (self::INTENTION_LIVESITE_EDITOR === $this->getIntention($request)) {
150
            $request->attributes->set(ActivateLivesiteEditorListener::ACTIVATION_KEY, $this->getToken($request));
151
        }
152
153
        // on success, let the request continue
154
        return;
155
    }
156
157
    public function onAuthenticationFailure(Request $request, AuthenticationException $exception)
158
    {
159
        $data = [
160
            'status' => 403,
161
            'message' => strtr($exception->getMessageKey(), $exception->getMessageData()),
162
        ];
163
164
        return new JsonResponse($data, 403);
165
    }
166
167
    /**
168
     * Called when authentication is needed, but it's not sent.
169
     */
170
    public function start(Request $request, AuthenticationException $authException = null)
171
    {
172
        $data = [
173
            'status' => 401,
174
            'message' => 'Authentication Required',
175
        ];
176
177
        return new JsonResponse($data, 401);
178
    }
179
180
    public function supportsRememberMe()
181
    {
182
        return false;
183
    }
184
185
    private function getIntention(Request $request)
186
    {
187
        return $request->headers->get('Intention', $request->query->get('intention'));
188
    }
189
190
    /**
191
     * @param Request $request
192
     *
193
     * @return string
194
     */
195
    private function getToken(Request $request)
196
    {
197
        return $request->headers->get('Authorization', $request->query->get('auth_token'));
198
    }
199
}
200