Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 15 additions & 4 deletions Refresh.Database/GameDatabaseContext.Assets.cs
Original file line number Diff line number Diff line change
Expand Up @@ -111,20 +111,30 @@ public void SetMainlinePhotoHash(GameAsset asset, string hash) =>
asset.AsMainlinePhotoHash = hash;
});

public bool IsAssetDisallowed(string hash)
{
string hashLower = hash.ToLower();
return this.DisallowedAssets.Any(u => u.AssetHash == hashLower);
}

public DisallowedAsset? GetDisallowedAssetInfo(string hash)
=> this.DisallowedAssets.FirstOrDefault(d => d.AssetHash == hash);
{
string hashLower = hash.ToLower();
return this.DisallowedAssets.FirstOrDefault(d => d.AssetHash == hashLower);
}

/// <returns>
/// The asset's disallowance info + whether the asset wasn't already disallowed before
/// </returns
public (DisallowedAsset, bool) DisallowAsset(string hash, GameAssetType type, string reason)
{
DisallowedAsset? existing = this.GetDisallowedAssetInfo(hash);
string hashLower = hash.ToLower();
DisallowedAsset? existing = this.GetDisallowedAssetInfo(hashLower);
if (existing != null) return (existing, false);

DisallowedAsset disallowed = new()
{
AssetHash = hash,
AssetHash = hashLower,
AssetType = type,
Reason = reason,
DisallowedAt = this._time.Now,
Expand All @@ -137,7 +147,8 @@ public void SetMainlinePhotoHash(GameAsset asset, string hash) =>

public bool ReallowAsset(string hash)
{
DisallowedAsset? existing = this.GetDisallowedAssetInfo(hash);
string hashLower = hash.ToLower();
DisallowedAsset? existing = this.GetDisallowedAssetInfo(hashLower);
if (existing == null) return false;

this.DisallowedAssets.Remove(existing);
Expand Down
58 changes: 37 additions & 21 deletions Refresh.Database/GameDatabaseContext.Registration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -219,22 +219,29 @@ public void RemoveEmailVerificationCode(EmailVerificationCode code)
}

public bool IsUserDisallowed(string username)
=> this.DisallowedUsers.Any(u => u.Username == username);
{
string lowercaseUsername = username.ToLower();
return this.DisallowedUsers.Any(u => u.Username == lowercaseUsername);
}

public DisallowedUser? GetDisallowedUserInfo(string username)
=> this.DisallowedUsers.FirstOrDefault(d => d.Username == username);
{
string lowercaseUsername = username.ToLower();
return this.DisallowedUsers.FirstOrDefault(d => d.Username == lowercaseUsername);
}

public DatabaseList<DisallowedUser> GetDisallowedUsers(int skip, int count)
=> new(this.DisallowedUsers.OrderByDescending(d => d.DisallowedAt), skip, count);

public (DisallowedUser, bool) DisallowUser(string username, string reason)
{
DisallowedUser? existing = this.GetDisallowedUserInfo(username);
string lowercaseUsername = username.ToLower();
DisallowedUser? existing = this.GetDisallowedUserInfo(lowercaseUsername);
if (existing != null) return (existing, false);

DisallowedUser disallowed = new()
{
Username = username,
Username = lowercaseUsername,
Reason = reason,
DisallowedAt = this._time.Now,
};
Expand All @@ -246,7 +253,8 @@ public DatabaseList<DisallowedUser> GetDisallowedUsers(int skip, int count)

public bool ReallowUser(string username)
{
DisallowedUser? disallowedUser = this.GetDisallowedUserInfo(username);
string lowercaseUsername = username.ToLower();
DisallowedUser? disallowedUser = this.GetDisallowedUserInfo(lowercaseUsername);
if (disallowedUser == null)
return false;

Expand All @@ -257,22 +265,29 @@ public bool ReallowUser(string username)
}

public bool IsEmailAddressDisallowed(string emailAddress)
=> this.DisallowedEmailAddresses.Any(u => u.Address == emailAddress);
{
string emailAddressLower = emailAddress.ToLowerInvariant();
return this.DisallowedEmailAddresses.Any(u => u.Address == emailAddressLower);
}

public DisallowedEmailAddress? GetDisallowedEmailAddressInfo(string emailAddress)
=> this.DisallowedEmailAddresses.FirstOrDefault(d => d.Address == emailAddress);
{
string emailAddressLower = emailAddress.ToLowerInvariant();
return this.DisallowedEmailAddresses.FirstOrDefault(d => d.Address == emailAddressLower);
}

public DatabaseList<DisallowedEmailAddress> GetDisallowedEmailAddresses(int skip, int count)
=> new(this.DisallowedEmailAddresses.OrderByDescending(d => d.DisallowedAt), skip, count);

public (DisallowedEmailAddress, bool) DisallowEmailAddress(string emailAddress, string reason)
{
DisallowedEmailAddress? existing = this.GetDisallowedEmailAddressInfo(emailAddress);
string emailAddressLower = emailAddress.ToLowerInvariant();
DisallowedEmailAddress? existing = this.GetDisallowedEmailAddressInfo(emailAddressLower);
if (existing != null) return (existing, false);

DisallowedEmailAddress disallowed = new()
{
Address = emailAddress,
Address = emailAddressLower,
Reason = reason,
DisallowedAt = this._time.Now,
};
Expand All @@ -284,7 +299,8 @@ public DatabaseList<DisallowedEmailAddress> GetDisallowedEmailAddresses(int skip

public bool ReallowEmailAddress(string emailAddress)
{
DisallowedEmailAddress? disallowed = this.GetDisallowedEmailAddressInfo(emailAddress);
string emailAddressLower = emailAddress.ToLowerInvariant();
DisallowedEmailAddress? disallowed = this.GetDisallowedEmailAddressInfo(emailAddressLower);
if (disallowed == null)
return false;

Expand All @@ -294,33 +310,33 @@ public bool ReallowEmailAddress(string emailAddress)
return true;
}

private string GetEmailDomainFromAddress(string emailAddress)
=> emailAddress.Split('@').Last();
private string GetLowercaseEmailDomainFromAddress(string emailAddress)
=> emailAddress.Split('@').Last().ToLowerInvariant();

public bool IsEmailDomainDisallowed(string emailAddress)
{
string emailDomain = this.GetEmailDomainFromAddress(emailAddress);
return this.DisallowedEmailDomains.Any(u => u.Domain == emailDomain);
string emailDomainLower = this.GetLowercaseEmailDomainFromAddress(emailAddress);
return this.DisallowedEmailDomains.Any(u => u.Domain == emailDomainLower);
}

public DisallowedEmailDomain? GetDisallowedEmailDomainInfo(string emailAddress)
{
string emailDomain = this.GetEmailDomainFromAddress(emailAddress);
return this.DisallowedEmailDomains.FirstOrDefault(d => d.Domain == emailDomain);
string emailDomainLower = this.GetLowercaseEmailDomainFromAddress(emailAddress);
return this.DisallowedEmailDomains.FirstOrDefault(d => d.Domain == emailDomainLower);
}

public DatabaseList<DisallowedEmailDomain> GetDisallowedEmailDomains(int skip, int count)
=> new(this.DisallowedEmailDomains.OrderByDescending(d => d.DisallowedAt), skip, count);

public (DisallowedEmailDomain, bool) DisallowEmailDomain(string emailAddress, string reason)
{
string emailDomain = this.GetEmailDomainFromAddress(emailAddress);
DisallowedEmailDomain? existing = this.GetDisallowedEmailDomainInfo(emailDomain);
string emailDomainLower = this.GetLowercaseEmailDomainFromAddress(emailAddress);
DisallowedEmailDomain? existing = this.GetDisallowedEmailDomainInfo(emailDomainLower);
if (existing != null) return (existing, false);

DisallowedEmailDomain disallowed = new()
{
Domain = emailDomain,
Domain = emailDomainLower,
Reason = reason,
DisallowedAt = this._time.Now,
};
Expand All @@ -332,8 +348,8 @@ public DatabaseList<DisallowedEmailDomain> GetDisallowedEmailDomains(int skip, i

public bool ReallowEmailDomain(string emailAddress)
{
string emailDomain = this.GetEmailDomainFromAddress(emailAddress);
DisallowedEmailDomain? disallowedDomain = this.GetDisallowedEmailDomainInfo(emailDomain);
string emailDomainLower = this.GetLowercaseEmailDomainFromAddress(emailAddress);
DisallowedEmailDomain? disallowedDomain = this.GetDisallowedEmailDomainInfo(emailDomainLower);
if (disallowedDomain == null)
return false;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;

#nullable disable

namespace Refresh.Database.Migrations
{
/// <inheritdoc />
[DbContext(typeof(GameDatabaseContext))]
[Migration("20260527172459_DisallowEntitiesCaseInsensitively")]
public partial class DisallowEntitiesCaseInsensitively : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
// Remove case-insensitively duplicate entries before lowercasing primary keys to not cause duplicate keys
// Email Addresses
migrationBuilder.Sql
("""
DELETE FROM "DisallowedEmailAddresses"
WHERE "Address" NOT IN (
SELECT min("Address")
FROM "DisallowedEmailAddresses"
GROUP BY lower("Address")
)
""");
// for some reason, Postgres won't actually execute these separately if we use semicolons, so we have to do separate method calls
migrationBuilder.Sql
("""
UPDATE "DisallowedEmailAddresses" SET "Address" = lower("Address");
""");

// Email Domains
migrationBuilder.Sql
("""
DELETE FROM "DisallowedEmailDomains"
WHERE "Domain" NOT IN (
SELECT min("Domain")
FROM "DisallowedEmailDomains"
GROUP BY lower("Domain")
)
""");
migrationBuilder.Sql
("""
UPDATE "DisallowedEmailDomains" SET "Domain" = lower("Domain");
""");

// Usernames
migrationBuilder.Sql
("""
DELETE FROM "DisallowedUsers"
WHERE "Username" NOT IN (
SELECT min("Username")
FROM "DisallowedUsers"
GROUP BY lower("Username")
)
""");
migrationBuilder.Sql
("""
UPDATE "DisallowedUsers" SET "Username" = lower("Username");
""");

// Assets
migrationBuilder.Sql
("""
DELETE FROM "DisallowedAssets"
WHERE "AssetHash" NOT IN (
SELECT min("AssetHash")
FROM "DisallowedAssets"
GROUP BY lower("AssetHash")
)
""");
migrationBuilder.Sql
("""
UPDATE "DisallowedAssets" SET "AssetHash" = lower("AssetHash");
""");
}

/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{

}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -1602,12 +1602,13 @@ protected override void BuildModel(ModelBuilder modelBuilder)
b.Property<byte>("Entity")
.HasColumnType("smallint");

b.Property<int>("UploadCount")
.HasColumnType("integer");

b.Property<DateTimeOffset>("ExpiryDate")
.HasColumnType("timestamp with time zone");

// EF badly wanted to move this below ExpiryDate fsr
b.Property<int>("UploadCount")
.HasColumnType("integer");

b.HasKey("UserId", "Entity");

b.ToTable("EntityUploadRateLimits");
Expand Down
3 changes: 3 additions & 0 deletions Refresh.Database/Models/Users/DisallowedEmailAddress.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ namespace Refresh.Database.Models.Users;

public partial class DisallowedEmailAddress
{
/// <summary>
/// Lower-case email address to allow case-insensitive lookup.
/// </summary>
[Key]
public string Address { get; set; }
public string Reason { get; set; }
Expand Down
3 changes: 3 additions & 0 deletions Refresh.Database/Models/Users/DisallowedEmailDomain.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ namespace Refresh.Database.Models.Users;

public partial class DisallowedEmailDomain
{
/// <summary>
/// Lower-case email domain to allow case-insensitive lookup.
/// </summary>
[Key]
public string Domain { get; set; }
public string Reason { get; set; }
Expand Down
3 changes: 3 additions & 0 deletions Refresh.Database/Models/Users/DisallowedUser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ namespace Refresh.Database.Models.Users;

public partial class DisallowedUser
{
/// <summary>
/// Lower-case username to allow case-insensitive lookup.
/// </summary>
[Key]
public string Username { get; set; }
public string Reason { get; set; }
Expand Down
2 changes: 1 addition & 1 deletion Refresh.Interfaces.APIv3/Endpoints/ResourceApiEndpoints.cs
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,7 @@ IntegrationConfig integration
return new ApiValidationError($"You have exceeded your filesize quota.");
}

if (database.GetDisallowedAssetInfo(hash) != null)
if (database.IsAssetDisallowed(hash))
{
context.Logger.LogWarning(BunkumCategory.UserContent, "User {0} has tried to upload a disallowed asset, rejecting.", user);
return ApiModerationError.AssetDisallowedError;
Expand Down
2 changes: 1 addition & 1 deletion Refresh.Interfaces.Game/Endpoints/ResourceEndpoints.cs
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ public Response UploadAsset(RequestContext context, string hash, string type, by
return RequestEntityTooLarge;
}

if (database.GetDisallowedAssetInfo(hash) != null)
if (database.IsAssetDisallowed(hash))
{
context.Logger.LogWarning(BunkumCategory.UserContent, "User {0} has tried to upload a disallowed asset, rejecting.", user);
return Unauthorized;
Expand Down
Loading
Loading