Skip to content

Commit 5f7aa00

Browse files
authored
Ability to block email domains (#1064)
Adds the ability to disallow and reallow email domains by adding/removing them to a database table using the CLI. Registration will be impossible as long as the email address used is from a blocked domain. Also renames some parts of email address blocking to differenciate that feature better from this one.
2 parents 4060ec4 + e654ee7 commit 5f7aa00

File tree

12 files changed

+270
-44
lines changed

12 files changed

+270
-44
lines changed

Refresh.Database/GameDatabaseContext.Registration.cs

Lines changed: 48 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ public bool IsEmailTaken(string emailAddress)
115115
{
116116
return this.GameUsers.Any(u => u.EmailAddress == emailAddress) ||
117117
this.QueuedRegistrations.Any(r => r.EmailAddress == emailAddress) ||
118-
this.IsEmailDisallowed(emailAddress);
118+
this.IsEmailAddressDisallowed(emailAddress);
119119
}
120120

121121
public void AddRegistrationToQueue(string username, string emailAddress, string passwordBcrypt)
@@ -253,34 +253,71 @@ public bool IsUserDisallowed(string username)
253253
return this.DisallowedUsers.FirstOrDefault(u => u.Username == username) != null;
254254
}
255255

256-
public bool DisallowEmail(string email)
256+
public bool DisallowEmailAddress(string emailAddress)
257257
{
258-
if (this.IsEmailDisallowed(email))
258+
if (this.IsEmailAddressDisallowed(emailAddress))
259259
return false;
260260

261-
this.DisallowedEmails.Add(new()
261+
this.DisallowedEmailAddresses.Add(new()
262262
{
263-
Email = email,
263+
Address = emailAddress,
264264
});
265265
this.SaveChanges();
266266

267267
return true;
268268
}
269269

270-
public bool ReallowEmail(string email)
270+
public bool ReallowEmailAddress(string emailAddress)
271271
{
272-
DisallowedEmail? disallowedEmail = this.DisallowedEmails.FirstOrDefault(u => u.Email == email);
273-
if (disallowedEmail == null)
272+
DisallowedEmailAddress? DisallowedEmailAddress = this.DisallowedEmailAddresses.FirstOrDefault(u => u.Address == emailAddress);
273+
if (DisallowedEmailAddress == null)
274274
return false;
275275

276-
this.DisallowedEmails.Remove(disallowedEmail);
276+
this.DisallowedEmailAddresses.Remove(DisallowedEmailAddress);
277277
this.SaveChanges();
278278

279279
return true;
280280
}
281281

282-
public bool IsEmailDisallowed(string email)
282+
public bool IsEmailAddressDisallowed(string emailAddress)
283283
{
284-
return this.DisallowedEmails.Any(u => u.Email == email);
284+
return this.DisallowedEmailAddresses.Any(u => u.Address == emailAddress);
285+
}
286+
287+
private string GetEmailDomainFromAddress(string emailAddress)
288+
=> emailAddress.Split('@').Last();
289+
290+
public bool DisallowEmailDomain(string emailAddress)
291+
{
292+
string emailDomain = this.GetEmailDomainFromAddress(emailAddress);
293+
if (this.IsEmailDomainDisallowed(emailDomain))
294+
return false;
295+
296+
this.DisallowedEmailDomains.Add(new()
297+
{
298+
Domain = emailDomain,
299+
});
300+
this.SaveChanges();
301+
302+
return true;
303+
}
304+
305+
public bool ReallowEmailDomain(string emailAddress)
306+
{
307+
string emailDomain = this.GetEmailDomainFromAddress(emailAddress);
308+
DisallowedEmailDomain? disallowedDomain = this.DisallowedEmailDomains.FirstOrDefault(u => u.Domain == emailDomain);
309+
if (disallowedDomain == null)
310+
return false;
311+
312+
this.DisallowedEmailDomains.Remove(disallowedDomain);
313+
this.SaveChanges();
314+
315+
return true;
316+
}
317+
318+
public bool IsEmailDomainDisallowed(string emailAddress)
319+
{
320+
string emailDomain = this.GetEmailDomainFromAddress(emailAddress);
321+
return this.DisallowedEmailDomains.Any(u => u.Domain == emailDomain);
285322
}
286323
}

