Syncshells (#11)

* some groups stuff

* further groups rework

* fixes for pause changes

* adjsut timeout interval

* fixes and namespace change to file scoped

* more fixes

* further implement groups

* fix change group ownership

* add some more stuff for groups

* more fixes and additions

* some fixes based on analyzers, add shard info to ui

* add discord command, cleanup

* fix regex

* add group migration and deletion on user deletion

* add api method for client to check health of connection

* adjust regex for vanity

* fixes for server and bot

* fixes some string comparison in linq queries

* fixes group leave and sets alias to null

* fix syntax in changeownership

* add better logging, fixes for group leaving

* fixes for group leave

Co-authored-by: Stanley Dimant <root.darkarchon@outlook.com>
This commit is contained in:
rootdarkarchon
2022-10-04 14:13:43 +02:00
committed by GitHub
parent d866223069
commit bff21ead95
57 changed files with 3761 additions and 1239 deletions

View File

@@ -1,63 +1,56 @@
using System.Security.Claims;
using System.Security.Cryptography;
using System.Text;
using System.Text.Encodings.Web;
using MareSynchronosServer;
using MareSynchronosShared.Data;
using MareSynchronosShared.Metrics;
using MareSynchronosShared.Protos;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Http;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using ISystemClock = Microsoft.AspNetCore.Authentication.ISystemClock;
namespace MareSynchronosShared.Authentication
namespace MareSynchronosShared.Authentication;
public class SecretKeyGrpcAuthenticationHandler : AuthenticationHandler<AuthenticationSchemeOptions>
{
public const string AuthScheme = "SecretKeyGrpcAuth";
public class SecretKeyGrpcAuthenticationHandler : AuthenticationHandler<AuthenticationSchemeOptions>
private readonly AuthService.AuthServiceClient _authClient;
private readonly IHttpContextAccessor _accessor;
public SecretKeyGrpcAuthenticationHandler(IHttpContextAccessor accessor, AuthService.AuthServiceClient authClient,
IOptionsMonitor<AuthenticationSchemeOptions> options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock) : base(options, logger, encoder, clock)
{
public const string AuthScheme = "SecretKeyGrpcAuth";
this._authClient = authClient;
_accessor = accessor;
}
private readonly AuthService.AuthServiceClient _authClient;
private readonly IHttpContextAccessor _accessor;
public SecretKeyGrpcAuthenticationHandler(IHttpContextAccessor accessor, AuthService.AuthServiceClient authClient,
IOptionsMonitor<AuthenticationSchemeOptions> options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock) : base(options, logger, encoder, clock)
protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
{
if(!Request.Headers.TryGetValue("Authorization", out var authHeader))
{
this._authClient = authClient;
_accessor = accessor;
authHeader = string.Empty;
}
var ip = _accessor.GetIpAddress();
var authResult = await _authClient.AuthorizeAsync(new AuthRequest() {Ip = ip, SecretKey = authHeader}).ConfigureAwait(false);
if (!authResult.Success)
{
return AuthenticateResult.Fail("Failed Authorization");
}
protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
string uid = authResult.Uid;
var claims = new List<Claim>
{
if(!Request.Headers.TryGetValue("Authorization", out var authHeader))
{
authHeader = string.Empty;
}
var ip = _accessor.GetIpAddress();
new(ClaimTypes.NameIdentifier, uid),
new(ClaimTypes.Authentication, authHeader)
};
var authResult = await _authClient.AuthorizeAsync(new AuthRequest() {Ip = ip, SecretKey = authHeader}).ConfigureAwait(false);
var identity = new ClaimsIdentity(claims, nameof(SecretKeyGrpcAuthenticationHandler));
var principal = new ClaimsPrincipal(identity);
var ticket = new AuthenticationTicket(principal, Scheme.Name);
if (!authResult.Success)
{
return AuthenticateResult.Fail("Failed Authorization");
}
string uid = authResult.Uid;
var claims = new List<Claim>
{
new(ClaimTypes.NameIdentifier, uid),
new(ClaimTypes.Authentication, authHeader)
};
var identity = new ClaimsIdentity(claims, nameof(SecretKeyGrpcAuthenticationHandler));
var principal = new ClaimsPrincipal(identity);
var ticket = new AuthenticationTicket(principal, Scheme.Name);
return AuthenticateResult.Success(ticket);
}
return AuthenticateResult.Success(ticket);
}
}

View File

@@ -39,13 +39,14 @@ public class MareDbContext : DbContext
public DbSet<Auth> Auth { get; set; }
public DbSet<LodeStoneAuth> LodeStoneAuth { get; set; }
public DbSet<BannedRegistrations> BannedRegistrations { get; set; }
public DbSet<Group> Groups { get; set; }
public DbSet<GroupPair> GroupPairs { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Auth>().ToTable("auth");
modelBuilder.Entity<User>().ToTable("users");
//modelBuilder.Entity<User>().HasIndex(c => c.CharacterIdentification);
modelBuilder.Entity<FileCache>().ToTable("file_caches");
modelBuilder.Entity<FileCache>().HasIndex(c => c.UploaderUID);
modelBuilder.Entity<ClientPair>().ToTable("client_pairs");
@@ -56,5 +57,11 @@ public class MareDbContext : DbContext
modelBuilder.Entity<Banned>().ToTable("banned_users");
modelBuilder.Entity<LodeStoneAuth>().ToTable("lodestone_auth");
modelBuilder.Entity<BannedRegistrations>().ToTable("banned_registrations");
modelBuilder.Entity<Group>().ToTable("groups");
modelBuilder.Entity<Group>().HasIndex(c => c.OwnerUID);
modelBuilder.Entity<GroupPair>().ToTable("group_pairs");
modelBuilder.Entity<GroupPair>().HasKey(u => new { u.GroupGID, u.GroupUserUID });
modelBuilder.Entity<GroupPair>().HasIndex(c => c.GroupUserUID);
modelBuilder.Entity<GroupPair>().HasIndex(c => c.GroupGID);
}
}

View File

@@ -1,30 +1,28 @@
using Microsoft.AspNetCore.Http;
using System.Linq;
namespace MareSynchronosServer
namespace MareSynchronosServer;
public static class Extensions
{
public static class Extensions
public static string GetIpAddress(this IHttpContextAccessor accessor)
{
public static string GetIpAddress(this IHttpContextAccessor accessor)
if (!string.IsNullOrEmpty(accessor.HttpContext.Request.Headers["CF-CONNECTING-IP"]))
return accessor.HttpContext.Request.Headers["CF-CONNECTING-IP"];
if (!string.IsNullOrEmpty(accessor.HttpContext.Request.Headers["X-Forwarded-For"]))
{
if (!string.IsNullOrEmpty(accessor.HttpContext.Request.Headers["CF-CONNECTING-IP"]))
return accessor.HttpContext.Request.Headers["CF-CONNECTING-IP"];
if (!string.IsNullOrEmpty(accessor.HttpContext.Request.Headers["X-Forwarded-For"]))
{
return accessor.HttpContext.Request.Headers["X-Forwarded-For"];
}
var ipAddress = accessor.HttpContext.GetServerVariable("HTTP_X_FORWARDED_FOR");
if (!string.IsNullOrEmpty(ipAddress))
{
var addresses = ipAddress.Split(',');
if (addresses.Length != 0)
return addresses.Last();
}
return accessor.HttpContext.Connection.RemoteIpAddress.ToString();
return accessor.HttpContext.Request.Headers["X-Forwarded-For"];
}
var ipAddress = accessor.HttpContext.GetServerVariable("HTTP_X_FORWARDED_FOR");
if (!string.IsNullOrEmpty(ipAddress))
{
var addresses = ipAddress.Split(',');
if (addresses.Length != 0)
return addresses.Last();
}
return accessor.HttpContext.Connection.RemoteIpAddress.ToString();
}
}
}

View File

