Completed
Push — master ( bb131b...ea520a )
by Bukashk0zzz
03:03
created

Security/JWTAuthenticator.php (1 issue)

Labels
Severity
1
<?php declare(strict_types = 1);
2
3
namespace AtlassianConnectBundle\Security;
4
5
use AtlassianConnectBundle\Entity\Tenant;
6
use AtlassianConnectBundle\Model\QSH;
0 ignored issues
show
The type AtlassianConnectBundle\Model\QSH was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
7
use Doctrine\Common\Persistence\ManagerRegistry;
8
use Doctrine\ORM\EntityManager;
9
use Firebase\JWT\JWT;
10
use Symfony\Component\HttpFoundation\Request;
11
use Symfony\Component\HttpFoundation\Response;
12
use Symfony\Component\HttpKernel\KernelInterface;
13
use Symfony\Component\Security\Core\Authentication\Token\PreAuthenticatedToken;
14
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
15
use Symfony\Component\Security\Core\Exception\AuthenticationException;
16
use Symfony\Component\Security\Core\Exception\BadCredentialsException;
17
use Symfony\Component\Security\Core\User\UserProviderInterface;
18
use Symfony\Component\Security\Http\Authentication\AuthenticationFailureHandlerInterface;
19
use Symfony\Component\Security\Http\Authentication\SimplePreAuthenticatorInterface;
20
21
/**
22
 * Class JWTAuthenticator
23
 */
24
class JWTAuthenticator implements SimplePreAuthenticatorInterface, AuthenticationFailureHandlerInterface
25
{
26
    /**
27
     * @var JWTUserProvider
28
     */
29
    protected $userProvider;
30
31
    /**
32
     * @var KernelInterface
33
     */
34
    protected $kernel;
35
36
    /**
37
     * @var EntityManager
38
     */
39
    protected $em;
40
41
    /**
42
     * @var string
43
     */
44
    protected $tenantEntityClass;
45
46
    /**
47
     * @var int
48
     */
49
    protected $devTenant;
50
51
    /**
52
     * JWTAuthenticator constructor.
53
     *
54
     * @param JWTUserProvider $userProvider
55
     * @param KernelInterface $kernel
56
     * @param ManagerRegistry $registry
57
     * @param string          $tenantEntityClass
58
     * @param int             $devTenant
59
     */
60
    public function __construct(JWTUserProvider $userProvider, KernelInterface $kernel, ManagerRegistry $registry, string $tenantEntityClass, int $devTenant)
61
    {
62
        $this->userProvider = $userProvider;
63
        $this->kernel = $kernel;
64
        $this->em = $registry->getManager();
65
        $this->tenantEntityClass = $tenantEntityClass;
66
        $this->devTenant = $devTenant;
67
    }
68
69
    /**
70
     * @param Request $request
71
     * @param mixed   $providerKey
72
     *
73
     * @return PreAuthenticatedToken
74
     */
75
    public function createToken(Request $request, $providerKey): PreAuthenticatedToken
76
    {
77
        $jwt = $request->query->get('jwt');
78
        if (!$jwt && $request->headers->has('authorization')) {
79
            $authorizationHeaderArray = \explode(' ', $request->headers->get('authorization'));
80
            if (\count($authorizationHeaderArray) > 1) {
81
                $jwt = $authorizationHeaderArray[1];
82
            }
83
        }
84
85
        if (!$jwt && $this->devTenant && ($this->kernel->getEnvironment() === 'dev')) {
86
            $tenant = $this->em->getRepository($this->tenantEntityClass)->find($this->devTenant);
87
            if ($tenant === null) {
88
                throw new \RuntimeException(\sprintf('Cant find tenant with id %s - please set atlassian_connect.dev_tenant to false to disable dedicated dev tenant OR add valid id', $this->devTenant));
89
            }
90
            $clientKey = $tenant->getClientKey();
91
            $sharedSecret = $tenant->getSharedSecret();
92
            $qshHelper = new QSH();
93
            $qsh = $qshHelper->create('GET', $request->getRequestUri());
94
            $payload = [
95
                'iss' => $clientKey,
96
                'iat' => \time(),
97
                'exp' => \strtotime('+1 day'),
98
                'qsh' => $qsh,
99
                'sub' => 'admin',
100
            ];
101
102
            $jwt = JWT::encode($payload, $sharedSecret);
103
        }
104
105
        if (!$jwt) {
106
            throw new BadCredentialsException('No JWT token found');
107
        }
108
109
        return new PreAuthenticatedToken('anon.', $jwt, $providerKey);
110
    }
111
112
    /**
113
     * @param TokenInterface        $token
114
     * @param UserProviderInterface $userProvider
115
     * @param mixed                 $providerKey
116
     *
117
     * @return PreAuthenticatedToken
118
     */
119
    public function authenticateToken(TokenInterface $token, UserProviderInterface $userProvider, $providerKey): PreAuthenticatedToken
120
    {
121
        $jwt = $token->getCredentials();
122
        $token = $this->userProvider->getDecodedToken($jwt);
123
        $clientKey = $token->iss;
124
125
        if (!$clientKey) {
126
            throw new AuthenticationException(
127
                \sprintf('API Key "%s" does not exist.', $jwt)
128
            );
129
        }
130
131
        /** @var Tenant $user */
132
        $user = $this->userProvider->loadUserByUsername($clientKey);
133
        if (\property_exists($token, 'sub')) {
134
            // for some reasons, when webhooks are called - field sub is undefined
135
            $user->setUsername($token->sub);
136
        }
137
138
        return new PreAuthenticatedToken($user, $jwt, $providerKey, $user->getRoles());
139
    }
140
141
    /**
142
     * @param TokenInterface $token
143
     * @param mixed          $providerKey
144
     *
145
     * @return bool
146
     */
147
    public function supportsToken(TokenInterface $token, $providerKey): bool
148
    {
149
        return $token instanceof PreAuthenticatedToken && $token->getProviderKey() === $providerKey;
150
    }
151
152
    /**
153
     * @param Request                 $request
154
     * @param AuthenticationException $exception
155
     *
156
     * @return Response
157
     */
158
    public function onAuthenticationFailure(Request $request, AuthenticationException $exception): Response
159
    {
160
        return new Response('Authentication Failed: '.$exception->getMessage(), 403);
161
    }
162
}
163