Refresh.Database/GameDatabaseContext.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,8 @@ 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; }
67+
internal DbSet<DisallowedEmailAddress> DisallowedEmailAddresses { get; set; }
68+
internal DbSet<DisallowedEmailDomain> DisallowedEmailDomains { get; set; }
6869
internal DbSet<RateReviewRelation> RateReviewRelations { get; set; }
6970
internal DbSet<TagLevelRelation> TagLevelRelations { get; set; }
7071
internal DbSet<GamePlaylist> GamePlaylists { get; set; }
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
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("20260331143640_AddAbilityToDisallowEmailDomains")]
11+
public partial class AddAbilityToDisallowEmailDomains : Migration
12+
{
13+
/// <inheritdoc />
14+
protected override void Up(MigrationBuilder migrationBuilder)
15+
{
16+
migrationBuilder.RenameTable(name: "DisallowedEmails", newName: "DisallowedEmailAddresses");
17+
migrationBuilder.DropPrimaryKey(name: "PK_DisallowedEmails", table: "DisallowedEmailAddresses");
18+
migrationBuilder.RenameColumn(name: "Email", table: "DisallowedEmailAddresses", newName: "Address");
19+
migrationBuilder.AddPrimaryKey(name: "PK_DisallowedEmailAddresses", table: "DisallowedEmailAddresses", column: "Address");
20+
21+
migrationBuilder.CreateTable(
22+
name: "DisallowedEmailDomains",
23+
columns: table => new
24+
{
25+
Domain = table.Column<string>(type: "text", nullable: false)
26+
},
27+
constraints: table =>
28+
{
29+
table.PrimaryKey("PK_DisallowedEmailDomains", x => x.Domain);
30+
});
31+
}
32+
33+
/// <inheritdoc />
34+
protected override void Down(MigrationBuilder migrationBuilder)
35+
{
36+
migrationBuilder.DropTable(
37+
name: "DisallowedEmailDomains");
38+
39+
migrationBuilder.RenameTable(name: "DisallowedEmailAddresses", newName: "DisallowedEmails");
40+
migrationBuilder.DropPrimaryKey(name: "PK_DisallowedEmailAddresses", table: "DisallowedEmails");
41+
migrationBuilder.RenameColumn(name: "Address", table: "DisallowedEmails", newName: "Email");
42+
migrationBuilder.AddPrimaryKey(name: "PK_DisallowedEmails", table: "DisallowedEmails", column: "Email");
43+
}
44+
}
45+
}

Refresh.Database/Migrations/GameDatabaseContextModelSnapshot.cs

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

1513-
modelBuilder.Entity("Refresh.Database.Models.Users.DisallowedEmail", b =>
1513+
modelBuilder.Entity("Refresh.Database.Models.Users.DisallowedEmailAddress", b =>
15141514
{
1515-
b.Property<string>("Email")
1515+
b.Property<string>("Address")
15161516
.HasColumnType("text");
15171517

1518-
b.HasKey("Email");
1518+
b.HasKey("Address");
15191519

1520-
b.ToTable("DisallowedEmails");
1520+
b.ToTable("DisallowedEmailAddresses");
1521+
});
1522+
1523+
modelBuilder.Entity("Refresh.Database.Models.Users.DisallowedEmailDomain", b =>
1524+
{
1525+
b.Property<string>("Domain")
1526+
.HasColumnType("text");
1527+
1528+
b.HasKey("Domain");
1529+
1530+
b.ToTable("DisallowedEmailDomains");
15211531
});
15221532

15231533
modelBuilder.Entity("Refresh.Database.Models.Users.DisallowedUser", b =>

Refresh.Database/Models/Users/DisallowedEmail.cs

Lines changed: 0 additions & 9 deletions
This file was deleted.
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 DisallowedEmailAddress
6+
{
7+
[Key]
8+
public string Address { get; set; }
9+
}
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 DisallowedEmailDomain
6+
{
7+
[Key]
8+
public string Domain { get; set; }
9+
}

Refresh.GameServer/CommandLineManager.cs

Lines changed: 32 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -60,11 +60,17 @@ private class Options
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; }
6262

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; }
63+
[Option("disallow-email", HelpText = "Disallow the email address from being used by anyone in the future. Email option is required if this is set.")]
64+
public bool DisallowEmailAddress { get; set; }
6565

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; }
66+
[Option("reallow-email", HelpText = "Re-allow the email address to be used for account registration. Email option is required if this is set")]
67+
public bool ReallowEmailAddress { get; set; }
68+
69+
[Option("disallow-email-domain", HelpText = "Disallow the email domain from being used by anyone in the future. Email option is required if this is set. If a whole Email address is given, only the substring after the last @ will be used.")]
70+
public bool DisallowEmailDomain { get; set; }
71+
72+
[Option("reallow-email-domain", HelpText = "Re-allow the email domain to be used by anyone. Email option is required if this is set. If a whole Email address is given, only the substring after the last @ will be used.")]
73+
public bool ReallowEmailDomain { get; set; }
6874

