1520 lines
47 KiB
C#
1520 lines
47 KiB
C#
using System;
|
|
using System.Linq;
|
|
using System.Threading.Tasks;
|
|
using Microsoft.AspNetCore.Identity;
|
|
using xCommons.Extensions;
|
|
using xExceptions.Constants;
|
|
using xIdentityModels;
|
|
using xIdentityModels.Constants;
|
|
using xIdentityModels.Extensions;
|
|
using xIdentityModels.Models;
|
|
using xIdentityModels.Navigations;
|
|
|
|
namespace xIds.Providers
|
|
{
|
|
public partial class XIdentityManager
|
|
{
|
|
//
|
|
#region User Validators ...
|
|
/// <summary>
|
|
/// Validate User Name for Registration based on Policies
|
|
/// </summary>
|
|
/// <param name="userName">provided user name</param>
|
|
/// <returns>a boolean value</returns>
|
|
private bool ValidateUserName(
|
|
string userName
|
|
)
|
|
{
|
|
//
|
|
// Check min/max Length ...
|
|
if (userName.IsNullOrEmpty() ||
|
|
userName.Length < Configuration.Policy.User.MinLength ||
|
|
userName.Length > Configuration.Policy.User.MaxLength)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
//
|
|
// Check Allowed UserName Characters ...
|
|
if (!UserManager.Options.User.AllowedUserNameCharacters.IsNullOrEmpty() &&
|
|
userName.Any(c => !UserManager.Options.User.AllowedUserNameCharacters.Contains(c)))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
//
|
|
// Check invalid UserNames ...
|
|
if (Configuration.Policy.User.InvalidUserNames
|
|
.HasChild() && Configuration.Policy.User.InvalidUserNames
|
|
.Any(u => userName
|
|
.ToNormalString()
|
|
.Contains(u.ToNormalString())))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
//
|
|
return true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Validate a User Exists
|
|
/// </summary>
|
|
/// <param name="userSelectByParam">specifies user identifier</param>
|
|
/// <param name="exception">specify <see>Excetion</see> to thrown on failure</param>
|
|
/// <returns></returns>
|
|
private async Task ValidateUserExistsAsync(
|
|
string userSelectByParam,
|
|
Exception exception = null
|
|
)
|
|
{
|
|
//
|
|
ValidationProvider
|
|
.NotEmpty(userSelectByParam);
|
|
|
|
//
|
|
if (exception == null)
|
|
{
|
|
exception = XException.NotFound.ToException();
|
|
}
|
|
|
|
//
|
|
// Check User Exists ...
|
|
var isExists = await IsUserExistsAsync(userSelectByParam);
|
|
if (!isExists)
|
|
{
|
|
throw exception;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Validate a User Not Exists
|
|
/// </summary>
|
|
/// <param name="userSelectByParam">specifies user identifier</param>
|
|
/// <param name="exception">specify <see>Excetion</see> to thrown on failure</param>
|
|
/// <returns></returns>
|
|
private async Task ValidateUserNotExists(
|
|
string userSelectByParam,
|
|
Exception exception = null
|
|
)
|
|
{
|
|
//
|
|
ValidationProvider
|
|
.NotEmpty(userSelectByParam);
|
|
|
|
//
|
|
if (exception == null)
|
|
{
|
|
exception = XException.UserRegisteredBefore.ToException();
|
|
}
|
|
|
|
//
|
|
// Check User Exists ...
|
|
var isExists = await IsUserExistsAsync(userSelectByParam);
|
|
if (isExists)
|
|
{
|
|
throw exception;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Validate User Exists and Some Usefull Checks
|
|
/// </summary>
|
|
/// <param name="userSelectByParam">specifies user identifier</param>
|
|
/// <param name="containDetails">specifies returned object contains all Navigation Properties or not, default is false</param>
|
|
/// <param name="checkCanLoginPolicies">check a user can log in in system or not, default is false</param>
|
|
/// <param name="checkIsBanned">check user is banned or not, default is true</param>
|
|
/// <param name="ignoreDisabledUser">specify do not thrown <see>Exception</see> if user is Disabled, default is false</param>
|
|
/// <param name="forceAdmin">specify requested person is Admin role, default is false</param>
|
|
/// <param name="exception">specify <see>Excetion</see> to thrown on failure</param>
|
|
/// <returns>an instance of <see>XUser</see></returns>
|
|
public async Task<XUser> ValidateUserExistsAndRetrieve(
|
|
string userSelectByParam,
|
|
bool containDetails = false,
|
|
bool checkCanLoginPolicies = false,
|
|
bool checkIsBanned = true,
|
|
bool ignoreDisabledUser = false,
|
|
bool forceAdmin = false,
|
|
Exception exception = null
|
|
)
|
|
{
|
|
//
|
|
ValidationProvider
|
|
.NotEmpty(userSelectByParam);
|
|
ValidationProvider.NotNull(
|
|
Configuration,
|
|
Configuration.Policy,
|
|
Configuration.Policy.SignIn);
|
|
|
|
//
|
|
// Prepare Default Exception ...
|
|
if (exception == null)
|
|
{
|
|
exception = XException.InvalidData.ToException();
|
|
}
|
|
|
|
//
|
|
// Check User Exists ...
|
|
await ValidateUserExistsAsync(userSelectByParam);
|
|
|
|
//
|
|
// Retrieve User ...
|
|
var result = await GetUserAsync(userSelectByParam, containDetails);
|
|
if (result == null)
|
|
{
|
|
throw exception;
|
|
}
|
|
|
|
//
|
|
// Check Force Admin ...
|
|
if (forceAdmin)
|
|
{
|
|
//
|
|
var isAdmin = await IsAdmin(result);
|
|
if (!isAdmin)
|
|
{
|
|
XException.NotAuthorized.Throw();
|
|
}
|
|
}
|
|
|
|
//
|
|
// Check User not Bann ...
|
|
if (checkIsBanned &&
|
|
result.IsBanned)
|
|
{
|
|
XException.UserBanned.Throw();
|
|
}
|
|
|
|
//
|
|
var requireConfirmedEmail = Configuration.Policy.SignIn.RequireConfirmedEmail;
|
|
var requireConfirmedPhoneNumber = Configuration.Policy.SignIn.RequireConfirmedPhoneNumber;
|
|
var requireEnabled = Configuration.Policy.SignIn.RequiredEnabled;
|
|
|
|
//
|
|
var isEnabled = result.IsEnable;
|
|
var isMobileConfirmed = result.PhoneNumberConfirmed;
|
|
var isEmailConfirmed = result.EmailConfirmed;
|
|
|
|
//
|
|
if (checkCanLoginPolicies)
|
|
{
|
|
//
|
|
// Check IsEnabled ...
|
|
if (!ignoreDisabledUser &&
|
|
requireEnabled &&
|
|
!isEnabled)
|
|
{
|
|
XException.UserDisabled.Throw();
|
|
}
|
|
|
|
//
|
|
// Check Email Confirmed ...
|
|
if (requireConfirmedEmail &&
|
|
!isEmailConfirmed)
|
|
{
|
|
XException.EmailNotConfirmed.Throw();
|
|
}
|
|
|
|
//
|
|
// Check Mobile Confirmed ...
|
|
if (requireConfirmedPhoneNumber &&
|
|
!isMobileConfirmed)
|
|
{
|
|
XException.MobileNotConfirmed.Throw();
|
|
}
|
|
}
|
|
|
|
//
|
|
// Return Result ...
|
|
return result;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Validate a Device for User Actions
|
|
/// </summary>
|
|
/// <param name="device">an instance of <see>XDevice</see></param>
|
|
/// <returns></returns>
|
|
private async Task ValidateDeviceForActions(
|
|
XDevice device
|
|
)
|
|
{
|
|
//
|
|
ValidationProvider
|
|
.NotNull(device);
|
|
|
|
//
|
|
// Check is Device Banned or not ...
|
|
var isDeviceBanned = await IsDeviceBanned(device);
|
|
if (isDeviceBanned)
|
|
{
|
|
//
|
|
// Retrieve Banned Device ...
|
|
var xBannedDevice = await GetBannedDevice(device);
|
|
if (xBannedDevice == null)
|
|
{
|
|
XException.InvalidArgs.Throw();
|
|
}
|
|
|
|
//
|
|
// Check Banned Device Time out Passed or not ...
|
|
var isDelayTimePassed = IsDelayTimePassed(xBannedDevice);
|
|
if (!isDelayTimePassed)
|
|
{
|
|
//
|
|
var passedTime = GetPassedTime(xBannedDevice);
|
|
|
|
//
|
|
XException.DeviceBanned
|
|
.AddContentToException(passedTime.ToString());
|
|
}
|
|
|
|
//
|
|
// if Delay Passed of Banned Device
|
|
// the device must Removed ...
|
|
await BannedDeviceRemove(xBannedDevice);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Validate a DateOfBirth for Registration
|
|
/// </summary>
|
|
/// <param name="dateOfBirth">users dob date, an instance of <see>DateTime</see></param>
|
|
/// <param name="exception">specify <see>Excetion</see> to thrown on failure</param>
|
|
/// <returns></returns>
|
|
private void ValidateDateOfBirth(
|
|
DateTime dateOfBirth,
|
|
Exception exception = null
|
|
)
|
|
{
|
|
//
|
|
// Validate Args ...
|
|
ValidationProvider.NotNull(dateOfBirth);
|
|
|
|
//
|
|
if (exception.IsNull())
|
|
{
|
|
exception = XException.InvalidDate.ToException();
|
|
}
|
|
|
|
//
|
|
var currentDate = DateTime.UtcNow;
|
|
if (dateOfBirth >= currentDate)
|
|
{
|
|
throw exception;
|
|
}
|
|
|
|
//
|
|
var zeroDate = (new DateTime(1, 1, 1)).ToUniversalTime();
|
|
var minAge = Configuration.Policy.User.MinAgeForRegistration;
|
|
var maxAge = Configuration.Policy.User.MaxAgeForRegistration;
|
|
|
|
//
|
|
var timeSpan = currentDate - dateOfBirth;
|
|
var age = (zeroDate + timeSpan).Year - 1;
|
|
|
|
//
|
|
if (age > maxAge || age < minAge)
|
|
{
|
|
throw exception;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Validate a Given User Can Registere
|
|
/// </summary>
|
|
/// <param name="userSelectByParam">specifies user identifier</param>
|
|
/// <returns></returns>
|
|
private async Task ValidateCanRegister(
|
|
string userSelectByParam
|
|
)
|
|
{
|
|
//
|
|
// Validate Args ...
|
|
ValidationProvider.NotEmpty(userSelectByParam);
|
|
|
|
//
|
|
var canRegister = await CanRegister(userSelectByParam);
|
|
if (!canRegister)
|
|
{
|
|
XException.Unavailable.Throw();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Validate Password by Gicen Policies
|
|
/// </summary>
|
|
/// <param name="user">an instance of <see>XUser</see></param>
|
|
/// <param name="password">user's password</param>
|
|
/// <param name="exception">specify <see>Excetion</see> to thrown on failure</param>
|
|
/// <returns></returns>
|
|
private async Task ValidatePasswordPolicies(
|
|
XUser user,
|
|
string password,
|
|
Exception exception = null
|
|
)
|
|
{
|
|
//
|
|
// Validate Args ...
|
|
ValidationProvider.NotNull(user);
|
|
ValidationProvider.NotEmpty(password);
|
|
|
|
//
|
|
// Prepare Default Exception ...
|
|
if (exception == null)
|
|
{
|
|
exception = XException.InvalidPassword.ToException();
|
|
}
|
|
|
|
//
|
|
// Check new Password Validation ...
|
|
var passwordValidator = new PasswordValidator<XUser>();
|
|
var checkPasswordResult = await passwordValidator.ValidateAsync(UserManager, user, password);
|
|
if (!checkPasswordResult.Succeeded)
|
|
{
|
|
throw exception;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Validate a User for Dangerous Actions
|
|
/// </summary>
|
|
/// <param name="user">an instance of <see>XUser</see></param>
|
|
/// <param name="requester">specified which user request Action by an instance of <see>XUser</see></param>
|
|
/// <returns></returns>
|
|
private async Task ValidateUserForDangerousAction(
|
|
XUser user,
|
|
XUser requester
|
|
)
|
|
{
|
|
//
|
|
// Validate ARgs ...
|
|
ValidationProvider.NotNull(user, requester);
|
|
|
|
//
|
|
var requesterRoleNames = await GetRoleNamesAsync(requester.Id);
|
|
|
|
//
|
|
var isSame = user.Id == requester.Id;
|
|
var isRequesterAdmin = requesterRoleNames.Any(r => r.ToNormalString() == "admin");
|
|
if (!isSame && !isRequesterAdmin)
|
|
{
|
|
XException.NotAllowed.Throw();
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
//
|
|
#region Device Validators ...
|
|
/// <summary>
|
|
/// Validate a Device for Specified User
|
|
/// </summary>
|
|
/// <param name="userSelectByParam">specifies user identifier</param>
|
|
/// <param name="device">an instance of <see>XDevice</see></param>
|
|
/// <param name="exception">specify <see>Excetion</see> to thrown on failure</param>
|
|
/// <returns></returns>
|
|
private async Task ValidateUserAndDeviceRelation(
|
|
string userSelectByParam,
|
|
XDevice device,
|
|
Exception exception = null
|
|
)
|
|
{
|
|
//
|
|
ValidationProvider.NotEmpty(userSelectByParam);
|
|
ValidationProvider.NotNull(device);
|
|
|
|
//
|
|
if (exception == null)
|
|
{
|
|
exception = XException.InvalidDevice.ToException();
|
|
}
|
|
|
|
//
|
|
var user = await ValidateUserExistsAndRetrieve(
|
|
userSelectByParam,
|
|
containDetails: true);
|
|
|
|
//
|
|
var result = await IsDeviceRelateDToUser(
|
|
userSelectByParam,
|
|
device
|
|
);
|
|
if (!result)
|
|
{
|
|
throw exception;
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
//
|
|
#region Token Validators ...
|
|
/// <summary>
|
|
/// Validate a Token
|
|
/// </summary>
|
|
/// <param name="token">token string</param>
|
|
/// <param name="exception">specify <see>Excetion</see> to thrown on failure</param>
|
|
private void ValidateToken(
|
|
string token,
|
|
Exception exception = null
|
|
)
|
|
{
|
|
//
|
|
ValidationProvider.NotEmpty(token);
|
|
|
|
//
|
|
if (exception == null)
|
|
{
|
|
exception = XException.InvalidToken.ToException();
|
|
}
|
|
|
|
//
|
|
var result = IdentityHelper.ValidateToken(token);
|
|
if (!result)
|
|
{
|
|
throw exception;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Validate a Token is Specified to a Device and User
|
|
/// </summary>
|
|
/// <param name="device">an instance of <see>XDevice</see></param>
|
|
/// <param name="type">specifies Action type by a member of <see>XAction</see></param>
|
|
/// <param name="exception">specify <see>Excetion</see> to thrown on failure</param>
|
|
/// <returns>an instance of <see>XToken</see></returns>
|
|
private async Task<XToken> ValidateAndRetieveTokenByDeviceAndType(
|
|
XDevice device,
|
|
XAction type,
|
|
Exception exception = null
|
|
)
|
|
{
|
|
//
|
|
ValidationProvider.NotNull(device);
|
|
|
|
//
|
|
// Prepare Default Exception ...
|
|
if (exception == null)
|
|
{
|
|
exception = XException.NotFound.ToException();
|
|
}
|
|
|
|
//
|
|
var isExists = await IsTokenExistsByDeviceAndType(device, type);
|
|
if (!isExists)
|
|
{
|
|
throw exception;
|
|
}
|
|
|
|
//
|
|
var item = await GetTokenByDeviceAndType(device, type);
|
|
if (item == null)
|
|
{
|
|
throw exception;
|
|
}
|
|
|
|
//
|
|
return item;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Validate a Token By User and Specified Type
|
|
/// </summary>
|
|
/// <param name="userSelectByParam">specifies user identifier</param>
|
|
/// <param name="type">specifies Action type by a member of <see>XAction</see></param>
|
|
/// <param name="exception">specify <see>Excetion</see> to thrown on failure</param>
|
|
/// <returns>an instance of <see>XToken</see></returns>
|
|
private async Task<XToken> ValidateAndRetieveTokenByUserAndType(
|
|
string userSelectByParam,
|
|
XAction type,
|
|
Exception exception = null
|
|
)
|
|
{
|
|
//
|
|
ValidationProvider.NotEmpty(userSelectByParam);
|
|
|
|
//
|
|
// Prepare Default Exception ...
|
|
if (exception == null)
|
|
{
|
|
exception = XException.InvalidData.ToException();
|
|
}
|
|
|
|
//
|
|
var isExists = await IsTokenExistsByUserAndType(userSelectByParam, type);
|
|
if (!isExists)
|
|
{
|
|
throw exception;
|
|
}
|
|
|
|
//
|
|
var item = await GetTokenByUserAndType(userSelectByParam, type);
|
|
if (item == null)
|
|
{
|
|
throw exception;
|
|
}
|
|
|
|
//
|
|
return item;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Check Token Validation
|
|
/// </summary>
|
|
/// <param name="token">token string</param>
|
|
/// <returns>a boolean value</returns>
|
|
private bool IsValidToken(
|
|
string token
|
|
)
|
|
{
|
|
//
|
|
ValidationProvider.NotEmpty(token);
|
|
|
|
//
|
|
var result = IdentityHelper.ValidateToken(token);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Retrieve Action Request Based on Action Token
|
|
/// </summary>
|
|
/// <param name="token">token string</param>
|
|
/// <param name="exception">specify <see>Excetion</see> to thrown on failure</param>
|
|
/// <returns>an instance of <see>XActionRequestToken</see></returns>
|
|
private XActionRequestToken ValidateAndParseActionToken(
|
|
string token,
|
|
Exception exception = null
|
|
)
|
|
{
|
|
//
|
|
ValidationProvider.NotEmpty(token);
|
|
|
|
//
|
|
// Prepare Default Exception ...
|
|
if (exception == null)
|
|
{
|
|
exception = XException.InvalidToken.ToException();
|
|
}
|
|
|
|
//
|
|
ValidateToken(token);
|
|
|
|
//
|
|
var result = IdentityHelper.ParseActionRequestToken(token);
|
|
if (result == null)
|
|
{
|
|
throw exception;
|
|
}
|
|
|
|
//
|
|
ValidateActionRequest(result);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Parse Action Request from Token
|
|
/// </summary>
|
|
/// <param name="token">token string</param>
|
|
/// <returns>an instance of <see>XActionRequestToken</see></returns>
|
|
private XActionRequestToken ParseActionRequest(
|
|
string token
|
|
)
|
|
{
|
|
//
|
|
// Validate Args ...
|
|
ValidationProvider.NotEmpty(token);
|
|
|
|
//
|
|
var result = IdentityHelper
|
|
.ParseActionRequestToken(token);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Parse Action Hash by Corresponding Token
|
|
/// </summary>
|
|
/// <param name="hash">token hash string</param>
|
|
/// <returns>an instance of <see>XActionRequestToken</see></returns>
|
|
private async Task<XActionRequestToken> ParseActionHash(
|
|
string hash
|
|
)
|
|
{
|
|
//
|
|
ValidationProvider.NotEmpty(hash);
|
|
|
|
//
|
|
var isHashExists = await IsTokenExistsByHash(hash);
|
|
if (!isHashExists)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
//
|
|
var xToken = await GetTokenByHash(hash);
|
|
if (xToken == null)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
//
|
|
var isValidToken = IsValidToken(xToken.Token);
|
|
if (!isValidToken)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
//
|
|
var result = ParseActionRequest(xToken.Token);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Validate and Parse Action Hash
|
|
/// </summary>
|
|
/// <param name="hash">token hash string</param>
|
|
/// <param name="forceTokenValidation">specifies chack token Validation Parameter, default is true</param>
|
|
/// <param name="forceActionValidation">specifies check action Validations, default is true</param>
|
|
/// <param name="exception">specify <see>Excetion</see> to thrown on failure</param>
|
|
/// <returns>an instance of <see>XActionRequestToken</see></returns>
|
|
private async Task<XActionRequestToken> ValidateAndParseActionHash(
|
|
string hash,
|
|
bool forceTokenValidation = true,
|
|
bool forceActionValidation = true,
|
|
Exception exception = null
|
|
)
|
|
{
|
|
//
|
|
ValidationProvider.NotEmpty(hash);
|
|
|
|
//
|
|
// Prepare Default Exception ...
|
|
if (exception == null)
|
|
{
|
|
exception = XException.InvalidToken.ToException();
|
|
}
|
|
|
|
//
|
|
var token = await ValidateAndRetrieveRelatedToken(hash, forceTokenValidation);
|
|
|
|
//
|
|
var result = await ParseActionHash(hash);
|
|
if (result == null)
|
|
{
|
|
throw exception;
|
|
}
|
|
|
|
//
|
|
if (forceActionValidation)
|
|
{
|
|
ValidateActionRequest(result);
|
|
}
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Validate Action Request
|
|
/// </summary>
|
|
/// <param name="request">an instance of <see>XActionRequestToken</see></param>
|
|
/// <param name="exception">specify <see>Excetion</see> to thrown on failure</param>
|
|
private void ValidateActionRequest(
|
|
XActionRequestToken request,
|
|
Exception exception = null
|
|
)
|
|
{
|
|
//
|
|
ValidationProvider
|
|
.NotNull(request);
|
|
|
|
//
|
|
// Prepare Default Exception ...
|
|
if (exception == null)
|
|
{
|
|
exception = XException.InvalidData.ToException();
|
|
}
|
|
|
|
//
|
|
var isValid = IsValidActionRequest(request);
|
|
if (!isValid)
|
|
{
|
|
throw exception;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Check Action Request Validation
|
|
/// </summary>
|
|
/// <param name="request">an instance of <see>XActionRequestToken</see></param>
|
|
/// <returns>a boolean value</returns>
|
|
private bool IsValidActionRequest(
|
|
XActionRequestToken request
|
|
)
|
|
{
|
|
//
|
|
// Validate Args ...
|
|
if (request == null ||
|
|
Configuration.IdentitySecretKey.IsNullOrEmpty())
|
|
{
|
|
XException.InvalidArgs.Throw();
|
|
}
|
|
|
|
//
|
|
var result = request.Validate(Configuration.IdentitySecretKey);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Check Token and Act Based on it
|
|
/// </summary>
|
|
/// <param name="token">token string</param>
|
|
/// <param name="issueExcepion">specify <see>Excetion</see> to thrown on Issue failure</param>
|
|
/// <param name="exception">specify <see>Excetion</see> to thrown on failure</param>
|
|
/// <returns></returns>
|
|
private async Task ValidateAndHandleToken(
|
|
string token,
|
|
bool issueExcepion = false,
|
|
Exception exception = null
|
|
)
|
|
{
|
|
//
|
|
// Validate Args ...
|
|
ValidationProvider.NotEmpty(token);
|
|
|
|
//
|
|
// Prepare Default Exception ...
|
|
if (exception == null)
|
|
{
|
|
exception = XException.InvalidToken.ToException();
|
|
}
|
|
|
|
//
|
|
var isValidToken = IsValidToken(token);
|
|
|
|
//
|
|
if (!isValidToken)
|
|
{
|
|
//
|
|
await RemoveTokenByToken(token);
|
|
|
|
//
|
|
if (issueExcepion)
|
|
{
|
|
throw exception;
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Validate a Hash Exists or not
|
|
/// </summary>
|
|
/// <param name="hash">token hash string</param>
|
|
/// <param name="exception">specify <see>Excetion</see> to thrown on failure</param>
|
|
/// <returns></returns>
|
|
private async Task ValidateHashExists(
|
|
string hash,
|
|
Exception exception = null
|
|
)
|
|
{
|
|
//
|
|
// Validate Args ...
|
|
ValidationProvider.NotEmpty(hash);
|
|
|
|
//
|
|
// Prepare Default Exception ...
|
|
if (exception == null)
|
|
{
|
|
exception = XException.InvalidToken.ToException();
|
|
}
|
|
|
|
//
|
|
var isExists = await IsTokenExistsByHash(hash);
|
|
if (!isExists)
|
|
{
|
|
throw exception;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Validate a Token Exists
|
|
/// </summary>
|
|
/// <param name="token">token string</param>
|
|
/// <param name="exception">specify <see>Excetion</see> to thrown on failure</param>
|
|
/// <returns></returns>
|
|
private async Task ValidateTokenExists(
|
|
string token,
|
|
Exception exception = null
|
|
)
|
|
{
|
|
//
|
|
// Validate Args ...
|
|
ValidationProvider.NotEmpty(token);
|
|
|
|
//
|
|
// Prepare Default Exception ...
|
|
if (exception == null)
|
|
{
|
|
exception = XException.InvalidToken.ToException();
|
|
}
|
|
|
|
//
|
|
var isExists = await IsTokenExistsByToken(token);
|
|
if (!isExists)
|
|
{
|
|
throw exception;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Validate a Hash and Retrieve it's Corresponding Token
|
|
/// </summary>
|
|
/// <param name="hash">token hash string</param>
|
|
/// <param name="forceTokenValidation">specifies chack token Validation Parameter, default is true</param>
|
|
/// <param name="issueExcepion">specify <see>Excetion</see> to thrown on Issue failure</param>
|
|
/// <param name="exception">specify <see>Excetion</see> to thrown on failure</param>
|
|
/// <returns>token string</returns>
|
|
private async Task<string> ValidateAndRetrieveRelatedToken(
|
|
string hash,
|
|
bool forceTokenValidation = true,
|
|
bool issueExcepion = false,
|
|
Exception exception = null
|
|
)
|
|
{
|
|
//
|
|
// Validate Args ...
|
|
ValidationProvider.NotEmpty(hash);
|
|
|
|
//
|
|
// Prepare Default Exception ...
|
|
if (exception == null)
|
|
{
|
|
exception = XException.InvalidToken.ToException();
|
|
}
|
|
|
|
//
|
|
await ValidateHashExists(hash);
|
|
|
|
//
|
|
var token = await GetRelatedTokenByHash(hash);
|
|
if (token.IsNullOrEmpty())
|
|
{
|
|
throw exception;
|
|
}
|
|
|
|
//
|
|
if (forceTokenValidation)
|
|
{
|
|
await ValidateAndHandleToken(token, issueExcepion);
|
|
}
|
|
|
|
//
|
|
return token;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Validate a Token and Retrieve it's Corresponding Hash
|
|
/// </summary>
|
|
/// <param name="token">token string</param>
|
|
/// <param name="forceTokenValidation">specifies chack token Validation Parameter, default is true</param>
|
|
/// <param name="exception">specify <see>Excetion</see> to thrown on failure</param>
|
|
/// <returns>hash string</returns>
|
|
private async Task<string> ValidateAndRetrieveRelatedHash(
|
|
string token,
|
|
bool forceTokenValidation = true,
|
|
Exception exception = null
|
|
)
|
|
{
|
|
//
|
|
// Validate Args ...
|
|
ValidationProvider.NotEmpty(token);
|
|
|
|
//
|
|
// Prepare Default Exception ...
|
|
if (exception == null)
|
|
{
|
|
exception = XException.InvalidToken.ToException();
|
|
}
|
|
|
|
//
|
|
if (forceTokenValidation)
|
|
{
|
|
await ValidateAndHandleToken(token);
|
|
}
|
|
|
|
//
|
|
await ValidateTokenExists(token);
|
|
|
|
//
|
|
var hash = await GetRelatedHashByToken(token);
|
|
if (hash.IsNullOrEmpty())
|
|
{
|
|
throw exception;
|
|
}
|
|
|
|
//
|
|
return hash;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Validate Invitation Hash
|
|
/// </summary>
|
|
/// <param name="invitationHash">registration invitation hash string</param>
|
|
/// <param name="forceUserExists">thrown <see>Exception</see> if user Exists, default is true</param>
|
|
/// <returns></returns>
|
|
private async Task ValidateInvitationHash(
|
|
string invitationHash,
|
|
bool forceUserExists = true
|
|
)
|
|
{
|
|
//
|
|
// Validate Args ...
|
|
ValidationProvider.NotNull(Configuration);
|
|
|
|
//
|
|
if (Configuration.RegistrationJustWithInvite &&
|
|
invitationHash.IsNullOrEmpty())
|
|
{
|
|
XException.NotAllowed.Throw();
|
|
}
|
|
|
|
//
|
|
// Validate Invitation Token if it isn't null ...
|
|
if (!invitationHash.IsNullOrEmpty())
|
|
{
|
|
//
|
|
await ValidateHashExists(invitationHash);
|
|
|
|
//
|
|
// Token Validation also done in this task ...
|
|
var relatedToken = await ValidateAndRetrieveRelatedToken(invitationHash);
|
|
|
|
//
|
|
var inviteAction = ValidateAndParseActionToken(relatedToken);
|
|
ValidationProvider.NotNull(inviteAction);
|
|
|
|
//
|
|
var inviteUerSelectByParam = GetUserSelectByParam(inviteAction);
|
|
var inviteUserSelectBy = GetUserSelectByType(inviteUerSelectByParam);
|
|
if (inviteUserSelectBy != XUserSelectBy.Email)
|
|
{
|
|
XException.InvalidToken.Throw();
|
|
}
|
|
|
|
//
|
|
if (forceUserExists)
|
|
{
|
|
//
|
|
await ValidateUserNotExists(
|
|
inviteUerSelectByParam,
|
|
XException.EmailInUsed.ToException());
|
|
}
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
//
|
|
#region Profile Validators ...
|
|
/// <summary>
|
|
/// Validate a Request is Corresponding to Specific Device
|
|
/// </summary>
|
|
/// <param name="device">an instance of <see>XDevice</see></param>
|
|
/// <param name="requestDevice">specify requested device by an instance of <see>XDevice</see></param>
|
|
/// <param name="exception">specify <see>Excetion</see> to thrown on failure</param>
|
|
private void ValidateRequestAndGiveDevices(
|
|
XDevice device,
|
|
XDevice requestDevice,
|
|
Exception exception = null
|
|
)
|
|
{
|
|
//
|
|
// Validate Args ...
|
|
ValidationProvider.NotNull(device, requestDevice);
|
|
|
|
//
|
|
// Prepare Default Exception ...
|
|
if (exception == null)
|
|
{
|
|
exception = XException.InvalidDevice.ToException();
|
|
}
|
|
|
|
//
|
|
var isDevicesSame = device.IsSameAs(requestDevice);
|
|
|
|
//
|
|
if (!isDevicesSame)
|
|
{
|
|
throw exception;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Check Validation of a User SelectBy Param and Password
|
|
/// </summary>
|
|
/// <param name="userSelectByParam">specifies user identifier</param>
|
|
/// <param name="password">user's password</param>
|
|
/// <param name="checkCanLoginPolicies">check a user can log in in system or not, default is true</param>
|
|
/// <param name="exception">specify <see>Excetion</see> to thrown on failure</param>
|
|
/// <returns></returns>
|
|
private async Task ValidateUserAndPassword(
|
|
string userSelectByParam,
|
|
string password,
|
|
bool checkCanLoginPolicies = true,
|
|
Exception exception = null
|
|
)
|
|
{
|
|
//
|
|
// Validate Args ...
|
|
ValidationProvider.NotEmpty(
|
|
userSelectByParam,
|
|
password
|
|
);
|
|
|
|
//
|
|
// Prepare Default Exception ...
|
|
if (exception == null)
|
|
{
|
|
exception = XException.LoginFailed.ToException();
|
|
}
|
|
|
|
//
|
|
// Retrieve User and Validate User Exists ...
|
|
var user = await ValidateUserExistsAndRetrieve(
|
|
userSelectByParam,
|
|
containDetails: false,
|
|
checkCanLoginPolicies: checkCanLoginPolicies,
|
|
ignoreDisabledUser: false,
|
|
checkIsBanned: true,
|
|
exception: exception);
|
|
|
|
//
|
|
// Check Given Password Follows Policies ...
|
|
await ValidatePasswordPolicies(user, password, exception);
|
|
|
|
//
|
|
// Check Password for User ...
|
|
var checkPasswordResult = await UserManager.CheckPasswordAsync(user, password);
|
|
if (!checkPasswordResult)
|
|
{
|
|
throw exception;
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
//
|
|
#region Friendship Validators ...
|
|
/// <summary>
|
|
/// Validate Friendship Requested Before or not
|
|
/// </summary>
|
|
/// <param name="source">specifies a user to check as source by an instance of <see>XUser</see></param>
|
|
/// <param name="dest">specifies a user to check as dest by an instance of <see>XUser</see></param>
|
|
private void ValidateRequestBefore(
|
|
XUser source,
|
|
XUser dest
|
|
)
|
|
{
|
|
//
|
|
// Validate Args ...
|
|
ValidationProvider.NotNull(source, dest);
|
|
|
|
//
|
|
var isRequested = IsFollowRequested(source, dest);
|
|
if (!isRequested)
|
|
{
|
|
XException.NotFound.Throw();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Validate a Friendship Not Requested Before
|
|
/// </summary>
|
|
/// <param name="source">specifies a user to check as source by an instance of <see>XUser</see></param>
|
|
/// <param name="dest">specifies a user to check as dest by an instance of <see>XUser</see></param>
|
|
private void ValidateNotRequestBefore(
|
|
XUser source,
|
|
XUser dest
|
|
)
|
|
{
|
|
//
|
|
// Validate Args ...
|
|
ValidationProvider.NotNull(source, dest);
|
|
|
|
//
|
|
var isRequested = IsFollowRequested(source, dest);
|
|
if (isRequested)
|
|
{
|
|
XException.Duplicate.Throw();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Validate User Can Send Following Request to Dest User
|
|
/// </summary>
|
|
/// <param name="accepterUser">specifies a user to check as dest by an instance of <see>XUser</see></param>
|
|
/// <param name="requesterUser">specifies a user to check as source by an instance of <see>XUser</see></param>
|
|
private void ValidateFollowingRequest(
|
|
XUser accepterUser,
|
|
XUser requesterUser
|
|
)
|
|
{
|
|
//
|
|
// Validate Args ...
|
|
ValidationProvider.NotNull(accepterUser, requesterUser);
|
|
|
|
//
|
|
var isRequest = IsFollowRequested(requesterUser, accepterUser, XFriendshipState.Pending);
|
|
if (!isRequest)
|
|
{
|
|
XException.NotFound.Throw();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Validate a User can Accept a Following Request or Not
|
|
/// </summary>
|
|
/// <param name="accepterUser">specifies a user to check as dest by an instance of <see>XUser</see></param>
|
|
/// <param name="requesterUser">specifies a user to check as source by an instance of <see>XUser</see></param>
|
|
private void ValidateFollowingRequestForAccept(
|
|
XUser accepterUser,
|
|
XUser requesterUser
|
|
)
|
|
{
|
|
//
|
|
// Validate Args ...
|
|
ValidationProvider.NotNull(accepterUser, requesterUser);
|
|
|
|
//
|
|
var isRequestPending = IsFollowRequested(requesterUser, accepterUser, XFriendshipState.Pending);
|
|
var isRequestRejected = IsFollowRequested(requesterUser, accepterUser, XFriendshipState.Rejected);
|
|
if (!isRequestPending &&
|
|
!isRequestRejected)
|
|
{
|
|
XException.NotFound.Throw();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Validate a Request is not Passed for Same Users
|
|
/// </summary>
|
|
/// <param name="source">specifies a user to check as source by an instance of <see>XUser</see></param>
|
|
/// <param name="dest">specifies a user to check as dest by an instance of <see>XUser</see></param>
|
|
private void ValidateNotSameUsers(
|
|
XUser source,
|
|
XUser dest
|
|
)
|
|
{
|
|
//
|
|
// Validate ARgs ...
|
|
ValidationProvider.NotNull(source, dest);
|
|
|
|
//
|
|
var isSame = source.Id == dest.Id;
|
|
if (isSame)
|
|
{
|
|
XException.ActionFailed.Throw();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Check a user is in Followers of another
|
|
/// </summary>
|
|
/// <param name="source">specifies a user to check as source by an instance of <see>XUser</see></param>
|
|
/// <param name="dest">specifies a user to check as dest by an instance of <see>XUser</see></param>
|
|
private void ValidateIsFollowers(
|
|
XUser source,
|
|
XUser dest
|
|
)
|
|
{
|
|
//
|
|
// Validate Args ...
|
|
ValidationProvider.NotNull(source, dest);
|
|
|
|
//
|
|
ValidateNotSameUsers(source, dest);
|
|
ValidateRequestBefore(dest, source);
|
|
|
|
//
|
|
var isFollower = IsFollowRequested(dest, source, XFriendshipState.Accepted) ||
|
|
IsFollowRequested(dest, source, XFriendshipState.Blocked);
|
|
if (!isFollower)
|
|
{
|
|
XException.NotFound.Throw();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Check a user is in Followings of another
|
|
/// </summary>
|
|
/// <param name="source">specifies a user to check as source by an instance of <see>XUser</see></param>
|
|
/// <param name="dest">specifies a user to check as dest by an instance of <see>XUser</see></param>
|
|
private void ValidateIsFollowings(
|
|
XUser source,
|
|
XUser dest
|
|
)
|
|
{
|
|
//
|
|
// Validate Args ...
|
|
ValidationProvider.NotNull(source, dest);
|
|
|
|
//
|
|
ValidateNotSameUsers(source, dest);
|
|
ValidateRequestBefore(source, dest);
|
|
|
|
//
|
|
var isFollowing = IsFollowRequested(source, dest, XFriendshipState.Accepted);
|
|
if (!isFollowing)
|
|
{
|
|
XException.NotFound.Throw();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Validate a User Blocked another
|
|
/// </summary>
|
|
/// <param name="source">specifies a user to check as source by an instance of <see>XUser</see></param>
|
|
/// <param name="dest">specifies a user to check as dest by an instance of <see>XUser</see></param>
|
|
private void ValidateIsBlock(
|
|
XUser source,
|
|
XUser dest
|
|
)
|
|
{
|
|
//
|
|
// Validate Args ...
|
|
ValidationProvider.NotNull(source, dest);
|
|
|
|
//
|
|
ValidateNotSameUsers(source, dest);
|
|
ValidateRequestBefore(dest, source);
|
|
|
|
//
|
|
var isFollower = IsFollowRequested(dest, source, XFriendshipState.Blocked);
|
|
if (!isFollower)
|
|
{
|
|
XException.NotFound.Throw();
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
//
|
|
#region XActionRequest Validators ...
|
|
/// <summary>
|
|
/// Validate a Device for Request
|
|
/// </summary>
|
|
/// <param name="device">an instance of <see>XDevice</see></param>
|
|
/// <param name="requestDevice">specify requested device by an instance of <see>XDevice</see></param>
|
|
/// <param name="exception">specify <see>Excetion</see> to thrown on failure</param>
|
|
private void ValidateRequestAndGivenDevices(
|
|
XDevice device,
|
|
XDevice requestDevice,
|
|
Exception exception = null
|
|
)
|
|
{
|
|
//
|
|
// Validate Args ...
|
|
ValidationProvider.NotNull(device, requestDevice);
|
|
|
|
//
|
|
// Prepare Default Exception ...
|
|
if (exception == null)
|
|
{
|
|
exception = XException.InvalidDevice.ToException();
|
|
}
|
|
|
|
//
|
|
var isDevicesSame = device.IsSameAs(requestDevice);
|
|
|
|
//
|
|
if (!isDevicesSame)
|
|
{
|
|
throw exception;
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
//
|
|
#region XVerification Request ...
|
|
/// <summary>
|
|
/// Validate Verification Request Exists by Device
|
|
/// </summary>
|
|
/// <param name="device">an instance of <see>XDevice</see></param>
|
|
/// <param name="exception">specify <see>Excetion</see> to thrown on failure</param>
|
|
/// <returns></returns>
|
|
private async Task ValidateVerificationRequestExists(
|
|
XDevice device,
|
|
Exception exception = null
|
|
)
|
|
{
|
|
//
|
|
ValidationProvider.NotNull(device);
|
|
|
|
//
|
|
if (exception == null)
|
|
{
|
|
exception = XException.InvalidDevice.ToException();
|
|
}
|
|
|
|
//
|
|
var isExists = await IsDeviceRequestedForVerificationCodeBefor(device);
|
|
if (!isExists)
|
|
{
|
|
throw exception;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Validate Verification Request Exists by VerificationCode
|
|
/// </summary>
|
|
/// <param name="verificationCode">Confirm Verification Code</param>
|
|
/// <param name="exception">specify <see>Excetion</see> to thrown on failure</param>
|
|
/// <returns></returns>
|
|
private async Task ValidateVerificationRequestExists(
|
|
string verificationCode,
|
|
Exception exception = null
|
|
)
|
|
{
|
|
//
|
|
ValidationProvider.NotEmpty(verificationCode);
|
|
|
|
//
|
|
if (exception == null)
|
|
{
|
|
exception = XException.InvalidVerificationCode.ToException();
|
|
}
|
|
|
|
//
|
|
var isExists = await IsVerificationCodeRequested(verificationCode);
|
|
if (!isExists)
|
|
{
|
|
throw exception;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Validate and Retrive Verification Request for Given Device
|
|
/// </summary>
|
|
/// <param name="device">an instance of <see>XDevice</see></param>
|
|
/// <param name="exception">specify <see>Excetion</see> to thrown on failure</param>
|
|
/// <returns>an instance of <see>XVerificationRequest</see></returns>
|
|
private async Task<XVerificationRequest> ValidateAndRetrieveVerificationRequest(
|
|
XDevice device,
|
|
Exception exception = null
|
|
)
|
|
{
|
|
//
|
|
ValidationProvider.NotNull(device);
|
|
|
|
//
|
|
if (exception == null)
|
|
{
|
|
exception = XException.InvalidDevice.ToException();
|
|
}
|
|
|
|
//
|
|
await ValidateVerificationRequestExists(device);
|
|
|
|
//
|
|
var result = await GetVerificationRequest(device);
|
|
if (result == null)
|
|
{
|
|
throw exception;
|
|
}
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Validate and Retrieve Verification Request for Given VerificationCode
|
|
/// </summary>
|
|
/// <param name="verificationCode">Confirm Verification Code</param>
|
|
/// <param name="exception">specify <see>Excetion</see> to thrown on failure</param>
|
|
/// <returns>an instance of <see>XVerificationRequest</see></returns>
|
|
private async Task<XVerificationRequest> ValidateAndRetrieveVerificationRequest(
|
|
string verificationCode,
|
|
Exception exception = null
|
|
)
|
|
{
|
|
//
|
|
ValidationProvider.NotEmpty(verificationCode);
|
|
|
|
//
|
|
if (exception == null)
|
|
{
|
|
exception = XException.InvalidVerificationCode.ToException();
|
|
}
|
|
|
|
//
|
|
await ValidateVerificationRequestExists(verificationCode);
|
|
|
|
//
|
|
var result = await GetVerificationRequest(verificationCode);
|
|
if (result == null)
|
|
{
|
|
throw exception;
|
|
}
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Handle Prepare Verification Request
|
|
/// </summary>
|
|
/// <param name="verificationCode">Confirm Verification Code</param>
|
|
/// <param name="request">an instance of <see>XActionRequestToken</see></param>
|
|
/// <returns>an instance of <see>XVerificationRequest</see></returns>
|
|
private async Task<XVerificationRequest> HandleVerificationRequestPreparation(
|
|
string verificationCode,
|
|
XActionRequestToken request
|
|
)
|
|
{
|
|
//
|
|
ValidationProvider
|
|
.NotEmpty(verificationCode);
|
|
|
|
//
|
|
// Check Action Request Validation ...
|
|
ValidateActionRequest(request);
|
|
|
|
//
|
|
var device = request.Context.Device;
|
|
|
|
//
|
|
// Check is Device Requested Before or not ...
|
|
XVerificationRequest xVerificationRequest = null;
|
|
var isDeviceRequested = await IsDeviceRequestedForVerificationCodeBefor(device);
|
|
if (!isDeviceRequested)
|
|
{
|
|
//
|
|
xVerificationRequest = new XVerificationRequest
|
|
{
|
|
VerificationCode = verificationCode,
|
|
NumberOfTries = 1,
|
|
LastTryOn = DateTime.UtcNow,
|
|
Device = device,
|
|
Request = request
|
|
};
|
|
|
|
//
|
|
await AddVerificationRequest(xVerificationRequest);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Get Verification Request ...
|
|
xVerificationRequest = await ValidateAndRetrieveVerificationRequest(device);
|
|
}
|
|
|
|
//
|
|
return xVerificationRequest;
|
|
}
|
|
#endregion
|
|
}
|
|
} |