Session wip

This commit is contained in:
Wvader 2022-09-17 20:13:35 +01:00
parent e0e9df2edd
commit 3b8d82049f
79 changed files with 1086 additions and 244 deletions

View File

@ -12,16 +12,18 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.ApiAuthorization.IdentityServer" Version="6.0.2" /> <PackageReference Include="Grpc.AspNetCore" Version="2.49.0-pre1" />
<PackageReference Include="Microsoft.AspNetCore.ApiAuthorization.IdentityServer" Version="6.0.8" />
<PackageReference Include="Microsoft.AspNetCore.Authorization" Version="6.0.8" /> <PackageReference Include="Microsoft.AspNetCore.Authorization" Version="6.0.8" />
<PackageReference Include="Microsoft.AspNetCore.Authorization.Policy" Version="2.2.0" /> <PackageReference Include="Microsoft.AspNetCore.Authorization.Policy" Version="2.2.0" />
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="6.0.7" /> <PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="6.0.8" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="6.0.2"> <PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="6.0.8">
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference> </PackageReference>
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="6.0.8" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="6.0.8" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="6.0.8" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="6.0.2"> <PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="6.0.8">
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference> </PackageReference>
@ -42,6 +44,7 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\BlueWest.Data.Application\BlueWest.Data.Application.csproj" />
<ProjectReference Include="..\BlueWest.Data.Capital\BlueWest.Data.Capital.csproj" /> <ProjectReference Include="..\BlueWest.Data.Capital\BlueWest.Data.Capital.csproj" />
<ProjectReference Include="..\BlueWest\BlueWest.csproj" /> <ProjectReference Include="..\BlueWest\BlueWest.csproj" />
<ProjectReference Include="..\include\BlueWest.EfMethods\src\BlueWest.EfMethods\BlueWest.EfMethods.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" /> <ProjectReference Include="..\include\BlueWest.EfMethods\src\BlueWest.EfMethods\BlueWest.EfMethods.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />

View File

@ -25,25 +25,24 @@ namespace BlueWest.WebApi.Context
[EfGetMany(typeof(ApplicationUserClaimUnique))] [EfGetMany(typeof(ApplicationUserClaimUnique))]
public sealed override DbSet<ApplicationUserClaim> UserClaims { get; set; } public sealed override DbSet<ApplicationUserClaim> UserClaims { get; set; }
[EfGetMany(typeof(ApplicationUserRoleUnique))]
/// <inheritdoc /> /// <inheritdoc />
[EfGetMany(typeof(ApplicationUserRoleUnique))]
public sealed override DbSet<ApplicationUserRole> UserRoles { get; set; } public sealed override DbSet<ApplicationUserRole> UserRoles { get; set; }
[EfGetMany(typeof(ApplicationRoleUnique))]
/// <inheritdoc /> /// <inheritdoc />
[EfGetMany(typeof(ApplicationRoleUnique))]
public sealed override DbSet<ApplicationRole> Roles { get; set; } public sealed override DbSet<ApplicationRole> Roles { get; set; }
[EfGetMany(typeof(ApplicationRoleClaimUnique))]
/// <inheritdoc /> /// <inheritdoc />
[EfGetMany(typeof(ApplicationRoleClaimUnique))]
public sealed override DbSet<ApplicationRoleClaim> RoleClaims { get; set; } public sealed override DbSet<ApplicationRoleClaim> RoleClaims { get; set; }
[EfGetMany(typeof(ApplicationUserUnique))] [EfGetMany(typeof(ApplicationUserUnique))]
[EfUpdateMethods( updateType: typeof(ApplicationUserUnique), returnType: typeof(ApplicationUserUnique), keyPropertyMemberName: nameof(ApplicationUserUnique.Id))] [EfUpdateMethods( updateType: typeof(ApplicationUserUnique), returnType: typeof(ApplicationUserUnique), keyPropertyMemberName: nameof(ApplicationUserUnique.Id))]
public sealed override DbSet<ApplicationUser> Users { get; set; } public sealed override DbSet<ApplicationUser> Users { get; set; }
#region Initialization
/// <inheritdoc /> /// <inheritdoc />
#region Initialization
public ApplicationUserDbContext(DbContextOptions<ApplicationUserDbContext> options) : base(options) public ApplicationUserDbContext(DbContextOptions<ApplicationUserDbContext> options) : base(options)
{ {
Database.EnsureCreated(); Database.EnsureCreated();
@ -55,62 +54,13 @@ namespace BlueWest.WebApi.Context
{ {
base.OnModelCreating(builder); base.OnModelCreating(builder);
builder.Entity<ApplicationUserRole>().ToTable("UserRoles");
builder.Entity<ApplicationUser>(b =>
{
b.HasMany<ApplicationUserRole>()
.WithOne()
.HasForeignKey(ur => ur.UserId).IsRequired();
});
builder.Entity<ApplicationUser>().ToTable("ApplicationUser")
.HasKey(x => x.Id);
builder.Entity<ApplicationRole>(b =>
{
b.HasKey(r => r.Id);
b.HasIndex(r => r.NormalizedName).HasDatabaseName("RoleNameIndex").IsUnique();
b.ToTable("Roles");
b.Property(r => r.ConcurrencyStamp).IsConcurrencyToken();
b.Property(u => u.Name).HasMaxLength(256);
b.Property(u => u.NormalizedName).HasMaxLength(256);
b.HasMany<ApplicationUserRole>().WithOne().HasForeignKey(ur => ur.RoleId).IsRequired();
b.HasMany<ApplicationRoleClaim>().WithOne().HasForeignKey(rc => rc.RoleId).IsRequired();
});
builder.Entity<ApplicationUserRole>().HasOne<ApplicationRole>(x => x.ApplicationRole);
builder.Entity<ApplicationRoleClaim>().HasOne<ApplicationRole>(x => x.ApplicationRole);
builder.Entity<ApplicationUserClaim>().HasOne<ApplicationUser>(x => x.ApplicationUser);
builder.Entity<ApplicationRoleClaim>(b =>
{
b.HasKey(rc => rc.Id);
b.ToTable("RoleClaims");
});
builder.Entity<ApplicationUserRole>(b =>
{
b.HasKey(r => new {r.UserId, r.RoleId});
b.ToTable("UserRoles");
});
builder.Entity<User>(b => b.HasOne<ApplicationUser>()
.WithMany(x => x.Users)
.HasForeignKey(x => x.ApplicationUserId));
builder.Entity<ApplicationRoleClaim>().ToTable("RoleClaims");
builder.Entity<ApplicationUserRole>().ToTable("UserRole");
builder.ConfigureCurrentDbModel(); builder.ConfigureCurrentDbModel();
} }
#endregion #endregion
/// <inheritdoc />
} }
} }

View File

