Work on indieauth protocol, fix PKCE being always required

This commit is contained in:
Saphire 2023-10-29 09:18:37 +06:00
parent a4742a8250
commit b62cf121c8
24 changed files with 3202 additions and 128 deletions

View File

@ -7,9 +7,9 @@ using System.Text;
using System.Web;
using Lunar.Exchange.Lapis.Data;
using Lunar.Exchange.Lapis.Data.Models;
using Lunar.Exchange.Lapis.Data.Models.IndieAuth;
using Lunar.Exchange.Lapis.Models;
using Lunar.Exchange.Lapis.Utilities;
//using Lunar.Exchange.Lapis.Models;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Cors;
@ -43,8 +43,10 @@ public class IndieauthController : ControllerBase
}
[HttpGet("metadata")]
[HttpGet("/.well-known/openid-configuration")]
[HttpGet("/.well-known/oauth-authorization-server")]
[EnableCors("Public")]
[SnakeCaseConverterAttribute]
[SnakeCaseConverter]
public IActionResult Metadata()
{
return Ok(new
@ -69,6 +71,7 @@ public class IndieauthController : ControllerBase
});
}
[HttpGet("authorize")]
[HttpGet("/indieauth")]
public IActionResult RedirectToConsentPage()
{
@ -80,7 +83,8 @@ public class IndieauthController : ControllerBase
[HttpPost("authorize")]
[HttpPost("/indieauth")]
[SnakeCaseConverterAttribute]
[EnableCors("Public")]
[SnakeCaseConverter]
[Consumes("application/x-www-form-urlencoded")]
public async Task<IActionResult> ClientGetProfile([FromForm] IndieauthAuthorizeModel model)
{
@ -89,22 +93,82 @@ public class IndieauthController : ControllerBase
return result;
// Nasty but eh
var requestCode = await DbContext.IndieauthCodes
.Include(c => c.Client)
var requestCode = await DbContext.IndieauthRequests
.Include(c => c.Link)
.ThenInclude(l => l.Profile)
.SingleAsync(
c => c.Value == model.Code
&& c.Client.ClientId == model.ClientId
&& c.Client.RedirectUri == model.RedirectUri
&& c.Link.Client.ClientId == model.ClientId
&& c.Link.Client.RedirectUri == model.RedirectUri
&& c.Claimed != null
);
requestCode.Link.LastSeen = DateTime.UtcNow;
await DbContext.SaveChangesAsync();
return Ok(new
{
Me = requestCode.Client.Profile.ProfileUrl
Me = requestCode.Link.Profile.ProfileUrl
});
}
[HttpPost("token")]
[SnakeCaseConverter(true)]
[Consumes("application/x-www-form-urlencoded")]
public async Task<IActionResult> TokenClaim([FromForm] IndieauthAuthorizeModel model)
{
var result = await AuthorizeClient(model);
if (result is not OkResult)
return result;
// Nasty but eh
var requestCode = await DbContext.IndieauthRequests
.Include(c => c.Link)
.ThenInclude(l => l.Profile)
.Include(c => c.Link.Client)
.SingleAsync(
c => c.Value == model.Code
&& c.Link.Client.ClientId == model.ClientId
&& c.Link.Client.RedirectUri == model.RedirectUri
&& c.Claimed != null
);
if (String.IsNullOrEmpty(requestCode.Link.Scope))
return BadRequest(new { Error = "invalid_grant", ErrorDescription = "Token requested with no scopes" });
var now = DateTime.UtcNow;
requestCode.Link.LastSeen = now;
var token = DbContext.IndieauthTokens.Add(new Token
{
Request = requestCode,
Created = now,
Expires = now.AddMinutes(30),
LastSeen = now,
Value = RandomCode(48),
RefreshToken = RandomCode(48)
}).Entity;
await DbContext.SaveChangesAsync();
var scopes = (requestCode.Link.Scope ?? "").Split(" ");
return Ok(new
{
Me = requestCode.Link.Profile.ProfileUrl,
Scope = requestCode.Link.Scope,
AccessToken = token.Value,
TokenType = "Bearer",
RefreshToken = token.RefreshToken,
ExpiresIn = (token.Expires - token.Created).TotalSeconds,
Profile = (!scopes.Contains("profile") || requestCode.Link.Client.Type != Client.ClientType.IndieAuth) ? null : new
{
Name = requestCode.Link.Profile.Name,
Url = requestCode.Link.Profile.ProfileUrl,
Photo = requestCode.Link.Profile.PhotoUrl
}
});
}
public async Task<IActionResult> AuthorizeClient(IndieauthAuthorizeModel model)
{
@ -117,10 +181,10 @@ public class IndieauthController : ControllerBase
if (model.Code is null || model.ClientId is null || model.RedirectUri is null)
return BadRequest(new { Error = "invalid_request", ErrorMessage = "Missing required parameters" });
var requestCode = await DbContext.IndieauthCodes.SingleOrDefaultAsync(
var requestCode = await DbContext.IndieauthRequests.SingleOrDefaultAsync(
c => c.Value == model.Code
&& c.Client.ClientId == model.ClientId
&& c.Client.RedirectUri == model.RedirectUri
&& c.Link.Client.ClientId == model.ClientId
&& c.Link.Client.RedirectUri == model.RedirectUri
&& c.Claimed == null
);
@ -176,6 +240,7 @@ public class IndieauthController : ControllerBase
return Ok();
}
// Used by the frontend, not for external RP client use
[HttpPost("request")]
[Authorize]
public async Task<IActionResult> AuthRequest(IndieauthRequestModel model)
@ -191,12 +256,53 @@ public class IndieauthController : ControllerBase
if (profile is null)
return NotFound(new { Error = "No such profile for the current user" });
// TODO: support non-indieauth clients (aka Relying Party)
Uri? clientUri = null;
try
{
clientUri = new Uri(model.ClientId);
}
catch (UriFormatException)
{
}
var client = new Uri(model.ClientId);
var redirect = new Uri(model.RedirectUri);
if (redirect.Host != client.Host || redirect.Scheme != client.Scheme || redirect.Port != client.Port)
return BadRequest(new { Error = "Redirect does not match the basic client data, checking not implemented yet, bailing" });
Client? baseClient = null;
if (clientUri is not null)
{
// IndieAuth Client/RP
if (redirect.Host != clientUri.Host || redirect.Scheme != clientUri.Scheme || redirect.Port != clientUri.Port)
return BadRequest(new { Error = "Redirect does not match the basic client data, checking not implemented yet, bailing" });
baseClient = await DbContext.IndieauthClients.SingleOrDefaultAsync(c => c.ClientId == model.ClientId && c.RedirectUri == model.RedirectUri && c.ClientSecret == null);
// For IndieAuth, we can create one
// Though this might caues multiple ones to be made if the redirect URL changes... Though sounds kinda useful?
if (baseClient is null)
baseClient = DbContext.IndieauthClients.Add(new Client
{
ClientId = model.ClientId,
ClientSecret = null,
RedirectUri = model.RedirectUri,
Type = Client.ClientType.IndieAuth
}).Entity;
}
else
{
// Plain OAuth Relying Party
baseClient = await DbContext.IndieauthClients.SingleOrDefaultAsync(c => c.ClientId == model.ClientId && c.RedirectUri == model.RedirectUri && c.ClientSecret != null);
// For OAuth/OIDC proper, do not proceed
if (baseClient is null)
return BadRequest(new { Error = "Unknown clientId for OAuth client" });
}
if (model.CodeChallenge is not null != model.CodeChallengeMethod is not null)
return BadRequest(new { Error = "PKCE options are present, but only in part" });
if (model.CodeChallenge is not null && model.CodeChallengeMethod is not null)
{
@ -209,47 +315,24 @@ public class IndieauthController : ControllerBase
if (model.Me is not null && profile.ProfileUrl != model.Me)
return BadRequest(new { Error = "Mismatched profile urls" });
var existing = await DbContext.IndieauthLinks.FirstOrDefaultAsync(l => l.Profile.UserId == user.Id && l.ClientId == model.ClientId);
var randomCode = RandomCode(48);
// Probably not the best...
string? randomCode = null;
const string codeAlphabet = "abcdefghijklmnopqrtuvwxyzABCDEFGHIJKLMNOPQRTUVWXYZ1234567890!@#$%^&*()[]{}\\|-_;:'\",.`~ ";
const int size = 48;
using (var rng = RandomNumberGenerator.Create())
{
byte[] randomBytes = new byte[4 * size];
rng.GetBytes(randomBytes);
StringBuilder result = new StringBuilder(size);
for (int i = 0; i < size; i++)
var linkEntity = await DbContext.IndieauthLinks.FirstOrDefaultAsync(l => l.Profile.UserId == user.Id && l.Client.ClientId == model.ClientId);
if (linkEntity is null)
linkEntity = DbContext.IndieauthLinks.Add(new ClientLink
{
var rnd = BitConverter.ToUInt32(randomBytes, i * 4);
var idx = rnd % codeAlphabet.Length;
ProfileId = profile.Id,
Client = baseClient,
result.Append(codeAlphabet[((int)idx)]);
}
randomCode = result.ToString();
}
if (randomCode is null || randomCode.Length != size)
throw new Exception("Code creation error");
var linkEntity = existing ?? DbContext.IndieauthLinks.Add(new IndieauthClient
{
ProfileId = profile.Id,
ClientId = model.ClientId,
RedirectUri = model.RedirectUri,
FirstSeen = DateTime.UtcNow,
Scope = model.Scope
}).Entity;
FirstSeen = DateTime.UtcNow,
Scope = model.Scope
}).Entity;
linkEntity.LastSeen = DateTime.UtcNow;
DbContext.IndieauthCodes.Add(new IndieauthRequest
DbContext.IndieauthRequests.Add(new Request
{
Client = linkEntity,
Link = linkEntity,
Value = randomCode,
CodeChallenge = model.CodeChallenge,
@ -268,4 +351,34 @@ public class IndieauthController : ControllerBase
return Ok(new { Message = "Successfully authorized", RedirectUri = redirectTo.ToString() });
}
private string RandomCode(int size)
{
if (size < 32)
throw new Exception("Code length too low");
// Probably not the best...
string? randomCode = null;
const string codeAlphabet = "abcdefghijklmnopqrtuvwxyzABCDEFGHIJKLMNOPQRTUVWXYZ1234567890!@#$%^&*()[]{}\\|-_;:'\",.`~ ";
using (var rng = RandomNumberGenerator.Create())
{
byte[] randomBytes = new byte[4 * size];
rng.GetBytes(randomBytes);
StringBuilder result = new StringBuilder(size);
for (int i = 0; i < size; i++)
{
var rnd = BitConverter.ToUInt32(randomBytes, i * 4);
// This will clip off upper parts of the alphabet when the value is high enough... but probably negligible for 32 bit
var idx = rnd % codeAlphabet.Length;
result.Append(codeAlphabet[((int)idx)]);
}
randomCode = result.ToString();
}
if (randomCode is null || randomCode.Length != size)
throw new Exception("Code creation error");
return randomCode;
}
}

View File

@ -3,7 +3,6 @@ namespace Lunar.Exchange.Lapis.Controllers;
using System.Security.Claims;
using Lunar.Exchange.Lapis.Data;
using Lunar.Exchange.Lapis.Models;
//using Lunar.Exchange.Lapis.Models;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
@ -74,7 +73,7 @@ public class UserController : ControllerBase
{
if (User.Identity is null)
return Ok(new { });
var user = await UserManager.FindByNameAsync(User.Identity!.Name);
var user = await UserManager.FindByNameAsync(User.Identity!.Name!);
if (user is null)
return Ok(new { });
@ -104,28 +103,33 @@ public class UserController : ControllerBase
[HttpGet("")]
public async Task<IActionResult> UserInfo()
{
var user = await UserManager.FindByNameAsync(User.Identity!.Name);
// User should not be null given this is behind [Authorized]
var user = await UserManager.FindByNameAsync(User.Identity!.Name!);
var profiles = await DbContext.UserProfiles
.Where(u => u.UserId == user.Id)
.Where(u => u.UserId == user!.Id)
.Select(p => new
{
p.Id,
p.ProfileUrl,
Clients = p.Clients.Select(c => new
{
c.Id,
c.ClientId,
c.RedirectUri,
c.Scope,
c.FirstSeen,
c.LastSeen
c.LastSeen,
Client = new
{
c.Client.Id,
c.Client.ClientId,
c.Client.RedirectUri,
c.Client.Type
}
})
})
.ToListAsync();
return Ok(new
{
Username = user.UserName,
Username = user!.UserName,
Profiles = profiles
});
}

View File

@ -8,8 +8,10 @@ using Microsoft.EntityFrameworkCore;
public class LapisContext : IdentityDbContext<IdentityUser>
{
public DbSet<IndieauthClient> IndieauthLinks { get; set; } = null!;
public DbSet<IndieauthRequest> IndieauthCodes { get; set; } = null!;
public DbSet<Models.IndieAuth.Client> IndieauthClients { get; set; } = null!;
public DbSet<Models.IndieAuth.ClientLink> IndieauthLinks { get; set; } = null!;
public DbSet<Models.IndieAuth.Request> IndieauthRequests { get; set; } = null!;
public DbSet<Models.IndieAuth.Token> IndieauthTokens { get; set; } = null!;
public DbSet<UserProfile> UserProfiles { get; set; } = null!;
public LapisContext(DbContextOptions<LapisContext> options) : base(options) { }

View File

@ -0,0 +1,400 @@
// <auto-generated />
using System;
using Lunar.Exchange.Lapis.Data;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
#nullable disable
namespace Lunar.Exchange.Lapis.Data.Migrations
{
[DbContext(typeof(LapisContext))]
[Migration("20230531162534_OptionalPKCE")]
partial class OptionalPKCE
{
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder.HasAnnotation("ProductVersion", "6.0.4");
modelBuilder.Entity("Lunar.Exchange.Lapis.Data.Models.IndieauthClient", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<string>("ClientId")
.IsRequired()
.HasColumnType("TEXT");
b.Property<DateTime>("FirstSeen")
.HasColumnType("TEXT");
b.Property<DateTime>("LastSeen")
.HasColumnType("TEXT");
b.Property<int>("ProfileId")
.HasColumnType("INTEGER");
b.Property<string>("RedirectUri")
.IsRequired()
.HasColumnType("TEXT");
b.Property<string>("Scope")
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("ProfileId");
b.ToTable("IndieauthLinks");
});
modelBuilder.Entity("Lunar.Exchange.Lapis.Data.Models.IndieauthRequest", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<DateTime?>("Claimed")
.HasColumnType("TEXT");
b.Property<int>("ClientId")
.HasColumnType("INTEGER");
b.Property<string>("CodeChallenge")
.HasColumnType("TEXT");
b.Property<string>("CodeChallengeMethod")
.HasColumnType("TEXT");
b.Property<DateTime>("Created")
.HasColumnType("TEXT");
b.Property<string>("Value")
.IsRequired()
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("ClientId");
b.ToTable("IndieauthCodes");
});
modelBuilder.Entity("Lunar.Exchange.Lapis.Data.Models.UserProfile", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<string>("ProfileUrl")
.IsRequired()
.HasColumnType("TEXT");
b.Property<string>("UserId")
.IsRequired()
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("UserId");
b.ToTable("UserProfiles");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b =>
{
b.Property<string>("Id")
.HasColumnType("TEXT");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.HasColumnType("TEXT");
b.Property<string>("Name")
.HasMaxLength(256)
.HasColumnType("TEXT");
b.Property<string>("NormalizedName")
.HasMaxLength(256)
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("NormalizedName")
.IsUnique()
.HasDatabaseName("RoleNameIndex");
b.ToTable("AspNetRoles", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<string>("ClaimType")
.HasColumnType("TEXT");
b.Property<string>("ClaimValue")
.HasColumnType("TEXT");
b.Property<string>("RoleId")
.IsRequired()
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("RoleId");
b.ToTable("AspNetRoleClaims", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUser", b =>
{
b.Property<string>("Id")
.HasColumnType("TEXT");
b.Property<int>("AccessFailedCount")
.HasColumnType("INTEGER");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.HasColumnType("TEXT");
b.Property<string>("Email")
.HasMaxLength(256)
.HasColumnType("TEXT");
b.Property<bool>("EmailConfirmed")
.HasColumnType("INTEGER");
b.Property<bool>("LockoutEnabled")
.HasColumnType("INTEGER");
b.Property<DateTimeOffset?>("LockoutEnd")
.HasColumnType("TEXT");
b.Property<string>("NormalizedEmail")
.HasMaxLength(256)
.HasColumnType("TEXT");
b.Property<string>("NormalizedUserName")
.HasMaxLength(256)
.HasColumnType("TEXT");
b.Property<string>("PasswordHash")
.HasColumnType("TEXT");
b.Property<string>("PhoneNumber")
.HasColumnType("TEXT");
b.Property<bool>("PhoneNumberConfirmed")
.HasColumnType("INTEGER");
b.Property<string>("SecurityStamp")
.HasColumnType("TEXT");
b.Property<bool>("TwoFactorEnabled")
.HasColumnType("INTEGER");
b.Property<string>("UserName")
.HasMaxLength(256)
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("NormalizedEmail")
.HasDatabaseName("EmailIndex");
b.HasIndex("NormalizedUserName")
.IsUnique()
.HasDatabaseName("UserNameIndex");
b.ToTable("AspNetUsers", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<string>("ClaimType")
.HasColumnType("TEXT");
b.Property<string>("ClaimValue")
.HasColumnType("TEXT");
b.Property<string>("UserId")
.IsRequired()
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("UserId");
b.ToTable("AspNetUserClaims", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
{
b.Property<string>("LoginProvider")
.HasMaxLength(128)
.HasColumnType("TEXT");
b.Property<string>("ProviderKey")
.HasMaxLength(128)
.HasColumnType("TEXT");
b.Property<string>("ProviderDisplayName")
.HasColumnType("TEXT");
b.Property<string>("UserId")
.IsRequired()
.HasColumnType("TEXT");
b.HasKey("LoginProvider", "ProviderKey");
b.HasIndex("UserId");
b.ToTable("AspNetUserLogins", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
{
b.Property<string>("UserId")
.HasColumnType("TEXT");
b.Property<string>("RoleId")
.HasColumnType("TEXT");
b.HasKey("UserId", "RoleId");
b.HasIndex("RoleId");
b.ToTable("AspNetUserRoles", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
{
b.Property<string>("UserId")
.HasColumnType("TEXT");
b.Property<string>("LoginProvider")
.HasMaxLength(128)
.HasColumnType("TEXT");
b.Property<string>("Name")
.HasMaxLength(128)
.HasColumnType("TEXT");
b.Property<string>("Value")
.HasColumnType("TEXT");
b.HasKey("UserId", "LoginProvider", "Name");
b.ToTable("AspNetUserTokens", (string)null);
});
modelBuilder.Entity("Lunar.Exchange.Lapis.Data.Models.IndieauthClient", b =>
{
b.HasOne("Lunar.Exchange.Lapis.Data.Models.UserProfile", "Profile")
.WithMany("Clients")
.HasForeignKey("ProfileId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Profile");
});
modelBuilder.Entity("Lunar.Exchange.Lapis.Data.Models.IndieauthRequest", b =>
{
b.HasOne("Lunar.Exchange.Lapis.Data.Models.IndieauthClient", "Client")
.WithMany("Codes")
.HasForeignKey("ClientId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Client");
});
modelBuilder.Entity("Lunar.Exchange.Lapis.Data.Models.UserProfile", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", "User")
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("User");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
.WithMany()
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
.WithMany()
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Lunar.Exchange.Lapis.Data.Models.IndieauthClient", b =>
{
b.Navigation("Codes");
});
modelBuilder.Entity("Lunar.Exchange.Lapis.Data.Models.UserProfile", b =>
{
b.Navigation("Clients");
});
#pragma warning restore 612, 618
}
}
}

View File

@ -0,0 +1,51 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Lunar.Exchange.Lapis.Data.Migrations
{
public partial class OptionalPKCE : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AlterColumn<string>(
name: "CodeChallengeMethod",
table: "IndieauthCodes",
type: "TEXT",
nullable: true,
oldClrType: typeof(string),
oldType: "TEXT");
migrationBuilder.AlterColumn<string>(
name: "CodeChallenge",
table: "IndieauthCodes",
type: "TEXT",
nullable: true,
oldClrType: typeof(string),
oldType: "TEXT");
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.AlterColumn<string>(
name: "CodeChallengeMethod",
table: "IndieauthCodes",
type: "TEXT",
nullable: false,
defaultValue: "",
oldClrType: typeof(string),
oldType: "TEXT",
oldNullable: true);
migrationBuilder.AlterColumn<string>(
name: "CodeChallenge",
table: "IndieauthCodes",
type: "TEXT",
nullable: false,
defaultValue: "",
oldClrType: typeof(string),
oldType: "TEXT",
oldNullable: true);
}
}
}

View File

@ -0,0 +1,400 @@
// <auto-generated />
using System;
using Lunar.Exchange.Lapis.Data;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
#nullable disable
namespace Lunar.Exchange.Lapis.Data.Migrations
{
[DbContext(typeof(LapisContext))]
[Migration("20230531165106_RenameTables")]
partial class RenameTables
{
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder.HasAnnotation("ProductVersion", "6.0.4");
modelBuilder.Entity("Lunar.Exchange.Lapis.Data.Models.IndieauthClient", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<string>("ClientId")
.IsRequired()
.HasColumnType("TEXT");
b.Property<DateTime>("FirstSeen")
.HasColumnType("TEXT");
b.Property<DateTime>("LastSeen")
.HasColumnType("TEXT");
b.Property<int>("ProfileId")
.HasColumnType("INTEGER");
b.Property<string>("RedirectUri")
.IsRequired()
.HasColumnType("TEXT");
b.Property<string>("Scope")
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("ProfileId");
b.ToTable("IndieauthClients");
});
modelBuilder.Entity("Lunar.Exchange.Lapis.Data.Models.IndieauthRequest", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<DateTime?>("Claimed")
.HasColumnType("TEXT");
b.Property<int>("ClientId")
.HasColumnType("INTEGER");
b.Property<string>("CodeChallenge")
.HasColumnType("TEXT");
b.Property<string>("CodeChallengeMethod")
.HasColumnType("TEXT");
b.Property<DateTime>("Created")
.HasColumnType("TEXT");
b.Property<string>("Value")
.IsRequired()
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("ClientId");
b.ToTable("IndieauthRequests");
});
modelBuilder.Entity("Lunar.Exchange.Lapis.Data.Models.UserProfile", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<string>("ProfileUrl")
.IsRequired()
.HasColumnType("TEXT");
b.Property<string>("UserId")
.IsRequired()
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("UserId");
b.ToTable("UserProfiles");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b =>
{
b.Property<string>("Id")
.HasColumnType("TEXT");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.HasColumnType("TEXT");
b.Property<string>("Name")
.HasMaxLength(256)
.HasColumnType("TEXT");
b.Property<string>("NormalizedName")
.HasMaxLength(256)
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("NormalizedName")
.IsUnique()
.HasDatabaseName("RoleNameIndex");
b.ToTable("AspNetRoles", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<string>("ClaimType")
.HasColumnType("TEXT");
b.Property<string>("ClaimValue")
.HasColumnType("TEXT");
b.Property<string>("RoleId")
.IsRequired()
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("RoleId");
b.ToTable("AspNetRoleClaims", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUser", b =>
{
b.Property<string>("Id")
.HasColumnType("TEXT");
b.Property<int>("AccessFailedCount")
.HasColumnType("INTEGER");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.HasColumnType("TEXT");
b.Property<string>("Email")
.HasMaxLength(256)
.HasColumnType("TEXT");
b.Property<bool>("EmailConfirmed")
.HasColumnType("INTEGER");
b.Property<bool>("LockoutEnabled")
.HasColumnType("INTEGER");
b.Property<DateTimeOffset?>("LockoutEnd")
.HasColumnType("TEXT");
b.Property<string>("NormalizedEmail")
.HasMaxLength(256)
.HasColumnType("TEXT");
b.Property<string>("NormalizedUserName")
.HasMaxLength(256)
.HasColumnType("TEXT");
b.Property<string>("PasswordHash")
.HasColumnType("TEXT");
b.Property<string>("PhoneNumber")
.HasColumnType("TEXT");
b.Property<bool>("PhoneNumberConfirmed")
.HasColumnType("INTEGER");
b.Property<string>("SecurityStamp")
.HasColumnType("TEXT");
b.Property<bool>("TwoFactorEnabled")
.HasColumnType("INTEGER");
b.Property<string>("UserName")
.HasMaxLength(256)
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("NormalizedEmail")
.HasDatabaseName("EmailIndex");
b.HasIndex("NormalizedUserName")
.IsUnique()
.HasDatabaseName("UserNameIndex");
b.ToTable("AspNetUsers", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<string>("ClaimType")
.HasColumnType("TEXT");
b.Property<string>("ClaimValue")
.HasColumnType("TEXT");
b.Property<string>("UserId")
.IsRequired()
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("UserId");
b.ToTable("AspNetUserClaims", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
{
b.Property<string>("LoginProvider")
.HasMaxLength(128)
.HasColumnType("TEXT");
b.Property<string>("ProviderKey")
.HasMaxLength(128)
.HasColumnType("TEXT");
b.Property<string>("ProviderDisplayName")
.HasColumnType("TEXT");
b.Property<string>("UserId")
.IsRequired()
.HasColumnType("TEXT");
b.HasKey("LoginProvider", "ProviderKey");
b.HasIndex("UserId");
b.ToTable("AspNetUserLogins", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
{
b.Property<string>("UserId")
.HasColumnType("TEXT");
b.Property<string>("RoleId")
.HasColumnType("TEXT");
b.HasKey("UserId", "RoleId");
b.HasIndex("RoleId");
b.ToTable("AspNetUserRoles", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
{
b.Property<string>("UserId")
.HasColumnType("TEXT");
b.Property<string>("LoginProvider")
.HasMaxLength(128)
.HasColumnType("TEXT");
b.Property<string>("Name")
.HasMaxLength(128)
.HasColumnType("TEXT");
b.Property<string>("Value")
.HasColumnType("TEXT");
b.HasKey("UserId", "LoginProvider", "Name");
b.ToTable("AspNetUserTokens", (string)null);
});
modelBuilder.Entity("Lunar.Exchange.Lapis.Data.Models.IndieauthClient", b =>
{
b.HasOne("Lunar.Exchange.Lapis.Data.Models.UserProfile", "Profile")
.WithMany("Clients")
.HasForeignKey("ProfileId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Profile");
});
modelBuilder.Entity("Lunar.Exchange.Lapis.Data.Models.IndieauthRequest", b =>
{
b.HasOne("Lunar.Exchange.Lapis.Data.Models.IndieauthClient", "Client")
.WithMany("Codes")
.HasForeignKey("ClientId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Client");
});
modelBuilder.Entity("Lunar.Exchange.Lapis.Data.Models.UserProfile", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", "User")
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("User");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
.WithMany()
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
.WithMany()
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Lunar.Exchange.Lapis.Data.Models.IndieauthClient", b =>
{
b.Navigation("Codes");
});
modelBuilder.Entity("Lunar.Exchange.Lapis.Data.Models.UserProfile", b =>
{
b.Navigation("Clients");
});
#pragma warning restore 612, 618
}
}
}

View File

@ -0,0 +1,135 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Lunar.Exchange.Lapis.Data.Migrations
{
public partial class RenameTables : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropForeignKey(
name: "FK_IndieauthCodes_IndieauthLinks_ClientId",
table: "IndieauthCodes");
migrationBuilder.DropForeignKey(
name: "FK_IndieauthLinks_UserProfiles_ProfileId",
table: "IndieauthLinks");
migrationBuilder.DropPrimaryKey(
name: "PK_IndieauthLinks",
table: "IndieauthLinks");
migrationBuilder.DropPrimaryKey(
name: "PK_IndieauthCodes",
table: "IndieauthCodes");
migrationBuilder.RenameTable(
name: "IndieauthLinks",
newName: "IndieauthClients");
migrationBuilder.RenameTable(
name: "IndieauthCodes",
newName: "IndieauthRequests");
migrationBuilder.RenameIndex(
name: "IX_IndieauthLinks_ProfileId",
table: "IndieauthClients",
newName: "IX_IndieauthClients_ProfileId");
migrationBuilder.RenameIndex(
name: "IX_IndieauthCodes_ClientId",
table: "IndieauthRequests",
newName: "IX_IndieauthRequests_ClientId");
migrationBuilder.AddPrimaryKey(
name: "PK_IndieauthClients",
table: "IndieauthClients",
column: "Id");
migrationBuilder.AddPrimaryKey(
name: "PK_IndieauthRequests",
table: "IndieauthRequests",
column: "Id");
migrationBuilder.AddForeignKey(
name: "FK_IndieauthClients_UserProfiles_ProfileId",
table: "IndieauthClients",
column: "ProfileId",
principalTable: "UserProfiles",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
migrationBuilder.AddForeignKey(
name: "FK_IndieauthRequests_IndieauthClients_ClientId",
table: "IndieauthRequests",
column: "ClientId",
principalTable: "IndieauthClients",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropForeignKey(
name: "FK_IndieauthClients_UserProfiles_ProfileId",
table: "IndieauthClients");
migrationBuilder.DropForeignKey(
name: "FK_IndieauthRequests_IndieauthClients_ClientId",
table: "IndieauthRequests");
migrationBuilder.DropPrimaryKey(
name: "PK_IndieauthRequests",
table: "IndieauthRequests");
migrationBuilder.DropPrimaryKey(
name: "PK_IndieauthClients",
table: "IndieauthClients");
migrationBuilder.RenameTable(
name: "IndieauthRequests",
newName: "IndieauthCodes");
migrationBuilder.RenameTable(
name: "IndieauthClients",
newName: "IndieauthLinks");
migrationBuilder.RenameIndex(
name: "IX_IndieauthRequests_ClientId",
table: "IndieauthCodes",
newName: "IX_IndieauthCodes_ClientId");
migrationBuilder.RenameIndex(
name: "IX_IndieauthClients_ProfileId",
table: "IndieauthLinks",
newName: "IX_IndieauthLinks_ProfileId");
migrationBuilder.AddPrimaryKey(
name: "PK_IndieauthCodes",
table: "IndieauthCodes",
column: "Id");
migrationBuilder.AddPrimaryKey(
name: "PK_IndieauthLinks",
table: "IndieauthLinks",
column: "Id");
migrationBuilder.AddForeignKey(
name: "FK_IndieauthCodes_IndieauthLinks_ClientId",
table: "IndieauthCodes",
column: "ClientId",
principalTable: "IndieauthLinks",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
migrationBuilder.AddForeignKey(
name: "FK_IndieauthLinks_UserProfiles_ProfileId",
table: "IndieauthLinks",
column: "ProfileId",
principalTable: "UserProfiles",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
}
}
}

View File

@ -0,0 +1,440 @@
// <auto-generated />
using System;
using Lunar.Exchange.Lapis.Data;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
#nullable disable
namespace Lunar.Exchange.Lapis.Data.Migrations
{
[DbContext(typeof(LapisContext))]
[Migration("20230602094351_Refactor")]
partial class Refactor
{
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder.HasAnnotation("ProductVersion", "6.0.4");
modelBuilder.Entity("Lunar.Exchange.Lapis.Data.Models.IndieAuth.Client", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<string>("ClientId")
.IsRequired()
.HasColumnType("TEXT");
b.Property<string>("ClientSecret")
.HasColumnType("TEXT");
b.Property<string>("RedirectUri")
.IsRequired()
.HasColumnType("TEXT");
b.Property<int>("Type")
.HasColumnType("INTEGER");
b.HasKey("Id");
b.HasIndex("ClientId")
.IsUnique();
b.ToTable("IndieauthClients");
});
modelBuilder.Entity("Lunar.Exchange.Lapis.Data.Models.IndieAuth.ClientLink", b =>
{
b.Property<int>("ClientId")
.HasColumnType("INTEGER");
b.Property<int>("ProfileId")
.HasColumnType("INTEGER");
b.Property<DateTime>("FirstSeen")
.HasColumnType("TEXT");
b.Property<DateTime>("LastSeen")
.HasColumnType("TEXT");
b.Property<string>("Scope")
.HasColumnType("TEXT");
b.HasKey("ClientId", "ProfileId");
b.HasIndex("ProfileId");
b.ToTable("IndieauthLinks");
});
modelBuilder.Entity("Lunar.Exchange.Lapis.Data.Models.IndieAuth.Request", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<DateTime?>("Claimed")
.HasColumnType("TEXT");
b.Property<string>("CodeChallenge")
.HasColumnType("TEXT");
b.Property<string>("CodeChallengeMethod")
.HasColumnType("TEXT");
b.Property<DateTime>("Created")
.HasColumnType("TEXT");
b.Property<int>("LinkClientId")
.HasColumnType("INTEGER");
b.Property<int>("LinkId")
.HasColumnType("INTEGER");
b.Property<int>("LinkProfileId")
.HasColumnType("INTEGER");
b.Property<string>("Value")
.IsRequired()
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("Value");
b.HasIndex("LinkClientId", "LinkProfileId");
b.ToTable("IndieauthRequests");
});
modelBuilder.Entity("Lunar.Exchange.Lapis.Data.Models.UserProfile", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<string>("ProfileUrl")
.IsRequired()
.HasColumnType("TEXT");
b.Property<string>("UserId")
.IsRequired()
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("UserId");
b.ToTable("UserProfiles");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b =>
{
b.Property<string>("Id")
.HasColumnType("TEXT");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.HasColumnType("TEXT");
b.Property<string>("Name")
.HasMaxLength(256)
.HasColumnType("TEXT");
b.Property<string>("NormalizedName")
.HasMaxLength(256)
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("NormalizedName")
.IsUnique()
.HasDatabaseName("RoleNameIndex");
b.ToTable("AspNetRoles", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<string>("ClaimType")
.HasColumnType("TEXT");
b.Property<string>("ClaimValue")
.HasColumnType("TEXT");
b.Property<string>("RoleId")
.IsRequired()
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("RoleId");
b.ToTable("AspNetRoleClaims", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUser", b =>
{
b.Property<string>("Id")
.HasColumnType("TEXT");
b.Property<int>("AccessFailedCount")
.HasColumnType("INTEGER");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.HasColumnType("TEXT");
b.Property<string>("Email")
.HasMaxLength(256)
.HasColumnType("TEXT");
b.Property<bool>("EmailConfirmed")
.HasColumnType("INTEGER");
b.Property<bool>("LockoutEnabled")
.HasColumnType("INTEGER");
b.Property<DateTimeOffset?>("LockoutEnd")
.HasColumnType("TEXT");
b.Property<string>("NormalizedEmail")
.HasMaxLength(256)
.HasColumnType("TEXT");
b.Property<string>("NormalizedUserName")
.HasMaxLength(256)
.HasColumnType("TEXT");
b.Property<string>("PasswordHash")
.HasColumnType("TEXT");
b.Property<string>("PhoneNumber")
.HasColumnType("TEXT");
b.Property<bool>("PhoneNumberConfirmed")
.HasColumnType("INTEGER");
b.Property<string>("SecurityStamp")
.HasColumnType("TEXT");
b.Property<bool>("TwoFactorEnabled")
.HasColumnType("INTEGER");
b.Property<string>("UserName")
.HasMaxLength(256)
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("NormalizedEmail")
.HasDatabaseName("EmailIndex");
b.HasIndex("NormalizedUserName")
.IsUnique()
.HasDatabaseName("UserNameIndex");
b.ToTable("AspNetUsers", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<string>("ClaimType")
.HasColumnType("TEXT");
b.Property<string>("ClaimValue")
.HasColumnType("TEXT");
b.Property<string>("UserId")
.IsRequired()
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("UserId");
b.ToTable("AspNetUserClaims", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
{
b.Property<string>("LoginProvider")
.HasMaxLength(128)
.HasColumnType("TEXT");
b.Property<string>("ProviderKey")
.HasMaxLength(128)
.HasColumnType("TEXT");
b.Property<string>("ProviderDisplayName")
.HasColumnType("TEXT");
b.Property<string>("UserId")
.IsRequired()
.HasColumnType("TEXT");
b.HasKey("LoginProvider", "ProviderKey");
b.HasIndex("UserId");
b.ToTable("AspNetUserLogins", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
{
b.Property<string>("UserId")
.HasColumnType("TEXT");
b.Property<string>("RoleId")
.HasColumnType("TEXT");
b.HasKey("UserId", "RoleId");
b.HasIndex("RoleId");
b.ToTable("AspNetUserRoles", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
{
b.Property<string>("UserId")
.HasColumnType("TEXT");
b.Property<string>("LoginProvider")
.HasMaxLength(128)
.HasColumnType("TEXT");
b.Property<string>("Name")
.HasMaxLength(128)
.HasColumnType("TEXT");
b.Property<string>("Value")
.HasColumnType("TEXT");
b.HasKey("UserId", "LoginProvider", "Name");
b.ToTable("AspNetUserTokens", (string)null);
});
modelBuilder.Entity("Lunar.Exchange.Lapis.Data.Models.IndieAuth.ClientLink", b =>
{
b.HasOne("Lunar.Exchange.Lapis.Data.Models.IndieAuth.Client", "Client")
.WithMany("Users")
.HasForeignKey("ClientId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Lunar.Exchange.Lapis.Data.Models.UserProfile", "Profile")
.WithMany("Clients")
.HasForeignKey("ProfileId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Client");
b.Navigation("Profile");
});
modelBuilder.Entity("Lunar.Exchange.Lapis.Data.Models.IndieAuth.Request", b =>
{
b.HasOne("Lunar.Exchange.Lapis.Data.Models.IndieAuth.ClientLink", "Link")
.WithMany("Codes")
.HasForeignKey("LinkClientId", "LinkProfileId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Link");
});
modelBuilder.Entity("Lunar.Exchange.Lapis.Data.Models.UserProfile", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", "User")
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("User");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
.WithMany()
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
.WithMany()
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Lunar.Exchange.Lapis.Data.Models.IndieAuth.Client", b =>
{
b.Navigation("Users");
});
modelBuilder.Entity("Lunar.Exchange.Lapis.Data.Models.IndieAuth.ClientLink", b =>
{
b.Navigation("Codes");
});
modelBuilder.Entity("Lunar.Exchange.Lapis.Data.Models.UserProfile", b =>
{
b.Navigation("Clients");
});
#pragma warning restore 612, 618
}
}
}

View File

@ -0,0 +1,244 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Lunar.Exchange.Lapis.Data.Migrations
{
public partial class Refactor : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropForeignKey(
name: "FK_IndieauthClients_UserProfiles_ProfileId",
table: "IndieauthClients");
migrationBuilder.DropForeignKey(
name: "FK_IndieauthRequests_IndieauthClients_ClientId",
table: "IndieauthRequests");
migrationBuilder.DropIndex(
name: "IX_IndieauthRequests_ClientId",
table: "IndieauthRequests");
migrationBuilder.DropIndex(
name: "IX_IndieauthClients_ProfileId",
table: "IndieauthClients");
migrationBuilder.CreateTable(
name: "IndieauthLinks",
columns: table => new
{
ClientId = table.Column<int>(type: "INTEGER", nullable: false),
ProfileId = table.Column<int>(type: "INTEGER", nullable: false),
Scope = table.Column<string>(type: "TEXT", nullable: true),
FirstSeen = table.Column<DateTime>(type: "TEXT", nullable: false),
LastSeen = table.Column<DateTime>(type: "TEXT", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_IndieauthLinks", x => new { x.ClientId, x.ProfileId });
table.ForeignKey(
name: "FK_IndieauthLinks_IndieauthClients_ClientId",
column: x => x.ClientId,
principalTable: "IndieauthClients",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_IndieauthLinks_UserProfiles_ProfileId",
column: x => x.ProfileId,
principalTable: "UserProfiles",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.Sql("""
INSERT INTO "IndieauthLinks" ("ClientId", "ProfileId", "FirstSeen", "LastSeen", "Scope")
SELECT "Id", "ProfileId", "FirstSeen", "LastSeen", "Scope"
FROM "IndieauthClients"
""");
migrationBuilder.DropColumn(
name: "FirstSeen",
table: "IndieauthClients");
migrationBuilder.DropColumn(
name: "LastSeen",
table: "IndieauthClients");
migrationBuilder.RenameColumn(
name: "ClientId",
table: "IndieauthRequests",
newName: "LinkClientId");
migrationBuilder.DropColumn(
name: "Scope",
table: "IndieauthClients");
migrationBuilder.AddColumn<string>(
name: "ClientSecret",
table: "IndieauthClients",
type: "TEXT",
nullable: true);
migrationBuilder.DropColumn(
name: "ProfileId",
table: "IndieauthClients");
migrationBuilder.AddColumn<int>(
name: "Type",
table: "IndieauthClients",
type: "INTEGER",
nullable: false,
defaultValue: 0);
migrationBuilder.AddColumn<int>(
name: "LinkProfileId",
table: "IndieauthRequests",
type: "INTEGER",
nullable: false,
defaultValue: 0);
migrationBuilder.Sql("""
UPDATE IndieauthRequests
SET LinkProfileId = (
SELECT C.ProfileId
FROM IndieauthLinks C
JOIN IndieauthRequests R ON R.LinkClientId=C.ClientId
);
""");
migrationBuilder.AddColumn<int>(
name: "LinkId",
table: "IndieauthRequests",
type: "INTEGER",
nullable: false,
defaultValue: 0);
migrationBuilder.CreateIndex(
name: "IX_IndieauthRequests_LinkClientId_LinkProfileId",
table: "IndieauthRequests",
columns: new[] { "LinkClientId", "LinkProfileId" });
migrationBuilder.CreateIndex(
name: "IX_IndieauthRequests_Value",
table: "IndieauthRequests",
column: "Value");
migrationBuilder.CreateIndex(
name: "IX_IndieauthClients_ClientId",
table: "IndieauthClients",
column: "ClientId",
unique: true);
migrationBuilder.CreateIndex(
name: "IX_IndieauthLinks_ProfileId",
table: "IndieauthLinks",
column: "ProfileId");
migrationBuilder.AddForeignKey(
name: "FK_IndieauthRequests_IndieauthLinks_LinkClientId_LinkProfileId",
table: "IndieauthRequests",
columns: new[] { "LinkClientId", "LinkProfileId" },
principalTable: "IndieauthLinks",
principalColumns: new[] { "ClientId", "ProfileId" },
onDelete: ReferentialAction.Cascade);
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropForeignKey(
name: "FK_IndieauthRequests_IndieauthLinks_LinkClientId_LinkProfileId",
table: "IndieauthRequests");
migrationBuilder.DropTable(
name: "IndieauthLinks");
migrationBuilder.DropIndex(
name: "IX_IndieauthRequests_LinkClientId_LinkProfileId",
table: "IndieauthRequests");
migrationBuilder.DropIndex(
name: "IX_IndieauthRequests_Value",
table: "IndieauthRequests");
migrationBuilder.DropIndex(
name: "IX_IndieauthClients_ClientId",
table: "IndieauthClients");
migrationBuilder.DropColumn(
name: "LinkClientId",
table: "IndieauthRequests");
migrationBuilder.DropColumn(
name: "LinkId",
table: "IndieauthRequests");
migrationBuilder.RenameColumn(
name: "LinkProfileId",
table: "IndieauthRequests",
newName: "ClientId");
migrationBuilder.DropColumn(
name: "ClientSecret",
table: "IndieauthClients");
migrationBuilder.AddColumn<string>(
name: "Scope",
table: "IndieauthClients",
type: "TEXT",
nullable: true);
migrationBuilder.DropColumn(
name: "Type",
table: "IndieauthClients");
migrationBuilder.AddColumn<int>(
name: "ProfileId",
table: "IndieauthClients",
type: "INTEGER",
nullable: false,
defaultValue: 0);
migrationBuilder.AddColumn<DateTime>(
name: "FirstSeen",
table: "IndieauthClients",
type: "TEXT",
nullable: false,
defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified));
migrationBuilder.AddColumn<DateTime>(
name: "LastSeen",
table: "IndieauthClients",
type: "TEXT",
nullable: false,
defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified));
migrationBuilder.CreateIndex(
name: "IX_IndieauthRequests_ClientId",
table: "IndieauthRequests",
column: "ClientId");
migrationBuilder.CreateIndex(
name: "IX_IndieauthClients_ProfileId",
table: "IndieauthClients",
column: "ProfileId");
migrationBuilder.AddForeignKey(
name: "FK_IndieauthClients_UserProfiles_ProfileId",
table: "IndieauthClients",
column: "ProfileId",
principalTable: "UserProfiles",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
migrationBuilder.AddForeignKey(
name: "FK_IndieauthRequests_IndieauthClients_ClientId",
table: "IndieauthRequests",
column: "ClientId",
principalTable: "IndieauthClients",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
}
}
}

View File

@ -0,0 +1,455 @@
// <auto-generated />
using System;
using Lunar.Exchange.Lapis.Data;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
#nullable disable
namespace Lunar.Exchange.Lapis.Data.Migrations
{
[DbContext(typeof(LapisContext))]
[Migration("20230602165805_ClientLink")]
partial class ClientLink
{
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder.HasAnnotation("ProductVersion", "6.0.4");
modelBuilder.Entity("Lunar.Exchange.Lapis.Data.Models.IndieAuth.Client", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<string>("ClientId")
.IsRequired()
.HasColumnType("TEXT");
b.Property<string>("ClientSecret")
.HasColumnType("TEXT");
b.Property<string>("RedirectUri")
.IsRequired()
.HasColumnType("TEXT");
b.Property<int>("Type")
.HasColumnType("INTEGER");
b.HasKey("Id");
b.HasIndex("ClientId")
.IsUnique();
b.ToTable("IndieauthClients");
});
modelBuilder.Entity("Lunar.Exchange.Lapis.Data.Models.IndieAuth.ClientLink", b =>
{
b.Property<int>("ClientId")
.HasColumnType("INTEGER");
b.Property<int>("ProfileId")
.HasColumnType("INTEGER");
b.Property<DateTime>("FirstSeen")
.HasColumnType("TEXT");
b.Property<DateTime>("LastSeen")
.HasColumnType("TEXT");
b.Property<string>("Scope")
.HasColumnType("TEXT");
b.HasKey("ClientId", "ProfileId");
b.HasIndex("ProfileId");
b.ToTable("IndieauthLinks");
});
modelBuilder.Entity("Lunar.Exchange.Lapis.Data.Models.IndieAuth.Request", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<DateTime?>("Claimed")
.HasColumnType("TEXT");
b.Property<string>("CodeChallenge")
.HasColumnType("TEXT");
b.Property<string>("CodeChallengeMethod")
.HasColumnType("TEXT");
b.Property<DateTime>("Created")
.HasColumnType("TEXT");
b.Property<int>("LinkClientId")
.HasColumnType("INTEGER");
b.Property<int>("LinkProfileId")
.HasColumnType("INTEGER");
b.Property<string>("Value")
.IsRequired()
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("LinkProfileId");
b.HasIndex("Value");
b.HasIndex("LinkClientId", "LinkProfileId");
b.ToTable("IndieauthRequests");
});
modelBuilder.Entity("Lunar.Exchange.Lapis.Data.Models.UserProfile", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<string>("ProfileUrl")
.IsRequired()
.HasColumnType("TEXT");
b.Property<string>("UserId")
.IsRequired()
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("UserId");
b.ToTable("UserProfiles");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b =>
{
b.Property<string>("Id")
.HasColumnType("TEXT");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.HasColumnType("TEXT");
b.Property<string>("Name")
.HasMaxLength(256)
.HasColumnType("TEXT");
b.Property<string>("NormalizedName")
.HasMaxLength(256)
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("NormalizedName")
.IsUnique()
.HasDatabaseName("RoleNameIndex");
b.ToTable("AspNetRoles", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<string>("ClaimType")
.HasColumnType("TEXT");
b.Property<string>("ClaimValue")
.HasColumnType("TEXT");
b.Property<string>("RoleId")
.IsRequired()
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("RoleId");
b.ToTable("AspNetRoleClaims", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUser", b =>
{
b.Property<string>("Id")
.HasColumnType("TEXT");
b.Property<int>("AccessFailedCount")
.HasColumnType("INTEGER");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.HasColumnType("TEXT");
b.Property<string>("Email")
.HasMaxLength(256)
.HasColumnType("TEXT");
b.Property<bool>("EmailConfirmed")
.HasColumnType("INTEGER");
b.Property<bool>("LockoutEnabled")
.HasColumnType("INTEGER");
b.Property<DateTimeOffset?>("LockoutEnd")
.HasColumnType("TEXT");
b.Property<string>("NormalizedEmail")
.HasMaxLength(256)
.HasColumnType("TEXT");
b.Property<string>("NormalizedUserName")
.HasMaxLength(256)
.HasColumnType("TEXT");
b.Property<string>("PasswordHash")
.HasColumnType("TEXT");
b.Property<string>("PhoneNumber")
.HasColumnType("TEXT");
b.Property<bool>("PhoneNumberConfirmed")
.HasColumnType("INTEGER");
b.Property<string>("SecurityStamp")
.HasColumnType("TEXT");
b.Property<bool>("TwoFactorEnabled")
.HasColumnType("INTEGER");
b.Property<string>("UserName")
.HasMaxLength(256)
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("NormalizedEmail")
.HasDatabaseName("EmailIndex");
b.HasIndex("NormalizedUserName")
.IsUnique()
.HasDatabaseName("UserNameIndex");
b.ToTable("AspNetUsers", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<string>("ClaimType")
.HasColumnType("TEXT");
b.Property<string>("ClaimValue")
.HasColumnType("TEXT");
b.Property<string>("UserId")
.IsRequired()
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("UserId");
b.ToTable("AspNetUserClaims", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
{
b.Property<string>("LoginProvider")
.HasMaxLength(128)
.HasColumnType("TEXT");
b.Property<string>("ProviderKey")
.HasMaxLength(128)
.HasColumnType("TEXT");
b.Property<string>("ProviderDisplayName")
.HasColumnType("TEXT");
b.Property<string>("UserId")
.IsRequired()
.HasColumnType("TEXT");
b.HasKey("LoginProvider", "ProviderKey");
b.HasIndex("UserId");
b.ToTable("AspNetUserLogins", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
{
b.Property<string>("UserId")
.HasColumnType("TEXT");
b.Property<string>("RoleId")
.HasColumnType("TEXT");
b.HasKey("UserId", "RoleId");
b.HasIndex("RoleId");
b.ToTable("AspNetUserRoles", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
{
b.Property<string>("UserId")
.HasColumnType("TEXT");
b.Property<string>("LoginProvider")
.HasMaxLength(128)
.HasColumnType("TEXT");
b.Property<string>("Name")
.HasMaxLength(128)
.HasColumnType("TEXT");
b.Property<string>("Value")
.HasColumnType("TEXT");
b.HasKey("UserId", "LoginProvider", "Name");
b.ToTable("AspNetUserTokens", (string)null);
});
modelBuilder.Entity("Lunar.Exchange.Lapis.Data.Models.IndieAuth.ClientLink", b =>
{
b.HasOne("Lunar.Exchange.Lapis.Data.Models.IndieAuth.Client", "Client")
.WithMany("Users")
.HasForeignKey("ClientId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Lunar.Exchange.Lapis.Data.Models.UserProfile", "Profile")
.WithMany("Clients")
.HasForeignKey("ProfileId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Client");
b.Navigation("Profile");
});
modelBuilder.Entity("Lunar.Exchange.Lapis.Data.Models.IndieAuth.Request", b =>
{
b.HasOne("Lunar.Exchange.Lapis.Data.Models.IndieAuth.Client", "LinkClient")
.WithMany()
.HasForeignKey("LinkClientId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Lunar.Exchange.Lapis.Data.Models.UserProfile", "LinkProfile")
.WithMany()
.HasForeignKey("LinkProfileId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Lunar.Exchange.Lapis.Data.Models.IndieAuth.ClientLink", "Link")
.WithMany("Codes")
.HasForeignKey("LinkClientId", "LinkProfileId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Link");
b.Navigation("LinkClient");
b.Navigation("LinkProfile");
});
modelBuilder.Entity("Lunar.Exchange.Lapis.Data.Models.UserProfile", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", "User")
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("User");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
.WithMany()
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
.WithMany()
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Lunar.Exchange.Lapis.Data.Models.IndieAuth.Client", b =>
{
b.Navigation("Users");
});
modelBuilder.Entity("Lunar.Exchange.Lapis.Data.Models.IndieAuth.ClientLink", b =>
{
b.Navigation("Codes");
});
modelBuilder.Entity("Lunar.Exchange.Lapis.Data.Models.UserProfile", b =>
{
b.Navigation("Clients");
});
#pragma warning restore 612, 618
}
}
}

View File

@ -0,0 +1,59 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Lunar.Exchange.Lapis.Data.Migrations
{
public partial class ClientLink : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "LinkId",
table: "IndieauthRequests");
migrationBuilder.CreateIndex(
name: "IX_IndieauthRequests_LinkProfileId",
table: "IndieauthRequests",
column: "LinkProfileId");
migrationBuilder.AddForeignKey(
name: "FK_IndieauthRequests_IndieauthClients_LinkClientId",
table: "IndieauthRequests",
column: "LinkClientId",
principalTable: "IndieauthClients",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
migrationBuilder.AddForeignKey(
name: "FK_IndieauthRequests_UserProfiles_LinkProfileId",
table: "IndieauthRequests",
column: "LinkProfileId",
principalTable: "UserProfiles",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropForeignKey(
name: "FK_IndieauthRequests_IndieauthClients_LinkClientId",
table: "IndieauthRequests");
migrationBuilder.DropForeignKey(
name: "FK_IndieauthRequests_UserProfiles_LinkProfileId",
table: "IndieauthRequests");
migrationBuilder.DropIndex(
name: "IX_IndieauthRequests_LinkProfileId",
table: "IndieauthRequests");
migrationBuilder.AddColumn<int>(
name: "LinkId",
table: "IndieauthRequests",
type: "INTEGER",
nullable: false,
defaultValue: 0);
}
}
}

View File

@ -0,0 +1,508 @@
// <auto-generated />
using System;
using Lunar.Exchange.Lapis.Data;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
#nullable disable
namespace Lunar.Exchange.Lapis.Data.Migrations
{
[DbContext(typeof(LapisContext))]
[Migration("20230602194741_ProfileProperties")]
partial class ProfileProperties
{
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder.HasAnnotation("ProductVersion", "6.0.4");
modelBuilder.Entity("Lunar.Exchange.Lapis.Data.Models.IndieAuth.Client", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<string>("ClientId")
.IsRequired()
.HasColumnType("TEXT");
b.Property<string>("ClientSecret")
.HasColumnType("TEXT");
b.Property<string>("RedirectUri")
.IsRequired()
.HasColumnType("TEXT");
b.Property<int>("Type")
.HasColumnType("INTEGER");
b.HasKey("Id");
b.HasIndex("ClientId")
.IsUnique();
b.ToTable("IndieauthClients");
});
modelBuilder.Entity("Lunar.Exchange.Lapis.Data.Models.IndieAuth.ClientLink", b =>
{
b.Property<int>("ClientId")
.HasColumnType("INTEGER");
b.Property<int>("ProfileId")
.HasColumnType("INTEGER");
b.Property<DateTime>("FirstSeen")
.HasColumnType("TEXT");
b.Property<DateTime>("LastSeen")
.HasColumnType("TEXT");
b.Property<string>("Scope")
.HasColumnType("TEXT");
b.HasKey("ClientId", "ProfileId");
b.HasIndex("ProfileId");
b.ToTable("IndieauthLinks");
});
modelBuilder.Entity("Lunar.Exchange.Lapis.Data.Models.IndieAuth.Request", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<DateTime?>("Claimed")
.HasColumnType("TEXT");
b.Property<string>("CodeChallenge")
.HasColumnType("TEXT");
b.Property<string>("CodeChallengeMethod")
.HasColumnType("TEXT");
b.Property<DateTime>("Created")
.HasColumnType("TEXT");
b.Property<int>("LinkClientId")
.HasColumnType("INTEGER");
b.Property<int>("LinkProfileId")
.HasColumnType("INTEGER");
b.Property<string>("Value")
.IsRequired()
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("LinkProfileId");
b.HasIndex("Value");
b.HasIndex("LinkClientId", "LinkProfileId");
b.ToTable("IndieauthRequests");
});
modelBuilder.Entity("Lunar.Exchange.Lapis.Data.Models.IndieAuth.Token", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<DateTime>("Created")
.HasColumnType("TEXT");
b.Property<DateTime>("Expires")
.HasColumnType("TEXT");
b.Property<DateTime>("LastSeen")
.HasColumnType("TEXT");
b.Property<string>("RefreshToken")
.HasColumnType("TEXT");
b.Property<int>("RequestId")
.HasColumnType("INTEGER");
b.Property<string>("Value")
.IsRequired()
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("RequestId");
b.ToTable("IndieauthTokens");
});
modelBuilder.Entity("Lunar.Exchange.Lapis.Data.Models.UserProfile", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<string>("Email")
.HasColumnType("TEXT");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("TEXT");
b.Property<string>("PhotoUrl")
.HasColumnType("TEXT");
b.Property<string>("ProfileUrl")
.IsRequired()
.HasColumnType("TEXT");
b.Property<string>("UserId")
.IsRequired()
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("UserId");
b.ToTable("UserProfiles");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b =>
{
b.Property<string>("Id")
.HasColumnType("TEXT");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.HasColumnType("TEXT");
b.Property<string>("Name")
.HasMaxLength(256)
.HasColumnType("TEXT");
b.Property<string>("NormalizedName")
.HasMaxLength(256)
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("NormalizedName")
.IsUnique()
.HasDatabaseName("RoleNameIndex");
b.ToTable("AspNetRoles", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<string>("ClaimType")
.HasColumnType("TEXT");
b.Property<string>("ClaimValue")
.HasColumnType("TEXT");
b.Property<string>("RoleId")
.IsRequired()
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("RoleId");
b.ToTable("AspNetRoleClaims", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUser", b =>
{
b.Property<string>("Id")
.HasColumnType("TEXT");
b.Property<int>("AccessFailedCount")
.HasColumnType("INTEGER");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.HasColumnType("TEXT");
b.Property<string>("Email")
.HasMaxLength(256)
.HasColumnType("TEXT");
b.Property<bool>("EmailConfirmed")
.HasColumnType("INTEGER");
b.Property<bool>("LockoutEnabled")
.HasColumnType("INTEGER");
b.Property<DateTimeOffset?>("LockoutEnd")
.HasColumnType("TEXT");
b.Property<string>("NormalizedEmail")
.HasMaxLength(256)
.HasColumnType("TEXT");
b.Property<string>("NormalizedUserName")
.HasMaxLength(256)
.HasColumnType("TEXT");
b.Property<string>("PasswordHash")
.HasColumnType("TEXT");
b.Property<string>("PhoneNumber")
.HasColumnType("TEXT");
b.Property<bool>("PhoneNumberConfirmed")
.HasColumnType("INTEGER");
b.Property<string>("SecurityStamp")
.HasColumnType("TEXT");
b.Property<bool>("TwoFactorEnabled")
.HasColumnType("INTEGER");
b.Property<string>("UserName")
.HasMaxLength(256)
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("NormalizedEmail")
.HasDatabaseName("EmailIndex");
b.HasIndex("NormalizedUserName")
.IsUnique()
.HasDatabaseName("UserNameIndex");
b.ToTable("AspNetUsers", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<string>("ClaimType")
.HasColumnType("TEXT");
b.Property<string>("ClaimValue")
.HasColumnType("TEXT");
b.Property<string>("UserId")
.IsRequired()
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("UserId");
b.ToTable("AspNetUserClaims", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
{
b.Property<string>("LoginProvider")
.HasMaxLength(128)
.HasColumnType("TEXT");
b.Property<string>("ProviderKey")
.HasMaxLength(128)
.HasColumnType("TEXT");
b.Property<string>("ProviderDisplayName")
.HasColumnType("TEXT");
b.Property<string>("UserId")
.IsRequired()
.HasColumnType("TEXT");
b.HasKey("LoginProvider", "ProviderKey");
b.HasIndex("UserId");
b.ToTable("AspNetUserLogins", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
{
b.Property<string>("UserId")
.HasColumnType("TEXT");
b.Property<string>("RoleId")
.HasColumnType("TEXT");
b.HasKey("UserId", "RoleId");
b.HasIndex("RoleId");
b.ToTable("AspNetUserRoles", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
{
b.Property<string>("UserId")
.HasColumnType("TEXT");
b.Property<string>("LoginProvider")
.HasMaxLength(128)
.HasColumnType("TEXT");
b.Property<string>("Name")
.HasMaxLength(128)
.HasColumnType("TEXT");
b.Property<string>("Value")
.HasColumnType("TEXT");
b.HasKey("UserId", "LoginProvider", "Name");
b.ToTable("AspNetUserTokens", (string)null);
});
modelBuilder.Entity("Lunar.Exchange.Lapis.Data.Models.IndieAuth.ClientLink", b =>
{
b.HasOne("Lunar.Exchange.Lapis.Data.Models.IndieAuth.Client", "Client")
.WithMany("Users")
.HasForeignKey("ClientId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Lunar.Exchange.Lapis.Data.Models.UserProfile", "Profile")
.WithMany("Clients")
.HasForeignKey("ProfileId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Client");
b.Navigation("Profile");
});
modelBuilder.Entity("Lunar.Exchange.Lapis.Data.Models.IndieAuth.Request", b =>
{
b.HasOne("Lunar.Exchange.Lapis.Data.Models.IndieAuth.Client", "LinkClient")
.WithMany()
.HasForeignKey("LinkClientId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Lunar.Exchange.Lapis.Data.Models.UserProfile", "LinkProfile")
.WithMany()
.HasForeignKey("LinkProfileId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Lunar.Exchange.Lapis.Data.Models.IndieAuth.ClientLink", "Link")
.WithMany("Codes")
.HasForeignKey("LinkClientId", "LinkProfileId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Link");
b.Navigation("LinkClient");
b.Navigation("LinkProfile");
});
modelBuilder.Entity("Lunar.Exchange.Lapis.Data.Models.IndieAuth.Token", b =>
{
b.HasOne("Lunar.Exchange.Lapis.Data.Models.IndieAuth.Request", "Request")
.WithMany()
.HasForeignKey("RequestId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Request");
});
modelBuilder.Entity("Lunar.Exchange.Lapis.Data.Models.UserProfile", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", "User")
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("User");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
.WithMany()
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
.WithMany()
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Lunar.Exchange.Lapis.Data.Models.IndieAuth.Client", b =>
{
b.Navigation("Users");
});
modelBuilder.Entity("Lunar.Exchange.Lapis.Data.Models.IndieAuth.ClientLink", b =>
{
b.Navigation("Codes");
});
modelBuilder.Entity("Lunar.Exchange.Lapis.Data.Models.UserProfile", b =>
{
b.Navigation("Clients");
});
#pragma warning restore 612, 618
}
}
}

View File

@ -0,0 +1,79 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Lunar.Exchange.Lapis.Data.Migrations
{
public partial class ProfileProperties : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<string>(
name: "Email",
table: "UserProfiles",
type: "TEXT",
nullable: true);
migrationBuilder.AddColumn<string>(
name: "Name",
table: "UserProfiles",
type: "TEXT",
nullable: false,
defaultValue: "");
migrationBuilder.AddColumn<string>(
name: "PhotoUrl",
table: "UserProfiles",
type: "TEXT",
nullable: true);
migrationBuilder.CreateTable(
name: "IndieauthTokens",
columns: table => new
{
Id = table.Column<int>(type: "INTEGER", nullable: false)
.Annotation("Sqlite:Autoincrement", true),
RequestId = table.Column<int>(type: "INTEGER", nullable: false),
Value = table.Column<string>(type: "TEXT", nullable: false),
RefreshToken = table.Column<string>(type: "TEXT", nullable: true),
Created = table.Column<DateTime>(type: "TEXT", nullable: false),
Expires = table.Column<DateTime>(type: "TEXT", nullable: false),
LastSeen = table.Column<DateTime>(type: "TEXT", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_IndieauthTokens", x => x.Id);
table.ForeignKey(
name: "FK_IndieauthTokens_IndieauthRequests_RequestId",
column: x => x.RequestId,
principalTable: "IndieauthRequests",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateIndex(
name: "IX_IndieauthTokens_RequestId",
table: "IndieauthTokens",
column: "RequestId");
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "IndieauthTokens");
migrationBuilder.DropColumn(
name: "Email",
table: "UserProfiles");
migrationBuilder.DropColumn(
name: "Name",
table: "UserProfiles");
migrationBuilder.DropColumn(
name: "PhotoUrl",
table: "UserProfiles");
}
}
}

View File

@ -17,7 +17,7 @@ namespace Lunar.Exchange.Lapis.Data.Migrations
#pragma warning disable 612, 618
modelBuilder.HasAnnotation("ProductVersion", "6.0.4");
modelBuilder.Entity("Lunar.Exchange.Lapis.Data.Models.IndieauthClient", b =>
modelBuilder.Entity("Lunar.Exchange.Lapis.Data.Models.IndieAuth.Client", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
@ -27,30 +27,49 @@ namespace Lunar.Exchange.Lapis.Data.Migrations
.IsRequired()
.HasColumnType("TEXT");
b.Property<string>("ClientSecret")
.HasColumnType("TEXT");
b.Property<string>("RedirectUri")
.IsRequired()
.HasColumnType("TEXT");
b.Property<int>("Type")
.HasColumnType("INTEGER");
b.HasKey("Id");
b.HasIndex("ClientId")
.IsUnique();
b.ToTable("IndieauthClients", (string)null);
});
modelBuilder.Entity("Lunar.Exchange.Lapis.Data.Models.IndieAuth.ClientLink", b =>
{
b.Property<int>("ClientId")
.HasColumnType("INTEGER");
b.Property<int>("ProfileId")
.HasColumnType("INTEGER");
b.Property<DateTime>("FirstSeen")
.HasColumnType("TEXT");
b.Property<DateTime>("LastSeen")
.HasColumnType("TEXT");
b.Property<int>("ProfileId")
.HasColumnType("INTEGER");
b.Property<string>("RedirectUri")
.IsRequired()
.HasColumnType("TEXT");
b.Property<string>("Scope")
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasKey("ClientId", "ProfileId");
b.HasIndex("ProfileId");
b.ToTable("IndieauthLinks");
b.ToTable("IndieauthLinks", (string)null);
});
modelBuilder.Entity("Lunar.Exchange.Lapis.Data.Models.IndieauthRequest", b =>
modelBuilder.Entity("Lunar.Exchange.Lapis.Data.Models.IndieAuth.Request", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
@ -59,29 +78,66 @@ namespace Lunar.Exchange.Lapis.Data.Migrations
b.Property<DateTime?>("Claimed")
.HasColumnType("TEXT");
b.Property<int>("ClientId")
.HasColumnType("INTEGER");
b.Property<string>("CodeChallenge")
.IsRequired()
.HasColumnType("TEXT");
b.Property<string>("CodeChallengeMethod")
.IsRequired()
.HasColumnType("TEXT");
b.Property<DateTime>("Created")
.HasColumnType("TEXT");
b.Property<int>("LinkClientId")
.HasColumnType("INTEGER");
b.Property<int>("LinkProfileId")
.HasColumnType("INTEGER");
b.Property<string>("Value")
.IsRequired()
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("ClientId");
b.HasIndex("LinkProfileId");
b.ToTable("IndieauthCodes");
b.HasIndex("Value");
b.HasIndex("LinkClientId", "LinkProfileId");
b.ToTable("IndieauthRequests", (string)null);
});
modelBuilder.Entity("Lunar.Exchange.Lapis.Data.Models.IndieAuth.Token", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<DateTime>("Created")
.HasColumnType("TEXT");
b.Property<DateTime>("Expires")
.HasColumnType("TEXT");
b.Property<DateTime>("LastSeen")
.HasColumnType("TEXT");
b.Property<string>("RefreshToken")
.HasColumnType("TEXT");
b.Property<int>("RequestId")
.HasColumnType("INTEGER");
b.Property<string>("Value")
.IsRequired()
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("RequestId");
b.ToTable("IndieauthTokens", (string)null);
});
modelBuilder.Entity("Lunar.Exchange.Lapis.Data.Models.UserProfile", b =>
@ -90,6 +146,16 @@ namespace Lunar.Exchange.Lapis.Data.Migrations
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<string>("Email")
.HasColumnType("TEXT");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("TEXT");
b.Property<string>("PhotoUrl")
.HasColumnType("TEXT");
b.Property<string>("ProfileUrl")
.IsRequired()
.HasColumnType("TEXT");
@ -102,7 +168,7 @@ namespace Lunar.Exchange.Lapis.Data.Migrations
b.HasIndex("UserId");
b.ToTable("UserProfiles");
b.ToTable("UserProfiles", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b =>
@ -301,26 +367,61 @@ namespace Lunar.Exchange.Lapis.Data.Migrations
b.ToTable("AspNetUserTokens", (string)null);
});
modelBuilder.Entity("Lunar.Exchange.Lapis.Data.Models.IndieauthClient", b =>
modelBuilder.Entity("Lunar.Exchange.Lapis.Data.Models.IndieAuth.ClientLink", b =>
{
b.HasOne("Lunar.Exchange.Lapis.Data.Models.IndieAuth.Client", "Client")
.WithMany("Users")
.HasForeignKey("ClientId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Lunar.Exchange.Lapis.Data.Models.UserProfile", "Profile")
.WithMany("Clients")
.HasForeignKey("ProfileId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Client");
b.Navigation("Profile");
});
modelBuilder.Entity("Lunar.Exchange.Lapis.Data.Models.IndieauthRequest", b =>
modelBuilder.Entity("Lunar.Exchange.Lapis.Data.Models.IndieAuth.Request", b =>
{
b.HasOne("Lunar.Exchange.Lapis.Data.Models.IndieauthClient", "Client")
.WithMany("Codes")
.HasForeignKey("ClientId")
b.HasOne("Lunar.Exchange.Lapis.Data.Models.IndieAuth.Client", "LinkClient")
.WithMany()
.HasForeignKey("LinkClientId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Client");
b.HasOne("Lunar.Exchange.Lapis.Data.Models.UserProfile", "LinkProfile")
.WithMany()
.HasForeignKey("LinkProfileId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Lunar.Exchange.Lapis.Data.Models.IndieAuth.ClientLink", "Link")
.WithMany("Codes")
.HasForeignKey("LinkClientId", "LinkProfileId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Link");
b.Navigation("LinkClient");
b.Navigation("LinkProfile");
});
modelBuilder.Entity("Lunar.Exchange.Lapis.Data.Models.IndieAuth.Token", b =>
{
b.HasOne("Lunar.Exchange.Lapis.Data.Models.IndieAuth.Request", "Request")
.WithMany()
.HasForeignKey("RequestId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Request");
});
modelBuilder.Entity("Lunar.Exchange.Lapis.Data.Models.UserProfile", b =>
@ -385,7 +486,12 @@ namespace Lunar.Exchange.Lapis.Data.Migrations
.IsRequired();
});
modelBuilder.Entity("Lunar.Exchange.Lapis.Data.Models.IndieauthClient", b =>
modelBuilder.Entity("Lunar.Exchange.Lapis.Data.Models.IndieAuth.Client", b =>
{
b.Navigation("Users");
});
modelBuilder.Entity("Lunar.Exchange.Lapis.Data.Models.IndieAuth.ClientLink", b =>
{
b.Navigation("Codes");
});

View File

@ -0,0 +1,28 @@
namespace Lunar.Exchange.Lapis.Data.Models.IndieAuth;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
public class Client : IEntityTypeConfiguration<Client>
{
public int Id { get; set; }
public string ClientId { get; set; } = null!;
public string? ClientSecret { get; set; } = null!;
public string RedirectUri { get; set; } = null!;
public ClientType Type { get; set; }
public IEnumerable<ClientLink> Users { get; set; } = null!;
public enum ClientType
{
IndieAuth = 0,
OAuth
}
public void Configure(EntityTypeBuilder<Client> builder)
{
builder.HasIndex(e => e.ClientId).IsUnique();
}
}

View File

@ -0,0 +1,26 @@
namespace Lunar.Exchange.Lapis.Data.Models.IndieAuth;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
public class ClientLink : IEntityTypeConfiguration<ClientLink>
{
public int ClientId { get; set; }
public Client Client { get; set; } = null!;
public int ProfileId { get; set; }
public UserProfile Profile { get; set; } = null!;
// Maybe store it as several entries elsewhere? Good enough for MVP
public string? Scope { get; set; }
public DateTime FirstSeen { get; set; }
public DateTime LastSeen { get; set; }
public IEnumerable<Request> Codes { get; set; } = null!;
public void Configure(EntityTypeBuilder<ClientLink> builder)
{
builder.HasKey(e => new { e.ClientId, e.ProfileId });
}
}

View File

@ -0,0 +1,28 @@
namespace Lunar.Exchange.Lapis.Data.Models.IndieAuth;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
public class Request : IEntityTypeConfiguration<Request>
{
public int Id { get; set; }
public Client LinkClient { get; set; } = null!;
public UserProfile LinkProfile { get; set; } = null!;
public int LinkClientId { get; set; }
public int LinkProfileId { get; set; }
public ClientLink Link { get; set; } = null!;
public string Value { get; set; } = null!;
public string? CodeChallenge { get; set; }
public string? CodeChallengeMethod { get; set; }
public DateTime Created { get; set; }
public DateTime? Claimed { get; set; }
public void Configure(EntityTypeBuilder<Request> builder)
{
builder.HasIndex(e => e.Value);
}
}

View File

@ -0,0 +1,23 @@
namespace Lunar.Exchange.Lapis.Data.Models.IndieAuth;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
public class Token : IEntityTypeConfiguration<Token>
{
public int Id { get; set; }
public int RequestId { get; set; }
public Request Request { get; set; } = null!;
public string Value { get; set; } = null!;
public string? RefreshToken { get; set; } = null!;
public DateTime Created { get; set; }
public DateTime Expires { get; set; }
public DateTime LastSeen { get; set; }
public void Configure(EntityTypeBuilder<Token> builder)
{
}
}

View File

@ -1,21 +0,0 @@
namespace Lunar.Exchange.Lapis.Data.Models;
public class IndieauthClient
{
public int Id { get; set; }
public int ProfileId { get; set; }
public UserProfile Profile { get; set; } = null!;
public string ClientId { get; set; } = null!;
public string RedirectUri { get; set; } = null!;
// Maybe store it as several entries elsewhere? Good enough for MVP
public string? Scope { get; set; }
public DateTime FirstSeen { get; set; }
public DateTime LastSeen { get; set; }
public IEnumerable<IndieauthRequest> Codes { get; set; } = null!;
}

View File

@ -1,16 +0,0 @@
namespace Lunar.Exchange.Lapis.Data.Models;
public class IndieauthRequest
{
public int Id { get; set; }
public int ClientId { get; set; }
public IndieauthClient Client { get; set; } = null!;
public string Value { get; set; } = null!;
public string CodeChallenge { get; set; } = null!;
public string CodeChallengeMethod { get; set; } = null!;
public DateTime Created { get; set; }
public DateTime? Claimed { get; set; }
}

View File

@ -1,7 +1,7 @@
using Microsoft.AspNetCore.Identity;
namespace Lunar.Exchange.Lapis.Data.Models;
using Lunar.Exchange.Lapis.Data.Models.IndieAuth;
using Microsoft.AspNetCore.Identity;
public class UserProfile
{
@ -9,7 +9,11 @@ public class UserProfile
public string UserId { get; set; } = null!;
public IdentityUser User { get; set; } = null!;
public string Name { get; set; } = null!;
public string ProfileUrl { get; set; } = null!;
public IEnumerable<IndieauthClient> Clients { get; set; } = null!;
public string? PhotoUrl { get; set; } = null!;
public string? Email { get; set; } = null!;
public IEnumerable<ClientLink> Clients { get; set; } = null!;
}

View File

@ -8,8 +8,8 @@ public class IndieauthRequestModel
public string ClientId { get; set; } = null!;
public string RedirectUri { get; set; } = null!;
public string State { get; set; } = null!;
public string CodeChallenge { get; set; } = null!;
public string CodeChallengeMethod { get; set; } = null!;
public string? CodeChallenge { get; set; }
public string? CodeChallengeMethod { get; set; }
public string? Scope { get; set; }
public string? Me { get; set; }

View File

@ -12,6 +12,12 @@ public class SnakeCaseConverterAttribute : ActionFilterAttribute
PropertyNamingPolicy = new SnakeCaseNamingPolicy()
};
public SnakeCaseConverterAttribute(bool ignoreNull = false)
{
if (ignoreNull)
SerializerOptions.DefaultIgnoreCondition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingNull;
}
public override void OnResultExecuting(ResultExecutingContext context)
{
if (context.Result is ObjectResult objectResult)

View File

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<TargetFramework>net7.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<RootNamespace>Lunar.Exchange.Lapis</RootNamespace>