Skip to content

Commit

Permalink
Add ability to block certain users from registering accounts (#380)
Browse files Browse the repository at this point in the history
Closes #378
  • Loading branch information
jvyden authored Mar 27, 2024
2 parents 47430d9 + bf5529f commit 8a6b9be
Show file tree
Hide file tree
Showing 7 changed files with 146 additions and 1 deletion.
40 changes: 40 additions & 0 deletions Refresh.GameServer/CommandLineManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,12 @@ private class Options

[Option('f', "force", Required = false, HelpText = "Force all operations to happen, skipping user consent")]
public bool Force { get; set; }

[Option('b', "disallow_user", Required = false, HelpText = "Disallow a user from registering. Username option is required if this is set.")]
public bool DisallowUser { get; set; }

[Option('r', "reallow_user", Required = false, HelpText = "Re-allow a user to register. Username option is requried if this is set.")]
public bool ReallowUser { get; set; }
}

internal void StartWithArgs(string[] args)
Expand Down Expand Up @@ -99,5 +105,39 @@ private void StartWithOptions(Options options)
Environment.Exit(1);
}
}

if (options.DisallowUser)
{
if (options.Username != null)
{
if (!this._server.DisallowUser(options.Username))
{
Console.WriteLine("User is already disallowed");
Environment.Exit(1);
}
}
else
{
Console.WriteLine("No user was provided, cannot continue.");
Environment.Exit(1);
}
}

if (options.ReallowUser)
{
if (options.Username != null)
{
if (!this._server.ReallowUser(options.Username))
{
Console.WriteLine("User is already allowed");
Environment.Exit(1);
}
}
else
{
Console.WriteLine("No user was provided, cannot continue.");
Environment.Exit(1);
}
}
}
}
35 changes: 35 additions & 0 deletions Refresh.GameServer/Database/GameDatabaseContext.Registration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -191,4 +191,39 @@ public void RemoveEmailVerificationCode(EmailVerificationCode code)
this._realm.Remove(code);
});
}

public bool DisallowUser(string username)
{
if (this._realm.Find<DisallowedUser>(username) != null)
return false;

this._realm.Write(() =>
{
this._realm.Add(new DisallowedUser
{
Username = username,
});
});

return true;
}

public bool ReallowUser(string username)
{
DisallowedUser? disallowedUser = this._realm.Find<DisallowedUser>(username);
if (disallowedUser == null)
return false;

this._realm.Write(() =>
{
this._realm.Remove(disallowedUser);
});

return true;
}

public bool IsUserDisallowed(string username)
{
return this._realm.Find<DisallowedUser>(username) != null;
}
}
3 changes: 2 additions & 1 deletion Refresh.GameServer/Database/GameDatabaseProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ protected GameDatabaseProvider(IDateTimeProvider time)
this._time = time;
}

protected override ulong SchemaVersion => 117;
protected override ulong SchemaVersion => 118;

protected override string Filename => "refreshGameServer.realm";

Expand Down Expand Up @@ -74,6 +74,7 @@ protected GameDatabaseProvider(IDateTimeProvider time)
typeof(ScreenRect),
typeof(Slot),
typeof(GameReview),
typeof(DisallowedUser)
};

