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

AuthController::generateOrGetApiKey()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 20
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 12

Importance

Changes 0
Metric Value
dl 0
loc 20
ccs 0
cts 0
cp 0
rs 9.4285
c 0
b 0
f 0
cc 3
eloc 14
nc 4
nop 2
crap 12
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 GuzzleHttp;
18
use Nelmio\ApiDocBundle\Annotation\ApiDoc;
19
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method;
20
use Sensio\Bundle\FrameworkExtraBundle\Configuration\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\Bundle\CoreBundle\Security\Authenticator\TokenAuthenticator;
26
use SWP\Component\Common\Response\ResponseContext;
27
use SWP\Component\Common\Response\SingleResourceResponse;
28
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
29
use Symfony\Component\HttpFoundation\RedirectResponse;
30
use Symfony\Component\HttpFoundation\Request;
31
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
32
use Symfony\Component\Security\Core\Exception\UsernameNotFoundException;
33
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
34
35
class AuthController extends Controller
36
{
37
    /**
38
     * Look for user matching provided credentials.
39
     *
40
     * @ApiDoc(
41
     *     resource=true,
42
     *     description="Look for user matching provided credentials",
43
     *     statusCodes={
44
     *         200="Returned on success.",
45
     *         401="No user found or not authorized."
46 2
     *     },
47
     *     input="SWP\Bundle\CoreBundle\Form\Type\UserAuthenticationType"
48 2
     * )
49 2
     * @Route("/api/{version}/auth/", options={"expose"=true}, defaults={"version"="v1"}, name="swp_api_auth")
50 2
     * @Method("POST")
51 2
     */
52
    public function authenticateAction(Request $request)
53 2
    {
54 1
        $form = $this->createForm(UserAuthenticationType::class, []);
55 1
        $form->handleRequest($request);
56
        if ($form->isValid()) {
57
            $formData = $form->getData();
58 2
            try {
59 1
                $user = $this->get('swp.security.user_provider')->loadUserByUsername($formData['username']);
60 1
            } catch (UsernameNotFoundException $e) {
61
                $user = null;
62
            }
63
64
            if (null !== $user) {
65 1
                if ($this->get('security.password_encoder')->isPasswordValid($user, $formData['password'])) {
66 1
                    return $this->returnApiTokenResponse($user, null);
67
                }
68 1
            }
69
        }
70
71
        return new SingleResourceResponse([
72
            'status' => 401,
73
            'message' => 'Unauthorized',
74
        ], new ResponseContext(401));
75
    }
76
77
    /**
78
     * Ask Superdesk server for user with those credentials and tries to authorize.
79
     *
80
     * @ApiDoc(
81
     *     resource=true,
82
     *     description="Authorize using Superdesk credentials",
83
     *     statusCodes={
84
     *         200="Returned on success.",
85
     *         401="No user found or not authorized."
86
     *     },
87
     *     input="SWP\Bundle\CoreBundle\Form\Type\SuperdeskCredentialAuthenticationType"
88
     * )
89
     * @Route("/api/{version}/auth/superdesk/", options={"expose"=true}, defaults={"version"="v1"}, name="swp_api_auth_superdesk")
90
     * @Method("POST")
91
     */
92
    public function authenticateWithSuperdeskAction(Request $request)
93
    {
94
        $form = $this->createForm(SuperdeskCredentialAuthenticationType::class, []);
95
        $form->handleRequest($request);
96
        if ($form->isValid()) {
97
            $formData = $form->getData();
98
            $authorizedSuperdeskHosts = $this->container->getParameter('superdesk_servers');
99
            $superdeskUser = null;
100
            $client = new GuzzleHttp\Client();
101
102
            foreach ($authorizedSuperdeskHosts as $baseUrl) {
103
                try {
104
                    $apiRequest = new GuzzleHttp\Psr7\Request('GET', sprintf('%s/api/sessions/%s', $baseUrl, $formData['session_id']), [
105
                        'Authorization' => $formData['token'],
106
                    ]);
107
                    $apiResponse = $client->send($apiRequest);
108
                    if ($apiResponse->getStatusCode() !== 200) {
109
                        continue;
110
                    }
111
112
                    $content = json_decode($apiResponse->getBody()->getContents(), true);
113
                    if (is_array($content) && array_key_exists('user', $content)) {
114
                        $superdeskUser = $content['user'];
115
116
                        break;
117
                    }
118
                } catch (GuzzleHttp\Exception\ClientException $e) {
119
                    if ($e->getResponse()->getStatusCode() !== 200) {
120
                        continue;
121
                    }
122
                }
123
            }
124
125
            if (null === $superdeskUser) {
126
                return new SingleResourceResponse([
127
                    'status' => 401,
128
                    'message' => 'Unauthorized (user not found in Superdesk)',
129
                ], new ResponseContext(401));
130
            }
131
132
            $userProvider = $this->get('swp.security.user_provider');
133
            $publisherUser = $userProvider->findOneByEmail($superdeskUser['email']);
134
            if (null === $publisherUser) {
135
                try {
136
                    $publisherUser = $userProvider->loadUserByUsername($superdeskUser['username']);
137 1
                } catch (UsernameNotFoundException $e) {
138
                    $publisherUser = null;
139 1
                }
140 1
            }
141 1
142
            if (null === $publisherUser) {
143
                $userManager = $this->get('fos_user.user_manager');
144
                /** @var UserInterface $publisherUser */
145
                $publisherUser = $userManager->createUser();
146
                $publisherUser->setUsername($superdeskUser['username']);
147
                $publisherUser->setEmail($superdeskUser['email']);
148 1
                $publisherUser->setRoles(['ROLE_INTERNAL_API', 'ROLE_USER']);
149 1
                $publisherUser->setFirstName($superdeskUser['first_name']);
150 1
                $publisherUser->setLastName($superdeskUser['last_name']);
151
                $publisherUser->setPlainPassword(password_hash(random_bytes(36), PASSWORD_BCRYPT));
152
                $publisherUser->setEnabled(true);
153 1
                $userManager->updateUser($publisherUser);
154
            }
155 1
156 1
            if (null !== $publisherUser) {
157
                return $this->returnApiTokenResponse($publisherUser, str_replace('Basic ', '', $formData['token']));
158 1
            }
159
        }
160
161
        return new SingleResourceResponse([
162
            'status' => 401,
163
            'message' => 'Unauthorized',
164
        ], new ResponseContext(401));
165
    }
166
167
    /**
168
     * Generate url with authentication code for authorization.
169
     *
170
     * @ApiDoc(
171
     *     resource=true,
172
     *     description="Generate url with authentication code for authorization",
173
     *     statusCodes={
174
     *         200="Returned on success.",
175
     *         401="No user found or not authorized."
176
     *     }
177
     * )
178
     * @Route("/api/{version}/livesite/auth/{intention}", options={"expose"=true}, defaults={"version"="v1", "intention"="api"}, name="swp_api_auth_url")
179
     *
180
     * @Method("POST")
181
     *
182
     * @return SingleResourceResponse
183
     */
184
    public function generateAuthenticationUrl($intention)
185
    {
186
        /** @var ApiKeyInterface $apiKey */
187
        $apiKey = $this->generateOrGetApiKey($this->getUser(), null);
0 ignored issues
show
Documentation introduced by
$this->getUser() is of type null|object, but the function expects a object<SWP\Bundle\CoreBundle\Model\UserInterface>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
188
        $parameters = [
189
            'auth_token' => $apiKey->getApiKey(),
190
        ];
191
192
        if (TokenAuthenticator::INTENTION_LIVESITE_EDITOR === $intention) {
193
            $parameters['intention'] = $intention;
194
        }
195
196
        $url = $this->generateUrl('swp_api_auth_redirect', $parameters, UrlGeneratorInterface::ABSOLUTE_URL);
197
198
        return new SingleResourceResponse([
199
            'token' => [
200
                'api_key' => $apiKey->getApiKey(),
201
                'valid_to' => $apiKey->getValidTo(),
202
            ],
203
            'url' => $url,
204
        ]);
205
    }
206
207
    /**
208
     * Redirect authorized user to homepage.
209
     *
210
     * @Route("/api/{version}/livesite/redirect", options={"expose"=true}, defaults={"version"="v1", "intention"="api"}, name="swp_api_auth_redirect")
211
     *
212
     * @Method("GET")
213
     *
214
     * @return RedirectResponse
215
     */
216
    public function redirectAuthenticated()
217
    {
218
        $user = $this->getUser();
219
220
        if ($user instanceof UserInterface) {
221
            return new RedirectResponse($this->generateUrl('homepage'));
222
        }
223
224
        throw new AccessDeniedException('This user does not have access to this page.');
225
    }
226
227
    /**
228
     * @param UserInterface $user
229
     * @param string        $token
230
     *
231
     * @return SingleResourceResponse
232
     */
233
    private function returnApiTokenResponse(UserInterface $user, $token)
234
    {
235
        /** @var ApiKeyInterface $apiKey */
236
        $apiKey = $this->generateOrGetApiKey($user, $token);
237
238
        return new SingleResourceResponse([
239
            'token' => [
240
                'api_key' => $apiKey->getApiKey(),
241
                'valid_to' => $apiKey->getValidTo(),
242
            ],
243
            'user' => $user,
244
        ]);
245
    }
246
247
    /**
248
     * @param UserInterface $user
249
     * @param string        $token
250
     *
251
     * @return mixed|null
252
     */
253
    private function generateOrGetApiKey(UserInterface $user, $token)
254
    {
255
        $apiKey = null;
0 ignored issues
show
Unused Code introduced by
$apiKey is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
256
        $apiKeyRepository = $this->get('swp.repository.api_key');
257
        if (null !== $token) {
258
            $apiKey = $apiKeyRepository
259
                ->getValidToken($token)
260
                ->getQuery()
261
                ->getOneOrNullResult();
262
        } else {
263
            $apiKey = $apiKeyRepository->getValidTokenForUser($user)->getQuery()->getOneOrNullResult();
264
        }
265
266
        if (null === $apiKey) {
267
            $apiKey = $this->get('swp.factory.api_key')->create($user, $token);
268
            $apiKeyRepository->add($apiKey);
269
        }
270
271
        return $apiKey;
272
    }
273
}
274