Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Require email and password change checkboxes. #24

Draft
wants to merge 2 commits into
base: master
Choose a base branch
from
Draft
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
1,698 changes: 1,698 additions & 0 deletions SS14.Auth.Shared/Data/Migrations/20240625192927_email-pass-required.Designer.cs

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
using Microsoft.EntityFrameworkCore.Migrations;

#nullable disable

namespace SS14.Auth.Shared.Data.Migrations
{
public partial class emailpassrequired : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<bool>(
name: "RequireEmailChange",
table: "AspNetUsers",
type: "boolean",
nullable: false,
defaultValue: false);

migrationBuilder.AddColumn<bool>(
name: "RequirePasswordChange",
table: "AspNetUsers",
type: "boolean",
nullable: false,
defaultValue: false);
}

protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "RequireEmailChange",
table: "AspNetUsers");

migrationBuilder.DropColumn(
name: "RequirePasswordChange",
table: "AspNetUsers");
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -1249,6 +1249,12 @@ protected override void BuildModel(ModelBuilder modelBuilder)
b.Property<string>("PasswordHash")
.HasColumnType("text");

b.Property<bool>("RequireEmailChange")
.HasColumnType("boolean");

b.Property<bool>("RequirePasswordChange")
.HasColumnType("boolean");

b.Property<string>("SecurityStamp")
.HasColumnType("text");

Expand Down
8 changes: 7 additions & 1 deletion SS14.Auth.Shared/Data/SpaceSignInManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,12 @@ protected override async Task<SignInResult> PreSignInCheck(SpaceUser user)
if (user.AdminLocked)
return SpaceSignInResult.AdminLocked;

if (user.RequireEmailChange)
return SpaceSignInResult.RequireEmailChange;

if (user.RequirePasswordChange)
return SpaceSignInResult.RequirePasswordChange;

return await base.PreSignInCheck(user);
}
}
}
6 changes: 5 additions & 1 deletion SS14.Auth.Shared/Data/SpaceSignInResult.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ namespace SS14.Auth.Shared.Data;
public sealed class SpaceSignInResult : SignInResult
{
public bool IsAdminLocked { get; private set; }
public bool EmailChangeRequired { get; private set; }
public bool PasswordChangeRequired { get; private set; }

public static readonly SpaceSignInResult AdminLocked = new() { IsAdminLocked = true };
}
public static readonly SpaceSignInResult RequireEmailChange = new() { EmailChangeRequired = true };
public static readonly SpaceSignInResult RequirePasswordChange = new() { PasswordChangeRequired = true };
}
10 changes: 10 additions & 0 deletions SS14.Auth.Shared/Data/SpaceUser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,16 @@ public class SpaceUser : IdentityUser<Guid>
/// </summary>
public bool AdminLocked { get; set; }

/// <summary>
/// Account requires an email change before being accessible again.
/// </summary>
public bool RequireEmailChange { get; set; }

/// <summary>
/// Account requires a password change before being accessible again.
/// </summary>
public bool RequirePasswordChange { get; set; }

/// <summary>
/// Note set by hub administrator.
/// </summary>
Expand Down
28 changes: 22 additions & 6 deletions SS14.Auth/Controllers/AuthApiController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,20 @@ public async Task<IActionResult> Authenticate(AuthenticateRequest request)
AuthenticateDenyResponseCode.AccountLocked));
}

if (signInResult is SpaceSignInResult { EmailChangeRequired: true })
{
return Unauthorized(new AuthenticateDenyResponse(
new[] { "Account requires a new email." },
AuthenticateDenyResponseCode.EmailChangeNeeded));
}

if (signInResult is SpaceSignInResult { PasswordChangeRequired: true })
{
return Unauthorized(new AuthenticateDenyResponse(
new[] { "Account requires a new password." },
AuthenticateDenyResponseCode.PasswdChangeNeeded));
}

if (!signInResult.Succeeded)
{
return Unauthorized(new AuthenticateDenyResponse(
Expand All @@ -113,22 +127,22 @@ public async Task<IActionResult> Authenticate(AuthenticateRequest request)
new[] { "" },
AuthenticateDenyResponseCode.TfaRequired));
}

var verify = await _userManager.VerifyTwoFactorTokenAsync(
user,
user,
_userManager.Options.Tokens.AuthenticatorTokenProvider,
request.TfaCode);

