diff --git a/CHANGELOG.md b/CHANGELOG.md index cfb3ef1..25fdf74 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,12 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] +## [4.0.0] - 2020-05-18 +### Removed +- Remove Insite ID Store adapter +### Added +- New config option for converting Workday fields into ID Broker 'groups' + ## [3.3.1] - 2020-05-12 ### Fixed - Application logs from console scripts sent to stdout @@ -171,7 +177,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ### Added - First release. -[Unreleased]: https://github.com/silinternational/idp-id-sync/compare/3.3.1...develop +[Unreleased]: https://github.com/silinternational/idp-id-sync/compare/4.0.0...develop +[4.0.0]: https://github.com/silinternational/idp-id-sync/compare/3.3.1...4.0.0 [3.3.1]: https://github.com/silinternational/idp-id-sync/compare/3.3.0...3.3.1 [3.3.0]: https://github.com/silinternational/idp-id-sync/compare/3.2.0...3.3.0 [3.2.0]: https://github.com/silinternational/idp-id-sync/compare/3.1.0...3.2.0 diff --git a/Makefile b/Makefile index 2d7eb80..181f809 100644 --- a/Makefile +++ b/Makefile @@ -42,8 +42,11 @@ psr2: # NOTE: When running tests locally, make sure you don't exclude the integration # tests (which we do when testing on Codeship). -test: deps app broker +test: deps unittest app broker sleep 15 && make behat testci: deps app broker docker-compose run --rm cli bash -c "./run-tests.sh" + +unittest: + docker-compose run --rm cli vendor/bin/phpunit diff --git a/application/check-psr2.sh b/application/check-psr2.sh index aeb4708..76e53f6 100755 --- a/application/check-psr2.sh +++ b/application/check-psr2.sh @@ -2,7 +2,7 @@ # Try to install composer dev dependencies cd /data -composer install --no-interaction --no-scripts +composer install --no-interaction --no-scripts --no-progress # Check the code against PSR-2. vendor/bin/php-cs-fixer fix -v --dry-run --stop-on-violation --using-cache=no . diff --git a/application/common/components/IdStoreBase.php b/application/common/components/IdStoreBase.php index 67756ba..2fbe2f2 100644 --- a/application/common/components/IdStoreBase.php +++ b/application/common/components/IdStoreBase.php @@ -2,7 +2,6 @@ namespace Sil\Idp\IdSync\common\components; use Sil\Idp\IdSync\common\components\adapters\GoogleSheetsIdStore; -use Sil\Idp\IdSync\common\components\adapters\InsiteIdStore; use Sil\Idp\IdSync\common\components\adapters\SagePeopleIdStore; use Sil\Idp\IdSync\common\components\adapters\WorkdayIdStore; use Sil\Idp\IdSync\common\components\adapters\fakes\FakeIdStore; @@ -14,14 +13,12 @@ abstract class IdStoreBase extends Component implements IdStoreInterface { const ADAPTER_FAKE = 'fake'; const ADAPTER_GOOGLE_SHEETS = 'googlesheets'; - const ADAPTER_INSITE = 'insite'; const ADAPTER_WORKDAY = 'workday'; const ADAPTER_SAGE_PEOPLE = 'sagepeople'; protected static $adapters = [ self::ADAPTER_FAKE => FakeIdStore::class, self::ADAPTER_GOOGLE_SHEETS => GoogleSheetsIdStore::class, - self::ADAPTER_INSITE => InsiteIdStore::class, self::ADAPTER_WORKDAY => WorkdayIdStore::class, self::ADAPTER_SAGE_PEOPLE => SagePeopleIdStore::class, ]; diff --git a/application/common/components/adapters/InsiteIdStore.php b/application/common/components/adapters/InsiteIdStore.php deleted file mode 100644 index df4e99e..0000000 --- a/application/common/components/adapters/InsiteIdStore.php +++ /dev/null @@ -1,188 +0,0 @@ -$requiredProperty)) { - throw new InvalidArgumentException(sprintf( - 'No %s was provided.', - $requiredProperty - ), 1492115257); - } - } - - $this->baseUrl = rtrim($this->baseUrl, '/'); - - parent::init(); - } - - public static function getIdBrokerFieldNames() - { - return [ - 'employeenumber' => User::EMPLOYEE_ID, - 'firstname' => User::FIRST_NAME, - 'lastname' => User::LAST_NAME, - 'displayname' => User::DISPLAY_NAME, - 'email' => User::EMAIL, - 'username' => User::USERNAME, - 'locked' => User::LOCKED, - 'requires2sv' => User::REQUIRE_MFA, - 'supervisoremail' => User::MANAGER_EMAIL, - // No 'active' needed, since all ID Store records returned are active. - ]; - } - - /** - * Get the specified user's information. Note that inactive users will be - * treated as non-existent users. - * - * @param string $employeeId The Employee ID. - * @return User|null Information about the specified user, or null if no - * such active user was found. - * @throws Exception - */ - public function getActiveUser(string $employeeId) - { - $items = $this->getFromIdStore('/individual/' . $employeeId); - $numItems = count($items); - if ($numItems < 1) { - return null; - } elseif ($numItems === 1) { - return self::getAsUser($items[0]); - } else { - throw new Exception(sprintf( - 'Too many results (%s) for Employee ID %s.', - $numItems, - var_export($employeeId, true) - ), 1492443050); - } - } - - /** - * Get a list of users' information (containing at least an Employee ID) for - * all users changed since the specified time. - * - * @param int $unixTimestamp The time (as a UNIX timestamp). - * @return User[] - */ - public function getUsersChangedSince(int $unixTimestamp): array - { - $result = $this->getFromIdStore('/changes/' . $unixTimestamp); - if (! is_array($result)) { - throw new Exception(sprintf( - 'Unexpected result when getting users changed since %s (%s): %s', - var_export($unixTimestamp, true), - date('r', $unixTimestamp), - var_export($result, true) - ), 1492443064); - } - return self::getAsUsers($result); - } - - public function getAllActiveUsers(): array - { - $result = $this->getFromIdStore('/all/'); - if (! is_array($result)) { - throw new Exception(sprintf( - 'Unexpected result when getting all active users: %s', - var_export($result, true) - ), 1492444030); - } - return self::getAsUsers($result); - } - - /** - * Call the ID Store API itself. - * - * @param string $relativePath The URL to call, relative to the `baseUrl`. - * @param array $queryParameters (Optional:) An array with key => value - * pairs that should be included as query string parameters. The - * `api_key` and `api_sig` will be added automatically. - * @return array|null The resulting data, or null if unavailable (such as - * with a 404 response, or if no items were returned). - * @throws Exception - */ - protected function getFromIdStore( - string $relativePath, - array $queryParameters = [] - ) { - $fullUrl = $this->baseUrl . $relativePath; - $response = $this->getHttpClient()->get($fullUrl, [ - 'connect_timeout' => $this->timeout, - 'headers' => [ - 'Accept' => 'application/json', /** @todo Do we need/want this? */ - 'Accept-Encoding' => 'gzip', /** @todo Do we need/want this? */ - ], - 'http_errors' => false, - 'query' => ArrayHelper::merge($queryParameters, [ - 'api_key' => $this->apiKey, - 'api_sig' => HmacSigner::CalcApiSig( - $this->apiKey, - $this->apiSecret - ), - ]), - ]); - - $statusCode = (int)$response->getStatusCode(); - if ($statusCode === 404) { - return null; - } elseif (($statusCode >= 200) && ($statusCode <= 299)) { - $data = Json::decode($response->getBody()); - - /** - * @todo Detect paged results, and if present get the rest. - */ - - return $data['items'] ?? null; - } else { - throw new Exception(sprintf( - 'Unexpected response (%s %s): %s', - $response->getStatusCode(), - $response->getReasonPhrase(), - $response->getBody() - ), 1492113596); - } - } - - /** - * Get the HTTP client to use. - * - * @return Client - */ - protected function getHttpClient() - { - if ($this->httpClient === null) { - $this->httpClient = new Client(); - } - return $this->httpClient; - } - - public function getIdStoreName(): string - { - return 'Insite'; - } -} diff --git a/application/common/components/adapters/WorkdayIdStore.php b/application/common/components/adapters/WorkdayIdStore.php index 903edb6..aee6713 100644 --- a/application/common/components/adapters/WorkdayIdStore.php +++ b/application/common/components/adapters/WorkdayIdStore.php @@ -13,6 +13,7 @@ class WorkdayIdStore extends IdStoreBase public $apiUrl = null; public $username = null; public $password = null; + public $groupsFields = null; public $timeout = 45; // Timeout in seconds (per call to ID Store API). @@ -122,16 +123,7 @@ public function getAllActiveUsers() ), 1532982679); } - foreach ($allActiveUsers as $key => $user) { - $companyIDs = str_replace(" ", ",", $user["company_ids"] ?? ""); - $ouTree = str_replace(" ", ",", $user["ou_tree"] ?? ""); - if (strlen($companyIDs) > 0 && strlen($ouTree) > 0) { - $groups = $companyIDs . "," . $ouTree; - } else { - $groups = $companyIDs . $ouTree; - } - $allActiveUsers[$key]['Groups'] = $groups; - } + $this->generateGroupsLists($allActiveUsers); return self::getAsUsers($allActiveUsers); } @@ -153,4 +145,27 @@ public function getIdStoreName(): string { return 'Workday'; } + + public function generateGroupsLists(array &$users) + { + if ($this->groupsFields === null) { + $groupsFields = [ + 'company_ids', + 'ou_tree', + ]; + } else { + $groupsFields = explode(',', $this->groupsFields); + } + + foreach ($users as $key => $user) { + $groups = []; + foreach ($groupsFields as $groupsField) { + if (strlen($user[$groupsField]) > 0) { + $groupsSubList = explode(' ', $user[$groupsField ?? '']); + $groups = array_merge($groups, $groupsSubList); + } + } + $users[$key]['Groups'] = implode(',', $groups); + } + } } diff --git a/application/common/components/adapters/fakes/FakeIdStore.php b/application/common/components/adapters/fakes/FakeIdStore.php index ec05464..3d6e6d2 100644 --- a/application/common/components/adapters/fakes/FakeIdStore.php +++ b/application/common/components/adapters/fakes/FakeIdStore.php @@ -2,7 +2,7 @@ namespace Sil\Idp\IdSync\common\components\adapters\fakes; use Sil\Idp\IdSync\common\components\IdStoreBase; -use Sil\Idp\IdSync\common\components\adapters\InsiteIdStore; +use Sil\Idp\IdSync\common\models\User; use yii\helpers\ArrayHelper; class FakeIdStore extends IdStoreBase @@ -90,8 +90,18 @@ public function getAllActiveUsers() public static function getIdBrokerFieldNames() { - // For simplicity's sake, just use the field names from Insite. - return InsiteIdStore::getIdBrokerFieldNames(); + return [ + 'employeenumber' => User::EMPLOYEE_ID, + 'firstname' => User::FIRST_NAME, + 'lastname' => User::LAST_NAME, + 'displayname' => User::DISPLAY_NAME, + 'email' => User::EMAIL, + 'username' => User::USERNAME, + 'locked' => User::LOCKED, + 'requires2sv' => User::REQUIRE_MFA, + 'supervisoremail' => User::MANAGER_EMAIL, + // No 'active' needed, since all ID Store records returned are active. + ]; } public function getIdStoreName(): string diff --git a/application/composer.json b/application/composer.json index 476a865..9c19e38 100644 --- a/application/composer.json +++ b/application/composer.json @@ -6,23 +6,24 @@ "ext-json": "*", "fillup/fake-bower-assets": "2.0.9", "forevermatt/calc-api-sig": "^0.1.1", - "roave/security-advisories": "dev-master", "silinternational/email-service-php-client": "^2.0", "silinternational/idp-id-broker-php-client": "^3.0.0", "silinternational/php-array-dot-notation": "0.1.0", "silinternational/php-env": "^2.1.1", "silinternational/psr3-adapters": "^1.1", - "silinternational/yii2-json-log-targets": "^1.1", + "silinternational/yii2-json-log-targets": "^2.0", "yiisoft/yii2": "~2.0.15", "yiisoft/yii2-gii": "^2.0", "yiisoft/yii2-swiftmailer": "^2.0", "guzzlehttp/guzzle": "^6.2", - "google/apiclient": "^2.0" + "google/apiclient": "^2.0", + "codemix/yii2-streamlog": "^1.3" }, "require-dev": { "behat/behat": "^3.3", "phpunit/phpunit": "^6.0", - "friendsofphp/php-cs-fixer": "^2.9" + "friendsofphp/php-cs-fixer": "^2.9", + "roave/security-advisories": "dev-master" }, "autoload": { "psr-4": { diff --git a/application/composer.lock b/application/composer.lock index aa3c68b..a7b38de 100644 --- a/application/composer.lock +++ b/application/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "5d5d88157c53fc6e648b288a53fe769b", + "content-hash": "7cdb65c8e2ac1c6d957029dc8c4d311d", "packages": [ { "name": "bower-asset/inputmask", @@ -515,16 +515,16 @@ }, { "name": "google/apiclient-services", - "version": "v0.133", + "version": "v0.134", "source": { "type": "git", "url": "https://github.com/googleapis/google-api-php-client-services.git", - "reference": "dd53c4a23a5c4d868e409585e30ab31da278b5d2" + "reference": "4237f6725a48761701101e26e4575ddb053b5d80" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/googleapis/google-api-php-client-services/zipball/dd53c4a23a5c4d868e409585e30ab31da278b5d2", - "reference": "dd53c4a23a5c4d868e409585e30ab31da278b5d2", + "url": "https://api.github.com/repos/googleapis/google-api-php-client-services/zipball/4237f6725a48761701101e26e4575ddb053b5d80", + "reference": "4237f6725a48761701101e26e4575ddb053b5d80", "shasum": "" }, "require": { @@ -548,7 +548,7 @@ "keywords": [ "google" ], - "time": "2020-05-03T00:24:28+00:00" + "time": "2020-05-10T00:25:08+00:00" }, { "name": "google/auth", @@ -1355,12 +1355,12 @@ "source": { "type": "git", "url": "https://github.com/Roave/SecurityAdvisories.git", - "reference": "f46390d28af4fdb07c09d9aabf4c4e35149a7a08" + "reference": "5a342e2dc0408d026b97ee3176b5b406e54e3766" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/f46390d28af4fdb07c09d9aabf4c4e35149a7a08", - "reference": "f46390d28af4fdb07c09d9aabf4c4e35149a7a08", + "url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/5a342e2dc0408d026b97ee3176b5b406e54e3766", + "reference": "5a342e2dc0408d026b97ee3176b5b406e54e3766", "shasum": "" }, "conflict": { @@ -1373,6 +1373,7 @@ "asymmetricrypt/asymmetricrypt": ">=0,<9.9.99", "aws/aws-sdk-php": ">=3,<3.2.1", "bagisto/bagisto": "<0.1.5", + "barrelstrength/sprout-base-email": "<3.9", "bolt/bolt": "<3.6.10", "brightlocal/phpwhois": "<=4.2.5", "buddypress/buddypress": "<5.1.2", @@ -1505,7 +1506,7 @@ "socalnick/scn-social-auth": "<1.15.2", "spoonity/tcpdf": "<6.2.22", "squizlabs/php_codesniffer": ">=1,<2.8.1|>=3,<3.0.1", - "ssddanbrown/bookstack": "<0.25.3", + "ssddanbrown/bookstack": "<0.29.2", "stormpath/sdk": ">=0,<9.9.99", "studio-42/elfinder": "<2.1.49", "swiftmailer/swiftmailer": ">=4,<5.4.5", @@ -1550,8 +1551,8 @@ "titon/framework": ">=0,<9.9.99", "truckersmp/phpwhois": "<=4.3.1", "twig/twig": "<1.38|>=2,<2.7", - "typo3/cms": ">=6.2,<6.2.30|>=7,<7.6.32|>=8,<8.7.30|>=9,<9.5.12|>=10,<10.2.1", - "typo3/cms-core": ">=8,<8.7.30|>=9,<9.5.12|>=10,<10.2.1", + "typo3/cms": ">=6.2,<6.2.30|>=7,<7.6.32|>=8,<8.7.30|>=9,<9.5.17|>=10,<10.4.2", + "typo3/cms-core": ">=8,<8.7.30|>=9,<9.5.17|>=10,<10.4.2", "typo3/flow": ">=1,<1.0.4|>=1.1,<1.1.1|>=2,<2.0.1|>=2.3,<2.3.16|>=3,<3.0.10|>=3.1,<3.1.7|>=3.2,<3.2.7|>=3.3,<3.3.5", "typo3/neos": ">=1.1,<1.1.3|>=1.2,<1.2.13|>=2,<2.0.4", "typo3/phar-stream-wrapper": ">=1,<2.1.1|>=3,<3.1.1", @@ -1624,7 +1625,7 @@ "type": "tidelift" } ], - "time": "2020-05-04T14:37:25+00:00" + "time": "2020-05-12T11:18:47+00:00" }, { "name": "silinternational/email-service-php-client", @@ -1851,29 +1852,31 @@ }, { "name": "silinternational/yii2-json-log-targets", - "version": "1.1.0", + "version": "2.0.0", "source": { "type": "git", "url": "https://github.com/silinternational/yii2-json-log-targets.git", - "reference": "957a6cec938ebbadddc57dc84ce281fa49f2ced5" + "reference": "10b10f9253e86b38dde6e96589025952bfe18a9d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/silinternational/yii2-json-log-targets/zipball/957a6cec938ebbadddc57dc84ce281fa49f2ced5", - "reference": "957a6cec938ebbadddc57dc84ce281fa49f2ced5", + "url": "https://api.github.com/repos/silinternational/yii2-json-log-targets/zipball/10b10f9253e86b38dde6e96589025952bfe18a9d", + "reference": "10b10f9253e86b38dde6e96589025952bfe18a9d", "shasum": "" }, "require": { - "codemix/yii2-streamlog": "^1.3", "php": ">=5.6", - "roave/security-advisories": "dev-master", - "silinternational/email-service-php-client": "^2.0.0", "yiisoft/yii2": "^2.0" }, "require-dev": { "behat/behat": "^3.3", "fillup/fake-bower-assets": "^2.0", - "phpunit/phpunit": "^6.2 || ^5.7" + "phpunit/phpunit": "^6.2 || ^5.7", + "roave/security-advisories": "dev-master" + }, + "suggest": { + "codemix/yii2-streamlog": "To send log to a stream such as stdout using JsonStreamTarget", + "silinternational/email-service-php-client": "To send log via email using EmailServiceTarget" }, "type": "library", "autoload": { @@ -1894,7 +1897,7 @@ "target", "yii2" ], - "time": "2020-04-07T17:54:08+00:00" + "time": "2020-05-13T18:49:32+00:00" }, { "name": "swiftmailer/swiftmailer", @@ -1960,16 +1963,16 @@ }, { "name": "symfony/polyfill-iconv", - "version": "v1.15.0", + "version": "v1.17.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-iconv.git", - "reference": "ad6d62792bfbcfc385dd34b424d4fcf9712a32c8" + "reference": "c4de7601eefbf25f9d47190abe07f79fe0a27424" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-iconv/zipball/ad6d62792bfbcfc385dd34b424d4fcf9712a32c8", - "reference": "ad6d62792bfbcfc385dd34b424d4fcf9712a32c8", + "url": "https://api.github.com/repos/symfony/polyfill-iconv/zipball/c4de7601eefbf25f9d47190abe07f79fe0a27424", + "reference": "c4de7601eefbf25f9d47190abe07f79fe0a27424", "shasum": "" }, "require": { @@ -1981,7 +1984,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.15-dev" + "dev-master": "1.17-dev" } }, "autoload": { @@ -2029,20 +2032,20 @@ "type": "tidelift" } ], - "time": "2020-03-09T19:04:49+00:00" + "time": "2020-05-12T16:47:27+00:00" }, { "name": "symfony/polyfill-intl-idn", - "version": "v1.15.0", + "version": "v1.17.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-idn.git", - "reference": "47bd6aa45beb1cd7c6a16b7d1810133b728bdfcf" + "reference": "3bff59ea7047e925be6b7f2059d60af31bb46d6a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/47bd6aa45beb1cd7c6a16b7d1810133b728bdfcf", - "reference": "47bd6aa45beb1cd7c6a16b7d1810133b728bdfcf", + "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/3bff59ea7047e925be6b7f2059d60af31bb46d6a", + "reference": "3bff59ea7047e925be6b7f2059d60af31bb46d6a", "shasum": "" }, "require": { @@ -2056,7 +2059,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.15-dev" + "dev-master": "1.17-dev" } }, "autoload": { @@ -2105,20 +2108,20 @@ "type": "tidelift" } ], - "time": "2020-03-09T19:04:49+00:00" + "time": "2020-05-12T16:47:27+00:00" }, { "name": "symfony/polyfill-mbstring", - "version": "v1.15.0", + "version": "v1.17.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "81ffd3a9c6d707be22e3012b827de1c9775fc5ac" + "reference": "fa79b11539418b02fc5e1897267673ba2c19419c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/81ffd3a9c6d707be22e3012b827de1c9775fc5ac", - "reference": "81ffd3a9c6d707be22e3012b827de1c9775fc5ac", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/fa79b11539418b02fc5e1897267673ba2c19419c", + "reference": "fa79b11539418b02fc5e1897267673ba2c19419c", "shasum": "" }, "require": { @@ -2130,7 +2133,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.15-dev" + "dev-master": "1.17-dev" } }, "autoload": { @@ -2178,20 +2181,20 @@ "type": "tidelift" } ], - "time": "2020-03-09T19:04:49+00:00" + "time": "2020-05-12T16:47:27+00:00" }, { "name": "symfony/polyfill-php72", - "version": "v1.15.0", + "version": "v1.17.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php72.git", - "reference": "37b0976c78b94856543260ce09b460a7bc852747" + "reference": "f048e612a3905f34931127360bdd2def19a5e582" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/37b0976c78b94856543260ce09b460a7bc852747", - "reference": "37b0976c78b94856543260ce09b460a7bc852747", + "url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/f048e612a3905f34931127360bdd2def19a5e582", + "reference": "f048e612a3905f34931127360bdd2def19a5e582", "shasum": "" }, "require": { @@ -2200,7 +2203,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.15-dev" + "dev-master": "1.17-dev" } }, "autoload": { @@ -2247,7 +2250,7 @@ "type": "tidelift" } ], - "time": "2020-02-27T09:26:54+00:00" + "time": "2020-05-12T16:47:27+00:00" }, { "name": "yiisoft/yii2", @@ -5196,16 +5199,16 @@ }, { "name": "symfony/polyfill-ctype", - "version": "v1.15.0", + "version": "v1.17.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "4719fa9c18b0464d399f1a63bf624b42b6fa8d14" + "reference": "e94c8b1bbe2bc77507a1056cdb06451c75b427f9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/4719fa9c18b0464d399f1a63bf624b42b6fa8d14", - "reference": "4719fa9c18b0464d399f1a63bf624b42b6fa8d14", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/e94c8b1bbe2bc77507a1056cdb06451c75b427f9", + "reference": "e94c8b1bbe2bc77507a1056cdb06451c75b427f9", "shasum": "" }, "require": { @@ -5217,7 +5220,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.15-dev" + "dev-master": "1.17-dev" } }, "autoload": { @@ -5264,20 +5267,20 @@ "type": "tidelift" } ], - "time": "2020-02-27T09:26:54+00:00" + "time": "2020-05-12T16:14:59+00:00" }, { "name": "symfony/polyfill-php70", - "version": "v1.15.0", + "version": "v1.17.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php70.git", - "reference": "2a18e37a489803559284416df58c71ccebe50bf0" + "reference": "82225c2d7d23d7e70515496d249c0152679b468e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php70/zipball/2a18e37a489803559284416df58c71ccebe50bf0", - "reference": "2a18e37a489803559284416df58c71ccebe50bf0", + "url": "https://api.github.com/repos/symfony/polyfill-php70/zipball/82225c2d7d23d7e70515496d249c0152679b468e", + "reference": "82225c2d7d23d7e70515496d249c0152679b468e", "shasum": "" }, "require": { @@ -5287,7 +5290,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.15-dev" + "dev-master": "1.17-dev" } }, "autoload": { @@ -5323,20 +5326,34 @@ "portable", "shim" ], - "time": "2020-02-27T09:26:54+00:00" + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2020-05-12T16:47:27+00:00" }, { "name": "symfony/polyfill-php73", - "version": "v1.15.0", + "version": "v1.17.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php73.git", - "reference": "0f27e9f464ea3da33cbe7ca3bdf4eb66def9d0f7" + "reference": "a760d8964ff79ab9bf057613a5808284ec852ccc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/0f27e9f464ea3da33cbe7ca3bdf4eb66def9d0f7", - "reference": "0f27e9f464ea3da33cbe7ca3bdf4eb66def9d0f7", + "url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/a760d8964ff79ab9bf057613a5808284ec852ccc", + "reference": "a760d8964ff79ab9bf057613a5808284ec852ccc", "shasum": "" }, "require": { @@ -5345,7 +5362,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.15-dev" + "dev-master": "1.17-dev" } }, "autoload": { @@ -5395,7 +5412,7 @@ "type": "tidelift" } ], - "time": "2020-02-27T09:26:54+00:00" + "time": "2020-05-12T16:47:27+00:00" }, { "name": "symfony/process", diff --git a/application/features/behat.yml b/application/features/behat.yml index f8dfe52..9e8b0d3 100644 --- a/application/features/behat.yml +++ b/application/features/behat.yml @@ -6,11 +6,6 @@ default: idp_id_broker_integration_features: paths: [ "%paths.base%/../features/idp-id-broker-integration.feature" ] contexts: [ Sil\Idp\IdSync\Behat\Context\IdpIdBrokerIntegrationContext ] - insite_integration_features: - filters: - tags: '~@canUpdateLastSynced' - paths: [ "%paths.base%/../features/id-store-integration.feature" ] - contexts: [ Sil\Idp\IdSync\Behat\Context\InsiteIntegrationContext ] safety_cutoff_features: paths: [ "%paths.base%/../features/safetyCutoff.feature" ] contexts: [ Sil\Idp\IdSync\Behat\Context\SafetyCutoffContext ] diff --git a/application/features/bootstrap/InsiteIntegrationContext.php b/application/features/bootstrap/InsiteIntegrationContext.php deleted file mode 100644 index e1e342b..0000000 --- a/application/features/bootstrap/InsiteIntegrationContext.php +++ /dev/null @@ -1,43 +0,0 @@ -idStore = new InsiteIdStore([ - 'apiKey' => $insiteApiKey, - 'apiSecret' => $insiteApiSecret, - 'baseUrl' => $insiteBaseUrl, - ]); - } - - /** - * @When I ask the ID Store for a specific active user - */ - public function iAskTheIdStoreForASpecificActiveUser() - { - $this->activeEmployeeId = Env::requireEnv('TEST_INSITE_EMPLOYEE_ID'); - $this->result = $this->idStore->getActiveUser($this->activeEmployeeId); - } -} diff --git a/application/phpunit.xml b/application/phpunit.xml new file mode 100644 index 0000000..b8993c9 --- /dev/null +++ b/application/phpunit.xml @@ -0,0 +1,9 @@ + + + + tests + + + + + diff --git a/application/run-tests.sh b/application/run-tests.sh index 9e988a4..1bd691b 100755 --- a/application/run-tests.sh +++ b/application/run-tests.sh @@ -2,7 +2,7 @@ # Try to install composer dev dependencies cd /data -composer install --no-interaction --no-scripts +composer install --no-interaction --no-scripts --no-progress # If that failed, exit. rc=$?; if [[ $rc != 0 ]]; then exit $rc; fi @@ -18,6 +18,12 @@ rc=$?; if [[ $rc != 0 ]]; then exit $rc; fi whenavail brokerdb 3306 60 echo Waited for brokerdb sleep 15 +# Run the unit tests +./vendor/bin/phpunit + +# If they failed, exit. +rc=$?; if [[ $rc != 0 ]]; then exit $rc; fi + # Run the feature tests (skipping integration tests) ./vendor/bin/behat --config=features/behat.yml --tags '~@integration' diff --git a/application/tests/WorkdayIdStoreTest.php b/application/tests/WorkdayIdStoreTest.php new file mode 100644 index 0000000..6a9318f --- /dev/null +++ b/application/tests/WorkdayIdStoreTest.php @@ -0,0 +1,84 @@ +getWorkdayIdStore(); + $this->assertEquals('Workday', $idStore->getIdStoreName()); + } + + public function testGenerateGroupsListsDefault() + { + $idStore = $this->getWorkdayIdStore(); + $users = [ + [ + 'company_ids' => 'a b c', + 'ou_tree' => 'd e f', + ], + ]; + $idStore->generateGroupsLists($users); + $this->assertEquals('a,b,c,d,e,f', $users[0]['Groups']); + } + + public function testGenerateGroupsListsCustom() + { + $idStore = $this->getWorkdayIdStore([ + 'groupsFields' => 'field1,field2' + ]); + $users = [ + [ + 'field1' => 'x y z', + 'field2' => '1 2 3', + ], + ]; + $idStore->generateGroupsLists($users); + $this->assertEquals('x,y,z,1,2,3', $users[0]['Groups']); + } + + public function testGenerateGroupsListsEmptyCompanyIDs() + { + $idStore = $this->getWorkdayIdStore(); + $users = [ + [ + 'company_ids' => '', + 'ou_tree' => 'd e f', + ], + ]; + $idStore->generateGroupsLists($users); + $this->assertEquals('d,e,f', $users[0]['Groups']); + } + + public function testGenerateGroupsListsEmptyOUTree() + { + $idStore = $this->getWorkdayIdStore(); + $users = [ + [ + 'company_ids' => 'a b c', + 'ou_tree' => '', + ], + ]; + $idStore->generateGroupsLists($users); + $this->assertEquals('a,b,c', $users[0]['Groups']); + } + + private function getWorkdayIdStore($config = []): WorkdayIdStore + { + return new WorkdayIdStore(array_merge($config, [ + 'apiUrl' => 'https://workday.example.com', + 'username' => 'username', + 'password' => 'password', + ])); + } +} diff --git a/local.env.dist b/local.env.dist index 608822a..5e5a93f 100644 --- a/local.env.dist +++ b/local.env.dist @@ -30,14 +30,6 @@ ID_STORE_ADAPTER= ### Required values for Fake ID Store (when ID_STORE_ADAPTER=fake): (none) -### Required values for Insite ID Store (when ID_STORE_ADAPTER=insite): -#ID_STORE_CONFIG_apiKey= -#ID_STORE_CONFIG_apiSecret= - -# The URL to the ID Store. -# Example: http://id-store.example.com -#ID_STORE_CONFIG_baseUrl= - ### Required values for Google Sheets ID Store (when ID_STORE_ADAPTER=googlesheets): #ID_STORE_CONFIG_applicationName= #ID_STORE_CONFIG_jsonAuthFilePath= @@ -47,6 +39,10 @@ ID_STORE_ADAPTER= #ID_STORE_CONFIG_apiUrl= #ID_STORE_CONFIG_username= #ID_STORE_CONFIG_password= +# `groupsFields` is a comma-delimited list of Workday fields used to create the +# 'groups' field on the ID Broker. The content of each field is converted from +# space-delimited to comma-delimited and merged together to form the 'groups' field. +#ID_STORE_CONFIG_groupsFields= #### Required values for Sage People ID Store (when ID_STORE_ADAPTER=sagepeople): #ID_STORE_CONFIG_authUrl= @@ -97,10 +93,6 @@ ID_SYNC_ACCESS_TOKENS= #TEST_GOOGLE_SHEETS_CONFIG_spreadsheetId= #TEST_GOOGLE_SHEETS_EMPLOYEE_ID= #TEST_ID_SYNC_BASE_URL= -#TEST_INSITE_CONFIG_apiKey= -#TEST_INSITE_CONFIG_apiSecret= -#TEST_INSITE_CONFIG_baseUrl= -#TEST_INSITE_EMPLOYEE_ID= #TEST_WORKDAY_CONFIG_apiUrl= #TEST_WORKDAY_CONFIG_username= #TEST_WORKDAY_CONFIG_password=