Compare commits
No commits in common. "Dev" and "main" have entirely different histories.
@ -1,5 +0,0 @@
|
||||
{
|
||||
"version": 1,
|
||||
"isRoot": true,
|
||||
"tools": {}
|
||||
}
|
||||
@ -1,83 +0,0 @@
|
||||
@page
|
||||
@model AccessDeniedModel
|
||||
@{
|
||||
ViewData["Title"] = "Access denied";
|
||||
@inject UserManager<UserModel> _userManager
|
||||
var user = await _userManager.GetUserAsync(User);
|
||||
if (user != null)
|
||||
{
|
||||
var userComDept = user.departmentId;
|
||||
var userRole = await _userManager.GetRolesAsync(user);
|
||||
}
|
||||
}
|
||||
|
||||
<header id="deniedHeader">
|
||||
<template v-if="ldapUserInfo.role.length == 0"><p class="text-danger">You do not have access to this resource because you have no role. Please contact the system administrator.</p></template>
|
||||
<template v-else><p class="text-danger">You do not have access to this resource.</p></template>
|
||||
</header>
|
||||
@section Scripts {
|
||||
<script>
|
||||
if (typeof jQuery === 'undefined') {
|
||||
console.error('jQuery is not loaded.');
|
||||
}
|
||||
$(function () {
|
||||
app.mount('#deniedHeader');
|
||||
});
|
||||
|
||||
const app = Vue.createApp({
|
||||
data() {
|
||||
return {
|
||||
ldapUserInfo: {
|
||||
role: [],
|
||||
},
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
this.getUserInfo();
|
||||
},
|
||||
watch: {
|
||||
|
||||
},
|
||||
methods: {
|
||||
async getUserInfo() {
|
||||
try {
|
||||
// Show the loading modal
|
||||
$('#loadingModal').modal('show');
|
||||
|
||||
// Perform the fetch request
|
||||
const response = await fetch('/IdentityAPI/GetUserInformation', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
});
|
||||
|
||||
// Check if the response is OK
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json();
|
||||
throw new Error(errorData.message);
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
if (data.userInfo) {
|
||||
console.log(data.userInfo)
|
||||
this.ldapUserInfo = data.userInfo
|
||||
} else {
|
||||
console.error('Get user failed:', data);
|
||||
}
|
||||
}
|
||||
catch (error) {
|
||||
console.error('Error getting user information:', error);
|
||||
}
|
||||
finally {
|
||||
await new Promise(resolve => {
|
||||
$('#loadingModal').on('shown.bs.modal', resolve);
|
||||
});
|
||||
$('#loadingModal').modal('hide');
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
</script>
|
||||
}
|
||||
@ -1,23 +0,0 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
#nullable disable
|
||||
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||
|
||||
namespace PSTW_CentralSystem.Areas.Identity.Pages.Account
|
||||
{
|
||||
/// <summary>
|
||||
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public class AccessDeniedModel : PageModel
|
||||
{
|
||||
/// <summary>
|
||||
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public void OnGet()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,8 +0,0 @@
|
||||
@page
|
||||
@model ConfirmEmailModel
|
||||
@{
|
||||
ViewData["Title"] = "Confirm email";
|
||||
}
|
||||
|
||||
<h1>@ViewData["Title"]</h1>
|
||||
<partial name="_StatusMessage" model="Model.StatusMessage" />
|
||||
@ -1,52 +0,0 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using System.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 PSTW_CentralSystem.Models;
|
||||
|
||||
namespace PSTW_CentralSystem.Areas.Identity.Pages.Account
|
||||
{
|
||||
public class ConfirmEmailModel : PageModel
|
||||
{
|
||||
private readonly UserManager<UserModel> _userManager;
|
||||
|
||||
public ConfirmEmailModel(UserManager<UserModel> userManager)
|
||||
{
|
||||
_userManager = userManager;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
[TempData]
|
||||
public string StatusMessage { get; set; }
|
||||
public async Task<IActionResult> 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,8 +0,0 @@
|
||||
@page
|
||||
@model ConfirmEmailChangeModel
|
||||
@{
|
||||
ViewData["Title"] = "Confirm email change";
|
||||
}
|
||||
|
||||
<h1>@ViewData["Title"]</h1>
|
||||
<partial name="_StatusMessage" model="Model.StatusMessage" />
|
||||
@ -1,70 +0,0 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using System.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 PSTW_CentralSystem.Models;
|
||||
|
||||
namespace PSTW_CentralSystem.Areas.Identity.Pages.Account
|
||||
{
|
||||
public class ConfirmEmailChangeModel : PageModel
|
||||
{
|
||||
private readonly UserManager<UserModel> _userManager;
|
||||
private readonly SignInManager<UserModel> _signInManager;
|
||||
|
||||
public ConfirmEmailChangeModel(UserManager<UserModel> userManager, SignInManager<UserModel> signInManager)
|
||||
{
|
||||
_userManager = userManager;
|
||||
_signInManager = signInManager;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
[TempData]
|
||||
public string StatusMessage { get; set; }
|
||||
|
||||
public async Task<IActionResult> 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,33 +0,0 @@
|
||||
@page
|
||||
@model ExternalLoginModel
|
||||
@{
|
||||
ViewData["Title"] = "Register";
|
||||
}
|
||||
|
||||
<h1>@ViewData["Title"]</h1>
|
||||
<h2 id="external-login-title">Associate your @Model.ProviderDisplayName account.</h2>
|
||||
<hr />
|
||||
|
||||
<p id="external-login-description" class="text-info">
|
||||
You've successfully authenticated with <strong>@Model.ProviderDisplayName</strong>.
|
||||
Please enter an email address for this site below and click the Register button to finish
|
||||
logging in.
|
||||
</p>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-4">
|
||||
<form asp-page-handler="Confirmation" asp-route-returnUrl="@Model.ReturnUrl" method="post">
|
||||
<div asp-validation-summary="ModelOnly" class="text-danger" role="alert"></div>
|
||||
<div class="form-floating mb-3">
|
||||
<input asp-for="Input.Email" class="form-control" autocomplete="email" placeholder="Please enter your email."/>
|
||||
<label asp-for="Input.Email" class="form-label"></label>
|
||||
<span asp-validation-for="Input.Email" class="text-danger"></span>
|
||||
</div>
|
||||
<button type="submit" class="w-100 btn btn-lg btn-primary">Register</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@section Scripts {
|
||||
<partial name="_ValidationScriptsPartial" />
|
||||
}
|
||||
@ -1,224 +0,0 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Security.Claims;
|
||||
using System.Text;
|
||||
using System.Text.Encodings.Web;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.Extensions.Options;
|
||||
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 PSTW_CentralSystem.Models;
|
||||
|
||||
namespace PSTW_CentralSystem.Areas.Identity.Pages.Account
|
||||
{
|
||||
[AllowAnonymous]
|
||||
public class ExternalLoginModel : PageModel
|
||||
{
|
||||
private readonly SignInManager<UserModel> _signInManager;
|
||||
private readonly UserManager<UserModel> _userManager;
|
||||
private readonly IUserStore<UserModel> _userStore;
|
||||
private readonly IUserEmailStore<UserModel> _emailStore;
|
||||
private readonly IEmailSender _emailSender;
|
||||
private readonly ILogger<ExternalLoginModel> _logger;
|
||||
|
||||
public ExternalLoginModel(
|
||||
SignInManager<UserModel> signInManager,
|
||||
UserManager<UserModel> userManager,
|
||||
IUserStore<UserModel> userStore,
|
||||
ILogger<ExternalLoginModel> logger,
|
||||
IEmailSender emailSender)
|
||||
{
|
||||
_signInManager = signInManager;
|
||||
_userManager = userManager;
|
||||
_userStore = userStore;
|
||||
_emailStore = GetEmailStore();
|
||||
_logger = logger;
|
||||
_emailSender = emailSender;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
[BindProperty]
|
||||
public InputModel Input { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public string ProviderDisplayName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public string ReturnUrl { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
[TempData]
|
||||
public string ErrorMessage { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public class InputModel
|
||||
{
|
||||
/// <summary>
|
||||
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
[Required]
|
||||
[EmailAddress]
|
||||
public string Email { get; set; }
|
||||
}
|
||||
|
||||
public IActionResult OnGet() => 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<IActionResult> 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("{CompanyName} 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<IActionResult> 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 = CreateUser();
|
||||
|
||||
await _userStore.SetUserNameAsync(user, Input.Email, CancellationToken.None);
|
||||
await _emailStore.SetEmailAsync(user, Input.Email, CancellationToken.None);
|
||||
|
||||
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 {CompanyName} 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 <a href='{HtmlEncoder.Default.Encode(callbackUrl)}'>clicking here</a>.");
|
||||
|
||||
// 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();
|
||||
}
|
||||
|
||||
private UserModel CreateUser()
|
||||
{
|
||||
try
|
||||
{
|
||||
return Activator.CreateInstance<UserModel>();
|
||||
}
|
||||
catch
|
||||
{
|
||||
throw new InvalidOperationException($"Can't create an instance of '{nameof(UserModel)}'. " +
|
||||
$"Ensure that '{nameof(UserModel)}' is not an abstract class and has a parameterless constructor, or alternatively " +
|
||||
$"override the external login page in /Areas/Identity/Pages/Account/ExternalLogin.cshtml");
|
||||
}
|
||||
}
|
||||
|
||||
private IUserEmailStore<UserModel> GetEmailStore()
|
||||
{
|
||||
if (!_userManager.SupportsUserEmail)
|
||||
{
|
||||
throw new NotSupportedException("The default UI requires a user store with email support.");
|
||||
}
|
||||
return (IUserEmailStore<UserModel>)_userStore;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,26 +0,0 @@
|
||||
@page
|
||||
@model ForgotPasswordModel
|
||||
@{
|
||||
ViewData["Title"] = "Forgot your password?";
|
||||
}
|
||||
|
||||
<h1>@ViewData["Title"]</h1>
|
||||
<h2>Enter your email.</h2>
|
||||
<hr />
|
||||
<div class="row">
|
||||
<div class="col-md-4">
|
||||
<form method="post">
|
||||
<div asp-validation-summary="ModelOnly" class="text-danger" role="alert"></div>
|
||||
<div class="form-floating mb-3">
|
||||
<input asp-for="Input.Email" class="form-control" autocomplete="username" aria-required="true" placeholder="name@example.com" />
|
||||
<label asp-for="Input.Email" class="form-label"></label>
|
||||
<span asp-validation-for="Input.Email" class="text-danger"></span>
|
||||
</div>
|
||||
<button type="submit" class="w-100 btn btn-lg btn-primary">Reset Password</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@section Scripts {
|
||||
<partial name="_ValidationScriptsPartial" />
|
||||
}
|
||||
@ -1,85 +0,0 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using System.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 PSTW_CentralSystem.Models;
|
||||
|
||||
namespace PSTW_CentralSystem.Areas.Identity.Pages.Account
|
||||
{
|
||||
public class ForgotPasswordModel : PageModel
|
||||
{
|
||||
private readonly UserManager<UserModel> _userManager;
|
||||
private readonly IEmailSender _emailSender;
|
||||
|
||||
public ForgotPasswordModel(UserManager<UserModel> userManager, IEmailSender emailSender)
|
||||
{
|
||||
_userManager = userManager;
|
||||
_emailSender = emailSender;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
[BindProperty]
|
||||
public InputModel Input { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public class InputModel
|
||||
{
|
||||
/// <summary>
|
||||
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
[Required]
|
||||
[EmailAddress]
|
||||
public string Email { get; set; }
|
||||
}
|
||||
|
||||
public async Task<IActionResult> 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 <a href='{HtmlEncoder.Default.Encode(callbackUrl)}'>clicking here</a>.");
|
||||
|
||||
return RedirectToPage("./ForgotPasswordConfirmation");
|
||||
}
|
||||
|
||||
return Page();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,10 +0,0 @@
|
||||
@page
|
||||
@model ForgotPasswordConfirmation
|
||||
@{
|
||||
ViewData["Title"] = "Forgot password confirmation";
|
||||
}
|
||||
|
||||
<h1>@ViewData["Title"]</h1>
|
||||
<p>
|
||||
Please check your email to reset your password.
|
||||
</p>
|
||||
@ -1,25 +0,0 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
#nullable disable
|
||||
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||
|
||||
namespace PSTW_CentralSystem.Areas.Identity.Pages.Account
|
||||
{
|
||||
/// <summary>
|
||||
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
[AllowAnonymous]
|
||||
public class ForgotPasswordConfirmation : PageModel
|
||||
{
|
||||
/// <summary>
|
||||
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public void OnGet()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,10 +0,0 @@
|
||||
@page
|
||||
@model LockoutModel
|
||||
@{
|
||||
ViewData["Title"] = "Locked out";
|
||||
}
|
||||
|
||||
<header>
|
||||
<h1 class="text-danger">@ViewData["Title"]</h1>
|
||||
<p class="text-danger">This account has been locked out, please try again later.</p>
|
||||
</header>
|
||||
@ -1,25 +0,0 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
#nullable disable
|
||||
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||
|
||||
namespace PSTW_CentralSystem.Areas.Identity.Pages.Account
|
||||
{
|
||||
/// <summary>
|
||||
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
[AllowAnonymous]
|
||||
public class LockoutModel : PageModel
|
||||
{
|
||||
/// <summary>
|
||||
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public void OnGet()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,188 +0,0 @@
|
||||
@page
|
||||
@model LoginModel
|
||||
|
||||
@{
|
||||
ViewData["Title"] = "Log in";
|
||||
}
|
||||
|
||||
<div class="row" id="systemLogin">
|
||||
<div class="row">
|
||||
<h2><label class="col-md-2">Login Type</label></h2>
|
||||
<div class="btn-group col-md-4" role="group" aria-label="Login type">
|
||||
<input type="radio" class="btn-check" name="loginType" id="local-login" value="Local" v-model="loginType">
|
||||
<label class="btn btn-outline-primary" for="local-login">Local</label>
|
||||
|
||||
<input type="radio" class="btn-check" name="loginType" id="ad-login" value="AD" v-model="loginType" checked>
|
||||
<label class="btn btn-outline-primary" for="ad-login">AD</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-4" v-if="loginType == 'Local'">
|
||||
<form id="account" method="post">
|
||||
<h2>Use a local account to log in.</h2>
|
||||
<hr />
|
||||
<div asp-validation-summary="ModelOnly" class="text-danger" role="alert"></div>
|
||||
<div class="form-floating mb-3">
|
||||
<input asp-for="Input.Email" class="form-control" autocomplete="username" aria-required="true" placeholder="name@example.com" />
|
||||
<label asp-for="Input.Email" class="form-label">Email</label>
|
||||
<span asp-validation-for="Input.Email" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-floating mb-3">
|
||||
<input asp-for="Input.Password" class="form-control" autocomplete="current-password" aria-required="true" placeholder="password" />
|
||||
<label asp-for="Input.Password" class="form-label">Password</label>
|
||||
<span asp-validation-for="Input.Password" class="text-danger"></span>
|
||||
</div>
|
||||
<div>
|
||||
<button id="login-submit" type="submit" class="w-100 btn btn-lg btn-primary">Log in</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="col-md-4" v-if="loginType == 'AD'">
|
||||
<form v-on:submit.prevent="ldapLogin" id="login" method="post">
|
||||
<h2>Use a AD account to log in.</h2>
|
||||
<hr />
|
||||
<div class="text-danger" role="alert"></div>
|
||||
<div class="form-floating mb-3">
|
||||
<input v-model="ldapLoginInfo.username" id="ldapUsername" class="form-control" autocomplete="username" aria-required="true" placeholder="name@example.com" />
|
||||
<label id="ldapEmailLabel" class="form-label">Windows Login</label>
|
||||
<span id="ldapEmailError" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-floating mb-3">
|
||||
<input v-model="ldapLoginInfo.password" id="ldapPassword" class="form-control" type="password" autocomplete="current-password" aria-required="true" placeholder="password" />
|
||||
<label id="ldapPasswordLabel" class="form-label">Password</label>
|
||||
<span id="ldapPasswordError" class="text-danger"></span>
|
||||
</div>
|
||||
<div>
|
||||
<button id="ldap-login-submit" type="submit" class="w-100 btn btn-lg btn-primary">Log in</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="col-md-6 col-md-offset-2">
|
||||
<section>
|
||||
<h3>Use another service to log in.</h3>
|
||||
<hr />
|
||||
@{
|
||||
if ((Model.ExternalLogins?.Count ?? 0) == 0)
|
||||
{
|
||||
<div>
|
||||
<p>
|
||||
There are no external authentication services configured. See this <a href="https://go.microsoft.com/fwlink/?LinkID=532715">article
|
||||
about setting up this ASP.NET application to support logging in via external services</a>.
|
||||
</p>
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<form id="external-account" asp-page="./ExternalLogin" asp-route-returnUrl="@Model.ReturnUrl" method="post" class="form-horizontal">
|
||||
<div>
|
||||
<p>
|
||||
@foreach (var provider in Model.ExternalLogins!)
|
||||
{
|
||||
<button type="submit" class="btn btn-primary" name="provider" value="@provider.Name" title="Log in using your @provider.DisplayName account">@provider.DisplayName</button>
|
||||
}
|
||||
</p>
|
||||
</div>
|
||||
</form>
|
||||
}
|
||||
}
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@section Scripts {
|
||||
<partial name="_ValidationScriptsPartial" />
|
||||
|
||||
<script>
|
||||
|
||||
$(function () {
|
||||
app.mount('#systemLogin');
|
||||
|
||||
$('.closeModal').on('click', function () {
|
||||
$('.modal').modal('hide');
|
||||
});
|
||||
});
|
||||
|
||||
const app = Vue.createApp({
|
||||
data() {
|
||||
return {
|
||||
loginType: 'AD',
|
||||
ldapLoginInfo: {
|
||||
username: '',
|
||||
password: '',
|
||||
},
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
|
||||
},
|
||||
watch: {
|
||||
|
||||
},
|
||||
methods: {
|
||||
async ldapLogin() {
|
||||
try {
|
||||
// Show the loading modal
|
||||
$('#loadingModal').modal('show');
|
||||
|
||||
// Perform the fetch request
|
||||
const response = await fetch('/IdentityAPI/LdapLogin', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(this.ldapLoginInfo),
|
||||
});
|
||||
|
||||
// Check if the response is OK
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json();
|
||||
throw new Error(errorData.message);
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
// Redirect if a URL is provided
|
||||
if (data.redirectUrl) {
|
||||
window.location.href = data.redirectUrl;
|
||||
} else {
|
||||
console.error('Login failed:', data);
|
||||
alert('Login failed.');
|
||||
}
|
||||
}
|
||||
catch (error) {
|
||||
console.error('Error during LDAP login:', error);
|
||||
alert(error.message);
|
||||
}
|
||||
finally {
|
||||
await new Promise(resolve => {
|
||||
$('#loadingModal').on('shown.bs.modal', resolve);
|
||||
});
|
||||
$('#loadingModal').modal('hide');
|
||||
}
|
||||
},
|
||||
async fetchControllerMethodList() {
|
||||
try {
|
||||
const response = await fetch('/AdminAPI/GetListClassAndMethodInformation', {
|
||||
method: 'POST',
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! Status: ${response.status}`);
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
// Assign data if it exists
|
||||
if (data) {
|
||||
this.controllerMethodData = data;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('There was a problem with the fetch operation:', error);
|
||||
}
|
||||
},
|
||||
|
||||
},
|
||||
});
|
||||
</script>
|
||||
}
|
||||
@ -1,141 +0,0 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Linq;
|
||||
using System.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 PSTW_CentralSystem.Models;
|
||||
|
||||
namespace PSTW_CentralSystem.Areas.Identity.Pages.Account
|
||||
{
|
||||
public class LoginModel : PageModel
|
||||
{
|
||||
private readonly SignInManager<UserModel> _signInManager;
|
||||
private readonly ILogger<LoginModel> _logger;
|
||||
|
||||
public LoginModel(SignInManager<UserModel> signInManager, ILogger<LoginModel> logger)
|
||||
{
|
||||
_signInManager = signInManager;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
[BindProperty]
|
||||
public InputModel Input { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public IList<AuthenticationScheme> ExternalLogins { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public string ReturnUrl { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
[TempData]
|
||||
public string ErrorMessage { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public class InputModel
|
||||
{
|
||||
/// <summary>
|
||||
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
[Required]
|
||||
[EmailAddress]
|
||||
public string Email { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
[Required]
|
||||
[DataType(DataType.Password)]
|
||||
public string Password { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
[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<IActionResult> 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,39 +0,0 @@
|
||||
@page
|
||||
@model LoginWith2faModel
|
||||
@{
|
||||
ViewData["Title"] = "Two-factor authentication";
|
||||
}
|
||||
|
||||
<h1>@ViewData["Title"]</h1>
|
||||
<hr />
|
||||
<p>Your login is protected with an authenticator app. Enter your authenticator code below.</p>
|
||||
<div class="row">
|
||||
<div class="col-md-4">
|
||||
<form method="post" asp-route-returnUrl="@Model.ReturnUrl">
|
||||
<input asp-for="RememberMe" type="hidden" />
|
||||
<div asp-validation-summary="ModelOnly" class="text-danger" role="alert"></div>
|
||||
<div class="form-floating mb-3">
|
||||
<input asp-for="Input.TwoFactorCode" class="form-control" autocomplete="off" />
|
||||
<label asp-for="Input.TwoFactorCode" class="form-label"></label>
|
||||
<span asp-validation-for="Input.TwoFactorCode" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="checkbox mb-3">
|
||||
<label asp-for="Input.RememberMachine" class="form-label">
|
||||
<input asp-for="Input.RememberMachine" />
|
||||
@Html.DisplayNameFor(m => m.Input.RememberMachine)
|
||||
</label>
|
||||
</div>
|
||||
<div>
|
||||
<button type="submit" class="w-100 btn btn-lg btn-primary">Log in</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<p>
|
||||
Don't have access to your authenticator device? You can
|
||||
<a id="recovery-code-login" asp-page="./LoginWithRecoveryCode" asp-route-returnUrl="@Model.ReturnUrl">log in with a recovery code</a>.
|
||||
</p>
|
||||
|
||||
@section Scripts {
|
||||
<partial name="_ValidationScriptsPartial" />
|
||||
}
|
||||
@ -1,132 +0,0 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using PSTW_CentralSystem.Models;
|
||||
|
||||
namespace PSTW_CentralSystem.Areas.Identity.Pages.Account
|
||||
{
|
||||
public class LoginWith2faModel : PageModel
|
||||
{
|
||||
private readonly SignInManager<UserModel> _signInManager;
|
||||
private readonly UserManager<UserModel> _userManager;
|
||||
private readonly ILogger<LoginWith2faModel> _logger;
|
||||
|
||||
public LoginWith2faModel(
|
||||
SignInManager<UserModel> signInManager,
|
||||
UserManager<UserModel> userManager,
|
||||
ILogger<LoginWith2faModel> logger)
|
||||
{
|
||||
_signInManager = signInManager;
|
||||
_userManager = userManager;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
[BindProperty]
|
||||
public InputModel Input { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public bool RememberMe { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public string ReturnUrl { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public class InputModel
|
||||
{
|
||||
/// <summary>
|
||||
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
[Required]
|
||||
[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; }
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
[Display(Name = "Remember this machine")]
|
||||
public bool RememberMachine { get; set; }
|
||||
}
|
||||
|
||||
public async Task<IActionResult> 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<IActionResult> 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);
|
||||
|
||||
var userId = await _userManager.GetUserIdAsync(user);
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,29 +0,0 @@
|
||||
@page
|
||||
@model LoginWithRecoveryCodeModel
|
||||
@{
|
||||
ViewData["Title"] = "Recovery code verification";
|
||||
}
|
||||
|
||||
<h1>@ViewData["Title"]</h1>
|
||||
<hr />
|
||||
<p>
|
||||
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.
|
||||
</p>
|
||||
<div class="row">
|
||||
<div class="col-md-4">
|
||||
<form method="post">
|
||||
<div asp-validation-summary="ModelOnly" class="text-danger" role="alert"></div>
|
||||
<div class="form-floating mb-3">
|
||||
<input asp-for="Input.RecoveryCode" class="form-control" autocomplete="off" placeholder="RecoveryCode" />
|
||||
<label asp-for="Input.RecoveryCode" class="form-label"></label>
|
||||
<span asp-validation-for="Input.RecoveryCode" class="text-danger"></span>
|
||||
</div>
|
||||
<button type="submit" class="w-100 btn btn-lg btn-primary">Log in</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@section Scripts {
|
||||
<partial name="_ValidationScriptsPartial" />
|
||||
}
|
||||
@ -1,113 +0,0 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
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 PSTW_CentralSystem.Models;
|
||||
namespace PSTW_CentralSystem.Areas.Identity.Pages.Account
|
||||
{
|
||||
public class LoginWithRecoveryCodeModel : PageModel
|
||||
{
|
||||
private readonly SignInManager<UserModel> _signInManager;
|
||||
private readonly UserManager<UserModel> _userManager;
|
||||
private readonly ILogger<LoginWithRecoveryCodeModel> _logger;
|
||||
|
||||
public LoginWithRecoveryCodeModel(
|
||||
SignInManager<UserModel> signInManager,
|
||||
UserManager<UserModel> userManager,
|
||||
ILogger<LoginWithRecoveryCodeModel> logger)
|
||||
{
|
||||
_signInManager = signInManager;
|
||||
_userManager = userManager;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
[BindProperty]
|
||||
public InputModel Input { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public string ReturnUrl { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public class InputModel
|
||||
{
|
||||
/// <summary>
|
||||
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
[BindProperty]
|
||||
[Required]
|
||||
[DataType(DataType.Text)]
|
||||
[Display(Name = "Recovery Code")]
|
||||
public string RecoveryCode { get; set; }
|
||||
}
|
||||
|
||||
public async Task<IActionResult> 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<IActionResult> 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);
|
||||
|
||||
var userId = await _userManager.GetUserIdAsync(user);
|
||||
|
||||
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 account locked out.");
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,21 +0,0 @@
|
||||
@page
|
||||
@model LogoutModel
|
||||
@{
|
||||
ViewData["Title"] = "Log out";
|
||||
}
|
||||
|
||||
<header>
|
||||
<h1>@ViewData["Title"]</h1>
|
||||
@{
|
||||
if (User.Identity?.IsAuthenticated ?? false)
|
||||
{
|
||||
<form class="form-inline" asp-area="Identity" asp-page="/Account/Logout" asp-route-returnUrl="@Url.Page("/", new { area = "" })" method="post">
|
||||
<button type="submit" class="nav-link btn btn-link text-dark">Click here to Logout</button>
|
||||
</form>
|
||||
}
|
||||
else
|
||||
{
|
||||
<p>You have successfully logged out of the application.</p>
|
||||
}
|
||||
}
|
||||
</header>
|
||||
@ -1,43 +0,0 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using PSTW_CentralSystem.Models;
|
||||
|
||||
namespace PSTW_CentralSystem.Areas.Identity.Pages.Account
|
||||
{
|
||||
public class LogoutModel : PageModel
|
||||
{
|
||||
private readonly SignInManager<UserModel> _signInManager;
|
||||
private readonly ILogger<LogoutModel> _logger;
|
||||
|
||||
public LogoutModel(SignInManager<UserModel> signInManager, ILogger<LogoutModel> logger)
|
||||
{
|
||||
_signInManager = signInManager;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public async Task<IActionResult> OnPost(string returnUrl = null)
|
||||
{
|
||||
await _signInManager.SignOutAsync();
|
||||
_logger.LogInformation("User logged out.");
|
||||
if (returnUrl != null)
|
||||
{
|
||||
return LocalRedirect(returnUrl);
|
||||
}
|
||||
else
|
||||
{
|
||||
// This needs to be a redirect so that the browser performs a new
|
||||
// request and the identity for the user gets updated.
|
||||
return RedirectToPage();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,36 +0,0 @@
|
||||
@page
|
||||
@model ChangePasswordModel
|
||||
@{
|
||||
ViewData["Title"] = "Change password";
|
||||
ViewData["ActivePage"] = ManageNavPages.ChangePassword;
|
||||
}
|
||||
|
||||
<h3>@ViewData["Title"]</h3>
|
||||
<partial name="_StatusMessage" for="StatusMessage" />
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<form id="change-password-form" method="post">
|
||||
<div asp-validation-summary="ModelOnly" class="text-danger" role="alert"></div>
|
||||
<div class="form-floating mb-3">
|
||||
<input asp-for="Input.OldPassword" class="form-control" autocomplete="current-password" aria-required="true" placeholder="Please enter your old password." />
|
||||
<label asp-for="Input.OldPassword" class="form-label"></label>
|
||||
<span asp-validation-for="Input.OldPassword" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-floating mb-3">
|
||||
<input asp-for="Input.NewPassword" class="form-control" autocomplete="new-password" aria-required="true" placeholder="Please enter your new password." />
|
||||
<label asp-for="Input.NewPassword" class="form-label"></label>
|
||||
<span asp-validation-for="Input.NewPassword" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-floating mb-3">
|
||||
<input asp-for="Input.ConfirmPassword" class="form-control" autocomplete="new-password" aria-required="true" placeholder="Please confirm your new password."/>
|
||||
<label asp-for="Input.ConfirmPassword" class="form-label"></label>
|
||||
<span asp-validation-for="Input.ConfirmPassword" class="text-danger"></span>
|
||||
</div>
|
||||
<button type="submit" class="w-100 btn btn-lg btn-primary">Update password</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@section Scripts {
|
||||
<partial name="_ValidationScriptsPartial" />
|
||||
}
|
||||
@ -1,128 +0,0 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using PSTW_CentralSystem.Models;
|
||||
|
||||
namespace PSTW_CentralSystem.Areas.Identity.Pages.Account.Manage
|
||||
{
|
||||
public class ChangePasswordModel : PageModel
|
||||
{
|
||||
private readonly UserManager<UserModel> _userManager;
|
||||
private readonly SignInManager<UserModel> _signInManager;
|
||||
private readonly ILogger<ChangePasswordModel> _logger;
|
||||
|
||||
public ChangePasswordModel(
|
||||
UserManager<UserModel> userManager,
|
||||
SignInManager<UserModel> signInManager,
|
||||
ILogger<ChangePasswordModel> logger)
|
||||
{
|
||||
_userManager = userManager;
|
||||
_signInManager = signInManager;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
[BindProperty]
|
||||
public InputModel Input { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
[TempData]
|
||||
public string StatusMessage { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public class InputModel
|
||||
{
|
||||
/// <summary>
|
||||
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
[Required]
|
||||
[DataType(DataType.Password)]
|
||||
[Display(Name = "Current password")]
|
||||
public string OldPassword { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
[Required]
|
||||
[StringLength(100, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)]
|
||||
[DataType(DataType.Password)]
|
||||
[Display(Name = "New password")]
|
||||
public string NewPassword { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
[DataType(DataType.Password)]
|
||||
[Display(Name = "Confirm new password")]
|
||||
[Compare("NewPassword", ErrorMessage = "The new password and confirmation password do not match.")]
|
||||
public string ConfirmPassword { get; set; }
|
||||
}
|
||||
|
||||
public async Task<IActionResult> 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<IActionResult> 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,33 +0,0 @@
|
||||
@page
|
||||
@model DeletePersonalDataModel
|
||||
@{
|
||||
ViewData["Title"] = "Delete Personal Data";
|
||||
ViewData["ActivePage"] = ManageNavPages.PersonalData;
|
||||
}
|
||||
|
||||
<h3>@ViewData["Title"]</h3>
|
||||
|
||||
<div class="alert alert-warning" role="alert">
|
||||
<p>
|
||||
<strong>Deleting this data will permanently remove your account, and this cannot be recovered.</strong>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<form id="delete-user" method="post">
|
||||
<div asp-validation-summary="ModelOnly" class="text-danger" role="alert"></div>
|
||||
@if (Model.RequirePassword)
|
||||
{
|
||||
<div class="form-floating mb-3">
|
||||
<input asp-for="Input.Password" class="form-control" autocomplete="current-password" aria-required="true" placeholder="Please enter your password." />
|
||||
<label asp-for="Input.Password" class="form-label"></label>
|
||||
<span asp-validation-for="Input.Password" class="text-danger"></span>
|
||||
</div>
|
||||
}
|
||||
<button class="w-100 btn btn-lg btn-danger" type="submit">Delete data and close my account</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
@section Scripts {
|
||||
<partial name="_ValidationScriptsPartial" />
|
||||
}
|
||||
@ -1,104 +0,0 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using PSTW_CentralSystem.Models;
|
||||
|
||||
namespace PSTW_CentralSystem.Areas.Identity.Pages.Account.Manage
|
||||
{
|
||||
public class DeletePersonalDataModel : PageModel
|
||||
{
|
||||
private readonly UserManager<UserModel> _userManager;
|
||||
private readonly SignInManager<UserModel> _signInManager;
|
||||
private readonly ILogger<DeletePersonalDataModel> _logger;
|
||||
|
||||
public DeletePersonalDataModel(
|
||||
UserManager<UserModel> userManager,
|
||||
SignInManager<UserModel> signInManager,
|
||||
ILogger<DeletePersonalDataModel> logger)
|
||||
{
|
||||
_userManager = userManager;
|
||||
_signInManager = signInManager;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
[BindProperty]
|
||||
public InputModel Input { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public class InputModel
|
||||
{
|
||||
/// <summary>
|
||||
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
[Required]
|
||||
[DataType(DataType.Password)]
|
||||
public string Password { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public bool RequirePassword { get; set; }
|
||||
|
||||
public async Task<IActionResult> 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<IActionResult> 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.");
|
||||
}
|
||||
|
||||
await _signInManager.SignOutAsync();
|
||||
|
||||
_logger.LogInformation("User with ID '{UserId}' deleted themselves.", userId);
|
||||
|
||||
return Redirect("~/");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,25 +0,0 @@
|
||||
@page
|
||||
@model Disable2faModel
|
||||
@{
|
||||
ViewData["Title"] = "Disable two-factor authentication (2FA)";
|
||||
ViewData["ActivePage"] = ManageNavPages.TwoFactorAuthentication;
|
||||
}
|
||||
|
||||
<partial name="_StatusMessage" for="StatusMessage" />
|
||||
<h3>@ViewData["Title"]</h3>
|
||||
|
||||
<div class="alert alert-warning" role="alert">
|
||||
<p>
|
||||
<strong>This action only disables 2FA.</strong>
|
||||
</p>
|
||||
<p>
|
||||
Disabling 2FA does not change the keys used in authenticator apps. If you wish to change the key
|
||||
used in an authenticator app you should <a asp-page="./ResetAuthenticator">reset your authenticator keys.</a>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<form method="post">
|
||||
<button class="btn btn-danger" type="submit">Disable 2FA</button>
|
||||
</form>
|
||||
</div>
|
||||
@ -1,70 +0,0 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using PSTW_CentralSystem.Models;
|
||||
|
||||
namespace PSTW_CentralSystem.Areas.Identity.Pages.Account.Manage
|
||||
{
|
||||
public class Disable2faModel : PageModel
|
||||
{
|
||||
private readonly UserManager<UserModel> _userManager;
|
||||
private readonly ILogger<Disable2faModel> _logger;
|
||||
|
||||
public Disable2faModel(
|
||||
UserManager<UserModel> userManager,
|
||||
ILogger<Disable2faModel> logger)
|
||||
{
|
||||
_userManager = userManager;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
[TempData]
|
||||
public string StatusMessage { get; set; }
|
||||
|
||||
public async Task<IActionResult> 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 as it's not currently enabled.");
|
||||
}
|
||||
|
||||
return Page();
|
||||
}
|
||||
|
||||
public async Task<IActionResult> 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.");
|
||||
}
|
||||
|
||||
_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");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,12 +0,0 @@
|
||||
@page
|
||||
@model DownloadPersonalDataModel
|
||||
@{
|
||||
ViewData["Title"] = "Download Your Data";
|
||||
ViewData["ActivePage"] = ManageNavPages.PersonalData;
|
||||
}
|
||||
|
||||
<h3>@ViewData["Title"]</h3>
|
||||
|
||||
@section Scripts {
|
||||
<partial name="_ValidationScriptsPartial" />
|
||||
}
|
||||
@ -1,68 +0,0 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.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 PSTW_CentralSystem.Models;
|
||||
|
||||
namespace PSTW_CentralSystem.Areas.Identity.Pages.Account.Manage
|
||||
{
|
||||
public class DownloadPersonalDataModel : PageModel
|
||||
{
|
||||
private readonly UserManager<UserModel> _userManager;
|
||||
private readonly ILogger<DownloadPersonalDataModel> _logger;
|
||||
|
||||
public DownloadPersonalDataModel(
|
||||
UserManager<UserModel> userManager,
|
||||
ILogger<DownloadPersonalDataModel> logger)
|
||||
{
|
||||
_userManager = userManager;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public IActionResult OnGet()
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
public async Task<IActionResult> 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<string, string>();
|
||||
var personalDataProps = typeof(UserModel).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);
|
||||
}
|
||||
|
||||
personalData.Add($"Authenticator Key", await _userManager.GetAuthenticatorKeyAsync(user));
|
||||
|
||||
Response.Headers.TryAdd("Content-Disposition", "attachment; filename=PersonalData.json");
|
||||
return new FileContentResult(JsonSerializer.SerializeToUtf8Bytes(personalData), "application/json");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,44 +0,0 @@
|
||||
@page
|
||||
@model EmailModel
|
||||
@{
|
||||
ViewData["Title"] = "Manage Email";
|
||||
ViewData["ActivePage"] = ManageNavPages.Email;
|
||||
}
|
||||
|
||||
<h3>@ViewData["Title"]</h3>
|
||||
<partial name="_StatusMessage" for="StatusMessage" />
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<form id="email-form" method="post">
|
||||
<div asp-validation-summary="All" class="text-danger" role="alert"></div>
|
||||
@if (Model.IsEmailConfirmed)
|
||||
{
|
||||
<div class="form-floating mb-3 input-group">
|
||||
<input asp-for="Email" class="form-control" placeholder="Please enter your email." disabled />
|
||||
<div class="input-group-append">
|
||||
<span class="h-100 input-group-text text-success font-weight-bold">✓</span>
|
||||
</div>
|
||||
<label asp-for="Email" class="form-label"></label>
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<div class="form-floating mb-3">
|
||||
<input asp-for="Email" class="form-control" placeholder="Please enter your email." disabled />
|
||||
<label asp-for="Email" class="form-label"></label>
|
||||
<button id="email-verification" type="submit" asp-page-handler="SendVerificationEmail" class="btn btn-link">Send verification email</button>
|
||||
</div>
|
||||
}
|
||||
<div class="form-floating mb-3">
|
||||
<input asp-for="Input.NewEmail" class="form-control" autocomplete="email" aria-required="true" placeholder="Please enter new email." />
|
||||
<label asp-for="Input.NewEmail" class="form-label"></label>
|
||||
<span asp-validation-for="Input.NewEmail" class="text-danger"></span>
|
||||
</div>
|
||||
<button id="change-email-button" type="submit" asp-page-handler="ChangeEmail" class="w-100 btn btn-lg btn-primary">Change email</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@section Scripts {
|
||||
<partial name="_ValidationScriptsPartial" />
|
||||
}
|
||||
@ -1,172 +0,0 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Text;
|
||||
using System.Text.Encodings.Web;
|
||||
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 PSTW_CentralSystem.Models;
|
||||
|
||||
namespace PSTW_CentralSystem.Areas.Identity.Pages.Account.Manage
|
||||
{
|
||||
public class EmailModel : PageModel
|
||||
{
|
||||
private readonly UserManager<UserModel> _userManager;
|
||||
private readonly SignInManager<UserModel> _signInManager;
|
||||
private readonly IEmailSender _emailSender;
|
||||
|
||||
public EmailModel(
|
||||
UserManager<UserModel> userManager,
|
||||
SignInManager<UserModel> signInManager,
|
||||
IEmailSender emailSender)
|
||||
{
|
||||
_userManager = userManager;
|
||||
_signInManager = signInManager;
|
||||
_emailSender = emailSender;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public string Email { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public bool IsEmailConfirmed { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
[TempData]
|
||||
public string StatusMessage { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
[BindProperty]
|
||||
public InputModel Input { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public class InputModel
|
||||
{
|
||||
/// <summary>
|
||||
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
[Required]
|
||||
[EmailAddress]
|
||||
[Display(Name = "New email")]
|
||||
public string NewEmail { get; set; }
|
||||
}
|
||||
|
||||
private async Task LoadAsync(UserModel user)
|
||||
{
|
||||
var email = await _userManager.GetEmailAsync(user);
|
||||
Email = email;
|
||||
|
||||
Input = new InputModel
|
||||
{
|
||||
NewEmail = email,
|
||||
};
|
||||
|
||||
IsEmailConfirmed = await _userManager.IsEmailConfirmedAsync(user);
|
||||
}
|
||||
|
||||
public async Task<IActionResult> 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<IActionResult> 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 { area = "Identity", userId = userId, email = Input.NewEmail, code = code },
|
||||
protocol: Request.Scheme);
|
||||
await _emailSender.SendEmailAsync(
|
||||
Input.NewEmail,
|
||||
"Confirm your email",
|
||||
$"Please confirm your account by <a href='{HtmlEncoder.Default.Encode(callbackUrl)}'>clicking here</a>.");
|
||||
|
||||
StatusMessage = "Confirmation link to change email sent. Please check your email.";
|
||||
return RedirectToPage();
|
||||
}
|
||||
|
||||
StatusMessage = "Your email is unchanged.";
|
||||
return RedirectToPage();
|
||||
}
|
||||
|
||||
public async Task<IActionResult> 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 <a href='{HtmlEncoder.Default.Encode(callbackUrl)}'>clicking here</a>.");
|
||||
|
||||
StatusMessage = "Verification email sent. Please check your email.";
|
||||
return RedirectToPage();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,53 +0,0 @@
|
||||
@page
|
||||
@model EnableAuthenticatorModel
|
||||
@{
|
||||
ViewData["Title"] = "Configure authenticator app";
|
||||
ViewData["ActivePage"] = ManageNavPages.TwoFactorAuthentication;
|
||||
}
|
||||
|
||||
<partial name="_StatusMessage" for="StatusMessage" />
|
||||
<h3>@ViewData["Title"]</h3>
|
||||
<div>
|
||||
<p>To use an authenticator app go through the following steps:</p>
|
||||
<ol class="list">
|
||||
<li>
|
||||
<p>
|
||||
Download a two-factor authenticator app like Microsoft Authenticator for
|
||||
<a href="https://go.microsoft.com/fwlink/?Linkid=825072">Android</a> and
|
||||
<a href="https://go.microsoft.com/fwlink/?Linkid=825073">iOS</a> or
|
||||
Google Authenticator for
|
||||
<a href="https://play.google.com/store/apps/details?id=com.google.android.apps.authenticator2&hl=en">Android</a> and
|
||||
<a href="https://itunes.apple.com/us/app/google-authenticator/id388497605?mt=8">iOS</a>.
|
||||
</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Scan the QR Code or enter this key <kbd>@Model.SharedKey</kbd> into your two factor authenticator app. Spaces and casing do not matter.</p>
|
||||
<div class="alert alert-info">Learn how to <a href="https://go.microsoft.com/fwlink/?Linkid=852423">enable QR code generation</a>.</div>
|
||||
<div id="qrCode"></div>
|
||||
<div id="qrCodeData" data-url="@Model.AuthenticatorUri"></div>
|
||||
</li>
|
||||
<li>
|
||||
<p>
|
||||
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.
|
||||
</p>
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<form id="send-code" method="post">
|
||||
<div class="form-floating mb-3">
|
||||
<input asp-for="Input.Code" class="form-control" autocomplete="off" placeholder="Please enter the code."/>
|
||||
<label asp-for="Input.Code" class="control-label form-label">Verification Code</label>
|
||||
<span asp-validation-for="Input.Code" class="text-danger"></span>
|
||||
</div>
|
||||
<button type="submit" class="w-100 btn btn-lg btn-primary">Verify</button>
|
||||
<div asp-validation-summary="ModelOnly" class="text-danger" role="alert"></div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</ol>
|
||||
</div>
|
||||
|
||||
@section Scripts {
|
||||
<partial name="_ValidationScriptsPartial" />
|
||||
}
|
||||
@ -1,189 +0,0 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.Encodings.Web;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using PSTW_CentralSystem.Models;
|
||||
|
||||
namespace PSTW_CentralSystem.Areas.Identity.Pages.Account.Manage
|
||||
{
|
||||
public class EnableAuthenticatorModel : PageModel
|
||||
{
|
||||
private readonly UserManager<UserModel> _userManager;
|
||||
private readonly ILogger<EnableAuthenticatorModel> _logger;
|
||||
private readonly UrlEncoder _urlEncoder;
|
||||
|
||||
private const string AuthenticatorUriFormat = "otpauth://totp/{0}:{1}?secret={2}&issuer={0}&digits=6";
|
||||
|
||||
public EnableAuthenticatorModel(
|
||||
UserManager<UserModel> userManager,
|
||||
ILogger<EnableAuthenticatorModel> logger,
|
||||
UrlEncoder urlEncoder)
|
||||
{
|
||||
_userManager = userManager;
|
||||
_logger = logger;
|
||||
_urlEncoder = urlEncoder;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public string SharedKey { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public string AuthenticatorUri { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
[TempData]
|
||||
public string[] RecoveryCodes { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
[TempData]
|
||||
public string StatusMessage { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
[BindProperty]
|
||||
public InputModel Input { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public class InputModel
|
||||
{
|
||||
/// <summary>
|
||||
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
[Required]
|
||||
[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<IActionResult> 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<IActionResult> 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 hyphens
|
||||
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(UserModel 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.AsSpan(currentPosition, 4)).Append(' ');
|
||||
currentPosition += 4;
|
||||
}
|
||||
if (currentPosition < unformattedKey.Length)
|
||||
{
|
||||
result.Append(unformattedKey.AsSpan(currentPosition));
|
||||
}
|
||||
|
||||
return result.ToString().ToLowerInvariant();
|
||||
}
|
||||
|
||||
private string GenerateQrCodeUri(string email, string unformattedKey)
|
||||
{
|
||||
return string.Format(
|
||||
CultureInfo.InvariantCulture,
|
||||
AuthenticatorUriFormat,
|
||||
_urlEncoder.Encode("Microsoft.AspNetCore.Identity.UI"),
|
||||
_urlEncoder.Encode(email),
|
||||
unformattedKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,53 +0,0 @@
|
||||
@page
|
||||
@model ExternalLoginsModel
|
||||
@{
|
||||
ViewData["Title"] = "Manage your external logins";
|
||||
ViewData["ActivePage"] = ManageNavPages.ExternalLogins;
|
||||
}
|
||||
|
||||
<partial name="_StatusMessage" for="StatusMessage" />
|
||||
@if (Model.CurrentLogins?.Count > 0)
|
||||
{
|
||||
<h3>Registered Logins</h3>
|
||||
<table class="table">
|
||||
<tbody>
|
||||
@foreach (var login in Model.CurrentLogins)
|
||||
{
|
||||
<tr>
|
||||
<td id="@($"login-provider-{login.LoginProvider}")">@login.ProviderDisplayName</td>
|
||||
<td>
|
||||
@if (Model.ShowRemoveButton)
|
||||
{
|
||||
<form id="@($"remove-login-{login.LoginProvider}")" asp-page-handler="RemoveLogin" method="post">
|
||||
<div>
|
||||
<input asp-for="@login.LoginProvider" name="LoginProvider" type="hidden" />
|
||||
<input asp-for="@login.ProviderKey" name="ProviderKey" type="hidden" />
|
||||
<button type="submit" class="btn btn-primary" title="Remove this @login.ProviderDisplayName login from your account">Remove</button>
|
||||
</div>
|
||||
</form>
|
||||
}
|
||||
else
|
||||
{
|
||||
@:
|
||||
}
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
}
|
||||
@if (Model.OtherLogins?.Count > 0)
|
||||
{
|
||||
<h4>Add another service to log in.</h4>
|
||||
<hr />
|
||||
<form id="link-login-form" asp-page-handler="LinkLogin" method="post" class="form-horizontal">
|
||||
<div id="socialLoginList">
|
||||
<p>
|
||||
@foreach (var provider in Model.OtherLogins)
|
||||
{
|
||||
<button id="@($"link-login-button-{provider.Name}")" type="submit" class="btn btn-primary" name="provider" value="@provider.Name" title="Log in using your @provider.DisplayName account">@provider.DisplayName</button>
|
||||
}
|
||||
</p>
|
||||
</div>
|
||||
</form>
|
||||
}
|
||||
@ -1,142 +0,0 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||
using PSTW_CentralSystem.Models;
|
||||
|
||||
namespace PSTW_CentralSystem.Areas.Identity.Pages.Account.Manage
|
||||
{
|
||||
public class ExternalLoginsModel : PageModel
|
||||
{
|
||||
private readonly UserManager<UserModel> _userManager;
|
||||
private readonly SignInManager<UserModel> _signInManager;
|
||||
private readonly IUserStore<UserModel> _userStore;
|
||||
|
||||
public ExternalLoginsModel(
|
||||
UserManager<UserModel> userManager,
|
||||
SignInManager<UserModel> signInManager,
|
||||
IUserStore<UserModel> userStore)
|
||||
{
|
||||
_userManager = userManager;
|
||||
_signInManager = signInManager;
|
||||
_userStore = userStore;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public IList<UserLoginInfo> CurrentLogins { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public IList<AuthenticationScheme> OtherLogins { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public bool ShowRemoveButton { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
[TempData]
|
||||
public string StatusMessage { get; set; }
|
||||
|
||||
public async Task<IActionResult> OnGetAsync()
|
||||
{
|
||||
var user = await _userManager.GetUserAsync(User);
|
||||
if (user == null)
|
||||
{
|
||||
return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'.");
|
||||
}
|
||||
|
||||
CurrentLogins = await _userManager.GetLoginsAsync(user);
|
||||
OtherLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync())
|
||||
.Where(auth => CurrentLogins.All(ul => auth.Name != ul.LoginProvider))
|
||||
.ToList();
|
||||
|
||||
string passwordHash = null;
|
||||
if (_userStore is IUserPasswordStore<UserModel> userPasswordStore)
|
||||
{
|
||||
passwordHash = await userPasswordStore.GetPasswordHashAsync(user, HttpContext.RequestAborted);
|
||||
}
|
||||
|
||||
ShowRemoveButton = passwordHash != null || CurrentLogins.Count > 1;
|
||||
return Page();
|
||||
}
|
||||
|
||||
public async Task<IActionResult> OnPostRemoveLoginAsync(string loginProvider, string providerKey)
|
||||
{
|
||||
var user = await _userManager.GetUserAsync(User);
|
||||
if (user == null)
|
||||
{
|
||||
return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'.");
|
||||
}
|
||||
|
||||
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<IActionResult> 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<IActionResult> OnGetLinkLoginCallbackAsync()
|
||||
{
|
||||
var user = await _userManager.GetUserAsync(User);
|
||||
if (user == null)
|
||||
{
|
||||
return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'.");
|
||||
}
|
||||
|
||||
var userId = await _userManager.GetUserIdAsync(user);
|
||||
var info = await _signInManager.GetExternalLoginInfoAsync(userId);
|
||||
if (info == null)
|
||||
{
|
||||
throw new InvalidOperationException($"Unexpected error occurred loading external login info.");
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,27 +0,0 @@
|
||||
@page
|
||||
@model GenerateRecoveryCodesModel
|
||||
@{
|
||||
ViewData["Title"] = "Generate two-factor authentication (2FA) recovery codes";
|
||||
ViewData["ActivePage"] = ManageNavPages.TwoFactorAuthentication;
|
||||
}
|
||||
|
||||
<partial name="_StatusMessage" for="StatusMessage" />
|
||||
<h3>@ViewData["Title"]</h3>
|
||||
<div class="alert alert-warning" role="alert">
|
||||
<p>
|
||||
<span class="glyphicon glyphicon-warning-sign"></span>
|
||||
<strong>Put these codes in a safe place.</strong>
|
||||
</p>
|
||||
<p>
|
||||
If you lose your device and don't have the recovery codes you will lose access to your account.
|
||||
</p>
|
||||
<p>
|
||||
Generating new recovery codes does not change the keys used in authenticator apps. If you wish to change the key
|
||||
used in an authenticator app you should <a asp-page="./ResetAuthenticator">reset your authenticator keys.</a>
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<form method="post">
|
||||
<button class="btn btn-danger" type="submit">Generate Recovery Codes</button>
|
||||
</form>
|
||||
</div>
|
||||
@ -1,83 +0,0 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using PSTW_CentralSystem.Models;
|
||||
|
||||
namespace PSTW_CentralSystem.Areas.Identity.Pages.Account.Manage
|
||||
{
|
||||
public class GenerateRecoveryCodesModel : PageModel
|
||||
{
|
||||
private readonly UserManager<UserModel> _userManager;
|
||||
private readonly ILogger<GenerateRecoveryCodesModel> _logger;
|
||||
|
||||
public GenerateRecoveryCodesModel(
|
||||
UserManager<UserModel> userManager,
|
||||
ILogger<GenerateRecoveryCodesModel> logger)
|
||||
{
|
||||
_userManager = userManager;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
[TempData]
|
||||
public string[] RecoveryCodes { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
[TempData]
|
||||
public string StatusMessage { get; set; }
|
||||
|
||||
public async Task<IActionResult> 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)
|
||||
{
|
||||
throw new InvalidOperationException($"Cannot generate recovery codes for user because they do not have 2FA enabled.");
|
||||
}
|
||||
|
||||
return Page();
|
||||
}
|
||||
|
||||
public async Task<IActionResult> 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 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");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,30 +0,0 @@
|
||||
@page
|
||||
@model IndexModel
|
||||
@{
|
||||
ViewData["Title"] = "Profile";
|
||||
ViewData["ActivePage"] = ManageNavPages.Index;
|
||||
}
|
||||
|
||||
<h3>@ViewData["Title"]</h3>
|
||||
<partial name="_StatusMessage" for="StatusMessage" />
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<form id="profile-form" method="post">
|
||||
<div asp-validation-summary="ModelOnly" class="text-danger" role="alert"></div>
|
||||
<div class="form-floating mb-3">
|
||||
<input asp-for="Username" class="form-control" placeholder="Please choose your username." disabled />
|
||||
<label asp-for="Username" class="form-label"></label>
|
||||
</div>
|
||||
<div class="form-floating mb-3">
|
||||
<input asp-for="Input.PhoneNumber" class="form-control" placeholder="Please enter your phone number."/>
|
||||
<label asp-for="Input.PhoneNumber" class="form-label"></label>
|
||||
<span asp-validation-for="Input.PhoneNumber" class="text-danger"></span>
|
||||
</div>
|
||||
<button id="update-profile-button" type="submit" class="w-100 btn btn-lg btn-primary">Save</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@section Scripts {
|
||||
<partial name="_ValidationScriptsPartial" />
|
||||
}
|
||||
@ -1,119 +0,0 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Text.Encodings.Web;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||
using PSTW_CentralSystem.Models;
|
||||
|
||||
namespace PSTW_CentralSystem.Areas.Identity.Pages.Account.Manage
|
||||
{
|
||||
public class IndexModel : PageModel
|
||||
{
|
||||
private readonly UserManager<UserModel> _userManager;
|
||||
private readonly SignInManager<UserModel> _signInManager;
|
||||
|
||||
public IndexModel(
|
||||
UserManager<UserModel> userManager,
|
||||
SignInManager<UserModel> signInManager)
|
||||
{
|
||||
_userManager = userManager;
|
||||
_signInManager = signInManager;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public string Username { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
[TempData]
|
||||
public string StatusMessage { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
[BindProperty]
|
||||
public InputModel Input { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public class InputModel
|
||||
{
|
||||
/// <summary>
|
||||
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
[Phone]
|
||||
[Display(Name = "Phone number")]
|
||||
public string PhoneNumber { get; set; }
|
||||
}
|
||||
|
||||
private async Task LoadAsync(UserModel user)
|
||||
{
|
||||
var userName = await _userManager.GetUserNameAsync(user);
|
||||
var phoneNumber = await _userManager.GetPhoneNumberAsync(user);
|
||||
|
||||
Username = userName;
|
||||
|
||||
Input = new InputModel
|
||||
{
|
||||
PhoneNumber = phoneNumber
|
||||
};
|
||||
}
|
||||
|
||||
public async Task<IActionResult> 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<IActionResult> 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,123 +0,0 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using Microsoft.AspNetCore.Mvc.Rendering;
|
||||
|
||||
namespace PSTW_CentralSystem.Areas.Identity.Pages.Account.Manage
|
||||
{
|
||||
/// <summary>
|
||||
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public static class ManageNavPages
|
||||
{
|
||||
/// <summary>
|
||||
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public static string Index => "Index";
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public static string Email => "Email";
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public static string ChangePassword => "ChangePassword";
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public static string DownloadPersonalData => "DownloadPersonalData";
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public static string DeletePersonalData => "DeletePersonalData";
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public static string ExternalLogins => "ExternalLogins";
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public static string PersonalData => "PersonalData";
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public static string TwoFactorAuthentication => "TwoFactorAuthentication";
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public static string IndexNavClass(ViewContext viewContext) => PageNavClass(viewContext, Index);
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public static string EmailNavClass(ViewContext viewContext) => PageNavClass(viewContext, Email);
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public static string ChangePasswordNavClass(ViewContext viewContext) => PageNavClass(viewContext, ChangePassword);
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public static string DownloadPersonalDataNavClass(ViewContext viewContext) => PageNavClass(viewContext, DownloadPersonalData);
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public static string DeletePersonalDataNavClass(ViewContext viewContext) => PageNavClass(viewContext, DeletePersonalData);
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public static string ExternalLoginsNavClass(ViewContext viewContext) => PageNavClass(viewContext, ExternalLogins);
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public static string PersonalDataNavClass(ViewContext viewContext) => PageNavClass(viewContext, PersonalData);
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public static string TwoFactorAuthenticationNavClass(ViewContext viewContext) => PageNavClass(viewContext, TwoFactorAuthentication);
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,27 +0,0 @@
|
||||
@page
|
||||
@model PersonalDataModel
|
||||
@{
|
||||
ViewData["Title"] = "Personal Data";
|
||||
ViewData["ActivePage"] = ManageNavPages.PersonalData;
|
||||
}
|
||||
|
||||
<h3>@ViewData["Title"]</h3>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<p>Your account contains personal data that you have given us. This page allows you to download or delete that data.</p>
|
||||
<p>
|
||||
<strong>Deleting this data will permanently remove your account, and this cannot be recovered.</strong>
|
||||
</p>
|
||||
<form id="download-data" asp-page="DownloadPersonalData" method="post">
|
||||
<button class="btn btn-primary" type="submit">Download</button>
|
||||
</form>
|
||||
<p>
|
||||
<a id="delete" asp-page="DeletePersonalData" class="btn btn-danger">Delete</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@section Scripts {
|
||||
<partial name="_ValidationScriptsPartial" />
|
||||
}
|
||||
@ -1,37 +0,0 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using PSTW_CentralSystem.Models;
|
||||
|
||||
namespace PSTW_CentralSystem.Areas.Identity.Pages.Account.Manage
|
||||
{
|
||||
public class PersonalDataModel : PageModel
|
||||
{
|
||||
private readonly UserManager<UserModel> _userManager;
|
||||
private readonly ILogger<PersonalDataModel> _logger;
|
||||
|
||||
public PersonalDataModel(
|
||||
UserManager<UserModel> userManager,
|
||||
ILogger<PersonalDataModel> logger)
|
||||
{
|
||||
_userManager = userManager;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public async Task<IActionResult> OnGet()
|
||||
{
|
||||
var user = await _userManager.GetUserAsync(User);
|
||||
if (user == null)
|
||||
{
|
||||
return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'.");
|
||||
}
|
||||
|
||||
return Page();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,24 +0,0 @@
|
||||
@page
|
||||
@model ResetAuthenticatorModel
|
||||
@{
|
||||
ViewData["Title"] = "Reset authenticator key";
|
||||
ViewData["ActivePage"] = ManageNavPages.TwoFactorAuthentication;
|
||||
}
|
||||
|
||||
<partial name="_StatusMessage" for="StatusMessage" />
|
||||
<h3>@ViewData["Title"]</h3>
|
||||
<div class="alert alert-warning" role="alert">
|
||||
<p>
|
||||
<span class="glyphicon glyphicon-warning-sign"></span>
|
||||
<strong>If you reset your authenticator key your authenticator app will not work until you reconfigure it.</strong>
|
||||
</p>
|
||||
<p>
|
||||
This process disables 2FA until you verify your authenticator app.
|
||||
If you do not complete your authenticator app configuration you may lose access to your account.
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<form id="reset-authenticator-form" method="post">
|
||||
<button id="reset-authenticator-button" class="btn btn-danger" type="submit">Reset authenticator key</button>
|
||||
</form>
|
||||
</div>
|
||||
@ -1,68 +0,0 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using PSTW_CentralSystem.Models;
|
||||
|
||||
namespace PSTW_CentralSystem.Areas.Identity.Pages.Account.Manage
|
||||
{
|
||||
public class ResetAuthenticatorModel : PageModel
|
||||
{
|
||||
private readonly UserManager<UserModel> _userManager;
|
||||
private readonly SignInManager<UserModel> _signInManager;
|
||||
private readonly ILogger<ResetAuthenticatorModel> _logger;
|
||||
|
||||
public ResetAuthenticatorModel(
|
||||
UserManager<UserModel> userManager,
|
||||
SignInManager<UserModel> signInManager,
|
||||
ILogger<ResetAuthenticatorModel> logger)
|
||||
{
|
||||
_userManager = userManager;
|
||||
_signInManager = signInManager;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
[TempData]
|
||||
public string StatusMessage { get; set; }
|
||||
|
||||
public async Task<IActionResult> 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<IActionResult> 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);
|
||||
var userId = await _userManager.GetUserIdAsync(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");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,35 +0,0 @@
|
||||
@page
|
||||
@model SetPasswordModel
|
||||
@{
|
||||
ViewData["Title"] = "Set password";
|
||||
ViewData["ActivePage"] = ManageNavPages.ChangePassword;
|
||||
}
|
||||
|
||||
<h3>Set your password</h3>
|
||||
<partial name="_StatusMessage" for="StatusMessage" />
|
||||
<p class="text-info">
|
||||
You do not have a local username/password for this site. Add a local
|
||||
account so you can log in without an external login.
|
||||
</p>
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<form id="set-password-form" method="post">
|
||||
<div asp-validation-summary="ModelOnly" class="text-danger" role="alert"></div>
|
||||
<div class="form-floating mb-3">
|
||||
<input asp-for="Input.NewPassword" class="form-control" autocomplete="new-password" placeholder="Please enter your new password."/>
|
||||
<label asp-for="Input.NewPassword" class="form-label"></label>
|
||||
<span asp-validation-for="Input.NewPassword" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-floating mb-3">
|
||||
<input asp-for="Input.ConfirmPassword" class="form-control" autocomplete="new-password" placeholder="Please confirm your new password."/>
|
||||
<label asp-for="Input.ConfirmPassword" class="form-label"></label>
|
||||
<span asp-validation-for="Input.ConfirmPassword" class="text-danger"></span>
|
||||
</div>
|
||||
<button type="submit" class="w-100 btn btn-lg btn-primary">Set password</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@section Scripts {
|
||||
<partial name="_ValidationScriptsPartial" />
|
||||
}
|
||||
@ -1,115 +0,0 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||
using PSTW_CentralSystem.Models;
|
||||
|
||||
namespace PSTW_CentralSystem.Areas.Identity.Pages.Account.Manage
|
||||
{
|
||||
public class SetPasswordModel : PageModel
|
||||
{
|
||||
private readonly UserManager<UserModel> _userManager;
|
||||
private readonly SignInManager<UserModel> _signInManager;
|
||||
|
||||
public SetPasswordModel(
|
||||
UserManager<UserModel> userManager,
|
||||
SignInManager<UserModel> signInManager)
|
||||
{
|
||||
_userManager = userManager;
|
||||
_signInManager = signInManager;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
[BindProperty]
|
||||
public InputModel Input { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
[TempData]
|
||||
public string StatusMessage { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public class InputModel
|
||||
{
|
||||
/// <summary>
|
||||
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
[Required]
|
||||
[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; }
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
[DataType(DataType.Password)]
|
||||
[Display(Name = "Confirm new password")]
|
||||
[Compare("NewPassword", ErrorMessage = "The new password and confirmation password do not match.")]
|
||||
public string ConfirmPassword { get; set; }
|
||||
}
|
||||
|
||||
public async Task<IActionResult> 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<IActionResult> 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,25 +0,0 @@
|
||||
@page
|
||||
@model ShowRecoveryCodesModel
|
||||
@{
|
||||
ViewData["Title"] = "Recovery codes";
|
||||
ViewData["ActivePage"] = "TwoFactorAuthentication";
|
||||
}
|
||||
|
||||
<partial name="_StatusMessage" for="StatusMessage" />
|
||||
<h3>@ViewData["Title"]</h3>
|
||||
<div class="alert alert-warning" role="alert">
|
||||
<p>
|
||||
<strong>Put these codes in a safe place.</strong>
|
||||
</p>
|
||||
<p>
|
||||
If you lose your device and don't have the recovery codes you will lose access to your account.
|
||||
</p>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
@for (var row = 0; row < Model.RecoveryCodes.Length; row += 2)
|
||||
{
|
||||
<code class="recovery-code">@Model.RecoveryCodes[row]</code><text> </text><code class="recovery-code">@Model.RecoveryCodes[row + 1]</code><br />
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
@ -1,47 +0,0 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
#nullable disable
|
||||
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using PSTW_CentralSystem.Models;
|
||||
|
||||
namespace PSTW_CentralSystem.Areas.Identity.Pages.Account.Manage
|
||||
{
|
||||
/// <summary>
|
||||
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public class ShowRecoveryCodesModel : PageModel
|
||||
{
|
||||
/// <summary>
|
||||
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
[TempData]
|
||||
public string[] RecoveryCodes { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
[TempData]
|
||||
public string StatusMessage { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public IActionResult OnGet()
|
||||
{
|
||||
if (RecoveryCodes == null || RecoveryCodes.Length == 0)
|
||||
{
|
||||
return RedirectToPage("./TwoFactorAuthentication");
|
||||
}
|
||||
|
||||
return Page();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,71 +0,0 @@
|
||||
@page
|
||||
@using Microsoft.AspNetCore.Http.Features
|
||||
@model TwoFactorAuthenticationModel
|
||||
@{
|
||||
ViewData["Title"] = "Two-factor authentication (2FA)";
|
||||
ViewData["ActivePage"] = ManageNavPages.TwoFactorAuthentication;
|
||||
}
|
||||
|
||||
<partial name="_StatusMessage" for="StatusMessage" />
|
||||
<h3>@ViewData["Title"]</h3>
|
||||
@{
|
||||
var consentFeature = HttpContext.Features.Get<ITrackingConsentFeature>();
|
||||
@if (consentFeature?.CanTrack ?? true)
|
||||
{
|
||||
@if (Model.Is2faEnabled)
|
||||
{
|
||||
if (Model.RecoveryCodesLeft == 0)
|
||||
{
|
||||
<div class="alert alert-danger">
|
||||
<strong>You have no recovery codes left.</strong>
|
||||
<p>You must <a asp-page="./GenerateRecoveryCodes">generate a new set of recovery codes</a> before you can log in with a recovery code.</p>
|
||||
</div>
|
||||
}
|
||||
else if (Model.RecoveryCodesLeft == 1)
|
||||
{
|
||||
<div class="alert alert-danger">
|
||||
<strong>You have 1 recovery code left.</strong>
|
||||
<p>You can <a asp-page="./GenerateRecoveryCodes">generate a new set of recovery codes</a>.</p>
|
||||
</div>
|
||||
}
|
||||
else if (Model.RecoveryCodesLeft <= 3)
|
||||
{
|
||||
<div class="alert alert-warning">
|
||||
<strong>You have @Model.RecoveryCodesLeft recovery codes left.</strong>
|
||||
<p>You should <a asp-page="./GenerateRecoveryCodes">generate a new set of recovery codes</a>.</p>
|
||||
</div>
|
||||
}
|
||||
|
||||
if (Model.IsMachineRemembered)
|
||||
{
|
||||
<form method="post" style="display: inline-block">
|
||||
<button type="submit" class="btn btn-primary">Forget this browser</button>
|
||||
</form>
|
||||
}
|
||||
<a asp-page="./Disable2fa" class="btn btn-primary">Disable 2FA</a>
|
||||
<a asp-page="./GenerateRecoveryCodes" class="btn btn-primary">Reset recovery codes</a>
|
||||
}
|
||||
|
||||
<h4>Authenticator app</h4>
|
||||
@if (!Model.HasAuthenticator)
|
||||
{
|
||||
<a id="enable-authenticator" asp-page="./EnableAuthenticator" class="btn btn-primary">Add authenticator app</a>
|
||||
}
|
||||
else
|
||||
{
|
||||
<a id="enable-authenticator" asp-page="./EnableAuthenticator" class="btn btn-primary">Set up authenticator app</a>
|
||||
<a id="reset-authenticator" asp-page="./ResetAuthenticator" class="btn btn-primary">Reset authenticator app</a>
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
<div class="alert alert-danger">
|
||||
<strong>Privacy and cookie policy have not been accepted.</strong>
|
||||
<p>You must accept the policy before you can enable two factor authentication.</p>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
|
||||
@section Scripts {
|
||||
<partial name="_ValidationScriptsPartial" />
|
||||
}
|
||||
@ -1,90 +0,0 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using PSTW_CentralSystem.Models;
|
||||
|
||||
namespace PSTW_CentralSystem.Areas.Identity.Pages.Account.Manage
|
||||
{
|
||||
public class TwoFactorAuthenticationModel : PageModel
|
||||
{
|
||||
private readonly UserManager<UserModel> _userManager;
|
||||
private readonly SignInManager<UserModel> _signInManager;
|
||||
private readonly ILogger<TwoFactorAuthenticationModel> _logger;
|
||||
|
||||
public TwoFactorAuthenticationModel(
|
||||
UserManager<UserModel> userManager, SignInManager<UserModel> signInManager, ILogger<TwoFactorAuthenticationModel> logger)
|
||||
{
|
||||
_userManager = userManager;
|
||||
_signInManager = signInManager;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public bool HasAuthenticator { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public int RecoveryCodesLeft { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
[BindProperty]
|
||||
public bool Is2faEnabled { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public bool IsMachineRemembered { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
[TempData]
|
||||
public string StatusMessage { get; set; }
|
||||
|
||||
public async Task<IActionResult> OnGetAsync()
|
||||
{
|
||||
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<IActionResult> OnPostAsync()
|
||||
{
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,29 +0,0 @@
|
||||
@{
|
||||
if (ViewData.TryGetValue("ParentLayout", out var parentLayout) && parentLayout != null)
|
||||
{
|
||||
Layout = parentLayout.ToString();
|
||||
}
|
||||
else
|
||||
{
|
||||
Layout = "/Areas/Identity/Pages/_Layout.cshtml";
|
||||
}
|
||||
}
|
||||
|
||||
<h1>Manage your account</h1>
|
||||
|
||||
<div>
|
||||
<h2>Change your account settings</h2>
|
||||
<hr />
|
||||
<div class="row">
|
||||
<div class="col-md-3">
|
||||
<partial name="_ManageNav" />
|
||||
</div>
|
||||
<div class="col-md-9">
|
||||
@RenderBody()
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@section Scripts {
|
||||
@RenderSection("Scripts", required: false)
|
||||
}
|
||||
@ -1,15 +0,0 @@
|
||||
@inject SignInManager<UserModel> SignInManager
|
||||
@{
|
||||
var hasExternalLogins = (await SignInManager.GetExternalAuthenticationSchemesAsync()).Any();
|
||||
}
|
||||
<ul class="nav nav-pills flex-column">
|
||||
<li class="nav-item"><a class="nav-link @ManageNavPages.IndexNavClass(ViewContext)" id="profile" asp-page="./Index">Profile</a></li>
|
||||
<li class="nav-item"><a class="nav-link @ManageNavPages.EmailNavClass(ViewContext)" id="email" asp-page="./Email">Email</a></li>
|
||||
<li class="nav-item"><a class="nav-link @ManageNavPages.ChangePasswordNavClass(ViewContext)" id="change-password" asp-page="./ChangePassword">Password</a></li>
|
||||
@if (hasExternalLogins)
|
||||
{
|
||||
<li id="external-logins" class="nav-item"><a id="external-login" class="nav-link @ManageNavPages.ExternalLoginsNavClass(ViewContext)" asp-page="./ExternalLogins">External logins</a></li>
|
||||
}
|
||||
<li class="nav-item"><a class="nav-link @ManageNavPages.TwoFactorAuthenticationNavClass(ViewContext)" id="two-factor" asp-page="./TwoFactorAuthentication">Two-factor authentication</a></li>
|
||||
<li class="nav-item"><a class="nav-link @ManageNavPages.PersonalDataNavClass(ViewContext)" id="personal-data" asp-page="./PersonalData">Personal data</a></li>
|
||||
</ul>
|
||||
@ -1,10 +0,0 @@
|
||||
@model string
|
||||
|
||||
@if (!String.IsNullOrEmpty(Model))
|
||||
{
|
||||
var statusMessageClass = Model.StartsWith("Error") ? "danger" : "success";
|
||||
<div class="alert alert-@statusMessageClass alert-dismissible" role="alert">
|
||||
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
|
||||
@Model
|
||||
</div>
|
||||
}
|
||||
@ -1 +0,0 @@
|
||||
@using PSTW_CentralSystem.Areas.Identity.Pages.Account.Manage
|
||||
@ -1,67 +0,0 @@
|
||||
@page
|
||||
@model RegisterModel
|
||||
@{
|
||||
ViewData["Title"] = "Register";
|
||||
}
|
||||
|
||||
<h1>@ViewData["Title"]</h1>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-4">
|
||||
<form id="registerForm" asp-route-returnUrl="@Model.ReturnUrl" method="post">
|
||||
<h2>Create a new account.</h2>
|
||||
<hr />
|
||||
<div asp-validation-summary="ModelOnly" class="text-danger" role="alert"></div>
|
||||
<div class="form-floating mb-3">
|
||||
<input asp-for="Input.Email" class="form-control" autocomplete="username" aria-required="true" placeholder="name@example.com" />
|
||||
<label asp-for="Input.Email">Email</label>
|
||||
<span asp-validation-for="Input.Email" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-floating mb-3">
|
||||
<input asp-for="Input.Password" class="form-control" autocomplete="new-password" aria-required="true" placeholder="password" />
|
||||
<label asp-for="Input.Password">Password</label>
|
||||
<span asp-validation-for="Input.Password" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-floating mb-3">
|
||||
<input asp-for="Input.ConfirmPassword" class="form-control" autocomplete="new-password" aria-required="true" placeholder="password" />
|
||||
<label asp-for="Input.ConfirmPassword">Confirm Password</label>
|
||||
<span asp-validation-for="Input.ConfirmPassword" class="text-danger"></span>
|
||||
</div>
|
||||
<button id="registerSubmit" type="submit" class="w-100 btn btn-lg btn-primary">Register</button>
|
||||
</form>
|
||||
</div>
|
||||
<div class="col-md-6 col-md-offset-2">
|
||||
<section>
|
||||
<h3>Use another service to register.</h3>
|
||||
<hr />
|
||||
@{
|
||||
if ((Model.ExternalLogins?.Count ?? 0) == 0)
|
||||
{
|
||||
<div>
|
||||
<p>
|
||||
There are no external authentication services configured. See this <a href="https://go.microsoft.com/fwlink/?LinkID=532715">article
|
||||
about setting up this ASP.NET application to support logging in via external services</a>.
|
||||
</p>
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<form id="external-account" asp-page="./ExternalLogin" asp-route-returnUrl="@Model.ReturnUrl" method="post" class="form-horizontal">
|
||||
<div>
|
||||
<p>
|
||||
@foreach (var provider in Model.ExternalLogins!)
|
||||
{
|
||||
<button type="submit" class="btn btn-primary" name="provider" value="@provider.Name" title="Log in using your @provider.DisplayName account">@provider.DisplayName</button>
|
||||
}
|
||||
</p>
|
||||
</div>
|
||||
</form>
|
||||
}
|
||||
}
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@section Scripts {
|
||||
<partial name="_ValidationScriptsPartial" />
|
||||
}
|
||||
@ -1,182 +0,0 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.Encodings.Web;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using 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 PSTW_CentralSystem.Models;
|
||||
|
||||
namespace PSTW_CentralSystem.Areas.Identity.Pages.Account
|
||||
{
|
||||
[Authorize]
|
||||
public class RegisterModel : PageModel
|
||||
{
|
||||
private readonly SignInManager<UserModel> _signInManager;
|
||||
private readonly UserManager<UserModel> _userManager;
|
||||
private readonly IUserStore<UserModel> _userStore;
|
||||
private readonly IUserEmailStore<UserModel> _emailStore;
|
||||
private readonly ILogger<RegisterModel> _logger;
|
||||
private readonly IEmailSender _emailSender;
|
||||
|
||||
public RegisterModel(
|
||||
UserManager<UserModel> userManager,
|
||||
IUserStore<UserModel> userStore,
|
||||
SignInManager<UserModel> signInManager,
|
||||
ILogger<RegisterModel> logger,
|
||||
IEmailSender emailSender)
|
||||
{
|
||||
_userManager = userManager;
|
||||
_userStore = userStore;
|
||||
_emailStore = GetEmailStore();
|
||||
_signInManager = signInManager;
|
||||
_logger = logger;
|
||||
_emailSender = emailSender;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
[BindProperty]
|
||||
public InputModel Input { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public string ReturnUrl { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public IList<AuthenticationScheme> ExternalLogins { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public class InputModel
|
||||
{
|
||||
/// <summary>
|
||||
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
[Required]
|
||||
[EmailAddress]
|
||||
[Display(Name = "Email")]
|
||||
public string Email { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
[Required]
|
||||
[StringLength(100, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)]
|
||||
[DataType(DataType.Password)]
|
||||
[Display(Name = "Password")]
|
||||
public string Password { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
[DataType(DataType.Password)]
|
||||
[Display(Name = "Confirm password")]
|
||||
[Compare("Password", ErrorMessage = "The password and confirmation password do not match.")]
|
||||
public string ConfirmPassword { get; set; }
|
||||
}
|
||||
|
||||
|
||||
public async Task OnGetAsync(string returnUrl = null)
|
||||
{
|
||||
ReturnUrl = returnUrl;
|
||||
ExternalLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync()).ToList();
|
||||
}
|
||||
|
||||
public async Task<IActionResult> OnPostAsync(string returnUrl = null)
|
||||
{
|
||||
returnUrl ??= Url.Content("~/");
|
||||
ExternalLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync()).ToList();
|
||||
if (ModelState.IsValid)
|
||||
{
|
||||
var user = CreateUser();
|
||||
|
||||
await _userStore.SetUserNameAsync(user, Input.Email, CancellationToken.None);
|
||||
await _emailStore.SetEmailAsync(user, Input.Email, CancellationToken.None);
|
||||
var result = await _userManager.CreateAsync(user, Input.Password);
|
||||
|
||||
if (result.Succeeded)
|
||||
{
|
||||
_logger.LogInformation("User created a new account with password.");
|
||||
|
||||
var userId = await _userManager.GetUserIdAsync(user);
|
||||
var code = await _userManager.GenerateEmailConfirmationTokenAsync(user);
|
||||
code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code));
|
||||
var callbackUrl = Url.Page(
|
||||
"/Account/ConfirmEmail",
|
||||
pageHandler: null,
|
||||
values: new { area = "Identity", userId = userId, code = code, returnUrl = returnUrl },
|
||||
protocol: Request.Scheme);
|
||||
|
||||
await _emailSender.SendEmailAsync(Input.Email, "Confirm your email",
|
||||
$"Please confirm your account by <a href='{HtmlEncoder.Default.Encode(callbackUrl)}'>clicking here</a>.");
|
||||
|
||||
if (_userManager.Options.SignIn.RequireConfirmedAccount)
|
||||
{
|
||||
return RedirectToPage("RegisterConfirmation", new { email = Input.Email, returnUrl = returnUrl });
|
||||
}
|
||||
else
|
||||
{
|
||||
await _signInManager.SignInAsync(user, isPersistent: false);
|
||||
return LocalRedirect(returnUrl);
|
||||
}
|
||||
}
|
||||
foreach (var error in result.Errors)
|
||||
{
|
||||
ModelState.AddModelError(string.Empty, error.Description);
|
||||
}
|
||||
}
|
||||
|
||||
// If we got this far, something failed, redisplay form
|
||||
return Page();
|
||||
}
|
||||
|
||||
private UserModel CreateUser()
|
||||
{
|
||||
try
|
||||
{
|
||||
return Activator.CreateInstance<UserModel>();
|
||||
}
|
||||
catch
|
||||
{
|
||||
throw new InvalidOperationException($"Can't create an instance of '{nameof(UserModel)}'. " +
|
||||
$"Ensure that '{nameof(UserModel)}' is not an abstract class and has a parameterless constructor, or alternatively " +
|
||||
$"override the register page in /Areas/Identity/Pages/Account/Register.cshtml");
|
||||
}
|
||||
}
|
||||
|
||||
private IUserEmailStore<UserModel> GetEmailStore()
|
||||
{
|
||||
if (!_userManager.SupportsUserEmail)
|
||||
{
|
||||
throw new NotSupportedException("The default UI requires a user store with email support.");
|
||||
}
|
||||
return (IUserEmailStore<UserModel>)_userStore;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,23 +0,0 @@
|
||||
@page
|
||||
@model RegisterConfirmationModel
|
||||
@{
|
||||
ViewData["Title"] = "Register confirmation";
|
||||
}
|
||||
|
||||
<h1>@ViewData["Title"]</h1>
|
||||
@{
|
||||
if (@Model.DisplayConfirmAccountLink)
|
||||
{
|
||||
<p>
|
||||
This app does not currently have a real email sender registered, see <a href="https://aka.ms/aspaccountconf">these docs</a> for how to configure a real email sender.
|
||||
Normally this would be emailed: <a id="confirm-link" href="@Model.EmailConfirmationUrl">Click here to confirm your account</a>
|
||||
</p>
|
||||
}
|
||||
else
|
||||
{
|
||||
<p>
|
||||
Please check your email to confirm your account.
|
||||
</p>
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,80 +0,0 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using System.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 PSTW_CentralSystem.Models;
|
||||
|
||||
namespace PSTW_CentralSystem.Areas.Identity.Pages.Account
|
||||
{
|
||||
[AllowAnonymous]
|
||||
public class RegisterConfirmationModel : PageModel
|
||||
{
|
||||
private readonly UserManager<UserModel> _userManager;
|
||||
private readonly IEmailSender _sender;
|
||||
|
||||
public RegisterConfirmationModel(UserManager<UserModel> userManager, IEmailSender sender)
|
||||
{
|
||||
_userManager = userManager;
|
||||
_sender = sender;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public string Email { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public bool DisplayConfirmAccountLink { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public string EmailConfirmationUrl { get; set; }
|
||||
|
||||
public async Task<IActionResult> OnGetAsync(string email, string returnUrl = null)
|
||||
{
|
||||
if (email == null)
|
||||
{
|
||||
return RedirectToPage("/Index");
|
||||
}
|
||||
returnUrl = returnUrl ?? Url.Content("~/");
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,26 +0,0 @@
|
||||
@page
|
||||
@model ResendEmailConfirmationModel
|
||||
@{
|
||||
ViewData["Title"] = "Resend email confirmation";
|
||||
}
|
||||
|
||||
<h1>@ViewData["Title"]</h1>
|
||||
<h2>Enter your email.</h2>
|
||||
<hr />
|
||||
<div class="row">
|
||||
<div class="col-md-4">
|
||||
<form method="post">
|
||||
<div asp-validation-summary="All" class="text-danger" role="alert"></div>
|
||||
<div class="form-floating mb-3">
|
||||
<input asp-for="Input.Email" class="form-control" aria-required="true" placeholder="name@example.com" />
|
||||
<label asp-for="Input.Email" class="form-label"></label>
|
||||
<span asp-validation-for="Input.Email" class="text-danger"></span>
|
||||
</div>
|
||||
<button type="submit" class="w-100 btn btn-lg btn-primary">Resend</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@section Scripts {
|
||||
<partial name="_ValidationScriptsPartial" />
|
||||
}
|
||||
@ -1,89 +0,0 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using System.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 PSTW_CentralSystem.Models;
|
||||
|
||||
namespace PSTW_CentralSystem.Areas.Identity.Pages.Account
|
||||
{
|
||||
[AllowAnonymous]
|
||||
public class ResendEmailConfirmationModel : PageModel
|
||||
{
|
||||
private readonly UserManager<UserModel> _userManager;
|
||||
private readonly IEmailSender _emailSender;
|
||||
|
||||
public ResendEmailConfirmationModel(UserManager<UserModel> userManager, IEmailSender emailSender)
|
||||
{
|
||||
_userManager = userManager;
|
||||
_emailSender = emailSender;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
[BindProperty]
|
||||
public InputModel Input { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public class InputModel
|
||||
{
|
||||
/// <summary>
|
||||
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
[Required]
|
||||
[EmailAddress]
|
||||
public string Email { get; set; }
|
||||
}
|
||||
|
||||
public void OnGet()
|
||||
{
|
||||
}
|
||||
|
||||
public async Task<IActionResult> 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 <a href='{HtmlEncoder.Default.Encode(callbackUrl)}'>clicking here</a>.");
|
||||
|
||||
ModelState.AddModelError(string.Empty, "Verification email sent. Please check your email.");
|
||||
return Page();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,37 +0,0 @@
|
||||
@page
|
||||
@model ResetPasswordModel
|
||||
@{
|
||||
ViewData["Title"] = "Reset password";
|
||||
}
|
||||
|
||||
<h1>@ViewData["Title"]</h1>
|
||||
<h2>Reset your password.</h2>
|
||||
<hr />
|
||||
<div class="row">
|
||||
<div class="col-md-4">
|
||||
<form method="post">
|
||||
<div asp-validation-summary="ModelOnly" class="text-danger" role="alert"></div>
|
||||
<input asp-for="Input.Code" type="hidden" />
|
||||
<div class="form-floating mb-3">
|
||||
<input asp-for="Input.Email" class="form-control" autocomplete="username" aria-required="true" placeholder="name@example.com" />
|
||||
<label asp-for="Input.Email" class="form-label"></label>
|
||||
<span asp-validation-for="Input.Email" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-floating mb-3">
|
||||
<input asp-for="Input.Password" class="form-control" autocomplete="new-password" aria-required="true" placeholder="Please enter your password." />
|
||||
<label asp-for="Input.Password" class="form-label"></label>
|
||||
<span asp-validation-for="Input.Password" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-floating mb-3">
|
||||
<input asp-for="Input.ConfirmPassword" class="form-control" autocomplete="new-password" aria-required="true" placeholder="Please confirm your password." />
|
||||
<label asp-for="Input.ConfirmPassword" class="form-label"></label>
|
||||
<span asp-validation-for="Input.ConfirmPassword" class="text-danger"></span>
|
||||
</div>
|
||||
<button type="submit" class="w-100 btn btn-lg btn-primary">Reset</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@section Scripts {
|
||||
<partial name="_ValidationScriptsPartial" />
|
||||
}
|
||||
@ -1,118 +0,0 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
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 PSTW_CentralSystem.Models;
|
||||
|
||||
namespace PSTW_CentralSystem.Areas.Identity.Pages.Account
|
||||
{
|
||||
public class ResetPasswordModel : PageModel
|
||||
{
|
||||
private readonly UserManager<UserModel> _userManager;
|
||||
|
||||
public ResetPasswordModel(UserManager<UserModel> userManager)
|
||||
{
|
||||
_userManager = userManager;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
[BindProperty]
|
||||
public InputModel Input { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public class InputModel
|
||||
{
|
||||
/// <summary>
|
||||
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
[Required]
|
||||
[EmailAddress]
|
||||
public string Email { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
[Required]
|
||||
[StringLength(100, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)]
|
||||
[DataType(DataType.Password)]
|
||||
public string Password { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
[DataType(DataType.Password)]
|
||||
[Display(Name = "Confirm password")]
|
||||
[Compare("Password", ErrorMessage = "The password and confirmation password do not match.")]
|
||||
public string ConfirmPassword { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
[Required]
|
||||
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<IActionResult> 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,10 +0,0 @@
|
||||
@page
|
||||
@model ResetPasswordConfirmationModel
|
||||
@{
|
||||
ViewData["Title"] = "Reset password confirmation";
|
||||
}
|
||||
|
||||
<h1>@ViewData["Title"]</h1>
|
||||
<p>
|
||||
Your password has been reset. Please <a asp-page="./Login">click here to log in</a>.
|
||||
</p>
|
||||
@ -1,25 +0,0 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
#nullable disable
|
||||
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||
|
||||
namespace PSTW_CentralSystem.Areas.Identity.Pages.Account
|
||||
{
|
||||
/// <summary>
|
||||
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
[AllowAnonymous]
|
||||
public class ResetPasswordConfirmationModel : PageModel
|
||||
{
|
||||
/// <summary>
|
||||
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public void OnGet()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,10 +0,0 @@
|
||||
@model string
|
||||
|
||||
@if (!String.IsNullOrEmpty(Model))
|
||||
{
|
||||
var statusMessageClass = Model.StartsWith("Error") ? "danger" : "success";
|
||||
<div class="alert alert-@statusMessageClass alert-dismissible" role="alert">
|
||||
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
|
||||
@Model
|
||||
</div>
|
||||
}
|
||||
@ -1 +0,0 @@
|
||||
@using PSTW_CentralSystem.Areas.Identity.Pages.Account
|
||||
@ -1,23 +0,0 @@
|
||||
@page
|
||||
@model ErrorModel
|
||||
@{
|
||||
ViewData["Title"] = "Error";
|
||||
}
|
||||
|
||||
<h1 class="text-danger">Error.</h1>
|
||||
<h2 class="text-danger">An error occurred while processing your request.</h2>
|
||||
|
||||
@if (Model.ShowRequestId)
|
||||
{
|
||||
<p>
|
||||
<strong>Request ID:</strong> <code>@Model.RequestId</code>
|
||||
</p>
|
||||
}
|
||||
|
||||
<h3>Development Mode</h3>
|
||||
<p>
|
||||
Swapping to <strong>Development</strong> environment will display more detailed information about the error that occurred.
|
||||
</p>
|
||||
<p>
|
||||
<strong>Development environment should not be enabled in deployed applications</strong>, as it can result in sensitive information from exceptions being displayed to end users. For local debugging, development environment can be enabled by setting the <strong>ASPNETCORE_ENVIRONMENT</strong> environment variable to <strong>Development</strong>, and restarting the application.
|
||||
</p>
|
||||
@ -1,41 +0,0 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
#nullable disable
|
||||
|
||||
using System.Diagnostics;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||
|
||||
namespace PSTW_CentralSystem.Areas.Identity.Pages
|
||||
{
|
||||
/// <summary>
|
||||
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
[AllowAnonymous]
|
||||
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
|
||||
public class ErrorModel : PageModel
|
||||
{
|
||||
/// <summary>
|
||||
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public string RequestId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public bool ShowRequestId => !string.IsNullOrEmpty(RequestId);
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public void OnGet()
|
||||
{
|
||||
RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,2 +0,0 @@
|
||||
<script src="~/lib/jquery-validation/dist/jquery.validate.min.js"></script>
|
||||
<script src="~/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.min.js"></script>
|
||||
@ -1,5 +0,0 @@
|
||||
@using Microsoft.AspNetCore.Identity
|
||||
@using PSTW_CentralSystem.Areas.Identity
|
||||
@using PSTW_CentralSystem.Areas.Identity.Pages
|
||||
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
|
||||
@using PSTW_CentralSystem.Models
|
||||
@ -1,4 +0,0 @@
|
||||
|
||||
@{
|
||||
Layout = "/Views/Shared/_Layout.cshtml";
|
||||
}
|
||||
@ -1,34 +0,0 @@
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace PSTW_CentralSystem.Areas.Inventory.Controllers.Admin
|
||||
{
|
||||
[Area("Inventory")]
|
||||
[Authorize(Policy = "RoleModulePolicy")]
|
||||
public class InventoryMasterController : Controller
|
||||
{
|
||||
public IActionResult AdminDashboard()
|
||||
{
|
||||
return View();
|
||||
}
|
||||
|
||||
public IActionResult ItemRegistration()
|
||||
{
|
||||
return View();
|
||||
}
|
||||
|
||||
public IActionResult ProductRegistration()
|
||||
{
|
||||
return View();
|
||||
}
|
||||
|
||||
public IActionResult SupplierRegistration()
|
||||
{
|
||||
return View();
|
||||
}
|
||||
public IActionResult ManifacturerRegistration()
|
||||
{
|
||||
return View();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,27 +0,0 @@
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using PSTW_CentralSystem.Areas.Inventory.Models;
|
||||
|
||||
namespace PSTW_CentralSystem.Areas.Inventory.Controllers
|
||||
{
|
||||
[Area("Inventory")]
|
||||
|
||||
//[Authorize(Policy = "RoleModulePolicy")]
|
||||
public class ItemMovementController : Controller
|
||||
{
|
||||
// GET: Inventory
|
||||
public ActionResult Index()
|
||||
{
|
||||
return View();
|
||||
}
|
||||
|
||||
[Authorize]
|
||||
[HttpPost("/i/{id}")]
|
||||
public IActionResult ItemRecognization(string id, [FromBody] ItemModel item)
|
||||
{
|
||||
return View();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@ -1,18 +0,0 @@
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace PSTW_CentralSystem.Areas.Inventory.Controllers
|
||||
{
|
||||
[Area("Inventory")]
|
||||
//[Authorize(Policy = "RoleModulePolicy")]
|
||||
public class MainController : Controller
|
||||
{
|
||||
// GET: Inventory
|
||||
public ActionResult Index()
|
||||
{
|
||||
return View();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@ -1,20 +0,0 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using PSTW_CentralSystem.Models;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace PSTW_CentralSystem.Areas.Inventory.Models
|
||||
{
|
||||
public class InventoryMasterModel
|
||||
{
|
||||
[Key]
|
||||
public int StoreId { get; set; }
|
||||
public int UserId { get; set; }
|
||||
[ForeignKey("UserId")]
|
||||
public virtual UserModel? User { get; set; }
|
||||
[ForeignKey("StoreId")]
|
||||
public virtual StoreModel? Store { get; set; }
|
||||
}
|
||||
}
|
||||
@ -1,50 +0,0 @@
|
||||
using System.ComponentModel;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using PSTW_CentralSystem.Models;
|
||||
|
||||
namespace PSTW_CentralSystem.Areas.Inventory.Models
|
||||
{
|
||||
public class ItemModel
|
||||
{
|
||||
[Key]
|
||||
public int ItemID { get; set; }
|
||||
public string UniqueID { get; set; } = string.Empty;
|
||||
public required int CompanyId { get; set; }
|
||||
public required int DepartmentId { get; set; }
|
||||
public required int ProductId { get; set; }
|
||||
public required string? SerialNumber { get; set; }
|
||||
public required string? TeamType { get; set; }
|
||||
public required int Quantity { get; set; }
|
||||
public required string Supplier { get; set; }
|
||||
public required DateTime PurchaseDate { get; set; }
|
||||
public required string PONo { get; set; }
|
||||
public required string Currency { get; set; }
|
||||
public required float DefaultPrice { get; set; }
|
||||
public required float CurrencyRate { get; set; }
|
||||
public required float ConvertPrice { get; set; }
|
||||
public string? DONo { get; set; }
|
||||
public DateTime? DODate { get; set; }
|
||||
public required int Warranty { get; set; }
|
||||
public required DateTime EndWDate { get; set; }
|
||||
public string? InvoiceNo { get; set; }
|
||||
public DateTime? InvoiceDate { get; set; }
|
||||
[Comment("1 = In stock; 2 = Item Moving; 3 = Item Out; 4 = Item Broken; 5 = Item Lost; 6 = Item Stolen; 7 = Item Damaged; 8 = Item Discarded; 9 = Item Destroyed; 10 = Item Finished;")]
|
||||
public int ItemStatus { get; set; } = 1;
|
||||
public int? MovementId { get; set; }
|
||||
public string PartNumber { get; set; } = string.Empty;
|
||||
public int CreatedByUserId { get; set; }
|
||||
[ForeignKey("CreatedByUserId")]
|
||||
public virtual UserModel? CreatedBy { get; set; }
|
||||
[ForeignKey("CompanyId")]
|
||||
public virtual CompanyModel? Company { get; set; }
|
||||
[ForeignKey("DepartmentId")]
|
||||
public virtual DepartmentModel? Department { get; set; }
|
||||
[ForeignKey("ProductId")]
|
||||
public virtual ProductModel? Product { get; set; }
|
||||
[ForeignKey("MovementId")]
|
||||
public virtual ItemMovementModel? Movement { get; set; }
|
||||
|
||||
}
|
||||
}
|
||||
@ -1,43 +0,0 @@
|
||||
using PSTW_CentralSystem.Models;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
namespace PSTW_CentralSystem.Areas.Inventory.Models
|
||||
{
|
||||
public class ItemMovementModel
|
||||
{
|
||||
[Key]
|
||||
public int Id { get; set; }
|
||||
public int ItemId { get; set; }
|
||||
public int? ToStation { get; set; }
|
||||
public int? ToStore { get; set; }
|
||||
public int? ToUser { get; set; }
|
||||
[Comment("Repair, Calibration, Faulty, Ready To Deploy, On Delivery")]
|
||||
public string? ToOther { get; set; }
|
||||
[Comment("Register, StockIn, Stock Out")]
|
||||
public string? Action { get; set; }
|
||||
public int? Quantity { get; set; }
|
||||
public string? Remark { get; set; }
|
||||
public string? ConsignmentNote { get; set; }
|
||||
public DateTime Date { get; set; }
|
||||
public int? LastUser { get; set; }
|
||||
public int? LastStore{ get; set; }
|
||||
public int? LastStation{ get; set; }
|
||||
[Comment("Repair, Calibration, Faulty, Ready To Deploy, On Delivery")]
|
||||
public string? LatestStatus { get; set; }
|
||||
public bool MovementComplete { get; set; } = false;
|
||||
public virtual ItemModel? Item { get; set; }
|
||||
[ForeignKey("ToStore")]
|
||||
public virtual StoreModel? NextStore { get; set; }
|
||||
[ForeignKey("ToStation")]
|
||||
public virtual StationModel? NextStation { get; set; }
|
||||
[ForeignKey("ToUser")]
|
||||
public virtual UserModel? NextUser { get; set; }
|
||||
[ForeignKey("LastStore")]
|
||||
public virtual StoreModel? FromStore { get; set; }
|
||||
[ForeignKey("LastStation")]
|
||||
public virtual StationModel? FromStation { get; set; }
|
||||
[ForeignKey("LastUser")]
|
||||
public virtual UserModel? FromUser { get; set; }
|
||||
}
|
||||
}
|
||||
@ -1,11 +0,0 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace PSTW_CentralSystem.Areas.Inventory.Models
|
||||
{
|
||||
public class ManufacturerModel
|
||||
{
|
||||
[Key]
|
||||
public int ManufacturerId { get; set; }
|
||||
public required string ManufacturerName { get; set; }
|
||||
}
|
||||
}
|
||||
@ -1,21 +0,0 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace PSTW_CentralSystem.Areas.Inventory.Models
|
||||
{
|
||||
public class ProductModel
|
||||
{
|
||||
[Key]
|
||||
public int ProductId { get; set; }
|
||||
public required string ProductName { get; set; }
|
||||
public required string ProductShortName { get; set; }
|
||||
public required int ManufacturerId { get; set; }
|
||||
public required string Category { get; set; }
|
||||
public required string ModelNo { get; set; }
|
||||
public int? QuantityProduct { get; set; }
|
||||
public required string ImageProduct { get; set; }
|
||||
[ForeignKey("ManufacturerId")]
|
||||
public virtual ManufacturerModel? Manufacturer { get; set; }
|
||||
public virtual ICollection<ItemModel>? Items { get; set; } // Navigation property>
|
||||
}
|
||||
}
|
||||
@ -1,19 +0,0 @@
|
||||
using PSTW_CentralSystem.Models;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
namespace PSTW_CentralSystem.Areas.Inventory.Models
|
||||
{
|
||||
public class StationModel
|
||||
{
|
||||
[Key]
|
||||
public int StationId { get; set; }
|
||||
public int StationPicID { get; set; }
|
||||
public string? StationName { get; set; }
|
||||
public int DepartmentId { get; set; }
|
||||
[ForeignKey("DepartmentId")]
|
||||
public virtual DepartmentModel? Department { get; set; }
|
||||
[ForeignKey("StationPicID")]
|
||||
public virtual UserModel? StationPic { get; set; }
|
||||
|
||||
}
|
||||
}
|
||||
@ -1,16 +0,0 @@
|
||||
using PSTW_CentralSystem.Models;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
namespace PSTW_CentralSystem.Areas.Inventory.Models
|
||||
{
|
||||
public class StoreModel
|
||||
{
|
||||
[Key]
|
||||
public int Id { get; set; }
|
||||
public int CompanyId { get; set; }
|
||||
public string StoreName { get; set; } = string.Empty;
|
||||
[ForeignKey("CompanyId")]
|
||||
public virtual CompanyModel? Company { get; set; }
|
||||
|
||||
}
|
||||
}
|
||||
@ -1,19 +0,0 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
namespace PSTW_CentralSystem.Areas.Inventory.Models
|
||||
{
|
||||
public class SupplierModel
|
||||
{
|
||||
[Key]
|
||||
public int SupplierId { get; set; }
|
||||
public required string SupplierCompName { get; set; }
|
||||
public required string SupplierAddress { get; set; }
|
||||
[AllowNull]
|
||||
public string? SupplierPIC { get; set; }
|
||||
[AllowNull]
|
||||
public string? SupplierEmail { get; set; }
|
||||
[AllowNull]
|
||||
public string? SupplierPhoneNo { get; set; }
|
||||
}
|
||||
}
|
||||
@ -1,116 +0,0 @@
|
||||
@{
|
||||
ViewData["Title"] = "Dashboard";
|
||||
Layout = "~/Views/Shared/_Layout.cshtml";
|
||||
}
|
||||
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
|
||||
<div class="container" id="invAdmin">
|
||||
<div class="row">
|
||||
<div class="text-center">
|
||||
<p><h1 class="display-4">Inventory Admin Dashboard</h1></p>
|
||||
<p v-show="currentUserCompanyDept.departmentName"><h2 class="display-6">Store: {{ currentUserCompanyDept.departmentName }}</h2></p>
|
||||
</div>
|
||||
</div>
|
||||
@await Html.PartialAsync("~/Areas/Inventory/Views/_InventoryPartial.cshtml");
|
||||
<div class="row card">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">Inventory Report</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div v-if="reportData">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-3">
|
||||
<h4>Statistic</h4>
|
||||
<p>Total Number of Item Registered: {{ reportData.itemCountRegistered }}</p>
|
||||
<p>Total Number of Item Still in Stock: {{ reportData.itemCountStillInStock }}</p>
|
||||
</div>
|
||||
<div class="col-3">
|
||||
<h4>Item Registered </h4>
|
||||
<p>This Month: {{ reportData.itemCountRegisteredThisMonth }}</p>
|
||||
<p>Last Month: {{ reportData.itemCountRegisteredLastMonth }}</p>
|
||||
</div>
|
||||
<div class="col-3">
|
||||
<h4>Item Stock Out </h4>
|
||||
<p>This Month: {{ reportData.itemCountStockOutThisMonth }}</p>
|
||||
<p>Last Month: {{ reportData.itemCountStockOutLastMonth }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@section Scripts {
|
||||
@{
|
||||
await Html.RenderPartialAsync("_ValidationScriptsPartial");
|
||||
}
|
||||
<script>
|
||||
$(function () {
|
||||
app.mount('#invAdmin');
|
||||
|
||||
$('.closeModal').on('click', function () {
|
||||
// Show the modal with the ID 'addManufacturerModal'.
|
||||
$('.modal').modal('hide');
|
||||
});
|
||||
});
|
||||
const app = Vue.createApp({
|
||||
data() {
|
||||
return {
|
||||
currentUser: null,
|
||||
currentUserCompanyDept: {
|
||||
departmentName: null,
|
||||
departmentId: null
|
||||
},
|
||||
reportData: null,
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.fetchUser();
|
||||
},
|
||||
methods: {
|
||||
async fetchUser() {
|
||||
try {
|
||||
const response = await fetch(`/IdentityAPI/GetUserInformation/`, {
|
||||
method: 'POST',
|
||||
});
|
||||
if (response.ok) {
|
||||
const data = await response.json();
|
||||
this.currentUser = data?.userInfo || null;
|
||||
const companyDeptData = await this.currentUser.department;
|
||||
const userRole = this.currentUser.role;
|
||||
if(userRole == "SuperAdmin" || userRole == "SystemAdmin"){
|
||||
this.currentUserCompanyDept = {departmentId : 0, departmentName : "All"}
|
||||
this.fetchInventoryReport(0);
|
||||
}
|
||||
else{
|
||||
this.currentUserCompanyDept = companyDeptData;
|
||||
this.fetchInventoryReport(companyDeptData.departmentId);
|
||||
}
|
||||
}
|
||||
else {
|
||||
console.error(`Failed to fetch user: ${response.statusText}`);
|
||||
}
|
||||
}
|
||||
catch (error) {
|
||||
console.error('There was a problem with the fetch operation:', error);
|
||||
}
|
||||
},
|
||||
async fetchInventoryReport(deptId){
|
||||
try {
|
||||
const response = await fetch(`/InvMainAPI/GetInventoryReport/` + deptId, {
|
||||
method: 'POST',
|
||||
});
|
||||
if (response.ok) {
|
||||
const data = await response.json();
|
||||
this.reportData = data;
|
||||
}
|
||||
else {
|
||||
console.error(`Failed to fetch user: ${response.statusText}`);
|
||||
}
|
||||
}
|
||||
catch (error) {
|
||||
console.error('There was a problem with the fetch operation:', error);
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
</script>
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@ -1,207 +0,0 @@
|
||||
@{
|
||||
ViewData["Title"] = "Manufactures";
|
||||
Layout = "~/Views/Shared/_Layout.cshtml";
|
||||
}
|
||||
|
||||
@await Html.PartialAsync("~/Areas/Inventory/Views/_InventoryPartial.cshtml");
|
||||
<div id="app">
|
||||
<div class="row card">
|
||||
<div class="card-header">
|
||||
<button id="addManufacturerBtn" class="btn btn-success col-md-3 m-1"><i class="fa fa-plus"></i> Add Manufacturer</button>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div v-if="loading">
|
||||
<div class="spinner-border text-info" role="status">
|
||||
<span class="visually-hidden">Loading...</span>
|
||||
</div>
|
||||
</div>
|
||||
<table class="table table-bordered table-hover table-striped no-wrap" id="manufacturerTable" style="width:100%;border-style: solid; border-width: 1px"></table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal fade" id="addManufacturerModal" tabindex="-1" role="dialog" aria-labelledby="addManufacturerModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog modal-dialog-centered" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="addManufacturerModalLabel">Add Manufacturer</h5>
|
||||
<button type="button" class="closeModal" data-dismiss="modal" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<form v-on:submit.prevent="addManufacturer">
|
||||
<div class="modal-body">
|
||||
<div class="form-group">
|
||||
<label for="manufacturerName">Manufacturer Name:</label>
|
||||
<input type="text" class="form-control" id="manufacturerName" v-model="newManufacturer.manufacturerName" required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary closeModal" data-dismiss="modal">Close</button>
|
||||
<button type="submit" class="btn btn-primary">Save changes</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@section Scripts {
|
||||
@{
|
||||
await Html.RenderPartialAsync("_ValidationScriptsPartial");
|
||||
}
|
||||
<script>
|
||||
const app = Vue.createApp({
|
||||
data() {
|
||||
return {
|
||||
manufacturer: null,
|
||||
manufacturerDatatable: null,
|
||||
newManufacturer: {
|
||||
manufacturerName: null,
|
||||
},
|
||||
loading: true,
|
||||
}
|
||||
|
||||
},
|
||||
mounted() {
|
||||
// Fetch companies, depts, and products from the API
|
||||
this.fetchManufactures();
|
||||
this.initiateTable();
|
||||
},
|
||||
methods: {
|
||||
async fetchManufactures() {
|
||||
fetch('/InvMainAPI/ManufacturerList', {
|
||||
method: 'POST'
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data != null && data.length > 0)
|
||||
{
|
||||
this.manufacturer = data;
|
||||
}
|
||||
if (!this.manufacturerDatatable) {
|
||||
this.initiateTable();
|
||||
} else {
|
||||
this.fillTable(data);
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('There was a problem with the fetch operation:', error);
|
||||
});
|
||||
|
||||
},
|
||||
async initiateTable() {
|
||||
this.manufacturerDatatable = $('#manufacturerTable').DataTable({
|
||||
"data": this.manufacturer,
|
||||
"columns": [
|
||||
{ "title": "Manufacturer Name",
|
||||
"data": "manufacturerName",
|
||||
},
|
||||
{ "title": "Delete",
|
||||
"data": "manufacturerName",
|
||||
"render": function (data, type, full, meta) {
|
||||
var deleteButton = `<button type="button" class="btn btn-danger delete-btn" data-id="${full.manufacturerId}">Delete</button>`;
|
||||
return deleteButton;
|
||||
},
|
||||
"width": '10%',
|
||||
},
|
||||
],
|
||||
})
|
||||
self = this;
|
||||
// Attach click event listener to the delete buttons
|
||||
$('#manufacturerTable tbody').on('click', '.delete-btn', function () {
|
||||
const manufacturerId = $(this).data('id'); // Get the manufacturer ID from the button
|
||||
self.deleteManufacturer(manufacturerId); // Call the Vue method
|
||||
});
|
||||
|
||||
this.loading = false;
|
||||
},
|
||||
fillTable(data){
|
||||
if (!this.manufacturerDatatable) {
|
||||
console.error("DataTable not initialized");
|
||||
return;
|
||||
}
|
||||
this.manufacturerDatatable.clear();
|
||||
this.manufacturerDatatable.rows.add(data);
|
||||
this.manufacturerDatatable.draw();
|
||||
this.loading = false;
|
||||
},
|
||||
addManufacturer() {
|
||||
this.loading = true;
|
||||
const existingManufacturer = this.manufacturer != null ? this.manufacturer.find(m => m.manufacturerName.toLowerCase() === this.newManufacturer.manufacturerName.toLowerCase()) : null;
|
||||
if (existingManufacturer) {
|
||||
alert('Manufacturer already exists');
|
||||
return;
|
||||
}
|
||||
fetch('/InvMainAPI/AddManufacturer', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify(this.newManufacturer)
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data != null && data.length > 0)
|
||||
{
|
||||
this.manufacturer = data;
|
||||
}
|
||||
this.fillTable(data);
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('There was a problem with the fetch operation:', error);
|
||||
})
|
||||
.finally(() => {
|
||||
$('#preloader').modal('hide');
|
||||
this.newManufacturer.manufacturerName = null;
|
||||
});
|
||||
},
|
||||
async deleteManufacturer(manufacturerId) {
|
||||
if (!confirm("Are you sure you want to delete this manufacturer?")) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const response = await fetch(`/InvMainAPI/DeleteManufacturer/${manufacturerId}`, {
|
||||
method: 'DELETE',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
});
|
||||
const result = await response.json();
|
||||
|
||||
if (result.success) {
|
||||
alert(result.message);
|
||||
// Remove the row from DataTables
|
||||
this.manufacturerDatatable
|
||||
.row($(`.delete-btn[data-id="${manufacturerId}"]`).closest('tr'))
|
||||
.remove()
|
||||
.draw();
|
||||
} else {
|
||||
alert(result.message);
|
||||
}
|
||||
}
|
||||
catch (error) {
|
||||
console.error("Error deleting manufacturer:", error);
|
||||
alert("An error occurred while deleting the manufacturer.");
|
||||
}
|
||||
finally {
|
||||
this.loading = false;
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
$(function () {
|
||||
app.mount('#app');
|
||||
|
||||
// Attach a click event listener to elements with the class 'btn-success'.
|
||||
$('#addManufacturerBtn').on('click', function () {
|
||||
// Show the modal with the ID 'addManufacturerModal'.
|
||||
$('#addManufacturerModal').modal('show');
|
||||
});
|
||||
$('.closeModal').on('click', function () {
|
||||
// Show the modal with the ID 'addManufacturerModal'.
|
||||
$('#addManufacturerModal').modal('hide');
|
||||
});
|
||||
});
|
||||
</script>
|
||||
}
|
||||
|
||||
@ -1,377 +0,0 @@
|
||||
@{
|
||||
ViewData["Title"] = "Product Form";
|
||||
Layout = "~/Views/Shared/_Layout.cshtml";
|
||||
string userId = ViewBag.UserId;
|
||||
}
|
||||
|
||||
@await Html.PartialAsync("~/Areas/Inventory/Views/_InventoryPartial.cshtml");
|
||||
<div class="row">
|
||||
<div id="registerProduct" class="card m-1">
|
||||
<div class="row" v-if="addSection == true">
|
||||
<form v-on:submit.prevent="addProduct" data-aos="fade-right" >
|
||||
<div class="container register" data-aos="fade-right">
|
||||
<div class="row" data-aos="fade-right">
|
||||
<div class="col-md-12">
|
||||
<div class="tab-content" id="myTabContent">
|
||||
<div class="tab-pane fade show active" id="home" role="tabpanel" aria-labelledby="home-tab">
|
||||
<div class="card-header">
|
||||
<h3 class="register-heading">REGISTRATION PRODUCT</h3>
|
||||
</div>
|
||||
<div class="row register-form card-body">
|
||||
<div class="col-md-6">
|
||||
|
||||
@* Product Name *@
|
||||
<div class="form-group row">
|
||||
<label for="productName" class="col-sm-3">Product Name:</label>
|
||||
<div class="col-sm-9">
|
||||
<input type="text" id="productName" name="productName" class="form-control" required v-model="productName">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@* Product Short Name *@
|
||||
<div class="form-group row">
|
||||
<label for="productName" class="col-sm-3">Product Short Name:</label>
|
||||
<div class="col-sm-9">
|
||||
<input type="text" id="productShortName" name="productShortName" class="form-control" maxlength="13" v-model="productShortName" required>
|
||||
<p><em><small class="text-danger">* Product short name limited to 13 characters</small></em></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@* Manufacturer *@
|
||||
<div class="form-group row">
|
||||
<label class="col-sm-3">Manufacturer:</label>
|
||||
<div class="col-sm-9">
|
||||
<div class="">
|
||||
<select class="btn btn-primary form-select" v-model="manufacturer" required>
|
||||
<option class="btn-light" value="" selected disabled>Select Manufacturer</option>
|
||||
<option class="btn-light" v-for="(item, index) in manufacturers" :key="item.manufacturerId" :value="item.manufacturerId">{{ item.manufacturerName ?? 'Select Manufacturer' }}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@* Category *@
|
||||
<div class="form-group row">
|
||||
<label class="col-sm-3">Category:</label>
|
||||
<div class="col-sm-9">
|
||||
<div class="">
|
||||
<select class="btn btn-primary form-select" v-model="category" required>
|
||||
<option class="btn-light" value="" selected disabled>Select Category</option>
|
||||
<option class="btn-light" v-for="(item, index) in categories" :key="item" :value="item">{{ item ?? 'Select Category' }}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
|
||||
@* Model No Coding *@
|
||||
<div class="form-group row">
|
||||
<label for="modelNo" class="col-sm-3">Model No:</label>
|
||||
<div class="col-sm-9">
|
||||
<input type="text" id="modelNo" name="modelNo" class="form-control" required v-model="modelNo">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@* Image Product Coding *@
|
||||
<div class="form-group row">
|
||||
<label for="imageProduct" class="col-sm-3">Image:</label>
|
||||
<div class="col-sm-9">
|
||||
<input type="file" id="imageProduct" name="imageProduct" class="form-control" v-on:change="previewImage" accept="image/*" required>
|
||||
<br>
|
||||
<img v-if="imageSrc" :src="imageSrc" alt="Image Preview" class="img-thumbnail" style="width: 200px; margin-top: 10px;" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<div class="col-sm-9 offset-sm-3">
|
||||
<button type="button" v-on:click="resetForm" class="btn btn-secondary mx-1">Reset</button>
|
||||
<input type="submit" class="btn btn-primary mx-1">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<div class="row">
|
||||
<button class="btn btn-danger col-md-3 m-1" v-on:click="addSection = false"><i class="fa fa-minus"></i> Hide Add Product Section</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<button id="addProductBtn" class="btn btn-success col-md-3 m-1" v-show="addSection == false" v-on:click="addSection = true"><i class="fa fa-plus"></i> Show Add Product Section</button>
|
||||
</div>
|
||||
<div class="row table-responsive">
|
||||
<table class="table table-bordered table-hover table-striped no-wrap" id="productDatatable" style="width:100%;border-style: solid; border-width: 1px"></table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@section Scripts {
|
||||
@{
|
||||
await Html.RenderPartialAsync("_ValidationScriptsPartial");
|
||||
}
|
||||
<script>
|
||||
$(function () {
|
||||
app.mount('#registerProduct');
|
||||
});
|
||||
const app = Vue.createApp({
|
||||
data() {
|
||||
return {
|
||||
addSection: false,
|
||||
productName: null,
|
||||
manufacturer: '',
|
||||
manufacturers: null,
|
||||
category: '',
|
||||
categories: ["Asset", "Part", "Disposable"],
|
||||
modelNo: null,
|
||||
imageProduct: null,
|
||||
manufactures: [],
|
||||
showOtherManufacturer: false,
|
||||
imageSrc: '',
|
||||
products: null,
|
||||
productDatatable: null,
|
||||
productShortName: null,
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
// Fetch companies, depts, and products from the API
|
||||
this.fetchManufactures();
|
||||
this.fetchProducts();
|
||||
},
|
||||
methods: {
|
||||
initiateTable() {
|
||||
console.log(this.products)
|
||||
this.productDatatable = $('#productDatatable').DataTable({
|
||||
"data": this.products,
|
||||
"columns": [
|
||||
{ "title": "Product Name",
|
||||
"data": "productName",
|
||||
},
|
||||
{ "title": "Product Short Name",
|
||||
"data": "productShortName",
|
||||
},
|
||||
{ "title": "Model Number",
|
||||
"data": "modelNo",
|
||||
},
|
||||
{ "title": "Manufacturer",
|
||||
"data": "manufacturer.manufacturerName",
|
||||
},
|
||||
{ "title": "Product Category",
|
||||
"data": "category",
|
||||
},
|
||||
{ "title": "Product Stock",
|
||||
"data": "quantityProduct",
|
||||
},
|
||||
{ "title": "Image",
|
||||
"data": "imageProduct",
|
||||
"render": function (data, type, full, meta) {
|
||||
var image = `<a href="${data}" target="_blank" data-lightbox="image-1">
|
||||
<img src="${data}" alt="Image" class="img-thumbnail" style="width: 100px; height: 100px;" />
|
||||
</a>`;
|
||||
return image;
|
||||
},
|
||||
},
|
||||
{
|
||||
"title": "Delete",
|
||||
"data": "productId",
|
||||
"render": function (data) {
|
||||
var deleteButton = `<button type="button" class="btn btn-danger delete-btn" data-id="${data.productId}">Delete</button>`;
|
||||
return deleteButton;
|
||||
},
|
||||
}
|
||||
|
||||
],
|
||||
responsive:true,
|
||||
})
|
||||
self = this;
|
||||
// Attach click event listener to the delete buttons
|
||||
$('#productDatatable tbody').on('click', '.delete-btn', function () {
|
||||
const productId = $(this).data('id'); // Get the manufacturer ID from the button
|
||||
self.deleteProduct(productId); // Call the Vue method
|
||||
});
|
||||
|
||||
this.loading = false;
|
||||
},
|
||||
async fetchManufactures() {
|
||||
fetch('/InvMainAPI/ManufacturerList', {
|
||||
method: 'POST'
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data != null && data.length > 0)
|
||||
{
|
||||
this.manufacturers = data;
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('There was a problem with the fetch operation:', error);
|
||||
});
|
||||
},
|
||||
async fetchProducts() {
|
||||
try {
|
||||
const response = await fetch('/InvMainAPI/ProductList',{
|
||||
method: 'POST'
|
||||
}); // Call the API
|
||||
if (!response.ok) {
|
||||
throw new Error('Failed to fetch products');
|
||||
}
|
||||
this.products = await response.json(); // Store the fetched products
|
||||
this.$nextTick(() => {
|
||||
this.initiateTable()
|
||||
})
|
||||
} catch (error) {
|
||||
console.error('Error fetching products:', error);
|
||||
}
|
||||
},
|
||||
async addProduct() {
|
||||
// const existingProduct = this.products.find(p => p.modelNo === this.modelNo);
|
||||
// if (existingProduct) {
|
||||
// alert(`Product Error: The model number ${this.modelNo} already exists.`, 'error');
|
||||
// return; // Exit early if the modelNo exists
|
||||
// }
|
||||
|
||||
// Create the payload
|
||||
const formData = {
|
||||
productName: this.productName,
|
||||
productShortName: this.productShortName,
|
||||
manufacturerId: this.manufacturer,
|
||||
category: this.category,
|
||||
modelNo: this.modelNo,
|
||||
imageProduct: this.imageProduct
|
||||
};
|
||||
|
||||
try {
|
||||
// List of required fields
|
||||
const requiredFields = ['productName', 'manufacturer', 'category', 'modelNo', 'imageProduct'];
|
||||
|
||||
// Loop through required fields and check if any are null or empty
|
||||
for (let field of requiredFields) {
|
||||
if (this[field] === null || this[field] === '') {
|
||||
alert('Product Error', `Please fill in required fields: ${field}.`, 'warning');
|
||||
return; // Exit early if validation fails
|
||||
}
|
||||
}
|
||||
|
||||
// Proceed to send the data as raw JSON string
|
||||
const response = await fetch('/InvMainAPI/AddProduct', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify(formData) // Convert the formData to a JSON string
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json();
|
||||
console.error('Error response:', errorData);
|
||||
this.errorMessage = 'Error: ' + (errorData.message || 'Unknown error');
|
||||
|
||||
} else {
|
||||
this.products = await response.json();
|
||||
alert('Success!', 'Product form has been successfully submitted.', 'success');
|
||||
this.fillTable(this.products);
|
||||
this.resetForm();
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error:', error);
|
||||
alert('Product Error', `An error occurred: ${error.message}`, 'error');
|
||||
}
|
||||
},
|
||||
resetForm() {
|
||||
this.productName = null;
|
||||
this.manufacturer = '';
|
||||
this.category = '';
|
||||
this.modelNo = null;
|
||||
this.imageProduct = null;
|
||||
this.imageSrc = '';
|
||||
const fileInput = document.getElementById('imageProduct');
|
||||
if (fileInput) {
|
||||
fileInput.value = ''; // Clear the file input value
|
||||
}
|
||||
},
|
||||
|
||||
// Update Select View
|
||||
updateManufacturer(manufacturer) {
|
||||
this.manufacturer = manufacturer;
|
||||
this.showOtherManufacturer = false;
|
||||
},
|
||||
updateCategory(category) {
|
||||
this.category = category;
|
||||
},
|
||||
|
||||
// When User Presses Button Other
|
||||
toggleOtherInput(type) {
|
||||
if (type === 'manufacturer') {
|
||||
this.showOtherManufacturer = true;
|
||||
}
|
||||
},
|
||||
|
||||
// User Inserting an Image
|
||||
previewImage(event) {
|
||||
const file = event.target.files[0];
|
||||
|
||||
if (file) {
|
||||
const reader = new FileReader();
|
||||
reader.onload = (e) => {
|
||||
this.imageSrc = e.target.result; // Show the image preview
|
||||
this.imageProduct = e.target.result.split(',')[1]; // Get Base64 string (remove metadata)
|
||||
};
|
||||
reader.readAsDataURL(file);
|
||||
} else {
|
||||
this.imageSrc = '';
|
||||
this.imageProduct = null;
|
||||
}
|
||||
},
|
||||
async deleteProduct(productId) {
|
||||
if (!confirm("Are you sure you want to delete this product?")) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const response = await fetch(`/InvMainAPI/DeleteProduct/${productId}`, {
|
||||
method: 'DELETE',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
});
|
||||
const result = await response.json();
|
||||
|
||||
if (result.success) {
|
||||
alert(result.message);
|
||||
// Remove the row from DataTables
|
||||
this.productDatatable
|
||||
.row($(`.delete-btn[data-id="${productId}"]`).closest('tr'))
|
||||
.remove()
|
||||
.draw();
|
||||
} else {
|
||||
alert(result.message);
|
||||
}
|
||||
}
|
||||
catch (error) {
|
||||
console.error("Error deleting product:", error);
|
||||
alert("An error occurred while deleting the product.");
|
||||
}
|
||||
finally {
|
||||
this.loading = false;
|
||||
}
|
||||
},
|
||||
fillTable(data){
|
||||
if (!this.productDatatable) {
|
||||
console.error("DataTable not initialized");
|
||||
return;
|
||||
}
|
||||
this.productDatatable.clear();
|
||||
this.productDatatable.rows.add(data);
|
||||
this.productDatatable.draw();
|
||||
this.loading = false;
|
||||
},
|
||||
|
||||
}
|
||||
});
|
||||
</script>
|
||||
}
|
||||
@ -1,287 +0,0 @@
|
||||
@{
|
||||
ViewData["Title"] = "User Form";
|
||||
Layout = "~/Views/Shared/_Layout.cshtml";
|
||||
}
|
||||
|
||||
@await Html.PartialAsync("~/Areas/Inventory/Views/_InventoryPartial.cshtml");
|
||||
<div id="registerSupplier">
|
||||
<form v-on:submit.prevent="addSupplier" data-aos="fade-right" id="registerSupplierForm" v-if="registerSupplierForm">
|
||||
<div class="container register" data-aos="fade-right">
|
||||
<div class="row">
|
||||
@*Right Side*@
|
||||
<div class="col-md-9 register-rights" data-aos="fade-right">
|
||||
<div class="tab-content" id="myTabContent" data-aos="fade-right">
|
||||
<div class="tab-pane fade show active" id="home" role="tabpanel" aria-labelledby="home-tab" data-aos="fade-right">
|
||||
<h3 class="register-heading">REGISTRATION SUPPLIER</h3>
|
||||
<div class="row register-form">
|
||||
<div class="col-md-61">
|
||||
|
||||
@* Supplier Name *@
|
||||
<div class="form-group row">
|
||||
<label for="supplierCompName" class="col-sm-3">Supplier Company Name:</label>
|
||||
<div class="col-sm-9">
|
||||
<input type="text" id="supplierCompName" name="supplierCompName" class="form-control" required v-model="supplierCompName">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@* Supplier Gender *@
|
||||
<div class="form-group row">
|
||||
<label class="col-sm-3">Supplier Address:</label>
|
||||
<div class="col-sm-9">
|
||||
<div class="dropdown">
|
||||
<textarea type="text" id="supplierAddress" name="supplierAddress" class="form-control" required v-model="supplierAddress"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@* Supplier PIC *@
|
||||
<div class="form-group row">
|
||||
<label class="col-sm-3">Supplier PIC:</label>
|
||||
<div class="col-sm-9">
|
||||
<input type="email" id="supplierPIC" name="supplierPIC" class="form-control" v-model="supplierPIC">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@* Supplier Email *@
|
||||
<div class="form-group row">
|
||||
<label class="col-sm-3">Supplier Email:</label>
|
||||
<div class="col-sm-9">
|
||||
<input type="email" id="supplierEmail" name="supplierEmail" class="form-control" v-model="supplierEmail">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@* Supplier Number Phone *@
|
||||
<div class="form-group row">
|
||||
<label class="col-sm-3">Supplier Phone Number:</label>
|
||||
<div class="col-sm-9">
|
||||
<input type="tel" id="supplierPhoneNo" name="supplierPhoneNo" class="form-control" v-model="supplierPhoneNo">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<div class="col-sm-9 offset-sm-3">
|
||||
<button type="button" v-on:click="resetForm" class="btn btn-secondary m-1">Reset</button>
|
||||
<button type="submit" class="btn btn-primary m-1">Submit</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<div class="row card">
|
||||
<div class="card-header">
|
||||
<button id="addSupplierBtn" :class="['btn', 'col-md-3', 'col-lg-3', 'm-1', 'col-12', registerSupplierForm ? 'btn-danger' : 'btn-success']" v-on:click="registerSupplierForm = !registerSupplierForm"><i :class="['fa', registerSupplierForm ? 'fa-minus' : 'fa-plus']"></i> {{registerSupplierForm ? 'Hide Add Supplier' : 'Show Add Supplier'}}</button>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<table class="table table-bordered table-hover table-striped no-wrap" id="supplierDatatable" style="width:100%;border-style: solid; border-width: 1px"></table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@section Scripts {
|
||||
@{
|
||||
await Html.RenderPartialAsync("_ValidationScriptsPartial");
|
||||
}
|
||||
<script>
|
||||
$(function () {
|
||||
app.mount('#registerSupplier');
|
||||
|
||||
// Attach a click event listener to elements with the class 'btn-success'.
|
||||
$('#addSupplierBtn').on('click', function () {
|
||||
// Show the modal with the ID 'addManufacturerModal'.
|
||||
$('#registerSupplierModal').modal('show');
|
||||
});
|
||||
$('.closeModal').on('click', function () {
|
||||
// Show the modal with the ID 'addManufacturerModal'.
|
||||
$('.modal').modal('hide');
|
||||
});
|
||||
});
|
||||
const app = Vue.createApp({
|
||||
data() {
|
||||
return {
|
||||
supplierCompName : null,
|
||||
supplierEmail : null,
|
||||
supplierAddress : null,
|
||||
supplierPhoneNo : null,
|
||||
supplierPIC : null,
|
||||
suuppliers: null,
|
||||
supplierDatatable: null,
|
||||
gender: ["Male", "Female", "Helicopter"],
|
||||
registerSupplierForm: false
|
||||
}
|
||||
},
|
||||
mounted(){
|
||||
this.fetchSuppliers();
|
||||
},
|
||||
methods: {
|
||||
async fetchSuppliers() {
|
||||
try {
|
||||
const response = await fetch('/InvMainAPI/SupplierList', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json();
|
||||
console.error('Error response:', errorData);
|
||||
this.errorMessage = 'Error: ' + (errorData.message || 'Unknown error');
|
||||
return;
|
||||
}
|
||||
if (this.supplierDatatable){
|
||||
this.supplierDatatable.clear().destroy();
|
||||
}
|
||||
const suppliers = await response.json();
|
||||
this.suppliers = suppliers;
|
||||
|
||||
if (this.itemDatatable) {
|
||||
this.itemDatatable.clear().destroy();
|
||||
}
|
||||
this.initiateTable();
|
||||
} catch (error) {
|
||||
console.error('Error fetching suppliers:', error);
|
||||
this.errorMessage = 'Error: ' + error.message;
|
||||
}
|
||||
},
|
||||
async addSupplier() {
|
||||
$('#loadingModal').modal('show');
|
||||
// Create the payload
|
||||
const formData = {
|
||||
supplierCompName: this.supplierCompName,
|
||||
supplierAddress: this.supplierAddress,
|
||||
supplierPIC: this.supplierPIC,
|
||||
supplierEmail: this.supplierEmail,
|
||||
supplierPhoneNo: this.supplierPhoneNo,
|
||||
};
|
||||
|
||||
try {
|
||||
// List of required fields
|
||||
const requiredFields = ['supplierCompName', 'supplierAddress'];
|
||||
|
||||
// Loop through required fields and check if any are null or empty
|
||||
for (let field of requiredFields) {
|
||||
if (this[field] === null || this[field] === '') {
|
||||
alert('Product Error', `Please fill in required fields: ${field}.`, 'warning');
|
||||
return; // Exit early if validation fails
|
||||
}
|
||||
}
|
||||
|
||||
// Proceed to send the data as raw JSON string
|
||||
const response = await fetch('/InvMainAPI/AddSupplier', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify(formData) // Convert the formData to a JSON string
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json();
|
||||
console.error('Error response:', errorData);
|
||||
this.errorMessage = 'Error: ' + (errorData.message || 'Unknown error');
|
||||
|
||||
} else {
|
||||
alert('Success!', 'Supplier form has been successfully submitted.', 'success');
|
||||
this.fetchSuppliers();
|
||||
this.resetForm();
|
||||
}
|
||||
|
||||
}
|
||||
catch (error) {
|
||||
console.error('Error:', error);
|
||||
alert('Product Error', `An error occurred: ${error.message}`, 'error');
|
||||
}
|
||||
finally {
|
||||
await new Promise(resolve => {
|
||||
$('#loadingModal').on('shown.bs.modal', resolve);
|
||||
});
|
||||
$('#loadingModal').modal('hide');
|
||||
}
|
||||
},
|
||||
resetForm() {
|
||||
this.supplierCompName = null;
|
||||
this.supplierAddress = null;
|
||||
this.supplierEmail = null;
|
||||
this.supplierPhoneNo = null;
|
||||
this.supplierPIC = null;
|
||||
},
|
||||
initiateTable() {
|
||||
self = this;
|
||||
this.supplierDatatable = $('#supplierDatatable').DataTable({
|
||||
"data": this.suppliers,
|
||||
"columns": [
|
||||
{
|
||||
"title": "Supplier Company Name",
|
||||
"data": "supplierCompName",
|
||||
},
|
||||
{
|
||||
"title": "Supplier Address",
|
||||
"data": "supplierAddress",
|
||||
},
|
||||
{
|
||||
"title": "Company PIC",
|
||||
"data": "supplierPIC",
|
||||
},
|
||||
{
|
||||
"title": "Supplier Email",
|
||||
"data": "supplierEmail",
|
||||
},
|
||||
{
|
||||
"title": "Supplier Phone No",
|
||||
"data": "supplierPhoneNo",
|
||||
},
|
||||
{
|
||||
"title": "Delete",
|
||||
"data": "supplierId",
|
||||
"render": function (data) {
|
||||
var deleteButton = `<button type="button" class="btn btn-danger delete-btn" data-id="${data}">Delete</button>`;
|
||||
return deleteButton;
|
||||
},
|
||||
}
|
||||
],
|
||||
responsive: true,
|
||||
|
||||
})
|
||||
|
||||
// Attach click event listener to the delete buttons
|
||||
$('#supplierDatatable tbody').on('click', '.delete-btn', function () {
|
||||
const supplierId = $(this).data('id');
|
||||
self.deleteSupplier(supplierId);
|
||||
});
|
||||
|
||||
this.loading = false;
|
||||
},
|
||||
async deleteSupplier(supplierId) {
|
||||
if (!confirm("Are you sure you want to delete this supplier?")) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const response = await fetch(`/InvMainAPI/DeleteSupplier/${supplierId}`, {
|
||||
method: 'DELETE',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({ supplierId })
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json();
|
||||
console.error('Error response:', errorData);
|
||||
this.errorMessage = 'Error: ' + (errorData.message || 'Unknown error');
|
||||
return;
|
||||
}
|
||||
|
||||
this.fetchSuppliers();
|
||||
} catch (error) {
|
||||
console.error('Error deleting supplier:', error);
|
||||
this.errorMessage = 'Error: ' + error.message;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
}
|
||||
@ -1,107 +0,0 @@
|
||||
@{
|
||||
ViewData["Title"] = "QR & Barcode Scanner";
|
||||
Layout = "~/Views/Shared/_Layout.cshtml";
|
||||
}
|
||||
|
||||
<div id="app" data-aos="fade-right">
|
||||
<h1 data-aos="fade-right">QR & Barcode Scanner</h1>
|
||||
<div id="reader" data-aos="fade-right"></div>
|
||||
<div v-if="qrCodeResult" id="qrCodeResult">
|
||||
<h2>Scan Result:</h2>
|
||||
<p>{{ qrCodeResult }}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
new Vue({
|
||||
el: '#app',
|
||||
data() {
|
||||
return {
|
||||
qrCodeResult: null,
|
||||
html5QrCodeScanner: null,
|
||||
debounceTimeout: null,
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
this.startScanner();
|
||||
},
|
||||
methods: {
|
||||
startScanner() {
|
||||
const config = {
|
||||
fps: 60,
|
||||
qrbox: 200
|
||||
};
|
||||
|
||||
navigator.mediaDevices.getUserMedia({
|
||||
video: {
|
||||
width: { ideal: 1920 }, // Higher resolution
|
||||
height: { ideal: 1080 },
|
||||
}
|
||||
})
|
||||
|
||||
.catch((err) => {
|
||||
console.error("Error accessing camera:", err);
|
||||
});
|
||||
|
||||
this.html5QrCodeScanner = new Html5QrcodeScanner(
|
||||
"reader", config, false
|
||||
);
|
||||
|
||||
this.html5QrCodeScanner.render(
|
||||
(decodedText, decodedResult) => {
|
||||
if (!this.debounceTimeout) {
|
||||
this.debounceTimeout = setTimeout(() => {
|
||||
this.qrCodeResult = decodedText;
|
||||
this.sendDataToBackend(decodedText);
|
||||
this.debounceTimeout = null;
|
||||
}, this.debounceTime);
|
||||
}
|
||||
}
|
||||
);
|
||||
},
|
||||
sendDataToBackend(data) {
|
||||
fetch("/api/Qr", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({ data: data }),
|
||||
})
|
||||
.then((response) => response.json())
|
||||
.then((result) => {
|
||||
console.log("Response from server:", result);
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error("Error sending data to the backend:", error);
|
||||
});
|
||||
},
|
||||
decodeWithApi(barcodeData) {
|
||||
fetch(`https://api.qrserver.com/v1/read-qr-code/?fileurl=${encodeURIComponent(barcodeData)}`)
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
console.log("Decoded using API:", data);
|
||||
if (data[0].symbol[0].data) {
|
||||
this.qrCodeResult = data[0].symbol[0].data; // Update result with API response
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error("Error decoding with API:", error);
|
||||
});
|
||||
},
|
||||
decodeWithZxing() {
|
||||
const reader = new ZXing.BrowserQRCodeReader();
|
||||
reader.decodeFromInputVideoDevice(undefined, 'reader').then(result => {
|
||||
this.qrCodeResult = result.text;
|
||||
this.sendDataToBackend(result.text);
|
||||
}).catch(err => {
|
||||
console.error("ZXing decoding failed:", err);
|
||||
});
|
||||
},
|
||||
},
|
||||
beforeDestroy() {
|
||||
if (this.html5QrCodeScanner) {
|
||||
this.html5QrCodeScanner.clear();
|
||||
}
|
||||
},
|
||||
});
|
||||
</script>
|
||||
@ -1,11 +0,0 @@
|
||||
@{
|
||||
ViewData["Title"] = "PSTW Centralized System";
|
||||
Layout = "~/Views/Shared/_Layout.cshtml";
|
||||
}
|
||||
|
||||
<div class="row">
|
||||
<div class="text-center">
|
||||
<h1 class="display-4">Welcome To Invetory Module</h1>
|
||||
<p>Learn about <a href="https://learn.microsoft.com/aspnet/core">building Web apps with ASP.NET Core</a>.</p>
|
||||
</div>
|
||||
</div>
|
||||
@ -1,51 +0,0 @@
|
||||
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
|
||||
<div class="row">
|
||||
<div class="col-6 col-md-6 col-lg-3">
|
||||
<div class="card card-hover">
|
||||
<a asp-area="Inventory" asp-controller="InventoryMaster" asp-action="SupplierRegistration">
|
||||
<div class="box bg-info text-center">
|
||||
<h1 class="font-light text-white">
|
||||
<i class="mdi mdi-truck"></i>
|
||||
</h1>
|
||||
<h6 class="text-white">Supplier</h6>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-6 col-md-6 col-lg-3">
|
||||
<div class="card card-hover">
|
||||
<a asp-area="Inventory" asp-controller="InventoryMaster" asp-action="ManifacturerRegistration">
|
||||
<div class="box bg-warning text-center">
|
||||
<h1 class="font-light text-white">
|
||||
<i class="mdi mdi-factory"></i>
|
||||
</h1>
|
||||
<h6 class="text-white">Manifacturer</h6>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-6 col-md-6 col-lg-3">
|
||||
<a href="@Url.Action("ProductRegistration", "InventoryMaster", new { area = "Inventory" })">
|
||||
<div class="card card-hover">
|
||||
<div class="box bg-cyan text-center">
|
||||
<h1 class="font-light text-white">
|
||||
<i class="mdi mdi-checkbox-multiple-blank"></i>
|
||||
</h1>
|
||||
<h6 class="text-white">Product</h6>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-6 col-md-6 col-lg-3">
|
||||
<div class="card card-hover">
|
||||
<a asp-area="Inventory" asp-controller="InventoryMaster" asp-action="ItemRegistration">
|
||||
<div class="box bg-success text-center">
|
||||
<h1 class="font-light text-white">
|
||||
<i class="mdi mdi-checkbox-multiple-blank-circle"></i>
|
||||
</h1>
|
||||
<h6 class="text-white">Item</h6>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -1,39 +0,0 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace PSTW_CentralSystem.Areas.JSA.Controllers
|
||||
{
|
||||
[Area("JSA")]
|
||||
public class JSAController : Controller
|
||||
{
|
||||
// GET: JSAController
|
||||
public ActionResult Index()
|
||||
{
|
||||
return View();
|
||||
}
|
||||
|
||||
// GET: JSAController/Details/5
|
||||
public ActionResult Details(int id)
|
||||
{
|
||||
return View();
|
||||
}
|
||||
|
||||
// GET: JSAController/Create
|
||||
public ActionResult Create()
|
||||
{
|
||||
return View();
|
||||
}
|
||||
|
||||
// GET: JSAController/Edit/5
|
||||
public ActionResult Edit(int id)
|
||||
{
|
||||
return View();
|
||||
}
|
||||
|
||||
// GET: JSAController/Delete/5
|
||||
public ActionResult Delete(int id)
|
||||
{
|
||||
return View();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@ -1,17 +0,0 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace PSTW_CentralSystem.Areas.Report.Controllers
|
||||
{
|
||||
[Area("Report")]
|
||||
public class ReportingController : Controller
|
||||
{
|
||||
public IActionResult Index()
|
||||
{
|
||||
return View();
|
||||
}
|
||||
public IActionResult InventoryReport()
|
||||
{
|
||||
return View();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,5 +0,0 @@
|
||||
@*
|
||||
For more information on enabling MVC for empty projects, visit https://go.microsoft.com/fwlink/?LinkID=397860
|
||||
*@
|
||||
@{
|
||||
}
|
||||
@ -1,241 +0,0 @@
|
||||
@{
|
||||
ViewData["Title"] = "Dashboard";
|
||||
Layout = "~/Views/Shared/_Layout.cshtml";
|
||||
}
|
||||
<div class="container" id="invAdmin">
|
||||
<div class="row">
|
||||
<div class="text-center">
|
||||
<p><h1 class="display-4">Reporting Dashboard</h1></p>
|
||||
<p v-show="currentUserCompanyDept.departmentName"><h2 class="display-6">Store: {{ currentUserCompanyDept.departmentName }}</h2></p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row card">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">Report Filter</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div v-if="reportData">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-3">
|
||||
<div class="row col-10">
|
||||
<h4>Department</h4>
|
||||
<multiselect v-model="selectedDepartment" :options="compDeptList" :multiple="true" group-values="departments" group-label="companyName"
|
||||
:group-select="true" placeholder="Seach Department" track-by="departmentId" label="departmentName">
|
||||
</multiselect>
|
||||
<div class=""><button class="btn btn-danger" v-on:click="selectedDepartment = []">Clear</button></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-3">
|
||||
<div class="row col-10">
|
||||
<h4>Category</h4>
|
||||
<multiselect v-model="selectedCategory" :options="categoryList" :multiple="true" placeholder="Seach Category">
|
||||
</multiselect>
|
||||
<div class=""><button class="btn btn-danger" v-on:click="selectedCategory = []">Clear</button></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-3">
|
||||
<div class="row col-10">
|
||||
<h4>Product</h4>
|
||||
<multiselect v-model="selectedItem" :options="selectedCategory.length == 0 && selectedDepartment.length == 0 ? productList : filteredProduct " :multiple="true" placeholder="Seach Product" track-by="productId" label="productName">
|
||||
</multiselect>
|
||||
<div class=""><button class="btn btn-danger" v-on:click="selectedItem = []">Clear</button></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-3">
|
||||
<div class="row col-10">
|
||||
<h4>Date filter</h4>
|
||||
<vue-date-picker v-model="selectedMonth" month-picker range></vue-date-picker>
|
||||
<div class=""><button class="btn btn-danger" v-on:click="selectedMonth = []">Clear</button></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row card">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">Inventory Report</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div v-if="reportData">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-3">
|
||||
<h4>Statistic</h4>
|
||||
<p>Total Number of Item Registered: {{ reportData.itemCountRegistered }}</p>
|
||||
<p>Total Number of Item Still in Stock: {{ reportData.itemCountStillInStock }}</p>
|
||||
</div>
|
||||
<div class="col-3">
|
||||
<h4>Item Registered </h4>
|
||||
<p>This Month: {{ reportData.itemCountRegisteredThisMonth }}</p>
|
||||
<p>Last Month: {{ reportData.itemCountRegisteredLastMonth }}</p>
|
||||
</div>
|
||||
<div class="col-3">
|
||||
<h4>Item Stock Out </h4>
|
||||
<p>This Month: {{ reportData.itemCountStockOutThisMonth }}</p>
|
||||
<p>Last Month: {{ reportData.itemCountStockOutLastMonth }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@section Scripts {
|
||||
@{
|
||||
await Html.RenderPartialAsync("_ValidationScriptsPartial");
|
||||
}
|
||||
<script>
|
||||
$(function () {
|
||||
app.mount('#invAdmin');
|
||||
$('.closeModal').on('click', function () {
|
||||
// Show the modal with the ID 'addManufacturerModal'.
|
||||
$('.modal').modal('hide');
|
||||
});
|
||||
});
|
||||
const app = Vue.createApp({
|
||||
components: {
|
||||
'multiselect': window.VueMultiselect.default,
|
||||
VueDatePicker,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
currentUser: null,
|
||||
currentUserCompanyDept: {
|
||||
departmentName: null,
|
||||
departmentId: null
|
||||
},
|
||||
reportData: null,
|
||||
compDeptList: {},
|
||||
productList: {},
|
||||
categoryList:['Asset', 'Part', 'Disposable'],
|
||||
monthList: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],
|
||||
filteredProduct: [],
|
||||
selectedMonth: [],
|
||||
selectedDepartment: [],
|
||||
selectedItem: [],
|
||||
selectedCategory: []
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.fetchUser();
|
||||
this.fetchProductList();
|
||||
this.fetchDepartmentsCompaniesList();
|
||||
},
|
||||
watch: {
|
||||
//watch selectedDepartment. when selectedDepartment is changed, if selectedCategory is null filter productList based on selectedDepartment only. otherwise filter the productList based on selectedDepartment and selectedCategory
|
||||
selectedDepartment() {
|
||||
this.filterProducts();
|
||||
},
|
||||
selectedCategory() {
|
||||
this.filterProducts();
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async fetchUser() {
|
||||
try {
|
||||
const response = await fetch(`/IdentityAPI/GetUserInformation/`, {
|
||||
method: 'POST',
|
||||
});
|
||||
if (response.ok) {
|
||||
const data = await response.json();
|
||||
this.currentUser = data?.userInfo || null;
|
||||
const companyDeptData = await this.currentUser.department;
|
||||
const userRole = this.currentUser.role;
|
||||
if(userRole == "SuperAdmin" || userRole == "SystemAdmin"){
|
||||
this.currentUserCompanyDept = {departmentId : 0, departmentName : "All"}
|
||||
this.fetchInventoryReport(0);
|
||||
}
|
||||
else{
|
||||
this.currentUserCompanyDept = companyDeptData;
|
||||
this.fetchInventoryReport(0);
|
||||
}
|
||||
}
|
||||
else {
|
||||
console.error(`Failed to fetch user: ${response.statusText}`);
|
||||
}
|
||||
}
|
||||
catch (error) {
|
||||
console.error('There was a problem with the fetch operation:', error);
|
||||
}
|
||||
},
|
||||
async fetchInventoryReport(deptId){
|
||||
try {
|
||||
const response = await fetch(`/ReportingAPI/GetInventoryReport/` + deptId, {
|
||||
method: 'POST',
|
||||
});
|
||||
if (response.ok) {
|
||||
const data = await response.json();
|
||||
this.reportData = data;
|
||||
}
|
||||
else {
|
||||
console.error(`Failed to fetch user: ${response.statusText}`);
|
||||
}
|
||||
}
|
||||
catch (error) {
|
||||
console.error('There was a problem with the fetch operation:', error);
|
||||
}
|
||||
},
|
||||
async fetchDepartmentsCompaniesList(){
|
||||
try {
|
||||
const response = await fetch(`/AdminAPI/GetDepartmentWithCompanyList/`, {
|
||||
method: 'POST',
|
||||
});
|
||||
if (response.ok) {
|
||||
const data = await response.json();
|
||||
this.compDeptList = data;
|
||||
}
|
||||
else {
|
||||
console.error(`Failed to fetch company & department list: ${response.statusText}`);
|
||||
}
|
||||
}
|
||||
catch (error) {
|
||||
console.error('There was a problem with the fetch operation:', error);
|
||||
}
|
||||
},
|
||||
async fetchProductList(){
|
||||
try {
|
||||
const response = await fetch(`/InvMainAPI/ItemList/`, {
|
||||
method: 'POST',
|
||||
});
|
||||
if (response.ok) {
|
||||
const data = await response.json();
|
||||
this.productList = data;
|
||||
}
|
||||
else {
|
||||
console.error(`Failed to fetch item list: ${response.statusText}`);
|
||||
}
|
||||
}
|
||||
catch (error) {
|
||||
console.error('There was a problem with the fetch operation:', error);
|
||||
}
|
||||
},
|
||||
filterProducts() {
|
||||
const selectedDepartmentIds = this.selectedDepartment.map(department => department.departmentId);
|
||||
const selectedCategory = this.selectedCategory;
|
||||
if (selectedDepartmentIds.length === 0 && selectedCategory.length === 0) {
|
||||
// No filters applied
|
||||
this.filteredProduct = this.productList;
|
||||
}
|
||||
else if (selectedDepartmentIds.length === 0) {
|
||||
// Filter by category only
|
||||
this.filteredProduct = this.productList.filter(product =>
|
||||
selectedCategory.includes(product.category)
|
||||
);
|
||||
}
|
||||
else if (selectedCategory.length === 0) {
|
||||
// Filter by department only
|
||||
this.filteredProduct = this.productList.filter(product =>
|
||||
selectedDepartmentIds.includes(product.departmentId)
|
||||
);
|
||||
}
|
||||
else {
|
||||
// Filter by both department and category
|
||||
this.filteredProduct = this.productList.filter(product =>
|
||||
selectedDepartmentIds.includes(product.departmentId) &&
|
||||
selectedCategory.includes(product.category)
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
</script>
|
||||
}
|
||||
@ -1,296 +0,0 @@
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using PSTW_CentralSystem.DBContext;
|
||||
using PSTW_CentralSystem.Models;
|
||||
using System.Reflection;
|
||||
using static System.Runtime.InteropServices.JavaScript.JSType;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using PSTW_CentralSystem.Areas.Inventory.Models;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
|
||||
namespace PSTW_CentralSystem.Controllers.API
|
||||
{
|
||||
[ApiController]
|
||||
[Route("[controller]")]
|
||||
|
||||
public class AdminAPI : Controller
|
||||
{
|
||||
private readonly ILogger<AdminAPI> _logger;
|
||||
private readonly CentralSystemContext _centralDbContext;
|
||||
private readonly UserManager<UserModel> _userManager;
|
||||
private readonly SignInManager<UserModel> _signInManager;
|
||||
private readonly RoleManager<RoleModel> _roleManager;
|
||||
|
||||
public AdminAPI(ILogger<AdminAPI> logger, CentralSystemContext authDbContext, UserManager<UserModel> userManager, SignInManager<UserModel> signInManager, RoleManager<RoleModel> roleManager)
|
||||
{
|
||||
_logger = logger;
|
||||
_centralDbContext = authDbContext;
|
||||
_userManager = userManager;
|
||||
_signInManager = signInManager;
|
||||
_roleManager = roleManager;
|
||||
}
|
||||
|
||||
[HttpPost("GetClassAndMethodInformation")]
|
||||
public async Task<IActionResult> GetClassAndMethodInformation(string moduleName)
|
||||
{
|
||||
var controllerAndMethodList = new object();
|
||||
|
||||
// Get the assembly containing the controllers
|
||||
var assembly = Assembly.GetExecutingAssembly();
|
||||
|
||||
// Get all types in the assembly (controllers will typically be in the "Controllers" namespace)
|
||||
//var controllerTypes = await Task.Run(() => assembly.GetTypes().Where(type => typeof(ControllerBase).IsAssignableFrom(type) && type.IsClass && type.CompanyName.Contains("Controller") && type.CompanyName != "AdminController") .ToList());
|
||||
var controllerTypes = await Task.Run(() => assembly.GetTypes().Where(type => typeof(ControllerBase).IsAssignableFrom(type) && !type.Name.Contains("API") && type.IsClass && type.Name.Contains(moduleName)).FirstOrDefault());
|
||||
|
||||
// Iterate over the controller types and get their methods
|
||||
var methods = controllerTypes?.GetMethods(BindingFlags.Public | BindingFlags.Instance)
|
||||
.Where(m => m.DeclaringType == controllerTypes) // Filter methods declared directly in the controller (ignoring inherited ones)
|
||||
.Select(m => m.Name) // Get the method names
|
||||
.ToList();
|
||||
|
||||
controllerAndMethodList = (new
|
||||
{
|
||||
Controller = controllerTypes?.Name,
|
||||
Methods = methods
|
||||
});
|
||||
|
||||
// Return the list as JSON
|
||||
return Json(controllerAndMethodList);
|
||||
}
|
||||
|
||||
[HttpPost("GetListClassAndMethodInformation")]
|
||||
public async Task<IActionResult> GetListClassAndMethodInformation()
|
||||
{
|
||||
var controllerAndMethodList = new List<object>();
|
||||
|
||||
// Get the assembly containing the controllers
|
||||
var assembly = Assembly.GetExecutingAssembly();
|
||||
|
||||
// Get all types in the assembly (controllers will typically be in the "Controllers" namespace)
|
||||
//var controllerTypes = await Task.Run(() => assembly.GetTypes().Where(type => typeof(ControllerBase).IsAssignableFrom(type) && type.IsClass && type.CompanyName.Contains("Controller") && type.CompanyName != "AdminController") .ToList());
|
||||
var controllerTypes = await Task.Run(() => assembly.GetTypes().Where(type => typeof(ControllerBase).IsAssignableFrom(type) && type.IsClass && !type.Name.Contains("API") && !type.Name.Contains("Admin")).ToList());
|
||||
|
||||
// Iterate over the controller types and get their methods
|
||||
foreach (var controllerType in controllerTypes) {
|
||||
|
||||
// Get the [Area] attribute, if it exists
|
||||
var areaAttribute = controllerType.GetCustomAttributes(true)
|
||||
.OfType<AreaAttribute>()
|
||||
.FirstOrDefault();
|
||||
|
||||
var areaName = areaAttribute?.RouteValue ?? null;
|
||||
|
||||
var methods = controllerType?.GetMethods(BindingFlags.Public | BindingFlags.Instance)
|
||||
.Where(m => m.DeclaringType == controllerType) // Filter methods declared directly in the controller (ignoring inherited ones)
|
||||
.Select(m => m.Name) // Get the method names
|
||||
.ToList();
|
||||
|
||||
controllerAndMethodList.Add(new
|
||||
{
|
||||
Module = areaName,
|
||||
Controller = controllerType?.Name.Replace("Controller", string.Empty),
|
||||
Methods = methods
|
||||
});
|
||||
}
|
||||
// Return the list as JSON
|
||||
return Json(controllerAndMethodList);
|
||||
}
|
||||
|
||||
[HttpPost("GetUserList")]
|
||||
public async Task<IActionResult> GetUserList()
|
||||
{
|
||||
try
|
||||
{
|
||||
var user = await _userManager.GetUserAsync(User);
|
||||
|
||||
var userRole = await _userManager.GetRolesAsync(user??new UserModel());
|
||||
List<UserModel> userInfo = new List<UserModel>();
|
||||
|
||||
// Fetch all users excluding those with roles SuperAdmin or SystemAdmin
|
||||
var allUsers = await _centralDbContext.Users
|
||||
.Include(u => u.Department!.Company)
|
||||
.ToListAsync();
|
||||
|
||||
if (userRole == null || userRole.Count == 0)
|
||||
{
|
||||
foreach (var u in allUsers)
|
||||
{
|
||||
var roles = await _userManager.GetRolesAsync(u);
|
||||
if (!roles.Contains("SuperAdmin") && !roles.Contains("SystemAdmin"))
|
||||
{
|
||||
userInfo.Add(u);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
userInfo = allUsers;
|
||||
}
|
||||
var userList = userInfo.Select(u => new
|
||||
{
|
||||
id = u.Id,
|
||||
email = u.NormalizedEmail,
|
||||
company = u.Department?.Company?.CompanyName,
|
||||
department = u.Department?.DepartmentName,
|
||||
role = _userManager.GetRolesAsync(u).Result,
|
||||
status = u.UserInfoStatus,
|
||||
}).ToList();
|
||||
return Ok(new { UserInfo = userList });
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return StatusCode(500, $"An error occurred: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
[HttpPatch("UpdateUserStatusAndRole/{id}")]
|
||||
public async Task<IActionResult> UpdateUserStatusAndRole(int id, [FromBody] string role)
|
||||
{
|
||||
try
|
||||
{
|
||||
var user = await _centralDbContext.Users.Include("Department").FirstOrDefaultAsync(u => u.Id == id);
|
||||
|
||||
if (user == null)
|
||||
{
|
||||
return NotFound(new { message = "User not found" });
|
||||
}
|
||||
|
||||
var existingUserRoles = await _userManager.GetRolesAsync(user);
|
||||
if (existingUserRoles != null && existingUserRoles.Count > 0) {
|
||||
await _userManager.RemoveFromRolesAsync(user, existingUserRoles);
|
||||
}
|
||||
|
||||
await _userManager.AddToRoleAsync(user, role);
|
||||
|
||||
user.UserInfoStatus = 1;
|
||||
if (role == "Inventory Master") {
|
||||
var StoreName = user.Department!.DepartmentName + " (" + user.Department!.DepartmentCode + ")";
|
||||
|
||||
var existingStore = await _centralDbContext.Stores.FirstOrDefaultAsync(s => s.StoreName == StoreName);
|
||||
if (existingStore == null)
|
||||
{
|
||||
existingStore = new StoreModel
|
||||
{
|
||||
StoreName = StoreName,
|
||||
CompanyId = user.Department.CompanyId,
|
||||
};
|
||||
|
||||
await _centralDbContext.Stores.AddAsync(existingStore);
|
||||
await _centralDbContext.SaveChangesAsync();
|
||||
}
|
||||
|
||||
var masterStore = await _centralDbContext.Stores.FirstOrDefaultAsync(s => s.Id == existingStore.Id);
|
||||
var newInventoryMaster = new InventoryMasterModel
|
||||
{
|
||||
UserId = user.Id,
|
||||
StoreId = existingStore.Id,
|
||||
};
|
||||
await _centralDbContext.InventoryMasters.AddAsync(newInventoryMaster);
|
||||
}
|
||||
await _centralDbContext.SaveChangesAsync();
|
||||
|
||||
return Ok(new { message = "User updated successfully" });
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return StatusCode(500, $"An error occurred: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
[HttpPost("GetDepartmentWithCompanyList")]
|
||||
public async Task<IActionResult> GetDepartmentWithCompanyList()
|
||||
{
|
||||
var companyList = await _centralDbContext.Companies
|
||||
.Include(c => c.Departments)
|
||||
.Select(c => new {
|
||||
c.CompanyId,
|
||||
c.CompanyName,
|
||||
Departments = c.Departments!
|
||||
.OrderBy(d => d.DepartmentId)
|
||||
.Select(d => new { d.DepartmentId, d.DepartmentName, d.DepartmentCode })
|
||||
})
|
||||
.ToListAsync();
|
||||
return Json(companyList);
|
||||
}
|
||||
|
||||
[HttpPost("GetDepartmentWithCompany")]
|
||||
public async Task<DepartmentCompany> GetDepartmentWithCompany(int companyId, int departmentId)
|
||||
{
|
||||
var departmentList = await _centralDbContext.Departments.FirstOrDefaultAsync(d => d.DepartmentId == departmentId);
|
||||
var companyList = await _centralDbContext.Companies.FirstOrDefaultAsync(c => c.CompanyId == companyId);
|
||||
|
||||
// Create a new list to store departments with their company name
|
||||
var departmentWithCompany = new DepartmentCompany
|
||||
{
|
||||
DepartmentId = departmentList!.DepartmentId,
|
||||
DepartmentName = departmentList.DepartmentName,
|
||||
CompanyId = departmentList.CompanyId,
|
||||
CompanyName = companyList?.CompanyName,
|
||||
};
|
||||
|
||||
// Return the constructed list as JSON
|
||||
return departmentWithCompany;
|
||||
}
|
||||
|
||||
[HttpPost("AddCompanyDepartment")]
|
||||
public async Task<IActionResult> AddCompanyDepartment([FromBody] UpdateDepartmentCompany departmentCompanyDetails)
|
||||
{
|
||||
try
|
||||
{
|
||||
CompanyModel companyModel = new CompanyModel
|
||||
{
|
||||
CompanyName = departmentCompanyDetails.Company!
|
||||
};
|
||||
|
||||
_centralDbContext.Companies.Add(companyModel);
|
||||
await _centralDbContext.SaveChangesAsync();
|
||||
|
||||
foreach (var department in departmentCompanyDetails.Department!)
|
||||
{
|
||||
DepartmentModel departmentModel = new DepartmentModel
|
||||
{
|
||||
CompanyId = companyModel.CompanyId,
|
||||
DepartmentName = department.DepartmentName ?? string.Empty,
|
||||
DepartmentCode = department.DepartmentCode ?? string.Empty
|
||||
};
|
||||
|
||||
_centralDbContext.Departments.Add(departmentModel);
|
||||
await _centralDbContext.SaveChangesAsync();
|
||||
}
|
||||
|
||||
return Ok( new { message = "Company and department added successfully" });
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return StatusCode(500, new { message = $"An error occurred: {ex.Message}" });
|
||||
}
|
||||
}
|
||||
|
||||
public class UpdateDepartmentCompany
|
||||
{
|
||||
[Required]
|
||||
public string? Company { get; set; }
|
||||
[Required]
|
||||
public List<DepartmentInfo>? Department { get; set; }
|
||||
|
||||
}
|
||||
public class DepartmentInfo
|
||||
{
|
||||
[Required]
|
||||
public string? DepartmentName { get; set; }
|
||||
[Required]
|
||||
public string? DepartmentCode { get; set; }
|
||||
}
|
||||
public class DepartmentCompany
|
||||
{
|
||||
public int DepartmentId { get; set; }
|
||||
public string? DepartmentName { get; set; }
|
||||
public int CompanyId { get; set; }
|
||||
public string? CompanyName { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,252 +0,0 @@
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using PSTW_CentralSystem.DBContext;
|
||||
using PSTW_CentralSystem.Models;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
namespace PSTW_CentralSystem.Controllers.API
|
||||
{
|
||||
[ApiController]
|
||||
[Route("[controller]")]
|
||||
|
||||
public class IdentityAPI : Controller
|
||||
{
|
||||
private readonly ILogger<IdentityAPI> _logger;
|
||||
private readonly CentralSystemContext _identityDbContext;
|
||||
private readonly UserManager<UserModel> _userManager;
|
||||
private readonly SignInManager<UserModel> _signInManager;
|
||||
// Communication Key for API. Not API authentication key
|
||||
private readonly string _commKey = "LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUlJQklqQU5CZ2txaGtpRzl3MEJBUUVGQUFPQ0FROEFNSUlCQ2dLQ0FRRUF4NW42anlkNlpTYzZNSE1Zem9qaApUbldpYTIra2pud2ZNbVhpSWlyK0RadjM2cEVGMGhRUWFLaWpaMWtyMGNiT25Ha2d2QnNwTzNiYkFua0E3SWwzCk4zM3NNYWdQV0JOQzZyVm1jT04zNEhDSWJCM0hvQXFYQUtkSHFUOGZneklMRzFhRzdxK2h4RDZhZzZsemhTMnEKdDA1bHdNc0hONWpOdmVNNnFWalRnTVB4aEFOMUhnUTkrd1lRWFh5bnZYYUo5OUNySHBqS21WdUt2VUh6WXdlRwp6SnBtYXZOclc4bE9oM1lMeVNuUVU5bjRrdURubGc1OWNHeUtKbzJ2YUxZbll4MkR1ZDNabzBXMHRMWGd0dlQyCjVXdVFsY0NVbldvaVpBV1JBTGI3anRpcTF0MGY5eVBiV2gxYXpMMjFoL3QvckJUMXNCL2FQd2kzRCt3MnBUR00KeVFJREFRQUIKLS0tLS1FTkQgUFVCTElDIEtFWS0tLS0tCg==";
|
||||
|
||||
|
||||
public IdentityAPI(ILogger<IdentityAPI> logger, CentralSystemContext authDbContext, UserManager<UserModel> userManager, SignInManager<UserModel> signInManager)
|
||||
{
|
||||
_logger = logger;
|
||||
_identityDbContext = authDbContext;
|
||||
_userManager = userManager;
|
||||
_signInManager = signInManager;
|
||||
}
|
||||
|
||||
#region User
|
||||
[HttpPost("GetUserInformation")]
|
||||
public async Task<IActionResult> GetUserInformation()
|
||||
{
|
||||
try
|
||||
{
|
||||
var user = await _userManager.GetUserAsync(User);
|
||||
var userRole = await _userManager.GetRolesAsync(user!);
|
||||
if (user == null)
|
||||
{
|
||||
return NotFound(new { message = $"Unable to load user with ID '{_userManager.GetUserId(User)}'." });
|
||||
}
|
||||
|
||||
var userInfo = await _identityDbContext.Users.Include(u => u.Department).Select(u => new
|
||||
{
|
||||
id = u.Id,
|
||||
email = u.NormalizedEmail,
|
||||
company = u.Department!.Company!.CompanyName,
|
||||
department = u.Department,
|
||||
role = userRole,
|
||||
}).Where(u => u.id == user.Id).FirstOrDefaultAsync();
|
||||
|
||||
if (userInfo == null)
|
||||
{
|
||||
return NotFound(new { message = "User not found" });
|
||||
}
|
||||
|
||||
return Ok(new { UserInfo = userInfo });
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return StatusCode(500, $"An error occurred: {ex.Message}");
|
||||
}
|
||||
}
|
||||
#endregion User
|
||||
|
||||
#region LDAP Login
|
||||
[HttpPost("LdapLogin")]
|
||||
public async Task<IActionResult> LdapLogin([FromBody] LdapLoginCredential ldapLoginInfo)
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return BadRequest(ModelState);
|
||||
}
|
||||
|
||||
byte[] noFormatString = Convert.FromBase64String(_commKey);
|
||||
string initUrlKey = Encoding.UTF8.GetString(noFormatString);
|
||||
|
||||
string jsonData = JsonSerializer.Serialize(ldapLoginInfo);
|
||||
|
||||
RSA rsaBase = RSA.Create();
|
||||
rsaBase.ImportFromPem(initUrlKey.ToCharArray());
|
||||
byte[] rsaData = rsaBase.Encrypt(Encoding.UTF8.GetBytes(jsonData), RSAEncryptionPadding.Pkcs1);
|
||||
string rsaDataBase64 = Convert.ToBase64String(rsaData);
|
||||
|
||||
string ldapUrl = "http://192.168.11.231/api/ldap/";
|
||||
string ldapUrlResult = "";
|
||||
|
||||
using (HttpClient httpClient = new HttpClient())
|
||||
{
|
||||
try
|
||||
{
|
||||
StringContent rsaDataB64HttpContent = new(rsaDataBase64, Encoding.UTF8);
|
||||
HttpResponseMessage ldapUrlResponse = await httpClient.PostAsync(ldapUrl, rsaDataB64HttpContent);
|
||||
ldapUrlResponse.EnsureSuccessStatusCode();
|
||||
if (ldapUrlResponse.IsSuccessStatusCode)
|
||||
{
|
||||
ldapUrlResult = await ldapUrlResponse.Content.ReadAsStringAsync();
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
return BadRequest(new { Message = $"Message: {e.Message}\nException Caught!" });
|
||||
}
|
||||
}
|
||||
|
||||
userLdapInfo userLdapInfo = JsonSerializer.Deserialize<userLdapInfo>(ldapUrlResult)!;
|
||||
|
||||
if (!userLdapInfo.Authenticated)
|
||||
{
|
||||
return BadRequest(new { Message = $"Login Failed. {userLdapInfo.Response}" });
|
||||
}
|
||||
|
||||
userInfo userInfo = userLdapInfo.UserInfo!;
|
||||
|
||||
UserModel ldapuser = new UserModel()
|
||||
{
|
||||
FullName = userInfo!.Username,
|
||||
UserName = userInfo.Email,
|
||||
NormalizedUserName = userInfo.Email.ToUpper(),
|
||||
Email = userInfo.Email,
|
||||
NormalizedEmail = userInfo.Email.ToUpper(),
|
||||
EmailConfirmed = true,
|
||||
PhoneNumberConfirmed = false,
|
||||
TwoFactorEnabled = false,
|
||||
LockoutEnabled = false,
|
||||
AccessFailedCount = 0,
|
||||
};
|
||||
var existUser = await doUserExists(ldapuser.Email);
|
||||
if (existUser == null)
|
||||
{
|
||||
await _userManager.CreateAsync(ldapuser);
|
||||
await _signInManager.SignInAsync(ldapuser, false);
|
||||
return Ok(new { RedirectUrl = Url.Action("ComDeptAssignment", "Identity") });
|
||||
|
||||
};
|
||||
|
||||
await _signInManager.SignInAsync(existUser, false);
|
||||
|
||||
if (existUser.UserInfoStatus == null || existUser.UserInfoStatus == 0)
|
||||
{
|
||||
return Ok(new { RedirectUrl = Url.Action("ComDeptAssignment", "Identity") });
|
||||
}
|
||||
|
||||
return Ok(new { RedirectUrl = Url.Action("Index", "Home") });
|
||||
|
||||
}
|
||||
#endregion LDAP Login
|
||||
|
||||
#region Company
|
||||
|
||||
[HttpPost("CompanyDepartmentList")]
|
||||
public async Task<IActionResult> CompanyDepartmentList()
|
||||
{
|
||||
var companyList = await _identityDbContext.Companies
|
||||
.Include(c => c.Departments)
|
||||
.Select(c => new {
|
||||
c.CompanyId,
|
||||
c.CompanyName,
|
||||
Departments = c.Departments
|
||||
.OrderBy(d => d.DepartmentId)
|
||||
.Select(d => new { d.DepartmentId, d.DepartmentName })
|
||||
})
|
||||
.ToListAsync();
|
||||
return Json(companyList);
|
||||
}
|
||||
|
||||
#endregion Company
|
||||
|
||||
#region Department
|
||||
|
||||
[HttpPost("DepartmentCompanyList")]
|
||||
public async Task<IActionResult> DepartmentCompanyList()
|
||||
{
|
||||
var itemDepartment = await _identityDbContext.Departments
|
||||
.Include(d => d.Company) // Include the related Company entity
|
||||
.Select(d => new
|
||||
{
|
||||
d.DepartmentId,
|
||||
d.DepartmentName,
|
||||
d.CompanyId,
|
||||
d.Company.CompanyName,
|
||||
})
|
||||
.ToListAsync();
|
||||
|
||||
//return Json(await GetDepartmentWithCompanyList());
|
||||
return Json(itemDepartment);
|
||||
}
|
||||
|
||||
[HttpPost("UserComptDeptAssignment/{id}")]
|
||||
public async Task<IActionResult> UserComptDeptAssignment([FromBody] UserDeptAssignment userDeptAssignment, int id)
|
||||
{
|
||||
var deptId = userDeptAssignment.departmentId;
|
||||
if (deptId <= 0)
|
||||
{
|
||||
return BadRequest(new { message = "Invalid department ID provided." });
|
||||
}
|
||||
var user = await _userManager.FindByIdAsync(id.ToString());
|
||||
if(user == null)
|
||||
{
|
||||
return NotFound( new { message = $"Unable to load user with ID '{id}'." });
|
||||
}
|
||||
user.UserInfoStatus = 0;
|
||||
user.departmentId = deptId;
|
||||
await _userManager.UpdateAsync(user);
|
||||
return Ok( new { message = "User updated successfully", RedirectUrl = Url.Action("Index", "Home") });
|
||||
}
|
||||
|
||||
#endregion Department
|
||||
|
||||
public async Task<UserModel?> doUserExists(string username)
|
||||
{
|
||||
var user = await _userManager.FindByNameAsync(username);
|
||||
return user != null ? user : null;
|
||||
}
|
||||
|
||||
public class LdapLoginCredential
|
||||
{
|
||||
public required string username { get; set; }
|
||||
public required string password { get; set; }
|
||||
}
|
||||
public class UserDeptAssignment()
|
||||
{
|
||||
public required int departmentId { get; set; }
|
||||
}
|
||||
class userLdapInfo()
|
||||
{
|
||||
public required bool Authenticated { get; set; }
|
||||
public userInfo? UserInfo { get; set; }
|
||||
public string? Response { get; set; }
|
||||
}
|
||||
class userInfo()
|
||||
{
|
||||
public required string FirstName { get; set; }
|
||||
public required string LastName { get; set; }
|
||||
public required string DisplayName { get; set; }
|
||||
public required string Description { get; set; }
|
||||
public required string Username { get; set; }
|
||||
public required string Office { get; set; }
|
||||
public required string Email { get; set; }
|
||||
public required string Street { get; set; }
|
||||
public required string City { get; set; }
|
||||
public required string State { get; set; }
|
||||
public required string ZipCode { get; set; }
|
||||
public required string Country { get; set; }
|
||||
public required string Home { get; set; }
|
||||
public required string Mobile { get; set; }
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,614 +0,0 @@
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Mono.TextTemplating;
|
||||
using Newtonsoft.Json;
|
||||
using PSTW_CentralSystem.Areas.Inventory.Models;
|
||||
using PSTW_CentralSystem.DBContext;
|
||||
using PSTW_CentralSystem.Models;
|
||||
using System.ComponentModel.Design;
|
||||
using System.Data;
|
||||
using System.Diagnostics;
|
||||
using System.Reflection;
|
||||
|
||||
namespace PSTW_CentralSystem.Controllers.API.Inventory
|
||||
{
|
||||
[ApiController]
|
||||
[Route("[controller]")]
|
||||
|
||||
public class InvMainAPI : Controller
|
||||
{
|
||||
private readonly ILogger<InvMainAPI> _logger;
|
||||
private readonly CentralSystemContext _centralDbContext;
|
||||
private readonly UserManager<UserModel> _userManager;
|
||||
|
||||
public InvMainAPI(ILogger<InvMainAPI> logger, CentralSystemContext centralDbContext, UserManager<UserModel> userManager)
|
||||
{
|
||||
_logger = logger;
|
||||
_centralDbContext = centralDbContext;
|
||||
_userManager = userManager;
|
||||
}
|
||||
|
||||
public class DepartmentCompany
|
||||
{
|
||||
public int DepartmentId { get; set; }
|
||||
public string? DepartmentName { get; set; }
|
||||
public int CompanyId { get; set; }
|
||||
public string? CompanyName { get; set; }
|
||||
public string? DepartmentCode { get; set; }
|
||||
}
|
||||
|
||||
public async Task<List<DepartmentCompany>> GetDepartmentWithCompanyList()
|
||||
{
|
||||
var departmentList = await _centralDbContext.Departments.ToListAsync();
|
||||
var companyList = await _centralDbContext.Companies.ToListAsync();
|
||||
|
||||
// Create a new list to store departments with their company name
|
||||
var departmentWithCompanyList = departmentList.Select(department => new DepartmentCompany
|
||||
{
|
||||
DepartmentId = department.DepartmentId,
|
||||
DepartmentName = department.DepartmentName,
|
||||
CompanyId = department.CompanyId,
|
||||
CompanyName = companyList.FirstOrDefault(company => company.CompanyId == department.CompanyId)?.CompanyName
|
||||
}).ToList();
|
||||
|
||||
// Return the constructed list as JSON
|
||||
return departmentWithCompanyList;
|
||||
}
|
||||
public async Task<DepartmentCompany> GetDepartmentWithCompany(int companyId, int departmentId)
|
||||
{
|
||||
var departmentList = await _centralDbContext.Departments.FirstOrDefaultAsync(d => d.DepartmentId == departmentId );
|
||||
var companyList = await _centralDbContext.Companies.FirstOrDefaultAsync(c => c.CompanyId == companyId);
|
||||
|
||||
// Create a new list to store departments with their company name
|
||||
var departmentWithCompany = new DepartmentCompany
|
||||
{
|
||||
DepartmentId = departmentList!.DepartmentId,
|
||||
DepartmentName = departmentList.DepartmentName,
|
||||
CompanyId = departmentList.CompanyId,
|
||||
CompanyName = companyList?.CompanyName,
|
||||
DepartmentCode = departmentList.DepartmentCode,
|
||||
};
|
||||
|
||||
// Return the constructed list as JSON
|
||||
return departmentWithCompany;
|
||||
}
|
||||
|
||||
#region Manufacturer
|
||||
|
||||
[HttpPost("ManufacturerList")]
|
||||
public async Task<IActionResult> ManufacturerList()
|
||||
{
|
||||
var manifacturerList = await _centralDbContext.Manufacturers.ToListAsync();
|
||||
return Json(manifacturerList);
|
||||
}
|
||||
|
||||
[HttpPost("AddManufacturer")]
|
||||
public async Task<IActionResult> AddManufacturer([FromBody] ManufacturerModel manufacturer)
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return BadRequest(ModelState);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
_centralDbContext.Manufacturers.Add(new ManufacturerModel
|
||||
{
|
||||
ManufacturerName = manufacturer.ManufacturerName,
|
||||
});
|
||||
await _centralDbContext.SaveChangesAsync();
|
||||
var updatedList = await _centralDbContext.Manufacturers.ToListAsync();
|
||||
return Json(updatedList);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return BadRequest(ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
[HttpDelete("DeleteManufacturer/{id}")]
|
||||
public async Task<IActionResult> DeleteManufacturer(int id)
|
||||
{
|
||||
var manufacturer = await _centralDbContext.Manufacturers.FindAsync(id);
|
||||
if (manufacturer == null)
|
||||
{
|
||||
return NotFound(new { success = false, message = "Manufacturer not found" });
|
||||
}
|
||||
|
||||
_centralDbContext.Manufacturers.Remove(manufacturer);
|
||||
await _centralDbContext.SaveChangesAsync();
|
||||
|
||||
return Ok(new { success = true, message = "Manufacturer deleted successfully" });
|
||||
}
|
||||
|
||||
#endregion Manufacturer
|
||||
|
||||
#region Product
|
||||
|
||||
[HttpPost("ProductList")]
|
||||
public async Task<IActionResult> ProductList()
|
||||
{
|
||||
var productList = await _centralDbContext.Products.Include("Manufacturer").ToListAsync();
|
||||
return Json(productList);
|
||||
}
|
||||
|
||||
[HttpPost("ProductListWithItem")]
|
||||
public async Task<IActionResult> ProductListWithItem()
|
||||
{
|
||||
var productList = await _centralDbContext.Products
|
||||
.Include(p => p.Items) // Include related items
|
||||
.Include(p => p.Manufacturer) // Include related manufacturer
|
||||
.ToListAsync();
|
||||
|
||||
return Json(productList);
|
||||
}
|
||||
|
||||
|
||||
[HttpPost("AddProduct")]
|
||||
public async Task<IActionResult> AddProduct([FromBody] ProductModel product)
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return BadRequest(ModelState);
|
||||
}
|
||||
if (product == null)
|
||||
{
|
||||
return NotFound("Product is null");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
product.QuantityProduct = 0;
|
||||
var productImage = product.ImageProduct; // Save image to wwwroot/media/inventory/images | Images name is product.ModelNo | product.ImageProduct is in base64 string
|
||||
if (!string.IsNullOrEmpty(product.ImageProduct))
|
||||
{
|
||||
var bytes = Convert.FromBase64String(product.ImageProduct);
|
||||
var filePath = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot/media/inventory/images", product.ModelNo + ".jpg");
|
||||
await System.IO.File.WriteAllBytesAsync(filePath, bytes);
|
||||
product.ImageProduct = "/media/inventory/images/" + product.ModelNo + ".jpg";
|
||||
}
|
||||
_centralDbContext.Products.Add(product);
|
||||
await _centralDbContext.SaveChangesAsync();
|
||||
var updatedList = await _centralDbContext.Products.Include("Manufacturer").Where(x => x.ManufacturerId == x.ManufacturerId).ToListAsync();
|
||||
return Json(updatedList);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return BadRequest(ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
[HttpDelete("DeleteProduct/{id}")]
|
||||
public async Task<IActionResult> DeleteProduct(int id)
|
||||
{
|
||||
var Product = await _centralDbContext.Products.FindAsync(id);
|
||||
if (Product == null)
|
||||
{
|
||||
return NotFound(new { success = false, message = "Product not found" });
|
||||
}
|
||||
|
||||
_centralDbContext.Products.Remove(Product);
|
||||
await _centralDbContext.SaveChangesAsync();
|
||||
|
||||
return Ok(new { success = true, message = "Product deleted successfully" });
|
||||
}
|
||||
|
||||
#endregion Product
|
||||
|
||||
#region Supplier
|
||||
|
||||
[HttpPost("SupplierList")]
|
||||
public async Task<IActionResult> SupplierList()
|
||||
{
|
||||
var supplierList = await _centralDbContext.Suppliers.ToListAsync();
|
||||
return Json(supplierList);
|
||||
}
|
||||
|
||||
[HttpPost("AddSupplier")]
|
||||
public async Task<IActionResult> AddSupplier([FromBody] SupplierModel supplier)
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return BadRequest(ModelState);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
_centralDbContext.Suppliers.Add(supplier);
|
||||
await _centralDbContext.SaveChangesAsync();
|
||||
var updatedList = await _centralDbContext.Suppliers.ToListAsync();
|
||||
return Json(updatedList);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return BadRequest(ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
[HttpDelete("DeleteSupplier/{id}")]
|
||||
public async Task<IActionResult> DeleteSupplier(int id)
|
||||
{
|
||||
var supplier = await _centralDbContext.Suppliers.FindAsync(id);
|
||||
if (supplier == null)
|
||||
{
|
||||
return NotFound(new { success = false, message = "Supplier not found" });
|
||||
}
|
||||
|
||||
_centralDbContext.Suppliers.Remove(supplier);
|
||||
await _centralDbContext.SaveChangesAsync();
|
||||
|
||||
return Ok(new { success = true, message = "Supplier deleted successfully" });
|
||||
}
|
||||
#endregion Supplier
|
||||
|
||||
#region Item
|
||||
|
||||
[HttpPost("ItemList")]
|
||||
public async Task<IActionResult> ItemList()
|
||||
{
|
||||
try
|
||||
{
|
||||
var user = await _userManager.GetUserAsync(User);
|
||||
if (user == null)
|
||||
{
|
||||
return BadRequest("User not found");
|
||||
}
|
||||
else
|
||||
{
|
||||
user.departmentId = user.departmentId != null ? user.departmentId : 0;
|
||||
}
|
||||
|
||||
var userRole = await _userManager.GetRolesAsync(user);
|
||||
var isAdmin = userRole.Contains("SystemAdmin") || userRole.Contains("SuperAdmin") || userRole.Contains("Finance");
|
||||
List<ItemModel> itemList = new List<ItemModel>();
|
||||
// Get the item list
|
||||
if (isAdmin)
|
||||
{
|
||||
itemList = await _centralDbContext.Items
|
||||
.AsNoTracking()
|
||||
.Include("CreatedBy")
|
||||
.Include("Department")
|
||||
.Include("Product")
|
||||
.Include(i => i.Movement)
|
||||
.ThenInclude(m => m!.FromStore)
|
||||
.Include(i => i.Movement)
|
||||
.ThenInclude(m => m!.FromStation)
|
||||
.Include(i => i.Movement)
|
||||
.ThenInclude(m => m!.FromUser)
|
||||
.ToListAsync();
|
||||
}
|
||||
else
|
||||
{
|
||||
itemList = await _centralDbContext.Items
|
||||
.AsNoTracking()
|
||||
.Include("CreatedBy")
|
||||
.Include("Department")
|
||||
.Include("Product")
|
||||
.Include(i => i.Movement)
|
||||
.ThenInclude(m => m!.FromStore)
|
||||
.Include(i => i.Movement)
|
||||
.ThenInclude(m => m!.FromStation)
|
||||
.Include(i => i.Movement)
|
||||
.ThenInclude(m => m!.FromUser)
|
||||
.Where(i => i.DepartmentId == user.departmentId)
|
||||
.ToListAsync();
|
||||
}
|
||||
|
||||
// Get the departments list (DepartmentId references Departments)
|
||||
var departments = await _centralDbContext.Departments.ToListAsync();
|
||||
|
||||
// Now join items with users and departments manually
|
||||
var itemListWithDetails = itemList.Select(item => new
|
||||
{
|
||||
item.ItemID,
|
||||
item.UniqueID,
|
||||
item.CompanyId,
|
||||
item.DepartmentId,
|
||||
item.ProductId,
|
||||
item.SerialNumber,
|
||||
item.Quantity,
|
||||
item.Supplier,
|
||||
PurchaseDate = item.PurchaseDate.ToString("dd/MM/yyyy"),
|
||||
item.PONo,
|
||||
item.Currency,
|
||||
item.DefaultPrice,
|
||||
item.CurrencyRate,
|
||||
item.ConvertPrice,
|
||||
item.DODate,
|
||||
item.Warranty,
|
||||
item.PartNumber,
|
||||
EndWDate = item.EndWDate.ToString("dd/MM/yyyy"),
|
||||
InvoiceDate = item.InvoiceDate?.ToString("dd/MM/yyyy"),
|
||||
item.Department?.DepartmentName,
|
||||
CreatedBy=item.CreatedBy!.UserName,
|
||||
item.Product!.ProductName,
|
||||
item.Product!.ProductShortName,
|
||||
item.Product!.Category,
|
||||
//CurrentUser = item.Movement?.FromUser?.UserName,
|
||||
CurrentUser = item.Movement?.FromUser?.UserName,
|
||||
CurrentStore = item.Movement?.FromStore?.StoreName,
|
||||
CurrentStation = item.Movement?.FromStation?.StationName,
|
||||
|
||||
QRString = $"{HttpContext.Request.Scheme}://{HttpContext.Request.Host.Value}/I/{item.UniqueID}" // Generate QR String
|
||||
}).ToList();
|
||||
|
||||
return Json(itemListWithDetails);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return BadRequest(ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
[HttpPost("GenerateItemQr/{id}")]
|
||||
public IActionResult GenerateItemQr(string id)
|
||||
{
|
||||
// Retrieve the request's host and scheme
|
||||
var request = HttpContext.Request;
|
||||
string domain = $"{request.Scheme}://{request.Host.Value}";
|
||||
|
||||
// Append the QR path and item ID
|
||||
string QRString = $"{domain}/Inventory/ItemInformation/{id}";
|
||||
return Json(QRString);
|
||||
}
|
||||
|
||||
[HttpPost("AddItem")]
|
||||
public async Task<IActionResult> AddItem([FromBody] ItemModel item)
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return BadRequest(ModelState);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var product = await _centralDbContext.Products.FirstOrDefaultAsync(p => p.ProductId == item.ProductId) ?? throw new Exception("Product not found");
|
||||
var inventoryMaster = await _centralDbContext.InventoryMasters.Include("User").FirstOrDefaultAsync(i => i.UserId == item.CreatedByUserId) ?? new InventoryMasterModel{ UserId = item.CreatedByUserId };
|
||||
var addToProduct = item.Quantity;
|
||||
product.QuantityProduct += addToProduct;
|
||||
|
||||
if (product.Category == "Disposable")
|
||||
{
|
||||
item.SerialNumber = null;
|
||||
}
|
||||
|
||||
_centralDbContext.Items.Add(item);
|
||||
_centralDbContext.Products.Update(product);
|
||||
|
||||
await _centralDbContext.SaveChangesAsync(); // This generates the auto-incremented ItemID
|
||||
|
||||
|
||||
ItemMovementModel itemMovement = new ItemMovementModel
|
||||
{
|
||||
ItemId = item.ItemID,
|
||||
ToUser = inventoryMaster.UserId,
|
||||
ToStore = inventoryMaster.StoreId,
|
||||
LastStore = inventoryMaster.StoreId,
|
||||
LastUser = inventoryMaster.UserId,
|
||||
LatestStatus = "Ready To Deploy",
|
||||
Quantity = item.Quantity,
|
||||
Action= "Register",
|
||||
Date = DateTime.Now,
|
||||
MovementComplete = true,
|
||||
};
|
||||
|
||||
_centralDbContext.ItemMovements.Add(itemMovement);
|
||||
await _centralDbContext.SaveChangesAsync();
|
||||
|
||||
// Fetch the generated ItemID
|
||||
var savedItem = await _centralDbContext.Items.FirstOrDefaultAsync(i => i.ItemID == item.ItemID);
|
||||
// Fetch the generated itemMovement
|
||||
var savedMovement = await _centralDbContext.ItemMovements.FirstOrDefaultAsync(i => i.Id == itemMovement.Id);
|
||||
|
||||
if (savedItem != null)
|
||||
{
|
||||
var companyDepartment = await GetDepartmentWithCompany(item.CompanyId, item.DepartmentId);
|
||||
var itemProduct = _centralDbContext.Products.Where(p => p.ProductId == item.ProductId).FirstOrDefault();
|
||||
|
||||
string? companyInitial = companyDepartment!.CompanyName?.ToString().Substring(0, 1).ToUpper();
|
||||
string? departmentInitial = companyDepartment!.DepartmentName?.ToString().Substring(0, 1).ToUpper();
|
||||
string? deptCode = companyDepartment!.DepartmentCode?.ToString();
|
||||
char? initialCategory = itemProduct!.Category.ToString().Substring(0, 1).ToUpper().FirstOrDefault();
|
||||
string? productId = itemProduct!.ProductId.ToString("D3");
|
||||
string? itemId = item.ItemID.ToString("D5");
|
||||
var uniqueId = $"{deptCode}{initialCategory}{productId}{itemId}".ToUpper();
|
||||
savedItem.UniqueID = uniqueId;
|
||||
savedItem.MovementId = savedMovement?.Id;
|
||||
|
||||
_centralDbContext.Items.Update(savedItem);
|
||||
await _centralDbContext.SaveChangesAsync();
|
||||
}
|
||||
|
||||
var updatedItem = new
|
||||
{
|
||||
savedItem!.ItemID,
|
||||
savedItem.UniqueID,
|
||||
savedItem.CompanyId,
|
||||
savedItem.DepartmentId,
|
||||
savedItem.ProductId,
|
||||
savedItem.SerialNumber,
|
||||
savedItem.Quantity,
|
||||
savedItem.Supplier,
|
||||
savedItem.PurchaseDate,
|
||||
savedItem.PONo,
|
||||
savedItem.Currency,
|
||||
savedItem.DefaultPrice,
|
||||
savedItem.CurrencyRate,
|
||||
savedItem.ConvertPrice,
|
||||
savedItem.DODate,
|
||||
savedItem.Warranty,
|
||||
savedItem.EndWDate,
|
||||
savedItem.InvoiceDate,
|
||||
savedItem.PartNumber,
|
||||
};
|
||||
return Json(updatedItem);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return BadRequest(ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
[HttpDelete("DeleteItem/{id}")]
|
||||
public async Task<IActionResult> DeleteItem(int id)
|
||||
{
|
||||
var item = await _centralDbContext.Items.FindAsync(id);
|
||||
if (item == null)
|
||||
{
|
||||
return NotFound(new { success = false, message = "Item not found" });
|
||||
}
|
||||
|
||||
_centralDbContext.Items.Remove(item);
|
||||
await _centralDbContext.SaveChangesAsync();
|
||||
|
||||
return Ok(new { success = true, message = "Item deleted successfully" });
|
||||
}
|
||||
|
||||
[HttpPost("GetItem/{id}")] // Endpoint to retrieve an item by its ID
|
||||
public async Task<IActionResult> GetItem(string id)
|
||||
{
|
||||
var item = await _centralDbContext.Items
|
||||
.Include("CreatedBy")
|
||||
.Include("Department")
|
||||
.Include("Product")
|
||||
.Include(i => i.Movement)
|
||||
.ThenInclude(m => m!.FromStore)
|
||||
.Include(i => i.Movement)
|
||||
.ThenInclude(m => m!.FromStation)
|
||||
.Include(i => i.Movement)
|
||||
.ThenInclude(m => m!.FromUser).FirstOrDefaultAsync(i => i.UniqueID == id);
|
||||
if (item == null){
|
||||
return NotFound(new { success = false, message = "Item not found" });
|
||||
}
|
||||
var singleItem = new
|
||||
{
|
||||
item.ItemID,
|
||||
item.UniqueID,
|
||||
item.CompanyId,
|
||||
item.DepartmentId,
|
||||
item.ProductId,
|
||||
item.SerialNumber,
|
||||
item.Quantity,
|
||||
item.Supplier,
|
||||
PurchaseDate = item.PurchaseDate.ToString("dd/MM/yyyy"),
|
||||
item.PONo,
|
||||
item.Currency,
|
||||
item.DefaultPrice,
|
||||
item.CurrencyRate,
|
||||
item.ConvertPrice,
|
||||
item.DODate,
|
||||
item.Warranty,
|
||||
item.PartNumber,
|
||||
EndWDate = item.EndWDate.ToString("dd/MM/yyyy"),
|
||||
InvoiceDate = item.InvoiceDate?.ToString("dd/MM/yyyy"),
|
||||
item.Department?.DepartmentName,
|
||||
item.CreatedBy!.UserName,
|
||||
item.Product!.ProductName,
|
||||
item.Product!.ProductShortName,
|
||||
item.Product!.ImageProduct,
|
||||
CurrentUser = item.Movement?.FromUser?.UserName,
|
||||
CurrentStore = item.Movement?.FromStore?.StoreName,
|
||||
CurrentStation = item.Movement?.FromStation?.StationName,
|
||||
QRString = $"{HttpContext.Request.Scheme}://{HttpContext.Request.Host.Value}/I/{item.UniqueID}" // Generate QR String
|
||||
};
|
||||
return Json(singleItem);
|
||||
}
|
||||
|
||||
#endregion Item
|
||||
|
||||
#region ItemReport
|
||||
|
||||
[HttpPost("GetInventoryReport/{deptId}")]
|
||||
public async Task<IActionResult> GetInventoryReport(int deptId)
|
||||
{
|
||||
try{
|
||||
var user = await _userManager.GetUserAsync(User);
|
||||
|
||||
var userRole = await _userManager.GetRolesAsync(user ?? new UserModel());
|
||||
List<ItemModel> items = new List<ItemModel>();
|
||||
if (userRole.Contains("SuperAdmin") && userRole.Contains("SystemAdmin"))
|
||||
{
|
||||
items = await _centralDbContext.Items
|
||||
.Include("CreatedBy")
|
||||
.Include("Department")
|
||||
.Include("Product")
|
||||
.ToListAsync();
|
||||
}
|
||||
else
|
||||
{
|
||||
items = await _centralDbContext.Items
|
||||
.Include("CreatedBy")
|
||||
.Include("Department")
|
||||
.Include("Product")
|
||||
.Where(i => i.DepartmentId == deptId)
|
||||
.ToListAsync();
|
||||
}
|
||||
var itemListWithDetails = items.Where(i => i.Quantity > 0).Select(item => new
|
||||
{
|
||||
item.ItemID,
|
||||
item.UniqueID,
|
||||
item.CompanyId,
|
||||
item.DepartmentId,
|
||||
item.ProductId,
|
||||
item.SerialNumber,
|
||||
item.Quantity,
|
||||
item.Supplier,
|
||||
PurchaseDate = item.PurchaseDate.ToString("dd/MM/yyyy"),
|
||||
item.PONo,
|
||||
item.Currency,
|
||||
item.DefaultPrice,
|
||||
item.CurrencyRate,
|
||||
item.ConvertPrice,
|
||||
item.DODate,
|
||||
item.Warranty,
|
||||
EndWDate = item.EndWDate.ToString("dd/MM/yyyy"),
|
||||
InvoiceDate = item.InvoiceDate?.ToString("dd/MM/yyyy"),
|
||||
item.Department?.DepartmentName,
|
||||
CreatedBy = item.CreatedBy!.UserName,
|
||||
item.Product!.ProductName,
|
||||
item.Product!.ProductShortName,
|
||||
item.Product!.Category,
|
||||
//CurrentUser = item.Movement?.FromUser?.UserName,
|
||||
CurrentUser = item.Movement?.FromUser?.UserName,
|
||||
CurrentStore = item.Movement?.FromStore?.StoreName,
|
||||
CurrentStation = item.Movement?.FromStation?.StationName,
|
||||
|
||||
QRString = $"{HttpContext.Request.Scheme}://{HttpContext.Request.Host.Value}/I/{item.UniqueID}" // Generate QR String
|
||||
}).ToList();
|
||||
|
||||
int itemCountRegistered = items.Count;
|
||||
int itemCountStillInStock = items.Where(i => i.Quantity > 0).Count();
|
||||
var itemsMovementsThisMonth = _centralDbContext.ItemMovements
|
||||
.Where(i => i.Date.Month == DateTime.Now.Month);
|
||||
int itemCountRegisteredThisMonth = itemsMovementsThisMonth.Count(i => i.Action == "Register");
|
||||
int itemCountStockOutThisMonth = itemsMovementsThisMonth.Count(i => i.Action == "Stock Out");
|
||||
|
||||
var lastMonth = DateTime.Now.AddMonths(-1).Month;
|
||||
var itemsMovementsLastMonth = _centralDbContext.ItemMovements
|
||||
.Where(i => i.Date.Month == lastMonth);
|
||||
int itemCountRegisteredLastMonth = itemsMovementsLastMonth.Count(i => i.Action == "Register");
|
||||
int itemCountStockOutLastMonth = itemsMovementsLastMonth.Count(i => i.Action == "Stock Out");
|
||||
|
||||
var report = new
|
||||
{
|
||||
itemListWithDetails,
|
||||
itemCountRegistered,
|
||||
itemCountStillInStock,
|
||||
itemCountRegisteredThisMonth,
|
||||
itemCountStockOutThisMonth,
|
||||
itemCountRegisteredLastMonth,
|
||||
itemCountStockOutLastMonth
|
||||
};
|
||||
return Json(report);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return BadRequest(ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@ -1,153 +0,0 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using PSTW_CentralSystem.Areas.Inventory.Models;
|
||||
using PSTW_CentralSystem.DBContext;
|
||||
using PSTW_CentralSystem.Models;
|
||||
|
||||
namespace PSTW_CentralSystem.Controllers.API
|
||||
{
|
||||
[ApiController]
|
||||
[Route("[controller]")]
|
||||
public class ModuleAPI : Controller
|
||||
{
|
||||
private readonly ILogger<HomeController> _logger;
|
||||
private readonly CentralSystemContext _authDbContext;
|
||||
public ModuleAPI(ILogger<HomeController> logger, CentralSystemContext authDbContext)
|
||||
{
|
||||
_logger = logger;
|
||||
_authDbContext = authDbContext;
|
||||
}
|
||||
|
||||
[HttpPost("GetModuleInformation")]
|
||||
public async Task<IActionResult> GetModuleInformation()
|
||||
{
|
||||
var qcList = await _authDbContext.ModuleSettings.ToListAsync();
|
||||
return Json(qcList);
|
||||
}
|
||||
|
||||
[HttpPost("GetXModuleInformation")]
|
||||
public async Task<IActionResult> GetXModuleInformation(int? id)
|
||||
{
|
||||
var qcList = await _authDbContext.ModuleSettings.Where(x => x.SettingId == id).FirstOrDefaultAsync();
|
||||
return Json(qcList);
|
||||
}
|
||||
|
||||
[HttpPost("AddMethod")]
|
||||
public async Task<IActionResult> AddMethod(int? id, string? module)
|
||||
{
|
||||
|
||||
// Retrieve the module setting from the database
|
||||
var moduleSetting = await _authDbContext.ModuleSettings
|
||||
.Where(x => x.SettingId == id)
|
||||
.FirstOrDefaultAsync();
|
||||
|
||||
if (moduleSetting == null)
|
||||
{
|
||||
return NotFound("Module setting not found");
|
||||
}
|
||||
|
||||
// Initialize the list if null
|
||||
if (moduleSetting.MethodAllowedUserType == null)
|
||||
{
|
||||
moduleSetting.MethodAllowedUserType = new List<MethodAllowedUserType>();
|
||||
}
|
||||
|
||||
// Add the new module
|
||||
moduleSetting.MethodAllowedUserType.Add(new MethodAllowedUserType
|
||||
{
|
||||
MethodName = module,
|
||||
AllowedUserTypesArray = Array.Empty<string>()
|
||||
});
|
||||
|
||||
// Save the changes to the database
|
||||
_authDbContext.ModuleSettings.Update(moduleSetting);
|
||||
await _authDbContext.SaveChangesAsync();
|
||||
|
||||
return Json(moduleSetting);
|
||||
}
|
||||
|
||||
|
||||
[HttpPost("SaveData")]
|
||||
public async Task<IActionResult> SaveData([FromBody] ModuleSettingModel modelSettingList)
|
||||
{
|
||||
var qcList = await _authDbContext.ModuleSettings
|
||||
.Where(x => x.SettingId == modelSettingList.SettingId)
|
||||
.FirstOrDefaultAsync();
|
||||
|
||||
if (qcList != null)
|
||||
{
|
||||
|
||||
qcList.ModuleName = modelSettingList.ModuleName;
|
||||
qcList.AllowedUserType = modelSettingList.AllowedUserType;
|
||||
|
||||
if (modelSettingList.MethodAllowedUserType != null)
|
||||
{
|
||||
qcList.MethodAllowedUserType = modelSettingList.MethodAllowedUserType
|
||||
.Select(m => new MethodAllowedUserType
|
||||
{
|
||||
MethodName = m.MethodName,
|
||||
AllowedUserTypesArray = m.AllowedUserTypesArray
|
||||
}).ToList();
|
||||
}
|
||||
|
||||
qcList.ModuleStatus = modelSettingList.ModuleStatus;
|
||||
qcList.Description = modelSettingList.Description;
|
||||
|
||||
_authDbContext.ModuleSettings.Update(qcList);
|
||||
await _authDbContext.SaveChangesAsync();
|
||||
}
|
||||
|
||||
return Json(qcList);
|
||||
}
|
||||
|
||||
[HttpPost("AddModule")]
|
||||
public async Task<IActionResult> AddModule([FromBody] ModuleSettingModel module)
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return BadRequest(ModelState);
|
||||
}
|
||||
if (module == null)
|
||||
{
|
||||
return NotFound("Module is null");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
_authDbContext.ModuleSettings.Add(module);
|
||||
await _authDbContext.SaveChangesAsync();
|
||||
var updatedList = await _authDbContext.ModuleSettings.ToListAsync();
|
||||
return Json(updatedList);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return BadRequest(ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
[HttpPost("DeleteModule/{id}")]
|
||||
public async Task<IActionResult> DeleteModule(int? id)
|
||||
{
|
||||
if (id == null)
|
||||
{
|
||||
return NotFound("Module is null");
|
||||
}
|
||||
var moduleSetting = await _authDbContext.ModuleSettings.FindAsync(id);
|
||||
if (moduleSetting == null)
|
||||
{
|
||||
return NotFound("Module not found");
|
||||
}
|
||||
try
|
||||
{
|
||||
_authDbContext.ModuleSettings.Remove(moduleSetting!);
|
||||
await _authDbContext.SaveChangesAsync();
|
||||
var updatedList = await _authDbContext.ModuleSettings.ToListAsync();
|
||||
return Json(updatedList);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return BadRequest(ex.Message);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user