From 2dba0374eacace5a5b00413bc684a22fd451378a Mon Sep 17 00:00:00 2001 From: SimonFlapse Date: Mon, 1 Jun 2020 15:15:53 +0200 Subject: [PATCH 1/5] Added create account page Added new WebAccountManager service to handle all account actions across the upcoming account pages --- .../Pages/Admin/CreateAccount.cshtml | 64 +++++++++++ .../Pages/Admin/CreateAccount.cshtml.cs | 106 ++++++++++++++++++ .../Services/WebAccountManager.cs | 97 ++++++++++++++++ FactorioWebInterface/Startup.cs | 1 + 4 files changed, 268 insertions(+) create mode 100644 FactorioWebInterface/Pages/Admin/CreateAccount.cshtml create mode 100644 FactorioWebInterface/Pages/Admin/CreateAccount.cshtml.cs create mode 100644 FactorioWebInterface/Services/WebAccountManager.cs diff --git a/FactorioWebInterface/Pages/Admin/CreateAccount.cshtml b/FactorioWebInterface/Pages/Admin/CreateAccount.cshtml new file mode 100644 index 00000000..61ef7ced --- /dev/null +++ b/FactorioWebInterface/Pages/Admin/CreateAccount.cshtml @@ -0,0 +1,64 @@ +@page +@model FactorioWebInterface.Pages.Admin.CreateAccountModel +@{ + ViewData["Title"] = "Create New Account"; +} + +@section Scripts { + @await Html.PartialAsync("_ValidationScriptsPartial") +} + +
+ +

Create New Account

+ +
+
+
+

Create User

