diff --git a/app/plugins/Transmogrify/config/schema/tables.json b/app/plugins/Transmogrify/config/schema/tables.json
index 430c6c39..36200d00 100644
--- a/app/plugins/Transmogrify/config/schema/tables.json
+++ b/app/plugins/Transmogrify/config/schema/tables.json
@@ -356,6 +356,17 @@
},
"addChangelog": true
},
+ "authenticators": {
+ "source": "cm_authenticators",
+ "displayField": "description",
+ "cache": ["co_id"],
+ "fieldMap": {
+ "plugin": "&mapAuthenticatorPlugin",
+ "co_message_template_id": "message_template_id"
+ },
+ "addChangelog": true
+ },
+
"__NOTES__": "DATA MIGRATIONS",
"authentication_events": {
"source": "cm_authentication_events",
@@ -371,6 +382,18 @@
"status": "&mapPersonStatus"
}
},
+ "authenticator_statuses": {
+ "source": "cm_authenticator_statuses",
+ "displayField": "id",
+ "booleans": [
+ "locked"
+ ],
+ "cache": ["person_id", "authenticator_id"],
+ "fieldMap": {
+ "co_person_id": "person_id"
+ },
+ "addChangelog": true
+ },
"person_roles": {
"source": "cm_co_person_roles",
"sqlSelect": "roleSqlSelect",
diff --git a/app/plugins/Transmogrify/src/Lib/Traits/TypeMapperTrait.php b/app/plugins/Transmogrify/src/Lib/Traits/TypeMapperTrait.php
index 912917af..0688267f 100644
--- a/app/plugins/Transmogrify/src/Lib/Traits/TypeMapperTrait.php
+++ b/app/plugins/Transmogrify/src/Lib/Traits/TypeMapperTrait.php
@@ -256,25 +256,30 @@ protected function mapApiIdFromCache(array $row): ?int {
/**
- * Map v4 External Identity Source plugin name to v5 plugin model path.
+ * Generic mapper for v4 plugin names to v5 plugin model paths.
*
- * Generic logic:
- * - If a plugin exists with the same name as the v4 Source (ends with "Source"),
- * return "Plugin.PluralTable" (eg: EnvSource -> EnvSource.EnvSources).
- * - Else, if a "Connector" plugin exists for a "BaseSource", return
- * "BaseConnector.PluralTable" (eg: FileSource -> FileConnector.FileSources).
+ * Logic:
+ * - Only process plugin names that end with the given $suffix (eg, "Source" or "Authenticator")
+ * - If a plugin exists with the same name, return "Plugin.PluralTable"
+ * - Else, if a "Connector" plugin exists, return "BaseConnector.PluralTable"
*
- * @param array $row A row from cm_org_identity_sources containing 'plugin'
- * @return string|null The v5 Plugin.Table string or null if unmapped/empty
- * @since COmanage Registry v5.2.0
+ * @param array $row Row data containing 'plugin'
+ * @param string $suffix Required suffix for the plugin name (eg, "Source", "Authenticator")
+ * @param string $context Human‑readable context for exception messages
+ * @return string|null
*/
- protected function mapExternalIdentitySourcePlugin(array $row): ?string
+ protected function mapPlugin(array $row, string $suffix, string $context): ?string
{
$v4 = $row['plugin'] ?? null;
if (!$v4) {
return null;
}
+ // Only process names that end with the expected suffix
+ if (!str_ends_with($v4, $suffix)) {
+ return null;
+ }
+
$pluginsTable = TableRegistry::getTableLocator()->get('Plugins');
$pluginRows = $pluginsTable
@@ -287,37 +292,61 @@ protected function mapExternalIdentitySourcePlugin(array $row): ?string
$available = array_map(static fn(array $r) => (string)$r['plugin'], $pluginRows->toList());
- // Only process names that end with "Source"
- if (!str_ends_with($v4, 'Source')) {
- return null;
- }
-
// Pluralized Table name for the model side (eg, EnvSource -> EnvSources)
$pluralTable = Inflector::pluralize($v4);
- // 1) Direct plugin exists with same name as the Source
- // Example: EnvSource -> EnvSource.EnvSources
+ // 1) Direct plugin exists with the same name
if (in_array($v4, $available, true)) {
return $v4 . '.' . $pluralTable;
}
- // 2) If it's a "Source", check for "Connector"
- // Example: FileSource -> FileConnector.FileSources
- $base = substr($v4, 0, -strlen('Source'));
+ // 2) Check for "Connector"
+ $base = substr($v4, 0, -strlen($suffix));
$connector = $base . 'Connector';
- if (in_array($connector, $available, true)) {
+ if ($base !== '' && in_array($connector, $available, true)) {
return $connector . '.' . $pluralTable;
}
// No mapping found
-// return null;
- throw new \InvalidArgumentException("Unable to map External Identity Source plugin: $v4. Plugin not implemented in COmanage Registry v5.");
+ throw new \InvalidArgumentException(
+ "Unable to map {$context} plugin: {$v4}. Plugin not implemented in COmanage Registry v5."
+ );
}
+ /**
+ * Map v4 External Identity Source plugin name to v5 plugin model path.
+ *
+ * Examples:
+ * EnvSource -> EnvSource.EnvSources
+ * FileSource -> FileConnector.FileSources
+ *
+ * @param array $row A row from cm_org_identity_sources containing 'plugin'
+ * @return string|null
+ */
+ protected function mapExternalIdentitySourcePlugin(array $row): ?string
+ {
+ return $this->mapPlugin($row, 'Source', 'External Identity Source');
+ }
- /**
+
+ /**
+ * Map v4 Authenticator plugin name to v5 plugin model path.
+ *
+ * Examples:
+ * SshKeyAuthenticator -> SshKeyAuthenticator.SshKeyAuthenticators
+ *
+ * @param array $row Row data containing 'plugin'
+ * @return string|null
+ */
+ protected function mapAuthenticatorPlugin(array $row): ?string
+ {
+ return $this->mapPlugin($row, 'Authenticator', 'Authenticator');
+ }
+
+
+ /**
* Map email type to corresponding type ID
*
* @param array $row Row data containing email type