refactoring email verification

This commit is contained in:
Shaun Walker 2020-02-04 15:14:27 -05:00
parent 0aed11e71c
commit ee682516c3
17 changed files with 187 additions and 60 deletions

View File

@ -50,15 +50,24 @@
public string Password = "";
public bool Remember = false;
protected override void OnInitialized()
protected override async Task OnInitializedAsync()
{
if (PageState.QueryString.ContainsKey("returnurl"))
{
ReturnUrl = PageState.QueryString["returnurl"];
}
if (PageState.QueryString.ContainsKey("verified"))
if (PageState.QueryString.ContainsKey("name"))
{
if (PageState.QueryString["verified"] == "1")
Username = PageState.QueryString["name"];
}
if (PageState.QueryString.ContainsKey("token"))
{
User user = new User();
user.SiteId = PageState.Site.SiteId;
user.Username = Username;
user = await UserService.VerifyEmailAsync(user, PageState.QueryString["token"]);
if (user != null)
{
Message = "User Account Verified Successfully. You Can Now Login With Your Username And Password Below.";
}
@ -141,8 +150,8 @@
}
else
{
Message = "Please Enter The Username Related To Your Account And Then Click The Forgot Password Option";
Message = "Please Enter The Username Related To Your Account And Then Select The Forgot Password Option Again";
}
StateHasChanged();
}
}
}

View File

@ -50,7 +50,6 @@
User user = new User();
user.SiteId = PageState.Site.SiteId;
user.Username = Username;
user.DisplayName = Username;
user.Password = Password;
user = await UserService.ResetPasswordAsync(user, PageState.QueryString["token"]);

View File

@ -78,8 +78,7 @@ else
string modifiedby;
DateTime modifiedon;
protected override async Task
OnAfterRenderAsync(bool firstRender)
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{

View File

@ -28,6 +28,7 @@
}
catch (Exception ex)
{
await logger.LogError(ex, "An Error Occurred Loading Html/Text Content. " + ex.Message);
AddModuleMessage(ex.Message, MessageType.Error);
}
}

View File

@ -24,6 +24,8 @@ namespace Oqtane.Services
Task LogoutUserAsync(User User);
Task<User> VerifyEmailAsync(User User, string Token);
Task ForgotPasswordAsync(User User);
Task<User> ResetPasswordAsync(User User, string Token);

View File

