Completed
Push — 7.x ( cc723d...f47664 )
by Samuel
07:48 queued 10s
created

MultiAuthenticate::handle()   B

Complexity

Conditions 10
Paths 24

Size

Total Lines 73
Code Lines 30

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 28
CRAP Score 10.0296

Importance

Changes 9
Bugs 1 Features 1
Metric Value
cc 10
eloc 30
c 9
b 1
f 1
nc 24
nop 3
dl 0
loc 73
ccs 28
cts 30
cp 0.9333
crap 10.0296
rs 7.6666

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
namespace SMartins\PassportMultiauth\Http\Middleware;
4
5
use Closure;
6
use Illuminate\Auth\AuthenticationException;
7
use Illuminate\Auth\Middleware\Authenticate;
8
use Illuminate\Auth\RequestGuard;
9
use Illuminate\Contracts\Auth\Authenticatable;
10
use Illuminate\Contracts\Auth\Factory as Auth;
11
use Illuminate\Foundation\Application;
12
use Illuminate\Support\Facades\Auth as AuthFacade;
13
use League\OAuth2\Server\Exception\OAuthServerException;
14
use League\OAuth2\Server\ResourceServer;
15
use Psr\Http\Message\ServerRequestInterface;
16
use SMartins\PassportMultiauth\Config\AuthConfigHelper;
17
use SMartins\PassportMultiauth\Facades\ServerRequest;
18
use SMartins\PassportMultiauth\Guards\GuardChecker;
19
use SMartins\PassportMultiauth\PassportMultiauth;
20
use SMartins\PassportMultiauth\Provider as Token;
21
use SMartins\PassportMultiauth\ProviderRepository;
22
23
class MultiAuthenticate extends Authenticate
24
{
25
    /**
26
     * @var ResourceServer
27
     */
28
    protected $server;
29
30
    /**
31
     * @var ProviderRepository
32
     */
33
    protected $providers;
34
35
    /**
36
     * Create a new middleware instance.
37
     *
38
     * @param ResourceServer $server
39
     * @param ProviderRepository $providers
40
     * @param Auth $auth
41
     */
42 15
    public function __construct(
43
        ResourceServer $server,
44
        ProviderRepository $providers,
45
        Auth $auth
46
    ) {
47 15
        parent::__construct($auth);
48
49 15
        $this->server = $server;
50 15
        $this->providers = $providers;
51 15
    }
52
53
    /**
54
     * Handle an incoming request. Authenticates the guard from access token
55
     * used on request.
56
     *
57
     * @param \Illuminate\Http\Request $request
58
     * @param \Closure $next
59
     * @param string[] ...$guards
60
     * @return mixed
61
     *
62
     * @throws \Illuminate\Auth\AuthenticationException
63
     * @throws \SMartins\PassportMultiauth\Exceptions\MissingConfigException
64
     */
65 15
    public function handle($request, Closure $next, ...$guards)
66
    {
67
        // If don't has any guard follow the flow
68 15
        if (empty($guards)) {
69 2
            $this->authenticate($request, $guards);
70
71
            // Stop laravel from checking for a token if session is not set
72
            return $next($request);
73
        }
74
75 13
        $psrRequest = ServerRequest::createRequest($request);
76
77
        try {
78 13
            $psrRequest = $this->server->validateAuthenticatedRequest($psrRequest);
79
80 10
            if (! ($accessToken = $this->getAccessTokenFromRequest($psrRequest))) {
81 2
                throw new AuthenticationException('Unauthenticated', $guards);
82
            }
83
84 8
            $guard = $this->getTokenGuard($accessToken, $guards);
85
86 8
            if (empty($guard)) {
87 3
                throw new AuthenticationException('Unauthenticated', $guards);
88
            }
89
90
            // At this point, the authentication will be done by Laravel Passport default driver.
91 5
            $this->authenticate($request, $guard);
92
93 5
            $guardsModels = $this->getGuardsModels($guards);
94
95
            // The laravel passport will define the logged user on request.
96
            // The returned model can be anywhere, depending on the guard.
97 5
            $user = $request->user();
98
99
            // But we need check if the user logged has the correct guard.
100
            $request->setUserResolver(function ($guard) use ($user, $guardsModels) {
101
                // If don't exists any guard on $request->user() parameter, the
102
                // default user will be returned.
103
                // If has the guard on guards passed on middleware and the model
104
                // instance are the same on an guard.
105 4
                if (! $guard || (isset($guardsModels[$guard]) && $user instanceof $guardsModels[$guard])) {
106 4
                    return $user;
107
                }
108 5
            });
109
110
            // After it, we'll change the passport driver behavior to get the
111
            // authenticated user. It'll change on methods like Auth::user(),
112
            // Auth::guard('company')->user(), Auth::check().
113 5
            AuthFacade::extend(
114 5
                'passport',
115
                function ($app, $name, array $config) use ($request) {
116 4
                    $providerGuard = AuthConfigHelper::getProviderGuard($config['provider']);
117
118
                    return tap($this->makeGuard($request, $providerGuard), function ($guard) {
119 4
                        Application::getInstance()->refresh('request', $guard, 'setRequest');
120 4
                    });
121 5
                }
122
            );
123 5
            AuthFacade::clearGuardsCache();
124 8
        } catch (OAuthServerException $e) {
125
            // If has an OAuthServerException check if has unit tests and fake
126
            // user authenticated.
127 3
            if (($user = PassportMultiauth::userActing()) &&
128 3
                $this->canBeAuthenticated($user, $guards)
129
            ) {
130
                return $next($request);
131
            }
132
133
            // @todo Check if it's the best way to handle with OAuthServerException
134 3
            throw new AuthenticationException('Unauthenticated', $guards);
135
        }
136
137 5
        return $next($request);
138
    }
139
140
    /**
141
     * @param ServerRequestInterface $request
142
     * @return null|Token
143
     */
144 10
    public function getAccessTokenFromRequest(ServerRequestInterface $request)
145
    {
146 10
        if (! ($tokenId = $request->getAttribute('oauth_access_token_id'))) {
147 1
            return;
148
        }
149
150 9
        return $this->providers->findForToken($tokenId);
151
    }
152
153
    /**
154
     * Check if user acting has the required guards and scopes on request.
155
     *
156
     * @param Authenticatable $user
157
     * @param  array $guards
158
     * @return bool
159
     * @throws \SMartins\PassportMultiauth\Exceptions\MissingConfigException
160
     */
161 2
    public function canBeAuthenticated(Authenticatable $user, $guards)
162
    {
163 2
        $userGuard = AuthConfigHelper::getUserGuard($user);
164
165 2
        return in_array($userGuard, $guards);
166
    }
167
168
    /**
169
     * Get guard related with token.
170
     *
171
     * @param Token $token
172
     * @param $guards
173
     * @return array
174
     */
175 8
    public function getTokenGuard(Token $token, $guards)
176
    {
177 8
        $providers = GuardChecker::getGuardsProviders($guards);
178
179
        // use only guard associated to access token provider
180 8
        return $providers->has($token->provider) ? [$providers->get($token->provider)] : [];
181
    }
182
183
    /**
184
     * @param \Illuminate\Http\Request $request
185
     * @param string $guard
186
     * @return RequestGuard
187
     */
188 4
    private function makeGuard($request, $guard)
189
    {
190
        return new RequestGuard(function ($request) use ($guard) {
191 4
            return $request->user($guard);
192 4
        }, $request);
193
    }
194
195
    /**
196
     * Get models from guards. It'll return an associative array where the keys
197
     * are the guards and the values are the correspondent models.
198
     *
199
     * @param array $guards
200
     * @return array
201
     */
202 5
    private function getGuardsModels(array $guards)
203
    {
204 5
        $guardsModels = [];
205 5
        foreach ($guards as $guard) {
206 5
            $provider = GuardChecker::defaultGuardProvider($guard);
207 5
            $guardsModels[$guard] = AuthConfigHelper::getProviderModel($provider);
208
        }
209
210 5
        return $guardsModels;
211
    }
212
}
213