Refactor token handling and add BaseAddress to apps

Replaced LoginCommand and obsolete GetTokensCommand with updated implementations for consistency. Token type enum was updated, enforcing proper access and refresh token validation. Added BaseAddress property to master app, ensuring correct audience and initialization logic improvements.
This commit is contained in:
Christian Werner 2025-05-31 02:16:02 +02:00
parent e6b0e4ab99
commit 4ffc2a135c
24 changed files with 2686 additions and 59 deletions

View File

@ -1,7 +0,0 @@
namespace Suspectus.Gandalf.Core.Abstractions.CQRS.Commands;
public class LoginCommand : ICommand<bool>
{
public required string UsernameOrEmail { get; set; }
public required string Password { get; set; }
}

View File

@ -0,0 +1,13 @@
namespace Suspectus.Gandalf.Core.Abstractions.CQRS.Commands;
public class ValidateCredentialsCommand : ICommand<ValidateCredentialsResponse>
{
public required string UsernameOrEmail { get; set; }
public required string Password { get; set; }
}
public class ValidateCredentialsResponse
{
public required bool IsValid { get; set; }
public required long SubjectId { get; set; }
}

View File

@ -0,0 +1,53 @@
using Suspectus.Gandalf.Core.Abstractions.CQRS;
namespace Suspectus.Gandalf.Core.Abstractions.DTOs.Internal.Auth;
public enum GrandType
{
Password,
RefreshToken,
}
public interface ITokenRequestBase
{
public GrandType GrandType { get; set; }
public string ClientId { get; set; }
}
public interface ITokenRequestWithScope : ITokenRequestBase
{
public string Scope { get; set; }
}
public interface IResourceOwnerPasswordCredentialsTokenRequest : ITokenRequestWithScope
{
public string ClientSecret { get; set; }
public string Username { get; set; }
public string Password { get; set; }
}
public interface IRefreshTokenTokenRequest : ITokenRequestBase
{
public string RefreshToken { get; set; }
}
public class TokenRequestCommand : IResourceOwnerPasswordCredentialsTokenRequest, IRefreshTokenTokenRequest, ICommand<TokenRequestResponse>
{
public required GrandType GrandType { get; set; }
public required string ClientId { get; set; } = string.Empty;
public string Scope { get; set; } = string.Empty;
public string ClientSecret { get; set; } = string.Empty;
public string Username { get; set; } = string.Empty;
public string Password { get; set; } = string.Empty;
public string RefreshToken { get; set; } = string.Empty;
}
public class TokenRequestResponse
{
public required string SubjectId { get; set; }
public required string Scope { get; set; }
public required string AccessToken { get; set; }
public required string RefreshToken { get; set; }
public required DateTimeOffset AccessTokenExpiresAt { get; set; }
public required DateTimeOffset RefreshTokenExpiresAt { get; set; }
}

View File

@ -1,4 +1,5 @@
using LanguageExt.Common;
using Suspectus.Gandalf.Core.Abstractions.DTOs.Internal.Auth;
using Suspectus.Gandalf.Palantir.Data.Dto;
namespace Suspectus.Gandalf.Palantir.Api.Commands;
@ -11,7 +12,8 @@ public class AuthCodeRequestCommand : IGrCommand<Result<AuthCodeDto>>
public required string Algorithm { get; set; }
};
public class GetTokensCommand : IGrCommand<Result<TokenDto>>
[Obsolete("Use TokenRequestCommand instead.")]
public class GetTokensCommand : IGrCommand<Result<TokenRequestResponse>>
{
public required string AuthCode { get; set; }
public required string ProofKey { get; set; }

View File

@ -1,9 +1,13 @@
using LanguageExt.Common;
using Suspectus.Gandalf.Core.Abstractions.CQRS;
using Suspectus.Gandalf.Core.Abstractions.DTOs.Internal.Auth;
using Suspectus.Gandalf.Palantir.Data.Dto;
namespace Suspectus.Gandalf.Palantir.Api.Commands;
public class CreateTokensCommand : IGrCommand<Result<TokenDto>>
public class CreateTokensCommand : ICommand<TokenRequestResponse>
{
public required long SubjectId { get; set; }
public required long ClientId { get; set; }
public required string Scope { get; set; }
}

View File

@ -6,7 +6,7 @@ using Microsoft.EntityFrameworkCore;
using Suspectus.Gandalf.Core.Abstractions.DTOs;
using Suspectus.Gandalf.Palantir.Data.Database;
namespace Suspectus.Gandalf.Palantir.Api.Controllers;
namespace Suspectus.Gandalf.Palantir.Api.Controllers.Internal;
[ApiController]
[Route("api/internal/[controller]")]
@ -33,7 +33,7 @@ public class AppController : ControllerBase
Id = appId,
IsMaster = x.Tenant!.IsMaster,
Name = x.Name,
BaseAddress = x.Tenant!.IsMaster ? "http://localhost:5035/" : "null"
BaseAddress = x.BaseAddress
})
.SingleOrDefaultAsync(cancellationToken: cancellationToken);
return Ok(appInfo);
@ -48,9 +48,26 @@ public class AppController : ControllerBase
Id = _hashids.EncodeLong(x.Id!.Value),
IsMaster = x.Tenant!.IsMaster,
Name = x.Name,
BaseAddress = x.Tenant!.IsMaster ? "http://localhost:5035/" : "null"
BaseAddress = x.BaseAddress
}).ToListAsync(cancellationToken: cancellationToken);
return Ok(appInfos);
}
[HttpGet("master/info")]
[AllowAnonymous]
public async Task<IActionResult> GetMasterInfo(CancellationToken cancellationToken)
{
var appInfo = await _context.Apps
.Where(x => x.Tenant!.IsMaster)
.Select(x => new AppInfo
{
Id = _hashids.EncodeLong(x.Id!.Value),
IsMaster = x.Tenant!.IsMaster,
Name = x.Name,
BaseAddress = x.BaseAddress
})
.SingleOrDefaultAsync(cancellationToken: cancellationToken);
return Ok(appInfo);
}
}

View File

@ -1,14 +1,17 @@
using System.ComponentModel.DataAnnotations;
using MediatR;
using Microsoft.AspNetCore.Mvc;
using Suspectus.Gandalf.Core.Abstractions.CQRS.Commands;
using Suspectus.Gandalf.Core.Abstractions.DTOs.Internal.Auth;
using Suspectus.Gandalf.Palantir.Api.Commands;
using Suspectus.Gandalf.Palantir.Contracts.Controller.Auth;
namespace Suspectus.Gandalf.Palantir.Api.Controllers;
namespace Suspectus.Gandalf.Palantir.Api.Controllers.Internal;
[ApiController]
[Route("api/internal/[controller]")]
public class AuthController(IMediator mediator) : ControllerBase
{
[Obsolete("Unused, will be removed in future versions.")]
[HttpPost("[action]")]
public async Task<IActionResult> Register([FromBody] RegisterCommand registerCommand)
{
@ -16,6 +19,7 @@ public class AuthController(IMediator mediator) : ControllerBase
return result.Match<IActionResult>(Ok, e => BadRequest($"{e.Message}\n{e.InnerException?.Message}"));
}
[Obsolete("Unused, will be removed in future versions.")]
[HttpPost("[action]")]
public async Task<IActionResult> Login([FromBody] AuthCodeRequestCommand authCodeRequestCommand)
{
@ -24,9 +28,9 @@ public class AuthController(IMediator mediator) : ControllerBase
}
[HttpPost("[action]")]
public async Task<IActionResult> Token([FromBody] GetTokensCommand getTokensCommand)
public async Task<IActionResult> Token([FromBody][Required] TokenRequestCommand tokenRequestCommand)
{
var result = await mediator.Send(getTokensCommand);
var result = await mediator.Send(tokenRequestCommand);
return result.Match<IActionResult>(Ok, e => BadRequest($"{e.Message}\n{e.InnerException?.Message}"));
}

View File

@ -2,17 +2,17 @@ using LanguageExt.Common;
using MediatR;
using Microsoft.EntityFrameworkCore;
using Suspectus.Gandalf.Core.Abstractions.CQRS;
using Suspectus.Gandalf.Core.Abstractions.CQRS.Commands;
using Suspectus.Gandalf.Core.Abstractions.Extensions;
using Suspectus.Gandalf.Palantir.Api.Commands;
using Suspectus.Gandalf.Palantir.Contracts.Controller.Auth;
using Suspectus.Gandalf.Palantir.Data.Database;
using Suspectus.Gandalf.Palantir.Data.Entities.Subject.SignIn;
namespace Suspectus.Gandalf.Palantir.Api.Handlers.Commands;
public class ValidateCredentialsCommandHandler(ApplicationContext applicationContext, IMediator mediator) : ICommandHandler<ValidateCredentialsCommand, bool>
public class ValidateCredentialsCommandHandler(ApplicationContext applicationContext, IMediator mediator) : ICommandHandler<ValidateCredentialsCommand, ValidateCredentialsResponse>
{
public async Task<Result<bool>> Handle(ValidateCredentialsCommand request, CancellationToken cancellationToken)
public async Task<Result<ValidateCredentialsResponse>> Handle(ValidateCredentialsCommand request, CancellationToken cancellationToken)
{
var subject = await applicationContext.Subjects
.Include(x => x.SignInMethods)
@ -26,10 +26,21 @@ public class ValidateCredentialsCommandHandler(ApplicationContext applicationCon
), cancellationToken);
if (subject is null)
return "User does not exist.".AsErrorResult<bool>();
return "User does not exist.".AsErrorResult<ValidateCredentialsResponse>();
var signIn = subject.SignInMethods.Single(x => x.Method == SignInMethod.Simple);
return await mediator.Send(new VerifyPasswordCommand { RawPassword = request.Password, SignInId = signIn.Id!.Value }, cancellationToken);
var verifyPasswordResponse= await mediator.Send(new VerifyPasswordCommand { RawPassword = request.Password, SignInId = signIn.Id!.Value }, cancellationToken);
if (verifyPasswordResponse.IsFaulted)
{
return verifyPasswordResponse.AsErrorResult<ValidateCredentialsResponse, bool>();
}
return new ValidateCredentialsResponse
{
IsValid = verifyPasswordResponse.GetValue(),
SubjectId = signIn.SubjectId
};
}
}

View File

