diff --git a/Controllers/AuthController.cs b/Controllers/AuthController.cs new file mode 100644 index 0000000..fb40f23 --- /dev/null +++ b/Controllers/AuthController.cs @@ -0,0 +1,211 @@ +using Microsoft.AspNetCore.Mvc; +using EduCoreSuite.Models; +using EduCoreSuite.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.AspNetCore.Authentication; +using Microsoft.AspNetCore.Authentication.Cookies; +using System.Security.Claims; +using System.Security.Cryptography; +using System.Text; +using EduCoreSuite.Models.ViewModels.Auth; +using Microsoft.AspNetCore.Authorization; + +[Route("Auth")] +public class AuthController : Controller +{ + private readonly ApplicationDbContext _db; + + public AuthController(ApplicationDbContext db) + { + _db = db; + } + + [HttpGet("Register")] + public IActionResult Register() + { + var viewModel = new RegisterViewModel(); + ViewBag.Roles = _db.Roles.ToList(); + return View(viewModel); + } + + [HttpPost("Register")] + [ValidateAntiForgeryToken] + public async Task Register(RegisterViewModel viewModel) + { + if (!ModelState.IsValid) + { + ViewBag.Roles = _db.Roles.ToList(); + return View(viewModel); + } + + if (await _db.Users.AnyAsync(u => u.Email == viewModel.Email || u.Username == viewModel.UserName)) + { + ModelState.AddModelError("Email", "User already exists"); + ViewBag.Roles = _db.Roles.ToList(); + return View(viewModel); + } + + var user = new User + { + Username = viewModel.UserName, + Email = viewModel.Email, + PasswordHash = HashPassword(viewModel.Password), + FirstName = viewModel.FirstName, + LastName = viewModel.LastName, + RoleID = viewModel.RoleID + }; + + _db.Users.Add(user); + await _db.SaveChangesAsync(); + return RedirectToAction("Login"); + } + + [HttpGet("Login")] + [AllowAnonymous] + public IActionResult Login() + { + return View(new LoginViewModel()); + } + + [HttpPost("Login")] + [ValidateAntiForgeryToken] + public async Task Login(LoginViewModel viewModel) + { + if (!ModelState.IsValid) + { + return View(viewModel); + } + + var user = await _db.Users.FirstOrDefaultAsync(u => u.Email == viewModel.Email); + if (user == null || !VerifyPassword(viewModel.Password, user.PasswordHash)) + { + ModelState.AddModelError("", "Invalid credentials"); + return View(viewModel); + } + + var claims = new List + { + new Claim(ClaimTypes.NameIdentifier, user.ID.ToString()), + new Claim(ClaimTypes.Name, user.Username), + new Claim(ClaimTypes.Email, user.Email), + new Claim("CustomUserId", user.CustomUserId), + new Claim(ClaimTypes.Role, user.RoleID.ToString()) + }; + + var identity = new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme); + var principal = new ClaimsPrincipal(identity); + + await HttpContext.SignInAsync( + CookieAuthenticationDefaults.AuthenticationScheme, + principal, + new AuthenticationProperties { IsPersistent = viewModel.RememberMe }); + + return RedirectToAction("Approval", "User"); + } + + [HttpGet("Logout")] + public async Task Logout() + { + await HttpContext.SignOutAsync(); + return RedirectToAction("Login"); + } + + [HttpGet("ForgotPassword")] + public IActionResult ForgotPassword() + { + return View(new ForgotPasswordViewModel()); + } + + [HttpPost("ForgotPassword")] + [ValidateAntiForgeryToken] + public async Task ForgotPassword(ForgotPasswordViewModel viewModel) + { + if (!ModelState.IsValid) + { + return View(viewModel); + } + + var user = await _db.Users.FirstOrDefaultAsync(u => u.Email == viewModel.Email); + if (user == null) + { + ModelState.AddModelError("", "Email not found"); + return View(viewModel); + } + + string code = new Random().Next(100000, 999999).ToString(); + HttpContext.Session.SetString("ResetCode", code); + HttpContext.Session.SetString("ResetEmail", viewModel.Email); + // TODO: Send email using SMTP or SendGrid + + return RedirectToAction("VerifyCode"); + } + + [HttpGet("VerifyCode")] + public IActionResult VerifyCode() + { + return View(new VerifyCodeViewModel()); + } + + [HttpPost("VerifyCode")] + [ValidateAntiForgeryToken] + public IActionResult VerifyCode(VerifyCodeViewModel viewModel) + { + if (!ModelState.IsValid) + { + return View(viewModel); + } + + var sessionCode = HttpContext.Session.GetString("ResetCode"); + if (viewModel.Code != sessionCode) + { + ModelState.AddModelError("", "Invalid code"); + return View(viewModel); + } + + return RedirectToAction("ResetPassword"); + } + + [HttpGet("ResetPassword")] + public IActionResult ResetPassword() + { + return View(new ResetPasswordViewModel()); + } + + [HttpPost("ResetPassword")] + [ValidateAntiForgeryToken] + public async Task ResetPassword(ResetPasswordViewModel viewModel) + { + if (!ModelState.IsValid) + { + return View(viewModel); + } + + var email = HttpContext.Session.GetString("ResetEmail"); + var user = await _db.Users.FirstOrDefaultAsync(u => u.Email == email); + if (user == null) + { + return RedirectToAction("Login"); + } + + user.PasswordHash = HashPassword(viewModel.NewPassword); + await _db.SaveChangesAsync(); + + // Clear the reset session + HttpContext.Session.Remove("ResetCode"); + HttpContext.Session.Remove("ResetEmail"); + + return RedirectToAction("Login"); + } + + private string HashPassword(string password) + { + using var sha = SHA256.Create(); + var bytes = sha.ComputeHash(Encoding.UTF8.GetBytes(password)); + return Convert.ToBase64String(bytes); + } + + private bool VerifyPassword(string password, string hash) + { + return HashPassword(password) == hash; + } +} \ No newline at end of file diff --git a/Controllers/BooksController.cs b/Controllers/BooksController.cs deleted file mode 100644 index 6ccaa11..0000000 --- a/Controllers/BooksController.cs +++ /dev/null @@ -1,81 +0,0 @@ -using EduCoreSuite.Data; -using EduCoreSuite.Models; -using Microsoft.AspNetCore.Mvc; -using Microsoft.EntityFrameworkCore; - -namespace EduCoreSuite.Controllers -{ - [Route("api/[controller]")] - [ApiController] - public class BooksController : ControllerBase - { - private readonly ApplicationDbContext _context; - - public BooksController(ApplicationDbContext context) - { - _context = context; - } - - [HttpGet] - public async Task>> GetBooks() - { - return await _context.Books.ToListAsync(); - } - - [HttpGet("{id}")] - public async Task> GetBook(int id) - { - var book = await _context.Books.FindAsync(id); - - if (book == null) - return NotFound(); - - return book; - } - - [HttpPost] - public async Task> CreateBook(Book book) - { - _context.Books.Add(book); - await _context.SaveChangesAsync(); - - return CreatedAtAction(nameof(GetBook), new { id = book.Id }, book); - } - - [HttpPut("{id}")] - public async Task UpdateBook(int id, Book book) - { - if (id != book.Id) - return BadRequest(); - - _context.Entry(book).State = EntityState.Modified; - - try - { - await _context.SaveChangesAsync(); - } - catch (DbUpdateConcurrencyException) - { - if (!_context.Books.Any(e => e.Id == id)) - return NotFound(); - - throw; - } - - return NoContent(); - } - - [HttpDelete("{id}")] - public async Task DeleteBook(int id) - { - var book = await _context.Books.FindAsync(id); - if (book == null) - return NotFound(); - - _context.Books.Remove(book); - await _context.SaveChangesAsync(); - - return NoContent(); - } - } -} diff --git a/Controllers/UserController.cs b/Controllers/UserController.cs new file mode 100644 index 0000000..6f40085 --- /dev/null +++ b/Controllers/UserController.cs @@ -0,0 +1,31 @@ +using EduCoreSuite.Data; +using EduCoreSuite.Models.ViewModels.UserApproval; +using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore; +using Microsoft.AspNetCore.Authorization; + + +namespace EduCoreSuite.Controllers +{ + public class UserController : Controller + { + private readonly ApplicationDbContext _db; + + public UserController(ApplicationDbContext db) + { + _db = db; + } + [Authorize(Roles = "3")] + + [HttpGet] + public async Task Approval() + { + var viewModel = new ApprovalViewModel + { + Users = await _db.Users.ToListAsync(), + //Roles = await _db.Roles.ToDictionaryAsync(r => r.ID.ToString(), r => r.Name) + }; + return View(viewModel); + } + } +} \ No newline at end of file diff --git a/Data/ApplicationDbContext.cs b/Data/ApplicationDbContext.cs index 0701fdc..c91b094 100644 --- a/Data/ApplicationDbContext.cs +++ b/Data/ApplicationDbContext.cs @@ -5,7 +5,6 @@ namespace EduCoreSuite.Data { public class ApplicationDbContext : DbContext { - public DbSet Books { get; set; } public DbSet Users { get; set; } diff --git a/EduCoreSuite.csproj b/EduCoreSuite.csproj index 1871bb1..ed88d63 100644 --- a/EduCoreSuite.csproj +++ b/EduCoreSuite.csproj @@ -4,6 +4,7 @@ net8.0 enable enable + fd318432-ae1c-4044-aa3e-05aef00aa902 @@ -18,6 +19,10 @@ runtime; build; native; contentfiles; analyzers; buildtransitive + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + diff --git a/Models/Book.cs b/Models/Book.cs deleted file mode 100644 index d6f3409..0000000 --- a/Models/Book.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System.ComponentModel.DataAnnotations; - -namespace EduCoreSuite.Models -{ - public class Book - { - public int Id { get; set; } - - [Required] - public string Title { get; set; } = string.Empty; - - [Required] - public string Author { get; set; } = string.Empty; - - public int YearPublished { get; set; } - - public string Genre { get; set; } = string.Empty; - } -} diff --git a/Models/User.cs b/Models/User.cs index e125110..10c3477 100644 --- a/Models/User.cs +++ b/Models/User.cs @@ -1,5 +1,6 @@ using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; + using System.Data; using System.Text.Json.Serialization; @@ -7,6 +8,8 @@ namespace EduCoreSuite.Models { public class User { + + [Key] [DatabaseGenerated(DatabaseGeneratedOption.Identity)] public int ID { get; set; } @@ -34,6 +37,9 @@ public class User public string? ResetOTP { get; set; } public DateTime? OTPGeneratedAt { get; set; } + + [NotMapped] + public string CustomUserId => ID.ToString("D3"); } } diff --git a/Models/ViewModels/Auth/ForgotPasswordViewModel.cs b/Models/ViewModels/Auth/ForgotPasswordViewModel.cs new file mode 100644 index 0000000..7cc78bc --- /dev/null +++ b/Models/ViewModels/Auth/ForgotPasswordViewModel.cs @@ -0,0 +1,10 @@ +using System.ComponentModel.DataAnnotations; + +namespace EduCoreSuite.Models.ViewModels.Auth +{ + public class ForgotPasswordViewModel + { + [Required, EmailAddress] + public string Email { get; set; } = string.Empty; + } +} \ No newline at end of file diff --git a/Models/ViewModels/Auth/LoginViewModel.cs b/Models/ViewModels/Auth/LoginViewModel.cs new file mode 100644 index 0000000..6a0ffeb --- /dev/null +++ b/Models/ViewModels/Auth/LoginViewModel.cs @@ -0,0 +1,15 @@ +using System.ComponentModel.DataAnnotations; + +namespace EduCoreSuite.Models.ViewModels.Auth +{ + public class LoginViewModel + { + [Required, EmailAddress] + public string Email { get; set; } = string.Empty; + + [Required, DataType(DataType.Password)] + public string Password { get; set; } = string.Empty; + + public bool RememberMe { get; set; } + } +} \ No newline at end of file diff --git a/Models/ViewModels/Auth/RegisterViewModel.cs b/Models/ViewModels/Auth/RegisterViewModel.cs new file mode 100644 index 0000000..27efef9 --- /dev/null +++ b/Models/ViewModels/Auth/RegisterViewModel.cs @@ -0,0 +1,27 @@ +using System.ComponentModel.DataAnnotations; + +namespace EduCoreSuite.Models.ViewModels.Auth +{ + public class RegisterViewModel + { + [Required, StringLength(50)] + public string UserName { get; set; } = string.Empty; + + [Required, EmailAddress] + public string Email { get; set; } = string.Empty; + + [Required, DataType(DataType.Password)] + [StringLength(100, MinimumLength = 8)] + public string Password { get; set; } = string.Empty; + + [DataType(DataType.Password)] + [Compare("Password", ErrorMessage = "Passwords don't match")] + public string ConfirmPassword { get; set; } = string.Empty; + + public string? FirstName { get; set; } + public string? LastName { get; set; } + + [Required] + public int RoleID { get; set; } + } +} \ No newline at end of file diff --git a/Models/ViewModels/Auth/ResetPasswordViewModel.cs b/Models/ViewModels/Auth/ResetPasswordViewModel.cs new file mode 100644 index 0000000..dc84b15 --- /dev/null +++ b/Models/ViewModels/Auth/ResetPasswordViewModel.cs @@ -0,0 +1,15 @@ +using System.ComponentModel.DataAnnotations; + +namespace EduCoreSuite.Models.ViewModels.Auth +{ + public class ResetPasswordViewModel + { + [Required, DataType(DataType.Password)] + [StringLength(100, MinimumLength = 8)] + public string NewPassword { get; set; } = string.Empty; + + [DataType(DataType.Password)] + [Compare("NewPassword", ErrorMessage = "Passwords don't match")] + public string ConfirmPassword { get; set; } = string.Empty; + } +} \ No newline at end of file diff --git a/Models/ViewModels/Auth/VerifyCodeViewModel.cs b/Models/ViewModels/Auth/VerifyCodeViewModel.cs new file mode 100644 index 0000000..6195330 --- /dev/null +++ b/Models/ViewModels/Auth/VerifyCodeViewModel.cs @@ -0,0 +1,10 @@ +using System.ComponentModel.DataAnnotations; + +namespace EduCoreSuite.Models.ViewModels.Auth +{ + public class VerifyCodeViewModel + { + [Required] + public string Code { get; set; } = string.Empty; + } +} \ No newline at end of file diff --git a/Models/ViewModels/User/ApprovalViewModel.cs b/Models/ViewModels/User/ApprovalViewModel.cs new file mode 100644 index 0000000..6aefd14 --- /dev/null +++ b/Models/ViewModels/User/ApprovalViewModel.cs @@ -0,0 +1,11 @@ +using EduCoreSuite.Models; +using System.Collections.Generic; + +namespace EduCoreSuite.Models.ViewModels.UserApproval +{ + public class ApprovalViewModel + { + public List Users { get; set; } // Fully qualify with Models.User + public Dictionary Roles { get; set; } + } +} \ No newline at end of file diff --git a/Pages/Auth/changepassword.cshtml.cs b/Pages/Auth/changepassword.cshtml.cs index 61962fb..5f02621 100644 --- a/Pages/Auth/changepassword.cshtml.cs +++ b/Pages/Auth/changepassword.cshtml.cs @@ -1,11 +1,13 @@ +using EduCoreSuite.Data; +using EduCoreSuite.Models; +using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.RazorPages; using Microsoft.Extensions.Logging; +using System; using System.ComponentModel.DataAnnotations; -using System.Threading.Tasks; using System.Linq; -using System; -using EduCoreSuite.Data; +using System.Threading.Tasks; public class ChangePasswordModel : PageModel { @@ -98,8 +100,9 @@ public async Task OnPostAsync() } // Update password - user.PasswordHash = BCrypt.Net.BCrypt.HashPassword(Input.NewPassword); - user.ResetOTP = null; + var passwordHasher = new PasswordHasher(); + user.Password = passwordHasher.HashPassword(user, user.Password); + user.PasswordHash = passwordHasher.HashPassword(user, user.Password); user.ResetOTP = null; user.OTPGeneratedAt = null; await _db.SaveChangesAsync(); diff --git a/Pages/Auth/forgotpassword.cshtml.cs b/Pages/Auth/forgotpassword.cshtml.cs index 4b1a084..c5fdbc4 100644 --- a/Pages/Auth/forgotpassword.cshtml.cs +++ b/Pages/Auth/forgotpassword.cshtml.cs @@ -8,71 +8,73 @@ using EduCoreSuite.Data; using EduCoreSuite.Pages; using EducoreSuite.stmpservices; -namespace EducoreSuite.forgotpassword; - - -public class ForgotPasswordModel : PageModel +namespace EducoreSuite.forgotpassword { - private readonly ApplicationDbContext _db; - private readonly ILogger _logger; - private readonly IEmailSender _emailSender; - - public ForgotPasswordModel(ApplicationDbContext db, ILogger logger, IEmailSender emailSender) - { - _db = db; - _logger = logger; - _emailSender = emailSender; - } - - [BindProperty] - [Required(ErrorMessage = "Email is required.")] - [EmailAddress(ErrorMessage = "Invalid email address.")] - public string Email { get; set; } - public string InfoMessage { get; set; } - public string ErrorMessage { get; set; } - public async Task OnPostAsync() + public class ForgotPasswordModel : PageModel { - _logger.LogInformation("ForgotPassword: OnPostAsync triggered."); + private readonly ApplicationDbContext _db; + private readonly ILogger _logger; + private readonly IEmailSender _emailSender; - if (!ModelState.IsValid) + public ForgotPasswordModel(ApplicationDbContext db, ILogger logger, IEmailSender emailSender) { - _logger.LogWarning("ModelState is invalid. Errors: {@Errors}, Email: {Email}", - ModelState.Values.SelectMany(v => v.Errors).Select(e => e.ErrorMessage).ToList(), Email); - return Page(); + _db = db; + _logger = logger; + _emailSender = emailSender; } - var user = _db.Users.FirstOrDefault(u => u.Email == Email); + [BindProperty] + [Required(ErrorMessage = "Email is required.")] + [EmailAddress(ErrorMessage = "Invalid email address.")] + public string Email { get; set; } - if (user == null) + public string InfoMessage { get; set; } + public string ErrorMessage { get; set; } + + public async Task OnPostAsync() { - InfoMessage = "If this email exists, a reset code has been sent."; - _logger.LogWarning("User with email {Email} not found.", Email); - return Page(); - } + _logger.LogInformation("ForgotPassword: OnPostAsync triggered."); - // Generate and store OTP - var otp = new Random().Next(100000, 999999).ToString(); - user.ResetOTP = otp; - user.OTPGeneratedAt = DateTime.UtcNow; + if (!ModelState.IsValid) + { + _logger.LogWarning("ModelState is invalid. Errors: {@Errors}, Email: {Email}", + ModelState.Values.SelectMany(v => v.Errors).Select(e => e.ErrorMessage).ToList(), Email); + return Page(); + } - try - { - await _db.SaveChangesAsync(); - await _emailSender.SendEmailAsync(user.Email, "Your OTP Code", $"Your One-Time Password is: {otp}"); - _logger.LogInformation($"OTP sent to {Email}: {otp}"); - } - catch (Exception ex) - { - _logger.LogError(ex, "Error sending OTP."); - ErrorMessage = "An error occurred while sending the OTP. Please try again."; - return Page(); - } + var user = _db.Users.FirstOrDefault(u => u.Email == Email); - // Store email temporarily and redirect - TempData["ResetEmail"] = Email; - return RedirectToPage("ChangePassword"); + if (user == null) + { + InfoMessage = "If this email exists, a reset code has been sent."; + _logger.LogWarning("User with email {Email} not found.", Email); + return Page(); + } + + // Generate and store OTP + var otp = new Random().Next(100000, 999999).ToString(); + user.ResetOTP = otp; + user.OTPGeneratedAt = DateTime.UtcNow; + + try + { + await _db.SaveChangesAsync(); + await _emailSender.SendEmailAsync(user.Email, "Your OTP Code", $"Your One-Time Password is: {otp}"); + _logger.LogInformation($"OTP sent to {Email}: {otp}"); + } + catch (Exception ex) + { + _logger.LogError(ex, "Error sending OTP."); + ErrorMessage = "An error occurred while sending the OTP. Please try again."; + return Page(); + } + + // Store email temporarily and redirect + TempData["ResetEmail"] = Email; + return RedirectToPage("ChangePassword"); + } } } diff --git a/Program.cs b/Program.cs index f313dad..dcf0f6a 100644 --- a/Program.cs +++ b/Program.cs @@ -1,18 +1,35 @@ +using EduCoreSuite.Data; +using Microsoft.AspNetCore.Authentication.Cookies; using EducoreSuite.stmpservices; -using EduCoreSuite.Data; using Microsoft.EntityFrameworkCore; var builder = WebApplication.CreateBuilder(args); // ✅ Register MVC controller services -builder.Services.AddControllers(); // <-- Add this line +builder.Services.AddControllers(); // <-- Add controllers builder.Services.AddRazorPages(); // ✅ Register your DbContext builder.Services.AddDbContext(options => options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection"))); + + +builder.Services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme) + .AddCookie(options => + { + options.LoginPath = "/Auth/Login"; + options.LogoutPath = "/Auth/Logout"; + options.ExpireTimeSpan = TimeSpan.FromDays(7); + options.SlidingExpiration = true; + }); + +builder.Services.AddSession(); +builder.Services.AddHttpContextAccessor(); + +builder.Services.AddControllersWithViews(); + builder.Services.AddScoped(); builder.Services.Configure( @@ -30,13 +47,40 @@ app.UseHttpsRedirection(); app.UseStaticFiles(); - app.UseRouting(); - +app.UseAuthentication(); app.UseAuthorization(); +app.UseSession(); +app.MapControllerRoute( + name: "default", + pattern: "{controller=Auth}/{action=Login}/{id?}"); + +app.MapFallbackToController("Login", "Auth"); // ✅ Map Razor Pages and API Controllers app.MapRazorPages(); app.MapControllers(); // <-- Add this line to expose your API endpoints app.Run(); + +//var app = builder.Build(); + +//// ✅ Configure HTTP request pipeline +//if (!app.Environment.IsDevelopment()) +//{ +// app.UseExceptionHandler("/Error"); +// app.UseHsts(); +//} + +//app.UseHttpsRedirection(); +//app.UseStaticFiles(); + +//app.UseRouting(); + +//app.UseAuthorization(); + +//// ✅ Map Razor Pages and API Controllers +//app.MapRazorPages(); +//app.MapControllers(); // <-- Add this line to expose your API endpoints + +//app.Run(); \ No newline at end of file diff --git a/Properties/launchSettings.json b/Properties/launchSettings.json index 625109a..664e4ca 100644 --- a/Properties/launchSettings.json +++ b/Properties/launchSettings.json @@ -22,7 +22,7 @@ "commandName": "Project", "dotnetRunMessages": true, "launchBrowser": true, - "applicationUrl": "https://localhost:7065;http://localhost:5138", + "applicationUrl": "https://192.168.100.61;http://localhost:5138", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" } diff --git a/Properties/serviceDependencies.json b/Properties/serviceDependencies.json new file mode 100644 index 0000000..1729da0 --- /dev/null +++ b/Properties/serviceDependencies.json @@ -0,0 +1,12 @@ +{ + "dependencies": { + "secrets1": { + "type": "secrets" + }, + "mssql1": { + "type": "mssql", + "connectionId": "ConnectionStrings:DefaultConnection1", + "dynamicId": null + } + } +} \ No newline at end of file diff --git a/Properties/serviceDependencies.local.json b/Properties/serviceDependencies.local.json new file mode 100644 index 0000000..3661c45 --- /dev/null +++ b/Properties/serviceDependencies.local.json @@ -0,0 +1,13 @@ +{ + "dependencies": { + "secrets1": { + "type": "secrets.user" + }, + "mssql1": { + "secretStore": "LocalSecretsFile", + "type": "mssql.local", + "connectionId": "ConnectionStrings:DefaultConnection1", + "dynamicId": null + } + } +} \ No newline at end of file diff --git a/VaultDB.sql b/VaultDB.sql new file mode 100644 index 0000000..1051d20 Binary files /dev/null and b/VaultDB.sql differ diff --git a/Views/Auth/ForgotPassword.cshtml b/Views/Auth/ForgotPassword.cshtml new file mode 100644 index 0000000..bdb46da --- /dev/null +++ b/Views/Auth/ForgotPassword.cshtml @@ -0,0 +1,34 @@ +@model EduCoreSuite.Models.ViewModels.Auth.ForgotPasswordViewModel +@{ + ViewData["Title"] = "Forgot Password"; +} + +
+

Forgot Password

+ +
+
+ + + +
+ +
+ +
+
+ + +
+ +@section Scripts { + @{ + await Html.RenderPartialAsync("_ValidationScriptsPartial"); + } +} \ No newline at end of file diff --git a/Views/Auth/Login.cshtml b/Views/Auth/Login.cshtml new file mode 100644 index 0000000..7bff321 --- /dev/null +++ b/Views/Auth/Login.cshtml @@ -0,0 +1,45 @@ +@model EduCoreSuite.Models.ViewModels.Auth.LoginViewModel +@{ + ViewData["Title"] = "Login"; +} + + + +@section Scripts { + @{ + await Html.RenderPartialAsync("_ValidationScriptsPartial"); + } +} \ No newline at end of file diff --git a/Views/Auth/Register.cshtml b/Views/Auth/Register.cshtml new file mode 100644 index 0000000..15b1514 --- /dev/null +++ b/Views/Auth/Register.cshtml @@ -0,0 +1,211 @@ +@model EduCoreSuite.Models.ViewModels.Auth.RegisterViewModel +@{ + ViewData["Title"] = "Register"; + var roles = ViewBag.Roles as List; +} + +
+
+
+ +
+

Sign Up

+

Create new account

+
+ +
+ +
+ + + +
+ + +
+
+ + + +
+
+ + + +
+
+ +
+ +
+ + + +
+ + +
+ + +
+
+ + +
+
+ +
+ + +
+ +
+
+ +
+ + +
+ +
+
+ + + @*
+

Password Requirements:

+
+
+ + At least 8 characters long +
+
+ + Contains uppercase letter +
+
+ + Contains number +
+
+
*@ + + +
+ +
+ + +
+

Already have an account?

+ + Sign In + +
+
+
+
+
+ +@section Scripts { + @{ + await Html.RenderPartialAsync("_ValidationScriptsPartial"); + } + + + + + +} \ No newline at end of file diff --git a/Views/Auth/ResetPassword.cshtml b/Views/Auth/ResetPassword.cshtml new file mode 100644 index 0000000..f45b299 --- /dev/null +++ b/Views/Auth/ResetPassword.cshtml @@ -0,0 +1,34 @@ +@model EduCoreSuite.Models.ViewModels.Auth.ResetPasswordViewModel +@{ + ViewData["Title"] = "Reset Password"; +} + +
+

Reset Password

+ +
+
+ + + +
+ +
+ + + +
+ +
+ +
+
+
+ +@section Scripts { + @{ + await Html.RenderPartialAsync("_ValidationScriptsPartial"); + } +} \ No newline at end of file diff --git a/Views/Auth/VerifyCode.cshtml b/Views/Auth/VerifyCode.cshtml new file mode 100644 index 0000000..9650847 --- /dev/null +++ b/Views/Auth/VerifyCode.cshtml @@ -0,0 +1,28 @@ +@model EduCoreSuite.Models.ViewModels.Auth.VerifyCodeViewModel +@{ + ViewData["Title"] = "Verify Code"; +} + +
+

Verify Code

+ +
+
+ + + +
+ +
+ +
+
+
+ +@section Scripts { + @{ + await Html.RenderPartialAsync("_ValidationScriptsPartial"); + } +} \ No newline at end of file diff --git a/Pages/Error.cshtml b/Views/Error.cshtml similarity index 100% rename from Pages/Error.cshtml rename to Views/Error.cshtml diff --git a/Pages/Error.cshtml.cs b/Views/Error.cshtml.cs similarity index 100% rename from Pages/Error.cshtml.cs rename to Views/Error.cshtml.cs diff --git a/Pages/Index.cshtml b/Views/Index.cshtml similarity index 100% rename from Pages/Index.cshtml rename to Views/Index.cshtml diff --git a/Pages/Index.cshtml.cs b/Views/Index.cshtml.cs similarity index 100% rename from Pages/Index.cshtml.cs rename to Views/Index.cshtml.cs diff --git a/Pages/Privacy.cshtml b/Views/Privacy.cshtml similarity index 100% rename from Pages/Privacy.cshtml rename to Views/Privacy.cshtml diff --git a/Pages/Privacy.cshtml.cs b/Views/Privacy.cshtml.cs similarity index 100% rename from Pages/Privacy.cshtml.cs rename to Views/Privacy.cshtml.cs diff --git a/Pages/Shared/_Layout.cshtml b/Views/Shared/_Layout.cshtml similarity index 94% rename from Pages/Shared/_Layout.cshtml rename to Views/Shared/_Layout.cshtml index 714d889..484c40e 100644 --- a/Pages/Shared/_Layout.cshtml +++ b/Views/Shared/_Layout.cshtml @@ -24,7 +24,7 @@ Home