Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
980a2af
Add email blacklist and refactor email validity checks
FeTetra Dec 17, 2024
fbe3cee
Slight method refactor
FeTetra Dec 17, 2024
9e9c96e
Prevent access to GameServer features if email enforcement is enabled…
FeTetra Dec 17, 2024
7495c41
Add announcement text to notify users of email enforcement requirements
FeTetra Dec 17, 2024
cb6d772
Fix comments and unauthorize null users
FeTetra Dec 17, 2024
5bdb1c0
General cleanup, move warning messages, fix bugs
FeTetra Dec 20, 2024
f6cbafd
Slight refactor and finish enforcement on endpoints
FeTetra Dec 23, 2024
67a8c6d
Merge branch 'LBPUnion:main' into emailenforcement_pr
FeTetra Jan 26, 2025
a322a32
Commit of shame
FeTetra Jan 27, 2025
561e1d4
Move everything into middleware
FeTetra Feb 17, 2025
91c5976
Oops missed one
FeTetra Feb 17, 2025
9732936
Missed another
FeTetra Feb 17, 2025
64e21ab
Remove empty array items after split
FeTetra Feb 17, 2025
0c8ca73
Slight refactor and polish
FeTetra Feb 17, 2025
479b1da
Fix string formatting on announce text
FeTetra Feb 17, 2025
3041d2c
Replace configuration strings with translatable strings
FeTetra Feb 17, 2025
3fb598e
Edit invalid email message
FeTetra Feb 17, 2025
048f60c
Remove remaining code from before middleware
FeTetra Feb 17, 2025
f77e5ee
Revert translation change
FeTetra Feb 17, 2025
3fb1441
Small fixup and fix unit test conflicts
FeTetra Feb 18, 2025
40a4d5e
Fix Koko suggestions
FeTetra Feb 21, 2025
0d3e139
Fix more koko suggestions
FeTetra Mar 2, 2025
ed9c146
Fix zaprit suggestion
FeTetra Mar 8, 2025
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
6 changes: 0 additions & 6 deletions .idea/.idea.ProjectLighthouse/.idea/sqldialects.xml

This file was deleted.

9 changes: 9 additions & 0 deletions ProjectLighthouse.Localization/BaseLayout.resx
Original file line number Diff line number Diff line change
Expand Up @@ -93,4 +93,13 @@
<data name="read_only_warn" xml:space="preserve">
<value>This instance is currently in read-only mode. Level and photo uploads, comments, reviews, and certain profile changes will be restricted until read-only mode is disabled.</value>
</data>
<data name="email_enforcement_message_main" xml:space="preserve">
<value>This instance has email enforcement enabled. If you haven't already, you will need to set and verify an email address to use most features.</value>
</data>
<data name="email_enforcement_message_no_email" xml:space="preserve">
<value>You do not have an email set on your account. You can set an email by opening the text chat and typing "/setemail [youremail@example.com]" (do not include the brackets.)</value>
</data>
<data name="email_enforcement_message_verify_email" xml:space="preserve">
<value>You have set an email address on your account, but you have not verified it. Make sure to check your inbox for a verification email. If you have not recieved an email, please contact an instance administrator for further assistance.</value>
</data>
Comment on lines +96 to +104
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
<data name="email_enforcement_message_main" xml:space="preserve">
<value>This instance has email enforcement enabled. If you haven't already, you will need to set and verify an email address to use most features.</value>
</data>
<data name="email_enforcement_message_no_email" xml:space="preserve">
<value>You do not have an email set on your account. You can set an email by opening the text chat and typing "/setemail [youremail@example.com]" (do not include the brackets.)</value>
</data>
<data name="email_enforcement_message_verify_email" xml:space="preserve">
<value>You have set an email address on your account, but you have not verified it. Make sure to check your inbox for a verification email. If you have not recieved an email, please contact an instance administrator for further assistance.</value>
</data>

These should probably be hardcoded since they're not used in the website.

