From 3b8d82049f088198bc3cbc44aed9086491ac9c35 Mon Sep 17 00:00:00 2001
From: Wvader <34067397+wvader@users.noreply.github.com>
Date: Sat, 17 Sep 2022 20:13:35 +0100
Subject: [PATCH] Session wip
---
BlueWest.Api/BlueWest.Api.csproj | 11 +-
.../Context/ApplicationUserDbContext.cs | 62 +-------
.../Extensions/ModelBuilderExtensions.cs | 140 ++++++++++++------
BlueWest.Api/Context/SessionDbContext.cs | 30 ++++
.../SwaggerEnumSchemaFilter.cs | 0
.../Controllers/ApplicationController.cs | 21 +++
.../Controllers/ApplicationUserController.cs | 20 ++-
BlueWest.Api/Controllers/AuthController.cs | 97 ++++++++----
BlueWest.Api/Controllers/CountryController.cs | 3 +-
.../Controllers/CurrencyController.cs | 3 +
BlueWest.Api/Controllers/FinanceController.cs | 5 +-
BlueWest.Api/Controllers/UserController.cs | 6 +-
BlueWest.Api/Dockerfile | 4 +-
BlueWest.Api/Services/GreeterService.cs | 9 ++
BlueWest.Api/Services/IndexCreationDevice.cs | 33 +++++
.../Interceptors/ServerLoggerInterceptor.cs | 13 ++
BlueWest.Api/Session/ISessionManager.cs | 16 ++
BlueWest.Api/Session/SessionManager.cs | 97 ++++++++++++
BlueWest.Api/Startup.cs | 53 +++++--
BlueWest.Api/StartupExtensions.cs | 38 +++--
BlueWest.Api/Users/ApplicationUserManager.cs | 24 ++-
BlueWest.Api/Users/Auth/AuthManager.cs | 68 ++++++---
.../{ => Users/Auth}/Crypto/BaseCryptoItem.cs | 0
.../{ => Users/Auth}/Crypto/Hasher.cs | 2 +-
.../{ => Users/Auth}/Crypto/IHasher.cs | 0
.../Jwt => Users/Auth/Crypto}/IJwtFactory.cs | 0
.../Auth/Crypto}/IJwtTokenHandler.cs | 0
.../Users/Auth/Crypto/ISessionHasher.cs | 12 ++
.../Auth/Crypto}/ITokenFactory.cs | 0
.../Jwt => Users/Auth/Crypto}/JwtFactory.cs | 2 +
.../Auth/Crypto}/JwtIssuerOptions.cs | 0
.../Auth/Crypto}/JwtTokenHandler.cs | 0
.../{ => Users/Auth}/Crypto/SHA_512.cs | 0
BlueWest.Api/Users/Auth/IAuthManager.cs | 16 +-
BlueWest.Api/Users/IUserManager.cs | 4 +
.../{LoginViewModel.cs => LoginRequest.cs} | 12 +-
.../Users/Models/RegisterViewModel.cs | 11 ++
BlueWest.Api/Users/RoleStore.cs | 17 ---
BlueWest.Api/appsettings.json | 5 +
BlueWest.Console/.dockerignore | 25 ++++
BlueWest.Console/BlueWest.Console.csproj | 11 ++
BlueWest.Console/Dockerfile | 18 +++
BlueWest.Console/Program.cs | 25 ++++
.../AccessToken.cs | 1 +
.../ApplicationDevice/ApplicationDevice.cs | 14 ++
.../ApplicationDeviceCreate.cs | 14 ++
.../ClientOperatingSystem.cs | 39 +++++
.../ApplicationDevice/ClientPlatformType.cs | 17 +++
.../ApplicationDevice/OperatingSystemType.cs | 12 ++
.../ApplicationRole/ApplicationRole.cs | 0
.../ApplicationRole/ApplicationRoleUnique.cs | 0
.../ApplicationRoleClaim.cs | 0
.../ApplicationRoleClaimUnique.cs | 0
.../ApplicationUser/ApplicationUser.cs | 13 +-
.../ApplicationUser/ApplicationUserUnique.cs | 0
.../ApplicationUserClaim.cs | 0
.../ApplicationUserClaimUnique.cs | 0
.../ApplicationUserLogin.cs | 0
.../ApplicationUserRole.cs | 0
.../ApplicationUserRoleUnique.cs | 0
.../ApplicationUserToken.cs | 9 ++
.../BlueWest.Data.Application.csproj | 26 ++++
.../Crypto/Jwt/IJwtTokenHandler.cs | 12 ++
.../Crypto/Jwt/ITokenFactory.cs | 6 +
.../Crypto/Jwt/JwtIssuerOptions.cs | 54 +++++++
.../Crypto/Jwt/JwtTokenHandler.cs | 53 +++++++
.../SessionToken/SessionCacheItem.cs | 16 ++
.../SessionToken/SessionData.cs | 14 ++
.../SessionToken/SessionToken.cs | 33 +++++
.../SessionToken/SessionTokenUnique.cs | 17 +++
.../ApplicationUserToken.cs | 6 -
.../BlueWest.Data.Capital.csproj | 5 +-
BlueWest.Frontend | 2 +-
BlueWest.Posts/BlueWest.Posts.csproj | 9 ++
BlueWest.Posts/PostType.cs | 8 +
BlueWest.sln | 18 +++
BlueWest/Core/Events/EventManager.cs | 6 +-
.../Core/Tests/BlueSocketServerSingleton.cs | 2 +-
docker-compose.yml | 11 +-
79 files changed, 1086 insertions(+), 244 deletions(-)
create mode 100644 BlueWest.Api/Context/SessionDbContext.cs
rename BlueWest.Api/{Controllers => Context}/SwaggerEnumSchemaFilter.cs (100%)
create mode 100644 BlueWest.Api/Controllers/ApplicationController.cs
create mode 100644 BlueWest.Api/Services/GreeterService.cs
create mode 100644 BlueWest.Api/Services/IndexCreationDevice.cs
create mode 100644 BlueWest.Api/Services/Interceptors/ServerLoggerInterceptor.cs
create mode 100644 BlueWest.Api/Session/ISessionManager.cs
create mode 100644 BlueWest.Api/Session/SessionManager.cs
rename BlueWest.Api/{ => Users/Auth}/Crypto/BaseCryptoItem.cs (100%)
rename BlueWest.Api/{ => Users/Auth}/Crypto/Hasher.cs (97%)
rename BlueWest.Api/{ => Users/Auth}/Crypto/IHasher.cs (100%)
rename BlueWest.Api/{Crypto/Jwt => Users/Auth/Crypto}/IJwtFactory.cs (100%)
rename BlueWest.Api/{Crypto/Jwt => Users/Auth/Crypto}/IJwtTokenHandler.cs (100%)
create mode 100644 BlueWest.Api/Users/Auth/Crypto/ISessionHasher.cs
rename BlueWest.Api/{Crypto/Jwt => Users/Auth/Crypto}/ITokenFactory.cs (100%)
rename BlueWest.Api/{Crypto/Jwt => Users/Auth/Crypto}/JwtFactory.cs (97%)
rename BlueWest.Api/{Crypto/Jwt => Users/Auth/Crypto}/JwtIssuerOptions.cs (100%)
rename BlueWest.Api/{Crypto/Jwt => Users/Auth/Crypto}/JwtTokenHandler.cs (100%)
rename BlueWest.Api/{ => Users/Auth}/Crypto/SHA_512.cs (100%)
rename BlueWest.Api/Users/Models/{LoginViewModel.cs => LoginRequest.cs} (73%)
delete mode 100644 BlueWest.Api/Users/RoleStore.cs
create mode 100644 BlueWest.Console/.dockerignore
create mode 100644 BlueWest.Console/BlueWest.Console.csproj
create mode 100644 BlueWest.Console/Dockerfile
create mode 100644 BlueWest.Console/Program.cs
rename {BlueWest.Api/Users/Auth => BlueWest.Data.Application}/AccessToken.cs (99%)
create mode 100644 BlueWest.Data.Application/ApplicationDevice/ApplicationDevice.cs
create mode 100644 BlueWest.Data.Application/ApplicationDevice/ApplicationDeviceCreate.cs
create mode 100644 BlueWest.Data.Application/ApplicationDevice/ClientOperatingSystem.cs
create mode 100644 BlueWest.Data.Application/ApplicationDevice/ClientPlatformType.cs
create mode 100644 BlueWest.Data.Application/ApplicationDevice/OperatingSystemType.cs
rename {BlueWest.Data.Capital/Application => BlueWest.Data.Application}/ApplicationRole/ApplicationRole.cs (100%)
rename {BlueWest.Data.Capital/Application => BlueWest.Data.Application}/ApplicationRole/ApplicationRoleUnique.cs (100%)
rename {BlueWest.Data.Capital/Application => BlueWest.Data.Application}/ApplicationRoleClaim/ApplicationRoleClaim.cs (100%)
rename {BlueWest.Data.Capital/Application => BlueWest.Data.Application}/ApplicationRoleClaim/ApplicationRoleClaimUnique.cs (100%)
rename {BlueWest.Data.Capital/Application => BlueWest.Data.Application}/ApplicationUser/ApplicationUser.cs (93%)
rename {BlueWest.Data.Capital/Application => BlueWest.Data.Application}/ApplicationUser/ApplicationUserUnique.cs (100%)
rename {BlueWest.Data.Capital/Application => BlueWest.Data.Application}/ApplicationUserClaim/ApplicationUserClaim.cs (100%)
rename {BlueWest.Data.Capital/Application => BlueWest.Data.Application}/ApplicationUserClaim/ApplicationUserClaimUnique.cs (100%)
rename {BlueWest.Data.Capital/Application => BlueWest.Data.Application}/ApplicationUserLogin/ApplicationUserLogin.cs (100%)
rename {BlueWest.Data.Capital/Application => BlueWest.Data.Application}/ApplicationUserRole/ApplicationUserRole.cs (100%)
rename {BlueWest.Data.Capital/Application => BlueWest.Data.Application}/ApplicationUserRole/ApplicationUserRoleUnique.cs (100%)
create mode 100644 BlueWest.Data.Application/ApplicationUserToken/ApplicationUserToken.cs
create mode 100644 BlueWest.Data.Application/BlueWest.Data.Application.csproj
create mode 100644 BlueWest.Data.Application/Crypto/Jwt/IJwtTokenHandler.cs
create mode 100644 BlueWest.Data.Application/Crypto/Jwt/ITokenFactory.cs
create mode 100644 BlueWest.Data.Application/Crypto/Jwt/JwtIssuerOptions.cs
create mode 100644 BlueWest.Data.Application/Crypto/Jwt/JwtTokenHandler.cs
create mode 100644 BlueWest.Data.Application/SessionToken/SessionCacheItem.cs
create mode 100644 BlueWest.Data.Application/SessionToken/SessionData.cs
create mode 100644 BlueWest.Data.Application/SessionToken/SessionToken.cs
create mode 100644 BlueWest.Data.Application/SessionToken/SessionTokenUnique.cs
delete mode 100644 BlueWest.Data.Capital/Application/ApplicationUserToken/ApplicationUserToken.cs
create mode 100644 BlueWest.Posts/BlueWest.Posts.csproj
create mode 100644 BlueWest.Posts/PostType.cs
diff --git a/BlueWest.Api/BlueWest.Api.csproj b/BlueWest.Api/BlueWest.Api.csproj
index fd088be..ae93c5c 100644
--- a/BlueWest.Api/BlueWest.Api.csproj
+++ b/BlueWest.Api/BlueWest.Api.csproj
@@ -12,16 +12,18 @@
-
+
+
-
-
+
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
+
-
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
@@ -42,6 +44,7 @@
+
diff --git a/BlueWest.Api/Context/ApplicationUserDbContext.cs b/BlueWest.Api/Context/ApplicationUserDbContext.cs
index 74f98b9..d3c3968 100644
--- a/BlueWest.Api/Context/ApplicationUserDbContext.cs
+++ b/BlueWest.Api/Context/ApplicationUserDbContext.cs
@@ -25,25 +25,24 @@ namespace BlueWest.WebApi.Context
[EfGetMany(typeof(ApplicationUserClaimUnique))]
public sealed override DbSet UserClaims { get; set; }
- [EfGetMany(typeof(ApplicationUserRoleUnique))]
///
+ [EfGetMany(typeof(ApplicationUserRoleUnique))]
public sealed override DbSet UserRoles { get; set; }
- [EfGetMany(typeof(ApplicationRoleUnique))]
///
+ [EfGetMany(typeof(ApplicationRoleUnique))]
public sealed override DbSet Roles { get; set; }
- [EfGetMany(typeof(ApplicationRoleClaimUnique))]
///
+ [EfGetMany(typeof(ApplicationRoleClaimUnique))]
public sealed override DbSet RoleClaims { get; set; }
[EfGetMany(typeof(ApplicationUserUnique))]
[EfUpdateMethods( updateType: typeof(ApplicationUserUnique), returnType: typeof(ApplicationUserUnique), keyPropertyMemberName: nameof(ApplicationUserUnique.Id))]
public sealed override DbSet Users { get; set; }
- #region Initialization
-
///
+ #region Initialization
public ApplicationUserDbContext(DbContextOptions options) : base(options)
{
Database.EnsureCreated();
@@ -55,62 +54,13 @@ namespace BlueWest.WebApi.Context
{
base.OnModelCreating(builder);
- builder.Entity().ToTable("UserRoles");
- builder.Entity(b =>
- {
- b.HasMany()
- .WithOne()
- .HasForeignKey(ur => ur.UserId).IsRequired();
- });
-
- builder.Entity().ToTable("ApplicationUser")
- .HasKey(x => x.Id);
-
-
- builder.Entity(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().WithOne().HasForeignKey(ur => ur.RoleId).IsRequired();
- b.HasMany().WithOne().HasForeignKey(rc => rc.RoleId).IsRequired();
- });
-
- builder.Entity().HasOne(x => x.ApplicationRole);
- builder.Entity().HasOne(x => x.ApplicationRole);
- builder.Entity().HasOne(x => x.ApplicationUser);
-
-
- builder.Entity(b =>
- {
- b.HasKey(rc => rc.Id);
- b.ToTable("RoleClaims");
- });
-
- builder.Entity(b =>
- {
- b.HasKey(r => new {r.UserId, r.RoleId});
- b.ToTable("UserRoles");
- });
-
- builder.Entity(b => b.HasOne()
- .WithMany(x => x.Users)
- .HasForeignKey(x => x.ApplicationUserId));
-
- builder.Entity().ToTable("RoleClaims");
- builder.Entity().ToTable("UserRole");
-
builder.ConfigureCurrentDbModel();
+
+
}
#endregion
- ///
}
}
\ No newline at end of file
diff --git a/BlueWest.Api/Context/Extensions/ModelBuilderExtensions.cs b/BlueWest.Api/Context/Extensions/ModelBuilderExtensions.cs
index 46012ca..d76dfac 100644
--- a/BlueWest.Api/Context/Extensions/ModelBuilderExtensions.cs
+++ b/BlueWest.Api/Context/Extensions/ModelBuilderExtensions.cs
@@ -1,4 +1,5 @@
using BlueWest.Data;
+using BlueWest.Data.Application;
using BlueWest.WebApi.Context.Users;
using Microsoft.EntityFrameworkCore;
@@ -11,9 +12,7 @@ namespace BlueWest.WebApi.EF.Model
{
#region Initialization
- static ModelBuilderExtensions()
- {
- }
+ static ModelBuilderExtensions() { }
///
/// Setup the database model
@@ -24,11 +23,105 @@ namespace BlueWest.WebApi.EF.Model
modelBuilder
.ConfigureDatabaseKeys()
.CurrencyModel()
- .ConfigureUserModel();
+ .ConfigureUserModel()
+ .ConfigureAppContextModel();
//.ConfigureIdentityModel();
}
+
+ #endregion
+
+ #region Application Users
+
+ ///
+ /// Configure App context model
+ ///
+ ///
+ private static void ConfigureAppContextModel(this ModelBuilder builder)
+ {
+ builder.Entity().ToTable("UserRoles");
+ builder.Entity(b =>
+ {
+ b.HasMany()
+ .WithOne(b => b.User)
+ .HasForeignKey(ur => ur.UserId).IsRequired();
+ });
+
+
+ builder.Entity().ToTable("ApplicationUser")
+ .HasKey(x => x.Id);
+
+
+ builder.Entity(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().WithOne().HasForeignKey(ur => ur.RoleId).IsRequired();
+ b.HasMany().WithOne().HasForeignKey(rc => rc.RoleId).IsRequired();
+ });
+
+ builder.Entity().HasOne(x => x.ApplicationRole);
+ builder.Entity().HasOne(x => x.ApplicationRole);
+ builder.Entity().HasOne(x => x.ApplicationUser);
+
+
+ builder.Entity(b =>
+ {
+ b.HasKey(rc => rc.Id);
+ b.ToTable("RoleClaims");
+ });
+
+ builder.Entity(b =>
+ {
+ b.HasKey(r => new {r.UserId, r.RoleId});
+ b.ToTable("UserRoles");
+ });
+
+ builder.Entity().ToTable("RoleClaims");
+ builder.Entity().ToTable("UserRole");
+
+ // Session Token
+ builder.Entity()
+ .HasOne(b => b.User)
+ .WithMany(x => x.SessionToken)
+ .HasForeignKey(x => x.UserId);
+
+ // Session Token Primary Key
+ builder.Entity(b =>
+ b.HasKey(x => x.Id));
+ builder.Entity(b =>
+ b.Property(x => x.Id).ValueGeneratedOnAdd());
+
+ // Session Data
+ builder.Entity()
+ .HasOne(b => b.User)
+ .WithMany(x => x.SessionDatas)
+ .HasForeignKey(x => x.UserId);
+
+ // Session Data
+ builder.Entity()
+ .HasOne(b => b.SessionToken)
+ .WithOne(x => x.SessionData);
+
+
+
+ // Session Data Primary Key
+ builder.Entity(b =>
+ b.HasKey(x => x.Id));
+ builder.Entity(b =>
+ b.Property(x => x.Id).ValueGeneratedOnAdd());
+
+
+ //.ConfigureIdentityModel();
+ }
+
#endregion
#region DatabasePK
@@ -121,48 +214,11 @@ namespace BlueWest.WebApi.EF.Model
.WithMany(ft => ft.FinanceTransactions)
.HasForeignKey(x => x.UserId));
- modelBuilder.Entity(b => b.HasOne()
- .WithMany(x => x.Users)
- .HasForeignKey(x => x.ApplicationUserId));
-
return modelBuilder;
}
#endregion
-
- public static void ConfigureIdentityModel(this ModelBuilder builder)
- {
- builder.Entity(b =>
- {
- b.HasMany().WithOne().HasForeignKey(ur => ur.UserId).IsRequired();
- });
-
- builder.Entity(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().WithOne().HasForeignKey(ur => ur.RoleId).IsRequired();
- b.HasMany().WithOne().HasForeignKey(rc => rc.RoleId).IsRequired();
- });
-
- builder.Entity(b =>
- {
- b.HasKey(rc => rc.Id);
- b.ToTable("RoleClaims");
- });
-
- builder.Entity(b =>
- {
- b.HasKey(r => new { r.UserId, r.RoleId });
- b.ToTable("UserRoles");
- });
- }
+
}
}
diff --git a/BlueWest.Api/Context/SessionDbContext.cs b/BlueWest.Api/Context/SessionDbContext.cs
new file mode 100644
index 0000000..8a2eaba
--- /dev/null
+++ b/BlueWest.Api/Context/SessionDbContext.cs
@@ -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
+ {
+ ///
+ /// CountryDbContext Constructor.
+ ///
+ ///
+
+ public SessionDbContext(DbContextOptions options) : base(options)
+ {
+ Database.EnsureCreated();
+ }
+
+
+ protected override void OnModelCreating(ModelBuilder modelBuilder)
+ {
+ base.OnModelCreating(modelBuilder);
+ modelBuilder.ConfigureCurrentDbModel();
+ }
+
+ public DbSet SessionTokens { get; set; }
+ }
+}
diff --git a/BlueWest.Api/Controllers/SwaggerEnumSchemaFilter.cs b/BlueWest.Api/Context/SwaggerEnumSchemaFilter.cs
similarity index 100%
rename from BlueWest.Api/Controllers/SwaggerEnumSchemaFilter.cs
rename to BlueWest.Api/Context/SwaggerEnumSchemaFilter.cs
diff --git a/BlueWest.Api/Controllers/ApplicationController.cs b/BlueWest.Api/Controllers/ApplicationController.cs
new file mode 100644
index 0000000..abf8b4a
--- /dev/null
+++ b/BlueWest.Api/Controllers/ApplicationController.cs
@@ -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());
+}
\ No newline at end of file
diff --git a/BlueWest.Api/Controllers/ApplicationUserController.cs b/BlueWest.Api/Controllers/ApplicationUserController.cs
index 0375d38..516d423 100644
--- a/BlueWest.Api/Controllers/ApplicationUserController.cs
+++ b/BlueWest.Api/Controllers/ApplicationUserController.cs
@@ -1,5 +1,6 @@
using BlueWest.Data;
using BlueWest.WebApi.Context;
+using BlueWest.WebApi.Context.Users;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Authorization;
@@ -9,6 +10,7 @@ using Microsoft.AspNetCore.Mvc;
namespace BlueWest.WebApi.Controllers
{
+ ///
[ApiController]
[Route("application/users")]
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
@@ -19,6 +21,7 @@ namespace BlueWest.WebApi.Controllers
{
private readonly ApplicationUserDbContext _dbContext;
+ ///
public ApplicationUserController(ApplicationUserDbContext context)
{
_dbContext = context;
@@ -54,16 +57,15 @@ namespace BlueWest.WebApi.Controllers
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
[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)
{
- return Ok(User);
+ return Ok(updatedUser);
}
- */
+
return new NotFoundResult();
}
@@ -124,8 +126,14 @@ namespace BlueWest.WebApi.Controllers
}
}
+///
+/// Application Constants
+///
public static class Constants
{
+ ///
+ /// Policy Name
+ ///
public const string CorsPolicyName = "_myAllowSpecificOrigins";
}
diff --git a/BlueWest.Api/Controllers/AuthController.cs b/BlueWest.Api/Controllers/AuthController.cs
index 14ba787..7884191 100644
--- a/BlueWest.Api/Controllers/AuthController.cs
+++ b/BlueWest.Api/Controllers/AuthController.cs
@@ -1,5 +1,8 @@
+using System;
using System.Security.Claims;
using System.Threading.Tasks;
+using BlueWest.Cryptography;
+using BlueWest.Data.Application;
using BlueWest.WebApi.Context.Users;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies;
@@ -14,25 +17,28 @@ namespace BlueWest.WebApi.Controllers;
///
/// Auth controller
///
-[Route("api/[controller]")]
- [Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
- [Authorize(AuthenticationSchemes = CookieAuthenticationDefaults.AuthenticationScheme)]
[ApiController]
-[EnableCors(Constants.CorsPolicyName)]
-public class AuthController : Controller
+ [Route("api/[controller]")]
+
+ [Authorize(Policy = "ApiUser")]
+
+/*[EnableCors(Constants.CorsPolicyName)]*/
+ public class AuthController : Controller
{
private readonly IAuthManager _authManager;
private readonly IUserManager _userManager;
+ private readonly ISessionManager _sessionManager;
///
///
///
///
///
- public AuthController( IAuthManager authManager, IUserManager userManager)
+ public AuthController( IAuthManager authManager, IUserManager userManager, ISessionManager sessionManager)
{
_authManager = authManager;
_userManager = userManager;
+ _sessionManager = sessionManager;
}
@@ -49,25 +55,60 @@ public class AuthController : Controller
}
+
///
/// Gets a bearer token
///
///
///
[AllowAnonymous]
- [HttpPost("login")]
- public async Task> GetTokenAsync(LoginViewModel loginViewModel)
+ [HttpPost("token")]
+ public async Task> 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();
}
+
+ ///
+ /// Check if user is logged in
+ ///
+ ///
+ [HttpGet("isLoggedIn")]
+
+ public ActionResult IsLoggedIn()
+ {
+ var identity = new ClaimsIdentity(CookieAuthenticationDefaults.AuthenticationScheme);
+
+ if (identity.IsAuthenticated)
+ {
+ return Ok(true);
+ }
+
+ return Ok(false);
+ }
+
+ ///
+ /// Checks if the session is authorized
+ ///
+ ///
+ ///
+ [HttpGet("isAuthorized")]
+
+ public ActionResult IsAuthorized(string hash)
+ {
+ var isAuthorized = _sessionManager.IsAuthorized(hash);
+
+ return Ok(isAuthorized ? new {authenticated = true} : new {authenticated = false});
+ }
+
+
///
/// Do Cookie based login.
@@ -75,23 +116,26 @@ public class AuthController : Controller
///
///
[AllowAnonymous]
- [HttpPost("logincookie")]
- public async Task> DoLoginAsync(LoginViewModel loginDto)
+ [HttpPost("login")]
+ public async Task DoLoginAsync(LoginRequest loginDto)
{
- var user = await _userManager.FindByEmailAsync(loginDto.Email);
-
- if (user != null)
+ var (success, identity, sessionToken) = await _authManager.DoLogin(loginDto);
+
+ if (success)
{
- if(await _userManager.CheckPasswordAsync(user, loginDto.Password))
- {
- var identity = new ClaimsIdentity(CookieAuthenticationDefaults.AuthenticationScheme);
- identity.AddClaim(new Claim(ClaimTypes.Email, user.Email));
- await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, new ClaimsPrincipal(identity));
- return Ok(new {authenticated = true});
- }
+ await HttpContext.SignInAsync(
+ CookieAuthenticationDefaults.AuthenticationScheme,
+ new ClaimsPrincipal(identity),
+ new AuthenticationProperties
+ {
+ IsPersistent = true,
+ ExpiresUtc = DateTime.UtcNow.AddDays(1)
+ });
+
+ return Ok(new {authenticated = true, sessionToken});
}
- return Ok(new {authenticated = false});
+ return new ForbidResult(CookieAuthenticationDefaults.AuthenticationScheme);
}
///
@@ -101,10 +145,9 @@ public class AuthController : Controller
///
[AllowAnonymous]
[HttpPost("logout")]
- public async Task> DoLogoutAsync(LoginViewModel loginDto)
+ public async Task DoLogoutAsync()
{
- await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
- return Json(true);
+ await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
}
}
\ No newline at end of file
diff --git a/BlueWest.Api/Controllers/CountryController.cs b/BlueWest.Api/Controllers/CountryController.cs
index 273cdfd..d82e63c 100644
--- a/BlueWest.Api/Controllers/CountryController.cs
+++ b/BlueWest.Api/Controllers/CountryController.cs
@@ -18,8 +18,7 @@ namespace BlueWest.WebApi.Controllers
///
[ApiController]
[Route("[controller]")]
- [Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
- [Authorize(AuthenticationSchemes = CookieAuthenticationDefaults.AuthenticationScheme)]
+ [Authorize(Policy = "ApiUser")]
[EnableCors(Constants.CorsPolicyName)]
// [Authorize(Roles = "Administrator")]
public class CountryController : ControllerBase
diff --git a/BlueWest.Api/Controllers/CurrencyController.cs b/BlueWest.Api/Controllers/CurrencyController.cs
index 9c5b5e1..73e0892 100644
--- a/BlueWest.Api/Controllers/CurrencyController.cs
+++ b/BlueWest.Api/Controllers/CurrencyController.cs
@@ -6,6 +6,7 @@ using BlueWest.WebApi.EF;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Authorization;
+using Microsoft.AspNetCore.Cors;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
@@ -18,6 +19,8 @@ namespace BlueWest.WebApi.Controllers
[Route("[controller]")]
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
[Authorize(AuthenticationSchemes = CookieAuthenticationDefaults.AuthenticationScheme)]
+ [EnableCors(Constants.CorsPolicyName)]
+
// [Authorize(Roles = "Administrator")]
public partial class CurrencyController : ControllerBase
{
diff --git a/BlueWest.Api/Controllers/FinanceController.cs b/BlueWest.Api/Controllers/FinanceController.cs
index 13dc7cb..bde67c3 100644
--- a/BlueWest.Api/Controllers/FinanceController.cs
+++ b/BlueWest.Api/Controllers/FinanceController.cs
@@ -4,6 +4,7 @@ using BlueWest.WebApi.EF;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Authorization;
+using Microsoft.AspNetCore.Cors;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
@@ -16,7 +17,9 @@ namespace BlueWest.WebApi.Controllers;
[Route("[controller]")]
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
[Authorize(AuthenticationSchemes = CookieAuthenticationDefaults.AuthenticationScheme)]
-[Authorize(Roles = "Administrator")]
+//[Authorize(Roles = "Administrator")]
+[EnableCors(Constants.CorsPolicyName)]
+
public class FinanceController : ControllerBase
{
private readonly FinanceDbContext _dbContext;
diff --git a/BlueWest.Api/Controllers/UserController.cs b/BlueWest.Api/Controllers/UserController.cs
index 3c63e1a..d41c830 100644
--- a/BlueWest.Api/Controllers/UserController.cs
+++ b/BlueWest.Api/Controllers/UserController.cs
@@ -7,6 +7,7 @@ using BlueWest.WebApi.EF;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Authorization;
+using Microsoft.AspNetCore.Cors;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
@@ -19,8 +20,9 @@ namespace BlueWest.WebApi.Controllers
[Route("[controller]")]
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
[Authorize(AuthenticationSchemes = CookieAuthenticationDefaults.AuthenticationScheme)]
- [Authorize(Roles = "Administrator")]
-
+ //[Authorize(Roles = "Administrator")]
+ [EnableCors(Constants.CorsPolicyName)]
+
public class UserController : ControllerBase
{
diff --git a/BlueWest.Api/Dockerfile b/BlueWest.Api/Dockerfile
index 46f67a9..3789d9a 100644
--- a/BlueWest.Api/Dockerfile
+++ b/BlueWest.Api/Dockerfile
@@ -15,8 +15,8 @@ COPY ["BlueWest/BlueWest.csproj", "BlueWest/"]
RUN dotnet restore "BlueWest/BlueWest.csproj"
-COPY ["BlueWest.Data.Geography/BlueWest.Data.Geography.csproj", "BlueWest.Data.Geography/"]
-RUN dotnet restore "BlueWest.Data.Geography/BlueWest.Data.Geography.csproj"
+COPY ["BlueWest.Data.Application/BlueWest.Data.Application.csproj", "BlueWest.Data.Application/"]
+RUN dotnet restore "BlueWest.Data.Application/BlueWest.Data.Application.csproj"
COPY ["BlueWest.Api/BlueWest.Api.csproj", "BlueWest.Api/"]
diff --git a/BlueWest.Api/Services/GreeterService.cs b/BlueWest.Api/Services/GreeterService.cs
new file mode 100644
index 0000000..7d236eb
--- /dev/null
+++ b/BlueWest.Api/Services/GreeterService.cs
@@ -0,0 +1,9 @@
+using System.Threading.Tasks;
+using Microsoft.Extensions.Logging;
+
+namespace BlueWest.WebApi;
+
+public class GreeterService
+{
+
+}
\ No newline at end of file
diff --git a/BlueWest.Api/Services/IndexCreationDevice.cs b/BlueWest.Api/Services/IndexCreationDevice.cs
new file mode 100644
index 0000000..24329b4
--- /dev/null
+++ b/BlueWest.Api/Services/IndexCreationDevice.cs
@@ -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;
+
+ ///
+ /// Index Creation Device
+ ///
+ ///
+ public IndexCreationDevice(RedisConnectionProvider provider)
+ {
+ _provider = provider;
+ }
+
+ ///
+ public async Task StartAsync(CancellationToken cancellationToken)
+ {
+ await _provider.Connection.CreateIndexAsync(typeof(SessionToken)); }
+
+ ///
+ public Task StopAsync(CancellationToken cancellationToken)
+ {
+ return Task.CompletedTask; }
+ }
+}
+
diff --git a/BlueWest.Api/Services/Interceptors/ServerLoggerInterceptor.cs b/BlueWest.Api/Services/Interceptors/ServerLoggerInterceptor.cs
new file mode 100644
index 0000000..27ac98b
--- /dev/null
+++ b/BlueWest.Api/Services/Interceptors/ServerLoggerInterceptor.cs
@@ -0,0 +1,13 @@
+using Grpc.Core.Interceptors;
+
+namespace BlueWest.WebApi.Interceptors
+{
+ ///
+ /// Server Logger Interceptor
+ ///
+ public class ServerLoggerInterceptor : Interceptor
+ {
+
+ }
+}
+
diff --git a/BlueWest.Api/Session/ISessionManager.cs b/BlueWest.Api/Session/ISessionManager.cs
new file mode 100644
index 0000000..2017751
--- /dev/null
+++ b/BlueWest.Api/Session/ISessionManager.cs
@@ -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);
+
+ }
+}
+
diff --git a/BlueWest.Api/Session/SessionManager.cs b/BlueWest.Api/Session/SessionManager.cs
new file mode 100644
index 0000000..45e6a25
--- /dev/null
+++ b/BlueWest.Api/Session/SessionManager.cs
@@ -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;
+ }
+
+
+ ///
+ /// Check if token is authorized
+ ///
+ ///
+ ///
+ 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;
+
+ }
+
+ ///
+ /// Gets a new or existing session token
+ ///
+ ///
+ ///
+ ///
+ 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;
+
+ }
+
+ ///
+ /// Gets a session token for the user following a login request
+ ///
+ ///
+ ///
+ ///
+ 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;
+ }
+ }
+}
+
diff --git a/BlueWest.Api/Startup.cs b/BlueWest.Api/Startup.cs
index d8b118a..99711cb 100644
--- a/BlueWest.Api/Startup.cs
+++ b/BlueWest.Api/Startup.cs
@@ -7,12 +7,11 @@ using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using System.Text.Json.Serialization;
using BlueWest.Tools;
-using BlueWest.WebApi.Context.Users;
+using BlueWest.WebApi.Interceptors;
using BlueWest.WebApi.Interfaces;
using BlueWest.WebApi.Tools;
-using Microsoft.AspNetCore.Authentication;
-using Microsoft.AspNetCore.Identity;
-using Microsoft.Extensions.FileProviders;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.Extensions.Logging;
using Microsoft.OpenApi.Models;
namespace BlueWest.WebApi
@@ -24,6 +23,7 @@ namespace BlueWest.WebApi
{
private readonly IConfiguration _configuration;
private readonly IWebHostEnvironment _environment;
+
private readonly string MyAllowSpecificOrigins = Constants.CorsPolicyName;
///
@@ -47,7 +47,7 @@ namespace BlueWest.WebApi
options.AddPolicy(name: MyAllowSpecificOrigins,
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()
.AllowAnyHeader()
.AllowCredentials();
@@ -55,9 +55,27 @@ namespace BlueWest.WebApi
});
services
- .AddControllers()
+ .AddResponseCaching()
+ .AddControllers(options =>
+ {
+ options.CacheProfiles.Add("Default30",
+ new CacheProfile()
+ {
+ Duration = 30
+ });
+ })
.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
.AddSwaggerGen(options =>
{
@@ -101,7 +119,14 @@ namespace BlueWest.WebApi
});
});
-
+
+
+ services.AddGrpc(options =>
+ {
+ options.Interceptors.Add();
+ });
+
+ services.AddSingleton();
/*
services.AddSingleton(
new PhysicalFileProvider(
@@ -119,10 +144,12 @@ namespace BlueWest.WebApi
services
.AddSingleton();
+
- services.AddAuthServerServices(MyAllowSpecificOrigins, _configuration, _environment);
+ services.AddAuthServerServices( _configuration, _environment);
services.AddScoped();
+
switch (allowedDatabase)
{
case "mysql":
@@ -138,10 +165,6 @@ namespace BlueWest.WebApi
}
-
-
-
- // services.AddGrpc();
}
@@ -156,19 +179,17 @@ namespace BlueWest.WebApi
//app.UseStaticFiles();
- app.UseDeveloperExceptionPage();
app.UseSwagger();
+
app.UseSwaggerUI(c =>
{
c.RoutePrefix = "swagger";
c.SwaggerEndpoint("/swagger/v1/swagger.json", "BlueWest.Api v1");
});
+
app.UseStaticFiles();
//app.UseHttpsRedirection();
-
-
-
app.UseRouting();
app.UseCors(MyAllowSpecificOrigins);
app.UseAuthentication();
diff --git a/BlueWest.Api/StartupExtensions.cs b/BlueWest.Api/StartupExtensions.cs
index c4ad530..11ff6ee 100644
--- a/BlueWest.Api/StartupExtensions.cs
+++ b/BlueWest.Api/StartupExtensions.cs
@@ -10,6 +10,7 @@ using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
+using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
@@ -80,7 +81,9 @@ namespace BlueWest.WebApi
.AddDbContextPool(options => options.GetMySqlSettings(configuration, environment))
.AddDbContextPool(options => options.GetMySqlSettings(configuration, environment))
.AddDbContextPool(options => options.GetMySqlSettings(configuration, environment))
- .AddDbContextPool(options => options.GetMySqlSettings(configuration, environment));
+ .AddDbContextPool(options =>
+ options.GetMySqlSettings(configuration, environment))
+ .AddDbContextPool(options => options.UseInMemoryDatabase("_s"));
}
///
@@ -100,27 +103,24 @@ namespace BlueWest.WebApi
.AddDbContextPool(options => options.UseSqlite(sqliteConString))
.AddDbContextPool(options => options.UseSqlite(sqliteConString))
.AddDbContextPool(options => options.UseSqlite(sqliteConString))
- .AddDbContextPool(options => options.UseSqlite(sqliteConString));
-
+ .AddDbContextPool(options => options.UseSqlite(sqliteConString))
+ .AddDbContextPool(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();
- services.AddScoped();
-
-
- services
- .AddScoped< UserRepository>()
+ services.AddScoped()
+ .AddScoped()
+ .AddSingleton()
+ .AddScoped()
+ .AddScoped()
.AddScoped()
.AddScoped()
+
.AddScoped();
- services
- .AddIdentityCore(opt => { opt.User.RequireUniqueEmail = true; })
- .AddUserManager()
- .AddUserStore();
// Database Context and Swagger
@@ -162,6 +162,7 @@ namespace BlueWest.WebApi
services.AddAuthentication(options =>
{
+
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
@@ -170,6 +171,9 @@ namespace BlueWest.WebApi
})
.AddCookie(options =>
{
+ options.Cookie.SameSite = SameSiteMode.Lax;
+ options.Cookie.SecurePolicy = CookieSecurePolicy.SameAsRequest;
+ options.Cookie.MaxAge = TimeSpan.FromDays(1);
options.LoginPath = "/api/auth/logincookie";
options.LogoutPath = "/api/auth/logout";
})
@@ -206,13 +210,17 @@ namespace BlueWest.WebApi
// add identity
var identityBuilder = services.AddIdentityCore(o =>
{
+ o.User.RequireUniqueEmail = true;
+
// configure identity options
o.Password.RequireDigit = false;
o.Password.RequireLowercase = false;
o.Password.RequireUppercase = false;
o.Password.RequireNonAlphanumeric = false;
o.Password.RequiredLength = 6;
- });
+ })
+ .AddUserManager()
+ .AddUserStore();
identityBuilder = new IdentityBuilder(identityBuilder.UserType, typeof(ApplicationRole), identityBuilder.Services);
identityBuilder
diff --git a/BlueWest.Api/Users/ApplicationUserManager.cs b/BlueWest.Api/Users/ApplicationUserManager.cs
index 96fa33f..c7592b5 100644
--- a/BlueWest.Api/Users/ApplicationUserManager.cs
+++ b/BlueWest.Api/Users/ApplicationUserManager.cs
@@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using BlueWest.Cryptography;
+using BlueWest.Data.Application;
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
@@ -16,12 +17,16 @@ internal class ApplicationUserManager : UserManager, IUserManag
{
private readonly IHasher _hasher;
private readonly UserRepository _usersRepo;
- public ApplicationUserManager(UserRepository store, IOptions optionsAccessor,
- IHasher passwordHasher, IEnumerable> userValidators,
- IEnumerable> passwordValidators, ILookupNormalizer keyNormalizer,
- IdentityErrorDescriber errors, IServiceProvider services, ILogger> logger) : base(store,
- optionsAccessor, passwordHasher, userValidators, passwordValidators, keyNormalizer, errors, services,
- logger)
+ public ApplicationUserManager(
+ UserRepository store,
+ IOptions optionsAccessor,
+ IHasher passwordHasher,
+ IEnumerable> userValidators,
+ IEnumerable> passwordValidators,
+ ILookupNormalizer keyNormalizer,
+ IdentityErrorDescriber errors,
+ IServiceProvider services,
+ ILogger> logger) : base(store, optionsAccessor, passwordHasher, userValidators, passwordValidators, keyNormalizer, errors, services, logger)
{
_hasher = passwordHasher;
_usersRepo = store;
@@ -49,6 +54,7 @@ internal class ApplicationUserManager : UserManager, IUserManag
return success;
}
+
protected override async Task VerifyPasswordAsync(IUserPasswordStore store, ApplicationUser user, string password)
{
string existingHash;
@@ -89,7 +95,11 @@ internal class ApplicationUserManager : UserManager, IUserManag
Logger.LogWarning(2, "Change password failed for user {userId}.", await GetUserIdAsync(user));
return IdentityResult.Failed(ErrorDescriber.PasswordMismatch());
}
-
+
+ public override Task SetAuthenticationTokenAsync(ApplicationUser user, string loginProvider, string tokenName, string tokenValue)
+ {
+ return base.SetAuthenticationTokenAsync(user, loginProvider, tokenName, tokenValue);
+ }
private IUserPasswordStore GetPasswordStore()
{
diff --git a/BlueWest.Api/Users/Auth/AuthManager.cs b/BlueWest.Api/Users/Auth/AuthManager.cs
index 3225359..eb77915 100644
--- a/BlueWest.Api/Users/Auth/AuthManager.cs
+++ b/BlueWest.Api/Users/Auth/AuthManager.cs
@@ -1,14 +1,20 @@
+using System;
+using System.Security.Claims;
using System.Threading;
using System.Threading.Tasks;
using BlueWest.Cryptography;
+using BlueWest.Data.Application;
+using Microsoft.AspNetCore.Authentication.Cookies;
+using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Identity;
namespace BlueWest.WebApi.Context.Users;
internal class AuthManager : IAuthManager
{
- private readonly IUserManager _userManager;
+ private readonly ApplicationUserManager _userManager;
private readonly UserRepository _usersRepo;
+ private readonly ISessionManager _sessionManager;
private readonly IHasher _hasher;
private readonly IJwtFactory _jwtFactory;
@@ -19,51 +25,79 @@ internal class AuthManager : IAuthManager
///
///
///
- public AuthManager(IUserManager userManager, IHasher hasher, UserRepository usersRepo, IJwtFactory jwtFactory)
+ public AuthManager(
+ ApplicationUserManager userManager,
+ IHasher hasher,
+ UserRepository usersRepo,
+ ISessionManager sessionManager,
+ IJwtFactory jwtFactory)
{
_userManager = userManager;
_hasher = hasher;
_usersRepo = usersRepo;
_jwtFactory = jwtFactory;
+ _sessionManager = sessionManager;
+ }
+
+ public async Task<(bool, ClaimsIdentity, SessionTokenUnique)> DoLogin(LoginRequest loginRequest)
+ {
+ var user = await _userManager.FindByEmailAsync(loginRequest.Email);
+
+ if (user != null)
+ {
+ if(await _userManager.CheckPasswordAsync(user, loginRequest.Password))
+ {
+ // Identity
+ var identity = new ClaimsIdentity(CookieAuthenticationDefaults.AuthenticationScheme);
+ identity.AddClaim(new Claim(ClaimTypes.Email, user.Email));
+
+ // Session
+ var sessionToken = _sessionManager.GetSessionToken(loginRequest, user);
+ var sessionResponse = new SessionTokenUnique(sessionToken);
+ return (true, identity, sessionResponse);
+ }
+ }
+
+ return (false, null, null);
}
///
- public async Task GetToken(LoginViewModel loginViewModel)
+ public async Task<(bool, SessionTokenUnique, AccessToken)> GetToken(LoginRequest loginRequest)
{
- if (!string.IsNullOrEmpty(loginViewModel.Email) && !string.IsNullOrEmpty(loginViewModel.Password))
+ if (!string.IsNullOrEmpty(loginRequest.Email) && !string.IsNullOrEmpty(loginRequest.Password))
{
- var user = await _userManager.FindByEmailAsync(loginViewModel.Email);
+ var user = await _userManager.FindByEmailAsync(loginRequest.Email);
if (user != null)
{
- if (await VerifyLoginAsync(loginViewModel.Email,loginViewModel.Password))
+ if (await VerifyLoginByEmailAsync(loginRequest.Email,loginRequest.Password))
{
- // Todo generate refresh token
- // Todo Add refresh token
await _usersRepo.UpdateAsync(user, CancellationToken.None);
- var token = await _jwtFactory.GenerateEncodedToken(user.Id.ToString(), user.UserName);
- // await _userManager.SetAuthenticationTokenAsync(user, "Income", "ApiUser", token.Token);
+ // 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 token;
+ return (completed == IdentityResult.Success, sessionResponse, token);
}
}
}
- return null;
+ return (false, null, null);
}
///
- public async Task VerifyLoginAsync(string email, string password)
+ public async Task VerifyLoginByEmailAsync(string email, string password)
{
var user = await _userManager.FindByEmailAsync(email);
if (user == null)
{
- return false; // return error user doesn't exist
+ return false;
}
return await _userManager.CheckPasswordAsync(user, password);
-
- // return await GenerateAuthenticationResultForUserAsync(user);
}
private RegisterViewModel FromSignupToUser(RegisterViewModel signupDto)
@@ -71,8 +105,6 @@ internal class AuthManager : IAuthManager
var pwd = signupDto.Password;
var hash = _hasher.CreateHash(pwd, BaseCryptoItem.HashAlgorithm.SHA3_512);
signupDto.Password = hash;
- signupDto.ConfirmPassword = hash;
-
return signupDto;
}
diff --git a/BlueWest.Api/Crypto/BaseCryptoItem.cs b/BlueWest.Api/Users/Auth/Crypto/BaseCryptoItem.cs
similarity index 100%
rename from BlueWest.Api/Crypto/BaseCryptoItem.cs
rename to BlueWest.Api/Users/Auth/Crypto/BaseCryptoItem.cs
diff --git a/BlueWest.Api/Crypto/Hasher.cs b/BlueWest.Api/Users/Auth/Crypto/Hasher.cs
similarity index 97%
rename from BlueWest.Api/Crypto/Hasher.cs
rename to BlueWest.Api/Users/Auth/Crypto/Hasher.cs
index 500cc84..f930403 100644
--- a/BlueWest.Api/Crypto/Hasher.cs
+++ b/BlueWest.Api/Users/Auth/Crypto/Hasher.cs
@@ -20,7 +20,7 @@ namespace BlueWest.Cryptography
///
///
///
- public string CreateHash(string text, BaseCryptoItem.HashAlgorithm algorithm)
+ public string CreateHash(string text, HashAlgorithm algorithm)
{
var salt = CreateRandomString(SaltLength);
return CreateHash(text, salt, algorithm, true);
diff --git a/BlueWest.Api/Crypto/IHasher.cs b/BlueWest.Api/Users/Auth/Crypto/IHasher.cs
similarity index 100%
rename from BlueWest.Api/Crypto/IHasher.cs
rename to BlueWest.Api/Users/Auth/Crypto/IHasher.cs
diff --git a/BlueWest.Api/Crypto/Jwt/IJwtFactory.cs b/BlueWest.Api/Users/Auth/Crypto/IJwtFactory.cs
similarity index 100%
rename from BlueWest.Api/Crypto/Jwt/IJwtFactory.cs
rename to BlueWest.Api/Users/Auth/Crypto/IJwtFactory.cs
diff --git a/BlueWest.Api/Crypto/Jwt/IJwtTokenHandler.cs b/BlueWest.Api/Users/Auth/Crypto/IJwtTokenHandler.cs
similarity index 100%
rename from BlueWest.Api/Crypto/Jwt/IJwtTokenHandler.cs
rename to BlueWest.Api/Users/Auth/Crypto/IJwtTokenHandler.cs
diff --git a/BlueWest.Api/Users/Auth/Crypto/ISessionHasher.cs b/BlueWest.Api/Users/Auth/Crypto/ISessionHasher.cs
new file mode 100644
index 0000000..52525a0
--- /dev/null
+++ b/BlueWest.Api/Users/Auth/Crypto/ISessionHasher.cs
@@ -0,0 +1,12 @@
+namespace BlueWest.Cryptography
+{
+ public interface ISessionHasher
+ {
+ ///
+ /// Generates a token for the current session
+ ///
+ void GenerateSessionToken();
+
+ }
+}
+
diff --git a/BlueWest.Api/Crypto/Jwt/ITokenFactory.cs b/BlueWest.Api/Users/Auth/Crypto/ITokenFactory.cs
similarity index 100%
rename from BlueWest.Api/Crypto/Jwt/ITokenFactory.cs
rename to BlueWest.Api/Users/Auth/Crypto/ITokenFactory.cs
diff --git a/BlueWest.Api/Crypto/Jwt/JwtFactory.cs b/BlueWest.Api/Users/Auth/Crypto/JwtFactory.cs
similarity index 97%
rename from BlueWest.Api/Crypto/Jwt/JwtFactory.cs
rename to BlueWest.Api/Users/Auth/Crypto/JwtFactory.cs
index 948f7ad..a0dd1aa 100644
--- a/BlueWest.Api/Crypto/Jwt/JwtFactory.cs
+++ b/BlueWest.Api/Users/Auth/Crypto/JwtFactory.cs
@@ -28,6 +28,8 @@ internal class JwtFactory : IJwtFactory
{
new Claim(JwtRegisteredClaimNames.Sub, userName),
new Claim(JwtRegisteredClaimNames.Jti, await _jwtOptions.JtiGenerator()),
+ new Claim(JwtRegisteredClaimNames.Aud, _jwtOptions.Audience),
+
new Claim(JwtRegisteredClaimNames.Iat, ToUnixEpochDate(_jwtOptions.IssuedAt).ToString(),
ClaimValueTypes.Integer64),
identity.FindFirst(JwtClaimIdentifiers.Rol),
diff --git a/BlueWest.Api/Crypto/Jwt/JwtIssuerOptions.cs b/BlueWest.Api/Users/Auth/Crypto/JwtIssuerOptions.cs
similarity index 100%
rename from BlueWest.Api/Crypto/Jwt/JwtIssuerOptions.cs
rename to BlueWest.Api/Users/Auth/Crypto/JwtIssuerOptions.cs
diff --git a/BlueWest.Api/Crypto/Jwt/JwtTokenHandler.cs b/BlueWest.Api/Users/Auth/Crypto/JwtTokenHandler.cs
similarity index 100%
rename from BlueWest.Api/Crypto/Jwt/JwtTokenHandler.cs
rename to BlueWest.Api/Users/Auth/Crypto/JwtTokenHandler.cs
diff --git a/BlueWest.Api/Crypto/SHA_512.cs b/BlueWest.Api/Users/Auth/Crypto/SHA_512.cs
similarity index 100%
rename from BlueWest.Api/Crypto/SHA_512.cs
rename to BlueWest.Api/Users/Auth/Crypto/SHA_512.cs
diff --git a/BlueWest.Api/Users/Auth/IAuthManager.cs b/BlueWest.Api/Users/Auth/IAuthManager.cs
index bf18985..d775f43 100644
--- a/BlueWest.Api/Users/Auth/IAuthManager.cs
+++ b/BlueWest.Api/Users/Auth/IAuthManager.cs
@@ -1,4 +1,7 @@
+using System;
+using System.Security.Claims;
using System.Threading.Tasks;
+using BlueWest.Data.Application;
using Microsoft.AspNetCore.Identity;
namespace BlueWest.WebApi.Context.Users;
@@ -21,13 +24,20 @@ public interface IAuthManager
///
///
///
- Task VerifyLoginAsync(string email, string password);
+ Task VerifyLoginByEmailAsync(string email, string password);
///
/// GetToken
///
- ///
+ ///
///
- Task GetToken(LoginViewModel loginViewModel);
+ Task<(bool, SessionTokenUnique, AccessToken)> GetToken(LoginRequest loginRequest);
+
+ ///
+ /// Does Login
+ ///
+ ///
+ ///
+ Task<(bool, ClaimsIdentity, SessionTokenUnique)> DoLogin(LoginRequest loginRequest);
}
\ No newline at end of file
diff --git a/BlueWest.Api/Users/IUserManager.cs b/BlueWest.Api/Users/IUserManager.cs
index 9b2ac56..9db5b03 100644
--- a/BlueWest.Api/Users/IUserManager.cs
+++ b/BlueWest.Api/Users/IUserManager.cs
@@ -1,4 +1,5 @@
using System.Threading.Tasks;
+using BlueWest.Data.Application;
using Microsoft.AspNetCore.Identity;
namespace BlueWest.WebApi.Context.Users
@@ -26,6 +27,9 @@ namespace BlueWest.WebApi.Context.Users
///
///
Task FindByEmailAsync(string email);
+
+
+
}
}
diff --git a/BlueWest.Api/Users/Models/LoginViewModel.cs b/BlueWest.Api/Users/Models/LoginRequest.cs
similarity index 73%
rename from BlueWest.Api/Users/Models/LoginViewModel.cs
rename to BlueWest.Api/Users/Models/LoginRequest.cs
index 2e5cfa4..7dea8b4 100644
--- a/BlueWest.Api/Users/Models/LoginViewModel.cs
+++ b/BlueWest.Api/Users/Models/LoginRequest.cs
@@ -4,9 +4,9 @@ namespace BlueWest.WebApi.Context.Users
{
// from: https://github.com/dotnet/aspnetcore/tree/main/src/Identity/samples/IdentitySample.Mvc/Models/AccountViewModels
///
- /// Login View Model
+ /// Login Request adata
///
- public class LoginViewModel
+ public class LoginRequest
{
///
/// Email
@@ -21,12 +21,10 @@ namespace BlueWest.WebApi.Context.Users
[Required]
[DataType(DataType.Password)]
public string Password { get; set; }
+
+ [Required]
+ public string Uuid { get; set; }
- ///
- /// RememberMe
- ///
- [Display(Name = "Remember me?")]
- public bool RememberMe { get; set; }
}
}
diff --git a/BlueWest.Api/Users/Models/RegisterViewModel.cs b/BlueWest.Api/Users/Models/RegisterViewModel.cs
index d599d3c..dc548a9 100644
--- a/BlueWest.Api/Users/Models/RegisterViewModel.cs
+++ b/BlueWest.Api/Users/Models/RegisterViewModel.cs
@@ -35,6 +35,16 @@ public class RegisterViewModel
[Display(Name = "Confirm password")]
[Compare("Password", ErrorMessage = "The password and confirmation password do not match.")]
public string ConfirmPassword { get; set; }
+
+
+ ///
+ /// ConfirmPassword
+ ///
+ [DataType(DataType.PhoneNumber)]
+ [Display(Name = "Phone Number")]
+ public string PhoneNumber { get; set; }
+
+
///
/// Convert RegisterViewModel to ApplicationUser
@@ -46,6 +56,7 @@ public class RegisterViewModel
newUser.Email = Email;
newUser.PasswordHash = Password;
newUser.UserName = Username;
+ newUser.PhoneNumber = PhoneNumber;
return newUser;
}
}
\ No newline at end of file
diff --git a/BlueWest.Api/Users/RoleStore.cs b/BlueWest.Api/Users/RoleStore.cs
deleted file mode 100644
index effeac8..0000000
--- a/BlueWest.Api/Users/RoleStore.cs
+++ /dev/null
@@ -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;
-
-///
-/// Role storage management
-///
-/*public class RoleStore : RoleStore
-{
-
-}*/
\ No newline at end of file
diff --git a/BlueWest.Api/appsettings.json b/BlueWest.Api/appsettings.json
index 547b74d..a12487d 100644
--- a/BlueWest.Api/appsettings.json
+++ b/BlueWest.Api/appsettings.json
@@ -10,7 +10,12 @@
"ConnectionStrings": {
"DockerMySQL": "server=db;user=blueuser;password=dXjw127124dJ;database=bluedb;"
},
+ "REDIS_CONNECTION_STRING": "redis://localhost:6379",
"AuthSettings": {
"SecretKey": "iJWHDmHLpUA283sqsfhqGbMRdRj1PVkH"
+ },
+ "JwtIssuerOptions": {
+ "Issuer": "SomeIssuer",
+ "Audience": "http://localhost:5000"
}
}
diff --git a/BlueWest.Console/.dockerignore b/BlueWest.Console/.dockerignore
new file mode 100644
index 0000000..38bece4
--- /dev/null
+++ b/BlueWest.Console/.dockerignore
@@ -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
\ No newline at end of file
diff --git a/BlueWest.Console/BlueWest.Console.csproj b/BlueWest.Console/BlueWest.Console.csproj
new file mode 100644
index 0000000..6e6affd
--- /dev/null
+++ b/BlueWest.Console/BlueWest.Console.csproj
@@ -0,0 +1,11 @@
+
+
+
+ Exe
+ net6.0
+ enable
+ enable
+ Linux
+
+
+
diff --git a/BlueWest.Console/Dockerfile b/BlueWest.Console/Dockerfile
new file mode 100644
index 0000000..b231d22
--- /dev/null
+++ b/BlueWest.Console/Dockerfile
@@ -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"]
diff --git a/BlueWest.Console/Program.cs b/BlueWest.Console/Program.cs
new file mode 100644
index 0000000..1920ca7
--- /dev/null
+++ b/BlueWest.Console/Program.cs
@@ -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)
+{
+ Console.WriteLine("Hello");
+ await task.Invoke();
+}
+
+async Task Task2()
+{
+ Console.WriteLine("World");
+}
+
+var f = Task2;
+
+await TaskReceiver(f);
\ No newline at end of file
diff --git a/BlueWest.Api/Users/Auth/AccessToken.cs b/BlueWest.Data.Application/AccessToken.cs
similarity index 99%
rename from BlueWest.Api/Users/Auth/AccessToken.cs
rename to BlueWest.Data.Application/AccessToken.cs
index 3a19ad1..cdc5771 100644
--- a/BlueWest.Api/Users/Auth/AccessToken.cs
+++ b/BlueWest.Data.Application/AccessToken.cs
@@ -1,3 +1,4 @@
+
namespace BlueWest.WebApi.Context.Users
{
public class AccessToken
diff --git a/BlueWest.Data.Application/ApplicationDevice/ApplicationDevice.cs b/BlueWest.Data.Application/ApplicationDevice/ApplicationDevice.cs
new file mode 100644
index 0000000..f0dd28f
--- /dev/null
+++ b/BlueWest.Data.Application/ApplicationDevice/ApplicationDevice.cs
@@ -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; }
+ }
+}
+
diff --git a/BlueWest.Data.Application/ApplicationDevice/ApplicationDeviceCreate.cs b/BlueWest.Data.Application/ApplicationDevice/ApplicationDeviceCreate.cs
new file mode 100644
index 0000000..b46e6af
--- /dev/null
+++ b/BlueWest.Data.Application/ApplicationDevice/ApplicationDeviceCreate.cs
@@ -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; }
+ }
+}
+
diff --git a/BlueWest.Data.Application/ApplicationDevice/ClientOperatingSystem.cs b/BlueWest.Data.Application/ApplicationDevice/ClientOperatingSystem.cs
new file mode 100644
index 0000000..4213583
--- /dev/null
+++ b/BlueWest.Data.Application/ApplicationDevice/ClientOperatingSystem.cs
@@ -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
+ }
+
+}
+
diff --git a/BlueWest.Data.Application/ApplicationDevice/ClientPlatformType.cs b/BlueWest.Data.Application/ApplicationDevice/ClientPlatformType.cs
new file mode 100644
index 0000000..4b78e42
--- /dev/null
+++ b/BlueWest.Data.Application/ApplicationDevice/ClientPlatformType.cs
@@ -0,0 +1,17 @@
+namespace BlueWest.Data.Application;
+
+public enum ClientPlatformType
+{
+ MicrosoftWindows,
+ WindowsPhone,
+ IPhone,
+ Samsung,
+ Unknown,
+ Wii,
+ Linux,
+ Blackberry,
+ Playstation,
+ Ipad,
+ IPod,
+ Curl
+}
diff --git a/BlueWest.Data.Application/ApplicationDevice/OperatingSystemType.cs b/BlueWest.Data.Application/ApplicationDevice/OperatingSystemType.cs
new file mode 100644
index 0000000..c192858
--- /dev/null
+++ b/BlueWest.Data.Application/ApplicationDevice/OperatingSystemType.cs
@@ -0,0 +1,12 @@
+namespace BlueWest.Data.Application
+{
+ public enum OperatingSystemType
+ {
+ MacOS,
+ iOS,
+ Windows,
+ Android,
+ Linux
+ }
+}
+
diff --git a/BlueWest.Data.Capital/Application/ApplicationRole/ApplicationRole.cs b/BlueWest.Data.Application/ApplicationRole/ApplicationRole.cs
similarity index 100%
rename from BlueWest.Data.Capital/Application/ApplicationRole/ApplicationRole.cs
rename to BlueWest.Data.Application/ApplicationRole/ApplicationRole.cs
diff --git a/BlueWest.Data.Capital/Application/ApplicationRole/ApplicationRoleUnique.cs b/BlueWest.Data.Application/ApplicationRole/ApplicationRoleUnique.cs
similarity index 100%
rename from BlueWest.Data.Capital/Application/ApplicationRole/ApplicationRoleUnique.cs
rename to BlueWest.Data.Application/ApplicationRole/ApplicationRoleUnique.cs
diff --git a/BlueWest.Data.Capital/Application/ApplicationRoleClaim/ApplicationRoleClaim.cs b/BlueWest.Data.Application/ApplicationRoleClaim/ApplicationRoleClaim.cs
similarity index 100%
rename from BlueWest.Data.Capital/Application/ApplicationRoleClaim/ApplicationRoleClaim.cs
rename to BlueWest.Data.Application/ApplicationRoleClaim/ApplicationRoleClaim.cs
diff --git a/BlueWest.Data.Capital/Application/ApplicationRoleClaim/ApplicationRoleClaimUnique.cs b/BlueWest.Data.Application/ApplicationRoleClaim/ApplicationRoleClaimUnique.cs
similarity index 100%
rename from BlueWest.Data.Capital/Application/ApplicationRoleClaim/ApplicationRoleClaimUnique.cs
rename to BlueWest.Data.Application/ApplicationRoleClaim/ApplicationRoleClaimUnique.cs
diff --git a/BlueWest.Data.Capital/Application/ApplicationUser/ApplicationUser.cs b/BlueWest.Data.Application/ApplicationUser/ApplicationUser.cs
similarity index 93%
rename from BlueWest.Data.Capital/Application/ApplicationUser/ApplicationUser.cs
rename to BlueWest.Data.Application/ApplicationUser/ApplicationUser.cs
index dabd0a9..a53a8c8 100644
--- a/BlueWest.Data.Capital/Application/ApplicationUser/ApplicationUser.cs
+++ b/BlueWest.Data.Application/ApplicationUser/ApplicationUser.cs
@@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using BlueWest.Data;
+using BlueWest.Data.Application;
using MapTo;
using Microsoft.AspNetCore.Identity;
@@ -20,10 +21,8 @@ namespace BlueWest.WebApi.Context.Users
///
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[PersonalData]
- public new string Id { get; set; }
-
- public List Users { get; set; }
-
+ public override string Id { get; set; }
+
[ProtectedPersonalData]
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.
///
public override int AccessFailedCount { get; set; }
+
+ public List SessionToken { get; set; }
+
+ public List SessionDatas { get; set; }
+
+
}
}
\ No newline at end of file
diff --git a/BlueWest.Data.Capital/Application/ApplicationUser/ApplicationUserUnique.cs b/BlueWest.Data.Application/ApplicationUser/ApplicationUserUnique.cs
similarity index 100%
rename from BlueWest.Data.Capital/Application/ApplicationUser/ApplicationUserUnique.cs
rename to BlueWest.Data.Application/ApplicationUser/ApplicationUserUnique.cs
diff --git a/BlueWest.Data.Capital/Application/ApplicationUserClaim/ApplicationUserClaim.cs b/BlueWest.Data.Application/ApplicationUserClaim/ApplicationUserClaim.cs
similarity index 100%
rename from BlueWest.Data.Capital/Application/ApplicationUserClaim/ApplicationUserClaim.cs
rename to BlueWest.Data.Application/ApplicationUserClaim/ApplicationUserClaim.cs
diff --git a/BlueWest.Data.Capital/Application/ApplicationUserClaim/ApplicationUserClaimUnique.cs b/BlueWest.Data.Application/ApplicationUserClaim/ApplicationUserClaimUnique.cs
similarity index 100%
rename from BlueWest.Data.Capital/Application/ApplicationUserClaim/ApplicationUserClaimUnique.cs
rename to BlueWest.Data.Application/ApplicationUserClaim/ApplicationUserClaimUnique.cs
diff --git a/BlueWest.Data.Capital/Application/ApplicationUserLogin/ApplicationUserLogin.cs b/BlueWest.Data.Application/ApplicationUserLogin/ApplicationUserLogin.cs
similarity index 100%
rename from BlueWest.Data.Capital/Application/ApplicationUserLogin/ApplicationUserLogin.cs
rename to BlueWest.Data.Application/ApplicationUserLogin/ApplicationUserLogin.cs
diff --git a/BlueWest.Data.Capital/Application/ApplicationUserRole/ApplicationUserRole.cs b/BlueWest.Data.Application/ApplicationUserRole/ApplicationUserRole.cs
similarity index 100%
rename from BlueWest.Data.Capital/Application/ApplicationUserRole/ApplicationUserRole.cs
rename to BlueWest.Data.Application/ApplicationUserRole/ApplicationUserRole.cs
diff --git a/BlueWest.Data.Capital/Application/ApplicationUserRole/ApplicationUserRoleUnique.cs b/BlueWest.Data.Application/ApplicationUserRole/ApplicationUserRoleUnique.cs
similarity index 100%
rename from BlueWest.Data.Capital/Application/ApplicationUserRole/ApplicationUserRoleUnique.cs
rename to BlueWest.Data.Application/ApplicationUserRole/ApplicationUserRoleUnique.cs
diff --git a/BlueWest.Data.Application/ApplicationUserToken/ApplicationUserToken.cs b/BlueWest.Data.Application/ApplicationUserToken/ApplicationUserToken.cs
new file mode 100644
index 0000000..6aefa55
--- /dev/null
+++ b/BlueWest.Data.Application/ApplicationUserToken/ApplicationUserToken.cs
@@ -0,0 +1,9 @@
+using Microsoft.AspNetCore.Identity;
+
+namespace BlueWest.WebApi.Context.Users;
+
+///
+public class ApplicationUserToken : IdentityUserToken
+{
+ public string Id { get; set; }
+}
\ No newline at end of file
diff --git a/BlueWest.Data.Application/BlueWest.Data.Application.csproj b/BlueWest.Data.Application/BlueWest.Data.Application.csproj
new file mode 100644
index 0000000..04745fb
--- /dev/null
+++ b/BlueWest.Data.Application/BlueWest.Data.Application.csproj
@@ -0,0 +1,26 @@
+
+
+
+ net6.0
+ enable
+ enable
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/BlueWest.Data.Application/Crypto/Jwt/IJwtTokenHandler.cs b/BlueWest.Data.Application/Crypto/Jwt/IJwtTokenHandler.cs
new file mode 100644
index 0000000..a88b355
--- /dev/null
+++ b/BlueWest.Data.Application/Crypto/Jwt/IJwtTokenHandler.cs
@@ -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);
+ }
+}
\ No newline at end of file
diff --git a/BlueWest.Data.Application/Crypto/Jwt/ITokenFactory.cs b/BlueWest.Data.Application/Crypto/Jwt/ITokenFactory.cs
new file mode 100644
index 0000000..392e198
--- /dev/null
+++ b/BlueWest.Data.Application/Crypto/Jwt/ITokenFactory.cs
@@ -0,0 +1,6 @@
+namespace BlueWest.WebApi.Context.Users;
+
+internal interface ITokenFactory
+{
+ string GenerateToken(int size= 32);
+}
\ No newline at end of file
diff --git a/BlueWest.Data.Application/Crypto/Jwt/JwtIssuerOptions.cs b/BlueWest.Data.Application/Crypto/Jwt/JwtIssuerOptions.cs
new file mode 100644
index 0000000..530e5a5
--- /dev/null
+++ b/BlueWest.Data.Application/Crypto/Jwt/JwtIssuerOptions.cs
@@ -0,0 +1,54 @@
+using System;
+using System.Threading.Tasks;
+using Microsoft.IdentityModel.Tokens;
+
+namespace BlueWest.WebApi.Context.Users;
+
+internal class JwtIssuerOptions
+{
+ ///
+ /// 4.1.1. "iss" (Issuer) Claim - The "iss" (issuer) claim identifies the principal that issued the JWT.
+ ///
+ public string Issuer { get; set; }
+
+ ///
+ /// 4.1.2. "sub" (Subject) Claim - The "sub" (subject) claim identifies the principal that is the subject of the JWT.
+ ///
+ public string Subject { get; set; }
+
+ ///
+ /// 4.1.3. "aud" (Audience) Claim - The "aud" (audience) claim identifies the recipients that the JWT is intended for.
+ ///
+ public string Audience { get; set; }
+
+ ///
+ /// 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.
+ ///
+ public DateTime Expiration => IssuedAt.Add(ValidFor);
+
+ ///
+ /// 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.
+ ///
+ public DateTime NotBefore => DateTime.UtcNow;
+
+ ///
+ /// 4.1.6. "iat" (Issued At) Claim - The "iat" (issued at) claim identifies the time at which the JWT was issued.
+ ///
+ public DateTime IssuedAt => DateTime.UtcNow;
+
+ ///
+ /// Set the timespan the token will be valid for (default is 120 min)
+ ///
+ public TimeSpan ValidFor { get; set; } = TimeSpan.FromMinutes(120);
+
+ ///
+ /// "jti" (JWT ID) Claim (default ID is a GUID)
+ ///
+ public Func> JtiGenerator =>
+ () => Task.FromResult(Guid.NewGuid().ToString());
+
+ ///
+ /// The signing key to use when generating tokens.
+ ///
+ public SigningCredentials SigningCredentials { get; set; }
+}
\ No newline at end of file
diff --git a/BlueWest.Data.Application/Crypto/Jwt/JwtTokenHandler.cs b/BlueWest.Data.Application/Crypto/Jwt/JwtTokenHandler.cs
new file mode 100644
index 0000000..54820c9
--- /dev/null
+++ b/BlueWest.Data.Application/Crypto/Jwt/JwtTokenHandler.cs
@@ -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;
+
+ ///
+ /// JwtTokenHandler
+ ///
+ public JwtTokenHandler()
+ {
+ _jwtSecurityTokenHandler = new JwtSecurityTokenHandler();
+ }
+
+ ///
+ /// Write token
+ ///
+ ///
+ ///
+ public string WriteToken(JwtSecurityToken jwt)
+ {
+ return _jwtSecurityTokenHandler.WriteToken(jwt);
+ }
+
+ ///
+ /// Validate Token
+ ///
+ ///
+ ///
+ ///
+ ///
+ 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;
+ }
+ }
+}
\ No newline at end of file
diff --git a/BlueWest.Data.Application/SessionToken/SessionCacheItem.cs b/BlueWest.Data.Application/SessionToken/SessionCacheItem.cs
new file mode 100644
index 0000000..7a13659
--- /dev/null
+++ b/BlueWest.Data.Application/SessionToken/SessionCacheItem.cs
@@ -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; }
+ }
+}
+
diff --git a/BlueWest.Data.Application/SessionToken/SessionData.cs b/BlueWest.Data.Application/SessionToken/SessionData.cs
new file mode 100644
index 0000000..49a6a77
--- /dev/null
+++ b/BlueWest.Data.Application/SessionToken/SessionData.cs
@@ -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; }
+ }
+}
+
diff --git a/BlueWest.Data.Application/SessionToken/SessionToken.cs b/BlueWest.Data.Application/SessionToken/SessionToken.cs
new file mode 100644
index 0000000..b73d4ec
--- /dev/null
+++ b/BlueWest.Data.Application/SessionToken/SessionToken.cs
@@ -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; }
+
+ }
+}
+
diff --git a/BlueWest.Data.Application/SessionToken/SessionTokenUnique.cs b/BlueWest.Data.Application/SessionToken/SessionTokenUnique.cs
new file mode 100644
index 0000000..07acccf
--- /dev/null
+++ b/BlueWest.Data.Application/SessionToken/SessionTokenUnique.cs
@@ -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; }
+
+ }
+}
+
diff --git a/BlueWest.Data.Capital/Application/ApplicationUserToken/ApplicationUserToken.cs b/BlueWest.Data.Capital/Application/ApplicationUserToken/ApplicationUserToken.cs
deleted file mode 100644
index 8d88574..0000000
--- a/BlueWest.Data.Capital/Application/ApplicationUserToken/ApplicationUserToken.cs
+++ /dev/null
@@ -1,6 +0,0 @@
-using Microsoft.AspNetCore.Identity;
-
-namespace BlueWest.WebApi.Context.Users;
-
-///
-public class ApplicationUserToken : IdentityUserToken { }
\ No newline at end of file
diff --git a/BlueWest.Data.Capital/BlueWest.Data.Capital.csproj b/BlueWest.Data.Capital/BlueWest.Data.Capital.csproj
index e63e3d0..d216f12 100644
--- a/BlueWest.Data.Capital/BlueWest.Data.Capital.csproj
+++ b/BlueWest.Data.Capital/BlueWest.Data.Capital.csproj
@@ -17,11 +17,12 @@
-
-
+
+
+
diff --git a/BlueWest.Frontend b/BlueWest.Frontend
index 2afc7cc..b67ba4a 160000
--- a/BlueWest.Frontend
+++ b/BlueWest.Frontend
@@ -1 +1 @@
-Subproject commit 2afc7cc1eb60369db3d2db021d514647baba93dc
+Subproject commit b67ba4a1a6bb8a58ee44b34dbadbd6a0e99c5c87
diff --git a/BlueWest.Posts/BlueWest.Posts.csproj b/BlueWest.Posts/BlueWest.Posts.csproj
new file mode 100644
index 0000000..eb2460e
--- /dev/null
+++ b/BlueWest.Posts/BlueWest.Posts.csproj
@@ -0,0 +1,9 @@
+
+
+
+ net6.0
+ enable
+ enable
+
+
+
diff --git a/BlueWest.Posts/PostType.cs b/BlueWest.Posts/PostType.cs
new file mode 100644
index 0000000..22b614a
--- /dev/null
+++ b/BlueWest.Posts/PostType.cs
@@ -0,0 +1,8 @@
+namespace BlueWest.Posts
+{
+ public class PostType
+ {
+
+ }
+}
+
diff --git a/BlueWest.sln b/BlueWest.sln
index 98d9c29..a3fe70c 100644
--- a/BlueWest.sln
+++ b/BlueWest.sln
@@ -36,6 +36,12 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BlueWest.Api.Gateway", "Blu
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BlueWest.EfMethods", "include\BlueWest.EfMethods\src\BlueWest.EfMethods\BlueWest.EfMethods.csproj", "{BBF5E860-A880-450B-B6C9-EF92F6421B3D}"
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
GlobalSection(SolutionConfigurationPlatforms) = preSolution
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}.Release|Any CPU.ActiveCfg = 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
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/BlueWest/Core/Events/EventManager.cs b/BlueWest/Core/Events/EventManager.cs
index b7059c1..724946f 100644
--- a/BlueWest/Core/Events/EventManager.cs
+++ b/BlueWest/Core/Events/EventManager.cs
@@ -1,5 +1,7 @@
using System;
using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
namespace BlueWest.Tools
{
@@ -83,8 +85,8 @@ namespace BlueWest.Tools
#endif
List list = _subscribersList[type];
-
- for (int i=0; i;
diff --git a/BlueWest/Core/Tests/BlueSocketServerSingleton.cs b/BlueWest/Core/Tests/BlueSocketServerSingleton.cs
index 32a20c0..3de810c 100644
--- a/BlueWest/Core/Tests/BlueSocketServerSingleton.cs
+++ b/BlueWest/Core/Tests/BlueSocketServerSingleton.cs
@@ -42,7 +42,7 @@ namespace PerformanceSolution
void StartServer()
{
- string ip = "127.0.0.1";
+ string ip = "127.0.0-preview.7.22376.2.1";
int port = 80;
var server = new TcpListener(IPAddress.Parse(ip), port);
diff --git a/docker-compose.yml b/docker-compose.yml
index ebe09e8..4a61b32 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -27,7 +27,7 @@ services:
context: ./
dockerfile: ./BlueWest.Api/Dockerfile
ports:
- - 8080:80
+ - "8080:80"
environment:
VIRTUAL_HOST: localhost
restart: always
@@ -35,4 +35,11 @@ services:
- db:db
container_name: BW1_API
-
\ No newline at end of file
+ redis:
+ image: "redis:alpine"
+ command: redis-server --requirepass Sup3rSecurePass0rd
+ ports:
+ - "6379:6379"
+
+ environment:
+ - REDIS_REPLICATION_MODE=master