diff --git a/src/Host/FSH.Starter.Api/appsettings.json b/src/Host/FSH.Starter.Api/appsettings.json index 60418ef856..5a9628a5f8 100644 --- a/src/Host/FSH.Starter.Api/appsettings.json +++ b/src/Host/FSH.Starter.Api/appsettings.json @@ -1,7 +1,7 @@ { "Billing": { "DefaultPlanKey": "free", - "GraceWindowDays": 7 + "GracePeriodDays": 7 }, "OpenTelemetryOptions": { "Enabled": true, diff --git a/src/Modules/Identity/Modules.Identity/IdentityModule.cs b/src/Modules/Identity/Modules.Identity/IdentityModule.cs index 74dafdcec3..f7f4da64ed 100644 --- a/src/Modules/Identity/Modules.Identity/IdentityModule.cs +++ b/src/Modules/Identity/Modules.Identity/IdentityModule.cs @@ -125,7 +125,7 @@ public void ConfigureServices(IHostApplicationBuilder builder) // Configure password policy options services.Configure(builder.Configuration.GetSection("PasswordPolicy")); - // Tenant subscription grace window (shared "Billing" section) — used by the login expiry check. + // Tenant subscription grace period (shared "Billing" section) — used by the login expiry check. services.Configure(builder.Configuration.GetSection(TenantGraceOptions.SectionName)); // Register password history service diff --git a/src/Modules/Identity/Modules.Identity/Services/IdentityService.cs b/src/Modules/Identity/Modules.Identity/Services/IdentityService.cs index 635f071423..a403a2363e 100644 --- a/src/Modules/Identity/Modules.Identity/Services/IdentityService.cs +++ b/src/Modules/Identity/Modules.Identity/Services/IdentityService.cs @@ -23,7 +23,7 @@ public sealed class IdentityService : IIdentityService private readonly IGroupRoleService _groupRoleService; private readonly TimeProvider _timeProvider; private readonly IdentityDbContext _dbContext; - private readonly int _graceWindowDays; + private readonly int _gracePeriodDays; public IdentityService( UserManager userManager, @@ -41,7 +41,7 @@ public IdentityService( _groupRoleService = groupRoleService; _timeProvider = timeProvider; _dbContext = dbContext; - _graceWindowDays = graceOptions.Value.GraceWindowDays; + _gracePeriodDays = graceOptions.Value.GracePeriodDays; } public async Task<(string Subject, IEnumerable Claims)?> @@ -286,9 +286,9 @@ private void ValidateTenantStatus(AppTenantInfo tenant) throw new UnauthorizedException($"tenant {tenant.Id} is deactivated"); } - // Honor the billing grace window: a lapsed tenant can still authenticate until + // Honor the billing grace period: a lapsed tenant can still authenticate until // ValidUpto + grace (matching the request-time guard in MultitenancyModule). - if (_timeProvider.GetUtcNow().UtcDateTime > tenant.ValidUpto.AddDays(_graceWindowDays)) + if (_timeProvider.GetUtcNow().UtcDateTime > tenant.ValidUpto.AddDays(_gracePeriodDays)) { throw new UnauthorizedException($"tenant {tenant.Id} validity has expired"); } diff --git a/src/Modules/Identity/Modules.Identity/TenantGraceOptions.cs b/src/Modules/Identity/Modules.Identity/TenantGraceOptions.cs index be0e2f4ea3..6bb26a5f46 100644 --- a/src/Modules/Identity/Modules.Identity/TenantGraceOptions.cs +++ b/src/Modules/Identity/Modules.Identity/TenantGraceOptions.cs @@ -1,12 +1,12 @@ namespace FSH.Modules.Identity; /// -/// Login-side view of the tenant billing grace window (config section "Billing"). A tenant -/// whose subscription has lapsed can still authenticate until ValidUpto + GraceWindowDays. +/// Login-side view of the tenant billing grace period (config section "Billing"). A tenant +/// whose subscription has lapsed can still authenticate until ValidUpto + GracePeriodDays. /// public sealed class TenantGraceOptions { public const string SectionName = "Billing"; - public int GraceWindowDays { get; set; } = 7; + public int GracePeriodDays { get; set; } = 7; } diff --git a/src/Modules/Multitenancy/Modules.Multitenancy.Contracts/Dtos/TenantStatusDto.cs b/src/Modules/Multitenancy/Modules.Multitenancy.Contracts/Dtos/TenantStatusDto.cs index 2f1ba77957..aee498877d 100644 --- a/src/Modules/Multitenancy/Modules.Multitenancy.Contracts/Dtos/TenantStatusDto.cs +++ b/src/Modules/Multitenancy/Modules.Multitenancy.Contracts/Dtos/TenantStatusDto.cs @@ -16,6 +16,6 @@ public sealed class TenantStatusDto /// Derived lifecycle state: "Active", "InGrace", or "Expired". public string ExpiryState { get; init; } = "Active"; - /// Instant after which a lapsed tenant is hard-blocked (ValidUpto + grace window). + /// Instant after which a lapsed tenant is hard-blocked (ValidUpto + grace period). public DateTime GraceEndsUtc { get; init; } } \ No newline at end of file diff --git a/src/Modules/Multitenancy/Modules.Multitenancy.Contracts/Events/TenantEnteredGraceIntegrationEvent.cs b/src/Modules/Multitenancy/Modules.Multitenancy.Contracts/Events/TenantEnteredGraceIntegrationEvent.cs index 8775f05b39..244c467e42 100644 --- a/src/Modules/Multitenancy/Modules.Multitenancy.Contracts/Events/TenantEnteredGraceIntegrationEvent.cs +++ b/src/Modules/Multitenancy/Modules.Multitenancy.Contracts/Events/TenantEnteredGraceIntegrationEvent.cs @@ -4,7 +4,7 @@ namespace FSH.Modules.Multitenancy.Contracts.Events; /// /// Raised by the daily expiry scan when a tenant has passed ValidUpto but is still inside the -/// grace window (access continues). Consumers warn the tenant that the grace period is counting down. +/// grace period (access continues). Consumers warn the tenant that the grace period is counting down. /// public sealed record TenantEnteredGraceIntegrationEvent( Guid Id, diff --git a/src/Modules/Multitenancy/Modules.Multitenancy/MultitenancyModule.cs b/src/Modules/Multitenancy/Modules.Multitenancy/MultitenancyModule.cs index a03b3539c6..fd157b1af1 100644 --- a/src/Modules/Multitenancy/Modules.Multitenancy/MultitenancyModule.cs +++ b/src/Modules/Multitenancy/Modules.Multitenancy/MultitenancyModule.cs @@ -175,10 +175,10 @@ public void ConfigureMiddleware(IApplicationBuilder app) throw new ForbiddenException("This tenant has been deactivated. Contact your administrator."); } - // Expiry is enforced on every request (not just at login) with a grace window: + // Expiry is enforced on every request (not just at login) with a grace period: // a tenant past ValidUpto still works until ValidUpto + grace, then is hard-blocked. var graceDays = ctx.RequestServices - .GetRequiredService>().Value.GraceWindowDays; + .GetRequiredService>().Value.GracePeriodDays; var nowUtc = ctx.RequestServices.GetRequiredService().GetUtcNow().UtcDateTime; var graceEndsUtc = tenant.ValidUpto.AddDays(graceDays); if (nowUtc > graceEndsUtc) @@ -186,7 +186,7 @@ public void ConfigureMiddleware(IApplicationBuilder app) throw new ForbiddenException("This tenant's subscription has expired. Please renew to continue."); } - // Inside the grace window: surface days-left so clients can warn. Set via OnStarting so + // Inside the grace period: surface days-left so clients can warn. Set via OnStarting so // the header survives even when an exception handler rewrites the response. if (nowUtc > tenant.ValidUpto) { diff --git a/src/Modules/Multitenancy/Modules.Multitenancy/Services/TenantExpiryScanJob.cs b/src/Modules/Multitenancy/Modules.Multitenancy/Services/TenantExpiryScanJob.cs index 6a19be0f30..a766ad16d5 100644 --- a/src/Modules/Multitenancy/Modules.Multitenancy/Services/TenantExpiryScanJob.cs +++ b/src/Modules/Multitenancy/Modules.Multitenancy/Services/TenantExpiryScanJob.cs @@ -84,7 +84,7 @@ public async Task RunAsync(CancellationToken cancellationToken) private async Task TryNotifyAsync(AppTenantInfo tenant, DateTime now, CancellationToken ct) { var validUpto = tenant.ValidUpto; - var graceEnds = validUpto.AddDays(_options.GraceWindowDays); + var graceEnds = validUpto.AddDays(_options.GracePeriodDays); string noticeType; if (now > graceEnds) diff --git a/src/Modules/Multitenancy/Modules.Multitenancy/Services/TenantService.cs b/src/Modules/Multitenancy/Modules.Multitenancy/Services/TenantService.cs index e1e81027a9..15d8fdbe01 100644 --- a/src/Modules/Multitenancy/Modules.Multitenancy/Services/TenantService.cs +++ b/src/Modules/Multitenancy/Modules.Multitenancy/Services/TenantService.cs @@ -166,7 +166,7 @@ public async Task GetStatusAsync(string id, CancellationToken c { var tenant = await GetTenantInfoAsync(id, cancellationToken).ConfigureAwait(false); - var graceEnds = tenant.ValidUpto.AddDays(_billingOptions.GraceWindowDays); + var graceEnds = tenant.ValidUpto.AddDays(_billingOptions.GracePeriodDays); var now = _timeProvider.GetUtcNow().UtcDateTime; string expiryState; if (now <= tenant.ValidUpto) diff --git a/src/Modules/Multitenancy/Modules.Multitenancy/TenantBillingOptions.cs b/src/Modules/Multitenancy/Modules.Multitenancy/TenantBillingOptions.cs index b3e54ebe16..a602706405 100644 --- a/src/Modules/Multitenancy/Modules.Multitenancy/TenantBillingOptions.cs +++ b/src/Modules/Multitenancy/Modules.Multitenancy/TenantBillingOptions.cs @@ -13,7 +13,7 @@ public sealed class TenantBillingOptions public string DefaultPlanKey { get; set; } = "free"; /// Days past ValidUpto during which requests/logins still succeed. - public int GraceWindowDays { get; set; } = 7; + public int GracePeriodDays { get; set; } = 7; /// How many days before ValidUpto the daily scan starts sending "nearing expiry" reminders. public int ExpiryNotificationLeadDays { get; set; } = 7; diff --git a/src/Tests/Integration.Tests/Tests/Multitenancy/AdjustTenantValidityTests.cs b/src/Tests/Integration.Tests/Tests/Multitenancy/AdjustTenantValidityTests.cs index 11f33f64de..6f573ebb9e 100644 --- a/src/Tests/Integration.Tests/Tests/Multitenancy/AdjustTenantValidityTests.cs +++ b/src/Tests/Integration.Tests/Tests/Multitenancy/AdjustTenantValidityTests.cs @@ -69,7 +69,7 @@ public async Task AdjustValidity_Should_Allow_Backdating_ToExpireImmediately() var planKey = await CreatePlanAsync(rootClient, $"adj-b-{unique}", monthlyBasePrice: 10m); await CreateTenantAsync(rootClient, tenantId, $"adj-back-{unique}@tenant.com", planKey); - // Backdate well past the grace window — renewal would reject this; the override allows it. + // Backdate well past the grace period — renewal would reject this; the override allows it. var target = DateTime.UtcNow.AddDays(-30); var response = await rootClient.PostAsJsonAsync( $"{TestConstants.TenantsBasePath}/{tenantId}/adjust-validity", diff --git a/src/Tests/Integration.Tests/Tests/Multitenancy/TenantExpiryEnforcementTests.cs b/src/Tests/Integration.Tests/Tests/Multitenancy/TenantExpiryEnforcementTests.cs index 09eef0b959..0f218983d1 100644 --- a/src/Tests/Integration.Tests/Tests/Multitenancy/TenantExpiryEnforcementTests.cs +++ b/src/Tests/Integration.Tests/Tests/Multitenancy/TenantExpiryEnforcementTests.cs @@ -9,7 +9,7 @@ namespace Integration.Tests.Tests.Multitenancy; /// /// Verifies subscription-expiry enforcement in the post-auth tenant guard: a tenant past -/// ValidUpto but within the grace window still passes (so requests/logins keep working during +/// ValidUpto but within the grace period still passes (so requests/logins keep working during /// dunning), while a tenant past ValidUpto + grace is hard-blocked with 403 — mirroring the /// deactivation guard. Grace defaults to 7 days. /// @@ -33,7 +33,7 @@ public async Task ExpiredTenant_PastGrace_Should_Be_Blocked() // While valid, the guard does not short-circuit — the token handler runs and fails on creds. (await TryIssueTokenAsync(tenantId)).ShouldNotBe(HttpStatusCode.Forbidden); - // Lapse well past the 7-day grace window. + // Lapse well past the 7-day grace period. await SetTenantValidityAsync(tenantId, DateTime.UtcNow.AddDays(-30)); (await TryIssueTokenAsync(tenantId)).ShouldBe(HttpStatusCode.Forbidden, @@ -45,11 +45,11 @@ public async Task LapsedTenant_WithinGrace_Should_Not_Be_Blocked() { var tenantId = await CreateTenantAsync(); - // One day past expiry is still inside the 7-day grace window. + // One day past expiry is still inside the 7-day grace period. await SetTenantValidityAsync(tenantId, DateTime.UtcNow.AddDays(-1)); (await TryIssueTokenAsync(tenantId)).ShouldNotBe(HttpStatusCode.Forbidden, - "a lapsed tenant within the grace window must still be allowed through the guard"); + "a lapsed tenant within the grace period must still be allowed through the guard"); } [Fact] @@ -57,13 +57,13 @@ public async Task LapsedTenant_WithinGrace_Should_Emit_GraceHeader() { var tenantId = await CreateTenantAsync(); - // One day past expiry — inside the 7-day grace window. + // One day past expiry — inside the 7-day grace period. await SetTenantValidityAsync(tenantId, DateTime.UtcNow.AddDays(-1)); var (status, grace) = await ProbeAsync(tenantId); status.ShouldNotBe(HttpStatusCode.Forbidden); - grace.ShouldNotBeNull("a tenant in the grace window must receive the X-Subscription-Grace header"); + grace.ShouldNotBeNull("a tenant in the grace period must receive the X-Subscription-Grace header"); int.Parse(grace!, CultureInfo.InvariantCulture).ShouldBeInRange(1, 7); } diff --git a/src/Tests/Integration.Tests/Tests/Multitenancy/TenantExpiryScanJobTests.cs b/src/Tests/Integration.Tests/Tests/Multitenancy/TenantExpiryScanJobTests.cs index 05570011af..81618be68f 100644 --- a/src/Tests/Integration.Tests/Tests/Multitenancy/TenantExpiryScanJobTests.cs +++ b/src/Tests/Integration.Tests/Tests/Multitenancy/TenantExpiryScanJobTests.cs @@ -37,7 +37,7 @@ public async Task ScanJob_Should_Record_GraceNotice_Dedup_And_Email() var planKey = await CreatePlanAsync(rootClient, $"scan-m-{unique}", 10m); await CreateTenantAsync(rootClient, tenantId, adminEmail, planKey); - // Lapse into the grace window (1 day past ValidUpto). + // Lapse into the grace period (1 day past ValidUpto). var adjust = await rootClient.PostAsJsonAsync( $"{TestConstants.TenantsBasePath}/{tenantId}/adjust-validity", new { tenantId, validUpto = DateTime.UtcNow.AddDays(-1) }); diff --git a/src/Tests/Multitenancy.Tests/Services/TenantServiceStatusBoundaryTests.cs b/src/Tests/Multitenancy.Tests/Services/TenantServiceStatusBoundaryTests.cs index 8a89ce7480..0e2b4c6c05 100644 --- a/src/Tests/Multitenancy.Tests/Services/TenantServiceStatusBoundaryTests.cs +++ b/src/Tests/Multitenancy.Tests/Services/TenantServiceStatusBoundaryTests.cs @@ -12,7 +12,7 @@ namespace Multitenancy.Tests.Services; /// /// Pins the expiry-state transitions in exactly at the /// ValidUpto and grace-window boundaries, so the Active → InGrace → Expired badges never drift. -/// Grace window is fixed at 7 days for these cases. +/// Grace period is fixed at 7 days for these cases. /// public sealed class TenantServiceStatusBoundaryTests { @@ -47,7 +47,7 @@ public async Task GetStatusAsync_Should_ResolveExpiryState_AtBoundary(double off dbContext: null!, provisioningService: null!, _clock, - Options.Create(new TenantBillingOptions { GraceWindowDays = GraceDays }), + Options.Create(new TenantBillingOptions { GracePeriodDays = GraceDays }), NullLogger.Instance); var status = await sut.GetStatusAsync(tenantId, CancellationToken.None);