rage quit

This commit is contained in:
Christian Werner 2025-05-25 22:37:46 +02:00
parent 1ad7e3d66a
commit 55d8d0a1c2
30 changed files with 1058 additions and 30 deletions

View File

@ -1,40 +1,53 @@
using Microsoft.AspNetCore.Mvc;
using Suspectus.Gandalf.Bridgekeeper.Contracts.Controller.Auth;
using Suspectus.Gandalf.Core.Abstractions.CQRS.Commands;
using Suspectus.Gandalf.Palantir.Client;
using Suspectus.Gandalf.Palantir.Contracts.Controller.Auth;
namespace Suspectus.Gandalf.Bridgekeeper.Api.Controllers;
[ApiController]
[Route("api/[controller]")]
public class AuthController : ControllerBase
public class AuthController(IPalantirClient client) : ControllerBase
{
[HttpGet("[action]")]
public async Task<IActionResult> Check()
{
return Ok(true);
}
[HttpPost("[action]")]
public async Task<IActionResult> Login([FromBody] LoginCommand loginCommand)
{
Response.Cookies.Append("MithrandirSession", loginCommand.UsernameOrEmail, new CookieOptions
{
Secure = true,
HttpOnly = true,
SameSite = SameSiteMode.Lax,
Expires = DateTime.UtcNow.AddMinutes(30)
});
return Ok();
var validationResult = await client.Auth.ValidateCredentials(new ValidateCredentialsCommand { Password = loginCommand.Password, UsernameOrEmail = loginCommand.UsernameOrEmail });
return validationResult.Match<IActionResult>(valid =>
{
if (!valid)
{
return BadRequest("Invalid username or password.");
}
Response.Cookies.Append("MithrandirSession", loginCommand.UsernameOrEmail, new CookieOptions
{
Secure = true,
HttpOnly = true,
SameSite = SameSiteMode.Lax,
Expires = DateTime.UtcNow.AddMinutes(30)
});
return Ok();
}, e => BadRequest($"{e.Message}\n{e.InnerException?.Message}")
);
}
[HttpGet("[action]")]
public async Task<IActionResult> Logout()
{
Response.Cookies.Delete("MithrandirSession");
return Ok();
}
[HttpPost("[action]")]
public async Task<IActionResult> Register()
{

View File

@ -1,8 +1,12 @@
using Suspectus.Gandalf.Palantir.Client.Extensions;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();
builder.Services.AddOpenApi();
builder.Services.AddPalantirClient();
var app = builder.Build();
if (app.Environment.IsDevelopment())

View File

@ -7,7 +7,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="9.0.1"/>
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="9.0.2"/>
<PackageReference Include="MediatR" Version="12.4.1" />
</ItemGroup>
@ -18,6 +18,7 @@
<ItemGroup>
<ProjectReference Include="..\Suspectus.Gandalf.Bridgekeeper.Contracts\Suspectus.Gandalf.Bridgekeeper.Contracts.csproj" />
<ProjectReference Include="..\Suspectus.Gandalf.Palantir.Client\Suspectus.Gandalf.Palantir.Client.csproj" />
</ItemGroup>
</Project>

View File

@ -10,4 +10,8 @@
<ProjectReference Include="..\Suspectus.Gandalf.Core.Abstractions\Suspectus.Gandalf.Core.Abstractions.csproj" />
</ItemGroup>
<ItemGroup>
<Folder Include="Controller\Auth\" />
</ItemGroup>
</Project>

View File

@ -1,6 +1,4 @@
using Suspectus.Gandalf.Core.Abstractions.CQRS;
namespace Suspectus.Gandalf.Bridgekeeper.Contracts.Controller.Auth;
namespace Suspectus.Gandalf.Core.Abstractions.CQRS.Commands;
public class LoginCommand : ICommand<bool>
{

View File

@ -0,0 +1,6 @@
using LanguageExt.Common;
using MediatR;
namespace Suspectus.Gandalf.Core.Abstractions.CQRS;
public interface ICommandHandler<in TCommand, TResult> : IRequestHandler<TCommand, Result<TResult>> where TCommand : ICommand<TResult>;

View File

@ -1,6 +1,6 @@
using LanguageExt.Common;
namespace Suspectus.Gandalf.Palantir.Api.Extensions;
namespace Suspectus.Gandalf.Core.Abstractions.Extensions;
public static class ResultExtensions
{

View File

@ -1,8 +1,9 @@
using LanguageExt.Common;
using Suspectus.Gandalf.Core.Abstractions.CQRS;
namespace Suspectus.Gandalf.Palantir.Api.Commands;
public class HashPasswordCommand : IGrCommand<Result<string>>
public class HashPasswordCommand : ICommand<string>
{
public required string RawPassword { get; set; }
}

View File

@ -0,0 +1,9 @@
using Suspectus.Gandalf.Core.Abstractions.CQRS;
namespace Suspectus.Gandalf.Palantir.Api.Commands;
public class VerifyPasswordCommand : ICommand<bool>
{
public required string RawPassword { get; set; }
public required long SignInId { get; set; }
}

View File

@ -1,6 +1,7 @@
using MediatR;
using Microsoft.AspNetCore.Mvc;
using Suspectus.Gandalf.Palantir.Api.Commands;
using Suspectus.Gandalf.Palantir.Contracts.Controller.Auth;
namespace Suspectus.Gandalf.Palantir.Api.Controllers;
@ -28,4 +29,11 @@ public class AuthController(IMediator mediator) : ControllerBase
var result = await mediator.Send(getTokensCommand);
return result.Match<IActionResult>(Ok, e => BadRequest($"{e.Message}\n{e.InnerException?.Message}"));
}
[HttpPost("validate-credentials")]
public async Task<IActionResult> ValidateCredentials([FromBody] ValidateCredentialsCommand validateCredentialsCommand)
{
var result = await mediator.Send(validateCredentialsCommand);
return result.Match<IActionResult>(s => Ok(s), e => BadRequest($"{e.Message}\n{e.InnerException?.Message}"));
}
}

View File

@ -1,7 +1,7 @@
using LanguageExt.Common;
using MediatR;
using Suspectus.Gandalf.Core.Abstractions.Extensions;
using Suspectus.Gandalf.Palantir.Api.Commands;
using Suspectus.Gandalf.Palantir.Api.Extensions;
using Suspectus.Gandalf.Palantir.Data.Database.Repositories;
using Suspectus.Gandalf.Palantir.Data.Entities.Base;
using Suspectus.Gandalf.Palantir.Data.Entities.Subject;

View File

@ -0,0 +1,35 @@
using LanguageExt.Common;
using MediatR;
using Microsoft.EntityFrameworkCore;
using Suspectus.Gandalf.Core.Abstractions.CQRS;
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 async Task<Result<bool>> Handle(ValidateCredentialsCommand request, CancellationToken cancellationToken)
{
var subject = await applicationContext.Subjects
.Include(x => x.SignInMethods)
.SingleOrDefaultAsync(x =>
x.Name == request.UsernameOrEmail
||
x.SignInMethods.Any(s =>
s.Method == SignInMethod.Simple
&&
s.Email == request.UsernameOrEmail
), cancellationToken);
if (subject is null)
return "User does not exist.".AsErrorResult<bool>();
var signIn = subject.SignInMethods.Single(x => x.Method == SignInMethod.Simple);
return await mediator.Send(new VerifyPasswordCommand { RawPassword = request.Password, SignInId = signIn.Id!.Value }, cancellationToken);
}
}

View File

@ -2,9 +2,9 @@ using LanguageExt.Common;
using MediatR;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using Suspectus.Gandalf.Core.Abstractions.Extensions;
using Suspectus.Gandalf.Palantir.Api.Commands;
using Suspectus.Gandalf.Palantir.Api.Handlers.Commands;
using Suspectus.Gandalf.Palantir.Api.Extensions;
using Suspectus.Gandalf.Palantir.Data.Database.Repositories;
using Suspectus.Gandalf.Palantir.Data.Dto;
using Suspectus.Gandalf.Palantir.Data.Entities.Subject.SignIn;

View File

@ -3,9 +3,9 @@ using JWT.Algorithms;
using JWT.Builder;
using JWT.Serializers;
using LanguageExt.Common;
using Suspectus.Gandalf.Core.Abstractions.Extensions;
using Suspectus.Gandalf.Palantir.Api.Commands;
using Suspectus.Gandalf.Palantir.Api.Handlers.Commands;
using Suspectus.Gandalf.Palantir.Api.Extensions;
using Suspectus.Gandalf.Palantir.Data.Database.Repositories;
using Suspectus.Gandalf.Palantir.Data.Dto;
using Suspectus.Gandalf.Palantir.Data.Entities.Security;

View File

@ -3,9 +3,9 @@ using System.Text;
using LanguageExt.Common;
using MediatR;
using Microsoft.EntityFrameworkCore;
using Suspectus.Gandalf.Core.Abstractions.Extensions;
using Suspectus.Gandalf.Palantir.Api.Commands;
using Suspectus.Gandalf.Palantir.Api.Handlers.Commands;
using Suspectus.Gandalf.Palantir.Api.Extensions;
using Suspectus.Gandalf.Palantir.Data.Database.Repositories;
using Suspectus.Gandalf.Palantir.Data.Dto;

View File

@ -1,12 +1,17 @@
using LanguageExt.Common;
using MediatR;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using Suspectus.Gandalf.Core.Abstractions.CQRS;
using Suspectus.Gandalf.Core.Abstractions.Extensions;
using Suspectus.Gandalf.Palantir.Api.Commands;
using Suspectus.Gandalf.Palantir.Api.Handlers.Commands;
using Suspectus.Gandalf.Palantir.Api.Extensions;
using Suspectus.Gandalf.Palantir.Data.Database;
using Suspectus.Gandalf.Palantir.Data.Dto;
namespace Suspectus.Gandalf.Palantir.Api.Handlers.Security;
public class PasswordHashingHandler(IPasswordHasher<object> passwordHasher) : IGrCommandHandler<HashPasswordCommand, Result<string>>
public class PasswordHashingHandler(ApplicationContext applicationContext, IMediator mediator, IPasswordHasher<object> passwordHasher) : ICommandHandler<HashPasswordCommand, string>, ICommandHandler<VerifyPasswordCommand, bool>
{
private static readonly object MicrosoftIsAMeme = new();
@ -23,4 +28,49 @@ public class PasswordHashingHandler(IPasswordHasher<object> passwordHasher) : IG
}
}
public async Task<Result<bool>> Handle(VerifyPasswordCommand request, CancellationToken cancellationToken)
{
var signIn = await applicationContext.SignIns.SingleOrDefaultAsync(x => x.Id == request.SignInId, cancellationToken: cancellationToken);
if (signIn is null)
{
return "Sign-in method not found.".AsErrorResult<bool>();
}
if (signIn.PasswordHash is null)
{
return "Sign-in method does not have a password hash.".AsErrorResult<bool>();
}
var verification = passwordHasher.VerifyHashedPassword(MicrosoftIsAMeme, signIn.PasswordHash, request.RawPassword);
switch (verification)
{
case PasswordVerificationResult.Failed:
return false;
case PasswordVerificationResult.Success:
return true;
case PasswordVerificationResult.SuccessRehashNeeded:
var newHashResult = await mediator.Send(new HashPasswordCommand { RawPassword = request.RawPassword }, cancellationToken);
if (newHashResult.IsFaulted)
{
return newHashResult.AsErrorResult<bool, string>();
}
var newHash = newHashResult.GetValue();
signIn.PasswordHash = newHash;
await applicationContext.SaveChangesAsync(cancellationToken);
return true;
default:
return $"Password verification type not supported: {verification}".AsErrorResult<bool>();
}
}
}

