forked from gongxuegit/tiku-backend.net
feat: modularize external providers
This commit is contained in:
@@ -3,6 +3,7 @@ using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Tiku.Application.Auth;
|
||||
using Tiku.Application.Security;
|
||||
using Tiku.Application.Tenancy;
|
||||
using Tiku.Domain.Identity;
|
||||
using Tiku.Domain.Tenancy;
|
||||
using Tiku.Infrastructure.Auth;
|
||||
@@ -182,7 +183,7 @@ public sealed class AuthServiceTests
|
||||
Assert.Equal(tenantId, result.Tenant.TenantId);
|
||||
Assert.Single(context.Users);
|
||||
Assert.Contains(context.UserIdentities, identity =>
|
||||
identity.Provider == "wechat-miniapp" &&
|
||||
identity.Provider == "wechat_miniapp" &&
|
||||
identity.ProviderSubject == "wx-app-id:mini-open-id" &&
|
||||
identity.OpenId == "mini-open-id" &&
|
||||
identity.UnionId == "union-id");
|
||||
@@ -198,11 +199,13 @@ public sealed class AuthServiceTests
|
||||
{
|
||||
await using var context = CreateContext();
|
||||
var tenantId = await SeedTenantWithWechatProviderAsync(context, "wechat-miniapp");
|
||||
context.TenantAuthProviders.Add(new TenantAuthProvider
|
||||
context.TenantExternalProviders.Add(new TenantExternalProvider
|
||||
{
|
||||
TenantId = tenantId,
|
||||
Provider = "wechat_web",
|
||||
Status = TenantAuthProviderStatus.Testing,
|
||||
Capability = TenantExternalProviderCapability.Identity,
|
||||
Status = TenantExternalProviderStatus.Active,
|
||||
SecretRef = "tenant_secrets:identity:wechat_web:default",
|
||||
ConfigPublic = WechatProviderConfig()
|
||||
});
|
||||
var user = new User
|
||||
@@ -257,14 +260,15 @@ public sealed class AuthServiceTests
|
||||
{
|
||||
var tokenService = new TokenService(Options.Create(JwtOptions));
|
||||
var sessionService = new SessionService(context, tokenService, Options.Create(JwtOptions));
|
||||
var smsService = new SmsVerificationService(context);
|
||||
var smsService = new SmsVerificationService(context, new FakeSmsProvider());
|
||||
|
||||
return new AuthService(
|
||||
context,
|
||||
new PasswordHasher(),
|
||||
smsService,
|
||||
sessionService,
|
||||
wechatOAuthClient ?? new FakeWechatOAuthClient());
|
||||
wechatOAuthClient ?? new FakeWechatOAuthClient(),
|
||||
new FakeProviderConfigService(context));
|
||||
}
|
||||
|
||||
private static async Task<Guid> SeedTenantWithWechatProviderAsync(
|
||||
@@ -278,11 +282,13 @@ public sealed class AuthServiceTests
|
||||
Name = "Wechat Tenant"
|
||||
};
|
||||
context.Tenants.Add(tenant);
|
||||
context.TenantAuthProviders.Add(new TenantAuthProvider
|
||||
context.TenantExternalProviders.Add(new TenantExternalProvider
|
||||
{
|
||||
TenantId = tenant.Id,
|
||||
Provider = provider,
|
||||
Status = TenantAuthProviderStatus.Testing,
|
||||
Provider = provider.Replace("-", "_", StringComparison.Ordinal),
|
||||
Capability = TenantExternalProviderCapability.Identity,
|
||||
Status = TenantExternalProviderStatus.Active,
|
||||
SecretRef = $"tenant_secrets:identity:{provider}:default",
|
||||
ConfigPublic = WechatProviderConfig()
|
||||
});
|
||||
await context.SaveChangesAsync();
|
||||
@@ -294,8 +300,7 @@ public sealed class AuthServiceTests
|
||||
{
|
||||
return JsonSerializer.SerializeToElement(new
|
||||
{
|
||||
appId = "wx-app-id",
|
||||
appSecret = "wx-app-secret"
|
||||
appId = "wx-app-id"
|
||||
});
|
||||
}
|
||||
|
||||
@@ -347,6 +352,64 @@ public sealed class AuthServiceTests
|
||||
return document.RootElement.Clone();
|
||||
}
|
||||
|
||||
private sealed class FakeProviderConfigService(TikuDbContext context) : ITenantExternalProviderConfigService
|
||||
{
|
||||
public Task<TenantExternalProviderAccount> GetActiveProviderAsync(
|
||||
Guid tenantId,
|
||||
TenantExternalProviderCapability capability,
|
||||
string? provider = null,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
var normalizedProvider = provider?.Replace("-", "_", StringComparison.Ordinal);
|
||||
var item = context.TenantExternalProviders
|
||||
.AsEnumerable()
|
||||
.Where(entity =>
|
||||
entity.TenantId == tenantId &&
|
||||
entity.Capability == capability &&
|
||||
entity.Status == TenantExternalProviderStatus.Active)
|
||||
.Where(entity => string.IsNullOrWhiteSpace(normalizedProvider) || entity.Provider == normalizedProvider)
|
||||
.OrderBy(entity => entity.Priority)
|
||||
.FirstOrDefault()
|
||||
?? throw new TenantExternalProviderException(
|
||||
"Tenant external provider is not configured.",
|
||||
"tenant_external_provider_not_configured");
|
||||
|
||||
return Task.FromResult(new TenantExternalProviderAccount(
|
||||
item.TenantId,
|
||||
item.Capability,
|
||||
item.Provider,
|
||||
item.Status,
|
||||
item.DisplayName,
|
||||
item.ConfigPublic,
|
||||
JsonSerializer.SerializeToElement(new { appSecret = "wx-app-secret" }),
|
||||
item.SecretRef,
|
||||
item.Priority,
|
||||
item.Metadata));
|
||||
}
|
||||
|
||||
public Task<IReadOnlyCollection<TenantExternalProviderItem>> GetProvidersAsync(
|
||||
Guid tenantId,
|
||||
TenantExternalProviderCapability? capability = null,
|
||||
string? provider = null,
|
||||
int? limit = null,
|
||||
CancellationToken cancellationToken = default) =>
|
||||
throw new NotSupportedException();
|
||||
|
||||
public Task<TenantExternalProviderItem> UpsertProviderAsync(
|
||||
Guid tenantId,
|
||||
UpsertTenantExternalProviderCommand command,
|
||||
CancellationToken cancellationToken = default) =>
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
private sealed class FakeSmsProvider : ISmsProvider
|
||||
{
|
||||
public Task<SmsProviderSendResult> SendAsync(
|
||||
SmsProviderSendRequest request,
|
||||
CancellationToken cancellationToken = default) =>
|
||||
Task.FromResult(new SmsProviderSendResult("fake", "accepted"));
|
||||
}
|
||||
|
||||
private sealed class FakeWechatOAuthClient(
|
||||
WechatIdentity? WebIdentity = null,
|
||||
WechatIdentity? MiniAppIdentity = null) : IWechatOAuthClient
|
||||
|
||||
Reference in New Issue
Block a user