From 1ecbf5e93d4d8b40725c27cf7434f088b3baf4ce Mon Sep 17 00:00:00 2001 From: Matt Date: Fri, 23 May 2025 01:09:59 +0100 Subject: [PATCH] Add back SasToken project. --- Directory.Packages.props | 1 - Elsa.sln | 7 ++ src/modules/Elsa.Http/Elsa.Http.csproj | 2 +- .../Contracts/DataProtectorTokenService.cs | 66 +++++++++++++++++++ .../Elsa.SasTokens/Contracts/ITokenService.cs | 32 +++++++++ .../Elsa.SasTokens/Elsa.SasTokens.csproj | 17 +++++ .../Extensions/ModuleExtensions.cs | 20 ++++++ .../Features/SasTokensFeature.cs | 35 ++++++++++ src/modules/Elsa.SasTokens/FodyWeavers.xml | 3 + 9 files changed, 181 insertions(+), 2 deletions(-) create mode 100644 src/modules/Elsa.SasTokens/Contracts/DataProtectorTokenService.cs create mode 100644 src/modules/Elsa.SasTokens/Contracts/ITokenService.cs create mode 100644 src/modules/Elsa.SasTokens/Elsa.SasTokens.csproj create mode 100644 src/modules/Elsa.SasTokens/Extensions/ModuleExtensions.cs create mode 100644 src/modules/Elsa.SasTokens/Features/SasTokensFeature.cs create mode 100644 src/modules/Elsa.SasTokens/FodyWeavers.xml diff --git a/Directory.Packages.props b/Directory.Packages.props index 7510cde91..b9e3eb784 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -31,7 +31,6 @@ - diff --git a/Elsa.sln b/Elsa.sln index d67f34f16..a6d64300a 100644 --- a/Elsa.sln +++ b/Elsa.sln @@ -260,6 +260,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elsa.Expressions.Liquid", " EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elsa.Expressions.Python", "src\modules\Elsa.Expressions.Python\Elsa.Expressions.Python.csproj", "{9D8FB664-88B4-10BE-58A2-D9A1644AD2E4}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elsa.SasTokens", "src\modules\Elsa.SasTokens\Elsa.SasTokens.csproj", "{A7DE02E3-405B-B6BA-7E47-9E27D4BEFB24}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -444,6 +446,10 @@ Global {9D8FB664-88B4-10BE-58A2-D9A1644AD2E4}.Debug|Any CPU.Build.0 = Debug|Any CPU {9D8FB664-88B4-10BE-58A2-D9A1644AD2E4}.Release|Any CPU.ActiveCfg = Release|Any CPU {9D8FB664-88B4-10BE-58A2-D9A1644AD2E4}.Release|Any CPU.Build.0 = Release|Any CPU + {A7DE02E3-405B-B6BA-7E47-9E27D4BEFB24}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A7DE02E3-405B-B6BA-7E47-9E27D4BEFB24}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A7DE02E3-405B-B6BA-7E47-9E27D4BEFB24}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A7DE02E3-405B-B6BA-7E47-9E27D4BEFB24}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -520,6 +526,7 @@ Global {08B69CC4-B5F0-44E8-FA7A-6BF6F00CA40A} = {6EF07978-A6D2-40EB-891D-7D70C5F37E76} {6AF53651-99F0-1DE0-D37B-4FF6B0348DBB} = {6EF07978-A6D2-40EB-891D-7D70C5F37E76} {9D8FB664-88B4-10BE-58A2-D9A1644AD2E4} = {6EF07978-A6D2-40EB-891D-7D70C5F37E76} + {A7DE02E3-405B-B6BA-7E47-9E27D4BEFB24} = {5948B0A5-7873-4DBB-BA03-EB283D6EA91B} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {D4B5CEAA-7D70-4FCB-A68E-B03FBE5E0E5E} diff --git a/src/modules/Elsa.Http/Elsa.Http.csproj b/src/modules/Elsa.Http/Elsa.Http.csproj index c6c6b1037..810b479a0 100644 --- a/src/modules/Elsa.Http/Elsa.Http.csproj +++ b/src/modules/Elsa.Http/Elsa.Http.csproj @@ -9,7 +9,6 @@ - @@ -22,6 +21,7 @@ + diff --git a/src/modules/Elsa.SasTokens/Contracts/DataProtectorTokenService.cs b/src/modules/Elsa.SasTokens/Contracts/DataProtectorTokenService.cs new file mode 100644 index 000000000..df79897c5 --- /dev/null +++ b/src/modules/Elsa.SasTokens/Contracts/DataProtectorTokenService.cs @@ -0,0 +1,66 @@ +using System.Text.Json; +using Microsoft.AspNetCore.DataProtection; + +namespace Elsa.SasTokens.Contracts; + +/// +/// A service that can create and decrypt SAS (Shared Access Signature) tokens using the service. +/// +public class DataProtectorTokenService : ITokenService +{ + private readonly IDataProtector _dataProtector; + + /// + /// Initializes a new instance of the class. + /// + public DataProtectorTokenService(IDataProtectionProvider dataProtector) + { + _dataProtector = dataProtector.CreateProtector("Elsa Tokens"); + } + + /// + public string CreateToken(T payload, TimeSpan lifetime) + { + var json = JsonSerializer.Serialize(payload); + return _dataProtector.ToTimeLimitedDataProtector().Protect(json, lifetime); + } + + /// + public string CreateToken(T payload, DateTimeOffset expiresAt) + { + var json = JsonSerializer.Serialize(payload); + return _dataProtector.ToTimeLimitedDataProtector().Protect(json, expiresAt); + } + + /// + public string CreateToken(T payload) + { + var json = JsonSerializer.Serialize(payload); + return _dataProtector.Protect(json); + } + + /// + public bool TryDecryptToken(string token, out T payload) + { + payload = default!; + + try + { + payload = DecryptToken(token); + return true; + } + catch + { + // ignored. + } + + return false; + } + + /// + public T DecryptToken(string token) + { + var json = _dataProtector.Unprotect(token); + return JsonSerializer.Deserialize(json)!; + } +} \ No newline at end of file diff --git a/src/modules/Elsa.SasTokens/Contracts/ITokenService.cs b/src/modules/Elsa.SasTokens/Contracts/ITokenService.cs new file mode 100644 index 000000000..8d489d555 --- /dev/null +++ b/src/modules/Elsa.SasTokens/Contracts/ITokenService.cs @@ -0,0 +1,32 @@ +namespace Elsa.SasTokens.Contracts; + +/// +/// A service that can create and decrypt SAS (Shared Access Signature) tokens. +/// +public interface ITokenService +{ + /// + /// Creates a SAS (Shared Access Signature) token containing the specified data. + /// + string CreateToken(T payload, TimeSpan lifetime); + + /// + /// Creates a SAS (Shared Access Signature) token containing the specified data. + /// + string CreateToken(T payload, DateTimeOffset expiresAt); + + /// + /// Creates a SAS (Shared Access Signature) token containing the specified data. + /// + string CreateToken(T payload); + + /// + /// Decrypts the specified SAS token. + /// + T DecryptToken(string token); + + /// + /// Decrypts the specified SAS token. + /// + bool TryDecryptToken(string token, out T payload); +} \ No newline at end of file diff --git a/src/modules/Elsa.SasTokens/Elsa.SasTokens.csproj b/src/modules/Elsa.SasTokens/Elsa.SasTokens.csproj new file mode 100644 index 000000000..eb5ea46cf --- /dev/null +++ b/src/modules/Elsa.SasTokens/Elsa.SasTokens.csproj @@ -0,0 +1,17 @@ + + + + + Provides services to generate SAS tokens. + + elsa module security sas tokens + + + + + + + + + + diff --git a/src/modules/Elsa.SasTokens/Extensions/ModuleExtensions.cs b/src/modules/Elsa.SasTokens/Extensions/ModuleExtensions.cs new file mode 100644 index 000000000..9c42b9c45 --- /dev/null +++ b/src/modules/Elsa.SasTokens/Extensions/ModuleExtensions.cs @@ -0,0 +1,20 @@ +using Elsa.Features.Services; +using Elsa.SasTokens.Features; + +// ReSharper disable once CheckNamespace +namespace Elsa.Extensions; + +/// +/// Provides extensions to install the feature. +/// +public static class ModuleExtensions +{ + /// + /// Install the feature. + /// + public static IModule UseSasTokens(this IModule module, Action? configure = default) + { + module.Configure(configure); + return module; + } +} \ No newline at end of file diff --git a/src/modules/Elsa.SasTokens/Features/SasTokensFeature.cs b/src/modules/Elsa.SasTokens/Features/SasTokensFeature.cs new file mode 100644 index 000000000..2678766e1 --- /dev/null +++ b/src/modules/Elsa.SasTokens/Features/SasTokensFeature.cs @@ -0,0 +1,35 @@ +using Elsa.Features.Abstractions; +using Elsa.Features.Services; +using Elsa.SasTokens.Contracts; +using Microsoft.AspNetCore.DataProtection; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.SasTokens.Features; + +/// +/// Adds the SAS tokens feature to the workflow runtime. +/// +/// +public class SasTokensFeature(IModule module) : FeatureBase(module) +{ + /// + /// Configures the used for setting up data protection. + /// Defaults to setting the application name to "Elsa Workflows". + /// + public Action ConfigureDataProtectionBuilder { get; set; } = b => { b.SetApplicationName("Elsa Workflows"); }; + + /// + /// Factory method to create an instance of . + /// Defaults to creating a using dependency injection. + /// + public Func TokenService { get; set; } = sp => ActivatorUtilities.CreateInstance(sp); + + /// + public override void Apply() + { + var builder = Services.AddDataProtection(); + ConfigureDataProtectionBuilder(builder); + + Services.AddScoped(TokenService); + } +} \ No newline at end of file diff --git a/src/modules/Elsa.SasTokens/FodyWeavers.xml b/src/modules/Elsa.SasTokens/FodyWeavers.xml new file mode 100644 index 000000000..00e1d9a1c --- /dev/null +++ b/src/modules/Elsa.SasTokens/FodyWeavers.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file