🐛 Serval bug fixes
This commit is contained in:
3
DysonNetwork.Pass/.gitignore
vendored
3
DysonNetwork.Pass/.gitignore
vendored
@@ -1 +1,2 @@
|
||||
/wwwroot/dist
|
||||
/wwwroot/dist
|
||||
/Keys
|
@@ -12,7 +12,7 @@ namespace DysonNetwork.Pass.Account;
|
||||
[Index(nameof(Name), IsUnique = true)]
|
||||
public class Account : ModelBase
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
public Guid Id { get; set; } = Guid.NewGuid();
|
||||
[MaxLength(256)] public string Name { get; set; } = string.Empty;
|
||||
[MaxLength(256)] public string Nick { get; set; } = string.Empty;
|
||||
[MaxLength(32)] public string Language { get; set; } = string.Empty;
|
||||
|
@@ -84,79 +84,69 @@ public class AccountService(
|
||||
bool isActivated = false
|
||||
)
|
||||
{
|
||||
await using var transaction = await db.Database.BeginTransactionAsync();
|
||||
try
|
||||
var dupeNameCount = await db.Accounts.Where(a => a.Name == name).CountAsync();
|
||||
if (dupeNameCount > 0)
|
||||
throw new InvalidOperationException("Account name has already been taken.");
|
||||
|
||||
var account = new Account
|
||||
{
|
||||
var dupeNameCount = await db.Accounts.Where(a => a.Name == name).CountAsync();
|
||||
if (dupeNameCount > 0)
|
||||
throw new InvalidOperationException("Account name has already been taken.");
|
||||
|
||||
var account = new Account
|
||||
Name = name,
|
||||
Nick = nick,
|
||||
Language = language,
|
||||
Contacts = new List<AccountContact>
|
||||
{
|
||||
Name = name,
|
||||
Nick = nick,
|
||||
Language = language,
|
||||
Contacts = new List<AccountContact>
|
||||
new()
|
||||
{
|
||||
new()
|
||||
{
|
||||
Type = AccountContactType.Email,
|
||||
Content = email,
|
||||
VerifiedAt = isEmailVerified ? SystemClock.Instance.GetCurrentInstant() : null,
|
||||
IsPrimary = true
|
||||
}
|
||||
},
|
||||
AuthFactors = password is not null
|
||||
? new List<AccountAuthFactor>
|
||||
{
|
||||
new AccountAuthFactor
|
||||
{
|
||||
Type = AccountAuthFactorType.Password,
|
||||
Secret = password,
|
||||
EnabledAt = SystemClock.Instance.GetCurrentInstant()
|
||||
}.HashSecret()
|
||||
}
|
||||
: [],
|
||||
Profile = new AccountProfile()
|
||||
};
|
||||
|
||||
if (isActivated)
|
||||
{
|
||||
account.ActivatedAt = SystemClock.Instance.GetCurrentInstant();
|
||||
var defaultGroup = await db.PermissionGroups.FirstOrDefaultAsync(g => g.Key == "default");
|
||||
if (defaultGroup is not null)
|
||||
{
|
||||
db.PermissionGroupMembers.Add(new PermissionGroupMember
|
||||
{
|
||||
Actor = $"user:{account.Id}",
|
||||
Group = defaultGroup
|
||||
});
|
||||
Type = AccountContactType.Email,
|
||||
Content = email,
|
||||
VerifiedAt = isEmailVerified ? SystemClock.Instance.GetCurrentInstant() : null,
|
||||
IsPrimary = true
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var spell = await spells.CreateMagicSpell(
|
||||
account,
|
||||
MagicSpellType.AccountActivation,
|
||||
new Dictionary<string, object>
|
||||
},
|
||||
AuthFactors = password is not null
|
||||
? new List<AccountAuthFactor>
|
||||
{
|
||||
new AccountAuthFactor
|
||||
{
|
||||
{ "contact_method", account.Contacts.First().Content }
|
||||
}
|
||||
);
|
||||
await spells.NotifyMagicSpell(spell, true);
|
||||
}
|
||||
Type = AccountAuthFactorType.Password,
|
||||
Secret = password,
|
||||
EnabledAt = SystemClock.Instance.GetCurrentInstant()
|
||||
}.HashSecret()
|
||||
}
|
||||
: [],
|
||||
Profile = new AccountProfile()
|
||||
};
|
||||
|
||||
db.Accounts.Add(account);
|
||||
await db.SaveChangesAsync();
|
||||
|
||||
await transaction.CommitAsync();
|
||||
return account;
|
||||
}
|
||||
catch
|
||||
if (isActivated)
|
||||
{
|
||||
await transaction.RollbackAsync();
|
||||
throw;
|
||||
account.ActivatedAt = SystemClock.Instance.GetCurrentInstant();
|
||||
var defaultGroup = await db.PermissionGroups.FirstOrDefaultAsync(g => g.Key == "default");
|
||||
if (defaultGroup is not null)
|
||||
{
|
||||
db.PermissionGroupMembers.Add(new PermissionGroupMember
|
||||
{
|
||||
Actor = $"user:{account.Id}",
|
||||
Group = defaultGroup
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
db.Accounts.Add(account);
|
||||
await db.SaveChangesAsync();
|
||||
|
||||
if (isActivated) return account;
|
||||
|
||||
var spell = await spells.CreateMagicSpell(
|
||||
account,
|
||||
MagicSpellType.AccountActivation,
|
||||
new Dictionary<string, object>
|
||||
{
|
||||
{ "contact_method", account.Contacts.First().Content }
|
||||
}
|
||||
);
|
||||
await spells.NotifyMagicSpell(spell, true);
|
||||
|
||||
return account;
|
||||
}
|
||||
|
||||
public async Task<Account> CreateAccount(OidcUserInfo userInfo)
|
||||
|
@@ -31,6 +31,7 @@ public class AuthSession : ModelBase
|
||||
LastGrantedAt = LastGrantedAt?.ToTimestamp(),
|
||||
ExpiredAt = ExpiredAt?.ToTimestamp(),
|
||||
AccountId = AccountId.ToString(),
|
||||
Account = Account.ToProtoValue(),
|
||||
ChallengeId = ChallengeId.ToString(),
|
||||
Challenge = Challenge.ToProtoValue(),
|
||||
AppId = AppId?.ToString()
|
||||
|
@@ -1,4 +1,3 @@
|
||||
using System.Text.Json;
|
||||
using DysonNetwork.Pass;
|
||||
using DysonNetwork.Pass.Pages.Data;
|
||||
using DysonNetwork.Pass.Startup;
|
||||
@@ -6,7 +5,6 @@ using DysonNetwork.Shared.Http;
|
||||
using DysonNetwork.Shared.PageData;
|
||||
using DysonNetwork.Shared.Registry;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.FileProviders;
|
||||
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
|
||||
@@ -18,11 +16,12 @@ builder.Services.AddAppMetrics();
|
||||
|
||||
// Add application services
|
||||
builder.Services.AddRegistryService(builder.Configuration);
|
||||
builder.Services.AddPusherService();
|
||||
builder.Services.AddAppServices(builder.Configuration);
|
||||
builder.Services.AddAppRateLimiting();
|
||||
builder.Services.AddAppAuthentication();
|
||||
builder.Services.AddAppSwagger();
|
||||
builder.Services.AddPusherService();
|
||||
builder.Services.AddDriveService();
|
||||
|
||||
// Add flush handlers and websocket handlers
|
||||
builder.Services.AddAppFlushHandlers();
|
||||
|
@@ -75,6 +75,7 @@ public static class ApplicationConfiguration
|
||||
app.MapGrpcService<AccountServiceGrpc>();
|
||||
app.MapGrpcService<AuthServiceGrpc>();
|
||||
app.MapGrpcService<ActionLogServiceGrpc>();
|
||||
app.MapGrpcService<PermissionServiceGrpc>();
|
||||
|
||||
return app;
|
||||
}
|
||||
|
@@ -16,6 +16,7 @@ builder.Services.AddAppRateLimiting();
|
||||
builder.Services.AddAppAuthentication();
|
||||
builder.Services.AddAppSwagger();
|
||||
builder.Services.AddDysonAuth();
|
||||
builder.Services.AddAccountService();
|
||||
|
||||
// Add flush handlers and websocket handlers
|
||||
builder.Services.AddAppFlushHandlers();
|
||||
|
@@ -29,8 +29,6 @@ public class DysonTokenAuthHandler(
|
||||
|
||||
try
|
||||
{
|
||||
var now = SystemClock.Instance.GetCurrentInstant();
|
||||
|
||||
// Validate token and extract session ID
|
||||
AuthSession session;
|
||||
try
|
||||
@@ -59,8 +57,20 @@ public class DysonTokenAuthHandler(
|
||||
new("token_type", tokenInfo.Type.ToString())
|
||||
};
|
||||
|
||||
// return AuthenticateResult.Success(ticket);
|
||||
return AuthenticateResult.NoResult();
|
||||
// Add scopes as claims
|
||||
session.Challenge.Scopes.ToList().ForEach(scope => claims.Add(new Claim("scope", scope)));
|
||||
|
||||
// Add superuser claim if applicable
|
||||
if (session.Account.IsSuperuser)
|
||||
claims.Add(new Claim("is_superuser", "1"));
|
||||
|
||||
// Create the identity and principal
|
||||
var identity = new ClaimsIdentity(claims, AuthConstants.SchemeName);
|
||||
var principal = new ClaimsPrincipal(identity);
|
||||
|
||||
var ticket = new AuthenticationTicket(principal, AuthConstants.SchemeName);
|
||||
|
||||
return AuthenticateResult.Success(ticket);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
@@ -49,6 +49,18 @@ public static class GrpcClientHelper
|
||||
return new AccountService.AccountServiceClient(CreateCallInvoker(url, clientCertPath, clientKeyPath,
|
||||
clientCertPassword));
|
||||
}
|
||||
|
||||
public static async Task<ActionLogService.ActionLogServiceClient> CreateActionLogServiceClient(
|
||||
IEtcdClient etcdClient,
|
||||
string clientCertPath,
|
||||
string clientKeyPath,
|
||||
string? clientCertPassword = null
|
||||
)
|
||||
{
|
||||
var url = await GetServiceUrlFromEtcd(etcdClient, "DysonNetwork.Pass");
|
||||
return new ActionLogService.ActionLogServiceClient(CreateCallInvoker(url, clientCertPath, clientKeyPath,
|
||||
clientCertPassword));
|
||||
}
|
||||
|
||||
public static async Task<AuthService.AuthServiceClient> CreateAuthServiceClient(
|
||||
IEtcdClient etcdClient,
|
||||
|
@@ -40,7 +40,21 @@ public static class ServiceHelper
|
||||
.CreateAccountServiceClient(etcdClient, clientCertPath, clientKeyPath, clientCertPassword)
|
||||
.GetAwaiter()
|
||||
.GetResult();
|
||||
});
|
||||
});
|
||||
|
||||
services.AddSingleton<ActionLogService.ActionLogServiceClient>(sp =>
|
||||
{
|
||||
var etcdClient = sp.GetRequiredService<IEtcdClient>();
|
||||
var config = sp.GetRequiredService<IConfiguration>();
|
||||
var clientCertPath = config["Service:ClientCert"]!;
|
||||
var clientKeyPath = config["Service:ClientKey"]!;
|
||||
var clientCertPassword = config["Service:CertPassword"];
|
||||
|
||||
return GrpcClientHelper
|
||||
.CreateActionLogServiceClient(etcdClient, clientCertPath, clientKeyPath, clientCertPassword)
|
||||
.GetAwaiter()
|
||||
.GetResult();
|
||||
});
|
||||
|
||||
return services;
|
||||
}
|
||||
|
@@ -8,8 +8,12 @@ namespace DysonNetwork.Shared.Registry;
|
||||
|
||||
public class ServiceRegistry(IEtcdClient etcd, ILogger<ServiceRegistry> logger)
|
||||
{
|
||||
public async Task RegisterService(string serviceName, string serviceUrl, long leaseTtlSeconds = 60,
|
||||
CancellationToken cancellationToken = default)
|
||||
public async Task RegisterService(
|
||||
string serviceName,
|
||||
string serviceUrl,
|
||||
long leaseTtlSeconds = 60,
|
||||
CancellationToken cancellationToken = default
|
||||
)
|
||||
{
|
||||
var key = $"/services/{serviceName}";
|
||||
var leaseResponse = await etcd.LeaseGrantAsync(
|
||||
|
@@ -177,6 +177,7 @@
|
||||
<_ContentIncludedByDefault Remove="Pages\Auth\VerifyFactor.cshtml" />
|
||||
<_ContentIncludedByDefault Remove="Pages\Checkpoint\CheckpointPage.cshtml" />
|
||||
<_ContentIncludedByDefault Remove="Pages\Spell\MagicSpellPage.cshtml" />
|
||||
<_ContentIncludedByDefault Remove="Keys\Solian.json" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
Reference in New Issue
Block a user