@ -87,6 +87,11 @@ namespace Oqtane.Services
await http.PostJsonAsync(apiurl + "/logout", User);
}
public async Task<User> VerifyEmailAsync(User User, string Token)
{
return await http.PostJsonAsync<User>(apiurl + "/verify?token=" + Token, User);
}
public async Task ForgotPasswordAsync(User User)
{
await http.PostJsonAsync(apiurl + "/forgot", User);

View File

@ -38,7 +38,7 @@ namespace Oqtane.Controllers
}
}
// GET api/<controller>/5?userid=x
// GET api/<controller>/5
[HttpGet("{id}")]
public Folder Get(int id)
{

View File

@ -108,8 +108,7 @@ namespace Oqtane.Controllers
notification.ToEmail = "";
notification.Subject = "User Account Verification";
string token = await IdentityUserManager.GenerateEmailConfirmationTokenAsync(identityuser);
string alias = Tenants.GetAlias().Path;
string url = HttpContext.Request.Scheme + "://" + HttpContext.Request.Host + "/pages/verify?name=" + User.Username + "&token=" + WebUtility.UrlEncode(token) + "&returnurl=" + (alias == "" ? "/" : alias);
string url = HttpContext.Request.Scheme + "://" + Tenants.GetAlias().Name + "/login?name=" + User.Username + "&token=" + WebUtility.UrlEncode(token);
notification.Body = "Dear " + User.DisplayName + ",\n\nIn Order To Complete The Registration Of Your User Account Please Click The Link Displayed Below:\n\n" + url + "\n\nThank You!";
notification.ParentId = null;
notification.CreatedOn = DateTime.Now;
@ -254,6 +253,35 @@ namespace Oqtane.Controllers
logger.Log(LogLevel.Information, this, LogFunction.Security, "User Logout {Username}", User.Username);
}
// POST api/<controller>/verify
[HttpPost("verify")]
public async Task<User> Verify([FromBody] User User, string token)
{
if (ModelState.IsValid)
{
IdentityUser identityuser = await IdentityUserManager.FindByNameAsync(User.Username);
if (identityuser != null)
{
var result = await IdentityUserManager.ConfirmEmailAsync(identityuser, token);
if (result.Succeeded)
{
logger.Log(LogLevel.Information, this, LogFunction.Security, "Email Verified For {Username}", User.Username);
}
else
{
logger.Log(LogLevel.Error, this, LogFunction.Security, "Email Verification Failed For {Username}", User.Username);
User = null;
}
}
else
{
logger.Log(LogLevel.Error, this, LogFunction.Security, "Email Verification Failed For {Username}", User.Username);
User = null;
}
}
return User;
}
// POST api/<controller>/forgot
[HttpPost("forgot")]
public async Task Forgot([FromBody] User User)
@ -290,7 +318,6 @@ namespace Oqtane.Controllers
[HttpPost("reset")]
public async Task<User> Reset([FromBody] User User, string token)
{
User user = null;
if (ModelState.IsValid)
{
IdentityUser identityuser = await IdentityUserManager.FindByNameAsync(User.Username);
@ -299,21 +326,22 @@ namespace Oqtane.Controllers
var result = await IdentityUserManager.ResetPasswordAsync(identityuser, token, User.Password);
if (result.Succeeded)
{
user = User;
user.Password = "";
logger.Log(LogLevel.Information, this, LogFunction.Security, "Password Reset For {Username}", User.Username);
User.Password = "";
}
else
{
logger.Log(LogLevel.Error, this, LogFunction.Security, "Password Reset Failed For {Username}", User.Username);
User = null;
}
}
else
{
logger.Log(LogLevel.Error, this, LogFunction.Security, "Password Reset Failed For {Username}", User.Username);
User = null;
}
}
return user;
return User;
}
// GET api/<controller>/current

View File