@@ -13,6 +13,10 @@
<FrameworkReference Include="Microsoft.AspNetCore.App" />
</ItemGroup>
<ItemGroup>
<None Include="..\.editorconfig" Link=".editorconfig" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="EFCore.NamingConventions" Version="6.0.0" />
<PackageReference Include="Grpc.AspNetCore" Version="2.47.0" />
@@ -33,6 +37,7 @@
<PackageReference Include="Microsoft.Extensions.Caching.StackExchangeRedis" Version="6.0.8" />
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="6.0.6" />
<PackageReference Include="prometheus-net" Version="6.0.0" />
<PackageReference Include="System.Linq.Async" Version="6.0.1" />
</ItemGroup>
<ItemGroup>

View File

@@ -1,6 +1,4 @@
using MareSynchronosShared.Data;
using Microsoft.Extensions.DependencyInjection;
using Prometheus;
using Prometheus;
namespace MareSynchronosShared.Metrics;

View File

@@ -19,4 +19,7 @@ public class MetricsAPI
public const string CounterAuthenticationCacheHits = "mare_auth_requests_cachehit";
public const string CounterAuthenticationFailures = "mare_auth_requests_fail";
public const string CounterAuthenticationSuccesses = "mare_auth_requests_success";
public const string GaugeGroups = "mare_groups";
public const string GaugeGroupPairs = "mare_groups_pairs";
public const string GaugeGroupPairsPaused = "mare_groups_pairs_paused";
}

View File

@@ -1,5 +1,4 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable

View File

@@ -1,5 +1,4 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable

View File

@@ -1,5 +1,4 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable

View File

@@ -0,0 +1,389 @@
// <auto-generated />
using System;
using MareSynchronosShared.Data;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
#nullable disable
namespace MareSynchronosServer.Migrations
{
[DbContext(typeof(MareDbContext))]
[Migration("20220917115233_Groups")]
partial class Groups
{
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "6.0.8")
.HasAnnotation("Relational:MaxIdentifierLength", 63);
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
modelBuilder.Entity("MareSynchronosShared.Models.Auth", b =>
{
b.Property<string>("HashedKey")
.HasMaxLength(64)
.HasColumnType("character varying(64)")
.HasColumnName("hashed_key");
b.Property<string>("UserUID")
.HasColumnType("character varying(10)")
.HasColumnName("user_uid");
b.HasKey("HashedKey")
.HasName("pk_auth");
b.HasIndex("UserUID")
.HasDatabaseName("ix_auth_user_uid");
b.ToTable("auth", (string)null);
});
modelBuilder.Entity("MareSynchronosShared.Models.Banned", b =>
{
b.Property<string>("CharacterIdentification")
.HasMaxLength(100)
.HasColumnType("character varying(100)")
.HasColumnName("character_identification");
b.Property<string>("Reason")
.HasColumnType("text")
.HasColumnName("reason");
b.Property<byte[]>("Timestamp")
.IsConcurrencyToken()
.ValueGeneratedOnAddOrUpdate()
.HasColumnType("bytea")
.HasColumnName("timestamp");
b.HasKey("CharacterIdentification")
.HasName("pk_banned_users");
b.ToTable("banned_users", (string)null);
});
modelBuilder.Entity("MareSynchronosShared.Models.BannedRegistrations", b =>
{
b.Property<string>("DiscordIdOrLodestoneAuth")
.HasMaxLength(100)
.HasColumnType("character varying(100)")
.HasColumnName("discord_id_or_lodestone_auth");
b.HasKey("DiscordIdOrLodestoneAuth")
.HasName("pk_banned_registrations");
b.ToTable("banned_registrations", (string)null);
});
modelBuilder.Entity("MareSynchronosShared.Models.ClientPair", b =>
{
b.Property<string>("UserUID")
.HasMaxLength(10)
.HasColumnType("character varying(10)")
.HasColumnName("user_uid");
b.Property<string>("OtherUserUID")
.HasMaxLength(10)
.HasColumnType("character varying(10)")
.HasColumnName("other_user_uid");
b.Property<bool>("AllowReceivingMessages")
.HasColumnType("boolean")
.HasColumnName("allow_receiving_messages");
b.Property<bool>("IsPaused")
.HasColumnType("boolean")
.HasColumnName("is_paused");
b.Property<byte[]>("Timestamp")
.IsConcurrencyToken()
.ValueGeneratedOnAddOrUpdate()
.HasColumnType("bytea")
.HasColumnName("timestamp");
b.HasKey("UserUID", "OtherUserUID")
.HasName("pk_client_pairs");
b.HasIndex("OtherUserUID")
.HasDatabaseName("ix_client_pairs_other_user_uid");
b.HasIndex("UserUID")
.HasDatabaseName("ix_client_pairs_user_uid");
b.ToTable("client_pairs", (string)null);
});
modelBuilder.Entity("MareSynchronosShared.Models.FileCache", b =>
{
b.Property<string>("Hash")
.HasMaxLength(40)
.HasColumnType("character varying(40)")
.HasColumnName("hash");
b.Property<byte[]>("Timestamp")
.IsConcurrencyToken()
.ValueGeneratedOnAddOrUpdate()
.HasColumnType("bytea")
.HasColumnName("timestamp");
b.Property<bool>("Uploaded")
.HasColumnType("boolean")
.HasColumnName("uploaded");
b.Property<string>("UploaderUID")
.HasMaxLength(10)
.HasColumnType("character varying(10)")
.HasColumnName("uploader_uid");
b.HasKey("Hash")
.HasName("pk_file_caches");
b.HasIndex("UploaderUID")
.HasDatabaseName("ix_file_caches_uploader_uid");
b.ToTable("file_caches", (string)null);
});
modelBuilder.Entity("MareSynchronosShared.Models.ForbiddenUploadEntry", b =>
{
b.Property<string>("Hash")
.HasMaxLength(40)
.HasColumnType("character varying(40)")
.HasColumnName("hash");
b.Property<string>("ForbiddenBy")
.HasMaxLength(100)
.HasColumnType("character varying(100)")
.HasColumnName("forbidden_by");
b.Property<byte[]>("Timestamp")
.IsConcurrencyToken()
.ValueGeneratedOnAddOrUpdate()
.HasColumnType("bytea")
.HasColumnName("timestamp");
b.HasKey("Hash")
.HasName("pk_forbidden_upload_entries");
b.ToTable("forbidden_upload_entries", (string)null);
});
modelBuilder.Entity("MareSynchronosShared.Models.Group", b =>
{
b.Property<string>("GID")
.HasMaxLength(14)
.HasColumnType("character varying(14)")
.HasColumnName("gid");
b.Property<string>("Alias")
.HasMaxLength(50)
.HasColumnType("character varying(50)")
.HasColumnName("alias");
b.Property<string>("HashedPassword")
.HasColumnType("text")
.HasColumnName("hashed_password");
b.Property<bool>("InvitesEnabled")
.HasColumnType("boolean")
.HasColumnName("invites_enabled");
b.Property<string>("OwnerUID")
.HasColumnType("character varying(10)")
.HasColumnName("owner_uid");
b.HasKey("GID")
.HasName("pk_groups");
b.HasIndex("OwnerUID")
.HasDatabaseName("ix_groups_owner_uid");
b.ToTable("groups", (string)null);
});
modelBuilder.Entity("MareSynchronosShared.Models.GroupPair", b =>
{
b.Property<string>("GroupGID")
.HasColumnType("character varying(14)")
.HasColumnName("group_gid");
b.Property<string>("GroupUserUID")
.HasColumnType("character varying(10)")
.HasColumnName("group_user_uid");
b.Property<bool>("IsPaused")
.HasColumnType("boolean")
.HasColumnName("is_paused");
b.HasKey("GroupGID", "GroupUserUID")
.HasName("pk_group_pairs");
b.HasIndex("GroupGID")
.HasDatabaseName("ix_group_pairs_group_gid");
b.HasIndex("GroupUserUID")
.HasDatabaseName("ix_group_pairs_group_user_uid");
b.ToTable("group_pairs", (string)null);
});
modelBuilder.Entity("MareSynchronosShared.Models.LodeStoneAuth", b =>
{
b.Property<decimal>("DiscordId")
.ValueGeneratedOnAdd()
.HasColumnType("numeric(20,0)")
.HasColumnName("discord_id");
b.Property<string>("HashedLodestoneId")
.HasMaxLength(100)
.HasColumnType("character varying(100)")
.HasColumnName("hashed_lodestone_id");
b.Property<string>("LodestoneAuthString")
.HasMaxLength(100)
.HasColumnType("character varying(100)")
.HasColumnName("lodestone_auth_string");
b.Property<DateTime?>("StartedAt")
.HasColumnType("timestamp with time zone")
.HasColumnName("started_at");
b.Property<string>("UserUID")
.HasColumnType("character varying(10)")
.HasColumnName("user_uid");
b.HasKey("DiscordId")
.HasName("pk_lodestone_auth");
b.HasIndex("UserUID")
.HasDatabaseName("ix_lodestone_auth_user_uid");
b.ToTable("lodestone_auth", (string)null);
});
modelBuilder.Entity("MareSynchronosShared.Models.User", b =>
{
b.Property<string>("UID")
.HasMaxLength(10)
.HasColumnType("character varying(10)")
.HasColumnName("uid");
b.Property<string>("Alias")
.HasMaxLength(10)
.HasColumnType("character varying(10)")
.HasColumnName("alias");
b.Property<bool>("IsAdmin")
.HasColumnType("boolean")
.HasColumnName("is_admin");
b.Property<bool>("IsModerator")
.HasColumnType("boolean")
.HasColumnName("is_moderator");
b.Property<DateTime>("LastLoggedIn")
.HasColumnType("timestamp with time zone")
.HasColumnName("last_logged_in");
b.Property<byte[]>("Timestamp")
.IsConcurrencyToken()
.ValueGeneratedOnAddOrUpdate()
.HasColumnType("bytea")
.HasColumnName("timestamp");
b.HasKey("UID")
.HasName("pk_users");
b.ToTable("users", (string)null);
});
modelBuilder.Entity("MareSynchronosShared.Models.Auth", b =>
{
b.HasOne("MareSynchronosShared.Models.User", "User")
.WithMany()
.HasForeignKey("UserUID")
.HasConstraintName("fk_auth_users_user_temp_id");
b.Navigation("User");
});
modelBuilder.Entity("MareSynchronosShared.Models.ClientPair", b =>
{
b.HasOne("MareSynchronosShared.Models.User", "OtherUser")
.WithMany()
.HasForeignKey("OtherUserUID")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired()
.HasConstraintName("fk_client_pairs_users_other_user_temp_id1");
b.HasOne("MareSynchronosShared.Models.User", "User")
.WithMany()
.HasForeignKey("UserUID")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired()
.HasConstraintName("fk_client_pairs_users_user_temp_id2");
b.Navigation("OtherUser");
b.Navigation("User");
});
modelBuilder.Entity("MareSynchronosShared.Models.FileCache", b =>
{
b.HasOne("MareSynchronosShared.Models.User", "Uploader")
.WithMany()
.HasForeignKey("UploaderUID")
.HasConstraintName("fk_file_caches_users_uploader_uid");
b.Navigation("Uploader");
});
modelBuilder.Entity("MareSynchronosShared.Models.Group", b =>
{
b.HasOne("MareSynchronosShared.Models.User", "Owner")
.WithMany()
.HasForeignKey("OwnerUID")
.HasConstraintName("fk_groups_users_owner_temp_id5");
b.Navigation("Owner");
});
modelBuilder.Entity("MareSynchronosShared.Models.GroupPair", b =>
{
b.HasOne("MareSynchronosShared.Models.Group", "Group")
.WithMany()
.HasForeignKey("GroupGID")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired()
.HasConstraintName("fk_group_pairs_groups_group_temp_id");
b.HasOne("MareSynchronosShared.Models.User", "GroupUser")
.WithMany()
.HasForeignKey("GroupUserUID")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired()
.HasConstraintName("fk_group_pairs_users_group_user_temp_id4");
b.Navigation("Group");
b.Navigation("GroupUser");
});
modelBuilder.Entity("MareSynchronosShared.Models.LodeStoneAuth", b =>
{
b.HasOne("MareSynchronosShared.Models.User", "User")
.WithMany()
.HasForeignKey("UserUID")
.HasConstraintName("fk_lodestone_auth_users_user_uid");
b.Navigation("User");
});
#pragma warning restore 612, 618
}
}
}

