This commit is contained in:
hamid
2026-06-16 01:32:43 +03:30
commit 69bbd28bb0
298 changed files with 24728 additions and 0 deletions
@@ -0,0 +1,20 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" />
<PackageReference Include="Serilog.Sinks.Console" />
<PackageReference Include="Serilog.Sinks.MSSqlServer" />
<PackageReference Include="Serilog.Sinks.PeriodicBatching" />
<PackageReference Include="Serilog.Exceptions" />
<PackageReference Include="Serilog.Sinks.Elasticsearch" />
<PackageReference Include="Serilog.Enrichers.Span" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\CleanArc.Infrastructure.Identity\CleanArc.Infrastructure.Identity.csproj" />
<ProjectReference Include="..\CleanArc.Infrastructure.Persistence\CleanArc.Infrastructure.Persistence.csproj" />
</ItemGroup>
</Project>
@@ -0,0 +1,73 @@
using System.Data;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
using Serilog;
using Serilog.Enrichers.Span;
using Serilog.Exceptions;
using Serilog.Formatting.Json;
using Serilog.Sinks.MSSqlServer;
namespace CleanArc.Infrastructure.CrossCutting.Logging;
public static class LoggingConfiguration
{
public static Action<HostBuilderContext, LoggerConfiguration> ConfigureLogger => (context, configuration) =>
{
#region Enriching Logger Context
var env = context.HostingEnvironment;
configuration.Enrich.FromLogContext()
.Enrich.WithProperty("ApplicationName", env.ApplicationName)
.Enrich.WithProperty("Environment", env.EnvironmentName)
.Enrich.WithSpan()
.Enrich.WithExceptionDetails();
#endregion
var columnOpts = new ColumnOptions();
columnOpts.Store.Remove(StandardColumn.Properties);
columnOpts.Store.Add(StandardColumn.LogEvent);
columnOpts.LogEvent.DataLength = 4096;
columnOpts.PrimaryKey = columnOpts.Id;
columnOpts.Id.DataType = SqlDbType.Int;
if (!context.HostingEnvironment.IsDevelopment())
{
configuration.WriteTo
.MSSqlServer(
connectionString: context.Configuration.GetConnectionString("logDb"),
sinkOptions: new MSSqlServerSinkOptions { TableName = "LogEvents", AutoCreateSqlTable = true, SchemaName = "log",AutoCreateSqlDatabase = true})
.MinimumLevel.Warning();
}
else{
configuration.WriteTo.Console().MinimumLevel.Information();
configuration.WriteTo.File(new JsonFormatter(), "logs/log.json").MinimumLevel.Information();
}
#region ElasticSearch Configuration. UnComment if Needed
//var elasticUrl = context.Configuration.GetValue<string>("Logging:ElasticUrl");
//if (!string.IsNullOrEmpty(elasticUrl))
//{
// configuration.WriteTo.Elasticsearch(
// new ElasticsearchSinkOptions(new Uri(elasticUrl))
// {
// AutoRegisterTemplate = true,
// AutoRegisterTemplateVersion = AutoRegisterTemplateVersion.ESv7,
// IndexFormat = "web-logs-{0:yyyy.MM.dd}",
// MinimumLogEventLevel = LogEventLevel.Debug
// });
//}
#endregion
};
}
@@ -0,0 +1,22 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Authorization" />
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" />
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" />
<PackageReference Include="Microsoft.Extensions.Identity.Core" />
<PackageReference Include="Microsoft.Extensions.Identity.Stores" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\Core\CleanArc.Application\CleanArc.Application.csproj" />
<ProjectReference Include="..\..\Core\CleanArc.Domain\CleanArc.Domain.csproj" />
<ProjectReference Include="..\CleanArc.Infrastructure.Persistence\CleanArc.Infrastructure.Persistence.csproj" />
</ItemGroup>
</Project>
@@ -0,0 +1,80 @@
using Microsoft.AspNetCore.Identity;
namespace CleanArc.Infrastructure.Identity.Identity;
public class AppErrorDescriber:IdentityErrorDescriber
{
public override IdentityError DefaultError()
{
return new IdentityError
{
Code = "DefaultError",
Description = "There was an error"
};
}
public override IdentityError DuplicateEmail(string email)
{
return new IdentityError
{
Code = nameof(DuplicateEmail),
Description = "Specified email already exists"
};
}
public override IdentityError DuplicateUserName(string userName)
{
return new IdentityError
{
Code = nameof(DuplicateUserName),
Description = "specified username already exists"
};
}
public override IdentityError PasswordMismatch()
{
return new IdentityError
{
Code = nameof(PasswordMismatch),
Description = "Incorrect password"
};
}
public override IdentityError PasswordTooShort(int length)
{
return new IdentityError
{
Code = nameof(PasswordTooShort),
Description = "Invalid password. Password is to short"
};
}
public override IdentityError InvalidUserName(string userName)
{
return new IdentityError
{
Code = nameof(InvalidUserName),
Description = "Invalid username"
};
}
public override IdentityError InvalidEmail(string email)
{
return new IdentityError
{
Code = nameof(InvalidEmail),
Description = "Invalid email"
};
}
public override IdentityError InvalidToken()
{
return new IdentityError
{
Code = nameof(InvalidToken),
Description = "Invalid given code. Please try again"
};
}
}
@@ -0,0 +1,34 @@
using System.Security.Claims;
using CleanArc.Domain.Entities.User;
using CleanArc.Infrastructure.Identity.Identity.Manager;
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Options;
namespace CleanArc.Infrastructure.Identity.Identity;
public class AppUserClaimsPrincipleFactory:UserClaimsPrincipalFactory<User,Role>
{
public AppUserClaimsPrincipleFactory(AppUserManager userManager, AppRoleManager roleManager, IOptions<IdentityOptions> options) : base(userManager, roleManager, options)
{
}
protected override async Task<ClaimsIdentity> GenerateClaimsAsync(User user)
{
var userRoles = await UserManager.GetRolesAsync(user);
var claimsIdentity = await base.GenerateClaimsAsync(user);
//claimsIdentity.AddClaim(new Claim(ClaimTypes.Email,user?.Email));
// claimsIdentity.AddClaim(new Claim(ClaimTypes.MobilePhone,user.PhoneNumber));
claimsIdentity.AddClaim(new Claim(ClaimTypes.UserData,user.GeneratedCode));
foreach (var roles in userRoles)
{
claimsIdentity.AddClaim(new Claim(ClaimTypes.Role,roles));
}
//claimsIdentity.AddClaim(new Claim(ClaimTypes.Role,RoleManager.GetRoleNameAsync(user.Roles)));
return claimsIdentity;
}
}
@@ -0,0 +1,78 @@
using System.Security.Cryptography;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Identity;
namespace CleanArc.Infrastructure.Identity.Identity.DataProtection;
public class KeyRing : ILookupProtectorKeyRing
{
private readonly IDictionary<string, string> _keyDictionary = new Dictionary<string, string>();
public KeyRing(IWebHostEnvironment hostingEnvironment)
{
// Create the keyring directory if one doesn't exist.
var keyRingDirectory = Path.Combine(hostingEnvironment.ContentRootPath, "keyring");
Directory.CreateDirectory(keyRingDirectory);
var directoryInfo = new DirectoryInfo(keyRingDirectory);
if (directoryInfo.GetFiles("*.key").Length == 0)
{
ProtectorAlgorithmHelper.GetAlgorithms(
ProtectorAlgorithmHelper.DefaultAlgorithm,
out SymmetricAlgorithm encryptionAlgorithm,
out KeyedHashAlgorithm signingAlgorithm,
out int derivationCount);
encryptionAlgorithm.GenerateKey();
var keyAsString = Convert.ToBase64String(encryptionAlgorithm.Key);
var keyId = Guid.NewGuid().ToString();
var keyFileName = Path.Combine(keyRingDirectory, keyId+".key");
using (var file = File.CreateText(keyFileName))
{
file.WriteLine(keyAsString);
}
_keyDictionary.Add(keyId, keyAsString);
CurrentKeyId = keyId;
encryptionAlgorithm.Clear();
encryptionAlgorithm.Dispose();
signingAlgorithm.Dispose();
}
else
{
var filesOrdered = directoryInfo.EnumerateFiles()
.OrderByDescending(d => d.CreationTime)
.Select(d => d.Name)
.ToList();
foreach (var fileName in filesOrdered)
{
var keyFileName = Path.Combine(keyRingDirectory, fileName);
var key = File.ReadAllText(keyFileName);
var keyId = Path.GetFileNameWithoutExtension(fileName);
_keyDictionary.Add(keyId, key);
CurrentKeyId = keyId;
}
}
}
public string this[string keyId]
{
get
{
return _keyDictionary[keyId];
}
}
public string CurrentKeyId
{
get; private set;
}
public IEnumerable<string> GetAllKeyIds()
{
return _keyDictionary.Keys;
}
}
@@ -0,0 +1,236 @@
using System.Runtime.CompilerServices;
using System.Security.Cryptography;
using System.Text;
using Microsoft.AspNetCore.Cryptography.KeyDerivation;
using Microsoft.AspNetCore.Identity;
namespace CleanArc.Infrastructure.Identity.Identity.DataProtection;
public class LookupProtector : ILookupProtector
{
private readonly ProtectorAlgorithm _defaultAlgorithm = ProtectorAlgorithm.Aes256Hmac512;
private readonly ILookupProtectorKeyRing _keyRing;
public LookupProtector(ILookupProtectorKeyRing keyRing)
{
_keyRing = keyRing;
}
public string Protect(string keyId, string data)
{
// Get the default algorithms.
// We does this so we can embed the algorithm details used in the cipher text so we can
// change algorithms as yet another collision appears in a hashing algorithm.
// See https://media.blackhat.com/bh-us-10/whitepapers/Sullivan/BlackHat-USA-2010-Sullivan-Cryptographic-Agility-wp.pdf
ProtectorAlgorithmHelper.GetAlgorithms(
_defaultAlgorithm,
out SymmetricAlgorithm encryptingAlgorithm,
out KeyedHashAlgorithm signingAlgorithm,
out int keyDerivationIterationCount);
var masterKey = MasterKey(keyId);
// Convert the string to bytes, because encryption works on bytes, not strings.
var plainText = Encoding.UTF8.GetBytes(data);
byte[] cipherTextAndIV;
// Derive a key for encryption from the master key
encryptingAlgorithm.Key = DerivedEncryptionKey(
masterKey,
encryptingAlgorithm,
keyDerivationIterationCount);
// As we need this to be deterministic, we need to force an IV that is derived from the plain text.
encryptingAlgorithm.IV = DerivedInitializationVector(
masterKey,
data,
encryptingAlgorithm,
keyDerivationIterationCount);
// And encrypt
using (var ms = new MemoryStream())
using (var cs = new CryptoStream(
ms,
encryptingAlgorithm.CreateEncryptor(),
CryptoStreamMode.Write))
{
cs.Write(plainText);
cs.FlushFinalBlock();
var encryptedData = ms.ToArray();
cipherTextAndIV = CombineByteArrays(encryptingAlgorithm.IV, encryptedData);
}
// Now get a signature for the data so we can detect tampering in situ.
byte[] signature = SignData(
cipherTextAndIV,
masterKey,
encryptingAlgorithm,
signingAlgorithm,
keyDerivationIterationCount);
// Add the signature to the cipher text.
var signedData = CombineByteArrays(signature, cipherTextAndIV);
// Add our algorithm identifier to the combined signature and cipher text.
var algorithmIdentifier = BitConverter.GetBytes((int)_defaultAlgorithm);
byte[] output = CombineByteArrays(algorithmIdentifier, signedData);
// Clean everything up.
encryptingAlgorithm.Clear();
signingAlgorithm.Clear();
encryptingAlgorithm.Dispose();
signingAlgorithm.Dispose();
Array.Clear(plainText, 0, plainText.Length);
// Return the results as a string.
return Convert.ToBase64String(output);
}
public string Unprotect(string keyId, string data)
{
var masterKey = MasterKey(keyId);
byte[] plainText;
// Take our string and convert it back to bytes.
var payload = Convert.FromBase64String(data);
// Read the saved algorithm details and create instances of those algorithms.
byte[] algorithmIdentifierAsBytes = new byte[4];
Buffer.BlockCopy(payload, 0, algorithmIdentifierAsBytes, 0, 4);
var algorithmIdentifier = (ProtectorAlgorithm)(BitConverter.ToInt32(algorithmIdentifierAsBytes, 0));
ProtectorAlgorithmHelper.GetAlgorithms(
_defaultAlgorithm,
out SymmetricAlgorithm encryptingAlgorithm,
out KeyedHashAlgorithm signingAlgorithm,
out int keyDerivationIterationCount);
// Now extract the signature
byte[] signature = new byte[signingAlgorithm.HashSize / 8];
Buffer.BlockCopy(payload, 4, signature, 0, signingAlgorithm.HashSize / 8);
// And finally grab the rest of the data
var dataLength = payload.Length - 4 - signature.Length;
byte[] cipherTextAndIV = new byte[dataLength];
Buffer.BlockCopy(payload, 4 + signature.Length, cipherTextAndIV, 0, dataLength);
// Check the signature before anything else is done to detect tampering and avoid
// oracles.
byte[] computedSignature = SignData(
cipherTextAndIV,
masterKey,
encryptingAlgorithm,
signingAlgorithm,
keyDerivationIterationCount);
if (!ByteArraysEqual(computedSignature, signature))
{
throw new CryptographicException(@"Invalid Signature.");
}
signingAlgorithm.Clear();
signingAlgorithm.Dispose();
// The signature is valid, so now we can work on decrypting the data.
var ivLength = encryptingAlgorithm.BlockSize / 8;
byte[] initializationVector = new byte[ivLength];
byte[] cipherText = new byte[cipherTextAndIV.Length - ivLength];
// The IV is embedded in the cipher text, so we extract it out.
Buffer.BlockCopy(cipherTextAndIV, 0, initializationVector, 0, ivLength);
// Then we get the encrypted data.
Buffer.BlockCopy(cipherTextAndIV, ivLength, cipherText, 0, cipherTextAndIV.Length - ivLength);
encryptingAlgorithm.Key = DerivedEncryptionKey(
masterKey,
encryptingAlgorithm,
keyDerivationIterationCount);
encryptingAlgorithm.IV = initializationVector;
// Decrypt
using (var ms = new MemoryStream())
using (var cs = new CryptoStream(ms, encryptingAlgorithm.CreateDecryptor(), CryptoStreamMode.Write))
{
cs.Write(cipherText);
cs.FlushFinalBlock();
plainText = ms.ToArray();
}
encryptingAlgorithm.Clear();
encryptingAlgorithm.Dispose();
// And convert from the bytes back to a string.
return Encoding.UTF8.GetString(plainText);
}
public byte[] MasterKey(string keyId)
{
return Convert.FromBase64String(_keyRing[keyId]);
}
private byte[] SignData(byte[] cipherText, byte[] masterKey, SymmetricAlgorithm symmetricAlgorithm, KeyedHashAlgorithm hashAlgorithm, int keyDerivationIterationCount)
{
hashAlgorithm.Key = DerivedSigningKey(masterKey, symmetricAlgorithm, keyDerivationIterationCount);
byte[] signature = hashAlgorithm.ComputeHash(cipherText);
hashAlgorithm.Clear();
return signature;
}
private byte[] DerivedSigningKey(byte[] key, SymmetricAlgorithm algorithm, int keyDerivationIterationCount)
{
return KeyDerivation.Pbkdf2(
@"IdentityLookupDataSigning",
key,
KeyDerivationPrf.HMACSHA512,
keyDerivationIterationCount,
algorithm.KeySize / 8);
}
private byte[] DerivedEncryptionKey(byte[] key, SymmetricAlgorithm algorithm, int keyDerivationIterationCount)
{
return KeyDerivation.Pbkdf2(
@"IdentityLookupEncryption",
key,
KeyDerivationPrf.HMACSHA512,
keyDerivationIterationCount,
algorithm.KeySize / 8);
}
private byte[] DerivedInitializationVector(byte[] key, string plainText, SymmetricAlgorithm algorithm, int keyDerivationIterationCount)
{
return KeyDerivation.Pbkdf2(
plainText,
key,
KeyDerivationPrf.HMACSHA512,
keyDerivationIterationCount,
algorithm.BlockSize / 8);
}
private byte[] CombineByteArrays(byte[] left, byte[] right)
{
byte[] output = new byte[left.Length + right.Length];
Buffer.BlockCopy(left, 0, output, 0, left.Length);
Buffer.BlockCopy(right, 0, output, left.Length, right.Length);
return output;
}
[MethodImpl(MethodImplOptions.NoOptimization)]
private static bool ByteArraysEqual(byte[] a, byte[] b)
{
if (ReferenceEquals(a, b))
{
return true;
}
if (a == null || b == null || a.Length != b.Length)
{
return false;
}
bool areSame = true;
for (int i = 0; i < a.Length; i++)
{
areSame &= (a[i] == b[i]);
}
return areSame;
}
}
@@ -0,0 +1,241 @@
using System.Runtime.CompilerServices;
using System.Security.Cryptography;
using System.Text;
using Microsoft.AspNetCore.Cryptography.KeyDerivation;
using Microsoft.AspNetCore.Identity;
namespace CleanArc.Infrastructure.Identity.Identity.DataProtection;
public class PersonalDataProtector : IPersonalDataProtector
{
private readonly ProtectorAlgorithm _defaultAlgorithm = ProtectorAlgorithm.Aes256Hmac512;
private readonly ILookupProtectorKeyRing _keyRing;
public PersonalDataProtector(ILookupProtectorKeyRing keyRing)
{
_keyRing = keyRing;
}
public string Protect(string data)
{
// Get the default algorithms.
// We does this so we can embed the algorithm details used in the cipher text so we can
// change algorithms as yet another collision appears in a hashing algorithm.
// See https://media.blackhat.com/bh-us-10/whitepapers/Sullivan/BlackHat-USA-2010-Sullivan-Cryptographic-Agility-wp.pdf
ProtectorAlgorithmHelper.GetAlgorithms(
_defaultAlgorithm,
out SymmetricAlgorithm encryptingAlgorithm,
out KeyedHashAlgorithm signingAlgorithm,
out int keyDerivationIterationCount);
// Use the newest key from the keyring.
// We know this is a guid, because that's how our keyring works underneath,
// so we can prepend this later to the result.
string keyId = _keyRing.CurrentKeyId;
var masterKey = Key(keyId);
// Convert the string to bytes, because encryption works on bytes, not strings.
var plainText = Encoding.UTF8.GetBytes(data);
byte[] cipherTextAndIV;
// Derive a key for encryption from the master key
encryptingAlgorithm.Key = DerivedEncryptionKey(
masterKey,
encryptingAlgorithm,
keyDerivationIterationCount);
// When the underlying encryption class is created it has a random IV by default
// So we don't need to do anything IV wise.
// And encrypt
using (var ms = new MemoryStream())
using (var cs = new CryptoStream(
ms,
encryptingAlgorithm.CreateEncryptor(),
CryptoStreamMode.Write))
{
cs.Write(plainText);
cs.FlushFinalBlock();
var encryptedData = ms.ToArray();
cipherTextAndIV = CombineByteArrays(encryptingAlgorithm.IV, encryptedData);
}
// Now get a signature for the data so we can detect tampering in situ.
byte[] signature = SignData(
cipherTextAndIV,
masterKey,
encryptingAlgorithm,
signingAlgorithm,
keyDerivationIterationCount);
// Add the signature to the cipher text.
var signedData = CombineByteArrays(signature, cipherTextAndIV);
// Add our algorithm identifier to the combined signature and cipher text.
var algorithmIdentifier = BitConverter.GetBytes((int)_defaultAlgorithm);
byte[] dataPlusAlgorithmId = CombineByteArrays(algorithmIdentifier, signedData);
// Now we need to put our key identifier in. In our implementation this is a GUID
// so let's convert it back to one, then turn it to bytes.
byte[] output = CombineByteArrays(Guid.Parse(keyId).ToByteArray(), dataPlusAlgorithmId);
// Clean everything up.
encryptingAlgorithm.Clear();
signingAlgorithm.Clear();
encryptingAlgorithm.Dispose();
signingAlgorithm.Dispose();
Array.Clear(plainText, 0, plainText.Length);
// Return the results as a string.
return Convert.ToBase64String(output);
}
public string Unprotect(string data)
{
byte[] plainText;
// Take our string and convert it back to bytes.
var payload = Convert.FromBase64String(data);
var offset = 0;
// First we extract our key ID and then the appropriate key.
byte[] keyIdAsBytes = new byte[16];
Buffer.BlockCopy(payload, offset, keyIdAsBytes, 0, 16);
var keyIdAsGuid = new Guid(keyIdAsBytes);
var keyId = keyIdAsGuid.ToString();
var masterKey = Key(keyId);
offset = 16;
// Next read the saved algorithm details and create instances of those algorithms.
byte[] algorithmIdentifierAsBytes = new byte[4];
Buffer.BlockCopy(payload, offset, algorithmIdentifierAsBytes, 0, 4);
var algorithmIdentifier = (ProtectorAlgorithm)(BitConverter.ToInt32(algorithmIdentifierAsBytes, 0));
ProtectorAlgorithmHelper.GetAlgorithms(
_defaultAlgorithm,
out SymmetricAlgorithm encryptingAlgorithm,
out KeyedHashAlgorithm signingAlgorithm,
out int keyDerivationIterationCount);
offset += 4;
// Now extract the signature
byte[] signature = new byte[signingAlgorithm.HashSize / 8];
Buffer.BlockCopy(payload, offset, signature, 0, signingAlgorithm.HashSize / 8);
offset += signature.Length;
// And finally grab the rest of the data
var dataLength = payload.Length - offset;
byte[] cipherTextAndIV = new byte[dataLength];
Buffer.BlockCopy(payload, offset, cipherTextAndIV, 0, dataLength);
// Check the signature before anything else is done to detect tampering and avoid
// oracles.
byte[] computedSignature = SignData(
cipherTextAndIV,
masterKey,
encryptingAlgorithm,
signingAlgorithm,
keyDerivationIterationCount);
if (!ByteArraysEqual(computedSignature, signature))
{
throw new CryptographicException(@"Invalid Signature.");
}
signingAlgorithm.Clear();
signingAlgorithm.Dispose();
// The signature is valid, so now we can work on decrypting the data.
var ivLength = encryptingAlgorithm.BlockSize / 8;
byte[] initializationVector = new byte[ivLength];
byte[] cipherText = new byte[cipherTextAndIV.Length - ivLength];
// The IV is embedded in the cipher text, so we extract it out.
Buffer.BlockCopy(cipherTextAndIV, 0, initializationVector, 0, ivLength);
// Then we get the encrypted data.
Buffer.BlockCopy(cipherTextAndIV, ivLength, cipherText, 0, cipherTextAndIV.Length - ivLength);
encryptingAlgorithm.Key = DerivedEncryptionKey(
masterKey,
encryptingAlgorithm,
keyDerivationIterationCount);
encryptingAlgorithm.IV = initializationVector;
// Decrypt
using (var ms = new MemoryStream())
using (var cs = new CryptoStream(ms, encryptingAlgorithm.CreateDecryptor(), CryptoStreamMode.Write))
{
cs.Write(cipherText);
cs.FlushFinalBlock();
plainText = ms.ToArray();
}
encryptingAlgorithm.Clear();
encryptingAlgorithm.Dispose();
// And convert from the bytes back to a string.
return Encoding.UTF8.GetString(plainText);
}
public byte[] Key(string keyId)
{
return Convert.FromBase64String(_keyRing[keyId]);
}
private byte[] SignData(byte[] cipherText, byte[] masterKey, SymmetricAlgorithm symmetricAlgorithm, KeyedHashAlgorithm hashAlgorithm, int keyDerivationIterationCount)
{
hashAlgorithm.Key = DerivedSigningKey(masterKey, symmetricAlgorithm, keyDerivationIterationCount);
byte[] signature = hashAlgorithm.ComputeHash(cipherText);
hashAlgorithm.Clear();
return signature;
}
private byte[] DerivedSigningKey(byte[] key, SymmetricAlgorithm algorithm, int keyDerivationIterationCount)
{
return KeyDerivation.Pbkdf2(
@"PersonalDataSigning",
key,
KeyDerivationPrf.HMACSHA512,
keyDerivationIterationCount,
algorithm.KeySize / 8);
}
private byte[] DerivedEncryptionKey(byte[] key, SymmetricAlgorithm algorithm, int keyDerivationIterationCount)
{
return KeyDerivation.Pbkdf2(
@"PersonalDataEncryption",
key,
KeyDerivationPrf.HMACSHA512,
keyDerivationIterationCount,
algorithm.KeySize / 8);
}
private byte[] CombineByteArrays(byte[] left, byte[] right)
{
byte[] output = new byte[left.Length + right.Length];
Buffer.BlockCopy(left, 0, output, 0, left.Length);
Buffer.BlockCopy(right, 0, output, left.Length, right.Length);
return output;
}
[MethodImpl(MethodImplOptions.NoOptimization)]
private static bool ByteArraysEqual(byte[] a, byte[] b)
{
if (ReferenceEquals(a, b))
{
return true;
}
if (a == null || b == null || a.Length != b.Length)
{
return false;
}
bool areSame = true;
for (int i = 0; i < a.Length; i++)
{
areSame &= (a[i] == b[i]);
}
return areSame;
}
}
@@ -0,0 +1,35 @@
using System.Security.Cryptography;
namespace CleanArc.Infrastructure.Identity.Identity.DataProtection;
public enum ProtectorAlgorithm
{
Aes256Hmac512 = 1
}
public static class ProtectorAlgorithmHelper
{
public static ProtectorAlgorithm DefaultAlgorithm
{
get { return ProtectorAlgorithm.Aes256Hmac512; }
}
public static void GetAlgorithms(
ProtectorAlgorithm algorithmId,
out SymmetricAlgorithm encryptionAlgorithm,
out KeyedHashAlgorithm signingAlgorithm,
out int keyDerivationIterationCount)
{
switch (algorithmId)
{
case ProtectorAlgorithm.Aes256Hmac512:
encryptionAlgorithm = Aes.Create();
encryptionAlgorithm.KeySize = 256;
signingAlgorithm = new HMACSHA512();
keyDerivationIterationCount = 10000;
break;
default:
throw new ArgumentOutOfRangeException(nameof(algorithmId));
}
}
}
@@ -0,0 +1,8 @@
namespace CleanArc.Infrastructure.Identity.Identity.Dtos
{
internal class CustomIdentityConstants
{
public const string OtpPasswordLessLoginProvider = "PasswordlessLoginTotpProvider";
public const string OtpPasswordLessLoginPurpose = "passwordless-auth";
}
}
@@ -0,0 +1,11 @@
namespace CleanArc.Infrastructure.Identity.Identity.Dtos;
public class IdentitySettings
{
public string SecretKey { get; set; }
public string Encryptkey { get; set; }
public string Issuer { get; set; }
public string Audience { get; set; }
public int NotBeforeMinutes { get; set; }
public int ExpirationMinutes { get; set; }
}
@@ -0,0 +1,55 @@
using Microsoft.AspNetCore.DataProtection;
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
namespace CleanArc.Infrastructure.Identity.Identity.Extensions;
public class DataProtectionTokenProviderOptions
{
public string Name { get; set; } = "DataProtectorTokenProvider";
public TimeSpan TokenLifespan { get; set; }
}
public static class CustomIdentityExtensions
{
public static IdentityBuilder AddPasswordlessLoginTotpTokenProvider(this IdentityBuilder builder)
{
var userType = builder.UserType;
var totpProvider = typeof(PasswordlessLoginTotpTokenProvider<>).MakeGenericType(userType);
return builder.AddTokenProvider("PasswordlessLoginTotpProvider", totpProvider);
}
}
public class PasswordlessLoginTotpTokenProvider<TUser> : TotpSecurityStampBasedTokenProvider<TUser>
where TUser : class
{
public override Task<bool> CanGenerateTwoFactorTokenAsync(UserManager<TUser> manager, TUser user)
{
return Task.FromResult(false);
}
public override async Task<string> GetUserModifierAsync(string purpose, UserManager<TUser> manager, TUser user)
{
var phone = await manager.GetPhoneNumberAsync(user);
return "PasswordlessLogin:" + purpose + ":" + phone;
}
}
public class PasswordlessLoginTokenProviderOptions : DataProtectionTokenProviderOptions
{
public PasswordlessLoginTokenProviderOptions()
{
// update the defaults
Name = "PasswordlessLoginTokenProvider";
TokenLifespan = TimeSpan.FromMinutes(1);
}
}
public class PasswordlessLoginTokenProvider<TUser> : DataProtectorTokenProvider<TUser>
where TUser : class
{
public PasswordlessLoginTokenProvider(IDataProtectionProvider dataProtectionProvider, IOptions<Microsoft.AspNetCore.Identity.DataProtectionTokenProviderOptions> options, ILogger<DataProtectorTokenProvider<TUser>> logger) : base(dataProtectionProvider, options,logger)
{
}
}
@@ -0,0 +1,12 @@
using CleanArc.Domain.Entities.User;
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Logging;
namespace CleanArc.Infrastructure.Identity.Identity.Manager;
public class AppRoleManager:RoleManager<Role>
{
public AppRoleManager(IRoleStore<Role> store, IEnumerable<IRoleValidator<Role>> roleValidators, ILookupNormalizer keyNormalizer, IdentityErrorDescriber errors, ILogger<RoleManager<Role>> logger) : base(store, roleValidators, keyNormalizer, errors, logger)
{
}
}
@@ -0,0 +1,15 @@
using CleanArc.Domain.Entities.User;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
namespace CleanArc.Infrastructure.Identity.Identity.Manager;
public class AppSignInManager : SignInManager<User>
{
public AppSignInManager(UserManager<User> userManager, IHttpContextAccessor contextAccessor, IUserClaimsPrincipalFactory<User> claimsFactory, IOptions<IdentityOptions> optionsAccessor, ILogger<SignInManager<User>> logger, IAuthenticationSchemeProvider schemes, IUserConfirmation<User> confirmation) : base(userManager, contextAccessor, claimsFactory, optionsAccessor, logger, schemes, confirmation)
{
}
}
@@ -0,0 +1,13 @@
using CleanArc.Domain.Entities.User;
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
namespace CleanArc.Infrastructure.Identity.Identity.Manager;
public class AppUserManager:UserManager<User>
{
public AppUserManager(IUserStore<User> store, IOptions<IdentityOptions> optionsAccessor, IPasswordHasher<User> passwordHasher, IEnumerable<IUserValidator<User>> userValidators, IEnumerable<IPasswordValidator<User>> passwordValidators, ILookupNormalizer keyNormalizer, IdentityErrorDescriber errors, IServiceProvider services, ILogger<UserManager<User>> logger) : base(store, optionsAccessor, passwordHasher, userValidators, passwordValidators, keyNormalizer, errors, services, logger)
{
}
}
@@ -0,0 +1,6 @@
namespace CleanArc.Infrastructure.Identity.Identity.PermissionManager;
public static class ConstantPolicies
{
public const string DynamicPermission = nameof(DynamicPermission);
}
@@ -0,0 +1,53 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Routing;
namespace CleanArc.Infrastructure.Identity.Identity.PermissionManager;
public class DynamicPermissionRequirement : IAuthorizationRequirement
{
}
public class DynamicPermissionHandler : AuthorizationHandler<DynamicPermissionRequirement>
{
private readonly IDynamicPermissionService _dynamicPermissionService;
private readonly IHttpContextAccessor _contextAccessor;
public DynamicPermissionHandler(
IDynamicPermissionService dynamicPermissionService,
IHttpContextAccessor contextAccessor
)
{
_dynamicPermissionService = dynamicPermissionService;
_contextAccessor = contextAccessor;
}
protected override Task HandleRequirementAsync(
AuthorizationHandlerContext context,
DynamicPermissionRequirement requirement)
{
var user = _contextAccessor.HttpContext.User;
var routeData = _contextAccessor.HttpContext.GetRouteData().Values;
var controller = routeData["controller"].ToString();
var action = routeData["action"].ToString();
var area = routeData["area"]?.ToString();
if (_dynamicPermissionService.CanAccess(user, area, controller, action))
{
context.Succeed(requirement);
}
else
{
context.Fail();
}
return Task.CompletedTask;
}
}
@@ -0,0 +1,23 @@
using System.Security.Claims;
namespace CleanArc.Infrastructure.Identity.Identity.PermissionManager;
public class DynamicPermissionService : IDynamicPermissionService
{
public bool CanAccess(ClaimsPrincipal user, string area, string controller, string action)
{
if (user.IsInRole("admin"))
{
return true;
}
var key = $"{area}:{controller}:";
var userClaims = user.FindAll(ConstantPolicies.DynamicPermission);
return userClaims.Any(item => item.Value.Equals(key, StringComparison.OrdinalIgnoreCase));
}
}
@@ -0,0 +1,8 @@
using System.Security.Claims;
namespace CleanArc.Infrastructure.Identity.Identity.PermissionManager;
public interface IDynamicPermissionService
{
bool CanAccess(ClaimsPrincipal user, string area, string controller, string action);
}
@@ -0,0 +1,214 @@
using System.ComponentModel.DataAnnotations;
using System.Reflection;
using CleanArc.Application.Contracts.Identity;
using CleanArc.Application.Models.Identity;
using CleanArc.Domain.Entities.User;
using CleanArc.Infrastructure.Identity.Identity.Manager;
using CleanArc.Infrastructure.Persistence;
using MapsterMapper;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.AspNetCore.Mvc.Infrastructure;
using Microsoft.AspNetCore.Routing;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
namespace CleanArc.Infrastructure.Identity.Identity.PermissionManager;
internal class RoleManagerService : IRoleManagerService
{
private readonly AppRoleManager _roleManger;
private readonly IMapper _mapper;
private readonly IActionDescriptorCollectionProvider _actionDescriptor;
private readonly EndpointDataSource _endpointDataSource;
private readonly AppUserManager _userManager;
private readonly ILogger<RoleManagerService> _logger;
private readonly ApplicationDbContext _db;
public RoleManagerService(AppRoleManager roleManger, IMapper mapper, IActionDescriptorCollectionProvider actionDescriptor, AppUserManager userManager, ILogger<RoleManagerService> logger, ApplicationDbContext db, EndpointDataSource endpointDataSource)
{
_roleManger = roleManger;
_mapper = mapper;
_actionDescriptor = actionDescriptor;
_userManager = userManager;
_logger = logger;
_db = db;
_endpointDataSource = endpointDataSource;
}
public async Task<List<GetRolesDto>> GetRolesAsync()
{
var result = await _roleManger.Roles.Where(c => !c.Name.Equals("admin")).Select(r => _mapper.Map<Role, GetRolesDto>(r)).ToListAsync();
return result;
}
public async Task<IdentityResult> CreateRoleAsync(CreateRoleDto model)
{
var role = new Role
{
Name = model.RoleName,
};
var result = await _roleManger.CreateAsync(role);
return result;
}
public async Task<bool> DeleteRoleAsync(int roleId)
{
var role = await _roleManger.Roles.Include(r => r.Claims)
.Include(r => r.Users).FirstOrDefaultAsync(r => r.Id == roleId);
if (role == null)
return false;
var users = await _userManager.GetUsersInRoleAsync(role.Name);
foreach (var user in users)
{
await _userManager.RemoveFromRoleAsync(user, role.Name);
await _userManager.UpdateSecurityStampAsync(user);
}
_db.RemoveRange(role.Claims);
_db.RemoveRange(role.Users);
_db.Remove(role);
await _db.SaveChangesAsync();
return true;
}
public Task<List<ActionDescriptionDto>> GetPermissionActionsAsync()
{
var actions = new List<ActionDescriptionDto>();
var actionDescriptors = _actionDescriptor.ActionDescriptors.Items;
string controllerName = "";
foreach (var actionDescriptor in actionDescriptors)
{
try
{
var descriptor = (ControllerActionDescriptor)actionDescriptor;
var hasPermission = descriptor.ControllerTypeInfo.GetCustomAttribute<AuthorizeAttribute>()?
.Policy == ConstantPolicies.DynamicPermission; //||
// descriptor.MethodInfo.GetCustomAttribute<AuthorizeAttribute>()?
// .Policy == ConstantPolicies.DynamicPermission;
if (hasPermission && (controllerName != descriptor.ControllerName))
{
actions.Add(new ActionDescriptionDto
{
// ActionName = descriptor.ActionName,
ControllerName = descriptor.ControllerName,
ActionDisplayName = descriptor.MethodInfo.GetCustomAttribute<DisplayAttribute>()?.Name,
AreaName = descriptor.ControllerTypeInfo.GetCustomAttribute<AreaAttribute>()?.RouteValue,
ControllerDisplayName = descriptor.ControllerTypeInfo.GetCustomAttribute<DisplayAttribute>()
?.Name,
ControllerDescription = descriptor.ControllerTypeInfo.GetCustomAttribute<DisplayAttribute>()
?.Description
});
controllerName = descriptor.ControllerName;
}
}
catch (Exception e)
{
_logger.LogError(e, e.Message);
}
}
return Task.FromResult(actions);
}
public async Task<RolePermissionDto> GetRolePermissionsAsync(int roleId)
{
var role = await _roleManger.Roles
.Include(x => x.Claims)
.SingleOrDefaultAsync(x => x.Id == roleId);
if (role == null)
return null;
var dynamicActions = await this.GetPermissionActionsAsync();
return new RolePermissionDto
{
Role = role,
Actions = dynamicActions
};
}
public async Task<bool> ChangeRolePermissionsAsync(EditRolePermissionsDto model)
{
var role = await _roleManger.Roles
.Include(x => x.Claims)
.SingleOrDefaultAsync(x => x.Id == model.RoleId);
if (role == null)
{
return false;
}
var selectedPermissions = model.Permissions;
var roleClaims = role.Claims
.Where(x => x.ClaimType == ConstantPolicies.DynamicPermission)
.Select(x => x.ClaimValue)
.ToList();
// add new permissions
var newPermissions = selectedPermissions.Except(roleClaims).ToList();
foreach (var permission in newPermissions)
{
role.Claims.Add(new RoleClaim
{
ClaimType = ConstantPolicies.DynamicPermission,
ClaimValue = permission,
CreatedClaim = DateTime.Now,
RoleId = role.Id
});
}
// remove deleted permissions
var removedPermissions = roleClaims.Except(selectedPermissions).ToList();
foreach (var permission in removedPermissions)
{
var roleClaim = role.Claims
.SingleOrDefault(x =>
x.ClaimType == ConstantPolicies.DynamicPermission &&
x.ClaimValue == permission);
if (roleClaim != null)
{
_db.RemoveRange(roleClaim);
}
}
await _db.SaveChangesAsync();
var result = await _roleManger.UpdateAsync(role);
if (result.Succeeded)
{
var users = await _userManager.GetUsersInRoleAsync(role.Name);
foreach (var user in users)
{
await _userManager.UpdateSecurityStampAsync(user);
}
return true;
}
return false;
}
public async Task<Role> GetRoleByIdAsync(int roleId)
{
return await _roleManger.FindByIdAsync(roleId.ToString());
}
}
@@ -0,0 +1,47 @@
using CleanArc.Domain.Entities.User;
using CleanArc.Infrastructure.Identity.Identity.Manager;
using Microsoft.EntityFrameworkCore;
namespace CleanArc.Infrastructure.Identity.Identity.SeedDatabaseService;
public interface ISeedDataBase
{
Task Seed();
}
public class SeedDataBase : ISeedDataBase
{
private readonly AppUserManager _userManager;
private readonly AppRoleManager _roleManager;
public SeedDataBase(AppUserManager userManager, AppRoleManager roleManager)
{
_userManager = userManager;
_roleManager = roleManager;
}
public async Task Seed()
{
if (!_roleManager.Roles.AsNoTracking().Any(r => r.Name.Equals("admin")))
{
var role=new Role
{
Name = "admin",
};
await _roleManager.CreateAsync(role);
}
if (!_userManager.Users.AsNoTracking().Any(u => u.UserName.Equals("admin")))
{
var user = new User
{
UserName = "admin",
Email = "admin@site.com",
PhoneNumberConfirmed = true
};
await _userManager.CreateAsync(user, "qw123321");
await _userManager.AddToRoleAsync(user,"admin");
}
}
}
@@ -0,0 +1,13 @@
using CleanArc.Domain.Entities.User;
using CleanArc.Infrastructure.Persistence;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
namespace CleanArc.Infrastructure.Identity.Identity.Store;
public class AppUserStore:UserStore<User,Role,ApplicationDbContext,int,UserClaim,UserRole,UserLogin,UserToken,RoleClaim>
{
public AppUserStore(ApplicationDbContext context, IdentityErrorDescriber describer = null) : base(context, describer)
{
}
}
@@ -0,0 +1,13 @@
using CleanArc.Domain.Entities.User;
using CleanArc.Infrastructure.Persistence;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
namespace CleanArc.Infrastructure.Identity.Identity.Store;
public class RoleStore:RoleStore<Role,ApplicationDbContext,int,UserRole,RoleClaim>
{
public RoleStore(ApplicationDbContext context, IdentityErrorDescriber describer = null) : base(context, describer)
{
}
}
@@ -0,0 +1,17 @@
using CleanArc.Domain.Entities.User;
using Microsoft.AspNetCore.Identity;
namespace CleanArc.Infrastructure.Identity.Identity.validator;
public class AppRoleValidator:RoleValidator<Role>
{
public AppRoleValidator(IdentityErrorDescriber errors):base(errors)
{
}
public override Task<IdentityResult> ValidateAsync(RoleManager<Role> manager, Role role)
{
return base.ValidateAsync(manager, role);
}
}
@@ -0,0 +1,21 @@
using CleanArc.Domain.Entities.User;
using Microsoft.AspNetCore.Identity;
namespace CleanArc.Infrastructure.Identity.Identity.validator;
public class AppUserValidator:UserValidator<User>
{
public AppUserValidator(IdentityErrorDescriber errors):base(errors)
{
}
public override async Task<IdentityResult> ValidateAsync(UserManager<User> manager, User user)
{
var result=await base.ValidateAsync(manager, user);
return result;
}
}
@@ -0,0 +1,122 @@
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;
using CleanArc.Application.Contracts;
using CleanArc.Application.Contracts.Persistence;
using CleanArc.Application.Models.Jwt;
using CleanArc.Domain.Entities.User;
using CleanArc.Infrastructure.Identity.Identity.Dtos;
using CleanArc.Infrastructure.Identity.Identity.Manager;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Options;
using Microsoft.IdentityModel.Tokens;
namespace CleanArc.Infrastructure.Identity.Jwt;
public class JwtService : IJwtService
{
private readonly IdentitySettings _siteSetting;
private readonly AppUserManager _userManager;
private IUserClaimsPrincipalFactory<User> _claimsPrincipal;
private readonly IUnitOfWork _unitOfWork;
//private readonly AppUserClaimsPrincipleFactory claimsPrincipleFactory;
public JwtService(IOptions<IdentitySettings> siteSetting, AppUserManager userManager, IUserClaimsPrincipalFactory<User> claimsPrincipal, IUnitOfWork unitOfWork)
{
_siteSetting = siteSetting.Value;
_userManager = userManager;
_claimsPrincipal = claimsPrincipal;
_unitOfWork = unitOfWork;
}
public async Task<AccessToken> GenerateAsync(User user)
{
var secretKey = Encoding.UTF8.GetBytes(_siteSetting.SecretKey); // longer that 16 character
var signingCredentials = new SigningCredentials(new SymmetricSecurityKey(secretKey), SecurityAlgorithms.HmacSha256Signature);
var encryptionkey = Encoding.UTF8.GetBytes(_siteSetting.Encryptkey); //must be 16 character
var encryptingCredentials = new EncryptingCredentials(new SymmetricSecurityKey(encryptionkey), SecurityAlgorithms.Aes128KW, SecurityAlgorithms.Aes128CbcHmacSha256);
var claims = await _getClaimsAsync(user);
var descriptor = new SecurityTokenDescriptor
{
Issuer = _siteSetting.Issuer,
Audience = _siteSetting.Audience,
IssuedAt = DateTime.Now,
NotBefore = DateTime.Now.AddMinutes(0),
Expires = DateTime.Now.AddMinutes(_siteSetting.ExpirationMinutes),
SigningCredentials = signingCredentials,
EncryptingCredentials = encryptingCredentials,
Subject = new ClaimsIdentity(claims)
};
var tokenHandler = new JwtSecurityTokenHandler();
var securityToken = tokenHandler.CreateJwtSecurityToken(descriptor);
var refreshToken = await _unitOfWork.UserRefreshTokenRepository.CreateToken(user.Id);
await _unitOfWork.CommitAsync();
return new AccessToken(securityToken,refreshToken.ToString());
}
public Task<ClaimsPrincipal> GetPrincipalFromExpiredToken(string token)
{
var tokenValidationParameters = new TokenValidationParameters
{
ValidateAudience = false, //you might want to validate the audience and issuer depending on your use case
ValidateIssuer = false,
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_siteSetting.SecretKey)),
ValidateLifetime = false,
TokenDecryptionKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_siteSetting.Encryptkey))
};
var tokenHandler = new JwtSecurityTokenHandler();
SecurityToken securityToken;
var principal = tokenHandler.ValidateToken(token, tokenValidationParameters, out securityToken);
var jwtSecurityToken = securityToken as JwtSecurityToken;
if (jwtSecurityToken == null || !jwtSecurityToken.Header.Alg.Equals(SecurityAlgorithms.HmacSha256, StringComparison.InvariantCultureIgnoreCase))
throw new SecurityTokenException("Invalid token");
return Task.FromResult(principal);
}
public async Task<AccessToken> GenerateByPhoneNumberAsync(string phoneNumber)
{
var user = await _userManager.Users.AsNoTracking().FirstOrDefaultAsync(u => u.PhoneNumber == phoneNumber);
var result = await this.GenerateAsync(user);
return result;
}
public async Task<AccessToken> RefreshToken(Guid refreshTokenId)
{
var refreshToken = await _unitOfWork.UserRefreshTokenRepository.GetTokenWithInvalidation(refreshTokenId);
if (refreshToken is null)
return null;
refreshToken.IsValid = false;
await _unitOfWork.CommitAsync();
var user = await _unitOfWork.UserRefreshTokenRepository.GetUserByRefreshToken(refreshTokenId);
if (user is null)
return null;
var result = await this.GenerateAsync(user);
return result;
}
private async Task<IEnumerable<Claim>> _getClaimsAsync(User user)
{
var result = await _claimsPrincipal.CreateAsync(user);
return result.Claims;
}
}
@@ -0,0 +1,224 @@
using System.Security.Claims;
using System.Text;
using CleanArc.Application.Contracts;
using CleanArc.Application.Contracts.Identity;
using CleanArc.Application.Models.ApiResult;
using CleanArc.Domain.Entities.User;
using CleanArc.Infrastructure.Identity.Identity;
using CleanArc.Infrastructure.Identity.Identity.Dtos;
using CleanArc.Infrastructure.Identity.Identity.Extensions;
using CleanArc.Infrastructure.Identity.Identity.Manager;
using CleanArc.Infrastructure.Identity.Identity.PermissionManager;
using CleanArc.Infrastructure.Identity.Identity.SeedDatabaseService;
using CleanArc.Infrastructure.Identity.Identity.Store;
using CleanArc.Infrastructure.Identity.Identity.validator;
using CleanArc.Infrastructure.Identity.Jwt;
using CleanArc.Infrastructure.Identity.UserManager;
using CleanArc.SharedKernel.Extensions;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.IdentityModel.Tokens;
namespace CleanArc.Infrastructure.Identity.ServiceConfiguration;
public static class ServiceCollectionExtension
{
public static IServiceCollection RegisterIdentityServices(this IServiceCollection services,IdentitySettings identitySettings)
{
services.AddScoped<IJwtService, JwtService>();
services.AddScoped<IAppUserManager, AppUserManagerImplementation>();
services.AddScoped<ISeedDataBase, SeedDataBase>();
services.AddScoped<IUserValidator<User>, AppUserValidator>();
services.AddScoped<UserValidator<User>, AppUserValidator>();
services.AddScoped<IUserClaimsPrincipalFactory<User>, AppUserClaimsPrincipleFactory>();
services.AddScoped<IRoleValidator<Role>, AppRoleValidator>();
services.AddScoped<RoleValidator<Role>, AppRoleValidator>();
services.AddScoped<IAuthorizationHandler, DynamicPermissionHandler>();
services.AddScoped<IDynamicPermissionService, DynamicPermissionService>();
services.AddScoped<IRoleStore<Role>, RoleStore>();
services.AddScoped<IUserStore<User>, AppUserStore>();
services.AddScoped<IRoleManagerService, RoleManagerService>();
services.AddIdentity<User, Role>(options =>
{
options.Stores.ProtectPersonalData = false;
options.Password.RequireDigit = false;
options.Password.RequireLowercase = false;
options.Password.RequireNonAlphanumeric = false;
options.Password.RequiredUniqueChars = 0;
options.Password.RequireUppercase = false;
options.SignIn.RequireConfirmedEmail = false;
options.SignIn.RequireConfirmedPhoneNumber = true;
options.Lockout.MaxFailedAccessAttempts = 5;
options.Lockout.AllowedForNewUsers = false;
options.User.RequireUniqueEmail = false;
//options.Stores.ProtectPersonalData = true;
}).AddUserStore<AppUserStore>()
.AddRoleStore<RoleStore>().
//.AddUserValidator<AppUserValidator>().
//AddRoleValidator<AppRoleValidator>().
AddUserManager<AppUserManager>().
AddRoleManager<AppRoleManager>().
AddErrorDescriber<AppErrorDescriber>()
//.AddClaimsPrincipalFactory<AppUserClaimsPrincipleFactory>()
.AddDefaultTokenProviders().
AddSignInManager<AppSignInManager>()
.AddDefaultTokenProviders()
.AddPasswordlessLoginTotpTokenProvider();
//For [ProtectPersonalData] Attribute In Identity
//services.AddScoped<ILookupProtectorKeyRing, KeyRing>();
//services.AddScoped<ILookupProtector, LookupProtector>();
//services.AddScoped<IPersonalDataProtector, PersonalDataProtector>();
services.AddAuthorization(options =>
{
options.AddPolicy(ConstantPolicies.DynamicPermission, policy =>
{
policy.RequireAuthenticatedUser();
policy.Requirements.Add(new DynamicPermissionRequirement());
});
});
services.AddAuthentication( options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(options =>
{
var secretkey = Encoding.UTF8.GetBytes(identitySettings.SecretKey);
var encryptionkey = Encoding.UTF8.GetBytes(identitySettings.Encryptkey);
var validationParameters = new TokenValidationParameters
{
ClockSkew = TimeSpan.Zero, // default: 5 min
RequireSignedTokens = true,
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(secretkey),
RequireExpirationTime = true,
ValidateLifetime = true,
ValidateAudience = true, //default : false
ValidAudience = identitySettings.Audience,
ValidateIssuer = true, //default : false
ValidIssuer = identitySettings.Issuer,
TokenDecryptionKey = new SymmetricSecurityKey(encryptionkey),
};
options.RequireHttpsMetadata = false;
options.SaveToken = true;
options.TokenValidationParameters = validationParameters;
options.Events = new JwtBearerEvents
{
OnAuthenticationFailed = context =>
{
//var logger = context.HttpContext.RequestServices.GetRequiredService<ILoggerFactory>().CreateLogger(nameof(JwtBearerEvents));
//logger.LogError("Authentication failed.", context.Exception);
return Task.CompletedTask;
},
OnTokenValidated = async context =>
{
var signInManager = context.HttpContext.RequestServices.GetRequiredService<AppSignInManager>();
var claimsIdentity = context.Principal.Identity as ClaimsIdentity;
if (claimsIdentity.Claims?.Any() != true)
context.Fail("This token has no claims.");
var securityStamp =
claimsIdentity.FindFirstValue(new ClaimsIdentityOptions().SecurityStampClaimType);
if (!securityStamp.HasValue())
context.Fail("This token has no secuirty stamp");
//Find user and token from database and perform your custom validation
var userId = claimsIdentity.GetUserId<int>();
// var user = await userRepository.GetByIdAsync(context.HttpContext.RequestAborted, userId);
//if (user.SecurityStamp != Guid.Parse(securityStamp))
// context.Fail("Token secuirty stamp is not valid.");
var validatedUser = await signInManager.ValidateSecurityStampAsync(context.Principal);
if (validatedUser == null)
context.Fail("Token secuirty stamp is not valid.");
},
OnChallenge = async context =>
{
//var logger = context.HttpContext.RequestServices.GetRequiredService<ILoggerFactory>().CreateLogger(nameof(JwtBearerEvents));
//logger.LogError("OnChallenge error", context.Error, context.ErrorDescription);
if (context.AuthenticateFailure is SecurityTokenExpiredException)
{
context.HandleResponse();
var response = new ApiResult(false,
ApiResultStatusCode.UnAuthorized, "Token is expired. refresh your token");
context.Response.StatusCode = StatusCodes.Status401Unauthorized;
await context.Response.WriteAsJsonAsync(response);
}
else if (context.AuthenticateFailure != null)
{
context.HandleResponse();
var response = new ApiResult(false,
ApiResultStatusCode.UnAuthorized, "Token is Not Valid");
context.Response.StatusCode = StatusCodes.Status401Unauthorized;
await context.Response.WriteAsJsonAsync(response);
}
else
{
context.HandleResponse();
context.Response.StatusCode = (int)StatusCodes.Status401Unauthorized;
await context.Response.WriteAsJsonAsync(new ApiResult(false, ApiResultStatusCode.UnAuthorized, "Invalid access token. Please login"));
}
},
OnForbidden =async context =>
{
context.Response.StatusCode = (int)StatusCodes.Status403Forbidden;
await context.Response.WriteAsJsonAsync(new ApiResult(false,
ApiResultStatusCode.Forbidden, ApiResultStatusCode.Forbidden.ToDisplay()));
}
};
});
return services;
}
public static async Task SeedDefaultUsersAsync(this WebApplication app)
{
await using var scope = app.Services.CreateAsyncScope();
var seedService = scope.ServiceProvider.GetRequiredService<ISeedDataBase>();
await seedService.Seed();
}
}
@@ -0,0 +1,145 @@
using CleanArc.Application.Contracts.Identity;
using CleanArc.Domain.Entities.User;
using CleanArc.Infrastructure.Identity.Identity.Dtos;
using CleanArc.Infrastructure.Identity.Identity.Manager;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
namespace CleanArc.Infrastructure.Identity.UserManager;
public class AppUserManagerImplementation : IAppUserManager
{
private readonly AppUserManager _userManager;
public AppUserManagerImplementation(AppUserManager userManager)
{
_userManager = userManager;
}
public Task<IdentityResult> CreateUser(User user)
{
return _userManager.CreateAsync(user);
}
public async Task<IdentityResult> CreateUser(User user, string password)
{
return await _userManager.CreateAsync(user, password);
}
public Task<bool> IsExistUser(string phoneNumber)
{
return _userManager.Users.AnyAsync(c => c.PhoneNumber == phoneNumber);
}
public Task<bool> IsExistUserName(string userName)
{
return _userManager.Users.AnyAsync(c => c.UserName.Equals(userName));
}
public async Task<string> GeneratePhoneNumberConfirmationToken(User user, string phoneNumber)
{
return await _userManager.GenerateChangePhoneNumberTokenAsync(user, phoneNumber);
}
public Task<User> GetUserByCode(string code)
{
return _userManager.Users.FirstOrDefaultAsync(c => c.GeneratedCode.Equals(code));
}
public async Task<IdentityResult> ChangePhoneNumber(User user, string phoneNumber, string code)
{
return await _userManager.ChangePhoneNumberAsync(user, phoneNumber, code);
}
public async Task<IdentityResult> VerifyUserCode(User user, string code)
{
var confirmationResult = await _userManager.VerifyUserTokenAsync(
user, CustomIdentityConstants.OtpPasswordLessLoginProvider, CustomIdentityConstants.OtpPasswordLessLoginPurpose, code);
if (confirmationResult)
await _userManager.UpdateSecurityStampAsync(user);
return confirmationResult
? IdentityResult.Success
: IdentityResult.Failed(new IdentityError() { Description = "Incorrect Code" });
}
public Task<string> GenerateOtpCode(User user)
{
return _userManager.GenerateUserTokenAsync(
user, CustomIdentityConstants.OtpPasswordLessLoginProvider, CustomIdentityConstants.OtpPasswordLessLoginPurpose);
}
public Task<User> GetUserByPhoneNumber(string phoneNumber)
{
return _userManager.Users.FirstOrDefaultAsync(c => c.PhoneNumber.Equals(phoneNumber));
}
public Task<User> GetByUserName(string userName)
{
return _userManager.FindByNameAsync(userName);
}
public async Task<User> GetUserByIdAsync(int userId)
{
return await _userManager.FindByIdAsync(userId.ToString());
}
public async Task<List<User>> GetAllUsersAsync()
{
return await _userManager.Users.AsNoTracking().ToListAsync();
}
public async Task<IdentityResult> CreateUserWithPasswordAsync(User user, string password)
{
return await _userManager.CreateAsync(user, password);
}
public async Task<IdentityResult> AddUserToRoleAsync(User user, Role role)
{
ArgumentNullException.ThrowIfNull(role, nameof(role));
ArgumentNullException.ThrowIfNull(role.Name, nameof(role.Name));
return await _userManager.AddToRoleAsync(user, role.Name);
}
public async Task<IdentityResult> IncrementAccessFailedCountAsync(User user)
{
return await _userManager.AccessFailedAsync(user);
}
public async Task<bool> IsUserLockedOutAsync(User user)
{
var lockoutEndDate = await _userManager.GetLockoutEndDateAsync(user);
return (lockoutEndDate.HasValue && lockoutEndDate.Value > DateTimeOffset.Now);
}
public async Task ResetUserLockoutAsync(User user)
{
await _userManager.SetLockoutEndDateAsync(user, null);
await _userManager.ResetAccessFailedCountAsync(user);
}
public async Task UpdateUserAsync(User user)
{
await _userManager.UpdateAsync(user);
}
public async Task UpdateSecurityStampAsync(User user)
{
await _userManager.UpdateSecurityStampAsync(user);
}
public async Task<bool> IsPasswordValidAsync(User user, string password)
{
return await _userManager.CheckPasswordAsync(user, password);
}
public async Task<string[]> GetRoleAsync(User user)
{
return (await _userManager.GetRolesAsync(user)).ToArray();
}
}
@@ -0,0 +1,22 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="AspNetCore.HealthChecks.SqlServer" />
<PackageReference Include="AspNetCore.HealthChecks.UI.Client" />
<PackageReference Include="AspNetCore.HealthChecks.UI.InMemory.Storage" />
<PackageReference Include="OpenTelemetry.Exporter.Prometheus.AspNetCore" />
<PackageReference Include="OpenTelemetry.Extensions.Hosting" />
<PackageReference Include="OpenTelemetry.Instrumentation.AspNetCore" />
<PackageReference Include="OpenTelemetry.Instrumentation.Runtime" />
<PackageReference Include="prometheus-net" />
<PackageReference Include="prometheus-net.AspNetCore" />
<PackageReference Include="prometheus-net.AspNetCore.HealthChecks" />
</ItemGroup>
</Project>
@@ -0,0 +1,38 @@
using System.Net.Security;
using HealthChecks.UI.Client;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Diagnostics.HealthChecks;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Prometheus;
namespace CleanArc.Infrastructure.Monitoring.Configurations;
public static class HealthCheckConfigurations
{
public static WebApplicationBuilder ConfigureHealthChecks(this WebApplicationBuilder builder)
{
builder.Services.AddHealthChecks()
.AddSqlServer(builder.Configuration.GetConnectionString("SqlServer")!, name: "SQL Server")
.ForwardToPrometheus();
var currentUrl = builder.Configuration["ASPNETCORE_URLS"]?.Split(';')[0].Replace("+", "localhost");
return builder;
}
public static WebApplication UseHealthChecks(this WebApplication app)
{
app.MapHealthChecks("/HealthCheck", new HealthCheckOptions
{
Predicate = _ => true,
ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse
}).ShortCircuit().DisableHttpMetrics();
return app;
}
}
@@ -0,0 +1,28 @@
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
using OpenTelemetry.Metrics;
namespace CleanArc.Infrastructure.Monitoring.Configurations;
public static class OpenTelemetryConfigurations
{
public static WebApplicationBuilder SetupOpenTelemetry(this WebApplicationBuilder builder)
{
builder.Services.AddOpenTelemetry()
.WithMetrics(metricsBuilder =>
{
metricsBuilder.AddRuntimeInstrumentation()
.AddAspNetCoreInstrumentation()
.AddMeter("Microsoft.AspNetCore.Hosting"
, "Microsoft.AspNetCore.Server.Kestrel"
, "System.Net.Http"
, "CleanArc.Web.Api"
, "ControllerMetrics")
.AddPrometheusExporter();
});
builder.Services.AddMetrics();
return builder;
}
}
@@ -0,0 +1,15 @@
using Microsoft.AspNetCore.Builder;
using Prometheus;
namespace CleanArc.Infrastructure.Monitoring.Configurations;
public static class PrometheusMetricsConfigurations
{
public static WebApplication UseMetrics(this WebApplication app)
{
app.UseMetricServer();
app.UseHttpMetrics();
return app;
}
}
@@ -0,0 +1,91 @@
using System.Reflection;
using CleanArc.Domain.Common;
using CleanArc.Domain.Entities.User;
using CleanArc.SharedKernel.Extensions;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
namespace CleanArc.Infrastructure.Persistence;
public class ApplicationDbContext: IdentityDbContext<User, Role, int, UserClaim, UserRole, UserLogin, RoleClaim, UserToken>
{
public ApplicationDbContext(DbContextOptions options)
: base(options)
{
base.SavingChanges += OnSavingChanges;
}
private void OnSavingChanges(object sender, SavingChangesEventArgs e)
{
_cleanString();
ConfigureEntityDates();
}
private void _cleanString()
{
var changedEntities = ChangeTracker.Entries()
.Where(x => x.State == EntityState.Added || x.State == EntityState.Modified);
foreach (var item in changedEntities)
{
if (item.Entity == null)
continue;
var properties = item.Entity.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance)
.Where(p => p.CanRead && p.CanWrite && p.PropertyType == typeof(string));
foreach (var property in properties)
{
var propName = property.Name;
var val = (string)property.GetValue(item.Entity, null);
if (val.HasValue())
{
var newVal = val.Fa2En().FixPersianChars();
if (newVal == val)
continue;
property.SetValue(item.Entity, newVal, null);
}
}
}
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
var entitiesAssembly = typeof(IEntity).Assembly;
modelBuilder.RegisterAllEntities<IEntity>(entitiesAssembly);
modelBuilder.ApplyConfigurationsFromAssembly(typeof(ApplicationDbContext).Assembly);
modelBuilder.AddRestrictDeleteBehaviorConvention();
modelBuilder.AddPluralizingTableNameConvention();
}
private void ConfigureEntityDates()
{
var updatedEntities = ChangeTracker.Entries().Where(x =>
x.Entity is ITimeModification && x.State == EntityState.Modified).Select(x => x.Entity as ITimeModification);
var addedEntities = ChangeTracker.Entries().Where(x =>
x.Entity is ITimeModification && x.State == EntityState.Added).Select(x => x.Entity as ITimeModification);
foreach (var entity in updatedEntities)
{
if (entity != null)
{
entity.ModifiedDate = DateTime.Now;
}
}
foreach (var entity in addedEntities)
{
if (entity != null)
{
entity.CreatedTime = DateTime.Now;
entity.ModifiedDate = DateTime.Now;
}
}
}
}
@@ -0,0 +1,21 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\Core\CleanArc.Application\CleanArc.Application.csproj" />
<ProjectReference Include="..\..\Core\CleanArc.Domain\CleanArc.Domain.csproj" />
</ItemGroup>
</Project>
@@ -0,0 +1,15 @@
using CleanArc.Domain.Entities.Order;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
namespace CleanArc.Infrastructure.Persistence.Configuration.OrderConfig;
internal class OrderConfig:IEntityTypeConfiguration<Order>
{
public void Configure(EntityTypeBuilder<Order> builder)
{
builder.HasOne(c => c.User).WithMany(c => c.Orders).HasForeignKey(c => c.UserId);
builder.HasQueryFilter(c => !c.IsDeleted);
}
}
@@ -0,0 +1,15 @@
using CleanArc.Domain.Entities.User;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
namespace CleanArc.Infrastructure.Persistence.Configuration.UserConfig;
internal class RefreshTokenConfig:IEntityTypeConfiguration<UserRefreshToken>
{
public void Configure(EntityTypeBuilder<UserRefreshToken> builder)
{
builder.HasOne(c => c.User).WithMany(c => c.UserRefreshTokens).HasForeignKey(c => c.UserId);
builder.ToTable("UserRefreshTokens", "usr");
}
}
@@ -0,0 +1,17 @@
using CleanArc.Domain.Entities.User;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
namespace CleanArc.Infrastructure.Persistence.Configuration.UserConfig;
internal class RoleClaimConfig:IEntityTypeConfiguration<RoleClaim>
{
public void Configure(EntityTypeBuilder<RoleClaim> builder)
{
builder.ToTable("RoleClaims","usr");
builder.HasOne(u => u.Role).WithMany(u => u.Claims).HasForeignKey(u => u.RoleId).OnDelete(DeleteBehavior.Cascade);
}
}
@@ -0,0 +1,13 @@
using CleanArc.Domain.Entities.User;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
namespace CleanArc.Infrastructure.Persistence.Configuration.UserConfig;
internal class RoleConfig:IEntityTypeConfiguration<Role>
{
public void Configure(EntityTypeBuilder<Role> builder)
{
builder.ToTable("Roles","usr");
}
}
@@ -0,0 +1,15 @@
using CleanArc.Domain.Entities.User;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
namespace CleanArc.Infrastructure.Persistence.Configuration.UserConfig;
internal class UserClaimConfig:IEntityTypeConfiguration<UserClaim>
{
public void Configure(EntityTypeBuilder<UserClaim> builder)
{
builder.HasOne(u => u.User).WithMany(u => u.Claims).HasForeignKey(u => u.UserId);
builder.ToTable("UserClaims","usr");
}
}
@@ -0,0 +1,13 @@
using CleanArc.Domain.Entities.User;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
namespace CleanArc.Infrastructure.Persistence.Configuration.UserConfig;
internal class UserConfig:IEntityTypeConfiguration<User>
{
public void Configure(EntityTypeBuilder<User> builder)
{
builder.ToTable("Users","usr").Property(p => p.Id).HasColumnName("UserId");
}
}
@@ -0,0 +1,14 @@
using CleanArc.Domain.Entities.User;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
namespace CleanArc.Infrastructure.Persistence.Configuration.UserConfig;
internal class UserLoginConfig:IEntityTypeConfiguration<UserLogin>
{
public void Configure(EntityTypeBuilder<UserLogin> builder)
{
builder.HasOne(u => u.User).WithMany(u => u.Logins).HasForeignKey(u => u.UserId);
builder.ToTable("UserLogins","usr");
}
}
@@ -0,0 +1,16 @@
using CleanArc.Domain.Entities.User;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
namespace CleanArc.Infrastructure.Persistence.Configuration.UserConfig;
internal class UserRoleConfig:IEntityTypeConfiguration<UserRole>
{
public void Configure(EntityTypeBuilder<UserRole> builder)
{
builder.HasOne(u => u.User).WithMany(u => u.UserRoles).HasForeignKey(u => u.UserId);
builder.HasOne(u => u.Role).WithMany(u => u.Users).HasForeignKey(u => u.RoleId);
builder.ToTable("UserRoles","usr");
}
}
@@ -0,0 +1,15 @@
using CleanArc.Domain.Entities.User;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
namespace CleanArc.Infrastructure.Persistence.Configuration.UserConfig;
internal class UserTokenConfig:IEntityTypeConfiguration<UserToken>
{
public void Configure(EntityTypeBuilder<UserToken> builder)
{
builder.HasOne(u => u.User).WithMany(u => u.Tokens).HasForeignKey(u => u.UserId);
builder.ToTable("UserTokens","usr");
}
}
@@ -0,0 +1,374 @@
// <auto-generated />
using System;
using CleanArc.Infrastructure.Persistence;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Persistence;
namespace Persistence.Migrations
{
[DbContext(typeof(ApplicationDbContext))]
[Migration("20210327210004_Init")]
partial class Init
{
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.UseIdentityColumns()
.HasAnnotation("Relational:MaxIdentifierLength", 128)
.HasAnnotation("ProductVersion", "5.0.0");
modelBuilder.Entity("Domain.Entities.User.Role", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int")
.UseIdentityColumn();
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.HasColumnType("nvarchar(max)");
b.Property<DateTime>("CreatedDate")
.HasColumnType("datetime2");
b.Property<string>("DisplayName")
.HasColumnType("nvarchar(max)");
b.Property<string>("Name")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<string>("NormalizedName")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.HasKey("Id");
b.HasIndex("NormalizedName")
.IsUnique()
.HasDatabaseName("RoleNameIndex")
.HasFilter("[NormalizedName] IS NOT NULL");
b.ToTable("Roles", "usr");
});
modelBuilder.Entity("Domain.Entities.User.RoleClaim", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int")
.UseIdentityColumn();
b.Property<string>("ClaimType")
.HasColumnType("nvarchar(max)");
b.Property<string>("ClaimValue")
.HasColumnType("nvarchar(max)");
b.Property<DateTime>("CreatedClaim")
.HasColumnType("datetime2");
b.Property<int>("RoleId")
.HasColumnType("int");
b.HasKey("Id");
b.HasIndex("RoleId");
b.ToTable("RoleClaims", "usr");
});
modelBuilder.Entity("Domain.Entities.User.User", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int")
.HasColumnName("UserId")
.UseIdentityColumn();
b.Property<int>("AccessFailedCount")
.HasColumnType("int");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.HasColumnType("nvarchar(max)");
b.Property<string>("Email")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<bool>("EmailConfirmed")
.HasColumnType("bit");
b.Property<string>("FamilyName")
.HasColumnType("nvarchar(max)");
b.Property<string>("GeneratedCode")
.HasColumnType("nvarchar(max)");
b.Property<bool>("LockoutEnabled")
.HasColumnType("bit");
b.Property<DateTimeOffset?>("LockoutEnd")
.HasColumnType("datetimeoffset");
b.Property<string>("Name")
.HasColumnType("nvarchar(max)");
b.Property<string>("NormalizedEmail")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<string>("NormalizedUserName")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<string>("PasswordHash")
.HasColumnType("nvarchar(max)");
b.Property<string>("PhoneNumber")
.HasColumnType("nvarchar(max)");
b.Property<bool>("PhoneNumberConfirmed")
.HasColumnType("bit");
b.Property<string>("SecurityStamp")
.HasColumnType("nvarchar(max)");
b.Property<bool>("TwoFactorEnabled")
.HasColumnType("bit");
b.Property<string>("UserName")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.HasKey("Id");
b.HasIndex("NormalizedEmail")
.HasDatabaseName("EmailIndex");
b.HasIndex("NormalizedUserName")
.IsUnique()
.HasDatabaseName("UserNameIndex")
.HasFilter("[NormalizedUserName] IS NOT NULL");
b.ToTable("Users", "usr");
});
modelBuilder.Entity("Domain.Entities.User.UserClaim", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int")
.UseIdentityColumn();
b.Property<string>("ClaimType")
.HasColumnType("nvarchar(max)");
b.Property<string>("ClaimValue")
.HasColumnType("nvarchar(max)");
b.Property<int>("UserId")
.HasColumnType("int");
b.HasKey("Id");
b.HasIndex("UserId");
b.ToTable("UserClaims", "usr");
});
modelBuilder.Entity("Domain.Entities.User.UserLogin", b =>
{
b.Property<string>("LoginProvider")
.HasColumnType("nvarchar(450)");
b.Property<string>("ProviderKey")
.HasColumnType("nvarchar(450)");
b.Property<DateTime>("LoggedOn")
.HasColumnType("datetime2");
b.Property<string>("ProviderDisplayName")
.HasColumnType("nvarchar(max)");
b.Property<int>("UserId")
.HasColumnType("int");
b.HasKey("LoginProvider", "ProviderKey");
b.HasIndex("UserId");
b.ToTable("UserLogins", "usr");
});
modelBuilder.Entity("Domain.Entities.User.UserRefreshToken", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uniqueidentifier");
b.Property<DateTime>("CreatedAt")
.HasColumnType("datetime2");
b.Property<DateTime>("CreatedTime")
.HasColumnType("datetime2");
b.Property<bool>("IsValid")
.HasColumnType("bit");
b.Property<DateTime?>("ModifiedDate")
.HasColumnType("datetime2");
b.Property<int>("UserId")
.HasColumnType("int");
b.HasKey("Id");
b.HasIndex("UserId");
b.ToTable("UserRefreshTokens", "usr");
});
modelBuilder.Entity("Domain.Entities.User.UserRole", b =>
{
b.Property<int>("UserId")
.HasColumnType("int");
b.Property<int>("RoleId")
.HasColumnType("int");
b.Property<DateTime>("CreatedUserRoleDate")
.HasColumnType("datetime2");
b.HasKey("UserId", "RoleId");
b.HasIndex("RoleId");
b.ToTable("UserRoles", "usr");
});
modelBuilder.Entity("Domain.Entities.User.UserToken", b =>
{
b.Property<int>("UserId")
.HasColumnType("int");
b.Property<string>("LoginProvider")
.HasColumnType("nvarchar(450)");
b.Property<string>("Name")
.HasColumnType("nvarchar(450)");
b.Property<DateTime>("GeneratedTime")
.HasColumnType("datetime2");
b.Property<string>("Value")
.HasColumnType("nvarchar(max)");
b.HasKey("UserId", "LoginProvider", "Name");
b.ToTable("UserTokens", "usr");
});
modelBuilder.Entity("Domain.Entities.User.RoleClaim", b =>
{
b.HasOne("Domain.Entities.User.Role", "Role")
.WithMany("Claims")
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Restrict)
.IsRequired();
b.Navigation("Role");
});
modelBuilder.Entity("Domain.Entities.User.UserClaim", b =>
{
b.HasOne("Domain.Entities.User.User", "User")
.WithMany("Claims")
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Restrict)
.IsRequired();
b.Navigation("User");
});
modelBuilder.Entity("Domain.Entities.User.UserLogin", b =>
{
b.HasOne("Domain.Entities.User.User", "User")
.WithMany("Logins")
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Restrict)
.IsRequired();
b.Navigation("User");
});
modelBuilder.Entity("Domain.Entities.User.UserRefreshToken", b =>
{
b.HasOne("Domain.Entities.User.User", "User")
.WithMany("UserRefreshTokens")
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Restrict)
.IsRequired();
b.Navigation("User");
});
modelBuilder.Entity("Domain.Entities.User.UserRole", b =>
{
b.HasOne("Domain.Entities.User.Role", "Role")
.WithMany("Users")
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Restrict)
.IsRequired();
b.HasOne("Domain.Entities.User.User", "User")
.WithMany("UserRoles")
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Restrict)
.IsRequired();
b.Navigation("Role");
b.Navigation("User");
});
modelBuilder.Entity("Domain.Entities.User.UserToken", b =>
{
b.HasOne("Domain.Entities.User.User", "User")
.WithMany("Tokens")
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Restrict)
.IsRequired();
b.Navigation("User");
});
modelBuilder.Entity("Domain.Entities.User.Role", b =>
{
b.Navigation("Claims");
b.Navigation("Users");
});
modelBuilder.Entity("Domain.Entities.User.User", b =>
{
b.Navigation("Claims");
b.Navigation("Logins");
b.Navigation("Tokens");
b.Navigation("UserRefreshTokens");
b.Navigation("UserRoles");
});
#pragma warning restore 612, 618
}
}
}
@@ -0,0 +1,294 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
namespace Persistence.Migrations
{
public partial class Init : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.EnsureSchema(
name: "usr");
migrationBuilder.CreateTable(
name: "Roles",
schema: "usr",
columns: table => new
{
Id = table.Column<int>(type: "int", nullable: false)
.Annotation("SqlServer:Identity", "1, 1"),
DisplayName = table.Column<string>(type: "nvarchar(max)", nullable: true),
CreatedDate = table.Column<DateTime>(type: "datetime2", nullable: false),
Name = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: true),
NormalizedName = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: true),
ConcurrencyStamp = table.Column<string>(type: "nvarchar(max)", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_Roles", x => x.Id);
});
migrationBuilder.CreateTable(
name: "Users",
schema: "usr",
columns: table => new
{
UserId = table.Column<int>(type: "int", nullable: false)
.Annotation("SqlServer:Identity", "1, 1"),
Name = table.Column<string>(type: "nvarchar(max)", nullable: true),
FamilyName = table.Column<string>(type: "nvarchar(max)", nullable: true),
GeneratedCode = table.Column<string>(type: "nvarchar(max)", nullable: true),
UserName = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: true),
NormalizedUserName = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: true),
Email = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: true),
NormalizedEmail = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: true),
EmailConfirmed = table.Column<bool>(type: "bit", nullable: false),
PasswordHash = table.Column<string>(type: "nvarchar(max)", nullable: true),
SecurityStamp = table.Column<string>(type: "nvarchar(max)", nullable: true),
ConcurrencyStamp = table.Column<string>(type: "nvarchar(max)", nullable: true),
PhoneNumber = table.Column<string>(type: "nvarchar(max)", nullable: true),
PhoneNumberConfirmed = table.Column<bool>(type: "bit", nullable: false),
TwoFactorEnabled = table.Column<bool>(type: "bit", nullable: false),
LockoutEnd = table.Column<DateTimeOffset>(type: "datetimeoffset", nullable: true),
LockoutEnabled = table.Column<bool>(type: "bit", nullable: false),
AccessFailedCount = table.Column<int>(type: "int", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_Users", x => x.UserId);
});
migrationBuilder.CreateTable(
name: "RoleClaims",
schema: "usr",
columns: table => new
{
Id = table.Column<int>(type: "int", nullable: false)
.Annotation("SqlServer:Identity", "1, 1"),
CreatedClaim = table.Column<DateTime>(type: "datetime2", nullable: false),
RoleId = table.Column<int>(type: "int", nullable: false),
ClaimType = table.Column<string>(type: "nvarchar(max)", nullable: true),
ClaimValue = table.Column<string>(type: "nvarchar(max)", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_RoleClaims", x => x.Id);
table.ForeignKey(
name: "FK_RoleClaims_Roles_RoleId",
column: x => x.RoleId,
principalSchema: "usr",
principalTable: "Roles",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
});
migrationBuilder.CreateTable(
name: "UserClaims",
schema: "usr",
columns: table => new
{
Id = table.Column<int>(type: "int", nullable: false)
.Annotation("SqlServer:Identity", "1, 1"),
UserId = table.Column<int>(type: "int", nullable: false),
ClaimType = table.Column<string>(type: "nvarchar(max)", nullable: true),
ClaimValue = table.Column<string>(type: "nvarchar(max)", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_UserClaims", x => x.Id);
table.ForeignKey(
name: "FK_UserClaims_Users_UserId",
column: x => x.UserId,
principalSchema: "usr",
principalTable: "Users",
principalColumn: "UserId",
onDelete: ReferentialAction.Restrict);
});
migrationBuilder.CreateTable(
name: "UserLogins",
schema: "usr",
columns: table => new
{
LoginProvider = table.Column<string>(type: "nvarchar(450)", nullable: false),
ProviderKey = table.Column<string>(type: "nvarchar(450)", nullable: false),
LoggedOn = table.Column<DateTime>(type: "datetime2", nullable: false),
ProviderDisplayName = table.Column<string>(type: "nvarchar(max)", nullable: true),
UserId = table.Column<int>(type: "int", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_UserLogins", x => new { x.LoginProvider, x.ProviderKey });
table.ForeignKey(
name: "FK_UserLogins_Users_UserId",
column: x => x.UserId,
principalSchema: "usr",
principalTable: "Users",
principalColumn: "UserId",
onDelete: ReferentialAction.Restrict);
});
migrationBuilder.CreateTable(
name: "UserRefreshTokens",
schema: "usr",
columns: table => new
{
Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
UserId = table.Column<int>(type: "int", nullable: false),
CreatedAt = table.Column<DateTime>(type: "datetime2", nullable: false),
IsValid = table.Column<bool>(type: "bit", nullable: false),
CreatedTime = table.Column<DateTime>(type: "datetime2", nullable: false),
ModifiedDate = table.Column<DateTime>(type: "datetime2", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_UserRefreshTokens", x => x.Id);
table.ForeignKey(
name: "FK_UserRefreshTokens_Users_UserId",
column: x => x.UserId,
principalSchema: "usr",
principalTable: "Users",
principalColumn: "UserId",
onDelete: ReferentialAction.Restrict);
});
migrationBuilder.CreateTable(
name: "UserRoles",
schema: "usr",
columns: table => new
{
UserId = table.Column<int>(type: "int", nullable: false),
RoleId = table.Column<int>(type: "int", nullable: false),
CreatedUserRoleDate = table.Column<DateTime>(type: "datetime2", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_UserRoles", x => new { x.UserId, x.RoleId });
table.ForeignKey(
name: "FK_UserRoles_Roles_RoleId",
column: x => x.RoleId,
principalSchema: "usr",
principalTable: "Roles",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
table.ForeignKey(
name: "FK_UserRoles_Users_UserId",
column: x => x.UserId,
principalSchema: "usr",
principalTable: "Users",
principalColumn: "UserId",
onDelete: ReferentialAction.Restrict);
});
migrationBuilder.CreateTable(
name: "UserTokens",
schema: "usr",
columns: table => new
{
UserId = table.Column<int>(type: "int", nullable: false),
LoginProvider = table.Column<string>(type: "nvarchar(450)", nullable: false),
Name = table.Column<string>(type: "nvarchar(450)", nullable: false),
GeneratedTime = table.Column<DateTime>(type: "datetime2", nullable: false),
Value = table.Column<string>(type: "nvarchar(max)", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_UserTokens", x => new { x.UserId, x.LoginProvider, x.Name });
table.ForeignKey(
name: "FK_UserTokens_Users_UserId",
column: x => x.UserId,
principalSchema: "usr",
principalTable: "Users",
principalColumn: "UserId",
onDelete: ReferentialAction.Restrict);
});
migrationBuilder.CreateIndex(
name: "IX_RoleClaims_RoleId",
schema: "usr",
table: "RoleClaims",
column: "RoleId");
migrationBuilder.CreateIndex(
name: "RoleNameIndex",
schema: "usr",
table: "Roles",
column: "NormalizedName",
unique: true,
filter: "[NormalizedName] IS NOT NULL");
migrationBuilder.CreateIndex(
name: "IX_UserClaims_UserId",
schema: "usr",
table: "UserClaims",
column: "UserId");
migrationBuilder.CreateIndex(
name: "IX_UserLogins_UserId",
schema: "usr",
table: "UserLogins",
column: "UserId");
migrationBuilder.CreateIndex(
name: "IX_UserRefreshTokens_UserId",
schema: "usr",
table: "UserRefreshTokens",
column: "UserId");
migrationBuilder.CreateIndex(
name: "IX_UserRoles_RoleId",
schema: "usr",
table: "UserRoles",
column: "RoleId");
migrationBuilder.CreateIndex(
name: "EmailIndex",
schema: "usr",
table: "Users",
column: "NormalizedEmail");
migrationBuilder.CreateIndex(
name: "UserNameIndex",
schema: "usr",
table: "Users",
column: "NormalizedUserName",
unique: true,
filter: "[NormalizedUserName] IS NOT NULL");
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "RoleClaims",
schema: "usr");
migrationBuilder.DropTable(
name: "UserClaims",
schema: "usr");
migrationBuilder.DropTable(
name: "UserLogins",
schema: "usr");
migrationBuilder.DropTable(
name: "UserRefreshTokens",
schema: "usr");
migrationBuilder.DropTable(
name: "UserRoles",
schema: "usr");
migrationBuilder.DropTable(
name: "UserTokens",
schema: "usr");
migrationBuilder.DropTable(
name: "Roles",
schema: "usr");
migrationBuilder.DropTable(
name: "Users",
schema: "usr");
}
}
}
@@ -0,0 +1,422 @@
// <auto-generated />
using System;
using CleanArc.Infrastructure.Persistence;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Persistence;
#nullable disable
namespace Persistence.Migrations
{
[DbContext(typeof(ApplicationDbContext))]
[Migration("20221205084354_AddedOrderAndUserRelation")]
partial class AddedOrderAndUserRelation
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "7.0.0")
.HasAnnotation("Relational:MaxIdentifierLength", 128);
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
modelBuilder.Entity("Domain.Entities.Order.Order", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<DateTime>("CreatedTime")
.HasColumnType("datetime2");
b.Property<DateTime?>("ModifiedDate")
.HasColumnType("datetime2");
b.Property<string>("OrderName")
.HasColumnType("nvarchar(max)");
b.Property<int>("UserId")
.HasColumnType("int");
b.HasKey("Id");
b.HasIndex("UserId");
b.ToTable("Orders");
});
modelBuilder.Entity("Domain.Entities.User.Role", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.HasColumnType("nvarchar(max)");
b.Property<DateTime>("CreatedDate")
.HasColumnType("datetime2");
b.Property<string>("DisplayName")
.HasColumnType("nvarchar(max)");
b.Property<string>("Name")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<string>("NormalizedName")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.HasKey("Id");
b.HasIndex("NormalizedName")
.IsUnique()
.HasDatabaseName("RoleNameIndex")
.HasFilter("[NormalizedName] IS NOT NULL");
b.ToTable("Roles", "usr");
});
modelBuilder.Entity("Domain.Entities.User.RoleClaim", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<string>("ClaimType")
.HasColumnType("nvarchar(max)");
b.Property<string>("ClaimValue")
.HasColumnType("nvarchar(max)");
b.Property<DateTime>("CreatedClaim")
.HasColumnType("datetime2");
b.Property<int>("RoleId")
.HasColumnType("int");
b.HasKey("Id");
b.HasIndex("RoleId");
b.ToTable("RoleClaims", "usr");
});
modelBuilder.Entity("Domain.Entities.User.User", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int")
.HasColumnName("UserId");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<int>("AccessFailedCount")
.HasColumnType("int");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.HasColumnType("nvarchar(max)");
b.Property<string>("Email")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<bool>("EmailConfirmed")
.HasColumnType("bit");
b.Property<string>("FamilyName")
.HasColumnType("nvarchar(max)");
b.Property<string>("GeneratedCode")
.HasColumnType("nvarchar(max)");
b.Property<bool>("LockoutEnabled")
.HasColumnType("bit");
b.Property<DateTimeOffset?>("LockoutEnd")
.HasColumnType("datetimeoffset");
b.Property<string>("Name")
.HasColumnType("nvarchar(max)");
b.Property<string>("NormalizedEmail")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<string>("NormalizedUserName")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<string>("PasswordHash")
.HasColumnType("nvarchar(max)");
b.Property<string>("PhoneNumber")
.HasColumnType("nvarchar(max)");
b.Property<bool>("PhoneNumberConfirmed")
.HasColumnType("bit");
b.Property<string>("SecurityStamp")
.HasColumnType("nvarchar(max)");
b.Property<bool>("TwoFactorEnabled")
.HasColumnType("bit");
b.Property<string>("UserName")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.HasKey("Id");
b.HasIndex("NormalizedEmail")
.HasDatabaseName("EmailIndex");
b.HasIndex("NormalizedUserName")
.IsUnique()
.HasDatabaseName("UserNameIndex")
.HasFilter("[NormalizedUserName] IS NOT NULL");
b.ToTable("Users", "usr");
});
modelBuilder.Entity("Domain.Entities.User.UserClaim", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<string>("ClaimType")
.HasColumnType("nvarchar(max)");
b.Property<string>("ClaimValue")
.HasColumnType("nvarchar(max)");
b.Property<int>("UserId")
.HasColumnType("int");
b.HasKey("Id");
b.HasIndex("UserId");
b.ToTable("UserClaims", "usr");
});
modelBuilder.Entity("Domain.Entities.User.UserLogin", b =>
{
b.Property<string>("LoginProvider")
.HasColumnType("nvarchar(450)");
b.Property<string>("ProviderKey")
.HasColumnType("nvarchar(450)");
b.Property<DateTime>("LoggedOn")
.HasColumnType("datetime2");
b.Property<string>("ProviderDisplayName")
.HasColumnType("nvarchar(max)");
b.Property<int>("UserId")
.HasColumnType("int");
b.HasKey("LoginProvider", "ProviderKey");
b.HasIndex("UserId");
b.ToTable("UserLogins", "usr");
});
modelBuilder.Entity("Domain.Entities.User.UserRefreshToken", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uniqueidentifier");
b.Property<DateTime>("CreatedAt")
.HasColumnType("datetime2");
b.Property<DateTime>("CreatedTime")
.HasColumnType("datetime2");
b.Property<bool>("IsValid")
.HasColumnType("bit");
b.Property<DateTime?>("ModifiedDate")
.HasColumnType("datetime2");
b.Property<int>("UserId")
.HasColumnType("int");
b.HasKey("Id");
b.HasIndex("UserId");
b.ToTable("UserRefreshTokens", "usr");
});
modelBuilder.Entity("Domain.Entities.User.UserRole", b =>
{
b.Property<int>("UserId")
.HasColumnType("int");
b.Property<int>("RoleId")
.HasColumnType("int");
b.Property<DateTime>("CreatedUserRoleDate")
.HasColumnType("datetime2");
b.HasKey("UserId", "RoleId");
b.HasIndex("RoleId");
b.ToTable("UserRoles", "usr");
});
modelBuilder.Entity("Domain.Entities.User.UserToken", b =>
{
b.Property<int>("UserId")
.HasColumnType("int");
b.Property<string>("LoginProvider")
.HasColumnType("nvarchar(450)");
b.Property<string>("Name")
.HasColumnType("nvarchar(450)");
b.Property<DateTime>("GeneratedTime")
.HasColumnType("datetime2");
b.Property<string>("Value")
.HasColumnType("nvarchar(max)");
b.HasKey("UserId", "LoginProvider", "Name");
b.ToTable("UserTokens", "usr");
});
modelBuilder.Entity("Domain.Entities.Order.Order", b =>
{
b.HasOne("Domain.Entities.User.User", "User")
.WithMany("Orders")
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Restrict)
.IsRequired();
b.Navigation("User");
});
modelBuilder.Entity("Domain.Entities.User.RoleClaim", b =>
{
b.HasOne("Domain.Entities.User.Role", "Role")
.WithMany("Claims")
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Restrict)
.IsRequired();
b.Navigation("Role");
});
modelBuilder.Entity("Domain.Entities.User.UserClaim", b =>
{
b.HasOne("Domain.Entities.User.User", "User")
.WithMany("Claims")
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Restrict)
.IsRequired();
b.Navigation("User");
});
modelBuilder.Entity("Domain.Entities.User.UserLogin", b =>
{
b.HasOne("Domain.Entities.User.User", "User")
.WithMany("Logins")
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Restrict)
.IsRequired();
b.Navigation("User");
});
modelBuilder.Entity("Domain.Entities.User.UserRefreshToken", b =>
{
b.HasOne("Domain.Entities.User.User", "User")
.WithMany("UserRefreshTokens")
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Restrict)
.IsRequired();
b.Navigation("User");
});
modelBuilder.Entity("Domain.Entities.User.UserRole", b =>
{
b.HasOne("Domain.Entities.User.Role", "Role")
.WithMany("Users")
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Restrict)
.IsRequired();
b.HasOne("Domain.Entities.User.User", "User")
.WithMany("UserRoles")
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Restrict)
.IsRequired();
b.Navigation("Role");
b.Navigation("User");
});
modelBuilder.Entity("Domain.Entities.User.UserToken", b =>
{
b.HasOne("Domain.Entities.User.User", "User")
.WithMany("Tokens")
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Restrict)
.IsRequired();
b.Navigation("User");
});
modelBuilder.Entity("Domain.Entities.User.Role", b =>
{
b.Navigation("Claims");
b.Navigation("Users");
});
modelBuilder.Entity("Domain.Entities.User.User", b =>
{
b.Navigation("Claims");
b.Navigation("Logins");
b.Navigation("Orders");
b.Navigation("Tokens");
b.Navigation("UserRefreshTokens");
b.Navigation("UserRoles");
});
#pragma warning restore 612, 618
}
}
}
@@ -0,0 +1,50 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Persistence.Migrations
{
/// <inheritdoc />
public partial class AddedOrderAndUserRelation : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "Orders",
columns: table => new
{
Id = table.Column<int>(type: "int", nullable: false)
.Annotation("SqlServer:Identity", "1, 1"),
OrderName = table.Column<string>(type: "nvarchar(max)", nullable: true),
UserId = table.Column<int>(type: "int", nullable: false),
CreatedTime = table.Column<DateTime>(type: "datetime2", nullable: false),
ModifiedDate = table.Column<DateTime>(type: "datetime2", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_Orders", x => x.Id);
table.ForeignKey(
name: "FK_Orders_Users_UserId",
column: x => x.UserId,
principalSchema: "usr",
principalTable: "Users",
principalColumn: "UserId",
onDelete: ReferentialAction.Restrict);
});
migrationBuilder.CreateIndex(
name: "IX_Orders_UserId",
table: "Orders",
column: "UserId");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "Orders");
}
}
}
@@ -0,0 +1,424 @@
// <auto-generated />
using System;
using CleanArc.Infrastructure.Persistence;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
#nullable disable
namespace Persistence.Migrations
{
[DbContext(typeof(ApplicationDbContext))]
[Migration("20231126140035_AddedOrderDeleteFlag")]
partial class AddedOrderDeleteFlag
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "8.0.0")
.HasAnnotation("Relational:MaxIdentifierLength", 128);
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
modelBuilder.Entity("CleanArc.Domain.Entities.Order.Order", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<DateTime>("CreatedTime")
.HasColumnType("datetime2");
b.Property<bool>("IsDeleted")
.HasColumnType("bit");
b.Property<DateTime?>("ModifiedDate")
.HasColumnType("datetime2");
b.Property<string>("OrderName")
.HasColumnType("nvarchar(max)");
b.Property<int>("UserId")
.HasColumnType("int");
b.HasKey("Id");
b.HasIndex("UserId");
b.ToTable("Orders");
});
modelBuilder.Entity("CleanArc.Domain.Entities.User.Role", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.HasColumnType("nvarchar(max)");
b.Property<DateTime>("CreatedDate")
.HasColumnType("datetime2");
b.Property<string>("DisplayName")
.HasColumnType("nvarchar(max)");
b.Property<string>("Name")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<string>("NormalizedName")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.HasKey("Id");
b.HasIndex("NormalizedName")
.IsUnique()
.HasDatabaseName("RoleNameIndex")
.HasFilter("[NormalizedName] IS NOT NULL");
b.ToTable("Roles", "usr");
});
modelBuilder.Entity("CleanArc.Domain.Entities.User.RoleClaim", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<string>("ClaimType")
.HasColumnType("nvarchar(max)");
b.Property<string>("ClaimValue")
.HasColumnType("nvarchar(max)");
b.Property<DateTime>("CreatedClaim")
.HasColumnType("datetime2");
b.Property<int>("RoleId")
.HasColumnType("int");
b.HasKey("Id");
b.HasIndex("RoleId");
b.ToTable("RoleClaims", "usr");
});
modelBuilder.Entity("CleanArc.Domain.Entities.User.User", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int")
.HasColumnName("UserId");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<int>("AccessFailedCount")
.HasColumnType("int");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.HasColumnType("nvarchar(max)");
b.Property<string>("Email")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<bool>("EmailConfirmed")
.HasColumnType("bit");
b.Property<string>("FamilyName")
.HasColumnType("nvarchar(max)");
b.Property<string>("GeneratedCode")
.HasColumnType("nvarchar(max)");
b.Property<bool>("LockoutEnabled")
.HasColumnType("bit");
b.Property<DateTimeOffset?>("LockoutEnd")
.HasColumnType("datetimeoffset");
b.Property<string>("Name")
.HasColumnType("nvarchar(max)");
b.Property<string>("NormalizedEmail")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<string>("NormalizedUserName")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<string>("PasswordHash")
.HasColumnType("nvarchar(max)");
b.Property<string>("PhoneNumber")
.HasColumnType("nvarchar(max)");
b.Property<bool>("PhoneNumberConfirmed")
.HasColumnType("bit");
b.Property<string>("SecurityStamp")
.HasColumnType("nvarchar(max)");
b.Property<bool>("TwoFactorEnabled")
.HasColumnType("bit");
b.Property<string>("UserName")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.HasKey("Id");
b.HasIndex("NormalizedEmail")
.HasDatabaseName("EmailIndex");
b.HasIndex("NormalizedUserName")
.IsUnique()
.HasDatabaseName("UserNameIndex")
.HasFilter("[NormalizedUserName] IS NOT NULL");
b.ToTable("Users", "usr");
});
modelBuilder.Entity("CleanArc.Domain.Entities.User.UserClaim", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<string>("ClaimType")
.HasColumnType("nvarchar(max)");
b.Property<string>("ClaimValue")
.HasColumnType("nvarchar(max)");
b.Property<int>("UserId")
.HasColumnType("int");
b.HasKey("Id");
b.HasIndex("UserId");
b.ToTable("UserClaims", "usr");
});
modelBuilder.Entity("CleanArc.Domain.Entities.User.UserLogin", b =>
{
b.Property<string>("LoginProvider")
.HasColumnType("nvarchar(450)");
b.Property<string>("ProviderKey")
.HasColumnType("nvarchar(450)");
b.Property<DateTime>("LoggedOn")
.HasColumnType("datetime2");
b.Property<string>("ProviderDisplayName")
.HasColumnType("nvarchar(max)");
b.Property<int>("UserId")
.HasColumnType("int");
b.HasKey("LoginProvider", "ProviderKey");
b.HasIndex("UserId");
b.ToTable("UserLogins", "usr");
});
modelBuilder.Entity("CleanArc.Domain.Entities.User.UserRefreshToken", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uniqueidentifier");
b.Property<DateTime>("CreatedAt")
.HasColumnType("datetime2");
b.Property<DateTime>("CreatedTime")
.HasColumnType("datetime2");
b.Property<bool>("IsValid")
.HasColumnType("bit");
b.Property<DateTime?>("ModifiedDate")
.HasColumnType("datetime2");
b.Property<int>("UserId")
.HasColumnType("int");
b.HasKey("Id");
b.HasIndex("UserId");
b.ToTable("UserRefreshTokens", "usr");
});
modelBuilder.Entity("CleanArc.Domain.Entities.User.UserRole", b =>
{
b.Property<int>("UserId")
.HasColumnType("int");
b.Property<int>("RoleId")
.HasColumnType("int");
b.Property<DateTime>("CreatedUserRoleDate")
.HasColumnType("datetime2");
b.HasKey("UserId", "RoleId");
b.HasIndex("RoleId");
b.ToTable("UserRoles", "usr");
});
modelBuilder.Entity("CleanArc.Domain.Entities.User.UserToken", b =>
{
b.Property<int>("UserId")
.HasColumnType("int");
b.Property<string>("LoginProvider")
.HasColumnType("nvarchar(450)");
b.Property<string>("Name")
.HasColumnType("nvarchar(450)");
b.Property<DateTime>("GeneratedTime")
.HasColumnType("datetime2");
b.Property<string>("Value")
.HasColumnType("nvarchar(max)");
b.HasKey("UserId", "LoginProvider", "Name");
b.ToTable("UserTokens", "usr");
});
modelBuilder.Entity("CleanArc.Domain.Entities.Order.Order", b =>
{
b.HasOne("CleanArc.Domain.Entities.User.User", "User")
.WithMany("Orders")
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Restrict)
.IsRequired();
b.Navigation("User");
});
modelBuilder.Entity("CleanArc.Domain.Entities.User.RoleClaim", b =>
{
b.HasOne("CleanArc.Domain.Entities.User.Role", "Role")
.WithMany("Claims")
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Restrict)
.IsRequired();
b.Navigation("Role");
});
modelBuilder.Entity("CleanArc.Domain.Entities.User.UserClaim", b =>
{
b.HasOne("CleanArc.Domain.Entities.User.User", "User")
.WithMany("Claims")
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Restrict)
.IsRequired();
b.Navigation("User");
});
modelBuilder.Entity("CleanArc.Domain.Entities.User.UserLogin", b =>
{
b.HasOne("CleanArc.Domain.Entities.User.User", "User")
.WithMany("Logins")
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Restrict)
.IsRequired();
b.Navigation("User");
});
modelBuilder.Entity("CleanArc.Domain.Entities.User.UserRefreshToken", b =>
{
b.HasOne("CleanArc.Domain.Entities.User.User", "User")
.WithMany("UserRefreshTokens")
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Restrict)
.IsRequired();
b.Navigation("User");
});
modelBuilder.Entity("CleanArc.Domain.Entities.User.UserRole", b =>
{
b.HasOne("CleanArc.Domain.Entities.User.Role", "Role")
.WithMany("Users")
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Restrict)
.IsRequired();
b.HasOne("CleanArc.Domain.Entities.User.User", "User")
.WithMany("UserRoles")
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Restrict)
.IsRequired();
b.Navigation("Role");
b.Navigation("User");
});
modelBuilder.Entity("CleanArc.Domain.Entities.User.UserToken", b =>
{
b.HasOne("CleanArc.Domain.Entities.User.User", "User")
.WithMany("Tokens")
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Restrict)
.IsRequired();
b.Navigation("User");
});
modelBuilder.Entity("CleanArc.Domain.Entities.User.Role", b =>
{
b.Navigation("Claims");
b.Navigation("Users");
});
modelBuilder.Entity("CleanArc.Domain.Entities.User.User", b =>
{
b.Navigation("Claims");
b.Navigation("Logins");
b.Navigation("Orders");
b.Navigation("Tokens");
b.Navigation("UserRefreshTokens");
b.Navigation("UserRoles");
});
#pragma warning restore 612, 618
}
}
}
@@ -0,0 +1,29 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Persistence.Migrations
{
/// <inheritdoc />
public partial class AddedOrderDeleteFlag : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<bool>(
name: "IsDeleted",
table: "Orders",
type: "bit",
nullable: false,
defaultValue: false);
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "IsDeleted",
table: "Orders");
}
}
}
@@ -0,0 +1,421 @@
// <auto-generated />
using System;
using CleanArc.Infrastructure.Persistence;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
#nullable disable
namespace Persistence.Migrations
{
[DbContext(typeof(ApplicationDbContext))]
partial class ApplicationDbContextModelSnapshot : ModelSnapshot
{
protected override void BuildModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "10.0.0")
.HasAnnotation("Relational:MaxIdentifierLength", 128);
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
modelBuilder.Entity("CleanArc.Domain.Entities.Order.Order", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<DateTime>("CreatedTime")
.HasColumnType("datetime2");
b.Property<bool>("IsDeleted")
.HasColumnType("bit");
b.Property<DateTime?>("ModifiedDate")
.HasColumnType("datetime2");
b.Property<string>("OrderName")
.HasColumnType("nvarchar(max)");
b.Property<int>("UserId")
.HasColumnType("int");
b.HasKey("Id");
b.HasIndex("UserId");
b.ToTable("Orders", (string)null);
});
modelBuilder.Entity("CleanArc.Domain.Entities.User.Role", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.HasColumnType("nvarchar(max)");
b.Property<DateTime>("CreatedDate")
.HasColumnType("datetime2");
b.Property<string>("DisplayName")
.HasColumnType("nvarchar(max)");
b.Property<string>("Name")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<string>("NormalizedName")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.HasKey("Id");
b.HasIndex("NormalizedName")
.IsUnique()
.HasDatabaseName("RoleNameIndex")
.HasFilter("[NormalizedName] IS NOT NULL");
b.ToTable("Roles", "usr");
});
modelBuilder.Entity("CleanArc.Domain.Entities.User.RoleClaim", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<string>("ClaimType")
.HasColumnType("nvarchar(max)");
b.Property<string>("ClaimValue")
.HasColumnType("nvarchar(max)");
b.Property<DateTime>("CreatedClaim")
.HasColumnType("datetime2");
b.Property<int>("RoleId")
.HasColumnType("int");
b.HasKey("Id");
b.HasIndex("RoleId");
b.ToTable("RoleClaims", "usr");
});
modelBuilder.Entity("CleanArc.Domain.Entities.User.User", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int")
.HasColumnName("UserId");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<int>("AccessFailedCount")
.HasColumnType("int");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.HasColumnType("nvarchar(max)");
b.Property<string>("Email")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<bool>("EmailConfirmed")
.HasColumnType("bit");
b.Property<string>("FamilyName")
.HasColumnType("nvarchar(max)");
b.Property<string>("GeneratedCode")
.HasColumnType("nvarchar(max)");
b.Property<bool>("LockoutEnabled")
.HasColumnType("bit");
b.Property<DateTimeOffset?>("LockoutEnd")
.HasColumnType("datetimeoffset");
b.Property<string>("Name")
.HasColumnType("nvarchar(max)");
b.Property<string>("NormalizedEmail")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<string>("NormalizedUserName")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<string>("PasswordHash")
.HasColumnType("nvarchar(max)");
b.Property<string>("PhoneNumber")
.HasColumnType("nvarchar(max)");
b.Property<bool>("PhoneNumberConfirmed")
.HasColumnType("bit");
b.Property<string>("SecurityStamp")
.HasColumnType("nvarchar(max)");
b.Property<bool>("TwoFactorEnabled")
.HasColumnType("bit");
b.Property<string>("UserName")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.HasKey("Id");
b.HasIndex("NormalizedEmail")
.HasDatabaseName("EmailIndex");
b.HasIndex("NormalizedUserName")
.IsUnique()
.HasDatabaseName("UserNameIndex")
.HasFilter("[NormalizedUserName] IS NOT NULL");
b.ToTable("Users", "usr");
});
modelBuilder.Entity("CleanArc.Domain.Entities.User.UserClaim", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<string>("ClaimType")
.HasColumnType("nvarchar(max)");
b.Property<string>("ClaimValue")
.HasColumnType("nvarchar(max)");
b.Property<int>("UserId")
.HasColumnType("int");
b.HasKey("Id");
b.HasIndex("UserId");
b.ToTable("UserClaims", "usr");
});
modelBuilder.Entity("CleanArc.Domain.Entities.User.UserLogin", b =>
{
b.Property<string>("LoginProvider")
.HasColumnType("nvarchar(450)");
b.Property<string>("ProviderKey")
.HasColumnType("nvarchar(450)");
b.Property<DateTime>("LoggedOn")
.HasColumnType("datetime2");
b.Property<string>("ProviderDisplayName")
.HasColumnType("nvarchar(max)");
b.Property<int>("UserId")
.HasColumnType("int");
b.HasKey("LoginProvider", "ProviderKey");
b.HasIndex("UserId");
b.ToTable("UserLogins", "usr");
});
modelBuilder.Entity("CleanArc.Domain.Entities.User.UserRefreshToken", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uniqueidentifier");
b.Property<DateTime>("CreatedAt")
.HasColumnType("datetime2");
b.Property<DateTime>("CreatedTime")
.HasColumnType("datetime2");
b.Property<bool>("IsValid")
.HasColumnType("bit");
b.Property<DateTime?>("ModifiedDate")
.HasColumnType("datetime2");
b.Property<int>("UserId")
.HasColumnType("int");
b.HasKey("Id");
b.HasIndex("UserId");
b.ToTable("UserRefreshTokens", "usr");
});
modelBuilder.Entity("CleanArc.Domain.Entities.User.UserRole", b =>
{
b.Property<int>("UserId")
.HasColumnType("int");
b.Property<int>("RoleId")
.HasColumnType("int");
b.Property<DateTime>("CreatedUserRoleDate")
.HasColumnType("datetime2");
b.HasKey("UserId", "RoleId");
b.HasIndex("RoleId");
b.ToTable("UserRoles", "usr");
});
modelBuilder.Entity("CleanArc.Domain.Entities.User.UserToken", b =>
{
b.Property<int>("UserId")
.HasColumnType("int");
b.Property<string>("LoginProvider")
.HasColumnType("nvarchar(450)");
b.Property<string>("Name")
.HasColumnType("nvarchar(450)");
b.Property<DateTime>("GeneratedTime")
.HasColumnType("datetime2");
b.Property<string>("Value")
.HasColumnType("nvarchar(max)");
b.HasKey("UserId", "LoginProvider", "Name");
b.ToTable("UserTokens", "usr");
});
modelBuilder.Entity("CleanArc.Domain.Entities.Order.Order", b =>
{
b.HasOne("CleanArc.Domain.Entities.User.User", "User")
.WithMany("Orders")
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Restrict)
.IsRequired();
b.Navigation("User");
});
modelBuilder.Entity("CleanArc.Domain.Entities.User.RoleClaim", b =>
{
b.HasOne("CleanArc.Domain.Entities.User.Role", "Role")
.WithMany("Claims")
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Restrict)
.IsRequired();
b.Navigation("Role");
});
modelBuilder.Entity("CleanArc.Domain.Entities.User.UserClaim", b =>
{
b.HasOne("CleanArc.Domain.Entities.User.User", "User")
.WithMany("Claims")
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Restrict)
.IsRequired();
b.Navigation("User");
});
modelBuilder.Entity("CleanArc.Domain.Entities.User.UserLogin", b =>
{
b.HasOne("CleanArc.Domain.Entities.User.User", "User")
.WithMany("Logins")
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Restrict)
.IsRequired();
b.Navigation("User");
});
modelBuilder.Entity("CleanArc.Domain.Entities.User.UserRefreshToken", b =>
{
b.HasOne("CleanArc.Domain.Entities.User.User", "User")
.WithMany("UserRefreshTokens")
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Restrict)
.IsRequired();
b.Navigation("User");
});
modelBuilder.Entity("CleanArc.Domain.Entities.User.UserRole", b =>
{
b.HasOne("CleanArc.Domain.Entities.User.Role", "Role")
.WithMany("Users")
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Restrict)
.IsRequired();
b.HasOne("CleanArc.Domain.Entities.User.User", "User")
.WithMany("UserRoles")
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Restrict)
.IsRequired();
b.Navigation("Role");
b.Navigation("User");
});
modelBuilder.Entity("CleanArc.Domain.Entities.User.UserToken", b =>
{
b.HasOne("CleanArc.Domain.Entities.User.User", "User")
.WithMany("Tokens")
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Restrict)
.IsRequired();
b.Navigation("User");
});
modelBuilder.Entity("CleanArc.Domain.Entities.User.Role", b =>
{
b.Navigation("Claims");
b.Navigation("Users");
});
modelBuilder.Entity("CleanArc.Domain.Entities.User.User", b =>
{
b.Navigation("Claims");
b.Navigation("Logins");
b.Navigation("Orders");
b.Navigation("Tokens");
b.Navigation("UserRefreshTokens");
b.Navigation("UserRoles");
});
#pragma warning restore 612, 618
}
}
}
@@ -0,0 +1,44 @@
using System.Linq.Expressions;
using CleanArc.Domain.Common;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Query;
namespace CleanArc.Infrastructure.Persistence.Repositories.Common;
internal abstract class BaseAsyncRepository<TEntity> where TEntity:class,IEntity
{
public readonly ApplicationDbContext DbContext;
protected DbSet<TEntity> Entities { get; }
protected virtual IQueryable<TEntity> Table => Entities;
protected virtual IQueryable<TEntity> TableNoTracking => Entities.AsNoTrackingWithIdentityResolution();
protected BaseAsyncRepository(ApplicationDbContext dbContext)
{
DbContext = dbContext;
Entities = DbContext.Set<TEntity>(); // City => Cities
}
protected virtual async Task<List<TEntity>> ListAllAsync()
{
return await Entities.ToListAsync();
}
protected virtual async Task AddAsync(TEntity entity)
{
await Entities.AddAsync(entity);
}
protected virtual async Task UpdateAsync(
Expression<Func<TEntity,bool>> whereExpression,Action<UpdateSettersBuilder<TEntity>> updateExpression)
{
await Entities.Where(whereExpression).ExecuteUpdateAsync(updateExpression);
}
protected virtual async Task DeleteAsync(Expression<Func<TEntity,bool>> deleteExpression)
{
await Entities.Where(deleteExpression).ExecuteDeleteAsync();
}
}
@@ -0,0 +1,28 @@
using CleanArc.Application.Contracts.Persistence;
namespace CleanArc.Infrastructure.Persistence.Repositories.Common;
public class UnitOfWork : IUnitOfWork
{
private readonly ApplicationDbContext _db;
public IUserRefreshTokenRepository UserRefreshTokenRepository { get; }
public IOrderRepository OrderRepository { get; }
public UnitOfWork(ApplicationDbContext db)
{
_db = db;
UserRefreshTokenRepository = new UserRefreshTokenRepository(_db);
OrderRepository= new OrderRepository(_db);
}
public Task CommitAsync()
{
return _db.SaveChangesAsync();
}
public ValueTask RollBackAsync()
{
return _db.DisposeAsync();
}
}
@@ -0,0 +1,41 @@
using CleanArc.Application.Contracts.Persistence;
using CleanArc.Domain.Entities.Order;
using CleanArc.Infrastructure.Persistence.Repositories.Common;
using Microsoft.EntityFrameworkCore;
namespace CleanArc.Infrastructure.Persistence.Repositories;
internal class OrderRepository(ApplicationDbContext dbContext) : BaseAsyncRepository<Order>(dbContext), IOrderRepository
{
public async Task AddOrderAsync(Order order)
{
await base.AddAsync(order);
}
public async Task<List<Order>> GetAllUserOrdersAsync(int userId)
{
return await base.TableNoTracking.Where(c => c.UserId == userId).ToListAsync();
}
public async Task<List<Order>> GetAllOrdersWithRelatedUserAsync()
{
var orders = await base.TableNoTracking.Include(c => c.User).ToListAsync();
return orders;
}
public async Task<Order> GetUserOrderByIdAndUserIdAsync(int userId, int orderId,bool trackEntity)
{
var order = await base.TableNoTracking.FirstOrDefaultAsync(c => c.UserId == userId && c.Id == orderId);
if (order is not null && trackEntity)
base.DbContext.Attach(order);
return order;
}
public async Task DeleteUserOrdersAsync(int userId)
{
await UpdateAsync(c => c.UserId == userId, p => p.SetProperty(order => order.IsDeleted, true));
}
}
@@ -0,0 +1,40 @@
using CleanArc.Application.Contracts.Persistence;
using CleanArc.Domain.Entities.User;
using CleanArc.Infrastructure.Persistence.Repositories.Common;
using Microsoft.EntityFrameworkCore;
namespace CleanArc.Infrastructure.Persistence.Repositories;
internal class UserRefreshTokenRepository : BaseAsyncRepository<UserRefreshToken>, IUserRefreshTokenRepository
{
public UserRefreshTokenRepository(ApplicationDbContext dbContext) : base(dbContext)
{
}
public async Task<Guid> CreateToken(int userId)
{
var token = new UserRefreshToken { IsValid = true, UserId = userId };
await base.AddAsync(token);
return token.Id;
}
public async Task<UserRefreshToken> GetTokenWithInvalidation(Guid id)
{
var token = await base.Table.Where(t => t.IsValid && t.Id.Equals(id)).FirstOrDefaultAsync();
return token;
}
public async Task<User> GetUserByRefreshToken(Guid tokenId)
{
var user = await base.TableNoTracking.Include(t => t.User).Where(c => c.Id.Equals(tokenId))
.Select(c => c.User).FirstOrDefaultAsync();
return user;
}
public Task RemoveUserOldTokens(int userId, CancellationToken cancellationToken)
{
throw new NotImplementedException();
}
}
@@ -0,0 +1,35 @@
using CleanArc.Application.Contracts.Persistence;
using CleanArc.Infrastructure.Persistence.Repositories.Common;
using Microsoft.AspNetCore.Builder;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
namespace CleanArc.Infrastructure.Persistence.ServiceConfiguration;
public static class ServiceCollectionExtensions
{
public static IServiceCollection AddPersistenceServices(this IServiceCollection services,IConfiguration configuration)
{
services.AddScoped<IUnitOfWork, UnitOfWork>();
services.AddDbContext<ApplicationDbContext>(options =>
{
options
.UseSqlServer(configuration.GetConnectionString("SqlServer"));
});
return services;
}
public static async Task ApplyMigrationsAsync(this WebApplication app)
{
await using var scope = app.Services.CreateAsyncScope();
var context = scope.ServiceProvider.GetService<ApplicationDbContext>();
if (context is null)
throw new Exception("Database Context Not Found");
await context.Database.MigrateAsync();
}
}