init
This commit is contained in:
+20
@@ -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>
|
||||
+73
@@ -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
|
||||
};
|
||||
}
|
||||
+22
@@ -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>
|
||||
+80
@@ -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"
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
+34
@@ -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;
|
||||
}
|
||||
}
|
||||
+78
@@ -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;
|
||||
}
|
||||
}
|
||||
+236
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
+241
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
+35
@@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
namespace CleanArc.Infrastructure.Identity.Identity.Dtos
|
||||
{
|
||||
internal class CustomIdentityConstants
|
||||
{
|
||||
public const string OtpPasswordLessLoginProvider = "PasswordlessLoginTotpProvider";
|
||||
public const string OtpPasswordLessLoginPurpose = "passwordless-auth";
|
||||
}
|
||||
}
|
||||
+11
@@ -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; }
|
||||
}
|
||||
+55
@@ -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)
|
||||
{
|
||||
}
|
||||
}
|
||||
+12
@@ -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)
|
||||
{
|
||||
}
|
||||
}
|
||||
+15
@@ -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)
|
||||
{
|
||||
}
|
||||
}
|
||||
+13
@@ -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)
|
||||
{
|
||||
}
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
namespace CleanArc.Infrastructure.Identity.Identity.PermissionManager;
|
||||
|
||||
public static class ConstantPolicies
|
||||
{
|
||||
public const string DynamicPermission = nameof(DynamicPermission);
|
||||
}
|
||||
+53
@@ -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;
|
||||
}
|
||||
}
|
||||
+23
@@ -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));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
+8
@@ -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);
|
||||
}
|
||||
+214
@@ -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());
|
||||
}
|
||||
}
|
||||
+47
@@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
+13
@@ -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)
|
||||
{
|
||||
}
|
||||
}
|
||||
+13
@@ -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)
|
||||
{
|
||||
}
|
||||
}
|
||||
+17
@@ -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);
|
||||
}
|
||||
}
|
||||
+21
@@ -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;
|
||||
}
|
||||
}
|
||||
+224
@@ -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();
|
||||
}
|
||||
}
|
||||
+145
@@ -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();
|
||||
}
|
||||
}
|
||||
+22
@@ -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>
|
||||
+38
@@ -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;
|
||||
}
|
||||
}
|
||||
+28
@@ -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;
|
||||
}
|
||||
}
|
||||
+15
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+21
@@ -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>
|
||||
+15
@@ -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);
|
||||
}
|
||||
}
|
||||
+15
@@ -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");
|
||||
}
|
||||
}
|
||||
+17
@@ -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);
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
+13
@@ -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");
|
||||
}
|
||||
}
|
||||
+15
@@ -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");
|
||||
}
|
||||
}
|
||||
+13
@@ -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");
|
||||
}
|
||||
}
|
||||
+14
@@ -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");
|
||||
}
|
||||
}
|
||||
+16
@@ -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");
|
||||
}
|
||||
}
|
||||
+15
@@ -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");
|
||||
}
|
||||
}
|
||||
+374
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
+294
@@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
+422
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
+50
@@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
+424
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
+29
@@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
+421
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
+44
@@ -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();
|
||||
}
|
||||
}
|
||||
+28
@@ -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();
|
||||
}
|
||||
}
|
||||
+41
@@ -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));
|
||||
}
|
||||
}
|
||||
+40
@@ -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();
|
||||
}
|
||||
}
|
||||
+35
@@ -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();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user