View File

@@ -0,0 +1,123 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace MareSynchronosServer.Migrations
{
public partial class Groups : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropIndex(
name: "ix_users_character_identification",
table: "users");
migrationBuilder.DropColumn(
name: "character_identification",
table: "users");
migrationBuilder.AlterColumn<string>(
name: "alias",
table: "users",
type: "character varying(10)",
maxLength: 10,
nullable: true,
oldClrType: typeof(string),
oldType: "character varying(100)",
oldMaxLength: 100,
oldNullable: true);
migrationBuilder.CreateTable(
name: "groups",
columns: table => new
{
gid = table.Column<string>(type: "character varying(14)", maxLength: 14, nullable: false),
owner_uid = table.Column<string>(type: "character varying(10)", nullable: true),
alias = table.Column<string>(type: "character varying(50)", maxLength: 50, nullable: true),
invites_enabled = table.Column<bool>(type: "boolean", nullable: false),
hashed_password = table.Column<string>(type: "text", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("pk_groups", x => x.gid);
table.ForeignKey(
name: "fk_groups_users_owner_temp_id5",
column: x => x.owner_uid,
principalTable: "users",
principalColumn: "uid");
});
migrationBuilder.CreateTable(
name: "group_pairs",
columns: table => new
{
group_gid = table.Column<string>(type: "character varying(14)", nullable: false),
group_user_uid = table.Column<string>(type: "character varying(10)", nullable: false),
is_paused = table.Column<bool>(type: "boolean", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("pk_group_pairs", x => new { x.group_gid, x.group_user_uid });
table.ForeignKey(
name: "fk_group_pairs_groups_group_temp_id",
column: x => x.group_gid,
principalTable: "groups",
principalColumn: "gid",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "fk_group_pairs_users_group_user_temp_id4",
column: x => x.group_user_uid,
principalTable: "users",
principalColumn: "uid",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateIndex(
name: "ix_group_pairs_group_gid",
table: "group_pairs",
column: "group_gid");
migrationBuilder.CreateIndex(
name: "ix_group_pairs_group_user_uid",
table: "group_pairs",
column: "group_user_uid");
migrationBuilder.CreateIndex(
name: "ix_groups_owner_uid",
table: "groups",
column: "owner_uid");
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "group_pairs");
migrationBuilder.DropTable(
name: "groups");
migrationBuilder.AlterColumn<string>(
name: "alias",
table: "users",
type: "character varying(100)",
maxLength: 100,
nullable: true,
oldClrType: typeof(string),
oldType: "character varying(10)",
oldMaxLength: 10,
oldNullable: true);
migrationBuilder.AddColumn<string>(
name: "character_identification",
table: "users",
type: "character varying(100)",
maxLength: 100,
nullable: true);
migrationBuilder.CreateIndex(
name: "ix_users_character_identification",
table: "users",
column: "character_identification");
}
}
}

View File

@@ -0,0 +1,389 @@
// <auto-generated />
using System;
using MareSynchronosShared.Data;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
#nullable disable
namespace MareSynchronosServer.Migrations
{
[DbContext(typeof(MareDbContext))]
[Migration("20220929150304_ChangeGidLength")]
partial class ChangeGidLength
{
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "6.0.8")
.HasAnnotation("Relational:MaxIdentifierLength", 63);
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
modelBuilder.Entity("MareSynchronosShared.Models.Auth", b =>
{
b.Property<string>("HashedKey")
.HasMaxLength(64)
.HasColumnType("character varying(64)")
.HasColumnName("hashed_key");
b.Property<string>("UserUID")
.HasColumnType("character varying(10)")
.HasColumnName("user_uid");
b.HasKey("HashedKey")
.HasName("pk_auth");
b.HasIndex("UserUID")
.HasDatabaseName("ix_auth_user_uid");
b.ToTable("auth", (string)null);
});
modelBuilder.Entity("MareSynchronosShared.Models.Banned", b =>
{
b.Property<string>("CharacterIdentification")
.HasMaxLength(100)
.HasColumnType("character varying(100)")
.HasColumnName("character_identification");
b.Property<string>("Reason")
.HasColumnType("text")
.HasColumnName("reason");
b.Property<byte[]>("Timestamp")
.IsConcurrencyToken()
.ValueGeneratedOnAddOrUpdate()
.HasColumnType("bytea")
.HasColumnName("timestamp");
b.HasKey("CharacterIdentification")
.HasName("pk_banned_users");
b.ToTable("banned_users", (string)null);
});
modelBuilder.Entity("MareSynchronosShared.Models.BannedRegistrations", b =>
{
b.Property<string>("DiscordIdOrLodestoneAuth")
.HasMaxLength(100)
.HasColumnType("character varying(100)")
.HasColumnName("discord_id_or_lodestone_auth");
b.HasKey("DiscordIdOrLodestoneAuth")
.HasName("pk_banned_registrations");
b.ToTable("banned_registrations", (string)null);
});
modelBuilder.Entity("MareSynchronosShared.Models.ClientPair", b =>
{
b.Property<string>("UserUID")
.HasMaxLength(10)
.HasColumnType("character varying(10)")
.HasColumnName("user_uid");
b.Property<string>("OtherUserUID")
.HasMaxLength(10)
.HasColumnType("character varying(10)")
.HasColumnName("other_user_uid");
b.Property<bool>("AllowReceivingMessages")
.HasColumnType("boolean")
.HasColumnName("allow_receiving_messages");
b.Property<bool>("IsPaused")
.HasColumnType("boolean")
.HasColumnName("is_paused");
b.Property<byte[]>("Timestamp")
.IsConcurrencyToken()
.ValueGeneratedOnAddOrUpdate()
.HasColumnType("bytea")
.HasColumnName("timestamp");
b.HasKey("UserUID", "OtherUserUID")
.HasName("pk_client_pairs");
b.HasIndex("OtherUserUID")
.HasDatabaseName("ix_client_pairs_other_user_uid");
b.HasIndex("UserUID")
.HasDatabaseName("ix_client_pairs_user_uid");
b.ToTable("client_pairs", (string)null);
});
modelBuilder.Entity("MareSynchronosShared.Models.FileCache", b =>
{
b.Property<string>("Hash")
.HasMaxLength(40)
.HasColumnType("character varying(40)")
.HasColumnName("hash");
b.Property<byte[]>("Timestamp")
.IsConcurrencyToken()
.ValueGeneratedOnAddOrUpdate()
.HasColumnType("bytea")
.HasColumnName("timestamp");
b.Property<bool>("Uploaded")
.HasColumnType("boolean")
.HasColumnName("uploaded");
b.Property<string>("UploaderUID")
.HasMaxLength(10)
.HasColumnType("character varying(10)")
.HasColumnName("uploader_uid");
b.HasKey("Hash")
.HasName("pk_file_caches");
b.HasIndex("UploaderUID")
.HasDatabaseName("ix_file_caches_uploader_uid");
b.ToTable("file_caches", (string)null);
});
modelBuilder.Entity("MareSynchronosShared.Models.ForbiddenUploadEntry", b =>
{
b.Property<string>("Hash")
.HasMaxLength(40)
.HasColumnType("character varying(40)")
.HasColumnName("hash");
b.Property<string>("ForbiddenBy")
.HasMaxLength(100)
.HasColumnType("character varying(100)")
.HasColumnName("forbidden_by");
b.Property<byte[]>("Timestamp")
.IsConcurrencyToken()
.ValueGeneratedOnAddOrUpdate()
.HasColumnType("bytea")
.HasColumnName("timestamp");
b.HasKey("Hash")
.HasName("pk_forbidden_upload_entries");
b.ToTable("forbidden_upload_entries", (string)null);
});
modelBuilder.Entity("MareSynchronosShared.Models.Group", b =>
{
b.Property<string>("GID")
.HasMaxLength(20)
.HasColumnType("character varying(20)")
.HasColumnName("gid");
b.Property<string>("Alias")
.HasMaxLength(50)
.HasColumnType("character varying(50)")
.HasColumnName("alias");
b.Property<string>("HashedPassword")
.HasColumnType("text")
.HasColumnName("hashed_password");
b.Property<bool>("InvitesEnabled")
.HasColumnType("boolean")
.HasColumnName("invites_enabled");
b.Property<string>("OwnerUID")
.HasColumnType("character varying(10)")
.HasColumnName("owner_uid");
b.HasKey("GID")
.HasName("pk_groups");
b.HasIndex("OwnerUID")
.HasDatabaseName("ix_groups_owner_uid");
b.ToTable("groups", (string)null);
});
modelBuilder.Entity("MareSynchronosShared.Models.GroupPair", b =>
{
b.Property<string>("GroupGID")
.HasColumnType("character varying(20)")
.HasColumnName("group_gid");
b.Property<string>("GroupUserUID")
.HasColumnType("character varying(10)")
.HasColumnName("group_user_uid");
b.Property<bool>("IsPaused")
.HasColumnType("boolean")
.HasColumnName("is_paused");
b.HasKey("GroupGID", "GroupUserUID")
.HasName("pk_group_pairs");
b.HasIndex("GroupGID")
.HasDatabaseName("ix_group_pairs_group_gid");
b.HasIndex("GroupUserUID")
.HasDatabaseName("ix_group_pairs_group_user_uid");
b.ToTable("group_pairs", (string)null);
});
modelBuilder.Entity("MareSynchronosShared.Models.LodeStoneAuth", b =>
{
b.Property<decimal>("DiscordId")
.ValueGeneratedOnAdd()
.HasColumnType("numeric(20,0)")
.HasColumnName("discord_id");
b.Property<string>("HashedLodestoneId")
.HasMaxLength(100)
.HasColumnType("character varying(100)")
.HasColumnName("hashed_lodestone_id");
b.Property<string>("LodestoneAuthString")
.HasMaxLength(100)
.HasColumnType("character varying(100)")
.HasColumnName("lodestone_auth_string");
b.Property<DateTime?>("StartedAt")
.HasColumnType("timestamp with time zone")
.HasColumnName("started_at");
b.Property<string>("UserUID")
.HasColumnType("character varying(10)")
.HasColumnName("user_uid");
b.HasKey("DiscordId")
.HasName("pk_lodestone_auth");
b.HasIndex("UserUID")
.HasDatabaseName("ix_lodestone_auth_user_uid");
b.ToTable("lodestone_auth", (string)null);
});
modelBuilder.Entity("MareSynchronosShared.Models.User", b =>
{
b.Property<string>("UID")
.HasMaxLength(10)
.HasColumnType("character varying(10)")
.HasColumnName("uid");
b.Property<string>("Alias")
.HasMaxLength(10)
.HasColumnType("character varying(10)")
.HasColumnName("alias");
b.Property<bool>("IsAdmin")
.HasColumnType("boolean")
.HasColumnName("is_admin");
b.Property<bool>("IsModerator")
.HasColumnType("boolean")
.HasColumnName("is_moderator");
b.Property<DateTime>("LastLoggedIn")
.HasColumnType("timestamp with time zone")
.HasColumnName("last_logged_in");
b.Property<byte[]>("Timestamp")
.IsConcurrencyToken()
.ValueGeneratedOnAddOrUpdate()
.HasColumnType("bytea")
.HasColumnName("timestamp");
b.HasKey("UID")
.HasName("pk_users");
b.ToTable("users", (string)null);
});
modelBuilder.Entity("MareSynchronosShared.Models.Auth", b =>
{
b.HasOne("MareSynchronosShared.Models.User", "User")
.WithMany()
.HasForeignKey("UserUID")
.HasConstraintName("fk_auth_users_user_temp_id");
b.Navigation("User");
});
modelBuilder.Entity("MareSynchronosShared.Models.ClientPair", b =>
{
b.HasOne("MareSynchronosShared.Models.User", "OtherUser")
.WithMany()
.HasForeignKey("OtherUserUID")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired()
.HasConstraintName("fk_client_pairs_users_other_user_temp_id1");
b.HasOne("MareSynchronosShared.Models.User", "User")
.WithMany()
.HasForeignKey("UserUID")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired()
.HasConstraintName("fk_client_pairs_users_user_temp_id2");
b.Navigation("OtherUser");
b.Navigation("User");
});
modelBuilder.Entity("MareSynchronosShared.Models.FileCache", b =>
{
b.HasOne("MareSynchronosShared.Models.User", "Uploader")
.WithMany()
.HasForeignKey("UploaderUID")
.HasConstraintName("fk_file_caches_users_uploader_uid");
b.Navigation("Uploader");
});
modelBuilder.Entity("MareSynchronosShared.Models.Group", b =>
{
b.HasOne("MareSynchronosShared.Models.User", "Owner")
.WithMany()
.HasForeignKey("OwnerUID")
.HasConstraintName("fk_groups_users_owner_temp_id5");
b.Navigation("Owner");
});
modelBuilder.Entity("MareSynchronosShared.Models.GroupPair", b =>
{
b.HasOne("MareSynchronosShared.Models.Group", "Group")
.WithMany()
.HasForeignKey("GroupGID")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired()
.HasConstraintName("fk_group_pairs_groups_group_temp_id");
b.HasOne("MareSynchronosShared.Models.User", "GroupUser")
.WithMany()
.HasForeignKey("GroupUserUID")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired()
.HasConstraintName("fk_group_pairs_users_group_user_temp_id4");
b.Navigation("Group");
b.Navigation("GroupUser");
});
modelBuilder.Entity("MareSynchronosShared.Models.LodeStoneAuth", b =>
{
b.HasOne("MareSynchronosShared.Models.User", "User")
.WithMany()
.HasForeignKey("UserUID")
.HasConstraintName("fk_lodestone_auth_users_user_uid");
b.Navigation("User");
});
#pragma warning restore 612, 618
}
}
}