@ -3,20 +3,31 @@ using JWT.Algorithms;
using JWT.Builder;
using JWT.Serializers;
using LanguageExt.Common;
using Microsoft.EntityFrameworkCore;
using Suspectus.Gandalf.Core.Abstractions.CQRS;
using Suspectus.Gandalf.Core.Abstractions.DTOs.Internal.Auth;
using Suspectus.Gandalf.Core.Abstractions.Extensions;
using Suspectus.Gandalf.Palantir.Api.Commands;
using Suspectus.Gandalf.Palantir.Api.Handlers.Commands;
using Suspectus.Gandalf.Palantir.Data.Database;
using Suspectus.Gandalf.Palantir.Data.Database.Repositories;
using Suspectus.Gandalf.Palantir.Data.Dto;
using Suspectus.Gandalf.Palantir.Data.Entities.Security;
using Suspectus.Gandalf.Palantir.Security.Scheme;
namespace Suspectus.Gandalf.Palantir.Api.Handlers.Security;
public class CreateTokensCommandHandler(TimeProvider timeProvider, IConfiguration configuration, IHashids hashids, ITokenMetadataRepository tokenMetadataRepository) : IGrCommandHandler<CreateTokensCommand, Result<TokenDto>>
public class CreateTokensCommandHandler(TimeProvider timeProvider, IConfiguration configuration, IHashids hashids, ITokenMetadataRepository tokenMetadataRepository, ApplicationContext context) : ICommandHandler<CreateTokensCommand, TokenRequestResponse>
{
public async Task<Result<TokenDto>> Handle(CreateTokensCommand command, CancellationToken cancellationToken)
public async Task<Result<TokenRequestResponse>> Handle(CreateTokensCommand command, CancellationToken cancellationToken)
{
var subject = await context.Subjects.SingleOrDefaultAsync(x => x.Id == command.SubjectId, cancellationToken: cancellationToken);
var app = await context.Apps.SingleOrDefaultAsync(x => x.Id == command.ClientId, cancellationToken: cancellationToken);
if (subject is null)
return "Subject not found.".AsErrorResult<TokenRequestResponse>();
if (app is null)
return "App not found.".AsErrorResult<TokenRequestResponse>();
var iat = timeProvider.GetUtcNow();
var accessExp = iat.AddMinutes(5);
var refreshExp = iat.AddDays(7);
@ -30,50 +41,59 @@ public class CreateTokensCommandHandler(TimeProvider timeProvider, IConfiguratio
{
Expiration = accessExp,
IsRevoked = false,
TokenType = TokenType.User,
UsedBy = command.SubjectId
TokenType = TokenType.Access,
UsedBy = command.SubjectId,
UsedFor = command.ClientId,
Scope = command.Scope
};
var refreshTokenMetadata = new TokenMetadataEntity
{
Expiration = refreshExp,
IsRevoked = false,
TokenType = TokenType.User,
UsedBy = command.SubjectId
TokenType = TokenType.Refresh,
UsedBy = command.SubjectId,
UsedFor = command.ClientId,
Scope = command.Scope
};
var entities = await tokenMetadataRepository.Upsert([accessTokenMetadata, refreshTokenMetadata]);
if (entities.IsFaulted)
return entities.AsErrorResult<TokenDto, ICollection<TokenMetadataEntity>>();
return entities.AsErrorResult<TokenRequestResponse, ICollection<TokenMetadataEntity>>();
var baseUrl = configuration.GetValue<string>("BaseUrl") ?? "https://localhost:7269";
var aud = $"{app.Name.ToLower()}-api";
var sub = hashids.EncodeLong(command.SubjectId);
var accessToken = builder.Encode(new GandalfRebornJwtBody
{
Id = hashids.EncodeLong(accessTokenMetadata.Id!.Value),
Sub = hashids.EncodeLong(command.SubjectId),
Sub = sub,
Iat = iat,
Exp = accessExp,
Iss = baseUrl,
Aud = baseUrl
Aud = aud
});
var refreshToken = builder.Encode(new GandalfRebornJwtBody
{
Id = hashids.EncodeLong(refreshTokenMetadata.Id!.Value),
Sub = hashids.EncodeLong(command.SubjectId),
Sub = sub,
Iat = iat,
Exp = refreshExp,
Iss = baseUrl,
Aud = baseUrl
Aud = aud
});
return new Result<TokenDto>(new TokenDto
return new TokenRequestResponse
{
SubjectId = sub,
AccessToken = accessToken,
RefreshToken = refreshToken
});
RefreshToken = refreshToken,
Scope = command.Scope,
AccessTokenExpiresAt = accessExp,
RefreshTokenExpiresAt = refreshExp
};
}
}

View File

@ -1,19 +1,30 @@
using System.Security.Cryptography;
using System.Text;
using HashidsNet;
using JWT.Algorithms;
using JWT.Builder;
using JWT.Serializers;
using LanguageExt.Common;
using MediatR;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Options;
using Suspectus.Gandalf.Core.Abstractions.CQRS;
using Suspectus.Gandalf.Core.Abstractions.CQRS.Commands;
using Suspectus.Gandalf.Core.Abstractions.DTOs.Internal.Auth;
using Suspectus.Gandalf.Core.Abstractions.Extensions;
using Suspectus.Gandalf.Palantir.Api.Commands;
using Suspectus.Gandalf.Palantir.Api.Handlers.Commands;
using Suspectus.Gandalf.Palantir.Data.Database;
using Suspectus.Gandalf.Palantir.Data.Database.Repositories;
using Suspectus.Gandalf.Palantir.Data.Dto;
using Suspectus.Gandalf.Palantir.Data.Entities.Security;
using Suspectus.Gandalf.Palantir.Security.Scheme;
namespace Suspectus.Gandalf.Palantir.Api.Handlers.Security;
public class GetTokensCommandHandler(IAuthCodeRepository authCodeRepository, TimeProvider timeProvider, IMediator mediator) : IGrCommandHandler<GetTokensCommand, Result<TokenDto>>
public class GetTokensCommandHandler(IAuthCodeRepository authCodeRepository, TimeProvider timeProvider, IMediator mediator) : IGrCommandHandler<GetTokensCommand, Result<TokenRequestResponse>>
{
public async Task<Result<TokenDto>> Handle(GetTokensCommand command, CancellationToken cancellationToken)
public async Task<Result<TokenRequestResponse>> Handle(GetTokensCommand command, CancellationToken cancellationToken)
{
var authCode = await authCodeRepository
.Query(q => q.Where(x => x.Code == command.AuthCode))
@ -21,7 +32,7 @@ public class GetTokensCommandHandler(IAuthCodeRepository authCodeRepository, Tim
if (authCode is null)
{
return "Auth code could not be found.".AsErrorResult<TokenDto>();
return "Auth code could not be found.".AsErrorResult<TokenRequestResponse>();
}
if (authCode.Expiration <= timeProvider.GetUtcNow())
@ -30,7 +41,7 @@ public class GetTokensCommandHandler(IAuthCodeRepository authCodeRepository, Tim
.Query(q => q.Where(x => x.Id == authCode.Id))
.ExecuteDeleteAsync(cancellationToken);
return "Auth code is expired.".AsErrorResult<TokenDto>();
return "Auth code is expired.".AsErrorResult<TokenRequestResponse>();
}
switch (authCode.Algorithm)
@ -40,17 +51,84 @@ public class GetTokensCommandHandler(IAuthCodeRepository authCodeRepository, Tim
var sha256Bytes = SHA256.HashData(proofKeyBytes);
var hexProofKey = BitConverter.ToString(sha256Bytes).Replace("-", string.Empty).ToLower();
if (authCode.Challenge != hexProofKey)
return "Code challenge failed.".AsErrorResult<TokenDto>();
return "Code challenge failed.".AsErrorResult<TokenRequestResponse>();
break;
default:
return $"Algorithm '{authCode.Algorithm}' not supported".AsErrorResult<TokenDto>();
return $"Algorithm '{authCode.Algorithm}' not supported".AsErrorResult<TokenRequestResponse>();
}
await authCodeRepository
.Query(q => q.Where(x => x.Id == authCode.Id))
.ExecuteDeleteAsync(cancellationToken);
return await mediator.Send(new CreateTokensCommand {SubjectId = authCode.SubjectId}, cancellationToken);
return await mediator.Send(new CreateTokensCommand {SubjectId = authCode.SubjectId, Scope = "null", ClientId = 0}, cancellationToken);
}
}
public class TokenRequestCommandHandler(TimeProvider timeProvider, IHashids hashids, ApplicationContext context, IConfiguration configuration, IMediator mediator) : ICommandHandler<TokenRequestCommand, TokenRequestResponse>
{
public async Task<Result<TokenRequestResponse>> Handle(TokenRequestCommand command, CancellationToken cancellationToken)
{
ValidateCredentialsResponse validateCredentialsResponse;
CreateTokensCommand createTokensCommand;
if (command.GrandType == GrandType.Password)
{
var validateCredentialsResult = await mediator.Send(new ValidateCredentialsCommand
{
UsernameOrEmail = command.Username,
Password = command.Password
}, cancellationToken);
if (validateCredentialsResult.IsFaulted)
{
return validateCredentialsResult.AsErrorResult<TokenRequestResponse, ValidateCredentialsResponse>();
}
validateCredentialsResponse = validateCredentialsResult.GetValue();
if (!validateCredentialsResponse.IsValid)
{
return "Username or password wrong.".AsErrorResult<TokenRequestResponse>();
}
createTokensCommand = new CreateTokensCommand
{
SubjectId = validateCredentialsResponse.SubjectId,
ClientId = hashids.DecodeSingleLong(command.ClientId),
Scope = command.Scope
};
}
else
{
var decodedToken = JwtBuilder.Create()
.WithAlgorithm(new HMACSHA512Algorithm())
.WithSecret(configuration.GetValue<string>("JwtSecret"))
.MustVerifySignature()
.WithJsonSerializer(new JsonNetSerializer())
.Decode<GandalfRebornJwtBody>(command.RefreshToken);
if (decodedToken.Exp is null || decodedToken.Exp?.ToUniversalTime() <= timeProvider.GetUtcNow())
return "One does not simply provide an expired token.".AsErrorResult<TokenRequestResponse>();
var refreshTokenMetadata = await context.TokenMetadata.SingleOrDefaultAsync(x => x.Id == hashids.DecodeSingleLong(decodedToken.Sub), cancellationToken: cancellationToken);
if (refreshTokenMetadata is null)
{
return "Token not found.".AsErrorResult<TokenRequestResponse>();
}
if (refreshTokenMetadata.TokenType != TokenType.Refresh)
return "Token is not a refresh token".AsErrorResult<TokenRequestResponse>();
createTokensCommand = new CreateTokensCommand
{
SubjectId = refreshTokenMetadata.UsedBy,
ClientId = refreshTokenMetadata.UsedFor,
Scope = refreshTokenMetadata.Scope
};
}
return await mediator.Send(createTokensCommand, cancellationToken);
}
}

View File

@ -18,13 +18,15 @@ public class InitService
private readonly ApplicationContext _applicationContext;
private readonly IMediator _mediator;
private readonly IConfiguration _configuration;
private readonly ILogger<InitService> _logger;
private readonly InvokerContext _invokerContext;
public InitService(ApplicationContext applicationContext, IMediator mediator, ILogger<InitService> logger, InvokerContext invokerContext)
public InitService(ApplicationContext applicationContext, IMediator mediator, IConfiguration configuration, ILogger<InitService> logger, InvokerContext invokerContext)
{
_applicationContext = applicationContext;
_mediator = mediator;
_configuration = configuration;
_logger = logger;
_invokerContext = invokerContext;
}
@ -48,10 +50,30 @@ public class InitService
private async Task InitializeMaster()
{
var baseAddress = _configuration.GetValue<string>("BaseUrl") ?? "https://localhost:7269";
var masterTenant = await _applicationContext.Tenants.SingleOrDefaultAsync(x => x.IsMaster);
if (masterTenant is not null)
{
var existingMasterApp = await _applicationContext.Apps
.Include(x => x.Tenant)
.SingleAsync(x => x.TenantId == masterTenant.Id);
if (existingMasterApp.BaseAddress == baseAddress) return;
existingMasterApp.BaseAddress = baseAddress;
_invokerContext.Invoker = new Invoker
{
SubjectId = existingMasterApp.Tenant!.OwnerId,
TenantAuthorityDictionary = new Dictionary<string, HashSet<string>>(),
AppAuthorityDictionary = new Dictionary<string, HashSet<string>>(),
IsAuthenticated = true
};
await _applicationContext.SaveChangesAsync();
return;
}
var masterTenantPassword = GeneratePassword();
var masterTenantPasswordHashResult = await _mediator.Send(new HashPasswordCommand { RawPassword = masterTenantPassword });
@ -103,7 +125,8 @@ public class InitService
{
Visibility = EntityVisibility.Active,
TenantId = masterTenant.Id!.Value,
Name = "Master"
Name = "Master",
BaseAddress = baseAddress
};
_applicationContext.Apps.Add(masterApp);

View File

@ -1,8 +1,9 @@
using System.Net.Http.Json;
using LanguageExt.Common;
using Suspectus.Gandalf.Core.Abstractions.CQRS.Commands;
using Suspectus.Gandalf.Core.Abstractions.DTOs;
using Suspectus.Gandalf.Core.Abstractions.DTOs.Internal.Auth;
using Suspectus.Gandalf.Core.Abstractions.Extensions;
using Suspectus.Gandalf.Palantir.Contracts.Controller.Auth;
namespace Suspectus.Gandalf.Palantir.Client;
@ -36,11 +37,13 @@ public class PalantirInternalClient : IPalantirInternalClient
public interface IPalantirInternalAuthClient
{
public Task<Result<bool>> ValidateCredentials(ValidateCredentialsCommand validateCredentialsCommand);
public Task<Result<TokenRequestResponse?>> Token(TokenRequestCommand command);
}
public interface IPalantirInternalAppClient
{
public Task<Result<AppInfo>> GetInfo(string appId);
public Task<Result<AppInfo>> GetMasterInfo();
}
public class PalantirClient : IPalantirClient
@ -83,6 +86,26 @@ public class PalantirInternalAuthClient : IPalantirInternalAuthClient
return $"status: {e.StatusCode} message: {e.Message}".AsErrorResult<bool>();
}
}
public async Task<Result<TokenRequestResponse?>> Token(TokenRequestCommand command)
{
try
{
var response = await _http.PostAsJsonAsync("token", command);
if (!response.IsSuccessStatusCode)
{
return $"status: {response.StatusCode}".AsErrorResult<TokenRequestResponse?>();
}
var result = await response.Content.ReadFromJsonAsync<TokenRequestResponse?>();
return result;
}
catch (HttpRequestException e)
{
return $"status: {e.StatusCode} message: {e.Message}".AsErrorResult<TokenRequestResponse?>();
}
}
}
public class PalantirInternalAppClient : IPalantirInternalAppClient
@ -119,4 +142,30 @@ public class PalantirInternalAppClient : IPalantirInternalAppClient
return $"status: {e.StatusCode} message: {e.Message}".AsErrorResult<AppInfo>();
}
}
public async Task<Result<AppInfo>> GetMasterInfo()
{
try
{
var response = await _http.GetAsync($"master/info");
if (!response.IsSuccessStatusCode)
{
return $"status: {response.StatusCode}".AsErrorResult<AppInfo>();
}
var result = await response.Content.ReadFromJsonAsync<AppInfo>();
if (result is null)
{
return "InternalApp not found.".AsErrorResult<AppInfo>();
}
return result;
}
catch (HttpRequestException e)
{
return $"status: {e.StatusCode} message: {e.Message}".AsErrorResult<AppInfo>();
}
}
}

