Completed
Push — master ( bbff6f...c51acf )
by Rafał
19:14 queued 09:31
created

AuthController::authenticateAction()   B

Complexity

Conditions 6
Paths 7

Size

Total Lines 25

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 13
CRAP Score 6

Importance

Changes 0
Metric Value
dl 0
loc 25
ccs 13
cts 13
cp 1
rs 8.8977
c 0
b 0
f 0
cc 6
nc 7
nop 1
crap 6
1
<?php
2
3
/*
4
 * This file is part of the Superdesk Web Publisher Core Bundle.
5
 *
6
 * Copyright 2016 Sourcefabric z.ú. and contributors.
7
 *
8
 * For the full copyright and license information, please see the
9
 * AUTHORS and LICENSE files distributed with this source code.
10
 *
11
 * @copyright 2016 Sourcefabric z.ú
12
 * @license http://www.superdesk.org/license
13
 */
14
15
namespace SWP\Bundle\CoreBundle\Controller;
16
17
use Doctrine\DBAL\Exception\UniqueConstraintViolationException;
18
use GuzzleHttp;
19
use Nelmio\ApiDocBundle\Annotation\ApiDoc;
20
use Symfony\Component\Routing\Annotation\Route;
21
use SWP\Bundle\CoreBundle\Form\Type\SuperdeskCredentialAuthenticationType;
22
use SWP\Bundle\CoreBundle\Form\Type\UserAuthenticationType;
23
use SWP\Bundle\CoreBundle\Model\ApiKeyInterface;
24
use SWP\Bundle\CoreBundle\Model\UserInterface;
25
use SWP\Component\Common\Response\ResponseContext;
26
use SWP\Component\Common\Response\SingleResourceResponse;
27
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
28
use Symfony\Component\HttpFoundation\Request;
29
use Symfony\Component\Security\Core\Exception\UsernameNotFoundException;
30
31
class AuthController extends Controller
0 ignored issues
show
Deprecated Code introduced by
The class Symfony\Bundle\Framework...e\Controller\Controller has been deprecated with message: since Symfony 4.2, use {@see AbstractController} instead.

This class, trait or interface has been deprecated. The supplier of the file has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the type will be removed from the class and what other constant to use instead.

