Initial ...
This commit is contained in:
@@ -0,0 +1,224 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using xCommons.Extensions;
|
||||
using xIdentityModels.Dtos;
|
||||
|
||||
namespace xIds.Providers
|
||||
{
|
||||
public partial class XIdentityManager
|
||||
{
|
||||
#region Admin Actions ...
|
||||
/// <summary>
|
||||
/// Retrieve All UnBanned Users Identifiers
|
||||
/// </summary>
|
||||
/// <param name="userSelectByParam">caller user identifier</param>
|
||||
/// <returns>a collection of user identifiers</returns>
|
||||
public async Task<ICollection<string>> GetAllUnbanned(
|
||||
ICollection<string> userSelectByParam
|
||||
)
|
||||
{
|
||||
//
|
||||
var result = new List<string>();
|
||||
if (!userSelectByParam.HasChild())
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
//
|
||||
foreach (var uParam in userSelectByParam)
|
||||
{
|
||||
//
|
||||
var destUser = await ValidateUserExistsAndRetrieve(uParam, checkIsBanned: false);
|
||||
if (!destUser.IsBanned)
|
||||
{
|
||||
result.Add(uParam);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get All Banned Users Identifiers
|
||||
/// </summary>
|
||||
/// <param name="userSelectByParam">caller user identifier</param>
|
||||
/// <returns>a collection of user identifiers</returns>
|
||||
public async Task<ICollection<string>> GetAllBanned(
|
||||
ICollection<string> userSelectByParam
|
||||
)
|
||||
{
|
||||
//
|
||||
var result = new List<string>();
|
||||
if (!userSelectByParam.HasChild())
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
//
|
||||
foreach (var uParam in userSelectByParam)
|
||||
{
|
||||
//
|
||||
var destUser = await ValidateUserExistsAndRetrieve(uParam, checkIsBanned: false);
|
||||
if (destUser.IsBanned)
|
||||
{
|
||||
result.Add(uParam);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ban a Collection of Users
|
||||
/// </summary>
|
||||
/// <param name="userSelectByParam">caller user identifier</param>
|
||||
/// <param name="model">an instance of <see>XUserNameIdRequest</see> which represents user identifier list to Ban</param>
|
||||
/// <returns>a collection of user identifiers</returns>
|
||||
public async Task<ICollection<string>> Ban(
|
||||
string userSelectByParam,
|
||||
XUserNameIdRequest model
|
||||
)
|
||||
{
|
||||
//
|
||||
// Validate Args ...
|
||||
ValidationProvider.NotEmpty(userSelectByParam);
|
||||
ValidationProvider.NotNull(model);
|
||||
ValidationProvider.NotZeroChilds(model.Ids);
|
||||
|
||||
//
|
||||
var user = await ValidateUserExistsAndRetrieve(
|
||||
userSelectByParam,
|
||||
forceAdmin: true,
|
||||
checkIsBanned: true,
|
||||
containDetails: false,
|
||||
ignoreDisabledUser: false,
|
||||
checkCanLoginPolicies: true
|
||||
);
|
||||
|
||||
//
|
||||
var result = new List<string>();
|
||||
foreach (var uParam in model.Ids)
|
||||
{
|
||||
//
|
||||
var destUser = await ValidateUserExistsAndRetrieve(uParam, checkIsBanned: false);
|
||||
|
||||
//
|
||||
var isBanned = destUser.IsBanned;
|
||||
if (!isBanned)
|
||||
{
|
||||
//
|
||||
destUser.IsBanned = true;
|
||||
|
||||
//
|
||||
await UpdateUserAsync(
|
||||
user,
|
||||
checkCanLoginPolicies: false,
|
||||
checkIsBanned: false
|
||||
);
|
||||
|
||||
//
|
||||
result.Add(destUser.Id);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// UnBan a Collection of Users
|
||||
/// </summary>
|
||||
/// <param name="userSelectByParam">caller user identifier</param>
|
||||
/// <param name="model">an instance of <see>XUserNameIdRequest</see> which represents user identifier list to Ban</param>
|
||||
/// <returns>a collection of user identifiers</returns>
|
||||
public async Task<ICollection<string>> UnBan(
|
||||
string userSelectByParam,
|
||||
XUserNameIdRequest model
|
||||
)
|
||||
{
|
||||
//
|
||||
// Validate Args ...
|
||||
ValidationProvider.NotEmpty(userSelectByParam);
|
||||
ValidationProvider.NotNull(model);
|
||||
ValidationProvider.NotZeroChilds(model.Ids);
|
||||
|
||||
//
|
||||
var user = await ValidateUserExistsAndRetrieve(
|
||||
userSelectByParam,
|
||||
forceAdmin: true,
|
||||
checkIsBanned: true,
|
||||
containDetails: false,
|
||||
ignoreDisabledUser: false,
|
||||
checkCanLoginPolicies: true
|
||||
);
|
||||
|
||||
//
|
||||
var result = new List<string>();
|
||||
foreach (var uParam in model.Ids)
|
||||
{
|
||||
//
|
||||
var destUser = await ValidateUserExistsAndRetrieve(uParam, checkIsBanned: false);
|
||||
|
||||
//
|
||||
var isBanned = destUser.IsBanned;
|
||||
if (isBanned)
|
||||
{
|
||||
//
|
||||
destUser.IsBanned = false;
|
||||
|
||||
//
|
||||
await UpdateUserAsync(
|
||||
user,
|
||||
checkCanLoginPolicies: false,
|
||||
checkIsBanned: false
|
||||
);
|
||||
|
||||
//
|
||||
result.Add(destUser.Id);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Detrmines a User is Banned or not
|
||||
/// </summary>
|
||||
/// <param name="userSelectByParam">caller user identifier</param>
|
||||
/// <param name="destUserSelectByParam">destination user identifier which checked is banned or not</param>
|
||||
/// <returns>a boolean value which represent user banned or not</returns>
|
||||
public async Task<bool> IsBanned(
|
||||
string userSelectByParam,
|
||||
string destUserSelectByParam
|
||||
)
|
||||
{
|
||||
//
|
||||
// Validate Args ...
|
||||
ValidationProvider.NotEmpty(
|
||||
userSelectByParam,
|
||||
destUserSelectByParam
|
||||
);
|
||||
|
||||
//
|
||||
var user = await ValidateUserExistsAndRetrieve(
|
||||
userSelectByParam,
|
||||
forceAdmin: false,
|
||||
checkIsBanned: true,
|
||||
containDetails: false,
|
||||
ignoreDisabledUser: false,
|
||||
checkCanLoginPolicies: true
|
||||
);
|
||||
var destUser = await ValidateUserExistsAndRetrieve(destUserSelectByParam, checkIsBanned: false);
|
||||
|
||||
//
|
||||
var result = destUser.IsBanned;
|
||||
|
||||
//
|
||||
return result;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,481 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using xCommons.Extensions;
|
||||
using xExceptions.Constants;
|
||||
using xIdentityModels;
|
||||
using xIdentityModels.Extensions;
|
||||
using xIdentityModels.Models;
|
||||
using xIdentityModels.Navigations;
|
||||
using xIds.Extensions;
|
||||
|
||||
namespace xIds.Providers
|
||||
{
|
||||
public partial class XIdentityManager
|
||||
{
|
||||
//
|
||||
#region Device Actions ...
|
||||
/// <summary>
|
||||
/// Check a Device Exists or Not
|
||||
/// </summary>
|
||||
/// <param name="device">an instance of <see>XDevice</see></param>
|
||||
/// <returns>a boolean value</returns>
|
||||
public async Task<bool> IsDeviceExistsAsync(
|
||||
XDevice device
|
||||
)
|
||||
{
|
||||
return await DbContext.Devices
|
||||
.AnyAsync(d => d.IsSameAs(device));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add new Device
|
||||
/// </summary>
|
||||
/// <param name="device">an instance of <see>XDevice</see></param>
|
||||
/// <returns>an instance of <see>XDevice</see></returns>
|
||||
public async Task<XDevice> AddDeviceAsync(
|
||||
XDevice device
|
||||
)
|
||||
{
|
||||
//
|
||||
var isDeviceExists = await IsDeviceExistsAsync(device);
|
||||
if (isDeviceExists)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
//
|
||||
await DbContext.Devices
|
||||
.AddAsync(device);
|
||||
|
||||
await DbContext.SaveChangesAsync();
|
||||
|
||||
return device;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a Device
|
||||
/// </summary>
|
||||
/// <param name="device">an instance of <see>XDevice</see></param>
|
||||
/// <returns>an instance of <see>XDevice</see></returns>
|
||||
public Task<XDevice> GetDeviceAsync(
|
||||
XDevice device
|
||||
)
|
||||
{
|
||||
//
|
||||
return DbContext.Devices
|
||||
.FirstOrDefaultAsync(d =>
|
||||
d.IsSameAs(device));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieve a List of User Related Devices
|
||||
/// </summary>
|
||||
/// <param name="userSelectByParam">user identifier</param>
|
||||
/// <param name="checkCanLoginPolicies">check a user can log in in system or not</param>
|
||||
/// <param name="checkIsBanned">check user is banned or not</param>
|
||||
/// <returns>a collection of <see>XDevice</see> instances which related to user</returns>
|
||||
public async Task<ICollection<XDevice>> GetUserDevices(
|
||||
string userSelectByParam,
|
||||
bool checkCanLoginPolicies = true,
|
||||
bool checkIsBanned = true
|
||||
)
|
||||
{
|
||||
//
|
||||
// Validate ARgs ...
|
||||
ValidationProvider.NotEmpty(userSelectByParam);
|
||||
|
||||
//
|
||||
// Validate And Retrieve User ...
|
||||
var user = await ValidateUserExistsAndRetrieve(
|
||||
userSelectByParam,
|
||||
containDetails: true,
|
||||
checkCanLoginPolicies: checkCanLoginPolicies,
|
||||
checkIsBanned: checkIsBanned
|
||||
);
|
||||
|
||||
//
|
||||
// Check User Devices ...
|
||||
if (!user.Devices.HasChild())
|
||||
{
|
||||
return new List<XDevice>();
|
||||
}
|
||||
|
||||
//
|
||||
return user.Devices;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines a Device is Exists in User's Devices or not
|
||||
/// </summary>
|
||||
/// <param name="user">an instance of <see>XUser</see></param>
|
||||
/// <param name="device">an instance of <see>XDevice</see></param>
|
||||
/// <returns>a boolean value</returns>
|
||||
public bool IsDeviceRelateDToUser(
|
||||
XUser user,
|
||||
XDevice device
|
||||
)
|
||||
{
|
||||
if (!user.Devices.HasChild())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
//
|
||||
var result = user.Devices.Any(d => d.IsSameAs(device));
|
||||
|
||||
//
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines a Device is Exists in User's Devices or not
|
||||
/// </summary>
|
||||
/// <param name="userSelectByParam">user identifier</param>
|
||||
/// <param name="device">an instance of <see>XDevice</see></param>
|
||||
/// <param name="checkCanLoginPolicies">check a user can log in in system or not</param>
|
||||
/// <param name="checkIsBanned">check user is banned or not</param>
|
||||
/// <returns>a boolean value</returns>
|
||||
public async Task<bool> IsDeviceRelateDToUser(
|
||||
string userSelectByParam,
|
||||
XDevice device,
|
||||
bool checkCanLoginPolicies = true,
|
||||
bool checkIsBanned = true
|
||||
)
|
||||
{
|
||||
//
|
||||
// Validate Args ...
|
||||
ValidationProvider.NotEmpty(userSelectByParam);
|
||||
ValidationProvider.NotNull(device);
|
||||
|
||||
//
|
||||
var userDevices = await GetUserDevices(
|
||||
userSelectByParam,
|
||||
checkCanLoginPolicies: checkCanLoginPolicies,
|
||||
checkIsBanned: checkIsBanned
|
||||
);
|
||||
if (!userDevices.HasChild())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
//
|
||||
var result = userDevices.Any(d => d.IsSameAs(device));
|
||||
|
||||
//
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add a Device to a User Related Devices
|
||||
/// </summary>
|
||||
/// <param name="user">an instance of <see>XUser</see></param>
|
||||
/// <param name="device">an instance of <see>XDevice</see></param>
|
||||
/// <returns>a boolean value</returns>
|
||||
public async Task<bool> AddUserRelatedDevice(
|
||||
XUser user,
|
||||
XDevice device
|
||||
)
|
||||
{
|
||||
//
|
||||
var isExists = user.Devices.Any(d =>
|
||||
d.IsSameAs(device));
|
||||
if (isExists)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
//
|
||||
user.Devices.Add(device);
|
||||
try
|
||||
{
|
||||
var result = await UpdateUserAsync(user);
|
||||
return result.Succeeded;
|
||||
}
|
||||
catch
|
||||
{
|
||||
throw XException.ActionFailed.ToException();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add a Device to a User
|
||||
/// </summary>
|
||||
/// <param name="userSelectByParam">user identifier</param>
|
||||
/// <param name="device">an instance of <see>XDevice</see></param>
|
||||
/// <param name="checkCanLoginPolicies">check a user can log in in system or not</param>
|
||||
/// <param name="checkIsBanned">check user is banned or not</param>
|
||||
/// <returns>a boolean value</returns>
|
||||
public async Task<bool> AddUserRelatedDevice(
|
||||
string userSelectByParam,
|
||||
XDevice device,
|
||||
bool checkCanLoginPolicies = true,
|
||||
bool checkIsBanned = true
|
||||
)
|
||||
{
|
||||
//
|
||||
// Validate Args ...
|
||||
ValidationProvider.NotEmpty(userSelectByParam);
|
||||
ValidationProvider.NotNull(device);
|
||||
|
||||
//
|
||||
// Check Device Related ...
|
||||
var isDeviceRelatedToUser = await IsDeviceRelateDToUser(
|
||||
userSelectByParam,
|
||||
device,
|
||||
checkCanLoginPolicies: checkCanLoginPolicies,
|
||||
checkIsBanned: checkIsBanned);
|
||||
if (isDeviceRelatedToUser)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
//
|
||||
// Retrieve User ...
|
||||
var user = await ValidateUserExistsAndRetrieve(
|
||||
userSelectByParam,
|
||||
containDetails: true,
|
||||
checkCanLoginPolicies: checkCanLoginPolicies,
|
||||
checkIsBanned: checkIsBanned
|
||||
);
|
||||
|
||||
//
|
||||
// Add User Device ...
|
||||
user.Devices.Add(device);
|
||||
|
||||
//
|
||||
var result = await UpdateUserAsync(
|
||||
user,
|
||||
checkCanLoginPolicies: false,
|
||||
checkIsBanned: false
|
||||
);
|
||||
|
||||
//
|
||||
return result.Succeeded;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove a Device from a User Related Devices List
|
||||
/// </summary>
|
||||
/// <param name="user">an instance of <see>XUser</see></param>
|
||||
/// <param name="device">an instance of <see>XDevice</see></param>
|
||||
/// <returns>a boolean value</returns>
|
||||
public async Task<bool> RemoveUserRelatedDevice(
|
||||
XUser user,
|
||||
XDevice device
|
||||
)
|
||||
{
|
||||
//
|
||||
var isRelated = IsDeviceRelateDToUser(user, device);
|
||||
if (!isRelated)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
//
|
||||
var instanse = user.Devices
|
||||
.FirstOrDefault(d =>
|
||||
d.IsSameAs(device));
|
||||
|
||||
//
|
||||
user.Devices.Remove(instanse);
|
||||
|
||||
//
|
||||
var result = await UpdateUserAsync(
|
||||
user,
|
||||
checkCanLoginPolicies: false,
|
||||
checkIsBanned: false
|
||||
);
|
||||
|
||||
//
|
||||
return result.Succeeded;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove a Device from a User Devices
|
||||
/// </summary>
|
||||
/// <param name="userSelectByParam">user identifier</param>
|
||||
/// <param name="device">an instance of <see>XDevice</see></param>
|
||||
/// <param name="checkCanLoginPolicies">check a user can log in in system or not</param>
|
||||
/// <param name="checkIsBanned">check user is banned or not</param>
|
||||
/// <returns>a boolean value</returns>
|
||||
public async Task<bool> RemoveUserRelatedDevice(
|
||||
string userSelectByParam,
|
||||
XDevice device,
|
||||
bool checkCanLoginPolicies = true,
|
||||
bool checkIsBanned = true
|
||||
)
|
||||
{
|
||||
//
|
||||
// Validate Args ...
|
||||
ValidationProvider.NotEmpty(userSelectByParam);
|
||||
ValidationProvider.NotNull(device);
|
||||
|
||||
//
|
||||
// Validate user exists and Retrieve it based on userselectbyparam ...
|
||||
var user = await ValidateUserExistsAndRetrieve(
|
||||
userSelectByParam,
|
||||
containDetails: true,
|
||||
checkCanLoginPolicies: checkCanLoginPolicies,
|
||||
checkIsBanned: checkIsBanned
|
||||
);
|
||||
|
||||
//
|
||||
return await RemoveUserRelatedDevice(
|
||||
user,
|
||||
device
|
||||
);
|
||||
}
|
||||
#endregion
|
||||
|
||||
//
|
||||
#region Banned Device Actions ...
|
||||
/// <summary>
|
||||
/// Check a Device is exists in Banned Devices or not
|
||||
/// </summary>
|
||||
/// <param name="device">an instance of <see>XDevice</see></param>
|
||||
/// <returns>a boolean value</returns>
|
||||
public async Task<bool> IsBannedDeviceExists(
|
||||
XDevice device
|
||||
)
|
||||
{
|
||||
//
|
||||
var result = false;
|
||||
var bannedDeviceEnumerable = DbContext.BannedDevices.AsAsyncEnumerable();
|
||||
await
|
||||
foreach (var bannedDevice in bannedDeviceEnumerable)
|
||||
{
|
||||
//
|
||||
if (bannedDevice.Device.IsSameAs(device))
|
||||
{
|
||||
//
|
||||
result = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Find First Banned Device
|
||||
/// </summary>
|
||||
/// <param name="device">an instance of <see>XDevice</see></param>
|
||||
/// <returns>an instance of <see>XDevice</see></returns>
|
||||
public async Task<XBannedDevice> BannedDeviceFindOne(
|
||||
XDevice device
|
||||
)
|
||||
{
|
||||
//
|
||||
var result = await DbContext.BannedDevices
|
||||
.FirstOrDefaultAsync(bd =>
|
||||
bd.Device.IsSameAs(device)
|
||||
);
|
||||
|
||||
//
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove Banned Device
|
||||
/// </summary>
|
||||
/// <param name="model">an instance of <see>XBannedDevice</see></param>
|
||||
/// <returns></returns>
|
||||
public async Task BannedDeviceRemove(
|
||||
XBannedDevice model
|
||||
)
|
||||
{
|
||||
//
|
||||
var isExists = await IsBannedDeviceExists(model.Device);
|
||||
if (!isExists)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
//
|
||||
var item = await BannedDeviceFindOne(model.Device);
|
||||
if (item == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
//
|
||||
DbContext.BannedDevices.Remove(item);
|
||||
await DbContext.SaveChangesAsync();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check a Device is Banned or not
|
||||
/// </summary>
|
||||
/// <param name="device">an instance of <see>XDevice</see></param>
|
||||
/// <returns>a boolean value</returns>
|
||||
private async Task<bool> IsDeviceBanned(
|
||||
XDevice device
|
||||
)
|
||||
{
|
||||
//
|
||||
var result = await IsBannedDeviceExists(device);
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieve Specific Banned Device
|
||||
/// </summary>
|
||||
/// <param name="device">an instance of <see>XDevice</see></param>
|
||||
/// <returns>an instance of <see>XBannedDevice</see></returns>
|
||||
private async Task<XBannedDevice> GetBannedDevice(
|
||||
XDevice device
|
||||
)
|
||||
{
|
||||
//
|
||||
var result = await BannedDeviceFindOne(device);
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Is a Banned Device passed Banning Time
|
||||
/// </summary>
|
||||
/// <param name="bannedDevice">an instance of <see>XBannedDevice</see></param>
|
||||
/// <returns>a boolean value</returns>
|
||||
private bool IsDelayTimePassed(
|
||||
XBannedDevice bannedDevice
|
||||
)
|
||||
{
|
||||
//
|
||||
// Validate Args ...
|
||||
if (bannedDevice == null ||
|
||||
Configuration == null)
|
||||
{
|
||||
XException.InvalidArgs.Throw();
|
||||
}
|
||||
|
||||
//
|
||||
var isPassed = bannedDevice
|
||||
.IsDelayTimePassed(Configuration.BannedDeviceTimeout);
|
||||
|
||||
//
|
||||
return isPassed;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieve Passed Time from Banning Time of specific Device
|
||||
/// </summary>
|
||||
/// <param name="bannedDevice">an instance of <see>XBannedDevice</see></param>
|
||||
/// <returns>an instance of <see>DateTime</see> which represent Passed Time</returns>
|
||||
private DateTime GetPassedTime(
|
||||
XBannedDevice bannedDevice
|
||||
)
|
||||
{
|
||||
//
|
||||
var result = bannedDevice.BannedOn
|
||||
.AddSeconds(Configuration.BannedDeviceTimeout);
|
||||
|
||||
//
|
||||
return result;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,342 @@
|
||||
using System;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using IdentityModel.Client;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using xCommons.Extensions;
|
||||
using xExceptions.Constants;
|
||||
using xExceptions.Models;
|
||||
using xIdentityModels.Extensions;
|
||||
using xIdentityModels.Models;
|
||||
using xIds.Extensions;
|
||||
using static IdentityModel.OidcConstants;
|
||||
|
||||
namespace xIds.Providers
|
||||
{
|
||||
public partial class XIdentityManager
|
||||
{
|
||||
//
|
||||
#region Identity Actions ...
|
||||
/// <summary>
|
||||
/// Request for Discovery Document
|
||||
/// </summary>
|
||||
/// <returns>an instance of <see>DiscoveryDocumentResponse</see></returns>
|
||||
public async Task<DiscoveryDocumentResponse> RequestDiscoveryDocument()
|
||||
{
|
||||
//
|
||||
var httpClient = GetHttpClient();
|
||||
var result = httpClient
|
||||
.GetDiscoveryDocumentAsync(IdentityResourceConfiguration.Authority)
|
||||
.ContinueWith(docTask =>
|
||||
{
|
||||
//
|
||||
httpClient.Dispose();
|
||||
return docTask.Result;
|
||||
});
|
||||
|
||||
//
|
||||
return await result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Request AccessToken for Specific XApiScope
|
||||
/// </summary>
|
||||
/// <param name="scope">a member of <see>XApiScope</see></param>
|
||||
/// <returns>an instance of <see>TokenResponse</see></returns>
|
||||
public async Task<IdentityModel.Client.TokenResponse> RequestScopeAccessToken(
|
||||
string scope
|
||||
)
|
||||
{
|
||||
//
|
||||
// Validate Args ...
|
||||
if (scope.IsNullOrEmpty())
|
||||
{
|
||||
XException.InvalidArgs.Throw();
|
||||
}
|
||||
|
||||
//
|
||||
// Retrieve Disco Doc ...
|
||||
var discoDoc = await RequestDiscoveryDocument();
|
||||
if (discoDoc.IsError)
|
||||
{
|
||||
XException.ActionFailed.Throw();
|
||||
}
|
||||
|
||||
//
|
||||
var request = new ClientCredentialsTokenRequest
|
||||
{
|
||||
Address = discoDoc.TokenEndpoint,
|
||||
GrantType = GrantTypes.ClientCredentials,
|
||||
ClientId = IdentityResourceConfiguration.ClientId,
|
||||
ClientSecret = IdentityResourceConfiguration.ClientSecret,
|
||||
Scope = scope
|
||||
};
|
||||
|
||||
//
|
||||
var httpClient = GetHttpClient();
|
||||
var result = await httpClient
|
||||
.RequestClientCredentialsTokenAsync(request)
|
||||
.ContinueWith(response =>
|
||||
{
|
||||
//
|
||||
httpClient.Dispose();
|
||||
return response.Result;
|
||||
});
|
||||
|
||||
//
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Authenticate a User
|
||||
/// </summary>
|
||||
/// <param name="model">an instance of <see>XLoginRequest</see> class which represent Authentication requirements</param>
|
||||
/// <returns>an instance of <see>TokenResponse</see></returns>
|
||||
public async Task<IdentityModel.Client.TokenResponse> Authenticate(
|
||||
XLoginRequest model
|
||||
)
|
||||
{
|
||||
//
|
||||
// Validate Args ...
|
||||
ValidationProvider
|
||||
.GroupValidationBuilder()
|
||||
.AddNotNull(model)
|
||||
.AddNotEmpty(
|
||||
model.UserSelectBy,
|
||||
model.Password
|
||||
)
|
||||
.ValidateGroup();
|
||||
|
||||
//
|
||||
// Retrieve Disco Doc ...
|
||||
var discoDoc = await RequestDiscoveryDocument();
|
||||
if (discoDoc.IsError)
|
||||
{
|
||||
XException.ActionFailed.Throw();
|
||||
}
|
||||
|
||||
//
|
||||
// Create Request ...
|
||||
var request = new PasswordTokenRequest
|
||||
{
|
||||
//
|
||||
Address = discoDoc.TokenEndpoint,
|
||||
|
||||
//
|
||||
GrantType = GrantTypes.ClientCredentials,
|
||||
ClientId = IdentityResourceConfiguration.ClientId,
|
||||
ClientSecret = IdentityResourceConfiguration.ClientSecret,
|
||||
|
||||
//
|
||||
UserName = model.UserSelectBy,
|
||||
Password = model.Password,
|
||||
|
||||
//
|
||||
// Pass Device to Request ...
|
||||
Parameters = { { "force", false.ToJSON () }
|
||||
}
|
||||
};
|
||||
|
||||
//
|
||||
var httpClient = GetHttpClient();
|
||||
var result = await httpClient
|
||||
.RequestPasswordTokenAsync(request)
|
||||
.ContinueWith(response =>
|
||||
{
|
||||
//
|
||||
httpClient.Dispose();
|
||||
return response.Result;
|
||||
});
|
||||
|
||||
//
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Do Login based on XLoginRequest
|
||||
/// </summary>
|
||||
/// <param name="model">an instance of <see>XLoginRequest</see> class which represent Authentication requirements</param>
|
||||
/// <returns>an instance of <see>XLoginResponse</see></returns>
|
||||
public async Task<XLoginResponse> Login(
|
||||
XLoginRequest model
|
||||
)
|
||||
{
|
||||
//
|
||||
// Validate Args ...
|
||||
ValidationProvider
|
||||
.GroupValidationBuilder()
|
||||
.AddNotNull(model, model.Device)
|
||||
.AddNotEmpty(
|
||||
model.UserSelectBy,
|
||||
model.Password
|
||||
)
|
||||
.ValidateGroup();
|
||||
|
||||
//
|
||||
// Get Token Response ...
|
||||
//
|
||||
// Retrieve Disco Doc ...
|
||||
var discoDoc = await RequestDiscoveryDocument();
|
||||
Logger.LogInformation($"discoDoc: {discoDoc.ToJSON()}");
|
||||
|
||||
if (discoDoc.IsError)
|
||||
{
|
||||
XException.ActionFailed.Throw();
|
||||
}
|
||||
|
||||
//
|
||||
// Create Request ...
|
||||
var request = new PasswordTokenRequest
|
||||
{
|
||||
//
|
||||
Address = discoDoc.TokenEndpoint,
|
||||
|
||||
//
|
||||
GrantType = GrantTypes.ClientCredentials,
|
||||
ClientId = IdentityResourceConfiguration.ClientId,
|
||||
ClientSecret = IdentityResourceConfiguration.ClientSecret,
|
||||
|
||||
//
|
||||
UserName = model.UserSelectBy,
|
||||
Password = model.Password,
|
||||
|
||||
//
|
||||
// Pass Device to Request ...
|
||||
Parameters = { { "force", false.ToJSON () },
|
||||
{ "device", model.Device.ToJSON () },
|
||||
{ "language", model.Language }
|
||||
}
|
||||
};
|
||||
|
||||
//
|
||||
var httpClient = GetHttpClient();
|
||||
var authResponse = await httpClient
|
||||
.RequestPasswordTokenAsync(request)
|
||||
.ContinueWith(response =>
|
||||
{
|
||||
//
|
||||
httpClient.Dispose();
|
||||
return response.Result;
|
||||
});
|
||||
|
||||
//
|
||||
// Check If Response Has Error ...
|
||||
Logger.LogInformation($"AuthResponse: {authResponse.ToJSON()}");
|
||||
|
||||
//
|
||||
if (authResponse.IsError)
|
||||
{
|
||||
throw authResponse.GetException();
|
||||
}
|
||||
|
||||
//
|
||||
// Create XLoginResponse Model ...
|
||||
var result = authResponse.CreateXLoginResponse();
|
||||
|
||||
//
|
||||
// Get ans Set User Profile ...
|
||||
result.Profile = await GetUserProfileAsync(model.UserSelectBy, model.UserSelectBy);
|
||||
|
||||
//
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Refresh Tokens
|
||||
/// </summary>
|
||||
/// <param name="model">Authentication Tokens, instance of <see>XTokenResponse</see></param>
|
||||
/// <returns>an instance of <see>XTokenResponse</see></returns>
|
||||
public async Task<XTokenResponse> RefreshTokens(
|
||||
XTokenResponse model
|
||||
)
|
||||
{
|
||||
//
|
||||
// Validate Args ...
|
||||
ValidationProvider
|
||||
.GroupValidationBuilder()
|
||||
.AddNotNull(model)
|
||||
.AddNotEmpty(
|
||||
model.AccessToken,
|
||||
model.RefreshToken
|
||||
)
|
||||
.ValidateGroup();
|
||||
|
||||
//
|
||||
var authResponse = await RequestDiscoveryDocument()
|
||||
.ContinueWith((discoTask) =>
|
||||
{
|
||||
//
|
||||
var discoDoc = discoTask.Result;
|
||||
if (discoDoc.IsError)
|
||||
{
|
||||
XException.ActionFailed.Throw();
|
||||
}
|
||||
|
||||
//
|
||||
using (var httpClient = GetHttpClient())
|
||||
{
|
||||
return httpClient.RequestRefreshTokenAsync(new RefreshTokenRequest
|
||||
{
|
||||
//
|
||||
Address = discoDoc.TokenEndpoint,
|
||||
GrantType = GrantTypes.RefreshToken,
|
||||
ClientId = IdentityResourceConfiguration.ClientId,
|
||||
ClientSecret = IdentityResourceConfiguration.ClientSecret,
|
||||
RefreshToken = model.RefreshToken
|
||||
}).Result;
|
||||
}
|
||||
});
|
||||
|
||||
//
|
||||
// Check If Response Has Error ...
|
||||
if (authResponse.IsError)
|
||||
{
|
||||
//
|
||||
XError error = authResponse.ErrorDescription.FromJSON<XError>();
|
||||
throw error.ToException();
|
||||
}
|
||||
|
||||
//
|
||||
// Create XLoginResponse Model ...
|
||||
var result = authResponse.CreateXTokenResponse();
|
||||
|
||||
//
|
||||
return result;
|
||||
}
|
||||
#endregion
|
||||
|
||||
//
|
||||
#region Requirements ...
|
||||
/// <summary>
|
||||
/// Get an Instance of Http Client
|
||||
/// </summary>
|
||||
/// <returns>an instance of <see>HttpClient</see></returns>
|
||||
public HttpClient GetHttpClient()
|
||||
{
|
||||
//
|
||||
HttpClient httpClient = null;
|
||||
httpClient = new HttpClient();
|
||||
var httpClientHandler = new HttpClientHandler()
|
||||
{
|
||||
ServerCertificateCustomValidationCallback = (sender, cert, chain, sslPolicyErrors) =>
|
||||
{
|
||||
//
|
||||
Logger.LogInformation(
|
||||
$"SSL Handler: {Environment.NewLine} sender: {sender}, {Environment.NewLine} cert: {cert}, {Environment.NewLine} chain: {chain}, {Environment.NewLine} sslPolicyErrors: {sslPolicyErrors}"
|
||||
);
|
||||
|
||||
//
|
||||
return true;
|
||||
},
|
||||
ClientCertificateOptions = ClientCertificateOption.Manual,
|
||||
};
|
||||
|
||||
//
|
||||
httpClient = new HttpClient(httpClientHandler);
|
||||
|
||||
//
|
||||
return httpClient;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,341 @@
|
||||
using System;
|
||||
using xCommons.Extensions;
|
||||
using xExceptions.Constants;
|
||||
using xIdentityModels.Navigations;
|
||||
using xMessageService.Models;
|
||||
|
||||
namespace xIds.Providers
|
||||
{
|
||||
public partial class XIdentityManager
|
||||
{
|
||||
//
|
||||
#region Message Provider ...
|
||||
/// <summary>
|
||||
/// Generate and Prepare an XMessage Instance for
|
||||
/// User Invite Action
|
||||
/// </summary>
|
||||
/// <param name="lang">specify destination language</param>
|
||||
/// <param name="emailAddress">reciever email address</param>
|
||||
/// <param name="inviteToken">invitation token</param>
|
||||
/// <param name="returnUrl">return url for invitation user to redirect</param>
|
||||
/// <returns>an instance of <see>XMessage</see></returns>
|
||||
private XMessage GetUserInviteMessage(
|
||||
string lang,
|
||||
string emailAddress,
|
||||
string inviteToken,
|
||||
string returnUrl
|
||||
)
|
||||
{
|
||||
//
|
||||
ValidationProvider
|
||||
.NotEmpty(lang, emailAddress, inviteToken, returnUrl);
|
||||
ValidationProvider
|
||||
.EmailAddress(emailAddress);
|
||||
ValidationProvider
|
||||
.Url(returnUrl);
|
||||
|
||||
//
|
||||
HandleMessageProviderPreperation(lang);
|
||||
|
||||
//
|
||||
var msgStr = IdentityMessageProvider.InviteMsg +
|
||||
Environment.NewLine +
|
||||
returnUrl +
|
||||
"?t=" +
|
||||
inviteToken;
|
||||
|
||||
//
|
||||
var message = new XMessage
|
||||
{
|
||||
Message = msgStr,
|
||||
};
|
||||
message.Recievers.Add(emailAddress);
|
||||
|
||||
//
|
||||
return message;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generate and Prepare an XMessage Instance for
|
||||
/// New Device LoggedIn Action
|
||||
/// </summary>
|
||||
/// <param name="lang">specify destination language</param>
|
||||
/// <param name="emailAddress">reciever email address</param>
|
||||
/// <param name="device">an instance of <see>XDevice</see> which represent user new Device</param>
|
||||
/// <returns>an instance of <see>XMessage</see></returns>
|
||||
private XMessage GetUserNewDeviceLoggedInMessage(
|
||||
string lang,
|
||||
string emailAddress,
|
||||
XDevice device
|
||||
)
|
||||
{
|
||||
//
|
||||
ValidationProvider
|
||||
.NotEmpty(lang, emailAddress);
|
||||
ValidationProvider
|
||||
.EmailAddress(emailAddress);
|
||||
ValidationProvider
|
||||
.NotNull(device);
|
||||
|
||||
//
|
||||
HandleMessageProviderPreperation(lang);
|
||||
|
||||
//
|
||||
// Generate Specific Message ...
|
||||
var msgStr = IdentityMessageProvider.NewDeviceLoggedInMsg +
|
||||
Environment.NewLine +
|
||||
nameof(XDevice.Os) + ": " + device.Os +
|
||||
Environment.NewLine +
|
||||
nameof(XDevice.OsVersion) + ": " + device.OsVersion +
|
||||
Environment.NewLine +
|
||||
nameof(XDevice.Browser) + ": " + device.Browser +
|
||||
Environment.NewLine +
|
||||
nameof(XDevice.UserAgent) + ": " + device.UserAgent;
|
||||
|
||||
//
|
||||
// Create XMessage Instance ...
|
||||
var message = new XMessage
|
||||
{
|
||||
Message = msgStr,
|
||||
};
|
||||
message.Recievers.Add(emailAddress);
|
||||
|
||||
//
|
||||
return message;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generate and Prepare an XMessage Instance for
|
||||
/// Verification Code Action
|
||||
/// </summary>
|
||||
/// <param name="lang">specify destination language</param>
|
||||
/// <param name="mobileNumberOrEmail">an string which points to a user email address or mobile number</param>
|
||||
/// <param name="verificationCode">user verification code</param>
|
||||
/// <returns>an instance of <see>XMessage</see></returns>
|
||||
private XMessage GetVerificationCodeMessage(
|
||||
string lang,
|
||||
string mobileNumberOrEmail,
|
||||
string verificationCode
|
||||
)
|
||||
{
|
||||
//
|
||||
ValidationProvider
|
||||
.NotEmpty(lang, mobileNumberOrEmail, verificationCode);
|
||||
|
||||
//
|
||||
HandleMessageProviderPreperation(lang);
|
||||
|
||||
//
|
||||
mobileNumberOrEmail = mobileNumberOrEmail.ToNormalString();
|
||||
|
||||
//
|
||||
// Validate Mobile or Email ...
|
||||
var isValidEmail = mobileNumberOrEmail.IsValidEmail();
|
||||
var isValidMobileNumber = mobileNumberOrEmail.IsValidMobileNumber();
|
||||
if (!isValidEmail &&
|
||||
!isValidMobileNumber)
|
||||
{
|
||||
XException.InvalidData.Throw();
|
||||
}
|
||||
|
||||
//
|
||||
// Prepare Message String ...
|
||||
var msgStr = IdentityMessageProvider.VerificationCodeMsg +
|
||||
Environment.NewLine +
|
||||
verificationCode;
|
||||
|
||||
//
|
||||
// Create XMessage Instance ...
|
||||
var message = new XMessage
|
||||
{
|
||||
Message = msgStr,
|
||||
};
|
||||
message.Recievers.Add(mobileNumberOrEmail);
|
||||
|
||||
//
|
||||
return message;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generate and Prepare an XMessage Instance for
|
||||
/// Registration Approve Action
|
||||
/// </summary>
|
||||
/// <param name="lang">specify destination language</param>
|
||||
/// <param name="emailAddress">reciever email address</param>
|
||||
/// <param name="actionToken">a token which approved user action</param>
|
||||
/// <param name="returnUrl">return url for redirect</param>
|
||||
/// <returns>an instance of <see>XMessage</see></returns>
|
||||
private XMessage GetRegistrationConfirmMessage(
|
||||
string lang,
|
||||
string emailAddress,
|
||||
string actionToken,
|
||||
string returnUrl
|
||||
)
|
||||
{
|
||||
//
|
||||
ValidationProvider
|
||||
.NotEmpty(lang, emailAddress, actionToken, returnUrl);
|
||||
ValidationProvider
|
||||
.EmailAddress(emailAddress);
|
||||
ValidationProvider
|
||||
.Url(returnUrl);
|
||||
|
||||
//
|
||||
HandleMessageProviderPreperation(lang);
|
||||
|
||||
//
|
||||
// Prepare Message Str ...
|
||||
var msgStr = IdentityMessageProvider.RegistrationApproveMsg +
|
||||
Environment.NewLine +
|
||||
returnUrl +
|
||||
"?t=" +
|
||||
actionToken;
|
||||
|
||||
//
|
||||
var message = new XMessage
|
||||
{
|
||||
Message = msgStr,
|
||||
};
|
||||
message.Recievers.Add(emailAddress);
|
||||
|
||||
//
|
||||
return message;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generate and Prepare an XMessage Instance for
|
||||
/// Registration Finished Action
|
||||
/// </summary>
|
||||
/// <param name="lang">specify destination language</param>
|
||||
/// <param name="emailAddress">reciever email address</param>
|
||||
/// <param name="returnUrl">return url for redirect</param>
|
||||
/// <returns>an instance of <see>XMessage</see></returns>
|
||||
private XMessage GetRegistrationFinishedMessage(
|
||||
string lang,
|
||||
string emailAddress,
|
||||
string returnUrl
|
||||
)
|
||||
{
|
||||
//
|
||||
ValidationProvider
|
||||
.NotEmpty(lang, emailAddress, returnUrl);
|
||||
ValidationProvider
|
||||
.EmailAddress(emailAddress);
|
||||
ValidationProvider
|
||||
.Url(returnUrl);
|
||||
|
||||
//
|
||||
HandleMessageProviderPreperation(lang);
|
||||
|
||||
//
|
||||
var msgStr = IdentityMessageProvider.RegisteredMsg +
|
||||
Environment.NewLine +
|
||||
returnUrl;
|
||||
|
||||
//
|
||||
var message = new XMessage
|
||||
{
|
||||
Message = msgStr,
|
||||
};
|
||||
message.Recievers.Add(emailAddress);
|
||||
|
||||
//
|
||||
return message;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generate and Prepare an XMessage Instance for
|
||||
/// Password Changed Action
|
||||
/// </summary>
|
||||
/// <param name="lang">specify destination language</param>
|
||||
/// <param name="emailAddress">reciever email address</param>
|
||||
/// <param name="returnUrl">return url for redirect</param>
|
||||
/// <param name="throwException">specify throw exceptions on failure or not, default is true</param>
|
||||
/// <returns>an instance of <see>XMessage</see></returns>
|
||||
private XMessage GetPasswordChangedMessage(
|
||||
string lang,
|
||||
string emailAddress,
|
||||
string returnUrl,
|
||||
bool throwException = true
|
||||
)
|
||||
{
|
||||
//
|
||||
if (throwException)
|
||||
{
|
||||
ValidationProvider
|
||||
.NotEmpty(lang, emailAddress, returnUrl);
|
||||
ValidationProvider
|
||||
.EmailAddress(emailAddress);
|
||||
ValidationProvider
|
||||
.Url(returnUrl);
|
||||
}
|
||||
|
||||
//
|
||||
HandleMessageProviderPreperation(lang);
|
||||
|
||||
//
|
||||
var msgStr = IdentityMessageProvider.PasswordChangedMsg +
|
||||
(returnUrl.IsNullOrEmpty() ?
|
||||
"" :
|
||||
Environment.NewLine +
|
||||
returnUrl);
|
||||
|
||||
//
|
||||
var message = new XMessage
|
||||
{
|
||||
Message = msgStr,
|
||||
};
|
||||
message.Recievers.Add(emailAddress);
|
||||
|
||||
//
|
||||
return message;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generate and Prepare an XMessage Instance for
|
||||
/// Reset Password Action
|
||||
/// </summary>
|
||||
/// <param name="lang">specify destination language</param>
|
||||
/// <param name="emailAddress">reciever email address</param>
|
||||
/// <param name="actionToken">a token which approved user action</param>
|
||||
/// <param name="returnUrl">return url for redirect</param>
|
||||
/// <returns>an instance of <see>XMessage</see></returns>
|
||||
private XMessage GetResetPasswordMessage(
|
||||
string lang,
|
||||
string emailAddress,
|
||||
string actionToken,
|
||||
string returnUrl
|
||||
)
|
||||
{
|
||||
//
|
||||
ValidationProvider
|
||||
.NotEmpty(lang, emailAddress, actionToken, returnUrl);
|
||||
ValidationProvider
|
||||
.EmailAddress(emailAddress);
|
||||
ValidationProvider
|
||||
.Url(returnUrl);
|
||||
|
||||
//
|
||||
HandleMessageProviderPreperation(lang);
|
||||
|
||||
//
|
||||
// Prepare Message Str ...
|
||||
var msgStr = IdentityMessageProvider.ChangePasswordMsg +
|
||||
Environment.NewLine +
|
||||
returnUrl +
|
||||
"?t=" +
|
||||
actionToken;
|
||||
|
||||
//
|
||||
var message = new XMessage
|
||||
{
|
||||
Message = msgStr,
|
||||
};
|
||||
message.Recievers.Add(emailAddress);
|
||||
|
||||
//
|
||||
return message;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,211 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using xCommons.Extensions;
|
||||
using xExceptions.Constants;
|
||||
using xIdentityModels.Dtos;
|
||||
|
||||
namespace xIds.Providers
|
||||
{
|
||||
public partial class XIdentityManager
|
||||
{
|
||||
//
|
||||
#region OpenGet ...
|
||||
public async Task<XOpenActionInnerDto> OpenGet(
|
||||
XOpenActionInnerDto model
|
||||
)
|
||||
{
|
||||
//
|
||||
// Validate Args ...
|
||||
await ValidationProvider
|
||||
.GroupValidationBuilder()
|
||||
.AddNotNull(model)
|
||||
.AddNotEmpty(model.Token)
|
||||
.AddNotEmpty(model.Payload)
|
||||
.ValidateGroupAsync();
|
||||
|
||||
//
|
||||
return await OpenGet(
|
||||
token: model.Token,
|
||||
actionRequest: model.Payload
|
||||
);
|
||||
}
|
||||
|
||||
//
|
||||
public async Task<XOpenActionInnerDto> OpenGet(
|
||||
string token,
|
||||
string actionRequest
|
||||
)
|
||||
{
|
||||
//
|
||||
// Validatr Args ...
|
||||
await ValidationProvider
|
||||
.GroupValidationBuilder()
|
||||
.AddNotEmpty(token)
|
||||
.AddNotEmpty(actionRequest)
|
||||
.ValidateGroupAsync();
|
||||
//
|
||||
// Try to Decrypt Request ...
|
||||
var requestModel = ParseModel(
|
||||
token: token,
|
||||
request: actionRequest
|
||||
);
|
||||
if (requestModel.IsNull() ||
|
||||
requestModel.Action.IsNullOrEmpty())
|
||||
{
|
||||
XException.BadRequest.Throw();
|
||||
}
|
||||
|
||||
//
|
||||
var responseJson = string.Empty;
|
||||
switch (requestModel.Action)
|
||||
{
|
||||
//
|
||||
case XOpenActions.GetUserInfo:
|
||||
//
|
||||
ValidationProvider.NotEmpty(requestModel.Payload);
|
||||
var userInfo = await GetUserInfo(requestModel);
|
||||
if (userInfo.IsNull())
|
||||
{
|
||||
XException.InvalidData.Throw();
|
||||
}
|
||||
|
||||
//
|
||||
responseJson = userInfo.ToJSON();
|
||||
break;
|
||||
|
||||
//
|
||||
case XOpenActions.GetUserInfos:
|
||||
ValidationProvider.NotEmpty(requestModel.Payload);
|
||||
var userInfos = await GetUserInfos(requestModel);
|
||||
if (userInfos.IsNull())
|
||||
{
|
||||
XException.InvalidData.Throw();
|
||||
}
|
||||
|
||||
//
|
||||
responseJson = userInfos.ToJSON();
|
||||
break;
|
||||
}
|
||||
|
||||
//
|
||||
// Encrypt Json Str ...
|
||||
var responseStr = SecurityProvider.Encrypt(responseJson);
|
||||
requestModel.Payload = responseStr;
|
||||
requestModel.Timestamp = DateTime.UtcNow
|
||||
.AddMinutes(5)
|
||||
.ToTimestamp()
|
||||
.ToString();
|
||||
|
||||
//
|
||||
token = requestModel.ToOpenActionToken();
|
||||
requestModel.Checksum = token;
|
||||
var result = new XOpenActionInnerDto
|
||||
{
|
||||
Token = token,
|
||||
Payload = requestModel.ToJSON()
|
||||
};
|
||||
|
||||
//
|
||||
// Check Result ...
|
||||
if (result.IsNull())
|
||||
{
|
||||
XException.ActionFailed.Throw();
|
||||
}
|
||||
|
||||
//
|
||||
return result;
|
||||
}
|
||||
#endregion
|
||||
|
||||
public async Task<XOpenActionUserInfoDto> GetUserInfo(
|
||||
XOpenActionRequestDto model
|
||||
)
|
||||
{
|
||||
//
|
||||
// Retrieve User Profile ...
|
||||
var userProfile = await GetUserProfileAsync(
|
||||
checkIsBanned: false,
|
||||
checkCanLoginPolicies: false,
|
||||
requestedUserSelectByParam: "",
|
||||
forceCheckRequestedUser: false,
|
||||
userSelectByParam: model.Payload
|
||||
);
|
||||
|
||||
//
|
||||
// Generate a Dynamic Object ...
|
||||
var result = new XOpenActionUserInfoDto
|
||||
{
|
||||
Username = userProfile.UserName,
|
||||
Firstname = userProfile.FirstName,
|
||||
Lastname = userProfile.LastName,
|
||||
Avatar = userProfile.Avatar
|
||||
};
|
||||
|
||||
//
|
||||
return result;
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<XOpenActionUserInfoDto>> GetUserInfos(
|
||||
XOpenActionRequestDto model
|
||||
)
|
||||
{
|
||||
//
|
||||
var ids = model.Payload.ParseListString<string>().ToList();
|
||||
var profiles = await GetUserProfilesAsync(
|
||||
ids: ids,
|
||||
checkIsBanned: false,
|
||||
checkCanLoginPolicies: false,
|
||||
requestedUserSelectByParam: "",
|
||||
forceCheckRequestedUser: false
|
||||
);
|
||||
var result = profiles.Select(x => new XOpenActionUserInfoDto
|
||||
{
|
||||
Avatar = x.Avatar,
|
||||
Username = x.UserName,
|
||||
Lastname = x.LastName,
|
||||
Firstname = x.FirstName,
|
||||
});
|
||||
|
||||
//
|
||||
return result;
|
||||
}
|
||||
|
||||
//
|
||||
#region Private ...
|
||||
private XOpenActionRequestDto ParseModel(
|
||||
string token,
|
||||
string request
|
||||
)
|
||||
{
|
||||
//
|
||||
// Decript request to get Json string of request object ...
|
||||
var result = new XOpenActionRequestDto();
|
||||
|
||||
//
|
||||
var jsonStr = SecurityProvider.Decrypt(request);
|
||||
result = jsonStr.FromJSON<XOpenActionRequestDto>();
|
||||
if (result.IsNull() ||
|
||||
!result.Validate() ||
|
||||
token != result.Checksum
|
||||
)
|
||||
{
|
||||
//
|
||||
result = null;
|
||||
return result;
|
||||
}
|
||||
|
||||
//
|
||||
var modelChecksum = result.ToOpenActionToken();
|
||||
result =
|
||||
token == modelChecksum
|
||||
? result
|
||||
: null;
|
||||
|
||||
//
|
||||
return result;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,552 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using xCommons.Extensions;
|
||||
using xCommons.Helpers;
|
||||
using xExceptions.Constants;
|
||||
using xIdentityModels;
|
||||
using xIdentityModels.Constants;
|
||||
using xIdentityModels.Dtos;
|
||||
using xIdentityModels.Models;
|
||||
using xIdentityModels.Navigations;
|
||||
|
||||
namespace xIds.Providers
|
||||
{
|
||||
public partial class XIdentityManager
|
||||
{
|
||||
//
|
||||
#region Generators ...
|
||||
/// <summary>
|
||||
/// Generate a Random Number ...
|
||||
/// </summary>
|
||||
/// <returns>a long value</returns>
|
||||
private long GenerateRandom()
|
||||
{
|
||||
return new Random().Next(100000, 999999);
|
||||
}
|
||||
#endregion
|
||||
|
||||
//
|
||||
#region UserSelectBy Actions ...
|
||||
/// <summary>
|
||||
/// Retrieve UserSelectByType based on Given Info
|
||||
/// </summary>
|
||||
/// <param name="userSelectByParam">a user identifier</param>
|
||||
/// <param name="forceUserSelectByParamNotEmpty">a boolean value which specify the identifier
|
||||
/// must be specific and not empty, default is true</param>
|
||||
/// <param name="forceUserSelectByTypeMustSpecified">a boolean value which specify the type
|
||||
/// must be specific, default is false</param>
|
||||
/// <returns>a member of <see>XUserSelectBy</see></returns>
|
||||
private XUserSelectBy GetUserSelectByType(
|
||||
string userSelectByParam,
|
||||
bool forceUserSelectByParamNotEmpty = true,
|
||||
bool forceUserSelectByTypeMustSpecified = false
|
||||
)
|
||||
{
|
||||
//
|
||||
if (forceUserSelectByParamNotEmpty)
|
||||
{
|
||||
ValidationProvider.NotEmpty(userSelectByParam);
|
||||
}
|
||||
|
||||
//
|
||||
var result = XUserSelectBy.NotSpecified;
|
||||
var normalizedString = userSelectByParam.ToNormalString();
|
||||
if (userSelectByParam.IsValidEmail())
|
||||
{
|
||||
//
|
||||
// Find User by Email ...
|
||||
result = XUserSelectBy.Email;
|
||||
}
|
||||
else if (userSelectByParam.IsValidMobileNumber())
|
||||
{
|
||||
//
|
||||
// Find User by Mobile Number ...
|
||||
result = XUserSelectBy.MobileNumber;
|
||||
}
|
||||
else if (userSelectByParam.IsGuid())
|
||||
{
|
||||
//
|
||||
// Find User By it's ID ...
|
||||
result = XUserSelectBy.ID;
|
||||
}
|
||||
else
|
||||
{
|
||||
//
|
||||
// UserName ...
|
||||
result = XUserSelectBy.Username;
|
||||
}
|
||||
|
||||
//
|
||||
if (forceUserSelectByTypeMustSpecified &&
|
||||
result == XUserSelectBy.NotSpecified)
|
||||
{
|
||||
XException.InvalidData.Throw();
|
||||
}
|
||||
|
||||
//
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieve UserSelectByParam based on XUser Object
|
||||
/// </summary>
|
||||
/// <param name="user">an instance of <see>XUser</see></param>
|
||||
/// <param name="forceNotNull">a boolean value which specify the identifier must nut empty,
|
||||
/// and throw exception if it is empty, default is true</param>
|
||||
/// <param name="excludes">a collection of <see>XUserSelectBy</see> members which
|
||||
/// exclude them from result, default is null</param>
|
||||
/// <returns>a user identifier</returns>
|
||||
private string GetUserSelectByParam(
|
||||
XUser user,
|
||||
bool forceNotNull = true,
|
||||
ICollection<XUserSelectBy> excludes = null
|
||||
)
|
||||
{
|
||||
//
|
||||
// Validate Args ...
|
||||
ValidationProvider.NotNull(user);
|
||||
|
||||
//
|
||||
var id = user.Id;
|
||||
var userName = user.UserName;
|
||||
var email = user.Email;
|
||||
var phoneNumber = user.PhoneNumber;
|
||||
|
||||
//
|
||||
if (excludes != null)
|
||||
{
|
||||
if (excludes.Contains(XUserSelectBy.ID))
|
||||
{
|
||||
id = null;
|
||||
}
|
||||
if (excludes.Contains(XUserSelectBy.Email))
|
||||
{
|
||||
email = null;
|
||||
}
|
||||
if (excludes.Contains(XUserSelectBy.MobileNumber))
|
||||
{
|
||||
phoneNumber = null;
|
||||
}
|
||||
if (excludes.Contains(XUserSelectBy.Username))
|
||||
{
|
||||
userName = null;
|
||||
}
|
||||
}
|
||||
|
||||
var selectByParam =
|
||||
id.IsNullOrEmpty() ?
|
||||
userName.IsNullOrEmpty() ?
|
||||
email.IsNullOrEmpty() ?
|
||||
phoneNumber.IsNullOrEmpty() ? null : phoneNumber : email : userName : id;
|
||||
|
||||
//
|
||||
// Check result ...
|
||||
if (forceNotNull &&
|
||||
selectByParam.IsNullOrEmpty())
|
||||
{
|
||||
XException.InvalidData.Throw();
|
||||
}
|
||||
|
||||
//
|
||||
return selectByParam;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieve UserSelectByParam based on <see>XActionRequestContext</see> instance
|
||||
/// </summary>
|
||||
/// <param name="context">an instance of <see>XActionRequestContext</see></param>
|
||||
/// <param name="forceNotNull">a boolean value which specify the identifier must nut empty,
|
||||
/// and throw exception if it is empty, default is true</param>
|
||||
/// <param name="excludes">a collection of <see>XUserSelectBy</see> members which
|
||||
/// exclude them from result, default is null</param>
|
||||
/// <returns>a user identifier</returns>
|
||||
private string GetUserSelectByParam(
|
||||
XActionRequestContext context,
|
||||
bool forceNotNull = true,
|
||||
ICollection<XUserSelectBy> excludes = null
|
||||
)
|
||||
{
|
||||
//
|
||||
// Validate Args ...
|
||||
ValidationProvider.NotNull(context);
|
||||
|
||||
//
|
||||
// Generate User SelectBy ...
|
||||
var id = context.UserId;
|
||||
var userName = context.UserName;
|
||||
var email = context.Email;
|
||||
var phoneNumber = context.MobileNumber;
|
||||
|
||||
//
|
||||
if (excludes != null)
|
||||
{
|
||||
//
|
||||
if (excludes.Contains(XUserSelectBy.ID))
|
||||
{
|
||||
id = null;
|
||||
}
|
||||
if (excludes.Contains(XUserSelectBy.Email))
|
||||
{
|
||||
email = null;
|
||||
}
|
||||
if (excludes.Contains(XUserSelectBy.MobileNumber))
|
||||
{
|
||||
phoneNumber = null;
|
||||
}
|
||||
if (excludes.Contains(XUserSelectBy.Username))
|
||||
{
|
||||
userName = null;
|
||||
}
|
||||
}
|
||||
|
||||
var selectByParam =
|
||||
id.IsNullOrEmpty() ?
|
||||
userName.IsNullOrEmpty() ?
|
||||
email.IsNullOrEmpty() ?
|
||||
phoneNumber.IsNullOrEmpty() ? null : phoneNumber : email : userName : id;
|
||||
|
||||
//
|
||||
// Check param ...
|
||||
if (forceNotNull &&
|
||||
selectByParam.IsNullOrEmpty())
|
||||
{
|
||||
XException.InvalidData.Throw();
|
||||
}
|
||||
|
||||
//
|
||||
return selectByParam;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieve UserSelectByParam based on <see>XActionRequestToken</see> instance
|
||||
/// </summary>
|
||||
/// <param name="request">an instance of <see>XActionRequestToken</see> which
|
||||
/// provides required informations</param>
|
||||
/// <param name="forceNotNull">a boolean value which specify the identifier must nut empty,
|
||||
/// and throw exception if it is empty, default is true</param>
|
||||
/// <param name="excludes">a collection of <see>XUserSelectBy</see> members which
|
||||
/// exclude them from result, default is null</param>
|
||||
/// <returns>a user identifier</returns>
|
||||
private string GetUserSelectByParam(
|
||||
XActionRequestToken request,
|
||||
bool forceNotNull = true,
|
||||
ICollection<XUserSelectBy> excludes = null
|
||||
)
|
||||
{
|
||||
//
|
||||
// Validate Args ...
|
||||
ValidationProvider.NotNull(request);
|
||||
|
||||
//
|
||||
// Generate User SelectBy ...
|
||||
return GetUserSelectByParam(
|
||||
context: request.Context,
|
||||
forceNotNull: forceNotNull,
|
||||
excludes: excludes
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieve Available UserSelectByParams from given XUser
|
||||
/// </summary>
|
||||
/// <param name="user">an instance of <see>XUser</see></param>
|
||||
/// <returns></returns>
|
||||
private ICollection<string> GenerateUserSelectByParams(
|
||||
XUser user
|
||||
)
|
||||
{
|
||||
//
|
||||
if (user == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
//
|
||||
var result = new List<string> {
|
||||
user.Id,
|
||||
user.UserName,
|
||||
user.PhoneNumber,
|
||||
user.Email,
|
||||
};
|
||||
|
||||
//
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieve Required XUserSelectByTypes
|
||||
/// </summary>
|
||||
/// <param name="ignores">a collection of <see>XUserSelectBy</see> members which
|
||||
/// ignored in result</param>
|
||||
/// <returns>a collection of available <see>XUserSelectBy</see> members</returns>
|
||||
private ICollection<XUserSelectBy> GenerateUserSelectByExcludes(
|
||||
ICollection<XUserSelectBy> ignores
|
||||
)
|
||||
{
|
||||
//
|
||||
var result = ObjectHelper
|
||||
.ToEnumerableValues<XUserSelectBy>()
|
||||
.Except(ignores);
|
||||
|
||||
//
|
||||
return result.ToList();
|
||||
}
|
||||
#endregion
|
||||
|
||||
//
|
||||
#region XAction Actions ...
|
||||
/// <summary>
|
||||
/// Retrieve Action Result Response based on XToken
|
||||
/// </summary>
|
||||
/// <param name="xToken">an instance of <see>XToken</see></param>
|
||||
/// <returns>an instance of <see>XActionResponse</see></returns>
|
||||
private XActionResponse GetActionResultResponse(
|
||||
XToken xToken
|
||||
)
|
||||
{
|
||||
//
|
||||
ValidationProvider.NotNull(xToken);
|
||||
ValidationProvider.NotEmpty(xToken.Hash, xToken.Token);
|
||||
|
||||
//
|
||||
ValidateToken(xToken.Token);
|
||||
|
||||
//
|
||||
var expDate = GetTokenExpirationDate(xToken.Token);
|
||||
ValidationProvider.NotNull(expDate);
|
||||
|
||||
//
|
||||
// Generate Result class ...
|
||||
var result = new XActionResponse
|
||||
{
|
||||
Token = xToken.Hash,
|
||||
Expiration = expDate
|
||||
};
|
||||
|
||||
//
|
||||
return result;
|
||||
}
|
||||
#endregion
|
||||
|
||||
//
|
||||
#region XIdentityMessage Actions ...
|
||||
/// <summary>
|
||||
/// Prepare Message Provider and Check it's State
|
||||
/// </summary>
|
||||
/// <param name="lang">specify destination language</param>
|
||||
private void HandleMessageProviderPreperation(
|
||||
string lang
|
||||
)
|
||||
{
|
||||
//
|
||||
ValidationProvider
|
||||
.NotEmpty(lang);
|
||||
|
||||
//
|
||||
// Prepare Identity Messages ...
|
||||
// the PrepareMessageMethod check IsReady of Helper automatically ...
|
||||
IdentityMessageProvider.PrepareMessages(lang);
|
||||
|
||||
//
|
||||
// Check Message Provider ...
|
||||
var isMessageProviderReady = MessageProvider.IsReady(true);
|
||||
if (!isMessageProviderReady)
|
||||
{
|
||||
XException.MessageServiceInitialFailed.Throw();
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
//
|
||||
#region Friendship Actions ...
|
||||
/// <summary>
|
||||
/// Check Follow Request Sent Before
|
||||
/// from dest to source
|
||||
/// </summary>
|
||||
/// <param name="source">an instance of <see>XUser</see></param>
|
||||
/// <param name="dest">an instance of <see>XUser</see></param>
|
||||
/// <returns>a boolean value</returns>
|
||||
private bool IsFollowRequested(
|
||||
XUser source,
|
||||
XUser dest
|
||||
)
|
||||
{
|
||||
//
|
||||
// Validate Args ...
|
||||
ValidationProvider.NotNull(source, dest);
|
||||
|
||||
//
|
||||
var result = source.Followings.Any(f => f.DestId == dest.Id);
|
||||
|
||||
//
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check Follow Request Sent Before
|
||||
/// from dest to source
|
||||
/// </summary>
|
||||
/// <param name="source">an instance of <see>XUser</see></param>
|
||||
/// <param name="dest">an instance of <see>XUser</see></param>
|
||||
/// <param name="state">a member of <see>XFriendshipState</see></param>
|
||||
/// <returns>a boolean value</returns>
|
||||
private bool IsFollowRequested(
|
||||
XUser source,
|
||||
XUser dest,
|
||||
XFriendshipState state
|
||||
)
|
||||
{
|
||||
//
|
||||
// Validate Args ...
|
||||
ValidationProvider.NotNull(source, dest);
|
||||
|
||||
//
|
||||
var result = source.Followings
|
||||
.Any(f =>
|
||||
f.DestId == dest.Id &&
|
||||
f.State == state);
|
||||
|
||||
//
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts to XFriendDto ...
|
||||
/// </summary>
|
||||
/// <param name="entity"></param>
|
||||
/// <returns></returns>
|
||||
private async Task<XFriendDto> ToDto(XFriendshipFollower entity)
|
||||
{
|
||||
//
|
||||
XFriendDto result = null;
|
||||
|
||||
//
|
||||
if (entity.IsNull())
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
//
|
||||
// Reading Dest User Profile ...
|
||||
var profile = await GetUserProfileAsync(
|
||||
userSelectByParam: entity.DestId,
|
||||
requestedUserSelectByParam: entity.UserId,
|
||||
forceCheckRequestedUser: false,
|
||||
checkCanLoginPolicies: false,
|
||||
checkIsBanned: false
|
||||
);
|
||||
|
||||
//
|
||||
// Preparing Result ...
|
||||
result = new XFriendDto
|
||||
{
|
||||
UserId = profile.UserId,
|
||||
Avatar = profile.Avatar,
|
||||
Username = profile.UserName,
|
||||
Lastname = profile.LastName,
|
||||
Firstname = profile.FirstName,
|
||||
Type = XFriendshipType.Follower,
|
||||
State = profile.FriendshipInfo.FollowerState,
|
||||
};
|
||||
|
||||
//
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts to XFriendDto ...
|
||||
/// </summary>
|
||||
/// <param name="entity"></param>
|
||||
/// <returns></returns>
|
||||
private async Task<XFriendDto> ToDto(XFriendshipFollowing entity)
|
||||
{
|
||||
//
|
||||
XFriendDto result = null;
|
||||
|
||||
//
|
||||
if (entity.IsNull())
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
//
|
||||
// Reading Dest User Profile ...
|
||||
var profile = await GetUserProfileAsync(
|
||||
checkIsBanned: false,
|
||||
checkCanLoginPolicies: false,
|
||||
forceCheckRequestedUser: false,
|
||||
userSelectByParam: entity.DestId,
|
||||
requestedUserSelectByParam: entity.UserId
|
||||
);
|
||||
|
||||
//
|
||||
// Preparing Result ...
|
||||
result = new XFriendDto
|
||||
{
|
||||
UserId = profile.UserId,
|
||||
Avatar = profile.Avatar,
|
||||
Username = profile.UserName,
|
||||
Lastname = profile.LastName,
|
||||
Firstname = profile.FirstName,
|
||||
Type = XFriendshipType.Following,
|
||||
State = profile.FriendshipInfo.FollowingState,
|
||||
};
|
||||
|
||||
//
|
||||
return result;
|
||||
}
|
||||
#endregion
|
||||
|
||||
//
|
||||
#region User Profile ...
|
||||
/// <summary>
|
||||
/// Count User's Page
|
||||
/// </summary>
|
||||
/// <param name="pageSize">an integer value which represent page size</param>
|
||||
/// <returns>an inetger value which represent pages count</returns>
|
||||
public async Task<int> UserPagesCount(
|
||||
int pageSize
|
||||
)
|
||||
{
|
||||
//
|
||||
int count = await UserManager.Users.CountAsync();
|
||||
int pagesCount = count / pageSize;
|
||||
|
||||
//
|
||||
if (count % pageSize > 0)
|
||||
{
|
||||
pagesCount++;
|
||||
}
|
||||
|
||||
//
|
||||
return pagesCount;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Count Avatar's Page
|
||||
/// </summary>
|
||||
/// <param name="pageSize">an integer value which represent page size</param>
|
||||
/// <returns>an inetger value which represent pages count</returns>
|
||||
public async Task<int> ProfileImagePagesCount(
|
||||
int pageSize
|
||||
)
|
||||
{
|
||||
//
|
||||
int count = await DbContext.Avatars.CountAsync();
|
||||
int pagesCount = count / pageSize;
|
||||
|
||||
//
|
||||
if (count % pageSize > 0)
|
||||
{
|
||||
pagesCount++;
|
||||
}
|
||||
|
||||
//
|
||||
return pagesCount;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,110 @@
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using xCommons.Extensions;
|
||||
using xExceptions.Constants;
|
||||
using xDataService.Extensions;
|
||||
using xIds.Extensions;
|
||||
using xModels.Dtos;
|
||||
using xIdentityModels.Extensions;
|
||||
|
||||
namespace xIds.Providers
|
||||
{
|
||||
public partial class XIdentityManager
|
||||
{
|
||||
/// <summary>
|
||||
/// Retrieve Users as Query Model for Query Service ...
|
||||
/// </summary>
|
||||
/// <param name="requestedUserSelectByParam"></param>
|
||||
/// <param name="query"></param>
|
||||
/// <param name="role"></param>
|
||||
/// <param name="forceRole"></param>
|
||||
/// <returns></returns>
|
||||
public async Task<XQueryResult<string>> QueryUsers(
|
||||
string requestedUserSelectByParam,
|
||||
XQuery query,
|
||||
string role = null,
|
||||
bool forceRole = false
|
||||
)
|
||||
{
|
||||
//
|
||||
// Validation ...
|
||||
if (query.IsNull())
|
||||
{
|
||||
XException.InvalidArgs.Throw();
|
||||
}
|
||||
|
||||
//
|
||||
// Normalize ...
|
||||
query = query.NormalizeQuery(DataConfiguration.ToXDataServiceConfig());
|
||||
|
||||
//
|
||||
// Since in Resourceable Entities we have to Search on Locales
|
||||
// we Must Implement Senario Custom ...
|
||||
var items = GetUsersDbSet()
|
||||
.ToList()
|
||||
.Where(u => !u.ContainsUserSelectByParam(requestedUserSelectByParam))
|
||||
.ToList()
|
||||
.AsEnumerable();
|
||||
|
||||
//
|
||||
var totalItemsCount = items.Count();
|
||||
|
||||
//
|
||||
// Apply Filter ...
|
||||
if (!query.Filter.IsNullOrEmpty())
|
||||
{
|
||||
//
|
||||
items = items
|
||||
.ApplyFilter(query.Filter);
|
||||
}
|
||||
int filteredItemsCount = items.Count();
|
||||
|
||||
//
|
||||
// Count Pages ...
|
||||
var totalPagesCount = query.CountPages(totalItemsCount);
|
||||
var filteredPagesCount = query.CountPages(filteredItemsCount);
|
||||
|
||||
//
|
||||
// Apply Paging and Sorting ...
|
||||
if (totalItemsCount > 0 &&
|
||||
filteredItemsCount > 0)
|
||||
{
|
||||
//
|
||||
// Apply Sorting ...
|
||||
items = items
|
||||
.ToList()
|
||||
.ApplySorting(
|
||||
query.SortBy,
|
||||
query.IsAscending
|
||||
);
|
||||
|
||||
//
|
||||
// Apply Paging ...
|
||||
items = items
|
||||
.ToList()
|
||||
.ApplyPaging(
|
||||
query.Page,
|
||||
query.PageSize
|
||||
);
|
||||
}
|
||||
|
||||
//
|
||||
// Generate Result Object ...
|
||||
// Query = query,
|
||||
var result = new XQueryResult<string>
|
||||
{
|
||||
Items = items
|
||||
.Select(u => u.Id),
|
||||
Page = query.Page,
|
||||
PageSize = query.PageSize,
|
||||
TotalPages = totalPagesCount,
|
||||
TotalItems = totalItemsCount,
|
||||
TotalFilteredPages = filteredPagesCount,
|
||||
TotalFilteredItems = filteredItemsCount
|
||||
};
|
||||
|
||||
//
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,700 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using xCommons.Extensions;
|
||||
using xExceptions.Constants;
|
||||
using xIdentityModels;
|
||||
using xIdentityModels.Constants;
|
||||
using xIdentityModels.Models;
|
||||
using xIdentityModels.Navigations;
|
||||
|
||||
namespace xIds.Providers
|
||||
{
|
||||
public partial class XIdentityManager
|
||||
{
|
||||
//
|
||||
#region Registration Actions ...
|
||||
/// <summary>
|
||||
/// Invite a User to Register on Dashboard
|
||||
/// </summary>
|
||||
/// <param name="lang">specify destination language</param>
|
||||
/// <param name="device">an instance of <see>XDevice</see></param>
|
||||
/// <param name="email">which email address is going to invite</param>
|
||||
/// <param name="returnUrl">return url for invitation user to redirect</param>
|
||||
/// <returns>an instance of <see>XActionResponse</see></returns>
|
||||
public async Task<XActionResponse> InviteUser(
|
||||
string lang,
|
||||
XDevice device,
|
||||
string email,
|
||||
string returnUrl
|
||||
)
|
||||
{
|
||||
//
|
||||
// Validate Args ...
|
||||
await ValidationProvider
|
||||
.GroupValidationBuilder()
|
||||
.AddNotEmpty(
|
||||
lang,
|
||||
email,
|
||||
returnUrl
|
||||
)
|
||||
.AddNotNull(device)
|
||||
.AddEmailAddress(email)
|
||||
.ValidateGroupAsync();
|
||||
|
||||
//
|
||||
// Check Email is Unique ...
|
||||
await ValidateUserNotExists(email);
|
||||
|
||||
//
|
||||
// Get Request Result ...
|
||||
var result = await ActionRequest(
|
||||
lang,
|
||||
device,
|
||||
email,
|
||||
XAction.Invite,
|
||||
forceCheckContext: false,
|
||||
forceCheckUserExists: false);
|
||||
|
||||
//
|
||||
// Prepare Propper Message ...
|
||||
var xMessage = GetUserInviteMessage(lang, email, result.Token, returnUrl);
|
||||
|
||||
//
|
||||
// Send Message ...
|
||||
try
|
||||
{
|
||||
await MessageProvider.SendMailAsync(xMessage);
|
||||
}
|
||||
catch { }
|
||||
|
||||
//
|
||||
// Send Result ...
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Recieve some Basic Informations and Start Registration Proccess
|
||||
/// if they Valid
|
||||
///
|
||||
/// Registration Proccess Starts with Invoking this Action
|
||||
/// </summary>
|
||||
/// <param name="lang">specify destination language</param>
|
||||
/// <param name="device">an instance of <see>XDevice</see></param>
|
||||
/// <param name="invitationHash">optional, if user invited, this is the invitation token</param>
|
||||
/// <param name="firstName">user's FirstName</param>
|
||||
/// <param name="lastName">user's LastName</param>
|
||||
/// <param name="dateOfBirth">user's dob date</param>
|
||||
/// <param name="mobileNumber">user's Mobile Number</param>
|
||||
/// <param name="emailAddress">user's Email address</param>
|
||||
/// <returns>an instance of <see>XActionResponse</see></returns>
|
||||
public async Task<XActionResponse> RequestRegistration(
|
||||
string lang,
|
||||
XDevice device,
|
||||
string invitationHash,
|
||||
string firstName,
|
||||
string lastName,
|
||||
DateTime dateOfBirth,
|
||||
string mobileNumber = null,
|
||||
string emailAddress = null
|
||||
)
|
||||
{
|
||||
//
|
||||
// Validate Args ...
|
||||
await ValidationProvider
|
||||
.GroupValidationBuilder()
|
||||
.AddNotEmpty(
|
||||
lang,
|
||||
firstName,
|
||||
lastName
|
||||
)
|
||||
.AddNotNull(
|
||||
device,
|
||||
dateOfBirth
|
||||
)
|
||||
.ValidateGroupAsync();
|
||||
|
||||
//
|
||||
// Validate Mobile Number if Exists ...
|
||||
if (!mobileNumber.IsNullOrEmpty())
|
||||
{
|
||||
//
|
||||
ValidationProvider.MobileNumber(mobileNumber);
|
||||
|
||||
//
|
||||
// Check Mobile is Uniqsue ...
|
||||
await ValidateUserNotExists(mobileNumber);
|
||||
}
|
||||
|
||||
//
|
||||
// Validate Email Address if Exists ...
|
||||
if (!emailAddress.IsNullOrEmpty())
|
||||
{
|
||||
//
|
||||
ValidationProvider.EmailAddress(emailAddress);
|
||||
|
||||
//
|
||||
// Check Email is Unique ...
|
||||
await ValidateUserNotExists(emailAddress);
|
||||
}
|
||||
|
||||
//
|
||||
// Get Related Token and Validate it and
|
||||
// Check Related Configurations ...
|
||||
await ValidateInvitationHash(invitationHash);
|
||||
|
||||
//
|
||||
// Check Device Validation ...
|
||||
await ValidateDeviceForActions(device);
|
||||
|
||||
//
|
||||
// Validate DateOfBirth ...
|
||||
ValidateDateOfBirth(dateOfBirth);
|
||||
|
||||
//
|
||||
// Define Empty Objects for Using ...
|
||||
var userSelectByParam = "";
|
||||
var context = new XActionRequestContext();
|
||||
//
|
||||
context.Lang = lang;
|
||||
context.Device = device;
|
||||
context.LastName = lastName;
|
||||
context.FirstName = firstName;
|
||||
context.DateOfBirth = dateOfBirth;
|
||||
context.MobileNumber = mobileNumber ?? "";
|
||||
context.Email = !emailAddress.IsNullOrEmpty() ? emailAddress.ToNormalString() : "";
|
||||
|
||||
//
|
||||
userSelectByParam = GetUserSelectByParam(context);
|
||||
|
||||
//
|
||||
// Token Data Parsing ...
|
||||
if (!invitationHash.IsNullOrEmpty())
|
||||
{
|
||||
//
|
||||
// Get Related Token, Validate it and Parse Contains
|
||||
// XAction Request and Validate the Request itself and
|
||||
// Return it ...
|
||||
var inviteRequest = await ValidateAndParseActionHash(invitationHash);
|
||||
|
||||
//
|
||||
// Extract User Select By Param and Type ...
|
||||
userSelectByParam = GetUserSelectByParam(inviteRequest);
|
||||
var userSelectBy = GetUserSelectByType(userSelectByParam);
|
||||
|
||||
//
|
||||
// Check Invitation Only done with Email Address ...
|
||||
if (userSelectBy != XUserSelectBy.Email)
|
||||
{
|
||||
XException.InvalidData.Throw();
|
||||
}
|
||||
|
||||
//
|
||||
// Check Invited Email and Given Email to be Same ...
|
||||
if (emailAddress.IsNullOrEmpty() ||
|
||||
(!emailAddress.IsNullOrEmpty() &&
|
||||
!emailAddress.IsValidEmail()))
|
||||
{
|
||||
XException.InvalidEmailAddress.Throw();
|
||||
}
|
||||
var isSameEmail = inviteRequest.Context.Email.ToNormalString() == emailAddress.ToNormalString();
|
||||
if (!isSameEmail)
|
||||
{
|
||||
XException.EmailsSame.Throw();
|
||||
}
|
||||
|
||||
//
|
||||
// Update Context Email for Request Action ...
|
||||
context.Email = userSelectByParam.ToNormalString();
|
||||
context.EmailVerified = true;
|
||||
}
|
||||
|
||||
//
|
||||
var result = await ActionRequest(
|
||||
lang,
|
||||
device,
|
||||
userSelectByParam,
|
||||
XAction.Registration,
|
||||
context,
|
||||
forceCheckUserExists: false
|
||||
);
|
||||
|
||||
//
|
||||
// Remove Invitation Token ...
|
||||
if (!invitationHash.IsNullOrEmpty())
|
||||
{
|
||||
await RemoveTokenByHash(invitationHash);
|
||||
}
|
||||
|
||||
//
|
||||
// Return Result ...
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add User Account Info
|
||||
/// </summary>
|
||||
/// <param name="lang">specify destination language</param>
|
||||
/// <param name="device">an instance of <see>XDevice</see></param>
|
||||
/// <param name="actionToken">a token which approved user action</param>
|
||||
/// <param name="userName">user name</param>
|
||||
/// <param name="password">assigne password</param>
|
||||
/// <returns>an instance of <see>XActionResponse</see></returns>
|
||||
public async Task<XActionResponse> AddAcountInfo(
|
||||
string lang,
|
||||
XDevice device,
|
||||
string actionToken,
|
||||
string userName,
|
||||
string password
|
||||
)
|
||||
{
|
||||
//
|
||||
// Validate Args ...
|
||||
await ValidationProvider
|
||||
.GroupValidationBuilder()
|
||||
.AddNotEmpty(
|
||||
lang,
|
||||
actionToken,
|
||||
userName,
|
||||
password)
|
||||
.AddNotNull(device)
|
||||
.ValidateGroupAsync();
|
||||
|
||||
//
|
||||
// Check Device Validation ...
|
||||
await ValidateDeviceForActions(device);
|
||||
|
||||
//
|
||||
// Validate a UserSelector is Available or not ...
|
||||
await ValidateCanRegister(userName);
|
||||
|
||||
//
|
||||
// Check UserName is Unique ...
|
||||
await ValidateUserNotExists(userName);
|
||||
|
||||
//
|
||||
// Get Related Token to Registration Hash and Validate it,
|
||||
// then Parse XActionRequest Instance from Token ...
|
||||
var request = await ValidateAndParseActionHash(actionToken);
|
||||
|
||||
//
|
||||
// Validate Requeired Data for Register User must be in Action Context ...
|
||||
await ValidationProvider
|
||||
.GroupValidationBuilder()
|
||||
.AddNotNull(
|
||||
request.Context,
|
||||
request.Context.Device,
|
||||
request.Context.DateOfBirth)
|
||||
.AddNotEmpty(
|
||||
request.Context.Email,
|
||||
request.Context.MobileNumber,
|
||||
request.Context.FirstName,
|
||||
request.Context.LastName)
|
||||
.AddEmailAddress(request.Context.Email)
|
||||
.AddMobileNumber(request.Context.MobileNumber)
|
||||
.ValidateGroupAsync();
|
||||
|
||||
//
|
||||
// Validate two Device Must Same ...
|
||||
ValidateRequestAndGivenDevices(device, request.Context.Device);
|
||||
|
||||
//
|
||||
// Extract UserSelectByParam from XActionRequest ...
|
||||
var userSelectByParam = GetUserSelectByParam(request);
|
||||
|
||||
//
|
||||
// Validate Request Pointer User doesn't Exists ...
|
||||
await ValidateUserNotExists(userSelectByParam);
|
||||
|
||||
//
|
||||
// Create an Empty Instance of XUser ...
|
||||
var xUser = new XUser();
|
||||
|
||||
//
|
||||
// Start Filling User Fields ...
|
||||
xUser.UserName = userName;
|
||||
xUser.Email = request.Context.Email;
|
||||
xUser.PhoneNumber = request.Context.MobileNumber;
|
||||
|
||||
//
|
||||
xUser.FirstName = request.Context.FirstName;
|
||||
xUser.LastName = request.Context.LastName;
|
||||
xUser.DateOfBirth = request.Context.DateOfBirth;
|
||||
|
||||
//
|
||||
xUser.EmailConfirmed = request.Context.EmailVerified;
|
||||
xUser.PhoneNumberConfirmed = request.Context.MobileVerified;
|
||||
|
||||
//
|
||||
xUser.CreationDate = DateTime.UtcNow;
|
||||
xUser.LastLogin = null;
|
||||
|
||||
//
|
||||
#region Handling New User IsEnable State ...
|
||||
//
|
||||
// By Default New Users will Enables ...
|
||||
xUser.IsEnable = true;
|
||||
xUser.IsBanned = false;
|
||||
|
||||
//
|
||||
// Check Auto Enable Users ...
|
||||
if (Configuration.RequireRegistrationConfirm)
|
||||
{
|
||||
xUser.IsEnable = false;
|
||||
}
|
||||
|
||||
//
|
||||
// Check Auto Confirm Email Users ...
|
||||
if (Configuration.AutoConfirmNewUsersEmail)
|
||||
{
|
||||
xUser.EmailConfirmed = true;
|
||||
}
|
||||
#endregion
|
||||
|
||||
//
|
||||
// Check Auto Confirm Phone Number Users ...
|
||||
if (Configuration.AutoConfirmNewUsersPhoneNumber)
|
||||
{
|
||||
xUser.PhoneNumberConfirmed = true;
|
||||
}
|
||||
|
||||
//
|
||||
// Validating given Password ...
|
||||
await ValidatePasswordPolicies(xUser, password);
|
||||
|
||||
//
|
||||
// Now add new User to Database ...
|
||||
var createUserResult = await CreateUserAsync(xUser, password);
|
||||
if (!createUserResult.Succeeded)
|
||||
{
|
||||
XException.ActionFailed.Throw();
|
||||
}
|
||||
|
||||
//
|
||||
// Update Request UserId Value ...
|
||||
request.Context.UserId = xUser.Id;
|
||||
request.Context.UserName = xUser.UserName;
|
||||
|
||||
//
|
||||
#region Handling New User Role ...
|
||||
//
|
||||
// Now Try to Assign Default User role ...
|
||||
var isContainsDefaultUserRole = !Configuration.NewUsersRole.IsNullOrEmpty();
|
||||
if (isContainsDefaultUserRole)
|
||||
{
|
||||
//
|
||||
var newUserRoleName = Configuration.NewUsersRole;
|
||||
var isNewUserRoleExists = await IsRoleExistsAsync(newUserRoleName);
|
||||
//
|
||||
// Make Sure New User role Exists ...
|
||||
if (!isNewUserRoleExists)
|
||||
{
|
||||
//
|
||||
// Create Default Roles ...
|
||||
await CreateIdentityRoles();
|
||||
}
|
||||
|
||||
//
|
||||
// Assign User to Role ...
|
||||
var assignNewUserToRoleResult = await AddUserToRoleAsync(xUser, newUserRoleName);
|
||||
if (!assignNewUserToRoleResult.Succeeded)
|
||||
{
|
||||
//
|
||||
// Delete User if Role Assignment Faild ...
|
||||
await UserManager.DeleteAsync(xUser);
|
||||
|
||||
//
|
||||
// Thrown ActionFailed Error ...
|
||||
XException.ActionFailed.Throw();
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
//
|
||||
#region Handle Profile Image ...
|
||||
// Save and Attach Profile Image to User if it's Added Before ...
|
||||
if (!request.Context.UserId.IsNullOrEmpty() &&
|
||||
!request.Context.Thubmnail.IsNull())
|
||||
{
|
||||
//
|
||||
xUser = await ValidateUserExistsAndRetrieve(
|
||||
userSelectByParam,
|
||||
containDetails: true,
|
||||
checkCanLoginPolicies: false,
|
||||
checkIsBanned: false,
|
||||
ignoreDisabledUser: true,
|
||||
exception: XException.NotFound.ToException()
|
||||
);
|
||||
|
||||
//
|
||||
// Handle Saving File and Attach it to User ...
|
||||
var saveResult = await StorageProvider
|
||||
.HandleProfileImageSave(request.Context.Thubmnail);
|
||||
|
||||
//
|
||||
var xProfileImage = new XProfileImage
|
||||
{
|
||||
UserId = request.Context.UserId,
|
||||
Name = saveResult.FileName,
|
||||
Path = saveResult.FilePath,
|
||||
Thumb = saveResult.Thmbnail,
|
||||
ThumbPath = saveResult.ThmbnailPath
|
||||
};
|
||||
|
||||
//
|
||||
xUser.Avatars.Add(xProfileImage);
|
||||
var updateResult = await UpdateUserAsync(
|
||||
xUser,
|
||||
checkCanLoginPolicies: false,
|
||||
checkIsBanned: false
|
||||
);
|
||||
|
||||
//
|
||||
if (updateResult.Succeeded)
|
||||
{
|
||||
//
|
||||
xUser.Avatar = xProfileImage.ThumbPath;
|
||||
await UpdateUserAsync(
|
||||
xUser,
|
||||
checkCanLoginPolicies: false,
|
||||
checkIsBanned: false
|
||||
);
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
//
|
||||
// Handle Attaching User device ...
|
||||
var xUserDevice = await AddUserRelatedDevice(
|
||||
userSelectByParam,
|
||||
device,
|
||||
checkCanLoginPolicies: false);
|
||||
|
||||
//
|
||||
// Prepare New Token Result Resource,
|
||||
// by Requesting an Action ...
|
||||
var result = await ActionRequest(
|
||||
lang,
|
||||
device,
|
||||
userSelectByParam,
|
||||
XAction.Finish,
|
||||
request.Context,
|
||||
forceCheckUserExists: false
|
||||
);
|
||||
|
||||
//
|
||||
// Remove Previous Token ...
|
||||
await RemoveTokenByHash(actionToken);
|
||||
|
||||
//
|
||||
// Return Result ...
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attach Profile Image
|
||||
/// </summary>
|
||||
/// <param name="actionToken">a token which approved user action</param>
|
||||
/// <param name="file">an instance of <see>IFormFile</see> for user's Avatar</param>
|
||||
/// <returns>an instance of <see>XActionResponse</see></returns>
|
||||
public async Task<XActionResponse> AttachProfileImage(
|
||||
string actionToken,
|
||||
IFormFile file
|
||||
)
|
||||
{
|
||||
//
|
||||
// Validate Args ...
|
||||
await ValidationProvider
|
||||
.GroupValidationBuilder()
|
||||
.AddNotEmpty(actionToken)
|
||||
.AddNotNull(file)
|
||||
.ValidateGroupAsync();
|
||||
|
||||
//
|
||||
// Get Related Token to Registration Hash and Validate it,
|
||||
// then Parse XActionRequest Instance from Token ...
|
||||
var request = await ValidateAndParseActionHash(actionToken);
|
||||
|
||||
//
|
||||
// Extract UserSelectByParam from XActionRequest ...
|
||||
var userSelectByParam = GetUserSelectByParam(request);
|
||||
if (userSelectByParam.IsNullOrEmpty())
|
||||
{
|
||||
XException.InvalidData.Throw();
|
||||
}
|
||||
|
||||
//
|
||||
// Validate Given FormFile as Profile Image ...
|
||||
StorageProvider.ValidateProfileImage(file);
|
||||
|
||||
//
|
||||
request.Context.Thubmnail = file;
|
||||
|
||||
//
|
||||
// Save and Attach Profile Image to User if it's Added Before ...
|
||||
if (!request.Context.UserId.IsNullOrEmpty())
|
||||
{
|
||||
//
|
||||
var xUser = await ValidateUserExistsAndRetrieve(
|
||||
userSelectByParam,
|
||||
containDetails: true,
|
||||
checkCanLoginPolicies: false,
|
||||
checkIsBanned: false,
|
||||
ignoreDisabledUser: true,
|
||||
exception: XException.NotFound.ToException()
|
||||
);
|
||||
|
||||
//
|
||||
// Handle Saving File and Attach it to User ...
|
||||
var saveResult = await StorageProvider.HandleProfileImageSave(file);
|
||||
|
||||
//
|
||||
var xProfileImage = new XProfileImage
|
||||
{
|
||||
UserId = request.Context.UserId,
|
||||
Name = saveResult.FileName,
|
||||
Path = saveResult.FilePath,
|
||||
Thumb = saveResult.Thmbnail,
|
||||
ThumbPath = saveResult.ThmbnailPath
|
||||
};
|
||||
|
||||
//
|
||||
xUser.Avatars.Add(xProfileImage);
|
||||
var updateResult = await UpdateUserAsync(
|
||||
xUser,
|
||||
checkCanLoginPolicies: false,
|
||||
checkIsBanned: false
|
||||
);
|
||||
|
||||
//
|
||||
if (updateResult.Succeeded)
|
||||
{
|
||||
//
|
||||
xUser.Avatar = xProfileImage.ThumbPath;
|
||||
await UpdateUserAsync(
|
||||
xUser,
|
||||
checkCanLoginPolicies: false,
|
||||
checkIsBanned: false
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Remove Invitation Token ...
|
||||
if (!actionToken.IsNullOrEmpty())
|
||||
{
|
||||
await RemoveTokenByHash(actionToken);
|
||||
}
|
||||
|
||||
//
|
||||
var result = await ActionRequest(
|
||||
request.Context.Lang,
|
||||
request.Context.Device,
|
||||
userSelectByParam,
|
||||
XAction.ProfileAction,
|
||||
request.Context,
|
||||
forceCheckUserExists: false
|
||||
);
|
||||
|
||||
//
|
||||
// Return Result ...
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finishing Registration
|
||||
/// </summary>
|
||||
/// <param name="lang">specify destination language</param>
|
||||
/// <param name="device">an instance of <see>XDevice</see></param>
|
||||
/// <param name="userSelectByParam">user identifier</param>
|
||||
/// <param name="password">assigne password</param>
|
||||
/// <param name="returnUrl">return url for invitation user to redirect</param>
|
||||
/// <returns></returns>
|
||||
public async Task FinishRegistration(
|
||||
string lang,
|
||||
XDevice device,
|
||||
string userSelectByParam,
|
||||
string password,
|
||||
string returnUrl
|
||||
)
|
||||
{
|
||||
//
|
||||
// Validate Args ...
|
||||
await ValidationProvider
|
||||
.GroupValidationBuilder()
|
||||
.AddNotEmpty(lang, userSelectByParam, password, returnUrl)
|
||||
.AddNotNull(device)
|
||||
.AddUrl(returnUrl)
|
||||
.ValidateGroupAsync();
|
||||
|
||||
//
|
||||
// Validate User Exists And Retrieve it ...
|
||||
var xUser = await ValidateUserExistsAndRetrieve(
|
||||
userSelectByParam,
|
||||
containDetails: false,
|
||||
checkCanLoginPolicies: false,
|
||||
checkIsBanned: false,
|
||||
ignoreDisabledUser: true,
|
||||
forceAdmin: false
|
||||
);
|
||||
|
||||
//
|
||||
#region Handling New User Email Notification Senario ...
|
||||
//
|
||||
// Here we Check if User is Enable and Approved Email
|
||||
// send RegisteredMsg and if User is not Enable and
|
||||
// Verify Registration value in Configuration is True
|
||||
// send Verify Registration mail to user and handle it ...
|
||||
if (Configuration.RequireRegistrationConfirm)
|
||||
{
|
||||
//
|
||||
if (xUser.IsEnable)
|
||||
{
|
||||
//
|
||||
// Send Registration Finished Message ...
|
||||
// since in this step we need to prevent errors from
|
||||
// going forward. add this try catch here ...
|
||||
try
|
||||
{
|
||||
//
|
||||
var xMessage = GetRegistrationFinishedMessage(lang, xUser.Email, returnUrl);
|
||||
|
||||
//
|
||||
await MessageProvider.SendMailAsync(xMessage);
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
else
|
||||
{
|
||||
//
|
||||
// Send Registration Confirm Message ...
|
||||
// since in this step we need to prevent errors from
|
||||
// going forward. add this try catch here ...
|
||||
try
|
||||
{
|
||||
//
|
||||
var request = await ActionRequest(
|
||||
lang,
|
||||
device,
|
||||
userSelectByParam,
|
||||
XAction.Finish,
|
||||
forceCheckContext: false,
|
||||
forceCheckUserExists: true);
|
||||
|
||||
//
|
||||
var xMessage = GetRegistrationConfirmMessage(lang, xUser.Email, request.Token, returnUrl);
|
||||
|
||||
//
|
||||
await MessageProvider.SendMailAsync(xMessage);
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
}
|
||||
|
||||
// //
|
||||
// await RemoveTokenByDevice (device);
|
||||
#endregion
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,722 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using xCommons.Extensions;
|
||||
using xExceptions.Constants;
|
||||
using xIdentityModels.Constants;
|
||||
using xIdentityModels.Extensions;
|
||||
using xIdentityModels.Models;
|
||||
using xIdentityModels.Navigations;
|
||||
|
||||
namespace xIds.Providers
|
||||
{
|
||||
public partial class XIdentityManager
|
||||
{
|
||||
//
|
||||
#region Action Handlers ...
|
||||
/// <summary>
|
||||
/// Request an Action
|
||||
/// </summary>
|
||||
/// <param name="lang">specify destination language</param>
|
||||
/// <param name="device">an instance of <see>XDevice</see></param>
|
||||
/// <param name="userSelectByParam">specified user's identifier</param>
|
||||
/// <param name="step">a member of <see>XAction</see> which represent Action Step</param>
|
||||
/// <param name="context">an instance of <see>XActionRequestContext</see> which provides required informations for specified step</param>
|
||||
/// <param name="forceCheckContext">specify checking context, default is false</param>
|
||||
/// <param name="forceCheckUserExists">specify check user exists, default is true</param>
|
||||
/// <param name="forceCheckUserDevice">specify check user and device relation, default is false</param>
|
||||
/// <param name="forceRenewToken">specifies force renew Action Token if it's expired, default is false</param>
|
||||
/// <returns>an instance of <see>XActionResponse</see></returns>
|
||||
private async Task<XActionResponse> ActionRequest(
|
||||
string lang,
|
||||
XDevice device,
|
||||
string userSelectByParam,
|
||||
XAction step,
|
||||
XActionRequestContext context = null,
|
||||
bool forceCheckContext = false,
|
||||
bool forceCheckUserExists = true,
|
||||
bool forceCheckUserDevice = false,
|
||||
bool forceRenewToken = false
|
||||
)
|
||||
{
|
||||
//
|
||||
// Validate Args ...
|
||||
ValidationProvider.NotEmpty(lang);
|
||||
ValidationProvider.NotNull(device);
|
||||
|
||||
//
|
||||
// Check Device not Banned ...
|
||||
await ValidateDeviceForActions(device);
|
||||
|
||||
//
|
||||
// Check User Exists and Device Relation ...
|
||||
if (forceCheckUserExists)
|
||||
{
|
||||
if (forceCheckUserDevice)
|
||||
{
|
||||
//
|
||||
if (context.Device != null &&
|
||||
!device.IsSameAs(context.Device))
|
||||
{
|
||||
XException.InvalidDevice.Throw();
|
||||
}
|
||||
|
||||
//
|
||||
await ValidateUserAndDeviceRelation(userSelectByParam, device);
|
||||
}
|
||||
else
|
||||
{
|
||||
await ValidateUserExistsAsync(userSelectByParam);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
XToken xToken = null;
|
||||
XActionResponse result = null;
|
||||
XActionRequestToken xRequest = null;
|
||||
|
||||
//
|
||||
var isExists = false;
|
||||
var forceUserSelectByParamNotEmpty = true;
|
||||
switch (step)
|
||||
{
|
||||
case XAction.Registration:
|
||||
//
|
||||
forceUserSelectByParamNotEmpty = false;
|
||||
isExists = await IsTokenExistsByDeviceAndType(device, step);
|
||||
|
||||
//
|
||||
if (isExists)
|
||||
{
|
||||
xToken = await ValidateAndRetieveTokenByDeviceAndType(device, step);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
//
|
||||
isExists = await IsTokenExistsByUserAndType(userSelectByParam, step);
|
||||
|
||||
//
|
||||
if (isExists)
|
||||
{
|
||||
xToken = await ValidateAndRetieveTokenByUserAndType(userSelectByParam, step);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
//
|
||||
if (isExists)
|
||||
{
|
||||
//
|
||||
var isValidToken = IsValidToken(xToken.Token);
|
||||
if (!isValidToken && !forceRenewToken)
|
||||
{
|
||||
switch (step)
|
||||
{
|
||||
case XAction.Registration:
|
||||
case XAction.Invite:
|
||||
forceRenewToken = true;
|
||||
break;
|
||||
|
||||
default:
|
||||
throw XException.InvalidToken.ToException();
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
if (!isValidToken && forceRenewToken)
|
||||
{
|
||||
await ValidateAndHandleToken(xToken.Token);
|
||||
return await ActionRequest(
|
||||
lang,
|
||||
device,
|
||||
userSelectByParam,
|
||||
step,
|
||||
context,
|
||||
forceCheckContext,
|
||||
forceCheckUserExists,
|
||||
forceCheckUserDevice,
|
||||
forceRenewToken);
|
||||
}
|
||||
|
||||
//
|
||||
xRequest = ValidateAndParseActionToken(xToken.Token);
|
||||
|
||||
//
|
||||
// Check Exists Context ...
|
||||
if (forceCheckContext)
|
||||
{
|
||||
//
|
||||
var isSameContext = xRequest.Context.IsSameAs(context);
|
||||
if (!isSameContext)
|
||||
{
|
||||
XException.InvalidData.Throw();
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//
|
||||
if (context != null)
|
||||
{
|
||||
//
|
||||
var contextUserSelectByParam = GetUserSelectByParam(
|
||||
context,
|
||||
false,
|
||||
new List<XUserSelectBy> {
|
||||
XUserSelectBy.ID,
|
||||
XUserSelectBy.Email,
|
||||
XUserSelectBy.MobileNumber
|
||||
});
|
||||
var contextLang = context.Lang;
|
||||
var contextDevice = context.Device;
|
||||
|
||||
//
|
||||
// prevent from issues on Registration Request ...
|
||||
if (forceUserSelectByParamNotEmpty)
|
||||
{
|
||||
var userSelectByType = GetUserSelectByType(userSelectByParam);
|
||||
var contextUserSelectByType = GetUserSelectByType(contextUserSelectByParam);
|
||||
if (contextUserSelectByType != userSelectByType)
|
||||
{
|
||||
//
|
||||
var excludesList = GenerateUserSelectByExcludes(new List<XUserSelectBy> { userSelectByType });
|
||||
|
||||
//
|
||||
contextUserSelectByParam = GetUserSelectByParam(context, false, excludesList);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Fix Device Not Same on Invitation and Request
|
||||
if (step == XAction.Registration)
|
||||
{
|
||||
//
|
||||
context.Device = device;
|
||||
contextDevice = context.Device;
|
||||
}
|
||||
|
||||
//
|
||||
// Validate Context Data must be Same as Request Data ...
|
||||
if ((!contextUserSelectByParam.IsNullOrEmpty() &&
|
||||
contextUserSelectByParam != userSelectByParam) ||
|
||||
(!contextLang.IsNullOrEmpty() &&
|
||||
contextLang != lang) ||
|
||||
(contextDevice != null &&
|
||||
!contextDevice.IsSameAs(device)))
|
||||
{
|
||||
XException.InvalidData.Throw();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//
|
||||
context = new XActionRequestContext
|
||||
{
|
||||
Lang = lang,
|
||||
Device = device
|
||||
};
|
||||
|
||||
//
|
||||
// Add Content ...
|
||||
var userSelectByType = GetUserSelectByType(
|
||||
userSelectByParam,
|
||||
forceUserSelectByParamNotEmpty);
|
||||
switch (userSelectByType)
|
||||
{
|
||||
case XUserSelectBy.MobileNumber:
|
||||
context.MobileNumber = userSelectByParam;
|
||||
break;
|
||||
|
||||
case XUserSelectBy.Email:
|
||||
context.Email = userSelectByParam;
|
||||
break;
|
||||
|
||||
case XUserSelectBy.Username:
|
||||
context.UserName = userSelectByParam;
|
||||
break;
|
||||
|
||||
case XUserSelectBy.ID:
|
||||
context.UserId = userSelectByParam;
|
||||
break;
|
||||
|
||||
case XUserSelectBy.NotSpecified:
|
||||
default:
|
||||
if (forceCheckUserExists)
|
||||
{
|
||||
XException.InvalidArgs.Throw();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Generate Registration Request ...
|
||||
xRequest = new XActionRequestToken
|
||||
{
|
||||
Action = step,
|
||||
Context = context
|
||||
};
|
||||
|
||||
//
|
||||
// Add User Id if Exists to Context ...
|
||||
var isUserExists = await IsUserExistsAsync(userSelectByParam);
|
||||
if (isUserExists)
|
||||
{
|
||||
//
|
||||
var user = await GetUserAsync(userSelectByParam);
|
||||
if (user != null)
|
||||
{
|
||||
xRequest.Context.UserId = user.Id;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
xRequest.Prepare(Configuration.IdentitySecretKey);
|
||||
|
||||
//
|
||||
xToken = await HashRequest(
|
||||
xRequest,
|
||||
forceUserSelectByParamNotEmpty);
|
||||
}
|
||||
|
||||
//
|
||||
// When XToken Exists Passed XToken ...
|
||||
result = GetActionResultResponse(xToken);
|
||||
|
||||
//
|
||||
if (result == null)
|
||||
{
|
||||
XException.ActionFailed.Throw();
|
||||
}
|
||||
|
||||
//
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Request a Mobile Verification Code
|
||||
/// </summary>
|
||||
/// <param name="lang">specify destination language</param>
|
||||
/// <param name="device">an instance of <see>XDevice</see></param>
|
||||
/// <param name="actionToken">a token which approved user action</param>
|
||||
/// <param name="mobileNumber">the mobile number which is going to request a verification code</param>
|
||||
/// <param name="checkMobileInUse">check mobile number is in use or not, default is true</param>
|
||||
/// <returns>an instance of <see>XActionResponse</see></returns>
|
||||
private async Task<XActionResponse> RequestMobileVerificationCode(
|
||||
string lang,
|
||||
XDevice device,
|
||||
string actionToken,
|
||||
string mobileNumber,
|
||||
bool checkMobileInUse = true
|
||||
)
|
||||
{
|
||||
//
|
||||
// Validate Args ...
|
||||
await ValidationProvider
|
||||
.GroupValidationBuilder()
|
||||
.AddNotEmpty(lang, actionToken, mobileNumber)
|
||||
.AddMobileNumber(mobileNumber)
|
||||
.AddNotNull(device)
|
||||
.ValidateGroupAsync();
|
||||
|
||||
//
|
||||
// Check Device Validation ...
|
||||
await ValidateDeviceForActions(device);
|
||||
|
||||
//
|
||||
// Check Mobile is Unique ...
|
||||
if (checkMobileInUse)
|
||||
{
|
||||
var isUserExists = await IsUserExistsAsync(mobileNumber);
|
||||
if (isUserExists)
|
||||
{
|
||||
//
|
||||
var xUser = await GetUserAsync(mobileNumber);
|
||||
|
||||
//
|
||||
if (xUser.PhoneNumberConfirmed)
|
||||
{
|
||||
XException.MobileInUsed.Throw();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Get Related Token to Registration Hash and Validate it,
|
||||
// then Parse XActionRequest Instance from Token ...
|
||||
var request = await ValidateAndParseActionHash(actionToken);
|
||||
|
||||
//
|
||||
// Validate two Device Must Same ...
|
||||
ValidateRequestAndGiveDevices(device, request.Context.Device);
|
||||
|
||||
//
|
||||
// Extract UserSelectByParam from XActionRequest ...
|
||||
var userSelectByParam = GetUserSelectByParam(
|
||||
request,
|
||||
forceNotNull: false,
|
||||
excludes: new List<XUserSelectBy> {
|
||||
XUserSelectBy.ID,
|
||||
XUserSelectBy.Email,
|
||||
XUserSelectBy.MobileNumber
|
||||
});
|
||||
|
||||
//
|
||||
// if it's null, means user doesn't have Invitation Token
|
||||
// and there is no User Selector ...
|
||||
if (userSelectByParam.IsNullOrEmpty())
|
||||
{
|
||||
//
|
||||
// Since Mobile is Unique so it can act as a
|
||||
// UserSelector ...
|
||||
userSelectByParam = mobileNumber;
|
||||
}
|
||||
|
||||
//
|
||||
// Define empty Verification Code
|
||||
// and Empty Verification Request ...
|
||||
var verificationCode = "";
|
||||
XVerificationRequest xVerificationRequest = null;
|
||||
|
||||
//
|
||||
// Check Device Request Verification Code before or Not ...
|
||||
var isDeviceRequested = await IsDeviceRequestedForVerificationCodeBefor(device);
|
||||
if (isDeviceRequested)
|
||||
{
|
||||
//
|
||||
// if Requested before, Retrieve XVerification instance by Device from Db
|
||||
// and Validate it ...
|
||||
xVerificationRequest = await ValidateAndRetrieveVerificationRequest(device);
|
||||
|
||||
//
|
||||
if (xVerificationRequest.VerificationCode.IsNullOrEmpty())
|
||||
{
|
||||
//
|
||||
verificationCode = GenerateRandom().ToString();
|
||||
xVerificationRequest.VerificationCode = verificationCode;
|
||||
|
||||
//
|
||||
await RemoveVerificationCodeRequestByDevice(
|
||||
device,
|
||||
saveChanges: false
|
||||
);
|
||||
await AddVerificationRequest(
|
||||
xVerificationRequest,
|
||||
saveChanges: false
|
||||
);
|
||||
|
||||
//
|
||||
await DbContext.SaveChangesAsync();
|
||||
}
|
||||
|
||||
//
|
||||
// if XVerificationRequest is Valid, it must Contain an Alive Verification Code
|
||||
// so retrieve it and assign it ...
|
||||
verificationCode = xVerificationRequest.VerificationCode;
|
||||
}
|
||||
else
|
||||
{
|
||||
//
|
||||
verificationCode = GenerateRandom().ToString();
|
||||
}
|
||||
|
||||
//
|
||||
// if it's First Verification Request of given Device
|
||||
// Generate a new Verification Code ...
|
||||
if (verificationCode.IsNullOrEmpty())
|
||||
{
|
||||
verificationCode = GenerateRandom().ToString();
|
||||
}
|
||||
|
||||
//
|
||||
// Check Context Mobile and given Mobile ...
|
||||
if (!request.Context.MobileNumber.IsNullOrEmpty() &&
|
||||
mobileNumber != request.Context.MobileNumber)
|
||||
{
|
||||
XException.InvalidMobileNumber.Throw();
|
||||
}
|
||||
|
||||
//
|
||||
// Check User Added To Db or Not ...
|
||||
var isAddedUser = !request.Context.UserId.IsNullOrEmpty();
|
||||
if (isAddedUser)
|
||||
{
|
||||
//
|
||||
// Validate User Exists and Retireve User Object ...
|
||||
var user = await ValidateUserExistsAndRetrieve(
|
||||
userSelectByParam,
|
||||
containDetails: false,
|
||||
checkCanLoginPolicies: false,
|
||||
checkIsBanned: false);
|
||||
|
||||
//
|
||||
// Check Mobiles Same ...
|
||||
if (user.PhoneNumber == mobileNumber &&
|
||||
user.PhoneNumberConfirmed)
|
||||
{
|
||||
XException.MobilesSame.Throw();
|
||||
}
|
||||
|
||||
//
|
||||
// Update User Object's Mobile Number and
|
||||
// Confirmation Status ...
|
||||
user.PhoneNumber = mobileNumber;
|
||||
user.PhoneNumberConfirmed = false;
|
||||
|
||||
//
|
||||
// Save Changes to Db ...
|
||||
await UpdateUserAsync(user, checkCanLoginPolicies: false, checkIsBanned: false);
|
||||
}
|
||||
|
||||
//
|
||||
// Prepare Verification Request by Checking Next Tries,
|
||||
// Device Status, and etc, Update or Add XVerificationRequest to db
|
||||
// and Retrieve the added Entity ...
|
||||
xVerificationRequest = await HandleVerificationRequestPreparation(verificationCode, request);
|
||||
|
||||
//
|
||||
// Update Request Context MobileNumber
|
||||
// and it's Confirmation Status ...
|
||||
request.Context.MobileNumber = mobileNumber;
|
||||
request.Context.MobileVerified = false;
|
||||
|
||||
//
|
||||
// Remove Previous Token ...
|
||||
await RemoveTokenByHash(actionToken);
|
||||
|
||||
//
|
||||
// Prepare New Token Result Resource,
|
||||
// by Requesting an Action ...
|
||||
var result = await ActionRequest(
|
||||
lang,
|
||||
device,
|
||||
userSelectByParam,
|
||||
XAction.RequestMobileVerificationCode,
|
||||
request.Context,
|
||||
forceCheckUserExists: isAddedUser
|
||||
);
|
||||
|
||||
//
|
||||
// Prepare Message ...
|
||||
var xMessage = GetVerificationCodeMessage(lang, mobileNumber, verificationCode);
|
||||
|
||||
//
|
||||
// Send Verification Code Message to User Using SMS Provider ...
|
||||
await MessageProvider.SendSmsAsync(xMessage, throwException: false);
|
||||
|
||||
//
|
||||
// Return Result ...
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Request a Email Verification Code
|
||||
/// </summary>
|
||||
/// <param name="lang">specify destination language</param>
|
||||
/// <param name="device">an instance of <see>XDevice</see></param>
|
||||
/// <param name="actionToken">a token which approved user action</param>
|
||||
/// <param name="emailAddress">the email address which is going to request a verification code</param>
|
||||
/// <param name="checkEmailInUse">check email address is in use or not, default is true</param>
|
||||
/// <returns>an instance of <see>XActionResponse</see></returns>
|
||||
public async Task<XActionResponse> RequestEmailVerificationCode(
|
||||
string lang,
|
||||
XDevice device,
|
||||
string actionToken,
|
||||
string emailAddress,
|
||||
bool checkEmailInUse = true
|
||||
)
|
||||
{
|
||||
//
|
||||
// Validate Args ...
|
||||
await ValidationProvider
|
||||
.GroupValidationBuilder()
|
||||
.AddNotEmpty(lang, actionToken, emailAddress)
|
||||
.AddEmailAddress(emailAddress)
|
||||
.AddNotNull(device)
|
||||
.ValidateGroupAsync();
|
||||
|
||||
//
|
||||
// Check Device Validation ...
|
||||
await ValidateDeviceForActions(device);
|
||||
|
||||
//
|
||||
// Check Email is Unique ...
|
||||
if (checkEmailInUse)
|
||||
{
|
||||
var isUserExists = await IsUserExistsAsync(emailAddress);
|
||||
if (isUserExists)
|
||||
{
|
||||
//
|
||||
var xUser = await GetUserAsync(emailAddress);
|
||||
|
||||
//
|
||||
if (xUser.EmailConfirmed)
|
||||
{
|
||||
XException.EmailInUsed.Throw();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Get Related Token to Registration Hash and Validate it,
|
||||
// then Parse XActionRequest Instance from Token ...
|
||||
var request = await ValidateAndParseActionHash(actionToken);
|
||||
|
||||
//
|
||||
// Validate two Device Must Same ...
|
||||
ValidateRequestAndGiveDevices(device, request.Context.Device);
|
||||
|
||||
//
|
||||
// Extract UserSelectByParam from XActionRequest ...
|
||||
var userSelectByParam = GetUserSelectByParam(
|
||||
request,
|
||||
forceNotNull: false,
|
||||
excludes: new List<XUserSelectBy> {
|
||||
XUserSelectBy.ID,
|
||||
XUserSelectBy.Email,
|
||||
XUserSelectBy.MobileNumber
|
||||
});
|
||||
|
||||
//
|
||||
// if it's null, means user doesn't have Invitation Token
|
||||
// and there is no User Selector ...
|
||||
if (userSelectByParam.IsNullOrEmpty())
|
||||
{
|
||||
//
|
||||
// Since Email is Unique so it can act as a
|
||||
// UserSelector ...
|
||||
userSelectByParam = emailAddress;
|
||||
}
|
||||
|
||||
//
|
||||
// Define empty Verification Code
|
||||
// and Empty Verification Request ...
|
||||
var verificationCode = "";
|
||||
XVerificationRequest xVerificationRequest = null;
|
||||
|
||||
//
|
||||
// Check Device Request Verification Code before or Not ...
|
||||
var isDeviceRequested = await IsDeviceRequestedForVerificationCodeBefor(device);
|
||||
if (isDeviceRequested)
|
||||
{
|
||||
//
|
||||
// if Requested before, Retrieve XVerification instance by Device from Db
|
||||
// and Validate it ...
|
||||
xVerificationRequest = await ValidateAndRetrieveVerificationRequest(device);
|
||||
|
||||
//
|
||||
if (xVerificationRequest.VerificationCode.IsNullOrEmpty())
|
||||
{
|
||||
//
|
||||
verificationCode = GenerateRandom().ToString();
|
||||
xVerificationRequest.VerificationCode = verificationCode;
|
||||
|
||||
//
|
||||
await RemoveVerificationCodeRequestByDevice(
|
||||
device,
|
||||
saveChanges: false
|
||||
);
|
||||
await AddVerificationRequest(
|
||||
xVerificationRequest,
|
||||
saveChanges: true
|
||||
);
|
||||
}
|
||||
|
||||
//
|
||||
// if XVerificationRequest is Valid, it must Contain an Alive Verification Code
|
||||
// so retrieve it and assign it ...
|
||||
verificationCode = xVerificationRequest.VerificationCode;
|
||||
}
|
||||
else
|
||||
{
|
||||
//
|
||||
verificationCode = GenerateRandom().ToString();
|
||||
}
|
||||
|
||||
//
|
||||
// Check Context Email and given Email ...
|
||||
if (!request.Context.Email.IsNullOrEmpty() &&
|
||||
emailAddress != request.Context.Email)
|
||||
{
|
||||
XException.InvalidEmailAddress.Throw();
|
||||
}
|
||||
|
||||
//
|
||||
// Check User Added To Db or Not ...
|
||||
var isAddedUser = !request.Context.UserId.IsNullOrEmpty();
|
||||
if (isAddedUser)
|
||||
{
|
||||
//
|
||||
// Validate User Exists and Retireve User Object ...
|
||||
var user = await ValidateUserExistsAndRetrieve(
|
||||
userSelectByParam,
|
||||
containDetails: false,
|
||||
checkCanLoginPolicies: false,
|
||||
checkIsBanned: false);
|
||||
|
||||
//
|
||||
// Check Email Same ...
|
||||
if (user.Email == emailAddress &&
|
||||
user.EmailConfirmed)
|
||||
{
|
||||
XException.MobilesSame.Throw();
|
||||
}
|
||||
|
||||
//
|
||||
// Update User Object's Email and
|
||||
// Confirmation Status ...
|
||||
user.Email = emailAddress;
|
||||
user.EmailConfirmed = false;
|
||||
|
||||
//
|
||||
// Save Changes to Db ...
|
||||
await UpdateUserAsync(
|
||||
user,
|
||||
checkCanLoginPolicies: false,
|
||||
checkIsBanned: false
|
||||
);
|
||||
}
|
||||
|
||||
//
|
||||
// Prepare Verification Request by Checking Next Tries,
|
||||
// Device Status, and etc, Update or Add XVerificationRequest to db
|
||||
// and Retrieve the added Entity ...
|
||||
xVerificationRequest = await HandleVerificationRequestPreparation(verificationCode, request);
|
||||
|
||||
//
|
||||
// Update Request Context Email
|
||||
// and it's Confirmation Status ...
|
||||
request.Context.Email = emailAddress;
|
||||
request.Context.EmailVerified = false;
|
||||
|
||||
//
|
||||
// Prepare New Token Result Resource,
|
||||
// by Requesting an Action ...
|
||||
var result = await ActionRequest(
|
||||
lang,
|
||||
device,
|
||||
userSelectByParam,
|
||||
XAction.RequestEmailVerificationCode,
|
||||
request.Context,
|
||||
forceCheckUserExists: isAddedUser
|
||||
);
|
||||
|
||||
//
|
||||
// Remove Hash ...
|
||||
await RemoveTokenByHash(actionToken);
|
||||
|
||||
//
|
||||
// Prepare Message ...
|
||||
var xMessage = GetVerificationCodeMessage(lang, emailAddress, verificationCode);
|
||||
|
||||
//
|
||||
// Send Verification Code Message to User Using Mail Provider ...
|
||||
await MessageProvider.SendMailAsync(xMessage, throwException: false);
|
||||
|
||||
//
|
||||
// Return Result ...
|
||||
return result;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,273 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using xCommons.Extensions;
|
||||
using xExceptions.Constants;
|
||||
using xIdentityModels;
|
||||
|
||||
namespace xIds.Providers
|
||||
{
|
||||
public partial class XIdentityManager
|
||||
{
|
||||
//
|
||||
#region Role Actions ...
|
||||
/// <summary>
|
||||
/// Check a Role exists or not
|
||||
/// </summary>
|
||||
/// <param name="roleName">role name</param>
|
||||
/// <returns>a boolean value</returns>
|
||||
public async Task<bool> IsRoleExistsAsync(
|
||||
string roleName
|
||||
)
|
||||
{
|
||||
//
|
||||
if (roleName.IsNullOrEmpty())
|
||||
{
|
||||
XException.InvalidArgs.Throw();
|
||||
}
|
||||
|
||||
//
|
||||
return await RoleManager.RoleExistsAsync(roleName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a new Role
|
||||
/// </summary>
|
||||
/// <param name="roleName">role name</param>
|
||||
/// <returns>an instance of <see>IdentityResult</see></returns>
|
||||
public async Task<IdentityResult> CreateRoleAsync(
|
||||
string roleName
|
||||
)
|
||||
{
|
||||
//
|
||||
var isExists = await RoleManager
|
||||
.RoleExistsAsync(roleName);
|
||||
if (isExists)
|
||||
{
|
||||
XException.Duplicate.Throw();
|
||||
}
|
||||
|
||||
//
|
||||
var result = await RoleManager
|
||||
.CreateAsync(new IdentityRole(roleName));
|
||||
|
||||
//
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a Role
|
||||
/// </summary>
|
||||
/// <param name="roleName">role name</param>
|
||||
/// <returns>an instance of <see>IdentityRole</see></returns>
|
||||
public async Task<IdentityRole> GetRoleAsync(
|
||||
string roleName
|
||||
)
|
||||
{
|
||||
//
|
||||
var isExists = await IsRoleExistsAsync(roleName);
|
||||
if (!isExists)
|
||||
{
|
||||
XException.NotFound.Throw();
|
||||
}
|
||||
|
||||
//
|
||||
var result = await RoleManager
|
||||
.FindByNameAsync(roleName);
|
||||
|
||||
//
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove a User From Role
|
||||
/// </summary>
|
||||
/// <param name="user">an instance of <see>XUser</see></param>
|
||||
/// <param name="roleName">role name</param>
|
||||
/// <returns>an instance of <see>IdentityResult</see></returns>
|
||||
public async Task<IdentityResult> RemoveFromRoleAsync(
|
||||
XUser user,
|
||||
string roleName
|
||||
)
|
||||
{
|
||||
return await UserManager.RemoveFromRoleAsync(user, roleName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove a User From Roles
|
||||
/// </summary>
|
||||
/// <param name="user">an instance of <see>XUser</see></param>
|
||||
/// <param name="roleNames">a collection of role names</param>
|
||||
/// <returns>an instance of <see>IdentityResult</see></returns>
|
||||
public async Task<IdentityResult> RemoveFromRolesAsync(
|
||||
XUser user,
|
||||
IEnumerable<string> roleNames
|
||||
)
|
||||
{
|
||||
return await UserManager.RemoveFromRolesAsync(user, roleNames);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Assign a User to Specific Role
|
||||
/// </summary>
|
||||
/// <param name="user">an instance of <see>XUser</see></param>
|
||||
/// <param name="roleName">role name</param>
|
||||
/// <returns>an instance of <see>IdentityResult</see></returns>
|
||||
public async Task<IdentityResult> AddUserToRoleAsync(
|
||||
XUser user,
|
||||
string roleName
|
||||
)
|
||||
{
|
||||
//
|
||||
if (user == null ||
|
||||
roleName.IsNullOrEmpty())
|
||||
{
|
||||
XException.InvalidArgs.Throw();
|
||||
}
|
||||
|
||||
//
|
||||
var userSelectByParam = GetUserSelectByParam(user);
|
||||
var isExistsUser = await IsUserExistsAsync(userSelectByParam);
|
||||
var isExistsRole = await IsRoleExistsAsync(roleName);
|
||||
if (!isExistsUser || !isExistsRole)
|
||||
{
|
||||
XException.NotFound.Throw();
|
||||
}
|
||||
|
||||
//
|
||||
var result = await UserManager
|
||||
.AddToRoleAsync(user, roleName);
|
||||
|
||||
//
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieve Role Name by it's ID
|
||||
/// </summary>
|
||||
/// <param name="roleId">role id</param>
|
||||
/// <returns>role name as string</returns>
|
||||
public async Task<string> GetRoleNameAsync(
|
||||
string roleId
|
||||
)
|
||||
{
|
||||
//
|
||||
// Validate Args ...
|
||||
if (roleId.IsNullOrEmpty())
|
||||
{
|
||||
XException.InvalidArgs.Throw();
|
||||
}
|
||||
|
||||
//
|
||||
var role = await RoleManager
|
||||
.FindByIdAsync(roleId);
|
||||
|
||||
//
|
||||
return role.Name.ToNormalString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a List Of User Roles
|
||||
/// </summary>
|
||||
/// <param name="userSelectByParam">user identifier</param>
|
||||
/// <param name="checkCanLoginPolicies"></param>
|
||||
/// <param name="checkIsBanned"></param>
|
||||
/// <returns>a collection of role names</returns>
|
||||
public async Task<IEnumerable<string>> GetRoleNamesAsync(
|
||||
string userSelectByParam,
|
||||
bool checkCanLoginPolicies = true,
|
||||
bool checkIsBanned = true
|
||||
)
|
||||
{
|
||||
//
|
||||
// Validate Args ...
|
||||
if (userSelectByParam.IsNullOrEmpty())
|
||||
{
|
||||
XException.InvalidArgs.Throw();
|
||||
}
|
||||
|
||||
//
|
||||
var user = await ValidateUserExistsAndRetrieve(
|
||||
userSelectByParam,
|
||||
containDetails: true,
|
||||
checkCanLoginPolicies: checkCanLoginPolicies,
|
||||
checkIsBanned: checkIsBanned
|
||||
);
|
||||
|
||||
//
|
||||
var result = new List<string>();
|
||||
foreach (var role in user.Roles)
|
||||
{
|
||||
//
|
||||
var roleName = await GetRoleNameAsync(role.RoleId);
|
||||
roleName = roleName.ToNormalString();
|
||||
|
||||
//
|
||||
result.Add(roleName);
|
||||
}
|
||||
|
||||
//
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check a User is Adminr not
|
||||
/// </summary>
|
||||
/// <param name="userSelectByParam">user identifier</param>
|
||||
/// <returns>a boolean value</returns>
|
||||
public async Task<bool> IsAdmin(
|
||||
string userSelectByParam
|
||||
)
|
||||
{
|
||||
//
|
||||
// Validate Args ...
|
||||
ValidationProvider.NotEmpty(userSelectByParam);
|
||||
|
||||
//
|
||||
var user = await ValidateUserExistsAndRetrieve(
|
||||
userSelectByParam,
|
||||
containDetails: true,
|
||||
checkCanLoginPolicies: true);
|
||||
|
||||
//
|
||||
var result = await IsAdmin(user);
|
||||
|
||||
//
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check a User is Adminr not
|
||||
/// </summary>
|
||||
/// <param name="user">an instance of <see>XUser</see></param>
|
||||
/// <returns>a boolean value</returns>
|
||||
public async Task<bool> IsAdmin(
|
||||
XUser user
|
||||
)
|
||||
{
|
||||
//
|
||||
// Validate Args ...
|
||||
ValidationProvider.NotNull(user);
|
||||
ValidationProvider.NotEmpty(user.Id);
|
||||
|
||||
//
|
||||
// Retrieve Admin Role ...
|
||||
// TODO: Fix this ...
|
||||
var adminRole = "admin";
|
||||
// XUserRole.Admin
|
||||
// .GetStringValue ()
|
||||
// .ToNormalString ();
|
||||
|
||||
//
|
||||
// Check Admin Role Exists in User Roles or not ...
|
||||
var userRoleNames = await GetRoleNamesAsync(user.Id);
|
||||
var isContainsAdminRole = userRoleNames.Contains(adminRole);
|
||||
|
||||
//
|
||||
// Return Result ...
|
||||
return isContainsAdminRole;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
using System.Threading.Tasks;
|
||||
using xCommons.Extensions;
|
||||
using xExceptions.Constants;
|
||||
using xIdentityModels.Dtos;
|
||||
using xModels.Dtos;
|
||||
using xDataService.Extensions;
|
||||
using xIds.Extensions;
|
||||
using System.Linq;
|
||||
using xIdentityModels.Extensions;
|
||||
|
||||
namespace xIds.Providers
|
||||
{
|
||||
public partial class XIdentityManager
|
||||
{
|
||||
//
|
||||
#region Actions ...
|
||||
/// <summary>
|
||||
/// Query User Profiles
|
||||
/// </summary>
|
||||
/// <param name="requestedUserSelectByParam">requested user's identifier</param>
|
||||
/// <param name="query">how to filter results based on <see>XQuery</see> structure</param>
|
||||
/// <returns>an instance of <see>XQueryResult</see> of <see>XUserProfileDto</see></returns>
|
||||
public async Task<XQueryResult<XUserProfileDto>> QueryOpenToSearchUsers(
|
||||
string requestedUserSelectByParam,
|
||||
XQuery query
|
||||
)
|
||||
{
|
||||
//
|
||||
// Validation ...
|
||||
if (query.IsNull())
|
||||
{
|
||||
XException.InvalidArgs.Throw();
|
||||
}
|
||||
|
||||
//
|
||||
// Normalize ...
|
||||
query = query.NormalizeQuery(DataConfiguration.ToXDataServiceConfig());
|
||||
|
||||
//
|
||||
// Since in Resourceable Entities we have to Search on Locales
|
||||
// we Must Implement Senario Custom ...
|
||||
var totalEntities = GetUsersDbSet()
|
||||
.Where(u => u.OpenToSearch)
|
||||
.ToList()
|
||||
.Where(u => !u.ContainsUserSelectByParam(requestedUserSelectByParam))
|
||||
.Select(u => u.Id)
|
||||
.ToList();
|
||||
|
||||
//
|
||||
var items = await GetUserProfilesAsync(
|
||||
totalEntities,
|
||||
requestedUserSelectByParam,
|
||||
checkIsBanned: false,
|
||||
checkCanLoginPolicies: false,
|
||||
forceCheckRequestedUser: false
|
||||
);
|
||||
|
||||
//
|
||||
var totalItemsCount = items.Count();
|
||||
|
||||
//
|
||||
// Apply Filter ...
|
||||
if (!query.Filter.IsNullOrEmpty())
|
||||
{
|
||||
//
|
||||
items = items
|
||||
.ApplyFilter(query.Filter);
|
||||
}
|
||||
int filteredItemsCount = items.Count();
|
||||
|
||||
//
|
||||
// Count Pages ...
|
||||
var totalPagesCount = query.CountPages(totalItemsCount);
|
||||
var filteredPagesCount = query.CountPages(filteredItemsCount);
|
||||
|
||||
//
|
||||
// Apply Paging and Sorting ...
|
||||
if (totalItemsCount > 0 &&
|
||||
filteredItemsCount > 0)
|
||||
{
|
||||
//
|
||||
// Apply Sorting ...
|
||||
items = items
|
||||
.ToList()
|
||||
.ApplySorting(
|
||||
query.SortBy,
|
||||
query.IsAscending
|
||||
);
|
||||
|
||||
//
|
||||
// Apply Paging ...
|
||||
items = items
|
||||
.ToList()
|
||||
.ApplyPaging(
|
||||
query.Page,
|
||||
query.PageSize
|
||||
);
|
||||
}
|
||||
|
||||
//
|
||||
// Generate Result Object ...
|
||||
// Query = query,
|
||||
var result = new XQueryResult<XUserProfileDto>
|
||||
{
|
||||
Items = items,
|
||||
Page = query.Page,
|
||||
PageSize = query.PageSize,
|
||||
TotalPages = totalPagesCount,
|
||||
TotalItems = totalItemsCount,
|
||||
TotalFilteredPages = filteredPagesCount,
|
||||
TotalFilteredItems = filteredItemsCount
|
||||
};
|
||||
|
||||
//
|
||||
return result;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using xCommons.Extensions;
|
||||
using xExceptions.Constants;
|
||||
using xIdentityModels.Descriptors;
|
||||
using xIdentityModels.Extensions;
|
||||
|
||||
namespace xIds.Providers
|
||||
{
|
||||
public partial class XIdentityManager
|
||||
{
|
||||
//
|
||||
#region Default Identity Preparation Actions ...
|
||||
/// <summary>
|
||||
/// Create Default Roles based on Identity Configuration
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public async Task CreateIdentityRoles()
|
||||
{
|
||||
//
|
||||
if (Configuration == null ||
|
||||
Configuration.IdentityRoles == null ||
|
||||
Configuration.IdentityRoles.Count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
//
|
||||
// Check New Users Role exists in IdentityRole or not ...
|
||||
if (!Configuration.NewUsersRole.IsNullOrEmpty())
|
||||
{
|
||||
//
|
||||
// Ensure NewUsersRole Exists in IdentityRoles ...
|
||||
var isNewUserRoleExistsInIdentityRoles = Configuration
|
||||
.IdentityRoles
|
||||
.Contains(Configuration.NewUsersRole);
|
||||
if (!isNewUserRoleExistsInIdentityRoles)
|
||||
{
|
||||
XException.InvalidArgs.Throw();
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// loop through all roles and create them one by one ...
|
||||
foreach (var roleName in Configuration.IdentityRoles)
|
||||
{
|
||||
//
|
||||
var isRoleExists = await RoleManager.RoleExistsAsync(roleName);
|
||||
if (!isRoleExists)
|
||||
{
|
||||
//
|
||||
var result = await RoleManager.CreateAsync(new IdentityRole(roleName));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a user Based on User Descriptor
|
||||
/// </summary>
|
||||
/// <param name="user">an instance of <see>XIdentityUserDescriptor</see></param>
|
||||
/// <returns></returns>
|
||||
public async Task CreateUserAsync(
|
||||
XIdentityUserDescriptor user
|
||||
)
|
||||
{
|
||||
//
|
||||
// Validate Args ...
|
||||
ValidationProvider.NotNull(user);
|
||||
|
||||
//
|
||||
var isExistsRole = await RoleManager.RoleExistsAsync(user.Role);
|
||||
if (!isExistsRole)
|
||||
{
|
||||
//
|
||||
// Check Assigned role to user exists in app roles ...
|
||||
if (!Configuration
|
||||
.IdentityRoles
|
||||
.Contains(user.Role))
|
||||
{
|
||||
XException.InvalidArgs.Throw();
|
||||
}
|
||||
|
||||
//
|
||||
// Ensure All Roles Created ...
|
||||
await CreateIdentityRoles();
|
||||
}
|
||||
|
||||
//
|
||||
// Check User Exists ...
|
||||
var isExistsUser = await IsUserExistsAsync(user.UserName);
|
||||
if (!isExistsUser)
|
||||
{
|
||||
//
|
||||
// Create a DashboardUser instance based on Configuration Data ...
|
||||
var userEntity = user.ToXUser();
|
||||
|
||||
//
|
||||
// Try to Create User ...
|
||||
var userCreateResult = await CreateUserAsync(
|
||||
userEntity,
|
||||
user.Password
|
||||
);
|
||||
if (!userCreateResult.Succeeded)
|
||||
{
|
||||
XException.ActionFailed.Throw();
|
||||
}
|
||||
|
||||
//
|
||||
// Assign Admin User to it's Role ...
|
||||
var roleAssignResult = await UserManager.AddToRoleAsync(userEntity, user.Role);
|
||||
if (!roleAssignResult.Succeeded)
|
||||
{
|
||||
XException.ActionFailed.Throw();
|
||||
}
|
||||
|
||||
//
|
||||
var claims = await ToJwtClaims(
|
||||
userEntity,
|
||||
checkCanLoginPolicies: true,
|
||||
checkIsBanned: true);
|
||||
|
||||
//
|
||||
var addClaimsResult = await UserManager.AddClaimsAsync(userEntity, claims);
|
||||
if (!addClaimsResult.Succeeded)
|
||||
{
|
||||
XException.ActionFailed.Throw();
|
||||
}
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,742 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IdentityModel.Tokens.Jwt;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.IdentityModel.Tokens;
|
||||
using xCommons.Extensions;
|
||||
using xCommons.Helpers;
|
||||
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 Token Actions ...
|
||||
/// <summary>
|
||||
/// Check a Token Entity Exists or not
|
||||
/// </summary>
|
||||
/// <param name="device">an instance of <see>XDevice</see></param>
|
||||
/// <param name="type">specifies Action Step by a member of <see>XAction</see></param>
|
||||
/// <returns>a boolean value</returns>
|
||||
private async Task<bool> IsTokenExistsByDeviceAndType(
|
||||
XDevice device,
|
||||
XAction type
|
||||
)
|
||||
{
|
||||
//
|
||||
ValidationProvider.NotNull(device);
|
||||
|
||||
//
|
||||
var isExists = false;
|
||||
var enumerator = DbContext.Tokens.AsAsyncEnumerable();
|
||||
await
|
||||
foreach (var token in enumerator)
|
||||
{
|
||||
//
|
||||
if (token.Type == type &&
|
||||
token.Device.IsSameAs(device))
|
||||
{
|
||||
isExists = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
return isExists;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check a Token Entity Exists or not
|
||||
/// </summary>
|
||||
/// <param name="userSelectByParam">specified user's identifier</param>
|
||||
/// <param name="type">specifies Action Step by a member of <see>XAction</see></param>
|
||||
/// <returns>a boolean value</returns>
|
||||
private async Task<bool> IsTokenExistsByUserAndType(
|
||||
string userSelectByParam,
|
||||
XAction type
|
||||
)
|
||||
{
|
||||
//
|
||||
ValidationProvider.NotEmpty(userSelectByParam);
|
||||
|
||||
//
|
||||
var isExists = false;
|
||||
var tokensEnumerator = DbContext.Tokens.AsAsyncEnumerable();
|
||||
await
|
||||
foreach (var token in tokensEnumerator)
|
||||
{
|
||||
//
|
||||
if (token.Type == type &&
|
||||
token.UserSelectByParam
|
||||
.ToNormalString() == userSelectByParam
|
||||
.ToNormalString())
|
||||
{
|
||||
//
|
||||
isExists = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
return isExists;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check a Token Entity Exists or not
|
||||
/// </summary>
|
||||
/// <param name="token">represent specified action token</param>
|
||||
/// <returns>a boolean value</returns>
|
||||
private async Task<bool> IsTokenExistsByToken(
|
||||
string token
|
||||
)
|
||||
{
|
||||
//
|
||||
var result = await DbContext.Tokens
|
||||
.AnyAsync(xt => xt.Token == token);
|
||||
|
||||
//
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check a Token Entity Exists or not
|
||||
/// </summary>
|
||||
/// <param name="hash">represent specified action token hash</param>
|
||||
/// <returns>a boolean value</returns>
|
||||
private async Task<bool> IsTokenExistsByHash(
|
||||
string hash
|
||||
)
|
||||
{
|
||||
//
|
||||
var result = await DbContext.Tokens
|
||||
.AnyAsync(xt => xt.Hash == hash);
|
||||
|
||||
//
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check a Token Entity Exists or not
|
||||
/// </summary>
|
||||
/// <param name="item">an instance of <see>XToken</see></param>
|
||||
/// <returns>a boolean value</returns>
|
||||
private async Task<bool> IsTokenExistsByToken(
|
||||
XToken item
|
||||
)
|
||||
{
|
||||
//
|
||||
var result = false;
|
||||
var tokenEnumerator = DbContext.Tokens
|
||||
.AsAsyncEnumerable();
|
||||
await
|
||||
foreach (var token in tokenEnumerator)
|
||||
{
|
||||
//
|
||||
if (token.Hash == item.Hash &&
|
||||
token.Token == item.Token &&
|
||||
token.Type == item.Type &&
|
||||
token.UserSelectByParam
|
||||
.ToNormalString() == item.UserSelectByParam
|
||||
.ToNormalString() &&
|
||||
token.Device.IsSameAs(item.Device))
|
||||
{
|
||||
//
|
||||
result = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check a Token Entity Exists or not
|
||||
/// </summary>
|
||||
/// <param name="id">specifies token id</param>
|
||||
/// <returns>a boolean value</returns>
|
||||
private async Task<bool> IsTokenExistsById(
|
||||
int id
|
||||
)
|
||||
{
|
||||
return await DbContext.Tokens.AnyAsync(t => t.Id == id);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieve Token Entity
|
||||
/// </summary>
|
||||
/// <param name="device">an instance of <see>XDevice</see></param>
|
||||
/// <param name="type">specifies Action Step by a member of <see>XAction</see></param>
|
||||
/// <returns>an instance of <see>XToken</see></returns>
|
||||
private async Task<XToken> GetTokenByDeviceAndType(
|
||||
XDevice device,
|
||||
XAction type
|
||||
)
|
||||
{
|
||||
//
|
||||
var isExists = await IsTokenExistsByDeviceAndType(device, type);
|
||||
if (!isExists)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
//
|
||||
XToken item = null;
|
||||
var enumerator = DbContext.Tokens.AsAsyncEnumerable();
|
||||
await
|
||||
foreach (var token in enumerator)
|
||||
{
|
||||
//
|
||||
if (token.Type == type &&
|
||||
token.Device.IsSameAs(device))
|
||||
{
|
||||
//
|
||||
item = token;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
return item;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieve Token Entity
|
||||
/// </summary>
|
||||
/// <param name="userSelectByParam">specified user's identifier</param>
|
||||
/// <param name="type">specifies Action Step by a member of <see>XAction</see></param>
|
||||
/// <returns>an instance of <see>XToken</see></returns>
|
||||
private async Task<XToken> GetTokenByUserAndType(
|
||||
string userSelectByParam,
|
||||
XAction type
|
||||
)
|
||||
{
|
||||
//
|
||||
var isExists = await IsTokenExistsByUserAndType(userSelectByParam, type);
|
||||
if (!isExists)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
//
|
||||
XToken item = null;
|
||||
var enumerator = DbContext.Tokens.AsAsyncEnumerable();
|
||||
await
|
||||
foreach (var token in enumerator)
|
||||
{
|
||||
//
|
||||
if (token.Type == type &&
|
||||
token.UserSelectByParam
|
||||
.ToNormalString() == userSelectByParam
|
||||
.ToNormalString())
|
||||
{
|
||||
//
|
||||
item = token;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
return item;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieve a Token Entity
|
||||
/// </summary>
|
||||
/// <param name="token">represent specified action token</param>
|
||||
/// <returns>an instance of <see>XToken</see></returns>
|
||||
private async Task<XToken> GetTokenByToken(
|
||||
string token
|
||||
)
|
||||
{
|
||||
//
|
||||
var isExists = await IsTokenExistsByToken(token);
|
||||
if (!isExists)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
//
|
||||
var result = await DbContext.Tokens
|
||||
.FirstOrDefaultAsync(xt => xt.Token == token);
|
||||
|
||||
//
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieve Related Hash to a Token
|
||||
/// </summary>
|
||||
/// <param name="token">represent specified action token</param>
|
||||
/// <returns>hash string</returns>
|
||||
private async Task<string> GetRelatedHashByToken(
|
||||
string token
|
||||
)
|
||||
{
|
||||
//
|
||||
var item = await GetTokenByToken(token);
|
||||
if (item == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
//
|
||||
return item.Hash;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieve a Token Entity
|
||||
/// </summary>
|
||||
/// <param name="hash">represent specified action token hash</param>
|
||||
/// <returns>an instance of <see>XToken</see></returns>
|
||||
private async Task<XToken> GetTokenByHash(
|
||||
string hash
|
||||
)
|
||||
{
|
||||
//
|
||||
// Validate Args ...
|
||||
ValidationProvider.NotEmpty(hash);
|
||||
|
||||
//
|
||||
var isExists = await IsTokenExistsByHash(hash);
|
||||
if (!isExists)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
//
|
||||
var item = await DbContext.Tokens
|
||||
.FirstOrDefaultAsync(t => t.Hash == hash);
|
||||
|
||||
//
|
||||
return item;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieve Related Token to a Hash string
|
||||
/// </summary>
|
||||
/// <param name="hash">represent specified action token hash</param>
|
||||
/// <returns>token string</returns>
|
||||
private async Task<string> GetRelatedTokenByHash(
|
||||
string hash
|
||||
)
|
||||
{
|
||||
//
|
||||
var item = await GetTokenByHash(hash);
|
||||
if (item == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
//
|
||||
return item.Token;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieve a Token Expiration Date
|
||||
/// </summary>
|
||||
/// <param name="token">represent specified action token</param>
|
||||
/// <returns>an instance of <see>DateTime</see></returns>
|
||||
private DateTime GetTokenExpirationDate(
|
||||
string token
|
||||
)
|
||||
{
|
||||
//
|
||||
ValidationProvider.NotEmpty(token);
|
||||
|
||||
//
|
||||
var result = IdentityHelper.GetTokenExpirationDate(token);
|
||||
|
||||
//
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove a Token Entity
|
||||
/// </summary>
|
||||
/// <param name="token">represent specified action token</param>
|
||||
/// <returns></returns>
|
||||
private async Task RemoveTokenByToken(
|
||||
string token
|
||||
)
|
||||
{
|
||||
//
|
||||
// Validate Args ...
|
||||
ValidationProvider.NotEmpty(token);
|
||||
|
||||
//
|
||||
var isExists = await IsTokenExistsByToken(token);
|
||||
if (!isExists)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
//
|
||||
var item = await GetTokenByToken(token);
|
||||
if (item == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
//
|
||||
DbContext.Tokens.Remove(item);
|
||||
await DbContext.SaveChangesAsync();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove a Token Entity
|
||||
/// </summary>
|
||||
/// <param name="hash">represent specified action token hash</param>
|
||||
/// <returns></returns>
|
||||
private async Task RemoveTokenByHash(
|
||||
string hash
|
||||
)
|
||||
{
|
||||
//
|
||||
// Validate Args ...
|
||||
ValidationProvider.NotEmpty(hash);
|
||||
|
||||
//
|
||||
var isExists = await IsTokenExistsByHash(hash);
|
||||
if (!isExists)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
//
|
||||
var item = await GetTokenByHash(hash);
|
||||
if (item == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
//
|
||||
DbContext.Tokens.Remove(item);
|
||||
await DbContext.SaveChangesAsync();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove a Token Entity
|
||||
/// </summary>
|
||||
/// <param name="device">an instance of <see>XDevice</see></param>
|
||||
/// <returns></returns>
|
||||
private async Task RemoveTokenByDevice(
|
||||
XDevice device
|
||||
)
|
||||
{
|
||||
//
|
||||
// Validate Args ...
|
||||
ValidationProvider.NotNull(device);
|
||||
|
||||
//
|
||||
XToken items = null;
|
||||
var enumerator = DbContext.Tokens.AsAsyncEnumerable();
|
||||
await
|
||||
foreach (var token in enumerator)
|
||||
{
|
||||
//
|
||||
if (token.Device.IsSameAs(device))
|
||||
{
|
||||
//
|
||||
items = token;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
if (!items.IsNull())
|
||||
{
|
||||
DbContext.Tokens.RemoveRange(items);
|
||||
await DbContext.SaveChangesAsync();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// add a token Entity
|
||||
/// </summary>
|
||||
/// <param name="item">an instance of <see>XToken</see></param>
|
||||
/// <returns>an instance of <see>XToken</see></returns>
|
||||
private async Task<XToken> AddTokenAsync(
|
||||
XToken item
|
||||
)
|
||||
{
|
||||
//
|
||||
var isExist = await IsTokenExistsByToken(item);
|
||||
if (isExist)
|
||||
{
|
||||
return await GetTokenByHash(item.Hash);
|
||||
}
|
||||
|
||||
//
|
||||
var entry = await DbContext.Tokens.AddAsync(item);
|
||||
await DbContext.SaveChangesAsync();
|
||||
|
||||
//
|
||||
return entry.Entity;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Hash a Request Token
|
||||
/// </summary>
|
||||
/// <param name="request">an instance of <see>XActionRequestToken</see></param>
|
||||
/// <param name="forceNotNullUserSelectByParam"></param>
|
||||
/// <param name="forceDeviceNotNull"></param>
|
||||
/// <returns>an instance of <see>XToken</see></returns>
|
||||
private async Task<XToken> HashRequest(
|
||||
XActionRequestToken request,
|
||||
bool forceNotNullUserSelectByParam = true,
|
||||
bool forceDeviceNotNull = true
|
||||
)
|
||||
{
|
||||
//
|
||||
// Validate Args ...
|
||||
var secretKey = Configuration.IdentitySecretKey;
|
||||
ValidationProvider.NotNull(request);
|
||||
ValidationProvider.NotEmpty(secretKey);
|
||||
|
||||
//
|
||||
ValidateActionRequest(request);
|
||||
|
||||
//
|
||||
var type = request.Action;
|
||||
var device = request.Context.Device;
|
||||
var tokenObject = ToSecurityToken(request);
|
||||
var tokenString = ToTokenString(tokenObject);
|
||||
var tokenHash = ToHash(tokenString);
|
||||
var userSelectByParam = GetUserSelectByParam(request, forceNotNullUserSelectByParam);
|
||||
|
||||
//
|
||||
if (forceNotNullUserSelectByParam)
|
||||
{
|
||||
ValidationProvider.NotEmpty(userSelectByParam);
|
||||
}
|
||||
|
||||
//
|
||||
ValidationProvider.NotEmpty(tokenString, tokenHash);
|
||||
|
||||
//
|
||||
if (forceDeviceNotNull)
|
||||
{
|
||||
ValidationProvider.NotNull(device);
|
||||
}
|
||||
|
||||
//
|
||||
ValidationProvider.NotNull(tokenObject);
|
||||
|
||||
//
|
||||
var xToken = new XToken
|
||||
{
|
||||
Type = type,
|
||||
Device = device,
|
||||
Hash = tokenHash,
|
||||
Token = tokenString,
|
||||
UserSelectByParam = userSelectByParam
|
||||
};
|
||||
|
||||
//
|
||||
var addedToken = await AddTokenAsync(xToken);
|
||||
|
||||
//
|
||||
return addedToken;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generate Token Hash string
|
||||
/// </summary>
|
||||
/// <param name="token">represent specified action token</param>
|
||||
/// <param name="checkValidation">check token validation parameters, default is true</param>
|
||||
/// <returns>hash string</returns>
|
||||
private string ToHash(
|
||||
string token,
|
||||
bool checkValidation = true
|
||||
)
|
||||
{
|
||||
//
|
||||
ValidationProvider.NotEmpty(token);
|
||||
|
||||
//
|
||||
if (checkValidation)
|
||||
{
|
||||
ValidateToken(token);
|
||||
}
|
||||
|
||||
//
|
||||
var result = token.ToMd5String().ToNormalString();
|
||||
|
||||
//
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generate Token string
|
||||
/// </summary>
|
||||
/// <param name="token">represent specified action token</param>
|
||||
/// <returns>token string</returns>
|
||||
private string ToTokenString(
|
||||
SecurityToken token
|
||||
)
|
||||
{
|
||||
//
|
||||
ValidationProvider.NotNull(token, IdentityHelper);
|
||||
|
||||
//
|
||||
var result = IdentityHelper.ToTokenString(token);
|
||||
|
||||
//
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generate Token string
|
||||
/// </summary>
|
||||
/// <param name="token">represent specified action token</param>
|
||||
/// <returns>token string</returns>
|
||||
private string ToTokenString(
|
||||
JwtSecurityToken token
|
||||
)
|
||||
{
|
||||
//
|
||||
ValidationProvider.NotNull(token, IdentityHelper);
|
||||
|
||||
//
|
||||
var result = IdentityHelper.ToTokenString(token);
|
||||
|
||||
//
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generate Security Token
|
||||
/// </summary>
|
||||
/// <param name="token">represent specified action token</param>
|
||||
/// <returns>an instance of <see>SecurityToken</see></returns>
|
||||
private SecurityToken ToSecurityToken(
|
||||
string token
|
||||
)
|
||||
{
|
||||
//
|
||||
ValidationProvider.NotEmpty(token);
|
||||
|
||||
//
|
||||
var result = IdentityHelper.ToSecurityToken(token);
|
||||
|
||||
//
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generate Security Token
|
||||
/// </summary>
|
||||
/// <param name="request">an instance of <see>XActionRequestToken</see></param>
|
||||
/// <returns>an instance of <see>SecurityToken</see></returns>
|
||||
private SecurityToken ToSecurityToken(
|
||||
XActionRequestToken request
|
||||
)
|
||||
{
|
||||
//
|
||||
ValidationProvider.NotNull(request);
|
||||
|
||||
//
|
||||
ValidateActionRequest(request);
|
||||
|
||||
//
|
||||
var result = IdentityHelper.ToSecurityToken(request);
|
||||
if (result == null)
|
||||
{
|
||||
XException.InvalidToken.Throw();
|
||||
}
|
||||
|
||||
//
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cleanup all exists tokens related to specified User
|
||||
/// </summary>
|
||||
/// <param name="userSelectByParam">specified user's identifier</param>
|
||||
/// <returns></returns>
|
||||
private async Task HandleRemoveExistsTokens(
|
||||
string userSelectByParam
|
||||
)
|
||||
{
|
||||
//
|
||||
// Validate Args ...
|
||||
ValidationProvider.NotEmpty(userSelectByParam);
|
||||
|
||||
//
|
||||
var xTokens = new List<XToken>();
|
||||
var xTokenEnumerator = DbContext.Tokens.AsAsyncEnumerable();
|
||||
await
|
||||
foreach (var xToken in xTokenEnumerator)
|
||||
{
|
||||
//
|
||||
if (xToken.UserSelectByParam == userSelectByParam &&
|
||||
ObjectHelper.ToEnumerableValues<XAction>().Contains(xToken.Type))
|
||||
{
|
||||
xTokens.Add(xToken);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
if (xTokens.HasChild())
|
||||
{
|
||||
DbContext.Tokens.RemoveRange(xTokens);
|
||||
await DbContext.SaveChangesAsync();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cleanup all tokens related to specified User
|
||||
/// </summary>
|
||||
/// <param name="userSelectByParams">a collection of user identifiers</param>
|
||||
/// <returns></returns>
|
||||
private async Task HandleCleanUserTokens(
|
||||
ICollection<string> userSelectByParams
|
||||
)
|
||||
{
|
||||
//
|
||||
// Validate Args ...
|
||||
if (!userSelectByParams.HasChild())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
//
|
||||
foreach (var userSelectByParam in userSelectByParams)
|
||||
{
|
||||
await HandleRemoveExistsTokens(userSelectByParam);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cleanup all tokens related to specified User
|
||||
/// </summary>
|
||||
/// <param name="user">an instance of <see>XUser</see></param>
|
||||
/// <returns></returns>
|
||||
private async Task HandleCleanUserTokens(
|
||||
XUser user
|
||||
)
|
||||
{
|
||||
//
|
||||
if (user == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
//
|
||||
var userSelectByParams = GenerateUserSelectByParams(user);
|
||||
|
||||
//
|
||||
await HandleCleanUserTokens(userSelectByParams);
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,503 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IdentityModel.Tokens.Jwt;
|
||||
using System.Linq;
|
||||
using System.Security.Claims;
|
||||
using System.Threading.Tasks;
|
||||
using IdentityModel;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using xCommons.Constants;
|
||||
using xCommons.Extensions;
|
||||
using xExceptions.Constants;
|
||||
using xIdentityModels;
|
||||
using xIdentityModels.Constants;
|
||||
using xIdentityModels.Dtos;
|
||||
using xIdentityModels.Extensions;
|
||||
using xIdentityModels.Navigations;
|
||||
|
||||
namespace xIds.Providers
|
||||
{
|
||||
public partial class XIdentityManager
|
||||
{
|
||||
//
|
||||
#region User Handlers ...
|
||||
/// <summary>
|
||||
/// Check a User Exists or not
|
||||
/// </summary>
|
||||
/// <param name="userSelectByParam">specified user's identifier</param>
|
||||
/// <returns>a boolean value</returns>
|
||||
public async Task<bool> IsUserExistsAsync(
|
||||
string userSelectByParam
|
||||
)
|
||||
{
|
||||
//
|
||||
// Validate Args ...
|
||||
if (userSelectByParam.IsNullOrEmpty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
//
|
||||
var selectType = GetUserSelectByType(userSelectByParam);
|
||||
|
||||
//
|
||||
var result = false;
|
||||
XUser user = null;
|
||||
switch (selectType)
|
||||
{
|
||||
case XUserSelectBy.MobileNumber:
|
||||
user = await GetUsersDbSet()
|
||||
.Where(ur => ur.PhoneNumber == userSelectByParam)
|
||||
.FirstOrDefaultAsync();
|
||||
break;
|
||||
|
||||
case XUserSelectBy.Email:
|
||||
user = await UserManager
|
||||
.FindByEmailAsync(userSelectByParam);
|
||||
break;
|
||||
|
||||
case XUserSelectBy.Username:
|
||||
user = await UserManager
|
||||
.FindByNameAsync(userSelectByParam);
|
||||
break;
|
||||
|
||||
case XUserSelectBy.ID:
|
||||
default:
|
||||
user = await UserManager
|
||||
.FindByIdAsync(userSelectByParam);
|
||||
break;
|
||||
}
|
||||
|
||||
//
|
||||
result = user != null;
|
||||
|
||||
//
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check a User Selector is Available for Registration or Not
|
||||
/// </summary>
|
||||
/// <param name="userSelectByParam">specified user's identifier</param>
|
||||
/// <returns>a boolean value</returns>
|
||||
public async Task<bool> CanRegister(
|
||||
string userSelectByParam
|
||||
)
|
||||
{
|
||||
//
|
||||
// Validate ARgs ...
|
||||
ValidationProvider.NotEmpty(userSelectByParam);
|
||||
|
||||
//
|
||||
var selectByType = GetUserSelectByType(userSelectByParam);
|
||||
|
||||
//
|
||||
if (selectByType == XUserSelectBy.Username &&
|
||||
!ValidateUserName(userSelectByParam))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
//
|
||||
var isExists = await IsUserExistsAsync(userSelectByParam);
|
||||
if (isExists)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
//
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a User object
|
||||
/// </summary>
|
||||
/// <param name="userSelectByParam">specified user's identifier</param>
|
||||
/// <param name="containDetails">specifies returned object contains all Navigation Properties or not, default is false</param>
|
||||
/// <returns>an instance of <see>XUser</see></returns>
|
||||
public async Task<XUser> GetUserAsync(
|
||||
string userSelectByParam,
|
||||
bool containDetails = false
|
||||
)
|
||||
{
|
||||
//
|
||||
var isExists = await IsUserExistsAsync(userSelectByParam);
|
||||
if (!isExists)
|
||||
{
|
||||
XException.NotFound.Throw();
|
||||
}
|
||||
|
||||
//
|
||||
// Fill User ...
|
||||
var selectType = GetUserSelectByType(userSelectByParam);
|
||||
XUser user = null;
|
||||
var usersStore = GetUsersDbSet(containDetails);
|
||||
|
||||
//
|
||||
// Select Type ...
|
||||
switch (selectType)
|
||||
{
|
||||
case XUserSelectBy.MobileNumber:
|
||||
user = await usersStore
|
||||
.FirstOrDefaultAsync(ur => ur.PhoneNumber ==
|
||||
userSelectByParam);
|
||||
break;
|
||||
|
||||
case XUserSelectBy.Email:
|
||||
user = await usersStore
|
||||
.FirstOrDefaultAsync(ur => ur.Email ==
|
||||
userSelectByParam);
|
||||
break;
|
||||
|
||||
case XUserSelectBy.Username:
|
||||
user = await usersStore
|
||||
.FirstOrDefaultAsync(ur => ur.UserName ==
|
||||
userSelectByParam);
|
||||
break;
|
||||
|
||||
case XUserSelectBy.ID:
|
||||
default:
|
||||
user = await usersStore
|
||||
.FirstOrDefaultAsync(ur => ur.Id ==
|
||||
userSelectByParam);
|
||||
break;
|
||||
}
|
||||
|
||||
//
|
||||
return user;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieve User Names based on UserIds
|
||||
/// </summary>
|
||||
/// <param name="userIds">a collection of user identifiers</param>
|
||||
/// <returns>a collection of Usernames</returns>
|
||||
public async Task<IEnumerable<string>> GetUserNamesAsync(
|
||||
IEnumerable<string> userIds
|
||||
)
|
||||
{
|
||||
//
|
||||
// Validate Args ...
|
||||
if (userIds == null ||
|
||||
userIds.Count() == 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
//
|
||||
var result = await GetUsersDbSet()
|
||||
.Where(
|
||||
xu =>
|
||||
userIds
|
||||
.Contains(xu.Id))
|
||||
.Select(xt => xt.UserName)
|
||||
.ToListAsync();
|
||||
|
||||
//
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// get user ids and retrieve corresponding user names
|
||||
/// </summary>
|
||||
/// <param name="model">an instance of <see>XUserNameIdRequest</see> which represent required UserIds collection</param>
|
||||
/// <returns>a collection of <see>XUserNameIdResponse</see> instances</returns>
|
||||
public async Task<IEnumerable<XUserNameIdResponse>> GetUserNameIdsAsync(
|
||||
XUserNameIdRequest model
|
||||
)
|
||||
{
|
||||
//
|
||||
// Validate Args ...
|
||||
if (model.IsNull() || !model.Ids.HasChild())
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
//
|
||||
var result = await GetUsersDbSet()
|
||||
.Where(
|
||||
xu =>
|
||||
model.Ids.Contains(xu.Id) ||
|
||||
model.Ids.Contains(xu.Email) ||
|
||||
model.Ids.Contains(xu.UserName) ||
|
||||
model.Ids.Contains(xu.PhoneNumber))
|
||||
.Select(xt => new XUserNameIdResponse
|
||||
{
|
||||
Id = xt.Id,
|
||||
UserName = xt.UserName,
|
||||
})
|
||||
.ToListAsync();
|
||||
|
||||
//
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a User
|
||||
/// </summary>
|
||||
/// <param name="user">an instance of <see>XUser</see></param>
|
||||
/// <param name="password">user's password</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 false</param>
|
||||
/// <returns>an instance of <see>IdentityResult</see></returns>
|
||||
public async Task<IdentityResult> CreateUserAsync(
|
||||
XUser user,
|
||||
string password,
|
||||
bool checkCanLoginPolicies = false,
|
||||
bool checkIsBanned = false
|
||||
)
|
||||
{
|
||||
//
|
||||
if (user == null ||
|
||||
password.IsNullOrEmpty())
|
||||
{
|
||||
XException.InvalidArgs.Throw();
|
||||
}
|
||||
|
||||
//
|
||||
// Get User Select By param
|
||||
var userSelectByParam = GetUserSelectByParam(user, excludes: new List<XUserSelectBy> { XUserSelectBy.ID });
|
||||
|
||||
//
|
||||
// Check User Created before or not ...
|
||||
var isExists = await IsUserExistsAsync(userSelectByParam);
|
||||
if (isExists)
|
||||
{
|
||||
XException.Duplicate.Throw();
|
||||
}
|
||||
|
||||
//
|
||||
var result = await UserManager.CreateAsync(user, password);
|
||||
if (result.Succeeded)
|
||||
{
|
||||
//
|
||||
var claims = await ToJwtClaims(
|
||||
user,
|
||||
checkCanLoginPolicies: checkCanLoginPolicies,
|
||||
checkIsBanned: checkIsBanned);
|
||||
|
||||
//
|
||||
var addClaimsResult = await UserManager.AddClaimsAsync(user, claims);
|
||||
|
||||
//
|
||||
return addClaimsResult;
|
||||
}
|
||||
|
||||
//
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update a User Information
|
||||
/// </summary>
|
||||
/// <param name="user">an instance of <see>XUser</see></param>
|
||||
/// <param name="checkCanLoginPolicies">check a user can log in in system or not, default is true</param>
|
||||
/// <param name="checkIsBanned">check user is banned or not, default is true</param>
|
||||
/// <returns>an instance of <see>IdentityResult</see></returns>
|
||||
public async Task<IdentityResult> UpdateUserAsync(
|
||||
XUser user,
|
||||
bool checkCanLoginPolicies = true,
|
||||
bool checkIsBanned = true
|
||||
)
|
||||
{
|
||||
//
|
||||
var updateResult = await this.UserManager.UpdateAsync(user);
|
||||
if (updateResult.Succeeded)
|
||||
{
|
||||
//
|
||||
var claims = await ToJwtClaims(
|
||||
user,
|
||||
checkCanLoginPolicies: checkCanLoginPolicies,
|
||||
checkIsBanned: checkIsBanned
|
||||
);
|
||||
var existsClaims = await UserManager.GetClaimsAsync(user);
|
||||
|
||||
//
|
||||
await UserManager.RemoveClaimsAsync(user, existsClaims);
|
||||
var addClaimsResult = await UserManager.AddClaimsAsync(user, claims);
|
||||
|
||||
//
|
||||
return addClaimsResult;
|
||||
}
|
||||
|
||||
//
|
||||
return updateResult;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generate JWT Claims froma DashboardUser object
|
||||
/// </summary>
|
||||
/// <param name="user">an instance of <see>XUser</see></param>
|
||||
/// <param name="checkCanLoginPolicies">check a user can log in in system or not, default is true</param>
|
||||
/// <param name="checkIsBanned">check user is banned or not, default is true</param>
|
||||
/// <returns>a collection of <see>Claim</see> instances</returns>
|
||||
public async Task<Claim[]> ToJwtClaims(
|
||||
XUser user,
|
||||
bool checkCanLoginPolicies = true,
|
||||
bool checkIsBanned = true
|
||||
)
|
||||
{
|
||||
//
|
||||
// Get New Instance of Claims ...
|
||||
var claims = new List<Claim> {
|
||||
//
|
||||
// Mapping to User.Identity.Name ...
|
||||
new Claim (JwtClaimTypes.Name, user.UserName),
|
||||
|
||||
//
|
||||
// Mapping to User.Identity.Name ...
|
||||
new Claim (JwtRegisteredClaimNames.UniqueName, user.UserName),
|
||||
|
||||
//
|
||||
// Other Usefull User Related Data ...
|
||||
new Claim (JwtClaimTypes.GivenName, user.FirstName),
|
||||
new Claim (JwtClaimTypes.FamilyName, user.LastName),
|
||||
|
||||
//
|
||||
// User Gendre ...
|
||||
new Claim (JwtClaimTypes.Gender, user.Gender.ToString (), ClaimValueTypes.Integer),
|
||||
|
||||
//
|
||||
// User Profile Picture ...
|
||||
new Claim (JwtClaimTypes.Picture, !user.Avatar.IsNullOrEmpty () ?
|
||||
user.Avatar.ToString () :
|
||||
""),
|
||||
|
||||
//
|
||||
// The Unique Identifier for Each Token ...
|
||||
new Claim (JwtRegisteredClaimNames.Jti, Guid.NewGuid ().ToString ()),
|
||||
|
||||
//
|
||||
// Add isEnable and isBanned feature ...
|
||||
new Claim (
|
||||
XCustomClaims.IsEnabled,
|
||||
user.IsEnable.ToString (),
|
||||
ClaimValueTypes.Boolean
|
||||
),
|
||||
new Claim (
|
||||
XCustomClaims.IsBanned,
|
||||
user.IsBanned.ToString (),
|
||||
ClaimValueTypes.Boolean
|
||||
),
|
||||
};
|
||||
|
||||
//
|
||||
#region Set Claims based on Profile Policies ...
|
||||
//
|
||||
if (Configuration.Policy.Profile.ContainsDateOfBirth)
|
||||
{
|
||||
claims.Add(new Claim(JwtRegisteredClaimNames.Birthdate, user.DateOfBirth.ToString()));
|
||||
}
|
||||
|
||||
//
|
||||
if (Configuration.Policy.Profile.ContainsEmail)
|
||||
{
|
||||
claims.Add(new Claim(JwtClaimTypes.Email, user.Email));
|
||||
claims.Add(new Claim(
|
||||
JwtClaimTypes.EmailVerified,
|
||||
user.EmailConfirmed.ToString(),
|
||||
ClaimValueTypes.Boolean));
|
||||
}
|
||||
|
||||
//
|
||||
if (Configuration.Policy.Profile.ContainsPhoneNumber)
|
||||
{
|
||||
claims.Add(new Claim(JwtClaimTypes.PhoneNumber, user.PhoneNumber));
|
||||
claims.Add(new Claim(
|
||||
JwtClaimTypes.PhoneNumberVerified,
|
||||
user.PhoneNumberConfirmed.ToString(),
|
||||
ClaimValueTypes.Boolean));
|
||||
}
|
||||
|
||||
//
|
||||
if (Configuration.Policy.Profile.ContainsRoles)
|
||||
{
|
||||
var roleNames = await GetRoleNamesAsync(
|
||||
user.UserName,
|
||||
checkCanLoginPolicies: checkCanLoginPolicies,
|
||||
checkIsBanned: checkIsBanned);
|
||||
roleNames.ToList()
|
||||
.ForEach(rn =>
|
||||
{
|
||||
claims.Add(new Claim(JwtClaimTypes.Role, rn));
|
||||
});
|
||||
}
|
||||
|
||||
//
|
||||
if (Configuration.Policy.Profile.ContainsLastLogin)
|
||||
{
|
||||
claims.Add(new Claim(
|
||||
JwtClaimTypes.AuthenticationTime,
|
||||
user.LastLogin.ToString(),
|
||||
ClaimValueTypes.DateTime));
|
||||
}
|
||||
#endregion
|
||||
|
||||
//
|
||||
// Return Result ...
|
||||
return claims.ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check User and Password relation
|
||||
/// </summary>
|
||||
/// <param name="user">an instance of <see>XUser</see></param>
|
||||
/// <param name="password">user's password</param>
|
||||
/// <param name="device">an instance of <see>XDevice</see></param>
|
||||
/// <param name="language">specify destination language</param>
|
||||
/// <param name="lockoutOnFailure">specify account lockout on failure log in</param>
|
||||
/// <param name="isForced">specify log in force without checking <see>XDevice</see> and lang relation</param>
|
||||
/// <returns>an instance of <see>SignInResult</see></returns>
|
||||
public async Task<SignInResult> CheckPasswordSignInAsync(
|
||||
XUser user,
|
||||
string password,
|
||||
XDevice device,
|
||||
string language,
|
||||
bool lockoutOnFailure,
|
||||
bool isForced = true
|
||||
)
|
||||
{
|
||||
//
|
||||
var result = await SignInManager.CheckPasswordSignInAsync(
|
||||
user, password, lockoutOnFailure
|
||||
);
|
||||
|
||||
//
|
||||
if (result.Succeeded && isForced)
|
||||
{
|
||||
//
|
||||
var isExistsDevice = user.Devices.Any(d => d.IsSameAs(device));
|
||||
if (!isExistsDevice && !user.Email.IsNullOrEmpty() && user.EmailConfirmed)
|
||||
{
|
||||
//
|
||||
user.Devices.Add(device);
|
||||
|
||||
//
|
||||
var updateUserResult = await UpdateUserAsync(user);
|
||||
if (updateUserResult.Succeeded)
|
||||
{
|
||||
//
|
||||
try
|
||||
{
|
||||
//
|
||||
var xMessage = GetUserNewDeviceLoggedInMessage(
|
||||
language,
|
||||
user.Email,
|
||||
device
|
||||
);
|
||||
|
||||
//
|
||||
await MessageProvider.SendMailAsync(xMessage);
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
return result;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,220 @@
|
||||
using System.Threading.Tasks;
|
||||
using xCommons.Extensions;
|
||||
using xIdentityModels.Extensions;
|
||||
using xIdentityModels.Models;
|
||||
using xIdentityModels.Navigations;
|
||||
|
||||
namespace xIds.Providers
|
||||
{
|
||||
public partial class XIdentityManager
|
||||
{
|
||||
//
|
||||
#region Verification Code Actions ...
|
||||
/// <summary>
|
||||
/// Check a Device Requeste for Verification Code before or not
|
||||
/// </summary>
|
||||
/// <param name="device">an instance of <see>XDevice</see></param>
|
||||
/// <returns>a boolean value</returns>
|
||||
private async Task<bool> IsDeviceRequestedForVerificationCodeBefor(
|
||||
XDevice device
|
||||
)
|
||||
{
|
||||
//
|
||||
var result = false;
|
||||
var enumerator = DbContext.VerificationRequests
|
||||
.AsAsyncEnumerable();
|
||||
await
|
||||
foreach (var entity in enumerator)
|
||||
{
|
||||
//
|
||||
if (entity.Device.IsSameAs(device))
|
||||
{
|
||||
//
|
||||
result = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check a Verification Code Requested before or not
|
||||
/// </summary>
|
||||
/// <param name="verificationCode">Confirm Verification Code</param>
|
||||
/// <returns>a boolean value</returns>
|
||||
private async Task<bool> IsVerificationCodeRequested(
|
||||
string verificationCode
|
||||
)
|
||||
{
|
||||
//
|
||||
// Validate Args ...
|
||||
if (verificationCode.IsNullOrEmpty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
//
|
||||
var result = false;
|
||||
var enumerator = DbContext.VerificationRequests
|
||||
.AsAsyncEnumerable();
|
||||
await
|
||||
foreach (var entity in enumerator)
|
||||
{
|
||||
//
|
||||
if (entity.VerificationCode == verificationCode)
|
||||
{
|
||||
//
|
||||
result = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieve Verification Request
|
||||
/// </summary>
|
||||
/// <param name="device">an instance of <see>XDevice</see></param>
|
||||
/// <returns>an instance of <see>XVerificationRequest</see></returns>
|
||||
private async Task<XVerificationRequest> GetVerificationRequest(
|
||||
XDevice device
|
||||
)
|
||||
{
|
||||
//
|
||||
XVerificationRequest result = null;
|
||||
var enumerator = DbContext.VerificationRequests
|
||||
.AsAsyncEnumerable();
|
||||
await
|
||||
foreach (var entity in enumerator)
|
||||
{
|
||||
//
|
||||
if (entity.Device.IsSameAs(device))
|
||||
{
|
||||
//
|
||||
result = entity;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieve Verification Request
|
||||
/// </summary>
|
||||
/// <param name="verificationCode">Confirm Verification Code</param>
|
||||
/// <returns>an instance of <see>XVerificationRequest</see></returns>
|
||||
private async Task<XVerificationRequest> GetVerificationRequest(
|
||||
string verificationCode
|
||||
)
|
||||
{
|
||||
//
|
||||
XVerificationRequest result = null;
|
||||
var enumerator = DbContext.VerificationRequests
|
||||
.AsAsyncEnumerable();
|
||||
await
|
||||
foreach (var entity in enumerator)
|
||||
{
|
||||
//
|
||||
if (entity.VerificationCode == verificationCode)
|
||||
{
|
||||
//
|
||||
result = entity;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove Exists Verification Code Request
|
||||
/// </summary>
|
||||
/// <param name="device">an instance of <see>XDevice</see></param>
|
||||
/// <param name="saveChanges">save changes on DbContext, default is true</param>
|
||||
/// <returns></returns>
|
||||
private async Task RemoveVerificationCodeRequestByDevice(
|
||||
XDevice device,
|
||||
bool saveChanges = true
|
||||
)
|
||||
{
|
||||
//
|
||||
if (!await IsDeviceRequestedForVerificationCodeBefor(device))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
//
|
||||
var xvr = await GetVerificationRequest(device);
|
||||
DbContext.VerificationRequests.Remove(xvr);
|
||||
|
||||
//
|
||||
if (saveChanges)
|
||||
{
|
||||
await DbContext.SaveChangesAsync();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove Exists Verification Code Request
|
||||
/// </summary>
|
||||
/// <param name="verificationCode">Confirm Verification Code</param>
|
||||
/// <param name="saveChanges">save changes on DbContext, default is true</param>
|
||||
/// <returns></returns>
|
||||
private async Task RemoveVerificationRequest(
|
||||
string verificationCode,
|
||||
bool saveChanges = true
|
||||
)
|
||||
{
|
||||
//
|
||||
ValidationProvider.NotEmpty(verificationCode);
|
||||
|
||||
//
|
||||
var request = await GetVerificationRequest(verificationCode);
|
||||
DbContext.VerificationRequests.Remove(request);
|
||||
|
||||
//
|
||||
if (saveChanges)
|
||||
{
|
||||
await DbContext.SaveChangesAsync();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add Verification Code Request
|
||||
/// </summary>
|
||||
/// <param name="request">an instance of <see>XVerificationRequest</see></param>
|
||||
/// <param name="saveChanges">save changes on DbContext, default is true</param>
|
||||
/// <returns>an instance of <see>XVerificationRequest</see></returns>
|
||||
private async Task<XVerificationRequest> AddVerificationRequest(
|
||||
XVerificationRequest request,
|
||||
bool saveChanges = true
|
||||
)
|
||||
{
|
||||
//
|
||||
if (await IsDeviceRequestedForVerificationCodeBefor(request.Device))
|
||||
{
|
||||
await RemoveVerificationCodeRequestByDevice(request.Device);
|
||||
}
|
||||
|
||||
//
|
||||
var entry = await DbContext.VerificationRequests.AddAsync(request);
|
||||
|
||||
//
|
||||
if (saveChanges)
|
||||
{
|
||||
await DbContext.SaveChangesAsync();
|
||||
}
|
||||
|
||||
//
|
||||
return entry.Entity;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user