feat: harden SaaS authentication and authorization

This commit is contained in:
2026-07-28 12:15:51 +08:00
parent f22f329d33
commit 5d2248efee
123 changed files with 9090 additions and 2822 deletions

View File

@@ -1,20 +1,35 @@
using System.Text.Json;
using Microsoft.AspNetCore.Identity;
using Tiku.Domain.Common;
namespace Tiku.Domain.Identity;
public sealed class User : AuditableEntity
public sealed class User : IdentityUser<Guid>, IHasTimestamps
{
public User()
{
Id = Guid.NewGuid();
SecurityStamp = Guid.NewGuid().ToString("N");
ConcurrencyStamp = Guid.NewGuid().ToString("N");
}
public string? LegacyId { get; set; }
public string? Username { get; set; }
public string? Email { get; set; }
public string? Phone { get; set; }
public string? Name { get; set; }
public string? AvatarUrl { get; set; }
public string PrimaryRole { get; set; } = "student";
public int Score { get; set; }
public DateTimeOffset? LastSeenAt { get; set; }
public string? LegacyPasswordHash { get; set; }
public bool PasswordMigrationRequired { get; set; }
public UserStatus Status { get; set; } = UserStatus.Active;
public bool ForcePasswordChange { get; set; }
public JsonElement RawProfile { get; set; } = JsonDefaults.Object();
public DateTimeOffset CreatedAt { get; set; } = DateTimeOffset.UtcNow;
public DateTimeOffset UpdatedAt { get; set; } = DateTimeOffset.UtcNow;
}
public enum UserStatus
{
Active,
Disabled,
Archived
}

View File

@@ -1,4 +1,3 @@
using System.Text.Json;
using Tiku.Domain.Common;
namespace Tiku.Domain.Identity;
@@ -12,5 +11,4 @@ public sealed class UserIdentity : AuditableEntity
public string? OpenId { get; set; }
public string? Phone { get; set; }
public string? Email { get; set; }
public JsonElement SecretPayload { get; set; } = JsonDefaults.Object();
}

View File

@@ -1,4 +1,3 @@
using System.Text.Json;
using Tiku.Domain.Common;
namespace Tiku.Domain.Tenancy;
@@ -6,16 +5,13 @@ namespace Tiku.Domain.Tenancy;
public sealed class TenantMembership : AuditableTenantEntity
{
public Guid UserId { get; set; }
public Guid? RoleTemplateId { get; set; }
public TenantRole Role { get; set; } = TenantRole.Student;
public MembershipStatus Status { get; set; } = MembershipStatus.Active;
public JsonElement Permissions { get; set; } = JsonDefaults.Object();
public string? LegacyRole { get; set; }
}
public enum TenantRole
{
PlatformAdmin,
TenantOwner,
TenantAdmin,
TenantOperator,

View File

@@ -47,9 +47,9 @@ public sealed class SmsVerificationCode : Entity, ITenantOwned
public DateTimeOffset CreatedAt { get; set; } = DateTimeOffset.UtcNow;
}
public sealed class AuthLoginEvent : Entity, ITenantOwned
public sealed class AuthLoginEvent : Entity
{
public Guid TenantId { get; set; }
public Guid? TenantId { get; set; }
public Guid? UserId { get; set; }
public string Provider { get; set; } = string.Empty;
public string? Identifier { get; set; }
@@ -61,18 +61,46 @@ public sealed class AuthLoginEvent : Entity, ITenantOwned
public DateTimeOffset CreatedAt { get; set; } = DateTimeOffset.UtcNow;
}
public sealed class AuthSession : AuditableTenantEntity
public sealed class AuthSession : AuditableEntity
{
public AuthRealm Realm { get; set; } = AuthRealm.Tenant;
public Guid? TenantId { get; set; }
public Guid UserId { get; set; }
public Guid TokenFamilyId { get; set; }
public Guid? ParentSessionId { get; set; }
public Guid? ReplacedBySessionId { get; set; }
public string TokenHash { get; set; } = string.Empty;
public string SecurityStamp { get; set; } = string.Empty;
public bool MfaSatisfied { get; set; }
public string Provider { get; set; } = string.Empty;
public DateTimeOffset ExpiresAt { get; set; }
public DateTimeOffset? RevokedAt { get; set; }
public string? RevokedReason { get; set; }
public string? IpAddress { get; set; }
public string? UserAgent { get; set; }
public JsonElement Metadata { get; set; } = JsonDefaults.Object();
}
public enum AuthRealm { Tenant, Platform }
public sealed class AuthChallenge : Entity
{
public Guid UserId { get; set; }
public AuthRealm Realm { get; set; }
public Guid? TenantId { get; set; }
public AuthChallengePurpose Purpose { get; set; }
public string TokenHash { get; set; } = string.Empty;
public string SecurityStamp { get; set; } = string.Empty;
public string Provider { get; set; } = string.Empty;
public DateTimeOffset ExpiresAt { get; set; }
public DateTimeOffset? ConsumedAt { get; set; }
public string? IpAddress { get; set; }
public string? UserAgent { get; set; }
public DateTimeOffset CreatedAt { get; set; } = DateTimeOffset.UtcNow;
}
public enum AuthChallengePurpose { MfaEnrollment, MfaVerification, PasswordChange }
public sealed class SmsSendRateLimit : ITenantOwned
{
public Guid TenantId { get; set; }
@@ -83,24 +111,6 @@ public sealed class SmsSendRateLimit : ITenantOwned
public DateTimeOffset UpdatedAt { get; set; } = DateTimeOffset.UtcNow;
}
public sealed class TenantRoleTemplate : AuditableTenantEntity
{
public string Code { get; set; } = string.Empty;
public string Name { get; set; } = string.Empty;
public string? Description { get; set; }
public TenantRole BaseRole { get; set; } = TenantRole.TenantOperator;
public TenantRoleTemplateStatus Status { get; set; } = TenantRoleTemplateStatus.Active;
public JsonElement Permissions { get; set; } = JsonDefaults.Object();
public JsonElement MenuPermissions { get; set; } = JsonDefaults.Object();
public JsonElement ModulePermissions { get; set; } = JsonDefaults.Object();
public JsonElement FieldPermissions { get; set; } = JsonDefaults.Object();
public JsonElement DataScope { get; set; } = JsonDefaults.Object();
public bool IsSystem { get; set; }
public int SortOrder { get; set; } = 100;
public Guid? CreatedBy { get; set; }
public Guid? UpdatedBy { get; set; }
}
public sealed class TenantClass : AuditableTenantEntity
{
public Guid? RegionId { get; set; }
@@ -165,7 +175,6 @@ public enum SmsPurpose { Login, BindPhone, ResetPassword }
public enum SmsVerificationStatus { Pending, Sent, Verified, Expired, Blocked, Failed }
public enum AuthLoginResult { Sent, Success, Failed, Blocked }
public enum SmsRateLimitDimension { Tenant, Phone, Ip, Device }
public enum TenantRoleTemplateStatus { Active, Disabled, Archived }
public enum TenantRecordStatus { Active, Disabled, Archived }
public enum TenantClassMemberType { Student, Teacher, Assistant, HeadTeacher }
public enum TenantClassMemberStatus { Active, Disabled, Removed }

View File

@@ -6,4 +6,8 @@
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Identity.Stores" />
</ItemGroup>
</Project>