blob: 8e428f9b53aff7a54bf69bc0b7c99f02c3e370ca [file] [log] [blame]
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +01001<?php
2
3namespace OAuth2\Storage;
4
5class Bootstrap
6{
7 const DYNAMODB_PHP_VERSION = 'none';
8
9 protected static $instance;
10 private $mysql;
11 private $sqlite;
12 private $postgres;
13 private $mongo;
14 private $mongoDb;
15 private $redis;
16 private $cassandra;
17 private $configDir;
18 private $dynamodb;
19 private $couchbase;
20
21 public function __construct()
22 {
23 $this->configDir = __DIR__.'/../../../config';
24 }
25
26 public static function getInstance()
27 {
28 if (!self::$instance) {
29 self::$instance = new self();
30 }
31
32 return self::$instance;
33 }
34
35 public function getSqlitePdo()
36 {
37 if (!$this->sqlite) {
38 $this->removeSqliteDb();
39 $pdo = new \PDO(sprintf('sqlite:%s', $this->getSqliteDir()));
40 $pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
41 $this->createSqliteDb($pdo);
42
43 $this->sqlite = new Pdo($pdo);
44 }
45
46 return $this->sqlite;
47 }
48
49 public function getPostgresPdo()
50 {
51 if (!$this->postgres) {
52 if (in_array('pgsql', \PDO::getAvailableDrivers())) {
53 $this->removePostgresDb();
54 $this->createPostgresDb();
55 if ($pdo = $this->getPostgresDriver()) {
56 $pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
57 $this->populatePostgresDb($pdo);
58 $this->postgres = new Pdo($pdo);
59 }
60 } else {
61 $this->postgres = new NullStorage('Postgres', 'Missing postgres PDO extension.');
62 }
63 }
64
65 return $this->postgres;
66 }
67
68 public function getPostgresDriver()
69 {
70 try {
71 $pdo = new \PDO('pgsql:host=localhost;dbname=oauth2_server_php', 'postgres');
72
73 return $pdo;
74 } catch (\PDOException $e) {
75 $this->postgres = new NullStorage('Postgres', $e->getMessage());
76 }
77 }
78
79 public function getMemoryStorage()
80 {
81 return new Memory(json_decode(file_get_contents($this->configDir. '/storage.json'), true));
82 }
83
84 public function getRedisStorage()
85 {
86 if (!$this->redis) {
87 if (class_exists('Predis\Client')) {
88 $redis = new \Predis\Client();
89 if ($this->testRedisConnection($redis)) {
90 $redis->flushdb();
91 $this->redis = new Redis($redis);
92 $this->createRedisDb($this->redis);
93 } else {
94 $this->redis = new NullStorage('Redis', 'Unable to connect to redis server on port 6379');
95 }
96 } else {
97 $this->redis = new NullStorage('Redis', 'Missing redis library. Please run "composer.phar require predis/predis:dev-master"');
98 }
99 }
100
101 return $this->redis;
102 }
103
104 private function testRedisConnection(\Predis\Client $redis)
105 {
106 try {
107 $redis->connect();
108 } catch (\Predis\CommunicationException $exception) {
109 // we were unable to connect to the redis server
110 return false;
111 }
112
113 return true;
114 }
115
116 public function getMysqlPdo()
117 {
118 if (!$this->mysql) {
119 $pdo = null;
120 try {
121 $pdo = new \PDO('mysql:host=localhost;', 'root');
122 } catch (\PDOException $e) {
123 $this->mysql = new NullStorage('MySQL', 'Unable to connect to MySQL on root@localhost');
124 }
125
126 if ($pdo) {
127 $pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
128 $this->removeMysqlDb($pdo);
129 $this->createMysqlDb($pdo);
130
131 $this->mysql = new Pdo($pdo);
132 }
133 }
134
135 return $this->mysql;
136 }
137
138 public function getMongo()
139 {
140 if (!$this->mongo) {
141 if (class_exists('MongoClient')) {
142 $mongo = new \MongoClient('mongodb://localhost:27017', array('connect' => false));
143 if ($this->testMongoConnection($mongo)) {
144 $db = $mongo->oauth2_server_php_legacy;
145 $this->removeMongo($db);
146 $this->createMongo($db);
147
148 $this->mongo = new Mongo($db);
149 } else {
150 $this->mongo = new NullStorage('Mongo', 'Unable to connect to mongo server on "localhost:27017"');
151 }
152 } else {
153 $this->mongo = new NullStorage('Mongo', 'Missing mongo php extension. Please install mongo.so');
154 }
155 }
156
157 return $this->mongo;
158 }
159
160 public function getMongoDb()
161 {
162 if (!$this->mongoDb) {
163 if (class_exists('MongoDB\Client')) {
164 $mongoDb = new \MongoDB\Client('mongodb://localhost:27017');
165 if ($this->testMongoDBConnection($mongoDb)) {
166 $db = $mongoDb->oauth2_server_php;
167 $this->removeMongoDb($db);
168 $this->createMongoDb($db);
169
170 $this->mongoDb = new MongoDB($db);
171 } else {
172 $this->mongoDb = new NullStorage('MongoDB', 'Unable to connect to mongo server on "localhost:27017"');
173 }
174 } else {
175 $this->mongoDb = new NullStorage('MongoDB', 'Missing MongoDB php extension. Please install mongodb.so');
176 }
177 }
178
179 return $this->mongoDb;
180 }
181
182 private function testMongoConnection(\MongoClient $mongo)
183 {
184 try {
185 $mongo->connect();
186 } catch (\MongoConnectionException $e) {
187 return false;
188 }
189
190 return true;
191 }
192
193 private function testMongoDBConnection(\MongoDB\Client $mongo)
194 {
195 return true;
196 }
197
198 public function getCouchbase()
199 {
200 if (!$this->couchbase) {
201 if ($this->getEnvVar('SKIP_COUCHBASE_TESTS')) {
202 $this->couchbase = new NullStorage('Couchbase', 'Skipping Couchbase tests');
203 } elseif (!class_exists('Couchbase')) {
204 $this->couchbase = new NullStorage('Couchbase', 'Missing Couchbase php extension. Please install couchbase.so');
205 } else {
206 // round-about way to make sure couchbase is working
207 // this is required because it throws a "floating point exception" otherwise
208 $code = "new \Couchbase(array('localhost:8091'), '', '', 'auth', false);";
209 $exec = sprintf('php -r "%s"', $code);
210 $ret = exec($exec, $test, $var);
211 if ($ret != 0) {
212 $couchbase = new \Couchbase(array('localhost:8091'), '', '', 'auth', false);
213 if ($this->testCouchbaseConnection($couchbase)) {
214 $this->clearCouchbase($couchbase);
215 $this->createCouchbaseDB($couchbase);
216
217 $this->couchbase = new CouchbaseDB($couchbase);
218 } else {
219 $this->couchbase = new NullStorage('Couchbase', 'Unable to connect to Couchbase server on "localhost:8091"');
220 }
221 } else {
222 $this->couchbase = new NullStorage('Couchbase', 'Error while trying to connect to Couchbase');
223 }
224 }
225 }
226
227 return $this->couchbase;
228 }
229
230 private function testCouchbaseConnection(\Couchbase $couchbase)
231 {
232 try {
233 if (count($couchbase->getServers()) > 0) {
234 return true;
235 }
236 } catch (\CouchbaseException $e) {
237 return false;
238 }
239
240 return true;
241 }
242
243 public function getCassandraStorage()
244 {
245 if (!$this->cassandra) {
246 if (class_exists('phpcassa\ColumnFamily')) {
247 $cassandra = new \phpcassa\Connection\ConnectionPool('oauth2_test', array('127.0.0.1:9160'));
248 if ($this->testCassandraConnection($cassandra)) {
249 $this->removeCassandraDb();
250 $this->cassandra = new Cassandra($cassandra);
251 $this->createCassandraDb($this->cassandra);
252 } else {
253 $this->cassandra = new NullStorage('Cassandra', 'Unable to connect to cassandra server on "127.0.0.1:9160"');
254 }
255 } else {
256 $this->cassandra = new NullStorage('Cassandra', 'Missing cassandra library. Please run "composer.phar require thobbs/phpcassa:dev-master"');
257 }
258 }
259
260 return $this->cassandra;
261 }
262
263 private function testCassandraConnection(\phpcassa\Connection\ConnectionPool $cassandra)
264 {
265 try {
266 new \phpcassa\SystemManager('localhost:9160');
267 } catch (\Exception $e) {
268 return false;
269 }
270
271 return true;
272 }
273
274 private function removeCassandraDb()
275 {
276 $sys = new \phpcassa\SystemManager('localhost:9160');
277
278 try {
279 $sys->drop_keyspace('oauth2_test');
280 } catch (\cassandra\InvalidRequestException $e) {
281
282 }
283 }
284
285 private function createCassandraDb(Cassandra $storage)
286 {
287 // create the cassandra keyspace and column family
288 $sys = new \phpcassa\SystemManager('localhost:9160');
289
290 $sys->create_keyspace('oauth2_test', array(
291 "strategy_class" => \phpcassa\Schema\StrategyClass::SIMPLE_STRATEGY,
292 "strategy_options" => array('replication_factor' => '1')
293 ));
294
295 $sys->create_column_family('oauth2_test', 'auth');
296 $cassandra = new \phpcassa\Connection\ConnectionPool('oauth2_test', array('127.0.0.1:9160'));
297 $cf = new \phpcassa\ColumnFamily($cassandra, 'auth');
298
299 // populate the data
300 $storage->setClientDetails("oauth_test_client", "testpass", "http://example.com", 'implicit password');
301 $storage->setAccessToken("testtoken", "Some Client", '', time() + 1000);
302 $storage->setAuthorizationCode("testcode", "Some Client", '', '', time() + 1000);
303
304 $storage->setScope('supportedscope1 supportedscope2 supportedscope3 supportedscope4');
305 $storage->setScope('defaultscope1 defaultscope2', null, 'default');
306
307 $storage->setScope('clientscope1 clientscope2', 'Test Client ID');
308 $storage->setScope('clientscope1 clientscope2', 'Test Client ID', 'default');
309
310 $storage->setScope('clientscope1 clientscope2 clientscope3', 'Test Client ID 2');
311 $storage->setScope('clientscope1 clientscope2', 'Test Client ID 2', 'default');
312
313 $storage->setScope('clientscope1 clientscope2', 'Test Default Scope Client ID');
314 $storage->setScope('clientscope1 clientscope2', 'Test Default Scope Client ID', 'default');
315
316 $storage->setScope('clientscope1 clientscope2 clientscope3', 'Test Default Scope Client ID 2');
317 $storage->setScope('clientscope3', 'Test Default Scope Client ID 2', 'default');
318
319 $storage->setClientKey('oauth_test_client', $this->getTestPublicKey(), 'test_subject');
320
321 $cf->insert("oauth_public_keys:ClientID_One", array('__data' => json_encode(array("public_key" => "client_1_public", "private_key" => "client_1_private", "encryption_algorithm" => "RS256"))));
322 $cf->insert("oauth_public_keys:ClientID_Two", array('__data' => json_encode(array("public_key" => "client_2_public", "private_key" => "client_2_private", "encryption_algorithm" => "RS256"))));
323 $cf->insert("oauth_public_keys:", array('__data' => json_encode(array("public_key" => $this->getTestPublicKey(), "private_key" => $this->getTestPrivateKey(), "encryption_algorithm" => "RS256"))));
324
325 $cf->insert("oauth_users:testuser", array('__data' =>json_encode(array("password" => "password", "email" => "testuser@test.com", "email_verified" => true))));
326
327 }
328
329 private function createSqliteDb(\PDO $pdo)
330 {
331 $this->runPdoSql($pdo);
332 }
333
334 private function removeSqliteDb()
335 {
336 if (file_exists($this->getSqliteDir())) {
337 unlink($this->getSqliteDir());
338 }
339 }
340
341 private function createMysqlDb(\PDO $pdo)
342 {
343 $pdo->exec('CREATE DATABASE oauth2_server_php');
344 $pdo->exec('USE oauth2_server_php');
345 $this->runPdoSql($pdo);
346 }
347
348 private function removeMysqlDb(\PDO $pdo)
349 {
350 $pdo->exec('DROP DATABASE IF EXISTS oauth2_server_php');
351 }
352
353 private function createPostgresDb()
354 {
355 if (!`psql postgres -tAc "SELECT 1 FROM pg_roles WHERE rolname='postgres'"`) {
356 `createuser -s -r postgres`;
357 }
358
359 `createdb -O postgres oauth2_server_php`;
360 }
361
362 private function populatePostgresDb(\PDO $pdo)
363 {
364 $this->runPdoSql($pdo);
365 }
366
367 private function removePostgresDb()
368 {
369 if (trim(`psql -l | grep oauth2_server_php | wc -l`)) {
370 `dropdb oauth2_server_php`;
371 }
372 }
373
374 public function runPdoSql(\PDO $pdo)
375 {
376 $storage = new Pdo($pdo);
377 foreach (explode(';', $storage->getBuildSql()) as $statement) {
378 $result = $pdo->exec($statement);
379 }
380
381 // set up scopes
382 $sql = 'INSERT INTO oauth_scopes (scope) VALUES (?)';
383 foreach (explode(' ', 'supportedscope1 supportedscope2 supportedscope3 supportedscope4 clientscope1 clientscope2 clientscope3') as $supportedScope) {
384 $pdo->prepare($sql)->execute(array($supportedScope));
385 }
386
387 $sql = 'INSERT INTO oauth_scopes (scope, is_default) VALUES (?, ?)';
388 foreach (array('defaultscope1', 'defaultscope2') as $defaultScope) {
389 $pdo->prepare($sql)->execute(array($defaultScope, true));
390 }
391
392 // set up clients
393 $sql = 'INSERT INTO oauth_clients (client_id, client_secret, scope, grant_types) VALUES (?, ?, ?, ?)';
394 $pdo->prepare($sql)->execute(array('Test Client ID', 'TestSecret', 'clientscope1 clientscope2', null));
395 $pdo->prepare($sql)->execute(array('Test Client ID 2', 'TestSecret', 'clientscope1 clientscope2 clientscope3', null));
396 $pdo->prepare($sql)->execute(array('Test Default Scope Client ID', 'TestSecret', 'clientscope1 clientscope2', null));
397 $pdo->prepare($sql)->execute(array('oauth_test_client', 'testpass', null, 'implicit password'));
398
399 // set up misc
400 $sql = 'INSERT INTO oauth_access_tokens (access_token, client_id, expires, user_id) VALUES (?, ?, ?, ?)';
401 $pdo->prepare($sql)->execute(array('testtoken', 'Some Client', date('Y-m-d H:i:s', strtotime('+1 hour')), null));
402 $pdo->prepare($sql)->execute(array('accesstoken-openid-connect', 'Some Client', date('Y-m-d H:i:s', strtotime('+1 hour')), 'testuser'));
403
404 $sql = 'INSERT INTO oauth_authorization_codes (authorization_code, client_id, expires) VALUES (?, ?, ?)';
405 $pdo->prepare($sql)->execute(array('testcode', 'Some Client', date('Y-m-d H:i:s', strtotime('+1 hour'))));
406
407 $sql = 'INSERT INTO oauth_users (username, password, email, email_verified) VALUES (?, ?, ?, ?)';
408 $pdo->prepare($sql)->execute(array('testuser', 'password', 'testuser@test.com', true));
409
410 $sql = 'INSERT INTO oauth_public_keys (client_id, public_key, private_key, encryption_algorithm) VALUES (?, ?, ?, ?)';
411 $pdo->prepare($sql)->execute(array('ClientID_One', 'client_1_public', 'client_1_private', 'RS256'));
412 $pdo->prepare($sql)->execute(array('ClientID_Two', 'client_2_public', 'client_2_private', 'RS256'));
413
414 $sql = 'INSERT INTO oauth_public_keys (client_id, public_key, private_key, encryption_algorithm) VALUES (?, ?, ?, ?)';
415 $pdo->prepare($sql)->execute(array(null, $this->getTestPublicKey(), $this->getTestPrivateKey(), 'RS256'));
416
417 $sql = 'INSERT INTO oauth_jwt (client_id, subject, public_key) VALUES (?, ?, ?)';
418 $pdo->prepare($sql)->execute(array('oauth_test_client', 'test_subject', $this->getTestPublicKey()));
419 }
420
421 public function getSqliteDir()
422 {
423 return $this->configDir. '/test.sqlite';
424 }
425
426 public function getConfigDir()
427 {
428 return $this->configDir;
429 }
430
431 private function createCouchbaseDB(\Couchbase $db)
432 {
433 $db->set('oauth_clients-oauth_test_client',json_encode(array(
434 'client_id' => "oauth_test_client",
435 'client_secret' => "testpass",
436 'redirect_uri' => "http://example.com",
437 'grant_types' => 'implicit password'
438 )));
439
440 $db->set('oauth_access_tokens-testtoken',json_encode(array(
441 'access_token' => "testtoken",
442 'client_id' => "Some Client"
443 )));
444
445 $db->set('oauth_authorization_codes-testcode',json_encode(array(
446 'access_token' => "testcode",
447 'client_id' => "Some Client"
448 )));
449
450 $db->set('oauth_users-testuser',json_encode(array(
451 'username' => 'testuser',
452 'password' => 'password',
453 'email' => 'testuser@test.com',
454 'email_verified' => true,
455 )));
456
457 $db->set('oauth_jwt-oauth_test_client',json_encode(array(
458 'client_id' => 'oauth_test_client',
459 'key' => $this->getTestPublicKey(),
460 'subject' => 'test_subject',
461 )));
462 }
463
464 private function clearCouchbase(\Couchbase $cb)
465 {
466 $cb->delete('oauth_authorization_codes-new-openid-code');
467 $cb->delete('oauth_access_tokens-newtoken');
468 $cb->delete('oauth_authorization_codes-newcode');
469 $cb->delete('oauth_refresh_tokens-refreshtoken');
470 }
471
472 private function createMongo(\MongoDB $db)
473 {
474 $db->oauth_clients->insert(array(
475 'client_id' => "oauth_test_client",
476 'client_secret' => "testpass",
477 'redirect_uri' => "http://example.com",
478 'grant_types' => 'implicit password'
479 ));
480
481 $db->oauth_access_tokens->insert(array(
482 'access_token' => "testtoken",
483 'client_id' => "Some Client"
484 ));
485
486 $db->oauth_authorization_codes->insert(array(
487 'authorization_code' => "testcode",
488 'client_id' => "Some Client"
489 ));
490
491 $db->oauth_users->insert(array(
492 'username' => 'testuser',
493 'password' => 'password',
494 'email' => 'testuser@test.com',
495 'email_verified' => true,
496 ));
497
498 $db->oauth_keys->insert(array(
499 'client_id' => null,
500 'public_key' => $this->getTestPublicKey(),
501 'private_key' => $this->getTestPrivateKey(),
502 'encryption_algorithm' => 'RS256'
503 ));
504
505 $db->oauth_jwt->insert(array(
506 'client_id' => 'oauth_test_client',
507 'key' => $this->getTestPublicKey(),
508 'subject' => 'test_subject',
509 ));
510 }
511
512 public function removeMongo(\MongoDB $db)
513 {
514 $db->drop();
515 }
516
517 private function createMongoDB(\MongoDB\Database $db)
518 {
519 $db->oauth_clients->insertOne(array(
520 'client_id' => "oauth_test_client",
521 'client_secret' => "testpass",
522 'redirect_uri' => "http://example.com",
523 'grant_types' => 'implicit password'
524 ));
525
526 $db->oauth_access_tokens->insertOne(array(
527 'access_token' => "testtoken",
528 'client_id' => "Some Client"
529 ));
530
531 $db->oauth_authorization_codes->insertOne(array(
532 'authorization_code' => "testcode",
533 'client_id' => "Some Client"
534 ));
535
536 $db->oauth_users->insertOne(array(
537 'username' => 'testuser',
538 'password' => 'password',
539 'email' => 'testuser@test.com',
540 'email_verified' => true,
541 ));
542
543 $db->oauth_keys->insertOne(array(
544 'client_id' => null,
545 'public_key' => $this->getTestPublicKey(),
546 'private_key' => $this->getTestPrivateKey(),
547 'encryption_algorithm' => 'RS256'
548 ));
549
550 $db->oauth_jwt->insertOne(array(
551 'client_id' => 'oauth_test_client',
552 'key' => $this->getTestPublicKey(),
553 'subject' => 'test_subject',
554 ));
555 }
556
557 public function removeMongoDB(\MongoDB\Database $db)
558 {
559 $db->drop();
560 }
561
562 private function createRedisDb(Redis $storage)
563 {
564 $storage->setClientDetails("oauth_test_client", "testpass", "http://example.com", 'implicit password');
565 $storage->setAccessToken("testtoken", "Some Client", '', time() + 1000);
566 $storage->setAuthorizationCode("testcode", "Some Client", '', '', time() + 1000);
567 $storage->setUser("testuser", "password");
568
569 $storage->setScope('supportedscope1 supportedscope2 supportedscope3 supportedscope4');
570 $storage->setScope('defaultscope1 defaultscope2', null, 'default');
571
572 $storage->setScope('clientscope1 clientscope2', 'Test Client ID');
573 $storage->setScope('clientscope1 clientscope2', 'Test Client ID', 'default');
574
575 $storage->setScope('clientscope1 clientscope2 clientscope3', 'Test Client ID 2');
576 $storage->setScope('clientscope1 clientscope2', 'Test Client ID 2', 'default');
577
578 $storage->setScope('clientscope1 clientscope2', 'Test Default Scope Client ID');
579 $storage->setScope('clientscope1 clientscope2', 'Test Default Scope Client ID', 'default');
580
581 $storage->setScope('clientscope1 clientscope2 clientscope3', 'Test Default Scope Client ID 2');
582 $storage->setScope('clientscope3', 'Test Default Scope Client ID 2', 'default');
583
584 $storage->setClientKey('oauth_test_client', $this->getTestPublicKey(), 'test_subject');
585 }
586
587 public function getTestPublicKey()
588 {
589 return file_get_contents(__DIR__.'/../../../config/keys/id_rsa.pub');
590 }
591
592 private function getTestPrivateKey()
593 {
594 return file_get_contents(__DIR__.'/../../../config/keys/id_rsa');
595 }
596
597 public function getDynamoDbStorage()
598 {
599 if (!$this->dynamodb) {
600 // only run once per travis build
601 if (true == $this->getEnvVar('TRAVIS')) {
602 if (self::DYNAMODB_PHP_VERSION != $this->getEnvVar('TRAVIS_PHP_VERSION')) {
603 $this->dynamodb = new NullStorage('DynamoDb', 'Skipping for travis.ci - only run once per build');
604
605 return;
606 }
607 }
608 if (class_exists('\Aws\DynamoDb\DynamoDbClient')) {
609 if ($client = $this->getDynamoDbClient()) {
610 // travis runs a unique set of tables per build, to avoid conflict
611 $prefix = '';
612 if ($build_id = $this->getEnvVar('TRAVIS_JOB_NUMBER')) {
613 $prefix = sprintf('build_%s_', $build_id);
614 } else {
615 if (!$this->deleteDynamoDb($client, $prefix, true)) {
616 return $this->dynamodb = new NullStorage('DynamoDb', 'Timed out while waiting for DynamoDB deletion (30 seconds)');
617 }
618 }
619 $this->createDynamoDb($client, $prefix);
620 $this->populateDynamoDb($client, $prefix);
621 $config = array(
622 'client_table' => $prefix.'oauth_clients',
623 'access_token_table' => $prefix.'oauth_access_tokens',
624 'refresh_token_table' => $prefix.'oauth_refresh_tokens',
625 'code_table' => $prefix.'oauth_authorization_codes',
626 'user_table' => $prefix.'oauth_users',
627 'jwt_table' => $prefix.'oauth_jwt',
628 'scope_table' => $prefix.'oauth_scopes',
629 'public_key_table' => $prefix.'oauth_public_keys',
630 );
631 $this->dynamodb = new DynamoDB($client, $config);
632 } elseif (!$this->dynamodb) {
633 $this->dynamodb = new NullStorage('DynamoDb', 'unable to connect to DynamoDB');
634 }
635 } else {
636 $this->dynamodb = new NullStorage('DynamoDb', 'Missing DynamoDB library. Please run "composer.phar require aws/aws-sdk-php:dev-master');
637 }
638 }
639
640 return $this->dynamodb;
641 }
642
643 private function getDynamoDbClient()
644 {
645 $config = array();
646 // check for environment variables
647 if (($key = $this->getEnvVar('AWS_ACCESS_KEY_ID')) && ($secret = $this->getEnvVar('AWS_SECRET_KEY'))) {
648 $config['key'] = $key;
649 $config['secret'] = $secret;
650 } else {
651 // fall back on ~/.aws/credentials file
652 // @see http://docs.aws.amazon.com/aws-sdk-php/guide/latest/credentials.html#credential-profiles
653 if (!file_exists($this->getEnvVar('HOME') . '/.aws/credentials')) {
654 $this->dynamodb = new NullStorage('DynamoDb', 'No aws credentials file found, and no AWS_ACCESS_KEY_ID or AWS_SECRET_KEY environment variable set');
655
656 return;
657 }
658
659 // set profile in AWS_PROFILE environment variable, defaults to "default"
660 $config['profile'] = $this->getEnvVar('AWS_PROFILE', 'default');
661 }
662
663 // set region in AWS_REGION environment variable, defaults to "us-east-1"
664 $config['region'] = $this->getEnvVar('AWS_REGION', \Aws\Common\Enum\Region::US_EAST_1);
665
666 return \Aws\DynamoDb\DynamoDbClient::factory($config);
667 }
668
669 private function deleteDynamoDb(\Aws\DynamoDb\DynamoDbClient $client, $prefix = null, $waitForDeletion = false)
670 {
671 $tablesList = explode(' ', 'oauth_access_tokens oauth_authorization_codes oauth_clients oauth_jwt oauth_public_keys oauth_refresh_tokens oauth_scopes oauth_users');
672 $nbTables = count($tablesList);
673
674 // Delete all table.
675 foreach ($tablesList as $key => $table) {
676 try {
677 $client->deleteTable(array('TableName' => $prefix.$table));
678 } catch (\Aws\DynamoDb\Exception\DynamoDbException $e) {
679 // Table does not exist : nothing to do
680 }
681 }
682
683 // Wait for deleting
684 if ($waitForDeletion) {
685 $retries = 5;
686 $nbTableDeleted = 0;
687 while ($nbTableDeleted != $nbTables) {
688 $nbTableDeleted = 0;
689 foreach ($tablesList as $key => $table) {
690 try {
691 $result = $client->describeTable(array('TableName' => $prefix.$table));
692 } catch (\Aws\DynamoDb\Exception\DynamoDbException $e) {
693 // Table does not exist : nothing to do
694 $nbTableDeleted++;
695 }
696 }
697 if ($nbTableDeleted != $nbTables) {
698 if ($retries < 0) {
699 // we are tired of waiting
700 return false;
701 }
702 sleep(5);
703 echo "Sleeping 5 seconds for DynamoDB ($retries more retries)...\n";
704 $retries--;
705 }
706 }
707 }
708
709 return true;
710 }
711
712 private function createDynamoDb(\Aws\DynamoDb\DynamoDbClient $client, $prefix = null)
713 {
714 $tablesList = explode(' ', 'oauth_access_tokens oauth_authorization_codes oauth_clients oauth_jwt oauth_public_keys oauth_refresh_tokens oauth_scopes oauth_users');
715 $nbTables = count($tablesList);
716 $client->createTable(array(
717 'TableName' => $prefix.'oauth_access_tokens',
718 'AttributeDefinitions' => array(
719 array('AttributeName' => 'access_token','AttributeType' => 'S')
720 ),
721 'KeySchema' => array(array('AttributeName' => 'access_token','KeyType' => 'HASH')),
722 'ProvisionedThroughput' => array('ReadCapacityUnits' => 1,'WriteCapacityUnits' => 1)
723 ));
724
725 $client->createTable(array(
726 'TableName' => $prefix.'oauth_authorization_codes',
727 'AttributeDefinitions' => array(
728 array('AttributeName' => 'authorization_code','AttributeType' => 'S')
729 ),
730 'KeySchema' => array(array('AttributeName' => 'authorization_code','KeyType' => 'HASH')),
731 'ProvisionedThroughput' => array('ReadCapacityUnits' => 1,'WriteCapacityUnits' => 1)
732 ));
733
734 $client->createTable(array(
735 'TableName' => $prefix.'oauth_clients',
736 'AttributeDefinitions' => array(
737 array('AttributeName' => 'client_id','AttributeType' => 'S')
738 ),
739 'KeySchema' => array(array('AttributeName' => 'client_id','KeyType' => 'HASH')),
740 'ProvisionedThroughput' => array('ReadCapacityUnits' => 1,'WriteCapacityUnits' => 1)
741 ));
742
743 $client->createTable(array(
744 'TableName' => $prefix.'oauth_jwt',
745 'AttributeDefinitions' => array(
746 array('AttributeName' => 'client_id','AttributeType' => 'S'),
747 array('AttributeName' => 'subject','AttributeType' => 'S')
748 ),
749 'KeySchema' => array(
750 array('AttributeName' => 'client_id','KeyType' => 'HASH'),
751 array('AttributeName' => 'subject','KeyType' => 'RANGE')
752 ),
753 'ProvisionedThroughput' => array('ReadCapacityUnits' => 1,'WriteCapacityUnits' => 1)
754 ));
755
756 $client->createTable(array(
757 'TableName' => $prefix.'oauth_public_keys',
758 'AttributeDefinitions' => array(
759 array('AttributeName' => 'client_id','AttributeType' => 'S')
760 ),
761 'KeySchema' => array(array('AttributeName' => 'client_id','KeyType' => 'HASH')),
762 'ProvisionedThroughput' => array('ReadCapacityUnits' => 1,'WriteCapacityUnits' => 1)
763 ));
764
765 $client->createTable(array(
766 'TableName' => $prefix.'oauth_refresh_tokens',
767 'AttributeDefinitions' => array(
768 array('AttributeName' => 'refresh_token','AttributeType' => 'S')
769 ),
770 'KeySchema' => array(array('AttributeName' => 'refresh_token','KeyType' => 'HASH')),
771 'ProvisionedThroughput' => array('ReadCapacityUnits' => 1,'WriteCapacityUnits' => 1)
772 ));
773
774 $client->createTable(array(
775 'TableName' => $prefix.'oauth_scopes',
776 'AttributeDefinitions' => array(
777 array('AttributeName' => 'scope','AttributeType' => 'S'),
778 array('AttributeName' => 'is_default','AttributeType' => 'S')
779 ),
780 'KeySchema' => array(array('AttributeName' => 'scope','KeyType' => 'HASH')),
781 'GlobalSecondaryIndexes' => array(
782 array(
783 'IndexName' => 'is_default-index',
784 'KeySchema' => array(array('AttributeName' => 'is_default', 'KeyType' => 'HASH')),
785 'Projection' => array('ProjectionType' => 'ALL'),
786 'ProvisionedThroughput' => array('ReadCapacityUnits' => 1,'WriteCapacityUnits' => 1)
787 ),
788 ),
789 'ProvisionedThroughput' => array('ReadCapacityUnits' => 1,'WriteCapacityUnits' => 1)
790 ));
791
792 $client->createTable(array(
793 'TableName' => $prefix.'oauth_users',
794 'AttributeDefinitions' => array(array('AttributeName' => 'username','AttributeType' => 'S')),
795 'KeySchema' => array(array('AttributeName' => 'username','KeyType' => 'HASH')),
796 'ProvisionedThroughput' => array('ReadCapacityUnits' => 1,'WriteCapacityUnits' => 1)
797 ));
798
799 // Wait for creation
800 $nbTableCreated = 0;
801 while ($nbTableCreated != $nbTables) {
802 $nbTableCreated = 0;
803 foreach ($tablesList as $key => $table) {
804 try {
805 $result = $client->describeTable(array('TableName' => $prefix.$table));
806 if ($result['Table']['TableStatus'] == 'ACTIVE') {
807 $nbTableCreated++;
808 }
809 } catch (\Aws\DynamoDb\Exception\DynamoDbException $e) {
810 // Table does not exist : nothing to do
811 $nbTableCreated++;
812 }
813 }
814 if ($nbTableCreated != $nbTables) {
815 sleep(1);
816 }
817 }
818 }
819
820 private function populateDynamoDb($client, $prefix = null)
821 {
822 // set up scopes
823 foreach (explode(' ', 'supportedscope1 supportedscope2 supportedscope3 supportedscope4 clientscope1 clientscope2 clientscope3') as $supportedScope) {
824 $client->putItem(array(
825 'TableName' => $prefix.'oauth_scopes',
826 'Item' => array('scope' => array('S' => $supportedScope))
827 ));
828 }
829
830 foreach (array('defaultscope1', 'defaultscope2') as $defaultScope) {
831 $client->putItem(array(
832 'TableName' => $prefix.'oauth_scopes',
833 'Item' => array('scope' => array('S' => $defaultScope), 'is_default' => array('S' => "true"))
834 ));
835 }
836
837 $client->putItem(array(
838 'TableName' => $prefix.'oauth_clients',
839 'Item' => array(
840 'client_id' => array('S' => 'Test Client ID'),
841 'client_secret' => array('S' => 'TestSecret'),
842 'scope' => array('S' => 'clientscope1 clientscope2')
843 )
844 ));
845
846 $client->putItem(array(
847 'TableName' => $prefix.'oauth_clients',
848 'Item' => array(
849 'client_id' => array('S' => 'Test Client ID 2'),
850 'client_secret' => array('S' => 'TestSecret'),
851 'scope' => array('S' => 'clientscope1 clientscope2 clientscope3')
852 )
853 ));
854
855 $client->putItem(array(
856 'TableName' => $prefix.'oauth_clients',
857 'Item' => array(
858 'client_id' => array('S' => 'Test Default Scope Client ID'),
859 'client_secret' => array('S' => 'TestSecret'),
860 'scope' => array('S' => 'clientscope1 clientscope2')
861 )
862 ));
863
864 $client->putItem(array(
865 'TableName' => $prefix.'oauth_clients',
866 'Item' => array(
867 'client_id' => array('S' => 'oauth_test_client'),
868 'client_secret' => array('S' => 'testpass'),
869 'grant_types' => array('S' => 'implicit password')
870 )
871 ));
872
873 $client->putItem(array(
874 'TableName' => $prefix.'oauth_access_tokens',
875 'Item' => array(
876 'access_token' => array('S' => 'testtoken'),
877 'client_id' => array('S' => 'Some Client'),
878 )
879 ));
880
881 $client->putItem(array(
882 'TableName' => $prefix.'oauth_access_tokens',
883 'Item' => array(
884 'access_token' => array('S' => 'accesstoken-openid-connect'),
885 'client_id' => array('S' => 'Some Client'),
886 'user_id' => array('S' => 'testuser'),
887 )
888 ));
889
890 $client->putItem(array(
891 'TableName' => $prefix.'oauth_authorization_codes',
892 'Item' => array(
893 'authorization_code' => array('S' => 'testcode'),
894 'client_id' => array('S' => 'Some Client'),
895 )
896 ));
897
898 $client->putItem(array(
899 'TableName' => $prefix.'oauth_users',
900 'Item' => array(
901 'username' => array('S' => 'testuser'),
902 'password' => array('S' => 'password'),
903 'email' => array('S' => 'testuser@test.com'),
904 'email_verified' => array('S' => 'true'),
905 )
906 ));
907
908 $client->putItem(array(
909 'TableName' => $prefix.'oauth_public_keys',
910 'Item' => array(
911 'client_id' => array('S' => 'ClientID_One'),
912 'public_key' => array('S' => 'client_1_public'),
913 'private_key' => array('S' => 'client_1_private'),
914 'encryption_algorithm' => array('S' => 'RS256'),
915 )
916 ));
917
918 $client->putItem(array(
919 'TableName' => $prefix.'oauth_public_keys',
920 'Item' => array(
921 'client_id' => array('S' => 'ClientID_Two'),
922 'public_key' => array('S' => 'client_2_public'),
923 'private_key' => array('S' => 'client_2_private'),
924 'encryption_algorithm' => array('S' => 'RS256'),
925 )
926 ));
927
928 $client->putItem(array(
929 'TableName' => $prefix.'oauth_public_keys',
930 'Item' => array(
931 'client_id' => array('S' => '0'),
932 'public_key' => array('S' => $this->getTestPublicKey()),
933 'private_key' => array('S' => $this->getTestPrivateKey()),
934 'encryption_algorithm' => array('S' => 'RS256'),
935 )
936 ));
937
938 $client->putItem(array(
939 'TableName' => $prefix.'oauth_jwt',
940 'Item' => array(
941 'client_id' => array('S' => 'oauth_test_client'),
942 'subject' => array('S' => 'test_subject'),
943 'public_key' => array('S' => $this->getTestPublicKey()),
944 )
945 ));
946 }
947
948 public function cleanupTravisDynamoDb($prefix = null)
949 {
950 if (is_null($prefix)) {
951 // skip this when not applicable
952 if (!$this->getEnvVar('TRAVIS') || self::DYNAMODB_PHP_VERSION != $this->getEnvVar('TRAVIS_PHP_VERSION')) {
953 return;
954 }
955
956 $prefix = sprintf('build_%s_', $this->getEnvVar('TRAVIS_JOB_NUMBER'));
957 }
958
959 $client = $this->getDynamoDbClient();
960 $this->deleteDynamoDb($client, $prefix);
961 }
962
963 private function getEnvVar($var, $default = null)
964 {
965 return isset($_SERVER[$var]) ? $_SERVER[$var] : (getenv($var) ?: $default);
966 }
967}