feat(sms): list Type + SMS-config management commands/handlers (S5)
This commit is contained in:
@@ -1,9 +1,13 @@
|
||||
using ZB.MOM.WW.ScadaBridge.Commons.Types.Enums;
|
||||
|
||||
namespace ZB.MOM.WW.ScadaBridge.Commons.Messages.Management;
|
||||
|
||||
public record ListNotificationListsCommand;
|
||||
public record GetNotificationListCommand(int NotificationListId);
|
||||
public record CreateNotificationListCommand(string Name, IReadOnlyList<string> RecipientEmails);
|
||||
public record UpdateNotificationListCommand(int NotificationListId, string Name, IReadOnlyList<string> RecipientEmails);
|
||||
public record CreateNotificationListCommand(string Name, IReadOnlyList<string> RecipientEmails, NotificationType Type = NotificationType.Email);
|
||||
public record UpdateNotificationListCommand(int NotificationListId, string Name, IReadOnlyList<string> RecipientEmails, NotificationType Type = NotificationType.Email);
|
||||
public record DeleteNotificationListCommand(int NotificationListId);
|
||||
public record ListSmtpConfigsCommand;
|
||||
public record UpdateSmtpConfigCommand(int SmtpConfigId, string Server, int Port, string AuthMode, string FromAddress, string? TlsMode = null, string? Credentials = null);
|
||||
public record ListSmsConfigsCommand;
|
||||
public record UpdateSmsConfigCommand(int SmsConfigId, string AccountSid, string FromNumber, string? MessagingServiceSid = null, string? ApiBaseUrl = null, string? AuthToken = null);
|
||||
|
||||
@@ -187,6 +187,7 @@ public class ManagementActor : ReceiveActor
|
||||
or CreateNotificationListCommand or UpdateNotificationListCommand
|
||||
or DeleteNotificationListCommand
|
||||
or UpdateSmtpConfigCommand
|
||||
or UpdateSmsConfigCommand
|
||||
or CreateDataConnectionCommand or UpdateDataConnectionCommand
|
||||
or DeleteDataConnectionCommand or MoveDataConnectionCommand
|
||||
or AddTemplateAttributeCommand or UpdateTemplateAttributeCommand or DeleteTemplateAttributeCommand
|
||||
@@ -331,6 +332,8 @@ public class ManagementActor : ReceiveActor
|
||||
DeleteNotificationListCommand cmd => await HandleDeleteNotificationList(sp, cmd, user.Username),
|
||||
ListSmtpConfigsCommand => await HandleListSmtpConfigs(sp),
|
||||
UpdateSmtpConfigCommand cmd => await HandleUpdateSmtpConfig(sp, cmd, user.Username),
|
||||
ListSmsConfigsCommand => await HandleListSmsConfigs(sp),
|
||||
UpdateSmsConfigCommand cmd => await HandleUpdateSmsConfig(sp, cmd, user.Username),
|
||||
|
||||
// Shared Scripts
|
||||
ListSharedScriptsCommand => await HandleListSharedScripts(sp),
|
||||
@@ -1682,7 +1685,7 @@ public class ManagementActor : ReceiveActor
|
||||
private static async Task<object?> HandleCreateNotificationList(IServiceProvider sp, CreateNotificationListCommand cmd, string user)
|
||||
{
|
||||
var repo = sp.GetRequiredService<INotificationRepository>();
|
||||
var list = new NotificationList(cmd.Name);
|
||||
var list = new NotificationList(cmd.Name) { Type = cmd.Type };
|
||||
foreach (var email in cmd.RecipientEmails)
|
||||
{
|
||||
list.Recipients.Add(new NotificationRecipient(email, email));
|
||||
@@ -1699,6 +1702,7 @@ public class ManagementActor : ReceiveActor
|
||||
var list = await repo.GetNotificationListByIdAsync(cmd.NotificationListId)
|
||||
?? throw new ManagementCommandException($"NotificationList with ID {cmd.NotificationListId} not found.");
|
||||
list.Name = cmd.Name;
|
||||
list.Type = cmd.Type;
|
||||
|
||||
var existingRecipients = await repo.GetRecipientsByListIdAsync(cmd.NotificationListId);
|
||||
foreach (var r in existingRecipients)
|
||||
@@ -1784,6 +1788,58 @@ public class ManagementActor : ReceiveActor
|
||||
return publicShape;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// MgmtSvc-020 (SMS): project an SmsConfiguration to a credential-free shape so the
|
||||
/// stored AuthToken (Twilio Auth Token secret) never leaves this boundary via
|
||||
/// response payloads or audit afterState. Mirrors <see cref="SmtpConfigPublicShape"/>.
|
||||
/// </summary>
|
||||
private static object SmsConfigPublicShape(Commons.Entities.Notifications.SmsConfiguration c) =>
|
||||
new
|
||||
{
|
||||
c.Id,
|
||||
c.AccountSid,
|
||||
c.FromNumber,
|
||||
c.MessagingServiceSid,
|
||||
c.ApiBaseUrl,
|
||||
c.ConnectionTimeoutSeconds,
|
||||
c.MaxRetries,
|
||||
c.RetryDelay,
|
||||
HasAuthToken = !string.IsNullOrEmpty(c.AuthToken),
|
||||
};
|
||||
|
||||
private static async Task<object?> HandleListSmsConfigs(IServiceProvider sp)
|
||||
{
|
||||
var repo = sp.GetRequiredService<INotificationRepository>();
|
||||
var configs = await repo.GetAllSmsConfigurationsAsync();
|
||||
// MgmtSvc-020: project away the AuthToken field — read access to this
|
||||
// list is broader than the Designer-gated UpdateSmsConfig path that owns
|
||||
// the secret.
|
||||
return configs.Select(SmsConfigPublicShape).ToList();
|
||||
}
|
||||
|
||||
private static async Task<object?> HandleUpdateSmsConfig(IServiceProvider sp, UpdateSmsConfigCommand cmd, string user)
|
||||
{
|
||||
var repo = sp.GetRequiredService<INotificationRepository>();
|
||||
var configs = await repo.GetAllSmsConfigurationsAsync();
|
||||
var config = configs.FirstOrDefault(c => c.Id == cmd.SmsConfigId)
|
||||
?? throw new ManagementCommandException($"SmsConfiguration with ID {cmd.SmsConfigId} not found.");
|
||||
config.AccountSid = cmd.AccountSid;
|
||||
config.FromNumber = cmd.FromNumber;
|
||||
config.MessagingServiceSid = cmd.MessagingServiceSid;
|
||||
// Preserve-if-null: an update that omits ApiBaseUrl/AuthToken leaves the
|
||||
// existing values intact (non-breaking for callers that do not send them,
|
||||
// and so the secret AuthToken survives a config edit that does not rotate it).
|
||||
if (cmd.ApiBaseUrl is not null) config.ApiBaseUrl = cmd.ApiBaseUrl;
|
||||
if (cmd.AuthToken is not null) config.AuthToken = cmd.AuthToken;
|
||||
await repo.UpdateSmsConfigurationAsync(config);
|
||||
await repo.SaveChangesAsync();
|
||||
// MgmtSvc-020: audit the credential-free shape — the AuthToken secret is
|
||||
// not persisted to the audit log where OperationalAuditRoles can read it.
|
||||
var publicShape = SmsConfigPublicShape(config);
|
||||
await AuditAsync(sp, user, "Update", "SmsConfiguration", config.Id.ToString(), config.AccountSid, publicShape);
|
||||
return publicShape;
|
||||
}
|
||||
|
||||
// ========================================================================
|
||||
// Security handlers
|
||||
// ========================================================================
|
||||
|
||||
Reference in New Issue
Block a user