View File

@ -1,5 +0,0 @@
using Suspectus.Gandalf.Core.Abstractions.CQRS.Commands;
namespace Suspectus.Gandalf.Palantir.Contracts.Controller.Auth;
public class ValidateCredentialsCommand: LoginCommand;

View File

@ -23,4 +23,5 @@ public class AppVersionEntity : AppData, IVersionEntity<AppEntity>
public abstract class AppData : TenantRelationData
{
public required string Name { get; set; }
public required string BaseAddress { get; set; }
}

View File

@ -8,4 +8,6 @@ public class TokenMetadataEntity : IdData
public required bool IsRevoked { get; set; }
public required TokenType TokenType { get; set; }
public required long UsedBy { get; set; }
public required long UsedFor { get; set; }
public required string Scope { get; set; }
}

View File

@ -2,6 +2,6 @@ namespace Suspectus.Gandalf.Palantir.Data.Entities.Security;
public enum TokenType
{
Application,
User
Access,
Refresh
}

View File

@ -0,0 +1,731 @@
// <auto-generated />
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
using Suspectus.Gandalf.Palantir.Data.Database;
#nullable disable
namespace W542.GandalfReborn.Data.Migrations
{
[DbContext(typeof(ApplicationContext))]
[Migration("20250530220220_updateTokenMetadata")]
partial class updateTokenMetadata
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "9.0.2")
.HasAnnotation("Relational:MaxIdentifierLength", 63);
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
modelBuilder.Entity("Suspectus.Gandalf.Palantir.Data.Entities.App.AppEntity", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
b.Property<string>("Name")
.IsRequired()
.HasColumnType("text");
b.Property<long>("TenantId")
.HasColumnType("bigint");
b.Property<string>("Visibility")
.IsRequired()
.HasColumnType("text");
b.HasKey("Id");
b.HasIndex("TenantId");
b.ToTable("App", "gr");
});
modelBuilder.Entity("Suspectus.Gandalf.Palantir.Data.Entities.App.AppSubjectRelationEntity", b =>
{
b.Property<long>("SubjectId")
.HasColumnType("bigint");
b.Property<long>("AppId")
.HasColumnType("bigint");
b.HasKey("SubjectId", "AppId");
b.HasIndex("AppId");
b.ToTable("AppSubjectRelation", "gr");
});
modelBuilder.Entity("Suspectus.Gandalf.Palantir.Data.Entities.App.AppSubjectRelationVersionEntity", b =>
{
b.Property<long>("SubjectId")
.HasColumnType("bigint");
b.Property<long>("AppId")
.HasColumnType("bigint");
b.Property<DateTimeOffset>("At")
.HasColumnType("timestamp with time zone");
b.Property<string>("Action")
.IsRequired()
.HasColumnType("text");
b.Property<long>("SuspectId")
.HasColumnType("bigint");
b.HasKey("SubjectId", "AppId", "At");
b.HasIndex("SuspectId");
b.ToTable("AppSubjectRelationVersion", "gr");
});
modelBuilder.Entity("Suspectus.Gandalf.Palantir.Data.Entities.App.AppVersionEntity", b =>
{
b.Property<long>("Id")
.HasColumnType("bigint");
b.Property<DateTimeOffset>("At")
.HasColumnType("timestamp with time zone");
b.Property<string>("Action")
.IsRequired()
.HasColumnType("text");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("text");
b.Property<long>("SuspectId")
.HasColumnType("bigint");
b.Property<long>("TenantId")
.HasColumnType("bigint");
b.Property<string>("Visibility")
.IsRequired()
.HasColumnType("text");
b.HasKey("Id", "At");
b.HasIndex("SuspectId");
b.ToTable("AppVersion", "gr");
});
modelBuilder.Entity("Suspectus.Gandalf.Palantir.Data.Entities.Security.AppSubjectRelationInternalAuthorityRelationEntity", b =>
{
b.Property<long>("SubjectId")
.HasColumnType("bigint");
b.Property<long>("AppId")
.HasColumnType("bigint");
b.Property<long>("InternalAuthorityId")
.HasColumnType("bigint");
b.HasKey("SubjectId", "AppId", "InternalAuthorityId");
b.HasIndex("InternalAuthorityId");
b.ToTable("AppSubjectRelationInternalAuthorityRelation", "gr");
});
modelBuilder.Entity("Suspectus.Gandalf.Palantir.Data.Entities.Security.AppSubjectRelationInternalAuthorityRelationVersionEntity", b =>
{
b.Property<long>("SubjectId")
.HasColumnType("bigint");
b.Property<long>("AppId")
.HasColumnType("bigint");
b.Property<long>("InternalAuthorityId")
.HasColumnType("bigint");
b.Property<DateTimeOffset>("At")
.HasColumnType("timestamp with time zone");
b.Property<string>("Action")
.IsRequired()
.HasColumnType("text");
b.Property<long>("SuspectId")
.HasColumnType("bigint");
b.HasKey("SubjectId", "AppId", "InternalAuthorityId", "At");
b.HasIndex("SuspectId");
b.ToTable("AppSubjectRelationInternalAuthorityRelationVersion", "gr");
});
modelBuilder.Entity("Suspectus.Gandalf.Palantir.Data.Entities.Security.AuthCodeEntity", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
b.Property<string>("Algorithm")
.IsRequired()
.HasColumnType("text");
b.Property<string>("Challenge")
.IsRequired()
.HasColumnType("text");
b.Property<string>("Code")
.IsRequired()
.HasColumnType("text");
b.Property<DateTimeOffset>("Expiration")
.HasColumnType("timestamp with time zone");
b.Property<bool>("IsRevoked")
.HasColumnType("boolean");
b.Property<long>("SubjectId")
.HasColumnType("bigint");
b.HasKey("Id");
b.ToTable("AuthCode", "gr");
});
modelBuilder.Entity("Suspectus.Gandalf.Palantir.Data.Entities.Security.AuthorityEntity", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
b.Property<string>("Description")
.HasColumnType("text");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("text");
b.Property<string>("Type")
.IsRequired()
.HasColumnType("text");
b.HasKey("Id");
b.HasIndex("Name")
.IsUnique();
b.ToTable("Authority", "gr");
b.HasData(
new
{
Id = 1L,
Description = "Allows users to read tenants",
Name = "Tenant_Read",
Type = "Tenant"
},
new
{
Id = 2L,
Description = "Allows users to read apps",
Name = "App_Read",
Type = "App"
});
});
modelBuilder.Entity("Suspectus.Gandalf.Palantir.Data.Entities.Security.TenantSubjectRelationInternalAuthorityRelationEntity", b =>
{
b.Property<long>("SubjectId")
.HasColumnType("bigint");
b.Property<long>("TenantId")
.HasColumnType("bigint");
b.Property<long>("InternalAuthorityId")
.HasColumnType("bigint");
b.HasKey("SubjectId", "TenantId", "InternalAuthorityId");
b.HasIndex("InternalAuthorityId");
b.ToTable("TenantSubjectRelationInternalAuthorityRelation", "gr");
});
modelBuilder.Entity("Suspectus.Gandalf.Palantir.Data.Entities.Security.TenantSubjectRelationInternalAuthorityRelationVersionEntity", b =>
{
b.Property<long>("SubjectId")
.HasColumnType("bigint");
b.Property<long>("TenantId")
.HasColumnType("bigint");
b.Property<long>("InternalAuthorityId")
.HasColumnType("bigint");
b.Property<DateTimeOffset>("At")
.HasColumnType("timestamp with time zone");
b.Property<string>("Action")
.IsRequired()
.HasColumnType("text");
b.Property<long>("SuspectId")
.HasColumnType("bigint");
b.HasKey("SubjectId", "TenantId", "InternalAuthorityId", "At");
b.HasIndex("SuspectId");
b.ToTable("TenantSubjectRelationInternalAuthorityRelationVersion", "gr");
});
modelBuilder.Entity("Suspectus.Gandalf.Palantir.Data.Entities.Security.TokenMetadataEntity", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
b.Property<DateTimeOffset>("Expiration")
.HasColumnType("timestamp with time zone");
b.Property<bool>("IsRevoked")
.HasColumnType("boolean");
b.Property<string>("Scope")
.IsRequired()
.HasColumnType("text");
b.Property<string>("TokenType")
.IsRequired()
.HasColumnType("text");
b.Property<long>("UsedBy")
.HasColumnType("bigint");
b.Property<long>("UsedFor")
.HasColumnType("bigint");
b.HasKey("Id");
b.ToTable("TokenMetadata", "gr");
});
modelBuilder.Entity("Suspectus.Gandalf.Palantir.Data.Entities.Subject.SignIn.SignInEntity", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
b.Property<string>("Email")
.HasColumnType("text");
b.Property<bool>("IsLegacy")
.HasColumnType("boolean");
b.Property<string>("Method")
.IsRequired()
.HasColumnType("text");
b.Property<string>("PasswordHash")
.HasColumnType("text");
b.Property<long>("SubjectId")
.HasColumnType("bigint");
b.Property<string>("Visibility")
.IsRequired()
.HasColumnType("text");
b.HasKey("Id");
b.HasIndex("Email")
.IsUnique();
b.HasIndex("SubjectId");
b.ToTable("SignIn", "gr");
});
modelBuilder.Entity("Suspectus.Gandalf.Palantir.Data.Entities.Subject.SubjectEntity", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
b.Property<string>("Name")
.IsRequired()
.HasColumnType("text");
b.Property<string>("Visibility")
.IsRequired()
.HasColumnType("text");
b.HasKey("Id");
b.HasIndex("Name")
.IsUnique();
b.ToTable("Subject", "gr");
});
modelBuilder.Entity("Suspectus.Gandalf.Palantir.Data.Entities.Tenant.TenantEntity", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
b.Property<bool>("IsMaster")
.HasColumnType("boolean");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("text");
b.Property<long>("OwnerId")
.HasColumnType("bigint");
b.Property<string>("Visibility")
.IsRequired()
.HasColumnType("text");
b.HasKey("Id");
b.HasIndex("OwnerId");
b.ToTable("Tenant", "gr");
});
modelBuilder.Entity("Suspectus.Gandalf.Palantir.Data.Entities.Tenant.TenantSubjectRelationEntity", b =>
{
b.Property<long>("SubjectId")
.HasColumnType("bigint");
b.Property<long>("TenantId")
.HasColumnType("bigint");
b.HasKey("SubjectId", "TenantId");
b.HasIndex("TenantId");
b.ToTable("TenantSubjectRelation", "gr");
});
modelBuilder.Entity("Suspectus.Gandalf.Palantir.Data.Entities.Tenant.TenantSubjectRelationVersionEntity", b =>
{
b.Property<long>("SubjectId")
.HasColumnType("bigint");
b.Property<long>("TenantId")
.HasColumnType("bigint");
b.Property<DateTimeOffset>("At")
.HasColumnType("timestamp with time zone");
b.Property<string>("Action")
.IsRequired()
.HasColumnType("text");
b.Property<long>("SuspectId")
.HasColumnType("bigint");
b.HasKey("SubjectId", "TenantId", "At");
b.HasIndex("SuspectId");
b.ToTable("TenantSubjectRelationVersion", "gr");
});
modelBuilder.Entity("Suspectus.Gandalf.Palantir.Data.Entities.Tenant.TenantVersionEntity", b =>
{
b.Property<long>("Id")
.HasColumnType("bigint");
b.Property<DateTimeOffset>("At")
.HasColumnType("timestamp with time zone");
b.Property<string>("Action")
.IsRequired()
.HasColumnType("text");
b.Property<bool>("IsMaster")
.HasColumnType("boolean");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("text");
b.Property<long>("OwnerId")
.HasColumnType("bigint");
b.Property<long>("SuspectId")
.HasColumnType("bigint");
b.Property<string>("Visibility")
.IsRequired()
.HasColumnType("text");
b.HasKey("Id", "At");
b.HasIndex("SuspectId");
b.ToTable("TenantVersion", "gr");
});
modelBuilder.Entity("Suspectus.Gandalf.Palantir.Data.Entities.App.AppEntity", b =>
{
b.HasOne("Suspectus.Gandalf.Palantir.Data.Entities.Tenant.TenantEntity", "Tenant")
.WithMany("Apps")
.HasForeignKey("TenantId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Tenant");
});
modelBuilder.Entity("Suspectus.Gandalf.Palantir.Data.Entities.App.AppSubjectRelationEntity", b =>
{
b.HasOne("Suspectus.Gandalf.Palantir.Data.Entities.App.AppEntity", "App")
.WithMany()
.HasForeignKey("AppId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Suspectus.Gandalf.Palantir.Data.Entities.Subject.SubjectEntity", "Subject")
.WithMany()
.HasForeignKey("SubjectId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("App");
b.Navigation("Subject");
});
modelBuilder.Entity("Suspectus.Gandalf.Palantir.Data.Entities.App.AppSubjectRelationVersionEntity", b =>
{
b.HasOne("Suspectus.Gandalf.Palantir.Data.Entities.Subject.SubjectEntity", "Suspect")
.WithMany()
.HasForeignKey("SuspectId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Suspectus.Gandalf.Palantir.Data.Entities.App.AppSubjectRelationEntity", "Reference")
.WithMany()
.HasForeignKey("SubjectId", "AppId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Reference");
b.Navigation("Suspect");
});
modelBuilder.Entity("Suspectus.Gandalf.Palantir.Data.Entities.App.AppVersionEntity", b =>
{
b.HasOne("Suspectus.Gandalf.Palantir.Data.Entities.App.AppEntity", "Reference")
.WithMany()
.HasForeignKey("Id")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Suspectus.Gandalf.Palantir.Data.Entities.Subject.SubjectEntity", "Suspect")
.WithMany()
.HasForeignKey("SuspectId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Reference");
b.Navigation("Suspect");
});
modelBuilder.Entity("Suspectus.Gandalf.Palantir.Data.Entities.Security.AppSubjectRelationInternalAuthorityRelationEntity", b =>
{
b.HasOne("Suspectus.Gandalf.Palantir.Data.Entities.Security.AuthorityEntity", "InternalAuthority")
.WithMany()
.HasForeignKey("InternalAuthorityId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Suspectus.Gandalf.Palantir.Data.Entities.App.AppSubjectRelationEntity", "AppSubjectRelation")
.WithMany()
.HasForeignKey("SubjectId", "AppId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("AppSubjectRelation");
b.Navigation("InternalAuthority");
});
modelBuilder.Entity("Suspectus.Gandalf.Palantir.Data.Entities.Security.AppSubjectRelationInternalAuthorityRelationVersionEntity", b =>
{
b.HasOne("Suspectus.Gandalf.Palantir.Data.Entities.Subject.SubjectEntity", "Suspect")
.WithMany()
.HasForeignKey("SuspectId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Suspectus.Gandalf.Palantir.Data.Entities.Security.AppSubjectRelationInternalAuthorityRelationEntity", "Reference")
.WithMany()
.HasForeignKey("SubjectId", "AppId", "InternalAuthorityId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Reference");
b.Navigation("Suspect");
});
modelBuilder.Entity("Suspectus.Gandalf.Palantir.Data.Entities.Security.TenantSubjectRelationInternalAuthorityRelationEntity", b =>
{
b.HasOne("Suspectus.Gandalf.Palantir.Data.Entities.Security.AuthorityEntity", "InternalAuthority")
.WithMany()
.HasForeignKey("InternalAuthorityId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Suspectus.Gandalf.Palantir.Data.Entities.Tenant.TenantSubjectRelationEntity", "TenantSubjectRelation")
.WithMany()
.HasForeignKey("SubjectId", "TenantId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("InternalAuthority");
b.Navigation("TenantSubjectRelation");
});
modelBuilder.Entity("Suspectus.Gandalf.Palantir.Data.Entities.Security.TenantSubjectRelationInternalAuthorityRelationVersionEntity", b =>
{
b.HasOne("Suspectus.Gandalf.Palantir.Data.Entities.Subject.SubjectEntity", "Suspect")
.WithMany()
.HasForeignKey("SuspectId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Suspectus.Gandalf.Palantir.Data.Entities.Security.TenantSubjectRelationInternalAuthorityRelationEntity", "Reference")
.WithMany()
.HasForeignKey("SubjectId", "TenantId", "InternalAuthorityId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Reference");
b.Navigation("Suspect");
});
modelBuilder.Entity("Suspectus.Gandalf.Palantir.Data.Entities.Subject.SignIn.SignInEntity", b =>
{
b.HasOne("Suspectus.Gandalf.Palantir.Data.Entities.Subject.SubjectEntity", "Subject")
.WithMany("SignInMethods")
.HasForeignKey("SubjectId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Subject");
});
modelBuilder.Entity("Suspectus.Gandalf.Palantir.Data.Entities.Tenant.TenantEntity", b =>
{
b.HasOne("Suspectus.Gandalf.Palantir.Data.Entities.Subject.SubjectEntity", "Owner")
.WithMany()
.HasForeignKey("OwnerId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Owner");
});
modelBuilder.Entity("Suspectus.Gandalf.Palantir.Data.Entities.Tenant.TenantSubjectRelationEntity", b =>
{
b.HasOne("Suspectus.Gandalf.Palantir.Data.Entities.Subject.SubjectEntity", "Subject")
.WithMany()
.HasForeignKey("SubjectId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Suspectus.Gandalf.Palantir.Data.Entities.Tenant.TenantEntity", "Tenant")
.WithMany()
.HasForeignKey("TenantId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Subject");
b.Navigation("Tenant");
});
modelBuilder.Entity("Suspectus.Gandalf.Palantir.Data.Entities.Tenant.TenantSubjectRelationVersionEntity", b =>
{
b.HasOne("Suspectus.Gandalf.Palantir.Data.Entities.Subject.SubjectEntity", "Suspect")
.WithMany()
.HasForeignKey("SuspectId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Suspectus.Gandalf.Palantir.Data.Entities.Tenant.TenantSubjectRelationEntity", "Reference")
.WithMany()
.HasForeignKey("SubjectId", "TenantId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Reference");
b.Navigation("Suspect");
});
modelBuilder.Entity("Suspectus.Gandalf.Palantir.Data.Entities.Tenant.TenantVersionEntity", b =>
{
b.HasOne("Suspectus.Gandalf.Palantir.Data.Entities.Tenant.TenantEntity", "Reference")
.WithMany()
.HasForeignKey("Id")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Suspectus.Gandalf.Palantir.Data.Entities.Subject.SubjectEntity", "Suspect")
.WithMany()
.HasForeignKey("SuspectId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Reference");
b.Navigation("Suspect");
});
modelBuilder.Entity("Suspectus.Gandalf.Palantir.Data.Entities.Subject.SubjectEntity", b =>
{
b.Navigation("SignInMethods");
});
modelBuilder.Entity("Suspectus.Gandalf.Palantir.Data.Entities.Tenant.TenantEntity", b =>
{
b.Navigation("Apps");
});
#pragma warning restore 612, 618
}
}
}

