Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[NEW-FEATURE] Auto log-out for users #471

Draft
wants to merge 14 commits into
base: dev
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
140 changes: 140 additions & 0 deletions src/Security/Security.sln
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
Microsoft Visual Studio Solution File, Format Version 12.00
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "this", "this.proj", "{838B2E33-ECE5-4D43-83B3-6B4E983A5FD5}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AXOpen.Base.Abstractions", "..\base\src\AXOpen.Base.Abstractions\AXOpen.Base.Abstractions.csproj", "{665D0CFD-82C4-4265-9304-EB14463C5C59}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AXOpen.Data.InMemory", "..\data\src\repositories\InMemory\AXOpen.Data.InMemory.csproj", "{10D92A1A-0B43-464A-BE99-70C47A48118F}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AXOpen.Data.Json", "..\data\src\repositories\Json\AXOpen.Data.Json.csproj", "{5B8F59BA-D635-4ED6-AD9C-30A85BCCDEA2}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AXOpen.Data.MongoDb", "..\data\src\repositories\MongoDb\AXOpen.Data.MongoDb.csproj", "{4F8519F2-4133-4EED-AA2C-F920E16437DF}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AXOpen.Data.RavenDb", "..\data\src\repositories\RavenDb\AXOpen.Data.RavenDb\AXOpen.Data.RavenDb.csproj", "{43311BB6-E215-4C5C-BE8E-E3B645B011F5}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "axopen_blazor_auth_app", "integrations\axopen_blazor_auth_app\axopen_blazor_auth_app.csproj", "{2773528C-64E5-4554-93C4-E02DC7565EDC}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AXOpen.Security.Blazor", "src\AXOpen.Security.Blazor\AXOpen.Security.Blazor.csproj", "{9DB96930-A1F2-4788-A8B7-D1818FD3F760}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AXOpen.Security", "src\AXOpen.Security\AXOpen.Security.csproj", "{9EE55814-E7E8-46D6-8C4B-E1B3AE340346}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AXOpen.SecurityTests_L1", "tests\axopen_security_tests\AXOpen.SecurityTests_L1.csproj", "{2B512EE1-4B9E-44AB-A534-0C9C97FFCB16}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "AXOpen.Base.Abstractions", "..\base\src\AXOpen.Base.Abstractions", "{83F31AB1-B15C-4EF6-8E53-A81A53302521}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "..\base\src", "{411A6B73-18F1-48C5-87D4-826EC2D8D647}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "base", "..\base", "{A925B0D9-3B08-47C2-9D5E-D6A524E190E5}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "InMemory", "..\data\src\repositories\InMemory", "{5D0DFD2A-EA1B-40BD-A673-E8A284E2AF29}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Json", "..\data\src\repositories\Json", "{0D7E1332-22B0-4875-85A5-8EA9538167A7}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "MongoDb", "..\data\src\repositories\MongoDb", "{F8754EB9-301E-4A3F-807A-0AAE58EFAC37}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "AXOpen.Data.RavenDb", "..\data\src\repositories\RavenDb\AXOpen.Data.RavenDb", "{897EA375-B99B-4B86-BF57-0EED905B9458}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "RavenDb", "..\data\src\repositories\RavenDb", "{CEA0B298-02C1-457E-A371-CA25344C7BDC}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "repositories", "..\data\src\repositories", "{B4061626-3263-4217-B0B4-F6B9F0FCD2DD}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "..\data\src", "{0659C482-2A98-4009-94FE-9334B0CA0351}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "data", "..\data", "{B0F5E97B-74A3-45A5-AEAD-3166553D681B}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "axopen_blazor_auth_app", "integrations\axopen_blazor_auth_app", "{E6B1B7C6-58C7-4B90-9112-1AE2DB3BC87C}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "integrations", "integrations", "{36701393-9B31-4C6B-9D41-51431B729B6C}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "AXOpen.Security.Blazor", "src\AXOpen.Security.Blazor", "{5C3885F1-84BF-4D06-A355-EF37AF617ECD}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "AXOpen.Security", "src\AXOpen.Security", "{27B03F8E-58E1-4004-93B5-9604C4AC6F64}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{103CD471-C355-4E98-8930-91D111FBE3A5}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "axopen_security_tests", "tests\axopen_security_tests", "{4E0482DA-A1AE-4724-BA84-CAF28ADF387A}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{BB0B9DEF-C0E9-4C71-9DA9-0184B4E25A50}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Security", "..\Security", "{BA61E15F-E917-4F5B-A66B-9633788E7062}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{838B2E33-ECE5-4D43-83B3-6B4E983A5FD5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{838B2E33-ECE5-4D43-83B3-6B4E983A5FD5}.Debug|Any CPU.Build.0 = Debug|Any CPU
{838B2E33-ECE5-4D43-83B3-6B4E983A5FD5}.Release|Any CPU.ActiveCfg = Release|Any CPU
{838B2E33-ECE5-4D43-83B3-6B4E983A5FD5}.Release|Any CPU.Build.0 = Release|Any CPU
{665D0CFD-82C4-4265-9304-EB14463C5C59}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{665D0CFD-82C4-4265-9304-EB14463C5C59}.Debug|Any CPU.Build.0 = Debug|Any CPU
{665D0CFD-82C4-4265-9304-EB14463C5C59}.Release|Any CPU.ActiveCfg = Release|Any CPU
{665D0CFD-82C4-4265-9304-EB14463C5C59}.Release|Any CPU.Build.0 = Release|Any CPU
{10D92A1A-0B43-464A-BE99-70C47A48118F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{10D92A1A-0B43-464A-BE99-70C47A48118F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{10D92A1A-0B43-464A-BE99-70C47A48118F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{10D92A1A-0B43-464A-BE99-70C47A48118F}.Release|Any CPU.Build.0 = Release|Any CPU
{5B8F59BA-D635-4ED6-AD9C-30A85BCCDEA2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{5B8F59BA-D635-4ED6-AD9C-30A85BCCDEA2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{5B8F59BA-D635-4ED6-AD9C-30A85BCCDEA2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{5B8F59BA-D635-4ED6-AD9C-30A85BCCDEA2}.Release|Any CPU.Build.0 = Release|Any CPU
{4F8519F2-4133-4EED-AA2C-F920E16437DF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4F8519F2-4133-4EED-AA2C-F920E16437DF}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4F8519F2-4133-4EED-AA2C-F920E16437DF}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4F8519F2-4133-4EED-AA2C-F920E16437DF}.Release|Any CPU.Build.0 = Release|Any CPU
{43311BB6-E215-4C5C-BE8E-E3B645B011F5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{43311BB6-E215-4C5C-BE8E-E3B645B011F5}.Debug|Any CPU.Build.0 = Debug|Any CPU
{43311BB6-E215-4C5C-BE8E-E3B645B011F5}.Release|Any CPU.ActiveCfg = Release|Any CPU
{43311BB6-E215-4C5C-BE8E-E3B645B011F5}.Release|Any CPU.Build.0 = Release|Any CPU
{2773528C-64E5-4554-93C4-E02DC7565EDC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{2773528C-64E5-4554-93C4-E02DC7565EDC}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2773528C-64E5-4554-93C4-E02DC7565EDC}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2773528C-64E5-4554-93C4-E02DC7565EDC}.Release|Any CPU.Build.0 = Release|Any CPU
{9DB96930-A1F2-4788-A8B7-D1818FD3F760}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{9DB96930-A1F2-4788-A8B7-D1818FD3F760}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9DB96930-A1F2-4788-A8B7-D1818FD3F760}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9DB96930-A1F2-4788-A8B7-D1818FD3F760}.Release|Any CPU.Build.0 = Release|Any CPU
{9EE55814-E7E8-46D6-8C4B-E1B3AE340346}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{9EE55814-E7E8-46D6-8C4B-E1B3AE340346}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9EE55814-E7E8-46D6-8C4B-E1B3AE340346}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9EE55814-E7E8-46D6-8C4B-E1B3AE340346}.Release|Any CPU.Build.0 = Release|Any CPU
{2B512EE1-4B9E-44AB-A534-0C9C97FFCB16}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{2B512EE1-4B9E-44AB-A534-0C9C97FFCB16}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2B512EE1-4B9E-44AB-A534-0C9C97FFCB16}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2B512EE1-4B9E-44AB-A534-0C9C97FFCB16}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{665D0CFD-82C4-4265-9304-EB14463C5C59} = {83F31AB1-B15C-4EF6-8E53-A81A53302521}
{83F31AB1-B15C-4EF6-8E53-A81A53302521} = {411A6B73-18F1-48C5-87D4-826EC2D8D647}
{411A6B73-18F1-48C5-87D4-826EC2D8D647} = {A925B0D9-3B08-47C2-9D5E-D6A524E190E5}
{10D92A1A-0B43-464A-BE99-70C47A48118F} = {5D0DFD2A-EA1B-40BD-A673-E8A284E2AF29}
{5D0DFD2A-EA1B-40BD-A673-E8A284E2AF29} = {B4061626-3263-4217-B0B4-F6B9F0FCD2DD}
{5B8F59BA-D635-4ED6-AD9C-30A85BCCDEA2} = {0D7E1332-22B0-4875-85A5-8EA9538167A7}
{0D7E1332-22B0-4875-85A5-8EA9538167A7} = {B4061626-3263-4217-B0B4-F6B9F0FCD2DD}
{4F8519F2-4133-4EED-AA2C-F920E16437DF} = {F8754EB9-301E-4A3F-807A-0AAE58EFAC37}
{F8754EB9-301E-4A3F-807A-0AAE58EFAC37} = {B4061626-3263-4217-B0B4-F6B9F0FCD2DD}
{43311BB6-E215-4C5C-BE8E-E3B645B011F5} = {897EA375-B99B-4B86-BF57-0EED905B9458}
{897EA375-B99B-4B86-BF57-0EED905B9458} = {CEA0B298-02C1-457E-A371-CA25344C7BDC}
{CEA0B298-02C1-457E-A371-CA25344C7BDC} = {B4061626-3263-4217-B0B4-F6B9F0FCD2DD}
{B4061626-3263-4217-B0B4-F6B9F0FCD2DD} = {0659C482-2A98-4009-94FE-9334B0CA0351}
{0659C482-2A98-4009-94FE-9334B0CA0351} = {B0F5E97B-74A3-45A5-AEAD-3166553D681B}
{2773528C-64E5-4554-93C4-E02DC7565EDC} = {E6B1B7C6-58C7-4B90-9112-1AE2DB3BC87C}
{E6B1B7C6-58C7-4B90-9112-1AE2DB3BC87C} = {36701393-9B31-4C6B-9D41-51431B729B6C}
{36701393-9B31-4C6B-9D41-51431B729B6C} = {BA61E15F-E917-4F5B-A66B-9633788E7062}
{9DB96930-A1F2-4788-A8B7-D1818FD3F760} = {5C3885F1-84BF-4D06-A355-EF37AF617ECD}
{5C3885F1-84BF-4D06-A355-EF37AF617ECD} = {103CD471-C355-4E98-8930-91D111FBE3A5}
{9EE55814-E7E8-46D6-8C4B-E1B3AE340346} = {27B03F8E-58E1-4004-93B5-9604C4AC6F64}
{27B03F8E-58E1-4004-93B5-9604C4AC6F64} = {103CD471-C355-4E98-8930-91D111FBE3A5}
{103CD471-C355-4E98-8930-91D111FBE3A5} = {BA61E15F-E917-4F5B-A66B-9633788E7062}
{2B512EE1-4B9E-44AB-A534-0C9C97FFCB16} = {4E0482DA-A1AE-4724-BA84-CAF28ADF387A}
{4E0482DA-A1AE-4724-BA84-CAF28ADF387A} = {BB0B9DEF-C0E9-4C71-9DA9-0184B4E25A50}
{BB0B9DEF-C0E9-4C71-9DA9-0184B4E25A50} = {BA61E15F-E917-4F5B-A66B-9633788E7062}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {E8038EAD-B369-4907-89AB-2FB3776B456B}
EndGlobalSection
EndGlobal
8 changes: 8 additions & 0 deletions src/Security/slngen.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
dotnet slngen this.proj -o this.sln --folders true --launch false