@ -1,4 +1,5 @@
using BlueWest.Data; using BlueWest.Data;
using BlueWest.Data.Application;
using BlueWest.WebApi.Context.Users; using BlueWest.WebApi.Context.Users;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
@ -11,9 +12,7 @@ namespace BlueWest.WebApi.EF.Model
{ {
#region Initialization #region Initialization
static ModelBuilderExtensions() static ModelBuilderExtensions() { }
{
}
/// <summary> /// <summary>
/// Setup the database model /// Setup the database model
@ -24,7 +23,101 @@ namespace BlueWest.WebApi.EF.Model
modelBuilder modelBuilder
.ConfigureDatabaseKeys() .ConfigureDatabaseKeys()
.CurrencyModel() .CurrencyModel()
.ConfigureUserModel(); .ConfigureUserModel()
.ConfigureAppContextModel();
//.ConfigureIdentityModel();
}
#endregion
#region Application Users
/// <summary>
/// Configure App context model
/// </summary>
/// <param name="modelBuilder"></param>
private static void ConfigureAppContextModel(this ModelBuilder builder)
{
builder.Entity<ApplicationUserRole>().ToTable("UserRoles");
builder.Entity<ApplicationUser>(b =>
{
b.HasMany<ApplicationUserRole>()
.WithOne(b => b.User)
.HasForeignKey(ur => ur.UserId).IsRequired();
});
builder.Entity<ApplicationUser>().ToTable("ApplicationUser")
.HasKey(x => x.Id);
builder.Entity<ApplicationRole>(b =>
{
b.HasKey(r => r.Id);
b.HasIndex(r => r.NormalizedName).HasDatabaseName("RoleNameIndex").IsUnique();
b.ToTable("Roles");
b.Property(r => r.ConcurrencyStamp).IsConcurrencyToken();
b.Property(u => u.Name).HasMaxLength(256);
b.Property(u => u.NormalizedName).HasMaxLength(256);
b.HasMany<ApplicationUserRole>().WithOne().HasForeignKey(ur => ur.RoleId).IsRequired();
b.HasMany<ApplicationRoleClaim>().WithOne().HasForeignKey(rc => rc.RoleId).IsRequired();
});
builder.Entity<ApplicationUserRole>().HasOne(x => x.ApplicationRole);
builder.Entity<ApplicationRoleClaim>().HasOne<ApplicationRole>(x => x.ApplicationRole);
builder.Entity<ApplicationUserClaim>().HasOne<ApplicationUser>(x => x.ApplicationUser);
builder.Entity<ApplicationRoleClaim>(b =>
{
b.HasKey(rc => rc.Id);
b.ToTable("RoleClaims");
});
builder.Entity<ApplicationUserRole>(b =>
{
b.HasKey(r => new {r.UserId, r.RoleId});
b.ToTable("UserRoles");
});
builder.Entity<ApplicationRoleClaim>().ToTable("RoleClaims");
builder.Entity<ApplicationUserRole>().ToTable("UserRole");
// Session Token
builder.Entity<SessionToken>()
.HasOne(b => b.User)
.WithMany(x => x.SessionToken)
.HasForeignKey(x => x.UserId);
// Session Token Primary Key
builder.Entity<SessionToken>(b =>
b.HasKey(x => x.Id));
builder.Entity<SessionToken>(b =>
b.Property(x => x.Id).ValueGeneratedOnAdd());
// Session Data
builder.Entity<SessionData>()
.HasOne(b => b.User)
.WithMany(x => x.SessionDatas)
.HasForeignKey(x => x.UserId);
// Session Data
builder.Entity<SessionData>()
.HasOne(b => b.SessionToken)
.WithOne(x => x.SessionData);
// Session Data Primary Key
builder.Entity<SessionData>(b =>
b.HasKey(x => x.Id));
builder.Entity<SessionData>(b =>
b.Property(x => x.Id).ValueGeneratedOnAdd());
//.ConfigureIdentityModel(); //.ConfigureIdentityModel();
} }
@ -121,48 +214,11 @@ namespace BlueWest.WebApi.EF.Model
.WithMany(ft => ft.FinanceTransactions) .WithMany(ft => ft.FinanceTransactions)
.HasForeignKey(x => x.UserId)); .HasForeignKey(x => x.UserId));
modelBuilder.Entity<User>(b => b.HasOne<ApplicationUser>()
.WithMany(x => x.Users)
.HasForeignKey(x => x.ApplicationUserId));
return modelBuilder; return modelBuilder;
} }
#endregion #endregion
public static void ConfigureIdentityModel(this ModelBuilder builder)
{
builder.Entity<ApplicationUser>(b =>
{
b.HasMany<ApplicationUserRole>().WithOne().HasForeignKey(ur => ur.UserId).IsRequired();
});
builder.Entity<ApplicationRole>(b =>
{
b.HasKey(r => r.Id);
b.HasIndex(r => r.NormalizedName).HasDatabaseName("RoleNameIndex").IsUnique();
b.ToTable("Roles");
b.Property(r => r.ConcurrencyStamp).IsConcurrencyToken();
b.Property(u => u.Name).HasMaxLength(256);
b.Property(u => u.NormalizedName).HasMaxLength(256);
b.HasMany<ApplicationUserRole>().WithOne().HasForeignKey(ur => ur.RoleId).IsRequired();
b.HasMany<ApplicationRoleClaim>().WithOne().HasForeignKey(rc => rc.RoleId).IsRequired();
});
builder.Entity<ApplicationRoleClaim>(b =>
{
b.HasKey(rc => rc.Id);
b.ToTable("RoleClaims");
});
builder.Entity<ApplicationUserRole>(b =>
{
b.HasKey(r => new { r.UserId, r.RoleId });
b.ToTable("UserRoles");
});
}
} }
} }

View File

@ -0,0 +1,30 @@
using BlueWest.Data.Application;
using BlueWest.WebApi.EF.Model;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.InMemory;
namespace BlueWest.WebApi.Context
{
public class SessionDbContext : DbContext
{
/// <summary>
/// CountryDbContext Constructor.
/// </summary>
/// <param name="options"></param>
public SessionDbContext(DbContextOptions<SessionDbContext> options) : base(options)
{
Database.EnsureCreated();
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.ConfigureCurrentDbModel();
}
public DbSet<SessionToken> SessionTokens { get; set; }
}
}

View File

@ -0,0 +1,21 @@
using System;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Cors;
using Microsoft.AspNetCore.Mvc;
namespace BlueWest.WebApi.Controllers;
[ApiController]
[Route("application")]
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
[Authorize(AuthenticationSchemes = CookieAuthenticationDefaults.AuthenticationScheme)]
[EnableCors(Constants.CorsPolicyName)]
public class ApplicationController : ControllerBase
{
[HttpGet]
[ResponseCache(Location = ResponseCacheLocation.None, NoStore = true)]
public ContentResult GetTimeTicks() => Content(
DateTime.Now.Ticks.ToString());
}

View File

@ -1,5 +1,6 @@
using BlueWest.Data; using BlueWest.Data;
using BlueWest.WebApi.Context; using BlueWest.WebApi.Context;
using BlueWest.WebApi.Context.Users;
using Microsoft.AspNetCore.Authentication.Cookies; using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
@ -9,6 +10,7 @@ using Microsoft.AspNetCore.Mvc;
namespace BlueWest.WebApi.Controllers namespace BlueWest.WebApi.Controllers
{ {
/// <inheritdoc />
[ApiController] [ApiController]
[Route("application/users")] [Route("application/users")]
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)] [Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
@ -19,6 +21,7 @@ namespace BlueWest.WebApi.Controllers
{ {
private readonly ApplicationUserDbContext _dbContext; private readonly ApplicationUserDbContext _dbContext;
/// <inheritdoc />
public ApplicationUserController(ApplicationUserDbContext context) public ApplicationUserController(ApplicationUserDbContext context)
{ {
_dbContext = context; _dbContext = context;
@ -54,16 +57,15 @@ namespace BlueWest.WebApi.Controllers
[ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status400BadRequest)] [ProducesResponseType(StatusCodes.Status400BadRequest)]
[HttpPut("{UserCode}")] [HttpPut("{UserCode}")]
public ActionResult UpdateApplicationUser(int UserCode, UserUnique UserToUpdate) public ActionResult UpdateApplicationUser(string applicationUserId, ApplicationUserUnique UserToUpdate)
{ {
//var (success, User) = _dbContext.UpdateUser(UserToUpdate, UserCode); var (success, updatedUser) = _dbContext.UpdateApplicationUser(UserToUpdate, applicationUserId);
/*
if (success) if (success)
{ {
return Ok(User); return Ok(updatedUser);
} }
*/
return new NotFoundResult(); return new NotFoundResult();
} }
@ -124,8 +126,14 @@ namespace BlueWest.WebApi.Controllers
} }
} }
/// <summary>
/// Application Constants
/// </summary>
public static class Constants public static class Constants
{ {
/// <summary>
/// Policy Name
/// </summary>
public const string CorsPolicyName = "_myAllowSpecificOrigins"; public const string CorsPolicyName = "_myAllowSpecificOrigins";
} }

View File

