Skip to content

Commit 1c83be6

Browse files
committed
Do reset email when deleting user again, but also disallow email
1 parent e89510e commit 1c83be6

11 files changed

Lines changed: 170 additions & 4 deletions

File tree

Refresh.Database/GameDatabaseContext.Registration.cs

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@ public bool IsUsernameTaken(string username, GameUser? userToName = null)
100100
{
101101
if (this.GameUsers.Any(u => u.Username == username)) return true;
102102
if (this.QueuedRegistrations.Any(r => r.Username == username)) return true;
103+
if (this.IsUserDisallowed(username)) return true;
103104

104105
PreviousUsername? previous = this.PreviousUsernames.FirstOrDefault(p => p.Username == username);
105106
// no one has ever had this name before
@@ -113,7 +114,8 @@ public bool IsUsernameTaken(string username, GameUser? userToName = null)
113114
public bool IsEmailTaken(string emailAddress)
114115
{
115116
return this.GameUsers.Any(u => u.EmailAddress == emailAddress) ||
116-
this.QueuedRegistrations.Any(r => r.EmailAddress == emailAddress);
117+
this.QueuedRegistrations.Any(r => r.EmailAddress == emailAddress) ||
118+
this.IsEmailDisallowed(emailAddress);
117119
}
118120

119121
public void AddRegistrationToQueue(string username, string emailAddress, string passwordBcrypt)
@@ -250,4 +252,35 @@ public bool IsUserDisallowed(string username)
250252
{
251253
return this.DisallowedUsers.FirstOrDefault(u => u.Username == username) != null;
252254
}
255+
256+
public bool DisallowEmail(string email)
257+
{
258+
if (this.IsEmailDisallowed(email))
259+
return false;
260+
261+
this.DisallowedEmails.Add(new()
262+
{
263+
Email = email,
264+
});
265+
this.SaveChanges();
266+
267+
return true;
268+
}
269+
270+
public bool ReallowEmail(string email)
271+
{
272+
DisallowedEmail? disallowedEmail = this.DisallowedEmails.FirstOrDefault(u => u.Email == email);
273+
if (disallowedEmail == null)
274+
return false;
275+
276+
this.DisallowedEmails.Remove(disallowedEmail);
277+
this.SaveChanges();
278+
279+
return true;
280+
}
281+
282+
public bool IsEmailDisallowed(string email)
283+
{
284+
return this.DisallowedEmails.Any(u => u.Email == email);
285+
}
253286
}

Refresh.Database/GameDatabaseContext.Users.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -389,13 +389,15 @@ public void DeleteUser(GameUser user)
389389
this.BanUser(user, deletedReason, DateTimeOffset.MaxValue);
390390
this.RevokeAllTokensForUser(user);
391391
this.DeleteNotificationsByUser(user);
392+
if (user.EmailAddress != null)
393+
this.DisallowEmail(user.EmailAddress);
392394

393395
this.Write(() =>
394396
{
395397
user.LocationX = 0;
396398
user.LocationY = 0;
397399
user.Description = deletedReason;
398-
//user.EmailAddress = null; // should not be reset for now, to prevent them from just registering with it again
400+
user.EmailAddress = null;
399401
user.PasswordBcrypt = "deleted";
400402
user.JoinDate = DateTimeOffset.MinValue;
401403
user.LastLoginDate = DateTimeOffset.MinValue;

Refresh.Database/GameDatabaseContext.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ public partial class GameDatabaseContext : DbContext, IDatabaseContext
6464
internal DbSet<AssetDependencyRelation> AssetDependencyRelations { get; set; }
6565
internal DbSet<GameReview> GameReviews { get; set; }
6666
internal DbSet<DisallowedUser> DisallowedUsers { get; set; }
67+
internal DbSet<DisallowedEmail> DisallowedEmails { get; set; }
6768
internal DbSet<RateReviewRelation> RateReviewRelations { get; set; }
6869
internal DbSet<TagLevelRelation> TagLevelRelations { get; set; }
6970
internal DbSet<GamePlaylist> GamePlaylists { get; set; }
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
using Microsoft.EntityFrameworkCore.Infrastructure;
2+
using Microsoft.EntityFrameworkCore.Migrations;
3+
4+
#nullable disable
5+
6+
namespace Refresh.Database.Migrations
7+
{
8+
/// <inheritdoc />
9+
[DbContext(typeof(GameDatabaseContext))]
10+
[Migration("20260312203825_AddAbilityToDisallowEmails")]
11+
public partial class AddAbilityToDisallowEmails : Migration
12+
{
13+
/// <inheritdoc />
14+
protected override void Up(MigrationBuilder migrationBuilder)
15+
{
16+
migrationBuilder.CreateTable(
17+
name: "DisallowedEmails",
18+
columns: table => new
19+
{
20+
Email = table.Column<string>(type: "text", nullable: false)
21+
},
22+
constraints: table =>
23+
{
24+
table.PrimaryKey("PK_DisallowedEmails", x => x.Email);
25+
});
26+
}
27+
28+
/// <inheritdoc />
29+
protected override void Down(MigrationBuilder migrationBuilder)
30+
{
31+
migrationBuilder.DropTable(
32+
name: "DisallowedEmails");
33+
}
34+
}
35+
}

Refresh.Database/Migrations/GameDatabaseContextModelSnapshot.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1510,6 +1510,16 @@ protected override void BuildModel(ModelBuilder modelBuilder)
15101510
b.ToTable("RequestStatistics");
15111511
});
15121512