View File

@@ -0,0 +1,51 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace MareSynchronosServer.Migrations
{
public partial class ChangeGidLength : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AlterColumn<string>(
name: "gid",
table: "groups",
type: "character varying(20)",
maxLength: 20,
nullable: false,
oldClrType: typeof(string),
oldType: "character varying(14)",
oldMaxLength: 14);
migrationBuilder.AlterColumn<string>(
name: "group_gid",
table: "group_pairs",
type: "character varying(20)",
nullable: false,
oldClrType: typeof(string),
oldType: "character varying(14)");
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.AlterColumn<string>(
name: "gid",
table: "groups",
type: "character varying(14)",
maxLength: 14,
nullable: false,
oldClrType: typeof(string),
oldType: "character varying(20)",
oldMaxLength: 20);
migrationBuilder.AlterColumn<string>(
name: "group_gid",
table: "group_pairs",
type: "character varying(14)",
nullable: false,
oldClrType: typeof(string),
oldType: "character varying(20)");
}
}
}

View File

@@ -0,0 +1,393 @@
// <auto-generated />
using System;
using MareSynchronosShared.Data;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
#nullable disable
namespace MareSynchronosServer.Migrations
{
[DbContext(typeof(MareDbContext))]
[Migration("20221002105428_IsPinned")]
partial class IsPinned
{
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "6.0.8")
.HasAnnotation("Relational:MaxIdentifierLength", 63);
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
modelBuilder.Entity("MareSynchronosShared.Models.Auth", b =>
{
b.Property<string>("HashedKey")
.HasMaxLength(64)
.HasColumnType("character varying(64)")
.HasColumnName("hashed_key");
b.Property<string>("UserUID")
.HasColumnType("character varying(10)")
.HasColumnName("user_uid");
b.HasKey("HashedKey")
.HasName("pk_auth");
b.HasIndex("UserUID")
.HasDatabaseName("ix_auth_user_uid");
b.ToTable("auth", (string)null);
});
modelBuilder.Entity("MareSynchronosShared.Models.Banned", b =>
{
b.Property<string>("CharacterIdentification")
.HasMaxLength(100)
.HasColumnType("character varying(100)")
.HasColumnName("character_identification");
b.Property<string>("Reason")
.HasColumnType("text")
.HasColumnName("reason");
b.Property<byte[]>("Timestamp")
.IsConcurrencyToken()
.ValueGeneratedOnAddOrUpdate()
.HasColumnType("bytea")
.HasColumnName("timestamp");
b.HasKey("CharacterIdentification")
.HasName("pk_banned_users");
b.ToTable("banned_users", (string)null);
});
modelBuilder.Entity("MareSynchronosShared.Models.BannedRegistrations", b =>
{
b.Property<string>("DiscordIdOrLodestoneAuth")
.HasMaxLength(100)
.HasColumnType("character varying(100)")
.HasColumnName("discord_id_or_lodestone_auth");
b.HasKey("DiscordIdOrLodestoneAuth")
.HasName("pk_banned_registrations");
b.ToTable("banned_registrations", (string)null);
});
modelBuilder.Entity("MareSynchronosShared.Models.ClientPair", b =>
{
b.Property<string>("UserUID")
.HasMaxLength(10)
.HasColumnType("character varying(10)")
.HasColumnName("user_uid");
b.Property<string>("OtherUserUID")
.HasMaxLength(10)
.HasColumnType("character varying(10)")
.HasColumnName("other_user_uid");
b.Property<bool>("AllowReceivingMessages")
.HasColumnType("boolean")
.HasColumnName("allow_receiving_messages");
b.Property<bool>("IsPaused")
.HasColumnType("boolean")
.HasColumnName("is_paused");
b.Property<byte[]>("Timestamp")
.IsConcurrencyToken()
.ValueGeneratedOnAddOrUpdate()
.HasColumnType("bytea")
.HasColumnName("timestamp");
b.HasKey("UserUID", "OtherUserUID")
.HasName("pk_client_pairs");
b.HasIndex("OtherUserUID")
.HasDatabaseName("ix_client_pairs_other_user_uid");
b.HasIndex("UserUID")
.HasDatabaseName("ix_client_pairs_user_uid");
b.ToTable("client_pairs", (string)null);
});
modelBuilder.Entity("MareSynchronosShared.Models.FileCache", b =>
{
b.Property<string>("Hash")
.HasMaxLength(40)
.HasColumnType("character varying(40)")
.HasColumnName("hash");
b.Property<byte[]>("Timestamp")
.IsConcurrencyToken()
.ValueGeneratedOnAddOrUpdate()
.HasColumnType("bytea")
.HasColumnName("timestamp");
b.Property<bool>("Uploaded")
.HasColumnType("boolean")
.HasColumnName("uploaded");
b.Property<string>("UploaderUID")
.HasMaxLength(10)
.HasColumnType("character varying(10)")
.HasColumnName("uploader_uid");
b.HasKey("Hash")
.HasName("pk_file_caches");
b.HasIndex("UploaderUID")
.HasDatabaseName("ix_file_caches_uploader_uid");
b.ToTable("file_caches", (string)null);
});
modelBuilder.Entity("MareSynchronosShared.Models.ForbiddenUploadEntry", b =>
{
b.Property<string>("Hash")
.HasMaxLength(40)
.HasColumnType("character varying(40)")
.HasColumnName("hash");
b.Property<string>("ForbiddenBy")
.HasMaxLength(100)
.HasColumnType("character varying(100)")
.HasColumnName("forbidden_by");
b.Property<byte[]>("Timestamp")
.IsConcurrencyToken()
.ValueGeneratedOnAddOrUpdate()
.HasColumnType("bytea")
.HasColumnName("timestamp");
b.HasKey("Hash")
.HasName("pk_forbidden_upload_entries");
b.ToTable("forbidden_upload_entries", (string)null);
});
modelBuilder.Entity("MareSynchronosShared.Models.Group", b =>
{
b.Property<string>("GID")
.HasMaxLength(20)
.HasColumnType("character varying(20)")
.HasColumnName("gid");
b.Property<string>("Alias")
.HasMaxLength(50)
.HasColumnType("character varying(50)")
.HasColumnName("alias");
b.Property<string>("HashedPassword")
.HasColumnType("text")
.HasColumnName("hashed_password");
b.Property<bool>("InvitesEnabled")
.HasColumnType("boolean")
.HasColumnName("invites_enabled");
b.Property<string>("OwnerUID")
.HasColumnType("character varying(10)")
.HasColumnName("owner_uid");
b.HasKey("GID")
.HasName("pk_groups");
b.HasIndex("OwnerUID")
.HasDatabaseName("ix_groups_owner_uid");
b.ToTable("groups", (string)null);
});
modelBuilder.Entity("MareSynchronosShared.Models.GroupPair", b =>
{
b.Property<string>("GroupGID")
.HasColumnType("character varying(20)")
.HasColumnName("group_gid");
b.Property<string>("GroupUserUID")
.HasColumnType("character varying(10)")
.HasColumnName("group_user_uid");
b.Property<bool>("IsPaused")
.HasColumnType("boolean")
.HasColumnName("is_paused");
b.Property<bool>("IsPinned")
.HasColumnType("boolean")
.HasColumnName("is_pinned");
b.HasKey("GroupGID", "GroupUserUID")
.HasName("pk_group_pairs");
b.HasIndex("GroupGID")
.HasDatabaseName("ix_group_pairs_group_gid");
b.HasIndex("GroupUserUID")
.HasDatabaseName("ix_group_pairs_group_user_uid");
b.ToTable("group_pairs", (string)null);
});
modelBuilder.Entity("MareSynchronosShared.Models.LodeStoneAuth", b =>
{
b.Property<decimal>("DiscordId")
.ValueGeneratedOnAdd()
.HasColumnType("numeric(20,0)")
.HasColumnName("discord_id");
b.Property<string>("HashedLodestoneId")
.HasMaxLength(100)
.HasColumnType("character varying(100)")
.HasColumnName("hashed_lodestone_id");
b.Property<string>("LodestoneAuthString")
.HasMaxLength(100)
.HasColumnType("character varying(100)")
.HasColumnName("lodestone_auth_string");
b.Property<DateTime?>("StartedAt")
.HasColumnType("timestamp with time zone")
.HasColumnName("started_at");
b.Property<string>("UserUID")
.HasColumnType("character varying(10)")
.HasColumnName("user_uid");
b.HasKey("DiscordId")
.HasName("pk_lodestone_auth");
b.HasIndex("UserUID")
.HasDatabaseName("ix_lodestone_auth_user_uid");
b.ToTable("lodestone_auth", (string)null);
});
modelBuilder.Entity("MareSynchronosShared.Models.User", b =>
{
b.Property<string>("UID")
.HasMaxLength(10)
.HasColumnType("character varying(10)")
.HasColumnName("uid");
b.Property<string>("Alias")
.HasMaxLength(10)
.HasColumnType("character varying(10)")
.HasColumnName("alias");
b.Property<bool>("IsAdmin")
.HasColumnType("boolean")
.HasColumnName("is_admin");
b.Property<bool>("IsModerator")
.HasColumnType("boolean")
.HasColumnName("is_moderator");
b.Property<DateTime>("LastLoggedIn")
.HasColumnType("timestamp with time zone")
.HasColumnName("last_logged_in");
b.Property<byte[]>("Timestamp")
.IsConcurrencyToken()
.ValueGeneratedOnAddOrUpdate()
.HasColumnType("bytea")
.HasColumnName("timestamp");
b.HasKey("UID")
.HasName("pk_users");
b.ToTable("users", (string)null);
});
modelBuilder.Entity("MareSynchronosShared.Models.Auth", b =>
{
b.HasOne("MareSynchronosShared.Models.User", "User")
.WithMany()
.HasForeignKey("UserUID")
.HasConstraintName("fk_auth_users_user_temp_id");
b.Navigation("User");
});
modelBuilder.Entity("MareSynchronosShared.Models.ClientPair", b =>
{
b.HasOne("MareSynchronosShared.Models.User", "OtherUser")
.WithMany()
.HasForeignKey("OtherUserUID")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired()
.HasConstraintName("fk_client_pairs_users_other_user_temp_id1");
b.HasOne("MareSynchronosShared.Models.User", "User")
.WithMany()
.HasForeignKey("UserUID")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired()
.HasConstraintName("fk_client_pairs_users_user_temp_id2");
b.Navigation("OtherUser");
b.Navigation("User");
});
modelBuilder.Entity("MareSynchronosShared.Models.FileCache", b =>
{
b.HasOne("MareSynchronosShared.Models.User", "Uploader")
.WithMany()
.HasForeignKey("UploaderUID")
.HasConstraintName("fk_file_caches_users_uploader_uid");
b.Navigation("Uploader");
});
modelBuilder.Entity("MareSynchronosShared.Models.Group", b =>
{
b.HasOne("MareSynchronosShared.Models.User", "Owner")
.WithMany()
.HasForeignKey("OwnerUID")
.HasConstraintName("fk_groups_users_owner_temp_id5");
b.Navigation("Owner");
});
modelBuilder.Entity("MareSynchronosShared.Models.GroupPair", b =>
{
b.HasOne("MareSynchronosShared.Models.Group", "Group")
.WithMany()
.HasForeignKey("GroupGID")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired()
.HasConstraintName("fk_group_pairs_groups_group_temp_id");
b.HasOne("MareSynchronosShared.Models.User", "GroupUser")
.WithMany()
.HasForeignKey("GroupUserUID")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired()
.HasConstraintName("fk_group_pairs_users_group_user_temp_id4");
b.Navigation("Group");
b.Navigation("GroupUser");
});
modelBuilder.Entity("MareSynchronosShared.Models.LodeStoneAuth", b =>
{
b.HasOne("MareSynchronosShared.Models.User", "User")
.WithMany()
.HasForeignKey("UserUID")
.HasConstraintName("fk_lodestone_auth_users_user_uid");
b.Navigation("User");
});
#pragma warning restore 612, 618
}
}
}

