Merge remote-tracking branch 'oqtane/dev' into dev
This commit is contained in:
commit
6b57202421
|
@ -40,19 +40,10 @@
|
|||
[Parameter]
|
||||
public string Expanded { get; set; } // optional - will default to false if not provided
|
||||
|
||||
protected override void OnInitialized()
|
||||
protected override void OnParametersSet()
|
||||
{
|
||||
_heading = (!string.IsNullOrEmpty(Heading)) ? Heading : Name;
|
||||
_heading = !string.IsNullOrEmpty(Heading) ? Localize(nameof(Heading), Heading) : Localize(nameof(Name), Name);
|
||||
_expanded = (!string.IsNullOrEmpty(Expanded)) ? Expanded.ToLower() : "false";
|
||||
if (_expanded == "true") { _show = "show"; }
|
||||
}
|
||||
|
||||
protected override void OnParametersSet()
|
||||
{
|
||||
base.OnParametersSet();
|
||||
|
||||
_heading = !string.IsNullOrEmpty(Heading)
|
||||
? Localize(nameof(Heading), Heading)
|
||||
: Localize(nameof(Name), Name);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ using System.Collections.Generic;
|
|||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Net.Http.Json;
|
||||
using System.Reflection.Metadata.Ecma335;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Oqtane.Models;
|
||||
|
@ -105,7 +106,7 @@ namespace Oqtane.Services
|
|||
protected async Task GetAsync(string uri)
|
||||
{
|
||||
var response = await GetHttpClient().GetAsync(uri);
|
||||
CheckResponse(response);
|
||||
CheckResponse(response, uri);
|
||||
}
|
||||
|
||||
protected async Task<string> GetStringAsync(string uri)
|
||||
|
@ -139,7 +140,7 @@ namespace Oqtane.Services
|
|||
protected async Task<T> GetJsonAsync<T>(string uri)
|
||||
{
|
||||
var response = await GetHttpClient().GetAsync(uri, HttpCompletionOption.ResponseHeadersRead, CancellationToken.None);
|
||||
if (CheckResponse(response) && ValidateJsonContent(response.Content))
|
||||
if (CheckResponse(response, uri) && ValidateJsonContent(response.Content))
|
||||
{
|
||||
return await response.Content.ReadFromJsonAsync<T>();
|
||||
}
|
||||
|
@ -150,7 +151,7 @@ namespace Oqtane.Services
|
|||
protected async Task PutAsync(string uri)
|
||||
{
|
||||
var response = await GetHttpClient().PutAsync(uri, null);
|
||||
CheckResponse(response);
|
||||
CheckResponse(response, uri);
|
||||
}
|
||||
|
||||
protected async Task<T> PutJsonAsync<T>(string uri, T value)
|
||||
|
@ -161,7 +162,7 @@ namespace Oqtane.Services
|
|||
protected async Task<TResult> PutJsonAsync<TValue, TResult>(string uri, TValue value)
|
||||
{
|
||||
var response = await GetHttpClient().PutAsJsonAsync(uri, value);
|
||||
if (CheckResponse(response) && ValidateJsonContent(response.Content))
|
||||
if (CheckResponse(response, uri) && ValidateJsonContent(response.Content))
|
||||
{
|
||||
var result = await response.Content.ReadFromJsonAsync<TResult>();
|
||||
return result;
|
||||
|
@ -172,7 +173,7 @@ namespace Oqtane.Services
|
|||
protected async Task PostAsync(string uri)
|
||||
{
|
||||
var response = await GetHttpClient().PostAsync(uri, null);
|
||||
CheckResponse(response);
|
||||
CheckResponse(response, uri);
|
||||
}
|
||||
|
||||
protected async Task<T> PostJsonAsync<T>(string uri, T value)
|
||||
|
@ -183,7 +184,7 @@ namespace Oqtane.Services
|
|||
protected async Task<TResult> PostJsonAsync<TValue, TResult>(string uri, TValue value)
|
||||
{
|
||||
var response = await GetHttpClient().PostAsJsonAsync(uri, value);
|
||||
if (CheckResponse(response) && ValidateJsonContent(response.Content))
|
||||
if (CheckResponse(response, uri) && ValidateJsonContent(response.Content))
|
||||
{
|
||||
var result = await response.Content.ReadFromJsonAsync<TResult>();
|
||||
return result;
|
||||
|
@ -195,11 +196,16 @@ namespace Oqtane.Services
|
|||
protected async Task DeleteAsync(string uri)
|
||||
{
|
||||
var response = await GetHttpClient().DeleteAsync(uri);
|
||||
CheckResponse(response);
|
||||
CheckResponse(response, uri);
|
||||
}
|
||||
|
||||
private bool CheckResponse(HttpResponseMessage response)
|
||||
private bool CheckResponse(HttpResponseMessage response, string uri)
|
||||
{
|
||||
if (!response.RequestMessage.RequestUri.AbsolutePath.Contains("/api/"))
|
||||
{
|
||||
Console.WriteLine($"Request: {uri} Not Mapped To A Controller Method");
|
||||
return false;
|
||||
}
|
||||
if (response.IsSuccessStatusCode) return true;
|
||||
if (response.StatusCode != HttpStatusCode.NoContent && response.StatusCode != HttpStatusCode.NotFound)
|
||||
{
|
||||
|
|
|
@ -9,12 +9,12 @@
|
|||
@if (_useadminborder)
|
||||
{
|
||||
<div class="app-pane-admin-border">
|
||||
@DynamicComponent
|
||||
<DynamicComponent Type="@ComponentType"></DynamicComponent>
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
@DynamicComponent
|
||||
<DynamicComponent Type="@ComponentType"></DynamicComponent>
|
||||
}
|
||||
</CascadingValue>
|
||||
|
||||
|
@ -23,6 +23,7 @@
|
|||
@code {
|
||||
private bool _visible = true;
|
||||
private bool _useadminborder = false;
|
||||
public Type ComponentType { get; set; }
|
||||
|
||||
[CascadingParameter]
|
||||
protected PageState PageState { get; set; }
|
||||
|
@ -30,8 +31,6 @@
|
|||
[Parameter]
|
||||
public Module ModuleState { get; set; }
|
||||
|
||||
RenderFragment DynamicComponent { get; set; }
|
||||
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
((INotifyPropertyChanged)SiteState.Properties).PropertyChanged += PropertyChanged;
|
||||
|
@ -54,17 +53,7 @@
|
|||
_useadminborder = false;
|
||||
}
|
||||
|
||||
DynamicComponent = builder =>
|
||||
{
|
||||
Type containerType = Type.GetType(container);
|
||||
if (containerType == null)
|
||||
{
|
||||
// fallback
|
||||
containerType = Type.GetType(Constants.DefaultContainer);
|
||||
}
|
||||
builder.OpenComponent(0, containerType);
|
||||
builder.CloseComponent();
|
||||
};
|
||||
ComponentType = Type.GetType(container) ?? Type.GetType(Constants.DefaultContainer);
|
||||
}
|
||||
|
||||
private void PropertyChanged(object sender, PropertyChangedEventArgs e)
|
||||
|
@ -83,4 +72,6 @@
|
|||
{
|
||||
((INotifyPropertyChanged)SiteState.Properties).PropertyChanged -= PropertyChanged;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
using System.Collections.Generic;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
|
@ -9,7 +8,6 @@ using System.Linq;
|
|||
using System.Security.Claims;
|
||||
using Oqtane.Shared;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using Oqtane.Enums;
|
||||
using Oqtane.Infrastructure;
|
||||
|
@ -23,31 +21,27 @@ namespace Oqtane.Controllers
|
|||
public class UserController : Controller
|
||||
{
|
||||
private readonly IUserRepository _users;
|
||||
private readonly IUserRoleRepository _userRoles;
|
||||
private readonly UserManager<IdentityUser> _identityUserManager;
|
||||
private readonly SignInManager<IdentityUser> _identitySignInManager;
|
||||
private readonly ITenantManager _tenantManager;
|
||||
private readonly INotificationRepository _notifications;
|
||||
private readonly IFolderRepository _folders;
|
||||
private readonly IUserManager _userManager;
|
||||
private readonly ISiteRepository _sites;
|
||||
private readonly IUserPermissions _userPermissions;
|
||||
private readonly IJwtManager _jwtManager;
|
||||
private readonly ISyncManager _syncManager;
|
||||
private readonly ILogManager _logger;
|
||||
|
||||
public UserController(IUserRepository users, IUserRoleRepository userRoles, UserManager<IdentityUser> identityUserManager, SignInManager<IdentityUser> identitySignInManager, ITenantManager tenantManager, INotificationRepository notifications, IFolderRepository folders, ISiteRepository sites, IUserPermissions userPermissions, IJwtManager jwtManager, ISyncManager syncManager, ILogManager logger)
|
||||
public UserController(IUserRepository users, UserManager<IdentityUser> identityUserManager, SignInManager<IdentityUser> identitySignInManager, ITenantManager tenantManager, INotificationRepository notifications, IUserManager userManager, ISiteRepository sites, IUserPermissions userPermissions, IJwtManager jwtManager, ILogManager logger)
|
||||
{
|
||||
_users = users;
|
||||
_userRoles = userRoles;
|
||||
_identityUserManager = identityUserManager;
|
||||
_identitySignInManager = identitySignInManager;
|
||||
_tenantManager = tenantManager;
|
||||
_notifications = notifications;
|
||||
_folders = folders;
|
||||
_userManager = userManager;
|
||||
_sites = sites;
|
||||
_userPermissions = userPermissions;
|
||||
_jwtManager = jwtManager;
|
||||
_syncManager = syncManager;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
|
@ -56,16 +50,10 @@ namespace Oqtane.Controllers
|
|||
[Authorize]
|
||||
public User Get(int id, string siteid)
|
||||
{
|
||||
int SiteId;
|
||||
if (int.TryParse(siteid, out SiteId) && SiteId == _tenantManager.GetAlias().SiteId)
|
||||
if (int.TryParse(siteid, out int SiteId) && SiteId == _tenantManager.GetAlias().SiteId)
|
||||
{
|
||||
User user = _users.GetUser(id);
|
||||
if (user != null)
|
||||
{
|
||||
user.SiteId = int.Parse(siteid);
|
||||
user.Roles = GetUserRoles(user.UserId, user.SiteId);
|
||||
}
|
||||
else
|
||||
User user = _userManager.GetUser(id, SiteId);
|
||||
if (user == null)
|
||||
{
|
||||
HttpContext.Response.StatusCode = (int)HttpStatusCode.NotFound;
|
||||
}
|
||||
|
@ -83,16 +71,10 @@ namespace Oqtane.Controllers
|
|||
[HttpGet("name/{name}")]
|
||||
public User Get(string name, string siteid)
|
||||
{
|
||||
int SiteId;
|
||||
if (int.TryParse(siteid, out SiteId) && SiteId == _tenantManager.GetAlias().SiteId)
|
||||
if (int.TryParse(siteid, out int SiteId) && SiteId == _tenantManager.GetAlias().SiteId)
|
||||
{
|
||||
User user = _users.GetUser(name);
|
||||
if (user != null)
|
||||
{
|
||||
user.SiteId = int.Parse(siteid);
|
||||
user.Roles = GetUserRoles(user.UserId, user.SiteId);
|
||||
}
|
||||
else
|
||||
User user = _userManager.GetUser(name, SiteId);
|
||||
if (user == null)
|
||||
{
|
||||
HttpContext.Response.StatusCode = (int)HttpStatusCode.NotFound;
|
||||
}
|
||||
|
@ -141,8 +123,28 @@ namespace Oqtane.Controllers
|
|||
{
|
||||
if (ModelState.IsValid && user.SiteId == _tenantManager.GetAlias().SiteId)
|
||||
{
|
||||
var User = await CreateUser(user);
|
||||
return User;
|
||||
bool allowregistration;
|
||||
if (_userPermissions.IsAuthorized(User, user.SiteId, EntityNames.User, -1, PermissionNames.Write, RoleNames.Admin))
|
||||
{
|
||||
user.EmailConfirmed = true;
|
||||
allowregistration = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
user.EmailConfirmed = false;
|
||||
allowregistration = _sites.GetSite(user.SiteId).AllowRegistration;
|
||||
}
|
||||
|
||||
if (allowregistration)
|
||||
{
|
||||
user = await _userManager.AddUser(user);
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.Log(user.SiteId, LogLevel.Error, this, LogFunction.Create, "User Registration Is Not Enabled For Site. User Was Not Added {User}", user);
|
||||
}
|
||||
|
||||
return user;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -153,99 +155,6 @@ namespace Oqtane.Controllers
|
|||
}
|
||||
}
|
||||
|
||||
private async Task<User> CreateUser(User user)
|
||||
{
|
||||
User newUser = null;
|
||||
|
||||
bool verified;
|
||||
bool allowregistration;
|
||||
if (_userPermissions.IsAuthorized(User, user.SiteId, EntityNames.User, -1, PermissionNames.Write, RoleNames.Admin))
|
||||
{
|
||||
verified = true;
|
||||
allowregistration = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
verified = false;
|
||||
allowregistration = _sites.GetSite(user.SiteId).AllowRegistration;
|
||||
}
|
||||
|
||||
if (allowregistration)
|
||||
{
|
||||
bool succeeded;
|
||||
string errors = "";
|
||||
IdentityUser identityuser = await _identityUserManager.FindByNameAsync(user.Username);
|
||||
if (identityuser == null)
|
||||
{
|
||||
identityuser = new IdentityUser();
|
||||
identityuser.UserName = user.Username;
|
||||
identityuser.Email = user.Email;
|
||||
identityuser.EmailConfirmed = verified;
|
||||
var result = await _identityUserManager.CreateAsync(identityuser, user.Password);
|
||||
succeeded = result.Succeeded;
|
||||
if (!succeeded)
|
||||
{
|
||||
errors = string.Join(", ", result.Errors.Select(e => e.Description));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var result = await _identitySignInManager.CheckPasswordSignInAsync(identityuser, user.Password, false);
|
||||
succeeded = result.Succeeded;
|
||||
if (!succeeded)
|
||||
{
|
||||
errors = "Password Not Valid For User";
|
||||
}
|
||||
verified = succeeded;
|
||||
}
|
||||
|
||||
if (succeeded)
|
||||
{
|
||||
user.LastLoginOn = null;
|
||||
user.LastIPAddress = "";
|
||||
newUser = _users.AddUser(user);
|
||||
_syncManager.AddSyncEvent(_tenantManager.GetAlias().TenantId, EntityNames.User, newUser.UserId, SyncEventActions.Create);
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.Log(user.SiteId, LogLevel.Error, this, LogFunction.Create, "Unable To Add User {Username} - {Errors}", user.Username, errors);
|
||||
}
|
||||
|
||||
if (newUser != null)
|
||||
{
|
||||
if (!verified)
|
||||
{
|
||||
string token = await _identityUserManager.GenerateEmailConfirmationTokenAsync(identityuser);
|
||||
string url = HttpContext.Request.Scheme + "://" + _tenantManager.GetAlias().Name + "/login?name=" + user.Username + "&token=" + WebUtility.UrlEncode(token);
|
||||
string 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!";
|
||||
var notification = new Notification(user.SiteId, newUser, "User Account Verification", body);
|
||||
_notifications.AddNotification(notification);
|
||||
}
|
||||
else
|
||||
{
|
||||
string url = HttpContext.Request.Scheme + "://" + _tenantManager.GetAlias().Name;
|
||||
string body = "Dear " + user.DisplayName + ",\n\nA User Account Has Been Successfully Created For You. Please Use The Following Link To Access The Site:\n\n" + url + "\n\nThank You!";
|
||||
var notification = new Notification(user.SiteId, newUser, "User Account Notification", body);
|
||||
_notifications.AddNotification(notification);
|
||||
}
|
||||
|
||||
newUser.Password = ""; // remove sensitive information
|
||||
_logger.Log(user.SiteId, LogLevel.Information, this, LogFunction.Create, "User Added {User}", newUser);
|
||||
}
|
||||
else
|
||||
{
|
||||
user.Password = ""; // remove sensitive information
|
||||
_logger.Log(user.SiteId, LogLevel.Error, this, LogFunction.Create, "Unable To Add User {User}", user);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.Log(user.SiteId, LogLevel.Error, this, LogFunction.Create, "User Registration Is Not Enabled For Site. User Was Not Added {User}", user);
|
||||
}
|
||||
|
||||
return newUser;
|
||||
}
|
||||
|
||||
// PUT api/<controller>/5
|
||||
[HttpPut("{id}")]
|
||||
[Authorize]
|
||||
|
@ -254,37 +163,7 @@ namespace Oqtane.Controllers
|
|||
if (ModelState.IsValid && user.SiteId == _tenantManager.GetAlias().SiteId && _users.GetUser(user.UserId, false) != null
|
||||
&& (_userPermissions.IsAuthorized(User, user.SiteId, EntityNames.User, -1, PermissionNames.Write, RoleNames.Admin) || User.Identity.Name == user.Username))
|
||||
{
|
||||
IdentityUser identityuser = await _identityUserManager.FindByNameAsync(user.Username);
|
||||
if (identityuser != null)
|
||||
{
|
||||
identityuser.Email = user.Email;
|
||||
var valid = true;
|
||||
if (user.Password != "")
|
||||
{
|
||||
var validator = new PasswordValidator<IdentityUser>();
|
||||
var result = await validator.ValidateAsync(_identityUserManager, null, user.Password);
|
||||
valid = result.Succeeded;
|
||||
if (valid)
|
||||
{
|
||||
identityuser.PasswordHash = _identityUserManager.PasswordHasher.HashPassword(identityuser, user.Password);
|
||||
}
|
||||
}
|
||||
if (valid)
|
||||
{
|
||||
await _identityUserManager.UpdateAsync(identityuser);
|
||||
|
||||
user = _users.UpdateUser(user);
|
||||
_syncManager.AddSyncEvent(_tenantManager.GetAlias().TenantId, EntityNames.User, user.UserId, SyncEventActions.Update);
|
||||
_syncManager.AddSyncEvent(_tenantManager.GetAlias().TenantId, EntityNames.User, user.UserId, SyncEventActions.Refresh);
|
||||
user.Password = ""; // remove sensitive information
|
||||
_logger.Log(LogLevel.Information, this, LogFunction.Update, "User Updated {User}", user);
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.Log(user.SiteId, LogLevel.Error, this, LogFunction.Update, "Unable To Update User {Username}. Password Does Not Meet Complexity Requirements.", user.Username);
|
||||
user = null;
|
||||
}
|
||||
}
|
||||
user = await _userManager.UpdateUser(user);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -301,51 +180,10 @@ namespace Oqtane.Controllers
|
|||
[Authorize(Policy = $"{EntityNames.User}:{PermissionNames.Write}:{RoleNames.Admin}")]
|
||||
public async Task Delete(int id, string siteid)
|
||||
{
|
||||
int SiteId;
|
||||
User user = _users.GetUser(id);
|
||||
if (user != null && int.TryParse(siteid, out SiteId) && SiteId == _tenantManager.GetAlias().SiteId)
|
||||
User user = _users.GetUser(id, false);
|
||||
if (user != null && int.TryParse(siteid, out int SiteId) && SiteId == _tenantManager.GetAlias().SiteId)
|
||||
{
|
||||
// remove user roles for site
|
||||
foreach (UserRole userrole in _userRoles.GetUserRoles(user.UserId, SiteId).ToList())
|
||||
{
|
||||
_userRoles.DeleteUserRole(userrole.UserRoleId);
|
||||
_logger.Log(LogLevel.Information, this, LogFunction.Delete, "User Role Deleted {UserRole}", userrole);
|
||||
}
|
||||
|
||||
// remove user folder for site
|
||||
var folder = _folders.GetFolder(SiteId, $"Users{user.UserId}/");
|
||||
if (folder != null)
|
||||
{
|
||||
if (Directory.Exists(_folders.GetFolderPath(folder)))
|
||||
{
|
||||
Directory.Delete(_folders.GetFolderPath(folder), true);
|
||||
}
|
||||
_folders.DeleteFolder(folder.FolderId);
|
||||
_logger.Log(LogLevel.Information, this, LogFunction.Delete, "User Folder Deleted {Folder}", folder);
|
||||
}
|
||||
|
||||
// delete user if they are not a member of any other sites
|
||||
if (!_userRoles.GetUserRoles(user.UserId, -1).Any())
|
||||
{
|
||||
// get identity user
|
||||
IdentityUser identityuser = await _identityUserManager.FindByNameAsync(user.Username);
|
||||
if (identityuser != null)
|
||||
{
|
||||
// delete identity user
|
||||
var result = await _identityUserManager.DeleteAsync(identityuser);
|
||||
if (result != null)
|
||||
{
|
||||
// delete user
|
||||
_users.DeleteUser(user.UserId);
|
||||
_syncManager.AddSyncEvent(_tenantManager.GetAlias().TenantId, EntityNames.User, user.UserId, SyncEventActions.Delete);
|
||||
_logger.Log(LogLevel.Information, this, LogFunction.Delete, "User Deleted {UserId}", user.UserId, result.ToString());
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.Log(LogLevel.Error, this, LogFunction.Delete, "Error Deleting User {UserId}", user.UserId);
|
||||
}
|
||||
}
|
||||
}
|
||||
await _userManager.DeleteUser(id, SiteId);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -358,83 +196,15 @@ namespace Oqtane.Controllers
|
|||
[HttpPost("login")]
|
||||
public async Task<User> Login([FromBody] User user, bool setCookie, bool isPersistent)
|
||||
{
|
||||
User loginUser = new User { SiteId = user.SiteId, Username = user.Username, IsAuthenticated = false };
|
||||
|
||||
if (ModelState.IsValid)
|
||||
{
|
||||
IdentityUser identityuser = await _identityUserManager.FindByNameAsync(user.Username);
|
||||
if (identityuser != null)
|
||||
{
|
||||
var result = await _identitySignInManager.CheckPasswordSignInAsync(identityuser, user.Password, true);
|
||||
if (result.Succeeded)
|
||||
{
|
||||
var LastIPAddress = user.LastIPAddress ?? "";
|
||||
|
||||
user = _users.GetUser(user.Username);
|
||||
if (user.TwoFactorRequired)
|
||||
{
|
||||
var token = await _identityUserManager.GenerateTwoFactorTokenAsync(identityuser, "Email");
|
||||
user.TwoFactorCode = token;
|
||||
user.TwoFactorExpiry = DateTime.UtcNow.AddMinutes(10);
|
||||
_users.UpdateUser(user);
|
||||
|
||||
string body = "Dear " + user.DisplayName + ",\n\nYou requested a secure verification code to log in to your account. Please enter the secure verification code on the site:\n\n" + token +
|
||||
"\n\nPlease note that the code is only valid for 10 minutes so if you are unable to take action within that time period, you should initiate a new login on the site." +
|
||||
"\n\nThank You!";
|
||||
var notification = new Notification(loginUser.SiteId, user, "User Verification Code", body);
|
||||
_notifications.AddNotification(notification);
|
||||
|
||||
_logger.Log(LogLevel.Information, this, LogFunction.Security, "User Verification Notification Sent For {Username}", user.Username);
|
||||
loginUser.TwoFactorRequired = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
loginUser = _users.GetUser(identityuser.UserName);
|
||||
if (loginUser != null)
|
||||
{
|
||||
if (identityuser.EmailConfirmed)
|
||||
{
|
||||
loginUser.IsAuthenticated = true;
|
||||
loginUser.LastLoginOn = DateTime.UtcNow;
|
||||
loginUser.LastIPAddress = LastIPAddress;
|
||||
_users.UpdateUser(loginUser);
|
||||
_logger.Log(LogLevel.Information, this, LogFunction.Security, "User Login Successful {Username}", user.Username);
|
||||
|
||||
if (setCookie)
|
||||
{
|
||||
await _identitySignInManager.SignInAsync(identityuser, isPersistent);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.Log(LogLevel.Information, this, LogFunction.Security, "User Not Verified {Username}", user.Username);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (result.IsLockedOut)
|
||||
{
|
||||
user = _users.GetUser(user.Username);
|
||||
string token = await _identityUserManager.GeneratePasswordResetTokenAsync(identityuser);
|
||||
string url = HttpContext.Request.Scheme + "://" + _tenantManager.GetAlias().Name + "/reset?name=" + user.Username + "&token=" + WebUtility.UrlEncode(token);
|
||||
string body = "Dear " + user.DisplayName + ",\n\nYou attempted multiple times unsuccessfully to log in to your account and it is now locked out. Please wait a few minutes and then try again... or use the link below to reset your password:\n\n" + url +
|
||||
"\n\nPlease note that the link is only valid for 24 hours so if you are unable to take action within that time period, you should initiate another password reset on the site." +
|
||||
"\n\nThank You!";
|
||||
var notification = new Notification(loginUser.SiteId, user, "User Lockout", body);
|
||||
_notifications.AddNotification(notification);
|
||||
_logger.Log(LogLevel.Information, this, LogFunction.Security, "User Lockout Notification Sent For {Username}", user.Username);
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.Log(LogLevel.Information, this, LogFunction.Security, "User Login Failed {Username}", user.Username);
|
||||
}
|
||||
}
|
||||
}
|
||||
user = await _userManager.LoginUser(user, setCookie, isPersistent);
|
||||
}
|
||||
|
||||
return loginUser;
|
||||
else
|
||||
{
|
||||
user = new User { SiteId = user.SiteId, Username = user.Username, IsAuthenticated = false };
|
||||
}
|
||||
return user;
|
||||
}
|
||||
|
||||
// POST api/<controller>/logout
|
||||
|
@ -452,25 +222,7 @@ namespace Oqtane.Controllers
|
|||
{
|
||||
if (ModelState.IsValid)
|
||||
{
|
||||
IdentityUser identityuser = await _identityUserManager.FindByNameAsync(user.Username);
|
||||
if (identityuser != null && !string.IsNullOrEmpty(token))
|
||||
{
|
||||
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} - Error {Error}", user.Username, string.Join(" ", result.Errors.ToList().Select(e => e.Description)));
|
||||
user = null;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Email Verification Failed For {Username}And Token {Token}", user.Username, token);
|
||||
user = null;
|
||||
}
|
||||
user = await _userManager.VerifyEmail(user, token);
|
||||
}
|
||||
return user;
|
||||
}
|
||||
|
@ -481,25 +233,7 @@ namespace Oqtane.Controllers
|
|||
{
|
||||
if (ModelState.IsValid)
|
||||
{
|
||||
IdentityUser identityuser = await _identityUserManager.FindByNameAsync(user.Username);
|
||||
if (identityuser != null)
|
||||
{
|
||||
user = _users.GetUser(user.Username);
|
||||
string token = await _identityUserManager.GeneratePasswordResetTokenAsync(identityuser);
|
||||
string url = HttpContext.Request.Scheme + "://" + _tenantManager.GetAlias().Name + "/reset?name=" + user.Username + "&token=" + WebUtility.UrlEncode(token);
|
||||
string body = "Dear " + user.DisplayName + ",\n\nYou recently requested to reset your password. Please use the link below to complete the process:\n\n" + url +
|
||||
"\n\nPlease note that the link is only valid for 24 hours so if you are unable to take action within that time period, you should initiate another password reset on the site." +
|
||||
"\n\nIf you did not request to reset your password you can safely ignore this message." +
|
||||
"\n\nThank You!";
|
||||
|
||||
var notification = new Notification(_tenantManager.GetAlias().SiteId, user, "User Password Reset", body);
|
||||
_notifications.AddNotification(notification);
|
||||
_logger.Log(LogLevel.Information, this, LogFunction.Security, "Password Reset Notification Sent For {Username}", user.Username);
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Password Reset Notification Failed For {Username}", user.Username);
|
||||
}
|
||||
await _userManager.ForgotPassword(user);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -509,26 +243,7 @@ namespace Oqtane.Controllers
|
|||
{
|
||||
if (ModelState.IsValid)
|
||||
{
|
||||
IdentityUser identityuser = await _identityUserManager.FindByNameAsync(user.Username);
|
||||
if (identityuser != null && !string.IsNullOrEmpty(token))
|
||||
{
|
||||
var result = await _identityUserManager.ResetPasswordAsync(identityuser, token, user.Password);
|
||||
if (result.Succeeded)
|
||||
{
|
||||
_logger.Log(LogLevel.Information, this, LogFunction.Security, "Password Reset For {Username}", user.Username);
|
||||
user.Password = "";
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.Log(LogLevel.Information, this, LogFunction.Security, "Password Reset Failed For {Username} - Error {Error}", user.Username, string.Join(" ", result.Errors.ToList().Select(e => e.Description)));
|
||||
user = null;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Password Reset Failed For {Username} And Token {Token}", user.Username, token);
|
||||
user = null;
|
||||
}
|
||||
user = await _userManager.ResetPassword(user, token);
|
||||
}
|
||||
return user;
|
||||
}
|
||||
|
@ -537,21 +252,16 @@ namespace Oqtane.Controllers
|
|||
[HttpPost("twofactor")]
|
||||
public User TwoFactor([FromBody] User user, string token)
|
||||
{
|
||||
User loginUser = new User { SiteId = user.SiteId, Username = user.Username, IsAuthenticated = false };
|
||||
|
||||
if (ModelState.IsValid && !string.IsNullOrEmpty(token))
|
||||
{
|
||||
user = _users.GetUser(user.Username);
|
||||
if (user != null)
|
||||
{
|
||||
if (user.TwoFactorRequired && user.TwoFactorCode == token && DateTime.UtcNow < user.TwoFactorExpiry)
|
||||
{
|
||||
loginUser.IsAuthenticated = true;
|
||||
}
|
||||
}
|
||||
user = _userManager.VerifyTwoFactor(user, token);
|
||||
}
|
||||
else
|
||||
{
|
||||
user.IsAuthenticated = false;
|
||||
}
|
||||
|
||||
return loginUser;
|
||||
return user;
|
||||
}
|
||||
|
||||
// POST api/<controller>/link
|
||||
|
@ -560,23 +270,7 @@ namespace Oqtane.Controllers
|
|||
{
|
||||
if (ModelState.IsValid)
|
||||
{
|
||||
IdentityUser identityuser = await _identityUserManager.FindByNameAsync(user.Username);
|
||||
if (identityuser != null && !string.IsNullOrEmpty(token))
|
||||
{
|
||||
var result = await _identityUserManager.ConfirmEmailAsync(identityuser, token);
|
||||
if (result.Succeeded)
|
||||
{
|
||||
// make LoginProvider multi-tenant aware
|
||||
type += ":" + user.SiteId.ToString();
|
||||
await _identityUserManager.AddLoginAsync(identityuser, new UserLoginInfo(type, key, name));
|
||||
_logger.Log(LogLevel.Information, this, LogFunction.Security, "External Login Linkage Successful For {Username} And Provider {Provider}", user.Username, type);
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.Log(LogLevel.Error, this, LogFunction.Security, "External Login Linkage Failed For {Username} - Error {Error}", user.Username, string.Join(" ", result.Errors.ToList().Select(e => e.Description)));
|
||||
user = null;
|
||||
}
|
||||
}
|
||||
user = await _userManager.LinkExternalAccount(user, token, type, key, name);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -590,9 +284,7 @@ namespace Oqtane.Controllers
|
|||
[HttpGet("validate/{password}")]
|
||||
public async Task<bool> Validate(string password)
|
||||
{
|
||||
var validator = new PasswordValidator<IdentityUser>();
|
||||
var result = await validator.ValidateAsync(_identityUserManager, null, password);
|
||||
return result.Succeeded;
|
||||
return await _userManager.ValidatePassword(password);
|
||||
}
|
||||
|
||||
// GET api/<controller>/token
|
||||
|
@ -648,25 +340,5 @@ namespace Oqtane.Controllers
|
|||
}
|
||||
return user;
|
||||
}
|
||||
|
||||
private string GetUserRoles(int userId, int siteId)
|
||||
{
|
||||
string roles = "";
|
||||
List<UserRole> userroles = _userRoles.GetUserRoles(userId, siteId).ToList();
|
||||
foreach (UserRole userrole in userroles)
|
||||
{
|
||||
roles += userrole.Role.Name + ";";
|
||||
if (userrole.Role.Name == RoleNames.Host && !userroles.Any(item => item.Role.Name == RoleNames.Admin))
|
||||
{
|
||||
roles += RoleNames.Admin + ";";
|
||||
}
|
||||
if (userrole.Role.Name == RoleNames.Host && !userroles.Any(item => item.Role.Name == RoleNames.Registered))
|
||||
{
|
||||
roles += RoleNames.Registered + ";";
|
||||
}
|
||||
}
|
||||
if (roles != "") roles = ";" + roles;
|
||||
return roles;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -73,13 +73,7 @@ namespace Microsoft.Extensions.DependencyInjection
|
|||
|
||||
internal static IServiceCollection AddOqtaneTransientServices(this IServiceCollection services)
|
||||
{
|
||||
services.AddTransient<IDBContextDependencies, DBContextDependencies>();
|
||||
services.AddTransient<ITenantManager, TenantManager>();
|
||||
services.AddTransient<IAliasAccessor, AliasAccessor>();
|
||||
services.AddTransient<IUserPermissions, UserPermissions>();
|
||||
services.AddTransient<ITenantResolver, TenantResolver>();
|
||||
services.AddTransient<IJwtManager, JwtManager>();
|
||||
|
||||
// repositories
|
||||
services.AddTransient<IModuleDefinitionRepository, ModuleDefinitionRepository>();
|
||||
services.AddTransient<IThemeRepository, ThemeRepository>();
|
||||
services.AddTransient<IAliasRepository, AliasRepository>();
|
||||
|
@ -95,7 +89,6 @@ namespace Microsoft.Extensions.DependencyInjection
|
|||
services.AddTransient<IPermissionRepository, PermissionRepository>();
|
||||
services.AddTransient<ISettingRepository, SettingRepository>();
|
||||
services.AddTransient<ILogRepository, LogRepository>();
|
||||
services.AddTransient<ILogManager, LogManager>();
|
||||
services.AddTransient<ILocalizationManager, LocalizationManager>();
|
||||
services.AddTransient<IJobRepository, JobRepository>();
|
||||
services.AddTransient<IJobLogRepository, JobLogRepository>();
|
||||
|
@ -104,11 +97,21 @@ namespace Microsoft.Extensions.DependencyInjection
|
|||
services.AddTransient<IFileRepository, FileRepository>();
|
||||
services.AddTransient<ISiteTemplateRepository, SiteTemplateRepository>();
|
||||
services.AddTransient<ISqlRepository, SqlRepository>();
|
||||
services.AddTransient<IUpgradeManager, UpgradeManager>();
|
||||
services.AddTransient<ILanguageRepository, LanguageRepository>();
|
||||
services.AddTransient<IVisitorRepository, VisitorRepository>();
|
||||
services.AddTransient<IUrlMappingRepository, UrlMappingRepository>();
|
||||
|
||||
// managers
|
||||
services.AddTransient<IDBContextDependencies, DBContextDependencies>();
|
||||
services.AddTransient<ITenantManager, TenantManager>();
|
||||
services.AddTransient<IAliasAccessor, AliasAccessor>();
|
||||
services.AddTransient<IUserPermissions, UserPermissions>();
|
||||
services.AddTransient<ITenantResolver, TenantResolver>();
|
||||
services.AddTransient<IJwtManager, JwtManager>();
|
||||
services.AddTransient<ILogManager, LogManager>();
|
||||
services.AddTransient<IUpgradeManager, UpgradeManager>();
|
||||
services.AddTransient<IUserManager, UserManager>();
|
||||
|
||||
// obsolete - replaced by ITenantManager
|
||||
services.AddTransient<ITenantResolver, TenantResolver>();
|
||||
|
||||
|
|
21
Oqtane.Server/Infrastructure/Interfaces/IUserManager.cs
Normal file
21
Oqtane.Server/Infrastructure/Interfaces/IUserManager.cs
Normal file
|
@ -0,0 +1,21 @@
|
|||
using System.Threading.Tasks;
|
||||
using Oqtane.Models;
|
||||
|
||||
namespace Oqtane.Infrastructure
|
||||
{
|
||||
public interface IUserManager
|
||||
{
|
||||
User GetUser(int userid, int siteid);
|
||||
User GetUser(string username, int siteid);
|
||||
Task<User> AddUser(User user);
|
||||
Task<User> UpdateUser(User user);
|
||||
Task DeleteUser(int userid, int siteid);
|
||||
Task<User> LoginUser(User user, bool setCookie, bool isPersistent);
|
||||
Task<User> VerifyEmail(User user, string token);
|
||||
Task ForgotPassword(User user);
|
||||
Task<User> ResetPassword(User user, string token);
|
||||
User VerifyTwoFactor(User user, string token);
|
||||
Task<User> LinkExternalAccount(User user, string token, string type, string key, string name);
|
||||
Task<bool> ValidatePassword(string password);
|
||||
}
|
||||
}
|
435
Oqtane.Server/Infrastructure/UserManager.cs
Normal file
435
Oqtane.Server/Infrastructure/UserManager.cs
Normal file
|
@ -0,0 +1,435 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Oqtane.Enums;
|
||||
using Oqtane.Models;
|
||||
using Oqtane.Modules.Admin.Roles;
|
||||
using Oqtane.Repository;
|
||||
using Oqtane.Shared;
|
||||
|
||||
namespace Oqtane.Infrastructure
|
||||
{
|
||||
public class UserManager : IUserManager
|
||||
{
|
||||
private readonly IUserRepository _users;
|
||||
private readonly IUserRoleRepository _userRoles;
|
||||
private readonly UserManager<IdentityUser> _identityUserManager;
|
||||
private readonly SignInManager<IdentityUser> _identitySignInManager;
|
||||
private readonly ITenantManager _tenantManager;
|
||||
private readonly INotificationRepository _notifications;
|
||||
private readonly IFolderRepository _folders;
|
||||
private readonly ISyncManager _syncManager;
|
||||
private readonly ILogManager _logger;
|
||||
|
||||
public UserManager(IUserRepository users, IUserRoleRepository userRoles, UserManager<IdentityUser> identityUserManager, SignInManager<IdentityUser> identitySignInManager, ITenantManager tenantManager, INotificationRepository notifications, IFolderRepository folders, ISyncManager syncManager, ILogManager logger)
|
||||
{
|
||||
_users = users;
|
||||
_userRoles = userRoles;
|
||||
_identityUserManager = identityUserManager;
|
||||
_identitySignInManager = identitySignInManager;
|
||||
_tenantManager = tenantManager;
|
||||
_notifications = notifications;
|
||||
_folders = folders;
|
||||
_syncManager = syncManager;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public User GetUser(int userid, int siteid)
|
||||
{
|
||||
User user = _users.GetUser(userid);
|
||||
if (user != null)
|
||||
{
|
||||
user.SiteId = siteid;
|
||||
user.Roles = GetUserRoles(user.UserId, user.SiteId);
|
||||
}
|
||||
return user;
|
||||
}
|
||||
|
||||
public User GetUser(string username, int siteid)
|
||||
{
|
||||
User user = _users.GetUser(username);
|
||||
if (user != null)
|
||||
{
|
||||
user.SiteId = siteid;
|
||||
user.Roles = GetUserRoles(user.UserId, user.SiteId);
|
||||
}
|
||||
return user;
|
||||
}
|
||||
|
||||
private string GetUserRoles(int userId, int siteId)
|
||||
{
|
||||
string roles = "";
|
||||
List<UserRole> userroles = _userRoles.GetUserRoles(userId, siteId).ToList();
|
||||
foreach (UserRole userrole in userroles)
|
||||
{
|
||||
roles += userrole.Role.Name + ";";
|
||||
if (userrole.Role.Name == RoleNames.Host && !userroles.Any(item => item.Role.Name == RoleNames.Admin))
|
||||
{
|
||||
roles += RoleNames.Admin + ";";
|
||||
}
|
||||
if (userrole.Role.Name == RoleNames.Host && !userroles.Any(item => item.Role.Name == RoleNames.Registered))
|
||||
{
|
||||
roles += RoleNames.Registered + ";";
|
||||
}
|
||||
}
|
||||
if (roles != "") roles = ";" + roles;
|
||||
return roles;
|
||||
}
|
||||
|
||||
public async Task<User> AddUser(User user)
|
||||
{
|
||||
User User = null;
|
||||
var alias = _tenantManager.GetAlias();
|
||||
bool succeeded = false;
|
||||
string errors = "";
|
||||
|
||||
IdentityUser identityuser = await _identityUserManager.FindByNameAsync(user.Username);
|
||||
if (identityuser == null)
|
||||
{
|
||||
identityuser = new IdentityUser();
|
||||
identityuser.UserName = user.Username;
|
||||
identityuser.Email = user.Email;
|
||||
identityuser.EmailConfirmed = user.EmailConfirmed;
|
||||
var result = await _identityUserManager.CreateAsync(identityuser, user.Password);
|
||||
succeeded = result.Succeeded;
|
||||
if (!succeeded)
|
||||
{
|
||||
errors = string.Join(", ", result.Errors.Select(e => e.Description));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var result = await _identitySignInManager.CheckPasswordSignInAsync(identityuser, user.Password, false);
|
||||
succeeded = result.Succeeded;
|
||||
if (!succeeded)
|
||||
{
|
||||
errors = "Password Not Valid For User";
|
||||
}
|
||||
user.EmailConfirmed = succeeded;
|
||||
}
|
||||
|
||||
if (succeeded)
|
||||
{
|
||||
user.LastLoginOn = null;
|
||||
user.LastIPAddress = "";
|
||||
User = _users.AddUser(user);
|
||||
_syncManager.AddSyncEvent(alias.TenantId, EntityNames.User, User.UserId, SyncEventActions.Create);
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.Log(user.SiteId, LogLevel.Error, this, LogFunction.Create, "Unable To Add User {Username} - {Errors}", user.Username, errors);
|
||||
}
|
||||
|
||||
if (User != null)
|
||||
{
|
||||
if (!user.EmailConfirmed)
|
||||
{
|
||||
string token = await _identityUserManager.GenerateEmailConfirmationTokenAsync(identityuser);
|
||||
string url = alias.Protocol + "://" + alias.Name + "/login?name=" + user.Username + "&token=" + WebUtility.UrlEncode(token);
|
||||
string 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!";
|
||||
var notification = new Notification(user.SiteId, User, "User Account Verification", body);
|
||||
_notifications.AddNotification(notification);
|
||||
}
|
||||
else
|
||||
{
|
||||
string url = alias.Protocol + "://" + alias.Name;
|
||||
string body = "Dear " + user.DisplayName + ",\n\nA User Account Has Been Successfully Created For You. Please Use The Following Link To Access The Site:\n\n" + url + "\n\nThank You!";
|
||||
var notification = new Notification(user.SiteId, User, "User Account Notification", body);
|
||||
_notifications.AddNotification(notification);
|
||||
}
|
||||
|
||||
User.Password = ""; // remove sensitive information
|
||||
_logger.Log(user.SiteId, LogLevel.Information, this, LogFunction.Create, "User Added {User}", User);
|
||||
}
|
||||
else
|
||||
{
|
||||
user.Password = ""; // remove sensitive information
|
||||
_logger.Log(user.SiteId, LogLevel.Error, this, LogFunction.Create, "Unable To Add User {User}", user);
|
||||
}
|
||||
|
||||
return User;
|
||||
}
|
||||
|
||||
public async Task<User> UpdateUser(User user)
|
||||
{
|
||||
IdentityUser identityuser = await _identityUserManager.FindByNameAsync(user.Username);
|
||||
if (identityuser != null)
|
||||
{
|
||||
identityuser.Email = user.Email;
|
||||
var valid = true;
|
||||
if (user.Password != "")
|
||||
{
|
||||
var validator = new PasswordValidator<IdentityUser>();
|
||||
var result = await validator.ValidateAsync(_identityUserManager, null, user.Password);
|
||||
valid = result.Succeeded;
|
||||
if (valid)
|
||||
{
|
||||
identityuser.PasswordHash = _identityUserManager.PasswordHasher.HashPassword(identityuser, user.Password);
|
||||
}
|
||||
}
|
||||
if (valid)
|
||||
{
|
||||
await _identityUserManager.UpdateAsync(identityuser);
|
||||
|
||||
user = _users.UpdateUser(user);
|
||||
_syncManager.AddSyncEvent(_tenantManager.GetAlias().TenantId, EntityNames.User, user.UserId, SyncEventActions.Update);
|
||||
_syncManager.AddSyncEvent(_tenantManager.GetAlias().TenantId, EntityNames.User, user.UserId, SyncEventActions.Refresh);
|
||||
user.Password = ""; // remove sensitive information
|
||||
_logger.Log(LogLevel.Information, this, LogFunction.Update, "User Updated {User}", user);
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.Log(user.SiteId, LogLevel.Error, this, LogFunction.Update, "Unable To Update User {Username}. Password Does Not Meet Complexity Requirements.", user.Username);
|
||||
user = null;
|
||||
}
|
||||
}
|
||||
|
||||
return user;
|
||||
}
|
||||
|
||||
public async Task DeleteUser(int userid, int siteid)
|
||||
{
|
||||
// remove user roles for site
|
||||
foreach (UserRole userrole in _userRoles.GetUserRoles(userid, siteid).ToList())
|
||||
{
|
||||
_userRoles.DeleteUserRole(userrole.UserRoleId);
|
||||
_logger.Log(LogLevel.Information, this, LogFunction.Delete, "User Role Deleted {UserRole}", userrole);
|
||||
}
|
||||
|
||||
// remove user folder for site
|
||||
var folder = _folders.GetFolder(siteid, $"Users/{userid}/");
|
||||
if (folder != null)
|
||||
{
|
||||
if (Directory.Exists(_folders.GetFolderPath(folder)))
|
||||
{
|
||||
Directory.Delete(_folders.GetFolderPath(folder), true);
|
||||
}
|
||||
_folders.DeleteFolder(folder.FolderId);
|
||||
_logger.Log(LogLevel.Information, this, LogFunction.Delete, "User Folder Deleted {Folder}", folder);
|
||||
}
|
||||
|
||||
// delete user if they are not a member of any other sites
|
||||
if (!_userRoles.GetUserRoles(userid, -1).Any())
|
||||
{
|
||||
// get identity user
|
||||
var user = _users.GetUser(userid, false);
|
||||
IdentityUser identityuser = await _identityUserManager.FindByNameAsync(user.Username);
|
||||
if (identityuser != null)
|
||||
{
|
||||
// delete identity user
|
||||
var result = await _identityUserManager.DeleteAsync(identityuser);
|
||||
if (result != null)
|
||||
{
|
||||
// delete user
|
||||
_users.DeleteUser(userid);
|
||||
_syncManager.AddSyncEvent(_tenantManager.GetAlias().TenantId, EntityNames.User, userid, SyncEventActions.Delete);
|
||||
_logger.Log(LogLevel.Information, this, LogFunction.Delete, "User Deleted {UserId}", userid, result.ToString());
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.Log(LogLevel.Error, this, LogFunction.Delete, "Error Deleting User {UserId}", userid);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<User> LoginUser(User user, bool setCookie, bool isPersistent)
|
||||
{
|
||||
user.IsAuthenticated = false;
|
||||
|
||||
IdentityUser identityuser = await _identityUserManager.FindByNameAsync(user.Username);
|
||||
if (identityuser != null)
|
||||
{
|
||||
var result = await _identitySignInManager.CheckPasswordSignInAsync(identityuser, user.Password, true);
|
||||
if (result.Succeeded)
|
||||
{
|
||||
var LastIPAddress = user.LastIPAddress ?? "";
|
||||
|
||||
user = _users.GetUser(user.Username);
|
||||
if (user.TwoFactorRequired)
|
||||
{
|
||||
var token = await _identityUserManager.GenerateTwoFactorTokenAsync(identityuser, "Email");
|
||||
user.TwoFactorCode = token;
|
||||
user.TwoFactorExpiry = DateTime.UtcNow.AddMinutes(10);
|
||||
_users.UpdateUser(user);
|
||||
|
||||
string body = "Dear " + user.DisplayName + ",\n\nYou requested a secure verification code to log in to your account. Please enter the secure verification code on the site:\n\n" + token +
|
||||
"\n\nPlease note that the code is only valid for 10 minutes so if you are unable to take action within that time period, you should initiate a new login on the site." +
|
||||
"\n\nThank You!";
|
||||
var notification = new Notification(user.SiteId, user, "User Verification Code", body);
|
||||
_notifications.AddNotification(notification);
|
||||
|
||||
_logger.Log(LogLevel.Information, this, LogFunction.Security, "User Verification Notification Sent For {Username}", user.Username);
|
||||
user.TwoFactorRequired = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
user = _users.GetUser(identityuser.UserName);
|
||||
if (user != null)
|
||||
{
|
||||
if (identityuser.EmailConfirmed)
|
||||
{
|
||||
user.IsAuthenticated = true;
|
||||
user.LastLoginOn = DateTime.UtcNow;
|
||||
user.LastIPAddress = LastIPAddress;
|
||||
_users.UpdateUser(user);
|
||||
_logger.Log(LogLevel.Information, this, LogFunction.Security, "User Login Successful {Username}", user.Username);
|
||||
|
||||
if (setCookie)
|
||||
{
|
||||
await _identitySignInManager.SignInAsync(identityuser, isPersistent);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.Log(LogLevel.Information, this, LogFunction.Security, "User Not Verified {Username}", user.Username);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (result.IsLockedOut)
|
||||
{
|
||||
var alias = _tenantManager.GetAlias();
|
||||
user = _users.GetUser(user.Username);
|
||||
string token = await _identityUserManager.GeneratePasswordResetTokenAsync(identityuser);
|
||||
string url = alias.Protocol + "://" + alias.Name + "/reset?name=" + user.Username + "&token=" + WebUtility.UrlEncode(token);
|
||||
string body = "Dear " + user.DisplayName + ",\n\nYou attempted multiple times unsuccessfully to log in to your account and it is now locked out. Please wait a few minutes and then try again... or use the link below to reset your password:\n\n" + url +
|
||||
"\n\nPlease note that the link is only valid for 24 hours so if you are unable to take action within that time period, you should initiate another password reset on the site." +
|
||||
"\n\nThank You!";
|
||||
var notification = new Notification(user.SiteId, user, "User Lockout", body);
|
||||
_notifications.AddNotification(notification);
|
||||
_logger.Log(LogLevel.Information, this, LogFunction.Security, "User Lockout Notification Sent For {Username}", user.Username);
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.Log(LogLevel.Information, this, LogFunction.Security, "User Login Failed {Username}", user.Username);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return user;
|
||||
}
|
||||
|
||||
public async Task<User> VerifyEmail(User user, string token)
|
||||
{
|
||||
IdentityUser identityuser = await _identityUserManager.FindByNameAsync(user.Username);
|
||||
if (identityuser != null && !string.IsNullOrEmpty(token))
|
||||
{
|
||||
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} - Error {Error}", user.Username, string.Join(" ", result.Errors.ToList().Select(e => e.Description)));
|
||||
user = null;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Email Verification Failed For {Username}And Token {Token}", user.Username, token);
|
||||
user = null;
|
||||
}
|
||||
return user;
|
||||
}
|
||||
public async Task ForgotPassword(User user)
|
||||
{
|
||||
IdentityUser identityuser = await _identityUserManager.FindByNameAsync(user.Username);
|
||||
if (identityuser != null)
|
||||
{
|
||||
var alias = _tenantManager.GetAlias();
|
||||
user = _users.GetUser(user.Username);
|
||||
string token = await _identityUserManager.GeneratePasswordResetTokenAsync(identityuser);
|
||||
string url = alias.Protocol + "://" + alias.Name + "/reset?name=" + user.Username + "&token=" + WebUtility.UrlEncode(token);
|
||||
string body = "Dear " + user.DisplayName + ",\n\nYou recently requested to reset your password. Please use the link below to complete the process:\n\n" + url +
|
||||
"\n\nPlease note that the link is only valid for 24 hours so if you are unable to take action within that time period, you should initiate another password reset on the site." +
|
||||
"\n\nIf you did not request to reset your password you can safely ignore this message." +
|
||||
"\n\nThank You!";
|
||||
|
||||
var notification = new Notification(_tenantManager.GetAlias().SiteId, user, "User Password Reset", body);
|
||||
_notifications.AddNotification(notification);
|
||||
_logger.Log(LogLevel.Information, this, LogFunction.Security, "Password Reset Notification Sent For {Username}", user.Username);
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Password Reset Notification Failed For {Username}", user.Username);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<User> ResetPassword(User user, string token)
|
||||
{
|
||||
IdentityUser identityuser = await _identityUserManager.FindByNameAsync(user.Username);
|
||||
if (identityuser != null && !string.IsNullOrEmpty(token))
|
||||
{
|
||||
var result = await _identityUserManager.ResetPasswordAsync(identityuser, token, user.Password);
|
||||
if (result.Succeeded)
|
||||
{
|
||||
_logger.Log(LogLevel.Information, this, LogFunction.Security, "Password Reset For {Username}", user.Username);
|
||||
user.Password = "";
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.Log(LogLevel.Information, this, LogFunction.Security, "Password Reset Failed For {Username} - Error {Error}", user.Username, string.Join(" ", result.Errors.ToList().Select(e => e.Description)));
|
||||
user = null;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Password Reset Failed For {Username} And Token {Token}", user.Username, token);
|
||||
user = null;
|
||||
}
|
||||
return user;
|
||||
}
|
||||
|
||||
public User VerifyTwoFactor(User user, string token)
|
||||
{
|
||||
user = _users.GetUser(user.Username);
|
||||
if (user != null)
|
||||
{
|
||||
if (user.TwoFactorRequired && user.TwoFactorCode == token && DateTime.UtcNow < user.TwoFactorExpiry)
|
||||
{
|
||||
user.IsAuthenticated = true;
|
||||
}
|
||||
}
|
||||
return user;
|
||||
}
|
||||
|
||||
public async Task<User> LinkExternalAccount(User user, string token, string type, string key, string name)
|
||||
{
|
||||
IdentityUser identityuser = await _identityUserManager.FindByNameAsync(user.Username);
|
||||
if (identityuser != null && !string.IsNullOrEmpty(token))
|
||||
{
|
||||
var result = await _identityUserManager.ConfirmEmailAsync(identityuser, token);
|
||||
if (result.Succeeded)
|
||||
{
|
||||
// make LoginProvider multi-tenant aware
|
||||
type += ":" + user.SiteId.ToString();
|
||||
await _identityUserManager.AddLoginAsync(identityuser, new UserLoginInfo(type, key, name));
|
||||
_logger.Log(LogLevel.Information, this, LogFunction.Security, "External Login Linkage Successful For {Username} And Provider {Provider}", user.Username, type);
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.Log(LogLevel.Error, this, LogFunction.Security, "External Login Linkage Failed For {Username} - Error {Error}", user.Username, string.Join(" ", result.Errors.ToList().Select(e => e.Description)));
|
||||
user = null;
|
||||
}
|
||||
}
|
||||
return user;
|
||||
}
|
||||
|
||||
public async Task<bool> ValidatePassword(string password)
|
||||
{
|
||||
var validator = new PasswordValidator<IdentityUser>();
|
||||
var result = await validator.ValidateAsync(_identityUserManager, null, password);
|
||||
return result.Succeeded;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -99,5 +99,11 @@ namespace Oqtane.Models
|
|||
{
|
||||
get => "Users\\" + UserId.ToString() + "\\";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Information if this user's email address is confirmed (set during user creation)
|
||||
/// </summary>
|
||||
[NotMapped]
|
||||
public bool EmailConfirmed { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user