1513+
modelBuilder.Entity("Refresh.Database.Models.Users.DisallowedEmail", b =>
1514+
{
1515+
b.Property<string>("Email")
1516+
.HasColumnType("text");
1517+
1518+
b.HasKey("Email");
1519+
1520+
b.ToTable("DisallowedEmails");
1521+
});
1522+
15131523
modelBuilder.Entity("Refresh.Database.Models.Users.DisallowedUser", b =>
15141524
{
15151525
b.Property<string>("Username")
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
namespace Refresh.Database.Models.Users;
2+
3+
#nullable disable
4+
5+
public partial class DisallowedEmail
6+
{
7+
[Key]
8+
public string Email { get; set; }
9+
}

Refresh.GameServer/CommandLineManager.cs

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,12 @@ private class Options
5959

6060
[Option("reallow-user", HelpText = "Re-allow a user to register. Username option is required if this is set.")]
6161
public bool ReallowUser { get; set; }
62+
63+
[Option("disallow-email", HelpText = "Disallow the email from being used by anyone in the future. Email option is required if this is set.")]
64+
public bool DisallowEmail { get; set; }
65+
66+
[Option("reallow-email", HelpText = "Re-allow the email to be used by anyone. Email option is required if this is set")]
67+
public bool ReallowEmail { get; set; }
6268

6369
[Option("rename-user", HelpText = "Changes a user's username. (old) username or Email option is required if this is set.")]
6470
public string? RenameUser { get; set; }
@@ -188,10 +194,28 @@ private void StartWithOptions(Options options)
188194
}
189195
else Fail("No username was provided");
190196
}
197+
else if (options.DisallowEmail)
198+
{
199+
if (options.EmailAddress != null)
200+
{
201+
if (!this._server.DisallowEmail(options.EmailAddress))
202+
Fail("Email address is already disallowed");
203+
}
204+
else Fail("No email address was provided");
205+
}
206+
else if (options.ReallowEmail)
207+
{
208+
if (options.EmailAddress != null)
209+
{
210+
if (!this._server.ReallowEmail(options.EmailAddress))
211+
Fail("Email address is already allowed");
212+
}
213+
else Fail("No email address was provided");
214+
}
191215
else if (options.RenameUser != null)
192216
{
193217
if(string.IsNullOrWhiteSpace(options.RenameUser))
194-
Fail("Username must contain content");
218+
Fail("Email address must contain content");
195219

196220
GameUser user = this.GetUserOrFail(options);
197221
this._server.RenameUser(user, options.RenameUser, options.Force);

Refresh.GameServer/RefreshGameServer.cs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -285,6 +285,20 @@ public bool ReallowUser(string username)
285285
return context.ReallowUser(username);
286286
}
287287

288+
public bool DisallowEmail(string email)
289+
{
290+
using GameDatabaseContext context = this.GetContext();
291+
292+
return context.DisallowEmail(email);
293+
}
294+
295+
public bool ReallowEmail(string email)
296+
{
297+
using GameDatabaseContext context = this.GetContext();
298+
299+
return context.ReallowEmail(email);
300+
}
301+
288302
public void RenameUser(GameUser user, string newUsername, bool force = false)
289303
{
290304
using GameDatabaseContext context = this.GetContext();

Refresh.Interfaces.APIv3/Endpoints/AuthenticationApiEndpoints.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -324,7 +324,7 @@ public ApiResponse<IApiAuthenticationResponse> Register(RequestContext context,
324324
if (!smtpService.CheckEmailDomainValidity(body.EmailAddress))
325325
return ApiValidationError.EmailDoesNotActuallyExistError;
326326

327-
if (database.IsUserDisallowed(body.Username))
327+
if (database.IsUserDisallowed(body.Username) || database.IsEmailDisallowed(body.EmailAddress))
328328
return new ApiAuthenticationError("You aren't allowed to play on this instance.");
329329

330330
if (!database.IsUsernameValid(body.Username))

RefreshTests.GameServer/Tests/ApiV3/UserApiTests.cs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,28 @@ public void RegisterAccount()
4545
context.Database.Refresh();
4646
Assert.That(context.Database.GetUserByUsername(username), Is.Not.EqualTo(null));
4747
}
48+
49+
[Test]
50+
public void CannotRegisterAccountWithDisallowedEmail()
51+
{
52+
using TestContext context = this.GetServer();
53+
54+
const string email = "guy@lil.com";
55+
context.Database.DisallowEmail(email);
56+
57+
ApiResponse<ApiAuthenticationResponse>? response = context.Http.PostData<ApiAuthenticationResponse>("/api/v3/register", new ApiRegisterRequest
58+
{
59+
Username = "a_lil_guy",
60+
EmailAddress = email,
61+
PasswordSha512 = "ee26b0dd4af7e749aa1a8ee3c10ae9923f618980772e473f8819a5d4940e0db27ac185f8a0e1d5f84f88bc887fd67b143732c304cc5fa9ad8e6f57f50028a8ff",
62+
}, false, true);
63+
Assert.That(response, Is.Not.Null);
64+
Assert.That(response!.Error, Is.Not.Null);
65+
Assert.That(response.Error!.Name, Is.EqualTo("ApiAuthenticationError"));
66+
67+
context.Database.Refresh();
68+
Assert.That(context.Database.GetUserByEmailAddress(email), Is.Null);
69+
}
4870

4971
[Test]
5072
public void CannotRegisterAccountWithDisallowedUsername()

0 commit comments

Comments
 (0)