+
+
+
+ +
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+ @foreach (var item in Model.Roles) + { + @item + } +
+ +
+ +
+ + @if (Model.AccountCreated) + { +
+

Account created

+ } +
+
+
+ + diff --git a/FactorioWebInterface/Pages/Admin/CreateAccount.cshtml.cs b/FactorioWebInterface/Pages/Admin/CreateAccount.cshtml.cs new file mode 100644 index 00000000..59d055a1 --- /dev/null +++ b/FactorioWebInterface/Pages/Admin/CreateAccount.cshtml.cs @@ -0,0 +1,106 @@ +using FactorioWebInterface.Data; +using FactorioWebInterface.Services; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Identity; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.RazorPages; +using Microsoft.Extensions.Logging; +using System.ComponentModel.DataAnnotations; +using System.Linq; +using System.Threading.Tasks; + +namespace FactorioWebInterface.Pages.Admin +{ + [Authorize(Roles = Constants.RootRole)] + public class CreateAccountModel : PageModel + { + private readonly SignInManager _signInManager; + private readonly IWebAccountManager _accountManager; + private readonly ILogger _logger; + + public CreateAccountModel( + IWebAccountManager accountManager, + SignInManager signInManager, + ILogger logger + ) + { + _accountManager = accountManager; + _signInManager = signInManager; + _logger = logger; + } + + public bool AccountCreated { get; set; } + + [BindProperty] + public InputModel Input { get; set; } = default!; + + public string[] Roles { get; set; } = { Constants.AdminRole, Constants.RootRole }; + + public class InputModel + { + [Required] + [DataType(DataType.Text)] + [Display(Name = "Username")] + public string UserName { get; set; } = default!; + + [Required] + [StringLength(100, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)] + [DataType(DataType.Password)] + [Display(Name = "Password")] + public string Password { get; set; } = default!; + + [DataType(DataType.Password)] + [Display(Name = "Confirm password")] + [Compare("Password", ErrorMessage = "The password and confirmation password do not match.")] + public string ConfirmPassword { get; set; } = default!; + + [Display(Name = "Select role")] + public string Role { get; set; } = Constants.AdminRole; + } + + public async Task OnGetAsync(bool accountCreated) + { + var user = await _accountManager.GetUserAsync(User); + + if (user == null || user.Suspended) + { + HttpContext.Session.SetString("returnUrl", "account"); + return RedirectToPage("signIn"); + } + + AccountCreated = accountCreated; + + return Page(); + } + + public async Task OnPostCreateAccountAsync() + { + var user = await _accountManager.GetUserAsync(User); + + if (user == null || user.Suspended) + { + HttpContext.Session.SetString("returnUrl", "account"); + return RedirectToPage("signIn"); + } + + await _accountManager.CreateAccountAsync(Input.UserName, Input.Password, new string[]{Input.Role}); + + /*if (!result.Succeeded) + { + foreach (var error in result.Errors) + { + ModelState.AddModelError(string.Empty, error.Description); + } + + return Page(); + }*/ + + //await _signInManager.SignInAsync(user, isPersistent: false); + + //_logger.LogInformation($"User {user.UserName} created password"); + + return RedirectToPage(new { AccountCreated = true }); + } + } +} \ No newline at end of file diff --git a/FactorioWebInterface/Services/WebAccountManager.cs b/FactorioWebInterface/Services/WebAccountManager.cs new file mode 100644 index 00000000..ca2e7fd0 --- /dev/null +++ b/FactorioWebInterface/Services/WebAccountManager.cs @@ -0,0 +1,97 @@ +using FactorioWebInterface.Data; +using Microsoft.AspNetCore.Identity; +using Microsoft.Extensions.Logging; +using System; +using System.Security.Claims; +using System.Threading.Tasks; + +namespace FactorioWebInterface.Services +{ + public interface IWebAccountManager + { + Task FindByNameAsync(string username); + Task FindByIdAsync(string id); + Task GetUserAsync(ClaimsPrincipal user); + Task CreateAccountAsync(string username, string password, string[] roles); + Task ChangePasswordAsync(ApplicationUser user, string oldPassword, string newPassword); + Task AddRoleAsync(ApplicationUser user, string role); + Task RemoveRoleAsync(ApplicationUser user, string role); + Task SuspendAccountAsync(ApplicationUser user, bool suspended = true); + } + + public class WebAccountManager : IWebAccountManager + { + private readonly SignInManager _signInManager; + private readonly UserManager _userManager; + private readonly ILogger _logger; + + public WebAccountManager(UserManager userManager, + SignInManager signInManager, + ILogger logger) + { + _signInManager = signInManager; + _userManager = userManager; + _logger = logger; + } + + public async Task AddRoleAsync(ApplicationUser user, string role) + { + await _userManager.AddToRoleAsync(user, role); + _logger.LogInformation("The user: " + user.UserName + " has been added to the role: " + role); + } + + public async Task ChangePasswordAsync(ApplicationUser user, string oldPassword, string newPassword) + { + await _userManager.ChangePasswordAsync(user, oldPassword, newPassword); + _logger.LogInformation("The user: " + user.UserName + " has changed their password"); + } + + public async Task CreateAccountAsync(string username, string password, string[] roles) + { + var id = Guid.NewGuid().ToString(); + var user = new ApplicationUser() + { + Id = id, + UserName = username + }; + + var result = await _userManager.CreateAsync(user, password); + if (!result.Succeeded) + { + _logger.LogError("User account couldn't be created"); + } + foreach (string role in roles) { + await AddRoleAsync(user, role); + } + _logger.LogInformation("User account created with username: " + username + " and id: " + id); + } + + public async Task FindByIdAsync(string id) + { + return await _userManager.FindByIdAsync(id); + } + + public async Task FindByNameAsync(string username) + { + return await _userManager.FindByIdAsync(username); + } + + public async Task GetUserAsync(ClaimsPrincipal user) + { + return await _userManager.GetUserAsync(user); + } + + public async Task RemoveRoleAsync(ApplicationUser user, string role) + { + await _userManager.RemoveFromRoleAsync(user, role); + _logger.LogInformation("The user: " + user.UserName + " has been removed from the role: " + role); + } + + public async Task SuspendAccountAsync(ApplicationUser user, bool suspended = true) + { + user.Suspended = suspended; + await _userManager.UpdateAsync(user); + _logger.LogInformation("The user: " + user.UserName + " has been suspended"); + } + } +} diff --git a/FactorioWebInterface/Startup.cs b/FactorioWebInterface/Startup.cs index 8bfc1932..3badd152 100644 --- a/FactorioWebInterface/Startup.cs +++ b/FactorioWebInterface/Startup.cs @@ -110,6 +110,7 @@ public void ConfigureServices(IServiceCollection services) services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); + services.AddScoped(); services.AddRouting(o => o.LowercaseUrls = true); From 97cd132d1343116b8ca4c81b3ce3eba10aff3667 Mon Sep 17 00:00:00 2001 From: SimonFlapse Date: Mon, 1 Jun 2020 15:25:00 +0200 Subject: [PATCH 2/5] Added navbar link to create account for root users --- .../Pages/Admin/_AdminNavBarPartial.cshtml | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/FactorioWebInterface/Pages/Admin/_AdminNavBarPartial.cshtml b/FactorioWebInterface/Pages/Admin/_AdminNavBarPartial.cshtml index 73fce7c1..658c9ff7 100644 --- a/FactorioWebInterface/Pages/Admin/_AdminNavBarPartial.cshtml +++ b/FactorioWebInterface/Pages/Admin/_AdminNavBarPartial.cshtml @@ -1,9 +1,9 @@ @using Microsoft.AspNetCore.Identity; @using FactorioWebInterface.Data; -@inject UserManager userManger; +@inject UserManager userManager; @{ - var user = await userManger.GetUserAsync(User); + var user = await userManager.GetUserAsync(User); }