if (!verify)
{
return Unauthorized(new AuthenticateDenyResponse(
new[] { "" },
AuthenticateDenyResponseCode.TfaInvalid));
}

// 2FA passed, we're good.
}

var (token, expireTime) =
await _sessionManager.RegisterNewSession(user, SessionManager.DefaultExpireTime);

Expand Down Expand Up @@ -278,6 +292,8 @@ public enum AuthenticateDenyResponseCode
TfaRequired = 3,
TfaInvalid = 4,
AccountLocked = 5,
EmailChangeNeeded = 6,
PasswdChangeNeeded = 7,
// @formatter:on
}

Expand Down Expand Up @@ -317,4 +333,4 @@ public enum RegisterResponseStatus
{
Registered,
RegisteredNeedConfirmation
}
}
18 changes: 18 additions & 0 deletions SS14.Web/Areas/Admin/Pages/Users/ViewUser.cshtml
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,24 @@
</div>
</div>

<div class="form-group row">
<div class="col-sm-10 offset-sm-2">
<div class="form-check">
<input asp-for="Input.RequireEmailChange" class="form-check-input"/>
<label asp-for="Input.RequireEmailChange" class="form-check-label"></label>
</div>
</div>
</div>

<div class="form-group row">
<div class="col-sm-10 offset-sm-2">
<div class="form-check">
<input asp-for="Input.RequirePasswordChange" class="form-check-input"/>
<label asp-for="Input.RequirePasswordChange" class="form-check-label"></label>
</div>
</div>
</div>

<div class="form-group row">
<label asp-for="Input.AdminNotes" class="col-sm-2 col-form-label"></label>
<textarea asp-for="Input.AdminNotes" class="form-control col-sm-10"></textarea>
Expand Down
20 changes: 20 additions & 0 deletions SS14.Web/Areas/Admin/Pages/Users/ViewUser.cshtml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,12 @@ public class InputModel
[Display(Name = "Locked?")]
public bool AdminLocked { get; set; }

[Display(Name = "Require Email change on next login?")]
public bool RequireEmailChange { get; set; }

[Display(Name = "Require Password change on next login?")]
public bool RequirePasswordChange { get; set; }

[Display(Name = "Administrative notes")]
public string AdminNotes { get; set; }
}
Expand Down Expand Up @@ -141,6 +147,18 @@ public async Task<IActionResult> OnPostSaveAsync(Guid id)
SpaceUser.AdminLocked = Input.AdminLocked;
}

if (SpaceUser.RequireEmailChange != Input.RequireEmailChange)
{
await _accountLogManager.Log(SpaceUser, new AccountLogAdminLockedChanged(Input.RequireEmailChange));
SpaceUser.RequireEmailChange = Input.RequireEmailChange;
}

if (SpaceUser.RequirePasswordChange != Input.RequirePasswordChange)
{
await _accountLogManager.Log(SpaceUser, new AccountLogAdminLockedChanged(Input.RequirePasswordChange));
SpaceUser.RequirePasswordChange = Input.RequirePasswordChange;
}

await CheckRole(Input.HubAdmin, AuthConstants.RoleSysAdmin);
await CheckRole(Input.ServerHubAdmin, AuthConstants.RoleServerHubAdmin);

Expand Down Expand Up @@ -237,6 +255,8 @@ private async Task LoadAsync()
ServerHubAdmin = await _userManager.IsInRoleAsync(SpaceUser, AuthConstants.RoleServerHubAdmin),
TfaEnabled = SpaceUser.TwoFactorEnabled,
AdminLocked = SpaceUser.AdminLocked,
RequireEmailChange = SpaceUser.RequireEmailChange,
RequirePasswordChange = SpaceUser.RequirePasswordChange,
AdminNotes = SpaceUser.AdminNotes
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,10 +64,16 @@ await _accountLogManager.LogAndSave(
new AccountLogEmailChanged(oldEmail, email),
_accountLogManager.ActorWithIP(user));

if (user.RequireEmailChange)
{
user.RequireEmailChange = false;
await _userManager.UpdateAsync(user);
}

await tx.CommitAsync();