@ -1,5 +1,8 @@
using System;
using System.Security.Claims; using System.Security.Claims;
using System.Threading.Tasks; using System.Threading.Tasks;
using BlueWest.Cryptography;
using BlueWest.Data.Application;
using BlueWest.WebApi.Context.Users; using BlueWest.WebApi.Context.Users;
using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies; using Microsoft.AspNetCore.Authentication.Cookies;
@ -14,25 +17,28 @@ namespace BlueWest.WebApi.Controllers;
/// <summary> /// <summary>
/// Auth controller /// Auth controller
/// </summary> /// </summary>
[Route("api/[controller]")]
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
[Authorize(AuthenticationSchemes = CookieAuthenticationDefaults.AuthenticationScheme)]
[ApiController] [ApiController]
[EnableCors(Constants.CorsPolicyName)] [Route("api/[controller]")]
public class AuthController : Controller
[Authorize(Policy = "ApiUser")]
/*[EnableCors(Constants.CorsPolicyName)]*/
public class AuthController : Controller
{ {
private readonly IAuthManager _authManager; private readonly IAuthManager _authManager;
private readonly IUserManager _userManager; private readonly IUserManager _userManager;
private readonly ISessionManager _sessionManager;
/// <summary> /// <summary>
/// ///
/// </summary> /// </summary>
/// <param name="authManager"></param> /// <param name="authManager"></param>
/// <param name="userManager"></param> /// <param name="userManager"></param>
public AuthController( IAuthManager authManager, IUserManager userManager) public AuthController( IAuthManager authManager, IUserManager userManager, ISessionManager sessionManager)
{ {
_authManager = authManager; _authManager = authManager;
_userManager = userManager; _userManager = userManager;
_sessionManager = sessionManager;
} }
@ -49,49 +55,87 @@ public class AuthController : Controller
} }
/// <summary> /// <summary>
/// Gets a bearer token /// Gets a bearer token
/// </summary> /// </summary>
/// <param name="loginViewModel"></param> /// <param name="loginViewModel"></param>
/// <returns></returns> /// <returns></returns>
[AllowAnonymous] [AllowAnonymous]
[HttpPost("login")] [HttpPost("token")]
public async Task<ActionResult<IdentityResult>> GetTokenAsync(LoginViewModel loginViewModel) public async Task<ActionResult<IdentityResult>> GetTokenAsync(LoginRequest loginViewModel)
{ {
var loginResultSucceded = await _authManager.GetToken(loginViewModel); var (success, sessionToken, token) = await _authManager.GetToken(loginViewModel);
if (loginResultSucceded != null) if (success)
{ {
return Ok(loginResultSucceded); return Ok(new {sessionToken, token});
} }
return Problem(); return Problem();
} }
/// <summary>
/// Check if user is logged in
/// </summary>
/// <returns></returns>
[HttpGet("isLoggedIn")]
public ActionResult<bool> IsLoggedIn()
{
var identity = new ClaimsIdentity(CookieAuthenticationDefaults.AuthenticationScheme);
if (identity.IsAuthenticated)
{
return Ok(true);
}
return Ok(false);
}
/// <summary>
/// Checks if the session is authorized
/// </summary>
/// <param name="hash"></param>
/// <returns></returns>
[HttpGet("isAuthorized")]
public ActionResult IsAuthorized(string hash)
{
var isAuthorized = _sessionManager.IsAuthorized(hash);
return Ok(isAuthorized ? new {authenticated = true} : new {authenticated = false});
}
/// <summary> /// <summary>
/// Do Cookie based login. /// Do Cookie based login.
/// </summary> /// </summary>
/// <param name="loginDto"></param> /// <param name="loginDto"></param>
/// <returns></returns> /// <returns></returns>
[AllowAnonymous] [AllowAnonymous]
[HttpPost("logincookie")] [HttpPost("login")]
public async Task<ActionResult<IdentityResult>> DoLoginAsync(LoginViewModel loginDto) public async Task<ActionResult> DoLoginAsync(LoginRequest loginDto)
{ {
var user = await _userManager.FindByEmailAsync(loginDto.Email); var (success, identity, sessionToken) = await _authManager.DoLogin(loginDto);
if (user != null) if (success)
{ {
if(await _userManager.CheckPasswordAsync(user, loginDto.Password)) await HttpContext.SignInAsync(
CookieAuthenticationDefaults.AuthenticationScheme,
new ClaimsPrincipal(identity),
new AuthenticationProperties
{ {
var identity = new ClaimsIdentity(CookieAuthenticationDefaults.AuthenticationScheme); IsPersistent = true,
identity.AddClaim(new Claim(ClaimTypes.Email, user.Email)); ExpiresUtc = DateTime.UtcNow.AddDays(1)
await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, new ClaimsPrincipal(identity)); });
return Ok(new {authenticated = true});
} return Ok(new {authenticated = true, sessionToken});
} }
return Ok(new {authenticated = false}); return new ForbidResult(CookieAuthenticationDefaults.AuthenticationScheme);
} }
/// <summary> /// <summary>
@ -101,10 +145,9 @@ public class AuthController : Controller
/// <returns></returns> /// <returns></returns>
[AllowAnonymous] [AllowAnonymous]
[HttpPost("logout")] [HttpPost("logout")]
public async Task<ActionResult<IdentityResult>> DoLogoutAsync(LoginViewModel loginDto) public async Task DoLogoutAsync()
{ {
await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme); await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
return Json(true);
} }
} }

View File

@ -18,8 +18,7 @@ namespace BlueWest.WebApi.Controllers
/// </summary> /// </summary>
[ApiController] [ApiController]
[Route("[controller]")] [Route("[controller]")]
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)] [Authorize(Policy = "ApiUser")]
[Authorize(AuthenticationSchemes = CookieAuthenticationDefaults.AuthenticationScheme)]
[EnableCors(Constants.CorsPolicyName)] [EnableCors(Constants.CorsPolicyName)]
// [Authorize(Roles = "Administrator")] // [Authorize(Roles = "Administrator")]
public class CountryController : ControllerBase public class CountryController : ControllerBase

View File