View File

@ -0,0 +1,44 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace W542.GandalfReborn.Data.Migrations
{
/// <inheritdoc />
public partial class updateTokenMetadata : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<string>(
name: "Scope",
schema: "gr",
table: "TokenMetadata",
type: "text",
nullable: false,
defaultValue: "");
migrationBuilder.AddColumn<long>(
name: "UsedFor",
schema: "gr",
table: "TokenMetadata",
type: "bigint",
nullable: false,
defaultValue: 0L);
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "Scope",
schema: "gr",
table: "TokenMetadata");
migrationBuilder.DropColumn(
name: "UsedFor",
schema: "gr",
table: "TokenMetadata");
}
}
}

View File

@ -0,0 +1,739 @@
// <auto-generated />
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
using Suspectus.Gandalf.Palantir.Data.Database;
#nullable disable
namespace W542.GandalfReborn.Data.Migrations
{
[DbContext(typeof(ApplicationContext))]
[Migration("20250530223053_addBaseAddress")]
partial class addBaseAddress
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "9.0.2")
.HasAnnotation("Relational:MaxIdentifierLength", 63);
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
modelBuilder.Entity("Suspectus.Gandalf.Palantir.Data.Entities.App.AppEntity", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
b.Property<string>("BaseAddress")
.IsRequired()
.HasColumnType("text");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("text");
b.Property<long>("TenantId")
.HasColumnType("bigint");
b.Property<string>("Visibility")
.IsRequired()
.HasColumnType("text");
b.HasKey("Id");
b.HasIndex("TenantId");
b.ToTable("App", "gr");
});
modelBuilder.Entity("Suspectus.Gandalf.Palantir.Data.Entities.App.AppSubjectRelationEntity", b =>
{
b.Property<long>("SubjectId")
.HasColumnType("bigint");
b.Property<long>("AppId")
.HasColumnType("bigint");
b.HasKey("SubjectId", "AppId");
b.HasIndex("AppId");
b.ToTable("AppSubjectRelation", "gr");
});
modelBuilder.Entity("Suspectus.Gandalf.Palantir.Data.Entities.App.AppSubjectRelationVersionEntity", b =>
{
b.Property<long>("SubjectId")
.HasColumnType("bigint");
b.Property<long>("AppId")
.HasColumnType("bigint");
b.Property<DateTimeOffset>("At")
.HasColumnType("timestamp with time zone");
b.Property<string>("Action")
.IsRequired()
.HasColumnType("text");
b.Property<long>("SuspectId")
.HasColumnType("bigint");
b.HasKey("SubjectId", "AppId", "At");
b.HasIndex("SuspectId");
b.ToTable("AppSubjectRelationVersion", "gr");
});
modelBuilder.Entity("Suspectus.Gandalf.Palantir.Data.Entities.App.AppVersionEntity", b =>
{
b.Property<long>("Id")
.HasColumnType("bigint");
b.Property<DateTimeOffset>("At")
.HasColumnType("timestamp with time zone");
b.Property<string>("Action")
.IsRequired()
.HasColumnType("text");
b.Property<string>("BaseAddress")
.IsRequired()
.HasColumnType("text");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("text");
b.Property<long>("SuspectId")
.HasColumnType("bigint");
b.Property<long>("TenantId")
.HasColumnType("bigint");
b.Property<string>("Visibility")
.IsRequired()
.HasColumnType("text");
b.HasKey("Id", "At");
b.HasIndex("SuspectId");
b.ToTable("AppVersion", "gr");
});
modelBuilder.Entity("Suspectus.Gandalf.Palantir.Data.Entities.Security.AppSubjectRelationInternalAuthorityRelationEntity", b =>
{
b.Property<long>("SubjectId")
.HasColumnType("bigint");
b.Property<long>("AppId")
.HasColumnType("bigint");
b.Property<long>("InternalAuthorityId")
.HasColumnType("bigint");
b.HasKey("SubjectId", "AppId", "InternalAuthorityId");
b.HasIndex("InternalAuthorityId");
b.ToTable("AppSubjectRelationInternalAuthorityRelation", "gr");
});
modelBuilder.Entity("Suspectus.Gandalf.Palantir.Data.Entities.Security.AppSubjectRelationInternalAuthorityRelationVersionEntity", b =>
{
b.Property<long>("SubjectId")
.HasColumnType("bigint");
b.Property<long>("AppId")
.HasColumnType("bigint");
b.Property<long>("InternalAuthorityId")
.HasColumnType("bigint");
b.Property<DateTimeOffset>("At")
.HasColumnType("timestamp with time zone");
b.Property<string>("Action")
.IsRequired()
.HasColumnType("text");
b.Property<long>("SuspectId")
.HasColumnType("bigint");
b.HasKey("SubjectId", "AppId", "InternalAuthorityId", "At");
b.HasIndex("SuspectId");
b.ToTable("AppSubjectRelationInternalAuthorityRelationVersion", "gr");
});
modelBuilder.Entity("Suspectus.Gandalf.Palantir.Data.Entities.Security.AuthCodeEntity", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
b.Property<string>("Algorithm")
.IsRequired()
.HasColumnType("text");
b.Property<string>("Challenge")
.IsRequired()
.HasColumnType("text");
b.Property<string>("Code")
.IsRequired()
.HasColumnType("text");
b.Property<DateTimeOffset>("Expiration")
.HasColumnType("timestamp with time zone");
b.Property<bool>("IsRevoked")
.HasColumnType("boolean");
b.Property<long>("SubjectId")
.HasColumnType("bigint");
b.HasKey("Id");
b.ToTable("AuthCode", "gr");
});
modelBuilder.Entity("Suspectus.Gandalf.Palantir.Data.Entities.Security.AuthorityEntity", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
b.Property<string>("Description")
.HasColumnType("text");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("text");
b.Property<string>("Type")
.IsRequired()
.HasColumnType("text");
b.HasKey("Id");
b.HasIndex("Name")
.IsUnique();
b.ToTable("Authority", "gr");
b.HasData(
new
{
Id = 1L,
Description = "Allows users to read tenants",
Name = "Tenant_Read",
Type = "Tenant"
},
new
{
Id = 2L,
Description = "Allows users to read apps",
Name = "App_Read",
Type = "App"
});
});
modelBuilder.Entity("Suspectus.Gandalf.Palantir.Data.Entities.Security.TenantSubjectRelationInternalAuthorityRelationEntity", b =>
{
b.Property<long>("SubjectId")
.HasColumnType("bigint");
b.Property<long>("TenantId")
.HasColumnType("bigint");
b.Property<long>("InternalAuthorityId")
.HasColumnType("bigint");
b.HasKey("SubjectId", "TenantId", "InternalAuthorityId");
b.HasIndex("InternalAuthorityId");
b.ToTable("TenantSubjectRelationInternalAuthorityRelation", "gr");
});
modelBuilder.Entity("Suspectus.Gandalf.Palantir.Data.Entities.Security.TenantSubjectRelationInternalAuthorityRelationVersionEntity", b =>
{
b.Property<long>("SubjectId")
.HasColumnType("bigint");
b.Property<long>("TenantId")
.HasColumnType("bigint");
b.Property<long>("InternalAuthorityId")
.HasColumnType("bigint");
b.Property<DateTimeOffset>("At")
.HasColumnType("timestamp with time zone");
b.Property<string>("Action")
.IsRequired()
.HasColumnType("text");
b.Property<long>("SuspectId")
.HasColumnType("bigint");
b.HasKey("SubjectId", "TenantId", "InternalAuthorityId", "At");
b.HasIndex("SuspectId");
b.ToTable("TenantSubjectRelationInternalAuthorityRelationVersion", "gr");
});
modelBuilder.Entity("Suspectus.Gandalf.Palantir.Data.Entities.Security.TokenMetadataEntity", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
b.Property<DateTimeOffset>("Expiration")
.HasColumnType("timestamp with time zone");
b.Property<bool>("IsRevoked")
.HasColumnType("boolean");
b.Property<string>("Scope")
.IsRequired()
.HasColumnType("text");
b.Property<string>("TokenType")
.IsRequired()
.HasColumnType("text");
b.Property<long>("UsedBy")
.HasColumnType("bigint");
b.Property<long>("UsedFor")
.HasColumnType("bigint");
b.HasKey("Id");
b.ToTable("TokenMetadata", "gr");
});
modelBuilder.Entity("Suspectus.Gandalf.Palantir.Data.Entities.Subject.SignIn.SignInEntity", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
b.Property<string>("Email")
.HasColumnType("text");
b.Property<bool>("IsLegacy")
.HasColumnType("boolean");
b.Property<string>("Method")
.IsRequired()
.HasColumnType("text");
b.Property<string>("PasswordHash")
.HasColumnType("text");
b.Property<long>("SubjectId")
.HasColumnType("bigint");
b.Property<string>("Visibility")
.IsRequired()
.HasColumnType("text");
b.HasKey("Id");
b.HasIndex("Email")
.IsUnique();
b.HasIndex("SubjectId");
b.ToTable("SignIn", "gr");
});
modelBuilder.Entity("Suspectus.Gandalf.Palantir.Data.Entities.Subject.SubjectEntity", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
b.Property<string>("Name")
.IsRequired()
.HasColumnType("text");
b.Property<string>("Visibility")
.IsRequired()
.HasColumnType("text");
b.HasKey("Id");
b.HasIndex("Name")
.IsUnique();
b.ToTable("Subject", "gr");
});
modelBuilder.Entity("Suspectus.Gandalf.Palantir.Data.Entities.Tenant.TenantEntity", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
b.Property<bool>("IsMaster")
.HasColumnType("boolean");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("text");
b.Property<long>("OwnerId")
.HasColumnType("bigint");
b.Property<string>("Visibility")
.IsRequired()
.HasColumnType("text");
b.HasKey("Id");
b.HasIndex("OwnerId");
b.ToTable("Tenant", "gr");
});
modelBuilder.Entity("Suspectus.Gandalf.Palantir.Data.Entities.Tenant.TenantSubjectRelationEntity", b =>
{
b.Property<long>("SubjectId")
.HasColumnType("bigint");
b.Property<long>("TenantId")
.HasColumnType("bigint");
b.HasKey("SubjectId", "TenantId");
b.HasIndex("TenantId");
b.ToTable("TenantSubjectRelation", "gr");
});
modelBuilder.Entity("Suspectus.Gandalf.Palantir.Data.Entities.Tenant.TenantSubjectRelationVersionEntity", b =>
{
b.Property<long>("SubjectId")
.HasColumnType("bigint");
b.Property<long>("TenantId")
.HasColumnType("bigint");
b.Property<DateTimeOffset>("At")
.HasColumnType("timestamp with time zone");
b.Property<string>("Action")
.IsRequired()
.HasColumnType("text");
b.Property<long>("SuspectId")
.HasColumnType("bigint");
b.HasKey("SubjectId", "TenantId", "At");
b.HasIndex("SuspectId");
b.ToTable("TenantSubjectRelationVersion", "gr");
});
modelBuilder.Entity("Suspectus.Gandalf.Palantir.Data.Entities.Tenant.TenantVersionEntity", b =>
{
b.Property<long>("Id")
.HasColumnType("bigint");
b.Property<DateTimeOffset>("At")
.HasColumnType("timestamp with time zone");
b.Property<string>("Action")
.IsRequired()
.HasColumnType("text");
b.Property<bool>("IsMaster")
.HasColumnType("boolean");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("text");
b.Property<long>("OwnerId")
.HasColumnType("bigint");
b.Property<long>("SuspectId")
.HasColumnType("bigint");
b.Property<string>("Visibility")
.IsRequired()
.HasColumnType("text");
b.HasKey("Id", "At");
b.HasIndex("SuspectId");
b.ToTable("TenantVersion", "gr");
});
modelBuilder.Entity("Suspectus.Gandalf.Palantir.Data.Entities.App.AppEntity", b =>
{
b.HasOne("Suspectus.Gandalf.Palantir.Data.Entities.Tenant.TenantEntity", "Tenant")
.WithMany("Apps")
.HasForeignKey("TenantId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Tenant");
});
modelBuilder.Entity("Suspectus.Gandalf.Palantir.Data.Entities.App.AppSubjectRelationEntity", b =>
{
b.HasOne("Suspectus.Gandalf.Palantir.Data.Entities.App.AppEntity", "App")
.WithMany()
.HasForeignKey("AppId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Suspectus.Gandalf.Palantir.Data.Entities.Subject.SubjectEntity", "Subject")
.WithMany()
.HasForeignKey("SubjectId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("App");
b.Navigation("Subject");
});
modelBuilder.Entity("Suspectus.Gandalf.Palantir.Data.Entities.App.AppSubjectRelationVersionEntity", b =>
{
b.HasOne("Suspectus.Gandalf.Palantir.Data.Entities.Subject.SubjectEntity", "Suspect")
.WithMany()
.HasForeignKey("SuspectId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Suspectus.Gandalf.Palantir.Data.Entities.App.AppSubjectRelationEntity", "Reference")
.WithMany()
.HasForeignKey("SubjectId", "AppId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Reference");
b.Navigation("Suspect");
});
modelBuilder.Entity("Suspectus.Gandalf.Palantir.Data.Entities.App.AppVersionEntity", b =>
{
b.HasOne("Suspectus.Gandalf.Palantir.Data.Entities.App.AppEntity", "Reference")
.WithMany()
.HasForeignKey("Id")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Suspectus.Gandalf.Palantir.Data.Entities.Subject.SubjectEntity", "Suspect")
.WithMany()
.HasForeignKey("SuspectId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Reference");
b.Navigation("Suspect");
});
modelBuilder.Entity("Suspectus.Gandalf.Palantir.Data.Entities.Security.AppSubjectRelationInternalAuthorityRelationEntity", b =>
{
b.HasOne("Suspectus.Gandalf.Palantir.Data.Entities.Security.AuthorityEntity", "InternalAuthority")
.WithMany()
.HasForeignKey("InternalAuthorityId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Suspectus.Gandalf.Palantir.Data.Entities.App.AppSubjectRelationEntity", "AppSubjectRelation")
.WithMany()
.HasForeignKey("SubjectId", "AppId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("AppSubjectRelation");
b.Navigation("InternalAuthority");
});
modelBuilder.Entity("Suspectus.Gandalf.Palantir.Data.Entities.Security.AppSubjectRelationInternalAuthorityRelationVersionEntity", b =>
{
b.HasOne("Suspectus.Gandalf.Palantir.Data.Entities.Subject.SubjectEntity", "Suspect")
.WithMany()
.HasForeignKey("SuspectId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Suspectus.Gandalf.Palantir.Data.Entities.Security.AppSubjectRelationInternalAuthorityRelationEntity", "Reference")
.WithMany()
.HasForeignKey("SubjectId", "AppId", "InternalAuthorityId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Reference");
b.Navigation("Suspect");
});
modelBuilder.Entity("Suspectus.Gandalf.Palantir.Data.Entities.Security.TenantSubjectRelationInternalAuthorityRelationEntity", b =>
{
b.HasOne("Suspectus.Gandalf.Palantir.Data.Entities.Security.AuthorityEntity", "InternalAuthority")
.WithMany()
.HasForeignKey("InternalAuthorityId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Suspectus.Gandalf.Palantir.Data.Entities.Tenant.TenantSubjectRelationEntity", "TenantSubjectRelation")
.WithMany()
.HasForeignKey("SubjectId", "TenantId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("InternalAuthority");
b.Navigation("TenantSubjectRelation");
});
modelBuilder.Entity("Suspectus.Gandalf.Palantir.Data.Entities.Security.TenantSubjectRelationInternalAuthorityRelationVersionEntity", b =>
{
b.HasOne("Suspectus.Gandalf.Palantir.Data.Entities.Subject.SubjectEntity", "Suspect")
.WithMany()
.HasForeignKey("SuspectId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Suspectus.Gandalf.Palantir.Data.Entities.Security.TenantSubjectRelationInternalAuthorityRelationEntity", "Reference")
.WithMany()
.HasForeignKey("SubjectId", "TenantId", "InternalAuthorityId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Reference");
b.Navigation("Suspect");
});
modelBuilder.Entity("Suspectus.Gandalf.Palantir.Data.Entities.Subject.SignIn.SignInEntity", b =>
{
b.HasOne("Suspectus.Gandalf.Palantir.Data.Entities.Subject.SubjectEntity", "Subject")
.WithMany("SignInMethods")
.HasForeignKey("SubjectId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Subject");
});
modelBuilder.Entity("Suspectus.Gandalf.Palantir.Data.Entities.Tenant.TenantEntity", b =>
{
b.HasOne("Suspectus.Gandalf.Palantir.Data.Entities.Subject.SubjectEntity", "Owner")
.WithMany()
.HasForeignKey("OwnerId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Owner");
});
modelBuilder.Entity("Suspectus.Gandalf.Palantir.Data.Entities.Tenant.TenantSubjectRelationEntity", b =>
{
b.HasOne("Suspectus.Gandalf.Palantir.Data.Entities.Subject.SubjectEntity", "Subject")
.WithMany()
.HasForeignKey("SubjectId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Suspectus.Gandalf.Palantir.Data.Entities.Tenant.TenantEntity", "Tenant")
.WithMany()
.HasForeignKey("TenantId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Subject");
b.Navigation("Tenant");
});
modelBuilder.Entity("Suspectus.Gandalf.Palantir.Data.Entities.Tenant.TenantSubjectRelationVersionEntity", b =>
{
b.HasOne("Suspectus.Gandalf.Palantir.Data.Entities.Subject.SubjectEntity", "Suspect")
.WithMany()
.HasForeignKey("SuspectId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Suspectus.Gandalf.Palantir.Data.Entities.Tenant.TenantSubjectRelationEntity", "Reference")
.WithMany()
.HasForeignKey("SubjectId", "TenantId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Reference");
b.Navigation("Suspect");
});
modelBuilder.Entity("Suspectus.Gandalf.Palantir.Data.Entities.Tenant.TenantVersionEntity", b =>
{
b.HasOne("Suspectus.Gandalf.Palantir.Data.Entities.Tenant.TenantEntity", "Reference")
.WithMany()
.HasForeignKey("Id")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Suspectus.Gandalf.Palantir.Data.Entities.Subject.SubjectEntity", "Suspect")
.WithMany()
.HasForeignKey("SuspectId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Reference");
b.Navigation("Suspect");
});
modelBuilder.Entity("Suspectus.Gandalf.Palantir.Data.Entities.Subject.SubjectEntity", b =>
{
b.Navigation("SignInMethods");
});
modelBuilder.Entity("Suspectus.Gandalf.Palantir.Data.Entities.Tenant.TenantEntity", b =>
{
b.Navigation("Apps");
});
#pragma warning restore 612, 618
}
}
}