$currentDirectory = Split-Path -Leaf (Get-Location).Path
$sourceFile = Join-Path (Get-Location).Path "this.sln"
$destinationFile = Join-Path (Get-Location).Path ("$currentDirectory.sln")
if (Test-Path $sourceFile) {
Copy-Item -Path $sourceFile -Destination $destinationFile -Force
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
using System.Security.Claims;
using AxOpen.Security.Entities;
using AXOpen;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Components.Authorization;
using Microsoft.AspNetCore.Components.Server;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;

namespace AxOpen.Security.Services
{
//Server-side AuthenticationStateProvider that revalidates the security stamp for the connected user
// every 30 seconds an interactive circuit is connected.
internal sealed class IdentityRevalidatingAuthenticationStateProvider(
ILoggerFactory loggerFactory,
IServiceScopeFactory scopeFactory,
IUserStore<User> userStore,
IOptions<IdentityOptions> options
)
: RevalidatingServerAuthenticationStateProvider(loggerFactory)
{
protected override TimeSpan RevalidationInterval => TimeSpan.FromSeconds(30);

protected override async Task<bool> ValidateAuthenticationStateAsync(
AuthenticationState authenticationState, CancellationToken cancellationToken)
{
// Get the user manager from a new scope to ensure it fetches fresh data
await using var scope = scopeFactory.CreateAsyncScope();

var userManager = scope.ServiceProvider.GetRequiredService<UserManager<User>>();

ClaimsPrincipal userPrincipal = (authenticationState.User as ClaimsPrincipal);

if (userPrincipal == null) { return false; }

var c = new CancellationTokenSource();

var user = await userStore.FindByNameAsync(userPrincipal.Identity.Name, c.Token);

if (user == null) return false; // user was deleted

if (user.EnableAutoLogOut)
{
var httpContext = scope.ServiceProvider.GetRequiredService<IHttpContextAccessor>().HttpContext;

if (httpContext == null) return false;

var ticket = await httpContext.AuthenticateAsync("Identity.Application");

if (!ticket.Succeeded)
{
AxoApplication.Current.Logger.Warning($"User ticket does not exist for '{user.UserName}' and the user will be logged out!", userPrincipal.Identity);

return false;
}

var start = ticket.Properties.IssuedUtc.Value;

//todo - fix
if (DateTime.UtcNow > (start + TimeSpan.FromMinutes(user.AutoLogOutTimeOutMinutes)))
{
AxoApplication.Current.Logger.Information($"User '{user.UserName}' has reached the logout timeout and will be logged out!", userPrincipal.Identity);
return false;
}
}

return await ValidateSecurityStampAsync(userManager, authenticationState.User, user);
}

/// <summary>
/// check user if user have changed sensitive information (password, role ....)
/// </summary>
/// <param name="userManager"></param>
/// <param name="principal"></param>
/// <param name="user"></param>
/// <returns></returns>
private async Task<bool> ValidateSecurityStampAsync(UserManager<User> userManager, ClaimsPrincipal principal, User user)
{
var principalStamp = principal.FindFirstValue(options.Value.ClaimsIdentity.SecurityStampClaimType);

if (principal.Identity is not { IsAuthenticated: true })
{
return false;
}

// !!! BUG !!! - tcopen repo do not find user => record key Tcopen/Sql? => EntityId / Id
// var user = await userManager.GetUserAsync(principal);

if (user is null)
{
return false;
}
else if (!userManager.SupportsUserSecurityStamp)
{
return true;
}
else
{
var userStamp = await userManager.GetSecurityStampAsync(user);

var equals = principalStamp == userStamp;
if (!equals)
{
AxoApplication.Current.Logger.Warning($"User security stamp was changed for '{user.UserName}', and the user will be logged out!", principal.Identity);
}

return equals;
}
}
}
}
38 changes: 38 additions & 0 deletions src/Security/src/AXOpen.Security/Services/InactivityService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
//namespace AxOpen.Security.Services
//{
// public class InactivityService : IDisposable
// {
// private TimeSpan _timeoutPeriod { get; set; }
// private Timer? _timer { get; set; }
// private Action _onTimeout { get; set; }

// public void StartMonitoring(TimeSpan timeoutPeriod)
// {
// _timeoutPeriod = timeoutPeriod;

// _timer = new Timer(OnTimerElapsed, null, _timeoutPeriod, Timeout.InfiniteTimeSpan);
// }

// private void OnTimerElapsed(object state)
// {
// _onTimeout?.Invoke();
// _timer?.Dispose();
// }

// public void SetAction(Action onTimeout)
// {
// _onTimeout = onTimeout;
// }

// public void Reset()
// {
// if (_timer != null)
// _timer.Change(_timeoutPeriod, Timeout.InfiniteTimeSpan);
// }

// public void Dispose()
// {
// _timer?.Dispose();
// }
// }
//}
5 changes: 5 additions & 0 deletions src/Security/this.proj
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<Project Sdk="Microsoft.Build.Traversal/4.1.0">
<ItemGroup>
<ProjectReference Include="**\*.csproj" Exclude="**\.apax\**"/>
</ItemGroup>
</Project>
Loading
Loading