@ -6,6 +6,7 @@ using BlueWest.WebApi.EF;
using Microsoft.AspNetCore.Authentication.Cookies; using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Cors;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
@ -18,6 +19,8 @@ namespace BlueWest.WebApi.Controllers
[Route("[controller]")] [Route("[controller]")]
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)] [Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
[Authorize(AuthenticationSchemes = CookieAuthenticationDefaults.AuthenticationScheme)] [Authorize(AuthenticationSchemes = CookieAuthenticationDefaults.AuthenticationScheme)]
[EnableCors(Constants.CorsPolicyName)]
// [Authorize(Roles = "Administrator")] // [Authorize(Roles = "Administrator")]
public partial class CurrencyController : ControllerBase public partial class CurrencyController : ControllerBase
{ {

View File

@ -4,6 +4,7 @@ using BlueWest.WebApi.EF;
using Microsoft.AspNetCore.Authentication.Cookies; using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Cors;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
@ -16,7 +17,9 @@ namespace BlueWest.WebApi.Controllers;
[Route("[controller]")] [Route("[controller]")]
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)] [Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
[Authorize(AuthenticationSchemes = CookieAuthenticationDefaults.AuthenticationScheme)] [Authorize(AuthenticationSchemes = CookieAuthenticationDefaults.AuthenticationScheme)]
[Authorize(Roles = "Administrator")] //[Authorize(Roles = "Administrator")]
[EnableCors(Constants.CorsPolicyName)]
public class FinanceController : ControllerBase public class FinanceController : ControllerBase
{ {
private readonly FinanceDbContext _dbContext; private readonly FinanceDbContext _dbContext;

View File

@ -7,6 +7,7 @@ using BlueWest.WebApi.EF;
using Microsoft.AspNetCore.Authentication.Cookies; using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Cors;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
@ -19,7 +20,8 @@ namespace BlueWest.WebApi.Controllers
[Route("[controller]")] [Route("[controller]")]
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)] [Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
[Authorize(AuthenticationSchemes = CookieAuthenticationDefaults.AuthenticationScheme)] [Authorize(AuthenticationSchemes = CookieAuthenticationDefaults.AuthenticationScheme)]
[Authorize(Roles = "Administrator")] //[Authorize(Roles = "Administrator")]
[EnableCors(Constants.CorsPolicyName)]
public class UserController : ControllerBase public class UserController : ControllerBase
{ {

View File

@ -15,8 +15,8 @@ COPY ["BlueWest/BlueWest.csproj", "BlueWest/"]
RUN dotnet restore "BlueWest/BlueWest.csproj" RUN dotnet restore "BlueWest/BlueWest.csproj"
COPY ["BlueWest.Data.Geography/BlueWest.Data.Geography.csproj", "BlueWest.Data.Geography/"] COPY ["BlueWest.Data.Application/BlueWest.Data.Application.csproj", "BlueWest.Data.Application/"]
RUN dotnet restore "BlueWest.Data.Geography/BlueWest.Data.Geography.csproj" RUN dotnet restore "BlueWest.Data.Application/BlueWest.Data.Application.csproj"
COPY ["BlueWest.Api/BlueWest.Api.csproj", "BlueWest.Api/"] COPY ["BlueWest.Api/BlueWest.Api.csproj", "BlueWest.Api/"]

View File

@ -0,0 +1,9 @@
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
namespace BlueWest.WebApi;
public class GreeterService
{
}

View File

@ -0,0 +1,33 @@
using System.Threading;
using System.Threading.Tasks;
using BlueWest.Data.Application;
using Microsoft.Extensions.Hosting;
using Redis.OM;
namespace BlueWest.WebApi
{
public class IndexCreationDevice : IHostedService
{
private readonly RedisConnectionProvider _provider;
/// <summary>
/// Index Creation Device
/// </summary>
/// <param name="provider"></param>
public IndexCreationDevice(RedisConnectionProvider provider)
{
_provider = provider;
}
/// <inheritdoc />
public async Task StartAsync(CancellationToken cancellationToken)
{
await _provider.Connection.CreateIndexAsync(typeof(SessionToken)); }
/// <inheritdoc />
public Task StopAsync(CancellationToken cancellationToken)
{
return Task.CompletedTask; }
}
}

View File

@ -0,0 +1,13 @@
using Grpc.Core.Interceptors;
namespace BlueWest.WebApi.Interceptors
{
/// <summary>
/// Server Logger Interceptor
/// </summary>
public class ServerLoggerInterceptor : Interceptor
{
}
}

View File

@ -0,0 +1,16 @@
using BlueWest.Data.Application;
using BlueWest.WebApi.Context.Users;
namespace BlueWest.WebApi
{
public interface ISessionManager
{
bool IsAuthorized(string tokenHash);
SessionToken GetSessionToken(string hash, ApplicationUser applicationUser);
SessionToken GetSessionToken(LoginRequest loginRequest, ApplicationUser user);
}
}

View File

@ -0,0 +1,97 @@
using System;
using System.Linq;
using BlueWest.Cryptography;
using BlueWest.Data.Application;
using BlueWest.WebApi.Context;
using BlueWest.WebApi.Context.Users;
namespace BlueWest.WebApi
{
internal class SessionManager : ISessionManager
{
private readonly SessionDbContext _dbContex;
private readonly IHasher _hasher;
public SessionManager(SessionDbContext sessionDbContext, IHasher hasher)
{
_dbContex = sessionDbContext;
_hasher = hasher;
}
/// <summary>
/// Check if token is authorized
/// </summary>
/// <param name="hash"></param>
/// <returns></returns>
public bool IsAuthorized(string hash)
{
var sessionToken = _dbContex.SessionTokens.FirstOrDefault(x => x.Token == hash);
if (sessionToken is not {IsValid: true}) return false;
var expirationDate = sessionToken.CreatedDate.Add(sessionToken.ValidFor);
if (expirationDate >= DateTime.Now)
{
return true;
}
return false;
}
/// <summary>
/// Gets a new or existing session token
/// </summary>
/// <param name="hash"></param>
/// <param name="applicationUser"></param>
/// <returns></returns>
public SessionToken GetSessionToken(string hash, ApplicationUser applicationUser)
{
var sessionToken = _dbContex.SessionTokens.FirstOrDefault(x => x.Token == hash);
if (sessionToken == null)
{
// Create token;
var newToken = new SessionToken
{
Token = hash,
CreatedDate = DateTime.Now,
ValidFor = TimeSpan.FromDays(1),
UserId = applicationUser.Id
};
_dbContex.SessionTokens.Add(newToken);
var result = _dbContex.SaveChanges() >= 0;
return !result ? null : newToken;
}
var expirationDate = sessionToken.CreatedDate.Add(sessionToken.ValidFor);
if (expirationDate >= DateTime.Now)
{
return sessionToken;
}
return null;
}
/// <summary>
/// Gets a session token for the user following a login request
/// </summary>
/// <param name="loginRequest"></param>
/// <param name="user"></param>
/// <returns></returns>
public SessionToken GetSessionToken(LoginRequest loginRequest, ApplicationUser user)
{
var content = $"{loginRequest.Uuid}|{user.Email}";
var hash = _hasher.CreateHash(content, BaseCryptoItem.HashAlgorithm.SHA2_512);
var sessionToken = GetSessionToken(hash, user);
return sessionToken;
}
}
}

View File

@ -7,12 +7,11 @@ using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
using BlueWest.Tools; using BlueWest.Tools;
using BlueWest.WebApi.Context.Users; using BlueWest.WebApi.Interceptors;
using BlueWest.WebApi.Interfaces; using BlueWest.WebApi.Interfaces;
using BlueWest.WebApi.Tools; using BlueWest.WebApi.Tools;
using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Identity; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.FileProviders;
using Microsoft.OpenApi.Models; using Microsoft.OpenApi.Models;
namespace BlueWest.WebApi namespace BlueWest.WebApi
@ -24,6 +23,7 @@ namespace BlueWest.WebApi
{ {
private readonly IConfiguration _configuration; private readonly IConfiguration _configuration;
private readonly IWebHostEnvironment _environment; private readonly IWebHostEnvironment _environment;
private readonly string MyAllowSpecificOrigins = Constants.CorsPolicyName; private readonly string MyAllowSpecificOrigins = Constants.CorsPolicyName;
/// <summary> /// <summary>
@ -47,7 +47,7 @@ namespace BlueWest.WebApi
options.AddPolicy(name: MyAllowSpecificOrigins, options.AddPolicy(name: MyAllowSpecificOrigins,
builder => builder =>
{ {
builder.WithOrigins("http://127.0.0.1:5173", "http://localhost:5173", "http://127.0.0.1:5173") builder.WithOrigins("http://localhost:5173/", "http://localhost:5173", "http://127.0.0.1:5173", "localhost:5173")
.AllowAnyMethod() .AllowAnyMethod()
.AllowAnyHeader() .AllowAnyHeader()
.AllowCredentials(); .AllowCredentials();
@ -55,9 +55,27 @@ namespace BlueWest.WebApi
}); });
services services
.AddControllers() .AddResponseCaching()
.AddControllers(options =>
{
options.CacheProfiles.Add("Default30",
new CacheProfile()
{
Duration = 30
});
})
.AddJsonOptions(options => options.JsonSerializerOptions.ReferenceHandler = ReferenceHandler.Preserve); .AddJsonOptions(options => options.JsonSerializerOptions.ReferenceHandler = ReferenceHandler.Preserve);
services.AddLogging(builder =>
{
builder.AddSimpleConsole();
});
services.AddSession(options =>
{
options.Cookie.Domain = "http://localhost:5173";
options.Cookie.HttpOnly = true;
});
services services
.AddSwaggerGen(options => .AddSwaggerGen(options =>
{ {
@ -102,6 +120,13 @@ namespace BlueWest.WebApi
}); });
services.AddGrpc(options =>
{
options.Interceptors.Add<ServerLoggerInterceptor>();
});
services.AddSingleton<ServerLoggerInterceptor>();
/* /*
services.AddSingleton<IFileProvider>( services.AddSingleton<IFileProvider>(
new PhysicalFileProvider( new PhysicalFileProvider(
@ -120,9 +145,11 @@ namespace BlueWest.WebApi
services services
.AddSingleton<EventManager>(); .AddSingleton<EventManager>();
services.AddAuthServerServices(MyAllowSpecificOrigins, _configuration, _environment);
services.AddAuthServerServices( _configuration, _environment);
services.AddScoped<ExchangeInterface>(); services.AddScoped<ExchangeInterface>();
switch (allowedDatabase) switch (allowedDatabase)
{ {
case "mysql": case "mysql":
@ -138,10 +165,6 @@ namespace BlueWest.WebApi
} }
// services.AddGrpc();
} }
@ -156,19 +179,17 @@ namespace BlueWest.WebApi
//app.UseStaticFiles(); //app.UseStaticFiles();
app.UseDeveloperExceptionPage();
app.UseSwagger(); app.UseSwagger();
app.UseSwaggerUI(c => app.UseSwaggerUI(c =>
{ {
c.RoutePrefix = "swagger"; c.RoutePrefix = "swagger";
c.SwaggerEndpoint("/swagger/v1/swagger.json", "BlueWest.Api v1"); c.SwaggerEndpoint("/swagger/v1/swagger.json", "BlueWest.Api v1");
}); });
app.UseStaticFiles(); app.UseStaticFiles();
//app.UseHttpsRedirection(); //app.UseHttpsRedirection();
app.UseRouting(); app.UseRouting();
app.UseCors(MyAllowSpecificOrigins); app.UseCors(MyAllowSpecificOrigins);
app.UseAuthentication(); app.UseAuthentication();

View File

@ -10,6 +10,7 @@ using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
@ -80,7 +81,9 @@ namespace BlueWest.WebApi
.AddDbContextPool<CountryDbContext>(options => options.GetMySqlSettings(configuration, environment)) .AddDbContextPool<CountryDbContext>(options => options.GetMySqlSettings(configuration, environment))
.AddDbContextPool<FinanceDbContext>(options => options.GetMySqlSettings(configuration, environment)) .AddDbContextPool<FinanceDbContext>(options => options.GetMySqlSettings(configuration, environment))
.AddDbContextPool<CompanyDbContext>(options => options.GetMySqlSettings(configuration, environment)) .AddDbContextPool<CompanyDbContext>(options => options.GetMySqlSettings(configuration, environment))
.AddDbContextPool<ApplicationUserDbContext>(options => options.GetMySqlSettings(configuration, environment)); .AddDbContextPool<ApplicationUserDbContext>(options =>
options.GetMySqlSettings(configuration, environment))
.AddDbContextPool<SessionDbContext>(options => options.UseInMemoryDatabase("_s"));
} }
/// <summary> /// <summary>
@ -100,27 +103,24 @@ namespace BlueWest.WebApi
.AddDbContextPool<CountryDbContext>(options => options.UseSqlite(sqliteConString)) .AddDbContextPool<CountryDbContext>(options => options.UseSqlite(sqliteConString))
.AddDbContextPool<FinanceDbContext>(options => options.UseSqlite(sqliteConString)) .AddDbContextPool<FinanceDbContext>(options => options.UseSqlite(sqliteConString))
.AddDbContextPool<CompanyDbContext>(options => options.UseSqlite(sqliteConString)) .AddDbContextPool<CompanyDbContext>(options => options.UseSqlite(sqliteConString))
.AddDbContextPool<ApplicationUserDbContext>(options => options.UseSqlite(sqliteConString)); .AddDbContextPool<ApplicationUserDbContext>(options => options.UseSqlite(sqliteConString))
.AddDbContextPool<SessionDbContext>(options => options.UseInMemoryDatabase("_s"));
} }
internal static IServiceCollection AddAuthServerServices(this IServiceCollection services, string origins, IConfiguration configuration , IWebHostEnvironment environment) internal static IServiceCollection AddAuthServerServices(this IServiceCollection services, IConfiguration configuration , IWebHostEnvironment environment)
{ {
services.AddScoped<IJwtTokenHandler, JwtTokenHandler>(); services.AddScoped<IJwtTokenHandler, JwtTokenHandler>()
services.AddScoped<IJwtFactory, JwtFactory>(); .AddScoped<IJwtFactory, JwtFactory>()
.AddSingleton<IndexCreationDevice>()
.AddScoped<ISessionManager, SessionManager>()
services .AddScoped<UserRepository>()
.AddScoped< UserRepository>()
.AddScoped<IUserManager, ApplicationUserManager>() .AddScoped<IUserManager, ApplicationUserManager>()
.AddScoped<IAuthManager, AuthManager>() .AddScoped<IAuthManager, AuthManager>()
.AddScoped<IHasher, Hasher>(); .AddScoped<IHasher, Hasher>();
services
.AddIdentityCore<ApplicationUser>(opt => { opt.User.RequireUniqueEmail = true; })
.AddUserManager<ApplicationUserManager>()
.AddUserStore<UserRepository>();
// Database Context and Swagger // Database Context and Swagger
@ -162,6 +162,7 @@ namespace BlueWest.WebApi
services.AddAuthentication(options => services.AddAuthentication(options =>
{ {
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme; options.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
@ -170,6 +171,9 @@ namespace BlueWest.WebApi
}) })
.AddCookie(options => .AddCookie(options =>
{ {
options.Cookie.SameSite = SameSiteMode.Lax;
options.Cookie.SecurePolicy = CookieSecurePolicy.SameAsRequest;
options.Cookie.MaxAge = TimeSpan.FromDays(1);
options.LoginPath = "/api/auth/logincookie"; options.LoginPath = "/api/auth/logincookie";
options.LogoutPath = "/api/auth/logout"; options.LogoutPath = "/api/auth/logout";
}) })
@ -206,13 +210,17 @@ namespace BlueWest.WebApi
// add identity // add identity
var identityBuilder = services.AddIdentityCore<ApplicationUser>(o => var identityBuilder = services.AddIdentityCore<ApplicationUser>(o =>
{ {
o.User.RequireUniqueEmail = true;
// configure identity options // configure identity options
o.Password.RequireDigit = false; o.Password.RequireDigit = false;
o.Password.RequireLowercase = false; o.Password.RequireLowercase = false;
o.Password.RequireUppercase = false; o.Password.RequireUppercase = false;
o.Password.RequireNonAlphanumeric = false; o.Password.RequireNonAlphanumeric = false;
o.Password.RequiredLength = 6; o.Password.RequiredLength = 6;
}); })
.AddUserManager<ApplicationUserManager>()
.AddUserStore<UserRepository>();
identityBuilder = new IdentityBuilder(identityBuilder.UserType, typeof(ApplicationRole), identityBuilder.Services); identityBuilder = new IdentityBuilder(identityBuilder.UserType, typeof(ApplicationRole), identityBuilder.Services);
identityBuilder identityBuilder

View File

@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using BlueWest.Cryptography; using BlueWest.Cryptography;
using BlueWest.Data.Application;
using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
@ -16,12 +17,16 @@ internal class ApplicationUserManager : UserManager<ApplicationUser>, IUserManag
{ {
private readonly IHasher _hasher; private readonly IHasher _hasher;
private readonly UserRepository _usersRepo; private readonly UserRepository _usersRepo;
public ApplicationUserManager(UserRepository store, IOptions<IdentityOptions> optionsAccessor, public ApplicationUserManager(
IHasher passwordHasher, IEnumerable<IUserValidator<ApplicationUser>> userValidators, UserRepository store,
IEnumerable<IPasswordValidator<ApplicationUser>> passwordValidators, ILookupNormalizer keyNormalizer, IOptions<IdentityOptions> optionsAccessor,
IdentityErrorDescriber errors, IServiceProvider services, ILogger<UserManager<ApplicationUser>> logger) : base(store, IHasher passwordHasher,
optionsAccessor, passwordHasher, userValidators, passwordValidators, keyNormalizer, errors, services, IEnumerable<IUserValidator<ApplicationUser>> userValidators,
logger) IEnumerable<IPasswordValidator<ApplicationUser>> passwordValidators,
ILookupNormalizer keyNormalizer,
IdentityErrorDescriber errors,
IServiceProvider services,
ILogger<UserManager<ApplicationUser>> logger) : base(store, optionsAccessor, passwordHasher, userValidators, passwordValidators, keyNormalizer, errors, services, logger)
{ {
_hasher = passwordHasher; _hasher = passwordHasher;
_usersRepo = store; _usersRepo = store;
@ -49,6 +54,7 @@ internal class ApplicationUserManager : UserManager<ApplicationUser>, IUserManag
return success; return success;
} }
protected override async Task<PasswordVerificationResult> VerifyPasswordAsync(IUserPasswordStore<ApplicationUser> store, ApplicationUser user, string password) protected override async Task<PasswordVerificationResult> VerifyPasswordAsync(IUserPasswordStore<ApplicationUser> store, ApplicationUser user, string password)
{ {
string existingHash; string existingHash;
@ -90,6 +96,10 @@ internal class ApplicationUserManager : UserManager<ApplicationUser>, IUserManag
return IdentityResult.Failed(ErrorDescriber.PasswordMismatch()); return IdentityResult.Failed(ErrorDescriber.PasswordMismatch());
} }
public override Task<IdentityResult> SetAuthenticationTokenAsync(ApplicationUser user, string loginProvider, string tokenName, string tokenValue)
{
return base.SetAuthenticationTokenAsync(user, loginProvider, tokenName, tokenValue);
}
private IUserPasswordStore<ApplicationUser> GetPasswordStore() private IUserPasswordStore<ApplicationUser> GetPasswordStore()
{ {

View File

@ -1,14 +1,20 @@
using System;
using System.Security.Claims;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using BlueWest.Cryptography; using BlueWest.Cryptography;
using BlueWest.Data.Application;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Identity;
namespace BlueWest.WebApi.Context.Users; namespace BlueWest.WebApi.Context.Users;
internal class AuthManager : IAuthManager internal class AuthManager : IAuthManager
{ {
private readonly IUserManager _userManager; private readonly ApplicationUserManager _userManager;
private readonly UserRepository _usersRepo; private readonly UserRepository _usersRepo;
private readonly ISessionManager _sessionManager;
private readonly IHasher _hasher; private readonly IHasher _hasher;
private readonly IJwtFactory _jwtFactory; private readonly IJwtFactory _jwtFactory;
@ -19,51 +25,79 @@ internal class AuthManager : IAuthManager
/// <param name="hasher"></param> /// <param name="hasher"></param>
/// <param name="usersRepo"></param> /// <param name="usersRepo"></param>
/// <param name="jwtFactory"></param> /// <param name="jwtFactory"></param>
public AuthManager(IUserManager userManager, IHasher hasher, UserRepository usersRepo, IJwtFactory jwtFactory) public AuthManager(
ApplicationUserManager userManager,
IHasher hasher,
UserRepository usersRepo,
ISessionManager sessionManager,
IJwtFactory jwtFactory)
{ {
_userManager = userManager; _userManager = userManager;
_hasher = hasher; _hasher = hasher;
_usersRepo = usersRepo; _usersRepo = usersRepo;
_jwtFactory = jwtFactory; _jwtFactory = jwtFactory;
_sessionManager = sessionManager;
} }
/// <inheritdoc /> public async Task<(bool, ClaimsIdentity, SessionTokenUnique)> DoLogin(LoginRequest loginRequest)
public async Task<AccessToken> GetToken(LoginViewModel loginViewModel)
{ {
if (!string.IsNullOrEmpty(loginViewModel.Email) && !string.IsNullOrEmpty(loginViewModel.Password)) var user = await _userManager.FindByEmailAsync(loginRequest.Email);
{
var user = await _userManager.FindByEmailAsync(loginViewModel.Email);
if (user != null) if (user != null)
{ {
if (await VerifyLoginAsync(loginViewModel.Email,loginViewModel.Password)) if(await _userManager.CheckPasswordAsync(user, loginRequest.Password))
{ {
// Todo generate refresh token // Identity
// Todo Add refresh token var identity = new ClaimsIdentity(CookieAuthenticationDefaults.AuthenticationScheme);
await _usersRepo.UpdateAsync(user, CancellationToken.None); identity.AddClaim(new Claim(ClaimTypes.Email, user.Email));
var token = await _jwtFactory.GenerateEncodedToken(user.Id.ToString(), user.UserName);
// await _userManager.SetAuthenticationTokenAsync(user, "Income", "ApiUser", token.Token);
return token; // Session
} var sessionToken = _sessionManager.GetSessionToken(loginRequest, user);
var sessionResponse = new SessionTokenUnique(sessionToken);
return (true, identity, sessionResponse);
} }
} }
return null; return (false, null, null);
} }
/// <inheritdoc /> /// <inheritdoc />
public async Task<bool> VerifyLoginAsync(string email, string password) public async Task<(bool, SessionTokenUnique, AccessToken)> GetToken(LoginRequest loginRequest)
{
if (!string.IsNullOrEmpty(loginRequest.Email) && !string.IsNullOrEmpty(loginRequest.Password))
{
var user = await _userManager.FindByEmailAsync(loginRequest.Email);
if (user != null)
{
if (await VerifyLoginByEmailAsync(loginRequest.Email,loginRequest.Password))
{
await _usersRepo.UpdateAsync(user, CancellationToken.None);
// Session
var sessionToken = _sessionManager.GetSessionToken(loginRequest, user);
var sessionResponse = new SessionTokenUnique(sessionToken);
var token = await _jwtFactory.GenerateEncodedToken(user.Id, user.UserName);
var completed = await _userManager.SetAuthenticationTokenAsync(user, "ApiUser", "ApiUser", token.Token);
return (completed == IdentityResult.Success, sessionResponse, token);
}
}
}
return (false, null, null);
}
/// <inheritdoc />
public async Task<bool> VerifyLoginByEmailAsync(string email, string password)
{ {
var user = await _userManager.FindByEmailAsync(email); var user = await _userManager.FindByEmailAsync(email);
if (user == null) if (user == null)
{ {
return false; // return error user doesn't exist return false;
} }
return await _userManager.CheckPasswordAsync(user, password); return await _userManager.CheckPasswordAsync(user, password);
// return await GenerateAuthenticationResultForUserAsync(user);
} }
private RegisterViewModel FromSignupToUser(RegisterViewModel signupDto) private RegisterViewModel FromSignupToUser(RegisterViewModel signupDto)
@ -71,8 +105,6 @@ internal class AuthManager : IAuthManager
var pwd = signupDto.Password; var pwd = signupDto.Password;
var hash = _hasher.CreateHash(pwd, BaseCryptoItem.HashAlgorithm.SHA3_512); var hash = _hasher.CreateHash(pwd, BaseCryptoItem.HashAlgorithm.SHA3_512);
signupDto.Password = hash; signupDto.Password = hash;
signupDto.ConfirmPassword = hash;
return signupDto; return signupDto;
} }

View File

@ -20,7 +20,7 @@ namespace BlueWest.Cryptography
/// <param name="text"></param> /// <param name="text"></param>
/// <param name="algorithm"></param> /// <param name="algorithm"></param>
/// <returns></returns> /// <returns></returns>
public string CreateHash(string text, BaseCryptoItem.HashAlgorithm algorithm) public string CreateHash(string text, HashAlgorithm algorithm)
{ {
var salt = CreateRandomString(SaltLength); var salt = CreateRandomString(SaltLength);
return CreateHash(text, salt, algorithm, true); return CreateHash(text, salt, algorithm, true);

View File

@ -0,0 +1,12 @@
namespace BlueWest.Cryptography
{
public interface ISessionHasher
{
/// <summary>
/// Generates a token for the current session
/// </summary>
void GenerateSessionToken();
}
}

View File

@ -28,6 +28,8 @@ internal class JwtFactory : IJwtFactory
{ {
new Claim(JwtRegisteredClaimNames.Sub, userName), new Claim(JwtRegisteredClaimNames.Sub, userName),
new Claim(JwtRegisteredClaimNames.Jti, await _jwtOptions.JtiGenerator()), new Claim(JwtRegisteredClaimNames.Jti, await _jwtOptions.JtiGenerator()),
new Claim(JwtRegisteredClaimNames.Aud, _jwtOptions.Audience),
new Claim(JwtRegisteredClaimNames.Iat, ToUnixEpochDate(_jwtOptions.IssuedAt).ToString(), new Claim(JwtRegisteredClaimNames.Iat, ToUnixEpochDate(_jwtOptions.IssuedAt).ToString(),
ClaimValueTypes.Integer64), ClaimValueTypes.Integer64),
identity.FindFirst(JwtClaimIdentifiers.Rol), identity.FindFirst(JwtClaimIdentifiers.Rol),

View File

@ -1,4 +1,7 @@
using System;
using System.Security.Claims;
using System.Threading.Tasks; using System.Threading.Tasks;
using BlueWest.Data.Application;
using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Identity;
namespace BlueWest.WebApi.Context.Users; namespace BlueWest.WebApi.Context.Users;
@ -21,13 +24,20 @@ public interface IAuthManager
/// <param name="email"></param> /// <param name="email"></param>
/// <param name="password"></param> /// <param name="password"></param>
/// <returns></returns> /// <returns></returns>
Task<bool> VerifyLoginAsync(string email, string password); Task<bool> VerifyLoginByEmailAsync(string email, string password);
/// <summary> /// <summary>
/// GetToken /// GetToken
/// </summary> /// </summary>
/// <param name="loginViewModel"></param> /// <param name="loginRequest"></param>
/// <returns></returns> /// <returns></returns>
Task<AccessToken> GetToken(LoginViewModel loginViewModel); Task<(bool, SessionTokenUnique, AccessToken)> GetToken(LoginRequest loginRequest);
/// <summary>
/// Does Login
/// </summary>
/// <param name="loginRequest"></param>
/// <returns></returns>
Task<(bool, ClaimsIdentity, SessionTokenUnique)> DoLogin(LoginRequest loginRequest);
} }

View File

@ -1,4 +1,5 @@
using System.Threading.Tasks; using System.Threading.Tasks;
using BlueWest.Data.Application;
using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Identity;
namespace BlueWest.WebApi.Context.Users namespace BlueWest.WebApi.Context.Users
@ -26,6 +27,9 @@ namespace BlueWest.WebApi.Context.Users
/// <param name="email"></param> /// <param name="email"></param>
/// <returns></returns> /// <returns></returns>
Task<ApplicationUser> FindByEmailAsync(string email); Task<ApplicationUser> FindByEmailAsync(string email);
} }
} }

View File

@ -4,9 +4,9 @@ namespace BlueWest.WebApi.Context.Users
{ {
// from: https://github.com/dotnet/aspnetcore/tree/main/src/Identity/samples/IdentitySample.Mvc/Models/AccountViewModels // from: https://github.com/dotnet/aspnetcore/tree/main/src/Identity/samples/IdentitySample.Mvc/Models/AccountViewModels
/// <summary> /// <summary>
/// Login View Model /// Login Request adata
/// </summary> /// </summary>
public class LoginViewModel public class LoginRequest
{ {
/// <summary> /// <summary>
/// Email /// Email
@ -22,11 +22,9 @@ namespace BlueWest.WebApi.Context.Users
[DataType(DataType.Password)] [DataType(DataType.Password)]
public string Password { get; set; } public string Password { get; set; }
/// <summary> [Required]
/// RememberMe public string Uuid { get; set; }
/// </summary>
[Display(Name = "Remember me?")]
public bool RememberMe { get; set; }
} }
} }

View File

@ -36,6 +36,16 @@ public class RegisterViewModel
[Compare("Password", ErrorMessage = "The password and confirmation password do not match.")] [Compare("Password", ErrorMessage = "The password and confirmation password do not match.")]
public string ConfirmPassword { get; set; } public string ConfirmPassword { get; set; }
/// <summary>
/// ConfirmPassword
/// </summary>
[DataType(DataType.PhoneNumber)]
[Display(Name = "Phone Number")]
public string PhoneNumber { get; set; }
/// <summary> /// <summary>
/// Convert RegisterViewModel to ApplicationUser /// Convert RegisterViewModel to ApplicationUser
/// </summary> /// </summary>
@ -46,6 +56,7 @@ public class RegisterViewModel
newUser.Email = Email; newUser.Email = Email;
newUser.PasswordHash = Password; newUser.PasswordHash = Password;
newUser.UserName = Username; newUser.UserName = Username;
newUser.PhoneNumber = PhoneNumber;
return newUser; return newUser;
} }
} }

View File

@ -1,17 +0,0 @@
using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
namespace BlueWest.WebApi.Context.Users;
/// <summary>
/// Role storage management
/// </summary>
/*public class RoleStore : RoleStore<ApplicationRole>
{
}*/

View File

@ -10,7 +10,12 @@
"ConnectionStrings": { "ConnectionStrings": {
"DockerMySQL": "server=db;user=blueuser;password=dXjw127124dJ;database=bluedb;" "DockerMySQL": "server=db;user=blueuser;password=dXjw127124dJ;database=bluedb;"
}, },
"REDIS_CONNECTION_STRING": "redis://localhost:6379",
"AuthSettings": { "AuthSettings": {
"SecretKey": "iJWHDmHLpUA283sqsfhqGbMRdRj1PVkH" "SecretKey": "iJWHDmHLpUA283sqsfhqGbMRdRj1PVkH"
},
"JwtIssuerOptions": {
"Issuer": "SomeIssuer",
"Audience": "http://localhost:5000"
} }
} }

View File

@ -0,0 +1,25 @@
**/.dockerignore
**/.env
**/.git
**/.gitignore
**/.project
**/.settings
**/.toolstarget
**/.vs
**/.vscode
**/.idea
**/*.*proj.user
**/*.dbmdl
**/*.jfm
**/azds.yaml
**/bin
**/charts
**/docker-compose*
**/Dockerfile*
**/node_modules
**/npm-debug.log
**/obj
**/secrets.dev.yaml
**/values.dev.yaml
LICENSE
README.md

View File

@ -0,0 +1,11 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
</PropertyGroup>
</Project>

View File

@ -0,0 +1,18 @@
FROM mcr.microsoft.com/dotnet/runtime:6.0 AS base
WORKDIR /app
FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build
WORKDIR /src
COPY ["BlueWest.Console/BlueWest.Console.csproj", "BlueWest.Console/"]
RUN dotnet restore "BlueWest.Console/BlueWest.Console.csproj"
COPY . .
WORKDIR "/src/BlueWest.Console"
RUN dotnet build "BlueWest.Console.csproj" -c Release -o /app/build
FROM build AS publish
RUN dotnet publish "BlueWest.Console.csproj" -c Release -o /app/publish
FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "BlueWest.Console.dll"]

View File

@ -0,0 +1,25 @@
using System.Globalization;
/*var s = CultureInfo.GetCultures(CultureTypes.AllCultures);
foreach (var culture in s)
{
var actualCulture = culture;
Console.WriteLine(actualCulture);
}*/
async Task TaskReceiver(Func<Task> task)
{
Console.WriteLine("Hello");
await task.Invoke();
}
async Task Task2()
{
Console.WriteLine("World");
}
var f = Task2;
await TaskReceiver(f);

View File

@ -1,3 +1,4 @@
namespace BlueWest.WebApi.Context.Users namespace BlueWest.WebApi.Context.Users
{ {
public class AccessToken public class AccessToken

View File

@ -0,0 +1,14 @@
using System.Globalization;
using System.Net;
namespace BlueWest.Data.Application
{
public class ApplicationDevice
{
public string Id { get; set; }
public ClientOperatingSystem ClientOperatingSystem { get; set; }
public ClientPlatformType ClientPlatformType { get; set; }
public string Resolution { get; set; }
}
}

View File

@ -0,0 +1,14 @@
using System.ComponentModel.DataAnnotations;
namespace BlueWest.Data.Application
{
public class ApplicationDeviceCreate
{
[Required] public string Uuid;
public string Languages { get; set; }
public string UserAgent { get; set; }
public OperatingSystemType OperatingSystemType { get; set; }
}
}

View File

@ -0,0 +1,39 @@
namespace BlueWest.Data.Application
{
public enum ClientOperatingSystem
{
Unknown,
WindowsVista,
Windows7,
Windows8,
Windows81,
Windows10,
Windows2003,
WindowsXp,
WindowsPhone8,
Playstation,
Wii,
Linux,
Linux64,
Curl,
Bada,
Iphone,
Ipad,
ChromeOs,
Osxx,
OsxElCapitan,
OsxYosemite,
OsxMavericks,
OsxMountainLion,
OsxPuma,
OsxPanther,
OsxJaguar,
OsxCheetah,
OsxLeopard,
OsxSnowLeopard,
MacOsSierra,
OsxLion
}
}

View File

@ -0,0 +1,17 @@
namespace BlueWest.Data.Application;
public enum ClientPlatformType
{
MicrosoftWindows,
WindowsPhone,
IPhone,
Samsung,
Unknown,
Wii,
Linux,
Blackberry,
Playstation,
Ipad,
IPod,
Curl
}

View File

@ -0,0 +1,12 @@
namespace BlueWest.Data.Application
{
public enum OperatingSystemType
{
MacOS,
iOS,
Windows,
Android,
Linux
}
}

View File

@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema; using System.ComponentModel.DataAnnotations.Schema;
using BlueWest.Data; using BlueWest.Data;
using BlueWest.Data.Application;
using MapTo; using MapTo;
using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Identity;
@ -20,9 +21,7 @@ namespace BlueWest.WebApi.Context.Users
/// </summary> /// </summary>
[DatabaseGenerated(DatabaseGeneratedOption.Identity)] [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[PersonalData] [PersonalData]
public new string Id { get; set; } public override string Id { get; set; }
public List<User> Users { get; set; }
[ProtectedPersonalData] [ProtectedPersonalData]
public override string UserName { get; set; } public override string UserName { get; set; }
@ -97,5 +96,11 @@ namespace BlueWest.WebApi.Context.Users
/// Gets or sets the number of failed login attempts for the current user. /// Gets or sets the number of failed login attempts for the current user.
/// </summary> /// </summary>
public override int AccessFailedCount { get; set; } public override int AccessFailedCount { get; set; }
public List<SessionToken> SessionToken { get; set; }
public List<SessionData> SessionDatas { get; set; }
} }
} }

View File

@ -0,0 +1,9 @@
using Microsoft.AspNetCore.Identity;
namespace BlueWest.WebApi.Context.Users;
/// <inheritdoc />
public class ApplicationUserToken : IdentityUserToken<string>
{
public string Id { get; set; }
}

View File

@ -0,0 +1,26 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\include\BlueWest.MapTo\src\BlueWest.MapTo\BlueWest.MapTo.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
<ProjectReference Include="..\include\Math-Expression-Evaluator\SimpleExpressionEvaluator\SimpleExpressionEvaluator.csproj" />
</ItemGroup>
<ItemGroup>
<Content Include="obj\rider.project.restore.info" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Identity.Core" Version="6.0.8" />
<PackageReference Include="Microsoft.Extensions.Identity.Stores" Version="6.0.8" />
<PackageReference Include="Redis.OM" Version="0.2.2" />
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="6.10.0" />
</ItemGroup>
<Import Project="..\include\BlueWest.MapTo\src\BlueWest.MapTo\MapTo.props" />
</Project>

View File

@ -0,0 +1,12 @@
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using Microsoft.IdentityModel.Tokens;
namespace BlueWest.WebApi.Context.Users
{
public interface IJwtTokenHandler
{
string WriteToken(JwtSecurityToken jwt);
ClaimsPrincipal ValidateToken(string token, TokenValidationParameters tokenValidationParameters);
}
}

View File

@ -0,0 +1,6 @@
namespace BlueWest.WebApi.Context.Users;
internal interface ITokenFactory
{
string GenerateToken(int size= 32);
}

View File

@ -0,0 +1,54 @@
using System;
using System.Threading.Tasks;
using Microsoft.IdentityModel.Tokens;
namespace BlueWest.WebApi.Context.Users;
internal class JwtIssuerOptions
{
/// <summary>
/// 4.1.1. "iss" (Issuer) Claim - The "iss" (issuer) claim identifies the principal that issued the JWT.
/// </summary>
public string Issuer { get; set; }
/// <summary>
/// 4.1.2. "sub" (Subject) Claim - The "sub" (subject) claim identifies the principal that is the subject of the JWT.
/// </summary>
public string Subject { get; set; }
/// <summary>
/// 4.1.3. "aud" (Audience) Claim - The "aud" (audience) claim identifies the recipients that the JWT is intended for.
/// </summary>
public string Audience { get; set; }
/// <summary>
/// 4.1.4. "exp" (Expiration Time) Claim - The "exp" (expiration time) claim identifies the expiration time on or after which the JWT MUST NOT be accepted for processing.
/// </summary>
public DateTime Expiration => IssuedAt.Add(ValidFor);
/// <summary>
/// 4.1.5. "nbf" (Not Before) Claim - The "nbf" (not before) claim identifies the time before which the JWT MUST NOT be accepted for processing.
/// </summary>
public DateTime NotBefore => DateTime.UtcNow;
/// <summary>
/// 4.1.6. "iat" (Issued At) Claim - The "iat" (issued at) claim identifies the time at which the JWT was issued.
/// </summary>
public DateTime IssuedAt => DateTime.UtcNow;
/// <summary>
/// Set the timespan the token will be valid for (default is 120 min)
/// </summary>
public TimeSpan ValidFor { get; set; } = TimeSpan.FromMinutes(120);
/// <summary>
/// "jti" (JWT ID) Claim (default ID is a GUID)
/// </summary>
public Func<Task<string>> JtiGenerator =>
() => Task.FromResult(Guid.NewGuid().ToString());
/// <summary>
/// The signing key to use when generating tokens.
/// </summary>
public SigningCredentials SigningCredentials { get; set; }
}

View File

@ -0,0 +1,53 @@
using System;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using Microsoft.IdentityModel.Tokens;
namespace BlueWest.WebApi.Context.Users;
public class JwtTokenHandler : IJwtTokenHandler
{
private readonly JwtSecurityTokenHandler _jwtSecurityTokenHandler;
/// <summary>
/// JwtTokenHandler
/// </summary>
public JwtTokenHandler()
{
_jwtSecurityTokenHandler = new JwtSecurityTokenHandler();
}
/// <summary>
/// Write token
/// </summary>
/// <param name="jwt"></param>
/// <returns></returns>
public string WriteToken(JwtSecurityToken jwt)
{
return _jwtSecurityTokenHandler.WriteToken(jwt);
}
/// <summary>
/// Validate Token
/// </summary>
/// <param name="token"></param>
/// <param name="tokenValidationParameters"></param>
/// <returns></returns>
/// <exception cref="SecurityTokenException"></exception>
public ClaimsPrincipal ValidateToken(string token, TokenValidationParameters tokenValidationParameters)
{
try
{
var principal = _jwtSecurityTokenHandler.ValidateToken(token, tokenValidationParameters, out var securityToken);
if (!(securityToken is JwtSecurityToken jwtSecurityToken) || !jwtSecurityToken.Header.Alg.Equals(SecurityAlgorithms.HmacSha256, StringComparison.InvariantCultureIgnoreCase))
throw new SecurityTokenException("Invalid token");
return principal;
}
catch (Exception e)
{
return null;
}
}
}

View File

@ -0,0 +1,16 @@
using Redis.OM.Modeling;
namespace BlueWest.Data.Application
{
public class SessionCacheItem
{
public string UserId { get; set; }
[RedisIdField] [Indexed]
public string SessionTokenId { get; set; }
[Indexed]
public string AccessToken { get; set; }
}
}

View File

@ -0,0 +1,14 @@
using BlueWest.WebApi.Context.Users;
namespace BlueWest.Data.Application
{
public class SessionData
{
public int Id { get; set; }
public string UserId { get; set; }
public string SessionTokenId { get; set; }
public SessionToken SessionToken { get; set; }
public ApplicationUser User { get; set; }
}
}

View File

@ -0,0 +1,33 @@
using BlueWest.WebApi.Context.Users;
using MapTo;
namespace BlueWest.Data.Application
{
[MapFrom(new []
{
typeof(SessionTokenUnique)
})]
public partial class SessionToken
{
[IgnoreMemberMapTo]
public string Id { get; set; }
public string Token { get; set; }
public TimeSpan ValidFor { get; set;}
public bool IsValid { get; set; }
public DateTime CreatedDate { get; set; }
public ApplicationUser User { get; set; }
public string UserId { get; set; }
public ApplicationDevice ApplicationDevice { get; set; }
public SessionData SessionData { get; set; }
public string AccessToken { get; set; }
}
}

View File

@ -0,0 +1,17 @@
using MapTo;
namespace BlueWest.Data.Application
{
[MapFrom(typeof(SessionToken))]
public partial class SessionTokenUnique
{
public string Id { get; set; }
public string Token { get; set; }
public TimeSpan ValidFor { get; set;}
public DateTime CreatedDate { get; set; }
public string UserId { get; set; }
}
}

View File

@ -1,6 +0,0 @@
using Microsoft.AspNetCore.Identity;
namespace BlueWest.WebApi.Context.Users;
/// <inheritdoc />
public class ApplicationUserToken : IdentityUserToken<string> { }

View File

@ -17,11 +17,12 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.Extensions.Identity.Core" Version="6.0.7" /> <PackageReference Include="Microsoft.Extensions.Identity.Core" Version="6.0.8" />
<PackageReference Include="Microsoft.Extensions.Identity.Stores" Version="6.0.7" /> <PackageReference Include="Microsoft.Extensions.Identity.Stores" Version="6.0.8" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Folder Include="Application" />
<Folder Include="Capital" /> <Folder Include="Capital" />
</ItemGroup> </ItemGroup>
<Import Project="..\include\BlueWest.MapTo\src\BlueWest.MapTo\MapTo.props" /> <Import Project="..\include\BlueWest.MapTo\src\BlueWest.MapTo\MapTo.props" />

@ -1 +1 @@
Subproject commit 2afc7cc1eb60369db3d2db021d514647baba93dc Subproject commit b67ba4a1a6bb8a58ee44b34dbadbd6a0e99c5c87

View File

@ -0,0 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>

View File

@ -0,0 +1,8 @@
namespace BlueWest.Posts
{
public class PostType
{
}
}

View File

@ -36,6 +36,12 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BlueWest.Api.Gateway", "Blu
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BlueWest.EfMethods", "include\BlueWest.EfMethods\src\BlueWest.EfMethods\BlueWest.EfMethods.csproj", "{BBF5E860-A880-450B-B6C9-EF92F6421B3D}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BlueWest.EfMethods", "include\BlueWest.EfMethods\src\BlueWest.EfMethods\BlueWest.EfMethods.csproj", "{BBF5E860-A880-450B-B6C9-EF92F6421B3D}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BlueWest.Data.Application", "BlueWest.Data.Application\BlueWest.Data.Application.csproj", "{F0F4A1F3-E279-4374-B146-1E1D82011574}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BlueWest.Console", "BlueWest.Console\BlueWest.Console.csproj", "{D50ED30A-B560-4D00-8D56-653F994045CE}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BlueWest.Posts", "BlueWest.Posts\BlueWest.Posts.csproj", "{78540D1E-240F-41FF-B8ED-A265DD5ADCB5}"
EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU Debug|Any CPU = Debug|Any CPU
@ -78,6 +84,18 @@ Global
{BBF5E860-A880-450B-B6C9-EF92F6421B3D}.Debug|Any CPU.Build.0 = Debug|Any CPU {BBF5E860-A880-450B-B6C9-EF92F6421B3D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{BBF5E860-A880-450B-B6C9-EF92F6421B3D}.Release|Any CPU.ActiveCfg = Release|Any CPU {BBF5E860-A880-450B-B6C9-EF92F6421B3D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{BBF5E860-A880-450B-B6C9-EF92F6421B3D}.Release|Any CPU.Build.0 = Release|Any CPU {BBF5E860-A880-450B-B6C9-EF92F6421B3D}.Release|Any CPU.Build.0 = Release|Any CPU
{F0F4A1F3-E279-4374-B146-1E1D82011574}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F0F4A1F3-E279-4374-B146-1E1D82011574}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F0F4A1F3-E279-4374-B146-1E1D82011574}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F0F4A1F3-E279-4374-B146-1E1D82011574}.Release|Any CPU.Build.0 = Release|Any CPU
{D50ED30A-B560-4D00-8D56-653F994045CE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D50ED30A-B560-4D00-8D56-653F994045CE}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D50ED30A-B560-4D00-8D56-653F994045CE}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D50ED30A-B560-4D00-8D56-653F994045CE}.Release|Any CPU.Build.0 = Release|Any CPU
{78540D1E-240F-41FF-B8ED-A265DD5ADCB5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{78540D1E-240F-41FF-B8ED-A265DD5ADCB5}.Debug|Any CPU.Build.0 = Debug|Any CPU
{78540D1E-240F-41FF-B8ED-A265DD5ADCB5}.Release|Any CPU.ActiveCfg = Release|Any CPU
{78540D1E-240F-41FF-B8ED-A265DD5ADCB5}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE

View File

@ -1,5 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace BlueWest.Tools namespace BlueWest.Tools
{ {

View File

@ -42,7 +42,7 @@ namespace PerformanceSolution
void StartServer() void StartServer()
{ {
string ip = "127.0.0.1"; string ip = "127.0.0-preview.7.22376.2.1";
int port = 80; int port = 80;
var server = new TcpListener(IPAddress.Parse(ip), port); var server = new TcpListener(IPAddress.Parse(ip), port);

View File

@ -27,7 +27,7 @@ services:
context: ./ context: ./
dockerfile: ./BlueWest.Api/Dockerfile dockerfile: ./BlueWest.Api/Dockerfile
ports: ports:
- 8080:80 - "8080:80"
environment: environment:
VIRTUAL_HOST: localhost VIRTUAL_HOST: localhost
restart: always restart: always
@ -35,4 +35,11 @@ services:
- db:db - db:db
container_name: BW1_API container_name: BW1_API
redis:
image: "redis:alpine"
command: redis-server --requirepass Sup3rSecurePass0rd
ports:
- "6379:6379"
environment:
- REDIS_REPLICATION_MODE=master