diff --git a/src/ZB.MOM.WW.ScadaBridge.CLI/Commands/BundleCommands.cs b/src/ZB.MOM.WW.ScadaBridge.CLI/Commands/BundleCommands.cs index c6c0a11b..efea33c0 100644 --- a/src/ZB.MOM.WW.ScadaBridge.CLI/Commands/BundleCommands.cs +++ b/src/ZB.MOM.WW.ScadaBridge.CLI/Commands/BundleCommands.cs @@ -61,7 +61,9 @@ public static class BundleCommands var dbConnectionsOption = NameListOption("--db-connections", "Comma-separated database-connection names"); var notificationListsOption = NameListOption("--notification-lists", "Comma-separated notification-list names"); var smtpConfigsOption = NameListOption("--smtp-configs", "Comma-separated SMTP host names"); - var apiKeysOption = NameListOption("--api-keys", "Comma-separated API-key names"); + // Inbound API keys are not transported between environments (re-arch C4) — no + // --api-keys option. Re-create keys and re-grant their method scopes on the + // destination via the admin UI/CLI. var apiMethodsOption = NameListOption("--api-methods", "Comma-separated API-method names"); var includeDepsOption = new Option("--include-dependencies") { @@ -85,7 +87,6 @@ public static class BundleCommands cmd.Add(dbConnectionsOption); cmd.Add(notificationListsOption); cmd.Add(smtpConfigsOption); - cmd.Add(apiKeysOption); cmd.Add(apiMethodsOption); cmd.Add(includeDepsOption); cmd.Add(sourceEnvOption); @@ -106,7 +107,6 @@ public static class BundleCommands DatabaseConnectionNames: result.GetValue(dbConnectionsOption), NotificationListNames: result.GetValue(notificationListsOption), SmtpConfigurationNames: result.GetValue(smtpConfigsOption), - ApiKeyNames: result.GetValue(apiKeysOption), ApiMethodNames: result.GetValue(apiMethodsOption), IncludeDependencies: includeDeps, Passphrase: passphrase, diff --git a/src/ZB.MOM.WW.ScadaBridge.CentralUI/Components/Pages/Design/TransportExport.razor b/src/ZB.MOM.WW.ScadaBridge.CentralUI/Components/Pages/Design/TransportExport.razor index 905e9fa5..c44554b8 100644 --- a/src/ZB.MOM.WW.ScadaBridge.CentralUI/Components/Pages/Design/TransportExport.razor +++ b/src/ZB.MOM.WW.ScadaBridge.CentralUI/Components/Pages/Design/TransportExport.razor @@ -138,14 +138,15 @@ @RenderCheckboxList(_smtpConfigs, s => s.Id, s => s.Host, _selectedSmtpConfigs) -
- API Keys - @RenderCheckboxList(_apiKeys, k => k.Id, k => k.Name, _selectedApiKeys) -
-
API Methods @RenderCheckboxList(_apiMethods, m => m.Id, m => m.Name, _selectedApiMethods) +
@@ -261,10 +262,7 @@ {
  • SmtpConfig: @s.Host
  • } - @foreach (var k in _resolved.ApiKeys.OrderBy(k => k.Name, StringComparer.OrdinalIgnoreCase)) - { -
  • ApiKey: @k.Name
  • - } + @* Inbound API keys are not transported (re-arch C4) — methods only. *@ @foreach (var m in _resolved.ApiMethods.OrderBy(m => m.Name, StringComparer.OrdinalIgnoreCase)) {
  • ApiMethod: @m.Name
  • diff --git a/src/ZB.MOM.WW.ScadaBridge.CentralUI/Components/Pages/Design/TransportExport.razor.cs b/src/ZB.MOM.WW.ScadaBridge.CentralUI/Components/Pages/Design/TransportExport.razor.cs index 6ee6f245..a0e0b807 100644 --- a/src/ZB.MOM.WW.ScadaBridge.CentralUI/Components/Pages/Design/TransportExport.razor.cs +++ b/src/ZB.MOM.WW.ScadaBridge.CentralUI/Components/Pages/Design/TransportExport.razor.cs @@ -69,7 +69,7 @@ public partial class TransportExport : ComponentBase private List _dbConnections = new(); private List _notificationLists = new(); private List _smtpConfigs = new(); - private List _apiKeys = new(); + // Inbound API keys are not transported between environments (re-arch C4); only methods. private List _apiMethods = new(); // ---- Step 1: selection state ---- @@ -82,7 +82,7 @@ public partial class TransportExport : ComponentBase private readonly HashSet _selectedDbConnections = new(); private readonly HashSet _selectedNotificationLists = new(); private readonly HashSet _selectedSmtpConfigs = new(); - private readonly HashSet _selectedApiKeys = new(); + // No _selectedApiKeys: inbound API keys are not transported (re-arch C4). private readonly HashSet _selectedApiMethods = new(); private string _filter = string.Empty; private bool _includeDependencies = true; @@ -124,7 +124,7 @@ public partial class TransportExport : ComponentBase _dbConnections = (await ExternalRepo.GetAllDatabaseConnectionsAsync()).ToList(); _notificationLists = (await NotificationRepo.GetAllNotificationListsAsync()).ToList(); _smtpConfigs = (await NotificationRepo.GetAllSmtpConfigurationsAsync()).ToList(); - _apiKeys = (await InboundApiRepo.GetAllApiKeysAsync()).ToList(); + // Inbound API keys are not transported (re-arch C4) — only methods are loaded. _apiMethods = (await InboundApiRepo.GetAllApiMethodsAsync()).ToList(); } catch (Exception ex) @@ -169,7 +169,6 @@ public partial class TransportExport : ComponentBase || _selectedDbConnections.Count > 0 || _selectedNotificationLists.Count > 0 || _selectedSmtpConfigs.Count > 0 - || _selectedApiKeys.Count > 0 || _selectedApiMethods.Count > 0; private bool PassphraseValid => @@ -205,7 +204,7 @@ public partial class TransportExport : ComponentBase DatabaseConnectionIds: _selectedDbConnections.ToList(), NotificationListIds: _selectedNotificationLists.ToList(), SmtpConfigurationIds: _selectedSmtpConfigs.ToList(), - ApiKeyIds: _selectedApiKeys.ToList(), + // Inbound API keys are not transported (re-arch C4) — methods only. ApiMethodIds: _selectedApiMethods.ToList(), IncludeDependencies: _includeDependencies); } @@ -393,7 +392,6 @@ public partial class TransportExport : ComponentBase _selectedDbConnections.Clear(); _selectedNotificationLists.Clear(); _selectedSmtpConfigs.Clear(); - _selectedApiKeys.Clear(); _selectedApiMethods.Clear(); _filter = string.Empty; _includeDependencies = true; diff --git a/src/ZB.MOM.WW.ScadaBridge.Commons/Messages/Management/TransportCommands.cs b/src/ZB.MOM.WW.ScadaBridge.Commons/Messages/Management/TransportCommands.cs index 9132dbb7..1c786a19 100644 --- a/src/ZB.MOM.WW.ScadaBridge.Commons/Messages/Management/TransportCommands.cs +++ b/src/ZB.MOM.WW.ScadaBridge.Commons/Messages/Management/TransportCommands.cs @@ -6,6 +6,12 @@ namespace ZB.MOM.WW.ScadaBridge.Commons.Messages.Management; /// Exports a bundle. Names rather than IDs in the selection so test scripts can /// be written without an ID lookup step. All=true overrides the per-type /// name lists and exports every entity of every supported type. +/// +/// Inbound API keys are intentionally not selectable: per the inbound-API-key +/// re-architecture (C4) keys are not transported between environments; only API +/// methods travel. Re-create keys and re-grant their method scopes on the +/// destination via the admin UI/CLI. +/// /// public sealed record ExportBundleCommand( bool All, @@ -15,7 +21,6 @@ public sealed record ExportBundleCommand( IReadOnlyList? DatabaseConnectionNames, IReadOnlyList? NotificationListNames, IReadOnlyList? SmtpConfigurationNames, - IReadOnlyList? ApiKeyNames, IReadOnlyList? ApiMethodNames, bool IncludeDependencies, string? Passphrase, diff --git a/src/ZB.MOM.WW.ScadaBridge.Commons/Types/Transport/BundleSummary.cs b/src/ZB.MOM.WW.ScadaBridge.Commons/Types/Transport/BundleSummary.cs index 0d38b1f8..328a682e 100644 --- a/src/ZB.MOM.WW.ScadaBridge.Commons/Types/Transport/BundleSummary.cs +++ b/src/ZB.MOM.WW.ScadaBridge.Commons/Types/Transport/BundleSummary.cs @@ -1,5 +1,7 @@ namespace ZB.MOM.WW.ScadaBridge.Commons.Types.Transport; +// ApiKeys is intentionally absent: inbound API keys are not transported between +// environments (re-arch C4). Only API methods are summarised. public sealed record BundleSummary( int Templates, int TemplateFolders, @@ -8,5 +10,4 @@ public sealed record BundleSummary( int DbConnections, int NotificationLists, int SmtpConfigs, - int ApiKeys, int ApiMethods); diff --git a/src/ZB.MOM.WW.ScadaBridge.Commons/Types/Transport/ExportSelection.cs b/src/ZB.MOM.WW.ScadaBridge.Commons/Types/Transport/ExportSelection.cs index 3bdf6c97..b0d2d622 100644 --- a/src/ZB.MOM.WW.ScadaBridge.Commons/Types/Transport/ExportSelection.cs +++ b/src/ZB.MOM.WW.ScadaBridge.Commons/Types/Transport/ExportSelection.cs @@ -1,5 +1,10 @@ namespace ZB.MOM.WW.ScadaBridge.Commons.Types.Transport; +// Inbound API keys are intentionally absent from the transport selection: per the +// inbound-API-key re-architecture (commit C4) keys are NOT carried between +// environments. They live in the per-environment SQLite store (per-env pepper + +// secret-shown-once) and are re-created/re-granted via the admin UI/CLI on the +// destination. Only API *methods* travel in a bundle. public sealed record ExportSelection( IReadOnlyList TemplateIds, IReadOnlyList SharedScriptIds, @@ -7,6 +12,5 @@ public sealed record ExportSelection( IReadOnlyList DatabaseConnectionIds, IReadOnlyList NotificationListIds, IReadOnlyList SmtpConfigurationIds, - IReadOnlyList ApiKeyIds, IReadOnlyList ApiMethodIds, bool IncludeDependencies); diff --git a/src/ZB.MOM.WW.ScadaBridge.Commons/Types/Transport/ImportResult.cs b/src/ZB.MOM.WW.ScadaBridge.Commons/Types/Transport/ImportResult.cs index e9502bfc..ec3e38f7 100644 --- a/src/ZB.MOM.WW.ScadaBridge.Commons/Types/Transport/ImportResult.cs +++ b/src/ZB.MOM.WW.ScadaBridge.Commons/Types/Transport/ImportResult.cs @@ -7,4 +7,8 @@ public sealed record ImportResult( int Skipped, int Renamed, IReadOnlyList StaleInstanceIds, - string AuditEventCorrelation); + string AuditEventCorrelation, + // Number of legacy inbound API keys found in the bundle that were ignored + // (re-arch C4 — keys are not transported; re-create them on this environment). + // Defaults to 0 so existing positional construction sites stay source-compatible. + int ApiKeysIgnored = 0); diff --git a/src/ZB.MOM.WW.ScadaBridge.ManagementService/ManagementActor.cs b/src/ZB.MOM.WW.ScadaBridge.ManagementService/ManagementActor.cs index dd1b7e3e..bd2e7251 100644 --- a/src/ZB.MOM.WW.ScadaBridge.ManagementService/ManagementActor.cs +++ b/src/ZB.MOM.WW.ScadaBridge.ManagementService/ManagementActor.cs @@ -1901,7 +1901,7 @@ public class ManagementActor : ReceiveActor var dbConnections = await externalRepo.GetAllDatabaseConnectionsAsync(); var notificationLists = await notifRepo.GetAllNotificationListsAsync(); var smtpConfigs = await notifRepo.GetAllSmtpConfigurationsAsync(); - var apiKeys = await inboundRepo.GetAllApiKeysAsync(); + // Inbound API keys are not transported between environments (re-arch C4); only methods. var apiMethods = await inboundRepo.GetAllApiMethodsAsync(); int[] ResolveIds(IReadOnlyList all, IReadOnlyList? names, @@ -1931,7 +1931,6 @@ public class ManagementActor : ReceiveActor // SmtpConfiguration is keyed by Host (no Name column); the bundle // preview row shows the Host value, so the CLI uses Host too. SmtpConfigurationIds: ResolveIds(smtpConfigs, cmd.SmtpConfigurationNames, s => s.Host, s => s.Id, "SMTP configuration"), - ApiKeyIds: ResolveIds(apiKeys, cmd.ApiKeyNames, k => k.Name, k => k.Id, "API key"), ApiMethodIds: ResolveIds(apiMethods, cmd.ApiMethodNames, m => m.Name, m => m.Id, "API method"), IncludeDependencies: cmd.IncludeDependencies); diff --git a/src/ZB.MOM.WW.ScadaBridge.Transport/Export/BundleExporter.cs b/src/ZB.MOM.WW.ScadaBridge.Transport/Export/BundleExporter.cs index 01efb9ed..7b079cdf 100644 --- a/src/ZB.MOM.WW.ScadaBridge.Transport/Export/BundleExporter.cs +++ b/src/ZB.MOM.WW.ScadaBridge.Transport/Export/BundleExporter.cs @@ -82,7 +82,7 @@ public sealed class BundleExporter : IBundleExporter DatabaseConnections: resolved.DatabaseConnections, NotificationLists: resolved.NotificationLists, SmtpConfigurations: resolved.SmtpConfigs, - ApiKeys: resolved.ApiKeys, + // Inbound API keys are not transported between environments (re-arch C4). ApiMethods: resolved.ApiMethods); var contentDto = _entitySerializer.ToBundleContent(aggregate); @@ -95,7 +95,6 @@ public sealed class BundleExporter : IBundleExporter DbConnections: resolved.DatabaseConnections.Count, NotificationLists: resolved.NotificationLists.Count, SmtpConfigs: resolved.SmtpConfigs.Count, - ApiKeys: resolved.ApiKeys.Count, ApiMethods: resolved.ApiMethods.Count); // 4. Build a TEMPLATE manifest. BundleSerializer.Pack re-stamps both diff --git a/src/ZB.MOM.WW.ScadaBridge.Transport/Export/DependencyResolver.cs b/src/ZB.MOM.WW.ScadaBridge.Transport/Export/DependencyResolver.cs index b6aaf6d4..4f7c3d50 100644 --- a/src/ZB.MOM.WW.ScadaBridge.Transport/Export/DependencyResolver.cs +++ b/src/ZB.MOM.WW.ScadaBridge.Transport/Export/DependencyResolver.cs @@ -98,13 +98,9 @@ public sealed class DependencyResolver if (sm is not null) smtpConfigs[sm.Id] = sm; } - var apiKeys = new Dictionary(); - foreach (var id in selection.ApiKeyIds.Distinct()) - { - var k = await _inboundApi.GetApiKeyByIdAsync(id, ct).ConfigureAwait(false); - if (k is not null) apiKeys[k.Id] = k; - } - + // Inbound API keys are intentionally NOT resolved into the bundle: per the + // inbound-API-key re-architecture (C4) keys are not transported between + // environments. Only API methods travel. var apiMethods = new Dictionary(); foreach (var id in selection.ApiMethodIds.Distinct()) { @@ -148,7 +144,6 @@ public sealed class DependencyResolver dbConnections.Values, notificationLists.Values, smtpConfigs.Values, - apiKeys.Values, apiMethods.Values); return new ResolvedExport( @@ -160,7 +155,6 @@ public sealed class DependencyResolver DatabaseConnections: dbConnections.Values.OrderBy(d => d.Name, StringComparer.Ordinal).ToList(), NotificationLists: notificationLists.Values.OrderBy(n => n.Name, StringComparer.Ordinal).ToList(), SmtpConfigs: smtpConfigs.Values.OrderBy(s => s.Host, StringComparer.Ordinal).ToList(), - ApiKeys: apiKeys.Values.OrderBy(a => a.Name, StringComparer.Ordinal).ToList(), ApiMethods: apiMethods.Values.OrderBy(a => a.Name, StringComparer.Ordinal).ToList(), ContentManifest: manifest); } @@ -368,7 +362,6 @@ public sealed class DependencyResolver IEnumerable dbConnections, IEnumerable notificationLists, IEnumerable smtpConfigs, - IEnumerable apiKeys, IEnumerable apiMethods) { var entries = new List(); @@ -419,10 +412,7 @@ public sealed class DependencyResolver { entries.Add(new ManifestContentEntry("SmtpConfiguration", s.Host, 1, Array.Empty())); } - foreach (var k in apiKeys.OrderBy(x => x.Name, StringComparer.Ordinal)) - { - entries.Add(new ManifestContentEntry("ApiKey", k.Name, 1, Array.Empty())); - } + // Inbound API keys are not transported (re-arch C4) — no ApiKey manifest entries. foreach (var m in apiMethods.OrderBy(x => x.Name, StringComparer.Ordinal)) { entries.Add(new ManifestContentEntry("ApiMethod", m.Name, 1, Array.Empty())); diff --git a/src/ZB.MOM.WW.ScadaBridge.Transport/Export/ResolvedExport.cs b/src/ZB.MOM.WW.ScadaBridge.Transport/Export/ResolvedExport.cs index 4ad5649c..c1ca966d 100644 --- a/src/ZB.MOM.WW.ScadaBridge.Transport/Export/ResolvedExport.cs +++ b/src/ZB.MOM.WW.ScadaBridge.Transport/Export/ResolvedExport.cs @@ -1,5 +1,5 @@ using ZB.MOM.WW.ScadaBridge.Commons.Entities.ExternalSystems; -using ZB.MOM.WW.ScadaBridge.Commons.Entities.InboundApi; +using ZB.MOM.WW.ScadaBridge.Commons.Entities.InboundApi; // ApiMethod using ZB.MOM.WW.ScadaBridge.Commons.Entities.Notifications; using ZB.MOM.WW.ScadaBridge.Commons.Entities.Scripts; using ZB.MOM.WW.ScadaBridge.Commons.Entities.Templates; @@ -23,6 +23,6 @@ public sealed record ResolvedExport( IReadOnlyList DatabaseConnections, IReadOnlyList NotificationLists, IReadOnlyList SmtpConfigs, - IReadOnlyList ApiKeys, + // Inbound API keys are not transported between environments (re-arch C4); only methods. IReadOnlyList ApiMethods, IReadOnlyList ContentManifest); diff --git a/src/ZB.MOM.WW.ScadaBridge.Transport/Import/ArtifactDiff.cs b/src/ZB.MOM.WW.ScadaBridge.Transport/Import/ArtifactDiff.cs index d6da90c5..959ece09 100644 --- a/src/ZB.MOM.WW.ScadaBridge.Transport/Import/ArtifactDiff.cs +++ b/src/ZB.MOM.WW.ScadaBridge.Transport/Import/ArtifactDiff.cs @@ -244,26 +244,8 @@ public sealed class ArtifactDiff return BuildItem("SmtpConfiguration", incoming.Host, changes); } - /// - /// Compares an incoming API key against the existing API key in the database. - /// - /// The incoming API key from the bundle. - /// The existing API key in the database, or null if new. - /// An import preview item describing the conflict type and differences. - public ImportPreviewItem CompareApiKey(ApiKeyDto incoming, ApiKey? existing) - { - ArgumentNullException.ThrowIfNull(incoming); - if (existing is null) return New("ApiKey", incoming.Name); - - var changes = new List(); - AddIfDifferent(changes, "IsEnabled", existing.IsEnabled, incoming.IsEnabled); - // KeyHash is opaque — record only changed/unchanged, not the value. - if (!string.Equals(existing.KeyHash, incoming.KeyHash, StringComparison.Ordinal)) - { - changes.Add(new FieldChange("KeyHash", "", "")); - } - return BuildItem("ApiKey", incoming.Name, changes); - } + // CompareApiKey was removed in re-arch C4: inbound API keys are not transported + // between environments, so the import preview never diffs keys. /// /// Compares an incoming API method against the existing API method in the database. @@ -277,7 +259,7 @@ public sealed class ArtifactDiff if (existing is null) return New("ApiMethod", incoming.Name); var changes = new List(); - AddIfDifferent(changes, "ApprovedApiKeyIds", existing.ApprovedApiKeyIds, incoming.ApprovedApiKeyIds); + // ApprovedApiKeyIds is not transported (re-arch C4) and is excluded from the diff. AddIfDifferent(changes, "ParameterDefinitions", existing.ParameterDefinitions, incoming.ParameterDefinitions); AddIfDifferent(changes, "ReturnDefinition", existing.ReturnDefinition, incoming.ReturnDefinition); AddIfDifferent(changes, "TimeoutSeconds", existing.TimeoutSeconds, incoming.TimeoutSeconds); diff --git a/src/ZB.MOM.WW.ScadaBridge.Transport/Import/BundleImporter.cs b/src/ZB.MOM.WW.ScadaBridge.Transport/Import/BundleImporter.cs index 18347dd0..9a5dab59 100644 --- a/src/ZB.MOM.WW.ScadaBridge.Transport/Import/BundleImporter.cs +++ b/src/ZB.MOM.WW.ScadaBridge.Transport/Import/BundleImporter.cs @@ -408,14 +408,12 @@ public sealed class BundleImporter : IBundleImporter items.Add(_diff.CompareSmtpConfiguration(sm, existing)); } - // ---- ApiKeys (no by-name lookup — scan GetAll) ---- - var allApiKeys = await _inboundApiRepo.GetAllApiKeysAsync(ct).ConfigureAwait(false); - var apiKeyByName = allApiKeys.ToDictionary(k => k.Name, k => k, StringComparer.Ordinal); - foreach (var k in content.ApiKeys) - { - apiKeyByName.TryGetValue(k.Name, out var existing); - items.Add(_diff.CompareApiKey(k, existing)); - } + // ---- ApiKeys ---- + // Inbound API keys are not transported between environments (re-arch C4). + // New bundles never carry a keys section. A pre-C4 bundle may still contain + // one; we do NOT surface those keys as importable preview rows (they would + // offer Add/Overwrite actions for keys that can't be meaningfully re-created + // from a hash). They are counted, ignored, and reported at apply time. // ---- ApiMethods ---- foreach (var m in content.ApiMethods) @@ -617,6 +615,12 @@ public sealed class BundleImporter : IBundleImporter r => r); var summary = new ImportSummary(); + // Inbound API keys are not transported between environments (re-arch C4). + // A pre-C4 bundle may still contain a keys section; we ignore those keys + // entirely (never re-create them) but count them so the result can tell + // the operator to re-issue keys on this environment. + var apiKeysIgnored = content.ApiKeys?.Count ?? 0; + // Set the correlation BEFORE the transaction so any audit writes // triggered during the apply pick up the BundleImportId — AuditService // reads the scoped context at the moment LogAsync is called. @@ -660,7 +664,8 @@ public sealed class BundleImporter : IBundleImporter await ApplyDatabaseConnectionsAsync(content.DatabaseConnections, resolutionMap, user, summary, ct).ConfigureAwait(false); await ApplyNotificationListsAsync(content.NotificationLists, resolutionMap, user, summary, ct).ConfigureAwait(false); await ApplySmtpConfigsAsync(content.SmtpConfigs, resolutionMap, user, summary, ct).ConfigureAwait(false); - await ApplyApiKeysAsync(content.ApiKeys, resolutionMap, user, summary, ct).ConfigureAwait(false); + // Inbound API keys are NOT applied from a bundle (re-arch C4) — any keys + // in a legacy bundle were counted above (apiKeysIgnored) and are skipped. await ApplyApiMethodsAsync(content.ApiMethods, resolutionMap, user, summary, ct).ConfigureAwait(false); // FU-B / #39 + remainder of #37 — second-pass rewire of name-keyed @@ -700,6 +705,9 @@ public sealed class BundleImporter : IBundleImporter summary.Skipped, summary.Renamed, }, + // re-arch C4: legacy inbound API keys present in the bundle that + // were ignored (not transported / not re-created here). + ApiKeysIgnored = apiKeysIgnored, }, cancellationToken: ct).ConfigureAwait(false); @@ -721,7 +729,8 @@ public sealed class BundleImporter : IBundleImporter Skipped: summary.Skipped, Renamed: summary.Renamed, StaleInstanceIds: Array.Empty(), - AuditEventCorrelation: bundleImportId.ToString()); + AuditEventCorrelation: bundleImportId.ToString(), + ApiKeysIgnored: apiKeysIgnored); } catch (Exception ex) { @@ -2015,59 +2024,9 @@ public sealed class BundleImporter : IBundleImporter target.Credentials = dto.Secrets?.Values.TryGetValue("Credentials", out var cred) == true ? cred : null; } - private async Task ApplyApiKeysAsync( - IReadOnlyList dtos, - Dictionary<(string, string), ImportResolution> map, - string user, - ImportSummary summary, - CancellationToken ct) - { - if (dtos.Count == 0) return; - var all = await _inboundApiRepo.GetAllApiKeysAsync(ct).ConfigureAwait(false); - var byName = all.ToDictionary(k => k.Name, k => k, StringComparer.Ordinal); - - foreach (var dto in dtos) - { - var resolution = ResolveOrDefault(map, "ApiKey", dto.Name); - switch (resolution.Action) - { - case ResolutionAction.Skip: - summary.Skipped++; - break; - case ResolutionAction.Rename: - { - var name = resolution.RenameTo ?? dto.Name; - var key = ApiKey.FromHash(name, dto.KeyHash); - key.IsEnabled = dto.IsEnabled; - await _inboundApiRepo.AddApiKeyAsync(key, ct).ConfigureAwait(false); - await _auditService.LogAsync(user, "Create", "ApiKey", "0", name, - new { key.Name, RenamedFrom = dto.Name }, ct).ConfigureAwait(false); - summary.Renamed++; - break; - } - case ResolutionAction.Overwrite when byName.TryGetValue(dto.Name, out var ex): - ex.KeyHash = dto.KeyHash; - ex.IsEnabled = dto.IsEnabled; - await _inboundApiRepo.UpdateApiKeyAsync(ex, ct).ConfigureAwait(false); - await _auditService.LogAsync(user, "Update", "ApiKey", ex.Id.ToString(), ex.Name, - new { ex.Name, ex.IsEnabled }, ct).ConfigureAwait(false); - summary.Overwritten++; - break; - case ResolutionAction.Add: - case ResolutionAction.Overwrite: - default: - { - var key = ApiKey.FromHash(dto.Name, dto.KeyHash); - key.IsEnabled = dto.IsEnabled; - await _inboundApiRepo.AddApiKeyAsync(key, ct).ConfigureAwait(false); - await _auditService.LogAsync(user, "Create", "ApiKey", "0", key.Name, - new { key.Name, key.IsEnabled }, ct).ConfigureAwait(false); - summary.Added++; - break; - } - } - } - } + // ApplyApiKeysAsync was removed in re-arch C4: inbound API keys are not + // transported between environments, so a bundle never re-creates keys. Any keys + // present in a legacy (pre-C4) bundle are counted and ignored in ApplyAsync. private async Task ApplyApiMethodsAsync( IReadOnlyList dtos, @@ -2098,7 +2057,9 @@ public sealed class BundleImporter : IBundleImporter } case ResolutionAction.Overwrite when existing is not null: existing.Script = dto.Script; - existing.ApprovedApiKeyIds = dto.ApprovedApiKeyIds; + // ApprovedApiKeyIds is NOT overwritten from a bundle (re-arch C4): + // method→key scopes are re-granted per environment and any value on + // the target row is preserved across an import. existing.ParameterDefinitions = dto.ParameterDefinitions; existing.ReturnDefinition = dto.ReturnDefinition; existing.TimeoutSeconds = dto.TimeoutSeconds; @@ -2124,9 +2085,10 @@ public sealed class BundleImporter : IBundleImporter private static ApiMethod BuildApiMethod(ApiMethodDto dto, string? overrideName) { + // ApprovedApiKeyIds is intentionally left at its default (null): keys are not + // transported (re-arch C4) and method→key scopes are re-granted per environment. return new ApiMethod(overrideName ?? dto.Name, dto.Script) { - ApprovedApiKeyIds = dto.ApprovedApiKeyIds, ParameterDefinitions = dto.ParameterDefinitions, ReturnDefinition = dto.ReturnDefinition, TimeoutSeconds = dto.TimeoutSeconds, diff --git a/src/ZB.MOM.WW.ScadaBridge.Transport/Serialization/EntityDtos.cs b/src/ZB.MOM.WW.ScadaBridge.Transport/Serialization/EntityDtos.cs index bd252cad..7fe8a253 100644 --- a/src/ZB.MOM.WW.ScadaBridge.Transport/Serialization/EntityDtos.cs +++ b/src/ZB.MOM.WW.ScadaBridge.Transport/Serialization/EntityDtos.cs @@ -13,6 +13,8 @@ namespace ZB.MOM.WW.ScadaBridge.Transport.Serialization; /// ignorant POCO types — what consumes/produces /// on the application side of the bundle boundary. /// +// ApiKeys is intentionally absent: inbound API keys are not transported between +// environments (re-arch C4). The aggregate only carries API methods. public sealed record EntityAggregate( IReadOnlyList TemplateFolders, IReadOnlyList