Skip to content

Commit

Permalink
Move group ownerships to Owners Groups (CFM-302)
Browse files Browse the repository at this point in the history
  • Loading branch information
Benn Oshrin committed Aug 14, 2023
1 parent afd5ab2 commit d7ea787
Show file tree
Hide file tree
Showing 24 changed files with 534 additions and 525 deletions.
19 changes: 4 additions & 15 deletions app/config/schema/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -270,14 +270,16 @@
"open": { "type": "boolean" },
"status": {},
"group_type": { "type": "string", "size": 2 },
"nesting_mode_all": { "type": "boolean" }
"nesting_mode_all": { "type": "boolean" },
"owners_group_id": { "type": "integer", "foreignkey": { "table": "groups", "column": "id" } }
},
"indexes": {
"groups_i1": { "columns": [ "co_id" ] },
"groups_i2": { "columns": [ "co_id", "name" ] },
"groups_i3": { "columns": [ "co_id", "group_type" ] },
"groups_i4": { "columns": [ "cou_id", "group_type" ] },
"groups_i5": { "needed": false, "columns": [ "cou_id" ]}
"groups_i5": { "needed": false, "columns": [ "cou_id" ]},
"groups_i6": { "needed": false, "columns": [ "owners_group_id" ]}
}
},

Expand Down Expand Up @@ -311,19 +313,6 @@
}
},

"group_owners": {
"columns": {
"id": {},
"group_id": {},
"person_id": {}
},
"indexes": {
"group_owners_i1": { "columns": [ "group_id" ]},
"group_owners_i2": { "columns": [ "person_id" ]},
"group_owners_i3": { "columns": [ "group_id", "person_id" ]}
}
},

"names": {
"columns": {
"id": {},
Expand Down
3 changes: 3 additions & 0 deletions app/resources/locales/en_US/enumeration.po
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,9 @@ msgstr "Admins"
msgid "GroupTypeEnum.M"
msgstr "All Members"

msgid "GroupTypeEnum.O"
msgstr "Owners"

msgid "GroupTypeEnum.S"
msgstr "Standard"

Expand Down
9 changes: 9 additions & 0 deletions app/resources/locales/en_US/error.po
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,9 @@ msgstr "Please recheck these fields: {0}"
msgid "fields.primary_link"
msgstr "The Primary Link {0} is frozen and cannot be changed"

msgid "fields.read_only"
msgstr "The field {0} is not modifiable"

msgid "file"
msgstr "Cannot read file {0}"

Expand All @@ -118,9 +121,15 @@ msgstr "Target group is already nested into this Group"
msgid "GroupNestings.same"
msgstr "Group cannot be nested into itself"

msgid "Groups.name.prefix"
msgstr "Standard Groups may not have names starting with \"CO:\""

msgid "Groups.nested"
msgstr "Group is nested or has nestings, and cannot be suspended or deleted"

msgid "Groups.owners.update"
msgstr "Update called on Owners Group"

msgid "IdentifierAssignments.exists"
msgstr "The identifier \"{0}\" is already in use"

Expand Down
9 changes: 9 additions & 0 deletions app/resources/locales/en_US/field.po
Original file line number Diff line number Diff line change
Expand Up @@ -354,6 +354,9 @@ msgstr "{0} Members"
msgid "Groups.desc.members.active"
msgstr "{0} Active Members"

msgid "Groups.group_type"
msgstr "Group Type"

msgid "Groups.nesting_mode_all"
msgstr "Require All for Nested Memberships"

Expand All @@ -366,6 +369,12 @@ msgstr "Open"
msgid "Groups.open.desc"
msgstr "Open groups may be self-joined by any Person in the CO"

msgid "Groups.owners.desc.affix"
msgstr "{0} Owners"

msgid "Groups.owners_group_id"
msgstr "Owners Group"

msgid "IdentifierAssignments.email_address_type_id"
msgstr "Email Address Type"

Expand Down
21 changes: 12 additions & 9 deletions app/src/Command/SetupCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -167,16 +167,19 @@ public function execute(Arguments $args, ConsoleIo $io)
],
['validate' => false])];

$person->group_members = [$coTable->People->GroupMembers->newEntity([
'group_id' => $coTable->Groups->getAdminGroupId(coId: $co_id)
],
['validate' => false])];
$g = $$coTable->Groups->find('adminGroup', ['co_id' => $co_id])->firstOrFail();

