diff --git a/.idea/.idea.TRPZ_PrintService/.idea/contentModel.xml b/.idea/.idea.TRPZ_PrintService/.idea/contentModel.xml index f0481eb..720f713 100644 --- a/.idea/.idea.TRPZ_PrintService/.idea/contentModel.xml +++ b/.idea/.idea.TRPZ_PrintService/.idea/contentModel.xml @@ -19,17 +19,81 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - @@ -48,6 +112,10 @@ + + + + @@ -61,10 +129,18 @@ + + + + + + + + @@ -84,6 +160,30 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/.idea/.idea.TRPZ_PrintService/.idea/workspace.xml b/.idea/.idea.TRPZ_PrintService/.idea/workspace.xml index ab0b455..e9ac305 100644 --- a/.idea/.idea.TRPZ_PrintService/.idea/workspace.xml +++ b/.idea/.idea.TRPZ_PrintService/.idea/workspace.xml @@ -1,100 +1,129 @@ + + TRPZ_PrintService/TRPZ_PrintService.csproj WebApplication1/WebApplication1.csproj - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -139,12 +187,15 @@ + + + @@ -219,8 +270,16 @@ \ No newline at end of file diff --git a/.vs/TRPZ_PrintService/v16/.suo b/.vs/TRPZ_PrintService/v16/.suo index 12bb1ef..4e97422 100644 Binary files a/.vs/TRPZ_PrintService/v16/.suo and b/.vs/TRPZ_PrintService/v16/.suo differ diff --git a/TRPZ_PrintService/Areas/Identity/IdentityHostingStartup.cs b/TRPZ_PrintService/Areas/Identity/IdentityHostingStartup.cs index 981df67..a16a566 100644 --- a/TRPZ_PrintService/Areas/Identity/IdentityHostingStartup.cs +++ b/TRPZ_PrintService/Areas/Identity/IdentityHostingStartup.cs @@ -24,6 +24,7 @@ public void Configure(IWebHostBuilder builder) { // options.SignIn.RequireConfirmedAccount = true; }) + .AddRoles() .AddEntityFrameworkStores(); }); } diff --git a/TRPZ_PrintService/Areas/Identity/Pages/Account/AccessDenied.cshtml b/TRPZ_PrintService/Areas/Identity/Pages/Account/AccessDenied.cshtml new file mode 100644 index 0000000..017f6ff --- /dev/null +++ b/TRPZ_PrintService/Areas/Identity/Pages/Account/AccessDenied.cshtml @@ -0,0 +1,10 @@ +@page +@model AccessDeniedModel +@{ + ViewData["Title"] = "Access denied"; +} + +
+

@ViewData["Title"]

+

You do not have access to this resource.

+
diff --git a/TRPZ_PrintService/Areas/Identity/Pages/Account/AccessDenied.cshtml.cs b/TRPZ_PrintService/Areas/Identity/Pages/Account/AccessDenied.cshtml.cs new file mode 100644 index 0000000..59cd874 --- /dev/null +++ b/TRPZ_PrintService/Areas/Identity/Pages/Account/AccessDenied.cshtml.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc.RazorPages; + +namespace TRPZ_PrintService.Areas.Identity.Pages.Account +{ + public class AccessDeniedModel : PageModel + { + public void OnGet() + { + + } + } +} + diff --git a/TRPZ_PrintService/Areas/Identity/Pages/Account/ConfirmEmail.cshtml b/TRPZ_PrintService/Areas/Identity/Pages/Account/ConfirmEmail.cshtml new file mode 100644 index 0000000..d84e93b --- /dev/null +++ b/TRPZ_PrintService/Areas/Identity/Pages/Account/ConfirmEmail.cshtml @@ -0,0 +1,7 @@ +@page +@model ConfirmEmailModel +@{ + ViewData["Title"] = "Confirm email"; +} + +

@ViewData["Title"]

\ No newline at end of file diff --git a/TRPZ_PrintService/Areas/Identity/Pages/Account/ConfirmEmail.cshtml.cs b/TRPZ_PrintService/Areas/Identity/Pages/Account/ConfirmEmail.cshtml.cs new file mode 100644 index 0000000..1ce44cd --- /dev/null +++ b/TRPZ_PrintService/Areas/Identity/Pages/Account/ConfirmEmail.cshtml.cs @@ -0,0 +1,47 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Identity; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.RazorPages; +using Microsoft.AspNetCore.WebUtilities; +using TRPZ_PrintService.Areas.Identity.Data; + +namespace TRPZ_PrintService.Areas.Identity.Pages.Account +{ + [AllowAnonymous] + public class ConfirmEmailModel : PageModel + { + private readonly UserManager _userManager; + + public ConfirmEmailModel(UserManager userManager) + { + _userManager = userManager; + } + + [TempData] + public string StatusMessage { get; set; } + + public async Task OnGetAsync(string userId, string code) + { + if (userId == null || code == null) + { + return RedirectToPage("/Index"); + } + + var user = await _userManager.FindByIdAsync(userId); + if (user == null) + { + return NotFound($"Unable to load user with ID '{userId}'."); + } + + code = Encoding.UTF8.GetString(WebEncoders.Base64UrlDecode(code)); + var result = await _userManager.ConfirmEmailAsync(user, code); + StatusMessage = result.Succeeded ? "Thank you for confirming your email." : "Error confirming your email."; + return Page(); + } + } +} diff --git a/TRPZ_PrintService/Areas/Identity/Pages/Account/ConfirmEmailChange.cshtml b/TRPZ_PrintService/Areas/Identity/Pages/Account/ConfirmEmailChange.cshtml new file mode 100644 index 0000000..cbe5275 --- /dev/null +++ b/TRPZ_PrintService/Areas/Identity/Pages/Account/ConfirmEmailChange.cshtml @@ -0,0 +1,8 @@ +@page +@model ConfirmEmailChangeModel +@{ + ViewData["Title"] = "Confirm email change"; +} + +

@ViewData["Title"]

+ \ No newline at end of file diff --git a/TRPZ_PrintService/Areas/Identity/Pages/Account/ConfirmEmailChange.cshtml.cs b/TRPZ_PrintService/Areas/Identity/Pages/Account/ConfirmEmailChange.cshtml.cs new file mode 100644 index 0000000..52b3293 --- /dev/null +++ b/TRPZ_PrintService/Areas/Identity/Pages/Account/ConfirmEmailChange.cshtml.cs @@ -0,0 +1,65 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Identity; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.RazorPages; +using Microsoft.AspNetCore.WebUtilities; +using TRPZ_PrintService.Areas.Identity.Data; + +namespace TRPZ_PrintService.Areas.Identity.Pages.Account +{ + [AllowAnonymous] + public class ConfirmEmailChangeModel : PageModel + { + private readonly UserManager _userManager; + private readonly SignInManager _signInManager; + + public ConfirmEmailChangeModel(UserManager userManager, SignInManager signInManager) + { + _userManager = userManager; + _signInManager = signInManager; + } + + [TempData] + public string StatusMessage { get; set; } + + public async Task OnGetAsync(string userId, string email, string code) + { + if (userId == null || email == null || code == null) + { + return RedirectToPage("/Index"); + } + + var user = await _userManager.FindByIdAsync(userId); + if (user == null) + { + return NotFound($"Unable to load user with ID '{userId}'."); + } + + code = Encoding.UTF8.GetString(WebEncoders.Base64UrlDecode(code)); + var result = await _userManager.ChangeEmailAsync(user, email, code); + if (!result.Succeeded) + { + StatusMessage = "Error changing email."; + return Page(); + } + + // In our UI email and user name are one and the same, so when we update the email + // we need to update the user name. + var setUserNameResult = await _userManager.SetUserNameAsync(user, email); + if (!setUserNameResult.Succeeded) + { + StatusMessage = "Error changing user name."; + return Page(); + } + + await _signInManager.RefreshSignInAsync(user); + StatusMessage = "Thank you for confirming your email change."; + return Page(); + } + } +} diff --git a/TRPZ_PrintService/Areas/Identity/Pages/Account/ExternalLogin.cshtml b/TRPZ_PrintService/Areas/Identity/Pages/Account/ExternalLogin.cshtml new file mode 100644 index 0000000..f7dc967 --- /dev/null +++ b/TRPZ_PrintService/Areas/Identity/Pages/Account/ExternalLogin.cshtml @@ -0,0 +1,33 @@ +@page +@model ExternalLoginModel +@{ + ViewData["Title"] = "Register"; +} + +

@ViewData["Title"]

+

Associate your @Model.ProviderDisplayName account.

+
+ +

+ You've successfully authenticated with @Model.ProviderDisplayName. + Please enter an email address for this site below and click the Register button to finish + logging in. +

+ +
+
+
+
+
+ + + +
+ +
+
+
+ +@section Scripts { + +} diff --git a/TRPZ_PrintService/Areas/Identity/Pages/Account/ExternalLogin.cshtml.cs b/TRPZ_PrintService/Areas/Identity/Pages/Account/ExternalLogin.cshtml.cs new file mode 100644 index 0000000..492e20c --- /dev/null +++ b/TRPZ_PrintService/Areas/Identity/Pages/Account/ExternalLogin.cshtml.cs @@ -0,0 +1,169 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Linq; +using System.Security.Claims; +using System.Text; +using System.Text.Encodings.Web; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authorization; +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; +using TRPZ_PrintService.Areas.Identity.Data; + +namespace TRPZ_PrintService.Areas.Identity.Pages.Account +{ + [AllowAnonymous] + public class ExternalLoginModel : PageModel + { + private readonly SignInManager _signInManager; + private readonly UserManager _userManager; + private readonly IEmailSender _emailSender; + private readonly ILogger _logger; + + public ExternalLoginModel( + SignInManager signInManager, + UserManager userManager, + ILogger logger, + IEmailSender emailSender) + { + _signInManager = signInManager; + _userManager = userManager; + _logger = logger; + _emailSender = emailSender; + } + + [BindProperty] + public InputModel Input { get; set; } + + public string ProviderDisplayName { get; set; } + + public string ReturnUrl { get; set; } + + [TempData] + public string ErrorMessage { get; set; } + + public class InputModel + { + [Required] + [EmailAddress] + public string Email { get; set; } + } + + public IActionResult OnGetAsync() + { + return RedirectToPage("./Login"); + } + + public IActionResult OnPost(string provider, string returnUrl = null) + { + // Request a redirect to the external login provider. + var redirectUrl = Url.Page("./ExternalLogin", pageHandler: "Callback", values: new { returnUrl }); + var properties = _signInManager.ConfigureExternalAuthenticationProperties(provider, redirectUrl); + return new ChallengeResult(provider, properties); + } + + public async Task OnGetCallbackAsync(string returnUrl = null, string remoteError = null) + { + returnUrl = returnUrl ?? Url.Content("~/"); + if (remoteError != null) + { + ErrorMessage = $"Error from external provider: {remoteError}"; + return RedirectToPage("./Login", new {ReturnUrl = returnUrl }); + } + var info = await _signInManager.GetExternalLoginInfoAsync(); + if (info == null) + { + ErrorMessage = "Error loading external login information."; + return RedirectToPage("./Login", new { ReturnUrl = returnUrl }); + } + + // Sign in the user with this external login provider if the user already has a login. + var result = await _signInManager.ExternalLoginSignInAsync(info.LoginProvider, info.ProviderKey, isPersistent: false, bypassTwoFactor : true); + if (result.Succeeded) + { + _logger.LogInformation("{Name} logged in with {LoginProvider} provider.", info.Principal.Identity.Name, info.LoginProvider); + return LocalRedirect(returnUrl); + } + if (result.IsLockedOut) + { + return RedirectToPage("./Lockout"); + } + else + { + // If the user does not have an account, then ask the user to create an account. + ReturnUrl = returnUrl; + ProviderDisplayName = info.ProviderDisplayName; + if (info.Principal.HasClaim(c => c.Type == ClaimTypes.Email)) + { + Input = new InputModel + { + Email = info.Principal.FindFirstValue(ClaimTypes.Email) + }; + } + return Page(); + } + } + + public async Task OnPostConfirmationAsync(string returnUrl = null) + { + returnUrl = returnUrl ?? Url.Content("~/"); + // Get the information about the user from the external login provider + var info = await _signInManager.GetExternalLoginInfoAsync(); + if (info == null) + { + ErrorMessage = "Error loading external login information during confirmation."; + return RedirectToPage("./Login", new { ReturnUrl = returnUrl }); + } + + if (ModelState.IsValid) + { + var user = new TRPZ_PrintServiceUser { UserName = Input.Email, Email = Input.Email }; + + var result = await _userManager.CreateAsync(user); + if (result.Succeeded) + { + result = await _userManager.AddLoginAsync(user, info); + if (result.Succeeded) + { + _logger.LogInformation("User created an account using {Name} provider.", info.LoginProvider); + + 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 }, + protocol: Request.Scheme); + + await _emailSender.SendEmailAsync(Input.Email, "Confirm your email", + $"Please confirm your account by clicking here."); + + // If account confirmation is required, we need to show the link if we don't have a real email sender + if (_userManager.Options.SignIn.RequireConfirmedAccount) + { + return RedirectToPage("./RegisterConfirmation", new { Email = Input.Email }); + } + + await _signInManager.SignInAsync(user, isPersistent: false, info.LoginProvider); + + return LocalRedirect(returnUrl); + } + } + foreach (var error in result.Errors) + { + ModelState.AddModelError(string.Empty, error.Description); + } + } + + ProviderDisplayName = info.ProviderDisplayName; + ReturnUrl = returnUrl; + return Page(); + } + } +} diff --git a/TRPZ_PrintService/Areas/Identity/Pages/Account/ForgotPassword.cshtml b/TRPZ_PrintService/Areas/Identity/Pages/Account/ForgotPassword.cshtml new file mode 100644 index 0000000..94f46b2 --- /dev/null +++ b/TRPZ_PrintService/Areas/Identity/Pages/Account/ForgotPassword.cshtml @@ -0,0 +1,26 @@ +@page +@model ForgotPasswordModel +@{ + ViewData["Title"] = "Forgot your password?"; +} + +

@ViewData["Title"]

+

Enter your email.

+
+
+
+
+
+
+ + + +
+ +
+
+
+ +@section Scripts { + +} diff --git a/TRPZ_PrintService/Areas/Identity/Pages/Account/ForgotPassword.cshtml.cs b/TRPZ_PrintService/Areas/Identity/Pages/Account/ForgotPassword.cshtml.cs new file mode 100644 index 0000000..e1a23c0 --- /dev/null +++ b/TRPZ_PrintService/Areas/Identity/Pages/Account/ForgotPassword.cshtml.cs @@ -0,0 +1,71 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Text.Encodings.Web; +using System.Text; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Identity; +using Microsoft.AspNetCore.Identity.UI.Services; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.RazorPages; +using Microsoft.AspNetCore.WebUtilities; +using TRPZ_PrintService.Areas.Identity.Data; + +namespace TRPZ_PrintService.Areas.Identity.Pages.Account +{ + [AllowAnonymous] + public class ForgotPasswordModel : PageModel + { + private readonly UserManager _userManager; + private readonly IEmailSender _emailSender; + + public ForgotPasswordModel(UserManager userManager, IEmailSender emailSender) + { + _userManager = userManager; + _emailSender = emailSender; + } + + [BindProperty] + public InputModel Input { get; set; } + + public class InputModel + { + [Required] + [EmailAddress] + public string Email { get; set; } + } + + public async Task OnPostAsync() + { + if (ModelState.IsValid) + { + var user = await _userManager.FindByEmailAsync(Input.Email); + if (user == null || !(await _userManager.IsEmailConfirmedAsync(user))) + { + // Don't reveal that the user does not exist or is not confirmed + return RedirectToPage("./ForgotPasswordConfirmation"); + } + + // For more information on how to enable account confirmation and password reset please + // visit https://go.microsoft.com/fwlink/?LinkID=532713 + var code = await _userManager.GeneratePasswordResetTokenAsync(user); + code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code)); + var callbackUrl = Url.Page( + "/Account/ResetPassword", + pageHandler: null, + values: new { area = "Identity", code }, + protocol: Request.Scheme); + + await _emailSender.SendEmailAsync( + Input.Email, + "Reset Password", + $"Please reset your password by clicking here."); + + return RedirectToPage("./ForgotPasswordConfirmation"); + } + + return Page(); + } + } +} diff --git a/TRPZ_PrintService/Areas/Identity/Pages/Account/ForgotPasswordConfirmation.cshtml b/TRPZ_PrintService/Areas/Identity/Pages/Account/ForgotPasswordConfirmation.cshtml new file mode 100644 index 0000000..1a1b7f9 --- /dev/null +++ b/TRPZ_PrintService/Areas/Identity/Pages/Account/ForgotPasswordConfirmation.cshtml @@ -0,0 +1,11 @@ +@page +@model ForgotPasswordConfirmation +@{ + ViewData["Title"] = "Forgot password confirmation"; +} + +

@ViewData["Title"]

+

+ Please check your email to reset your password. +

+ diff --git a/TRPZ_PrintService/Areas/Identity/Pages/Account/ForgotPasswordConfirmation.cshtml.cs b/TRPZ_PrintService/Areas/Identity/Pages/Account/ForgotPasswordConfirmation.cshtml.cs new file mode 100644 index 0000000..7bfa3cb --- /dev/null +++ b/TRPZ_PrintService/Areas/Identity/Pages/Account/ForgotPasswordConfirmation.cshtml.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc.RazorPages; + +namespace TRPZ_PrintService.Areas.Identity.Pages.Account +{ + [AllowAnonymous] + public class ForgotPasswordConfirmation : PageModel + { + public void OnGet() + { + } + } +} diff --git a/TRPZ_PrintService/Areas/Identity/Pages/Account/Lockout.cshtml b/TRPZ_PrintService/Areas/Identity/Pages/Account/Lockout.cshtml new file mode 100644 index 0000000..4eded88 --- /dev/null +++ b/TRPZ_PrintService/Areas/Identity/Pages/Account/Lockout.cshtml @@ -0,0 +1,10 @@ +@page +@model LockoutModel +@{ + ViewData["Title"] = "Locked out"; +} + +
+

@ViewData["Title"]

+

This account has been locked out, please try again later.

+
diff --git a/TRPZ_PrintService/Areas/Identity/Pages/Account/Lockout.cshtml.cs b/TRPZ_PrintService/Areas/Identity/Pages/Account/Lockout.cshtml.cs new file mode 100644 index 0000000..e8330ee --- /dev/null +++ b/TRPZ_PrintService/Areas/Identity/Pages/Account/Lockout.cshtml.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc.RazorPages; + +namespace TRPZ_PrintService.Areas.Identity.Pages.Account +{ + [AllowAnonymous] + public class LockoutModel : PageModel + { + public void OnGet() + { + + } + } +} diff --git a/TRPZ_PrintService/Areas/Identity/Pages/Account/Login.cshtml b/TRPZ_PrintService/Areas/Identity/Pages/Account/Login.cshtml new file mode 100644 index 0000000..8e4d327 --- /dev/null +++ b/TRPZ_PrintService/Areas/Identity/Pages/Account/Login.cshtml @@ -0,0 +1,85 @@ +@page +@model LoginModel + +@{ + ViewData["Title"] = "Log in"; +} + +

@ViewData["Title"]

+
+
+
+
+

Use a local account to log in.

+
+
+
+ + + +
+
+ + + +
+
+
+ +
+
+
+ +
+ +
+
+
+
+
+

Use another service to log in.

+
+ @{ + if ((Model.ExternalLogins?.Count ?? 0) == 0) + { +
+

+ There are no external authentication services configured. See this article + for details on setting up this ASP.NET application to support logging in via external services. +

+
+ } + else + { +
+
+

+ @foreach (var provider in Model.ExternalLogins) + { + + } +

+
+
+ } + } +
+
+
+ +@section Scripts { + +} diff --git a/TRPZ_PrintService/Areas/Identity/Pages/Account/Login.cshtml.cs b/TRPZ_PrintService/Areas/Identity/Pages/Account/Login.cshtml.cs new file mode 100644 index 0000000..ffe1ce3 --- /dev/null +++ b/TRPZ_PrintService/Areas/Identity/Pages/Account/Login.cshtml.cs @@ -0,0 +1,111 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Linq; +using System.Text.Encodings.Web; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Authentication; +using Microsoft.AspNetCore.Identity; +using Microsoft.AspNetCore.Identity.UI.Services; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.RazorPages; +using Microsoft.Extensions.Logging; +using TRPZ_PrintService.Areas.Identity.Data; + +namespace TRPZ_PrintService.Areas.Identity.Pages.Account +{ + [AllowAnonymous] + public class LoginModel : PageModel + { + private readonly UserManager _userManager; + private readonly SignInManager _signInManager; + private readonly ILogger _logger; + + public LoginModel(SignInManager signInManager, + ILogger logger, + UserManager userManager) + { + _userManager = userManager; + _signInManager = signInManager; + _logger = logger; + } + + [BindProperty] + public InputModel Input { get; set; } + + public IList ExternalLogins { get; set; } + + public string ReturnUrl { get; set; } + + [TempData] + public string ErrorMessage { get; set; } + + public class InputModel + { + [Required] + [EmailAddress] + public string Email { get; set; } + + [Required] + [DataType(DataType.Password)] + public string Password { get; set; } + + [Display(Name = "Remember me?")] + public bool RememberMe { get; set; } + } + + public async Task OnGetAsync(string returnUrl = null) + { + if (!string.IsNullOrEmpty(ErrorMessage)) + { + ModelState.AddModelError(string.Empty, ErrorMessage); + } + + returnUrl ??= Url.Content("~/"); + + // Clear the existing external cookie to ensure a clean login process + await HttpContext.SignOutAsync(IdentityConstants.ExternalScheme); + + ExternalLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync()).ToList(); + + ReturnUrl = returnUrl; + } + + public async Task OnPostAsync(string returnUrl = null) + { + returnUrl ??= Url.Content("~/"); + + ExternalLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync()).ToList(); + + if (ModelState.IsValid) + { + // This doesn't count login failures towards account lockout + // To enable password failures to trigger account lockout, set lockoutOnFailure: true + var result = await _signInManager.PasswordSignInAsync(Input.Email, Input.Password, Input.RememberMe, lockoutOnFailure: false); + if (result.Succeeded) + { + _logger.LogInformation("User logged in."); + return LocalRedirect(returnUrl); + } + if (result.RequiresTwoFactor) + { + return RedirectToPage("./LoginWith2fa", new { ReturnUrl = returnUrl, RememberMe = Input.RememberMe }); + } + if (result.IsLockedOut) + { + _logger.LogWarning("User account locked out."); + return RedirectToPage("./Lockout"); + } + else + { + ModelState.AddModelError(string.Empty, "Invalid login attempt."); + return Page(); + } + } + + // If we got this far, something failed, redisplay form + return Page(); + } + } +} diff --git a/TRPZ_PrintService/Areas/Identity/Pages/Account/LoginWith2fa.cshtml b/TRPZ_PrintService/Areas/Identity/Pages/Account/LoginWith2fa.cshtml new file mode 100644 index 0000000..780b4ec --- /dev/null +++ b/TRPZ_PrintService/Areas/Identity/Pages/Account/LoginWith2fa.cshtml @@ -0,0 +1,41 @@ +@page +@model LoginWith2faModel +@{ + ViewData["Title"] = "Two-factor authentication"; +} + +

@ViewData["Title"]

+
+

Your login is protected with an authenticator app. Enter your authenticator code below.

+
+
+
+ +
+
+ + + +
+
+
+ +
+
+
+ +
+
+
+
+

+ Don't have access to your authenticator device? You can + log in with a recovery code. +

+ +@section Scripts { + +} diff --git a/TRPZ_PrintService/Areas/Identity/Pages/Account/LoginWith2fa.cshtml.cs b/TRPZ_PrintService/Areas/Identity/Pages/Account/LoginWith2fa.cshtml.cs new file mode 100644 index 0000000..5d30025 --- /dev/null +++ b/TRPZ_PrintService/Areas/Identity/Pages/Account/LoginWith2fa.cshtml.cs @@ -0,0 +1,99 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Identity; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.RazorPages; +using Microsoft.Extensions.Logging; +using TRPZ_PrintService.Areas.Identity.Data; + +namespace TRPZ_PrintService.Areas.Identity.Pages.Account +{ + [AllowAnonymous] + public class LoginWith2faModel : PageModel + { + private readonly SignInManager _signInManager; + private readonly ILogger _logger; + + public LoginWith2faModel(SignInManager signInManager, ILogger logger) + { + _signInManager = signInManager; + _logger = logger; + } + + [BindProperty] + public InputModel Input { get; set; } + + public bool RememberMe { get; set; } + + public string ReturnUrl { get; set; } + + public class InputModel + { + [Required] + [StringLength(7, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)] + [DataType(DataType.Text)] + [Display(Name = "Authenticator code")] + public string TwoFactorCode { get; set; } + + [Display(Name = "Remember this machine")] + public bool RememberMachine { get; set; } + } + + public async Task OnGetAsync(bool rememberMe, string returnUrl = null) + { + // Ensure the user has gone through the username & password screen first + var user = await _signInManager.GetTwoFactorAuthenticationUserAsync(); + + if (user == null) + { + throw new InvalidOperationException($"Unable to load two-factor authentication user."); + } + + ReturnUrl = returnUrl; + RememberMe = rememberMe; + + return Page(); + } + + public async Task OnPostAsync(bool rememberMe, string returnUrl = null) + { + if (!ModelState.IsValid) + { + return Page(); + } + + returnUrl = returnUrl ?? Url.Content("~/"); + + var user = await _signInManager.GetTwoFactorAuthenticationUserAsync(); + if (user == null) + { + throw new InvalidOperationException($"Unable to load two-factor authentication user."); + } + + var authenticatorCode = Input.TwoFactorCode.Replace(" ", string.Empty).Replace("-", string.Empty); + + var result = await _signInManager.TwoFactorAuthenticatorSignInAsync(authenticatorCode, rememberMe, Input.RememberMachine); + + if (result.Succeeded) + { + _logger.LogInformation("User with ID '{UserId}' logged in with 2fa.", user.Id); + return LocalRedirect(returnUrl); + } + else if (result.IsLockedOut) + { + _logger.LogWarning("User with ID '{UserId}' account locked out.", user.Id); + return RedirectToPage("./Lockout"); + } + else + { + _logger.LogWarning("Invalid authenticator code entered for user with ID '{UserId}'.", user.Id); + ModelState.AddModelError(string.Empty, "Invalid authenticator code."); + return Page(); + } + } + } +} diff --git a/TRPZ_PrintService/Areas/Identity/Pages/Account/LoginWithRecoveryCode.cshtml b/TRPZ_PrintService/Areas/Identity/Pages/Account/LoginWithRecoveryCode.cshtml new file mode 100644 index 0000000..d866adb --- /dev/null +++ b/TRPZ_PrintService/Areas/Identity/Pages/Account/LoginWithRecoveryCode.cshtml @@ -0,0 +1,29 @@ +@page +@model LoginWithRecoveryCodeModel +@{ + ViewData["Title"] = "Recovery code verification"; +} + +

@ViewData["Title"]

+
+

+ You have requested to log in with a recovery code. This login will not be remembered until you provide + an authenticator app code at log in or disable 2FA and log in again. +

+
+
+
+
+
+ + + +
+ +
+
+
+ +@section Scripts { + +} diff --git a/TRPZ_PrintService/Areas/Identity/Pages/Account/LoginWithRecoveryCode.cshtml.cs b/TRPZ_PrintService/Areas/Identity/Pages/Account/LoginWithRecoveryCode.cshtml.cs new file mode 100644 index 0000000..cb9c26d --- /dev/null +++ b/TRPZ_PrintService/Areas/Identity/Pages/Account/LoginWithRecoveryCode.cshtml.cs @@ -0,0 +1,90 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Identity; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.RazorPages; +using Microsoft.Extensions.Logging; +using TRPZ_PrintService.Areas.Identity.Data; + +namespace TRPZ_PrintService.Areas.Identity.Pages.Account +{ + [AllowAnonymous] + public class LoginWithRecoveryCodeModel : PageModel + { + private readonly SignInManager _signInManager; + private readonly ILogger _logger; + + public LoginWithRecoveryCodeModel(SignInManager signInManager, ILogger logger) + { + _signInManager = signInManager; + _logger = logger; + } + + [BindProperty] + public InputModel Input { get; set; } + + public string ReturnUrl { get; set; } + + public class InputModel + { + [BindProperty] + [Required] + [DataType(DataType.Text)] + [Display(Name = "Recovery Code")] + public string RecoveryCode { get; set; } + } + + public async Task OnGetAsync(string returnUrl = null) + { + // Ensure the user has gone through the username & password screen first + var user = await _signInManager.GetTwoFactorAuthenticationUserAsync(); + if (user == null) + { + throw new InvalidOperationException($"Unable to load two-factor authentication user."); + } + + ReturnUrl = returnUrl; + + return Page(); + } + + public async Task OnPostAsync(string returnUrl = null) + { + if (!ModelState.IsValid) + { + return Page(); + } + + var user = await _signInManager.GetTwoFactorAuthenticationUserAsync(); + if (user == null) + { + throw new InvalidOperationException($"Unable to load two-factor authentication user."); + } + + var recoveryCode = Input.RecoveryCode.Replace(" ", string.Empty); + + var result = await _signInManager.TwoFactorRecoveryCodeSignInAsync(recoveryCode); + + if (result.Succeeded) + { + _logger.LogInformation("User with ID '{UserId}' logged in with a recovery code.", user.Id); + return LocalRedirect(returnUrl ?? Url.Content("~/")); + } + if (result.IsLockedOut) + { + _logger.LogWarning("User with ID '{UserId}' account locked out.", user.Id); + return RedirectToPage("./Lockout"); + } + else + { + _logger.LogWarning("Invalid recovery code entered for user with ID '{UserId}' ", user.Id); + ModelState.AddModelError(string.Empty, "Invalid recovery code entered."); + return Page(); + } + } + } +} diff --git a/TRPZ_PrintService/Areas/Identity/Pages/Account/Logout.cshtml b/TRPZ_PrintService/Areas/Identity/Pages/Account/Logout.cshtml new file mode 100644 index 0000000..eca33c6 --- /dev/null +++ b/TRPZ_PrintService/Areas/Identity/Pages/Account/Logout.cshtml @@ -0,0 +1,21 @@ +@page +@model LogoutModel +@{ + ViewData["Title"] = "Log out"; +} + +
+

@ViewData["Title"]

+ @{ + if (User.Identity.IsAuthenticated) + { +
+ +
+ } + else + { +

You have successfully logged out of the application.

+ } + } +
\ No newline at end of file diff --git a/TRPZ_PrintService/Areas/Identity/Pages/Account/Logout.cshtml.cs b/TRPZ_PrintService/Areas/Identity/Pages/Account/Logout.cshtml.cs new file mode 100644 index 0000000..a8f36a7 --- /dev/null +++ b/TRPZ_PrintService/Areas/Identity/Pages/Account/Logout.cshtml.cs @@ -0,0 +1,44 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Identity; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.RazorPages; +using Microsoft.Extensions.Logging; +using TRPZ_PrintService.Areas.Identity.Data; + +namespace TRPZ_PrintService.Areas.Identity.Pages.Account +{ + [AllowAnonymous] + public class LogoutModel : PageModel + { + private readonly SignInManager _signInManager; + private readonly ILogger _logger; + + public LogoutModel(SignInManager signInManager, ILogger logger) + { + _signInManager = signInManager; + _logger = logger; + } + + public void OnGet() + { + } + + public async Task OnPost(string returnUrl = null) + { + await _signInManager.SignOutAsync(); + _logger.LogInformation("User logged out."); + if (returnUrl != null) + { + return LocalRedirect(returnUrl); + } + else + { + return RedirectToPage(); + } + } + } +} diff --git a/TRPZ_PrintService/Areas/Identity/Pages/Account/Manage/ChangePassword.cshtml b/TRPZ_PrintService/Areas/Identity/Pages/Account/Manage/ChangePassword.cshtml new file mode 100644 index 0000000..31a2ea5 --- /dev/null +++ b/TRPZ_PrintService/Areas/Identity/Pages/Account/Manage/ChangePassword.cshtml @@ -0,0 +1,36 @@ +@page +@model ChangePasswordModel +@{ + ViewData["Title"] = "Change password"; + ViewData["ActivePage"] = ManageNavPages.ChangePassword; +} + +

@ViewData["Title"]

+ +
+
+
+
+
+ + + +
+
+ + + +
+
+ + + +
+ +
+
+
+ +@section Scripts { + +} \ No newline at end of file diff --git a/TRPZ_PrintService/Areas/Identity/Pages/Account/Manage/ChangePassword.cshtml.cs b/TRPZ_PrintService/Areas/Identity/Pages/Account/Manage/ChangePassword.cshtml.cs new file mode 100644 index 0000000..dc6e87c --- /dev/null +++ b/TRPZ_PrintService/Areas/Identity/Pages/Account/Manage/ChangePassword.cshtml.cs @@ -0,0 +1,101 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Identity; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.RazorPages; +using Microsoft.Extensions.Logging; +using TRPZ_PrintService.Areas.Identity.Data; +namespace TRPZ_PrintService.Areas.Identity.Pages.Account.Manage +{ + public class ChangePasswordModel : PageModel + { + private readonly UserManager _userManager; + private readonly SignInManager _signInManager; + private readonly ILogger _logger; + + public ChangePasswordModel( + UserManager userManager, + SignInManager signInManager, + ILogger logger) + { + _userManager = userManager; + _signInManager = signInManager; + _logger = logger; + } + + [BindProperty] + public InputModel Input { get; set; } + + [TempData] + public string StatusMessage { get; set; } + + public class InputModel + { + [Required] + [DataType(DataType.Password)] + [Display(Name = "Current password")] + public string OldPassword { get; set; } + + [Required] + [StringLength(100, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)] + [DataType(DataType.Password)] + [Display(Name = "New password")] + public string NewPassword { get; set; } + + [DataType(DataType.Password)] + [Display(Name = "Confirm new password")] + [Compare("NewPassword", ErrorMessage = "The new password and confirmation password do not match.")] + public string ConfirmPassword { get; set; } + } + + public async Task OnGetAsync() + { + var user = await _userManager.GetUserAsync(User); + if (user == null) + { + return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); + } + + var hasPassword = await _userManager.HasPasswordAsync(user); + if (!hasPassword) + { + return RedirectToPage("./SetPassword"); + } + + return Page(); + } + + public async Task OnPostAsync() + { + if (!ModelState.IsValid) + { + return Page(); + } + + var user = await _userManager.GetUserAsync(User); + if (user == null) + { + return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); + } + + var changePasswordResult = await _userManager.ChangePasswordAsync(user, Input.OldPassword, Input.NewPassword); + if (!changePasswordResult.Succeeded) + { + foreach (var error in changePasswordResult.Errors) + { + ModelState.AddModelError(string.Empty, error.Description); + } + return Page(); + } + + await _signInManager.RefreshSignInAsync(user); + _logger.LogInformation("User changed their password successfully."); + StatusMessage = "Your password has been changed."; + + return RedirectToPage(); + } + } +} diff --git a/TRPZ_PrintService/Areas/Identity/Pages/Account/Manage/DeletePersonalData.cshtml b/TRPZ_PrintService/Areas/Identity/Pages/Account/Manage/DeletePersonalData.cshtml new file mode 100644 index 0000000..c95ab92 --- /dev/null +++ b/TRPZ_PrintService/Areas/Identity/Pages/Account/Manage/DeletePersonalData.cshtml @@ -0,0 +1,33 @@ +@page +@model DeletePersonalDataModel +@{ + ViewData["Title"] = "Delete Personal Data"; + ViewData["ActivePage"] = ManageNavPages.PersonalData; +} + +

@ViewData["Title"]

+ + + +
+
+
+ @if (Model.RequirePassword) + { +
+ + + +
+ } + +
+
+ +@section Scripts { + +} \ No newline at end of file diff --git a/TRPZ_PrintService/Areas/Identity/Pages/Account/Manage/DeletePersonalData.cshtml.cs b/TRPZ_PrintService/Areas/Identity/Pages/Account/Manage/DeletePersonalData.cshtml.cs new file mode 100644 index 0000000..34f182b --- /dev/null +++ b/TRPZ_PrintService/Areas/Identity/Pages/Account/Manage/DeletePersonalData.cshtml.cs @@ -0,0 +1,84 @@ +using System; +using System.ComponentModel.DataAnnotations; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Identity; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.RazorPages; +using Microsoft.Extensions.Logging; +using TRPZ_PrintService.Areas.Identity.Data; + +namespace TRPZ_PrintService.Areas.Identity.Pages.Account.Manage +{ + public class DeletePersonalDataModel : PageModel + { + private readonly UserManager _userManager; + private readonly SignInManager _signInManager; + private readonly ILogger _logger; + + public DeletePersonalDataModel( + UserManager userManager, + SignInManager signInManager, + ILogger logger) + { + _userManager = userManager; + _signInManager = signInManager; + _logger = logger; + } + + [BindProperty] + public InputModel Input { get; set; } + + public class InputModel + { + [Required] + [DataType(DataType.Password)] + public string Password { get; set; } + } + + public bool RequirePassword { get; set; } + + public async Task OnGet() + { + var user = await _userManager.GetUserAsync(User); + if (user == null) + { + return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); + } + + RequirePassword = await _userManager.HasPasswordAsync(user); + return Page(); + } + + public async Task OnPostAsync() + { + var user = await _userManager.GetUserAsync(User); + if (user == null) + { + return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); + } + + RequirePassword = await _userManager.HasPasswordAsync(user); + if (RequirePassword) + { + if (!await _userManager.CheckPasswordAsync(user, Input.Password)) + { + ModelState.AddModelError(string.Empty, "Incorrect password."); + return Page(); + } + } + + var result = await _userManager.DeleteAsync(user); + var userId = await _userManager.GetUserIdAsync(user); + if (!result.Succeeded) + { + throw new InvalidOperationException($"Unexpected error occurred deleting user with ID '{userId}'."); + } + + await _signInManager.SignOutAsync(); + + _logger.LogInformation("User with ID '{UserId}' deleted themselves.", userId); + + return Redirect("~/"); + } + } +} diff --git a/TRPZ_PrintService/Areas/Identity/Pages/Account/Manage/Disable2fa.cshtml b/TRPZ_PrintService/Areas/Identity/Pages/Account/Manage/Disable2fa.cshtml new file mode 100644 index 0000000..96df752 --- /dev/null +++ b/TRPZ_PrintService/Areas/Identity/Pages/Account/Manage/Disable2fa.cshtml @@ -0,0 +1,25 @@ +@page +@model Disable2faModel +@{ + ViewData["Title"] = "Disable two-factor authentication (2FA)"; + ViewData["ActivePage"] = ManageNavPages.TwoFactorAuthentication; +} + + +

@ViewData["Title"]

+ + + +
+
+ +
+
diff --git a/TRPZ_PrintService/Areas/Identity/Pages/Account/Manage/Disable2fa.cshtml.cs b/TRPZ_PrintService/Areas/Identity/Pages/Account/Manage/Disable2fa.cshtml.cs new file mode 100644 index 0000000..45ca0b8 --- /dev/null +++ b/TRPZ_PrintService/Areas/Identity/Pages/Account/Manage/Disable2fa.cshtml.cs @@ -0,0 +1,64 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Identity; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.RazorPages; +using Microsoft.Extensions.Logging; +using TRPZ_PrintService.Areas.Identity.Data; + +namespace TRPZ_PrintService.Areas.Identity.Pages.Account.Manage +{ + public class Disable2faModel : PageModel + { + private readonly UserManager _userManager; + private readonly ILogger _logger; + + public Disable2faModel( + UserManager userManager, + ILogger logger) + { + _userManager = userManager; + _logger = logger; + } + + [TempData] + public string StatusMessage { get; set; } + + public async Task OnGet() + { + var user = await _userManager.GetUserAsync(User); + if (user == null) + { + return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); + } + + if (!await _userManager.GetTwoFactorEnabledAsync(user)) + { + throw new InvalidOperationException($"Cannot disable 2FA for user with ID '{_userManager.GetUserId(User)}' as it's not currently enabled."); + } + + return Page(); + } + + public async Task OnPostAsync() + { + var user = await _userManager.GetUserAsync(User); + if (user == null) + { + return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); + } + + var disable2faResult = await _userManager.SetTwoFactorEnabledAsync(user, false); + if (!disable2faResult.Succeeded) + { + throw new InvalidOperationException($"Unexpected error occurred disabling 2FA for user with ID '{_userManager.GetUserId(User)}'."); + } + + _logger.LogInformation("User with ID '{UserId}' has disabled 2fa.", _userManager.GetUserId(User)); + StatusMessage = "2fa has been disabled. You can reenable 2fa when you setup an authenticator app"; + return RedirectToPage("./TwoFactorAuthentication"); + } + } +} \ No newline at end of file diff --git a/TRPZ_PrintService/Areas/Identity/Pages/Account/Manage/DownloadPersonalData.cshtml b/TRPZ_PrintService/Areas/Identity/Pages/Account/Manage/DownloadPersonalData.cshtml new file mode 100644 index 0000000..87470c2 --- /dev/null +++ b/TRPZ_PrintService/Areas/Identity/Pages/Account/Manage/DownloadPersonalData.cshtml @@ -0,0 +1,12 @@ +@page +@model DownloadPersonalDataModel +@{ + ViewData["Title"] = "Download Your Data"; + ViewData["ActivePage"] = ManageNavPages.PersonalData; +} + +

@ViewData["Title"]

+ +@section Scripts { + +} \ No newline at end of file diff --git a/TRPZ_PrintService/Areas/Identity/Pages/Account/Manage/DownloadPersonalData.cshtml.cs b/TRPZ_PrintService/Areas/Identity/Pages/Account/Manage/DownloadPersonalData.cshtml.cs new file mode 100644 index 0000000..44a99a7 --- /dev/null +++ b/TRPZ_PrintService/Areas/Identity/Pages/Account/Manage/DownloadPersonalData.cshtml.cs @@ -0,0 +1,57 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Text.Json; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Identity; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.RazorPages; +using Microsoft.Extensions.Logging; +using TRPZ_PrintService.Areas.Identity.Data; + +namespace TRPZ_PrintService.Areas.Identity.Pages.Account.Manage +{ + public class DownloadPersonalDataModel : PageModel + { + private readonly UserManager _userManager; + private readonly ILogger _logger; + + public DownloadPersonalDataModel( + UserManager userManager, + ILogger logger) + { + _userManager = userManager; + _logger = logger; + } + + public async Task OnPostAsync() + { + var user = await _userManager.GetUserAsync(User); + if (user == null) + { + return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); + } + + _logger.LogInformation("User with ID '{UserId}' asked for their personal data.", _userManager.GetUserId(User)); + + // Only include personal data for download + var personalData = new Dictionary(); + var personalDataProps = typeof(TRPZ_PrintServiceUser).GetProperties().Where( + prop => Attribute.IsDefined(prop, typeof(PersonalDataAttribute))); + foreach (var p in personalDataProps) + { + personalData.Add(p.Name, p.GetValue(user)?.ToString() ?? "null"); + } + + var logins = await _userManager.GetLoginsAsync(user); + foreach (var l in logins) + { + personalData.Add($"{l.LoginProvider} external login provider key", l.ProviderKey); + } + + Response.Headers.Add("Content-Disposition", "attachment; filename=PersonalData.json"); + return new FileContentResult(JsonSerializer.SerializeToUtf8Bytes(personalData), "application/json"); + } + } +} diff --git a/TRPZ_PrintService/Areas/Identity/Pages/Account/Manage/Email.cshtml b/TRPZ_PrintService/Areas/Identity/Pages/Account/Manage/Email.cshtml new file mode 100644 index 0000000..2a599ee --- /dev/null +++ b/TRPZ_PrintService/Areas/Identity/Pages/Account/Manage/Email.cshtml @@ -0,0 +1,43 @@ +@page +@model EmailModel +@{ + ViewData["Title"] = "Manage Email"; + ViewData["ActivePage"] = ManageNavPages.Email; +} + +

@ViewData["Title"]

+ +
+
+
+
+
+ + @if (Model.IsEmailConfirmed) + { +
+ +
+ +
+
+ } + else + { + + + } +
+
+ + + +
+ +
+
+
+ +@section Scripts { + +} diff --git a/TRPZ_PrintService/Areas/Identity/Pages/Account/Manage/Email.cshtml.cs b/TRPZ_PrintService/Areas/Identity/Pages/Account/Manage/Email.cshtml.cs new file mode 100644 index 0000000..276362c --- /dev/null +++ b/TRPZ_PrintService/Areas/Identity/Pages/Account/Manage/Email.cshtml.cs @@ -0,0 +1,148 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Text; +using System.Text.Encodings.Web; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Identity; +using Microsoft.AspNetCore.Identity.UI.Services; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.RazorPages; +using Microsoft.AspNetCore.WebUtilities; +using TRPZ_PrintService.Areas.Identity.Data; + +namespace TRPZ_PrintService.Areas.Identity.Pages.Account.Manage +{ + public partial class EmailModel : PageModel + { + private readonly UserManager _userManager; + private readonly SignInManager _signInManager; + private readonly IEmailSender _emailSender; + + public EmailModel( + UserManager userManager, + SignInManager signInManager, + IEmailSender emailSender) + { + _userManager = userManager; + _signInManager = signInManager; + _emailSender = emailSender; + } + + public string Username { get; set; } + + public string Email { get; set; } + + public bool IsEmailConfirmed { get; set; } + + [TempData] + public string StatusMessage { get; set; } + + [BindProperty] + public InputModel Input { get; set; } + + public class InputModel + { + [Required] + [EmailAddress] + [Display(Name = "New email")] + public string NewEmail { get; set; } + } + + private async Task LoadAsync(TRPZ_PrintServiceUser user) + { + var email = await _userManager.GetEmailAsync(user); + Email = email; + + Input = new InputModel + { + NewEmail = email, + }; + + IsEmailConfirmed = await _userManager.IsEmailConfirmedAsync(user); + } + + public async Task OnGetAsync() + { + var user = await _userManager.GetUserAsync(User); + if (user == null) + { + return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); + } + + await LoadAsync(user); + return Page(); + } + + public async Task OnPostChangeEmailAsync() + { + var user = await _userManager.GetUserAsync(User); + if (user == null) + { + return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); + } + + if (!ModelState.IsValid) + { + await LoadAsync(user); + return Page(); + } + + var email = await _userManager.GetEmailAsync(user); + if (Input.NewEmail != email) + { + var userId = await _userManager.GetUserIdAsync(user); + var code = await _userManager.GenerateChangeEmailTokenAsync(user, Input.NewEmail); + code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code)); + var callbackUrl = Url.Page( + "/Account/ConfirmEmailChange", + pageHandler: null, + values: new { userId = userId, email = Input.NewEmail, code = code }, + protocol: Request.Scheme); + await _emailSender.SendEmailAsync( + Input.NewEmail, + "Confirm your email", + $"Please confirm your account by clicking here."); + + StatusMessage = "Confirmation link to change email sent. Please check your email."; + return RedirectToPage(); + } + + StatusMessage = "Your email is unchanged."; + return RedirectToPage(); + } + + public async Task OnPostSendVerificationEmailAsync() + { + var user = await _userManager.GetUserAsync(User); + if (user == null) + { + return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); + } + + if (!ModelState.IsValid) + { + await LoadAsync(user); + return Page(); + } + + var userId = await _userManager.GetUserIdAsync(user); + var email = await _userManager.GetEmailAsync(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 }, + protocol: Request.Scheme); + await _emailSender.SendEmailAsync( + email, + "Confirm your email", + $"Please confirm your account by clicking here."); + + StatusMessage = "Verification email sent. Please check your email."; + return RedirectToPage(); + } + } +} diff --git a/TRPZ_PrintService/Areas/Identity/Pages/Account/Manage/EnableAuthenticator.cshtml b/TRPZ_PrintService/Areas/Identity/Pages/Account/Manage/EnableAuthenticator.cshtml new file mode 100644 index 0000000..6ea8510 --- /dev/null +++ b/TRPZ_PrintService/Areas/Identity/Pages/Account/Manage/EnableAuthenticator.cshtml @@ -0,0 +1,53 @@ +@page +@model EnableAuthenticatorModel +@{ + ViewData["Title"] = "Configure authenticator app"; + ViewData["ActivePage"] = ManageNavPages.TwoFactorAuthentication; +} + + +

@ViewData["Title"]

+
+

To use an authenticator app go through the following steps:

+
    +
  1. +

    + Download a two-factor authenticator app like Microsoft Authenticator for + Android and + iOS or + Google Authenticator for + Android and + iOS. +

    +
  2. +
  3. +

    Scan the QR Code or enter this key @Model.SharedKey into your two factor authenticator app. Spaces and casing do not matter.

    + +
    +
    +
  4. +
  5. +

    + Once you have scanned the QR code or input the key above, your two factor authentication app will provide you + with a unique code. Enter the code in the confirmation box below. +

    +
    +
    +
    +
    + + + +
    + +
    +
    +
    +
    +
  6. +
+
+ +@section Scripts { + +} diff --git a/TRPZ_PrintService/Areas/Identity/Pages/Account/Manage/EnableAuthenticator.cshtml.cs b/TRPZ_PrintService/Areas/Identity/Pages/Account/Manage/EnableAuthenticator.cshtml.cs new file mode 100644 index 0000000..0fff6b7 --- /dev/null +++ b/TRPZ_PrintService/Areas/Identity/Pages/Account/Manage/EnableAuthenticator.cshtml.cs @@ -0,0 +1,157 @@ +using System; +using System.ComponentModel; +using System.ComponentModel.DataAnnotations; +using System.Collections.Generic; +using System.Text; +using System.Text.Encodings.Web; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Identity; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.RazorPages; +using Microsoft.Extensions.Logging; +using TRPZ_PrintService.Areas.Identity.Data; + +namespace TRPZ_PrintService.Areas.Identity.Pages.Account.Manage +{ + public class EnableAuthenticatorModel : PageModel + { + private readonly UserManager _userManager; + private readonly ILogger _logger; + private readonly UrlEncoder _urlEncoder; + + private const string AuthenticatorUriFormat = "otpauth://totp/{0}:{1}?secret={2}&issuer={0}&digits=6"; + + public EnableAuthenticatorModel( + UserManager userManager, + ILogger logger, + UrlEncoder urlEncoder) + { + _userManager = userManager; + _logger = logger; + _urlEncoder = urlEncoder; + } + + public string SharedKey { get; set; } + + public string AuthenticatorUri { get; set; } + + [TempData] + public string[] RecoveryCodes { get; set; } + + [TempData] + public string StatusMessage { get; set; } + + [BindProperty] + public InputModel Input { get; set; } + + public class InputModel + { + [Required] + [StringLength(7, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)] + [DataType(DataType.Text)] + [Display(Name = "Verification Code")] + public string Code { get; set; } + } + + public async Task OnGetAsync() + { + var user = await _userManager.GetUserAsync(User); + if (user == null) + { + return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); + } + + await LoadSharedKeyAndQrCodeUriAsync(user); + + return Page(); + } + + public async Task OnPostAsync() + { + var user = await _userManager.GetUserAsync(User); + if (user == null) + { + return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); + } + + if (!ModelState.IsValid) + { + await LoadSharedKeyAndQrCodeUriAsync(user); + return Page(); + } + + // Strip spaces and hypens + var verificationCode = Input.Code.Replace(" ", string.Empty).Replace("-", string.Empty); + + var is2faTokenValid = await _userManager.VerifyTwoFactorTokenAsync( + user, _userManager.Options.Tokens.AuthenticatorTokenProvider, verificationCode); + + if (!is2faTokenValid) + { + ModelState.AddModelError("Input.Code", "Verification code is invalid."); + await LoadSharedKeyAndQrCodeUriAsync(user); + return Page(); + } + + await _userManager.SetTwoFactorEnabledAsync(user, true); + var userId = await _userManager.GetUserIdAsync(user); + _logger.LogInformation("User with ID '{UserId}' has enabled 2FA with an authenticator app.", userId); + + StatusMessage = "Your authenticator app has been verified."; + + if (await _userManager.CountRecoveryCodesAsync(user) == 0) + { + var recoveryCodes = await _userManager.GenerateNewTwoFactorRecoveryCodesAsync(user, 10); + RecoveryCodes = recoveryCodes.ToArray(); + return RedirectToPage("./ShowRecoveryCodes"); + } + else + { + return RedirectToPage("./TwoFactorAuthentication"); + } + } + + private async Task LoadSharedKeyAndQrCodeUriAsync(TRPZ_PrintServiceUser user) + { + // Load the authenticator key & QR code URI to display on the form + var unformattedKey = await _userManager.GetAuthenticatorKeyAsync(user); + if (string.IsNullOrEmpty(unformattedKey)) + { + await _userManager.ResetAuthenticatorKeyAsync(user); + unformattedKey = await _userManager.GetAuthenticatorKeyAsync(user); + } + + SharedKey = FormatKey(unformattedKey); + + var email = await _userManager.GetEmailAsync(user); + AuthenticatorUri = GenerateQrCodeUri(email, unformattedKey); + } + + private string FormatKey(string unformattedKey) + { + var result = new StringBuilder(); + int currentPosition = 0; + while (currentPosition + 4 < unformattedKey.Length) + { + result.Append(unformattedKey.Substring(currentPosition, 4)).Append(" "); + currentPosition += 4; + } + if (currentPosition < unformattedKey.Length) + { + result.Append(unformattedKey.Substring(currentPosition)); + } + + return result.ToString().ToLowerInvariant(); + } + + private string GenerateQrCodeUri(string email, string unformattedKey) + { + return string.Format( + AuthenticatorUriFormat, + _urlEncoder.Encode("TRPZ_PrintService"), + _urlEncoder.Encode(email), + unformattedKey); + } + } +} diff --git a/TRPZ_PrintService/Areas/Identity/Pages/Account/Manage/ExternalLogins.cshtml b/TRPZ_PrintService/Areas/Identity/Pages/Account/Manage/ExternalLogins.cshtml new file mode 100644 index 0000000..d7a3c42 --- /dev/null +++ b/TRPZ_PrintService/Areas/Identity/Pages/Account/Manage/ExternalLogins.cshtml @@ -0,0 +1,53 @@ +@page +@model ExternalLoginsModel +@{ + ViewData["Title"] = "Manage your external logins"; + ViewData["ActivePage"] = ManageNavPages.ExternalLogins; +} + + +@if (Model.CurrentLogins?.Count > 0) +{ +

Registered Logins

+ + + @foreach (var login in Model.CurrentLogins) + { + + + + + } + +
@login.ProviderDisplayName + @if (Model.ShowRemoveButton) + { +
+
+ + + +
+
+ } + else + { + @:   + } +
+} +@if (Model.OtherLogins?.Count > 0) +{ +

Add another service to log in.

+
+ +} diff --git a/TRPZ_PrintService/Areas/Identity/Pages/Account/Manage/ExternalLogins.cshtml.cs b/TRPZ_PrintService/Areas/Identity/Pages/Account/Manage/ExternalLogins.cshtml.cs new file mode 100644 index 0000000..6674174 --- /dev/null +++ b/TRPZ_PrintService/Areas/Identity/Pages/Account/Manage/ExternalLogins.cshtml.cs @@ -0,0 +1,110 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authentication; +using Microsoft.AspNetCore.Identity; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.RazorPages; +using TRPZ_PrintService.Areas.Identity.Data; + +namespace TRPZ_PrintService.Areas.Identity.Pages.Account.Manage +{ + public class ExternalLoginsModel : PageModel + { + private readonly UserManager _userManager; + private readonly SignInManager _signInManager; + + public ExternalLoginsModel( + UserManager userManager, + SignInManager signInManager) + { + _userManager = userManager; + _signInManager = signInManager; + } + + public IList CurrentLogins { get; set; } + + public IList OtherLogins { get; set; } + + public bool ShowRemoveButton { get; set; } + + [TempData] + public string StatusMessage { get; set; } + + public async Task OnGetAsync() + { + var user = await _userManager.GetUserAsync(User); + if (user == null) + { + return NotFound($"Unable to load user with ID 'user.Id'."); + } + + CurrentLogins = await _userManager.GetLoginsAsync(user); + OtherLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync()) + .Where(auth => CurrentLogins.All(ul => auth.Name != ul.LoginProvider)) + .ToList(); + ShowRemoveButton = user.PasswordHash != null || CurrentLogins.Count > 1; + return Page(); + } + + public async Task OnPostRemoveLoginAsync(string loginProvider, string providerKey) + { + var user = await _userManager.GetUserAsync(User); + if (user == null) + { + return NotFound($"Unable to load user with ID 'user.Id'."); + } + + var result = await _userManager.RemoveLoginAsync(user, loginProvider, providerKey); + if (!result.Succeeded) + { + StatusMessage = "The external login was not removed."; + return RedirectToPage(); + } + + await _signInManager.RefreshSignInAsync(user); + StatusMessage = "The external login was removed."; + return RedirectToPage(); + } + + public async Task OnPostLinkLoginAsync(string provider) + { + // Clear the existing external cookie to ensure a clean login process + await HttpContext.SignOutAsync(IdentityConstants.ExternalScheme); + + // Request a redirect to the external login provider to link a login for the current user + var redirectUrl = Url.Page("./ExternalLogins", pageHandler: "LinkLoginCallback"); + var properties = _signInManager.ConfigureExternalAuthenticationProperties(provider, redirectUrl, _userManager.GetUserId(User)); + return new ChallengeResult(provider, properties); + } + + public async Task OnGetLinkLoginCallbackAsync() + { + var user = await _userManager.GetUserAsync(User); + if (user == null) + { + return NotFound($"Unable to load user with ID 'user.Id'."); + } + + var info = await _signInManager.GetExternalLoginInfoAsync(user.Id); + if (info == null) + { + throw new InvalidOperationException($"Unexpected error occurred loading external login info for user with ID '{user.Id}'."); + } + + var result = await _userManager.AddLoginAsync(user, info); + if (!result.Succeeded) + { + StatusMessage = "The external login was not added. External logins can only be associated with one account."; + return RedirectToPage(); + } + + // Clear the existing external cookie to ensure a clean login process + await HttpContext.SignOutAsync(IdentityConstants.ExternalScheme); + + StatusMessage = "The external login was added."; + return RedirectToPage(); + } + } +} diff --git a/TRPZ_PrintService/Areas/Identity/Pages/Account/Manage/GenerateRecoveryCodes.cshtml b/TRPZ_PrintService/Areas/Identity/Pages/Account/Manage/GenerateRecoveryCodes.cshtml new file mode 100644 index 0000000..284ab59 --- /dev/null +++ b/TRPZ_PrintService/Areas/Identity/Pages/Account/Manage/GenerateRecoveryCodes.cshtml @@ -0,0 +1,27 @@ +@page +@model GenerateRecoveryCodesModel +@{ + ViewData["Title"] = "Generate two-factor authentication (2FA) recovery codes"; + ViewData["ActivePage"] = ManageNavPages.TwoFactorAuthentication; +} + + +

@ViewData["Title"]

+ +
+
+ +
+
\ No newline at end of file diff --git a/TRPZ_PrintService/Areas/Identity/Pages/Account/Manage/GenerateRecoveryCodes.cshtml.cs b/TRPZ_PrintService/Areas/Identity/Pages/Account/Manage/GenerateRecoveryCodes.cshtml.cs new file mode 100644 index 0000000..9b79da4 --- /dev/null +++ b/TRPZ_PrintService/Areas/Identity/Pages/Account/Manage/GenerateRecoveryCodes.cshtml.cs @@ -0,0 +1,73 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Identity; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.RazorPages; +using Microsoft.Extensions.Logging; +using TRPZ_PrintService.Areas.Identity.Data; + +namespace TRPZ_PrintService.Areas.Identity.Pages.Account.Manage +{ + public class GenerateRecoveryCodesModel : PageModel + { + private readonly UserManager _userManager; + private readonly ILogger _logger; + + public GenerateRecoveryCodesModel( + UserManager userManager, + ILogger logger) + { + _userManager = userManager; + _logger = logger; + } + + [TempData] + public string[] RecoveryCodes { get; set; } + + [TempData] + public string StatusMessage { get; set; } + + public async Task OnGetAsync() + { + var user = await _userManager.GetUserAsync(User); + if (user == null) + { + return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); + } + + var isTwoFactorEnabled = await _userManager.GetTwoFactorEnabledAsync(user); + if (!isTwoFactorEnabled) + { + var userId = await _userManager.GetUserIdAsync(user); + throw new InvalidOperationException($"Cannot generate recovery codes for user with ID '{userId}' because they do not have 2FA enabled."); + } + + return Page(); + } + + public async Task OnPostAsync() + { + var user = await _userManager.GetUserAsync(User); + if (user == null) + { + return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); + } + + var isTwoFactorEnabled = await _userManager.GetTwoFactorEnabledAsync(user); + var userId = await _userManager.GetUserIdAsync(user); + if (!isTwoFactorEnabled) + { + throw new InvalidOperationException($"Cannot generate recovery codes for user with ID '{userId}' as they do not have 2FA enabled."); + } + + var recoveryCodes = await _userManager.GenerateNewTwoFactorRecoveryCodesAsync(user, 10); + RecoveryCodes = recoveryCodes.ToArray(); + + _logger.LogInformation("User with ID '{UserId}' has generated new 2FA recovery codes.", userId); + StatusMessage = "You have generated new recovery codes."; + return RedirectToPage("./ShowRecoveryCodes"); + } + } +} \ No newline at end of file diff --git a/TRPZ_PrintService/Areas/Identity/Pages/Account/Manage/Index.cshtml b/TRPZ_PrintService/Areas/Identity/Pages/Account/Manage/Index.cshtml new file mode 100644 index 0000000..e018437 --- /dev/null +++ b/TRPZ_PrintService/Areas/Identity/Pages/Account/Manage/Index.cshtml @@ -0,0 +1,30 @@ +@page +@model IndexModel +@{ + ViewData["Title"] = "Profile"; + ViewData["ActivePage"] = ManageNavPages.Index; +} + +

@ViewData["Title"]

+ +
+
+
+
+
+ + +
+
+ + + +
+ +
+
+
+ +@section Scripts { + +} \ No newline at end of file diff --git a/TRPZ_PrintService/Areas/Identity/Pages/Account/Manage/Index.cshtml.cs b/TRPZ_PrintService/Areas/Identity/Pages/Account/Manage/Index.cshtml.cs new file mode 100644 index 0000000..39573db --- /dev/null +++ b/TRPZ_PrintService/Areas/Identity/Pages/Account/Manage/Index.cshtml.cs @@ -0,0 +1,96 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Identity; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.RazorPages; +using TRPZ_PrintService.Areas.Identity.Data; + +namespace TRPZ_PrintService.Areas.Identity.Pages.Account.Manage +{ + public partial class IndexModel : PageModel + { + private readonly UserManager _userManager; + private readonly SignInManager _signInManager; + + public IndexModel( + UserManager userManager, + SignInManager signInManager) + { + _userManager = userManager; + _signInManager = signInManager; + } + + public string Username { get; set; } + + [TempData] + public string StatusMessage { get; set; } + + [BindProperty] + public InputModel Input { get; set; } + + public class InputModel + { + [Phone] + [Display(Name = "Phone number")] + public string PhoneNumber { get; set; } + } + + private async Task LoadAsync(TRPZ_PrintServiceUser user) + { + var userName = await _userManager.GetUserNameAsync(user); + var phoneNumber = await _userManager.GetPhoneNumberAsync(user); + + Username = userName; + + Input = new InputModel + { + PhoneNumber = phoneNumber + }; + } + + public async Task OnGetAsync() + { + var user = await _userManager.GetUserAsync(User); + if (user == null) + { + return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); + } + + await LoadAsync(user); + return Page(); + } + + public async Task OnPostAsync() + { + var user = await _userManager.GetUserAsync(User); + if (user == null) + { + return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); + } + + if (!ModelState.IsValid) + { + await LoadAsync(user); + return Page(); + } + + var phoneNumber = await _userManager.GetPhoneNumberAsync(user); + if (Input.PhoneNumber != phoneNumber) + { + var setPhoneResult = await _userManager.SetPhoneNumberAsync(user, Input.PhoneNumber); + if (!setPhoneResult.Succeeded) + { + StatusMessage = "Unexpected error when trying to set phone number."; + return RedirectToPage(); + } + } + + await _signInManager.RefreshSignInAsync(user); + StatusMessage = "Your profile has been updated"; + return RedirectToPage(); + } + } +} diff --git a/TRPZ_PrintService/Areas/Identity/Pages/Account/Manage/ManageNavPages.cs b/TRPZ_PrintService/Areas/Identity/Pages/Account/Manage/ManageNavPages.cs new file mode 100644 index 0000000..11714a7 --- /dev/null +++ b/TRPZ_PrintService/Areas/Identity/Pages/Account/Manage/ManageNavPages.cs @@ -0,0 +1,50 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc.Rendering; + +namespace TRPZ_PrintService.Areas.Identity.Pages.Account.Manage +{ + public static class ManageNavPages + { + public static string Index => "Index"; + + public static string Email => "Email"; + + public static string ChangePassword => "ChangePassword"; + + public static string DownloadPersonalData => "DownloadPersonalData"; + + public static string DeletePersonalData => "DeletePersonalData"; + + public static string ExternalLogins => "ExternalLogins"; + + public static string PersonalData => "PersonalData"; + + public static string TwoFactorAuthentication => "TwoFactorAuthentication"; + + public static string IndexNavClass(ViewContext viewContext) => PageNavClass(viewContext, Index); + + public static string EmailNavClass(ViewContext viewContext) => PageNavClass(viewContext, Email); + + public static string ChangePasswordNavClass(ViewContext viewContext) => PageNavClass(viewContext, ChangePassword); + + public static string DownloadPersonalDataNavClass(ViewContext viewContext) => PageNavClass(viewContext, DownloadPersonalData); + + public static string DeletePersonalDataNavClass(ViewContext viewContext) => PageNavClass(viewContext, DeletePersonalData); + + public static string ExternalLoginsNavClass(ViewContext viewContext) => PageNavClass(viewContext, ExternalLogins); + + public static string PersonalDataNavClass(ViewContext viewContext) => PageNavClass(viewContext, PersonalData); + + public static string TwoFactorAuthenticationNavClass(ViewContext viewContext) => PageNavClass(viewContext, TwoFactorAuthentication); + + private static string PageNavClass(ViewContext viewContext, string page) + { + var activePage = viewContext.ViewData["ActivePage"] as string + ?? System.IO.Path.GetFileNameWithoutExtension(viewContext.ActionDescriptor.DisplayName); + return string.Equals(activePage, page, StringComparison.OrdinalIgnoreCase) ? "active" : null; + } + } +} diff --git a/TRPZ_PrintService/Areas/Identity/Pages/Account/Manage/PersonalData.cshtml b/TRPZ_PrintService/Areas/Identity/Pages/Account/Manage/PersonalData.cshtml new file mode 100644 index 0000000..d64bd82 --- /dev/null +++ b/TRPZ_PrintService/Areas/Identity/Pages/Account/Manage/PersonalData.cshtml @@ -0,0 +1,27 @@ +@page +@model PersonalDataModel +@{ + ViewData["Title"] = "Personal Data"; + ViewData["ActivePage"] = ManageNavPages.PersonalData; +} + +

@ViewData["Title"]

+ +
+
+

Your account contains personal data that you have given us. This page allows you to download or delete that data.

+

+ Deleting this data will permanently remove your account, and this cannot be recovered. +

+
+ +
+

+ Delete +

+
+
+ +@section Scripts { + +} diff --git a/TRPZ_PrintService/Areas/Identity/Pages/Account/Manage/PersonalData.cshtml.cs b/TRPZ_PrintService/Areas/Identity/Pages/Account/Manage/PersonalData.cshtml.cs new file mode 100644 index 0000000..e69cdc3 --- /dev/null +++ b/TRPZ_PrintService/Areas/Identity/Pages/Account/Manage/PersonalData.cshtml.cs @@ -0,0 +1,34 @@ +using System.Threading.Tasks; +using Microsoft.AspNetCore.Identity; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.RazorPages; +using Microsoft.Extensions.Logging; +using TRPZ_PrintService.Areas.Identity.Data; + +namespace TRPZ_PrintService.Areas.Identity.Pages.Account.Manage +{ + public class PersonalDataModel : PageModel + { + private readonly UserManager _userManager; + private readonly ILogger _logger; + + public PersonalDataModel( + UserManager userManager, + ILogger logger) + { + _userManager = userManager; + _logger = logger; + } + + public async Task OnGet() + { + var user = await _userManager.GetUserAsync(User); + if (user == null) + { + return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); + } + + return Page(); + } + } +} \ No newline at end of file diff --git a/TRPZ_PrintService/Areas/Identity/Pages/Account/Manage/ResetAuthenticator.cshtml b/TRPZ_PrintService/Areas/Identity/Pages/Account/Manage/ResetAuthenticator.cshtml new file mode 100644 index 0000000..081c824 --- /dev/null +++ b/TRPZ_PrintService/Areas/Identity/Pages/Account/Manage/ResetAuthenticator.cshtml @@ -0,0 +1,24 @@ +@page +@model ResetAuthenticatorModel +@{ + ViewData["Title"] = "Reset authenticator key"; + ViewData["ActivePage"] = ManageNavPages.TwoFactorAuthentication; +} + + +

@ViewData["Title"]

+ +
+
+ +
+
\ No newline at end of file diff --git a/TRPZ_PrintService/Areas/Identity/Pages/Account/Manage/ResetAuthenticator.cshtml.cs b/TRPZ_PrintService/Areas/Identity/Pages/Account/Manage/ResetAuthenticator.cshtml.cs new file mode 100644 index 0000000..1dd5348 --- /dev/null +++ b/TRPZ_PrintService/Areas/Identity/Pages/Account/Manage/ResetAuthenticator.cshtml.cs @@ -0,0 +1,61 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Identity; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.RazorPages; +using Microsoft.Extensions.Logging; +using TRPZ_PrintService.Areas.Identity.Data; + +namespace TRPZ_PrintService.Areas.Identity.Pages.Account.Manage +{ + public class ResetAuthenticatorModel : PageModel + { + UserManager _userManager; + private readonly SignInManager _signInManager; + ILogger _logger; + + public ResetAuthenticatorModel( + UserManager userManager, + SignInManager signInManager, + ILogger logger) + { + _userManager = userManager; + _signInManager = signInManager; + _logger = logger; + } + + [TempData] + public string StatusMessage { get; set; } + + public async Task OnGet() + { + var user = await _userManager.GetUserAsync(User); + if (user == null) + { + return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); + } + + return Page(); + } + + public async Task OnPostAsync() + { + var user = await _userManager.GetUserAsync(User); + if (user == null) + { + return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); + } + + await _userManager.SetTwoFactorEnabledAsync(user, false); + await _userManager.ResetAuthenticatorKeyAsync(user); + _logger.LogInformation("User with ID '{UserId}' has reset their authentication app key.", user.Id); + + await _signInManager.RefreshSignInAsync(user); + StatusMessage = "Your authenticator app key has been reset, you will need to configure your authenticator app using the new key."; + + return RedirectToPage("./EnableAuthenticator"); + } + } +} \ No newline at end of file diff --git a/TRPZ_PrintService/Areas/Identity/Pages/Account/Manage/SetPassword.cshtml b/TRPZ_PrintService/Areas/Identity/Pages/Account/Manage/SetPassword.cshtml new file mode 100644 index 0000000..f1817aa --- /dev/null +++ b/TRPZ_PrintService/Areas/Identity/Pages/Account/Manage/SetPassword.cshtml @@ -0,0 +1,35 @@ +@page +@model SetPasswordModel +@{ + ViewData["Title"] = "Set password"; + ViewData["ActivePage"] = ManageNavPages.ChangePassword; +} + +

Set your password

+ +

+ You do not have a local username/password for this site. Add a local + account so you can log in without an external login. +

+
+
+
+
+
+ + + +
+
+ + + +
+ +
+
+
+ +@section Scripts { + +} \ No newline at end of file diff --git a/TRPZ_PrintService/Areas/Identity/Pages/Account/Manage/SetPassword.cshtml.cs b/TRPZ_PrintService/Areas/Identity/Pages/Account/Manage/SetPassword.cshtml.cs new file mode 100644 index 0000000..f969d61 --- /dev/null +++ b/TRPZ_PrintService/Areas/Identity/Pages/Account/Manage/SetPassword.cshtml.cs @@ -0,0 +1,93 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Identity; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.RazorPages; +using TRPZ_PrintService.Areas.Identity.Data; + +namespace TRPZ_PrintService.Areas.Identity.Pages.Account.Manage +{ + public class SetPasswordModel : PageModel + { + private readonly UserManager _userManager; + private readonly SignInManager _signInManager; + + public SetPasswordModel( + UserManager userManager, + SignInManager signInManager) + { + _userManager = userManager; + _signInManager = signInManager; + } + + [BindProperty] + public InputModel Input { get; set; } + + [TempData] + public string StatusMessage { get; set; } + + public class InputModel + { + [Required] + [StringLength(100, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)] + [DataType(DataType.Password)] + [Display(Name = "New password")] + public string NewPassword { get; set; } + + [DataType(DataType.Password)] + [Display(Name = "Confirm new password")] + [Compare("NewPassword", ErrorMessage = "The new password and confirmation password do not match.")] + public string ConfirmPassword { get; set; } + } + + public async Task OnGetAsync() + { + var user = await _userManager.GetUserAsync(User); + if (user == null) + { + return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); + } + + var hasPassword = await _userManager.HasPasswordAsync(user); + + if (hasPassword) + { + return RedirectToPage("./ChangePassword"); + } + + return Page(); + } + + public async Task OnPostAsync() + { + if (!ModelState.IsValid) + { + return Page(); + } + + var user = await _userManager.GetUserAsync(User); + if (user == null) + { + return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); + } + + var addPasswordResult = await _userManager.AddPasswordAsync(user, Input.NewPassword); + if (!addPasswordResult.Succeeded) + { + foreach (var error in addPasswordResult.Errors) + { + ModelState.AddModelError(string.Empty, error.Description); + } + return Page(); + } + + await _signInManager.RefreshSignInAsync(user); + StatusMessage = "Your password has been set."; + + return RedirectToPage(); + } + } +} diff --git a/TRPZ_PrintService/Areas/Identity/Pages/Account/Manage/ShowRecoveryCodes.cshtml b/TRPZ_PrintService/Areas/Identity/Pages/Account/Manage/ShowRecoveryCodes.cshtml new file mode 100644 index 0000000..23fa27b --- /dev/null +++ b/TRPZ_PrintService/Areas/Identity/Pages/Account/Manage/ShowRecoveryCodes.cshtml @@ -0,0 +1,25 @@ +@page +@model ShowRecoveryCodesModel +@{ + ViewData["Title"] = "Recovery codes"; + ViewData["ActivePage"] = "TwoFactorAuthentication"; +} + + +

@ViewData["Title"]

+ +
+
+ @for (var row = 0; row < Model.RecoveryCodes.Length; row += 2) + { + @Model.RecoveryCodes[row] @Model.RecoveryCodes[row + 1]
+ } +
+
\ No newline at end of file diff --git a/TRPZ_PrintService/Areas/Identity/Pages/Account/Manage/ShowRecoveryCodes.cshtml.cs b/TRPZ_PrintService/Areas/Identity/Pages/Account/Manage/ShowRecoveryCodes.cshtml.cs new file mode 100644 index 0000000..b78cd31 --- /dev/null +++ b/TRPZ_PrintService/Areas/Identity/Pages/Account/Manage/ShowRecoveryCodes.cshtml.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Identity; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.RazorPages; +using Microsoft.Extensions.Logging; +using TRPZ_PrintService.Areas.Identity.Data; + +namespace TRPZ_PrintService.Areas.Identity.Pages.Account.Manage +{ + public class ShowRecoveryCodesModel : PageModel + { + [TempData] + public string[] RecoveryCodes { get; set; } + + [TempData] + public string StatusMessage { get; set; } + + public IActionResult OnGet() + { + if (RecoveryCodes == null || RecoveryCodes.Length == 0) + { + return RedirectToPage("./TwoFactorAuthentication"); + } + + return Page(); + } + } +} diff --git a/TRPZ_PrintService/Areas/Identity/Pages/Account/Manage/TwoFactorAuthentication.cshtml b/TRPZ_PrintService/Areas/Identity/Pages/Account/Manage/TwoFactorAuthentication.cshtml new file mode 100644 index 0000000..a1729eb --- /dev/null +++ b/TRPZ_PrintService/Areas/Identity/Pages/Account/Manage/TwoFactorAuthentication.cshtml @@ -0,0 +1,57 @@ +@page +@model TwoFactorAuthenticationModel +@{ + ViewData["Title"] = "Two-factor authentication (2FA)"; + ViewData["ActivePage"] = ManageNavPages.TwoFactorAuthentication; +} + + +

@ViewData["Title"]

+@if (Model.Is2faEnabled) +{ + if (Model.RecoveryCodesLeft == 0) + { +
+ You have no recovery codes left. +

You must generate a new set of recovery codes before you can log in with a recovery code.

+
+ } + else if (Model.RecoveryCodesLeft == 1) + { +
+ You have 1 recovery code left. +

You can generate a new set of recovery codes.

+
+ } + else if (Model.RecoveryCodesLeft <= 3) + { +
+ You have @Model.RecoveryCodesLeft recovery codes left. +

You should generate a new set of recovery codes.

+
+ } + + if (Model.IsMachineRemembered) + { +
+ +
+ } + Disable 2FA + Reset recovery codes +} + +
Authenticator app
+@if (!Model.HasAuthenticator) +{ + Add authenticator app +} +else +{ + Setup authenticator app + Reset authenticator app +} + +@section Scripts { + +} \ No newline at end of file diff --git a/TRPZ_PrintService/Areas/Identity/Pages/Account/Manage/TwoFactorAuthentication.cshtml.cs b/TRPZ_PrintService/Areas/Identity/Pages/Account/Manage/TwoFactorAuthentication.cshtml.cs new file mode 100644 index 0000000..f47e5ae --- /dev/null +++ b/TRPZ_PrintService/Areas/Identity/Pages/Account/Manage/TwoFactorAuthentication.cshtml.cs @@ -0,0 +1,72 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Identity; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.RazorPages; +using Microsoft.Extensions.Logging; +using TRPZ_PrintService.Areas.Identity.Data; + +namespace TRPZ_PrintService.Areas.Identity.Pages.Account.Manage +{ + public class TwoFactorAuthenticationModel : PageModel + { + private const string AuthenicatorUriFormat = "otpauth://totp/{0}:{1}?secret={2}&issuer={0}"; + + private readonly UserManager _userManager; + private readonly SignInManager _signInManager; + private readonly ILogger _logger; + + public TwoFactorAuthenticationModel( + UserManager userManager, + SignInManager signInManager, + ILogger logger) + { + _userManager = userManager; + _signInManager = signInManager; + _logger = logger; + } + + public bool HasAuthenticator { get; set; } + + public int RecoveryCodesLeft { get; set; } + + [BindProperty] + public bool Is2faEnabled { get; set; } + + public bool IsMachineRemembered { get; set; } + + [TempData] + public string StatusMessage { get; set; } + + public async Task OnGet() + { + var user = await _userManager.GetUserAsync(User); + if (user == null) + { + return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); + } + + HasAuthenticator = await _userManager.GetAuthenticatorKeyAsync(user) != null; + Is2faEnabled = await _userManager.GetTwoFactorEnabledAsync(user); + IsMachineRemembered = await _signInManager.IsTwoFactorClientRememberedAsync(user); + RecoveryCodesLeft = await _userManager.CountRecoveryCodesAsync(user); + + return Page(); + } + + public async Task OnPost() + { + var user = await _userManager.GetUserAsync(User); + if (user == null) + { + return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); + } + + await _signInManager.ForgetTwoFactorClientAsync(); + StatusMessage = "The current browser has been forgotten. When you login again from this browser you will be prompted for your 2fa code."; + return RedirectToPage(); + } + } +} \ No newline at end of file diff --git a/TRPZ_PrintService/Areas/Identity/Pages/Account/Manage/_Layout.cshtml b/TRPZ_PrintService/Areas/Identity/Pages/Account/Manage/_Layout.cshtml new file mode 100644 index 0000000..d994053 --- /dev/null +++ b/TRPZ_PrintService/Areas/Identity/Pages/Account/Manage/_Layout.cshtml @@ -0,0 +1,29 @@ +@{ + if (ViewData.TryGetValue("ParentLayout", out var parentLayout)) + { + Layout = (string)parentLayout; + } + else + { + Layout = "_Layout.cshtml"; + } +} + +

Manage your account

+ +
+

Change your account settings

+
+
+
+ +
+
+ @RenderBody() +
+
+
+ +@section Scripts { + @RenderSection("Scripts", required: false) +} diff --git a/TRPZ_PrintService/Areas/Identity/Pages/Account/Manage/_ManageNav.cshtml b/TRPZ_PrintService/Areas/Identity/Pages/Account/Manage/_ManageNav.cshtml new file mode 100644 index 0000000..ec8b5cd --- /dev/null +++ b/TRPZ_PrintService/Areas/Identity/Pages/Account/Manage/_ManageNav.cshtml @@ -0,0 +1,15 @@ +@inject SignInManager SignInManager +@{ + var hasExternalLogins = (await SignInManager.GetExternalAuthenticationSchemesAsync()).Any(); +} + diff --git a/TRPZ_PrintService/Areas/Identity/Pages/Account/Manage/_StatusMessage.cshtml b/TRPZ_PrintService/Areas/Identity/Pages/Account/Manage/_StatusMessage.cshtml new file mode 100644 index 0000000..208a424 --- /dev/null +++ b/TRPZ_PrintService/Areas/Identity/Pages/Account/Manage/_StatusMessage.cshtml @@ -0,0 +1,10 @@ +@model string + +@if (!String.IsNullOrEmpty(Model)) +{ + var statusMessageClass = Model.StartsWith("Error") ? "danger" : "success"; + +} \ No newline at end of file diff --git a/TRPZ_PrintService/Areas/Identity/Pages/Account/Manage/_ViewImports.cshtml b/TRPZ_PrintService/Areas/Identity/Pages/Account/Manage/_ViewImports.cshtml new file mode 100644 index 0000000..0fd95b9 --- /dev/null +++ b/TRPZ_PrintService/Areas/Identity/Pages/Account/Manage/_ViewImports.cshtml @@ -0,0 +1 @@ +@using TRPZ_PrintService.Areas.Identity.Pages.Account.Manage diff --git a/TRPZ_PrintService/Areas/Identity/Pages/Account/Register.cshtml.cs b/TRPZ_PrintService/Areas/Identity/Pages/Account/Register.cshtml.cs index a123b0d..8c00793 100644 --- a/TRPZ_PrintService/Areas/Identity/Pages/Account/Register.cshtml.cs +++ b/TRPZ_PrintService/Areas/Identity/Pages/Account/Register.cshtml.cs @@ -24,21 +24,23 @@ public class RegisterModel : PageModel private readonly UserManager _userManager; private readonly ILogger _logger; private readonly IEmailSender _emailSender; + private readonly RoleManager _roleManager; public RegisterModel( UserManager userManager, SignInManager signInManager, ILogger logger, - IEmailSender emailSender) + IEmailSender emailSender, + RoleManager roleManager) { _userManager = userManager; _signInManager = signInManager; _logger = logger; _emailSender = emailSender; + _roleManager = roleManager; } - [BindProperty] - public InputModel Input { get; set; } + [BindProperty] public InputModel Input { get; set; } public string ReturnUrl { get; set; } @@ -50,19 +52,20 @@ public class InputModel [DataType(DataType.Text)] [Display(Name = "First name")] public string FirstName { get; set; } - + [Required] [DataType(DataType.Text)] [Display(Name = "Last name")] public string LastName { get; set; } - + [Required] [EmailAddress] [Display(Name = "Email")] public string Email { get; set; } [Required] - [StringLength(100, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)] + [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; } @@ -71,7 +74,6 @@ public class InputModel [Display(Name = "Confirm password")] [Compare("Password", ErrorMessage = "The password and confirmation password do not match.")] public string ConfirmPassword { get; set; } - } public async Task OnGetAsync(string returnUrl = null) @@ -94,8 +96,27 @@ public async Task OnPostAsync(string returnUrl = null) LastName = Input.LastName }; var result = await _userManager.CreateAsync(user, Input.Password); + if (result.Succeeded) { + //add client role + + if (!_roleManager.Roles.Any(role => role.Name == "Client")) + { + await _roleManager.CreateAsync(new IdentityRole("Client")); + } + if (!_roleManager.Roles.Any(role => role.Name == "Manager")) + { + await _roleManager.CreateAsync(new IdentityRole("Manager")); + } + if (!_roleManager.Roles.Any(role => role.Name == "Admin")) + { + await _roleManager.CreateAsync(new IdentityRole("Admin")); + } + + await _userManager.AddToRoleAsync(user, "Client"); + + _logger.LogInformation("User created a new account with password."); var code = await _userManager.GenerateEmailConfirmationTokenAsync(user); @@ -103,7 +124,7 @@ public async Task OnPostAsync(string returnUrl = null) var callbackUrl = Url.Page( "/Account/ConfirmEmail", pageHandler: null, - values: new { area = "Identity", userId = user.Id, code = code, returnUrl = returnUrl }, + values: new {area = "Identity", userId = user.Id, code = code, returnUrl = returnUrl}, protocol: Request.Scheme); await _emailSender.SendEmailAsync(Input.Email, "Confirm your email", @@ -111,7 +132,7 @@ await _emailSender.SendEmailAsync(Input.Email, "Confirm your email", if (_userManager.Options.SignIn.RequireConfirmedAccount) { - return RedirectToPage("RegisterConfirmation", new { email = Input.Email, returnUrl = returnUrl }); + return RedirectToPage("RegisterConfirmation", new {email = Input.Email, returnUrl = returnUrl}); } else { @@ -119,6 +140,7 @@ await _emailSender.SendEmailAsync(Input.Email, "Confirm your email", return LocalRedirect(returnUrl); } } + foreach (var error in result.Errors) { ModelState.AddModelError(string.Empty, error.Description); @@ -129,4 +151,4 @@ await _emailSender.SendEmailAsync(Input.Email, "Confirm your email", return Page(); } } -} +} \ No newline at end of file diff --git a/TRPZ_PrintService/Areas/Identity/Pages/Account/RegisterConfirmation.cshtml b/TRPZ_PrintService/Areas/Identity/Pages/Account/RegisterConfirmation.cshtml new file mode 100644 index 0000000..b851192 --- /dev/null +++ b/TRPZ_PrintService/Areas/Identity/Pages/Account/RegisterConfirmation.cshtml @@ -0,0 +1,22 @@ +@page +@model RegisterConfirmationModel +@{ + ViewData["Title"] = "Register confirmation"; +} + +

@ViewData["Title"]

+@{ + if (@Model.DisplayConfirmAccountLink) + { +

+ This app does not currently have a real email sender registered, see these docs for how to configure a real email sender. + Normally this would be emailed: Click here to confirm your account +

+ } + else + { +

+ Please check your email to confirm your account. +

+ } +} diff --git a/TRPZ_PrintService/Areas/Identity/Pages/Account/RegisterConfirmation.cshtml.cs b/TRPZ_PrintService/Areas/Identity/Pages/Account/RegisterConfirmation.cshtml.cs new file mode 100644 index 0000000..15c8b53 --- /dev/null +++ b/TRPZ_PrintService/Areas/Identity/Pages/Account/RegisterConfirmation.cshtml.cs @@ -0,0 +1,62 @@ +using Microsoft.AspNetCore.Authorization; +using System.Text; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Identity; +using Microsoft.AspNetCore.Identity.UI.Services; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.RazorPages; +using Microsoft.AspNetCore.WebUtilities; +using TRPZ_PrintService.Areas.Identity.Data; + +namespace TRPZ_PrintService.Areas.Identity.Pages.Account +{ + [AllowAnonymous] + public class RegisterConfirmationModel : PageModel + { + private readonly UserManager _userManager; + private readonly IEmailSender _sender; + + public RegisterConfirmationModel(UserManager userManager, IEmailSender sender) + { + _userManager = userManager; + _sender = sender; + } + + public string Email { get; set; } + + public bool DisplayConfirmAccountLink { get; set; } + + public string EmailConfirmationUrl { get; set; } + + public async Task OnGetAsync(string email, string returnUrl = null) + { + if (email == null) + { + return RedirectToPage("/Index"); + } + + var user = await _userManager.FindByEmailAsync(email); + if (user == null) + { + return NotFound($"Unable to load user with email '{email}'."); + } + + Email = email; + // Once you add a real email sender, you should remove this code that lets you confirm the account + DisplayConfirmAccountLink = true; + if (DisplayConfirmAccountLink) + { + var userId = await _userManager.GetUserIdAsync(user); + var code = await _userManager.GenerateEmailConfirmationTokenAsync(user); + code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code)); + EmailConfirmationUrl = Url.Page( + "/Account/ConfirmEmail", + pageHandler: null, + values: new { area = "Identity", userId = userId, code = code, returnUrl = returnUrl }, + protocol: Request.Scheme); + } + + return Page(); + } + } +} diff --git a/TRPZ_PrintService/Areas/Identity/Pages/Account/ResendEmailConfirmation.cshtml b/TRPZ_PrintService/Areas/Identity/Pages/Account/ResendEmailConfirmation.cshtml new file mode 100644 index 0000000..8578c23 --- /dev/null +++ b/TRPZ_PrintService/Areas/Identity/Pages/Account/ResendEmailConfirmation.cshtml @@ -0,0 +1,26 @@ +@page +@model ResendEmailConfirmationModel +@{ + ViewData["Title"] = "Resend email confirmation"; +} + +

@ViewData["Title"]

+

Enter your email.

+
+
+
+
+
+
+ + + +
+ +
+
+
+ +@section Scripts { + +} diff --git a/TRPZ_PrintService/Areas/Identity/Pages/Account/ResendEmailConfirmation.cshtml.cs b/TRPZ_PrintService/Areas/Identity/Pages/Account/ResendEmailConfirmation.cshtml.cs new file mode 100644 index 0000000..abb1da9 --- /dev/null +++ b/TRPZ_PrintService/Areas/Identity/Pages/Account/ResendEmailConfirmation.cshtml.cs @@ -0,0 +1,74 @@ +using System; +using System.ComponentModel.DataAnnotations; +using System.Text; +using System.Text.Encodings.Web; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authorization; + +using Microsoft.AspNetCore.Identity; +using Microsoft.AspNetCore.Identity.UI.Services; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.RazorPages; +using Microsoft.AspNetCore.WebUtilities; +using TRPZ_PrintService.Areas.Identity.Data; + +namespace TRPZ_PrintService.Areas.Identity.Pages.Account +{ + [AllowAnonymous] + public class ResendEmailConfirmationModel : PageModel + { + private readonly UserManager _userManager; + private readonly IEmailSender _emailSender; + + public ResendEmailConfirmationModel(UserManager userManager, IEmailSender emailSender) + { + _userManager = userManager; + _emailSender = emailSender; + } + + [BindProperty] + public InputModel Input { get; set; } + + public class InputModel + { + [Required] + [EmailAddress] + public string Email { get; set; } + } + + public void OnGet() + { + } + + public async Task OnPostAsync() + { + if (!ModelState.IsValid) + { + return Page(); + } + + var user = await _userManager.FindByEmailAsync(Input.Email); + if (user == null) + { + ModelState.AddModelError(string.Empty, "Verification email sent. Please check your email."); + return Page(); + } + + 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 { userId = userId, code = code }, + protocol: Request.Scheme); + await _emailSender.SendEmailAsync( + Input.Email, + "Confirm your email", + $"Please confirm your account by clicking here."); + + ModelState.AddModelError(string.Empty, "Verification email sent. Please check your email."); + return Page(); + } + } +} diff --git a/TRPZ_PrintService/Areas/Identity/Pages/Account/ResetPassword.cshtml b/TRPZ_PrintService/Areas/Identity/Pages/Account/ResetPassword.cshtml new file mode 100644 index 0000000..27bc951 --- /dev/null +++ b/TRPZ_PrintService/Areas/Identity/Pages/Account/ResetPassword.cshtml @@ -0,0 +1,37 @@ +@page +@model ResetPasswordModel +@{ + ViewData["Title"] = "Reset password"; +} + +

@ViewData["Title"]

+

Reset your password.

+
+
+
+
+
+ +
+ + + +
+
+ + + +
+
+ + + +
+ +
+
+
+ +@section Scripts { + +} diff --git a/TRPZ_PrintService/Areas/Identity/Pages/Account/ResetPassword.cshtml.cs b/TRPZ_PrintService/Areas/Identity/Pages/Account/ResetPassword.cshtml.cs new file mode 100644 index 0000000..7059344 --- /dev/null +++ b/TRPZ_PrintService/Areas/Identity/Pages/Account/ResetPassword.cshtml.cs @@ -0,0 +1,91 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Identity; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.RazorPages; +using Microsoft.AspNetCore.WebUtilities; +using TRPZ_PrintService.Areas.Identity.Data; + +namespace TRPZ_PrintService.Areas.Identity.Pages.Account +{ + [AllowAnonymous] + public class ResetPasswordModel : PageModel + { + private readonly UserManager _userManager; + + public ResetPasswordModel(UserManager userManager) + { + _userManager = userManager; + } + + [BindProperty] + public InputModel Input { get; set; } + + public class InputModel + { + [Required] + [EmailAddress] + public string Email { get; set; } + + [Required] + [StringLength(100, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)] + [DataType(DataType.Password)] + public string Password { get; set; } + + [DataType(DataType.Password)] + [Display(Name = "Confirm password")] + [Compare("Password", ErrorMessage = "The password and confirmation password do not match.")] + public string ConfirmPassword { get; set; } + + public string Code { get; set; } + } + + public IActionResult OnGet(string code = null) + { + if (code == null) + { + return BadRequest("A code must be supplied for password reset."); + } + else + { + Input = new InputModel + { + Code = Encoding.UTF8.GetString(WebEncoders.Base64UrlDecode(code)) + }; + return Page(); + } + } + + public async Task OnPostAsync() + { + if (!ModelState.IsValid) + { + return Page(); + } + + var user = await _userManager.FindByEmailAsync(Input.Email); + if (user == null) + { + // Don't reveal that the user does not exist + return RedirectToPage("./ResetPasswordConfirmation"); + } + + var result = await _userManager.ResetPasswordAsync(user, Input.Code, Input.Password); + if (result.Succeeded) + { + return RedirectToPage("./ResetPasswordConfirmation"); + } + + foreach (var error in result.Errors) + { + ModelState.AddModelError(string.Empty, error.Description); + } + return Page(); + } + } +} diff --git a/TRPZ_PrintService/Areas/Identity/Pages/Account/ResetPasswordConfirmation.cshtml b/TRPZ_PrintService/Areas/Identity/Pages/Account/ResetPasswordConfirmation.cshtml new file mode 100644 index 0000000..c52552f --- /dev/null +++ b/TRPZ_PrintService/Areas/Identity/Pages/Account/ResetPasswordConfirmation.cshtml @@ -0,0 +1,10 @@ +@page +@model ResetPasswordConfirmationModel +@{ + ViewData["Title"] = "Reset password confirmation"; +} + +

@ViewData["Title"]

+

+ Your password has been reset. Please click here to log in. +

diff --git a/TRPZ_PrintService/Areas/Identity/Pages/Account/ResetPasswordConfirmation.cshtml.cs b/TRPZ_PrintService/Areas/Identity/Pages/Account/ResetPasswordConfirmation.cshtml.cs new file mode 100644 index 0000000..58ab528 --- /dev/null +++ b/TRPZ_PrintService/Areas/Identity/Pages/Account/ResetPasswordConfirmation.cshtml.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc.RazorPages; + +namespace TRPZ_PrintService.Areas.Identity.Pages.Account +{ + [AllowAnonymous] + public class ResetPasswordConfirmationModel : PageModel + { + public void OnGet() + { + + } + } +} diff --git a/TRPZ_PrintService/Areas/Identity/Pages/Account/_StatusMessage.cshtml b/TRPZ_PrintService/Areas/Identity/Pages/Account/_StatusMessage.cshtml new file mode 100644 index 0000000..e996841 --- /dev/null +++ b/TRPZ_PrintService/Areas/Identity/Pages/Account/_StatusMessage.cshtml @@ -0,0 +1,10 @@ +@model string + +@if (!String.IsNullOrEmpty(Model)) +{ + var statusMessageClass = Model.StartsWith("Error") ? "danger" : "success"; + +} diff --git a/TRPZ_PrintService/Data/Client.cs b/TRPZ_PrintService/Data/Client.cs deleted file mode 100644 index b1306d3..0000000 --- a/TRPZ_PrintService/Data/Client.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System.Collections.Generic; -using TRPZ_PrintService.Areas.Identity.Data; - -namespace TRPZ_PrintService.Data -{ - public class Client : TRPZ_PrintServiceUser - { - public List Orders { get; set; } - - public Client() - { - Orders = new List(); - } - } -} \ No newline at end of file diff --git a/TRPZ_PrintService/Data/Manager.cs b/TRPZ_PrintService/Data/Manager.cs deleted file mode 100644 index c25359f..0000000 --- a/TRPZ_PrintService/Data/Manager.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System; -using System.Collections.Generic; -using TRPZ_PrintService.Areas.Identity.Data; - -namespace TRPZ_PrintService.Data -{ - public class Manager : TRPZ_PrintServiceUser - { - public String Type { get; set; } - public List ModelsInOrders { get; set; } - } -} \ No newline at end of file diff --git a/TRPZ_PrintService/Data/ModelInOrder.cs b/TRPZ_PrintService/Data/ModelInOrder.cs index 96b9658..16f26b5 100644 --- a/TRPZ_PrintService/Data/ModelInOrder.cs +++ b/TRPZ_PrintService/Data/ModelInOrder.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using System.ComponentModel.DataAnnotations.Schema; +using TRPZ_PrintService.Areas.Identity.Data; namespace TRPZ_PrintService.Data { @@ -14,7 +15,7 @@ public class ModelInOrder public ModelSettings ModelSettings { get; set; } public Material Material { get; set; } public PostProcessing PostProcessing { get; set; } - public Manager Manager { get; set; } + public TRPZ_PrintServiceUser Manager { get; set; } public Printer Printer { get; set; } public Model3D Model { get; set; } diff --git a/TRPZ_PrintService/Data/ModelSettings.cs b/TRPZ_PrintService/Data/ModelSettings.cs index 5b49225..a35f431 100644 --- a/TRPZ_PrintService/Data/ModelSettings.cs +++ b/TRPZ_PrintService/Data/ModelSettings.cs @@ -1,12 +1,15 @@ +using System.ComponentModel.DataAnnotations.Schema; + namespace TRPZ_PrintService.Data { public class ModelSettings { + [ForeignKey("ModelInOrder")] public int ModelSettingsId { get; set; } public int InfillPercentage { get; set; } public int NozzleDiameter { get; set; } public int LayerHeight { get; set; } - private ModelInOrder ModelInOrder { get; set; } + public ModelInOrder ModelInOrder { get; set; } } } \ No newline at end of file diff --git a/TRPZ_PrintService/Data/Order.cs b/TRPZ_PrintService/Data/Order.cs index 0e5b79d..bc73cc3 100644 --- a/TRPZ_PrintService/Data/Order.cs +++ b/TRPZ_PrintService/Data/Order.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using TRPZ_PrintService.Areas.Identity.Data; namespace TRPZ_PrintService.Data { @@ -12,7 +13,7 @@ public class Order public bool IsConfirmed { get; set; } public bool IsFinished { get; set; } - public Client Client { get; set; } + public TRPZ_PrintServiceUser Client { get; set; } public PromoCode PromoCode { get; set; } public List Models { get; set; } diff --git a/TRPZ_PrintService/Data/TRPZ_PrintServiceContext.cs b/TRPZ_PrintService/Data/TRPZ_PrintServiceContext.cs index 8ab5a19..61dcb97 100644 --- a/TRPZ_PrintService/Data/TRPZ_PrintServiceContext.cs +++ b/TRPZ_PrintService/Data/TRPZ_PrintServiceContext.cs @@ -12,8 +12,7 @@ namespace TRPZ_PrintService.Data { public class TRPZ_PrintServiceContext : IdentityDbContext { - - public DbSet Clients { get; set; } + // public DbSet Clients { get; set; } // public DbSet Managers { get; set; } public DbSet Materials { get; set; } public DbSet Models3D { get; set; } @@ -23,7 +22,7 @@ public class TRPZ_PrintServiceContext : IdentityDbContext public DbSet PostProcessings { get; set; } public DbSet Printers { get; set; } public DbSet PromoCodes { get; set; } - + public TRPZ_PrintServiceContext(DbContextOptions options) : base(options) { @@ -33,6 +32,14 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) { base.OnModelCreating(modelBuilder); modelBuilder.Entity().Property(mio => mio.Scale).HasDefaultValue(1); + modelBuilder.Entity() + .HasMany(o => o.Models) + .WithOne(p => p.Order) + .OnDelete(DeleteBehavior.Cascade); + modelBuilder.Entity() + .HasOne(o => o.ModelSettings) + .WithOne(settings => settings.ModelInOrder) + .OnDelete(DeleteBehavior.Cascade); } } -} +} \ No newline at end of file diff --git a/TRPZ_PrintService/Migrations/20201222133724_casc1.Designer.cs b/TRPZ_PrintService/Migrations/20201222133724_casc1.Designer.cs new file mode 100644 index 0000000..363c3c5 --- /dev/null +++ b/TRPZ_PrintService/Migrations/20201222133724_casc1.Designer.cs @@ -0,0 +1,593 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; +using TRPZ_PrintService.Data; + +namespace TRPZ_PrintService.Migrations +{ + [DbContext(typeof(TRPZ_PrintServiceContext))] + [Migration("20201222133724_casc1")] + partial class casc1 + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .UseIdentityByDefaultColumns() + .HasAnnotation("Relational:MaxIdentifierLength", 63) + .HasAnnotation("ProductVersion", "5.0.1"); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("text"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex"); + + b.ToTable("AspNetRoles"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .UseIdentityByDefaultColumn(); + + b.Property("ClaimType") + .HasColumnType("text"); + + b.Property("ClaimValue") + .HasColumnType("text"); + + b.Property("RoleId") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetRoleClaims"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .UseIdentityByDefaultColumn(); + + b.Property("ClaimType") + .HasColumnType("text"); + + b.Property("ClaimValue") + .HasColumnType("text"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserClaims"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("ProviderKey") + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("ProviderDisplayName") + .HasColumnType("text"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserLogins"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("text"); + + b.Property("RoleId") + .HasColumnType("text"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetUserRoles"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("text"); + + b.Property("LoginProvider") + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("Name") + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("Value") + .HasColumnType("text"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AspNetUserTokens"); + }); + + modelBuilder.Entity("TRPZ_PrintService.Areas.Identity.Data.TRPZ_PrintServiceUser", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("AccessFailedCount") + .HasColumnType("integer"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("text"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("EmailConfirmed") + .HasColumnType("boolean"); + + b.Property("FirstName") + .HasColumnType("text"); + + b.Property("LastName") + .HasColumnType("text"); + + b.Property("LockoutEnabled") + .HasColumnType("boolean"); + + b.Property("LockoutEnd") + .HasColumnType("timestamp with time zone"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("PasswordHash") + .HasColumnType("text"); + + b.Property("PhoneNumber") + .HasColumnType("text"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("boolean"); + + b.Property("SecurityStamp") + .HasColumnType("text"); + + b.Property("TwoFactorEnabled") + .HasColumnType("boolean"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex"); + + b.ToTable("AspNetUsers"); + }); + + modelBuilder.Entity("TRPZ_PrintService.Data.Material", b => + { + b.Property("MaterialId") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .UseIdentityByDefaultColumn(); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("Price") + .HasColumnType("integer"); + + b.HasKey("MaterialId"); + + b.ToTable("Materials"); + }); + + modelBuilder.Entity("TRPZ_PrintService.Data.Model3D", b => + { + b.Property("Model3DId") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .UseIdentityByDefaultColumn(); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("FilePath") + .HasColumnType("text"); + + b.HasKey("Model3DId"); + + b.ToTable("Models3D"); + }); + + modelBuilder.Entity("TRPZ_PrintService.Data.ModelInOrder", b => + { + b.Property("ModelInOrderId") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .UseIdentityByDefaultColumn(); + + b.Property("HasSolubleSupports") + .HasColumnType("boolean"); + + b.Property("ManagerId") + .HasColumnType("text"); + + b.Property("MaterialId") + .HasColumnType("integer"); + + b.Property("Model3DId") + .HasColumnType("integer"); + + b.Property("ModelSettingsId") + .HasColumnType("integer"); + + b.Property("OrderId") + .HasColumnType("integer"); + + b.Property("PostProcessingId") + .HasColumnType("integer"); + + b.Property("PriceTotal") + .HasColumnType("integer"); + + b.Property("PrinterId") + .HasColumnType("integer"); + + b.Property("Scale") + .ValueGeneratedOnAdd() + .HasColumnType("double precision") + .HasDefaultValue(1.0); + + b.HasKey("ModelInOrderId"); + + b.HasIndex("ManagerId"); + + b.HasIndex("MaterialId"); + + b.HasIndex("Model3DId"); + + b.HasIndex("ModelSettingsId"); + + b.HasIndex("OrderId"); + + b.HasIndex("PostProcessingId"); + + b.HasIndex("PrinterId"); + + b.ToTable("ModelsInOrders"); + }); + + modelBuilder.Entity("TRPZ_PrintService.Data.ModelSettings", b => + { + b.Property("ModelSettingsId") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .UseIdentityByDefaultColumn(); + + b.Property("InfillPercentage") + .HasColumnType("integer"); + + b.Property("LayerHeight") + .HasColumnType("integer"); + + b.Property("NozzleDiameter") + .HasColumnType("integer"); + + b.HasKey("ModelSettingsId"); + + b.ToTable("ModelsSettings"); + }); + + modelBuilder.Entity("TRPZ_PrintService.Data.Order", b => + { + b.Property("OrderId") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .UseIdentityByDefaultColumn(); + + b.Property("ClientId") + .HasColumnType("text"); + + b.Property("IsConfirmed") + .HasColumnType("boolean"); + + b.Property("IsFinished") + .HasColumnType("boolean"); + + b.Property("IsSent") + .HasColumnType("boolean"); + + b.Property("PromoCodeId") + .HasColumnType("integer"); + + b.Property("Timestamp") + .HasColumnType("timestamp without time zone"); + + b.Property("TotalPrintTime") + .HasColumnType("interval"); + + b.HasKey("OrderId"); + + b.HasIndex("ClientId"); + + b.HasIndex("PromoCodeId"); + + b.ToTable("Orders"); + }); + + modelBuilder.Entity("TRPZ_PrintService.Data.PostProcessing", b => + { + b.Property("PostProcessingId") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .UseIdentityByDefaultColumn(); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("Price") + .HasColumnType("integer"); + + b.HasKey("PostProcessingId"); + + b.ToTable("PostProcessings"); + }); + + modelBuilder.Entity("TRPZ_PrintService.Data.Printer", b => + { + b.Property("PrinterId") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .UseIdentityByDefaultColumn(); + + b.Property("BuildPlateLength") + .HasColumnType("integer"); + + b.Property("BuildPlateWidth") + .HasColumnType("integer"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("ExtruderCount") + .HasColumnType("integer"); + + b.Property("MaxTemp") + .HasColumnType("integer"); + + b.Property("Name") + .HasColumnType("text"); + + b.HasKey("PrinterId"); + + b.ToTable("Printers"); + }); + + modelBuilder.Entity("TRPZ_PrintService.Data.PromoCode", b => + { + b.Property("PromoCodeId") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .UseIdentityByDefaultColumn(); + + b.Property("ActiveTo") + .HasColumnType("timestamp without time zone"); + + b.Property("Count") + .HasColumnType("integer"); + + b.Property("Discount") + .HasColumnType("integer"); + + b.Property("Issue") + .HasColumnType("timestamp without time zone"); + + b.HasKey("PromoCodeId"); + + b.ToTable("PromoCodes"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("TRPZ_PrintService.Areas.Identity.Data.TRPZ_PrintServiceUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("TRPZ_PrintService.Areas.Identity.Data.TRPZ_PrintServiceUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("TRPZ_PrintService.Areas.Identity.Data.TRPZ_PrintServiceUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("TRPZ_PrintService.Areas.Identity.Data.TRPZ_PrintServiceUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("TRPZ_PrintService.Data.ModelInOrder", b => + { + b.HasOne("TRPZ_PrintService.Areas.Identity.Data.TRPZ_PrintServiceUser", "Manager") + .WithMany() + .HasForeignKey("ManagerId"); + + b.HasOne("TRPZ_PrintService.Data.Material", "Material") + .WithMany("ModelsInOrders") + .HasForeignKey("MaterialId"); + + b.HasOne("TRPZ_PrintService.Data.Model3D", "Model") + .WithMany() + .HasForeignKey("Model3DId"); + + b.HasOne("TRPZ_PrintService.Data.ModelSettings", "ModelSettings") + .WithMany() + .HasForeignKey("ModelSettingsId"); + + b.HasOne("TRPZ_PrintService.Data.Order", "Order") + .WithMany("Models") + .HasForeignKey("OrderId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("TRPZ_PrintService.Data.PostProcessing", "PostProcessing") + .WithMany("ModelsInOrders") + .HasForeignKey("PostProcessingId"); + + b.HasOne("TRPZ_PrintService.Data.Printer", "Printer") + .WithMany() + .HasForeignKey("PrinterId"); + + b.Navigation("Manager"); + + b.Navigation("Material"); + + b.Navigation("Model"); + + b.Navigation("ModelSettings"); + + b.Navigation("Order"); + + b.Navigation("PostProcessing"); + + b.Navigation("Printer"); + }); + + modelBuilder.Entity("TRPZ_PrintService.Data.Order", b => + { + b.HasOne("TRPZ_PrintService.Areas.Identity.Data.TRPZ_PrintServiceUser", "Client") + .WithMany() + .HasForeignKey("ClientId"); + + b.HasOne("TRPZ_PrintService.Data.PromoCode", "PromoCode") + .WithMany("Orders") + .HasForeignKey("PromoCodeId"); + + b.Navigation("Client"); + + b.Navigation("PromoCode"); + }); + + modelBuilder.Entity("TRPZ_PrintService.Data.Material", b => + { + b.Navigation("ModelsInOrders"); + }); + + modelBuilder.Entity("TRPZ_PrintService.Data.Order", b => + { + b.Navigation("Models"); + }); + + modelBuilder.Entity("TRPZ_PrintService.Data.PostProcessing", b => + { + b.Navigation("ModelsInOrders"); + }); + + modelBuilder.Entity("TRPZ_PrintService.Data.PromoCode", b => + { + b.Navigation("Orders"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/TRPZ_PrintService/Migrations/20201222133724_casc1.cs b/TRPZ_PrintService/Migrations/20201222133724_casc1.cs new file mode 100644 index 0000000..5263e72 --- /dev/null +++ b/TRPZ_PrintService/Migrations/20201222133724_casc1.cs @@ -0,0 +1,58 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +namespace TRPZ_PrintService.Migrations +{ + public partial class casc1 : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_ModelsInOrders_Orders_OrderId", + table: "ModelsInOrders"); + + migrationBuilder.DropColumn( + name: "Discriminator", + table: "AspNetUsers"); + + migrationBuilder.DropColumn( + name: "Type", + table: "AspNetUsers"); + + migrationBuilder.AddForeignKey( + name: "FK_ModelsInOrders_Orders_OrderId", + table: "ModelsInOrders", + column: "OrderId", + principalTable: "Orders", + principalColumn: "OrderId", + onDelete: ReferentialAction.Cascade); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_ModelsInOrders_Orders_OrderId", + table: "ModelsInOrders"); + + migrationBuilder.AddColumn( + name: "Discriminator", + table: "AspNetUsers", + type: "text", + nullable: false, + defaultValue: ""); + + migrationBuilder.AddColumn( + name: "Type", + table: "AspNetUsers", + type: "text", + nullable: true); + + migrationBuilder.AddForeignKey( + name: "FK_ModelsInOrders_Orders_OrderId", + table: "ModelsInOrders", + column: "OrderId", + principalTable: "Orders", + principalColumn: "OrderId", + onDelete: ReferentialAction.Restrict); + } + } +} diff --git a/TRPZ_PrintService/Migrations/20201222134232_casc2.Designer.cs b/TRPZ_PrintService/Migrations/20201222134232_casc2.Designer.cs new file mode 100644 index 0000000..48e4a0f --- /dev/null +++ b/TRPZ_PrintService/Migrations/20201222134232_casc2.Designer.cs @@ -0,0 +1,596 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; +using TRPZ_PrintService.Data; + +namespace TRPZ_PrintService.Migrations +{ + [DbContext(typeof(TRPZ_PrintServiceContext))] + [Migration("20201222134232_casc2")] + partial class casc2 + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .UseIdentityByDefaultColumns() + .HasAnnotation("Relational:MaxIdentifierLength", 63) + .HasAnnotation("ProductVersion", "5.0.1"); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("text"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex"); + + b.ToTable("AspNetRoles"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .UseIdentityByDefaultColumn(); + + b.Property("ClaimType") + .HasColumnType("text"); + + b.Property("ClaimValue") + .HasColumnType("text"); + + b.Property("RoleId") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetRoleClaims"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .UseIdentityByDefaultColumn(); + + b.Property("ClaimType") + .HasColumnType("text"); + + b.Property("ClaimValue") + .HasColumnType("text"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserClaims"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("ProviderKey") + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("ProviderDisplayName") + .HasColumnType("text"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserLogins"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("text"); + + b.Property("RoleId") + .HasColumnType("text"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetUserRoles"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("text"); + + b.Property("LoginProvider") + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("Name") + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("Value") + .HasColumnType("text"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AspNetUserTokens"); + }); + + modelBuilder.Entity("TRPZ_PrintService.Areas.Identity.Data.TRPZ_PrintServiceUser", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("AccessFailedCount") + .HasColumnType("integer"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("text"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("EmailConfirmed") + .HasColumnType("boolean"); + + b.Property("FirstName") + .HasColumnType("text"); + + b.Property("LastName") + .HasColumnType("text"); + + b.Property("LockoutEnabled") + .HasColumnType("boolean"); + + b.Property("LockoutEnd") + .HasColumnType("timestamp with time zone"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("PasswordHash") + .HasColumnType("text"); + + b.Property("PhoneNumber") + .HasColumnType("text"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("boolean"); + + b.Property("SecurityStamp") + .HasColumnType("text"); + + b.Property("TwoFactorEnabled") + .HasColumnType("boolean"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex"); + + b.ToTable("AspNetUsers"); + }); + + modelBuilder.Entity("TRPZ_PrintService.Data.Material", b => + { + b.Property("MaterialId") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .UseIdentityByDefaultColumn(); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("Price") + .HasColumnType("integer"); + + b.HasKey("MaterialId"); + + b.ToTable("Materials"); + }); + + modelBuilder.Entity("TRPZ_PrintService.Data.Model3D", b => + { + b.Property("Model3DId") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .UseIdentityByDefaultColumn(); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("FilePath") + .HasColumnType("text"); + + b.HasKey("Model3DId"); + + b.ToTable("Models3D"); + }); + + modelBuilder.Entity("TRPZ_PrintService.Data.ModelInOrder", b => + { + b.Property("ModelInOrderId") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .UseIdentityByDefaultColumn(); + + b.Property("HasSolubleSupports") + .HasColumnType("boolean"); + + b.Property("ManagerId") + .HasColumnType("text"); + + b.Property("MaterialId") + .HasColumnType("integer"); + + b.Property("Model3DId") + .HasColumnType("integer"); + + b.Property("OrderId") + .HasColumnType("integer"); + + b.Property("PostProcessingId") + .HasColumnType("integer"); + + b.Property("PriceTotal") + .HasColumnType("integer"); + + b.Property("PrinterId") + .HasColumnType("integer"); + + b.Property("Scale") + .ValueGeneratedOnAdd() + .HasColumnType("double precision") + .HasDefaultValue(1.0); + + b.HasKey("ModelInOrderId"); + + b.HasIndex("ManagerId"); + + b.HasIndex("MaterialId"); + + b.HasIndex("Model3DId"); + + b.HasIndex("OrderId"); + + b.HasIndex("PostProcessingId"); + + b.HasIndex("PrinterId"); + + b.ToTable("ModelsInOrders"); + }); + + modelBuilder.Entity("TRPZ_PrintService.Data.ModelSettings", b => + { + b.Property("ModelSettingsId") + .HasColumnType("integer"); + + b.Property("InfillPercentage") + .HasColumnType("integer"); + + b.Property("LayerHeight") + .HasColumnType("integer"); + + b.Property("NozzleDiameter") + .HasColumnType("integer"); + + b.HasKey("ModelSettingsId"); + + b.ToTable("ModelsSettings"); + }); + + modelBuilder.Entity("TRPZ_PrintService.Data.Order", b => + { + b.Property("OrderId") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .UseIdentityByDefaultColumn(); + + b.Property("ClientId") + .HasColumnType("text"); + + b.Property("IsConfirmed") + .HasColumnType("boolean"); + + b.Property("IsFinished") + .HasColumnType("boolean"); + + b.Property("IsSent") + .HasColumnType("boolean"); + + b.Property("PromoCodeId") + .HasColumnType("integer"); + + b.Property("Timestamp") + .HasColumnType("timestamp without time zone"); + + b.Property("TotalPrintTime") + .HasColumnType("interval"); + + b.HasKey("OrderId"); + + b.HasIndex("ClientId"); + + b.HasIndex("PromoCodeId"); + + b.ToTable("Orders"); + }); + + modelBuilder.Entity("TRPZ_PrintService.Data.PostProcessing", b => + { + b.Property("PostProcessingId") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .UseIdentityByDefaultColumn(); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("Price") + .HasColumnType("integer"); + + b.HasKey("PostProcessingId"); + + b.ToTable("PostProcessings"); + }); + + modelBuilder.Entity("TRPZ_PrintService.Data.Printer", b => + { + b.Property("PrinterId") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .UseIdentityByDefaultColumn(); + + b.Property("BuildPlateLength") + .HasColumnType("integer"); + + b.Property("BuildPlateWidth") + .HasColumnType("integer"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("ExtruderCount") + .HasColumnType("integer"); + + b.Property("MaxTemp") + .HasColumnType("integer"); + + b.Property("Name") + .HasColumnType("text"); + + b.HasKey("PrinterId"); + + b.ToTable("Printers"); + }); + + modelBuilder.Entity("TRPZ_PrintService.Data.PromoCode", b => + { + b.Property("PromoCodeId") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .UseIdentityByDefaultColumn(); + + b.Property("ActiveTo") + .HasColumnType("timestamp without time zone"); + + b.Property("Count") + .HasColumnType("integer"); + + b.Property("Discount") + .HasColumnType("integer"); + + b.Property("Issue") + .HasColumnType("timestamp without time zone"); + + b.HasKey("PromoCodeId"); + + b.ToTable("PromoCodes"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("TRPZ_PrintService.Areas.Identity.Data.TRPZ_PrintServiceUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("TRPZ_PrintService.Areas.Identity.Data.TRPZ_PrintServiceUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("TRPZ_PrintService.Areas.Identity.Data.TRPZ_PrintServiceUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("TRPZ_PrintService.Areas.Identity.Data.TRPZ_PrintServiceUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("TRPZ_PrintService.Data.ModelInOrder", b => + { + b.HasOne("TRPZ_PrintService.Areas.Identity.Data.TRPZ_PrintServiceUser", "Manager") + .WithMany() + .HasForeignKey("ManagerId"); + + b.HasOne("TRPZ_PrintService.Data.Material", "Material") + .WithMany("ModelsInOrders") + .HasForeignKey("MaterialId"); + + b.HasOne("TRPZ_PrintService.Data.Model3D", "Model") + .WithMany() + .HasForeignKey("Model3DId"); + + b.HasOne("TRPZ_PrintService.Data.Order", "Order") + .WithMany("Models") + .HasForeignKey("OrderId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("TRPZ_PrintService.Data.PostProcessing", "PostProcessing") + .WithMany("ModelsInOrders") + .HasForeignKey("PostProcessingId"); + + b.HasOne("TRPZ_PrintService.Data.Printer", "Printer") + .WithMany() + .HasForeignKey("PrinterId"); + + b.Navigation("Manager"); + + b.Navigation("Material"); + + b.Navigation("Model"); + + b.Navigation("Order"); + + b.Navigation("PostProcessing"); + + b.Navigation("Printer"); + }); + + modelBuilder.Entity("TRPZ_PrintService.Data.ModelSettings", b => + { + b.HasOne("TRPZ_PrintService.Data.ModelInOrder", "ModelInOrder") + .WithOne("ModelSettings") + .HasForeignKey("TRPZ_PrintService.Data.ModelSettings", "ModelSettingsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ModelInOrder"); + }); + + modelBuilder.Entity("TRPZ_PrintService.Data.Order", b => + { + b.HasOne("TRPZ_PrintService.Areas.Identity.Data.TRPZ_PrintServiceUser", "Client") + .WithMany() + .HasForeignKey("ClientId"); + + b.HasOne("TRPZ_PrintService.Data.PromoCode", "PromoCode") + .WithMany("Orders") + .HasForeignKey("PromoCodeId"); + + b.Navigation("Client"); + + b.Navigation("PromoCode"); + }); + + modelBuilder.Entity("TRPZ_PrintService.Data.Material", b => + { + b.Navigation("ModelsInOrders"); + }); + + modelBuilder.Entity("TRPZ_PrintService.Data.ModelInOrder", b => + { + b.Navigation("ModelSettings"); + }); + + modelBuilder.Entity("TRPZ_PrintService.Data.Order", b => + { + b.Navigation("Models"); + }); + + modelBuilder.Entity("TRPZ_PrintService.Data.PostProcessing", b => + { + b.Navigation("ModelsInOrders"); + }); + + modelBuilder.Entity("TRPZ_PrintService.Data.PromoCode", b => + { + b.Navigation("Orders"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/TRPZ_PrintService/Migrations/20201222134232_casc2.cs b/TRPZ_PrintService/Migrations/20201222134232_casc2.cs new file mode 100644 index 0000000..bebd917 --- /dev/null +++ b/TRPZ_PrintService/Migrations/20201222134232_casc2.cs @@ -0,0 +1,75 @@ +using Microsoft.EntityFrameworkCore.Migrations; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +namespace TRPZ_PrintService.Migrations +{ + public partial class casc2 : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_ModelsInOrders_ModelsSettings_ModelSettingsId", + table: "ModelsInOrders"); + + migrationBuilder.DropIndex( + name: "IX_ModelsInOrders_ModelSettingsId", + table: "ModelsInOrders"); + + migrationBuilder.DropColumn( + name: "ModelSettingsId", + table: "ModelsInOrders"); + + migrationBuilder.AlterColumn( + name: "ModelSettingsId", + table: "ModelsSettings", + type: "integer", + nullable: false, + oldClrType: typeof(int), + oldType: "integer") + .OldAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + migrationBuilder.AddForeignKey( + name: "FK_ModelsSettings_ModelsInOrders_ModelSettingsId", + table: "ModelsSettings", + column: "ModelSettingsId", + principalTable: "ModelsInOrders", + principalColumn: "ModelInOrderId", + onDelete: ReferentialAction.Cascade); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_ModelsSettings_ModelsInOrders_ModelSettingsId", + table: "ModelsSettings"); + + migrationBuilder.AlterColumn( + name: "ModelSettingsId", + table: "ModelsSettings", + type: "integer", + nullable: false, + oldClrType: typeof(int), + oldType: "integer") + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + migrationBuilder.AddColumn( + name: "ModelSettingsId", + table: "ModelsInOrders", + type: "integer", + nullable: true); + + migrationBuilder.CreateIndex( + name: "IX_ModelsInOrders_ModelSettingsId", + table: "ModelsInOrders", + column: "ModelSettingsId"); + + migrationBuilder.AddForeignKey( + name: "FK_ModelsInOrders_ModelsSettings_ModelSettingsId", + table: "ModelsInOrders", + column: "ModelSettingsId", + principalTable: "ModelsSettings", + principalColumn: "ModelSettingsId", + onDelete: ReferentialAction.Restrict); + } + } +} diff --git a/TRPZ_PrintService/Migrations/TRPZ_PrintServiceContextModelSnapshot.cs b/TRPZ_PrintService/Migrations/TRPZ_PrintServiceContextModelSnapshot.cs index 46205a7..2f6acde 100644 --- a/TRPZ_PrintService/Migrations/TRPZ_PrintServiceContextModelSnapshot.cs +++ b/TRPZ_PrintService/Migrations/TRPZ_PrintServiceContextModelSnapshot.cs @@ -165,10 +165,6 @@ protected override void BuildModel(ModelBuilder modelBuilder) .IsConcurrencyToken() .HasColumnType("text"); - b.Property("Discriminator") - .IsRequired() - .HasColumnType("text"); - b.Property("Email") .HasMaxLength(256) .HasColumnType("character varying(256)"); @@ -225,8 +221,6 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasDatabaseName("UserNameIndex"); b.ToTable("AspNetUsers"); - - b.HasDiscriminator("Discriminator").HasValue("TRPZ_PrintServiceUser"); }); modelBuilder.Entity("TRPZ_PrintService.Data.Material", b => @@ -288,9 +282,6 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("Model3DId") .HasColumnType("integer"); - b.Property("ModelSettingsId") - .HasColumnType("integer"); - b.Property("OrderId") .HasColumnType("integer"); @@ -316,8 +307,6 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasIndex("Model3DId"); - b.HasIndex("ModelSettingsId"); - b.HasIndex("OrderId"); b.HasIndex("PostProcessingId"); @@ -330,9 +319,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) modelBuilder.Entity("TRPZ_PrintService.Data.ModelSettings", b => { b.Property("ModelSettingsId") - .ValueGeneratedOnAdd() - .HasColumnType("integer") - .UseIdentityByDefaultColumn(); + .HasColumnType("integer"); b.Property("InfillPercentage") .HasColumnType("integer"); @@ -460,23 +447,6 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.ToTable("PromoCodes"); }); - modelBuilder.Entity("TRPZ_PrintService.Data.Client", b => - { - b.HasBaseType("TRPZ_PrintService.Areas.Identity.Data.TRPZ_PrintServiceUser"); - - b.HasDiscriminator().HasValue("Client"); - }); - - modelBuilder.Entity("TRPZ_PrintService.Data.Manager", b => - { - b.HasBaseType("TRPZ_PrintService.Areas.Identity.Data.TRPZ_PrintServiceUser"); - - b.Property("Type") - .HasColumnType("text"); - - b.HasDiscriminator().HasValue("Manager"); - }); - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => { b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) @@ -530,8 +500,8 @@ protected override void BuildModel(ModelBuilder modelBuilder) modelBuilder.Entity("TRPZ_PrintService.Data.ModelInOrder", b => { - b.HasOne("TRPZ_PrintService.Data.Manager", "Manager") - .WithMany("ModelsInOrders") + b.HasOne("TRPZ_PrintService.Areas.Identity.Data.TRPZ_PrintServiceUser", "Manager") + .WithMany() .HasForeignKey("ManagerId"); b.HasOne("TRPZ_PrintService.Data.Material", "Material") @@ -542,13 +512,10 @@ protected override void BuildModel(ModelBuilder modelBuilder) .WithMany() .HasForeignKey("Model3DId"); - b.HasOne("TRPZ_PrintService.Data.ModelSettings", "ModelSettings") - .WithMany() - .HasForeignKey("ModelSettingsId"); - b.HasOne("TRPZ_PrintService.Data.Order", "Order") .WithMany("Models") - .HasForeignKey("OrderId"); + .HasForeignKey("OrderId") + .OnDelete(DeleteBehavior.Cascade); b.HasOne("TRPZ_PrintService.Data.PostProcessing", "PostProcessing") .WithMany("ModelsInOrders") @@ -564,8 +531,6 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Navigation("Model"); - b.Navigation("ModelSettings"); - b.Navigation("Order"); b.Navigation("PostProcessing"); @@ -573,10 +538,21 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Navigation("Printer"); }); + modelBuilder.Entity("TRPZ_PrintService.Data.ModelSettings", b => + { + b.HasOne("TRPZ_PrintService.Data.ModelInOrder", "ModelInOrder") + .WithOne("ModelSettings") + .HasForeignKey("TRPZ_PrintService.Data.ModelSettings", "ModelSettingsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ModelInOrder"); + }); + modelBuilder.Entity("TRPZ_PrintService.Data.Order", b => { - b.HasOne("TRPZ_PrintService.Data.Client", "Client") - .WithMany("Orders") + b.HasOne("TRPZ_PrintService.Areas.Identity.Data.TRPZ_PrintServiceUser", "Client") + .WithMany() .HasForeignKey("ClientId"); b.HasOne("TRPZ_PrintService.Data.PromoCode", "PromoCode") @@ -593,6 +569,11 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Navigation("ModelsInOrders"); }); + modelBuilder.Entity("TRPZ_PrintService.Data.ModelInOrder", b => + { + b.Navigation("ModelSettings"); + }); + modelBuilder.Entity("TRPZ_PrintService.Data.Order", b => { b.Navigation("Models"); @@ -607,16 +588,6 @@ protected override void BuildModel(ModelBuilder modelBuilder) { b.Navigation("Orders"); }); - - modelBuilder.Entity("TRPZ_PrintService.Data.Client", b => - { - b.Navigation("Orders"); - }); - - modelBuilder.Entity("TRPZ_PrintService.Data.Manager", b => - { - b.Navigation("ModelsInOrders"); - }); #pragma warning restore 612, 618 } } diff --git a/TRPZ_PrintService/Pages/EditMIO.cshtml b/TRPZ_PrintService/Pages/EditMIO.cshtml new file mode 100644 index 0000000..980d66d --- /dev/null +++ b/TRPZ_PrintService/Pages/EditMIO.cshtml @@ -0,0 +1,35 @@ +@page +@model TRPZ_PrintService.Pages.EditMIO + +@{ + Layout = "_Layout"; +} + +
+
+

Any Heading

+ +
+ + + +
+ + + +
+ + + +
+ + + +
+ + +
+ +
+ +
\ No newline at end of file diff --git a/TRPZ_PrintService/Pages/EditMIO.cshtml.cs b/TRPZ_PrintService/Pages/EditMIO.cshtml.cs new file mode 100644 index 0000000..9150fd4 --- /dev/null +++ b/TRPZ_PrintService/Pages/EditMIO.cshtml.cs @@ -0,0 +1,113 @@ +using System; +using System.Linq; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.RazorPages; +using Microsoft.AspNetCore.Mvc.Rendering; +using Microsoft.EntityFrameworkCore; +using TRPZ_PrintService.Data; + +namespace TRPZ_PrintService.Pages +{ + public class EditMIO : PageModel + { + private TRPZ_PrintServiceContext _context; + + public SelectList Materials { get; set; } + + public SelectList PostProcessings { get; set; } + + public SelectList Printers { get; set; } + [BindProperty] public Order order { get; set; } + public ModelInOrder mio { get; set; } + + + public class FormModel + { + public double Scale { get; set; } + public bool HasSolubleSupports { get; set; } + public int PostProcessingId { get; set; } + public String Description { get; set; } + + public int InfillPercentage { get; set; } + public int Layerheight { get; set; } + public int NozzleDiameter { get; set; } + + public int PrinterId { get; set; } + public int MaterialId { get; set; } + } + + [BindProperty] public FormModel Fmodel { get; set; } + + public EditMIO(TRPZ_PrintServiceContext context) + { + _context = context; + } + + + public IActionResult OnGetEdit(int? orderId, int mioid) + { + Fmodel = new FormModel(); + mio = _context.ModelsInOrders + .Include(inOrder => inOrder.Model) + .Include(inOrder => inOrder.Order) + .Include(inOrder => inOrder.Material) + .Include(inOrder => inOrder.Printer) + .Include(inOrder => inOrder.ModelSettings) + .Include(inOrder => inOrder.PostProcessing) + .First(inOrder => inOrder.ModelInOrderId == mioid); + + order = mio.Order; + + Fmodel.Description = mio.Model.Description; + Fmodel.Layerheight = mio.ModelSettings.LayerHeight; + Fmodel.Scale = mio.Scale; + Fmodel.InfillPercentage = mio.ModelSettings.InfillPercentage; + Fmodel.MaterialId = mio.Material.MaterialId; + Fmodel.NozzleDiameter = mio.ModelSettings.NozzleDiameter; + Fmodel.PrinterId = mio.Printer.PrinterId; + Fmodel.HasSolubleSupports = mio.HasSolubleSupports; + Fmodel.PostProcessingId = mio.PostProcessing.PostProcessingId; + + Printers = new SelectList(_context.Printers.ToList(), nameof(Printer.PrinterId), nameof(Printer.Name)); + Materials = new SelectList(_context.Materials.ToList(), nameof(Material.MaterialId), nameof(Material.Name)); + PostProcessings = new SelectList(_context.PostProcessings.ToList(), nameof(PostProcessing.PostProcessingId), + nameof(PostProcessing.Name)); + + return Page(); + } + + public IActionResult OnPostEdit(int? orderId, int mioid) + { + mio = _context.ModelsInOrders + .Include(inOrder => inOrder.Model) + .Include(inOrder => inOrder.Order) + .Include(inOrder => inOrder.Material) + .Include(inOrder => inOrder.Printer) + .Include(inOrder => inOrder.ModelSettings) + .Include(inOrder => inOrder.PostProcessing) + .First(inOrder => inOrder.ModelInOrderId == mioid); + + order = mio.Order; + + mio.Model.Description = Fmodel.Description; + mio.ModelSettings.LayerHeight = Fmodel.Layerheight; + mio.Scale = Fmodel.Scale; + mio.ModelSettings.InfillPercentage = Fmodel.InfillPercentage; + mio.Material = _context.Materials.Find(Fmodel.MaterialId); + mio.ModelSettings.NozzleDiameter = Fmodel.NozzleDiameter; + mio.Printer.PrinterId = Fmodel.PrinterId; + mio.HasSolubleSupports = Fmodel.HasSolubleSupports; + mio.PostProcessing.PostProcessingId = Fmodel.PostProcessingId; + + // Printers = new SelectList(_context.Printers.ToList(), nameof(Printer.PrinterId), nameof(Printer.Name)); + // Materials = new SelectList(_context.Materials.ToList(), nameof(Material.MaterialId), nameof(Material.Name)); + // PostProcessings = new SelectList(_context.PostProcessings.ToList(), nameof(PostProcessing.PostProcessingId), + // nameof(PostProcessing.Name)); + + _context.SaveChanges(); + + + return new RedirectToPageResult("/ModelInOrder", "", new{id = order.OrderId}); + } + } +} \ No newline at end of file diff --git a/TRPZ_PrintService/Pages/Index.cshtml b/TRPZ_PrintService/Pages/Index.cshtml index b5f0c15..5674c6a 100644 --- a/TRPZ_PrintService/Pages/Index.cshtml +++ b/TRPZ_PrintService/Pages/Index.cshtml @@ -5,6 +5,20 @@ }
-

Welcome

-

Learn about building Web apps with ASP.NET Core.

-
+

Print Service

+ + \ No newline at end of file diff --git a/TRPZ_PrintService/Pages/ManageOrders.cshtml b/TRPZ_PrintService/Pages/ManageOrders.cshtml new file mode 100644 index 0000000..c503ff2 --- /dev/null +++ b/TRPZ_PrintService/Pages/ManageOrders.cshtml @@ -0,0 +1,78 @@ +@page +@model TRPZ_PrintService.Pages.ManageOrders + +@{ + Layout = "Shared/_Layout"; +} + + + + + + + + + + + + @foreach (var uRole in Model.UsersRoles) + { + + + + + + + + } + +
+ Client + + OrderId + + Order Status + + Models + +   +
+ @Html.DisplayFor(modelItem => uRole.user.Id) + + @Html.DisplayFor(modelItem => uRole.user.FirstName) + @Html.DisplayFor(modelItem => uRole.user.LastName) + + @Html.DisplayFor(modelItem => uRole.user.UserName) + + @foreach (var role in uRole.roles) + { + @Html.DisplayFor(modelItem => role) +
+ } +
+
    + + @if (!uRole.roles.Contains("Admin")) + { +
    + +
    } + @if (!uRole.roles.Contains("Manager")) + { +
    + +
    } + + +
    + +
    + + +
    + +
    + +
+ +
\ No newline at end of file diff --git a/TRPZ_PrintService/Pages/ManageOrders.cshtml.cs b/TRPZ_PrintService/Pages/ManageOrders.cshtml.cs new file mode 100644 index 0000000..f329040 --- /dev/null +++ b/TRPZ_PrintService/Pages/ManageOrders.cshtml.cs @@ -0,0 +1,26 @@ +using System.Threading.Tasks; +using Microsoft.AspNetCore.Identity; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.RazorPages; +using TRPZ_PrintService.Areas.Identity.Data; +using TRPZ_PrintService.Data; + +namespace TRPZ_PrintService.Pages +{ + public class ManageOrders : PageModel + { + private TRPZ_PrintServiceContext _context; + private UserManager _userManager; + + public ManageOrders(TRPZ_PrintServiceContext context, UserManager userManager) + { + _context = context; + _userManager = userManager; + } + + public async Task OnGet() + { + return new RedirectToPageResult("/ManageStaff", ""); + } + } +} \ No newline at end of file diff --git a/TRPZ_PrintService/Pages/ManageStaff.cshtml b/TRPZ_PrintService/Pages/ManageStaff.cshtml new file mode 100644 index 0000000..85c108e --- /dev/null +++ b/TRPZ_PrintService/Pages/ManageStaff.cshtml @@ -0,0 +1,79 @@ +@page +@model TRPZ_PrintService.Pages.ManageStaff + +@{ + Layout = "_Layout"; +} + + + + + + + + + + + + + @foreach (var uRole in Model.UsersRoles) + { + + + + + + + + } + +
+ Id + + Name + + Email + + Roles + +   +
+ @Html.DisplayFor(modelItem => uRole.user.Id) + + @Html.DisplayFor(modelItem => uRole.user.FirstName) + @Html.DisplayFor(modelItem => uRole.user.LastName) + + @Html.DisplayFor(modelItem => uRole.user.UserName) + + @foreach (var role in uRole.roles) + { + @Html.DisplayFor(modelItem => role) +
+ } +
+
    + + @if (!uRole.roles.Contains("Admin")) + { +
    + +
    } + @if (!uRole.roles.Contains("Manager")) + { +
    + +
    } + + +
    + +
    + + +
    + +
    + +
+ +
\ No newline at end of file diff --git a/TRPZ_PrintService/Pages/ManageStaff.cshtml.cs b/TRPZ_PrintService/Pages/ManageStaff.cshtml.cs new file mode 100644 index 0000000..d9e757c --- /dev/null +++ b/TRPZ_PrintService/Pages/ManageStaff.cshtml.cs @@ -0,0 +1,89 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Identity; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.RazorPages; +using TRPZ_PrintService.Areas.Identity.Data; +using TRPZ_PrintService.Data; + +namespace TRPZ_PrintService.Pages +{ + public class ManageStaff : PageModel + { + private TRPZ_PrintServiceContext _context; + private UserManager _userManager; + + public IList UsersRoles { get; set; } + + public class UserRole + { + public TRPZ_PrintServiceUser user { get; set; } + public IList roles { get; set; } + } + + + public ManageStaff(TRPZ_PrintServiceContext context, UserManager userManager) + { + _context = context; + _userManager = userManager; + } + + public async Task OnGet() + { + var users = _userManager.Users.OrderBy(user => user.Id).ToList(); + UsersRoles = new List(); + + users.ForEach(user => + { + var r = _userManager.GetRolesAsync(user); + r.Wait(); + UsersRoles.Add(new UserRole() + { + roles = r.Result, + user = user, + }); + }); + + return Page(); + } + + public async Task OnPostSetRole(string userId, string role) + { + var user = _userManager.Users.First(serviceUser => serviceUser.Id == userId); + var roles = await _userManager.GetRolesAsync(user); + + if (role == "Admin") + { + await _userManager.RemoveFromRoleAsync(user, "Manager"); + await _userManager.AddToRoleAsync(user, "Admin"); + } + + if (role == "Manager") + { + await _userManager.RemoveFromRoleAsync(user, "Admin"); + await _userManager.AddToRoleAsync(user, "Manager"); + } + + if (role == "Client") + { + await _userManager.RemoveFromRoleAsync(user, "Admin"); + await _userManager.RemoveFromRoleAsync(user, "Manager"); + await _userManager.AddToRoleAsync(user, "Client"); + } + + + return new RedirectToPageResult("/ManageStaff", ""); + } + + public async Task OnPostDelete(string userId) + { + var user = _userManager.Users.First(serviceUser => serviceUser.Id == userId); + await _userManager.DeleteAsync(user); + + + return new RedirectToPageResult("/ManageStaff", ""); + } + } +} \ No newline at end of file diff --git a/TRPZ_PrintService/Pages/ModelInOrder.cshtml b/TRPZ_PrintService/Pages/ModelInOrder.cshtml new file mode 100644 index 0000000..7556f0a --- /dev/null +++ b/TRPZ_PrintService/Pages/ModelInOrder.cshtml @@ -0,0 +1,150 @@ +@page +@using TRPZ_PrintService.Data +@model TRPZ_PrintService.Pages.ModelInOrderPage + +@{ + Layout = "Shared/_Layout"; + ViewData["Title"] = "My orders"; +} + +

My orders

+ + + + + + + + + + + + + + @foreach (var mio_model in Model.order.Models) + { + + + + + + + + + } + +
+ Order ID + + Material type + + Has soluble supports + + Total price + + File + +   +
+ @Html.DisplayFor(modelItem => mio_model.ModelInOrderId) + + @Html.DisplayFor(modelItem => mio_model.Material.Name) + + @Html.DisplayFor(modelItem => mio_model.HasSolubleSupports) + + @Html.DisplayFor(modelItem => mio_model.PriceTotal) + + @Html.DisplayFor(modelItem => mio_model.Model.FilePath) + + @if (!(Model.order.IsConfirmed || Model.order.IsFinished)) + { + + + +
+ +
} + else + { + + } +
+@* TODO add modal add window*@ + +@if (!(Model.order.IsConfirmed || Model.order.IsFinished)) +{ + +
+ +
+
+ +
+} + + \ No newline at end of file diff --git a/TRPZ_PrintService/Pages/ModelInOrder.cshtml.cs b/TRPZ_PrintService/Pages/ModelInOrder.cshtml.cs new file mode 100644 index 0000000..31b81bb --- /dev/null +++ b/TRPZ_PrintService/Pages/ModelInOrder.cshtml.cs @@ -0,0 +1,133 @@ +using System; +using System.ComponentModel.DataAnnotations; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.RazorPages; +using Microsoft.AspNetCore.Mvc.Rendering; +using Microsoft.EntityFrameworkCore; +using TRPZ_PrintService.Data; + +namespace TRPZ_PrintService.Pages +{ + public class ModelInOrderPage : PageModel + { + private TRPZ_PrintServiceContext _context; + private IWebHostEnvironment _environment; + + [BindProperty] public Order order { get; set; } + + public class FormModel + { + public double Scale { get; set; } + public bool HasSolubleSupports { get; set; } + public int PostProcessingId { get; set; } + public String Description { get; set; } + + public int InfillPercentage { get; set; } + public int Layerheight { get; set; } + public int NozzleDiameter { get; set; } + + public int PrinterId { get; set; } + public int MaterialId { get; set; } + } + + [BindProperty] public FormModel Fmodel { get; set; } + public SelectList Printers { get; set; } + + public SelectList Materials { get; set; } + public SelectList PostProcessings { get; set; } + [Required] [BindProperty] public IFormFile Upload { get; set; } + + + public ModelInOrderPage(TRPZ_PrintServiceContext context, IWebHostEnvironment environment) + { + _context = context; + _environment = environment; + } + + public IActionResult OnGet(int? id) + { + if (id == null) + { + return NotFound(); + } + + + order = _context.Orders.Include(order1 => order1.Models) + .ThenInclude(inOrder => inOrder.Model) + .Include(order1 => order1.Models) + .ThenInclude(inOrder => inOrder.ModelSettings) + .First(order1 => order1.OrderId == id); + if (order == null) + { + return NotFound(); + } + + Printers = new SelectList(_context.Printers.ToList(), nameof(Printer.PrinterId), nameof(Printer.Name)); + Materials = new SelectList(_context.Materials.ToList(), nameof(Material.MaterialId), nameof(Material.Name)); + PostProcessings = new SelectList(_context.PostProcessings.ToList(), nameof(PostProcessing.PostProcessingId), nameof(PostProcessing.Name)); + + + return Page(); + } + + public async Task OnPostRemove(int id, int mioid) + { + var modelInOrder = await _context.ModelsInOrders.FindAsync(mioid); + _context.ModelsInOrders.Remove(modelInOrder); + _context.SaveChanges(); + + return new RedirectToPageResult("/ModelInOrder", "", new{id}); + } + public async Task OnPostCreate(int id) + { + order = _context.Orders.Find(id); + var uploadFileName = Upload.FileName + "_" + DateTime.Now.Ticks; + var file = Path.Combine(_environment.ContentRootPath, "uploads", + uploadFileName); + using (var fileStream = new FileStream(file, FileMode.Create)) + { + await Upload.CopyToAsync(fileStream); + } + + var ms = new ModelSettings() + { + InfillPercentage = Fmodel.InfillPercentage, + LayerHeight = Fmodel.Layerheight, + NozzleDiameter = Fmodel.NozzleDiameter, + }; + + var m = new Model3D() + { + Description = Fmodel.Description, + FilePath = uploadFileName, + }; + + var printer = await _context.Printers.FindAsync(Fmodel.PrinterId); + var material = await _context.Materials.FindAsync(Fmodel.MaterialId); + var pp = await _context.PostProcessings.FindAsync(Fmodel.PostProcessingId); + + var mio = new ModelInOrder() + { + Manager = null, + Material = material, + Model = m, + Order = order, + Printer = printer, + Scale = Fmodel.Scale, + ModelSettings = ms, + PostProcessing = pp, + HasSolubleSupports = Fmodel.HasSolubleSupports, + }; + + order.Models.Add(mio); + await _context.SaveChangesAsync(); + + return new RedirectToPageResult("/ModelInOrder", "", new{id}); + } + } +} \ No newline at end of file diff --git a/TRPZ_PrintService/Pages/MyOrders.cshtml b/TRPZ_PrintService/Pages/MyOrders.cshtml index 7bbb213..28e55ed 100644 --- a/TRPZ_PrintService/Pages/MyOrders.cshtml +++ b/TRPZ_PrintService/Pages/MyOrders.cshtml @@ -61,57 +61,22 @@ @Html.DisplayFor(modelItem => order.Models.Count) @* TODO add edit button *@ - + - + @if (!(order.IsConfirmed || order.IsFinished)) + { +
+ } } @* TODO add modal add window*@ - + -@* *@ \ No newline at end of file diff --git a/TRPZ_PrintService/Pages/MyOrders.cshtml.cs b/TRPZ_PrintService/Pages/MyOrders.cshtml.cs index faa9b58..beb497a 100644 --- a/TRPZ_PrintService/Pages/MyOrders.cshtml.cs +++ b/TRPZ_PrintService/Pages/MyOrders.cshtml.cs @@ -2,20 +2,21 @@ using System.Collections.Generic; using System.Linq; using System.Security.Claims; +using System.Threading.Tasks; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.RazorPages; using Microsoft.AspNetCore.SignalR; +using Microsoft.EntityFrameworkCore; using TRPZ_PrintService.Data; namespace TRPZ_PrintService.Pages { - public class MyOrders : PageModel { private TRPZ_PrintServiceContext _context; - + [BindProperty] public List Orders { get; set; } public MyOrders(TRPZ_PrintServiceContext context) @@ -23,20 +24,55 @@ public MyOrders(TRPZ_PrintServiceContext context) _context = context; } - public void OnGet(int? id) + public void OnGet(string? id) + { + id ??= User.FindFirstValue(ClaimTypes.NameIdentifier); + + Orders = _context.Orders.Include(order => order.Models).Where(order => order.Client.Id == id).ToList(); + } + + public async Task OnPostSend(string? id, int orderId) { - var id1 = User.FindFirstValue(ClaimTypes.NameIdentifier); - Orders = _context.Orders.Where(order => order.Client.Id == id1).ToList(); + id ??= User.FindFirstValue(ClaimTypes.NameIdentifier); + var order = _context.Orders.Find(orderId); + order.IsSent = true; + await _context.SaveChangesAsync(); + + return new RedirectToPageResult("/MyOrders", ""); } - public void OnPostCreate() + public IActionResult OnPostCreate(string? id) { + id ??= User.FindFirstValue(ClaimTypes.NameIdentifier); + var order = new Order(); order.Timestamp = DateTime.Now; - - var id1 = User.FindFirstValue(ClaimTypes.NameIdentifier); - order.Client = _context.Clients.First(client => client.Id == id1); - + order.Client = _context.Users.First(user => user.Id == id); + + _context.Orders.Add(order); + _context.SaveChanges(); + return new RedirectToPageResult("/MyOrders"); + } + + public IActionResult OnPostRemove(string? id, int? orderId) + { + var order = _context.Orders.Find(orderId); + if (order == null) + { + return NotFound(); + } + + try + { + _context.Orders.Remove(order); + _context.SaveChanges(); + } + catch (Exception e) + { + Console.WriteLine(e); + } + + return new RedirectToPageResult("/MyOrders"); } } } \ No newline at end of file diff --git a/TRPZ_PrintService/Startup.cs b/TRPZ_PrintService/Startup.cs index 483d08a..40907c1 100644 --- a/TRPZ_PrintService/Startup.cs +++ b/TRPZ_PrintService/Startup.cs @@ -8,6 +8,7 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; +using Microsoft.AspNetCore.Authorization; namespace TRPZ_PrintService { @@ -28,6 +29,10 @@ public void ConfigureServices(IServiceCollection services) options.Conventions.AuthorizePage("/MyOrders"); }); + // services.AddAuthorization(options => + // { + // options.FallbackPolicy = new AuthorizationPolicyBuilder().RequireAuthenticatedUser().Build(); + // }); } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. @@ -49,8 +54,10 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env) app.UseRouting(); - app.UseAuthorization(); + app.UseAuthentication(); + app.UseAuthorization(); + app.UseEndpoints(endpoints => { diff --git a/TRPZ_PrintService/TRPZ_PrintService.csproj b/TRPZ_PrintService/TRPZ_PrintService.csproj index bd86e6d..a406ccc 100644 --- a/TRPZ_PrintService/TRPZ_PrintService.csproj +++ b/TRPZ_PrintService/TRPZ_PrintService.csproj @@ -21,6 +21,7 @@ + diff --git a/TRPZ_PrintService/uploads/Amolet.stl_637442177037608189 b/TRPZ_PrintService/uploads/Amolet.stl_637442177037608189 new file mode 100644 index 0000000..9355d03 Binary files /dev/null and b/TRPZ_PrintService/uploads/Amolet.stl_637442177037608189 differ diff --git a/TRPZ_PrintService/uploads/Amolet3 (2).stl_637442176642590876 b/TRPZ_PrintService/uploads/Amolet3 (2).stl_637442176642590876 new file mode 100644 index 0000000..9111d28 Binary files /dev/null and b/TRPZ_PrintService/uploads/Amolet3 (2).stl_637442176642590876 differ diff --git a/TRPZ_PrintService/uploads/DICK (small).stl_20 b/TRPZ_PrintService/uploads/DICK (small).stl_20 new file mode 100644 index 0000000..a234aaa Binary files /dev/null and b/TRPZ_PrintService/uploads/DICK (small).stl_20 differ diff --git a/TRPZ_PrintService/uploads/DICK (small).stl_637442476849554503 b/TRPZ_PrintService/uploads/DICK (small).stl_637442476849554503 new file mode 100644 index 0000000..a234aaa Binary files /dev/null and b/TRPZ_PrintService/uploads/DICK (small).stl_637442476849554503 differ diff --git a/TRPZ_PrintService/uploads/DICK-opalubka.stl_608 b/TRPZ_PrintService/uploads/DICK-opalubka.stl_608 new file mode 100644 index 0000000..cd7f393 Binary files /dev/null and b/TRPZ_PrintService/uploads/DICK-opalubka.stl_608 differ diff --git a/TRPZ_PrintService/uploads/DICK-opalubka.stl_637442157237044582 b/TRPZ_PrintService/uploads/DICK-opalubka.stl_637442157237044582 new file mode 100644 index 0000000..cd7f393 Binary files /dev/null and b/TRPZ_PrintService/uploads/DICK-opalubka.stl_637442157237044582 differ diff --git a/TRPZ_PrintService/uploads/DICK-opalubka.stl_637442169440671844 b/TRPZ_PrintService/uploads/DICK-opalubka.stl_637442169440671844 new file mode 100644 index 0000000..cd7f393 Binary files /dev/null and b/TRPZ_PrintService/uploads/DICK-opalubka.stl_637442169440671844 differ diff --git a/TRPZ_PrintService/uploads/DICK-opalubka.stl_637442170509572483 b/TRPZ_PrintService/uploads/DICK-opalubka.stl_637442170509572483 new file mode 100644 index 0000000..cd7f393 Binary files /dev/null and b/TRPZ_PrintService/uploads/DICK-opalubka.stl_637442170509572483 differ diff --git a/TRPZ_PrintService/uploads/DICK-opalubka.stl_637442171072820683 b/TRPZ_PrintService/uploads/DICK-opalubka.stl_637442171072820683 new file mode 100644 index 0000000..cd7f393 Binary files /dev/null and b/TRPZ_PrintService/uploads/DICK-opalubka.stl_637442171072820683 differ diff --git a/TRPZ_PrintService/uploads/DICK-opalubka.stl_637442174534378970 b/TRPZ_PrintService/uploads/DICK-opalubka.stl_637442174534378970 new file mode 100644 index 0000000..cd7f393 Binary files /dev/null and b/TRPZ_PrintService/uploads/DICK-opalubka.stl_637442174534378970 differ diff --git a/TRPZ_PrintService/uploads/DICK.stl_637442155279327152 b/TRPZ_PrintService/uploads/DICK.stl_637442155279327152 new file mode 100644 index 0000000..0639c19 Binary files /dev/null and b/TRPZ_PrintService/uploads/DICK.stl_637442155279327152 differ diff --git a/TRPZ_PrintService/uploads/DICK.stl_637442155373888837 b/TRPZ_PrintService/uploads/DICK.stl_637442155373888837 new file mode 100644 index 0000000..0639c19 Binary files /dev/null and b/TRPZ_PrintService/uploads/DICK.stl_637442155373888837 differ diff --git a/TRPZ_PrintService/uploads/DICK.stl_637442156284280425 b/TRPZ_PrintService/uploads/DICK.stl_637442156284280425 new file mode 100644 index 0000000..0639c19 Binary files /dev/null and b/TRPZ_PrintService/uploads/DICK.stl_637442156284280425 differ diff --git a/TRPZ_PrintService/uploads/DICK.stl_637442158575363723 b/TRPZ_PrintService/uploads/DICK.stl_637442158575363723 new file mode 100644 index 0000000..0639c19 Binary files /dev/null and b/TRPZ_PrintService/uploads/DICK.stl_637442158575363723 differ diff --git a/TRPZ_PrintService/uploads/DICK.stl_637442170257023752 b/TRPZ_PrintService/uploads/DICK.stl_637442170257023752 new file mode 100644 index 0000000..0639c19 Binary files /dev/null and b/TRPZ_PrintService/uploads/DICK.stl_637442170257023752 differ diff --git a/TRPZ_PrintService/uploads/DICK.stl_637442173078978892 b/TRPZ_PrintService/uploads/DICK.stl_637442173078978892 new file mode 100644 index 0000000..0639c19 Binary files /dev/null and b/TRPZ_PrintService/uploads/DICK.stl_637442173078978892 differ diff --git a/TRPZ_PrintService/uploads/DICK.stl_637442173732617344 b/TRPZ_PrintService/uploads/DICK.stl_637442173732617344 new file mode 100644 index 0000000..0639c19 Binary files /dev/null and b/TRPZ_PrintService/uploads/DICK.stl_637442173732617344 differ diff --git a/TRPZ_PrintService/uploads/DICK.stl_637442174259106184 b/TRPZ_PrintService/uploads/DICK.stl_637442174259106184 new file mode 100644 index 0000000..0639c19 Binary files /dev/null and b/TRPZ_PrintService/uploads/DICK.stl_637442174259106184 differ diff --git a/TRPZ_PrintService/uploads/DICK.stl_637442175383700402 b/TRPZ_PrintService/uploads/DICK.stl_637442175383700402 new file mode 100644 index 0000000..0639c19 Binary files /dev/null and b/TRPZ_PrintService/uploads/DICK.stl_637442175383700402 differ diff --git a/TRPZ_PrintService/uploads/DICK.stl_637442180845769547 b/TRPZ_PrintService/uploads/DICK.stl_637442180845769547 new file mode 100644 index 0000000..0639c19 Binary files /dev/null and b/TRPZ_PrintService/uploads/DICK.stl_637442180845769547 differ diff --git a/TRPZ_PrintService/uploads/DICK.stl_637442486355971023 b/TRPZ_PrintService/uploads/DICK.stl_637442486355971023 new file mode 100644 index 0000000..0639c19 Binary files /dev/null and b/TRPZ_PrintService/uploads/DICK.stl_637442486355971023 differ diff --git a/TRPZ_PrintService/uploads/DICK.stl_637442515206661356 b/TRPZ_PrintService/uploads/DICK.stl_637442515206661356 new file mode 100644 index 0000000..0639c19 Binary files /dev/null and b/TRPZ_PrintService/uploads/DICK.stl_637442515206661356 differ