Loading history...
32
{
33
    /**
34
     * Look for user matching provided credentials.
35
     *
36
     * @ApiDoc(
37
     *     resource=true,
38
     *     description="Look for user matching provided credentials",
39
     *     statusCodes={
40
     *         200="Returned on success.",
41
     *         401="No user found or not authorized."
42
     *     },
43
     *     input="SWP\Bundle\CoreBundle\Form\Type\UserAuthenticationType"
44
     * )
45
     * @Route("/api/{version}/auth/", options={"expose"=true}, defaults={"version"="v2"}, methods={"POST"}, name="swp_api_auth")
46 2
     */
47
    public function authenticateAction(Request $request)
48 2
    {
49 2
        $form = $this->get('form.factory')->createNamed('', UserAuthenticationType::class, []);
50 2
        $form->handleRequest($request);
51 2
        if ($form->isSubmitted() && $form->isValid()) {
52
            $formData = $form->getData();
53 2
54 1
            try {
55 1
                $user = $this->get('swp.security.user_provider')->loadUserByUsername($formData['username']);
56
            } catch (UsernameNotFoundException $e) {
57
                $user = null;
58 2
            }
59 1
60 1
            if (null !== $user) {
61
                if ($this->get('security.password_encoder')->isPasswordValid($user, $formData['password'])) {
62
                    return $this->returnApiTokenResponse($user, null);
63
                }
64
            }
65 1
        }
66 1
67
        return new SingleResourceResponse([
68 1
            'status' => 401,
69
            'message' => 'Unauthorized',
70
        ], new ResponseContext(401));
71
    }
72
73
    /**
74
     * Ask Superdesk server for user with those credentials and tries to authorize.
75
     *
76
     * @ApiDoc(
77
     *     resource=true,
78
     *     description="Authorize using Superdesk credentials",
79
     *     statusCodes={
80
     *         200="Returned on success.",
81
     *         401="No user found or not authorized."
82
     *     },
83
     *     input="SWP\Bundle\CoreBundle\Form\Type\SuperdeskCredentialAuthenticationType"
84
     * )
85
     * @Route("/api/{version}/auth/superdesk/", options={"expose"=true}, methods={"POST"}, defaults={"version"="v2"}, name="swp_api_auth_superdesk")
86
     */
87
    public function authenticateWithSuperdeskAction(Request $request)
88
    {
89
        $form = $this->get('form.factory')->createNamed('', SuperdeskCredentialAuthenticationType::class, []);
90
        $form->handleRequest($request);
91
        if ($form->isSubmitted() && $form->isValid()) {
92
            $formData = $form->getData();
93
            $authorizedSuperdeskHosts = (array) $this->container->getParameter('superdesk_servers');
94
            $superdeskUser = null;
95
            $client = new GuzzleHttp\Client();
96
97
            foreach ($authorizedSuperdeskHosts as $baseUrl) {
98
                try {
99
                    $apiRequest = new GuzzleHttp\Psr7\Request('GET', sprintf('%s/api/sessions/%s', $baseUrl, $formData['sessionId']), [
100
                        'Authorization' => $formData['token'],
101
                    ]);
102
                    $apiResponse = $client->send($apiRequest);
103
                    if (200 !== $apiResponse->getStatusCode()) {
104
                        continue;
105
                    }
106
107
                    $content = json_decode($apiResponse->getBody()->getContents(), true);
108
                    if (is_array($content) && array_key_exists('user', $content)) {
109
                        $superdeskUser = $content['user'];
110
111
                        break;
112
                    }
113
                } catch (GuzzleHttp\Exception\ClientException $e) {
114
                    if (200 !== $e->getResponse()->getStatusCode()) {
115
                        continue;
116
                    }
117
                }
118
            }
119
120
            if (null === $superdeskUser) {
121
                return new SingleResourceResponse([
122
                    'status' => 401,
123
                    'message' => 'Unauthorized (user not found in Superdesk)',
124
                ], new ResponseContext(401));
125
            }
126
127
            $userProvider = $this->get('swp.security.user_provider');
128
            $publisherUser = $userProvider->findOneByEmail($superdeskUser['email']);
129
            if (null === $publisherUser) {
130
                try {
131
                    $publisherUser = $userProvider->loadUserByUsername($superdeskUser['username']);
132
                } catch (UsernameNotFoundException $e) {
133
                    $publisherUser = null;
134
                }
135
            }
136
137 1
            if (null === $publisherUser) {
138
                $userManager = $this->get('fos_user.user_manager');
139 1
                /** @var UserInterface $publisherUser */
140 1
                $publisherUser = $userManager->createUser();
141 1
                $publisherUser->setUsername($superdeskUser['username']);
142
                $publisherUser->setEmail($superdeskUser['email']);
143
                $publisherUser->setRoles(['ROLE_INTERNAL_API']);
144
                $publisherUser->setFirstName(\array_key_exists('first_name', $superdeskUser) ? $superdeskUser['first_name'] : 'Anon.');
145
                $publisherUser->setLastName(\array_key_exists('last_name', $superdeskUser) ? $superdeskUser['last_name'] : '');
146
                $publisherUser->setPlainPassword(password_hash(random_bytes(36), PASSWORD_BCRYPT));
147
                $publisherUser->setEnabled(true);
148 1
                $userManager->updateUser($publisherUser);
149 1
            }
150 1
151
            if (null !== $publisherUser) {
152
                return $this->returnApiTokenResponse($publisherUser, str_replace('Basic ', '', $formData['token']));
153 1
            }
154
        }
155 1
156 1
        return new SingleResourceResponse([
157
            'status' => 401,
158 1
            'message' => 'Unauthorized',
159
        ], new ResponseContext(401));
160
    }
161
162
    /**
163
     * @param UserInterface $user
164
     * @param string        $token
165
     *
166
     * @return SingleResourceResponse
167
     */
168
    private function returnApiTokenResponse(UserInterface $user, $token)
169
    {
170
        /** @var ApiKeyInterface $apiKey */
171
        $apiKey = $this->generateOrGetApiKey($user, $token);
172
173
        return new SingleResourceResponse([
174
            'token' => [
175
                'api_key' => $apiKey->getApiKey(),
176
                'valid_to' => $apiKey->getValidTo(),
177
            ],
178
            'user' => $user,
179
        ]);
180
    }
181
182
    private function generateOrGetApiKey(UserInterface $user, $token)
183
    {
184
        $apiKey = null;
185
        $apiKeyRepository = $this->get('swp.repository.api_key');
186
        if (null !== $token) {
187
            $apiKey = $apiKeyRepository->getValidToken($token)->getQuery()->getOneOrNullResult();
188
        } else {
189
            $validKeys = $apiKeyRepository->getValidTokenForUser($user)->getQuery()->getResult();
190
            if (count($validKeys) > 0) {
191
                $apiKey = reset($validKeys);
192
            }
193
        }
194
195
        if (null === $apiKey) {
196
            $apiKey = $this->get('swp.factory.api_key')->create($user, $token);
197
198
            try {
199
                $apiKeyRepository->add($apiKey);
200
            } catch (UniqueConstraintViolationException $e) {
201
                return $this->generateOrGetApiKey($user, $token);
202
            }
203
        }
204
205
        return $apiKey;
206
    }
207
}
208