View File

@ -0,0 +1,44 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace W542.GandalfReborn.Data.Migrations
{
/// <inheritdoc />
public partial class addBaseAddress : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<string>(
name: "BaseAddress",
schema: "gr",
table: "AppVersion",
type: "text",
nullable: false,
defaultValue: "");
migrationBuilder.AddColumn<string>(
name: "BaseAddress",
schema: "gr",
table: "App",
type: "text",
nullable: false,
defaultValue: "");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "BaseAddress",
schema: "gr",
table: "AppVersion");
migrationBuilder.DropColumn(
name: "BaseAddress",
schema: "gr",
table: "App");
}
}
}

View File

@ -0,0 +1,739 @@
// <auto-generated />
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
using Suspectus.Gandalf.Palantir.Data.Database;
#nullable disable
namespace W542.GandalfReborn.Data.Migrations
{
[DbContext(typeof(ApplicationContext))]
[Migration("20250530235831_tokenType")]
partial class tokenType
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "9.0.2")
.HasAnnotation("Relational:MaxIdentifierLength", 63);
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
modelBuilder.Entity("Suspectus.Gandalf.Palantir.Data.Entities.App.AppEntity", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
b.Property<string>("BaseAddress")
.IsRequired()
.HasColumnType("text");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("text");
b.Property<long>("TenantId")
.HasColumnType("bigint");
b.Property<string>("Visibility")
.IsRequired()
.HasColumnType("text");
b.HasKey("Id");
b.HasIndex("TenantId");
b.ToTable("App", "gr");
});
modelBuilder.Entity("Suspectus.Gandalf.Palantir.Data.Entities.App.AppSubjectRelationEntity", b =>
{
b.Property<long>("SubjectId")
.HasColumnType("bigint");
b.Property<long>("AppId")
.HasColumnType("bigint");
b.HasKey("SubjectId", "AppId");
b.HasIndex("AppId");
b.ToTable("AppSubjectRelation", "gr");
});
modelBuilder.Entity("Suspectus.Gandalf.Palantir.Data.Entities.App.AppSubjectRelationVersionEntity", b =>
{
b.Property<long>("SubjectId")
.HasColumnType("bigint");
b.Property<long>("AppId")
.HasColumnType("bigint");
b.Property<DateTimeOffset>("At")
.HasColumnType("timestamp with time zone");
b.Property<string>("Action")
.IsRequired()
.HasColumnType("text");
b.Property<long>("SuspectId")
.HasColumnType("bigint");
b.HasKey("SubjectId", "AppId", "At");
b.HasIndex("SuspectId");
b.ToTable("AppSubjectRelationVersion", "gr");
});
modelBuilder.Entity("Suspectus.Gandalf.Palantir.Data.Entities.App.AppVersionEntity", b =>
{
b.Property<long>("Id")
.HasColumnType("bigint");
b.Property<DateTimeOffset>("At")
.HasColumnType("timestamp with time zone");
b.Property<string>("Action")
.IsRequired()
.HasColumnType("text");
b.Property<string>("BaseAddress")
.IsRequired()
.HasColumnType("text");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("text");
b.Property<long>("SuspectId")
.HasColumnType("bigint");
b.Property<long>("TenantId")
.HasColumnType("bigint");
b.Property<string>("Visibility")
.IsRequired()
.HasColumnType("text");
b.HasKey("Id", "At");
b.HasIndex("SuspectId");
b.ToTable("AppVersion", "gr");
});
modelBuilder.Entity("Suspectus.Gandalf.Palantir.Data.Entities.Security.AppSubjectRelationInternalAuthorityRelationEntity", b =>
{
b.Property<long>("SubjectId")
.HasColumnType("bigint");
b.Property<long>("AppId")
.HasColumnType("bigint");
b.Property<long>("InternalAuthorityId")
.HasColumnType("bigint");
b.HasKey("SubjectId", "AppId", "InternalAuthorityId");
b.HasIndex("InternalAuthorityId");
b.ToTable("AppSubjectRelationInternalAuthorityRelation", "gr");
});
modelBuilder.Entity("Suspectus.Gandalf.Palantir.Data.Entities.Security.AppSubjectRelationInternalAuthorityRelationVersionEntity", b =>
{
b.Property<long>("SubjectId")
.HasColumnType("bigint");
b.Property<long>("AppId")
.HasColumnType("bigint");
b.Property<long>("InternalAuthorityId")
.HasColumnType("bigint");
b.Property<DateTimeOffset>("At")
.HasColumnType("timestamp with time zone");
b.Property<string>("Action")
.IsRequired()
.HasColumnType("text");
b.Property<long>("SuspectId")
.HasColumnType("bigint");
b.HasKey("SubjectId", "AppId", "InternalAuthorityId", "At");
b.HasIndex("SuspectId");
b.ToTable("AppSubjectRelationInternalAuthorityRelationVersion", "gr");
});
modelBuilder.Entity("Suspectus.Gandalf.Palantir.Data.Entities.Security.AuthCodeEntity", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
b.Property<string>("Algorithm")
.IsRequired()
.HasColumnType("text");
b.Property<string>("Challenge")
.IsRequired()
.HasColumnType("text");
b.Property<string>("Code")
.IsRequired()
.HasColumnType("text");
b.Property<DateTimeOffset>("Expiration")
.HasColumnType("timestamp with time zone");
b.Property<bool>("IsRevoked")
.HasColumnType("boolean");
b.Property<long>("SubjectId")
.HasColumnType("bigint");
b.HasKey("Id");
b.ToTable("AuthCode", "gr");
});
modelBuilder.Entity("Suspectus.Gandalf.Palantir.Data.Entities.Security.AuthorityEntity", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
b.Property<string>("Description")
.HasColumnType("text");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("text");
b.Property<string>("Type")
.IsRequired()
.HasColumnType("text");
b.HasKey("Id");
b.HasIndex("Name")
.IsUnique();
b.ToTable("Authority", "gr");
b.HasData(
new
{
Id = 1L,
Description = "Allows users to read tenants",
Name = "Tenant_Read",
Type = "Tenant"
},
new
{
Id = 2L,
Description = "Allows users to read apps",
Name = "App_Read",
Type = "App"
});
});
modelBuilder.Entity("Suspectus.Gandalf.Palantir.Data.Entities.Security.TenantSubjectRelationInternalAuthorityRelationEntity", b =>
{
b.Property<long>("SubjectId")
.HasColumnType("bigint");
b.Property<long>("TenantId")
.HasColumnType("bigint");
b.Property<long>("InternalAuthorityId")
.HasColumnType("bigint");
b.HasKey("SubjectId", "TenantId", "InternalAuthorityId");
b.HasIndex("InternalAuthorityId");
b.ToTable("TenantSubjectRelationInternalAuthorityRelation", "gr");
});
modelBuilder.Entity("Suspectus.Gandalf.Palantir.Data.Entities.Security.TenantSubjectRelationInternalAuthorityRelationVersionEntity", b =>
{
b.Property<long>("SubjectId")
.HasColumnType("bigint");
b.Property<long>("TenantId")
.HasColumnType("bigint");
b.Property<long>("InternalAuthorityId")
.HasColumnType("bigint");
b.Property<DateTimeOffset>("At")
.HasColumnType("timestamp with time zone");
b.Property<string>("Action")
.IsRequired()
.HasColumnType("text");
b.Property<long>("SuspectId")
.HasColumnType("bigint");
b.HasKey("SubjectId", "TenantId", "InternalAuthorityId", "At");
b.HasIndex("SuspectId");
b.ToTable("TenantSubjectRelationInternalAuthorityRelationVersion", "gr");
});
modelBuilder.Entity("Suspectus.Gandalf.Palantir.Data.Entities.Security.TokenMetadataEntity", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
b.Property<DateTimeOffset>("Expiration")
.HasColumnType("timestamp with time zone");
b.Property<bool>("IsRevoked")
.HasColumnType("boolean");
b.Property<string>("Scope")
.IsRequired()
.HasColumnType("text");
b.Property<string>("TokenType")
.IsRequired()
.HasColumnType("text");
b.Property<long>("UsedBy")
.HasColumnType("bigint");
b.Property<long>("UsedFor")
.HasColumnType("bigint");
b.HasKey("Id");
b.ToTable("TokenMetadata", "gr");
});
modelBuilder.Entity("Suspectus.Gandalf.Palantir.Data.Entities.Subject.SignIn.SignInEntity", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
b.Property<string>("Email")
.HasColumnType("text");
b.Property<bool>("IsLegacy")
.HasColumnType("boolean");
b.Property<string>("Method")
.IsRequired()
.HasColumnType("text");
b.Property<string>("PasswordHash")
.HasColumnType("text");
b.Property<long>("SubjectId")
.HasColumnType("bigint");
b.Property<string>("Visibility")
.IsRequired()
.HasColumnType("text");
b.HasKey("Id");
b.HasIndex("Email")
.IsUnique();
b.HasIndex("SubjectId");
b.ToTable("SignIn", "gr");
});
modelBuilder.Entity("Suspectus.Gandalf.Palantir.Data.Entities.Subject.SubjectEntity", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
b.Property<string>("Name")
.IsRequired()
.HasColumnType("text");
b.Property<string>("Visibility")
.IsRequired()
.HasColumnType("text");
b.HasKey("Id");
b.HasIndex("Name")
.IsUnique();
b.ToTable("Subject", "gr");
});
modelBuilder.Entity("Suspectus.Gandalf.Palantir.Data.Entities.Tenant.TenantEntity", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
b.Property<bool>("IsMaster")
.HasColumnType("boolean");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("text");
b.Property<long>("OwnerId")
.HasColumnType("bigint");
b.Property<string>("Visibility")
.IsRequired()
.HasColumnType("text");
b.HasKey("Id");
b.HasIndex("OwnerId");
b.ToTable("Tenant", "gr");
});
modelBuilder.Entity("Suspectus.Gandalf.Palantir.Data.Entities.Tenant.TenantSubjectRelationEntity", b =>
{
b.Property<long>("SubjectId")
.HasColumnType("bigint");
b.Property<long>("TenantId")
.HasColumnType("bigint");
b.HasKey("SubjectId", "TenantId");
b.HasIndex("TenantId");
b.ToTable("TenantSubjectRelation", "gr");
});
modelBuilder.Entity("Suspectus.Gandalf.Palantir.Data.Entities.Tenant.TenantSubjectRelationVersionEntity", b =>
{
b.Property<long>("SubjectId")
.HasColumnType("bigint");
b.Property<long>("TenantId")
.HasColumnType("bigint");
b.Property<DateTimeOffset>("At")
.HasColumnType("timestamp with time zone");
b.Property<string>("Action")
.IsRequired()
.HasColumnType("text");
b.Property<long>("SuspectId")
.HasColumnType("bigint");
b.HasKey("SubjectId", "TenantId", "At");
b.HasIndex("SuspectId");
b.ToTable("TenantSubjectRelationVersion", "gr");
});
modelBuilder.Entity("Suspectus.Gandalf.Palantir.Data.Entities.Tenant.TenantVersionEntity", b =>
{
b.Property<long>("Id")
.HasColumnType("bigint");
b.Property<DateTimeOffset>("At")
.HasColumnType("timestamp with time zone");
b.Property<string>("Action")
.IsRequired()
.HasColumnType("text");
b.Property<bool>("IsMaster")
.HasColumnType("boolean");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("text");
b.Property<long>("OwnerId")
.HasColumnType("bigint");
b.Property<long>("SuspectId")
.HasColumnType("bigint");
b.Property<string>("Visibility")
.IsRequired()
.HasColumnType("text");
b.HasKey("Id", "At");
b.HasIndex("SuspectId");
b.ToTable("TenantVersion", "gr");
});
modelBuilder.Entity("Suspectus.Gandalf.Palantir.Data.Entities.App.AppEntity", b =>
{
b.HasOne("Suspectus.Gandalf.Palantir.Data.Entities.Tenant.TenantEntity", "Tenant")
.WithMany("Apps")
.HasForeignKey("TenantId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Tenant");
});
modelBuilder.Entity("Suspectus.Gandalf.Palantir.Data.Entities.App.AppSubjectRelationEntity", b =>
{
b.HasOne("Suspectus.Gandalf.Palantir.Data.Entities.App.AppEntity", "App")
.WithMany()
.HasForeignKey("AppId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Suspectus.Gandalf.Palantir.Data.Entities.Subject.SubjectEntity", "Subject")
.WithMany()
.HasForeignKey("SubjectId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("App");
b.Navigation("Subject");
});
modelBuilder.Entity("Suspectus.Gandalf.Palantir.Data.Entities.App.AppSubjectRelationVersionEntity", b =>
{
b.HasOne("Suspectus.Gandalf.Palantir.Data.Entities.Subject.SubjectEntity", "Suspect")
.WithMany()
.HasForeignKey("SuspectId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Suspectus.Gandalf.Palantir.Data.Entities.App.AppSubjectRelationEntity", "Reference")
.WithMany()
.HasForeignKey("SubjectId", "AppId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Reference");
b.Navigation("Suspect");
});
modelBuilder.Entity("Suspectus.Gandalf.Palantir.Data.Entities.App.AppVersionEntity", b =>
{
b.HasOne("Suspectus.Gandalf.Palantir.Data.Entities.App.AppEntity", "Reference")
.WithMany()
.HasForeignKey("Id")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Suspectus.Gandalf.Palantir.Data.Entities.Subject.SubjectEntity", "Suspect")
.WithMany()
.HasForeignKey("SuspectId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Reference");
b.Navigation("Suspect");
});
modelBuilder.Entity("Suspectus.Gandalf.Palantir.Data.Entities.Security.AppSubjectRelationInternalAuthorityRelationEntity", b =>
{
b.HasOne("Suspectus.Gandalf.Palantir.Data.Entities.Security.AuthorityEntity", "InternalAuthority")
.WithMany()
.HasForeignKey("InternalAuthorityId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Suspectus.Gandalf.Palantir.Data.Entities.App.AppSubjectRelationEntity", "AppSubjectRelation")
.WithMany()
.HasForeignKey("SubjectId", "AppId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("AppSubjectRelation");
b.Navigation("InternalAuthority");
});
modelBuilder.Entity("Suspectus.Gandalf.Palantir.Data.Entities.Security.AppSubjectRelationInternalAuthorityRelationVersionEntity", b =>
{
b.HasOne("Suspectus.Gandalf.Palantir.Data.Entities.Subject.SubjectEntity", "Suspect")
.WithMany()
.HasForeignKey("SuspectId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Suspectus.Gandalf.Palantir.Data.Entities.Security.AppSubjectRelationInternalAuthorityRelationEntity", "Reference")
.WithMany()
.HasForeignKey("SubjectId", "AppId", "InternalAuthorityId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Reference");
b.Navigation("Suspect");
});
modelBuilder.Entity("Suspectus.Gandalf.Palantir.Data.Entities.Security.TenantSubjectRelationInternalAuthorityRelationEntity", b =>
{
b.HasOne("Suspectus.Gandalf.Palantir.Data.Entities.Security.AuthorityEntity", "InternalAuthority")
.WithMany()
.HasForeignKey("InternalAuthorityId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Suspectus.Gandalf.Palantir.Data.Entities.Tenant.TenantSubjectRelationEntity", "TenantSubjectRelation")
.WithMany()
.HasForeignKey("SubjectId", "TenantId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("InternalAuthority");
b.Navigation("TenantSubjectRelation");
});
modelBuilder.Entity("Suspectus.Gandalf.Palantir.Data.Entities.Security.TenantSubjectRelationInternalAuthorityRelationVersionEntity", b =>
{
b.HasOne("Suspectus.Gandalf.Palantir.Data.Entities.Subject.SubjectEntity", "Suspect")
.WithMany()
.HasForeignKey("SuspectId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Suspectus.Gandalf.Palantir.Data.Entities.Security.TenantSubjectRelationInternalAuthorityRelationEntity", "Reference")
.WithMany()
.HasForeignKey("SubjectId", "TenantId", "InternalAuthorityId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Reference");
b.Navigation("Suspect");
});
modelBuilder.Entity("Suspectus.Gandalf.Palantir.Data.Entities.Subject.SignIn.SignInEntity", b =>
{
b.HasOne("Suspectus.Gandalf.Palantir.Data.Entities.Subject.SubjectEntity", "Subject")
.WithMany("SignInMethods")
.HasForeignKey("SubjectId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Subject");
});
modelBuilder.Entity("Suspectus.Gandalf.Palantir.Data.Entities.Tenant.TenantEntity", b =>
{
b.HasOne("Suspectus.Gandalf.Palantir.Data.Entities.Subject.SubjectEntity", "Owner")
.WithMany()
.HasForeignKey("OwnerId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Owner");
});
modelBuilder.Entity("Suspectus.Gandalf.Palantir.Data.Entities.Tenant.TenantSubjectRelationEntity", b =>
{
b.HasOne("Suspectus.Gandalf.Palantir.Data.Entities.Subject.SubjectEntity", "Subject")
.WithMany()
.HasForeignKey("SubjectId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Suspectus.Gandalf.Palantir.Data.Entities.Tenant.TenantEntity", "Tenant")
.WithMany()
.HasForeignKey("TenantId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Subject");
b.Navigation("Tenant");
});
modelBuilder.Entity("Suspectus.Gandalf.Palantir.Data.Entities.Tenant.TenantSubjectRelationVersionEntity", b =>
{
b.HasOne("Suspectus.Gandalf.Palantir.Data.Entities.Subject.SubjectEntity", "Suspect")
.WithMany()
.HasForeignKey("SuspectId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Suspectus.Gandalf.Palantir.Data.Entities.Tenant.TenantSubjectRelationEntity", "Reference")
.WithMany()
.HasForeignKey("SubjectId", "TenantId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Reference");
b.Navigation("Suspect");
});
modelBuilder.Entity("Suspectus.Gandalf.Palantir.Data.Entities.Tenant.TenantVersionEntity", b =>
{
b.HasOne("Suspectus.Gandalf.Palantir.Data.Entities.Tenant.TenantEntity", "Reference")
.WithMany()
.HasForeignKey("Id")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Suspectus.Gandalf.Palantir.Data.Entities.Subject.SubjectEntity", "Suspect")
.WithMany()
.HasForeignKey("SuspectId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Reference");
b.Navigation("Suspect");
});
modelBuilder.Entity("Suspectus.Gandalf.Palantir.Data.Entities.Subject.SubjectEntity", b =>
{
b.Navigation("SignInMethods");
});
modelBuilder.Entity("Suspectus.Gandalf.Palantir.Data.Entities.Tenant.TenantEntity", b =>
{
b.Navigation("Apps");
});
#pragma warning restore 612, 618
}
}
}