View File

@@ -0,0 +1,26 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace MareSynchronosServer.Migrations
{
public partial class IsPinned : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<bool>(
name: "is_pinned",
table: "group_pairs",
type: "boolean",
nullable: false,
defaultValue: false);
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "is_pinned",
table: "group_pairs");
}
}
}

View File

@@ -171,6 +171,69 @@ namespace MareSynchronosServer.Migrations
b.ToTable("forbidden_upload_entries", (string)null);
});
modelBuilder.Entity("MareSynchronosShared.Models.Group", b =>
{
b.Property<string>("GID")
.HasMaxLength(20)
.HasColumnType("character varying(20)")
.HasColumnName("gid");
b.Property<string>("Alias")
.HasMaxLength(50)
.HasColumnType("character varying(50)")
.HasColumnName("alias");
b.Property<string>("HashedPassword")
.HasColumnType("text")
.HasColumnName("hashed_password");
b.Property<bool>("InvitesEnabled")
.HasColumnType("boolean")
.HasColumnName("invites_enabled");
b.Property<string>("OwnerUID")
.HasColumnType("character varying(10)")
.HasColumnName("owner_uid");
b.HasKey("GID")
.HasName("pk_groups");
b.HasIndex("OwnerUID")
.HasDatabaseName("ix_groups_owner_uid");
b.ToTable("groups", (string)null);
});
modelBuilder.Entity("MareSynchronosShared.Models.GroupPair", b =>
{
b.Property<string>("GroupGID")
.HasColumnType("character varying(20)")
.HasColumnName("group_gid");
b.Property<string>("GroupUserUID")
.HasColumnType("character varying(10)")
.HasColumnName("group_user_uid");
b.Property<bool>("IsPaused")
.HasColumnType("boolean")
.HasColumnName("is_paused");
b.Property<bool>("IsPinned")
.HasColumnType("boolean")
.HasColumnName("is_pinned");
b.HasKey("GroupGID", "GroupUserUID")
.HasName("pk_group_pairs");
b.HasIndex("GroupGID")
.HasDatabaseName("ix_group_pairs_group_gid");
b.HasIndex("GroupUserUID")
.HasDatabaseName("ix_group_pairs_group_user_uid");
b.ToTable("group_pairs", (string)null);
});
modelBuilder.Entity("MareSynchronosShared.Models.LodeStoneAuth", b =>
{
b.Property<decimal>("DiscordId")
@@ -213,15 +276,10 @@ namespace MareSynchronosServer.Migrations
.HasColumnName("uid");
b.Property<string>("Alias")
.HasMaxLength(100)
.HasColumnType("character varying(100)")
.HasMaxLength(10)
.HasColumnType("character varying(10)")
.HasColumnName("alias");
b.Property<string>("CharacterIdentification")
.HasMaxLength(100)
.HasColumnType("character varying(100)")
.HasColumnName("character_identification");
b.Property<bool>("IsAdmin")
.HasColumnType("boolean")
.HasColumnName("is_admin");
@@ -243,9 +301,6 @@ namespace MareSynchronosServer.Migrations
b.HasKey("UID")
.HasName("pk_users");
b.HasIndex("CharacterIdentification")
.HasDatabaseName("ix_users_character_identification");
b.ToTable("users", (string)null);
});
@@ -290,6 +345,37 @@ namespace MareSynchronosServer.Migrations
b.Navigation("Uploader");
});
modelBuilder.Entity("MareSynchronosShared.Models.Group", b =>
{
b.HasOne("MareSynchronosShared.Models.User", "Owner")
.WithMany()
.HasForeignKey("OwnerUID")
.HasConstraintName("fk_groups_users_owner_temp_id5");
b.Navigation("Owner");
});
modelBuilder.Entity("MareSynchronosShared.Models.GroupPair", b =>
{
b.HasOne("MareSynchronosShared.Models.Group", "Group")
.WithMany()
.HasForeignKey("GroupGID")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired()
.HasConstraintName("fk_group_pairs_groups_group_temp_id");
b.HasOne("MareSynchronosShared.Models.User", "GroupUser")
.WithMany()
.HasForeignKey("GroupUserUID")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired()
.HasConstraintName("fk_group_pairs_users_group_user_temp_id4");
b.Navigation("Group");
b.Navigation("GroupUser");
});
modelBuilder.Entity("MareSynchronosShared.Models.LodeStoneAuth", b =>
{
b.HasOne("MareSynchronosShared.Models.User", "User")

View File

@@ -1,14 +1,13 @@
using System.ComponentModel.DataAnnotations;
namespace MareSynchronosShared.Models
{
public class Auth
{
[Key]
[MaxLength(64)]
public string HashedKey { get; set; }
namespace MareSynchronosShared.Models;
public string UserUID { get; set; }
public User User { get; set; }
}
public class Auth
{
[Key]
[MaxLength(64)]
public string HashedKey { get; set; }
public string UserUID { get; set; }
public User User { get; set; }
}

View File

@@ -1,14 +1,13 @@
using System.ComponentModel.DataAnnotations;
namespace MareSynchronosShared.Models
namespace MareSynchronosShared.Models;
public class Banned
{
public class Banned
{
[Key]
[MaxLength(100)]
public string CharacterIdentification { get; set; }
public string Reason { get; set; }
[Timestamp]
public byte[] Timestamp { get; set; }
}
[Key]
[MaxLength(100)]
public string CharacterIdentification { get; set; }
public string Reason { get; set; }
[Timestamp]
public byte[] Timestamp { get; set; }
}

View File

@@ -1,11 +1,10 @@
using System.ComponentModel.DataAnnotations;
namespace MareSynchronosShared.Models
namespace MareSynchronosShared.Models;
public class BannedRegistrations
{
public class BannedRegistrations
{
[Key]
[MaxLength(100)]
public string DiscordIdOrLodestoneAuth { get; set; }
}
[Key]
[MaxLength(100)]
public string DiscordIdOrLodestoneAuth { get; set; }
}

View File

@@ -1,18 +1,17 @@
using System.ComponentModel.DataAnnotations;
namespace MareSynchronosShared.Models
namespace MareSynchronosShared.Models;
public class ClientPair
{
public class ClientPair
{
[MaxLength(10)]
public string UserUID { get; set; }
public User User { get; set; }
[MaxLength(10)]
public string OtherUserUID { get; set; }
public User OtherUser { get; set; }
public bool IsPaused { get; set; }
public bool AllowReceivingMessages { get; set; } = false;
[Timestamp]
public byte[] Timestamp { get; set; }
}
[MaxLength(10)]
public string UserUID { get; set; }
public User User { get; set; }
[MaxLength(10)]
public string OtherUserUID { get; set; }
public User OtherUser { get; set; }
public bool IsPaused { get; set; }
public bool AllowReceivingMessages { get; set; } = false;
[Timestamp]
public byte[] Timestamp { get; set; }
}

View File

@@ -1,17 +1,16 @@
using System.ComponentModel.DataAnnotations;
namespace MareSynchronosShared.Models
namespace MareSynchronosShared.Models;
public class FileCache
{
public class FileCache
{
[Key]
[MaxLength(40)]
public string Hash { get; set; }
[MaxLength(10)]
public string UploaderUID { get; set; }
public User Uploader { get; set; }
public bool Uploaded { get; set; }
[Timestamp]
public byte[] Timestamp { get; set; }
}
[Key]
[MaxLength(40)]
public string Hash { get; set; }
[MaxLength(10)]
public string UploaderUID { get; set; }
public User Uploader { get; set; }
public bool Uploaded { get; set; }
[Timestamp]
public byte[] Timestamp { get; set; }
}

View File

@@ -1,15 +1,14 @@
using System.ComponentModel.DataAnnotations;
namespace MareSynchronosShared.Models
namespace MareSynchronosShared.Models;
public class ForbiddenUploadEntry
{
public class ForbiddenUploadEntry
{
[Key]
[MaxLength(40)]
public string Hash { get; set; }
[MaxLength(100)]
public string ForbiddenBy { get; set; }
[Timestamp]
public byte[] Timestamp { get; set; }
}
[Key]
[MaxLength(40)]
public string Hash { get; set; }
[MaxLength(100)]
public string ForbiddenBy { get; set; }
[Timestamp]
public byte[] Timestamp { get; set; }
}

View File

@@ -0,0 +1,16 @@
using System.ComponentModel.DataAnnotations;
namespace MareSynchronosShared.Models;
public class Group
{
[Key]
[MaxLength(20)]
public string GID { get; set; }
public string OwnerUID { get; set; }
public User Owner { get; set; }
[MaxLength(50)]
public string Alias { get; set; }
public bool InvitesEnabled { get; set; }
public string HashedPassword { get; set; }
}

View File

@@ -0,0 +1,11 @@
namespace MareSynchronosShared.Models;
public class GroupPair
{
public string GroupGID { get; set; }
public Group Group { get; set; }
public string GroupUserUID { get; set; }
public User GroupUser { get; set; }
public bool IsPaused { get; set; }
public bool IsPinned { get; set; }
}

View File

@@ -1,16 +1,15 @@
using System.ComponentModel.DataAnnotations;
namespace MareSynchronosShared.Models
namespace MareSynchronosShared.Models;
public class LodeStoneAuth
{
public class LodeStoneAuth
{
[Key]
public ulong DiscordId { get; set; }
[MaxLength(100)]
public string HashedLodestoneId { get; set; }
[MaxLength(100)]
public string? LodestoneAuthString { get; set; }
public User? User { get; set; }
public DateTime? StartedAt { get; set; }
}
[Key]
public ulong DiscordId { get; set; }
[MaxLength(100)]
public string HashedLodestoneId { get; set; }
[MaxLength(100)]
public string? LodestoneAuthString { get; set; }
public User? User { get; set; }
public DateTime? StartedAt { get; set; }
}

View File

@@ -1,23 +1,20 @@
using System.ComponentModel.DataAnnotations;
namespace MareSynchronosShared.Models
namespace MareSynchronosShared.Models;
public class User
{
public class User
{
[Key]
[MaxLength(10)]
public string UID { get; set; }
//[MaxLength(100)]
//public string CharacterIdentification { get; set; }
[Timestamp]
public byte[] Timestamp { get; set; }
[Key]
[MaxLength(10)]
public string UID { get; set; }
[Timestamp]
public byte[] Timestamp { get; set; }
public bool IsModerator { get; set; } = false;
public bool IsModerator { get; set; } = false;
public bool IsAdmin { get; set; } = false;
public bool IsAdmin { get; set; } = false;
public DateTime LastLoggedIn { get; set; }
[MaxLength(10)]
public string Alias { get; set; }
}
public DateTime LastLoggedIn { get; set; }
[MaxLength(10)]
public string Alias { get; set; }
}

View File

@@ -12,40 +12,43 @@ public abstract class BaseClientIdentificationService : IClientIdentificationSer
this.metrics = metrics;
}
public virtual int GetOnlineUsers()
public virtual Task<int> GetOnlineUsers()
{
return OnlineClients.Count;
return Task.FromResult(OnlineClients.Count);
}
public string? GetUidForCharacterIdent(string characterIdent)
public Task<string?> GetUidForCharacterIdent(string characterIdent)
{
var result = OnlineClients.SingleOrDefault(u =>
string.Compare(u.Value, characterIdent, StringComparison.InvariantCultureIgnoreCase) == 0);
return result.Equals(new KeyValuePair<string, string>()) ? null : result.Key;
return Task.FromResult(result.Equals(new KeyValuePair<string, string>()) ? null : result.Key);
}
public virtual string? GetCharacterIdentForUid(string uid)
public virtual Task<string?> GetCharacterIdentForUid(string uid)
{
if (!OnlineClients.TryGetValue(uid, out var result))
{
return null;
return Task.FromResult((string?)null);
}
return result;
return Task.FromResult(result);
}
public virtual void MarkUserOnline(string uid, string charaIdent)
public virtual Task MarkUserOnline(string uid, string charaIdent)
{
OnlineClients[uid] = charaIdent;
metrics.SetGaugeTo(MetricsAPI.GaugeAuthorizedConnections, OnlineClients.Count);
return Task.CompletedTask;
}
public virtual void MarkUserOffline(string uid)
public virtual Task MarkUserOffline(string uid)
{
if (OnlineClients.TryRemove(uid, out _))
{
metrics.SetGaugeTo(MetricsAPI.GaugeAuthorizedConnections, OnlineClients.Count);
}
return Task.CompletedTask;
}
public Task StartAsync(CancellationToken cancellationToken)

View File

@@ -21,14 +21,14 @@ public class DistributedClientIdentificationService : BaseClientIdentificationSe
this.configuration = configuration.GetSection("MareSynchronos");
}
public override int GetOnlineUsers()
public override async Task<int> GetOnlineUsers()
{
try
{
var redis = configuration.GetValue<string>("RedisConnectionString");
var conn = ConnectionMultiplexer.Connect(redis);
var conn = await ConnectionMultiplexer.ConnectAsync(redis).ConfigureAwait(false);
var endpoint = conn.GetEndPoints().First();
return conn.GetServer(endpoint).Keys(pattern: "*" + RedisPrefix + "*").Count();
return await conn.GetServer(endpoint).KeysAsync(pattern: "*" + RedisPrefix + "*").CountAsync().ConfigureAwait(false);
}
catch (Exception ex)
{
@@ -37,27 +37,27 @@ public class DistributedClientIdentificationService : BaseClientIdentificationSe
}
}
public override string? GetCharacterIdentForUid(string uid)
public override async Task<string?> GetCharacterIdentForUid(string uid)
{
var localIdent = base.GetCharacterIdentForUid(uid);
var localIdent = await base.GetCharacterIdentForUid(uid).ConfigureAwait(false);
if (localIdent != null) return localIdent;
var cachedIdent = distributedCache.Get(RedisPrefix + uid);
return cachedIdent == null ? null : Encoding.UTF8.GetString(cachedIdent);
var cachedIdent = await distributedCache.GetStringAsync(RedisPrefix + uid).ConfigureAwait(false);
return cachedIdent ?? null;
}
public override void MarkUserOffline(string uid)
public override async Task MarkUserOffline(string uid)
{
base.MarkUserOffline(uid);
distributedCache.Remove(RedisPrefix + uid);
await base.MarkUserOffline(uid).ConfigureAwait(false);
await distributedCache.RemoveAsync(RedisPrefix + uid).ConfigureAwait(false);
}
public override void MarkUserOnline(string uid, string charaIdent)
public override async Task MarkUserOnline(string uid, string charaIdent)
{
base.MarkUserOnline(uid, charaIdent);
distributedCache.Set(RedisPrefix + uid, Encoding.UTF8.GetBytes(charaIdent), new DistributedCacheEntryOptions()
await base.MarkUserOnline(uid, charaIdent).ConfigureAwait(false);
await distributedCache.SetAsync(RedisPrefix + uid, Encoding.UTF8.GetBytes(charaIdent), new DistributedCacheEntryOptions()
{
AbsoluteExpiration = DateTime.Now.AddDays(7)
});
}).ConfigureAwait(false);
}
public override Task StopAsync(CancellationToken cancellationToken)