</root>
Original file line number Diff line number Diff line change
Expand Up @@ -26,5 +26,10 @@ public static class BaseLayoutStrings
public static readonly TranslatableString ReadOnlyWarnTitle = create("read_only_warn_title");
public static readonly TranslatableString ReadOnlyWarn = create("read_only_warn");

public static readonly TranslatableString EmailEnforcementWarnMain = create("email_enforcement_message_main");
public static readonly TranslatableString EmailEnforcementWarnNoEmail = create("email_enforcement_message_no_email");
public static readonly TranslatableString EmailEnforcementWarnVerifyEmail = create("email_enforcement_message_verify_email");

Comment on lines +29 to +32
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You only use these strings within GameServer code which doesn't have localization support and will therefore always be in the default language. This should probably just be hardcoded in the Announce method.


private static TranslatableString create(string key) => new(TranslationAreas.BaseLayout, key);
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#nullable enable

Check warning on line 1 in ProjectLighthouse.Servers.GameServer/Controllers/CommentController.cs

View workflow job for this annotation

GitHub Actions / Qodana for .NET

Redundant nullable directive

Redundant nullable directive
using LBPUnion.ProjectLighthouse.Configuration;
using LBPUnion.ProjectLighthouse.Database;
using LBPUnion.ProjectLighthouse.Extensions;
Expand All @@ -22,7 +22,7 @@
public class CommentController : ControllerBase
{
private readonly DatabaseContext database;
public CommentController(DatabaseContext database)

Check notice on line 25 in ProjectLighthouse.Servers.GameServer/Controllers/CommentController.cs

View workflow job for this annotation

GitHub Actions / Qodana for .NET

Convert constructor into primary constructor

Convert into primary constructor
{
this.database = database;
}
Expand All @@ -49,7 +49,7 @@
GameTokenEntity token = this.GetToken();

UserEntity? user = await this.database.UserFromGameToken(token);
if (user == null) return this.Unauthorized();
if (user == null) return this.Forbid();

if ((slotId == 0 || SlotHelper.IsTypeInvalid(slotType)) == (username == null)) return this.BadRequest();

Expand Down Expand Up @@ -97,7 +97,7 @@
.ApplyPagination(pageData)
.ToListAsync()).ToSerializableList(c => GameComment.CreateFromEntity(c, token.UserId));