View File

@ -0,0 +1,42 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace W542.GandalfReborn.Data.Migrations
{
/// <inheritdoc />
public partial class tokenType : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.Sql(
"""
UPDATE gr."TokenMetadata"
SET "TokenType" = 'Access'
WHERE "TokenType" = 'User';
UPDATE gr."TokenMetadata"
SET "TokenType" = 'Refresh'
WHERE "TokenType" = 'Application';
"""
);
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.Sql(
"""
UPDATE gr.TokenMetadata
SET TokenType = 'User'
WHERE TokenType = 'Access';
UPDATE gr.TokenMetadata
SET TokenType = 'Application'
WHERE TokenType = 'Refresh';
"""
);
}
}
}

View File

@ -30,6 +30,10 @@ namespace W542.GandalfReborn.Data.Migrations
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
b.Property<string>("BaseAddress")
.IsRequired()
.HasColumnType("text");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("text");
@ -100,6 +104,10 @@ namespace W542.GandalfReborn.Data.Migrations
.IsRequired()
.HasColumnType("text");
b.Property<string>("BaseAddress")
.IsRequired()
.HasColumnType("text");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("text");
@ -304,6 +312,10 @@ namespace W542.GandalfReborn.Data.Migrations
b.Property<bool>("IsRevoked")
.HasColumnType("boolean");
b.Property<string>("Scope")
.IsRequired()
.HasColumnType("text");
b.Property<string>("TokenType")
.IsRequired()
.HasColumnType("text");
@ -311,6 +323,9 @@ namespace W542.GandalfReborn.Data.Migrations
b.Property<long>("UsedBy")
.HasColumnType("bigint");
b.Property<long>("UsedFor")
.HasColumnType("bigint");
b.HasKey("Id");
b.ToTable("TokenMetadata", "gr");

