♻️ Refactored grpc part of shared project
This commit is contained in:
95
DysonNetwork.Pass/AppDatabase.cs
Normal file
95
DysonNetwork.Pass/AppDatabase.cs
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
using DysonNetwork.Shared.Models;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Design;
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
using NodaTime;
|
||||||
|
|
||||||
|
namespace DysonNetwork.Pass;
|
||||||
|
|
||||||
|
public abstract class ModelBase
|
||||||
|
{
|
||||||
|
public Instant CreatedAt { get; set; }
|
||||||
|
public Instant UpdatedAt { get; set; }
|
||||||
|
public Instant? DeletedAt { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class AppDatabase(
|
||||||
|
DbContextOptions<AppDatabase> options,
|
||||||
|
IConfiguration configuration
|
||||||
|
) : DbContext(options)
|
||||||
|
{
|
||||||
|
public DbSet<PermissionNode> PermissionNodes { get; set; }
|
||||||
|
public DbSet<PermissionGroup> PermissionGroups { get; set; }
|
||||||
|
public DbSet<PermissionGroupMember> PermissionGroupMembers { get; set; }
|
||||||
|
|
||||||
|
public DbSet<Account> Accounts { get; set; }
|
||||||
|
public DbSet<AccountConnection> AccountConnections { get; set; }
|
||||||
|
public DbSet<Profile> AccountProfiles { get; set; }
|
||||||
|
public DbSet<AccountContact> AccountContacts { get; set; }
|
||||||
|
public DbSet<AccountAuthFactor> AccountAuthFactors { get; set; }
|
||||||
|
public DbSet<Relationship> AccountRelationships { get; set; }
|
||||||
|
|
||||||
|
public DbSet<Session> AuthSessions { get; set; }
|
||||||
|
public DbSet<Challenge> AuthChallenges { get; set; }
|
||||||
|
|
||||||
|
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
|
||||||
|
{
|
||||||
|
optionsBuilder.UseNpgsql(
|
||||||
|
configuration.GetConnectionString("App"),
|
||||||
|
opt => opt
|
||||||
|
.UseNodaTime()
|
||||||
|
).UseSnakeCaseNamingConvention();
|
||||||
|
|
||||||
|
base.OnConfiguring(optionsBuilder);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||||
|
{
|
||||||
|
base.OnModelCreating(modelBuilder);
|
||||||
|
|
||||||
|
modelBuilder.Entity<PermissionGroupMember>()
|
||||||
|
.HasKey(pg => new { pg.GroupId, pg.Actor });
|
||||||
|
|
||||||
|
modelBuilder.Entity<Relationship>()
|
||||||
|
.HasKey(r => new { FromAccountId = r.AccountId, ToAccountId = r.RelatedId });
|
||||||
|
}
|
||||||
|
|
||||||
|
public override async Task<int> SaveChangesAsync(CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
var now = SystemClock.Instance.GetCurrentInstant();
|
||||||
|
|
||||||
|
foreach (var entry in ChangeTracker.Entries<ModelBase>())
|
||||||
|
{
|
||||||
|
if (entry.State == EntityState.Added)
|
||||||
|
{
|
||||||
|
entry.Entity.CreatedAt = now;
|
||||||
|
entry.Entity.UpdatedAt = now;
|
||||||
|
}
|
||||||
|
else if (entry.State == EntityState.Modified)
|
||||||
|
{
|
||||||
|
entry.Entity.UpdatedAt = now;
|
||||||
|
}
|
||||||
|
else if (entry.State == EntityState.Deleted)
|
||||||
|
{
|
||||||
|
entry.State = EntityState.Modified;
|
||||||
|
entry.Entity.DeletedAt = now;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return await base.SaveChangesAsync(cancellationToken);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class AppDatabaseFactory : IDesignTimeDbContextFactory<AppDatabase>
|
||||||
|
{
|
||||||
|
public AppDatabase CreateDbContext(string[] args)
|
||||||
|
{
|
||||||
|
var configuration = new ConfigurationBuilder()
|
||||||
|
.SetBasePath(Directory.GetCurrentDirectory())
|
||||||
|
.AddJsonFile("appsettings.json")
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
var optionsBuilder = new DbContextOptionsBuilder<AppDatabase>();
|
||||||
|
return new AppDatabase(optionsBuilder.Options, configuration);
|
||||||
|
}
|
||||||
|
}
|
@ -5,7 +5,7 @@ package dyson_network.sphere.account;
|
|||||||
import "google/protobuf/empty.proto";
|
import "google/protobuf/empty.proto";
|
||||||
import "google/protobuf/timestamp.proto";
|
import "google/protobuf/timestamp.proto";
|
||||||
|
|
||||||
option csharp_namespace = "DysonNetwork.Sphere.Account.Proto";
|
option csharp_namespace = "DysonNetwork.Shared.Protos.Account";
|
||||||
|
|
||||||
service AccountService {
|
service AccountService {
|
||||||
rpc GetAccount(google.protobuf.Empty) returns (AccountResponse);
|
rpc GetAccount(google.protobuf.Empty) returns (AccountResponse);
|
||||||
|
@ -5,7 +5,7 @@ package dyson_network.sphere.auth;
|
|||||||
import "google/protobuf/empty.proto";
|
import "google/protobuf/empty.proto";
|
||||||
import "google/protobuf/timestamp.proto";
|
import "google/protobuf/timestamp.proto";
|
||||||
|
|
||||||
option csharp_namespace = "DysonNetwork.Sphere.Auth.Proto";
|
option csharp_namespace = "DysonNetwork.Shared.Protos.Auth";
|
||||||
|
|
||||||
service AuthService {
|
service AuthService {
|
||||||
rpc Login(LoginRequest) returns (LoginResponse);
|
rpc Login(LoginRequest) returns (LoginResponse);
|
||||||
|
@ -6,33 +6,21 @@ using Microsoft.EntityFrameworkCore;
|
|||||||
using NodaTime;
|
using NodaTime;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using DysonNetwork.Shared.Models;
|
using DysonNetwork.Shared.Models;
|
||||||
using Challenge = DysonNetwork.Sphere.Auth.Proto.Challenge;
|
|
||||||
using Session = DysonNetwork.Sphere.Auth.Proto.Session;
|
|
||||||
|
|
||||||
namespace DysonNetwork.Sphere.Auth;
|
namespace DysonNetwork.Sphere.Auth;
|
||||||
|
|
||||||
public class AuthGrpcService : DysonNetwork.Sphere.Auth.Proto.AuthService.AuthServiceBase
|
public class AuthGrpcService(AppDatabase db, AccountService accounts, AuthService auth)
|
||||||
|
: DysonNetwork.Sphere.Auth.Proto.AuthService.AuthServiceBase
|
||||||
{
|
{
|
||||||
private readonly AppDatabase _db;
|
|
||||||
private readonly AccountService _accounts;
|
|
||||||
private readonly AuthService _auth;
|
|
||||||
|
|
||||||
public AuthGrpcService(AppDatabase db, AccountService accounts, AuthService auth)
|
|
||||||
{
|
|
||||||
_db = db;
|
|
||||||
_accounts = accounts;
|
|
||||||
_auth = auth;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override async Task<LoginResponse> Login(LoginRequest request, ServerCallContext context)
|
public override async Task<LoginResponse> Login(LoginRequest request, ServerCallContext context)
|
||||||
{
|
{
|
||||||
var account = await _accounts.LookupAccount(request.Username);
|
var account = await accounts.LookupAccount(request.Username);
|
||||||
if (account == null)
|
if (account == null)
|
||||||
{
|
{
|
||||||
throw new RpcException(new Grpc.Core.Status(StatusCode.NotFound, "Account not found."));
|
throw new RpcException(new Grpc.Core.Status(StatusCode.NotFound, "Account not found."));
|
||||||
}
|
}
|
||||||
|
|
||||||
var factor = await _db.AccountAuthFactors.FirstOrDefaultAsync(f => f.AccountId == account.Id && f.Type == AccountAuthFactorType.Password);
|
var factor = await db.AccountAuthFactors.FirstOrDefaultAsync(f => f.AccountId == account.Id && f.Type == AccountAuthFactorType.Password);
|
||||||
if (factor == null || !factor.VerifyPassword(request.Password))
|
if (factor == null || !factor.VerifyPassword(request.Password))
|
||||||
{
|
{
|
||||||
throw new RpcException(new Grpc.Core.Status(StatusCode.Unauthenticated, "Invalid credentials."));
|
throw new RpcException(new Grpc.Core.Status(StatusCode.Unauthenticated, "Invalid credentials."));
|
||||||
@ -46,10 +34,10 @@ public class AuthGrpcService : DysonNetwork.Sphere.Auth.Proto.AuthService.AuthSe
|
|||||||
Challenge = new Challenge() // Create a dummy challenge
|
Challenge = new Challenge() // Create a dummy challenge
|
||||||
};
|
};
|
||||||
|
|
||||||
_db.AuthSessions.Add(session);
|
db.AuthSessions.Add(session);
|
||||||
await _db.SaveChangesAsync();
|
await db.SaveChangesAsync();
|
||||||
|
|
||||||
var token = _auth.CreateToken(session);
|
var token = auth.CreateToken(session);
|
||||||
|
|
||||||
return new LoginResponse
|
return new LoginResponse
|
||||||
{
|
{
|
||||||
@ -60,9 +48,9 @@ public class AuthGrpcService : DysonNetwork.Sphere.Auth.Proto.AuthService.AuthSe
|
|||||||
|
|
||||||
public override async Task<IntrospectionResponse> IntrospectToken(IntrospectTokenRequest request, ServerCallContext context)
|
public override async Task<IntrospectionResponse> IntrospectToken(IntrospectTokenRequest request, ServerCallContext context)
|
||||||
{
|
{
|
||||||
if (_auth.ValidateToken(request.Token, out var sessionId))
|
if (auth.ValidateToken(request.Token, out var sessionId))
|
||||||
{
|
{
|
||||||
var session = await _db.AuthSessions
|
var session = await db.AuthSessions
|
||||||
.Include(s => s.Account)
|
.Include(s => s.Account)
|
||||||
.Include(s => s.Challenge)
|
.Include(s => s.Challenge)
|
||||||
.FirstOrDefaultAsync(s => s.Id == sessionId);
|
.FirstOrDefaultAsync(s => s.Id == sessionId);
|
||||||
@ -91,13 +79,13 @@ public class AuthGrpcService : DysonNetwork.Sphere.Auth.Proto.AuthService.AuthSe
|
|||||||
if (authorizationHeader != null)
|
if (authorizationHeader != null)
|
||||||
{
|
{
|
||||||
var token = authorizationHeader.Value.Replace("Bearer ", "");
|
var token = authorizationHeader.Value.Replace("Bearer ", "");
|
||||||
if (_auth.ValidateToken(token, out var sessionId))
|
if (auth.ValidateToken(token, out var sessionId))
|
||||||
{
|
{
|
||||||
var session = await _db.AuthSessions.FindAsync(sessionId);
|
var session = await db.AuthSessions.FindAsync(sessionId);
|
||||||
if (session != null)
|
if (session != null)
|
||||||
{
|
{
|
||||||
_db.AuthSessions.Remove(session);
|
db.AuthSessions.Remove(session);
|
||||||
await _db.SaveChangesAsync();
|
await db.SaveChangesAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user