View File

@@ -4,9 +4,9 @@ namespace MareSynchronosShared.Services;
public interface IClientIdentificationService : IHostedService
{
int GetOnlineUsers();
string? GetUidForCharacterIdent(string characterIdent);
string? GetCharacterIdentForUid(string uid);
void MarkUserOnline(string uid, string charaIdent);
void MarkUserOffline(string uid);
Task<int> GetOnlineUsers();
Task<string?> GetUidForCharacterIdent(string characterIdent);
Task<string?> GetCharacterIdentForUid(string uid);
Task MarkUserOnline(string uid, string charaIdent);
Task MarkUserOffline(string uid);
}

View File

@@ -0,0 +1,52 @@
using MareSynchronosShared.Data;
using MareSynchronosShared.Models;
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace MareSynchronosShared.Utils;
public static class SharedDbFunctions
{
public static async Task<(bool, string)> MigrateOrDeleteGroup(MareDbContext context, Group group, List<GroupPair> groupPairs, int maxGroupsByUser)
{
bool groupHasMigrated = false;
string newOwner = string.Empty;
foreach (var potentialNewOwner in groupPairs)
{
groupHasMigrated = await TryMigrateGroup(context, group, potentialNewOwner.GroupUserUID, maxGroupsByUser).ConfigureAwait(false);
if (groupHasMigrated)
{
newOwner = potentialNewOwner.GroupUserUID;
break;
}
}
if (!groupHasMigrated)
{
context.GroupPairs.RemoveRange(groupPairs);
context.Groups.Remove(group);
await context.SaveChangesAsync().ConfigureAwait(false);
}
return (groupHasMigrated, newOwner);
}
private static async Task<bool> TryMigrateGroup(MareDbContext context, Group group, string potentialNewOwnerUid, int maxGroupsByUser)
{
var newOwnerOwnedGroups = await context.Groups.CountAsync(g => g.OwnerUID == potentialNewOwnerUid).ConfigureAwait(false);
if (newOwnerOwnedGroups >= maxGroupsByUser)
{
return false;
}
group.OwnerUID = potentialNewOwnerUid;
group.Alias = null;
await context.SaveChangesAsync().ConfigureAwait(false);
return true;
}
}

View File

@@ -0,0 +1,31 @@
using System.Security.Cryptography;
using System.Text;
namespace MareSynchronosShared.Utils;
public static class StringUtils
{
public static string GenerateRandomString(int length, string? allowableChars = null)
{
if (string.IsNullOrEmpty(allowableChars))
allowableChars = @"ABCDEFGHJKLMNPQRSTUVWXYZ0123456789";
// Generate random data
var rnd = RandomNumberGenerator.GetBytes(length);
// Generate the output string
var allowable = allowableChars.ToCharArray();
var l = allowable.Length;
var chars = new char[length];
for (var i = 0; i < length; i++)
chars[i] = allowable[rnd[i] % l];
return new string(chars);
}
public static string Sha256String(string input)
{
using var sha256 = SHA256.Create();
return BitConverter.ToString(sha256.ComputeHash(Encoding.UTF8.GetBytes(input))).Replace("-", "", StringComparison.OrdinalIgnoreCase);
}
}