742 lines
21 KiB
C#
742 lines
21 KiB
C#
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
|
|
}
|
|
} |