From cd30ba9626579e3151daac97779973a809626bc4 Mon Sep 17 00:00:00 2001 From: Ioannis Igoumenos Date: Mon, 12 Feb 2024 10:41:35 +0200 Subject: [PATCH 1/4] fix related permissions --- .../src/Model/Table/SqlProvisionersTable.php | 64 ++++++++------ .../Component/RegistryAuthComponent.php | 87 +++++++++++++++++-- app/src/Model/Table/CosTable.php | 5 +- .../Model/Table/ExternalIdentitiesTable.php | 30 ++++--- app/src/Model/Table/GroupsTable.php | 15 ++-- app/src/Model/Table/IdentifiersTable.php | 5 +- app/src/Model/Table/JobsTable.php | 5 +- app/src/Model/Table/NamesTable.php | 6 +- app/src/Model/Table/PeopleTable.php | 29 ++++--- .../ExternalIdentities/fields-nav.inc | 2 +- app/templates/ExternalIdentities/fields.inc | 6 +- 11 files changed, 182 insertions(+), 72 deletions(-) diff --git a/app/availableplugins/SqlConnector/src/Model/Table/SqlProvisionersTable.php b/app/availableplugins/SqlConnector/src/Model/Table/SqlProvisionersTable.php index 578e1e14e..fc2122527 100644 --- a/app/availableplugins/SqlConnector/src/Model/Table/SqlProvisionersTable.php +++ b/app/availableplugins/SqlConnector/src/Model/Table/SqlProvisionersTable.php @@ -61,17 +61,19 @@ class SqlProvisionersTable extends Table { 'source' => 'People', 'source_table' => 'people', 'related' => [ - 'AdHocAttributes', - 'Addresses', - 'EmailAddresses', - 'ExternalIdentities', - 'GroupMembers', - 'Identifiers', - 'Names', - 'PersonRoles', - 'Pronouns', - 'TelephoneNumbers', - 'Urls' + 'table' => [ + 'AdHocAttributes', + 'Addresses', + 'EmailAddresses', + 'ExternalIdentities', + 'GroupMembers', + 'Identifiers', + 'Names', + 'PersonRoles', + 'Pronouns', + 'TelephoneNumbers', + 'Urls' + ], ] ], 'Groups' => [ @@ -80,7 +82,9 @@ class SqlProvisionersTable extends Table { 'source' => 'Groups', 'source_table' => 'groups', 'related' => [ - 'GroupMembers' + 'table' => [ + 'GroupMembers' + ], ] ] ]; @@ -114,15 +118,17 @@ class SqlProvisionersTable extends Table { 'source' => 'ExternalIdentities', 'source_table' => 'external_identities', 'related' => [ - 'AdHocAttributes', - 'Addresses', - 'EmailAddresses', - 'ExternalIdentityRoles', - 'Identifiers', - 'Names', - 'Pronouns', - 'TelephoneNumbers', - 'Urls' + 'table' => [ + 'AdHocAttributes', + 'Addresses', + 'EmailAddresses', + 'ExternalIdentityRoles', + 'Identifiers', + 'Names', + 'Pronouns', + 'TelephoneNumbers', + 'Urls' + ], ] ], 'ExternalIdentityRoles' => [ @@ -131,9 +137,11 @@ class SqlProvisionersTable extends Table { 'source' => 'ExternalIdentityRoles', 'source_table' => 'external_identity_roles', 'related' => [ - 'AdHocAttributes', - 'Addresses', - 'TelephoneNumbers' + 'table' => [ + 'AdHocAttributes', + 'Addresses', + 'TelephoneNumbers' + ], ] ], 'GroupMembers' => [ @@ -164,9 +172,11 @@ class SqlProvisionersTable extends Table { 'source' => 'PersonRoles', 'source_table' => 'person_roles', 'related' => [ - 'AdHocAttributes', - 'Addresses', - 'TelephoneNumbers' + 'table' => [ + 'AdHocAttributes', + 'Addresses', + 'TelephoneNumbers' + ], ] ], 'Pronouns' => [ diff --git a/app/src/Controller/Component/RegistryAuthComponent.php b/app/src/Controller/Component/RegistryAuthComponent.php index 5f7df4a13..a5b076ea2 100644 --- a/app/src/Controller/Component/RegistryAuthComponent.php +++ b/app/src/Controller/Component/RegistryAuthComponent.php @@ -58,6 +58,7 @@ use \Cake\Http\Exception\UnauthorizedException; use \Cake\ORM\ResultSet; use \Cake\ORM\TableRegistry; +use \Cake\Utility\Inflector; use \App\Lib\Enum\AuthenticationEventEnum; use \App\Lib\Enum\SuspendableStatusEnum; use \App\Lib\Enum\TemplateableStatusEnum; @@ -319,6 +320,9 @@ protected function calculatePermissions(?int $id=null): array { // Is this user a CO Member? $coMember = $this->isCoMember($controller->getCOID()); + + // Get the action + $reqAction = $controller->getRequest()->getParam('action'); // Is this record read only? $readOnly = false; @@ -333,8 +337,27 @@ protected function calculatePermissions(?int $id=null): array { $readOnlyActions = ['view']; // Pull the record so we can interrogate it - - $obj = $table->get($id); + + // XXX Get the record along with the contains + // We use findById() rather than get() so we can apply subsequent + // query modifications via traits + $query = $table->findById($id); + + // QueryModificationTrait + $getActionMethod = "get{$reqAction}Contains"; + if(method_exists($table, $getActionMethod)) { + $query = $query->contain($table->$getActionMethod()); + } + + try { + // Pull the current record + $obj = $query->firstOrFail(); + } + catch(\Exception $e) { + // findById throws Cake\Datasource\Exception\RecordNotFoundException + $this->Flash->error($e->getMessage()); + return $this->generateRedirect(null); + } if(method_exists($obj, "isReadOnly")) { $readOnly = $obj->isReadOnly(); @@ -375,9 +398,62 @@ protected function calculatePermissions(?int $id=null): array { $ret[$action] = $ok; } + + if(!empty($permissions['related']['entity'])) { + foreach($permissions['related']['entity'] as $rtable) { + $RelatedTable = TableRegistry::getTableLocator()->get($rtable); + $rpermissions = $this->getTablePermissions($RelatedTable, $id); + $robj = $obj->get(Inflector::singularize(Inflector::underscore($rtable))); + $rreadOnlyActions = ['view']; + + // Is this record read only? + $rreadOnly = false; + + // Can this record be deleted? + $rcanDelete = true; + + if($robj !== null && method_exists($robj, "isReadOnly")) { + $rreadOnly = $robj->isReadOnly(); + + if(!empty($rpermissions['readOnly'])) { + // Merge in controller specific actions permitted on read only entities + $rreadOnlyActions = array_merge($rreadOnlyActions, $rpermissions['readOnly']); + } + } + + if($robj !== null && method_exists($robj, "canDelete")) { + $rcanDelete = $robj->canDelete(); + } + + foreach($rpermissions['entity'] as $action => $roles) { + $ok = false; + + if((($action != 'delete' || $rcanDelete) + && + !$rreadOnly) || in_array($action, $rreadOnlyActions)) { + if(is_array($roles)) { + // A list of roles authorized to perform this action, see if the + // current user has any + foreach($roles as $role) { + // eg: $role = "platformAdmin", which corresponds to the variables set, above + if($$role) { + $ok = true; + break; + } + } + } elseif($roles === true) { + // Any authenticated user is permitted + $ok = true; + } + } + + $ret[$rtable][$action] = $ok; + } + } + } - if(!empty($permissions['related'])) { - foreach($permissions['related'] as $rtable) { + if(!empty($permissions['related']['table'])) { + foreach($permissions['related']['table'] as $rtable) { $RelatedTable = TableRegistry::getTableLocator()->get($rtable); $rpermissions = $this->getTablePermissions($RelatedTable, $id); @@ -403,7 +479,8 @@ protected function calculatePermissions(?int $id=null): array { } } } - } else { + + } else { // No $id // Permissions for actions that operate over tables foreach($permissions['table'] as $action => $roles) { diff --git a/app/src/Model/Table/CosTable.php b/app/src/Model/Table/CosTable.php index 76b31bc57..fa1c4a609 100644 --- a/app/src/Model/Table/CosTable.php +++ b/app/src/Model/Table/CosTable.php @@ -131,7 +131,10 @@ public function initialize(array $config): void { ], // Related models whose permissions we'll need, typically for table views 'related' => [ - 'Dashboards' + 'entity' => [], + 'table' => [ + 'Dashboards' + ] ] ]); } diff --git a/app/src/Model/Table/ExternalIdentitiesTable.php b/app/src/Model/Table/ExternalIdentitiesTable.php index 83fdaa6a2..c07257610 100644 --- a/app/src/Model/Table/ExternalIdentitiesTable.php +++ b/app/src/Model/Table/ExternalIdentitiesTable.php @@ -84,7 +84,7 @@ public function initialize(array $config): void { $this->hasMany('ExternalIdentityRoles') ->setDependent(true) ->setCascadeCallbacks(true); - $this->hasMany('ExtIdentitySourceRecords') + $this->hasOne('ExtIdentitySourceRecords') ->setDependent(true) ->setCascadeCallbacks(true); $this->hasMany('HistoryRecords') @@ -162,18 +162,22 @@ public function initialize(array $config): void { ], // Related models whose permissions we'll need, typically for table views 'related' => [ - 'Names', - 'Addresses', - 'AdHocAttributes', - 'EmailAddresses', - 'ExternalIdentityRoles', - 'ExtIdentitySourceRecords', - 'HistoryRecords', - 'Identifiers', - 'JobHistoryRecords', - 'Pronouns', - 'TelephoneNumbers', - 'Urls' + 'entity' => [ + 'ExtIdentitySourceRecords', + ], + 'table' => [ + 'Names', + 'Addresses', + 'AdHocAttributes', + 'EmailAddresses', + 'ExternalIdentityRoles', + 'HistoryRecords', + 'Identifiers', + 'JobHistoryRecords', + 'Pronouns', + 'TelephoneNumbers', + 'Urls' + ], ] ]); } diff --git a/app/src/Model/Table/GroupsTable.php b/app/src/Model/Table/GroupsTable.php index 4dc712503..03730d4cb 100644 --- a/app/src/Model/Table/GroupsTable.php +++ b/app/src/Model/Table/GroupsTable.php @@ -164,16 +164,19 @@ public function initialize(array $config): void { ], // Related models whose permissions we'll need, typically for table views 'related' => [ + 'entity' => [], + 'table' => [ // XXX As a first pass, this (combined with the implementation in AppController::calculatePermissions) // will render a link to group-members?group_id=X for all groups in the index view // groups?co_id=2. This may or may not be right in the long term, eg for private // groups. Maybe it's OK for now, since all groups are visible to all members of the CO. - 'GroupMembers', - 'GroupNestings', - 'HistoryRecords', - 'IdentifierAssignments', - 'Identifiers', - 'ProvisioningTargets' + 'GroupMembers', + 'GroupNestings', + 'HistoryRecords', + 'IdentifierAssignments', + 'Identifiers', + 'ProvisioningTargets' + ], ] ]); } diff --git a/app/src/Model/Table/IdentifiersTable.php b/app/src/Model/Table/IdentifiersTable.php index 1d5fb5e74..2cbfac717 100644 --- a/app/src/Model/Table/IdentifiersTable.php +++ b/app/src/Model/Table/IdentifiersTable.php @@ -140,7 +140,10 @@ public function initialize(array $config): void { ], // Related models whose permissions we'll need, typically for table views 'related' => [ - 'AuthenticationEvents' + 'entity' => [], + 'table' => [ + 'AuthenticationEvents' + ] ] ]); } diff --git a/app/src/Model/Table/JobsTable.php b/app/src/Model/Table/JobsTable.php index cdf55aec5..e2fdd50d9 100644 --- a/app/src/Model/Table/JobsTable.php +++ b/app/src/Model/Table/JobsTable.php @@ -114,7 +114,10 @@ public function initialize(array $config): void { 'readOnly' => ['cancel'], // Related models whose permissions we'll need, typically for table views 'related' => [ - 'JobHistoryRecords' + 'entity' => [], + 'table' => [ + 'JobHistoryRecords' + ] ] ]); } diff --git a/app/src/Model/Table/NamesTable.php b/app/src/Model/Table/NamesTable.php index 69fcfc3cb..df1c2955e 100644 --- a/app/src/Model/Table/NamesTable.php +++ b/app/src/Model/Table/NamesTable.php @@ -93,7 +93,11 @@ public function initialize(array $config): void { $this->belongsTo('People'); $this->belongsTo('ExternalIdentities'); $this->belongsTo('Types'); - + $this->belongsTo('ExtIdentitySourceRecords') + ->setClassName('ExtIdentitySourceRecords') + ->setForeignKey('source_name_id') + ->setProperty('source_name'); + $this->setDisplayField('full_name'); $this->setPrimaryLink(['external_identity_id', 'person_id']); diff --git a/app/src/Model/Table/PeopleTable.php b/app/src/Model/Table/PeopleTable.php index b4a6527fd..30d5dad27 100644 --- a/app/src/Model/Table/PeopleTable.php +++ b/app/src/Model/Table/PeopleTable.php @@ -207,18 +207,21 @@ public function initialize(array $config): void { ], // Related models whose permissions we'll need, typically for table views 'related' => [ - 'Addresses', - 'AdHocAttributes', - 'Names', - 'EmailAddresses', - 'ExternalIdentities', - 'HistoryRecords', - 'IdentifierAssignments', - 'Identifiers', - 'PersonRoles', - 'ProvisioningTargets', - 'TelephoneNumbers', - 'Urls' + 'entity' => [], + 'table' => [ + 'Addresses', + 'AdHocAttributes', + 'Names', + 'EmailAddresses', + 'ExternalIdentities', + 'HistoryRecords', + 'IdentifierAssignments', + 'Identifiers', + 'PersonRoles', + 'ProvisioningTargets', + 'TelephoneNumbers', + 'Urls' + ], ] ]); } @@ -660,7 +663,7 @@ public function validationDefault(Validator $validator): Validator { 'content' => ['rule' => 'isInteger'] ]); $validator->notEmptyString('co_id'); - + $validator->add('status', [ 'content' => ['rule' => ['inList', StatusEnum::getConstValues()]] ]); diff --git a/app/templates/ExternalIdentities/fields-nav.inc b/app/templates/ExternalIdentities/fields-nav.inc index 9624c9c13..762e37b44 100644 --- a/app/templates/ExternalIdentities/fields-nav.inc +++ b/app/templates/ExternalIdentities/fields-nav.inc @@ -62,7 +62,7 @@ $topLinks = [ 'link' => [ 'controller' => 'ext_identity_source_records', 'action' => 'view', - $vv_obj->ext_identity_source_records[0]->id + $vv_obj->ext_identity_source_record->id ] ] ]; diff --git a/app/templates/ExternalIdentities/fields.inc b/app/templates/ExternalIdentities/fields.inc index cb7f442c7..65d4411d9 100644 --- a/app/templates/ExternalIdentities/fields.inc +++ b/app/templates/ExternalIdentities/fields.inc @@ -27,18 +27,18 @@ // This view will not support add/edit if($vv_action == 'add' || $vv_action == 'edit' || $vv_action == 'view') { - if(!empty($vv_obj->ext_identity_source_records[0])) { + if(!empty($vv_obj->ext_identity_source_record)) { $noticeText = __d( 'information', 'ExternalIdentities.source', [ $this->Html->link( - $vv_obj->ext_identity_source_records[0]->external_identity_source->description, + $vv_obj->ext_identity_source_record->external_identity_source->description, [ 'controller' => 'external-identity-sources', 'action' => 'edit', - $vv_obj->ext_identity_source_records[0]->external_identity_source->id + $vv_obj->ext_identity_source_record->external_identity_source->id ] ) ] From c3284250170440e4f9f3edc2a6e1181ef59cbfb4 Mon Sep 17 00:00:00 2001 From: Arlen Johnson Date: Mon, 12 Feb 2024 11:22:21 -0500 Subject: [PATCH 2/4] First step in moving the cached EIS Record to the Person/External Identity subnavigation (CFM-363) (#155) --- .../ExtIdentitySourceRecords/fields-nav.inc | 22 +++++++++---------- app/templates/element/subnavigation.php | 5 ++++- 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/app/templates/ExtIdentitySourceRecords/fields-nav.inc b/app/templates/ExtIdentitySourceRecords/fields-nav.inc index 952251c10..8892e8b7f 100644 --- a/app/templates/ExtIdentitySourceRecords/fields-nav.inc +++ b/app/templates/ExtIdentitySourceRecords/fields-nav.inc @@ -50,15 +50,15 @@ if(!empty($vv_obj->external_identity_id)) { ] ] ]; -} - -$subnav = [ - 'name' => 'plugin', - 'active' => 'search' -]; - -if(!empty($vv_obj->external_identity_source_id)) { - $subnav['tabsId'] = $vv_obj->external_identity_source_id; - $subnav['tabsController'] = 'external_identity_sources'; - $subnav['tabsSupertitle'] = $vv_obj->external_identity_source->description; + + $subnav = [ + 'name' => 'person', + 'active' => 'external_identities', + 'subActive' => 'external_identity_roles', + 'tabsId' => $vv_obj->person_id, + 'tabsController' => 'people', + 'subTabsId' => $vv_obj->external_identity_id, + 'subTabsController' => 'external_identities', + 'tabsSupertitle' => $vv_obj->external_identity->description + ]; } \ No newline at end of file diff --git a/app/templates/element/subnavigation.php b/app/templates/element/subnavigation.php index 0a6f50907..5aa1f15b1 100644 --- a/app/templates/element/subnavigation.php +++ b/app/templates/element/subnavigation.php @@ -194,6 +194,7 @@ $isExternalId = ( $vv_primary_link == 'external_identity_id' || $vv_primary_link == 'external_identity_role_id' + || $vv_primary_link == 'external_identity_source_id' || ($curController == 'ExternalIdentities' && ($curAction == 'edit' || $curAction == 'view')) || ($curController == 'ExternalIdentityRoles' && ($curAction == 'edit' || $curAction == 'view')) ) ? true : false; @@ -382,7 +383,9 @@ request->getQuery($vv_primary_link); - if(!empty($vv_ei_id)) { + if(!empty($subTabsId)) { + $curId = $subTabsId; + } elseif(!empty($vv_ei_id)) { $curId = $vv_ei_id; $linkFilter = ['external_identity_id' => $curId]; } elseif(!empty($vv_obj)) { From 86bce2bea2f208ec7a8cae32b767acce000dcf6f Mon Sep 17 00:00:00 2001 From: Ioannis Igoumenos Date: Mon, 12 Feb 2024 19:21:58 +0200 Subject: [PATCH 3/4] fetch missing person data --- app/src/Model/Table/ExtIdentitySourceRecordsTable.php | 3 ++- app/templates/ExtIdentitySourceRecords/fields-nav.inc | 7 ++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/app/src/Model/Table/ExtIdentitySourceRecordsTable.php b/app/src/Model/Table/ExtIdentitySourceRecordsTable.php index fec143baf..08a9afc56 100644 --- a/app/src/Model/Table/ExtIdentitySourceRecordsTable.php +++ b/app/src/Model/Table/ExtIdentitySourceRecordsTable.php @@ -73,7 +73,8 @@ public function initialize(array $config): void { $this->setRequiresCO(true); $this->setViewContains([ - 'ExternalIdentitySources' + 'ExternalIdentitySources', + 'ExternalIdentities' => ['People' => ['PrimaryName']], ]); $this->setPermissions([ diff --git a/app/templates/ExtIdentitySourceRecords/fields-nav.inc b/app/templates/ExtIdentitySourceRecords/fields-nav.inc index 8892e8b7f..07c5b2b55 100644 --- a/app/templates/ExtIdentitySourceRecords/fields-nav.inc +++ b/app/templates/ExtIdentitySourceRecords/fields-nav.inc @@ -50,14 +50,15 @@ if(!empty($vv_obj->external_identity_id)) { ] ] ]; - + + // primary name: $vv_obj->external_identity->person->primary_name->full_name $subnav = [ 'name' => 'person', 'active' => 'external_identities', 'subActive' => 'external_identity_roles', - 'tabsId' => $vv_obj->person_id, + 'tabsId' => $vv_obj->external_identity->person->id, 'tabsController' => 'people', - 'subTabsId' => $vv_obj->external_identity_id, + 'subTabsId' => $vv_obj->external_identity->id, 'subTabsController' => 'external_identities', 'tabsSupertitle' => $vv_obj->external_identity->description ]; From 29d4855b70a1e6aa057cde77a31743709ba0b4d9 Mon Sep 17 00:00:00 2001 From: Ioannis Igoumenos Date: Thu, 22 Feb 2024 16:27:02 +0200 Subject: [PATCH 4/4] Remove empty entity arrays --- app/src/Model/Table/CosTable.php | 1 - app/src/Model/Table/GroupsTable.php | 1 - app/src/Model/Table/IdentifiersTable.php | 1 - app/src/Model/Table/JobsTable.php | 1 - app/src/Model/Table/PeopleTable.php | 1 - 5 files changed, 5 deletions(-) diff --git a/app/src/Model/Table/CosTable.php b/app/src/Model/Table/CosTable.php index fa1c4a609..c126c03b2 100644 --- a/app/src/Model/Table/CosTable.php +++ b/app/src/Model/Table/CosTable.php @@ -131,7 +131,6 @@ public function initialize(array $config): void { ], // Related models whose permissions we'll need, typically for table views 'related' => [ - 'entity' => [], 'table' => [ 'Dashboards' ] diff --git a/app/src/Model/Table/GroupsTable.php b/app/src/Model/Table/GroupsTable.php index 03730d4cb..f3bab5ccb 100644 --- a/app/src/Model/Table/GroupsTable.php +++ b/app/src/Model/Table/GroupsTable.php @@ -164,7 +164,6 @@ public function initialize(array $config): void { ], // Related models whose permissions we'll need, typically for table views 'related' => [ - 'entity' => [], 'table' => [ // XXX As a first pass, this (combined with the implementation in AppController::calculatePermissions) // will render a link to group-members?group_id=X for all groups in the index view diff --git a/app/src/Model/Table/IdentifiersTable.php b/app/src/Model/Table/IdentifiersTable.php index 2cbfac717..a0578eff7 100644 --- a/app/src/Model/Table/IdentifiersTable.php +++ b/app/src/Model/Table/IdentifiersTable.php @@ -140,7 +140,6 @@ public function initialize(array $config): void { ], // Related models whose permissions we'll need, typically for table views 'related' => [ - 'entity' => [], 'table' => [ 'AuthenticationEvents' ] diff --git a/app/src/Model/Table/JobsTable.php b/app/src/Model/Table/JobsTable.php index e2fdd50d9..6b1cce01b 100644 --- a/app/src/Model/Table/JobsTable.php +++ b/app/src/Model/Table/JobsTable.php @@ -114,7 +114,6 @@ public function initialize(array $config): void { 'readOnly' => ['cancel'], // Related models whose permissions we'll need, typically for table views 'related' => [ - 'entity' => [], 'table' => [ 'JobHistoryRecords' ] diff --git a/app/src/Model/Table/PeopleTable.php b/app/src/Model/Table/PeopleTable.php index 30d5dad27..4883768d2 100644 --- a/app/src/Model/Table/PeopleTable.php +++ b/app/src/Model/Table/PeopleTable.php @@ -207,7 +207,6 @@ public function initialize(array $config): void { ], // Related models whose permissions we'll need, typically for table views 'related' => [ - 'entity' => [], 'table' => [ 'Addresses', 'AdHocAttributes',