Server ban exemption system (#15076)
This commit is contained in:
committed by
GitHub
parent
e037d12899
commit
c8e90e561b
@@ -12,12 +12,16 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="6.0.5">
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="7.0.4">
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="6.0.5" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="7.0.4" />
|
||||||
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="6.0.4" />
|
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="7.0.3" />
|
||||||
|
|
||||||
|
<!-- Necessary at design time -->
|
||||||
|
<PackageReference Include="SQLitePCLRaw.provider.e_sqlite3" Version="2.1.4" Condition="'$(UseSystemSqlite)' != 'True' and '$(Configuration)' != 'Release'" />
|
||||||
|
<PackageReference Include="SQLitePCLRaw.bundle_e_sqlite3" Version="2.1.4" Condition="'$(UseSystemSqlite)' != 'True' and '$(Configuration)' != 'Release'" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@@ -1,5 +1,9 @@
|
|||||||
using Microsoft.EntityFrameworkCore;
|
#if TOOLS
|
||||||
|
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Microsoft.EntityFrameworkCore.Design;
|
using Microsoft.EntityFrameworkCore.Design;
|
||||||
|
using SQLitePCL;
|
||||||
|
|
||||||
// ReSharper disable UnusedType.Global
|
// ReSharper disable UnusedType.Global
|
||||||
|
|
||||||
namespace Content.Server.Database;
|
namespace Content.Server.Database;
|
||||||
@@ -18,8 +22,14 @@ public sealed class DesignTimeContextFactorySqlite : IDesignTimeDbContextFactory
|
|||||||
{
|
{
|
||||||
public SqliteServerDbContext CreateDbContext(string[] args)
|
public SqliteServerDbContext CreateDbContext(string[] args)
|
||||||
{
|
{
|
||||||
|
#if !USE_SYSTEM_SQLITE
|
||||||
|
raw.SetProvider(new SQLite3Provider_e_sqlite3());
|
||||||
|
#endif
|
||||||
|
|
||||||
var optionsBuilder = new DbContextOptionsBuilder<SqliteServerDbContext>();
|
var optionsBuilder = new DbContextOptionsBuilder<SqliteServerDbContext>();
|
||||||
optionsBuilder.UseSqlite("Data Source=:memory:");
|
optionsBuilder.UseSqlite("Data Source=:memory:");
|
||||||
return new SqliteServerDbContext(optionsBuilder.Options);
|
return new SqliteServerDbContext(optionsBuilder.Options);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|||||||
1333
Content.Server.Database/Migrations/Postgres/20230319110655_ProfileTraitIndexUnique.Designer.cs
generated
Normal file
1333
Content.Server.Database/Migrations/Postgres/20230319110655_ProfileTraitIndexUnique.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,34 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace Content.Server.Database.Migrations.Postgres
|
||||||
|
{
|
||||||
|
public partial class ProfileTraitIndexUnique : Migration
|
||||||
|
{
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropIndex(
|
||||||
|
name: "IX_trait_profile_id",
|
||||||
|
table: "trait");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_trait_profile_id_trait_name",
|
||||||
|
table: "trait",
|
||||||
|
columns: new[] { "profile_id", "trait_name" },
|
||||||
|
unique: true);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropIndex(
|
||||||
|
name: "IX_trait_profile_id_trait_name",
|
||||||
|
table: "trait");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_trait_profile_id",
|
||||||
|
table: "trait",
|
||||||
|
column: "profile_id");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
1356
Content.Server.Database/Migrations/Postgres/20230319112124_ServerBanExemption.Designer.cs
generated
Normal file
1356
Content.Server.Database/Migrations/Postgres/20230319112124_ServerBanExemption.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,43 @@
|
|||||||
|
using System;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace Content.Server.Database.Migrations.Postgres
|
||||||
|
{
|
||||||
|
public partial class ServerBanExemption : Migration
|
||||||
|
{
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.AddColumn<int>(
|
||||||
|
name: "exempt_flags",
|
||||||
|
table: "server_ban",
|
||||||
|
type: "integer",
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: 0);
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "server_ban_exemption",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
user_id = table.Column<Guid>(type: "uuid", nullable: false),
|
||||||
|
flags = table.Column<int>(type: "integer", nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_server_ban_exemption", x => x.user_id);
|
||||||
|
table.CheckConstraint("FlagsNotZero", "flags != 0");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "server_ban_exemption");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "exempt_flags",
|
||||||
|
table: "server_ban");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
1364
Content.Server.Database/Migrations/Postgres/20230402214647_BanAutoDelete.Designer.cs
generated
Normal file
1364
Content.Server.Database/Migrations/Postgres/20230402214647_BanAutoDelete.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,29 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace Content.Server.Database.Migrations.Postgres
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class BanAutoDelete : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.AddColumn<bool>(
|
||||||
|
name: "auto_delete",
|
||||||
|
table: "server_ban",
|
||||||
|
type: "boolean",
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "auto_delete",
|
||||||
|
table: "server_ban");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -19,7 +19,7 @@ namespace Content.Server.Database.Migrations.Postgres
|
|||||||
{
|
{
|
||||||
#pragma warning disable 612, 618
|
#pragma warning disable 612, 618
|
||||||
modelBuilder
|
modelBuilder
|
||||||
.HasAnnotation("ProductVersion", "6.0.5")
|
.HasAnnotation("ProductVersion", "7.0.4")
|
||||||
.HasAnnotation("Relational:MaxIdentifierLength", 63);
|
.HasAnnotation("Relational:MaxIdentifierLength", 63);
|
||||||
|
|
||||||
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
|
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
|
||||||
@@ -302,8 +302,7 @@ namespace Content.Server.Database.Migrations.Postgres
|
|||||||
b.HasKey("Id")
|
b.HasKey("Id")
|
||||||
.HasName("PK_admin_rank_flag");
|
.HasName("PK_admin_rank_flag");
|
||||||
|
|
||||||
b.HasIndex("AdminRankId")
|
b.HasIndex("AdminRankId");
|
||||||
.HasDatabaseName("IX_admin_rank_flag_admin_rank_id");
|
|
||||||
|
|
||||||
b.HasIndex("Flag", "AdminRankId")
|
b.HasIndex("Flag", "AdminRankId")
|
||||||
.IsUnique();
|
.IsUnique();
|
||||||
@@ -408,9 +407,10 @@ namespace Content.Server.Database.Migrations.Postgres
|
|||||||
|
|
||||||
b.HasIndex("UserId");
|
b.HasIndex("UserId");
|
||||||
|
|
||||||
b.ToTable("connection_log", (string)null);
|
b.ToTable("connection_log", null, t =>
|
||||||
|
{
|
||||||
b.HasCheckConstraint("AddressNotIPv6MappedIPv4", "NOT inet '::ffff:0.0.0.0/96' >>= address");
|
t.HasCheckConstraint("AddressNotIPv6MappedIPv4", "NOT inet '::ffff:0.0.0.0/96' >>= address");
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Content.Server.Database.Job", b =>
|
modelBuilder.Entity("Content.Server.Database.Job", b =>
|
||||||
@@ -438,8 +438,7 @@ namespace Content.Server.Database.Migrations.Postgres
|
|||||||
b.HasKey("Id")
|
b.HasKey("Id")
|
||||||
.HasName("PK_job");
|
.HasName("PK_job");
|
||||||
|
|
||||||
b.HasIndex("ProfileId")
|
b.HasIndex("ProfileId");
|
||||||
.HasDatabaseName("IX_job_profile_id");
|
|
||||||
|
|
||||||
b.HasIndex("ProfileId", "JobName")
|
b.HasIndex("ProfileId", "JobName")
|
||||||
.IsUnique();
|
.IsUnique();
|
||||||
@@ -451,6 +450,37 @@ namespace Content.Server.Database.Migrations.Postgres
|
|||||||
b.ToTable("job", (string)null);
|
b.ToTable("job", (string)null);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Content.Server.Database.PlayTime", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("integer")
|
||||||
|
.HasColumnName("play_time_id");
|
||||||
|
|
||||||
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<Guid>("PlayerId")
|
||||||
|
.HasColumnType("uuid")
|
||||||
|
.HasColumnName("player_id");
|
||||||
|
|
||||||
|
b.Property<TimeSpan>("TimeSpent")
|
||||||
|
.HasColumnType("interval")
|
||||||
|
.HasColumnName("time_spent");
|
||||||
|
|
||||||
|
b.Property<string>("Tracker")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text")
|
||||||
|
.HasColumnName("tracker");
|
||||||
|
|
||||||
|
b.HasKey("Id")
|
||||||
|
.HasName("PK_play_time");
|
||||||
|
|
||||||
|
b.HasIndex("PlayerId", "Tracker")
|
||||||
|
.IsUnique();
|
||||||
|
|
||||||
|
b.ToTable("play_time", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Content.Server.Database.Player", b =>
|
modelBuilder.Entity("Content.Server.Database.Player", b =>
|
||||||
{
|
{
|
||||||
b.Property<int>("Id")
|
b.Property<int>("Id")
|
||||||
@@ -501,40 +531,10 @@ namespace Content.Server.Database.Migrations.Postgres
|
|||||||
b.HasIndex("UserId")
|
b.HasIndex("UserId")
|
||||||
.IsUnique();
|
.IsUnique();
|
||||||
|
|
||||||
b.ToTable("player", (string)null);
|
b.ToTable("player", null, t =>
|
||||||
|
{
|
||||||
b.HasCheckConstraint("LastSeenAddressNotIPv6MappedIPv4", "NOT inet '::ffff:0.0.0.0/96' >>= last_seen_address");
|
t.HasCheckConstraint("LastSeenAddressNotIPv6MappedIPv4", "NOT inet '::ffff:0.0.0.0/96' >>= last_seen_address");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Content.Server.Database.PlayTime", b =>
|
|
||||||
{
|
|
||||||
b.Property<int>("Id")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("integer")
|
|
||||||
.HasColumnName("play_time_id");
|
|
||||||
|
|
||||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
|
||||||
|
|
||||||
b.Property<Guid>("PlayerId")
|
|
||||||
.HasColumnType("uuid")
|
|
||||||
.HasColumnName("player_id");
|
|
||||||
|
|
||||||
b.Property<TimeSpan>("TimeSpent")
|
|
||||||
.HasColumnType("interval")
|
|
||||||
.HasColumnName("time_spent");
|
|
||||||
|
|
||||||
b.Property<string>("Tracker")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text")
|
|
||||||
.HasColumnName("tracker");
|
|
||||||
|
|
||||||
b.HasKey("Id")
|
|
||||||
.HasName("PK_play_time");
|
|
||||||
|
|
||||||
b.HasIndex("PlayerId", "Tracker")
|
|
||||||
.IsUnique();
|
|
||||||
|
|
||||||
b.ToTable("play_time", (string)null);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Content.Server.Database.Preference", b =>
|
modelBuilder.Entity("Content.Server.Database.Preference", b =>
|
||||||
@@ -729,6 +729,10 @@ namespace Content.Server.Database.Migrations.Postgres
|
|||||||
.HasColumnType("inet")
|
.HasColumnType("inet")
|
||||||
.HasColumnName("address");
|
.HasColumnName("address");
|
||||||
|
|
||||||
|
b.Property<bool>("AutoDelete")
|
||||||
|
.HasColumnType("boolean")
|
||||||
|
.HasColumnName("auto_delete");
|
||||||
|
|
||||||
b.Property<DateTime>("BanTime")
|
b.Property<DateTime>("BanTime")
|
||||||
.HasColumnType("timestamp with time zone")
|
.HasColumnType("timestamp with time zone")
|
||||||
.HasColumnName("ban_time");
|
.HasColumnName("ban_time");
|
||||||
@@ -737,6 +741,10 @@ namespace Content.Server.Database.Migrations.Postgres
|
|||||||
.HasColumnType("uuid")
|
.HasColumnType("uuid")
|
||||||
.HasColumnName("banning_admin");
|
.HasColumnName("banning_admin");
|
||||||
|
|
||||||
|
b.Property<int>("ExemptFlags")
|
||||||
|
.HasColumnType("integer")
|
||||||
|
.HasColumnName("exempt_flags");
|
||||||
|
|
||||||
b.Property<DateTime?>("ExpirationTime")
|
b.Property<DateTime?>("ExpirationTime")
|
||||||
.HasColumnType("timestamp with time zone")
|
.HasColumnType("timestamp with time zone")
|
||||||
.HasColumnName("expiration_time");
|
.HasColumnName("expiration_time");
|
||||||
@@ -761,11 +769,32 @@ namespace Content.Server.Database.Migrations.Postgres
|
|||||||
|
|
||||||
b.HasIndex("UserId");
|
b.HasIndex("UserId");
|
||||||
|
|
||||||
b.ToTable("server_ban", (string)null);
|
b.ToTable("server_ban", null, t =>
|
||||||
|
{
|
||||||
|
t.HasCheckConstraint("AddressNotIPv6MappedIPv4", "NOT inet '::ffff:0.0.0.0/96' >>= address");
|
||||||
|
|
||||||
b.HasCheckConstraint("AddressNotIPv6MappedIPv4", "NOT inet '::ffff:0.0.0.0/96' >>= address");
|
t.HasCheckConstraint("HaveEitherAddressOrUserIdOrHWId", "address IS NOT NULL OR user_id IS NOT NULL OR hwid IS NOT NULL");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
b.HasCheckConstraint("HaveEitherAddressOrUserIdOrHWId", "address IS NOT NULL OR user_id IS NOT NULL OR hwid IS NOT NULL");
|
modelBuilder.Entity("Content.Server.Database.ServerBanExemption", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("UserId")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("uuid")
|
||||||
|
.HasColumnName("user_id");
|
||||||
|
|
||||||
|
b.Property<int>("Flags")
|
||||||
|
.HasColumnType("integer")
|
||||||
|
.HasColumnName("flags");
|
||||||
|
|
||||||
|
b.HasKey("UserId")
|
||||||
|
.HasName("PK_server_ban_exemption");
|
||||||
|
|
||||||
|
b.ToTable("server_ban_exemption", null, t =>
|
||||||
|
{
|
||||||
|
t.HasCheckConstraint("FlagsNotZero", "flags != 0");
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Content.Server.Database.ServerBanHit", b =>
|
modelBuilder.Entity("Content.Server.Database.ServerBanHit", b =>
|
||||||
@@ -847,11 +876,12 @@ namespace Content.Server.Database.Migrations.Postgres
|
|||||||
|
|
||||||
b.HasIndex("UserId");
|
b.HasIndex("UserId");
|
||||||
|
|
||||||
b.ToTable("server_role_ban", (string)null);
|
b.ToTable("server_role_ban", null, t =>
|
||||||
|
{
|
||||||
|
t.HasCheckConstraint("AddressNotIPv6MappedIPv4", "NOT inet '::ffff:0.0.0.0/96' >>= address");
|
||||||
|
|
||||||
b.HasCheckConstraint("AddressNotIPv6MappedIPv4", "NOT inet '::ffff:0.0.0.0/96' >>= address");
|
t.HasCheckConstraint("HaveEitherAddressOrUserIdOrHWId", "address IS NOT NULL OR user_id IS NOT NULL OR hwid IS NOT NULL");
|
||||||
|
});
|
||||||
b.HasCheckConstraint("HaveEitherAddressOrUserIdOrHWId", "address IS NOT NULL OR user_id IS NOT NULL OR hwid IS NOT NULL");
|
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Content.Server.Database.ServerRoleUnban", b =>
|
modelBuilder.Entity("Content.Server.Database.ServerRoleUnban", b =>
|
||||||
@@ -935,8 +965,8 @@ namespace Content.Server.Database.Migrations.Postgres
|
|||||||
b.HasKey("Id")
|
b.HasKey("Id")
|
||||||
.HasName("PK_trait");
|
.HasName("PK_trait");
|
||||||
|
|
||||||
b.HasIndex("ProfileId")
|
b.HasIndex("ProfileId", "TraitName")
|
||||||
.HasDatabaseName("IX_trait_profile_id");
|
.IsUnique();
|
||||||
|
|
||||||
b.ToTable("trait", (string)null);
|
b.ToTable("trait", (string)null);
|
||||||
});
|
});
|
||||||
|
|||||||
1267
Content.Server.Database/Migrations/Sqlite/20230319110651_ProfileTraitIndexUnique.Designer.cs
generated
Normal file
1267
Content.Server.Database/Migrations/Sqlite/20230319110651_ProfileTraitIndexUnique.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,34 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace Content.Server.Database.Migrations.Sqlite
|
||||||
|
{
|
||||||
|
public partial class ProfileTraitIndexUnique : Migration
|
||||||
|
{
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropIndex(
|
||||||
|
name: "IX_trait_profile_id",
|
||||||
|
table: "trait");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_trait_profile_id_trait_name",
|
||||||
|
table: "trait",
|
||||||
|
columns: new[] { "profile_id", "trait_name" },
|
||||||
|
unique: true);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropIndex(
|
||||||
|
name: "IX_trait_profile_id_trait_name",
|
||||||
|
table: "trait");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_trait_profile_id",
|
||||||
|
table: "trait",
|
||||||
|
column: "profile_id");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
1290
Content.Server.Database/Migrations/Sqlite/20230319112120_ServerBanExemption.Designer.cs
generated
Normal file
1290
Content.Server.Database/Migrations/Sqlite/20230319112120_ServerBanExemption.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,43 @@
|
|||||||
|
using System;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace Content.Server.Database.Migrations.Sqlite
|
||||||
|
{
|
||||||
|
public partial class ServerBanExemption : Migration
|
||||||
|
{
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.AddColumn<int>(
|
||||||
|
name: "exempt_flags",
|
||||||
|
table: "server_ban",
|
||||||
|
type: "INTEGER",
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: 0);
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "server_ban_exemption",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
user_id = table.Column<Guid>(type: "TEXT", nullable: false),
|
||||||
|
flags = table.Column<int>(type: "INTEGER", nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_server_ban_exemption", x => x.user_id);
|
||||||
|
table.CheckConstraint("FlagsNotZero", "flags != 0");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "server_ban_exemption");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "exempt_flags",
|
||||||
|
table: "server_ban");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
1296
Content.Server.Database/Migrations/Sqlite/20230402214642_BanAutoDelete.Designer.cs
generated
Normal file
1296
Content.Server.Database/Migrations/Sqlite/20230402214642_BanAutoDelete.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,29 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace Content.Server.Database.Migrations.Sqlite
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class BanAutoDelete : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.AddColumn<bool>(
|
||||||
|
name: "auto_delete",
|
||||||
|
table: "server_ban",
|
||||||
|
type: "INTEGER",
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "auto_delete",
|
||||||
|
table: "server_ban");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -15,7 +15,7 @@ namespace Content.Server.Database.Migrations.Sqlite
|
|||||||
protected override void BuildModel(ModelBuilder modelBuilder)
|
protected override void BuildModel(ModelBuilder modelBuilder)
|
||||||
{
|
{
|
||||||
#pragma warning disable 612, 618
|
#pragma warning disable 612, 618
|
||||||
modelBuilder.HasAnnotation("ProductVersion", "6.0.5");
|
modelBuilder.HasAnnotation("ProductVersion", "7.0.4");
|
||||||
|
|
||||||
modelBuilder.Entity("Content.Server.Database.Admin", b =>
|
modelBuilder.Entity("Content.Server.Database.Admin", b =>
|
||||||
{
|
{
|
||||||
@@ -278,8 +278,7 @@ namespace Content.Server.Database.Migrations.Sqlite
|
|||||||
b.HasKey("Id")
|
b.HasKey("Id")
|
||||||
.HasName("PK_admin_rank_flag");
|
.HasName("PK_admin_rank_flag");
|
||||||
|
|
||||||
b.HasIndex("AdminRankId")
|
b.HasIndex("AdminRankId");
|
||||||
.HasDatabaseName("IX_admin_rank_flag_admin_rank_id");
|
|
||||||
|
|
||||||
b.HasIndex("Flag", "AdminRankId")
|
b.HasIndex("Flag", "AdminRankId")
|
||||||
.IsUnique();
|
.IsUnique();
|
||||||
@@ -404,8 +403,7 @@ namespace Content.Server.Database.Migrations.Sqlite
|
|||||||
b.HasKey("Id")
|
b.HasKey("Id")
|
||||||
.HasName("PK_job");
|
.HasName("PK_job");
|
||||||
|
|
||||||
b.HasIndex("ProfileId")
|
b.HasIndex("ProfileId");
|
||||||
.HasDatabaseName("IX_job_profile_id");
|
|
||||||
|
|
||||||
b.HasIndex("ProfileId", "JobName")
|
b.HasIndex("ProfileId", "JobName")
|
||||||
.IsUnique();
|
.IsUnique();
|
||||||
@@ -417,6 +415,35 @@ namespace Content.Server.Database.Migrations.Sqlite
|
|||||||
b.ToTable("job", (string)null);
|
b.ToTable("job", (string)null);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Content.Server.Database.PlayTime", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER")
|
||||||
|
.HasColumnName("play_time_id");
|
||||||
|
|
||||||
|
b.Property<Guid>("PlayerId")
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("player_id");
|
||||||
|
|
||||||
|
b.Property<TimeSpan>("TimeSpent")
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("time_spent");
|
||||||
|
|
||||||
|
b.Property<string>("Tracker")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("tracker");
|
||||||
|
|
||||||
|
b.HasKey("Id")
|
||||||
|
.HasName("PK_play_time");
|
||||||
|
|
||||||
|
b.HasIndex("PlayerId", "Tracker")
|
||||||
|
.IsUnique();
|
||||||
|
|
||||||
|
b.ToTable("play_time", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Content.Server.Database.Player", b =>
|
modelBuilder.Entity("Content.Server.Database.Player", b =>
|
||||||
{
|
{
|
||||||
b.Property<int>("Id")
|
b.Property<int>("Id")
|
||||||
@@ -468,35 +495,6 @@ namespace Content.Server.Database.Migrations.Sqlite
|
|||||||
b.ToTable("player", (string)null);
|
b.ToTable("player", (string)null);
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Content.Server.Database.PlayTime", b =>
|
|
||||||
{
|
|
||||||
b.Property<int>("Id")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("INTEGER")
|
|
||||||
.HasColumnName("play_time_id");
|
|
||||||
|
|
||||||
b.Property<Guid>("PlayerId")
|
|
||||||
.HasColumnType("TEXT")
|
|
||||||
.HasColumnName("player_id");
|
|
||||||
|
|
||||||
b.Property<TimeSpan>("TimeSpent")
|
|
||||||
.HasColumnType("TEXT")
|
|
||||||
.HasColumnName("time_spent");
|
|
||||||
|
|
||||||
b.Property<string>("Tracker")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("TEXT")
|
|
||||||
.HasColumnName("tracker");
|
|
||||||
|
|
||||||
b.HasKey("Id")
|
|
||||||
.HasName("PK_play_time");
|
|
||||||
|
|
||||||
b.HasIndex("PlayerId", "Tracker")
|
|
||||||
.IsUnique();
|
|
||||||
|
|
||||||
b.ToTable("play_time", (string)null);
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Content.Server.Database.Preference", b =>
|
modelBuilder.Entity("Content.Server.Database.Preference", b =>
|
||||||
{
|
{
|
||||||
b.Property<int>("Id")
|
b.Property<int>("Id")
|
||||||
@@ -679,6 +677,10 @@ namespace Content.Server.Database.Migrations.Sqlite
|
|||||||
.HasColumnType("TEXT")
|
.HasColumnType("TEXT")
|
||||||
.HasColumnName("address");
|
.HasColumnName("address");
|
||||||
|
|
||||||
|
b.Property<bool>("AutoDelete")
|
||||||
|
.HasColumnType("INTEGER")
|
||||||
|
.HasColumnName("auto_delete");
|
||||||
|
|
||||||
b.Property<DateTime>("BanTime")
|
b.Property<DateTime>("BanTime")
|
||||||
.HasColumnType("TEXT")
|
.HasColumnType("TEXT")
|
||||||
.HasColumnName("ban_time");
|
.HasColumnName("ban_time");
|
||||||
@@ -687,6 +689,10 @@ namespace Content.Server.Database.Migrations.Sqlite
|
|||||||
.HasColumnType("TEXT")
|
.HasColumnType("TEXT")
|
||||||
.HasColumnName("banning_admin");
|
.HasColumnName("banning_admin");
|
||||||
|
|
||||||
|
b.Property<int>("ExemptFlags")
|
||||||
|
.HasColumnType("INTEGER")
|
||||||
|
.HasColumnName("exempt_flags");
|
||||||
|
|
||||||
b.Property<DateTime?>("ExpirationTime")
|
b.Property<DateTime?>("ExpirationTime")
|
||||||
.HasColumnType("TEXT")
|
.HasColumnType("TEXT")
|
||||||
.HasColumnName("expiration_time");
|
.HasColumnName("expiration_time");
|
||||||
@@ -711,9 +717,30 @@ namespace Content.Server.Database.Migrations.Sqlite
|
|||||||
|
|
||||||
b.HasIndex("UserId");
|
b.HasIndex("UserId");
|
||||||
|
|
||||||
b.ToTable("server_ban", (string)null);
|
b.ToTable("server_ban", null, t =>
|
||||||
|
{
|
||||||
|
t.HasCheckConstraint("HaveEitherAddressOrUserIdOrHWId", "address IS NOT NULL OR user_id IS NOT NULL OR hwid IS NOT NULL");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
b.HasCheckConstraint("HaveEitherAddressOrUserIdOrHWId", "address IS NOT NULL OR user_id IS NOT NULL OR hwid IS NOT NULL");
|
modelBuilder.Entity("Content.Server.Database.ServerBanExemption", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("UserId")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("user_id");
|
||||||
|
|
||||||
|
b.Property<int>("Flags")
|
||||||
|
.HasColumnType("INTEGER")
|
||||||
|
.HasColumnName("flags");
|
||||||
|
|
||||||
|
b.HasKey("UserId")
|
||||||
|
.HasName("PK_server_ban_exemption");
|
||||||
|
|
||||||
|
b.ToTable("server_ban_exemption", null, t =>
|
||||||
|
{
|
||||||
|
t.HasCheckConstraint("FlagsNotZero", "flags != 0");
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Content.Server.Database.ServerBanHit", b =>
|
modelBuilder.Entity("Content.Server.Database.ServerBanHit", b =>
|
||||||
@@ -791,9 +818,10 @@ namespace Content.Server.Database.Migrations.Sqlite
|
|||||||
|
|
||||||
b.HasIndex("UserId");
|
b.HasIndex("UserId");
|
||||||
|
|
||||||
b.ToTable("server_role_ban", (string)null);
|
b.ToTable("server_role_ban", null, t =>
|
||||||
|
{
|
||||||
b.HasCheckConstraint("HaveEitherAddressOrUserIdOrHWId", "address IS NOT NULL OR user_id IS NOT NULL OR hwid IS NOT NULL");
|
t.HasCheckConstraint("HaveEitherAddressOrUserIdOrHWId", "address IS NOT NULL OR user_id IS NOT NULL OR hwid IS NOT NULL");
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Content.Server.Database.ServerRoleUnban", b =>
|
modelBuilder.Entity("Content.Server.Database.ServerRoleUnban", b =>
|
||||||
@@ -871,8 +899,8 @@ namespace Content.Server.Database.Migrations.Sqlite
|
|||||||
b.HasKey("Id")
|
b.HasKey("Id")
|
||||||
.HasName("PK_trait");
|
.HasName("PK_trait");
|
||||||
|
|
||||||
b.HasIndex("ProfileId")
|
b.HasIndex("ProfileId", "TraitName")
|
||||||
.HasDatabaseName("IX_trait_profile_id");
|
.IsUnique();
|
||||||
|
|
||||||
b.ToTable("trait", (string)null);
|
b.ToTable("trait", (string)null);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ namespace Content.Server.Database
|
|||||||
public DbSet<Whitelist> Whitelist { get; set; } = null!;
|
public DbSet<Whitelist> Whitelist { get; set; } = null!;
|
||||||
public DbSet<ServerBan> Ban { get; set; } = default!;
|
public DbSet<ServerBan> Ban { get; set; } = default!;
|
||||||
public DbSet<ServerUnban> Unban { get; set; } = default!;
|
public DbSet<ServerUnban> Unban { get; set; } = default!;
|
||||||
|
public DbSet<ServerBanExemption> BanExemption { get; set; } = default!;
|
||||||
public DbSet<ConnectionLog> ConnectionLog { get; set; } = default!;
|
public DbSet<ConnectionLog> ConnectionLog { get; set; } = default!;
|
||||||
public DbSet<ServerBanHit> ServerBanHit { get; set; } = default!;
|
public DbSet<ServerBanHit> ServerBanHit { get; set; } = default!;
|
||||||
public DbSet<ServerRoleBan> RoleBan { get; set; } = default!;
|
public DbSet<ServerRoleBan> RoleBan { get; set; } = default!;
|
||||||
@@ -125,8 +126,13 @@ namespace Content.Server.Database
|
|||||||
.HasIndex(p => p.BanId)
|
.HasIndex(p => p.BanId)
|
||||||
.IsUnique();
|
.IsUnique();
|
||||||
|
|
||||||
modelBuilder.Entity<ServerBan>()
|
modelBuilder.Entity<ServerBan>().ToTable(t =>
|
||||||
.HasCheckConstraint("HaveEitherAddressOrUserIdOrHWId", "address IS NOT NULL OR user_id IS NOT NULL OR hwid IS NOT NULL");
|
t.HasCheckConstraint("HaveEitherAddressOrUserIdOrHWId", "address IS NOT NULL OR user_id IS NOT NULL OR hwid IS NOT NULL"));
|
||||||
|
|
||||||
|
// Ban exemption can't have flags 0 since that wouldn't exempt anything.
|
||||||
|
// The row should be removed if setting to 0.
|
||||||
|
modelBuilder.Entity<ServerBanExemption>().ToTable(t =>
|
||||||
|
t.HasCheckConstraint("FlagsNotZero", "flags != 0"));
|
||||||
|
|
||||||
modelBuilder.Entity<ServerRoleBan>()
|
modelBuilder.Entity<ServerRoleBan>()
|
||||||
.HasIndex(p => p.UserId);
|
.HasIndex(p => p.UserId);
|
||||||
@@ -141,8 +147,8 @@ namespace Content.Server.Database
|
|||||||
.HasIndex(p => p.BanId)
|
.HasIndex(p => p.BanId)
|
||||||
.IsUnique();
|
.IsUnique();
|
||||||
|
|
||||||
modelBuilder.Entity<ServerRoleBan>()
|
modelBuilder.Entity<ServerRoleBan>().ToTable(t =>
|
||||||
.HasCheckConstraint("HaveEitherAddressOrUserIdOrHWId", "address IS NOT NULL OR user_id IS NOT NULL OR hwid IS NOT NULL");
|
t.HasCheckConstraint("HaveEitherAddressOrUserIdOrHWId", "address IS NOT NULL OR user_id IS NOT NULL OR hwid IS NOT NULL"));
|
||||||
|
|
||||||
modelBuilder.Entity<Player>()
|
modelBuilder.Entity<Player>()
|
||||||
.HasIndex(p => p.UserId)
|
.HasIndex(p => p.UserId)
|
||||||
@@ -440,39 +446,148 @@ namespace Content.Server.Database
|
|||||||
DateTime UnbanTime { get; set; }
|
DateTime UnbanTime { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Flags for use with <see cref="ServerBanExemption"/>.
|
||||||
|
/// </summary>
|
||||||
|
[Flags]
|
||||||
|
public enum ServerBanExemptFlags
|
||||||
|
{
|
||||||
|
// @formatter:off
|
||||||
|
None = 0,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Ban is a datacenter range, connections usually imply usage of a VPN service.
|
||||||
|
/// </summary>
|
||||||
|
Datacenter = 1 << 0,
|
||||||
|
// @formatter:on
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A ban from playing on the server.
|
||||||
|
/// If an incoming connection matches any of UserID, IP, or HWID, they will be blocked from joining the server.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// At least one of UserID, IP, or HWID must be given (otherwise the ban would match nothing).
|
||||||
|
/// </remarks>
|
||||||
[Table("server_ban")]
|
[Table("server_ban")]
|
||||||
public class ServerBan : IBanCommon<ServerUnban>
|
public class ServerBan : IBanCommon<ServerUnban>
|
||||||
{
|
{
|
||||||
public int Id { get; set; }
|
public int Id { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The user ID of the banned player.
|
||||||
|
/// </summary>
|
||||||
public Guid? UserId { get; set; }
|
public Guid? UserId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// CIDR IP address range of the ban. The whole range can match the ban.
|
||||||
|
/// </summary>
|
||||||
[Column(TypeName = "inet")] public (IPAddress, int)? Address { get; set; }
|
[Column(TypeName = "inet")] public (IPAddress, int)? Address { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Hardware ID of the banned player.
|
||||||
|
/// </summary>
|
||||||
public byte[]? HWId { get; set; }
|
public byte[]? HWId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The time when the ban was applied by an administrator.
|
||||||
|
/// </summary>
|
||||||
public DateTime BanTime { get; set; }
|
public DateTime BanTime { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The time the ban will expire. If null, the ban is permanent and will not expire naturally.
|
||||||
|
/// </summary>
|
||||||
public DateTime? ExpirationTime { get; set; }
|
public DateTime? ExpirationTime { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The administrator-stated reason for applying the ban.
|
||||||
|
/// </summary>
|
||||||
public string Reason { get; set; } = null!;
|
public string Reason { get; set; } = null!;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// User ID of the admin that applied the ban.
|
||||||
|
/// </summary>
|
||||||
public Guid? BanningAdmin { get; set; }
|
public Guid? BanningAdmin { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Optional flags that allow adding exemptions to the ban via <see cref="ServerBanExemption"/>.
|
||||||
|
/// </summary>
|
||||||
|
public ServerBanExemptFlags ExemptFlags { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// If present, an administrator has manually repealed this ban.
|
||||||
|
/// </summary>
|
||||||
public ServerUnban? Unban { get; set; }
|
public ServerUnban? Unban { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Whether this ban should be automatically deleted from the database when it expires.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// This isn't done automatically by the game,
|
||||||
|
/// you will need to set up something like a cron job to clear this from your database,
|
||||||
|
/// using a command like this:
|
||||||
|
/// psql -d ss14 -c "DELETE FROM server_ban WHERE auto_delete AND expiration_time < NOW()"
|
||||||
|
/// </remarks>
|
||||||
|
public bool AutoDelete { get; set; }
|
||||||
|
|
||||||
public List<ServerBanHit> BanHits { get; set; } = null!;
|
public List<ServerBanHit> BanHits { get; set; } = null!;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// An explicit repeal of a <see cref="ServerBan"/> by an administrator.
|
||||||
|
/// Having an entry for a ban neutralizes it.
|
||||||
|
/// </summary>
|
||||||
[Table("server_unban")]
|
[Table("server_unban")]
|
||||||
public class ServerUnban : IUnbanCommon
|
public class ServerUnban : IUnbanCommon
|
||||||
{
|
{
|
||||||
[Column("unban_id")] public int Id { get; set; }
|
[Column("unban_id")] public int Id { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The ID of ban that is being repealed.
|
||||||
|
/// </summary>
|
||||||
public int BanId { get; set; }
|
public int BanId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The ban that is being repealed.
|
||||||
|
/// </summary>
|
||||||
public ServerBan Ban { get; set; } = null!;
|
public ServerBan Ban { get; set; } = null!;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The admin that repealed the ban.
|
||||||
|
/// </summary>
|
||||||
public Guid? UnbanningAdmin { get; set; }
|
public Guid? UnbanningAdmin { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The time the ban repealed.
|
||||||
|
/// </summary>
|
||||||
public DateTime UnbanTime { get; set; }
|
public DateTime UnbanTime { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// An exemption for a specific user to a certain type of <see cref="ServerBan"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <example>
|
||||||
|
/// Certain players may need to be exempted from VPN bans due to issues with their ISP.
|
||||||
|
/// We would tag all VPN bans with <see cref="ServerBanExemptFlags.Datacenter"/>,
|
||||||
|
/// and then add an exemption for these players to this table with the same flag.
|
||||||
|
/// They will only be exempted from VPN bans, other bans (if they manage to get any) will still apply.
|
||||||
|
/// </example>
|
||||||
|
[Table("server_ban_exemption")]
|
||||||
|
public sealed class ServerBanExemption
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The UserID of the exempted player.
|
||||||
|
/// </summary>
|
||||||
|
[Key]
|
||||||
|
public Guid UserId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The ban flags to exempt this player from.
|
||||||
|
/// If any bit overlaps <see cref="ServerBan.ExemptFlags"/>, the ban is ignored.
|
||||||
|
/// </summary>
|
||||||
|
public ServerBanExemptFlags Flags { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
[Table("connection_log")]
|
[Table("connection_log")]
|
||||||
public class ConnectionLog
|
public class ConnectionLog
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -46,19 +46,20 @@ namespace Content.Server.Database
|
|||||||
// ReSharper disable StringLiteralTypo
|
// ReSharper disable StringLiteralTypo
|
||||||
// Enforce that an address cannot be IPv6-mapped IPv4.
|
// Enforce that an address cannot be IPv6-mapped IPv4.
|
||||||
// So that IPv4 addresses are consistent between separate-socket and dual-stack socket modes.
|
// So that IPv4 addresses are consistent between separate-socket and dual-stack socket modes.
|
||||||
modelBuilder.Entity<ServerBan>()
|
modelBuilder.Entity<ServerBan>().ToTable(t =>
|
||||||
.HasCheckConstraint("AddressNotIPv6MappedIPv4", "NOT inet '::ffff:0.0.0.0/96' >>= address");
|
t.HasCheckConstraint("AddressNotIPv6MappedIPv4", "NOT inet '::ffff:0.0.0.0/96' >>= address"));
|
||||||
|
|
||||||
modelBuilder.Entity<ServerRoleBan>()
|
modelBuilder.Entity<ServerRoleBan>().ToTable( t =>
|
||||||
.HasCheckConstraint("AddressNotIPv6MappedIPv4", "NOT inet '::ffff:0.0.0.0/96' >>= address");
|
t.HasCheckConstraint("AddressNotIPv6MappedIPv4", "NOT inet '::ffff:0.0.0.0/96' >>= address"));
|
||||||
|
|
||||||
modelBuilder.Entity<Player>()
|
modelBuilder.Entity<Player>().ToTable(t =>
|
||||||
.HasCheckConstraint("LastSeenAddressNotIPv6MappedIPv4",
|
t.HasCheckConstraint("LastSeenAddressNotIPv6MappedIPv4",
|
||||||
"NOT inet '::ffff:0.0.0.0/96' >>= last_seen_address");
|
"NOT inet '::ffff:0.0.0.0/96' >>= last_seen_address"));
|
||||||
|
|
||||||
|
modelBuilder.Entity<ConnectionLog>().ToTable(t =>
|
||||||
|
t.HasCheckConstraint("AddressNotIPv6MappedIPv4",
|
||||||
|
"NOT inet '::ffff:0.0.0.0/96' >>= address"));
|
||||||
|
|
||||||
modelBuilder.Entity<ConnectionLog>()
|
|
||||||
.HasCheckConstraint("AddressNotIPv6MappedIPv4",
|
|
||||||
"NOT inet '::ffff:0.0.0.0/96' >>= address");
|
|
||||||
// ReSharper restore StringLiteralTypo
|
// ReSharper restore StringLiteralTypo
|
||||||
|
|
||||||
modelBuilder.Entity<AdminLog>()
|
modelBuilder.Entity<AdminLog>()
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ namespace Content.Server.Database
|
|||||||
TypeMappingSourceDependencies dependencies,
|
TypeMappingSourceDependencies dependencies,
|
||||||
RelationalTypeMappingSourceDependencies relationalDependencies,
|
RelationalTypeMappingSourceDependencies relationalDependencies,
|
||||||
ISqlGenerationHelper sqlGenerationHelper,
|
ISqlGenerationHelper sqlGenerationHelper,
|
||||||
INpgsqlOptions? npgsqlOptions = null)
|
INpgsqlSingletonOptions npgsqlOptions)
|
||||||
: base(dependencies, relationalDependencies, sqlGenerationHelper, npgsqlOptions)
|
: base(dependencies, relationalDependencies, sqlGenerationHelper, npgsqlOptions)
|
||||||
{
|
{
|
||||||
StoreTypeMappings["inet"] =
|
StoreTypeMappings["inet"] =
|
||||||
|
|||||||
@@ -200,12 +200,12 @@ namespace Content.Server.Database
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (entityType.FindPrimaryKey() is IConventionKey primaryKey)
|
if (entityType.FindPrimaryKey() is { } primaryKey)
|
||||||
{
|
{
|
||||||
if (entityType.FindRowInternalForeignKeys(tableIdentifier).FirstOrDefault() is null
|
if (entityType.FindRowInternalForeignKeys(tableIdentifier).FirstOrDefault() is null
|
||||||
&& (entityType.BaseType is null || entityType.GetTableName() == entityType.BaseType.GetTableName()))
|
&& (entityType.BaseType is null || entityType.GetTableName() == entityType.BaseType.GetTableName()))
|
||||||
{
|
{
|
||||||
primaryKey.Builder.HasName(RewriteName(primaryKey.GetDefaultName()));
|
primaryKey.Builder.HasName(RewriteName(primaryKey.GetDefaultName()!));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -215,16 +215,16 @@ namespace Content.Server.Database
|
|||||||
|
|
||||||
foreach (var foreignKey in entityType.GetForeignKeys())
|
foreach (var foreignKey in entityType.GetForeignKeys())
|
||||||
{
|
{
|
||||||
foreignKey.Builder.HasConstraintName(RewriteName(foreignKey.GetDefaultName()));
|
foreignKey.Builder.HasConstraintName(RewriteName(foreignKey.GetDefaultName()!));
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var index in entityType.GetIndexes())
|
foreach (var index in entityType.GetIndexes())
|
||||||
{
|
{
|
||||||
index.Builder.HasDatabaseName(RewriteName(index.GetDefaultDatabaseName()));
|
index.Builder.HasDatabaseName(RewriteName(index.GetDefaultDatabaseName()!));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (annotation?.Value is not null
|
if (annotation?.Value is not null
|
||||||
&& entityType.FindOwnership() is IConventionForeignKey ownership
|
&& entityType.FindOwnership() is { } ownership
|
||||||
&& (string)annotation.Value != ownership.PrincipalEntityType.GetTableName())
|
&& (string)annotation.Value != ownership.PrincipalEntityType.GetTableName())
|
||||||
{
|
{
|
||||||
foreach (var property in entityType.GetProperties()
|
foreach (var property in entityType.GetProperties()
|
||||||
@@ -234,9 +234,9 @@ namespace Content.Server.Database
|
|||||||
RewriteColumnName(property.Builder);
|
RewriteColumnName(property.Builder);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (entityType.FindPrimaryKey() is IConventionKey key)
|
if (entityType.FindPrimaryKey() is { } key)
|
||||||
{
|
{
|
||||||
key.Builder.HasName(RewriteName(key.GetDefaultName()));
|
key.Builder.HasName(RewriteName(key.GetDefaultName()!));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -245,7 +245,7 @@ namespace Content.Server.Database
|
|||||||
IConventionForeignKeyBuilder relationshipBuilder,
|
IConventionForeignKeyBuilder relationshipBuilder,
|
||||||
IConventionContext<IConventionForeignKeyBuilder> context)
|
IConventionContext<IConventionForeignKeyBuilder> context)
|
||||||
{
|
{
|
||||||
relationshipBuilder.HasConstraintName(RewriteName(relationshipBuilder.Metadata.GetDefaultName()));
|
relationshipBuilder.HasConstraintName(RewriteName(relationshipBuilder.Metadata.GetDefaultName()!));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ProcessKeyAdded(IConventionKeyBuilder keyBuilder, IConventionContext<IConventionKeyBuilder> context)
|
public void ProcessKeyAdded(IConventionKeyBuilder keyBuilder, IConventionContext<IConventionKeyBuilder> context)
|
||||||
@@ -257,7 +257,7 @@ namespace Content.Server.Database
|
|||||||
|
|
||||||
if (entityType.FindOwnership() is null)
|
if (entityType.FindOwnership() is null)
|
||||||
{
|
{
|
||||||
keyBuilder.HasName(RewriteName(keyBuilder.Metadata.GetDefaultName()));
|
keyBuilder.HasName(RewriteName(keyBuilder.Metadata.GetDefaultName()!));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -270,7 +270,7 @@ namespace Content.Server.Database
|
|||||||
|
|
||||||
foreach (var property in entityType.GetProperties())
|
foreach (var property in entityType.GetProperties())
|
||||||
{
|
{
|
||||||
var columnName = property.GetColumnBaseName();
|
var columnName = property.GetColumnName();
|
||||||
if (columnName.StartsWith(entityType.ShortName() + '_', StringComparison.Ordinal))
|
if (columnName.StartsWith(entityType.ShortName() + '_', StringComparison.Ordinal))
|
||||||
{
|
{
|
||||||
property.Builder.HasColumnName(
|
property.Builder.HasColumnName(
|
||||||
@@ -310,11 +310,11 @@ namespace Content.Server.Database
|
|||||||
|
|
||||||
var baseColumnName = StoreObjectIdentifier.Create(property.DeclaringEntityType, StoreObjectType.Table) is { } tableIdentifier
|
var baseColumnName = StoreObjectIdentifier.Create(property.DeclaringEntityType, StoreObjectType.Table) is { } tableIdentifier
|
||||||
? property.GetDefaultColumnName(tableIdentifier)
|
? property.GetDefaultColumnName(tableIdentifier)
|
||||||
: property.GetDefaultColumnBaseName();
|
: property.GetDefaultColumnName();
|
||||||
|
|
||||||
if (baseColumnName == "Id")
|
if (baseColumnName == "Id")
|
||||||
baseColumnName = entityType.GetTableName() + baseColumnName;
|
baseColumnName = entityType.GetTableName() + baseColumnName;
|
||||||
propertyBuilder.HasColumnName(RewriteName(baseColumnName));
|
propertyBuilder.HasColumnName(RewriteName(baseColumnName!));
|
||||||
|
|
||||||
foreach (var storeObjectType in _storeObjectTypes)
|
foreach (var storeObjectType in _storeObjectTypes)
|
||||||
{
|
{
|
||||||
|
|||||||
122
Content.Server/Administration/Commands/BanExemptionCommands.cs
Normal file
122
Content.Server/Administration/Commands/BanExemptionCommands.cs
Normal file
@@ -0,0 +1,122 @@
|
|||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using Content.Server.Database;
|
||||||
|
using Content.Shared.Administration;
|
||||||
|
using Robust.Shared.Console;
|
||||||
|
|
||||||
|
namespace Content.Server.Administration.Commands;
|
||||||
|
|
||||||
|
[AdminCommand(AdminFlags.Admin)]
|
||||||
|
public sealed class BanExemptionUpdateCommand : LocalizedCommands
|
||||||
|
{
|
||||||
|
[Dependency] private readonly IServerDbManager _dbManager = default!;
|
||||||
|
[Dependency] private readonly IPlayerLocator _playerLocator = default!;
|
||||||
|
|
||||||
|
public override string Command => "ban_exemption_update";
|
||||||
|
|
||||||
|
public override async void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||||
|
{
|
||||||
|
if (args.Length < 2)
|
||||||
|
{
|
||||||
|
shell.WriteError(LocalizationManager.GetString("cmd-ban_exemption_update-nargs"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var flags = ServerBanExemptFlags.None;
|
||||||
|
for (var i = 1; i < args.Length; i++)
|
||||||
|
{
|
||||||
|
var arg = args[i];
|
||||||
|
if (!Enum.TryParse<ServerBanExemptFlags>(arg, ignoreCase: true, out var flag))
|
||||||
|
{
|
||||||
|
shell.WriteError(LocalizationManager.GetString("cmd-ban_exemption_update-invalid-flag", ("flag", arg)));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
flags |= flag;
|
||||||
|
}
|
||||||
|
|
||||||
|
var player = args[0];
|
||||||
|
var playerData = await _playerLocator.LookupIdByNameOrIdAsync(player);
|
||||||
|
if (playerData == null)
|
||||||
|
{
|
||||||
|
shell.WriteError(LocalizationManager.GetString("cmd-ban_exemption_update-locate", ("player", player)));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await _dbManager.UpdateBanExemption(playerData.UserId, flags);
|
||||||
|
shell.WriteLine(LocalizationManager.GetString(
|
||||||
|
"cmd-ban_exemption_update-success",
|
||||||
|
("player", player),
|
||||||
|
("uid", playerData.UserId)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public override CompletionResult GetCompletion(IConsoleShell shell, string[] args)
|
||||||
|
{
|
||||||
|
if (args.Length == 1)
|
||||||
|
return CompletionResult.FromHint(LocalizationManager.GetString("cmd-ban_exemption_get-arg-player"));
|
||||||
|
|
||||||
|
return CompletionResult.FromHintOptions(
|
||||||
|
Enum.GetNames<ServerBanExemptFlags>(),
|
||||||
|
LocalizationManager.GetString("cmd-ban_exemption_update-arg-flag"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[AdminCommand(AdminFlags.Admin)]
|
||||||
|
public sealed class BanExemptionGetCommand : LocalizedCommands
|
||||||
|
{
|
||||||
|
[Dependency] private readonly IServerDbManager _dbManager = default!;
|
||||||
|
[Dependency] private readonly IPlayerLocator _playerLocator = default!;
|
||||||
|
|
||||||
|
public override string Command => "ban_exemption_get";
|
||||||
|
|
||||||
|
public override async void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||||
|
{
|
||||||
|
if (args.Length != 1)
|
||||||
|
{
|
||||||
|
shell.WriteError(LocalizationManager.GetString("cmd-ban_exemption_get-nargs"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var player = args[0];
|
||||||
|
var playerData = await _playerLocator.LookupIdByNameOrIdAsync(player);
|
||||||
|
if (playerData == null)
|
||||||
|
{
|
||||||
|
shell.WriteError(LocalizationManager.GetString("cmd-ban_exemption_update-locate", ("player", player)));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var flags = await _dbManager.GetBanExemption(playerData.UserId);
|
||||||
|
if (flags == ServerBanExemptFlags.None)
|
||||||
|
{
|
||||||
|
shell.WriteLine(LocalizationManager.GetString("cmd-ban_exemption_get-none"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var joined = new StringBuilder();
|
||||||
|
var first = true;
|
||||||
|
for (var i = 0; i < sizeof(ServerBanExemptFlags) * 8; i++)
|
||||||
|
{
|
||||||
|
var mask = (ServerBanExemptFlags) (1 << i);
|
||||||
|
if ((mask & flags) == 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (!first)
|
||||||
|
joined.Append(", ");
|
||||||
|
first = false;
|
||||||
|
|
||||||
|
joined.Append(mask.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
shell.WriteLine(LocalizationManager.GetString(
|
||||||
|
"cmd-ban_exemption_get-show",
|
||||||
|
("flags", joined.ToString())));
|
||||||
|
}
|
||||||
|
|
||||||
|
public override CompletionResult GetCompletion(IConsoleShell shell, string[] args)
|
||||||
|
{
|
||||||
|
if (args.Length == 1)
|
||||||
|
return CompletionResult.FromHint(LocalizationManager.GetString("cmd-ban_exemption_get-arg-player"));
|
||||||
|
|
||||||
|
return CompletionResult.Empty;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -332,6 +332,52 @@ namespace Content.Server.Database
|
|||||||
|
|
||||||
public abstract Task AddServerBanAsync(ServerBanDef serverBan);
|
public abstract Task AddServerBanAsync(ServerBanDef serverBan);
|
||||||
public abstract Task AddServerUnbanAsync(ServerUnbanDef serverUnban);
|
public abstract Task AddServerUnbanAsync(ServerUnbanDef serverUnban);
|
||||||
|
|
||||||
|
protected static async Task<ServerBanExemptFlags?> GetBanExemptionCore(DbGuard db, NetUserId? userId)
|
||||||
|
{
|
||||||
|
if (userId == null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
var exemption = await db.DbContext.BanExemption
|
||||||
|
.SingleOrDefaultAsync(e => e.UserId == userId.Value.UserId);
|
||||||
|
|
||||||
|
return exemption?.Flags;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task UpdateBanExemption(NetUserId userId, ServerBanExemptFlags flags)
|
||||||
|
{
|
||||||
|
await using var db = await GetDb();
|
||||||
|
|
||||||
|
if (flags == 0)
|
||||||
|
{
|
||||||
|
// Delete whatever is there.
|
||||||
|
await db.DbContext.BanExemption.Where(u => u.UserId == userId.UserId).ExecuteDeleteAsync();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var exemption = await db.DbContext.BanExemption.SingleOrDefaultAsync(u => u.UserId == userId.UserId);
|
||||||
|
if (exemption == null)
|
||||||
|
{
|
||||||
|
exemption = new ServerBanExemption
|
||||||
|
{
|
||||||
|
UserId = userId
|
||||||
|
};
|
||||||
|
|
||||||
|
db.DbContext.BanExemption.Add(exemption);
|
||||||
|
}
|
||||||
|
|
||||||
|
exemption.Flags = flags;
|
||||||
|
await db.DbContext.SaveChangesAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<ServerBanExemptFlags> GetBanExemption(NetUserId userId)
|
||||||
|
{
|
||||||
|
await using var db = await GetDb();
|
||||||
|
|
||||||
|
var flags = await GetBanExemptionCore(db, userId);
|
||||||
|
return flags ?? ServerBanExemptFlags.None;
|
||||||
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Role Bans
|
#region Role Bans
|
||||||
@@ -985,6 +1031,5 @@ namespace Content.Server.Database
|
|||||||
|
|
||||||
public abstract ValueTask DisposeAsync();
|
public abstract ValueTask DisposeAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -84,6 +84,23 @@ namespace Content.Server.Database
|
|||||||
|
|
||||||
Task AddServerBanAsync(ServerBanDef serverBan);
|
Task AddServerBanAsync(ServerBanDef serverBan);
|
||||||
Task AddServerUnbanAsync(ServerUnbanDef serverBan);
|
Task AddServerUnbanAsync(ServerUnbanDef serverBan);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Update ban exemption information for a player.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Database rows are automatically created and removed when appropriate.
|
||||||
|
/// </remarks>
|
||||||
|
/// <param name="userId">The user to update</param>
|
||||||
|
/// <param name="flags">The new ban exemption flags.</param>
|
||||||
|
Task UpdateBanExemption(NetUserId userId, ServerBanExemptFlags flags);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get current ban exemption flags for a user
|
||||||
|
/// </summary>
|
||||||
|
/// <returns><see cref="ServerBanExemptFlags.None"/> if the user is not exempt from any bans.</returns>
|
||||||
|
Task<ServerBanExemptFlags> GetBanExemption(NetUserId userId);
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Role Bans
|
#region Role Bans
|
||||||
@@ -353,6 +370,18 @@ namespace Content.Server.Database
|
|||||||
return _db.AddServerUnbanAsync(serverUnban);
|
return _db.AddServerUnbanAsync(serverUnban);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Task UpdateBanExemption(NetUserId userId, ServerBanExemptFlags flags)
|
||||||
|
{
|
||||||
|
DbWriteOpsMetric.Inc();
|
||||||
|
return _db.UpdateBanExemption(userId, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task<ServerBanExemptFlags> GetBanExemption(NetUserId userId)
|
||||||
|
{
|
||||||
|
DbReadOpsMetric.Inc();
|
||||||
|
return _db.GetBanExemption(userId);
|
||||||
|
}
|
||||||
|
|
||||||
#region Role Ban
|
#region Role Ban
|
||||||
public Task<ServerRoleBanDef?> GetServerRoleBanAsync(int id)
|
public Task<ServerRoleBanDef?> GetServerRoleBanAsync(int id)
|
||||||
{
|
{
|
||||||
@@ -742,10 +771,10 @@ namespace Content.Server.Database
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public IDisposable BeginScope<TState>(TState state)
|
public IDisposable? BeginScope<TState>(TState state) where TState : notnull
|
||||||
{
|
{
|
||||||
// TODO: this
|
// TODO: this
|
||||||
return null!;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ using System.Threading;
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Robust.Shared.Network;
|
using Robust.Shared.Network;
|
||||||
|
using Robust.Shared.Utility;
|
||||||
|
|
||||||
namespace Content.Server.Database
|
namespace Content.Server.Database
|
||||||
{
|
{
|
||||||
@@ -58,7 +59,8 @@ namespace Content.Server.Database
|
|||||||
|
|
||||||
await using var db = await GetDbImpl();
|
await using var db = await GetDbImpl();
|
||||||
|
|
||||||
var query = MakeBanLookupQuery(address, userId, hwId, db, includeUnbanned: false)
|
var exempt = await GetBanExemptionCore(db, userId);
|
||||||
|
var query = MakeBanLookupQuery(address, userId, hwId, db, includeUnbanned: false, exempt)
|
||||||
.OrderByDescending(b => b.BanTime);
|
.OrderByDescending(b => b.BanTime);
|
||||||
|
|
||||||
var ban = await query.FirstOrDefaultAsync();
|
var ban = await query.FirstOrDefaultAsync();
|
||||||
@@ -77,7 +79,8 @@ namespace Content.Server.Database
|
|||||||
|
|
||||||
await using var db = await GetDbImpl();
|
await using var db = await GetDbImpl();
|
||||||
|
|
||||||
var query = MakeBanLookupQuery(address, userId, hwId, db, includeUnbanned);
|
var exempt = await GetBanExemptionCore(db, userId);
|
||||||
|
var query = MakeBanLookupQuery(address, userId, hwId, db, includeUnbanned, exempt);
|
||||||
|
|
||||||
var queryBans = await query.ToArrayAsync();
|
var queryBans = await query.ToArrayAsync();
|
||||||
var bans = new List<ServerBanDef>(queryBans.Length);
|
var bans = new List<ServerBanDef>(queryBans.Length);
|
||||||
@@ -100,8 +103,11 @@ namespace Content.Server.Database
|
|||||||
NetUserId? userId,
|
NetUserId? userId,
|
||||||
ImmutableArray<byte>? hwId,
|
ImmutableArray<byte>? hwId,
|
||||||
DbGuardImpl db,
|
DbGuardImpl db,
|
||||||
bool includeUnbanned)
|
bool includeUnbanned,
|
||||||
|
ServerBanExemptFlags? exemptFlags)
|
||||||
{
|
{
|
||||||
|
DebugTools.Assert(!(address == null && userId == null && hwId == null));
|
||||||
|
|
||||||
IQueryable<ServerBan>? query = null;
|
IQueryable<ServerBan>? query = null;
|
||||||
|
|
||||||
if (userId is { } uid)
|
if (userId is { } uid)
|
||||||
@@ -131,14 +137,22 @@ namespace Content.Server.Database
|
|||||||
query = query == null ? newQ : query.Union(newQ);
|
query = query == null ? newQ : query.Union(newQ);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DebugTools.Assert(
|
||||||
|
query != null,
|
||||||
|
"At least one filter item (IP/UserID/HWID) must have been given to make query not null.");
|
||||||
|
|
||||||
if (!includeUnbanned)
|
if (!includeUnbanned)
|
||||||
{
|
{
|
||||||
query = query?.Where(p =>
|
query = query.Where(p =>
|
||||||
p.Unban == null && (p.ExpirationTime == null || p.ExpirationTime.Value > DateTime.Now));
|
p.Unban == null && (p.ExpirationTime == null || p.ExpirationTime.Value > DateTime.Now));
|
||||||
}
|
}
|
||||||
|
|
||||||
query = query!.Distinct();
|
if (exemptFlags is { } exempt)
|
||||||
return query;
|
{
|
||||||
|
query = query.Where(b => (b.ExemptFlags & exempt) == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
return query.Distinct();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ServerBanDef? ConvertBan(ServerBan? ban)
|
private static ServerBanDef? ConvertBan(ServerBan? ban)
|
||||||
|
|||||||
@@ -68,9 +68,11 @@ namespace Content.Server.Database
|
|||||||
{
|
{
|
||||||
await using var db = await GetDbImpl();
|
await using var db = await GetDbImpl();
|
||||||
|
|
||||||
|
var exempt = await GetBanExemptionCore(db, userId);
|
||||||
|
|
||||||
// SQLite can't do the net masking stuff we need to match IP address ranges.
|
// SQLite can't do the net masking stuff we need to match IP address ranges.
|
||||||
// So just pull down the whole list into memory.
|
// So just pull down the whole list into memory.
|
||||||
var bans = await GetAllBans(db.SqliteDbContext, includeUnbanned: false);
|
var bans = await GetAllBans(db.SqliteDbContext, includeUnbanned: false, exempt);
|
||||||
|
|
||||||
return bans.FirstOrDefault(b => BanMatches(b, address, userId, hwId)) is { } foundBan
|
return bans.FirstOrDefault(b => BanMatches(b, address, userId, hwId)) is { } foundBan
|
||||||
? ConvertBan(foundBan)
|
? ConvertBan(foundBan)
|
||||||
@@ -83,9 +85,11 @@ namespace Content.Server.Database
|
|||||||
{
|
{
|
||||||
await using var db = await GetDbImpl();
|
await using var db = await GetDbImpl();
|
||||||
|
|
||||||
|
var exempt = await GetBanExemptionCore(db, userId);
|
||||||
|
|
||||||
// SQLite can't do the net masking stuff we need to match IP address ranges.
|
// SQLite can't do the net masking stuff we need to match IP address ranges.
|
||||||
// So just pull down the whole list into memory.
|
// So just pull down the whole list into memory.
|
||||||
var queryBans = await GetAllBans(db.SqliteDbContext, includeUnbanned);
|
var queryBans = await GetAllBans(db.SqliteDbContext, includeUnbanned, exempt);
|
||||||
|
|
||||||
return queryBans
|
return queryBans
|
||||||
.Where(b => BanMatches(b, address, userId, hwId))
|
.Where(b => BanMatches(b, address, userId, hwId))
|
||||||
@@ -95,7 +99,8 @@ namespace Content.Server.Database
|
|||||||
|
|
||||||
private static async Task<List<ServerBan>> GetAllBans(
|
private static async Task<List<ServerBan>> GetAllBans(
|
||||||
SqliteServerDbContext db,
|
SqliteServerDbContext db,
|
||||||
bool includeUnbanned)
|
bool includeUnbanned,
|
||||||
|
ServerBanExemptFlags? exemptFlags)
|
||||||
{
|
{
|
||||||
IQueryable<ServerBan> query = db.Ban.Include(p => p.Unban);
|
IQueryable<ServerBan> query = db.Ban.Include(p => p.Unban);
|
||||||
if (!includeUnbanned)
|
if (!includeUnbanned)
|
||||||
@@ -104,6 +109,11 @@ namespace Content.Server.Database
|
|||||||
p.Unban == null && (p.ExpirationTime == null || p.ExpirationTime.Value > DateTime.UtcNow));
|
p.Unban == null && (p.ExpirationTime == null || p.ExpirationTime.Value > DateTime.UtcNow));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (exemptFlags is { } exempt)
|
||||||
|
{
|
||||||
|
query = query.Where(b => (b.ExemptFlags & exempt) == 0);
|
||||||
|
}
|
||||||
|
|
||||||
return await query.ToListAsync();
|
return await query.ToListAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -19,3 +19,23 @@ cmd-banlist-desc = Lists a user's active bans.
|
|||||||
cmd-banlist-help = Usage: banlist <name or user ID>
|
cmd-banlist-help = Usage: banlist <name or user ID>
|
||||||
cmd-banlist-empty = No active bans found for {$user}
|
cmd-banlist-empty = No active bans found for {$user}
|
||||||
cmd-banlistF-hint = <name/user ID>
|
cmd-banlistF-hint = <name/user ID>
|
||||||
|
|
||||||
|
cmd-ban_exemption_update-desc = Set an exemption to a type of ban on a player.
|
||||||
|
cmd-ban_exemption_update-help = Usage: ban_exemption_update <player> <flag> [<flag> [...]]
|
||||||
|
Specify multiple flags to give a player multiple ban exemption flags.
|
||||||
|
To remove all exemptions, run this command and give "None" as only flag.
|
||||||
|
|
||||||
|
cmd-ban_exemption_update-nargs = Expected at least 2 arguments
|
||||||
|
cmd-ban_exemption_update-locate = Unable to locate player '{$player}'.
|
||||||
|
cmd-ban_exemption_update-invalid-flag = Invalid flag '{$flag}'.
|
||||||
|
cmd-ban_exemption_update-success = Updated ban exemption flags for '{$player}' ({$uid}).
|
||||||
|
cmd-ban_exemption_update-arg-player = <player>
|
||||||
|
cmd-ban_exemption_update-arg-flag = <flag>
|
||||||
|
|
||||||
|
cmd-ban_exemption_get-desc = Show ban exemptions for a certain player.
|
||||||
|
cmd-ban_exemption_get-help = Usage: ban_exemption_get <player>
|
||||||
|
|
||||||
|
cmd-ban_exemption_get-nargs = Expected exactly 1 argument
|
||||||
|
cmd-ban_exemption_get-none = User is not exempt from any bans.
|
||||||
|
cmd-ban_exemption_get-show = User is exempt from the following ban flags: {$flags}.
|
||||||
|
cmd-ban_exemption_get-arg-player = <player>
|
||||||
|
|||||||
Reference in New Issue
Block a user