6975
[Option("rename-user", HelpText = "Changes a user's username. (old) username or Email option is required if this is set.")]
7076
public string? RenameUser { get; set; }
@@ -194,24 +200,42 @@ private void StartWithOptions(Options options)
194200
}
195201
else Fail("No username was provided");
196202
}
197-
else if (options.DisallowEmail)
203+
else if (options.DisallowEmailAddress)
198204
{
199205
if (options.EmailAddress != null)
200206
{
201-
if (!this._server.DisallowEmail(options.EmailAddress))
207+
if (!this._server.DisallowEmailAddress(options.EmailAddress))
202208
Fail("Email address is already disallowed");
203209
}
204210
else Fail("No email address was provided");
205211
}
206-
else if (options.ReallowEmail)
212+
else if (options.ReallowEmailAddress)
207213
{
208214
if (options.EmailAddress != null)
209215
{
210-
if (!this._server.ReallowEmail(options.EmailAddress))
216+
if (!this._server.ReallowEmailAddress(options.EmailAddress))
211217
Fail("Email address is already allowed");
212218
}
213219
else Fail("No email address was provided");
214220
}
221+
else if (options.DisallowEmailDomain)
222+
{
223+
if (options.EmailAddress != null)
224+
{
225+
if (!this._server.DisallowEmailDomain(options.EmailAddress))
226+
Fail("Email domain is already disallowed");
227+
}
228+
else Fail("No email domain was provided");
229+
}
230+
else if (options.ReallowEmailDomain)
231+
{
232+
if (options.EmailAddress != null)
233+
{
234+
if (!this._server.ReallowEmailDomain(options.EmailAddress))
235+
Fail("Email domain is already allowed");
236+
}
237+
else Fail("No email domain was provided");
238+
}
215239
else if (options.RenameUser != null)
216240
{
217241
if(string.IsNullOrWhiteSpace(options.RenameUser))

Refresh.GameServer/RefreshGameServer.cs

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

288-
public bool DisallowEmail(string email)
288+
public bool DisallowEmailAddress(string address)
289289
{
290290
using GameDatabaseContext context = this.GetContext();
291-
292-
return context.DisallowEmail(email);
291+
292+
return context.DisallowEmailAddress(address);
293293
}
294294

295-
public bool ReallowEmail(string email)
295+
public bool ReallowEmailAddress(string address)
296296
{
297297
using GameDatabaseContext context = this.GetContext();
298-
299-
return context.ReallowEmail(email);
298+
299+
return context.ReallowEmailAddress(address);
300+
}
301+
302+
public bool DisallowEmailDomain(string domain)
303+
{
304+
using GameDatabaseContext context = this.GetContext();
305+
306+
return context.DisallowEmailDomain(domain);
307+
}
308+
309+
public bool ReallowEmailDomain(string domain)
310+
{
311+
using GameDatabaseContext context = this.GetContext();
312+
313+
return context.ReallowEmailDomain(domain);
300314
}
301315

302316
public void RenameUser(GameUser user, string newUsername, bool force = false)

Refresh.Interfaces.APIv3/Endpoints/AuthenticationApiEndpoints.cs

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

350-
if (database.IsUserDisallowed(body.Username) || database.IsEmailDisallowed(body.EmailAddress))
350+
if (database.IsUserDisallowed(body.Username) || database.IsEmailAddressDisallowed(body.EmailAddress) || database.IsEmailDomainDisallowed(body.EmailAddress))
351351
return new ApiAuthenticationError("You aren't allowed to play on this instance.");
352352

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

0 commit comments

Comments
 (0)