Created first time setup to create initial admin account
parent
a0d7134af3
commit
5bcf8d3e3c
|
@ -0,0 +1,52 @@
|
||||||
|
@page
|
||||||
|
@model FirstTimeSetupModel
|
||||||
|
@{
|
||||||
|
Layout = "/Views/Shared/_LayoutBasic.cshtml";
|
||||||
|
}
|
||||||
|
@{
|
||||||
|
ViewData["Title"] = "First Time Setup";
|
||||||
|
}
|
||||||
|
|
||||||
|
<div class="page page-center">
|
||||||
|
<form asp-route-returnUrl="@Model.ReturnUrl" class="container-tight py-4">
|
||||||
|
<div class="text-center mb-4">
|
||||||
|
<h2>LANCommander</h2>
|
||||||
|
</div>
|
||||||
|
<div class="card card-md">
|
||||||
|
<div class="card-body text-center py-4 p-sm-5">
|
||||||
|
<h1>Welcome to LANCommander!</h1>
|
||||||
|
<p class="text-muted">LANCommander is your one stop shop for distributing games on your LAN. Start your adventure with LANCommander and take control of your local multiplayer gaming!</p>
|
||||||
|
</div>
|
||||||
|
<div class="hr-text hr-text-center hr-text-spaceless">registration</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="mb-3">
|
||||||
|
<label class="form-label">Register your admin account</label>
|
||||||
|
<input asp-for="Input.UserName" type="text" class="form-control ps-1" autocomplete="off" placeholder="Username" />
|
||||||
|
<div class="form-hint">For first-time setup, an admin user is required. This user will be able to manage all aspects of the application.</div>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label asp-for="Input.Password" class="form-label"></label>
|
||||||
|
<input asp-for="Input.Password" type="password" class="form-control ps-1" autocomplete="new-password" />
|
||||||
|
<span asp-validation-for="Input.ConfirmPassword" class="text-danger"></span>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label asp-for="Input.ConfirmPassword" class="form-label"></label>
|
||||||
|
<input asp-for="Input.ConfirmPassword" type="password" class="form-control ps-1" autocomplete="new-password" />
|
||||||
|
<span asp-validation-for="Input.ConfirmPassword" class="text-danger"></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row align-items-center mt-3">
|
||||||
|
<div class="col">
|
||||||
|
<div class="btn-list justify-content-end">
|
||||||
|
<button type="submit" class="btn btn-primary">Continue</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@section Scripts {
|
||||||
|
<partial name="_ValidationScriptsPartial" />
|
||||||
|
}
|
|
@ -0,0 +1,184 @@
|
||||||
|
// Licensed to the .NET Foundation under one or more agreements.
|
||||||
|
// The .NET Foundation licenses this file to you under the MIT license.
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Text.Encodings.Web;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNetCore.Authentication;
|
||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
|
using LANCommander.Data.Models;
|
||||||
|
using Microsoft.AspNetCore.Identity;
|
||||||
|
using Microsoft.AspNetCore.Identity.UI.Services;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||||
|
using Microsoft.AspNetCore.WebUtilities;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
|
namespace LANCommander.Areas.Identity.Pages.Account
|
||||||
|
{
|
||||||
|
public class FirstTimeSetupModel : PageModel
|
||||||
|
{
|
||||||
|
private readonly SignInManager<User> _signInManager;
|
||||||
|
private readonly UserManager<User> _userManager;
|
||||||
|
private readonly RoleManager<Role> _roleManager;
|
||||||
|
private readonly IUserStore<User> _userStore;
|
||||||
|
private readonly IUserEmailStore<User> _emailStore;
|
||||||
|
private readonly ILogger<FirstTimeSetupModel> _logger;
|
||||||
|
private readonly IEmailSender _emailSender;
|
||||||
|
|
||||||
|
public FirstTimeSetupModel(
|
||||||
|
UserManager<User> userManager,
|
||||||
|
IUserStore<User> userStore,
|
||||||
|
SignInManager<User> signInManager,
|
||||||
|
RoleManager<Role> roleManager,
|
||||||
|
ILogger<FirstTimeSetupModel> logger,
|
||||||
|
IEmailSender emailSender)
|
||||||
|
{
|
||||||
|
_userManager = userManager;
|
||||||
|
_userStore = userStore;
|
||||||
|
_signInManager = signInManager;
|
||||||
|
_roleManager = roleManager;
|
||||||
|
_logger = logger;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
|
||||||
|
/// directly from your code. This API may change or be removed in future releases.
|
||||||
|
/// </summary>
|
||||||
|
[BindProperty]
|
||||||
|
public InputModel Input { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
|
||||||
|
/// directly from your code. This API may change or be removed in future releases.
|
||||||
|
/// </summary>
|
||||||
|
public string ReturnUrl { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
|
||||||
|
/// directly from your code. This API may change or be removed in future releases.
|
||||||
|
/// </summary>
|
||||||
|
public IList<AuthenticationScheme> ExternalLogins { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
|
||||||
|
/// directly from your code. This API may change or be removed in future releases.
|
||||||
|
/// </summary>
|
||||||
|
public class InputModel
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
|
||||||
|
/// directly from your code. This API may change or be removed in future releases.
|
||||||
|
/// </summary>
|
||||||
|
[Required]
|
||||||
|
[Display(Name = "Username")]
|
||||||
|
public string UserName { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
|
||||||
|
/// directly from your code. This API may change or be removed in future releases.
|
||||||
|
/// </summary>
|
||||||
|
[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; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
|
||||||
|
/// directly from your code. This API may change or be removed in future releases.
|
||||||
|
/// </summary>
|
||||||
|
[DataType(DataType.Password)]
|
||||||
|
[Display(Name = "Confirm password")]
|
||||||
|
[Compare("Password", ErrorMessage = "The password and confirmation password do not match.")]
|
||||||
|
public string ConfirmPassword { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public async Task<IActionResult> OnGetAsync(string returnUrl = null)
|
||||||
|
{
|
||||||
|
ReturnUrl = returnUrl;
|
||||||
|
ExternalLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync()).ToList();
|
||||||
|
|
||||||
|
var administratorRoleExists = await _roleManager.RoleExistsAsync("Administor");
|
||||||
|
|
||||||
|
if (!administratorRoleExists)
|
||||||
|
await _roleManager.CreateAsync(new Role()
|
||||||
|
{
|
||||||
|
Name = "Administrator"
|
||||||
|
});
|
||||||
|
|
||||||
|
var administrators = await _userManager.GetUsersInRoleAsync("Administrator");
|
||||||
|
|
||||||
|
if (administrators.Count > 0)
|
||||||
|
return RedirectToPage("./Login");
|
||||||
|
|
||||||
|
return Page();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<IActionResult> OnPostAsync(string returnUrl = null)
|
||||||
|
{
|
||||||
|
returnUrl ??= Url.Content("~/");
|
||||||
|
ExternalLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync()).ToList();
|
||||||
|
|
||||||
|
var administrators = await _userManager.GetUsersInRoleAsync("Administrator");
|
||||||
|
|
||||||
|
if (administrators.Count > 0)
|
||||||
|
return RedirectToPage("./Login");
|
||||||
|
|
||||||
|
if (ModelState.IsValid)
|
||||||
|
{
|
||||||
|
var user = CreateUser();
|
||||||
|
|
||||||
|
await _userStore.SetUserNameAsync(user, Input.UserName, CancellationToken.None);
|
||||||
|
var result = await _userManager.CreateAsync(user, Input.Password);
|
||||||
|
|
||||||
|
await _userManager.AddToRoleAsync(user, "Administrator");
|
||||||
|
|
||||||
|
if (result.Succeeded)
|
||||||
|
{
|
||||||
|
_logger.LogInformation("Administrator created a new account with password.");
|
||||||
|
|
||||||
|
var userId = await _userManager.GetUserIdAsync(user);
|
||||||
|
var code = await _userManager.GenerateEmailConfirmationTokenAsync(user);
|
||||||
|
code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code));
|
||||||
|
var callbackUrl = Url.Page(
|
||||||
|
"/Account/ConfirmEmail",
|
||||||
|
pageHandler: null,
|
||||||
|
values: new { area = "Identity", userId = userId, code = code, returnUrl = returnUrl },
|
||||||
|
protocol: Request.Scheme);
|
||||||
|
|
||||||
|
await _signInManager.SignInAsync(user, isPersistent: false);
|
||||||
|
return LocalRedirect(returnUrl);
|
||||||
|
}
|
||||||
|
foreach (var error in result.Errors)
|
||||||
|
{
|
||||||
|
ModelState.AddModelError(string.Empty, error.Description);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we got this far, something failed, redisplay form
|
||||||
|
return Page();
|
||||||
|
}
|
||||||
|
|
||||||
|
private User CreateUser()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return Activator.CreateInstance<User>();
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException($"Can't create an instance of '{nameof(User)}'. " +
|
||||||
|
$"Ensure that '{nameof(User)}' is not an abstract class and has a parameterless constructor, or alternatively " +
|
||||||
|
$"override the register page in /Areas/Identity/Pages/Account/FirstTimeSetupModel.cshtml");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -21,11 +21,17 @@ namespace LANCommander.Areas.Identity.Pages.Account
|
||||||
public class LoginModel : PageModel
|
public class LoginModel : PageModel
|
||||||
{
|
{
|
||||||
private readonly SignInManager<User> _signInManager;
|
private readonly SignInManager<User> _signInManager;
|
||||||
|
private readonly UserManager<User> _userManager;
|
||||||
private readonly ILogger<LoginModel> _logger;
|
private readonly ILogger<LoginModel> _logger;
|
||||||
|
|
||||||
public LoginModel(SignInManager<User> signInManager, ILogger<LoginModel> logger)
|
public LoginModel(
|
||||||
|
SignInManager<User> signInManager,
|
||||||
|
UserManager<User> userManager,
|
||||||
|
ILogger<LoginModel> logger
|
||||||
|
)
|
||||||
{
|
{
|
||||||
_signInManager = signInManager;
|
_signInManager = signInManager;
|
||||||
|
_userManager = userManager;
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -86,7 +92,7 @@ namespace LANCommander.Areas.Identity.Pages.Account
|
||||||
public bool RememberMe { get; set; }
|
public bool RememberMe { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task OnGetAsync(string returnUrl = null)
|
public async Task<IActionResult> OnGetAsync(string returnUrl = null)
|
||||||
{
|
{
|
||||||
if (!string.IsNullOrEmpty(ErrorMessage))
|
if (!string.IsNullOrEmpty(ErrorMessage))
|
||||||
{
|
{
|
||||||
|
@ -101,6 +107,15 @@ namespace LANCommander.Areas.Identity.Pages.Account
|
||||||
ExternalLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync()).ToList();
|
ExternalLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync()).ToList();
|
||||||
|
|
||||||
ReturnUrl = returnUrl;
|
ReturnUrl = returnUrl;
|
||||||
|
|
||||||
|
var administrators = await _userManager.GetUsersInRoleAsync("Administrator");
|
||||||
|
|
||||||
|
if (administrators.Count == 0)
|
||||||
|
{
|
||||||
|
return RedirectToPage("./FirstTimeSetup");
|
||||||
|
}
|
||||||
|
|
||||||
|
return Page();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<IActionResult> OnPostAsync(string returnUrl = null)
|
public async Task<IActionResult> OnPostAsync(string returnUrl = null)
|
||||||
|
|
|
@ -5,7 +5,7 @@ using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
namespace LANCommander.Data
|
namespace LANCommander.Data
|
||||||
{
|
{
|
||||||
public class DatabaseContext : IdentityDbContext<User, IdentityRole<Guid>, Guid>
|
public class DatabaseContext : IdentityDbContext<User, Role, Guid>
|
||||||
{
|
{
|
||||||
public DatabaseContext(DbContextOptions<DatabaseContext> options)
|
public DatabaseContext(DbContextOptions<DatabaseContext> options)
|
||||||
: base(options)
|
: base(options)
|
||||||
|
|
|
@ -0,0 +1,10 @@
|
||||||
|
using Microsoft.AspNetCore.Identity;
|
||||||
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
|
|
||||||
|
namespace LANCommander.Data.Models
|
||||||
|
{
|
||||||
|
[Table("Roles")]
|
||||||
|
public class Role : IdentityRole<Guid>
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
|
@ -21,7 +21,9 @@ builder.Services.AddDefaultIdentity<User>((IdentityOptions options) => {
|
||||||
options.SignIn.RequireConfirmedAccount = false;
|
options.SignIn.RequireConfirmedAccount = false;
|
||||||
options.Password.RequireNonAlphanumeric = false;
|
options.Password.RequireNonAlphanumeric = false;
|
||||||
options.SignIn.RequireConfirmedEmail = false;
|
options.SignIn.RequireConfirmedEmail = false;
|
||||||
}).AddEntityFrameworkStores<LANCommander.Data.DatabaseContext>();
|
})
|
||||||
|
.AddRoles<Role>()
|
||||||
|
.AddEntityFrameworkStores<LANCommander.Data.DatabaseContext>();
|
||||||
|
|
||||||
builder.Services.AddControllersWithViews();
|
builder.Services.AddControllersWithViews();
|
||||||
|
|
||||||
|
@ -57,4 +59,4 @@ app.MapRazorPages();
|
||||||
if (!Directory.Exists("Upload"))
|
if (!Directory.Exists("Upload"))
|
||||||
Directory.CreateDirectory("Upload");
|
Directory.CreateDirectory("Upload");
|
||||||
|
|
||||||
app.Run();
|
app.Run();
|
Loading…
Reference in New Issue