$person->group_members = [
$coTable->People->GroupMembers->newEntity(
['group_id' => $g->id],
['validate' => false]
),
$coTable->People->GroupMembers->newEntity(
['group_id' => $g->owners_group_id],
['validate' => false]
)
];

$person->group_owners = [$coTable->People->GroupOwners->newEntity([
'group_id' => $coTable->Groups->getAdminGroupId(coId: $co_id)
],
['validate' => false])];

$coTable->People->save($person);

// Write the salt file if not set in environment and file does not exist.
Expand Down
125 changes: 96 additions & 29 deletions app/src/Command/TransmogrifyCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
use Cake\I18n\FrozenTime;
use Cake\ORM\TableRegistry;
use Cake\Utility\Inflector;
use \App\Lib\Util\PaginatedSqlIterator;

use Doctrine\DBAL\DriverManager;
use Doctrine\DBAL\Exception\ForeignKeyConstraintViolationException;
Expand Down Expand Up @@ -77,7 +78,7 @@ class TransmogrifyCommand extends Command {
'fieldMap' => [
'global_search_limit' => 'search_global_limit',
'required_fields_addr' => 'required_fields_address',
'telephone_number_permitted_fields' => '&populate_co_settings_phone',
'permitted_fields_telephone_number' => '&populate_co_settings_phone',
// XXX CFM-80 these fields are not yet migrated
// be sure to add appropriate fields to 'booleans'
'enable_nsf_demo' => null, // CFM-123
Expand All @@ -98,7 +99,9 @@ class TransmogrifyCommand extends Command {
'co_theme_id' => null,
'person_picker_email_type' => null,
'person_picker_identifier_type' => null,
'person_picker_display_types' => null
'person_picker_display_types' => null,
// No longer supported in PE, see CFM-316
'group_create_admin_only' => null
]
],
'authentication_events' => [
Expand Down Expand Up @@ -171,14 +174,17 @@ class TransmogrifyCommand extends Command {
'groups' => [
'source' => 'cm_co_groups',
'displayField' => 'name',
'cache' => [ 'co_id' ],
'cache' => [ 'co_id', 'owners_group_id' ],
'booleans' => [ 'nesting_mode_all', 'open' ],
'fieldMap' => [
// auto is implied by group_type
'auto' => null,
// Rename the changelog key
'co_group_id' => 'group_id'
]
'co_group_id' => 'group_id',
// Make sure group_type is populated if not already set
'group_type' => '?S'
],
'postTable' => 'createOwnersGroups'
],
'group_nestings' => [
'source' => 'cm_co_group_nestings',
Expand Down Expand Up @@ -206,7 +212,7 @@ class TransmogrifyCommand extends Command {
// Temporary until implemented
'source_org_identity_id' => null
],
'preRow' => 'check_group_memberships'
'preRow' => 'check_group_membership'
],
'names' => [
'source' => 'cm_names',
Expand Down Expand Up @@ -326,7 +332,10 @@ class TransmogrifyCommand extends Command {
'complete_time' => 'finish_time',
'job_type_fk' => null,
'job_params' => 'parameters',
'requeued_from_co_job_id' => 'requeued_from_job_id'
'requeued_from_co_job_id' => 'requeued_from_job_id',
// XXX CFM-246 not yet supported
'max_retry' => null,
'max_retry_count' => null
],
'preRow' => 'filterJobs'
],
Expand Down Expand Up @@ -413,37 +422,88 @@ protected function cacheResults(string $table, array $row) {
}

/**
* Check if a group membership is actually asserted.
* Check if a group membership is actually asserted, and reassign ownerships.
*
* @since COmanage Registry v5.0.0
* @param array $origRow Row of table data (original data)
* @param array $row Row of table data (post fixes)
* @throws InvalidArgumentException
*/

protected function check_group_memberships(array $origRow, array $row) {
if($row['owner'] && !$row['deleted'] && !$row['co_group_member_id']) {
// Insert a GroupOwner row for this record. Note we ignore valid from and
// through for this. We also ignore non-current changelog records.

$ownerRow = [
'group_id' => $origRow['co_group_id'],
'person_id' => $origRow['co_person_id'],
'created' => $origRow['created'],
'modified' => $origRow['modified'],
'group_owner_id' => null,
'revision' => 0,
'deleted' => 'f',
'actor_identifier' => $origRow['actor_identifier']
];

$this->outconn->insert('group_owners', $ownerRow);
protected function check_group_membership(array $origRow, array $row) {
// We need to handle the various member+owner scenarios, but basically
// (1) If 'owner' is set, manually create a Group Membership in the appropriate
// Owners Group (we need to be called via preRow to do this)
// (2) If 'member' is NOT set, throw an exception so we don't create
// in invalid membership
// (3) Otherwise just return so the Membership gets created

if($origRow['owner'] && !$origRow['deleted'] && !$origRow['co_group_member_id']) {
// Create a membership in the appropriate owners group, but not
// on changelog entries

if(!empty($this->cache['groups']['id'][ $origRow['co_group_id'] ]['owners_group_id'])) {
$ownerRow = [
'group_id' => $this->cache['groups']['id'][ $origRow['co_group_id'] ]['owners_group_id'],
'person_id' => $origRow['co_person_id'],
'created' => $origRow['created'],
'modified' => $origRow['modified'],
'group_member_id' => null,
'revision' => 0,
'deleted' => 'f',
'actor_identifier' => $origRow['actor_identifier']
];

$this->outconn->insert('group_members', $ownerRow);
} else {
$this->io->error("Could not find owners group for CoGroupMember " . $origRow['id']);
}
}
if(!$row['member']) {

if(!$row['member'] && !$row['owner']) {
throw new \InvalidArgumentException('member not set on GroupMember');
}
}

/**
* Create an Owners Group for an existing Group.
*
* @since COmanage Registry v5.0.0
* @param array $origRow Row of table data (original data)
* @param array $row Row of table data (post fixes)
*/

protected function createOwnersGroups() {
// Pull all Groups and create Owners Group for them. Deployments generally
// don't have so many Groups that we need PaginatedSqlIterator, but we'll
// use it here anyway just in case.

// By doing this once for the table we avoid having to sort through
// changelog metadata to figure out which rows to actually create owners
// groups for.

$Groups = TableRegistry::getTableLocator()->get('Groups');

$iterator = new PaginatedSqlIterator($Groups, []);

foreach($iterator as $k => $group) {
try {
// Because PaginatedSqlIterator will pick up new Groups as we create them,
// we need to check for any Owners groups (that we just created) and skip them.
if(!$group->isOwners()) {
$ownersGid = $Groups->createOwnersGroup($group);

// We need to manually populate the cache
$this->cache['groups']['id'][$group->id]['owners_group_id'] = $ownersGid;
}
}
catch(\Exception $e) {
$this->io->error("Failed to create owners group for "
. $group->name . " (" . $group->id . "): "
. $e->getMessage());
}
}
}

/**
* Execute the Transmogrify Command.
Expand Down Expand Up @@ -888,6 +948,13 @@ protected function mapFields(string $table, array &$row) {
if(!$row[$oldname]) {
throw new \InvalidArgumentException("Could not find value for $table $oldname");
}
} elseif($newname[0] == '?') {
// This is a default value to populate if the current value is null
$v = substr($newname, 1);

if($row[$oldname] === null) {
$row[$oldname] = $v;
}
} else {
// Copy the value to the new name, then unset the old name
$row[$newname] = $row[$oldname];
Expand Down Expand Up @@ -917,7 +984,7 @@ protected function map_address_type(array $row) {
*/

protected function map_affiliation_type(array $row) {
return $this->map_type($row, 'PersonRoles.affiliation_type', $this->findCoId($row), 'affiliation_type');
return $this->map_type($row, 'PersonRoles.affiliation_type', $this->findCoId($row), 'affiliation');
}

/**
Expand Down Expand Up @@ -1134,7 +1201,7 @@ protected function map_telephone_type(array $row) {
* Map a type string to a foreign key.
*
* @since COmanage Registry v5.0.0
* @param array $row Row of table data (ignored)
* @param array $row Row of table data
* @param string $type Type to map (types:attribute)
* @param int $coId CO ID
* @param string $attr Row column to use for type value
Expand Down
Loading

0 comments on commit d7ea787

Please sign in to comment.