View File

@ -1,8 +1,8 @@
using MediatR;
using Microsoft.EntityFrameworkCore;
using Suspectus.Gandalf.Core.Abstractions.Extensions;
using Suspectus.Gandalf.Palantir.Abstractions;
using Suspectus.Gandalf.Palantir.Api.Commands;
using Suspectus.Gandalf.Palantir.Api.Extensions;
using Suspectus.Gandalf.Palantir.Data.Database;
using Suspectus.Gandalf.Palantir.Data.Entities.App;
using Suspectus.Gandalf.Palantir.Data.Entities.Base;
@ -48,7 +48,7 @@ public class InitService
private async Task InitializeMaster()
{
var masterTenant = await _applicationContext.Tenants.SingleOrDefaultAsync(x => x.Id == 0);
var masterTenant = await _applicationContext.Tenants.SingleOrDefaultAsync(x => x.IsMaster);
if (masterTenant is not null)
return;
@ -92,6 +92,7 @@ public class InitService
{
Visibility = EntityVisibility.Active,
Name = "Master",
IsMaster = true,
OwnerId = housemasterUser.Id!.Value,
};

View File

@ -22,6 +22,8 @@
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Suspectus.Gandalf.Core.Abstractions\Suspectus.Gandalf.Core.Abstractions.csproj" />
<ProjectReference Include="..\Suspectus.Gandalf.Palantir.Contracts\Suspectus.Gandalf.Palantir.Contracts.csproj" />
<ProjectReference Include="..\Suspectus.Gandalf.Palantir.Data\Suspectus.Gandalf.Palantir.Data.csproj" />
<ProjectReference Include="..\Suspectus.Gandalf.Palantir.Security\Suspectus.Gandalf.Palantir.Security.csproj" />
</ItemGroup>

View File

@ -36,7 +36,8 @@ public class TestController(IHashids hashids, ApplicationContext context, Invoke
{
Visibility = EntityVisibility.Active,
OwnerId = invoker.SubjectId,
Name = command.Name
Name = command.Name,
IsMaster = false
},
SubjectId = invoker.SubjectId,
InternalAuthorities = authorities.ToHashSet()

View File

@ -0,0 +1,14 @@
using Microsoft.Extensions.DependencyInjection;
namespace Suspectus.Gandalf.Palantir.Client.Extensions;
public static class ServiceCollectionExtensions
{
public static IServiceCollection AddPalantirClient(this IServiceCollection services)
{
services.AddHttpClient<IPalantirAuthClient, PalantirAuthClient>(opt => opt.BaseAddress = new Uri("https://localhost:7269/")); // TODO: WTF BRO WHY YOU DONT WORK
services.AddScoped<IPalantirAuthClient, PalantirAuthClient>();
services.AddScoped<IPalantirClient, PalantirClient>();
return services;
}
}

View File

@ -0,0 +1,56 @@
using System.Net.Http.Json;
using LanguageExt.Common;
using Suspectus.Gandalf.Core.Abstractions.Extensions;
using Suspectus.Gandalf.Palantir.Contracts.Controller.Auth;
namespace Suspectus.Gandalf.Palantir.Client;
public interface IPalantirClient
{
IPalantirAuthClient Auth { get; init; }
}
public interface IPalantirAuthClient
{
public Task<Result<bool>> ValidateCredentials(ValidateCredentialsCommand validateCredentialsCommand);
}
public class PalantirClient : IPalantirClient
{
public PalantirClient(IPalantirAuthClient authClient)
{
Auth = authClient;
}
public IPalantirAuthClient Auth { get; init; }
}
public class PalantirAuthClient : IPalantirAuthClient
{
private readonly HttpClient _http;
public PalantirAuthClient(HttpClient http)
{
_http = http;
}
public async Task<Result<bool>> ValidateCredentials(ValidateCredentialsCommand validateCredentialsCommand)
{
try
{
var response = await _http.PostAsJsonAsync("validate-credentials", validateCredentialsCommand);
if (!response.IsSuccessStatusCode)
{
return $"status: {response.StatusCode}".AsErrorResult<bool>();
}
var result = await response.Content.ReadFromJsonAsync<bool>();
return result;
}
catch (HttpRequestException e)
{
return $"status: {e.StatusCode} message: {e.Message}".AsErrorResult<bool>();
}
}
}

View File

@ -0,0 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Suspectus.Gandalf.Palantir.Contracts\Suspectus.Gandalf.Palantir.Contracts.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Http" Version="9.0.5" />
</ItemGroup>
</Project>

View File

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

View File

@ -0,0 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Suspectus.Gandalf.Core.Abstractions\Suspectus.Gandalf.Core.Abstractions.csproj" />
</ItemGroup>
</Project>

View File

@ -12,6 +12,7 @@ public abstract class CoreContext<T>(DbContextOptions<T> options) : DbContext(op
{
public DbSet<TenantEntity> Tenants { get; set; }
public DbSet<SubjectEntity> Subjects { get; set; }
public DbSet<SignInEntity> SignIns { get; set; }
public DbSet<AppEntity> Apps { get; set; }
public DbSet<TenantSubjectRelationEntity> TenantSubjectRelations { get; set; }
public DbSet<AppSubjectRelationEntity> AppSubjectRelations { get; set; }

View File

@ -25,4 +25,5 @@ public class TenantVersionEntity : TenantData, IVersionEntity<TenantEntity>
public abstract class TenantData : OwnerData
{
public required string Name { get; set; }
public required bool IsMaster { get; set; }
}

View File

@ -0,0 +1,724 @@
// <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("20250525174938_addIsMaster")]
partial class addIsMaster
{
/// <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>("TokenType")
.IsRequired()
.HasColumnType("text");
b.Property<long>("UsedBy")
.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 addIsMaster : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<bool>(
name: "IsMaster",
schema: "gr",
table: "TenantVersion",
type: "boolean",
nullable: false,
defaultValue: false);
migrationBuilder.AddColumn<bool>(
name: "IsMaster",
schema: "gr",
table: "Tenant",
type: "boolean",
nullable: false,
defaultValue: false);
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "IsMaster",
schema: "gr",
table: "TenantVersion");
migrationBuilder.DropColumn(
name: "IsMaster",
schema: "gr",
table: "Tenant");
}
}
}

View File

@ -386,6 +386,9 @@ namespace W542.GandalfReborn.Data.Migrations
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
b.Property<bool>("IsMaster")
.HasColumnType("boolean");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("text");
@ -456,6 +459,9 @@ namespace W542.GandalfReborn.Data.Migrations
.IsRequired()
.HasColumnType("text");
b.Property<bool>("IsMaster")
.HasColumnType("boolean");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("text");

View File

@ -20,6 +20,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Suspectus.Gandalf.Core.Abst
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Suspectus.Gandalf.Bridgekeeper.Contracts", "Suspectus.Gandalf.Bridgekeeper.Contracts\Suspectus.Gandalf.Bridgekeeper.Contracts.csproj", "{4ED2E3BC-FE29-4041-8274-987EB60BD5FF}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Suspectus.Gandalf.Palantir.Client", "Suspectus.Gandalf.Palantir.Client\Suspectus.Gandalf.Palantir.Client.csproj", "{1161299F-3623-42C9-9A8D-AB7664BF5FD6}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Suspectus.Gandalf.Palantir.Contracts", "Suspectus.Gandalf.Palantir.Contracts\Suspectus.Gandalf.Palantir.Contracts.csproj", "{AEE0EFB9-CA30-47CE-B494-C46A08DDEC72}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -54,6 +58,14 @@ Global
{4ED2E3BC-FE29-4041-8274-987EB60BD5FF}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4ED2E3BC-FE29-4041-8274-987EB60BD5FF}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4ED2E3BC-FE29-4041-8274-987EB60BD5FF}.Release|Any CPU.Build.0 = Release|Any CPU
{1161299F-3623-42C9-9A8D-AB7664BF5FD6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{1161299F-3623-42C9-9A8D-AB7664BF5FD6}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1161299F-3623-42C9-9A8D-AB7664BF5FD6}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1161299F-3623-42C9-9A8D-AB7664BF5FD6}.Release|Any CPU.Build.0 = Release|Any CPU
{AEE0EFB9-CA30-47CE-B494-C46A08DDEC72}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{AEE0EFB9-CA30-47CE-B494-C46A08DDEC72}.Debug|Any CPU.Build.0 = Debug|Any CPU
{AEE0EFB9-CA30-47CE-B494-C46A08DDEC72}.Release|Any CPU.ActiveCfg = Release|Any CPU
{AEE0EFB9-CA30-47CE-B494-C46A08DDEC72}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{44C283FD-2E01-496C-9E8F-4E43C26C9733} = {A82EA24B-1379-41B2-A363-CDCBF9F18833}
@ -63,5 +75,7 @@ Global
{264B8A9E-7A70-4DE3-906B-7E0722D09205} = {D54D8A40-A5C3-4273-B4D7-F2B83056161B}
{F8333692-CA81-4298-A2F5-CF7D3ACCE230} = {3E672AE3-D2E4-49C0-AB18-65E799E5277A}
{4ED2E3BC-FE29-4041-8274-987EB60BD5FF} = {D54D8A40-A5C3-4273-B4D7-F2B83056161B}
{AEE0EFB9-CA30-47CE-B494-C46A08DDEC72} = {A82EA24B-1379-41B2-A363-CDCBF9F18833}
{1161299F-3623-42C9-9A8D-AB7664BF5FD6} = {A82EA24B-1379-41B2-A363-CDCBF9F18833}
EndGlobalSection
EndGlobal