if (type == CommentType.Level && slotType == "developer" && user.IsModerator && pageData.PageStart == 1)
if (type == CommentType.Level && slotType == "developer" && user.IsModerator && pageData.PageStart == 1)
{
comments.Insert(0, new GameComment
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
#nullable enable

Check warning on line 1 in ProjectLighthouse.Servers.GameServer/Controllers/Matching/EnterLevelController.cs

View workflow job for this annotation

GitHub Actions / Qodana for .NET

Redundant nullable directive

Redundant nullable directive
using LBPUnion.ProjectLighthouse.Database;
using LBPUnion.ProjectLighthouse.Extensions;
using LBPUnion.ProjectLighthouse.Helpers;
using LBPUnion.ProjectLighthouse.Types.Entities.Interaction;
using LBPUnion.ProjectLighthouse.Types.Entities.Level;
using LBPUnion.ProjectLighthouse.Types.Entities.Profile;
using LBPUnion.ProjectLighthouse.Types.Entities.Token;
using LBPUnion.ProjectLighthouse.Types.Users;
using Microsoft.AspNetCore.Authorization;
Expand All @@ -20,7 +21,7 @@
{
private readonly DatabaseContext database;

public EnterLevelController(DatabaseContext database)

Check notice on line 24 in ProjectLighthouse.Servers.GameServer/Controllers/Matching/EnterLevelController.cs

View workflow job for this annotation

GitHub Actions / Qodana for .NET

Convert constructor into primary constructor

Convert into primary constructor
{
this.database = database;
}
Expand Down Expand Up @@ -100,6 +101,9 @@
{
GameTokenEntity token = this.GetToken();

UserEntity? user = await this.database.UserFromGameToken(token);
if (user == null) return this.Unauthorized();

if (SlotHelper.IsTypeInvalid(slotType)) return this.BadRequest();

if (slotType == "developer") return this.Ok();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#nullable enable

Check warning on line 1 in ProjectLighthouse.Servers.GameServer/Controllers/Matching/MatchController.cs

View workflow job for this annotation

GitHub Actions / Qodana for .NET

Redundant nullable directive

Redundant nullable directive
using System.Text.Json;
using LBPUnion.ProjectLighthouse.Configuration;
using LBPUnion.ProjectLighthouse.Database;
Expand All @@ -25,7 +25,7 @@
{
private readonly DatabaseContext database;

public MatchController(DatabaseContext database)

Check notice on line 28 in ProjectLighthouse.Servers.GameServer/Controllers/Matching/MatchController.cs

View workflow job for this annotation

GitHub Actions / Qodana for .NET

Convert constructor into primary constructor

Convert into primary constructor
{
this.database = database;
}
Expand All @@ -41,7 +41,7 @@
GameTokenEntity token = this.GetToken();

UserEntity? user = await this.database.UserFromGameToken(token);
if (user == null) return this.Forbid();
if (user == null) return this.Unauthorized();

await LastContactHelper.SetLastContact(this.database, user, token.GameVersion, token.Platform);

Expand Down Expand Up @@ -112,7 +112,7 @@
}
case CreateRoom createRoom:
{
List<int> users = new();

Check notice on line 115 in ProjectLighthouse.Servers.GameServer/Controllers/Matching/MatchController.cs

View workflow job for this annotation

GitHub Actions / Qodana for .NET

Use collection expression syntax

Use collection expression
foreach (string playerUsername in createRoom.Players)
{
UserEntity? player = await this.database.Users.FirstOrDefaultAsync(u => u.Username == playerUsername);
Expand All @@ -131,7 +131,7 @@

if (room != null)
{
List<UserEntity> users = new();

Check notice on line 134 in ProjectLighthouse.Servers.GameServer/Controllers/Matching/MatchController.cs

View workflow job for this annotation

GitHub Actions / Qodana for .NET

Use collection expression syntax

Use collection expression
foreach (string playerUsername in updatePlayersInRoom.Players)
{
UserEntity? player = await this.database.Users.FirstOrDefaultAsync(u => u.Username == playerUsername);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.";

public MessageController(DatabaseContext database)

Check notice on line 45 in ProjectLighthouse.Servers.GameServer/Controllers/MessageController.cs

View workflow job for this annotation

GitHub Actions / Qodana for .NET

Convert constructor into primary constructor

Convert into primary constructor
{
this.database = database;
}
Expand All @@ -55,16 +55,31 @@
{
GameTokenEntity token = this.GetToken();

string username = await this.database.UsernameFromGameToken(token);
UserEntity? user = await this.database.UserFromGameToken(token);
if (user == null) return this.BadRequest();

StringBuilder announceText = new(ServerConfiguration.Instance.AnnounceText);

announceText.Replace("%user", username);
announceText.Replace("%user", user.Username);
announceText.Replace("%id", token.UserId.ToString());

if (ServerConfiguration.Instance.UserGeneratedContentLimits.ReadOnlyMode)
{
announceText.Insert(0, BaseLayoutStrings.ReadOnlyWarn.Translate(LocalizationManager.DefaultLang) + "\n\n");
announceText.Append(BaseLayoutStrings.ReadOnlyWarn.Translate(LocalizationManager.DefaultLang) + "\n\n");
}

if (ServerConfiguration.Instance.EmailEnforcement.EnableEmailEnforcement)
{
announceText.Append("\n\n" + BaseLayoutStrings.EmailEnforcementWarnMain.Translate(LocalizationManager.DefaultLang) + "\n\n");

if (user.EmailAddress == null)
{
announceText.Append(BaseLayoutStrings.EmailEnforcementWarnNoEmail.Translate(LocalizationManager.DefaultLang) + "\n\n");
}
else if (!user.EmailAddressVerified)
{
announceText.Append(BaseLayoutStrings.EmailEnforcementWarnVerifyEmail.Translate(LocalizationManager.DefaultLang) + "\n\n");
}
}

#if DEBUG
Expand All @@ -72,7 +87,7 @@
$"user.UserId: {token.UserId}\n" +
$"token.GameVersion: {token.GameVersion}\n" +
$"token.TicketHash: {token.TicketHash}\n" +
$"token.ExpiresAt: {token.ExpiresAt.ToString()}\n" +

Check warning on line 90 in ProjectLighthouse.Servers.GameServer/Controllers/MessageController.cs

View workflow job for this annotation

GitHub Actions / Qodana for .NET

Specify string culture explicitly

Specify string culture explicitly
"---DEBUG INFO---");
#endif

Expand Down Expand Up @@ -128,13 +143,13 @@

if (message.StartsWith("/setemail ") && ServerConfiguration.Instance.Mail.MailEnabled)
{
string email = message[(message.IndexOf(" ", StringComparison.Ordinal)+1)..];

Check notice on line 146 in ProjectLighthouse.Servers.GameServer/Controllers/MessageController.cs

View workflow job for this annotation

GitHub Actions / Qodana for .NET

RoslynAnalyzers Use char overload

Use 'string.IndexOf(char)' instead of 'string.IndexOf(string)' when you have a string with a single char
if (!SanitizationHelper.IsValidEmail(email)) return this.Ok();

if (await this.database.Users.AnyAsync(u => u.EmailAddress == email)) return this.Ok();
// Return a bad request on invalid email address
if (!SMTPHelper.IsValidEmail(this.database, email)) return this.BadRequest();

UserEntity? user = await this.database.UserFromGameToken(token);
if (user == null || user.EmailAddressVerified) return this.Ok();
if (user == null || user.EmailAddressVerified) return this.BadRequest();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Have you tested that the game doesn't behave erratically if the request fails? The purpose of returning an empty 200 response is so that in a multiplayer lobby, players won't be able to see the user's email that they just typed.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have not tested this with other clients, but I would assume that if the response is empty then the clients shouldn't get the message either way. I can try testing this with a proxy or something since I don't necessarily have multiple devices I can run clients on to host a lobby.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The client sending the message seems to function perfectly fine when it gets the 400 back though.


user.EmailAddress = email;
await SMTPHelper.SendVerificationEmail(this.database, mailService, user);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#nullable enable

Check warning on line 1 in ProjectLighthouse.Servers.GameServer/Controllers/Resources/PhotosController.cs

View workflow job for this annotation

GitHub Actions / Qodana for .NET

Redundant nullable directive

Redundant nullable directive
using Discord;
using LBPUnion.ProjectLighthouse.Configuration;
using LBPUnion.ProjectLighthouse.Database;
Expand Down Expand Up @@ -27,7 +27,7 @@
{
private readonly DatabaseContext database;

public PhotosController(DatabaseContext database)

Check notice on line 30 in ProjectLighthouse.Servers.GameServer/Controllers/Resources/PhotosController.cs

View workflow job for this annotation

GitHub Actions / Qodana for .NET

Convert constructor into primary constructor

Convert into primary constructor
{
this.database = database;
}
Expand Down Expand Up @@ -174,7 +174,6 @@
[HttpGet("photos/{slotType}/{id:int}")]
public async Task<IActionResult> SlotPhotos(string slotType, int id, [FromQuery] string? by)
{

if (SlotHelper.IsTypeInvalid(slotType)) return this.BadRequest();

if (slotType == "developer") id = await SlotHelper.GetPlaceholderSlotId(this.database, id, SlotType.Developer);
Expand Down Expand Up @@ -202,7 +201,6 @@
[HttpGet("photos/by")]
public async Task<IActionResult> UserPhotosBy(string user)
{

int targetUserId = await this.database.UserIdFromUsername(user);
if (targetUserId == 0) return this.NotFound();

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#nullable enable

Check warning on line 1 in ProjectLighthouse.Servers.GameServer/Controllers/Resources/ResourcesController.cs

View workflow job for this annotation

GitHub Actions / Qodana for .NET

Redundant nullable directive

Redundant nullable directive
using System.Text;
using LBPUnion.ProjectLighthouse.Configuration;
using LBPUnion.ProjectLighthouse.Extensions;
Expand All @@ -19,7 +19,6 @@
[Route("LITTLEBIGPLANETPS3_XML")]
public class ResourcesController : ControllerBase
{

[HttpPost("showModerated")]
public IActionResult ShowModerated() => this.Ok(new ResourceList());

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
{
private readonly DatabaseContext database;

public CategoryController(DatabaseContext database)

Check notice on line 30 in ProjectLighthouse.Servers.GameServer/Controllers/Slots/CategoryController.cs

View workflow job for this annotation

GitHub Actions / Qodana for .NET

Convert constructor into primary constructor

Convert into primary constructor
{
this.database = database;
}
Expand All @@ -38,16 +38,13 @@
{
GameTokenEntity token = this.GetToken();

UserEntity? user = await this.database.UserFromGameToken(token);
if (user == null) return this.Forbid();

PaginationData pageData = this.Request.GetPaginationData();

pageData.TotalElements = CategoryHelper.Categories.Count(c => !string.IsNullOrWhiteSpace(c.Name));

if (!int.TryParse(this.Request.Query["num_categories_with_results"], out int results)) results = 5;

List<GameCategory> categories = new();

Check notice on line 47 in ProjectLighthouse.Servers.GameServer/Controllers/Slots/CategoryController.cs

View workflow job for this annotation

GitHub Actions / Qodana for .NET

Use collection expression syntax

Use collection expression

SlotQueryBuilder queryBuilder = this.FilterFromRequest(token);

Expand All @@ -72,9 +69,6 @@
{
GameTokenEntity token = this.GetToken();

UserEntity? user = await this.database.UserFromGameToken(token);
if (user == null) return this.Forbid();

Category? category = CategoryHelper.Categories.FirstOrDefault(c => c.Endpoint == endpointName);
if (category == null) return this.NotFound();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
{
private readonly DatabaseContext database;

public PublishController(DatabaseContext database)

Check notice on line 31 in ProjectLighthouse.Servers.GameServer/Controllers/Slots/PublishController.cs

View workflow job for this annotation

GitHub Actions / Qodana for .NET

Convert constructor into primary constructor

Convert into primary constructor
{
this.database = database;
}
Expand All @@ -40,7 +40,7 @@
public async Task<IActionResult> StartPublish()
{
GameTokenEntity token = this.GetToken();

UserEntity? user = await this.database.UserFromGameToken(token);
if (user == null) return this.Forbid();

Expand All @@ -64,7 +64,7 @@
return this.BadRequest();
}

if (slot.Resources?.Length == 0) slot.Resources = new[]{slot.RootLevel,};

Check notice on line 67 in ProjectLighthouse.Servers.GameServer/Controllers/Slots/PublishController.cs

View workflow job for this annotation

GitHub Actions / Qodana for .NET

Use collection expression syntax

Use collection expression

if (slot.Resources == null)
{
Expand Down Expand Up @@ -100,7 +100,7 @@
return this.Forbid();
}

HashSet<string> resources = new(slot.Resources)

Check notice on line 103 in ProjectLighthouse.Servers.GameServer/Controllers/Slots/PublishController.cs

View workflow job for this annotation

GitHub Actions / Qodana for .NET

Use collection expression syntax

Use collection expression
{
slot.IconHash,
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,14 +29,14 @@
{
private readonly DatabaseContext database;

public UserController(DatabaseContext database)

Check notice on line 32 in ProjectLighthouse.Servers.GameServer/Controllers/UserController.cs

View workflow job for this annotation

GitHub Actions / Qodana for .NET

Convert constructor into primary constructor

Convert into primary constructor
{
this.database = database;
}

[HttpGet("user/{username}")]
public async Task<IActionResult> GetUser(string username)
{
{
UserEntity? user = await this.database.Users.FirstOrDefaultAsync(u => u.Username == username);
if (user == null) return this.NotFound();

Expand All @@ -46,7 +46,7 @@
[HttpGet("users")]
public async Task<IActionResult> GetUserAlt([FromQuery(Name = "u")] string[] userList)
{
List<MinimalUserProfile> minimalUserList = new();

Check notice on line 49 in ProjectLighthouse.Servers.GameServer/Controllers/UserController.cs

View workflow job for this annotation

GitHub Actions / Qodana for .NET

Use collection expression syntax

Use collection expression
foreach (string username in userList)
{
MinimalUserProfile? profile = await this.database.Users.Where(u => u.Username == username)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
using LBPUnion.ProjectLighthouse.Configuration;
using LBPUnion.ProjectLighthouse.Database;
using LBPUnion.ProjectLighthouse.Middlewares;
using LBPUnion.ProjectLighthouse.Types.Entities.Profile;
using LBPUnion.ProjectLighthouse.Types.Entities.Token;

namespace LBPUnion.ProjectLighthouse.Servers.GameServer.Middlewares;

public class EmailEnforcementMiddleware : MiddlewareDBContext
{
private static readonly HashSet<string> enforcedPaths = ServerConfiguration.Instance.EmailEnforcement.BlockedEndpoints;

public EmailEnforcementMiddleware(RequestDelegate next) : base(next)

Check notice on line 13 in ProjectLighthouse.Servers.GameServer/Middlewares/EmailEnforcementMiddleware.cs

View workflow job for this annotation

GitHub Actions / Qodana for .NET

Convert constructor into primary constructor

Convert into primary constructor
{ }

public override async Task InvokeAsync(HttpContext context, DatabaseContext database)
{
if (ServerConfiguration.Instance.EmailEnforcement.EnableEmailEnforcement)
{
// Split path into segments
string[] pathSegments = context.Request.Path.ToString().Split("/", StringSplitOptions.RemoveEmptyEntries);

if (pathSegments[0] == "LITTLEBIGPLANETPS3_XML")
{
// Get user via GameToken
GameTokenEntity? token = await database.GameTokenFromRequest(context.Request);
UserEntity? user = await database.UserFromGameToken(token);

// Check second part of path to see if client is within an enforced path
// This could probably be reworked, seeing as you may want to check for a deeper sub-path
// But it should be perfectly fine for now
if (enforcedPaths.Contains(pathSegments[1]))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't look like it will handle arguments in the path. Take /LITTLEBIGPLANETPS3_XML/users?u=test1,test2,test3 for example, the second path segment will be users?u=test1,test2,test3 which won't match users.

{
// Check if user is valid, don't want any exceptions
if (user == null)
{
// Send bad request status
context.Response.StatusCode = StatusCodes.Status403Forbidden;
await context.Response.WriteAsync("Not a valid user");

// Don't go to next in pipeline
return;
}

// Check if email is there and verified
if (!user.EmailAddressVerified || user.EmailAddress == null)
{
// Send bad request status
context.Response.StatusCode = StatusCodes.Status400BadRequest;
await context.Response.WriteAsync("Invalid user email address");

// Don't go to next in pipeline
return;
}
}
}
}

// Go to next in pipeline
await this.next(context);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@

public class GameServerStartup
{
public GameServerStartup(IConfiguration configuration)

Check notice on line 22 in ProjectLighthouse.Servers.GameServer/Startup/GameServerStartup.cs

View workflow job for this annotation

GitHub Actions / Qodana for .NET

Convert constructor into primary constructor

Convert into primary constructor
{
this.Configuration = configuration;
}
Expand Down Expand Up @@ -107,6 +107,7 @@
app.UseMiddleware<RateLimitMiddleware>();
app.UseMiddleware<DigestMiddleware>(computeDigests);
app.UseMiddleware<SetLastContactMiddleware>();
app.UseMiddleware<EmailEnforcementMiddleware>();

app.UseRouting();

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#nullable enable

Check warning on line 1 in ProjectLighthouse.Servers.Website/Pages/Email/SetEmailForm.cshtml.cs

View workflow job for this annotation

GitHub Actions / Qodana for .NET

Redundant nullable directive

Redundant nullable directive
using System.Diagnostics.CodeAnalysis;
using LBPUnion.ProjectLighthouse.Configuration;
using LBPUnion.ProjectLighthouse.Database;
Expand All @@ -14,7 +14,7 @@

public class SetEmailForm : BaseLayout
{
public SetEmailForm(DatabaseContext database) : base(database)

Check notice on line 17 in ProjectLighthouse.Servers.Website/Pages/Email/SetEmailForm.cshtml.cs

View workflow job for this annotation

GitHub Actions / Qodana for .NET

Convert constructor into primary constructor

Convert into primary constructor
{}

public string? Error { get; private set; }
Expand All @@ -39,13 +39,13 @@
UserEntity? user = await this.Database.Users.FirstOrDefaultAsync(u => u.UserId == token.UserId);
if (user == null) return this.Redirect("~/login");

if (!SanitizationHelper.IsValidEmail(emailAddress))
if (!SMTPHelper.IsValidEmail(this.Database, emailAddress))
{
this.Error = this.Translate(ErrorStrings.EmailInvalid);
return this.Page();
}

if (await this.Database.Users.AnyAsync(u => u.EmailAddress != null && u.EmailAddress.ToLower() == emailAddress.ToLower()))

Check notice on line 48 in ProjectLighthouse.Servers.Website/Pages/Email/SetEmailForm.cshtml.cs

View workflow job for this annotation

GitHub Actions / Qodana for .NET

RoslynAnalyzers Use the 'StringComparison' method overloads to perform case-insensitive string comparisons

Prefer using 'string.Equals(string, StringComparison)' to perform a case-insensitive comparison, but keep in mind that this might cause subtle changes in behavior, so make sure to conduct thorough testing after applying the suggestion, or if culturally sensitive comparison is not required, consider using 'StringComparison.OrdinalIgnoreCase'
{
this.Error = this.Translate(ErrorStrings.EmailTaken);
return this.Page();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@

public string? Status { get; private set; }

public PasswordResetRequestForm(DatabaseContext database, IMailService mail) : base(database)

Check notice on line 21 in ProjectLighthouse.Servers.Website/Pages/Login/PasswordResetRequestForm.cshtml.cs

View workflow job for this annotation

GitHub Actions / Qodana for .NET

Convert constructor into primary constructor

Convert into primary constructor
{
this.Mail = mail;
}
Expand All @@ -38,9 +38,9 @@
return this.Page();
}

if (!SanitizationHelper.IsValidEmail(email))
if (!SMTPHelper.IsValidEmail(this.Database, email))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It might be worth refactoring the email syntax checking back out so that you can give the user proper feedback for why the email is invalid. Otherwise, people may be confused when their email is "invalid" when in reality their domain has been blacklisted.

{
this.Error = "This email is in an invalid format";
this.Error = "This email is invalid";
return this.Page();
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#nullable enable

Check warning on line 1 in ProjectLighthouse.Servers.Website/Pages/UserSettingsPage.cshtml.cs

View workflow job for this annotation

GitHub Actions / Qodana for .NET

Redundant nullable directive

Redundant nullable directive
using System.Diagnostics.CodeAnalysis;
using LBPUnion.ProjectLighthouse.Configuration;
using LBPUnion.ProjectLighthouse.Database;
Expand All @@ -17,7 +17,7 @@
{

public UserEntity? ProfileUser;
public UserSettingsPage(DatabaseContext database) : base(database)

Check notice on line 20 in ProjectLighthouse.Servers.Website/Pages/UserSettingsPage.cshtml.cs

View workflow job for this annotation

GitHub Actions / Qodana for .NET

Convert constructor into primary constructor

Convert into primary constructor
{}

[SuppressMessage("ReSharper", "SpecifyStringComparison")]
Expand Down Expand Up @@ -64,18 +64,14 @@
}
}

if (ServerConfiguration.Instance.Mail.MailEnabled &&
SanitizationHelper.IsValidEmail(email) &&
if (ServerConfiguration.Instance.Mail.MailEnabled &&
email != null && SMTPHelper.IsValidEmail(this.Database, email) &&
(this.User == this.ProfileUser || this.User.IsAdmin))
{
// if email hasn't already been used
if (!await this.Database.Users.AnyAsync(u => u.EmailAddress != null && u.EmailAddress.ToLower() == email!.ToLower()))
if (this.ProfileUser.EmailAddress != email)
{
if (this.ProfileUser.EmailAddress != email)
{
this.ProfileUser.EmailAddress = email;
this.ProfileUser.EmailAddressVerified = false;
}
this.ProfileUser.EmailAddress = email;
this.ProfileUser.EmailAddressVerified = false;
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@
messageController.SetupTestController(request);

CensorConfiguration.Instance.UserInputFilterMode = FilterMode.Asterisks;
CensorConfiguration.Instance.FilteredWordList = new List<string>

Check notice on line 139 in ProjectLighthouse.Tests.GameApiTests/Unit/Controllers/MessageControllerTests.cs

View workflow job for this annotation

GitHub Actions / Qodana for .NET

Use collection expression syntax

Use collection expression
{
"bruh",
};
Expand Down Expand Up @@ -167,7 +167,7 @@
messageController.SetupTestController(request);

ServerConfiguration.Instance.Mail.MailEnabled = false;
CensorConfiguration.Instance.FilteredWordList = new List<string>();

Check notice on line 170 in ProjectLighthouse.Tests.GameApiTests/Unit/Controllers/MessageControllerTests.cs

View workflow job for this annotation

GitHub Actions / Qodana for .NET

Use collection expression syntax

Use collection expression

const string expected = "/setemail unittest@unittest.com";

Expand Down Expand Up @@ -203,7 +203,7 @@
[Fact]
public async Task Filter_ShouldNotSendEmail_WhenMailEnabled_AndEmailTaken()
{
List<UserEntity> users = new()

Check notice on line 206 in ProjectLighthouse.Tests.GameApiTests/Unit/Controllers/MessageControllerTests.cs

View workflow job for this annotation

GitHub Actions / Qodana for .NET

Use collection expression syntax

Use collection expression
{
new UserEntity
{
Expand All @@ -224,7 +224,7 @@

IActionResult result = await messageController.Filter(mailMock.Object);

Assert.IsType<OkResult>(result);
Assert.IsType<BadRequestResult>(result);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See comment on MessageController about whether this has side effects on functionality. If it does, this should be reverted.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It doesn't seem to negatively affect the client in any unintended way. Any time it tries to access an endpoint that is blocked, the game complains that it is having trouble connecting to the servers.

mailMock.Verify(x => x.SendEmailAsync(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>()), Times.Never);
}

Expand All @@ -249,7 +249,7 @@

IActionResult result = await messageController.Filter(mailMock.Object);

Assert.IsType<OkResult>(result);
Assert.IsType<BadRequestResult>(result);
mailMock.Verify(x => x.SendEmailAsync(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>()), Times.Never);
}

Expand All @@ -271,7 +271,7 @@

IActionResult result = await messageController.Filter(mailMock.Object);

Assert.IsType<OkResult>(result);
Assert.IsType<BadRequestResult>(result);
mailMock.Verify(x => x.SendEmailAsync(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>()), Times.Never);
}
}
Loading
Loading