From abe6b92722584c35f95c69687d2a857ccf0b450b Mon Sep 17 00:00:00 2001 From: Nafis Bey Date: Sun, 9 Aug 2020 00:27:13 +0000 Subject: [PATCH 1/7] feat: add roles to SAML auth requests --- php-classes/Slate/Connectors/Looker/Connector.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/php-classes/Slate/Connectors/Looker/Connector.php b/php-classes/Slate/Connectors/Looker/Connector.php index 753a538..b7547d3 100644 --- a/php-classes/Slate/Connectors/Looker/Connector.php +++ b/php-classes/Slate/Connectors/Looker/Connector.php @@ -677,7 +677,8 @@ public static function getSAMLAttributes(IPerson $Person) return [ 'Email' => [$Person->Email], 'FName' => [$Person->FirstName], - 'LName' => [$Person->LastName] + 'LName' => [$Person->LastName], + 'Roles' => static::getUserRoles($Person) ]; } } From 9b1fdc27264a4f4c889ea1d16dbb2c92952aa8e6 Mon Sep 17 00:00:00 2001 From: Nafis Bey Date: Thu, 3 Sep 2020 00:45:29 +0000 Subject: [PATCH 2/7] chore: clean-up connector logs --- .../Slate/Connectors/Looker/Connector.php | 62 +++++++++---------- 1 file changed, 31 insertions(+), 31 deletions(-) diff --git a/php-classes/Slate/Connectors/Looker/Connector.php b/php-classes/Slate/Connectors/Looker/Connector.php index b7547d3..f88c411 100644 --- a/php-classes/Slate/Connectors/Looker/Connector.php +++ b/php-classes/Slate/Connectors/Looker/Connector.php @@ -163,26 +163,26 @@ public static function pushUser(IPerson $User, LoggerInterface $logger = null, $ if (!$pretend) { $lookerResponse = LookerAPI::updateUser($Mapping->ExternalIdentifier, DataUtil::extractToFromDelta($lookerUserChanges)); $logger->debug( - 'Updating Looker for user {slateUsername}', + 'Updating Looker for user {slateEmail}', [ - 'slateUsername' => $User->Username, + 'slateEmail' => $User->Email, 'lookerUserChanges' => $lookerUserChanges, 'lookerResponse' => $lookerResponse ] ); } $logger->notice( - 'Updated user {slateUsername}', + 'Updated user {slateEmail}', [ - 'slateUsername' => $User->Username, + 'slateEmail' => $User->Email, 'changes' => $changes['user'] ] ); } else { $logger->debug( - 'Looker user matches Slate user {slateUsername}', + 'Looker user matches Slate user {slateEmail}', [ - 'slateUsername' => $User->Username + 'slateEmail' => $User->Email ] ); } @@ -228,16 +228,16 @@ public static function pushUser(IPerson $User, LoggerInterface $logger = null, $ return new SyncResult( (!empty($changes) || !empty($userUpdated)) ? SyncResult::STATUS_UPDATED : SyncResult::STATUS_VERIFIED, - 'Looker account for {slateUsername} found and verified up-to-date.', + 'Looker account for {slateEmail} found and verified up-to-date.', [ - 'slateUsername' => $User->Username + 'slateEmail' => $User->Email ] ); } else { // try to create user if no mapping found // skip accounts with no email - if (!$User->PrimaryEmail) { + if (!$User->Email) { $logger->debug( - 'Skipping user {slateUsername} without Primary Email', + 'Skipping user {slateUsername} without Email', [ 'slateUsername' => $User->Username ] @@ -256,14 +256,14 @@ public static function pushUser(IPerson $User, LoggerInterface $logger = null, $ $lookerResponse = LookerAPI::createUser([ 'first_name' => $User->FirstName, 'last_name' => $User->LastName, - 'email' => $User->PrimaryEmail->toString() + 'email' => $User->Email ]); if (empty($lookerResponse['id'])) { throw new SyncException( - 'Failed to create Looker user for {slateUsername}', + 'Failed to create Looker user for {slateEmail}', [ - 'slateUsername' => $User->Username, + 'slateEmail' => $User->Email, 'lookerResponse' => $lookerResponse ] ); @@ -276,9 +276,9 @@ public static function pushUser(IPerson $User, LoggerInterface $logger = null, $ } $logger->notice( - 'Created Looker user for {slateUsername}', + 'Created Looker user for {slateEmail}', [ - 'slateUsername' => $User->Username, + 'slateEmail' => $User->Email, 'lookerResponse' => $pretend ? '(pretend-mode)' : $lookerResponse ] ); @@ -313,9 +313,9 @@ public static function pushUser(IPerson $User, LoggerInterface $logger = null, $ return new SyncResult( SyncResult::STATUS_CREATED, - 'Created Looker user for {slateUsername}, saved mapping to new Looker user #{lookerUserId}', + 'Created Looker user for {slateEmail}, saved mapping to new Looker user #{lookerUserId}', [ - 'slateUsername' => $User->Username, + 'slateEmail' => $User->Email, 'lookerUserId' => $pretend ? '(pretend-mode)' : $lookerResponse['id'], 'lookerUser' => $pretend ? '(pretend-mode)' : $lookerResponse ] @@ -357,9 +357,9 @@ protected static function syncUserRoles(IPerson $User, array $lookerUser, Logger if ($rolesToAdd = array_diff($userRoles, $roleIds)) { $logger->debug( - 'Updating {slateUsername} roles to: [{userRoles}]', + 'Updating {slateEmail} roles to: [{userRoles}]', [ - 'slateUsername' => $User->Username, + 'slateEmail' => $User->Email, 'userRoles' => join(',', array_unique(array_merge($userRoles, $roleIds))) ] ); @@ -441,9 +441,9 @@ protected static function syncUserGroups(IPerson $User, array $lookerUser, Logge if ($groupsToAdd = array_diff($userGroups, $groupIds)) { $logger->debug( - 'Updating {slateUsername} groups to: [{userGroups}]', + 'Updating {slateEmail} groups to: [{userGroups}]', [ - 'slateUsername' => $User->Username, + 'slateEmail' => $User->Email, 'userGroups' => join(',', array_unique(array_merge($userGroups, $groupIds))) ] ); @@ -492,9 +492,9 @@ protected static function syncUserGroups(IPerson $User, array $lookerUser, Logge return new SyncResult( empty($groupsToAdd) ? SyncResult::STATUS_VERIFIED : SyncResult::STATUS_UPDATED, - 'Updated groups for {slateUsername} in Looker ' . $pretend ? '(pretend-mode)' : '', + 'Updated groups for {slateEmail} in Looker ' . $pretend ? '(pretend-mode)' : '', [ - 'slateUsername' => $User->Username + 'slateEmail' => $User->Email ] ); } @@ -564,8 +564,8 @@ protected static function syncUserCustomAttributes(IPerson $User, array $lookerU ); } else { - $logger->debug('Syncing user custom attributes for {slateUsername}: {attributesToSet}', [ - 'slateUsername' => $User->Username, + $logger->debug('Syncing user custom attributes for {slateEmail}: {attributesToSet}', [ + 'slateEmail' => $User->Email, 'attributesToSet' => join(', ', array_map(function($c) { return "$c[name] => $c[value]"; }, $customAttributesToAdd)) ]); @@ -626,11 +626,10 @@ public static function pushUsers(IJob $Job, $pretend = true) foreach (User::getAllByWhere('Username IS NOT NULL AND AccountLevel != "Disabled"') AS $User) { $Job->debug( - 'Analyzing Slate user {slateUsername} ({slateUserClass}/{userGraduationYear})', + 'Analyzing Slate user {slateUsername} ({slateEmail})', [ 'slateUsername' => $User->Username, - 'slateUserClass' => $User->Class, - 'userGraduationYear' => $User->GraduationYear + 'slateEmail' => $User->Email ] ); $results['analyzed']++; @@ -673,12 +672,13 @@ public static function getSAMLNameId(IPerson $Person) public static function getSAMLAttributes(IPerson $Person) { - // TODO: add roles, groups, custom attributes + // TODO: add roles, groups, custom attributes? return [ 'Email' => [$Person->Email], 'FName' => [$Person->FirstName], - 'LName' => [$Person->LastName], - 'Roles' => static::getUserRoles($Person) + 'LName' => [$Person->LastName] + // removed until thoroughly tested + //'Roles' => static::getUserRoles($Person) ]; } } From 650e59a56b3ef26cf44850075e634183291dedb1 Mon Sep 17 00:00:00 2001 From: Nafis Bey Date: Thu, 3 Sep 2020 00:59:09 +0000 Subject: [PATCH 3/7] feat: add new API endpoints - createUserEmailCredentials - updateUserEmailCredentials - fix: updateUser --- php-classes/Slate/Connectors/Looker/API.php | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/php-classes/Slate/Connectors/Looker/API.php b/php-classes/Slate/Connectors/Looker/API.php index 4727ee6..8459d22 100644 --- a/php-classes/Slate/Connectors/Looker/API.php +++ b/php-classes/Slate/Connectors/Looker/API.php @@ -84,9 +84,19 @@ public static function createUser(array $data) public static function updateUser($userId, $data = []) { - \MICS::dump($data, $userId); - return static::post('users/'.$userId, $data); + return static::patch('users/'.$userId, $data); } + //https://building21.looker.com:19999/api-docs/index.html#!/3.1/User/create_user_credentials_email + public static function createUserEmailCredentials($userId, $data = []) + { + return static::post('users/'.$userId.'/credentials_email', $data); + } + //https://building21.looker.com:19999/api-docs/index.html#!/3.1/User/update_user_credentials_email + public static function updateUserEmailCredentials($userId, $data = []) + { + return static::patch('users/'.$userId.'/credentials_email', $data); + } + // https://building21.looker.com:19999/api-docs/index.html#!/3.1/User/set_user_roles public static function updateUserRoles($userId, $data = []) { @@ -243,4 +253,4 @@ public static function request($path, array $options = []) return $result; } -} \ No newline at end of file +} From eaa3e10baa0cdc17f2c28840ea2e90623c26ffd9 Mon Sep 17 00:00:00 2001 From: Nafis Bey Date: Thu, 3 Sep 2020 01:12:05 +0000 Subject: [PATCH 4/7] fix: sync looker user changes --- php-classes/Slate/Connectors/Looker/Connector.php | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/php-classes/Slate/Connectors/Looker/Connector.php b/php-classes/Slate/Connectors/Looker/Connector.php index f88c411..f3090de 100644 --- a/php-classes/Slate/Connectors/Looker/Connector.php +++ b/php-classes/Slate/Connectors/Looker/Connector.php @@ -141,20 +141,19 @@ public static function pushUser(IPerson $User, LoggerInterface $logger = null, $ // check for any changes $lookerUser = LookerAPI::getUserById($Mapping->ExternalIdentifier, ['fields' => 'id,first_name,last_name,email,group_ids,role_ids']); - $changes = []; $lookerUserChanges = []; - if ($lookerUser['first_name'] != ($User->PreferredName ?: $User->FirstName)) { - $lookerUserChanges['user[first_name]'] = [ + if ($lookerUser['first_name'] != $User->FirstName) { + $lookerUserChanges['first_name'] = [ 'from' => $lookerUser['first_name'], - 'to' => $User->PreferredName ?: $User->Firstname + 'to' => $User->FirstName ]; } if ($lookerUser['last_name'] != $User->LastName) { - $lookerUserChanges['user[last_name]'] = [ + $lookerUserChanges['last_name'] = [ 'from' => $lookerUser['last_name'], - 'to' => $User->Lastname + 'to' => $User->LastName ]; } @@ -227,7 +226,7 @@ public static function pushUser(IPerson $User, LoggerInterface $logger = null, $ } return new SyncResult( - (!empty($changes) || !empty($userUpdated)) ? SyncResult::STATUS_UPDATED : SyncResult::STATUS_VERIFIED, + (!empty($lookerUserChanges) || !empty($userUpdated)) ? SyncResult::STATUS_UPDATED : SyncResult::STATUS_VERIFIED, 'Looker account for {slateEmail} found and verified up-to-date.', [ 'slateEmail' => $User->Email From a5419aa8cf82889c22534f559e30304abe9ce240 Mon Sep 17 00:00:00 2001 From: Nafis Bey Date: Thu, 3 Sep 2020 01:17:04 +0000 Subject: [PATCH 5/7] fix: create email credentials - create new email credentials separately when creating looker account - use looker response for syncing groups/roles --- .../Slate/Connectors/Looker/Connector.php | 33 ++++++++++++------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/php-classes/Slate/Connectors/Looker/Connector.php b/php-classes/Slate/Connectors/Looker/Connector.php index f3090de..f474160 100644 --- a/php-classes/Slate/Connectors/Looker/Connector.php +++ b/php-classes/Slate/Connectors/Looker/Connector.php @@ -254,8 +254,7 @@ public static function pushUser(IPerson $User, LoggerInterface $logger = null, $ if (!$pretend) { $lookerResponse = LookerAPI::createUser([ 'first_name' => $User->FirstName, - 'last_name' => $User->LastName, - 'email' => $User->Email + 'last_name' => $User->LastName ]); if (empty($lookerResponse['id'])) { @@ -269,22 +268,34 @@ public static function pushUser(IPerson $User, LoggerInterface $logger = null, $ } $mappingData['ExternalIdentifier'] = $lookerResponse['id']; - Mapping::create($mappingData, true); - } else { - $lookerResponse = []; - } + $Mapping = Mapping::create($mappingData, true); - $logger->notice( - 'Created Looker user for {slateEmail}', + $credentialsResponse = LookerAPI::createUserEmailCredentials($Mapping->ExternalIdentifier, [ + 'email' => $User->Email + ]); + + $logger->notice( + 'Created Looker user credentials for {slateEmail}', [ 'slateEmail' => $User->Email, - 'lookerResponse' => $pretend ? '(pretend-mode)' : $lookerResponse + 'lookerResponse' => $pretend ? '(pretend-mode)' : $credentialsResponse ] ); + } else { + $lookerResponse = []; + } + + $logger->notice( + 'Created Looker user for {slateEmail}', + [ + 'slateEmail' => $User->Email, + 'lookerResponse' => $pretend ? '(pretend-mode)' : $lookerResponse + ] + ); // sync groups try { - $groupSyncResult = static::syncUserGroups($User, [], $logger, $pretend); + $groupSyncResult = static::syncUserGroups($User, $lookerResponse, $logger, $pretend); } catch (SyncException $e) { $logger->error( $e->getInterpolatedMessage(), @@ -293,7 +304,7 @@ public static function pushUser(IPerson $User, LoggerInterface $logger = null, $ } // sync roles try { - static::syncUserRoles($User, [], $logger, $pretend); + static::syncUserRoles($User, $lookerResponse, $logger, $pretend); } catch (SyncException $e) { $logger->error( $e->getInterpolatedMessage(), From 4c37533ce8c21955f12d1695ff0175fc05608fe9 Mon Sep 17 00:00:00 2001 From: Nafis Bey Date: Thu, 3 Sep 2020 01:18:45 +0000 Subject: [PATCH 6/7] chore: improve logging --- .../Slate/Connectors/Looker/Connector.php | 31 ++++++++++++++----- 1 file changed, 23 insertions(+), 8 deletions(-) diff --git a/php-classes/Slate/Connectors/Looker/Connector.php b/php-classes/Slate/Connectors/Looker/Connector.php index f474160..8b5f9c9 100644 --- a/php-classes/Slate/Connectors/Looker/Connector.php +++ b/php-classes/Slate/Connectors/Looker/Connector.php @@ -376,6 +376,18 @@ protected static function syncUserRoles(IPerson $User, array $lookerUser, Logger if (!$pretend) { $lookerResponse = LookerAPI::updateUserRoles($lookerUser['id'], $rolesToAdd); + if (empty($lookerResponse) || !is_array($lookerResponse)) { + $logger->error('Unexpected response syncing user roles', [ + 'lookerResponse' => $lookerResponse + ]); + + return new SyncException( + 'Unable to sync user roles.', + [ + 'lookerResponse' => $lookerResponse + ] + ); + } $userRoleIds = []; foreach ($lookerResponse as $userRoleData) { $userRoleIds[] = $userRoleData['id']; @@ -443,15 +455,16 @@ protected static function syncUserGroups(IPerson $User, array $lookerUser, Logge $groupIds = $lookerUser['group_ids'] ?: []; $userGroups = array_values(static::getUserGroups($User)); $logger->debug( - 'Analyzing user groups: [{groupIds}]', + 'Analyzing looker user groups: {groupIds}', [ - 'groupIds' => join(',', $groupIds) + 'groupIds' => empty($groupIds) ? '(none)' : '[' . join(',', $groupIds) .']' ] ); - if ($groupsToAdd = array_diff($userGroups, $groupIds)) { + if (!empty($groupsToAdd = array_diff($userGroups, $groupIds))) { + $logger->debug( - 'Updating {slateEmail} groups to: [{userGroups}]', + 'Updating {slateEmail} Looker groups to: [{userGroups}]', [ 'slateEmail' => $User->Email, 'userGroups' => join(',', array_unique(array_merge($userGroups, $groupIds))) @@ -627,14 +640,14 @@ public static function pushUsers(IJob $Job, $pretend = true) 'created' => 0, 'updated' => 0, 'skipped' => 0, - 'failed' => 0 + 'failed' => 0, + 'verified' => 0 ]; // iterate over Slate users - $slateUsers = []; - $slateOnlyUsers = []; + $UsersToSync = User::getAllByWhere('Username IS NOT NULL AND AccountLevel != "Disabled"'); - foreach (User::getAllByWhere('Username IS NOT NULL AND AccountLevel != "Disabled"') AS $User) { + foreach ($UsersToSync AS $User) { $Job->debug( 'Analyzing Slate user {slateUsername} ({slateEmail})', [ @@ -651,6 +664,8 @@ public static function pushUsers(IJob $Job, $pretend = true) $results['created']++; } elseif ($syncResult->getStatus() === SyncResult::STATUS_UPDATED) { $results['updated']++; + } elseif ($syncResult->getStatus() === SyncResult::STATUS_VERIFIED) { + $results['verified']++; } elseif ($syncResult->getStatus() === SyncResult::STATUS_SKIPPED) { $results['skipped']++; continue; From 67038e19fe61f3d0b965365f281818036ac814d4 Mon Sep 17 00:00:00 2001 From: Chris Alfano Date: Fri, 4 Sep 2020 15:53:32 +0000 Subject: [PATCH 7/7] feat: publish emergence-layer holobranch (and use for site) --- .../branches/emergence-layer/_slate-connector-looker.toml | 7 +++++++ .holo/branches/emergence-site/_slate-connector-looker.toml | 3 ++- 2 files changed, 9 insertions(+), 1 deletion(-) create mode 100644 .holo/branches/emergence-layer/_slate-connector-looker.toml diff --git a/.holo/branches/emergence-layer/_slate-connector-looker.toml b/.holo/branches/emergence-layer/_slate-connector-looker.toml new file mode 100644 index 0000000..317eaf3 --- /dev/null +++ b/.holo/branches/emergence-layer/_slate-connector-looker.toml @@ -0,0 +1,7 @@ +[holomapping] +files = [ + "*/**", + "!.github/", + "!.vscode/" +] +after = "*" \ No newline at end of file diff --git a/.holo/branches/emergence-site/_slate-connector-looker.toml b/.holo/branches/emergence-site/_slate-connector-looker.toml index 3897826..c333052 100644 --- a/.holo/branches/emergence-site/_slate-connector-looker.toml +++ b/.holo/branches/emergence-site/_slate-connector-looker.toml @@ -1,3 +1,4 @@ [holomapping] -files = "*/**" +holosource = "=>emergence-layer" +files = "**" after = "*"