Initial ...

This commit is contained in:
2026-03-22 14:08:25 +03:30
commit d3464ef39c
71 changed files with 19087 additions and 0 deletions
+14
View File
@@ -0,0 +1,14 @@
#
bin
obj
#
Db/*
!Db/.gitkeep
#
Migrations/*
#
wwwroot
!wwwroot/.gitkeep
@@ -0,0 +1,10 @@
namespace xIds.Configurations
{
public class CertificatesConfigurations
{
public struct CertificateNames
{
public const string IdentityServerSigning = "IdentityServerSigning";
}
}
}
@@ -0,0 +1,73 @@
using xIds.Constants;
namespace xIds.Configurations
{
/// <summary>
/// Represent Configurations of DataService Module ...
/// </summary>
public partial class XDataServiceConfiguration
{
/// <summary>
/// Provider Type ...
/// </summary>
/// <value></value>
public XDbProviders Provider { get; set; }
/// <summary>
/// Connection String which provide Requires Data to Connect to Db Provider ...
/// </summary>
/// <value></value>
public string ConnectionString { get; set; }
/// <summary>
/// Enable Tracking of Entities ...
/// Only Used on EFCore ...
/// </summary>
/// <value></value>
public bool EnableTracking { get; set; } = false;
/// <summary>
/// Enable Logging Details of Errors ...
/// Only Used on EFCore ...
/// </summary>
/// <value></value>
public bool EnableDetailedErrors { get; set; } = false;
/// <summary>
/// Enable Logging Sensitive Data ...
/// Only Used on EFCore ...
/// </summary>
/// <value></value>
public bool EnableSensitiveDataLogging { get; set; } = false;
/// <summary>
/// this is a way to provide Default Pagination Data on XQuery based requests ...
/// </summary>
/// <returns></returns>
public PagingConfiguration PagingConfiguration { get; set; } = new PagingConfiguration();
}
/// <summary>
/// this is a way to provide Default Pagination Data on XQuery based requests ...
/// </summary>
public partial class PagingConfiguration
{
/// <summary>
/// Default Page Size ...
/// </summary>
/// <value></value>
public int DefaultPageSize { get; set; } = XDataServiceConstants.DEFAULT_PAGE_SIZE;
/// <summary>
/// restrict Maximum Page Size ...
/// </summary>
/// <value></value>
public int MaxAvailablePageSize { get; set; } = XDataServiceConstants.MAX_AVAILABLE_PAGE_SIZE;
/// <summary>
/// restrice Minimum Page Size ...
/// </summary>
/// <value></value>
public int MinAvailablePageSize { get; set; } = XDataServiceConstants.MIN_AVAILABLE_PAGE_SIZE;
}
}
@@ -0,0 +1,18 @@
namespace xIds.Configurations
{
/// <summary>
/// determines a resource and it's connection info
/// </summary>
public partial class XIdentityResourceConfiguration
{
public string Authority { get; set; }
public string ApiName { get; set; }
public string ApiSecret { get; set; }
public string ClientId { get; set; }
public string ClientSecret { get; set; }
public string XPoweredValue { get; set; }
public string XRevisionSecretKey { get; set; }
}
}
+11
View File
@@ -0,0 +1,11 @@
namespace xIds.Constants
{
public struct ConfigurationNodeNames
{
public const string DB_SEED_NODE_NAME = "DbSeeder";
public const string PROVIDER_NODE_NAME = "DbProvider";
public const string IDENTITY_NODE_NAME = "IdentityConfiguration";
public const string DATA_SERVICE_NODE_NAME = "DataServiceConfiguration";
public const string IDENTITY_RESOURCE_NODE_NAME = "IdentityResourceConfiguration";
}
}
+9
View File
@@ -0,0 +1,9 @@
namespace xIds.Constants
{
public struct ConnectionStringNames
{
public const string IDENTITY_CONNECTION_NAME = "IdentityDb";
public const string CONFIGURATION_CONNECTION_NAME = "ConfigurationDb";
public const string PRESISTED_GRANTS_CONNECTION_NAME = "PersistedGrantDb";
}
}
+14
View File
@@ -0,0 +1,14 @@
namespace xIds.Constants
{
public struct IdentityMessagesKeys
{
public const string Terms = "terms";
public const string InviteMsg = "user_invite_msg";
public const string RegistrationApproveMsg = "registration_approve_msg";
public const string RegisteredMsg = "registered_msg";
public const string VerificationCodeMsg = "verification_code_msg";
public const string ChangePasswordMsg = "change_password_msg";
public const string PasswordChangedMsg = "password_changed_msg";
public const string NewDeviceLoggedInMsg = "new_device_logged_in_msg";
}
}
+12
View File
@@ -0,0 +1,12 @@
namespace xIds.Constants
{
/// <summary>
/// /// Default Pagination Values ...
/// </summary>
public partial struct XDataServiceConstants
{
public static int DEFAULT_PAGE_SIZE = 50;
public static int MAX_AVAILABLE_PAGE_SIZE = 500;
public static int MIN_AVAILABLE_PAGE_SIZE = 20;
}
}
+10
View File
@@ -0,0 +1,10 @@
namespace xIds.Constants
{
public partial class XDbProviderConfigurations
{
/// <summary>
/// Default ConnectionString Name ...
/// </summary>
public const string DEFAULT_CONNECTION_NAME = "DataConnection";
}
}
+23
View File
@@ -0,0 +1,23 @@
namespace xIds.Constants
{
/// <summary>
/// /// Represent Supported DBMS for Managing Data ...
/// </summary>
public enum XDbProviders
{
None,
MySQL,
SQLite,
SQLServer,
}
/// <summary>
/// Represent Supported DBMS for Managing Data ...
/// </summary>
public partial struct ProviderType
{
public const string MySQL = "MYSQL";
public const string SQLite = "SQLITE";
public const string SQLServer = "SQLSERVER";
}
}
@@ -0,0 +1,151 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using xIdentityHelper;
using xCommons.Attributes;
using xCommons.Extensions;
using xIdentityModels.Dtos;
using static IdentityServer4.IdentityServerConstants;
namespace xIds.Controllers
{
public partial class AccountController
{
//
#region Admin Actions ...
/// <summary>
/// Ban Specific Users
/// </summary>
/// <param name="model">an instance of <see>XUserNameIdRequest</see> which represents user identifier list to Ban</param>
/// <returns>a list of banned users identifiers</returns>
[RequireXPowered]
[HttpPost("Ban")]
[Authorize(Policy = LocalApi.PolicyName)]
[Authorize(Policy = XPolicies.EnabledAdmin)]
public async Task<ActionResult<IEnumerable<string>>> Ban(
[FromBody] XUserNameIdRequest model
)
{
//
// Do Action ...
try
{
//
// Validate Args ...
await ValidationProvider
.GroupValidationBuilder()
.AddNotNull(model)
.AddNotZeroChilds(model.Ids)
.ValidateGroupAsync();
//
var result = await IdentityManager
.Ban(
User.Identity.Name,
model
);
//
return Ok(result
.ToDynamicObject());
}
catch (Exception ex)
{
//
var exResult = GetExceptionActionResult(ex);
return exResult;
}
}
/// <summary>
/// UnBann Specific Users
/// </summary>
/// <param name="model">an instance of <see>XUserNameIdRequest</see> which represents user identifier list to Ban</param>
/// <returns>a list of unbanned users identifiers</returns>
[RequireXPowered]
[HttpPost("UnBan")]
[Authorize(Policy = LocalApi.PolicyName)]
[Authorize(Policy = XPolicies.EnabledAdmin)]
public async Task<ActionResult<IEnumerable<string>>> UnBan(
[FromBody] XUserNameIdRequest model
)
{
//
// Do Action ...
try
{
//
// Validate Args ...
await ValidationProvider
.GroupValidationBuilder()
.AddNotNull(model)
.AddNotZeroChilds(model.Ids)
.ValidateGroupAsync();
//
var result = await IdentityManager
.UnBan(
User.Identity.Name,
model
);
//
return Ok(result
.ToDynamicObject());
}
catch (Exception ex)
{
//
var exResult = GetExceptionActionResult(ex);
return exResult;
}
}
/// <summary>
/// Check Specific User is Banned or not
/// </summary>
/// <param name="userSelectByParam">specified user's identifier</param>
/// <returns>a boolean value which represent user banned or not</returns>
[RequireXPowered]
[Authorize(Policy = LocalApi.PolicyName)]
[HttpGet("IsBanned/{userSelectByParam?}")]
[Authorize(Policy = XPolicies.EnabledAdmin)]
[Authorize(Policy = XPolicies.EnabledAgent)]
public async Task<ActionResult<bool>> IsBanned(
[FromRoute] string userSelectByParam = null
)
{
//
// Do Action ...
try
{
//
// Validate Args ...
if (userSelectByParam.IsNullOrEmpty())
{
userSelectByParam = User.Identity.Name;
}
ValidationProvider.NotEmpty(userSelectByParam);
//
var result = await IdentityManager
.IsBanned(
User.Identity.Name,
userSelectByParam
);
//
return Ok(result);
}
catch (Exception ex)
{
//
var exResult = GetExceptionActionResult(ex);
return exResult;
}
}
#endregion
}
}
@@ -0,0 +1,392 @@
using System;
using System.Threading.Tasks;
using IdentityModel.Client;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using xIdentityHelper;
using xCommons.Attributes;
using xCommons.Extensions;
using xExceptions.Constants;
using xIdentityModels.Extensions;
using xIdentityModels.Models;
using static IdentityServer4.IdentityServerConstants;
using static xIdentityHelper.XApiScopeHelper;
using Microsoft.Extensions.Logging;
namespace xIds.Controllers
{
public partial class AccountController
{
//
#region Authentication Actions ...
/// <summary>
/// Retrieve OAuth Discovery Document
/// </summary>
/// <returns>an instance of <see>DiscoveryDocumentResponse</see></returns>
[AllowAnonymous]
[RequireXPowered]
[HttpGet("DiscoveryDocument")]
public async Task<ActionResult<DiscoveryDocumentResponse>> GetDiscoveryDocument()
{
//
// Do Action ...
try
{
//
var result = await IdentityManager
.RequestDiscoveryDocument();
//
// Ceck Response Result ...
if (result.IsError)
{
XException.InvalidConfiguration.Throw();
}
//
return Ok(result
.ToDynamicObject());
}
catch (Exception ex)
{
//
var result = GetExceptionActionResult(ex);
return result;
}
}
/// <summary>
/// Request AccessToken for Specific XApiScope
/// </summary>
/// <param name="scope">a member of <see>XApiScope</see></param>
/// <returns>an instance of <see>XTokenResponse</see></returns>
[AllowAnonymous]
[RequireXPowered]
[HttpPost("RequestScopeAccessToken")]
public async Task<ActionResult<XTokenResponse>> RequestScopeAccessToken(
[FromHeader] XApiScope scope
)
{
//
// Do Action ...
try
{
//
// Validate Args ...
var scopeName = string.Empty;
try
{
scopeName = scope.GetStringValue();
}
catch { }
ValidationProvider.NotEmpty(scopeName);
//
var result = await IdentityManager
.RequestScopeAccessToken(scopeName);
//
// Ceck Response Result ...
if (result.IsError)
{
XException.InvalidConfiguration.Throw();
}
//
return Ok(result
.CreateXTokenResponse()
.ToDynamicObject());
}
catch (Exception ex)
{
//
var result = GetExceptionActionResult(ex);
return result;
}
}
/// <summary>
/// Authenticate User
/// </summary>
/// <remarks>
/// Sample request:
///
/// {
/// "password": "",
/// "userSelectBy": "",
/// }
///
/// </remarks>
/// <param name="model">an instance of <see>XLoginRequest</see> class which represent Authentication requirements</param>
/// <returns>an instance of <see>XTokenResponse</see></returns>
[AllowAnonymous]
[RequireXPowered]
[HttpPost("Authenticate")]
public async Task<ActionResult<XTokenResponse>> Authenticate(
[FromBody] XLoginRequest model
)
{
//
// Do Action ...
try
{
//
// Validate Args ...
if (!ModelState.IsValid)
{
XException.InvalidArgs.Throw();
}
await ValidationProvider
.GroupValidationBuilder()
.AddNotNull(model)
.AddNotEmpty(
model.UserSelectBy,
model.Password
)
.ValidateGroupAsync();
//
var result = await IdentityManager
.Authenticate(model);
//
// Check Result ...
if (result.IsError)
{
throw result.GetException();
}
//
// Return Result ...
return Ok(result
.CreateXTokenResponse()
.ToDynamicObject());
}
catch (Exception ex)
{
//
var result = GetExceptionActionResult(ex);
return result;
}
}
/// <summary>
/// Authenticate User
/// </summary>
/// <remarks>
/// Sample request:
///
/// {
/// "language": "fa-IR"
/// "password": "",
/// "userSelectBy": "",
/// "device": {
/// "os":"Mac",
/// "browser":"Chrome",
/// "osVersion":"mac-os-x-15",
/// "userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_3) AppleWebKit/537.36 (KHTML, like Gecko) x-framework-test/0.0.0 Chrome/87.0.4280.141 Electron/11.2.0 Safari/537.36",
/// "deviceType":3,
/// "identifier":"[Mac]-[mac-os-x-15]-[3]-[Chrome]-[Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_3) AppleWebKit/537.36 (KHTML, like Gecko) x-framework-test/0.0.0 Chrome/87.0.4280.141 Electron/11.2.0 Safari/537.36]",
/// "token":"96f400dd9aa5c57a8cec9d6f4775bad3"
/// }
/// }
///
/// </remarks>
/// <param name="model">an instance of <see>XLoginRequest</see> class which represent Authentication requirements</param>
/// <returns>an instance of <see>XLoginResponse</see></returns>
[AllowAnonymous]
[RequireXPowered]
[HttpPost("Login")]
public async Task<ActionResult<XLoginResponse>> Login(
[FromBody] XLoginRequest model
)
{
//
// Do Action ...
try
{
//
// Validate Args ...
if (!ModelState.IsValid)
{
XException.InvalidArgs.Throw();
}
await ValidationProvider
.GroupValidationBuilder()
.AddNotNull(
model,
model.Device)
.AddNotEmpty(
model.Language,
model.UserSelectBy,
model.Password
)
.ValidateGroupAsync();
//
var result = await IdentityManager
.Login(model);
//
return Ok(result
.ToDynamicObject());
}
catch (Exception ex)
{
//
var result = GetExceptionActionResult(ex);
return result;
}
}
/// <summary>
/// Refresh Expired Tokens
/// </summary>
/// <remarks>
/// in addition to AccessToken, you had to pass RefreshToken due to Headers
/// </remarks>
/// <returns>an instance of <see>XTokenResponse</see></returns>
[AllowAnonymous]
[RequireXPowered]
[HttpPost("RefreshTokens")]
public async Task<ActionResult<XTokenResponse>> RefreshTokens()
{
//
// Do Action ...
try
{
//
// Retrieve Toke Response ...
var model = await RetrieveTokensAsXTokenResponse();
//
// Validate Args ...
ValidationProvider
.GroupValidationBuilder()
.AddNotNull(model)
.AddNotEmpty(
model.AccessToken,
model.RefreshToken)
.ValidateGroup();
//
var result = await IdentityManager
.RefreshTokens(model);
//
return Ok(result
.ToDynamicObject());
}
catch (Exception ex)
{
//
var result = GetExceptionActionResult(ex);
return result;
}
}
#endregion
//
#region Password Actions ...
/// <summary>
/// Change a User's Password ...
/// </summary>
/// <remarks>
/// for this action you have to provide:
/// - Lang: client device currently used locale, such as: en-US.
/// - Password: user's current active password.
/// - NewPassword: user's new password to change.
/// - Device: user's client device which is an instance of XDevice.
/// - ReturnUrl: client application Login URL.
///
/// Sample request:
///
/// {
/// "lang": "fa-IR",
/// "password": ""
/// "newPassword": "",
/// "returnUrl": "http://localhost/login",
/// "device": {
/// "os":"Mac",
/// "browser":"Chrome",
/// "osVersion":"mac-os-x-15",
/// "userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_3) AppleWebKit/537.36 (KHTML, like Gecko) x-framework-test/0.0.0 Chrome/87.0.4280.141 Electron/11.2.0 Safari/537.36",
/// "deviceType":3,
/// "identifier":"[Mac]-[mac-os-x-15]-[3]-[Chrome]-[Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_3) AppleWebKit/537.36 (KHTML, like Gecko) x-framework-test/0.0.0 Chrome/87.0.4280.141 Electron/11.2.0 Safari/537.36]",
/// "token":"96f400dd9aa5c57a8cec9d6f4775bad3"
/// }
/// }
///
/// </remarks>
/// <param name="model">an instance of <see>XActionRequest</see> class which provider requirement for action</param>
/// <returns></returns>
[RequireXPowered]
[HttpPost("ChangePassword")]
[Authorize(Policy = XPolicies.User)]
[Authorize(Policy = XPolicies.EnabledUser)]
[Authorize(Policy = LocalApi.PolicyName)]
public async Task<ActionResult> ChangePassword(
[FromBody] XActionRequest model
)
{
//
// Do Action ...
try
{
//
// Validate Args ...
if (!ModelState.IsValid)
{
XException.InvalidArgs.Throw();
}
await ValidationProvider
.GroupValidationBuilder()
.AddNotNull(
model,
model.Device
)
.AddNotEmpty(
model.Lang,
model.Password,
model.NewPassword,
model.ReturnUrl
)
.ValidateGroupAsync();
//
// Get User Select By ...
var userSelectByParam = GetUserSelectByParam(
model,
forceNotNull: false
);
if (userSelectByParam.IsNullOrEmpty())
{
userSelectByParam = User.Identity.Name;
}
ValidationProvider.NotEmpty(userSelectByParam);
//
// Get Request ...
await IdentityManager
.ChangePassword(
model.Lang,
model.Device,
userSelectByParam,
model.Password,
model.NewPassword,
model.ReturnUrl
);
//
return Ok();
}
catch (Exception ex)
{
//
var exResult = GetExceptionActionResult(ex);
return exResult;
}
}
#endregion
}
}
@@ -0,0 +1,889 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using xCommons.Attributes;
using xCommons.Extensions;
using xExceptions.Constants;
using xIdentityHelper;
using xIdentityModels.Constants;
using xIdentityModels.Dtos;
using xIdentityModels.Navigations;
using xModels.Dtos;
using static IdentityServer4.IdentityServerConstants;
namespace xIds.Controllers
{
public partial class AccountController
{
//
#region Friendship Actions ...
//
#region Actions ...
/// <summary>
/// Follow a User
/// </summary>
/// <param name="destUser">a user identifier which represent destination user</param>
/// <returns>an instance of <see>XFriendshipFollowing</see></returns>
[RequireXPowered]
[HttpPost("Friendship/{destUser}/Follow")]
[Authorize(Policy = LocalApi.PolicyName)]
[Authorize(Policy = XPolicies.EnabledUser)]
public async Task<ActionResult<XFriendshipFollowing>> Follow(
[FromRoute] string destUser
)
{
//
// Do Action ...
try
{
//
// Validate Args ...
ValidationProvider.NotEmpty(destUser);
//
var result = await IdentityManager
.Follow(
User.Identity.Name,
destUser
);
//
return Ok(result
.ToDynamicObject());
}
catch (Exception ex)
{
//
var exResult = GetExceptionActionResult(ex);
return exResult;
}
}
/// <summary>
/// Cancel Following
/// </summary>
/// <param name="destUser">a user identifier which represent destination user</param>
/// <returns>a boolean value</returns>
[HttpPost("Friendship/{destUser}/Cancel")]
[Authorize(Policy = LocalApi.PolicyName)]
[Authorize(Policy = XPolicies.EnabledUser)]
public async Task<ActionResult<bool>> Cancel(
[FromRoute] string destUser
)
{
//
// Do Action ...
try
{
//
// Validate Args ...
ValidationProvider.NotEmpty(destUser);
//
var result = await IdentityManager
.Cancel(
User.Identity.Name,
destUser
);
//
return Ok(result);
}
catch (Exception ex)
{
//
var exResult = GetExceptionActionResult(ex);
return exResult;
}
}
/// <summary>
/// Unfollow a Follower
/// </summary>
/// <param name="destUser">a user identifier which represent destination user</param>
/// <returns></returns>
[HttpPost("Friendship/{destUser}/UnFollowFollower")]
[Authorize(Policy = LocalApi.PolicyName)]
[Authorize(Policy = XPolicies.EnabledUser)]
public async Task<ActionResult> UnFollowFollower(
[FromRoute] string destUser
)
{
//
// Do Action ...
try
{
//
// Validate Args ...
ValidationProvider.NotEmpty(destUser);
//
await IdentityManager
.UnFollowFollower(
User.Identity.Name,
destUser
);
//
return Ok();
}
catch (Exception ex)
{
//
var exResult = GetExceptionActionResult(ex);
return exResult;
}
}
/// <summary>
/// Unfollow Following
/// </summary>
/// <param name="destUser">a user identifier which represent destination user</param>
/// <returns></returns>
[Authorize(Policy = LocalApi.PolicyName)]
[Authorize(Policy = XPolicies.EnabledUser)]
[HttpPost("Friendship/{destUser}/UnFollowFollowing")]
public async Task<ActionResult> UnFollowFollowing(
[FromRoute] string destUser
)
{
//
// Do Action ...
try
{
//
// Validate Args ...
ValidationProvider.NotEmpty(destUser);
//
await IdentityManager
.UnFollowFollowing(
User.Identity.Name,
destUser
);
//
return Ok();
}
catch (Exception ex)
{
//
var exResult = GetExceptionActionResult(ex);
return exResult;
}
}
/// <summary>
/// Block a Follower
/// </summary>
/// <param name="destUser">a user identifier which represent destination user</param>
/// <returns>an instance of <see>XFriendshipFollowing</see></returns>
[HttpPost("Friendship/{destUser}/Block")]
[Authorize(Policy = LocalApi.PolicyName)]
[Authorize(Policy = XPolicies.EnabledUser)]
public async Task<ActionResult<XFriendshipFollowing>> Block(
[FromRoute] string destUser
)
{
//
// Do Action ...
try
{
//
// Validate Args ...
ValidationProvider.NotEmpty(destUser);
//
var result = await IdentityManager
.Block(
User.Identity.Name,
destUser
);
//
return Ok(result
.ToDynamicObject());
}
catch (Exception ex)
{
//
var exResult = GetExceptionActionResult(ex);
//
return exResult;
}
}
/// <summary>
/// Unblock a Blocked User
/// </summary>
/// <param name="destUser">an instance of <see>XFriendshipFollowing</see></param>
/// <returns>an instance of <see>XFriendshipFollowing</see></returns>
[HttpPost("Friendship/{destUser}/UnBlock")]
[Authorize(Policy = LocalApi.PolicyName)]
[Authorize(Policy = XPolicies.EnabledUser)]
public async Task<ActionResult<XFriendshipFollowing>> UnBlock(
[FromRoute] string destUser
)
{
//
// Do Action ...
try
{
//
// Validate Args ...
ValidationProvider.NotEmpty(destUser);
//
var result = await IdentityManager
.UnBlock(
User.Identity.Name,
destUser
);
//
return Ok(result
.ToDynamicObject());
}
catch (Exception ex)
{
//
var exResult = GetExceptionActionResult(ex);
return exResult;
}
}
#endregion
//
#region Request Handlers ...
/// <summary>
/// Accept a Following Request
/// </summary>
/// <param name="destUser">a user identifier which represent destination user</param>
/// <returns>an instance of <see>XFriendshipFollower</see></returns>
[Authorize(Policy = LocalApi.PolicyName)]
[Authorize(Policy = XPolicies.EnabledUser)]
[HttpPost("Friendship/{destUser}/AcceptRequest")]
public async Task<ActionResult<XFriendshipFollower>> AcceptRequest(
[FromRoute] string destUser
)
{
//
// Do Action ...
try
{
//
// Validate Args ...
ValidationProvider.NotEmpty(destUser);
//
var result = await IdentityManager
.AcceptRequest(
User.Identity.Name,
destUser
);
//
return Ok(result
.ToDynamicObject());
}
catch (Exception ex)
{
//
var exResult = GetExceptionActionResult(ex);
return exResult;
}
}
/// <summary>
/// Reject a Following Request
/// </summary>
/// <param name="destUser">a user identifier which represent destination user</param>
/// <returns>an instance of <see>XFriendshipFollower</see></returns>
[Authorize(Policy = LocalApi.PolicyName)]
[Authorize(Policy = XPolicies.EnabledUser)]
[HttpPost("Friendship/{destUser}/RejectRequest")]
public async Task<ActionResult<XFriendshipFollower>> RejectRequest(
[FromRoute] string destUser
)
{
//
// Do Action ...
try
{
//
// Validate Args ...
ValidationProvider.NotEmpty(destUser);
//
var result = await IdentityManager
.RejectRequest(
User.Identity.Name,
destUser
);
//
return Ok(result
.ToDynamicObject());
}
catch (Exception ex)
{
//
var exResult = GetExceptionActionResult(ex);
return exResult;
}
}
#endregion
//
#region Getters ...
/// <summary>
/// Check a User IsFollower of Requested User
/// </summary>
/// <param name="destUser">a user identifier which represent destination user</param>
/// <returns>a boolean value</returns>
[Authorize(Policy = LocalApi.PolicyName)]
[Authorize(Policy = XPolicies.EnabledUser)]
[HttpGet("Friendship/{destUser}/IsFollower")]
public async Task<ActionResult<bool>> IsFollower(
[FromRoute] string destUser
)
{
//
// Do Action ...
try
{
//
// Validate Args ...
ValidationProvider.NotEmpty(destUser);
//
var result = await IdentityManager
.IsFollower(
User.Identity.Name,
destUser
);
//
return Ok(result);
}
catch (Exception ex)
{
//
var exResult = GetExceptionActionResult(ex);
return exResult;
}
}
/// <summary>
/// Get Follower State of a User
/// </summary>
/// <param name="destUser">a user identifier which represent destination user</param>
/// <returns>an instance of <see>XFriendshipState</see></returns>
[Authorize(Policy = LocalApi.PolicyName)]
[Authorize(Policy = XPolicies.EnabledUser)]
[HttpGet("Friendship/{destUser}/FollowerState")]
public async Task<ActionResult<XFriendshipState>> FollowerState(
[FromRoute] string destUser
)
{
//
// Do Action ...
try
{
//
// Validate Args ...
ValidationProvider.NotEmpty(destUser);
//
var result = await IdentityManager
.GetFollowerState(
User.Identity.Name,
destUser
);
//
return Ok(result);
}
catch (Exception ex)
{
//
var exResult = GetExceptionActionResult(ex);
return exResult;
}
}
/// <summary>
/// Get Specific Follower
/// </summary>
/// <param name="destUser">a user identifier which represent destination user</param>
/// <returns>an instance of <see>XFriendshipFollower</see></returns>
[Authorize(Policy = LocalApi.PolicyName)]
[Authorize(Policy = XPolicies.EnabledUser)]
[HttpGet("Friendship/{destUser}/Follower")]
public async Task<ActionResult<XFriendshipFollower>> Follower(
[FromRoute] string destUser
)
{
//
// Do Action ...
try
{
//
// Validate Args ...
ValidationProvider.NotEmpty(destUser);
//
var result = await IdentityManager
.GetFollower(
User.Identity.Name,
destUser
);
//
return Ok(result
.ToDynamicObject());
}
catch (Exception ex)
{
//
var exResult = GetExceptionActionResult(ex);
return exResult;
}
}
/// <summary>
/// Get Followers List Of Current User
/// </summary>
/// <returns>a collection of <see>XFriendshipFollower</see></returns>
[HttpGet("Friendship/Followers")]
[Authorize(Policy = LocalApi.PolicyName)]
[Authorize(Policy = XPolicies.EnabledUser)]
public async Task<ActionResult<IEnumerable<XFriendshipFollower>>> Followers()
{
//
// Do Action ...
try
{
//
var result = await IdentityManager
.GetFollowers(User.Identity.Name);
//
return Ok(result
.ToDynamicObject());
}
catch (Exception ex)
{
//
var exResult = GetExceptionActionResult(ex);
//
return exResult;
}
}
/// <summary>
/// Get All Followers List Includes Blocked, Requested and etc
/// of Current User
/// </summary>
/// <returns>a collection of <see>XFriendshipFollower</see></returns>
[HttpGet("Friendship/AllFollowers")]
[Authorize(Policy = LocalApi.PolicyName)]
[Authorize(Policy = XPolicies.EnabledUser)]
public async Task<ActionResult<IEnumerable<XFriendshipFollower>>> AllFollowers()
{
//
// Do Action ...
try
{
//
var result = await IdentityManager
.GetAllFollowers(User.Identity.Name);
//
return Ok(result
.ToDynamicObject());
}
catch (Exception ex)
{
//
var exResult = GetExceptionActionResult(ex);
return exResult;
}
}
/// <summary>
/// Get All Followers List Includes Blocked, Requested and etc
/// of Current User based On Query Model ...
/// </summary>
/// <returns>a Query Result of <see>XFriendDto</see></returns>
[HttpGet("Friendship/QueryFollowers")]
[Authorize(Policy = LocalApi.PolicyName)]
[Authorize(Policy = XPolicies.EnabledUser)]
public async Task<ActionResult<XQueryResult<XFriendDto>>> QueryFollowers(
[FromQuery] XQuery query,
[FromQuery] string destUser
)
{
//
// Do Action ...
try
{
//
// Normalizing Dest User ...
var userInfo = await GetUserInfo();
bool isAdmin = userInfo.Roles.Contains(XUserRoles.ADMIN);
if (destUser.IsNullOrEmpty())
{
destUser = userInfo.UserName;
}
bool isDestUserValid = isAdmin || destUser == userInfo.UserId || destUser == userInfo.Email || destUser == userInfo.PhoneNumber || destUser == userInfo.UserName;
if (!isDestUserValid)
{
XException.NotAllowed.Throw();
}
//
var result = await IdentityManager
.QueryFollowers(
query: query,
userSelectByParam: destUser
);
//
return Ok(result
.ToDynamicObject());
}
catch (Exception ex)
{
//
var exResult = GetExceptionActionResult(ex);
return exResult;
}
}
/// <summary>
/// Check a User is in Followings of Current User
/// </summary>
/// <param name="destUser">a user identifier which represent destination user</param>
/// <returns>a boolean value</returns>
[Authorize(Policy = LocalApi.PolicyName)]
[Authorize(Policy = XPolicies.EnabledUser)]
[HttpGet("Friendship/{destUser}/IsFollowing")]
public async Task<ActionResult<bool>> IsFollowing(
[FromRoute] string destUser
)
{
//
// Do Action ...
try
{
//
// Validate Args ...
ValidationProvider.NotEmpty(destUser);
//
var result = await IdentityManager
.IsFollowing(
User.Identity.Name,
destUser
);
//
return Ok(result);
}
catch (Exception ex)
{
//
var exResult = GetExceptionActionResult(ex);
return exResult;
}
}
/// <summary>
/// Get Following State Relation between Specific User
/// and Current User
/// </summary>
/// <param name="destUser">a user identifier which represent destination user</param>
/// <returns>an instance of <see>XFriendshipState</see></returns>
[Authorize(Policy = LocalApi.PolicyName)]
[Authorize(Policy = XPolicies.EnabledUser)]
[HttpGet("Friendship/{destUser}/FollowingState")]
public async Task<ActionResult<XFriendshipState>> FollowingState(
[FromRoute] string destUser
)
{
//
// Do Action ...
try
{
//
// Validate Args ...
ValidationProvider.NotEmpty(destUser);
//
var result = await IdentityManager
.GetFollowingState(
User.Identity.Name,
destUser
);
//
return Ok(result);
}
catch (Exception ex)
{
//
var exResult = GetExceptionActionResult(ex);
return exResult;
}
}
/// <summary>
/// Get Specific Following Model
/// </summary>
/// <param name="destUser">a user identifier which represent destination user</param>
/// <returns>an instance of <see>XFriendshipFollowing</see></returns>
[Authorize(Policy = LocalApi.PolicyName)]
[Authorize(Policy = XPolicies.EnabledUser)]
[HttpGet("Friendship/{destUser}/Following")]
public async Task<ActionResult<XFriendshipFollowing>> Following(
[FromRoute] string destUser
)
{
//
// Do Action ...
try
{
//
// Validate Args ...
ValidationProvider.NotEmpty(destUser);
//
var result = await IdentityManager
.GetFollowing(
User.Identity.Name,
destUser
);
//
return Ok(result
.ToDynamicObject());
}
catch (Exception ex)
{
//
var exResult = GetExceptionActionResult(ex);
return exResult;
}
}
/// <summary>
/// Get Followings of Current User
/// </summary>
/// <returns>a collection of <see>XFriendshipFollowing</see></returns>
[HttpGet("Friendship/Followings")]
[Authorize(Policy = LocalApi.PolicyName)]
[Authorize(Policy = XPolicies.EnabledUser)]
public async Task<ActionResult<IEnumerable<XFriendshipFollowing>>> Followings()
{
//
// Do Action ...
try
{
//
var result = await IdentityManager
.GetFollowings(User.Identity.Name);
//
return Ok(result
.ToDynamicObject());
}
catch (Exception ex)
{
//
var exResult = GetExceptionActionResult(ex);
return exResult;
}
}
/// <summary>
/// Get All Following List Includes Blocked, Requested and etc
/// of Current User
/// </summary>
/// <returns>a collection of <see>XFriendshipFollowing</see></returns>
[HttpGet("Friendship/AllFollowings")]
[Authorize(Policy = LocalApi.PolicyName)]
[Authorize(Policy = XPolicies.EnabledUser)]
public async Task<ActionResult<IEnumerable<XFriendshipFollowing>>> AllFollowings()
{
//
// Do Action ...
try
{
//
var result = await IdentityManager
.GetAllFollowings(User.Identity.Name);
//
return Ok(result
.ToDynamicObject());
}
catch (Exception ex)
{
//
var exResult = GetExceptionActionResult(ex);
return exResult;
}
}
/// <summary>
/// Get All Following List Includes Blocked, Requested and etc
/// of Current User based On Query Model ...
/// </summary>
/// <returns>a Query Result of <see>XFriendDto</see></returns>
[HttpGet("Friendship/QueryFollowings")]
[Authorize(Policy = LocalApi.PolicyName)]
[Authorize(Policy = XPolicies.EnabledUser)]
public async Task<ActionResult<XQueryResult<XFriendDto>>> QueryFollowings(
[FromQuery] XQuery query,
[FromQuery] string destUser
)
{
//
// Do Action ...
try
{
//
// Normalizing Dest User ...
var userInfo = await GetUserInfo();
bool isAdmin = userInfo.Roles.Contains(XUserRoles.ADMIN);
if (destUser.IsNullOrEmpty())
{
destUser = userInfo.UserName;
}
bool isDestUserValid = isAdmin || destUser == userInfo.UserId || destUser == userInfo.Email || destUser == userInfo.PhoneNumber || destUser == userInfo.UserName;
if (!isDestUserValid)
{
XException.NotAllowed.Throw();
}
//
var result = await IdentityManager
.QueryFollowings(
query: query,
userSelectByParam: destUser
);
//
return Ok(result
.ToDynamicObject());
}
catch (Exception ex)
{
//
var exResult = GetExceptionActionResult(ex);
return exResult;
}
}
#endregion
//
#region Others ...
/// <summary>
/// Return Following UserName's List of Current User
/// </summary>
/// <returns>a collection of UserNames</returns>
[HttpGet("Friendship/FollowingsList")]
[Authorize(Policy = LocalApi.PolicyName)]
[Authorize(Policy = XPolicies.EnabledUser)]
public async Task<ActionResult<IEnumerable<string>>> FollowingsList()
{
//
// Do Action ...
try
{
//
var result = await IdentityManager
.GetFollowingList(User.Identity.Name);
//
return Ok(result
.ToDynamicObject());
}
catch (Exception ex)
{
//
var exResult = GetExceptionActionResult(ex);
return exResult;
}
}
/// <summary>
/// Return Followers UserName's List of Current User
/// </summary>
/// <returns>a collection of UserNames</returns>
[HttpGet("Friendship/FollowersList")]
[Authorize(Policy = LocalApi.PolicyName)]
[Authorize(Policy = XPolicies.EnabledUser)]
public async Task<ActionResult<IEnumerable<string>>> FollowersList()
{
//
// Do Action ...
try
{
//
var result = await IdentityManager
.GetFollowersList(User.Identity.Name);
//
return Ok(result
.ToDynamicObject());
}
catch (Exception ex)
{
//
var exResult = GetExceptionActionResult(ex);
return exResult;
}
}
/// <summary>
/// Get Friendship Info Model between Specific User and Current User
/// </summary>
/// <param name="destUser">a user identifier which represent destination user</param>
/// <returns>an instance of <see>XFriendshipInfoDto</see></returns>
[HttpGet("Friendship/{destUser}/FriendshipInfo")]
[Authorize(Policy = LocalApi.PolicyName)]
[Authorize(Policy = XPolicies.EnabledUser)]
public async Task<ActionResult<XFriendshipInfoDto>> FriendshipInfo(
[FromRoute] string destUser
)
{
//
// Do Action ...
try
{
//
var result = await IdentityManager
.GetFriendshipInfo(User.Identity.Name, destUser);
//
return Ok(result
.ToDynamicObject());
}
catch (Exception ex)
{
//
var exResult = GetExceptionActionResult(ex);
return exResult;
}
}
#endregion
#endregion
}
}
@@ -0,0 +1,68 @@
using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using xCommons.Attributes;
using xCommons.Constants;
using xIdentityModels.Dtos;
namespace xIds.Controllers
{
public partial class AccountController
{
//
// Implememnt all Open Actions here ...
//
#region Open Actions ...
[AllowAnonymous]
[RequireXPowered]
[HttpGet("Open")]
public async Task<ActionResult<string>> Get(
[FromHeader] string token,
[FromQuery] string request
)
{
//
// Do Action ...
try
{
//
// Validate Args ...
await ValidationProvider
.GroupValidationBuilder()
.AddNotEmpty(token)
.AddNotEmpty(request)
.ValidateGroupAsync();
//
// Prepare Inner Dto Model ...
var dto = new XOpenActionInnerDto
{
Token = token,
Payload = request
};
//
// Do Action ...
var resultDto = await IdentityManager.OpenGet(dto);
Response.Headers.Add(XAuthentication.TOKEN, resultDto.Token);
//
// Return Result
return Ok(resultDto.Payload);
}
catch (Exception ex)
{
//
var result = GetExceptionActionResult(ex);
return result;
}
}
#endregion
//
#region Private ...
#endregion
}
}
@@ -0,0 +1,511 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using xIdentityHelper;
using xCommons.Attributes;
using xCommons.Extensions;
using xIdentityModels.Dtos;
using xIdentityModels.Models;
using xModels.Dtos;
using static IdentityServer4.IdentityServerConstants;
namespace xIds.Controllers
{
public partial class AccountController
{
//
#region Retrieve Actions ...
/// <summary>
/// Retrieve User Names based on UserIds
/// </summary>
/// <param name="ids">a comma seperated list of UserIds</param>
/// <returns>a collection of UserNames</returns>
[HttpGet("Profile/{ids}/GetNames")]
[Authorize(Policy = XPolicies.User)]
[Authorize(Policy = LocalApi.PolicyName)]
[Authorize(Policy = XPolicies.EnabledUser)]
public async Task<ActionResult<IEnumerable<string>>> GetNames(
[FromRoute] string ids
)
{
//
// Do Action ...
try
{
//
ValidationProvider.NotEmpty(ids);
//
var xIdList = ids.ParseListString<string>();
var result = await IdentityManager.GetUserNamesAsync(
xIdList
);
//
// Return Result
return Ok(result
.ToDynamicObject());
}
catch (Exception ex)
{
//
var result = GetExceptionActionResult(ex);
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> instance</returns>
[HttpPost("Profile/GetNameIds")]
[Authorize(Policy = XPolicies.User)]
[Authorize(Policy = LocalApi.PolicyName)]
[Authorize(Policy = XPolicies.EnabledUser)]
public async Task<ActionResult<IEnumerable<XUserNameIdResponse>>> GetNameIds(
[FromBody] XUserNameIdRequest model
)
{
//
// Do Action ...
try
{
//
ValidationProvider.NotNull(model);
//
var result = await IdentityManager.GetUserNameIdsAsync(
model
);
//
// Return Result
return Ok(result
.ToDynamicObject());
}
catch (Exception ex)
{
//
var result = GetExceptionActionResult(ex);
return result;
}
}
/// <summary>
/// Get Profile Object of Specific User
/// </summary>
/// <param name="userSelectByParam">specifies which user profile must be retrieved</param>
/// <returns>an instance of <see>XUserProfileDto</see></returns>
[RequireXPowered]
[Authorize(Policy = XPolicies.User)]
[Authorize(Policy = LocalApi.PolicyName)]
[HttpGet("Profile/{userSelectByParam?}")]
[Authorize(Policy = XPolicies.EnabledUser)]
public async Task<ActionResult<XUserProfileDto>> GetProfile(
[FromRoute] string userSelectByParam = ""
)
{
//
// Do Action ...
try
{
//
if (userSelectByParam.IsNullOrEmpty())
{
userSelectByParam = User.Identity.Name;
}
//
var result = await IdentityManager
.GetUserProfileAsync(
userSelectByParam,
User.Identity.Name
);
//
// Return Result
return Ok(result
.ToDynamicObject());
}
catch (Exception ex)
{
//
var result = GetExceptionActionResult(ex);
return result;
}
}
/// <summary>
/// Query Users
/// for specifiy users role for query user 'role: string' and 'forceRole: boolean' in header
/// </summary>
/// <param name="query">how to filter results based on <see>XQuery</see> structure</param>
/// <param name="role">an string which represent user role</param>
/// <param name="forceRole">if it's true the user must has exact role, otherwise top level users also listed</param>
/// <returns>an instance of <see>XQueryResult</see> of <see>XUserProfileDto</see></returns>
[RequireXPowered]
[HttpGet("Profile/Query")]
[Authorize(Policy = LocalApi.PolicyName)]
[Authorize(Policy = XPolicies.EnabledAdmin)]
[Authorize(Policy = XPolicies.EnabledAgent)]
public async Task<ActionResult<XQueryResult<XUserProfileDto>>> QueryProfiles(
[FromQuery] XQuery query, [FromHeader] string role, [FromHeader] bool forceRole = false
)
{
//
// Do Action ...
try
{
//
var userId = User.Identity.Name;
//
var result = new XQueryResult<XUserProfileDto>();
if (role.IsNullOrEmpty())
{
//
result = await IdentityManager.QueryUsers(
userId,
query
);
}
else
{
//
result = await IdentityManager.QueryInRoleUsers(
userId,
role,
query,
forceRole
);
}
//
return Ok(result
.ToDynamicObject());
}
catch (Exception ex)
{
//
var exception = GetExceptionActionResult(ex);
return exception;
}
}
/// <summary>
/// Query Specified User's Profile Images
/// </summary>
/// <param name="userSelectByParam">determine's which user profile must be retrieved</param>
/// <param name="query">how to filter results based on <see>XQuery</see> structure</param>
/// <returns>an instance of <see>XQueryResult</see> of <see>XProfileImage</see></returns>
[RequireXPowered]
[Authorize(Policy = XPolicies.User)]
[Authorize(Policy = LocalApi.PolicyName)]
[Authorize(Policy = XPolicies.EnabledUser)]
[Authorize(Policy = XPolicies.EnabledAdmin)]
[HttpGet("Profile/Avatars/Query/{userSelectByParam?}")]
public async Task<ActionResult<XQueryResult<XProfileImageDto>>> QueryAvatars(
[FromQuery] XQuery query, [FromRoute] string userSelectByParam = null
)
{
//
// Do Action ...
try
{
//
if (userSelectByParam.IsNullOrEmpty())
{
userSelectByParam = User.Identity.Name;
}
//
var result = await IdentityManager.QueryAvatars(
userSelectByParam,
query
);
//
return Ok(result
.ToDynamicObject());
}
catch (Exception ex)
{
//
var exception = GetExceptionActionResult(ex);
return exception;
}
}
#endregion
//
#region Update Actions ...
/// <summary>
/// Update User Profile based on XProfileUpdateRequest (FirstName/LastName/DateOfBirth)
/// </summary>
/// <param name="model">user update info, an instance of <see>XProfileUpdateRequest</see></param>
/// <param name="userSelectByParam">determine's which user profile must be retrieved</param>
/// <returns>an instance of <see>XUserProfileDto</see></returns>
[RequireXPowered]
[Authorize(Policy = LocalApi.PolicyName)]
[Authorize(Policy = XPolicies.EnabledUser)]
[HttpPost("Profile/Update/{userSelectByParam?}")]
public async Task<ActionResult<XUserProfileDto>> UpdateProfile(
[FromBody] XProfileUpdateRequest model, [FromRoute] string userSelectByParam = null
)
{
//
// Do Action ...
try
{
//
if (userSelectByParam.IsNullOrEmpty())
{
userSelectByParam = User.Identity.Name;
}
//
var result = await IdentityManager.ProfileUpdateAsync(
userSelectByParam,
User.Identity.Name,
model
);
//
// Return Result
return Ok(result
.ToDynamicObject());
}
catch (Exception ex)
{
//
var result = GetExceptionActionResult(ex);
return result;
}
}
/// <summary>
/// Update a User Profile based on XProfileUpdateRequest Full
/// </summary>
/// <param name="model">user update info, an instance of <see>XProfileUpdateRequest</see></param>
/// <param name="userSelectByParam">determine's which user profile must be retrieved</param>
/// <returns>an instance of <see>XUserProfileDto</see></returns>
[RequireXPowered]
[Authorize(Policy = LocalApi.PolicyName)]
[HttpPost("Profile/{userSelectByParam?}")]
[Authorize(Policy = XPolicies.EnabledAdminOrAgent)]
public async Task<ActionResult<XUserProfileDto>> FullUpdateProfile(
[FromBody] XProfileUpdateRequest model, [FromRoute] string userSelectByParam = null
)
{
//
// Do Action ...
try
{
//
if (userSelectByParam.IsNullOrEmpty())
{
userSelectByParam = User.Identity.Name;
}
//
var result = await IdentityManager.FullProfileUpdateAsync(
userSelectByParam,
User.Identity.Name,
model
);
//
// Return Result
return Ok(result
.ToDynamicObject());
}
catch (Exception ex)
{
//
var result = GetExceptionActionResult(ex);
return result;
}
}
#endregion
//
#region Avatar Actions ...
/// <summary>
/// Add a New Profile Image
/// </summary>
/// <param name="file">an specific File to upload, <see>IFormFile</see></param>
/// <returns>an instance of <see>XUserProfileDto</see></returns>
[RequireXPowered]
[HttpPost("Profile/Avatar")]
[RequestSizeLimit(966_367_641)]
[Authorize(Policy = XPolicies.User)]
[Authorize(Policy = LocalApi.PolicyName)]
[Authorize(Policy = XPolicies.EnabledUser)]
public async Task<ActionResult<XUserProfileDto>> AddAvatar(
[FromForm] IFormFile file
)
{
//
// Do Action ...
try
{
//
// Validate Args ...
ValidationProvider.NotNull(file);
//
// Get Request ...
var result = await IdentityManager
.AddAvatar(
User.Identity.Name,
file
);
//
return Ok(result
.ToDynamicObject());
}
catch (Exception ex)
{
//
var exResult = GetExceptionActionResult(ex);
return exResult;
}
}
/// <summary>
/// Add a New Collection of Profile Images
/// </summary>
/// <param name="files">a collection of Files to upload, <see>IFormFileCollection</see></param>
/// <returns>an instance of <see>XUserProfileDto</see></returns>
[RequireXPowered]
[HttpPost("Profile/Avatars")]
[RequestSizeLimit(966_367_641)]
[Authorize(Policy = XPolicies.User)]
[Authorize(Policy = LocalApi.PolicyName)]
[Authorize(Policy = XPolicies.EnabledUser)]
public async Task<ActionResult<XUserProfileDto>> AddAvatars(
[FromForm] IFormFileCollection files
)
{
//
// Do Action ...
try
{
//
// Validate Args ...
ValidationProvider.NotZeroChilds(files);
//
// Get Request ...
var result = await IdentityManager.AddAvatars(
User.Identity.Name,
files
);
//
return Ok(result
.ToDynamicObject());
}
catch (Exception ex)
{
//
var exResult = GetExceptionActionResult(ex);
return exResult;
}
}
/// <summary>
/// Remove Profile Images
/// </summary>
/// <param name="ids">a comma seperated list of avatarIds to remove</param>
/// <returns>an instance of <see>XUserProfileDto</see></returns>
[RequireXPowered]
[Authorize(Policy = XPolicies.User)]
[HttpDelete("Profile/Avatars/{ids}")]
[Authorize(Policy = LocalApi.PolicyName)]
[Authorize(Policy = XPolicies.EnabledUser)]
public async Task<ActionResult<XUserProfileDto>> RemoveAvatars(
[FromRoute] string ids
)
{
//
// Do Action ...
try
{
//
ValidationProvider.NotEmpty(ids);
var idList = ids.Split(",");
var idCollection = new Collection<int>();
//
foreach (var s in idList)
{
idCollection.Add(int.Parse(s));
}
//
// Get Request ...
var result = await IdentityManager
.RemoveAvatar(
User.Identity.Name,
idCollection.ToArray()
);
//
return Ok(result
.ToDynamicObject());
}
catch (Exception ex)
{
//
var result = GetExceptionActionResult(ex);
return result;
}
}
/// <summary>
/// set Specified Profile Image as Avatar
/// </summary>
/// <param name="id">an integer which reperesent AvatarId to set as current Avatar</param>
/// <returns>an instance of <see>XUserProfileDto</see></returns>
[RequireXPowered]
[HttpPost("Profile/Avatars/{id:int}/Set")]
[Authorize(Policy = XPolicies.User)]
[Authorize(Policy = LocalApi.PolicyName)]
[Authorize(Policy = XPolicies.EnabledUser)]
public async Task<ActionResult<XUserProfileDto>> SetAvatar(
[FromRoute] int id
)
{
//
// Do Action ...
try
{
//
var result = await IdentityManager
.SetAvatar(
User.Identity.Name,
id
);
//
return Ok(result
.ToDynamicObject());
}
catch (Exception ex)
{
//
var exception = GetExceptionActionResult(ex);
return exception;
}
}
#endregion
}
}
@@ -0,0 +1,63 @@
using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using xCommons.Attributes;
using xCommons.Extensions;
using xIdentityHelper;
using xModels.Dtos;
using static IdentityServer4.IdentityServerConstants;
namespace xIds.Controllers
{
public partial class AccountController
{
/// <summary>
/// Query Users
/// for specifiy users role for query user 'role: string' and 'forceRole: boolean' in header
/// </summary>
/// <param name="query">how to filter results based on <see>XQuery</see> structure</param>
/// <param name="role">an string which represent user role</param>
/// <param name="forceRole">if it's true the user must has exact role, otherwise top level users also listed</param>
/// <returns>an instance of <see>XQueryResult</see> of <see>XUserProfileDto</see></returns>
[RequireXPowered]
[HttpGet("Users/Query")]
[Authorize(Policy = LocalApi.PolicyName)]
[Authorize(Policy = XPolicies.EnabledUser)]
public async Task<ActionResult<XQueryResult<string>>> QueryUsers(
[FromQuery] XQuery query,
[FromHeader] string role,
[FromHeader] bool forceRole = false
)
{
//
// Do Actions ...
try
{
//
// Retrieve User Info ...
var userInfo = await GetUserInfo();
//
// Retrieve Result Based on Provided Query ...
var result = await IdentityManager.QueryUsers(
role: role,
query: query,
forceRole: forceRole,
requestedUserSelectByParam: userInfo.UserId
);
//
// Return result ...
return Ok(result
.ToDynamicObject());
}
catch (Exception ex)
{
//
var exception = GetExceptionActionResult(ex);
return exception;
}
}
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,62 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using xCommons.Attributes;
using xCommons.Extensions;
using xIdentityHelper;
using xIdentityModels.Dtos;
using xModels.Dtos;
using static IdentityServer4.IdentityServerConstants;
namespace xIds.Controllers
{
/// <summary>
/// Actions which related to Search and Suggest Users for
/// Friendship or Detecting ...
/// </summary>
public partial class AccountController
{
//
#region Actions of Search ...
/// <summary>
/// Query Open To Search Users Profiles ...
/// Query all Users Which their Profiles is Open To Search ...
/// </summary>
/// <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>
[RequireXPowered]
[HttpGet("OpenToSearch/Query")]
[Authorize(Policy = LocalApi.PolicyName)]
[Authorize(Policy = XPolicies.EnabledUser)]
public async Task<ActionResult<XQueryResult<XUserProfileDto>>> QueryOpenToSearchProfiles(
[FromQuery] XQuery query)
{
//
// Do Action ...
try
{
//
var userId = User.Identity.Name;
//
var result = await IdentityManager.QueryOpenToSearchUsers(
userId,
query
);
return Ok(result
.ToDynamicObject());
}
catch (Exception ex)
{
//
var exception = GetExceptionActionResult(ex);
return exception;
}
}
#endregion
}
}
@@ -0,0 +1,161 @@
using System.Linq;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using xIdentityHelper;
using xCommons.Attributes;
using static IdentityServer4.IdentityServerConstants;
namespace xIds.Controllers
{
public partial class AccountController
{
//
#region Test Actions ...
/// <summary>
/// API Read Scope
/// </summary>
/// <returns>string message</returns>
[RequireXPowered]
[Authorize(LocalApi.PolicyName)]
[HttpGet("Test/PassReadAccess")]
[Authorize(Policy = XPolicies.ReadAccess)]
public ActionResult<string> PassReadAccess()
{
return Ok("Read Access Passed ...");
}
/// <summary>
/// API Write Scope
/// </summary>
/// <returns>string message</returns>
[RequireXPowered]
[Authorize(LocalApi.PolicyName)]
[HttpGet("Test/PassWriteAccess")]
[Authorize(Policy = XPolicies.WriteAccess)]
public ActionResult<string> PassWriteAccess()
{
return Ok("Write Access Passed ...");
}
/// <summary>
/// API Admin Scope
/// </summary>
/// <returns>string message</returns>
[RequireXPowered]
[Authorize(LocalApi.PolicyName)]
[HttpGet("Test/PassAdminAccess")]
[Authorize(Policy = XPolicies.AdminAccess)]
public ActionResult<string> PassAdminAccess()
{
return Ok("Admin Access Passed ...");
}
/// <summary>
/// API Manage Scop
/// </summary>
/// <returns>string message</returns>
[RequireXPowered]
[Authorize(LocalApi.PolicyName)]
[HttpGet("Test/PassManageAccess")]
[Authorize(Policy = XPolicies.ManageAccess)]
public ActionResult<string> PassManageAccess()
{
return Ok("Manage Access Passed ...");
}
/// <summary>
/// a simple Action which returns a List of Authenticated User Claims
/// </summary>
/// <returns>string message which represent current user's claims</returns>
[RequireXPowered]
[HttpGet("Test/HiClaims")]
[Authorize(LocalApi.PolicyName)]
[Authorize(Policy = XPolicies.User)]
public ActionResult<string> HiClaims()
{
//
var result = new
{
name = User.Identity.Name,
claims = User.Claims.Select(c => new
{
c.Type,
c.Value
})
};
//
return Ok(result);
}
/// <summary>
/// a simple Hello User for Checking Authentication and Policy
/// </summary>
/// <returns>string message which contains authenticated user name</returns>
[RequireXPowered]
[HttpGet("Test/HiUser")]
[Authorize(LocalApi.PolicyName)]
[Authorize(Policy = XPolicies.User)]
public ActionResult<string> HiUser()
{
//
var result = $"Hi User: {User.Identity.Name} ...";
//
return Ok(result);
}
/// <summary>
/// a simple Hello User for Checking Authentication and Policy
/// </summary>
/// <returns>string message which contains authenticated user name</returns>
[RequireXPowered]
[HttpGet("Test/HiEnabledUser")]
[Authorize(LocalApi.PolicyName)]
[Authorize(Policy = XPolicies.EnabledUser)]
public ActionResult<string> HiEnabledUser()
{
//
var result = $"Hi User: {User.Identity.Name} is Enabled ...";
//
return Ok(result);
}
/// <summary>
/// a simple Hello User for Checking Authentication and Policy
/// </summary>
/// <returns>string message which contains authenticated user name</returns>
[RequireXPowered]
[HttpGet("Test/HiAdmin")]
[Authorize(LocalApi.PolicyName)]
[Authorize(Policy = XPolicies.Admin)]
public ActionResult<string> HiAdmin()
{
//
var result = $"Hi Admin: {User.Identity.Name} ...";
//
return Ok(result);
}
/// <summary>
/// a simple Hello User for Checking Authentication and Policy
/// </summary>
/// <returns>string message which contains authenticated user name</returns>
[RequireXPowered]
[HttpGet("Test/HiEnabledAdmin")]
[Authorize(LocalApi.PolicyName)]
[Authorize(Policy = XPolicies.EnabledAdmin)]
public ActionResult<string> HiEnabledAdmin()
{
//
var result = $"Hi Admin: {User.Identity.Name} is Enabled ...";
//
return Ok(result);
}
#endregion
}
}
+30
View File
@@ -0,0 +1,30 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using xCommons.Configurations;
using xCommons.Providers;
using xIds.Controllers.Base;
using xIds.Interfaces;
namespace xIds.Controllers
{
/// <summary>
/// Provide all available tools for manipulating users and accounts
/// </summary>
[ApiController]
public partial class AccountController : XIBaseController
{
public AccountController(
IXIdentityManager identityManager,
ILogger<AccountController> logger,
XAppConfiguration appConfiguration,
XValidationProvider validationProvider
) : base(
logger,
appConfiguration,
identityManager,
validationProvider
)
{ }
}
}
+269
View File
@@ -0,0 +1,269 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using xCommons.Configurations;
using xCommons.Constants;
using xCommons.Controllers;
using xCommons.Extensions;
using xCommons.Providers;
using xExceptions.Constants;
using xIdentityModels.Constants;
using xIdentityModels.Models;
using xIds.Interfaces;
namespace xIds.Controllers.Base
{
public abstract class XIBaseController : XBaseController
{
public IXIdentityManager IdentityManager { get; }
protected XIBaseController(
ILogger logger,
XAppConfiguration appConfiguration,
IXIdentityManager identityManager,
XValidationProvider validationProvider
) : base(
logger,
appConfiguration,
validationProvider
)
{
IdentityManager = identityManager;
}
//
#region User Handlers NonActions ...
/// <summary>
/// Retrieve User Identifier Base on XActionRequest
/// </summary>
/// <param name="model"></param>
/// <param name="forceNotNull"></param>
/// <param name="excludes"></param>
/// <returns></returns>
[NonAction]
public string GetUserSelectByParam(
XActionRequest model,
bool forceNotNull = true,
ICollection<XUserSelectBy> excludes = null)
{
//
// Validate Args ...
ValidationProvider.NotNull(model);
//
var result = IdentityManager.GetUserSelectByParam(
model,
forceNotNull,
excludes);
//
return result;
}
/// <summary>
/// Retrieve Access Token
/// </summary>
/// <returns></returns>
[NonAction]
public async Task<string> GetAccessToken()
{
//
var accessToken = Request.Headers[XAuthorization.Header].ToString();
if (accessToken.IsNullOrEmpty())
{
accessToken = await HttpContext.GetTokenAsync(XAuthorization.AccessToken);
}
//
if (accessToken
.ToNormalString()
.Contains(XAuthorization.TokenIdentifier.ToNormalString()))
{
accessToken = accessToken.Remove(0, XAuthorization.TokenIdentifier.Length);
}
//
Logger.LogInformation($"Token: {accessToken}");
return accessToken;
}
/// <summary>
/// Retrieve Refresh Token
/// </summary>
/// <returns></returns>
[NonAction]
public async Task<string> GetRefreshToken()
{
//
var refreshToken = Request.Headers[XAuthorization.RefreshToken].ToString();
if (refreshToken.IsNullOrEmpty())
{
refreshToken = await HttpContext.GetTokenAsync(XAuthorization.RefreshToken);
}
//
Logger.LogInformation($"Refresh Token: {refreshToken}");
return refreshToken;
}
/// <summary>
/// Retrieve Access Token Expiration Date
/// </summary>
/// <returns></returns>
[NonAction]
public async Task<long> GetTokenExpiresAt()
{
//
var expiresAtStr = Request.Headers[XAuthorization.ExpiresAt].ToString();
if (expiresAtStr.IsNullOrEmpty())
{
expiresAtStr = await HttpContext.GetTokenAsync(XAuthorization.ExpiresAt);
}
//
var expiresAt = expiresAtStr.ConvertTo<long>();
//
Logger.LogInformation($"Token ExpiresAt: {expiresAtStr}");
return expiresAt;
}
/// <summary>
/// Retrieve All Required Tokens
/// </summary>
/// <returns></returns>
[NonAction]
public async Task<XLoginResponse> RetrieveTokensAsXLoginResponse()
{
//
var accessToken = await GetAccessToken();
var refreshToken = await GetRefreshToken();
var expiresAt = await GetTokenExpiresAt();
//
return new XLoginResponse
{
AccessToken = accessToken,
RefreshToken = refreshToken,
ExpiresAt = expiresAt
};
}
/// <summary>
/// Retrieve All Required Tokens
/// </summary>
/// <returns></returns>
[NonAction]
public async Task<XTokenResponse> RetrieveTokensAsXTokenResponse()
{
//
var accessToken = await GetAccessToken();
var refreshToken = await GetRefreshToken();
var expiresAt = await GetTokenExpiresAt();
//
return new XTokenResponse
{
AccessToken = accessToken,
RefreshToken = refreshToken,
ExpiresAt = expiresAt
};
}
/// <summary>
/// Retrive UserInfo
/// </summary>
/// <returns></returns>
[NonAction]
public async Task<XUserClaimsInfoDto> GetUserInfo()
{
//
var claims = User.Claims ?? null;
if (!claims.HasChild())
{
return null;
}
var xTokens = await RetrieveTokensAsXTokenResponse();
var result = new XUserClaimsInfoDto(
xTokens.AccessToken,
xTokens.RefreshToken,
xTokens.ExpiresAt,
User.Claims
);
//
return result;
}
/// <summary>
/// Validate User Authenticated and Retrieve User Info
/// </summary>
/// <returns></returns>
[NonAction]
public async Task<XUserClaimsInfoDto> ValidateAndGetUserInfo()
{
//
if (!User.Identity.IsAuthenticated)
{
XException.NotAuthorized.Throw();
}
//
var result = await GetUserInfo();
if (result.IsNull())
{
XException.NotAuthorized.Throw();
}
//
return result;
}
#endregion
//
#region NonActions ...
/// <summary>
/// Convert an Exception to Propper Error Result
/// </summary>
/// <param name="ex"></param>
/// <returns></returns>
[NonAction]
public new ActionResult GetExceptionActionResult(Exception ex)
{
//
var exception = GetExceptionResult(ex);
var error = ex.Message.ToXError();
//
Logger.LogError($"exception: {exception}, error: {error}");
//
try
{
var xError = exception.Message.ToXError();
var xException = (XException)xError.Id;
//
switch (xException)
{
//
case XException.NotFound:
return NotFound(error);
//
case XException.NotAuthorized:
return Unauthorized(error);
}
}
catch { }
//
return BadRequest(error);
}
#endregion
}
}
+44
View File
@@ -0,0 +1,44 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using xCommons.Configurations;
using xCommons.Controllers;
using xCommons.Providers;
namespace xIds.Controllers
{
/// <summary>
/// Startup and Running Controller
/// </summary>
[Route("")]
[AllowAnonymous]
public class StartupController : XBaseController
{
public StartupController(
ILogger<StartupController> logger,
XAppConfiguration appConfiguration,
XValidationProvider validationProvider
) : base(
logger,
appConfiguration,
validationProvider
)
{ }
/// <summary>
/// Show Configured Welcome Message
/// </summary>
/// <returns>an string message</returns>
[HttpGet("")]
[AllowAnonymous]
public virtual ActionResult<string> Index()
{
//
var controllerName = GetControllerName();
var message = $"{AppConfiguration.WelcomeMessage}";
//
return Ok(message);
}
}
}
+420
View File
@@ -0,0 +1,420 @@
using System.Collections.Generic;
using IdentityServer4;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.HttpOverrides;
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using xCommons.Authorization;
using xCommons.Constants;
using xCommons.Extensions;
using xExceptions.Constants;
using xIdentityHelper;
using xIdentityModels;
using xIdentityModels.Configurations;
using xIds.Configurations;
using xIds.Constants;
using xIds.DbContext;
using xIds.Extensions;
using xIds.Helpers;
using xIds.Interfaces;
using xIds.Providers;
using xIds.Validators;
using xMessageService.DI;
using xMessageService.Interfaces;
using xStorageService.DI;
using xStorageService.Interfaces;
namespace xIds.DI
{
public static class XDIHelperExtension
{
/// <summary>
/// Register Application DataProvider
/// </summary>
/// <param name="services"></param>
/// <param name="configuration"></param>
public static void AddXDataProvider(
this IServiceCollection services,
IConfiguration configuration
)
{
//
// Retrieve DbInfo ...
var dbInfo = configuration.GetXDbInfo();
var identityDbConnectionString = configuration.GetConnectionString(ConnectionStringNames.IDENTITY_CONNECTION_NAME);
//
// Register Db Context for EF ...
services.AddDbContext<XIdentityDbContext>(builder =>
{
//
builder.PrepareXDbContextOptionsBuilder(
dbInfo,
identityDbConnectionString
);
});
}
/// <summary>
/// Extract Connection String From IConfiguration
/// </summary>
/// <param name="config"></param>
/// <param name="connectionName"></param>
/// <returns></returns>
public static string GetXConnectionString(
this IConfiguration config,
string connectionName = null
)
{
//
if (connectionName.IsNullOrEmpty())
{
connectionName = XDbProviderConfigurations.DEFAULT_CONNECTION_NAME;
}
//
return config.GetConnectionString(connectionName);
}
/// <summary>
/// Retrive XDataService Configurations
/// </summary>
/// <param name="source"></param>
/// <param name="connectionName"></param>
/// <returns></returns>
public static XDataServiceConfiguration GetXDataServiceConfiguration(
this IConfiguration source,
string connectionName = null
)
{
//
var xDataServiceConfigSection = source
.GetSection(Constants.ConfigurationNodeNames.DATA_SERVICE_NODE_NAME);
var result = xDataServiceConfigSection.Get<XDataServiceConfiguration>();
if (result.IsNull())
{
result = new XDataServiceConfiguration();
}
//
result.Provider = source.GetXDbProviderType();
result.ConnectionString = source.GetXConnectionString(connectionName);
//
return result;
}
/// <summary>
/// Register XDataService Configuration
/// </summary>
/// <param name="services"></param>
/// <param name="configuration"></param>
/// <param name="connectionName"></param>
public static void AddXDataServiceConfiguration(
this IServiceCollection services,
IConfiguration configuration,
string connectionName = null
)
{
//
var dataServiceConfiguration = configuration
.GetXDataServiceConfiguration(connectionName);
//
services.AddSingleton<XDataServiceConfiguration>(dataServiceConfiguration);
}
/// <summary>
/// Register All Requirements For XIdentityServer Usage
/// </summary>
/// <param name="services"></param>
/// <param name="configuration"></param>
public static void AddXIdentityServerRequirements(
this IServiceCollection services,
IConfiguration configuration
)
{
//
services.AddXDataProvider(configuration);
//
// Register XIdentityMessageProvider ...
services.AddXIdentityMessageProvider();
//
// Register IdentityManager Service ...
var xIdentityManager = services.GetRegisteredService<IXIdentityManager>();
if (xIdentityManager.IsNull())
{
services.AddXIdentityManager();
}
//
// Check XMessage Service Registered or not and Register it if not ...
var xMessageProvider = services.GetRegisteredService<IXMessageProvider>();
if (xMessageProvider.IsNull())
{
services.AddXMessageService(configuration);
}
//
// Check Storage Service Registered or not and Register it if not ...
var xStorageProvider = services.GetRegisteredService<IXStorageProvider>();
if (xStorageProvider.IsNull())
{
services.AddXStorageService(configuration);
}
//
// Register XIdentityConfiguration ...
var xIdentityConfiguration = configuration.GetXIdentityConfiguration();
services.AddSingleton<XIdentityConfiguration>(xIdentityConfiguration);
//
// Register XIdentityHelper ...
services.AddSingleton<IXIdentityHelper, XIdentityHelper>();
var xIdentityHelper = services.GetRegisteredService<IXIdentityHelper>();
//
// Register Asp.net Identity ...
services.AddXIdentity();
}
/// <summary>
/// Add IdentityServer with Support of Ef
/// </summary>
/// <param name="services"></param>
/// <param name="configuration"></param>
public static void AddEfSupportXIdentityServer(
this IServiceCollection services,
IConfiguration configuration
)
{
//
// Register All Requirements ...
services.AddXIdentityServerRequirements(configuration);
//
// add DbSeeder Config ...
services.AddXDebSeederDescriptor(configuration);
//
// Retrieve Certificate ...
var identityServerSigningCertificate = configuration.GetCertificate(CertificatesConfigurations.CertificateNames.IdentityServerSigning);
//
// Retrieve DbInfo ...
var dbInfo = configuration.GetXDbInfo();
//
// Retrieve Connection String Names ...
var configDbConnectionString = configuration.GetConnectionString(ConnectionStringNames.CONFIGURATION_CONNECTION_NAME);
var persistedGrantDbConnectionString = configuration.GetConnectionString(ConnectionStringNames.PRESISTED_GRANTS_CONNECTION_NAME);
//
// Register IdentityServer ...
services.AddIdentityServer(options =>
{
//
options.Discovery.CustomEntries.Add("account_api", "~/account");
//
// options.Events.RaiseErrorEvents = true;
// options.Events.RaiseFailureEvents = true;
// options.Events.RaiseSuccessEvents = true;
// options.Events.RaiseInformationEvents = true;
})
.LoadSigningCredentialFrom(identityServerSigningCertificate)
.AddXDbProvider(configuration)
.AddAspNetIdentity<XUser>()
.AddProfileService<XIdentityProfileService>()
.AddResourceOwnerValidator<XResourceOwnerPasswordValidator>();
}
/// <summary>
/// force app to use IdentityServer
/// </summary>
/// <param name="app"></param>
/// <param name="logger"></param>
public static void UseXIdentityServer(
this IApplicationBuilder app,
ILogger logger
)
{
//
// Update XIdentityServer Configurations before Use Identity Server ...
app.SeedData(logger)
.GetAwaiter()
.GetResult();
//
// Use Identity Server ...
app.UseIdentityServer();
}
/// <summary>
/// Register Asp.net Identity as User Store
/// </summary>
/// <param name="services"></param>
public static void AddXIdentity(this IServiceCollection services)
{
//
var xIdentityConfiguration = services.GetRegisteredService<XIdentityConfiguration>();
if (xIdentityConfiguration.IsNull())
{
XException.InvalidConfiguration.Throw();
}
//
var xIdentityHelper = services.GetRegisteredService<IXIdentityHelper>();
if (xIdentityHelper.IsNull())
{
XException.InvalidConfiguration.Throw();
}
//
// Add Identity for User Persists ...
services.AddIdentity<XUser, IdentityRole>(options =>
{
//
var mLockoutSpan = xIdentityHelper.GetLockoutTimeSpan();
//
// Lockout Setting ...
options.Lockout.DefaultLockoutTimeSpan = mLockoutSpan;
options.Lockout.MaxFailedAccessAttempts = xIdentityConfiguration.Policy.Lockout.MaxFailedAccessAttempts;
options.Lockout.AllowedForNewUsers = xIdentityConfiguration.Policy.Lockout.AllowedForNewUsers;
//
// Password settings ...
options.Password.RequireDigit = xIdentityConfiguration.Policy.Password.RequireDigit;
options.Password.RequireLowercase = xIdentityConfiguration.Policy.Password.RequireLowercase;
options.Password.RequireNonAlphanumeric = xIdentityConfiguration.Policy.Password.RequireNonAlphanumeric;
options.Password.RequireUppercase = xIdentityConfiguration.Policy.Password.RequireUppercase;
options.Password.RequiredLength = xIdentityConfiguration.Policy.Password.RequiredLength;
options.Password.RequiredUniqueChars = xIdentityConfiguration.Policy.Password.RequiredUniqueChars;
//
// SignIn settings ...
options.SignIn.RequireConfirmedEmail = xIdentityConfiguration.Policy.SignIn.RequireConfirmedEmail;
options.SignIn.RequireConfirmedPhoneNumber = xIdentityConfiguration.Policy.SignIn.RequireConfirmedPhoneNumber;
//
// User settings ...
options.User.AllowedUserNameCharacters = xIdentityConfiguration.Policy.User.AllowedUserNameCharacters;
options.User.RequireUniqueEmail = xIdentityConfiguration.Policy.User.RequireUniqueEmail;
})
.AddEntityFrameworkStores<XIdentityDbContext>()
.AddDefaultTokenProviders();
}
/// <summary>
/// Register IdentityServer Based Authentication
/// </summary>
/// <param name="services"></param>
/// <param name="configuration"></param>
public static void AddXIdentityServerAuthentication(
this IServiceCollection services,
IConfiguration configuration
)
{
//
services.AddXIdentityResourceConfiguration(configuration);
services.AddAuthentication()
.AddJwtBearer(options =>
{
//
options.SaveToken = true;
options.RequireHttpsMetadata = false;
//
options.ForwardDefault = XAuthentication.IDENTITY_SERVER_LOCAL_API;
})
.AddLocalApi();
}
/// <summary>
/// Register Authorization Policies
/// </summary>
/// <param name="services"></param>
/// <param name="policies"></param>
public static void AddXAuthorization(
this IServiceCollection services,
IDictionary<string, AuthorizationPolicy> policies = null
)
{
//
services.AddAuthorization(options =>
{
//
if (!policies.IsNull())
{
//
// Fill Policies ...
using (var policiesEnumerator = policies.GetEnumerator())
{
while (policiesEnumerator.MoveNext())
{
var policyDescriptor = policiesEnumerator.Current;
options.AddPolicy(policyDescriptor.Key, policyDescriptor.Value);
}
}
}
//
var xPolicies = XAuthorizationHelper.GetXAuthorizationPolicies();
if (!xPolicies.IsNull())
{
//
// Fill Policies ...
using (var policiesEnumerator = xPolicies.GetEnumerator())
{
while (policiesEnumerator.MoveNext())
{
var policyDescriptor = policiesEnumerator.Current;
options.AddPolicy(policyDescriptor.Key, policyDescriptor.Value);
}
}
}
//
// Add Local APi Policy ...
options.AddPolicy(IdentityServerConstants.LocalApi.PolicyName, policy =>
{
//
policy.AddAuthenticationSchemes(IdentityServerConstants.LocalApi.AuthenticationScheme);
policy.RequireAuthenticatedUser();
});
});
//
services.AddSingleton<IAuthorizationHandler, RequiredRolesHandler>();
}
/// <summary>
/// Use Forward Headers Options for resolving behind a proxy issues ...
/// </summary>
/// <param name="source"></param>
/// <returns></returns>
public static void UseXForwardOptions(this IApplicationBuilder source)
{
//
var forwardOptions = new ForwardedHeadersOptions
{
ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto,
RequireHeaderSymmetry = false
};
//
forwardOptions.KnownNetworks.Clear();
forwardOptions.KnownProxies.Clear();
//
// ref: https://github.com/aspnet/Docs/issues/2384
source.UseForwardedHeaders(forwardOptions);
}
}
}
View File
+57
View File
@@ -0,0 +1,57 @@
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
using xIdentityModels;
using xIdentityModels.Models;
using xIdentityModels.Navigations;
namespace xIds.DbContext
{
public class XIdentityDbContext : IdentityDbContext
{
//
#region Props/DbSets ...
public DbSet<XToken> Tokens { get; set; }
public DbSet<XDevice> Devices { get; set; }
public DbSet<XProfileImage> Avatars { get; set; }
public DbSet<XBannedDevice> BannedDevices { get; set; }
public DbSet<XFriendshipFollower> Followers { get; set; }
public DbSet<XFriendshipFollowing> Followings { get; set; }
public DbSet<XVerificationRequest> VerificationRequests { get; set; }
#endregion
public XIdentityDbContext(DbContextOptions<XIdentityDbContext> options) : base(options)
{
//
try
{
Database.EnsureCreated();
Database.Migrate();
}
catch { }
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
//
base.OnModelCreating(modelBuilder);
//
// Fix XUser GUID Generator on Add ...
modelBuilder
.Entity<IdentityUser>()
.Property(e => e.Id)
.ValueGeneratedOnAdd();
//
// Handle Role List ...
modelBuilder.Entity<XUser>(u =>
{
u.HasMany(x => x.Roles)
.WithOne()
.HasForeignKey(ur => ur.UserId)
.IsRequired();
});
}
}
}
+168
View File
@@ -0,0 +1,168 @@
using System;
using System.Reflection;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using MySql.EntityFrameworkCore.Infrastructure;
using xCommons.Extensions;
using xIdentityModels.Configurations;
using xIds.Configurations;
using xIds.Constants;
using xIds.Models;
namespace xIds.Extensions
{
public static class DbExtensions
{
/// <summary>
/// Retrieve Data Provider Type from Configurations
/// </summary>
/// <param name="source"></param>
/// <returns></returns>
public static XDbProviders GetXDbProviderType(this IConfiguration source)
{
//
var dbProvider = (source[$"{ConfigurationNodeNames.PROVIDER_NODE_NAME}"])
.ToNormalString();
//
return dbProvider == ProviderType.SQLServer.ToNormalString() ?
XDbProviders.SQLServer :
dbProvider == ProviderType.SQLite.ToNormalString() ?
XDbProviders.SQLite :
dbProvider == ProviderType.MySQL.ToNormalString() ?
XDbProviders.MySQL :
XDbProviders.None;
}
/// <summary>
/// Retrieve AspNet Identity Configurations
/// </summary>
/// <param name="source"></param>
/// <returns></returns>
public static XIdentityConfiguration GetXIdentityConfiguration(this IConfiguration source)
{
//
var xIdentityConfigSection = source
.GetSection(ConfigurationNodeNames.IDENTITY_NODE_NAME);
return xIdentityConfigSection.Get<XIdentityConfiguration>();
}
/// <summary>
/// Retrieve XIdentityResource Configuration from AppSettings
/// </summary>
/// <param name="configuration"></param>
/// <returns></returns>
public static XIdentityResourceConfiguration GetXIdentityResourceConfiguration(this IConfiguration configuration)
{
//
var xIdentityResourceSection = configuration.GetSection(ConfigurationNodeNames.IDENTITY_RESOURCE_NODE_NAME);
return xIdentityResourceSection.Get<XIdentityResourceConfiguration>();
}
/// <summary>
/// Register XIdentityResourceConfiguration
/// </summary>
/// <param name="services"></param>
/// <param name="configuration"></param>
public static void AddXIdentityResourceConfiguration(
this IServiceCollection services,
IConfiguration configuration
)
{
//
var xIdentityResourceConfiguration = configuration.GetXIdentityResourceConfiguration();
if (!xIdentityResourceConfiguration.IsNull())
{
services.AddSingleton<XIdentityResourceConfiguration>(xIdentityResourceConfiguration);
}
}
/// <summary>
/// Converts DbInfo Options Builder for MySql Usage
/// </summary>
/// <param name="source"></param>
/// <returns></returns>
public static Action<MySQLDbContextOptionsBuilder> GetMySqlOptionsBuilder(this XDbInfo source)
{
return ((Action<MySQLDbContextOptionsBuilder>)source.OptionsBuilder);
}
/// <summary>
/// Converts DbInfo Options Builder for SQLite Usage
/// </summary>
/// <param name="source"></param>
/// <returns></returns>
public static Action<SqliteDbContextOptionsBuilder> GetSQLiteOptionsBuilder(this XDbInfo source)
{
return ((Action<SqliteDbContextOptionsBuilder>)source.OptionsBuilder);
}
/// <summary>
/// Converts DbInfo Options Builder for SQLServer Usage
/// </summary>
/// <param name="source"></param>
/// <returns></returns>
public static Action<SqlServerDbContextOptionsBuilder> GetSQLServerOptionsBuilder(this XDbInfo source)
{
return ((Action<SqlServerDbContextOptionsBuilder>)source.OptionsBuilder);
}
/// <summary>
/// Retrieve Required Informations to Register DbContexts into Di as XDbInfo instance
/// </summary>
/// <param name="configuration"></param>
/// <returns></returns>
public static XDbInfo GetXDbInfo(this IConfiguration configuration)
{
return new XDbInfo
{
ProviderType = configuration.GetXDbProviderType(),
MigrationsAssembly = typeof(Startup).GetTypeInfo().Assembly.GetName().Name
};
}
/// <summary>
/// Prepare DbContextOptionsBuilder with Propper data to Support Configured DbProvider
/// </summary>
/// <param name="source"></param>
/// <param name="dbInfo"></param>
/// <param name="connectionString"></param>
public static void PrepareXDbContextOptionsBuilder(
this DbContextOptionsBuilder source,
XDbInfo dbInfo,
string connectionString
)
{
//
if (dbInfo.ProviderType == XDbProviders.MySQL)
{
//
// Add Support for MySql ...
source.UseMySQL(
connectionString,
dbInfo.GetMySqlOptionsBuilder()
);
}
else if (dbInfo.ProviderType == XDbProviders.SQLite)
{
//
// Add Support for SQLite ...
source.UseSqlite(
connectionString,
dbInfo.GetSQLiteOptionsBuilder()
);
}
else if (dbInfo.ProviderType == XDbProviders.SQLServer)
{
//
// Add Support for SQLServer ...
source.UseSqlServer(
connectionString,
dbInfo.GetSQLServerOptionsBuilder()
);
}
}
}
}
@@ -0,0 +1,117 @@
using System.Reflection;
using System.Security.Cryptography.X509Certificates;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using xCommons.Extensions;
using xCommons.Models;
using xIds.Constants;
namespace xIds.Extensions
{
public static class IIdentityServerBuilderExtensions
{
/// <summary>
/// Add Support for Configured XDbProvider to IdentityServer
/// </summary>
/// <param name="source"></param>
/// <param name="configuration"></param>
/// <returns></returns>
public static IIdentityServerBuilder AddXDbProvider(
this IIdentityServerBuilder source,
IConfiguration configuration
)
{
//
// Retrieve DB Info ...
var dbInfo = configuration.GetXDbInfo();
dbInfo.OptionsBuilder = (optionsBuilder) =>
{
optionsBuilder.MigrationsAssembly(typeof(Startup).GetTypeInfo().Assembly.GetName().Name);
optionsBuilder.UseQuerySplittingBehavior(QuerySplittingBehavior.SplitQuery);
};
//
// Retrieve Connection String Names ...
var identityDbConnectionString = configuration.GetConnectionString(ConnectionStringNames.IDENTITY_CONNECTION_NAME);
var configDbConnectionString = configuration.GetConnectionString(ConnectionStringNames.CONFIGURATION_CONNECTION_NAME);
var persistedGrantDbConnectionString = configuration.GetConnectionString(ConnectionStringNames.PRESISTED_GRANTS_CONNECTION_NAME);
//
// Add Operational Store ...
source.AddOperationalStore(opt =>
{
opt.ConfigureDbContext =
builder =>
{
//
builder.PrepareXDbContextOptionsBuilder(
dbInfo,
persistedGrantDbConnectionString
);
//
builder.UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking);
};
//
// this enables automatic token cleanup. this is optional.
opt.EnableTokenCleanup = true;
opt.TokenCleanupInterval = 3600;
});
//
// Add Configuration Store ...
source.AddConfigurationStore(opt =>
{
opt.ConfigureDbContext =
builder =>
{
//
builder.PrepareXDbContextOptionsBuilder(
dbInfo,
configDbConnectionString
);
};
});
//
return source;
}
/// <summary>
/// Set Configured Certificate File to IdentityServer
/// </summary>
/// <param name="builder"></param>
/// <param name="certificate"></param>
/// <returns></returns>
public static IIdentityServerBuilder LoadSigningCredentialFrom(
this IIdentityServerBuilder builder,
XCertificate certificate
)
{
//
if (!certificate.IsNull() &&
!certificate.Path.IsNullOrEmpty() &&
!certificate.Secret.IsNullOrEmpty()
)
{
try
{
builder.AddSigningCredential(new X509Certificate2(certificate.Path, certificate.Secret));
}
catch
{
builder.AddDeveloperSigningCredential();
}
}
else
{
builder.AddDeveloperSigningCredential();
}
//
return builder;
}
}
}
+18
View File
@@ -0,0 +1,18 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using xCommons.Extensions;
using xExceptions.Constants;
using xModels.Base;
using xIds.Constants;
namespace xIds.Extensions
{
public static class IQueryableExtensions
{
}
}
+29
View File
@@ -0,0 +1,29 @@
using Microsoft.Extensions.DependencyInjection;
using xIds.Interfaces;
using xIds.Providers;
namespace xIds.Extensions
{
public static class IdentityExtensions
{
/// <summary>
/// Register IdentityManager
/// </summary>
/// <param name="source"></param>
public static void AddXIdentityManager(this IServiceCollection source)
{
source.AddScoped<IXIdentityManager, XIdentityManager>();
}
/// <summary>
/// Register IdentityMessage Provider
/// </summary>
/// <param name="source"></param>
public static void AddXIdentityMessageProvider(
this IServiceCollection source
)
{
source.AddScoped<IXIdentityMessageProvider, XIdentityMessageProvider>();
}
}
}
+428
View File
@@ -0,0 +1,428 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using IdentityServer4.EntityFramework.DbContexts;
using IdentityServer4.EntityFramework.Mappers;
using Microsoft.AspNetCore.Builder;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using xCommons.Extensions;
using xIdentityModels.Descriptors;
using xIdentityModels.Extensions;
using xIds.Constants;
using xIds.Interfaces;
using xIds.Models;
namespace xIds.Extensions
{
public static class XDbSeeder
{
/// <summary>
/// Retrieve DbSeedDescriptor from appSetting Configuration
/// </summary>
/// <param name="configuration"></param>
/// <returns></returns>
public static XDbSeedDescriptor GetXDbSeedDescriptor(this IConfiguration configuration)
{
//
var dbSeedSection = configuration.GetSection(ConfigurationNodeNames.DB_SEED_NODE_NAME);
return dbSeedSection.Get<XDbSeedDescriptor>();
}
/// <summary>
/// Register XDbSeedDescriptor as Singleton Service
/// </summary>
/// <param name="services"></param>
/// <param name="configuration"></param>
public static void AddXDebSeederDescriptor(
this IServiceCollection services,
IConfiguration configuration
)
{
//
var dbSeedDescriptor = configuration.GetXDbSeedDescriptor();
if (!dbSeedDescriptor.IsNull())
{
services.AddSingleton<XDbSeedDescriptor>(dbSeedDescriptor);
}
else
{
Console.WriteLine("Error: DbSeeder Configuration not found in AppSetting ...");
}
}
/// <summary>
/// Add Base Required Data to XIdentitySer DbContexts
/// </summary>
/// <param name="app"></param>
/// <param name="logger"></param>
/// <returns></returns>
public static async Task SeedData(
this IApplicationBuilder app,
ILogger logger
)
{
//
logger.LogInformation($"Start Add/Update XIdentityServer Descriptors ...");
//
// Create Scope ...
using (var scope = app.ApplicationServices.CreateScope())
{
//
// Retrieve Required Configurations ...
var dbSeedDescriptor = scope.ServiceProvider.GetService<XDbSeedDescriptor>();
if (dbSeedDescriptor.IsNull())
{
//
Console.WriteLine("DbSeeder is Null ...");
return;
}
logger.LogInformation($"Update Exists Descriptors: {dbSeedDescriptor.UpdateExists.ToString()}");
//
// Retrieve Required Services ...
var identityProvider = scope.ServiceProvider.GetService<IXIdentityManager>();
//
// Retrieve DbContexts ...
var grantDbContext = scope.ServiceProvider.GetService<PersistedGrantDbContext>();
var configDbContext = scope.ServiceProvider.GetService<ConfigurationDbContext>();
//
#region Clients ...
//
logger.LogInformation($"Start Seeding Clients ...");
//
var dClients = dbSeedDescriptor.Clients;
var hasClient = configDbContext.Clients.Any();
if ((!hasClient ||
(hasClient && dbSeedDescriptor.UpdateExists)) &&
dClients.HasChild())
{
//
var clientEntities = dClients.Select(dc => dc.ToEntity());
//
foreach (var client in clientEntities)
{
//
logger.LogInformation($"Add/Update: {client.ClientId}");
//
// Retrieve Exists Client ...
var existsClient = configDbContext.Clients
.Include(c => c.AllowedCorsOrigins)
.Include(c => c.AllowedGrantTypes)
.Include(c => c.AllowedScopes)
.Include(c => c.Claims)
.Include(c => c.ClientSecrets)
.FirstOrDefault(cl => cl.ClientId == client.ClientId);
//
var isExistsClient = !existsClient.IsNull();
var isSameContent = isExistsClient &&
existsClient.IsSameContent(client, propertyBlackList: new[] { nameof(existsClient.Id) });
//
if (isExistsClient)
{
//
if (!isSameContent)
{
//
logger.LogInformation($"Update Client: {existsClient.ClientId}");
//
existsClient = existsClient.UpdateData(client, propertyBlackList: new[] { nameof(existsClient.Id) });
configDbContext.Clients.Update(existsClient);
}
}
else
{
//
logger.LogInformation($"Add Client: {client.ClientId}");
//
await configDbContext.Clients.AddAsync(client);
}
}
//
// Save Changes on Config Db Context ...
await configDbContext.SaveChangesAsync();
}
//
logger.LogInformation($"Finish Seeding Clients ...");
#endregion
//
#region IdentityResources ...
//
logger.LogInformation($"Start Seeding Identity Resources ...");
//
var dIdentityResources = dbSeedDescriptor.IdentityResources;
var hasIdentityResources = configDbContext.IdentityResources.Any();
if ((!hasIdentityResources ||
(hasIdentityResources && dbSeedDescriptor.UpdateExists)) &&
dIdentityResources.HasChild())
{
//
var identityResourceEntities = dIdentityResources.Select(di => di.ToEntity());
//
foreach (var identityResource in identityResourceEntities)
{
//
logger.LogInformation($"Add/Update: {identityResource.DisplayName}");
//
// Retrieve Exists IdentityResource ...
var existsIdentityResource = configDbContext.IdentityResources
.AsNoTracking()
.FirstOrDefault(ir => ir.Name == identityResource.Name);
//
var isExistsResource = !existsIdentityResource.IsNull();
var isSameContent = isExistsResource &&
existsIdentityResource.IsSameContent(identityResource, propertyBlackList: new[] { nameof(existsIdentityResource.Id) });
//
if (isExistsResource &&
dbSeedDescriptor.UpdateExists)
{
//
if (!isSameContent)
{
//
logger.LogInformation($"Update IdentityResource: {identityResource.DisplayName}");
//
existsIdentityResource = existsIdentityResource.UpdateData(identityResource, propertyBlackList: new[] { nameof(existsIdentityResource.Id) });
configDbContext.IdentityResources.Update(existsIdentityResource);
}
}
else
{
//
logger.LogInformation($"Add IdentityResource: {identityResource.DisplayName}");
//
await configDbContext.IdentityResources.AddAsync(identityResource);
}
}
//
// Save Changes on Config Db Context ...
// await configDbContext.SaveChangesAsync();
}
//
logger.LogInformation($"Finish Seeding Identity Resources ...");
#endregion
//
#region Api Resources ...
//
logger.LogInformation($"Start Seeding Api Resources ...");
//
var dApiResources = dbSeedDescriptor.ApiResources;
var hasApiResources = configDbContext.ApiResources.Any();
if ((!hasApiResources ||
(hasApiResources && dbSeedDescriptor.UpdateExists)) &&
dApiResources.HasChild())
{
//
var apiResourceEntities = dApiResources.Select(da => da.ToEntity());
//
foreach (var apiResource in apiResourceEntities)
{
//
logger.LogInformation($"Add/Update: {apiResource.DisplayName}");
//
// Retrieve Exists ApiResource ...
var existsApiResource = configDbContext.ApiResources
.Include(r => r.Secrets)
.Include(r => r.Scopes)
.FirstOrDefault(ar => ar.Name == apiResource.Name);
//
var isExistsResource = !existsApiResource.IsNull();
var isSameContent = isExistsResource &&
existsApiResource.IsSameContent(apiResource, propertyBlackList: new[] { nameof(existsApiResource.Id) });
//
if (isExistsResource &&
dbSeedDescriptor.UpdateExists)
{
//
if (!isSameContent)
{
//
logger.LogInformation($"Update ApiResource: {existsApiResource.DisplayName}");
//
existsApiResource = existsApiResource.UpdateData(apiResource, propertyBlackList: new[] { nameof(existsApiResource.Id) });
configDbContext.ApiResources.Update(existsApiResource);
}
}
else
{
//
logger.LogInformation($"Add ApiResource: {apiResource.DisplayName}");
//
await configDbContext.ApiResources.AddAsync(apiResource);
}
}
//
// Save Changes on Config Db Context ...
// await configDbContext.SaveChangesAsync();
}
//
logger.LogInformation($"Finish Seeding Api Resources ...");
#endregion
//
#region Api Scopes ...
//
logger.LogInformation($"Start Seeding Scopes ...");
//
var dScopes = dbSeedDescriptor.ApiScopes;
var hasScope = configDbContext.ApiScopes.Any();
if ((!hasScope ||
(hasScope && dbSeedDescriptor.UpdateExists)) &&
dScopes.HasChild())
{
//
var apiScopeEntities = dScopes.Select(dc => dc.ToEntity());
//
foreach (var apiScope in apiScopeEntities)
{
//
logger.LogInformation($"Add/Update: {apiScope.DisplayName}");
//
// Retrieve Exists Client ...
var existsScope = configDbContext.ApiScopes
.FirstOrDefault(sc => sc.Name == apiScope.Name);
//
var isExistsScope = !existsScope.IsNull();
var isSameContent = isExistsScope &&
existsScope.IsSameContent(apiScope, propertyBlackList: new[] { nameof(existsScope.Id) });
//
if (isExistsScope &&
dbSeedDescriptor.UpdateExists)
{
//
if (!isSameContent)
{
//
logger.LogInformation($"Update ApiScope: {existsScope.Name}");
//
existsScope = existsScope.UpdateData(apiScope, propertyBlackList: new[] { nameof(existsScope.Id) });
configDbContext.ApiScopes.Update(existsScope);
}
}
else
{
//
logger.LogInformation($"Add ApiScope: {apiScope.Name}");
//
await configDbContext.ApiScopes.AddAsync(apiScope);
}
}
//
// Save Changes on Config Db Context ...
await configDbContext.SaveChangesAsync();
}
//
logger.LogInformation($"Finish Seeding Scopes ...");
#endregion
//
#region Users ...
//
logger.LogInformation($"Start Seeding Users ...");
//
var configUsers = dbSeedDescriptor.Users;
var hasUser = identityProvider.GetUsersDbSet().Any();
if ((!hasUser ||
(hasUser && dbSeedDescriptor.UpdateExists)) &&
configUsers.HasChild())
{
foreach (var user in configUsers)
{
//
logger.LogInformation($"Add/Update User: {user.UserName}");
//
var isUserExists = await identityProvider.IsUserExistsAsync(user.UserName);
if (isUserExists &&
dbSeedDescriptor.UpdateExists)
{
//
var existsUser = await identityProvider.GetUserAsync(user.UserName);
var isSame = existsUser.IsSameAs(user);
if (!isSame)
{
//
// Update Entity ...
existsUser = existsUser.UpdateData(user, propertyWhiteList: new[] {
nameof (XIdentityUserDescriptor.FirstName),
nameof (XIdentityUserDescriptor.LastName),
nameof (XIdentityUserDescriptor.UserName),
nameof (XIdentityUserDescriptor.Email),
nameof (XIdentityUserDescriptor.EmailConfirmed),
nameof (XIdentityUserDescriptor.PhoneNumber),
nameof (XIdentityUserDescriptor.PhoneNumberConfirmed),
nameof (XIdentityUserDescriptor.IsEnable),
nameof (XIdentityUserDescriptor.IsBanned),
nameof (XIdentityUserDescriptor.DateOfBirth)
}, propertyValueProviders: new[] {
new KeyValuePair<string, Func<XIdentityUserDescriptor, object>> (
nameof (XIdentityUserDescriptor.DateOfBirth),
(user) => DateTime.Parse (user.DateOfBirth))
});
//
await identityProvider.UpdateUserAsync(existsUser, false, false);
}
}
else
{
await identityProvider.CreateUserAsync(user);
}
}
}
//
logger.LogInformation($"Finish Seeding Users ...");
#endregion
}
//
logger.LogInformation($"Finish Add/Update XIdentityServer Descriptors ...");
}
}
}
+91
View File
@@ -0,0 +1,91 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using xCommons.Extensions;
using xDataService.Configuration;
using xIdentityModels.Models;
namespace xIds.Extensions
{
public static class XModelExtensions
{
/// <summary>
/// Determine a banned time is passed or not
/// </summary>
/// <param name="source"></param>
/// <param name="delaySeconds"></param>
/// <returns></returns>
public static bool IsDelayTimePassed(
this XBannedDevice source,
int delaySeconds
)
{
//
var passedTime = source.BannedOn.AddSeconds(delaySeconds);
//
var result = DateTime.UtcNow >= passedTime;
//
return result;
}
/// <summary>
/// Applying Filter to IQueryable
/// /// </summary>
/// <typeparam name="T"></typeparam>
public static async Task<IQueryable<T>> ApplyFilterAsync<T>(
this IQueryable<T> source,
string filter
) where T : class
{
//
// Apply Filter ...
if (!filter.IsNullOrEmpty())
{
//
var items = source.AsAsyncEnumerable();
var filteredItems = new List<T>();
await
foreach (var item in items)
{
//
if (item.GetPropValues()
.ToNormalString()
.Contains(filter
.ToNormalString()))
{
//
filteredItems.Add(item);
}
}
//
return filteredItems.AsQueryable();
}
//
return source;
}
public static XDataServiceConfiguration ToXDataServiceConfig(this Configurations.XDataServiceConfiguration config)
{
//
var result = new XDataServiceConfiguration();
//
if (!config.IsNullOrDefault())
{
//
result.PagingConfiguration.DefaultPageSize = config.PagingConfiguration.DefaultPageSize;
result.PagingConfiguration.MaxAvailablePageSize = config.PagingConfiguration.MaxAvailablePageSize;
result.PagingConfiguration.MinAvailablePageSize = config.PagingConfiguration.MinAvailablePageSize;
}
//
return result;
}
}
}
+426
View File
@@ -0,0 +1,426 @@
using System;
using System.IdentityModel.Tokens.Jwt;
using Microsoft.IdentityModel.Tokens;
using xCommons.Extensions;
using xCommons.Providers;
using xExceptions.Constants;
using xIdentityModels.Configurations;
using xIdentityModels.Extensions;
using xIdentityModels.Models;
using xIds.Interfaces;
namespace xIds.Helpers
{
public class XIdentityHelper : IXIdentityHelper
{
private readonly XIdentityConfiguration configurations;
private readonly JwtSecurityTokenHandler tokenHandler;
private readonly XValidationProvider dataValidationHelper;
public XIdentityHelper(
XIdentityConfiguration configurations,
XValidationProvider dataValidationHelper
)
{
//
this.configurations = configurations;
this.tokenHandler = new JwtSecurityTokenHandler();
this.dataValidationHelper = dataValidationHelper;
}
//
#region Configuration Parser ...
/// <summary>
/// Generate a Key for Signing JWT Tokens
/// </summary>
/// <returns></returns>
public SymmetricSecurityKey GetTokenSecretKey()
{
//
dataValidationHelper
.NotEmpty(configurations.IdentitySecretKey);
//
var result = new SymmetricSecurityKey(configurations
.IdentitySecretKey.ToBytes());
//
return result;
}
/// <summary>
/// Generate Security Token Signing Key
/// </summary>
/// <returns></returns>
public SigningCredentials GetTokenSigningCredentials()
{
//
var key = GetTokenSecretKey();
dataValidationHelper
.NotNull(key);
//
var result = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
//
return result;
}
/// <summary>
/// Get Token Validation Parameters
/// </summary>
/// <returns></returns>
public TokenValidationParameters GetTokenValidationParams()
{
//
var key = GetTokenSecretKey();
var issuer = configurations.IdentityIssuer;
var audience = configurations.IdentityAudience;
//
dataValidationHelper
.NotNull(key);
dataValidationHelper
.NotEmpty(issuer, audience);
//
var result = new TokenValidationParameters
{
ValidIssuer = issuer,
ValidAudience = audience,
ValidateLifetime = true,
IssuerSigningKey = key
};
//
return result;
}
/// <summary>
/// Get Account Lockout Time Span after Max Fail Reached
/// </summary>
/// <returns></returns>
public TimeSpan GetLockoutTimeSpan()
{
//
dataValidationHelper
.NotNull(configurations.Policy, configurations.Policy.Lockout);
//
var timeSpanString = configurations.Policy.Lockout.LockoutTimeSpanProvider;
dataValidationHelper
.NotEmpty(timeSpanString);
//
var mExpireValue = 0;
var mExpireValueStr = timeSpanString.GetDigits();
var mExpireUnit = timeSpanString.Replace(mExpireValueStr, "");
dataValidationHelper
.NotEmpty(mExpireUnit);
dataValidationHelper
.NotEmpty(mExpireValueStr);
//
var isParsed = int.TryParse(mExpireValueStr, out mExpireValue);
//
switch (mExpireUnit)
{
case "d":
return TimeSpan.FromDays(mExpireValue);
case "h":
return TimeSpan.FromHours(mExpireValue);
case "m":
return TimeSpan.FromMinutes(mExpireValue);
case "s":
return TimeSpan.FromSeconds(mExpireValue);
case "ms":
default:
return TimeSpan.FromMilliseconds(mExpireValue);
}
}
/// <summary>
/// Generate Security Token Descriptor for Tokenize another
/// </summary>
/// <returns></returns>
public SecurityTokenDescriptor GetActionTokenDescriptor()
{
//
dataValidationHelper
.NotNull(configurations);
//
var issuer = configurations.IdentityIssuer;
var audience = configurations.IdentityAudience;
dataValidationHelper
.NotEmpty(issuer, audience);
//
var expDate = GetActionTokenExpirationDate();
var signingCreds = GetTokenSigningCredentials();
dataValidationHelper
.NotNull(expDate, signingCreds);
//
var descriptor = new SecurityTokenDescriptor
{
Issuer = issuer,
Audience = audience,
Expires = expDate,
SigningCredentials = signingCreds
};
//
return descriptor;
}
/// <summary>
/// Get Action Token Expiration Date
/// </summary>
/// <returns></returns>
public DateTime GetActionTokenExpirationDate()
{
//
dataValidationHelper
.NotNull(configurations);
//
var expDateProvider = configurations
.ActionTokenExpirationDateProvider;
dataValidationHelper
.NotEmpty(expDateProvider);
//
var result = GetExpirationTime(expDateProvider);
dataValidationHelper
.NotNull(result);
//
return result;
}
#endregion
//
#region Action Token Handlers ...
public bool ValidateToken(string token)
{
//
// Validate Args ...
dataValidationHelper
.NotEmpty(token);
dataValidationHelper
.NotNull(tokenHandler);
//
// Prepare Requirements ...
var tokenValidationParams = GetTokenValidationParams();
dataValidationHelper
.NotNull(tokenValidationParams);
//
var result = token
.ValidateToken(tokenHandler, tokenValidationParams);
//
return result;
}
/// <summary>
/// Converts a Token string to SecurityToken instance
/// </summary>
/// <param name="token"></param>
/// <returns></returns>
public SecurityToken ToSecurityToken(string token)
{
//
dataValidationHelper
.NotEmpty(token);
//
// Validate token ...
var isTokenValid = ValidateToken(token);
if (!isTokenValid)
{
XException.InvalidToken.Throw();
}
//
// Prepare and Validate Requirements ...
var tokenValidationParams = GetTokenValidationParams();
dataValidationHelper
.NotNull(tokenHandler, tokenValidationParams);
//
var result = token
.ToSecurityToken(tokenHandler, tokenValidationParams);
dataValidationHelper
.NotNull(result);
//
return result;
}
/// <summary>
/// Convert an ActionRequest to Token Object
/// </summary>
/// <param name="request"></param>
/// <returns></returns>
public SecurityToken ToSecurityToken(XActionRequestToken request)
{
//
var tokenDescriptor = GetActionTokenDescriptor();
//
dataValidationHelper.NotNull(request, tokenHandler, tokenDescriptor);
//
var result = request.ToJwtToken(tokenHandler, tokenDescriptor);
//
return result;
}
/// <summary>
/// Retrieve a Token Exiration Date
/// </summary>
/// <param name="token"></param>
/// <returns></returns>
public DateTime GetTokenExpirationDate(string token)
{
//
var tokenObj = ToSecurityToken(token);
dataValidationHelper.NotNull(tokenObj);
//
var result = tokenObj.ValidTo;
//
return result;
}
public XActionRequestToken ParseActionRequestToken(string token)
{
//
dataValidationHelper
.NotEmpty(token);
//
// Validate token ...
var isTokenValid = ValidateToken(token);
if (!isTokenValid)
{
XException.InvalidToken.Throw();
}
//
// Prepare and Validate Requirements ...
var tokenValidationParams = GetTokenValidationParams();
dataValidationHelper
.NotNull(tokenHandler, tokenValidationParams);
//
var result = token
.ParseActionRequestToken(tokenHandler, tokenValidationParams);
dataValidationHelper
.NotNull(result);
//
return result;
}
/// <summary>
/// Convert a JWT Security Token Object to
/// it's String Representation
/// </summary>
/// <param name="token"></param>
/// <returns></returns>
public string ToTokenString(JwtSecurityToken token)
{
//
dataValidationHelper.NotNull(token);
//
var result = tokenHandler
.WriteToken(token);
//
return result;
}
/// <summary>
/// Convert an Action Token Object to it's String Representation
/// </summary>
/// <param name="token"></param>
/// <returns></returns>
public string ToTokenString(SecurityToken token)
{
//
// Validate Args ...
dataValidationHelper
.NotNull(token, tokenHandler);
//
// Generate and return Result ...
var result = token.ToTokenString(tokenHandler);
dataValidationHelper
.NotEmpty(result);
//
return result;
}
#endregion
//
#region Private Actions ...
/// <summary>
/// Calculating Date Difference Provider String
/// and Generate Date based on UTC Time
/// </summary>
/// <param name="expirationProvider"></param>
/// <returns></returns>
private DateTime GetExpirationTime(string expirationProvider)
{
//
dataValidationHelper
.NotEmpty(expirationProvider);
//
var mExpireValue = 0;
var mExpireValueStr = expirationProvider.GetDigits();
var mExpireUnit = expirationProvider.Replace(mExpireValueStr, "");
dataValidationHelper
.NotEmpty(mExpireUnit);
dataValidationHelper
.NotEmpty(mExpireValueStr);
//
var isParsed = int.TryParse(mExpireValueStr, out mExpireValue);
//
switch (mExpireUnit)
{
case "d":
return DateTime.UtcNow.AddDays(mExpireValue);
case "h":
return DateTime.UtcNow.AddHours(mExpireValue);
case "m":
return DateTime.UtcNow.AddMinutes(mExpireValue);
case "s":
return DateTime.UtcNow.AddSeconds(mExpireValue);
case "ms":
default:
return DateTime.UtcNow.AddMilliseconds(mExpireValue);
}
}
#endregion
}
}
+29
View File
@@ -0,0 +1,29 @@
using System;
using System.IdentityModel.Tokens.Jwt;
using Microsoft.IdentityModel.Tokens;
using xIdentityModels.Models;
namespace xIds.Interfaces
{
public interface IXIdentityHelper
{
//
#region Configuration Parser ...
SymmetricSecurityKey GetTokenSecretKey();
SigningCredentials GetTokenSigningCredentials();
TokenValidationParameters GetTokenValidationParams();
TimeSpan GetLockoutTimeSpan();
#endregion
//
#region Action Token Handlers ...
bool ValidateToken(string token);
SecurityToken ToSecurityToken(string token);
SecurityToken ToSecurityToken(XActionRequestToken request);
string ToTokenString(SecurityToken token);
string ToTokenString(JwtSecurityToken token);
DateTime GetTokenExpirationDate(string token);
XActionRequestToken ParseActionRequestToken(string token);
#endregion
}
}
+506
View File
@@ -0,0 +1,506 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Security.Claims;
using System.Threading.Tasks;
using IdentityModel.Client;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Logging;
using xCommons.Providers;
using xIdentityModels;
using xIdentityModels.Constants;
using xIdentityModels.Descriptors;
using xIdentityModels.Dtos;
using xIdentityModels.Models;
using xIdentityModels.Navigations;
using xIds.DbContext;
using xModels.Dtos;
namespace xIds.Interfaces
{
public interface IXIdentityManager
{
XIdentityDbContext DbContext { get; }
UserManager<XUser> UserManager { get; }
ILogger<IXIdentityManager> Logger { get; }
SignInManager<XUser> SignInManager { get; }
IXSecurityProvider SecurityProvider { get; }
RoleManager<IdentityRole> RoleManager { get; }
XValidationProvider ValidationProvider { get; }
//
#region Class Getter (s) Methods ...
IQueryable<XUser> GetUsersDbSet(
bool containsDetail = false
);
IQueryable<XUser> GetUsersFullDbSet(bool enableTracking = false);
#endregion
//
#region Tools ...
string GetUserSelectByParam(
XActionRequest model,
bool forceNotNull = true,
ICollection<XUserSelectBy> excludes = null
);
Task<XUser> ValidateUserExistsAndRetrieve(
string userSelectByParam,
bool containsDetail = false,
bool checkCanLoginPolicies = false,
bool checkIsBanned = true,
bool ignoreDisabledUser = false,
bool forceAdmin = false,
Exception exception = null
);
#endregion
//
#region Default Identity Preparation Actions ...
Task CreateIdentityRoles();
Task CreateUserAsync(XIdentityUserDescriptor user);
#endregion
//
#region Identity Actions ...
HttpClient GetHttpClient();
Task<IdentityModel.Client.TokenResponse> RequestScopeAccessToken(string scope);
Task<DiscoveryDocumentResponse> RequestDiscoveryDocument();
Task<IdentityModel.Client.TokenResponse> Authenticate(XLoginRequest model);
Task<XLoginResponse> Login(XLoginRequest model);
Task<XTokenResponse> RefreshTokens(XTokenResponse model);
#endregion
//
#region Role Handlers ...
Task<bool> IsRoleExistsAsync(string roleName);
Task<IdentityResult> CreateRoleAsync(string roleName);
Task<IdentityRole> GetRoleAsync(string roleName);
Task<IdentityResult> AddUserToRoleAsync(XUser user, string roleName);
Task<string> GetRoleNameAsync(string roleId);
Task<IEnumerable<string>> GetRoleNamesAsync(
string userSelectByParam,
bool checkCanLoginPolicies = true,
bool checkIsBanned = true
);
Task<IdentityResult> RemoveFromRoleAsync(XUser user, string roleName);
Task<IdentityResult> RemoveFromRolesAsync(XUser user, IEnumerable<string> roleNames);
#endregion
//
#region User Handlers ...
Task<bool> CanRegister(string userSelectByParam);
Task<bool> IsUserExistsAsync(string userSelectByParam);
Task<XUser> GetUserAsync(
string userSelectByParam,
bool containsDetail = false);
Task<IdentityResult> CreateUserAsync(
XUser user,
string password,
bool checkCanLoginPolicies = true,
bool checkIsBanned = true
);
Task<IdentityResult> UpdateUserAsync(
XUser user,
bool checkCanLoginPolicies = true,
bool checkIsBanned = true
);
Task<IEnumerable<string>> GetUserNamesAsync(
IEnumerable<string> userIds
);
Task<IEnumerable<XUserNameIdResponse>> GetUserNameIdsAsync(
XUserNameIdRequest model
);
Task<Claim[]> ToJwtClaims(
XUser user,
bool checkCanLoginPolicies = true,
bool checkIsBanned = true
);
Task<SignInResult> CheckPasswordSignInAsync(
XUser user,
string password,
XDevice device,
string language,
bool lockoutOnFailure,
bool isForced = true
);
#endregion
//
#region User Profile Handlers ...
Task<XUserProfileDto> GetUserProfileAsync(
string userSelectByParam,
string requestedUserSelectByParam,
bool forceCheckRequestedUser = true,
bool checkCanLoginPolicies = true,
bool checkIsBanned = true
);
Task<XQueryResult<XUserProfileDto>> QueryUsers(
string requestedUserSelectByParam,
XQuery query
);
Task<XQueryResult<XUserProfileDto>> QueryInRoleUsers(
string requestedUserSelectByParam,
string role,
XQuery query,
bool forceRole = false
);
Task<IEnumerable<XUserProfileDto>> GetUserProfilesAsync(
ICollection<string> ids,
string requestedUserSelectByParam,
bool forceCheckRequestedUser = true,
bool checkCanLoginPolicies = true,
bool checkIsBanned = true
);
Task<XQueryResult<XProfileImage>> QueryAvatars(
string userSelectByParam,
XQuery query
);
Task<XUserProfileDto> ProfileUpdateAsync(
string userSelectByParam,
string requestedUserSelectByParam,
XProfileUpdateRequest request
);
Task<XUserProfileDto> FullProfileUpdateAsync(
string userSelectByParam,
string requestedUserSelectByParam,
XProfileUpdateRequest request
);
Task<XActionResponse> RequestConfirmMobile(
string lang,
XDevice device,
string userSelectByParam,
string password,
string mobileNumber = null
);
Task<XActionResponse> RequestConfirmEmail(
string lang,
XDevice device,
string userSelectByParam,
string password,
string emailAddress = null
);
Task<XActionResponse> RequestResetPassword(
string lang,
XDevice device,
string userSelectByParam,
string returnUrl
);
Task RequestConfirmRegistration(
string lang,
XDevice device,
string userSelectByParam,
string password,
string returnUrl
);
Task ChangePassword(
string lang,
XDevice device,
string userSelectByParam,
string password,
string newPassword,
string returnUrl
);
Task ResetPassword(
string lang,
XDevice device,
string actionHash,
string newPassword,
string returnUrl
);
Task<XUserProfileDto> AddAvatar(
string userSelectByParam,
IFormFile file
);
Task<XUserProfileDto> AddAvatars(
string userSelectByParam,
IFormFileCollection files
);
Task<XUserProfileDto> SetAvatar(
string userSelectByParam,
int profileImageId
);
Task<XUserProfileDto> RemoveAvatar(
string userSelectByParam,
ICollection<int> profileImageIds
);
Task ConfirmRegistration(
string lang,
XDevice device,
string actionHash,
string returnUrl
);
Task<XActionResponse> ConfirmMobileNumber(
string lang,
XDevice device,
string registrationHash,
string verificationCode
);
Task<XActionResponse> ConfirmEmailAddress(
string lang,
XDevice device,
string registrationHash,
string verificationCode
);
Task<bool> IsConfirmedEmail(string userSelectByParam);
Task<bool> IsConfirmedMobile(string userSelectByParam);
#endregion
//
#region Registration Handlers ...
Task<XActionResponse> InviteUser(
string lang,
XDevice device,
string email,
string handlerUrl
);
Task<XActionResponse> RequestRegistration(
string lang,
XDevice device,
string invitationHash,
string firstName,
string lastName,
DateTime dateOfBirth,
string mobileNumber = null,
string emailAddress = null
);
Task<XActionResponse> AddAcountInfo(
string lang,
XDevice device,
string registrationHash,
string userName,
string password
);
Task<XActionResponse> AttachProfileImage(
string tokenHash,
IFormFile file
);
Task FinishRegistration(
string lang,
XDevice device,
string userSelectByParam,
string password,
string returnUrl
);
#endregion
//
#region Friendship Handlers ...
//
#region Actions ...
Task<XFriendshipFollowing> Follow(
string userSelectByParam,
string destUserSelectByParam
);
Task<bool> Cancel(
string userSelectByParam,
string destUserSelectByParam
);
Task UnFollowFollower(
string userSelectByParam,
string destUserSelectByParam
);
Task UnFollowFollowing(
string userSelectByParam,
string destUserSelectByParam
);
Task<XFriendshipFollowing> Block(
string userSelectByParam,
string destUserSelectByParam
);
Task<XFriendshipFollowing> UnBlock(
string userSelectByParam,
string destUserSelectByParam
);
#endregion
//
#region Request Handlers ...
Task<XFriendshipFollower> AcceptRequest(
string userSelectByParam,
string destUserSelectByParam
);
Task<XFriendshipFollower> RejectRequest(
string userSelectByParam,
string destUserSelectByParam
);
#endregion
//
#region Getters ...
Task<bool> IsFollower(
string userSelectByParam,
string destUserSelectByParam,
bool checkIsBanned = true,
bool checkCanLoginPolicies = true
);
Task<XFriendshipState> GetFollowerState(
string userSelectByParam,
string destUserSelectByParam,
bool checkIsBanned = true,
bool checkCanLoginPolicies = true
);
Task<XFriendshipFollower> GetFollower(
string userSelectByParam,
string destUserSelectByParam
);
Task<IEnumerable<XFriendshipFollower>> GetFollowers(string userSelectByParam);
Task<IEnumerable<XFriendshipFollower>> GetAllFollowers(string userSelectByParam);
Task<ICollection<string>> GetFollowersList(string userSelectByParam);
Task<XQueryResult<XFriendDto>> QueryFollowers(
XQuery query,
string userSelectByParam
);
Task<bool> IsFollowing(
string userSelectByParam,
string destUserSelectByParam,
bool checkIsBanned = true,
bool checkCanLoginPolicies = true
);
Task<XFriendshipState> GetFollowingState(
string userSelectByParam,
string destUserSelectByParam,
bool checkIsBanned = true,
bool checkCanLoginPolicies = true
);
Task<XFriendshipFollowing> GetFollowing(
string userSelectByParam,
string destUserSelectByParam
);
Task<IEnumerable<XFriendshipFollowing>> GetFollowings(string userSelectByParam);
Task<IEnumerable<XFriendshipFollowing>> GetAllFollowings(string userSelectByParam);
Task<ICollection<string>> GetFollowingList(string userSelectByParam);
Task<XQueryResult<XFriendDto>> QueryFollowings(
XQuery query,
string userSelectByParam
);
#endregion
//
Task<XFriendshipInfoDto> GetFriendshipInfo(
string userSelectByParam,
string destUserSelectByParam,
bool checkCanLoginPolicies = true,
bool checkIsBanned = true
);
#endregion
//
#region Admin Actions ...
Task<ICollection<string>> Ban(
string userSelectByParam,
XUserNameIdRequest model
);
Task<ICollection<string>> UnBan(
string userSelectByParam,
XUserNameIdRequest model
);
Task<bool> IsBanned(
string userSelectByParam,
string destUserSelectByParam
);
#endregion
//
#region Open Actions ...
//
#region OpenGet ...
Task<XOpenActionInnerDto> OpenGet(
XOpenActionInnerDto model
);
Task<XOpenActionInnerDto> OpenGet(
string token,
string actionRequest
);
#endregion
Task<XOpenActionUserInfoDto> GetUserInfo(
XOpenActionRequestDto dto
);
Task<IEnumerable<XOpenActionUserInfoDto>> GetUserInfos(
XOpenActionRequestDto dto
);
#endregion
//
#region Search ...
/// <summary>
/// Query Open To Search 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>
Task<XQueryResult<XUserProfileDto>> QueryOpenToSearchUsers(
string requestedUserSelectByParam,
XQuery query
);
#endregion
//
#region Query Service ...
/// <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 Task<XQueryResult<string>> QueryUsers(
string requestedUserSelectByParam,
XQuery query,
string role = null,
bool forceRole = false
);
#endregion
}
}
+16
View File
@@ -0,0 +1,16 @@
namespace xIds.Interfaces
{
public interface IXIdentityMessageProvider
{
string InviteMsg { get; }
string RegistrationApproveMsg { get; }
string RegisteredMsg { get; }
string VerificationCodeMsg { get; }
string ChangePasswordMsg { get; }
string PasswordChangedMsg { get; }
string NewDeviceLoggedInMsg { get; }
bool IsReady();
void PrepareMessages(string lang);
}
}
+10
View File
@@ -0,0 +1,10 @@
namespace xIds.Interfaces
{
public interface IXSecurityProvider
{
string Encrypt(string plainText);
string Decrypt(string cipherText);
string EncryptFromBytes(byte[] textBytes);
string DecryptFromBytes(byte[] cipherBytes);
}
}
+31
View File
@@ -0,0 +1,31 @@
using System;
using xIds.Constants;
namespace xIds.Models
{
public class XDbInfo
{
private string _migrationAssembly;
public XDbProviders ProviderType { get; set; }
public string MigrationsAssembly
{
get
{
return _migrationAssembly;
}
set
{
//
_migrationAssembly = value;
//
OptionsBuilder = b =>
{
b.MigrationsAssembly(value);
};
}
}
public Action<dynamic> OptionsBuilder { get; set; }
}
}
+19
View File
@@ -0,0 +1,19 @@
using System.Collections.Generic;
using IdentityServer4.Models;
using xIdentityModels.Descriptors;
namespace xIds.Models
{
/// <summary>
/// the resource Descriptors for Using on DbSeed
/// </summary>
public class XDbSeedDescriptor
{
public bool UpdateExists { get; set; }
public ICollection<ApiScope> ApiScopes { get; set; }
public ICollection<ApiResource> ApiResources { get; set; }
public ICollection<IdentityResource> IdentityResources { get; set; }
public ICollection<Client> Clients { get; set; }
public ICollection<XIdentityUserDescriptor> Users { get; set; }
}
}
+20
View File
@@ -0,0 +1,20 @@
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Hosting;
namespace xIds
{
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
}
}
+30
View File
@@ -0,0 +1,30 @@
{
"$schema": "http://json.schemastore.org/launchsettings.json",
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:50046",
"sslPort": 44363
}
},
"profiles": {
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"launchUrl": "weatherforecast",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"xIdentityServerApi": {
"commandName": "Project",
"launchBrowser": true,
"launchUrl": "weatherforecast",
"applicationUrl": "https://localhost:4001;http://localhost:4000;https://0.0.0.0:4001;",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}
+187
View File
@@ -0,0 +1,187 @@
using System.Collections.Generic;
using System.Linq;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using xCommons.Extensions;
using xCommons.Providers;
using xExceptions.Constants;
using xIdentityModels;
using xIdentityModels.Configurations;
using xIdentityModels.Constants;
using xIdentityModels.Models;
using xMessageService.Interfaces;
using xStorageService.Interfaces;
using xIds.Configurations;
using xIds.DbContext;
using xIds.Interfaces;
namespace xIds.Providers
{
public partial class XIdentityManager : IXIdentityManager
{
//
private JsonSerializerSettings jsonSerializerSettings;
//
public XIdentityDbContext DbContext { get; }
public UserManager<XUser> UserManager { get; }
public IXIdentityHelper IdentityHelper { get; }
public ILogger<IXIdentityManager> Logger { get; }
public IXMessageProvider MessageProvider { get; }
public IXStorageProvider StorageProvider { get; }
public SignInManager<XUser> SignInManager { get; }
public IXSecurityProvider SecurityProvider { get; }
public XIdentityConfiguration Configuration { get; }
public RoleManager<IdentityRole> RoleManager { get; }
public XValidationProvider ValidationProvider { get; }
public XDataServiceConfiguration DataConfiguration { get; }
public IXIdentityMessageProvider IdentityMessageProvider { get; }
public XIdentityResourceConfiguration IdentityResourceConfiguration { get; }
public XIdentityManager(
XIdentityDbContext dbContext,
UserManager<XUser> userManager,
IXIdentityHelper identityHelper,
ILogger<IXIdentityManager> logger,
IXMessageProvider messageProvider,
IXStorageProvider storageProvider,
SignInManager<XUser> signInManager,
IXSecurityProvider securityProvider,
XIdentityConfiguration configuration,
RoleManager<IdentityRole> roleManager,
XValidationProvider validationProvider,
XDataServiceConfiguration dataConfiguration,
IXIdentityMessageProvider identityMessageProvider,
XIdentityResourceConfiguration identityResourceConfiguration
)
{
//
Logger = logger;
DbContext = dbContext;
RoleManager = roleManager;
UserManager = userManager;
SignInManager = signInManager;
SecurityProvider = securityProvider;
Configuration = configuration;
IdentityHelper = identityHelper;
MessageProvider = messageProvider;
StorageProvider = storageProvider;
DataConfiguration = dataConfiguration;
ValidationProvider = validationProvider;
IdentityMessageProvider = identityMessageProvider;
IdentityResourceConfiguration = identityResourceConfiguration;
//
this.jsonSerializerSettings = new JsonSerializerSettings
{
ContractResolver = new CamelCasePropertyNamesContractResolver()
};
}
//
#region Class Getter (s) Methods ...
/// <summary>
/// Retrieve User DB Set
/// </summary>
/// <param name="containDetails">specifies returned object contains all Navigation Properties or not, default is false</param>
/// <returns></returns>
public IQueryable<XUser> GetUsersDbSet(
bool containDetails = false
)
{
//
var dbSet = containDetails ? GetUsersFullDbSet() :
UserManager.Users;
return dbSet;
}
/// <summary>
/// Retrieve User Db Set Full Navigation Properties
/// </summary>
/// <param name="enableTracking"></param>
/// <returns></returns>
public IQueryable<XUser> GetUsersFullDbSet(bool enableTracking = false)
{
//
var dbSet = UserManager.Users
.Include(u => u.Roles)
.Include(u => u.Devices)
.Include(u => u.Followers)
.Include(u => u.Followings)
.Include(u => u.Avatars);
//
return dbSet.AsSplitQuery();
}
#endregion
//
#region Tools ...
/// <summary>
/// Get User SelectBy Param
/// </summary>
/// <param name="model"></param>
/// <param name="forceNotNull"></param>
/// <param name="excludes"></param>
/// <returns></returns>
public string GetUserSelectByParam(
XActionRequest model,
bool forceNotNull = true,
ICollection<XUserSelectBy> excludes = null
)
{
//
// Validate Args ...
ValidationProvider.NotNull(model);
//
var id = model.UserId;
var userName = model.UserName;
var email = model.Email;
var phoneNumber = model.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 result ...
if (forceNotNull &&
selectByParam.IsNullOrEmpty())
{
XException.InvalidData.Throw();
}
//
return selectByParam;
}
#endregion
}
}
@@ -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
}
}
+95
View File
@@ -0,0 +1,95 @@
using System.Linq;
using Microsoft.Extensions.Logging;
using xCommons.Extensions;
using xExceptions.Constants;
using xIdentityModels.Configurations;
using xIds.Constants;
using xIds.Interfaces;
namespace xIds.Providers
{
public class XIdentityMessageProvider : IXIdentityMessageProvider
{
//
public string InviteMsg { get; set; }
public string RegistrationApproveMsg { get; set; }
public string RegisteredMsg { get; set; }
public string VerificationCodeMsg { get; set; }
public string ChangePasswordMsg { get; set; }
public string PasswordChangedMsg { get; set; }
public string NewDeviceLoggedInMsg { get; set; }
private readonly ILogger<IXIdentityMessageProvider> logger;
private readonly XIdentityConfiguration identityConfiguration;
public XIdentityMessageProvider(
ILogger<IXIdentityMessageProvider> logger,
XIdentityConfiguration identityConfiguration
)
{
this.logger = logger;
this.identityConfiguration = identityConfiguration;
}
/// <summary>
/// Determine All things is Ready or Not
/// </summary>
/// <returns></returns>
public bool IsReady()
{
return !InviteMsg.IsNullOrEmpty() &&
!RegistrationApproveMsg.IsNullOrEmpty() &&
!RegisteredMsg.IsNullOrEmpty() &&
!VerificationCodeMsg.IsNullOrEmpty() &&
!ChangePasswordMsg.IsNullOrEmpty() &&
!PasswordChangedMsg.IsNullOrEmpty() &&
!NewDeviceLoggedInMsg.IsNullOrEmpty();
}
/// <summary>
/// Retrieve a Translated Value Based on Given Lang
/// </summary>
/// <param name="resourceTitle"></param>
/// <param name="lang"></param>
/// <returns></returns>
private string GetStringResource(string resourceTitle, string lang)
{
//
var item = identityConfiguration.IdentityMessages
.FirstOrDefault(sr =>
sr.Language.ToNormalString() == lang.ToNormalString() &&
sr.ResourceTitle.ToNormalString() == resourceTitle.ToNormalString());
//
if (item == null)
{
return null;
}
//
return item.TranslatedValue;
}
/// <summary>
/// Prepare Helper Class and Fill Messages
/// with Specific Language
/// </summary>
/// <param name="lang"></param>
public void PrepareMessages(string lang)
{
//
InviteMsg = GetStringResource(IdentityMessagesKeys.InviteMsg, lang);
RegisteredMsg = GetStringResource(IdentityMessagesKeys.RegisteredMsg, lang);
ChangePasswordMsg = GetStringResource(IdentityMessagesKeys.ChangePasswordMsg, lang);
PasswordChangedMsg = GetStringResource(IdentityMessagesKeys.PasswordChangedMsg, lang);
VerificationCodeMsg = GetStringResource(IdentityMessagesKeys.VerificationCodeMsg, lang);
NewDeviceLoggedInMsg = GetStringResource(IdentityMessagesKeys.NewDeviceLoggedInMsg, lang);
RegistrationApproveMsg = GetStringResource(IdentityMessagesKeys.RegistrationApproveMsg, lang);
//
if (!IsReady())
{
XException.InvalidArgs.Throw();
}
}
}
}
+51
View File
@@ -0,0 +1,51 @@
using System.Linq;
using System.Threading.Tasks;
using IdentityServer4.Extensions;
using IdentityServer4.Models;
using IdentityServer4.Services;
using Microsoft.AspNetCore.Identity;
using xCommons.Extensions;
using xIdentityModels;
using xIds.Interfaces;
namespace xIds.Providers
{
public class XIdentityProfileService : IProfileService
{
private readonly IXIdentityManager identityManager;
private readonly IUserClaimsPrincipalFactory<XUser> userClaimsPrincipalFacotry;
public XIdentityProfileService(
IXIdentityManager identityManager,
IUserClaimsPrincipalFactory<XUser> userClaimsPrincipalFacotry
)
{
this.identityManager = identityManager;
this.userClaimsPrincipalFacotry = userClaimsPrincipalFacotry;
}
public async Task GetProfileDataAsync(ProfileDataRequestContext context)
{
//
var sub = context.Subject.GetSubjectId();
var user = await identityManager.GetUserAsync(sub, true);
var principal = await userClaimsPrincipalFacotry.CreateAsync(user);
//
var claims = principal.Claims.ToList();
//
context.IssuedClaims = claims;
}
public async Task IsActiveAsync(IsActiveContext context)
{
//
var sub = context.Subject.GetSubjectId();
var user = await identityManager.GetUserAsync(sub);
//
context.IsActive = !user.IsNull() && user.IsEnable && !user.IsBanned;
}
}
}
+168
View File
@@ -0,0 +1,168 @@
using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;
using xCommons.Extensions;
using xCommons.Providers;
using xIds.Configurations;
using xIds.Interfaces;
namespace xIds.Providers
{
public class XSecurityProvider : IXSecurityProvider
{
//
#region Props ...
private readonly string secretKey;
private readonly byte[] iv;
private readonly byte[] key;
private readonly XValidationProvider validationProvider;
#endregion
//
#region Constructor ...
public XSecurityProvider(
XIdentityResourceConfiguration identityConfiguration,
XValidationProvider validationProvider
)
{
//
this.secretKey = identityConfiguration.XRevisionSecretKey.ToMd5String();
this.validationProvider = validationProvider;
//
var ivBytes = new byte[16];
var keyBytes = new byte[32];
var secretBytes = Encoding.UTF8.GetBytes(this.secretKey);
//
Array.Copy(
secretBytes,
ivBytes,
secretBytes.Length < ivBytes.Length ?
secretBytes.Length :
ivBytes.Length
);
this.iv = ivBytes;
//
Array.Copy(
secretBytes,
keyBytes,
secretBytes.Length < keyBytes.Length ?
secretBytes.Length :
keyBytes.Length
);
this.key = keyBytes;
// Encoding.UTF8.GetBytes (this.secretKey);
}
#endregion
//
#region Actions ...
public string Encrypt(string plainText)
{
//
validationProvider.NotEmpty(plainText);
//
var textBytes = Encoding.UTF8.GetBytes(plainText);
var result = EncryptFromBytes(textBytes);
//
return result;
}
public string Decrypt(string cipherText)
{
//
validationProvider.NotEmpty(cipherText);
//
var cipherBytes = Convert.FromBase64String(cipherText);
var result = DecryptFromBytes(cipherBytes);
//
return result;
}
public string EncryptFromBytes(byte[] textBytes)
{
//
// Validate Args ...
validationProvider.NotNull(textBytes);
// Declare the string used to hold
// the decrypted text.
byte[] encryptedBytes = null;
// Create an RijndaelManaged object
// with the specified key and IV.
using (var rijAlg = new RijndaelManaged())
{
//Settings
rijAlg.Mode = CipherMode.CBC;
rijAlg.Padding = PaddingMode.PKCS7;
// rijAlg.FeedbackSize = 128;
rijAlg.Key = key;
rijAlg.IV = iv;
//
using (var encryptor = rijAlg.CreateEncryptor(rijAlg.Key, rijAlg.IV))
{
encryptedBytes = encryptor.TransformFinalBlock(textBytes, 0, textBytes.Length);
}
}
//
return Convert.ToBase64String(encryptedBytes);
}
public string DecryptFromBytes(byte[] cipherBytes)
{
//
// Validate Args ...
validationProvider.NotNull(cipherBytes);
// Declare the string used to hold
// the decrypted text.
string plaintext = null;
// Create an RijndaelManaged object
// with the specified key and IV.
using (var rijAlg = new RijndaelManaged())
{
//Settings
rijAlg.Mode = CipherMode.CBC;
rijAlg.Padding = PaddingMode.PKCS7;
// rijAlg.FeedbackSize = 128;
rijAlg.Key = key;
rijAlg.IV = iv;
// Create a decrytor to perform the stream transform.
var decryptor = rijAlg.CreateDecryptor(rijAlg.Key, rijAlg.IV);
// Create the streams used for decryption.
using (var msDecrypt = new MemoryStream(cipherBytes))
{
using (var csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
{
using (var srDecrypt = new StreamReader(csDecrypt))
{
// Read the decrypted bytes from the decrypting stream
// and place them in a string.
plaintext = srDecrypt.ReadToEnd();
}
}
}
}
//
return plaintext;
}
#endregion
}
}
+12
View File
@@ -0,0 +1,12 @@
# xSaherElm.xIds
this project is an Identity Server for xSaherElm projects which provides Authentication and
Authorization of User's.
## Maintainer
Hadi Khazaee asl
[https://www.saherelm.ir](https://www.saherelm.ir)
[hadi_khazaee_asl@yahoo.com](mailto:hadi_khazaee_asl@yahoo.com)
+123
View File
@@ -0,0 +1,123 @@
using System;
using System.IO;
using System.Reflection;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using xCommons.Configurations;
using xCommons.Extensions;
using xStorageService.DI;
using xIds.Constants;
using xIds.DI;
using xIds.Interfaces;
using xIds.Providers;
namespace xIds
{
public class Startup
{
public IConfiguration Configuration { get; }
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public void ConfigureServices(IServiceCollection services)
{
//
// Register Validation Provider and all XCommons Module Services ...
services.AddXCommons();
//
// Register App Configuration ...
services.AddXAppConfiguration(Configuration);
var appConfiguration = services.GetRegisteredService<XAppConfiguration>();
//
// Register XDataService Configuration ...
services.AddXDataServiceConfiguration(Configuration, ConnectionStringNames.IDENTITY_CONNECTION_NAME);
//
// Register Allowed Origins ...
services.AddXCors(appConfiguration.AllowedOrigins);
//
// Register Swagger ...
var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile);
services.AddXSwagger(Configuration, xmlFilePath: xmlPath);
//
// Register IdentityServer to DI ...
// services.AddInMemoryXIdentityServer (Configuration);
services.AddEfSupportXIdentityServer(Configuration);
//
services.AddControllers()
.AddNewtonsoftJson(x => x.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore);
//
// Register Authentication ...
services.AddXIdentityServerAuthentication(Configuration);
//
// Register Authorization ...
services.AddXAuthorization();
//
// Register Security Provider ...
services.AddSingleton<IXSecurityProvider, XSecurityProvider>();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILogger<Startup> logger)
{
//
if (env.IsDevelopment())
{
//
app.UseDeveloperExceptionPage();
//
// app.UseXRequestLogger();
}
//
// Use Swagger Middleware ...
app.UseXSwagger();
//
app.UseHttpsRedirection();
app.UseXForwardOptions();
//
// Using Cors ...
app.UseXCors();
//
// Force App to Use IdentityServer ...
app.UseXIdentityServer(logger);
//
// Force app to Use Storage Service ...
app.UseXStorageService();
//
app.UseRouting();
//
app.UseAuthentication();
app.UseAuthorization();
//
app.UseEndpoints(endpoints =>
{
endpoints.MapDefaultControllerRoute();
});
}
}
}
+151
View File
@@ -0,0 +1,151 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using IdentityServer4.EntityFramework.DbContexts;
using IdentityServer4.EntityFramework.Mappers;
using IdentityServer4.Models;
using IdentityServer4.Stores;
using Microsoft.EntityFrameworkCore;
namespace xIds.Stores
{
public class XPersistedGrantStore : IPersistedGrantStore
{
private readonly PersistedGrantDbContext dbContext;
public XPersistedGrantStore(PersistedGrantDbContext dbContext)
{
this.dbContext = dbContext;
}
public async Task<IEnumerable<PersistedGrant>> GetAllAsync(string subjectId)
{
//
var result = (await dbContext.PersistedGrants
.Where(g => g.SubjectId == subjectId)
.AsNoTracking()
.Select(res => res.ToModel())
.ToListAsync()).AsEnumerable();
//
return result;
}
public async Task<PersistedGrant> GetAsync(string key)
{
//
var result = await dbContext.PersistedGrants
.FirstOrDefaultAsync(g => g.Key == key);
//
return result.ToModel();
}
public async Task RemoveAllAsync(string subjectId, string clientId)
{
//
var items = await dbContext.PersistedGrants
.Where(g =>
g.SubjectId == subjectId &&
g.ClientId == clientId)
.ToListAsync();
//
dbContext.PersistedGrants.RemoveRange(items);
//
await dbContext.SaveChangesAsync();
}
public async Task RemoveAllAsync(string subjectId, string clientId, string type)
{
//
var items = await dbContext.PersistedGrants
.Where(g =>
g.SubjectId == subjectId &&
g.ClientId == clientId &&
g.Type == type)
.ToListAsync();
//
dbContext.PersistedGrants.RemoveRange(items);
//
await dbContext.SaveChangesAsync();
}
public async Task RemoveAsync(string key)
{
//
var items = await dbContext.PersistedGrants
.Where(g => g.Key == key)
.ToListAsync();
//
dbContext.PersistedGrants.RemoveRange(items);
//
await dbContext.SaveChangesAsync();
}
public async Task<bool> IsExistsAsync(string key)
{
return await this.dbContext.PersistedGrants.AnyAsync(g => g.Key == key);
}
public async Task<bool> IsExistsAsync(PersistedGrant grant)
{
return await IsExistsAsync(grant.Key);
}
public async Task AddOrUpdateAsync(PersistedGrant grant)
{
//
var isExists = await IsExistsAsync(grant);
if (isExists)
{
await UpdateAsync(grant);
}
else
{
await AddAsync(grant);
}
}
public async Task AddAsync(PersistedGrant grant)
{
//
var item = grant.ToEntity();
await dbContext.PersistedGrants.AddAsync(item);
await dbContext.SaveChangesAsync();
}
public async Task UpdateAsync(PersistedGrant grant)
{
//
var item = grant.ToEntity();
var entity = dbContext.Attach(item);
entity.State = EntityState.Modified;
await dbContext.SaveChangesAsync();
}
public async Task StoreAsync(PersistedGrant grant)
{
await AddOrUpdateAsync(grant);
}
//
// TODO: Complete this ...
public Task<IEnumerable<PersistedGrant>> GetAllAsync(PersistedGrantFilter filter)
{
throw new System.NotImplementedException();
}
//
// TODO: Complete this ...
public Task RemoveAllAsync(PersistedGrantFilter filter)
{
throw new System.NotImplementedException();
}
}
}
@@ -0,0 +1,193 @@
using System;
using System.Threading.Tasks;
using IdentityServer4.Events;
using IdentityServer4.Models;
using IdentityServer4.Services;
using IdentityServer4.Validation;
using Microsoft.Extensions.Logging;
using xCommons.Extensions;
using xExceptions.Constants;
using xIdentityModels.Navigations;
using xIds.Interfaces;
using static IdentityModel.OidcConstants;
namespace xIds.Validators
{
public class XResourceOwnerPasswordValidator : IResourceOwnerPasswordValidator
{
private readonly IXIdentityManager identityManager;
private readonly IEventService events;
private readonly ILogger<XResourceOwnerPasswordValidator> logger;
public XResourceOwnerPasswordValidator(
IXIdentityManager identityProvider,
IEventService events,
ILogger<XResourceOwnerPasswordValidator> logger
)
{
this.identityManager = identityProvider;
this.events = events;
this.logger = logger;
}
/// <summary>
/// Validates the resource owner password credential
/// by providing UserName/Email or PhoneNumber
/// </summary>
/// <param name="context">The context.</param>
/// <returns></returns>
public virtual async Task ValidateAsync(ResourceOwnerPasswordValidationContext context)
{
//
var clientId = context.Request?.Client?.ClientId;
var xUser = await identityManager.GetUserAsync(context.UserName, true);
//
// Check Force Data ...
var isForced = true;
var isForcedStr = context.Request?.Raw["force"];
if (!isForcedStr.IsNullOrEmpty())
{
isForced = isForcedStr.FromJSON<bool>();
}
//
XDevice device = null;
var language = context.Request?.Raw["language"];
var deviceStr = context.Request?.Raw["device"];
if (deviceStr.IsNullOrEmpty() && isForced)
{
//
logger.LogError($"Device not Found ...");
//
await events
.RaiseAsync(
new UserLoginFailureEvent(
xUser.UserName,
XException.InvalidDevice.ToXError().Message,
false,
clientId
));
return;
}
device = isForced ? deviceStr.FromJSON<XDevice>() : null;
//
XException exception;
if (!xUser.IsNull())
{
//
var result = await identityManager
.CheckPasswordSignInAsync(
xUser,
context.Password,
device,
language,
true,
isForced
);
if (result.Succeeded)
{
//
logger.LogInformation("Credentials validated for username: {username}", xUser.UserName);
//
// Update User Last Login ...
xUser.LastLogin = DateTime.UtcNow;
await identityManager.UpdateUserAsync(xUser);
//
await events.RaiseAsync(
new UserLoginSuccessEvent(
xUser.UserName, xUser.Id, xUser.UserName, false, clientId
));
//
context.Result = new GrantValidationResult(xUser.Id, AuthenticationMethods.Password);
return;
}
else if (result.IsLockedOut)
{
//
logger.LogInformation("Authentication failed for username: {username}, reason: locked out", xUser.UserName);
//
exception = XException.AccountLockedOut;
await events
.RaiseAsync(
new UserLoginFailureEvent(
xUser.UserName,
exception.ToXError().Message,
false,
clientId
));
}
else if (result.IsNotAllowed)
{
//
logger.LogInformation("Authentication failed for username: {username}, reason: not allowed", xUser.UserName);
//
exception = XException.NotAllowed;
await events
.RaiseAsync(
new UserLoginFailureEvent(
xUser.UserName,
exception.ToXError().Message,
false,
clientId
));
}
else
{
//
logger.LogInformation("Authentication failed for username: {username}, reason: invalid credentials", xUser.UserName);
//
exception = XException.LoginFailed;
await events
.RaiseAsync(
new UserLoginFailureEvent(
xUser.UserName,
exception.ToXError().Message,
false,
clientId
));
//
}
}
else
{
//
logger.LogInformation("No user found matching username: {username}", xUser.UserName);
//
exception = XException.InvalidUserName;
await events
.RaiseAsync(
new UserLoginFailureEvent(
xUser.UserName,
exception.ToXError().Message,
false,
clientId
));
}
//
Exception ecs = exception.ToException();
if (exception == XException.AccountLockedOut && xUser.LockoutEnd.HasValue)
{
//
var passedTime = xUser.LockoutEnd.Value.UtcDateTime;
ecs = XException.AccountLockedOut
.AddContentToException(passedTime.ToString());
}
//
context.Result = new GrantValidationResult(TokenRequestErrors.InvalidGrant, ecs.Message);
}
}
}
+177
View File
@@ -0,0 +1,177 @@
{
"AllowedOrigins": [
"https://localhost:5001",
"https://localhost:6001",
"https://saherelm.ir",
"https://api.saherelm.ir",
"https://saherelmhub.ir",
"https://api.saherelmhub.ir",
"https://192.168.1.15:5001",
"https://192.168.1.110:5001"
],
"IdentityResourceConfiguration": {
"Authority": "https://localhost:4001",
"ApiName": "xSaherElmAPI",
"ApiSecret": "s@H@1694056",
"ClientId": "xSaherElmAPIClient",
"ClientSecret": "s@H@1694056",
"XPoweredValue": "SaherElmITCenter",
"XRevisionSecretKey": "SaherElmITCenter@1694056"
},
"StorageConfiguration": {
"Authority": "https://localhost:4001",
"IdentityAuthority": "https://localhost:4001",
"Storage": "Storage",
"Temp": "Temp",
"Thumb": "Thumb",
"Uploads": "Uploads",
"Widgets": "Widgets",
"Image": "Image",
"Audio": "Audio",
"Video": "Video",
"Document": "Document",
"FilePrefix": "xSaherElm_",
"ThumbPrefix": "Thumb_",
"ThumbSize": 512,
"ThumbQuality": 72,
"MaxFileSize": 41943040
},
"DbSeeder": {
"UpdateExists": false,
"Clients": [
{
"ClientId": "xSaherElmAPIClient",
"ClientName": "Web Client of xSaherElm API",
"ClientSecrets": [
{
"Description": null,
"Value": "NKaAKXeE9Ws+VVGGHsmM9DUO84cla4+hvx8swP1uuEg=",
"Expiration": null,
"Type": "SharedSecret"
}
],
"Enabled": true,
"RedirectUris": [
"https://localhost:5001/signin-oidc"
],
"AllowedGrantTypes": [
"hybrid",
"password",
"client_credentials"
],
"AllowedScopes": [
"IdentityServerApi",
"openid",
"profile",
"XAPI.read",
"XAPI.write",
"XAPI.admin",
"manage",
"offline_access"
],
"AllowedCorsOrigins": [
"https://localhost:5001",
"https://localhost:6001",
"http://saherelm.ir",
"https://saherelm.ir",
"http://saherelmhub.ir",
"https://saherelmhub.ir"
],
"AllowOfflineAccess": true,
"RefreshTokenUsage": 1,
"AccessTokenType": 1,
"RefreshTokenExpiration": 0,
"AbsoluteRefreshTokenLifetime": 2592000,
"SlidingRefreshTokenLifetime": 1296000,
"UpdateAccessTokenClaimsOnRefresh": true,
"IdentityTokenLifetime": 300,
"AccessTokenLifetime": 3600,
"AuthorizationCodeLifetime": 300,
"DeviceCodeLifetime": 300
}
],
"IdentityResources": [
"openid",
"profile",
"email",
"role"
],
"ApiResources": [
{
"Name": "IdentityServerApi"
},
{
"Name": "xSaherElmAPI",
"DisplayName": "xSaherElm API",
"Description": "Protected by IdentityServer API Access",
"ApiSecrets": [
{
"Description": null,
"Value": "NKaAKXeE9Ws+VVGGHsmM9DUO84cla4+hvx8swP1uuEg=",
"Expiration": null,
"Type": "SharedSecret"
}
],
"Enabled": true,
"Scopes": [
"openid",
"profile",
"XAPI.read",
"XAPI.write",
"XAPI.admin",
"manage"
],
"UserClaims": [
"name",
"role",
"unique_name",
"gender",
"birthdate",
"picture",
"email",
"phone_number",
"email_verified",
"phone_number_verified",
"is_enabled",
"is_banned",
"creation_date",
"last_login",
"given_name",
"family_name"
]
}
],
"ApiScopes": [
{
"Name": "IdentityServerApi"
},
{
"Name": "xSaherElmAPI"
},
{
"Name": "XAPI.read",
"DisplayName": "Read Access for XAPI"
},
{
"Name": "XAPI.write",
"DisplayName": "Write Access for XAPI"
},
{
"Name": "XAPI.admin",
"DisplayName": "Admin Access for XAPI"
},
{
"Name": "manage",
"DisplayName": "Admin Access for All APIs"
}
]
},
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"AllowedHosts": "*"
}
+397
View File
@@ -0,0 +1,397 @@
{
"Version": "0.1",
"XPoweredValue": "SaherElmITCenter",
"Name": "xIdentityServer",
"WelcomeMessage": "Welcome to xSaherElm Project's xIdentityServer",
"AllowedOrigins": [
"https://saherelm.ir",
"https://saherelmhub.ir",
"https://api.saherelm.ir",
"https://api.saherelmhub.ir"
],
"SwaggerConfiguration": {
"Version": "v1.0",
"Title": "xSaherElm Identity Server API",
"Description": "Complete API Documentation",
"Contact": {
"Name": "Hadi Khazaee Asl",
"Email": "hadi_khazaee_asl@yahoo.com",
"Url": "https://www.saherelm.ir"
}
},
"DbProvider": "SQLite",
"ConnectionStrings": {
"IdentityDb": "Filename=./Db/XIdentity.db;Foreign Keys=False",
"ConfigurationDb": "Filename=./Db/XConfigurations.db",
"PersistedGrantDb": "Filename=./Db/XPersistedGrants.db"
},
"DataServiceConfiguration": {
"PagingConfiguration": {
"DefaultPageSize": 20,
"MaxAvailablePageSize": 400,
"MinAvailablePageSize": 20
}
},
"Certificates": {
"IdentityServerSigning": {
"Path": "Certificate/xIds.pfx",
"Secret": "s@1694056"
}
},
"IdentityConfiguration": {
"IdentityIssuer": "SaherElm Identity Server",
"IdentityAudience": "SaherElmIdentityAPI",
"IdentitySecretKey": "SaherElmITCenter@1694056",
"ActionTokenExpirationDateProvider": "20m",
"IdentityRoles": [
"admin",
"responsible_manager",
"journalist",
"chief_clerk",
"chief_clerk_assistant",
"actuary",
"actuary_assistant",
"reporter",
"reporter_assistant",
"agent",
"user"
],
"AutoConfirmNewUsersEmail": false,
"AutoConfirmNewUsersPhoneNumber": false,
"RegistrationJustWithInvite": false,
"RequireRegistrationConfirm": true,
"MaxNumberOfVerificationCodeSend": 3,
"DeleyBetweenTwoVerificationCode": 180,
"NewUsersRole": "user",
"Policy": {
"Lockout": {
"LockoutTimeSpanProvider": "15m",
"MaxFailedAccessAttempts": 5,
"AllowedForNewUsers": true
},
"Password": {
"RequireDigit": true,
"RequiredLength": 6,
"RequiredUniqueChars": 1,
"RequireLowercase": true,
"RequireNonAlphanumeric": true,
"RequireUppercase": true
},
"SignIn": {
"RequiredEnabled": true,
"RequireConfirmedEmail": true,
"RequireConfirmedPhoneNumber": false
},
"User": {
"AllowedUserNameCharacters": "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._",
"RequireUniqueEmail": true,
"MinLength": 6,
"MaxLength": 20,
"MinAgeForRegistration": 18,
"MaxAgeForRegistration": 60,
"InvalidUserNames": [
"saherelm",
"admin",
"responsible_manager",
"journalist",
"chief_clerk",
"chief_clerk_assistant",
"actuary",
"actuary_assistant",
"reporter",
"reporter_assistant",
"agent",
"user"
]
},
"Profile": {
"ContainsEmail": false,
"ContainsPhoneNumber": false,
"ContainsRoles": true,
"ContainsLastLogin": false,
"ContainsDateOfBirth": false,
"ContainsCreationDate": true
}
},
"IdentityMessages": [
{
"Language": "fa-IR",
"ResourceTitle": "user_invite_msg",
"TranslatedValue": "شما دعوت شده اید تا در سامانه ثبت نام کنید"
},
{
"Language": "en-US",
"ResourceTitle": "user_invite_msg",
"TranslatedValue": "You are Invited to Register at Dashboard"
},
{
"Language": "fa-IR",
"ResourceTitle": "registration_approve_msg",
"TranslatedValue": "شما باید حساب کاربری خود را با ورود به پیوند زیر فعال کنید"
},
{
"Language": "en-US",
"ResourceTitle": "registration_approve_msg",
"TranslatedValue": "you must activate your account by going to this link"
},
{
"Language": "fa-IR",
"ResourceTitle": "registered_msg",
"TranslatedValue": "ثبت نام شما در سامانه با موفقیت پایان یافت"
},
{
"Language": "en-US",
"ResourceTitle": "registered_msg",
"TranslatedValue": "you successfully registered at Dashboard"
},
{
"Language": "fa-IR",
"ResourceTitle": "verification_code_msg",
"TranslatedValue": "کد اعتبار سنجی شما در سامانه"
},
{
"Language": "en-US",
"ResourceTitle": "verification_code_msg",
"TranslatedValue": "Your Verification Code at Dashboard"
},
{
"Language": "fa-IR",
"ResourceTitle": "change_password_msg",
"TranslatedValue": "برای تغییر کلمه عبور پیوند زیر را دنبال کنید"
},
{
"Language": "en-US",
"ResourceTitle": "change_password_msg",
"TranslatedValue": "for change password please follow this link"
},
{
"Language": "fa-IR",
"ResourceTitle": "password_changed_msg",
"TranslatedValue": "کلمه عبور شما در سامانه تغییر یافت"
},
{
"Language": "en-US",
"ResourceTitle": "password_changed_msg",
"TranslatedValue": "your password at Dashboard changed"
},
{
"Language": "fa-IR",
"ResourceTitle": "new_device_logged_in_msg",
"TranslatedValue": "ابزار جدیدی با مشخصات ذیل در سامانه با حساب کاربری شما وارد شده است"
},
{
"Language": "en-US",
"ResourceTitle": "new_device_logged_in_msg",
"TranslatedValue": "new device with following details loggedin with your account in Dashboard"
}
]
},
"IdentityResourceConfiguration": {
"Authority": "https://172.18.0.151",
"ApiName": "xSaherElmAPI",
"ApiSecret": "s@H@1694056",
"ClientId": "xSaherElmAPIClient",
"ClientSecret": "s@H@1694056",
"XPoweredValue": "SaherElmITCenter",
"XRevisionSecretKey": "SaherElmITCenter@1694056"
},
"StorageConfiguration": {
"Authority": "https://ids.saherelm.ir",
"IdentityAuthority": "https://ids.saherelm.ir",
"Storage": "Storage",
"Temp": "Temp",
"Thumb": "Thumb",
"Uploads": "Uploads",
"Widgets": "Widgets",
"Image": "Image",
"Audio": "Audio",
"Video": "Video",
"Document": "Document",
"FilePrefix": "xSaherElm_",
"ThumbPrefix": "Thumb_",
"ThumbSize": 512,
"ThumbQuality": 72,
"MaxFileSize": 41943040
},
"MessageConfiguration": {
"MailConfigurations": {
"gmail": {
"Title": "SaherElm Dashboard",
"Host": "smtp.gmail.com",
"Port": 587,
"EnableSsl": true,
"Username": "saherelm@gmail.com",
"Password": "vzuz gtrk tndb ctbc"
}
},
"SmsConfigurations": {
"yoursms": {
"ServiceUrl": "http://sms.yourprovider.ir",
"Username": "user",
"Password": "psw",
"LineNumber": "linenumber",
"UserApiKey": "",
"SecretKey": ""
}
}
},
"DbSeeder": {
"UpdateExists": false,
"Clients": [
{
"ClientId": "xSaherElmAPIClient",
"ClientName": "Web Client of xMabsut API",
"ClientSecrets": [
{
"Description": null,
"Value": "NKaAKXeE9Ws+VVGGHsmM9DUO84cla4+hvx8swP1uuEg=",
"Expiration": null,
"Type": "SharedSecret"
}
],
"Enabled": true,
"RedirectUris": ["https://localhost:5001/signin-oidc"],
"AllowedGrantTypes": [
"hybrid",
"password",
"client_credentials"
],
"AllowedScopes": [
"IdentityServerApi",
"openid",
"profile",
"XAPI.read",
"XAPI.write",
"XAPI.admin",
"manage",
"offline_access"
],
"AllowedCorsOrigins": [
"http://saherelm.ir",
"https://saherelm.ir",
"http://saherelmhub.ir",
"https://saherelmhub.ir"
],
"AllowOfflineAccess": true,
"RefreshTokenUsage": 1,
"AccessTokenType": 1,
"RefreshTokenExpiration": 0,
"AbsoluteRefreshTokenLifetime": 2592000,
"SlidingRefreshTokenLifetime": 1296000,
"UpdateAccessTokenClaimsOnRefresh": true,
"IdentityTokenLifetime": 300,
"AccessTokenLifetime": 3600,
"AuthorizationCodeLifetime": 300,
"DeviceCodeLifetime": 300
}
],
"IdentityResources": ["openid", "profile", "email", "role"],
"ApiResources": [
{
"Name": "IdentityServerApi"
},
{
"Name": "xSaherElmAPI",
"DisplayName": "xSaherElm API",
"Description": "Protected by IdentityServer API Access",
"ApiSecrets": [
{
"Description": null,
"Value": "NKaAKXeE9Ws+VVGGHsmM9DUO84cla4+hvx8swP1uuEg=",
"Expiration": null,
"Type": "SharedSecret"
}
],
"Enabled": true,
"Scopes": [
"openid",
"profile",
"XAPI.read",
"XAPI.write",
"XAPI.admin",
"manage"
],
"UserClaims": [
"name",
"role",
"unique_name",
"gender",
"birthdate",
"picture",
"email",
"phone_number",
"email_verified",
"phone_number_verified",
"is_enabled",
"is_banned",
"creation_date",
"last_login",
"given_name",
"family_name"
]
}
],
"ApiScopes": [
{
"Name": "IdentityServerApi"
},
{
"Name": "xSaherElmAPI"
},
{
"Name": "XAPI.read",
"DisplayName": "Read Access for XAPI"
},
{
"Name": "XAPI.write",
"DisplayName": "Write Access for XAPI"
},
{
"Name": "XAPI.admin",
"DisplayName": "Admin Access for XAPI"
},
{
"Name": "manage",
"DisplayName": "Admin Access for All APIs"
}
],
"Users": [
{
"Id": "b5713f9f-34f4-49b2-9680-b71e2b2de969",
"FirstName": "Hadi",
"LastName": "Khazaee Asl",
"UserName": "admin",
"Email": "hadi_khazaee_asl@yahoo.com",
"EmailConfirmed": true,
"PhoneNumber": "+989121694056",
"PhoneNumberConfirmed": true,
"Password": "s@H@1694056",
"Role": "admin",
"DateOfBirth": "1982-09-17 20:30:00.0000000",
"IsEnable": true
},
{
"Id": "83b09f5e-edd2-4e9a-8c5f-c4c129ee9d52",
"FirstName": "Hadi",
"LastName": "Khazaee Asl",
"UserName": "agent",
"Email": "hadi.khazaee.asl@gmail.com",
"EmailConfirmed": true,
"PhoneNumber": "+989101879789",
"PhoneNumberConfirmed": true,
"Password": "a@A@1694056",
"Role": "agent",
"DateOfBirth": "1982-09-17 20:30:00.0000000",
"IsEnable": true
}
]
},
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"AllowedHosts": "*"
}
+8
View File
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<packageSources>
<add key="nuget" value="https://api.nuget.org/v3/index.json" protocolVersion="3" />
<add key="baget" value="https://nuget.saherelmhub.ir/v3/index.json" protocolVersion="3" />
<add key="nugetIran" value="https://repo.nugetiran.ir/repository/nuget-group/v3/index.json" protocolVersion="3" />
</packageSources>
</configuration>
+1
View File
@@ -0,0 +1 @@
{"alg":"RS256","d":"ArwBivGf5QZR7tala9B6BmHJOVbPp_MQCGa1h9bXRhzZjpSC2Ppq3pFMLvu089GXvbgjgR5o_ORyP9OIKtvHkNKtwMV_z_Om3C864pAr4i1RdRtc5pwYNhFEZkEY8bZ6ne4KzTcqSZGw9rXjSWTna95bifHwM9m_UGtcHwljJnd-SXJjxRUWFTkCut1u1ACmhvuQeEQrHrJbL5pVBli80FinQzqG4CNGpk9pjiBPRfenp1ctzBSggIsX_lmCLHkvXPxl9Yy4i01DdOLCJUtYmf49TjP-MWUTz9IccrMJoX2iuh9iHaCafx5qq7k8L42DFR1tQS4hp3sKPQ3kXFqfkQ","dp":"TP1dStEZx2EeZF5Sz3PZJEqxFHe5Xa3zmhiGCe2Jm2wRZ04XOxzd2xw_cUoftAxemeMpRXq7KMarrD4WMFswX1yLK3LVO9WpdvEBBR7f-SIpVbKYdhP3P1zZqwudih6hbjaPd03G4CesbuActwtirc0323nxUXuZbTvkLztbYyc","dq":"sEXhyXCs-mDNNM_qKRCh7zIztQfc8LEFYXckZSk_1mnDA3WNQRqXXdXbLw0JkBBTHlALZoue8lfTyMfjqFkpSrl06qMKVU3SkF69omvemnuNeCKMFqItEkcynRuqf3vYsurxM6bXf57WdgDc708ZftWdXl9oo0baFh74yIlgsBk","e":"AQAB","kid":"38954A2B1CBC6B8ADFC020D8A81E7947","kty":"RSA","n":"rKTQ4bS9E8h_i7v-okGQPhqJ5V9xovOqCoqWlBGJ2osETHi05itedHMdK99t4Q8usI5cDkiI9AW1F59R-hFeXEyYxhrHoiYuU2HtstG-e5xFp2qLlbO_bGNCeLlEROnigWVMRYp7fsL3trb_VGTTTeFFn6-iuPVvhFvhBcxBEuisLtxmsywT58ucxdQa-2CDnA4UmGp8zI0BZPYGAdEXoCqlobtURdiSW8X1rxk96dHTy3sDzwofd-SWlJHcxz4DP6lCHTfxk3cxBY0KYTJbOhyzASiV8wJ0oMFQBeSB_5U4r0tOBIYZ_d_KwE_LQ-oxw-XN3Uv_qsy9w_0cy_FqEw","p":"1qnzbvQ2EdPz_gD4pVvf5NtoxqqDKxblhhWLYYFUQTEYmLvsoGh62oYv1F1e_CJm3DgwoDBzkrcAITdBmmYwqxcr7K3J_IZIN0R-tfLrqoE4L8pOwF3lvqGo7w5kzpaI3ofrgEfGjvypq698r3AqnqQ8saKKHgEvqJQ5aM1OyEc","q":"zeNyx4l6aBr4rhcw3lUmOTtxpcVMlo1WH5McjaUMFsh7Nd33nf9dB9gJlddu79SBwBXMVASWZ_bBzFCclb5viuvgvQgr1nbJq8hm3I52j9ZH1H4Nh05DYW2aiDo3HFf3FqMng3_mesu26cJrjBhir5Pxhx3OgBfuihCoOZs_gdU","qi":"i1RttjN0gRVILTzVdBZdBieXh5GYDiDcSvTy-rf4pg-6JBs5p7DyGFr9Fu5k5HZmzdqBecHTov-pZFC5PdDIUAU2DXaSWK0-UFhbNBxDgm8CcT_pW3_DjCve162Zb8QcZRLcg1jzovlQVwzw897ixJOlhm4JRDNKts_IHL6R4Do"}
+73
View File
@@ -0,0 +1,73 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<!-- Runtime Definitions -->
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
<PackageId>xSaherElm.xIds</PackageId>
<Version>1.0.0</Version>
<Authors>Hadi Khazaee Asl</Authors>
<Company>SaherElm IT Center</Company>
<Description>
this project is an Identity Server for xSaherElm projects which provides Authentication and
Authorization of User's.
</Description>
<!-- Fix Duplicate TargetFramework Issue -->
<GenerateTargetFrameworkAttribute>false</GenerateTargetFrameworkAttribute>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
</PropertyGroup>
<!-- Local Dependencies -->
<ItemGroup>
<PackageReference Include="xDashboard.xMessageService" Version="1.0.0" />
<PackageReference Include="xDashboard.xStorageService" Version="1.0.0" />
</ItemGroup>
<!-- Project Dependencies -->
<ItemGroup>
<ProjectReference Include="..\Modules\xIdentityHelper\xIdentityHelper.csproj" />
</ItemGroup>
<!-- IdentityServer -->
<ItemGroup>
<PackageReference Include="IdentityServer4" Version="4.1.1" />
<PackageReference Include="Microsoft.AspNetCore.Identity" Version="2.2.0" />
<PackageReference Include="IdentityServer4.AspNetIdentity" Version="4.1.1" />
<PackageReference Include="IdentityServer4.EntityFramework" Version="4.1.1" />
<PackageReference Include="Microsoft.AspNetCore.Authentication" Version="2.2.0" />
<PackageReference Include="IdentityServer4.AccessTokenValidation" Version="3.0.1" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="3.1.11" />
<PackageReference Include="IdentityModel.AspNetCore.OAuth2Introspection" Version="5.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="3.1.12" />
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="5.0.2" />
<PackageReference Include="IdentityModel.AspNetCore.AccessTokenValidation"
Version="1.0.0-preview.3" />
</ItemGroup>
<!-- Ef Core -->
<ItemGroup>
<PackageReference Include="MySql.EntityFrameworkCore" Version="5.0.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="5.0.2" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="5.0.2" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="5.0.2" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="5.0.2">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="5.0.2">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
</ItemGroup>
<!-- For XML Documentation Support -->
<PropertyGroup>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<NoWarn>$(NoWarn);1591</NoWarn>
</PropertyGroup>
<!-- Create Required Folders -->
<Target Name="CreateRequiredFolder" AfterTargets="AfterPublish">
<MakeDir Directories="$(PublishDir)wwwroot" Condition="!Exists('$(PublishDir)wwwroot')" />
</Target>
</Project>