View File

@ -17,6 +17,7 @@ using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Suspectus.Gandalf.Palantir.Abstractions;
using Suspectus.Gandalf.Palantir.Data.Database;
using Suspectus.Gandalf.Palantir.Data.Entities.Security;
namespace Suspectus.Gandalf.Palantir.Security.Scheme;
@ -50,7 +51,7 @@ public partial class GandalfRebornJwtTokenAuthSchemeHandler(
{
try
{
var jwtBody = GetJwtTokenBody(httpContextAccessor.HttpContext);
var jwtBody = await GetJwtTokenBody(httpContextAccessor.HttpContext);
if (!hashIds.TryDecodeSingleLong(jwtBody.Sub, out var subjectId))
{
@ -121,7 +122,7 @@ public partial class GandalfRebornJwtTokenAuthSchemeHandler(
}
}
private GandalfRebornJwtBody GetJwtTokenBody(HttpContext? httpContext)
private async Task<GandalfRebornJwtBody> GetJwtTokenBody(HttpContext? httpContext)
{
var authHeader = httpContext?.Request.Headers.Authorization.ToString();
@ -142,7 +143,14 @@ public partial class GandalfRebornJwtTokenAuthSchemeHandler(
.WithJsonSerializer(new JsonNetSerializer())
.Decode<GandalfRebornJwtBody>(token);
if (decodedToken.Aud is null || !decodedToken.Aud.StartsWith(Options.BaseUrl))
var tokenType = await context.TokenMetadata.Where(x => x.Id == hashIds.DecodeSingleLong(decodedToken.Id)).Select(x => x.TokenType).SingleOrDefaultAsync();
if (tokenType != TokenType.Access)
throw new UnauthorizedAccessException("One does not simply provide a refresh token as access token.");
var audience = await context.Apps.Where(x => x.Tenant!.IsMaster && x.Name == "Master").Select(x => $"{x.Name.ToLower()}-api").SingleAsync();
if (decodedToken.Aud is null || decodedToken.Aud != audience)
throw new UnauthorizedAccessException("One does not simply provide a token for a different audience.");
if (decodedToken.Iss is null || !decodedToken.Iss.StartsWith(Options.BaseUrl))