@ -37,6 +37,10 @@ namespace Oqtane.Pages
await IdentitySignInManager.SignInAsync(identityuser, remember);
}
if (returnurl == null)
{
returnurl = "";
}
if (!returnurl.StartsWith("/"))
{
returnurl = "/" + returnurl;

View File

@ -14,6 +14,10 @@ namespace Oqtane.Pages
{
await HttpContext.SignOutAsync(IdentityConstants.ApplicationScheme);
if (returnurl == null)
{
returnurl = "";
}
if (!returnurl.StartsWith("/"))
{
returnurl = "/" + returnurl;

View File

@ -1,3 +0,0 @@
@page "/pages/verify"
@namespace Oqtane.Pages
@model Oqtane.Pages.VerifyModel

View File

@ -1,42 +0,0 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Oqtane.Models;
using Oqtane.Repository;
using System.Threading.Tasks;
namespace Oqtane.Pages
{
[AllowAnonymous]
public class VerifyModel : PageModel
{
private readonly IUserRepository Users;
private readonly UserManager<IdentityUser> IdentityUserManager;
public VerifyModel(IUserRepository Users, UserManager<IdentityUser> IdentityUserManager)
{
this.Users = Users;
this.IdentityUserManager = IdentityUserManager;
}
public async Task<IActionResult> OnGet(string name, string token, string returnurl)
{
int verified = 0;
IdentityUser identityuser = await IdentityUserManager.FindByNameAsync(name);
if (identityuser != null)
{
var result = await IdentityUserManager.ConfirmEmailAsync(identityuser, token);
if (result.Succeeded)
{
verified = 1;
}
}
if (!returnurl.StartsWith("/"))
{
returnurl += "/" + returnurl;
}
return Redirect(HttpContext.Request.Scheme + "://" + HttpContext.Request.Host + returnurl + "login?verified=" + verified.ToString());
}
}
}

View File

@ -19,6 +19,7 @@ namespace Oqtane.Repository
public virtual DbSet<Log> Log { get; set; }
public virtual DbSet<Notification> Notification { get; set; }
public virtual DbSet<Folder> Folder { get; set; }
public virtual DbSet<File> File { get; set; }
public TenantDBContext(ITenantResolver TenantResolver, IHttpContextAccessor accessor) : base(TenantResolver, accessor)
{

View File

@ -0,0 +1,62 @@
using Microsoft.EntityFrameworkCore;
using System.Collections.Generic;
using System.Linq;
using Oqtane.Models;
namespace Oqtane.Repository
{
public class FileRepository : IFileRepository
{
private TenantDBContext db;
private readonly IPermissionRepository Permissions;
public FileRepository(TenantDBContext context, IPermissionRepository Permissions)
{
db = context;
this.Permissions = Permissions;
}
public IEnumerable<File> GetFiles(int FolderId)
{
IEnumerable<Permission> permissions = Permissions.GetPermissions("Folder", FolderId);
IEnumerable<File> files = db.File.Where(item => item.FolderId == FolderId);
foreach (File file in files)
{
file.Folder.Permissions = Permissions.EncodePermissions(FolderId, permissions);
}
return files;
}
public File AddFile(File File)
{
db.File.Add(File);
db.SaveChanges();
return File;
}
public File UpdateFile(File File)
{
db.Entry(File).State = EntityState.Modified;
db.SaveChanges();
return File;
}
public File GetFile(int FileId)
{
File file = db.File.Find(FileId);
if (file != null)
{
IEnumerable<Permission> permissions = Permissions.GetPermissions("Folder", file.FolderId);
file.Folder.Permissions = Permissions.EncodePermissions(file.FolderId, permissions);
}
return file;
}
public void DeleteFile(int FileId)
{
File File = db.File.Find(FileId);
db.File.Remove(File);
db.SaveChanges();
}
}
}

View File

@ -0,0 +1,14 @@
using System.Collections.Generic;
using Oqtane.Models;
namespace Oqtane.Repository
{
public interface IFileRepository
{
IEnumerable<File> GetFiles(int FolderId);
File AddFile(File File);
File UpdateFile(File File);
File GetFile(int FileId);
void DeleteFile(int FileId);
}
}

View File

@ -275,6 +275,24 @@ CREATE TABLE [dbo].[Folder](
)
GO
CREATE TABLE [dbo].[File](
[FileId] [int] IDENTITY(1,1) NOT NULL,
[FolderId] [int] NOT NULL,
[Name] [nvarchar](50) NOT NULL,
[CreatedBy] [nvarchar](256) NOT NULL,
[CreatedOn] [datetime] NOT NULL,
[ModifiedBy] [nvarchar](256) NOT NULL,
[ModifiedOn] [datetime] NOT NULL,
[DeletedBy] [nvarchar](256) NULL,
[DeletedOn] [datetime] NULL,
[IsDeleted][bit] NOT NULL,
CONSTRAINT [PK_File] PRIMARY KEY CLUSTERED
(
[FileId] ASC
)
)
GO
CREATE TABLE [dbo].[HtmlText](
[HtmlTextId] [int] IDENTITY(1,1) NOT NULL,
[ModuleId] [int] NOT NULL,
@ -361,6 +379,11 @@ REFERENCES [dbo].[Site] ([SiteId])
ON DELETE CASCADE
GO
ALTER TABLE [dbo].[File] WITH CHECK ADD CONSTRAINT [FK_File_Folder] FOREIGN KEY([FolderId])
REFERENCES [dbo].[Folder] ([FolderId])
ON DELETE CASCADE
GO
ALTER TABLE [dbo].[HtmlText] WITH CHECK ADD CONSTRAINT [FK_HtmlText_Module] FOREIGN KEY([ModuleId])
REFERENCES [dbo].[Module] ([ModuleId])
ON DELETE CASCADE

View File

@ -0,0 +1,21 @@
using System;
namespace Oqtane.Models
{
public class File : IAuditable
{
public int FileId { get; set; }
public int FolderId { get; set; }
public string Name { get; set; }
public string CreatedBy { get; set; }
public DateTime CreatedOn { get; set; }
public string ModifiedBy { get; set; }
public DateTime ModifiedOn { get; set; }
public string DeletedBy { get; set; }
public DateTime? DeletedOn { get; set; }
public bool IsDeleted { get; set; }
public Folder Folder { get; set; }
}
}