Completed
Push — master ( 8763c1...14778d )
by Conrad
03:00
created

tests/OAuthServerTest.php (2 issues)

Checks multi line function declaration space after function

Coding Style Informational

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
3
namespace AdvancedLearning\Oauth2Server\Tests;
4
5
use AdvancedLearning\Oauth2Server\Controllers\AuthoriseController;
6
use AdvancedLearning\Oauth2Server\Entities\UserEntity;
7
use AdvancedLearning\Oauth2Server\Middleware\ResourceServerMiddleware;
8
use AdvancedLearning\Oauth2Server\Models\AccessToken;
9
use AdvancedLearning\Oauth2Server\Models\Client;
10
use AdvancedLearning\Oauth2Server\Repositories\AccessTokenRepository;
11
use AdvancedLearning\Oauth2Server\Repositories\ClientRepository;
12
use AdvancedLearning\Oauth2Server\Repositories\RefreshTokenRepository;
13
use AdvancedLearning\Oauth2Server\Repositories\ScopeRepository;
14
use AdvancedLearning\Oauth2Server\Repositories\UserRepository;
15
use function base64_encode;
16
use function file_get_contents;
17
use function file_put_contents;
18
use GuzzleHttp\Psr7\Response;
19
use GuzzleHttp\Psr7\ServerRequest;
20
use League\OAuth2\Server\AuthorizationServer;
21
use League\OAuth2\Server\Entities\ClientEntityInterface;
22
use League\OAuth2\Server\Grant\ClientCredentialsGrant;
23
use League\OAuth2\Server\Grant\PasswordGrant;
24
use const PHP_EOL;
25
use Robbie\Psr7\HttpRequestAdapter;
26
use Robbie\Psr7\HttpResponseAdapter;
27
use SilverStripe\Control\Director;
28
use SilverStripe\Control\HTTPApplication;
29
use SilverStripe\Control\HTTPRequest;
30
use SilverStripe\Control\HTTPResponse;
31
use SilverStripe\Core\CoreKernel;
32
use SilverStripe\Core\Environment;
33
use SilverStripe\Core\Injector\Injector;
34
use SilverStripe\Core\Kernel;
35
use SilverStripe\Core\Tests\Startup\ErrorControlChainMiddlewareTest\BlankKernel;
36
use SilverStripe\Dev\SapphireTest;
37
use SilverStripe\Security\Member;
38
use SilverStripe\Security\Security;
39
use function sys_get_temp_dir;
40
41
class OAuthServerTest extends SapphireTest
42
{
43
    protected static $fixture_file = 'OAuthFixture.yml';
44
45
    protected static $privateKeyFile = 'private.key';
46
47
    protected static $publicKeyFile = 'public.key';
48
49
    /**
50
     * Setup test environment.
51
     */
52
    public function setUp()
53
    {
54
        parent::setUp();
55
56
        // copy private key so we can set correct permissions, file gets removed when tests finish
57
        $path = $this->getPrivateKeyPath();
58
        file_put_contents($path, file_get_contents(__DIR__ . '/' . self::$privateKeyFile));
59
        chmod($path, 0660);
60
        Environment::setEnv('OAUTH_PRIVATE_KEY_PATH', $path);
61
62
        // copy public key
63
        $path = $this->getPublicKeyPath();
64
        file_put_contents($path, file_get_contents(__DIR__ . '/' . self::$publicKeyFile));
65
        chmod($path, 0660);
66
        Environment::setEnv('OAUTH_PUBLIC_KEY_PATH', $path);
67
68
        Security::force_database_is_ready(true);
69
    }
70
71
    /**
72
     * Test a client grant.
73
     */
74
    public function testClientGrant()
75
    {
76
        $response = $this->generateClientAccessToken();
77
        $data = json_decode((string)$response->getBody(), true);
78
79
        $this->assertArrayHasKey('token_type', $data, 'Response should have a token_type');
80
        $this->assertArrayHasKey('expires_in', $data, 'Response should have expire time for token');
81
        $this->assertArrayHasKey('access_token', $data, 'Response should have a token');
82
        $this->assertEquals('Bearer', $data['token_type'], 'Token type should be Bearer');
83
    }
84
85
    public function testPasswordGrant()
86
    {
87
        $userRepository = new UserRepository();
88
        $refreshRepository = new RefreshTokenRepository();
89
90
        $server = $this->getAuthorisationServer();
91
        $server->enableGrantType(
92
            new PasswordGrant($userRepository, $refreshRepository),
93
            new \DateInterval('PT1H')
94
        );
95
96
        $client = $this->objFromFixture(Client::class, 'webapp');
97
        $member = $this->objFromFixture(Member::class, 'member1');
98
99
        $request = (new ServerRequest(
100
            'POST',
101
            '',
102
            ['Content-Type' => 'application/json']
103
        ))->withParsedBody([
104
            'grant_type' => 'password',
105
            'client_id' => $client->ID,
106
            'client_secret' => $client->Secret,
107
            'scope' => 'members',
108
            'username' => $member->Email,
109
            'password' => 'password1'
110
        ]);
111
112
        $response = new Response();
113
        $response = $server->respondToAccessTokenRequest($request, $response);
114
115
        $data = json_decode((string)$response->getBody(), true);
116
117
        $this->assertArrayHasKey('token_type', $data, 'Response should have a token_type');
118
        $this->assertArrayHasKey('expires_in', $data, 'Response should have expire time for token');
119
        $this->assertArrayHasKey('access_token', $data, 'Response should have a token');
120
        $this->assertEquals('Bearer', $data['token_type'], 'Token type should be Bearer');
121
    }
122
123
    public function testMiddleware()
124
    {
125
        $response = $this->generateClientAccessToken();
126
        $data = json_decode((string)$response->getBody(), true);
127
        $token = $data['access_token'];
128
129
        $server = $this->getResourceServer();
130
131
        $request = new HTTPRequest('GET', '/');
132
        $request->addHeader('authorization', 'Bearer ' . $token);
133
        // fake server port
134
        $_SERVER['SERVER_PORT'] = 443;
135
136
        // Mock app
137
        $app = new HTTPApplication(new BlankKernel(BASE_PATH));
138
        $app->getKernel()->setEnvironment(Kernel::LIVE);
139
140
        $result = (new ResourceServerMiddleware($app, $server))->process($request, function(){
0 ignored issues
show
Expected 1 space after FUNCTION keyword; 0 found
Loading history...
141
            return null;
142
        });
143
144
        $this->assertNull($result, 'Resource Server shouldn\'t modify the response');
145
146
        // failed authentication
147
        $request->removeHeader('authorization');
148
149
        $result = (new ResourceServerMiddleware($app, $server))->process($request, function(){
0 ignored issues
show
Expected 1 space after FUNCTION keyword; 0 found
Loading history...
150
            return null;
151
        });
152
153
        // should have an error response
154
        $this->assertNotNull($result);
155
        $this->assertEquals(401, $result->getStatusCode());
156
    }
157
158
    public function testAuthoriseController()
159
    {
160
        $controller = new AuthoriseController();
161
162
        $client = $this->objFromFixture(Client::class, 'webapp');
163
        $request = $this->getClientRequest($client);
164
165
        /**
166
         * @var HTTPResponse $response
167
         */
168
        $response = $controller->setRequest(
169
            (new HttpRequestAdapter())
170
                ->fromPsr7($request)
171
                // controller expects a string
172
                ->setBody(json_encode($request->getParsedBody()))
173
        )
174
            ->index();
175
176
        $this->assertInstanceOf(HTTPResponse::class, $response, 'Should receive a response object');
177
        $this->assertEquals(200, $response->getStatusCode(), 'Should receive a 200 response code');
178
179
        // check for access token
180
        $data = json_decode($response->getBody(), true);
181
        $this->assertArrayHasKey('token_type', $data, 'Response should have a token_type');
182
        $this->assertArrayHasKey('expires_in', $data, 'Response should have expire time for token');
183
        $this->assertArrayHasKey('access_token', $data, 'Response should have a token');
184
        $this->assertEquals('Bearer', $data['token_type'], 'Token type should be Bearer');
185
    }
186
187
    public function testUserEntity()
188
    {
189
        $member = $this->objFromFixture(Member::class, 'member1');
190
        $entity = new UserEntity($member);
191
192
        $this->assertEquals($member->ID, $entity->getMember()->ID, 'User entity member should have been set');
193
    }
194
195
    /**
196
     * Setup the Authorization Server.
197
     *
198
     * @return AuthorizationServer
199
     */
200
    protected function getAuthorisationServer()
201
    {
202
        // Init our repositories
203
        $clientRepository = new ClientRepository(); // instance of ClientRepositoryInterface
204
        $scopeRepository = new ScopeRepository(); // instance of ScopeRepositoryInterface
205
        $accessTokenRepository = new AccessTokenRepository(); // instance of AccessTokenRepositoryInterface
206
207
        // Path to public and private keys
208
        $privateKey = $this->getPrivateKeyPath();
209
        $encryptionKey = 'lxZFUEsBCJ2Yb14IF2ygAHI5N4+ZAUXXaSeeJm6+twsUmIen';
210
211
        // Setup the authorization server
212
        $server = new AuthorizationServer(
213
            $clientRepository,
214
            $accessTokenRepository,
215
            $scopeRepository,
216
            $privateKey,
217
            $encryptionKey
218
        );
219
220
        return $server;
221
    }
222
223
    /**
224
     * Get the resource server.
225
     *
226
     * @return \League\OAuth2\Server\ResourceServer
227
     */
228
    protected function getResourceServer()
229
    {
230
        // Init our repositories
231
        $accessTokenRepository = new AccessTokenRepository(); // instance of AccessTokenRepositoryInterface
232
233
        // Path to authorization server's public key
234
        $publicKeyPath = $this->getPublicKeyPath();
235
236
        // Setup the authorization server
237
        $server = new \League\OAuth2\Server\ResourceServer(
238
            $accessTokenRepository,
239
            $publicKeyPath
240
        );
241
242
        return $server;
243
    }
244
245
    /**
246
     * Get the full path the private key.
247
     *
248
     * @return string
249
     */
250
    protected function getPrivateKeyPath()
251
    {
252
        return sys_get_temp_dir() . '/' . self::$privateKeyFile;
253
    }
254
255
    /**
256
     * Get the full path the public key.
257
     *
258
     * @return string
259
     */
260
    protected function getPublicKeyPath()
261
    {
262
        return sys_get_temp_dir() . '/' . self::$publicKeyFile;
263
    }
264
265
    /**
266
     * Cleanup test environment.
267
     */
268
    protected function tearDown()
269
    {
270
        parent::tearDown();
271
        // remove private key after tests have finished
272
        unlink($this->getPrivateKeyPath());
273
        // remove public key after tests have finished
274
        unlink($this->getPublicKeyPath());
275
    }
276
277
    /**
278
     * Generates a response with an access token using the client grant.
279
     *
280
     * @return \Psr\Http\Message\ResponseInterface
281
     */
282
    protected function generateClientAccessToken()
283
    {
284
        $server = $this->getAuthorisationServer();
285
        // Enable the client credentials grant on the server
286
        $server->enableGrantType(
287
            new ClientCredentialsGrant(),
288
            new \DateInterval('PT1H') // access tokens will expire after 1 hour
289
        );
290
291
        $client = $this->objFromFixture(Client::class, 'webapp');
292
293
        $request = $this->getClientRequest($client);
294
295
        $response = new Response();
296
        return $server->respondToAccessTokenRequest($request, $response);
297
    }
298
299
    /**
300
     * Get PSR7 request object to be used for a client grant.
301
     *
302
     * @param Client $client
303
     *
304
     * @return ServerRequest
305
     */
306
    protected function getClientRequest(Client $client)
307
    {
308
        // setup server vars
309
        $_SERVER['SERVER_PORT'] = 80;
310
        $_SERVER['SERVER_PROTOCOL'] = 'HTTP/1.1';
311
312
        return (new ServerRequest(
313
            'POST',
314
            '',
315
            ['Content-Type' => 'application/json']
316
        ))->withParsedBody([
317
            'grant_type' => 'client_credentials',
318
            'client_id' => $client->ID,
319
            'client_secret' => $client->Secret,
320
            'scope' => 'members'
321
        ]);
322
    }
323
324
}
325