Completed
Push — master ( f552ba...56927b )
by Conrad
18:33
created

AuthenticationService::addMissingServerVariables()   A

Complexity

Conditions 4
Paths 6

Size

Total Lines 16
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 16
rs 9.2
c 0
b 0
f 0
cc 4
eloc 9
nc 6
nop 1
1
<?php
2
3
namespace AdvancedLearning\Oauth2Server\Services;
4
5
6
use AdvancedLearning\Oauth2Server\Exceptions\AuthenticationException;
7
use AdvancedLearning\Oauth2Server\Repositories\AccessTokenRepository;
8
use GuzzleHttp\Psr7\Response;
9
use League\OAuth2\Server\Exception\OAuthServerException;
10
use League\OAuth2\Server\ResourceServer;
11
use Robbie\Psr7\HttpRequestAdapter;
12
use Robbie\Psr7\HttpResponseAdapter;
13
use SilverStripe\Control\HTTPRequest;
14
use SilverStripe\Core\Environment;
15
16
class AuthenticationService implements Authenticator
17
{
18
    protected $server;
19
20
    /**
21
     * AuthenticationService constructor.
22
     *
23
     * @param ResourceServer|null $server Optional resource server.
24
     */
25
    public function __construct(ResourceServer $server = null)
26
    {
27
        $this->server = $server ?: $this->createServer();
28
    }
29
30
    /**
31
     * Authenticate the request. Returns modified request (probably not as SS doesn't support
32
     * request attributes).
33
     *
34
     * @param HTTPRequest $request The SilverStripe request object to be authenticated.
35
     *
36
     * @return HTTPRequest
37
     * @throws AuthenticationException
38
     */
39
    public function authenticate(HTTPRequest $request): HTTPRequest
40
    {
41
        $requestAdapter = new HttpRequestAdapter();
42
        $responseAdapter = new HttpResponseAdapter();
43
44
        // missing vars (cli)
45
        $this->addMissingServerVariables($requestAdapter);
46
47
        // store session as it doesn't get converted
48
        $session = $request->getSession();
49
        $routeParams = $request->params();
50
51
        $server = $this->getServer();
52
        $psrRequest = $requestAdapter->toPsr7($request);
53
        $psrResponse = new Response();
54
55
        try {
56
            $psrRequest = $server->validateAuthenticatedRequest($psrRequest);
57
        } catch (OAuthServerException $exception) {
58
            // convert to authentication exception
59
            throw new AuthenticationException(
60
                $exception->getMessage(),
61
                $exception->getCode(),
62
                $responseAdapter->fromPsr7($exception->generateHttpResponse($psrResponse))
0 ignored issues
show
Bug introduced by
It seems like $responseAdapter->fromPs...Response($psrResponse)) targeting Robbie\Psr7\HttpResponseAdapter::fromPsr7() can also be of type object<SilverStripe\Control\HTTPRequest>; however, AdvancedLearning\Oauth2S...xception::__construct() does only seem to accept object<SilverStripe\Control\HTTPResponse>, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
63
            );
64
        } catch (\Exception $exception) {
65
            // convert to authentication exception
66
            throw new AuthenticationException(
67
                $exception->getMessage(),
68
                $exception->getCode(),
69
                $responseAdapter->fromPsr7(
0 ignored issues
show
Bug introduced by
It seems like $responseAdapter->fromPs...Response($psrResponse)) targeting Robbie\Psr7\HttpResponseAdapter::fromPsr7() can also be of type object<SilverStripe\Control\HTTPRequest>; however, AdvancedLearning\Oauth2S...xception::__construct() does only seem to accept object<SilverStripe\Control\HTTPResponse>, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
70
                    (new OAuthServerException($exception->getMessage(), 0, 'unknown_error', 500))
71
                        ->generateHttpResponse($psrResponse)
72
                )
73
            );
74
        }
75
        $request = $requestAdapter->fromPsr7($psrRequest);
76
77
        // add session back
78
        $request->setSession($session);
0 ignored issues
show
Bug introduced by
The method setSession does only exist in SilverStripe\Control\HTTPRequest, but not in SilverStripe\Control\HTTPResponse.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
79
        $request->setRouteParams($routeParams);
0 ignored issues
show
Bug introduced by
The method setRouteParams does only exist in SilverStripe\Control\HTTPRequest, but not in SilverStripe\Control\HTTPResponse.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
80
81
        // add the request attributes as custom auth headers
82
        foreach ($psrRequest->getAttributes() as $attribute => $value) {
83
            $request->addHeader($attribute, $value);
84
        }
85
86
        return $request;
87
    }
88
89
    /**
90
     * Override the default ResourceServer.
91
     *
92
     * @param ResourceServer $v The new ResourceServer to use.
93
     *
94
     * @return $this
95
     */
96
    public function setServer(ResourceServer $v): Authenticator
97
    {
98
        $this->server = $v;
99
        return $this;
100
    }
101
102
    /**
103
     * Get the ResourceServer.
104
     *
105
     * @return ResourceServer
106
     */
107
    public function getServer(): ResourceServer
108
    {
109
        return $this->server;
110
    }
111
112
    /**
113
     * Create a default ResourceServer. Used if one isn't provided.
114
     *
115
     * @return ResourceServer
116
     */
117
    protected function createServer(): ResourceServer
118
    {
119
        // Init our repositories
120
        $accessTokenRepository = new AccessTokenRepository(); // instance of AccessTokenRepositoryInterface
121
122
        // Path to authorization server's public key
123
        $publicKeyPath = Environment::getEnv('OAUTH_PUBLIC_KEY_PATH');
124
125
        // Setup the authorization server
126
        return new ResourceServer(
127
            $accessTokenRepository,
128
            $publicKeyPath
129
        );
130
    }
131
132
    /**
133
     * Cli is missing some $_SERVER variables.
134
     *
135
     * @param HttpRequestAdapter $adapter
136
     */
137
    protected function addMissingServerVariables(HttpRequestAdapter $adapter)
138
    {
139
        $vars = $adapter->getServerVars() ?: [];
140
        $defaults = [
141
            'SERVER_PORT' => 80,
142
            'HTTP_HOST' => Environment::getEnv('SS_BASE_URL')
143
        ];
144
145
        foreach ($defaults as $key => $value) {
146
            if (empty($vars[$key])) {
147
                $vars[$key] = $value;
148
            }
149
        }
150
151
        $adapter->setServerVars($vars);
152
    }
153
}
154