await _signInManager.RefreshSignInAsync(user);
StatusMessage = "Thank you for confirming your email change.";
return Page();
}
}
}
11 changes: 11 additions & 0 deletions SS14.Web/Areas/Identity/Pages/Account/EmailChangeRequired.cshtml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
@page
@using SS14.Web.Areas.Identity.Pages.Account
@model LockoutModel
@{
ViewData["Title"] = "Email change required";
}

<header>
<h1 class="text-danger">@ViewData["Title"]</h1>
<p class="text-danger">This account needs a new email to be set before you can use it again. Please change it below.</p>
</header>
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc.RazorPages;

namespace SS14.Web.Areas.Identity.Pages.Account;

[AllowAnonymous]
public class ForcedEmailChangeModel : PageModel
{
public void OnGet()
{

}
}
6 changes: 1 addition & 5 deletions SS14.Web/Areas/Identity/Pages/Account/Lockout.cshtml.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,3 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc.RazorPages;

Expand All @@ -14,4 +10,4 @@ public void OnGet()
{

}
}
}
16 changes: 14 additions & 2 deletions SS14.Web/Areas/Identity/Pages/Account/Login.cshtml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ public async Task OnGetAsync(string returnUrl = null)

ExternalLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync()).ToList();

ReturnUrl = returnUrl;
ReturnUrl = returnUrl;
}

public async Task<IActionResult> OnPostAsync(string returnUrl = null)
Expand Down Expand Up @@ -106,6 +106,18 @@ public async Task<IActionResult> OnPostAsync(string returnUrl = null)
return RedirectToPage("./AdminLocked");
}

if (result is SpaceSignInResult { EmailChangeRequired: true })
{
_logger.LogWarning("User account needs an email change.");
return RedirectToPage("./EmailChangeRequired");
}

if (result is SpaceSignInResult { PasswordChangeRequired: true })
{
_logger.LogWarning("User account needs a password change.");
return RedirectToPage("./PasswordChangeRequired");
}

if (result.RequiresTwoFactor)
{
return RedirectToPage("./LoginWith2fa", new {ReturnUrl = returnUrl, RememberMe = Input.RememberMe});
Expand All @@ -123,4 +135,4 @@ public async Task<IActionResult> OnPostAsync(string returnUrl = null)

// If we got this far, something failed, redisplay form
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,11 @@ public async Task<IActionResult> OnPostAsync()
await _logManager.LogAndSave(user, new AccountLogPasswordChanged());
await _sessionManager.InvalidateSessions(user);
await _signInManager.RefreshSignInAsync(user);
if (user.RequirePasswordChange)
{
user.RequirePasswordChange = false;
await _userManager.UpdateAsync(user);
}
_logger.LogInformation("User changed their password successfully.");
StatusMessage = "Your password has been changed.";

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
@page
@using SS14.Web.Areas.Identity.Pages.Account
@model LockoutModel
@{
ViewData["Title"] = "Password change required";
}

<header>
<h1 class="text-danger">@ViewData["Title"]</h1>
<p class="text-danger">This account needs a new password to be set before you can use it again. Please change it below.</p>
</header>
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc.RazorPages;

namespace SS14.Web.Areas.Identity.Pages.Account;

[AllowAnonymous]
public class ForcedPassChangeModel : PageModel
{
public void OnGet()
{

}
}
13 changes: 9 additions & 4 deletions SS14.Web/Areas/Identity/Pages/Account/ResetPassword.cshtml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ public async Task<IActionResult> OnPostAsync()
}

await using var tx = await _dbContext.Database.BeginTransactionAsync();

var user = await _userManager.FindByEmailAsync(Input.Email);
if (user == null)
{
Expand All @@ -85,11 +85,16 @@ public async Task<IActionResult> OnPostAsync()

if (result.Succeeded)
{
if (user.RequirePasswordChange)
{
user.RequirePasswordChange = false;
await _userManager.UpdateAsync(user);
}
await _logManager.LogAndSave(user, new AccountLogPasswordChanged(), _logManager.ActorWithIP(user));
}

await tx.CommitAsync();

if (result.Succeeded)
{
return RedirectToPage("./ResetPasswordConfirmation");
Expand All @@ -101,4 +106,4 @@ public async Task<IActionResult> OnPostAsync()
}
return Page();
}
}
}
Loading
Loading