public override void Warmup()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,9 @@ public ApiResponse<IApiAuthenticationResponse> Register(RequestContext context,
if (!CommonPatterns.EmailAddressRegex().IsMatch(body.EmailAddress))
return new ApiValidationError("The email address given is invalid.");

if (database.IsUserDisallowed(body.Username))
return new ApiAuthenticationError("This username is disallowed from being registered.");

if (database.IsUsernameTaken(body.Username) || database.IsEmailTaken(body.EmailAddress))
{
return new ApiAuthenticationError(
Expand Down
14 changes: 14 additions & 0 deletions Refresh.GameServer/RefreshGameServer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,20 @@ public void SetAdminFromEmailAddress(string emailAddress)

context.SetUserRole(user, GameUserRole.Admin);
}

public bool DisallowUser(string username)
{
using GameDatabaseContext context = this.GetContext();

return context.DisallowUser(username);
}

public bool ReallowUser(string username)
{
using GameDatabaseContext context = this.GetContext();

return context.ReallowUser(username);
}

public override void Dispose()
{
Expand Down
9 changes: 9 additions & 0 deletions Refresh.GameServer/Types/UserData/DisallowedUser.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
using Realms;

namespace Refresh.GameServer.Types.UserData;

public partial class DisallowedUser : IRealmObject
{
[PrimaryKey]
public string Username { get; set; }

Check warning on line 8 in Refresh.GameServer/Types/UserData/DisallowedUser.cs

View workflow job for this annotation

GitHub Actions / Build, Test, and Upload Builds

Non-nullable property 'Username' must contain a non-null value when exiting constructor. Consider declaring the property as nullable.

Check warning on line 8 in Refresh.GameServer/Types/UserData/DisallowedUser.cs

View workflow job for this annotation

GitHub Actions / Build, Test, and Upload Builds

Non-nullable property 'Username' must contain a non-null value when exiting constructor. Consider declaring the property as nullable.
}
43 changes: 43 additions & 0 deletions RefreshTests.GameServer/Tests/ApiV3/UserApiTests.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using Refresh.GameServer.Authentication;
using Refresh.GameServer.Endpoints.ApiV3.ApiTypes;
using Refresh.GameServer.Endpoints.ApiV3.ApiTypes.Errors;
using Refresh.GameServer.Endpoints.ApiV3.DataTypes.Request.Authentication;
using Refresh.GameServer.Endpoints.ApiV3.DataTypes.Response;
using Refresh.GameServer.Types.UserData;
using RefreshTests.GameServer.Extensions;
Expand All @@ -22,6 +23,48 @@ public void GetsUserByUsername()
Assert.That(response, Is.Not.Null);
response!.AssertErrorIsEqual(ApiNotFoundError.UserMissingError);
}

[Test]
public void RegisterAccount()
{
using TestContext context = this.GetServer();

const string username = "a_lil_guy";

ApiResponse<ApiAuthenticationResponse>? response = context.Http.PostData<ApiAuthenticationResponse>("/api/v3/register", new ApiRegisterRequest
{
Username = username,
EmailAddress = "[email protected]",
PasswordSha512 = "ee26b0dd4af7e749aa1a8ee3c10ae9923f618980772e473f8819a5d4940e0db27ac185f8a0e1d5f84f88bc887fd67b143732c304cc5fa9ad8e6f57f50028a8ff",
});
Assert.That(response, Is.Not.EqualTo(null));

context.Database.Refresh();
Assert.That(context.Database.GetUserByUsername(username), Is.Not.EqualTo(null));
}

[Test]
public void CannotRegisterAccountWithDisallowedUsername()
{
using TestContext context = this.GetServer();

const string username = "a_lil_guy";

context.Database.DisallowUser(username);

ApiResponse<ApiAuthenticationResponse>? response = context.Http.PostData<ApiAuthenticationResponse>("/api/v3/register", new ApiRegisterRequest
{
Username = username,
EmailAddress = "[email protected]",
PasswordSha512 = "ee26b0dd4af7e749aa1a8ee3c10ae9923f618980772e473f8819a5d4940e0db27ac185f8a0e1d5f84f88bc887fd67b143732c304cc5fa9ad8e6f57f50028a8ff",
});
Assert.That(response, Is.Not.EqualTo(null));
Assert.That(response.Error, Is.Not.EqualTo(null));
Assert.That(response.Error.Name, Is.EqualTo("ApiAuthenticationError"));

context.Database.Refresh();
Assert.That(context.Database.GetUserByUsername(username), Is.EqualTo(null));
}

[Test]
public void GetsUserByUuid()
Expand Down

0 comments on commit 8a6b9be

Please sign in to comment.