fix(sms): code-review fixes — Admin-gate provider-config updates, guard secret-clear/data-loss, type-aware UI
Findings from the per-module code review of the SMS feature (code-reviews/):
- ManagementService (High): UpdateSmsConfig + UpdateSmtpConfig were Designer-gated
while both /notifications/{sms,smtp} pages enforce RequireAdmin — a Designer
blocked in the UI could still rotate a production credential via CLI. Moved both
to the Administrator arm so the actor gate matches the UI.
- ManagementService (Medium): UpdateSmsConfig treated --auth-token "" as a value,
silently clearing the stored Twilio token. Guard on IsNullOrWhiteSpace so empty ==
omitted (SMTP Credentials keeps its null-only guard — empty is valid for no-auth).
- CentralUI (Medium): NotificationLists recipient badge rendered Name <Email>
unconditionally, showing "Name <>" for SMS lists. Now type-aware (phone for SMS).
- ConfigurationDatabase (Medium): AddSmsNotifications.Down() backfilled NULL emails
to '' — silent data loss for SMS-only recipients. Added a pre-drop guard that
refuses rollback while such rows exist.
- NotificationOutbox (Low): SMS body truncation could split a surrogate pair at the
cap boundary; back off one code unit to stay well-formed.
- Commons (Low): NotificationRecipient public ctor name-guard now matches the
ForEmail factory (IsNullOrWhiteSpace). Documented SmsConfiguration.MaxRetries/
RetryDelay as RESERVED (dispatcher reuses the shared SMTP-derived retry policy).
This commit is contained in:
+13
@@ -51,6 +51,17 @@ IF OBJECT_ID(N'dbo.SmsConfigurations', N'U') IS NULL
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
// ConfigurationDatabase-NNN: refuse to roll back if SMS-only recipients exist.
|
||||
// Such a row carries a PhoneNumber and a (legitimately) NULL EmailAddress. The
|
||||
// backfill below sets every NULL EmailAddress to '' so the column can revert to
|
||||
// NOT NULL — but for an SMS-only recipient that silently destroys its contact
|
||||
// info (PhoneNumber is also dropped) and leaves an invalid empty-string email.
|
||||
// This guard MUST run before DROP COLUMN [PhoneNumber], while the column is still
|
||||
// queryable, so the operator gets a clear error instead of silent data loss.
|
||||
migrationBuilder.Sql(@"
|
||||
IF EXISTS (SELECT 1 FROM [NotificationRecipients] WHERE [EmailAddress] IS NULL AND [PhoneNumber] IS NOT NULL)
|
||||
THROW 60000, 'Cannot roll back AddSmsNotifications: SMS-only recipients (PhoneNumber set, EmailAddress NULL) exist. Reassign or delete them before rolling back -- otherwise their PhoneNumber would be dropped and EmailAddress backfilled to an invalid empty string.', 1;");
|
||||
|
||||
migrationBuilder.Sql(@"
|
||||
IF OBJECT_ID(N'dbo.SmsConfigurations', N'U') IS NOT NULL
|
||||
DROP TABLE [SmsConfigurations];");
|
||||
@@ -61,6 +72,8 @@ IF EXISTS (SELECT 1 FROM sys.columns WHERE Name='PhoneNumber' AND Object_ID=Obje
|
||||
|
||||
// Reversing to NOT NULL requires backfilling existing NULLs first, otherwise the
|
||||
// ALTER fails. Mirrors EF's generated default ('') for the previously-required column.
|
||||
// The guard above guarantees any remaining NULL here belongs to a pre-existing email
|
||||
// recipient that never had an address, not an SMS-only recipient.
|
||||
migrationBuilder.Sql("UPDATE [NotificationRecipients] SET [EmailAddress] = '' WHERE [EmailAddress] IS NULL;");
|
||||
migrationBuilder.Sql("ALTER TABLE [NotificationRecipients] ALTER COLUMN [EmailAddress] nvarchar(500) NOT NULL;");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user