diff options
author | vnugent <public@vaughnnugent.com> | 2023-01-12 17:47:41 -0500 |
---|---|---|
committer | vnugent <public@vaughnnugent.com> | 2023-01-12 17:47:41 -0500 |
commit | 751e1a107195f0c9c98c866e8267a5a760545982 (patch) | |
tree | 71a775c91bfd9d455b727c72d2fb628c530f64cc /Libs | |
parent | 9bb5ddd8f19c0ecabd7af4ee58d80c16826bc183 (diff) |
Large project reorder and consolidation
Diffstat (limited to 'Libs')
37 files changed, 0 insertions, 3302 deletions
diff --git a/Libs/VNLib.Plugins.Essentials.Sessions.OAuth/Endpoints/AccessTokenEndpoint.cs b/Libs/VNLib.Plugins.Essentials.Sessions.OAuth/Endpoints/AccessTokenEndpoint.cs deleted file mode 100644 index d968398..0000000 --- a/Libs/VNLib.Plugins.Essentials.Sessions.OAuth/Endpoints/AccessTokenEndpoint.cs +++ /dev/null @@ -1,194 +0,0 @@ -/* -* Copyright (c) 2022 Vaughn Nugent -* -* Library: VNLib -* Package: VNLib.Plugins.Essentials.Sessions.OAuth -* File: AccessTokenEndpoint.cs -* -* AccessTokenEndpoint.cs is part of VNLib.Plugins.Essentials.Sessions.OAuth which is part of the larger -* VNLib collection of libraries and utilities. -* -* VNLib.Plugins.Essentials.Sessions.OAuth is free software: you can redistribute it and/or modify -* it under the terms of the GNU Affero General Public License as -* published by the Free Software Foundation, either version 3 of the -* License, or (at your option) any later version. -* -* VNLib.Plugins.Essentials.Sessions.OAuth is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU Affero General Public License for more details. -* -* You should have received a copy of the GNU Affero General Public License -* along with this program. If not, see https://www.gnu.org/licenses/. -*/ - -using System; -using System.Net; -using System.Text.Json; - -using VNLib.Utils.Memory; -using VNLib.Hashing.IdentityUtility; -using VNLib.Plugins.Essentials.Oauth; -using VNLib.Plugins.Essentials.Endpoints; -using VNLib.Plugins.Essentials.Oauth.Tokens; -using VNLib.Plugins.Essentials.Oauth.Applications; -using VNLib.Plugins.Essentials.Extensions; -using VNLib.Plugins.Extensions.Loading; -using VNLib.Plugins.Extensions.Loading.Sql; -using VNLib.Plugins.Extensions.Validation; - -namespace VNLib.Plugins.Essentials.Sessions.OAuth.Endpoints -{ - delegate Task<IOAuth2TokenResult?> CreateTokenImpl(HttpEntity ev, UserApplication application, CancellationToken cancellation = default); - - /// <summary> - /// Grants authorization to OAuth2 clients to protected resources - /// with access tokens - /// </summary> - internal sealed class AccessTokenEndpoint : ResourceEndpointBase - { - - private readonly CreateTokenImpl CreateToken; - private readonly ApplicationStore Applications; - - private readonly Task<JsonDocument?> JWTVerificationKey; - - //override protection settings to allow most connections to authenticate - protected override ProtectionSettings EndpointProtectionSettings { get; } = new() - { - DisableBrowsersOnly = true, - DisableSessionsRequired = true, - DisableVerifySessionCors = true - }; - - public AccessTokenEndpoint(string path, PluginBase pbase, CreateTokenImpl tokenStore, Task<JsonDocument?> verificationKey) - { - InitPathAndLog(path, pbase.Log); - CreateToken = tokenStore; - Applications = new(pbase.GetContextOptions(), pbase.GetPasswords()); - JWTVerificationKey = verificationKey; - } - - - protected override async ValueTask<VfReturnType> PostAsync(HttpEntity entity) - { - //Check for refresh token - if (entity.RequestArgs.IsArgumentSet("grant_type", "refresh_token")) - { - //process a refresh token - } - - //See if we have an application authorized with JWT - else if (entity.RequestArgs.IsArgumentSet("grant_type", "application")) - { - if(entity.RequestArgs.TryGetNonEmptyValue("token", out string? appJwt)) - { - //Try to get and verify the app - UserApplication? app = GetApplicationFromJwt(appJwt); - - //generate token - return await GenerateTokenAsync(entity, app); - } - } - - //Check for grant_type parameter from the request body - else if (entity.RequestArgs.IsArgumentSet("grant_type", "client_credentials")) - { - //Get client id and secret (and make sure theyre not empty - if (entity.RequestArgs.TryGetNonEmptyValue("client_id", out string? clientId) && - entity.RequestArgs.TryGetNonEmptyValue("client_secret", out string? secret)) - { - - if (!ValidatorExtensions.OnlyAlphaNumRegx.IsMatch(clientId)) - { - //respond with error message - entity.CloseResponseError(HttpStatusCode.UnprocessableEntity, ErrorType.InvalidRequest, "Invalid client_id"); - return VfReturnType.VirtualSkip; - } - if (!ValidatorExtensions.OnlyAlphaNumRegx.IsMatch(secret)) - { - //respond with error message - entity.CloseResponseError(HttpStatusCode.UnprocessableEntity, ErrorType.InvalidRequest, "Invalid client_secret"); - return VfReturnType.VirtualSkip; - } - - //Convert the clientid and secret to lowercase - clientId = clientId.ToLower(); - secret = secret.ToLower(); - - //Convert secret to private string that is unreferrenced - using PrivateString secretPv = new(secret, false); - - //Get the application from apps store - UserApplication? app = await Applications.VerifyAppAsync(clientId, secretPv); - - return await GenerateTokenAsync(entity, app); - } - } - - entity.CloseResponseError(HttpStatusCode.BadRequest, ErrorType.InvalidClient, "Invalid grant type"); - //Default to bad request - return VfReturnType.VirtualSkip; - } - - private UserApplication? GetApplicationFromJwt(string jwtData) - { - //Not enabled - if (JWTVerificationKey?.Result == null) - { - return null; - } - - //Parse application token - using JsonWebToken jwt = JsonWebToken.Parse(jwtData); - - //verify the application jwt - if (!jwt.VerifyFromJwk(JWTVerificationKey.Result.RootElement)) - { - return null; - } - - using JsonDocument doc = jwt.GetPayload(); - - //Get expiration time - DateTimeOffset exp = doc.RootElement.GetProperty("exp").GetDateTimeOffset(); - - //Check if token is expired - return exp < DateTimeOffset.UtcNow ? null : UserApplication.FromJwtDoc(doc.RootElement); - } - - - private async Task<VfReturnType> GenerateTokenAsync(HttpEntity entity, UserApplication? app) - { - if (app == null) - { - //App was not found or the credentials do not match - entity.CloseResponseError(HttpStatusCode.UnprocessableEntity, ErrorType.InvalidClient, "The credentials are invalid or do not exist"); - return VfReturnType.VirtualSkip; - } - - IOAuth2TokenResult? result = await CreateToken(entity, app, entity.EventCancellation); - - if (result == null) - { - entity.CloseResponseError(HttpStatusCode.TooManyRequests, ErrorType.TemporarilyUnabavailable, "You have reached the maximum number of valid tokens for this application"); - return VfReturnType.VirtualSkip; - } - - //Create the new response message - OauthTokenResponseMessage tokenMessage = new() - { - AccessToken = result.AccessToken, - IdToken = result.IdentityToken, - //set expired as seconds in int form - Expires = result.ExpiresSeconds, - RefreshToken = result.RefreshToken, - TokenType = result.TokenType - }; - - //Respond with the token message - entity.CloseResponseJson(HttpStatusCode.OK, tokenMessage); - return VfReturnType.VirtualSkip; - } - } -}
\ No newline at end of file diff --git a/Libs/VNLib.Plugins.Essentials.Sessions.OAuth/Endpoints/RevocationEndpoint.cs b/Libs/VNLib.Plugins.Essentials.Sessions.OAuth/Endpoints/RevocationEndpoint.cs deleted file mode 100644 index d981f69..0000000 --- a/Libs/VNLib.Plugins.Essentials.Sessions.OAuth/Endpoints/RevocationEndpoint.cs +++ /dev/null @@ -1,55 +0,0 @@ -/* -* Copyright (c) 2022 Vaughn Nugent -* -* Library: VNLib -* Package: VNLib.Plugins.Essentials.Sessions.OAuth -* File: RevocationEndpoint.cs -* -* RevocationEndpoint.cs is part of VNLib.Plugins.Essentials.Sessions.OAuth which is part of the larger -* VNLib collection of libraries and utilities. -* -* VNLib.Plugins.Essentials.Sessions.OAuth is free software: you can redistribute it and/or modify -* it under the terms of the GNU Affero General Public License as -* published by the Free Software Foundation, either version 3 of the -* License, or (at your option) any later version. -* -* VNLib.Plugins.Essentials.Sessions.OAuth is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU Affero General Public License for more details. -* -* You should have received a copy of the GNU Affero General Public License -* along with this program. If not, see https://www.gnu.org/licenses/. -*/ - -using System; -using System.Text.Json; - -using VNLib.Plugins.Essentials.Oauth; -using VNLib.Plugins.Extensions.Loading; - -namespace VNLib.Plugins.Essentials.Sessions.OAuth.Endpoints -{ - /// <summary> - /// An OAuth2 authorized endpoint for revoking the access token - /// held by the current connection - /// </summary> - [ConfigurationName("o2_revocation_endpoint")] - internal class RevocationEndpoint : O2EndpointBase - { - - public RevocationEndpoint(PluginBase pbase, IReadOnlyDictionary<string, JsonElement> config) - { - string? path = config["path"].GetString(); - InitPathAndLog(path, pbase.Log); - } - - protected override VfReturnType Post(HttpEntity entity) - { - //Revoke the access token, by invalidating it - entity.Session.Invalidate(); - entity.CloseResponse(System.Net.HttpStatusCode.OK); - return VfReturnType.VirtualSkip; - } - } -} diff --git a/Libs/VNLib.Plugins.Essentials.Sessions.OAuth/IOauthSessionIdFactory.cs b/Libs/VNLib.Plugins.Essentials.Sessions.OAuth/IOauthSessionIdFactory.cs deleted file mode 100644 index 4878804..0000000 --- a/Libs/VNLib.Plugins.Essentials.Sessions.OAuth/IOauthSessionIdFactory.cs +++ /dev/null @@ -1,63 +0,0 @@ -/* -* Copyright (c) 2022 Vaughn Nugent -* -* Library: VNLib -* Package: VNLib.Plugins.Essentials.Sessions.OAuth -* File: IOauthSessionIdFactory.cs -* -* IOauthSessionIdFactory.cs is part of VNLib.Plugins.Essentials.Sessions.OAuth which is part of the larger -* VNLib collection of libraries and utilities. -* -* VNLib.Plugins.Essentials.Sessions.OAuth is free software: you can redistribute it and/or modify -* it under the terms of the GNU Affero General Public License as -* published by the Free Software Foundation, either version 3 of the -* License, or (at your option) any later version. -* -* VNLib.Plugins.Essentials.Sessions.OAuth is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU Affero General Public License for more details. -* -* You should have received a copy of the GNU Affero General Public License -* along with this program. If not, see https://www.gnu.org/licenses/. -*/ - -using System; - -using VNLib.Net.Http; -using VNLib.Plugins.Essentials.Oauth.Applications; -using VNLib.Plugins.Essentials.Sessions.Runtime; -using VNLib.Plugins.Sessions.Cache.Client; - -namespace VNLib.Plugins.Essentials.Sessions.OAuth -{ - public interface IOauthSessionIdFactory : ISessionIdFactory - { - /// <summary> - /// The maxium number of tokens allowed to be created per OAuth application - /// </summary> - int MaxTokensPerApp { get; } - /// <summary> - /// Allows for custom configuration of the newly created session and - /// the <see cref="IHttpEvent"/> its attached to - /// </summary> - /// <param name="session">The newly created session</param> - /// <param name="app">The application associated with the session</param> - /// <param name="entity">The http event that generated the new session</param> - void InitNewSession(RemoteSession session, UserApplication app, IHttpEvent entity); - /// <summary> - /// The time a session is valid for - /// </summary> - TimeSpan SessionValidFor { get; } - /// <summary> - /// Called when the session provider wishes to generate a new session - /// and required credential information to generate the new session - /// </summary> - /// <returns>The information genreated for the news ession</returns> - TokenAndSessionIdResult GenerateTokensAndId(); - /// <summary> - /// The type of token this session provider generates - /// </summary> - string TokenType { get; } - } -} diff --git a/Libs/VNLib.Plugins.Essentials.Sessions.OAuth/O2AuthenticationPluginEntry.cs b/Libs/VNLib.Plugins.Essentials.Sessions.OAuth/O2AuthenticationPluginEntry.cs deleted file mode 100644 index 4a48f8b..0000000 --- a/Libs/VNLib.Plugins.Essentials.Sessions.OAuth/O2AuthenticationPluginEntry.cs +++ /dev/null @@ -1,60 +0,0 @@ -/* -* Copyright (c) 2022 Vaughn Nugent -* -* Library: VNLib -* Package: VNLib.Plugins.Essentials.Sessions.OAuth -* File: O2AuthenticationPluginEntry.cs -* -* O2AuthenticationPluginEntry.cs is part of VNLib.Plugins.Essentials.Sessions.OAuth which is part of the larger -* VNLib collection of libraries and utilities. -* -* VNLib.Plugins.Essentials.Sessions.OAuth is free software: you can redistribute it and/or modify -* it under the terms of the GNU Affero General Public License as -* published by the Free Software Foundation, either version 3 of the -* License, or (at your option) any later version. -* -* VNLib.Plugins.Essentials.Sessions.OAuth is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU Affero General Public License for more details. -* -* You should have received a copy of the GNU Affero General Public License -* along with this program. If not, see https://www.gnu.org/licenses/. -*/ - -using VNLib.Utils.Logging; -using VNLib.Plugins.Essentials.Sessions.Runtime; - - -namespace VNLib.Plugins.Essentials.Sessions.Oauth -{ - public sealed class O2AuthenticationPluginEntry : PluginBase - { - public override string PluginName => "Essentials.Oauth.Authentication"; - - private readonly O2SessionProviderEntry SessionProvider = new(); - - protected override void OnLoad() - { - try - { - //Load the session provider, that will only load the endpoints - (SessionProvider as IRuntimeSessionProvider).Load(this, Log); - } - catch(KeyNotFoundException kne) - { - Log.Error("Missing required configuration keys {err}", kne.Message); - } - } - - protected override void OnUnLoad() - { - Log.Information("Plugin unloaded"); - } - - protected override void ProcessHostCommand(string cmd) - { - throw new NotImplementedException(); - } - } -}
\ No newline at end of file diff --git a/Libs/VNLib.Plugins.Essentials.Sessions.OAuth/O2SessionProviderEntry.cs b/Libs/VNLib.Plugins.Essentials.Sessions.OAuth/O2SessionProviderEntry.cs deleted file mode 100644 index 7e72714..0000000 --- a/Libs/VNLib.Plugins.Essentials.Sessions.OAuth/O2SessionProviderEntry.cs +++ /dev/null @@ -1,124 +0,0 @@ -/* -* Copyright (c) 2022 Vaughn Nugent -* -* Library: VNLib -* Package: VNLib.Plugins.Essentials.Sessions.OAuth -* File: O2SessionProviderEntry.cs -* -* O2SessionProviderEntry.cs is part of VNLib.Plugins.Essentials.Sessions.OAuth which is part of the larger -* VNLib collection of libraries and utilities. -* -* VNLib.Plugins.Essentials.Sessions.OAuth is free software: you can redistribute it and/or modify -* it under the terms of the GNU Affero General Public License as -* published by the Free Software Foundation, either version 3 of the -* License, or (at your option) any later version. -* -* VNLib.Plugins.Essentials.Sessions.OAuth is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU Affero General Public License for more details. -* -* You should have received a copy of the GNU Affero General Public License -* along with this program. If not, see https://www.gnu.org/licenses/. -*/ - -using System.Text.Json; - -using VNLib.Net.Http; -using VNLib.Utils.Logging; -using VNLib.Utils.Extensions; -using VNLib.Data.Caching; -using VNLib.Plugins.Sessions.Cache.Client; -using VNLib.Plugins.Essentials.Oauth.Tokens; -using VNLib.Plugins.Essentials.Oauth.Applications; -using VNLib.Plugins.Essentials.Sessions.OAuth; -using VNLib.Plugins.Essentials.Sessions.Runtime; -using VNLib.Plugins.Essentials.Sessions.OAuth.Endpoints; -using VNLib.Plugins.Extensions.VNCache; -using VNLib.Plugins.Extensions.Loading; -using VNLib.Plugins.Extensions.Loading.Routing; -using VNLib.Plugins.Extensions.Loading.Sql; -using VNLib.Plugins.Extensions.Loading.Events; - - -namespace VNLib.Plugins.Essentials.Sessions.Oauth -{ - - public sealed class O2SessionProviderEntry : IRuntimeSessionProvider - { - const string OAUTH2_CONFIG_KEY = "oauth2"; - - private OAuth2SessionProvider? _sessions; - - bool IRuntimeSessionProvider.CanProcess(IHttpEvent entity) - { - //If authorization header is set try to process as oauth2 session - return _sessions != null && entity.Server.Headers.HeaderSet(System.Net.HttpRequestHeader.Authorization); - } - - ValueTask<SessionHandle> ISessionProvider.GetSessionAsync(IHttpEvent entity, CancellationToken cancellationToken) - { - return _sessions!.GetSessionAsync(entity, cancellationToken); - } - - - void IRuntimeSessionProvider.Load(PluginBase plugin, ILogProvider localized) - { - IReadOnlyDictionary<string, JsonElement> oauth2Config = plugin.GetConfigForType<OAuth2SessionProvider>(); - - //Optional application jwt token - Task<JsonDocument?> jwtTokenSecret = plugin.TryGetSecretAsync("application_token_key") - .ContinueWith(static t => t.Result?.GetJsonDocument(), TaskScheduler.Default); - - //Access token endpoint is optional - if (oauth2Config.TryGetValue("token_path", out JsonElement el)) - { - //Init auth endpoint - AccessTokenEndpoint authEp = new(el.GetString()!, plugin, CreateTokenDelegateAsync, jwtTokenSecret); - - //route auth endpoint - plugin.Route(authEp); - } - - //Optional revocation endpoint - if (plugin.HasConfigForType<RevocationEndpoint>()) - { - //Route revocation endpoint - plugin.Route<RevocationEndpoint>(); - } - - int cacheLimit = oauth2Config["cache_size"].GetInt32(); - int maxTokensPerApp = oauth2Config["max_tokens_per_app"].GetInt32(); - int sessionIdSize = (int)oauth2Config["access_token_size"].GetUInt32(); - TimeSpan tokenValidFor = oauth2Config["token_valid_for_sec"].GetTimeSpan(TimeParseType.Seconds); - TimeSpan cleanupInterval = oauth2Config["gc_interval_sec"].GetTimeSpan(TimeParseType.Seconds); - string sessionIdPrefix = oauth2Config["cache_prefix"].GetString() ?? throw new KeyNotFoundException($"Missing required key 'cache_prefix' in '{OAUTH2_CONFIG_KEY}' config"); - - //init the id provider - OAuth2SessionIdProvider idProv = new(sessionIdPrefix, maxTokensPerApp, sessionIdSize, tokenValidFor); - - //Get shared global-cache - IGlobalCacheProvider globalCache = plugin.GetGlobalCache(); - - //Create cache store from global cache - GlobalCacheStore cacheStore = new(globalCache); - - //Init session provider now that client is loaded - _sessions = new(cacheStore, cacheLimit, 100, idProv, plugin.GetContextOptions()); - - //Schedule cleanup interval with the plugin scheduler - plugin.ScheduleInterval(_sessions, cleanupInterval); - - //Wait and cleanup expired sessions - _ = plugin.DeferTask(() => _sessions.CleanupExpiredSessionsAsync(localized, plugin.UnloadToken), 1000); - - localized.Information("Session provider loaded"); - - } - - private async Task<IOAuth2TokenResult?> CreateTokenDelegateAsync(HttpEntity entity, UserApplication app, CancellationToken cancellation) - { - return await _sessions!.CreateAccessTokenAsync(entity, app, cancellation).ConfigureAwait(false); - } - } -}
\ No newline at end of file diff --git a/Libs/VNLib.Plugins.Essentials.Sessions.OAuth/OAuth2Session.cs b/Libs/VNLib.Plugins.Essentials.Sessions.OAuth/OAuth2Session.cs deleted file mode 100644 index e9a69cd..0000000 --- a/Libs/VNLib.Plugins.Essentials.Sessions.OAuth/OAuth2Session.cs +++ /dev/null @@ -1,131 +0,0 @@ -/* -* Copyright (c) 2022 Vaughn Nugent -* -* Library: VNLib -* Package: VNLib.Plugins.Essentials.Sessions.OAuth -* File: OAuth2Session.cs -* -* OAuth2Session.cs is part of VNLib.Plugins.Essentials.Sessions.OAuth which is part of the larger -* VNLib collection of libraries and utilities. -* -* VNLib.Plugins.Essentials.Sessions.OAuth is free software: you can redistribute it and/or modify -* it under the terms of the GNU Affero General Public License as -* published by the Free Software Foundation, either version 3 of the -* License, or (at your option) any later version. -* -* VNLib.Plugins.Essentials.Sessions.OAuth is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU Affero General Public License for more details. -* -* You should have received a copy of the GNU Affero General Public License -* along with this program. If not, see https://www.gnu.org/licenses/. -*/ - -using System; - -using VNLib.Net.Http; -using VNLib.Plugins.Sessions.Cache.Client; -using VNLib.Plugins.Sessions.Cache.Client.Exceptions; - -using static VNLib.Plugins.Essentials.Sessions.ISessionExtensions; - -namespace VNLib.Plugins.Essentials.Sessions.OAuth -{ - /// <summary> - /// The implementation of the OAuth2 session container for HTTP sessions - /// </summary> - internal sealed class OAuth2Session : RemoteSession - { - private readonly Action<OAuth2Session> InvalidateCache; - - /// <summary> - /// Initalizes a new <see cref="OAuth2Session"/> - /// </summary> - /// <param name="sessionId">The session id (or token)</param> - /// <param name="client">The <see cref="IRemoteCacheStore"/> used as the backing cache provider</param> - /// <param name="backgroundTimeOut">The ammount of time to wait for a background operation (delete, update, get)</param> - /// <param name="invalidCache">Called when the session has been marked as invalid and the close even hook is being executed</param> - public OAuth2Session(string sessionId, IRemoteCacheStore client, TimeSpan backgroundTimeOut, Action<OAuth2Session> invalidCache) - : base(sessionId, client, backgroundTimeOut) - { - InvalidateCache = invalidCache; - IsInvalid = false; - } - - public bool IsInvalid { get; private set; } - - - ///<inheritdoc/> - ///<exception cref="NotSupportedException"></exception> - public override string Token - { - get => throw new NotSupportedException("Token property is not supported for OAuth2 sessions"); - set => throw new NotSupportedException("Token property is not supported for OAuth2 sessions"); - } - - ///<inheritdoc/> - protected override void IndexerSet(string key, string value) - { - //Guard protected entires - switch (key) - { - case TOKEN_ENTRY: - case LOGIN_TOKEN_ENTRY: - throw new InvalidOperationException("Token entry may not be changed!"); - } - base.IndexerSet(key, value); - } - ///<inheritdoc/> - ///<exception cref="SessionStatusException"></exception> - public override async Task WaitAndLoadAsync(IHttpEvent entity, CancellationToken token = default) - { - //Wait to enter lock - await base.WaitAndLoadAsync(entity, token); - if (IsInvalid) - { - //Release lock - MainLock.Release(); - throw new SessionStatusException("The session has been invalidated"); - } - //Set session type - if (IsNew) - { - SessionType = SessionType.OAuth2; - } - } - ///<inheritdoc/> - protected override async ValueTask<Task?> UpdateResource(bool isAsync, IHttpEvent state) - { - Task? result = null; - //Invalid flag is set, so exit - if (IsInvalid) - { - result = Task.CompletedTask; - } - //Check flags in priority level, Invalid is highest state priority - else if (Flags.IsSet(INVALID_MSK)) - { - //Clear all stored values - DataStore!.Clear(); - //Delete the entity syncronously - await ProcessDeleteAsync(); - //Set invalid flag - IsInvalid = true; - //Invlidate cache - InvalidateCache(this); - result = Task.CompletedTask; - } - else if (Flags.IsSet(MODIFIED_MSK)) - { - //Send update to server - result = Task.Run(ProcessUpdateAsync); - } - - //Clear all flags - Flags.ClearAll(); - - return result; - } - } -} diff --git a/Libs/VNLib.Plugins.Essentials.Sessions.OAuth/OAuth2SessionIdProvider.cs b/Libs/VNLib.Plugins.Essentials.Sessions.OAuth/OAuth2SessionIdProvider.cs deleted file mode 100644 index e1cfc7f..0000000 --- a/Libs/VNLib.Plugins.Essentials.Sessions.OAuth/OAuth2SessionIdProvider.cs +++ /dev/null @@ -1,132 +0,0 @@ -/* -* Copyright (c) 2022 Vaughn Nugent -* -* Library: VNLib -* Package: VNLib.Plugins.Essentials.Sessions.OAuth -* File: OAuth2SessionIdProvider.cs -* -* OAuth2SessionIdProvider.cs is part of VNLib.Plugins.Essentials.Sessions.OAuth which is part of the larger -* VNLib collection of libraries and utilities. -* -* VNLib.Plugins.Essentials.Sessions.OAuth is free software: you can redistribute it and/or modify -* it under the terms of the GNU Affero General Public License as -* published by the Free Software Foundation, either version 3 of the -* License, or (at your option) any later version. -* -* VNLib.Plugins.Essentials.Sessions.OAuth is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU Affero General Public License for more details. -* -* You should have received a copy of the GNU Affero General Public License -* along with this program. If not, see https://www.gnu.org/licenses/. -*/ - -using System; -using System.Diagnostics.CodeAnalysis; - -using VNLib.Utils.Memory; -using VNLib.Utils.Extensions; -using VNLib.Hashing; -using VNLib.Net.Http; -using VNLib.Plugins.Essentials.Extensions; -using VNLib.Plugins.Essentials.Sessions.Runtime; -using VNLib.Plugins.Essentials.Oauth.Applications; -using VNLib.Plugins.Sessions.Cache.Client; -using static VNLib.Plugins.Essentials.Oauth.OauthSessionExtensions; - -namespace VNLib.Plugins.Essentials.Sessions.OAuth -{ - /// <summary> - /// Generates secure OAuth2 session tokens and initalizes new OAuth2 sessions - /// </summary> - internal class OAuth2SessionIdProvider : IOauthSessionIdFactory - { - private readonly string SessionIdPrefix; - private readonly int _bufferSize; - private readonly int _tokenSize; - - ///<inheritdoc/> - public int MaxTokensPerApp { get; } - ///<inheritdoc/> - public TimeSpan SessionValidFor { get; } - ///<inheritdoc/> - string IOauthSessionIdFactory.TokenType => "Bearer"; - - public OAuth2SessionIdProvider(string sessionIdPrefix, int maxTokensPerApp, int tokenSize, TimeSpan validFor) - { - SessionIdPrefix = sessionIdPrefix; - MaxTokensPerApp = maxTokensPerApp; - SessionValidFor = validFor; - _tokenSize = tokenSize; - _bufferSize = tokenSize * 2; - } - - ///<inheritdoc/> - bool ISessionIdFactory.TryGetSessionId(IHttpEvent entity, [NotNullWhen(true)] out string? sessionId) - { - //Get authorization token and make sure its not too large to cause a buffer overflow - if (entity.Server.HasAuthorization(out string? token) && (token.Length + SessionIdPrefix.Length) <= _bufferSize) - { - //Compute session id from token - sessionId = ComputeSessionIdFromToken(token); - - return true; - } - else - { - sessionId = null; - } - - return false; - } - - private string ComputeSessionIdFromToken(string token) - { - //Buffer to copy data to - using UnsafeMemoryHandle<char> buffer = Memory.UnsafeAlloc<char>(_bufferSize, true); - - //Writer to accumulate data - ForwardOnlyWriter<char> writer = new(buffer.Span); - - //Append session id prefix and token - writer.Append(SessionIdPrefix); - writer.Append(token); - - //Compute base64 hash of token and - return ManagedHash.ComputeBase64Hash(writer.AsSpan(), HashAlg.SHA256); - } - - ///<inheritdoc/> - TokenAndSessionIdResult IOauthSessionIdFactory.GenerateTokensAndId() - { - //Alloc buffer for random data - using UnsafeMemoryHandle<byte> mem = Memory.UnsafeAlloc<byte>(_tokenSize, true); - - //Generate token from random cng bytes - RandomHash.GetRandomBytes(mem); - - //Token is the raw value - string token = Convert.ToBase64String(mem.Span); - - //The session id is the HMAC of the token - string sessionId = ComputeSessionIdFromToken(token); - - //Clear buffer - Memory.InitializeBlock(mem.Span); - - //Return sessid result - return new(sessionId, token, null); - } - - ///<inheritdoc/> - void IOauthSessionIdFactory.InitNewSession(RemoteSession session, UserApplication app, IHttpEvent entity) - { - //Store session variables - session[APP_ID_ENTRY] = app.Id; - session[TOKEN_TYPE_ENTRY] = "client_credential,bearer"; - session[SCOPES_ENTRY] = app.Permissions; - session.UserID = app.UserId; - } - } -} diff --git a/Libs/VNLib.Plugins.Essentials.Sessions.OAuth/OAuth2SessionProvider.cs b/Libs/VNLib.Plugins.Essentials.Sessions.OAuth/OAuth2SessionProvider.cs deleted file mode 100644 index dca7909..0000000 --- a/Libs/VNLib.Plugins.Essentials.Sessions.OAuth/OAuth2SessionProvider.cs +++ /dev/null @@ -1,228 +0,0 @@ -/* -* Copyright (c) 2022 Vaughn Nugent -* -* Library: VNLib -* Package: VNLib.Plugins.Essentials.Sessions.OAuth -* File: OAuth2SessionProvider.cs -* -* OAuth2SessionProvider.cs is part of VNLib.Plugins.Essentials.Sessions.OAuth which is part of the larger -* VNLib collection of libraries and utilities. -* -* VNLib.Plugins.Essentials.Sessions.OAuth is free software: you can redistribute it and/or modify -* it under the terms of the GNU Affero General Public License as -* published by the Free Software Foundation, either version 3 of the -* License, or (at your option) any later version. -* -* VNLib.Plugins.Essentials.Sessions.OAuth is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU Affero General Public License for more details. -* -* You should have received a copy of the GNU Affero General Public License -* along with this program. If not, see https://www.gnu.org/licenses/. -*/ - -using System; -using System.Net; - -using Microsoft.EntityFrameworkCore; - -using VNLib.Net.Http; -using VNLib.Utils; -using VNLib.Utils.Logging; -using VNLib.Data.Caching.Exceptions; -using VNLib.Plugins.Sessions.Cache.Client; -using VNLib.Plugins.Essentials.Oauth; -using VNLib.Plugins.Essentials.Oauth.Tokens; -using VNLib.Plugins.Essentials.Oauth.Applications; -using VNLib.Plugins.Extensions.Loading.Events; -using VNLib.Plugins.Extensions.Loading; - -namespace VNLib.Plugins.Essentials.Sessions.OAuth -{ - - /// <summary> - /// Provides OAuth2 session management - /// </summary> - [ConfigurationName("oauth2")] - internal sealed class OAuth2SessionProvider : SessionCacheClient, ITokenManager, IIntervalScheduleable - { - - private static readonly SessionHandle NotFoundHandle = new(null, FileProcessArgs.NotFound, null); - - private static readonly TimeSpan BackgroundTimeout = TimeSpan.FromSeconds(10); - - - private readonly IOauthSessionIdFactory factory; - private readonly TokenStore TokenStore; - private readonly uint MaxConnections; - - public OAuth2SessionProvider(IRemoteCacheStore client, int maxCacheItems, uint maxConnections, IOauthSessionIdFactory idFactory, DbContextOptions dbCtx) - : base(client, maxCacheItems) - { - factory = idFactory; - TokenStore = new(dbCtx); - MaxConnections = maxConnections; - } - - ///<inheritdoc/> - protected override RemoteSession SessionCtor(string sessionId) => new OAuth2Session(sessionId, Store, BackgroundTimeout, InvalidatateCache); - - private void InvalidatateCache(OAuth2Session session) - { - lock (CacheLock) - { - _ = CacheTable.Remove(session.SessionID); - } - } - - ///<inheritdoc/> - public async ValueTask<SessionHandle> GetSessionAsync(IHttpEvent entity, CancellationToken cancellationToken) - { - //Callback to close the session when the handle is closeed - static ValueTask HandleClosedAsync(ISession session, IHttpEvent entity) - { - return ((SessionBase)session).UpdateAndRelease(true, entity); - } - try - { - //Get session id - if (!factory.TryGetSessionId(entity, out string? sessionId)) - { - //Id not allowed/found, so do not attach a session - return SessionHandle.Empty; - } - - //Limit max number of waiting clients - if (WaitingConnections > MaxConnections) - { - //Set 503 for temporary unavail - entity.CloseResponse(HttpStatusCode.ServiceUnavailable); - return new SessionHandle(null, FileProcessArgs.VirtualSkip, null); - } - - //Recover the session - RemoteSession session = await base.GetSessionAsync(entity, sessionId, cancellationToken); - - //Session should not be new - if (session.IsNew) - { - //Invalidate the session, so it is deleted - session.Invalidate(); - await session.UpdateAndRelease(true, entity); - return SessionHandle.Empty; - } - //Make sure session is still valid - if (session.Created.Add(factory.SessionValidFor) < DateTimeOffset.UtcNow) - { - //Invalidate the handle - session.Invalidate(); - //Flush changes - await session.UpdateAndRelease(false, entity); - //Remove the token from the db backing store - await TokenStore.RevokeTokenAsync(sessionId, cancellationToken); - //close entity - entity.CloseResponseError(HttpStatusCode.Unauthorized, ErrorType.InvalidToken, "The token has expired"); - //return a completed handle - return NotFoundHandle; - } - - return new SessionHandle(session, HandleClosedAsync); - } - //Pass session exceptions - catch (SessionException) - { - throw; - } - catch (Exception ex) - { - throw new SessionException("Exception raised while retreiving or loading OAuth2 session", ex); - } - } - ///<inheritdoc/> - public async Task<IOAuth2TokenResult?> CreateAccessTokenAsync(IHttpEvent ev, UserApplication app, CancellationToken cancellation) - { - //Get a new session for the current connection - TokenAndSessionIdResult ids = factory.GenerateTokensAndId(); - //try to insert token into the store, may fail if max has been reached - if (await TokenStore.InsertTokenAsync(ids.SessionId, app.Id!, ids.RefreshToken, factory.MaxTokensPerApp, cancellation) != ERRNO.SUCCESS) - { - return null; - } - //Create new session from the session id - RemoteSession session = SessionCtor(ids.SessionId); - await session.WaitAndLoadAsync(ev, cancellation); - try - { - //Init new session - factory.InitNewSession(session, app, ev); - } - finally - { - await session.UpdateAndRelease(false, ev); - } - //Init new token result to pass to client - return new OAuth2TokenResult() - { - ExpiresSeconds = (int)factory.SessionValidFor.TotalSeconds, - TokenType = factory.TokenType, - //Return token and refresh token - AccessToken = ids.AccessToken, - RefreshToken = ids.RefreshToken, - }; - } - ///<inheritdoc/> - Task ITokenManager.RevokeTokensAsync(IReadOnlyCollection<string> tokens, CancellationToken cancellation) - { - return TokenStore.RevokeTokensAsync(tokens, cancellation); - } - ///<inheritdoc/> - Task ITokenManager.RevokeTokensForAppAsync(string appId, CancellationToken cancellation) - { - return TokenStore.RevokeTokenAsync(appId, cancellation); - } - - - /* - * Interval for removing expired tokens - */ - - ///<inheritdoc/> - async Task IIntervalScheduleable.OnIntervalAsync(ILogProvider log, CancellationToken cancellationToken) - { - //Calculate valid token time - DateTime validAfter = DateTime.UtcNow.Subtract(factory.SessionValidFor); - //Remove tokens from db store - IReadOnlyCollection<ActiveToken> revoked = await TokenStore.CleanupExpiredTokensAsync(validAfter, cancellationToken); - //exception list - List<Exception>? errors = null; - //Remove all sessions from the store - foreach (ActiveToken token in revoked) - { - try - { - //Remove tokens by thier object id from cache - await base.Store.DeleteObjectAsync(token.Id, cancellationToken); - } - //Ignore if the object has already been removed - catch (ObjectNotFoundException) - {} - catch (Exception ex) - { - errors = new() - { - ex - }; - } - } - if (errors?.Count > 0) - { - throw new AggregateException(errors); - } - if(revoked.Count > 0) - { - log.Debug("Cleaned up {0} expired tokens", revoked.Count); - } - } - } -} diff --git a/Libs/VNLib.Plugins.Essentials.Sessions.OAuth/OAuth2TokenResult.cs b/Libs/VNLib.Plugins.Essentials.Sessions.OAuth/OAuth2TokenResult.cs deleted file mode 100644 index 5517192..0000000 --- a/Libs/VNLib.Plugins.Essentials.Sessions.OAuth/OAuth2TokenResult.cs +++ /dev/null @@ -1,37 +0,0 @@ -/* -* Copyright (c) 2022 Vaughn Nugent -* -* Library: VNLib -* Package: VNLib.Plugins.Essentials.Sessions.OAuth -* File: OAuth2TokenResult.cs -* -* OAuth2TokenResult.cs is part of VNLib.Plugins.Essentials.Sessions.OAuth which is part of the larger -* VNLib collection of libraries and utilities. -* -* VNLib.Plugins.Essentials.Sessions.OAuth is free software: you can redistribute it and/or modify -* it under the terms of the GNU Affero General Public License as -* published by the Free Software Foundation, either version 3 of the -* License, or (at your option) any later version. -* -* VNLib.Plugins.Essentials.Sessions.OAuth is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU Affero General Public License for more details. -* -* You should have received a copy of the GNU Affero General Public License -* along with this program. If not, see https://www.gnu.org/licenses/. -*/ - -using VNLib.Plugins.Essentials.Oauth.Tokens; - -namespace VNLib.Plugins.Essentials.Sessions.OAuth -{ - internal class OAuth2TokenResult : IOAuth2TokenResult - { - public string? IdentityToken { get; } - public string? AccessToken { get; set; } - public string? RefreshToken { get; set; } - public string? TokenType { get; set; } - public int ExpiresSeconds { get; set; } - } -} diff --git a/Libs/VNLib.Plugins.Essentials.Sessions.OAuth/OauthTokenResponseMessage.cs b/Libs/VNLib.Plugins.Essentials.Sessions.OAuth/OauthTokenResponseMessage.cs deleted file mode 100644 index 1088373..0000000 --- a/Libs/VNLib.Plugins.Essentials.Sessions.OAuth/OauthTokenResponseMessage.cs +++ /dev/null @@ -1,44 +0,0 @@ -/* -* Copyright (c) 2022 Vaughn Nugent -* -* Library: VNLib -* Package: VNLib.Plugins.Essentials.Sessions.OAuth -* File: OauthTokenResponseMessage.cs -* -* OauthTokenResponseMessage.cs is part of VNLib.Plugins.Essentials.Sessions.OAuth which is part of the larger -* VNLib collection of libraries and utilities. -* -* VNLib.Plugins.Essentials.Sessions.OAuth is free software: you can redistribute it and/or modify -* it under the terms of the GNU Affero General Public License as -* published by the Free Software Foundation, either version 3 of the -* License, or (at your option) any later version. -* -* VNLib.Plugins.Essentials.Sessions.OAuth is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU Affero General Public License for more details. -* -* You should have received a copy of the GNU Affero General Public License -* along with this program. If not, see https://www.gnu.org/licenses/. -*/ - -using System.Text.Json.Serialization; - -#nullable enable - -namespace VNLib.Plugins.Essentials.Sessions.OAuth -{ - public sealed class OauthTokenResponseMessage - { - [JsonPropertyName("access_token")] - public string? AccessToken { get; set; } - [JsonPropertyName("refresh_token")] - public string? RefreshToken { get; set; } - [JsonPropertyName("token_type")] - public string? TokenType { get; set; } - [JsonPropertyName("expires_in")] - public int Expires { get; set; } - [JsonPropertyName("id_token")] - public string? IdToken { get; set; } - } -}
\ No newline at end of file diff --git a/Libs/VNLib.Plugins.Essentials.Sessions.OAuth/TokenAndSessionIdResult.cs b/Libs/VNLib.Plugins.Essentials.Sessions.OAuth/TokenAndSessionIdResult.cs deleted file mode 100644 index ceb3cb5..0000000 --- a/Libs/VNLib.Plugins.Essentials.Sessions.OAuth/TokenAndSessionIdResult.cs +++ /dev/null @@ -1,44 +0,0 @@ -/* -* Copyright (c) 2022 Vaughn Nugent -* -* Library: VNLib -* Package: VNLib.Plugins.Essentials.Sessions.OAuth -* File: TokenAndSessionIdResult.cs -* -* TokenAndSessionIdResult.cs is part of VNLib.Plugins.Essentials.Sessions.OAuth which is part of the larger -* VNLib collection of libraries and utilities. -* -* VNLib.Plugins.Essentials.Sessions.OAuth is free software: you can redistribute it and/or modify -* it under the terms of the GNU Affero General Public License as -* published by the Free Software Foundation, either version 3 of the -* License, or (at your option) any later version. -* -* VNLib.Plugins.Essentials.Sessions.OAuth is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU Affero General Public License for more details. -* -* You should have received a copy of the GNU Affero General Public License -* along with this program. If not, see https://www.gnu.org/licenses/. -*/ - -#nullable enable - -using VNLib; - -namespace VNLib.Plugins.Essentials.Sessions.OAuth -{ - public readonly struct TokenAndSessionIdResult - { - public readonly string SessionId; - public readonly string AccessToken; - public readonly string? RefreshToken; - - public TokenAndSessionIdResult(string sessionId, string token, string? refreshToken) - { - SessionId = sessionId; - AccessToken = token; - RefreshToken = refreshToken; - } - } -} diff --git a/Libs/VNLib.Plugins.Essentials.Sessions.OAuth/VNLib.Plugins.Essentials.Sessions.OAuth.csproj b/Libs/VNLib.Plugins.Essentials.Sessions.OAuth/VNLib.Plugins.Essentials.Sessions.OAuth.csproj deleted file mode 100644 index d6775c8..0000000 --- a/Libs/VNLib.Plugins.Essentials.Sessions.OAuth/VNLib.Plugins.Essentials.Sessions.OAuth.csproj +++ /dev/null @@ -1,48 +0,0 @@ -<Project Sdk="Microsoft.NET.Sdk"> - - <PropertyGroup> - <TargetFramework>net6.0</TargetFramework> - <ImplicitUsings>enable</ImplicitUsings> - <Nullable>enable</Nullable> - <GenerateDocumentationFile>True</GenerateDocumentationFile> - <Authors>Vaughn Nugent</Authors> - <Copyright>Copyright © 2022 Vaughn Nugent</Copyright> - - <EnableDynamicLoading>true</EnableDynamicLoading> - <CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies> - <Version>1.0.1.1</Version> - <PackageProjectUrl>https://www.vaughnugent.com</PackageProjectUrl> - <AnalysisLevel>latest-all</AnalysisLevel> - <SignAssembly>True</SignAssembly> - <AssemblyOriginatorKeyFile>\\vaughnnugent.com\Internal\Folder Redirection\vman\Documents\Programming\Software\StrongNameingKey.snk</AssemblyOriginatorKeyFile> - </PropertyGroup> - - <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'"> - <Deterministic>False</Deterministic> - </PropertyGroup> - - <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'"> - <Deterministic>False</Deterministic> - </PropertyGroup> - - <Target Name="PostBuild" AfterTargets="PostBuildEvent"> - <Exec Command="start xcopy "$(TargetDir)" "F:\Programming\Web Plugins\DevPlugins\RuntimeAssets\$(TargetName)" /E /Y /R" /> - </Target> - <Target Name="PreBuild" BeforeTargets="PreBuildEvent"> - <Exec Command="erase "F:\Programming\Web Plugins\DevPlugins\RuntimeAssets\$(TargetName)" /q > nul" /> - </Target> - <ItemGroup> - <ProjectReference Include="..\..\..\..\VNLib\Http\src\VNLib.Net.Http.csproj" /> - <ProjectReference Include="..\..\..\..\VNLib\Plugins\src\VNLib.Plugins.csproj" /> - <ProjectReference Include="..\..\..\..\VNLib\Utils\src\VNLib.Utils.csproj" /> - <ProjectReference Include="..\..\..\Extensions\VNLib.Plugins.Extensions.Loading.Sql\VNLib.Plugins.Extensions.Loading.Sql.csproj" /> - <ProjectReference Include="..\..\..\Extensions\VNLib.Plugins.Extensions.Loading\VNLib.Plugins.Extensions.Loading.csproj" /> - <ProjectReference Include="..\..\..\Extensions\VNLib.Plugins.Extensions.Validation\VNLib.Plugins.Extensions.Validation.csproj" /> - <ProjectReference Include="..\..\..\Extensions\VNLib.Plugins.Extensions.VNCache\VNLib.Plugins.Extensions.VNCache.csproj" /> - <ProjectReference Include="..\..\..\Oauth\Libs\VNLib.Plugins.Essentials.Oauth\VNLib.Plugins.Essentials.Oauth.csproj" /> - <ProjectReference Include="..\..\..\PluginBase\VNLib.Plugins.PluginBase.csproj" /> - <ProjectReference Include="..\VNLib.Plugins.Essentials.Sessions.Runtime\VNLib.Plugins.Essentials.Sessions.Runtime.csproj" /> - <ProjectReference Include="..\VNLib.Plugins.Sessions.Cache.Client\VNLib.Plugins.Sessions.Cache.Client.csproj" /> - </ItemGroup> - -</Project> diff --git a/Libs/VNLib.Plugins.Essentials.Sessions.Runtime/IRuntimeSessionProvider.cs b/Libs/VNLib.Plugins.Essentials.Sessions.Runtime/IRuntimeSessionProvider.cs deleted file mode 100644 index 5d4de0a..0000000 --- a/Libs/VNLib.Plugins.Essentials.Sessions.Runtime/IRuntimeSessionProvider.cs +++ /dev/null @@ -1,49 +0,0 @@ -/* -* Copyright (c) 2022 Vaughn Nugent -* -* Library: VNLib -* Package: VNLib.Plugins.Essentials.Sessions.Runtime -* File: IRuntimeSessionProvider.cs -* -* IRuntimeSessionProvider.cs is part of VNLib.Plugins.Essentials.Sessions.Runtime which is part of the larger -* VNLib collection of libraries and utilities. -* -* VNLib.Plugins.Essentials.Sessions.Runtime is free software: you can redistribute it and/or modify -* it under the terms of the GNU Affero General Public License as -* published by the Free Software Foundation, either version 3 of the -* License, or (at your option) any later version. -* -* VNLib.Plugins.Essentials.Sessions.Runtime is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU Affero General Public License for more details. -* -* You should have received a copy of the GNU Affero General Public License -* along with this program. If not, see https://www.gnu.org/licenses/. -*/ - -using VNLib.Net.Http; -using VNLib.Utils.Logging; - -namespace VNLib.Plugins.Essentials.Sessions.Runtime -{ - /// <summary> - /// Represents a dynamically loadable type that an provide sessions to http connections - /// </summary> - public interface IRuntimeSessionProvider : ISessionProvider - { - /// <summary> - /// Called immediatly after the plugin is loaded into the appdomain - /// </summary> - /// <param name="plugin">The plugin instance that is loading the module</param> - /// <param name="localizedLog">The localized log provider for the provider</param> - void Load(PluginBase plugin, ILogProvider localizedLog); - - /// <summary> - /// Determines if the provider can return a session for the connection - /// </summary> - /// <param name="entity">The entity to process</param> - /// <returns>A value indicating if this provider should be called to load a session for</returns> - bool CanProcess(IHttpEvent entity); - } -} diff --git a/Libs/VNLib.Plugins.Essentials.Sessions.Runtime/ISessionIdFactory.cs b/Libs/VNLib.Plugins.Essentials.Sessions.Runtime/ISessionIdFactory.cs deleted file mode 100644 index c9df924..0000000 --- a/Libs/VNLib.Plugins.Essentials.Sessions.Runtime/ISessionIdFactory.cs +++ /dev/null @@ -1,41 +0,0 @@ -/* -* Copyright (c) 2022 Vaughn Nugent -* -* Library: VNLib -* Package: VNLib.Plugins.Essentials.Sessions.Runtime -* File: ISessionIdFactory.cs -* -* ISessionIdFactory.cs is part of VNLib.Plugins.Essentials.Sessions.Runtime which is part of the larger -* VNLib collection of libraries and utilities. -* -* VNLib.Plugins.Essentials.Sessions.Runtime is free software: you can redistribute it and/or modify -* it under the terms of the GNU Affero General Public License as -* published by the Free Software Foundation, either version 3 of the -* License, or (at your option) any later version. -* -* VNLib.Plugins.Essentials.Sessions.Runtime is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU Affero General Public License for more details. -* -* You should have received a copy of the GNU Affero General Public License -* along with this program. If not, see https://www.gnu.org/licenses/. -*/ - -using System.Diagnostics.CodeAnalysis; - -using VNLib.Net.Http; - -namespace VNLib.Plugins.Essentials.Sessions.Runtime -{ - public interface ISessionIdFactory - { - /// <summary> - /// Attempts to recover a session-id from the connection - /// </summary> - /// <param name="entity">The connection to process</param> - /// <param name="sessionId"></param> - /// <returns></returns> - bool TryGetSessionId(IHttpEvent entity, [NotNullWhen(true)] out string? sessionId); - } -} diff --git a/Libs/VNLib.Plugins.Essentials.Sessions.Runtime/VNLib.Plugins.Essentials.Sessions.Runtime.csproj b/Libs/VNLib.Plugins.Essentials.Sessions.Runtime/VNLib.Plugins.Essentials.Sessions.Runtime.csproj deleted file mode 100644 index 87c29e8..0000000 --- a/Libs/VNLib.Plugins.Essentials.Sessions.Runtime/VNLib.Plugins.Essentials.Sessions.Runtime.csproj +++ /dev/null @@ -1,29 +0,0 @@ -<Project Sdk="Microsoft.NET.Sdk"> - - <PropertyGroup> - <TargetFramework>net6.0</TargetFramework> - <ImplicitUsings>enable</ImplicitUsings> - <Nullable>enable</Nullable> - <Authors>Vaughn Nugent</Authors> - <Copyright>Copyright © 2022 Vaughn Nugent</Copyright> - <Version>1.0.1.2</Version> - <SignAssembly>False</SignAssembly> - <PackageProjectUrl>https://www.vaughnnugent.com/resources</PackageProjectUrl> - <AnalysisLevel>latest-all</AnalysisLevel> - <GenerateDocumentationFile>True</GenerateDocumentationFile> - <SignAssembly>True</SignAssembly> - <AssemblyOriginatorKeyFile>\\vaughnnugent.com\Internal\Folder Redirection\vman\Documents\Programming\Software\StrongNameingKey.snk</AssemblyOriginatorKeyFile> - </PropertyGroup> - - <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'" /> - - <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'" /> - - <ItemGroup> - <ProjectReference Include="..\..\..\..\VNLib\Essentials\src\VNLib.Plugins.Essentials.csproj" /> - <ProjectReference Include="..\..\..\..\VNLib\Http\src\VNLib.Net.Http.csproj" /> - <ProjectReference Include="..\..\..\Extensions\VNLib.Plugins.Extensions.Loading\VNLib.Plugins.Extensions.Loading.csproj" /> - <ProjectReference Include="..\..\..\PluginBase\VNLib.Plugins.PluginBase.csproj" /> - </ItemGroup> - -</Project> diff --git a/Libs/VNLib.Plugins.Essentials.Sessions.VNCache/IWebSessionIdFactory.cs b/Libs/VNLib.Plugins.Essentials.Sessions.VNCache/IWebSessionIdFactory.cs deleted file mode 100644 index f1fdf39..0000000 --- a/Libs/VNLib.Plugins.Essentials.Sessions.VNCache/IWebSessionIdFactory.cs +++ /dev/null @@ -1,50 +0,0 @@ -/* -* Copyright (c) 2022 Vaughn Nugent -* -* Library: VNLib -* Package: VNLib.Plugins.Essentials.Sessions.VNCache -* File: IWebSessionIdFactory.cs -* -* IWebSessionIdFactory.cs is part of VNLib.Plugins.Essentials.Sessions.VNCache which is part of the larger -* VNLib collection of libraries and utilities. -* -* VNLib.Plugins.Essentials.Sessions.VNCache is free software: you can redistribute it and/or modify -* it under the terms of the GNU Affero General Public License as -* published by the Free Software Foundation, either version 3 of the -* License, or (at your option) any later version. -* -* VNLib.Plugins.Essentials.Sessions.VNCache is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU Affero General Public License for more details. -* -* You should have received a copy of the GNU Affero General Public License -* along with this program. If not, see https://www.gnu.org/licenses/. -*/ - -using VNLib.Net.Http; -using VNLib.Plugins.Essentials.Sessions.Runtime; - -namespace VNLib.Plugins.Essentials.Sessions.VNCache -{ - /// <summary> - /// Id factory for <see cref="WebSessionProvider"/> - /// </summary> - internal interface IWebSessionIdFactory: ISessionIdFactory - { - /// <summary> - /// The maxium amount of time a session is valid for. Sessions will be invalidated - /// after this time - /// </summary> - TimeSpan ValidFor { get; } - - /// <summary> - /// Gets a new session-id for the connection and manipulates the entity as necessary - /// </summary> - /// <param name="entity">The connection to generate the new session for</param> - /// <returns>The new session-id</returns> - string GenerateSessionId(IHttpEvent entity); - } - - -} diff --git a/Libs/VNLib.Plugins.Essentials.Sessions.VNCache/VNLib.Plugins.Essentials.Sessions.VNCache.csproj b/Libs/VNLib.Plugins.Essentials.Sessions.VNCache/VNLib.Plugins.Essentials.Sessions.VNCache.csproj deleted file mode 100644 index 9895eea..0000000 --- a/Libs/VNLib.Plugins.Essentials.Sessions.VNCache/VNLib.Plugins.Essentials.Sessions.VNCache.csproj +++ /dev/null @@ -1,43 +0,0 @@ -<Project Sdk="Microsoft.NET.Sdk"> - - <PropertyGroup> - <TargetFramework>net6.0</TargetFramework> - <ImplicitUsings>enable</ImplicitUsings> - <Nullable>enable</Nullable> - <Authors>Vaughn Nugent</Authors> - <Copyright>Copyright © 2022 Vaughn Nugent</Copyright> - <Version>1.0.1.1</Version> - <SignAssembly>False</SignAssembly> - <PackageProjectUrl>https://www.vaughnnugent.com/resources</PackageProjectUrl> - - <EnableDynamicLoading>true</EnableDynamicLoading> - <CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies> - <GenerateDocumentationFile>True</GenerateDocumentationFile> - <AnalysisLevel>latest-all</AnalysisLevel> - <SignAssembly>True</SignAssembly> - <AssemblyOriginatorKeyFile>\\vaughnnugent.com\Internal\Folder Redirection\vman\Documents\Programming\Software\StrongNameingKey.snk</AssemblyOriginatorKeyFile> - </PropertyGroup> - - <ItemGroup> - <Folder Include="Endpoints\" /> - </ItemGroup> - - <ItemGroup> - <ProjectReference Include="..\..\..\..\VNLib\Http\src\VNLib.Net.Http.csproj" /> - <ProjectReference Include="..\..\..\..\VNLib\Utils\src\VNLib.Utils.csproj" /> - <ProjectReference Include="..\..\..\Extensions\VNLib.Plugins.Extensions.Loading\VNLib.Plugins.Extensions.Loading.csproj" /> - <ProjectReference Include="..\..\..\Extensions\VNLib.Plugins.Extensions.VNCache\VNLib.Plugins.Extensions.VNCache.csproj" /> - <ProjectReference Include="..\..\..\PluginBase\VNLib.Plugins.PluginBase.csproj" /> - <ProjectReference Include="..\VNLib.Plugins.Essentials.Sessions.Runtime\VNLib.Plugins.Essentials.Sessions.Runtime.csproj" /> - <ProjectReference Include="..\VNLib.Plugins.Sessions.Cache.Client\VNLib.Plugins.Sessions.Cache.Client.csproj" /> - </ItemGroup> - - <Target Name="PostBuild" AfterTargets="PostBuildEvent"> - <Exec Command="start xcopy "$(TargetDir)" "F:\Programming\Web Plugins\DevPlugins\RuntimeAssets\$(TargetName)" /E /Y /R" /> - </Target> - - <Target Name="PreBuild" BeforeTargets="PreBuildEvent"> - <Exec Command="erase "F:\Programming\Web Plugins\DevPlugins\RuntimeAssets\$(TargetName)" /q > nul" /> - </Target> - -</Project> diff --git a/Libs/VNLib.Plugins.Essentials.Sessions.VNCache/WebSession.cs b/Libs/VNLib.Plugins.Essentials.Sessions.VNCache/WebSession.cs deleted file mode 100644 index 87e08e7..0000000 --- a/Libs/VNLib.Plugins.Essentials.Sessions.VNCache/WebSession.cs +++ /dev/null @@ -1,124 +0,0 @@ -/* -* Copyright (c) 2022 Vaughn Nugent -* -* Library: VNLib -* Package: VNLib.Plugins.Essentials.Sessions.VNCache -* File: WebSession.cs -* -* WebSession.cs is part of VNLib.Plugins.Essentials.Sessions.VNCache which is part of the larger -* VNLib collection of libraries and utilities. -* -* VNLib.Plugins.Essentials.Sessions.VNCache is free software: you can redistribute it and/or modify -* it under the terms of the GNU Affero General Public License as -* published by the Free Software Foundation, either version 3 of the -* License, or (at your option) any later version. -* -* VNLib.Plugins.Essentials.Sessions.VNCache is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU Affero General Public License for more details. -* -* You should have received a copy of the GNU Affero General Public License -* along with this program. If not, see https://www.gnu.org/licenses/. -*/ - -using VNLib.Net.Http; -using VNLib.Plugins.Essentials.Extensions; -using VNLib.Plugins.Sessions.Cache.Client; -using static VNLib.Plugins.Essentials.Sessions.ISessionExtensions; - -namespace VNLib.Plugins.Essentials.Sessions.VNCache -{ - internal class WebSession : RemoteSession - { - protected const ulong UPGRADE_MSK = 0b0000000000010000UL; - - protected readonly Func<IHttpEvent, string, string> UpdateId; - private string? _oldId; - - public WebSession(string sessionId, IRemoteCacheStore client, TimeSpan backgroundTimeOut, Func<IHttpEvent, string, string> UpdateId) - : base(sessionId, client, backgroundTimeOut) - { - this.UpdateId = UpdateId; - } - - protected override void IndexerSet(string key, string value) - { - //Set value - base.IndexerSet(key, value); - switch (key) - { - //Set the upgrade flag when token data is modified - case LOGIN_TOKEN_ENTRY: - case TOKEN_ENTRY: - Flags.Set(UPGRADE_MSK); - break; - } - } - - public override async Task WaitAndLoadAsync(IHttpEvent entity, CancellationToken cancellationToken) - { - //Wait for the session to load - await base.WaitAndLoadAsync(entity, cancellationToken); - //If the session is new, set to web mode - if (IsNew) - { - SessionType = SessionType.Web; - } - } - - private async Task ProcessUpgradeAsync() - { - //Setup timeout cancellation for the update, to cancel it - using CancellationTokenSource cts = new(UpdateTimeout); - await Client.AddOrUpdateObjectAsync(_oldId!, SessionID, DataStore, cts.Token); - _oldId = null; - } - - protected override ValueTask<Task?> UpdateResource(bool isAsync, IHttpEvent state) - { - Task? result = null; - //Check flags in priority level, Invalid is highest state priority - if (Flags.IsSet(INVALID_MSK)) - { - //Clear all stored values - DataStore!.Clear(); - //Reset ip-address - UserIP = state.Server.GetTrustedIp(); - //Update created time - Created = DateTimeOffset.UtcNow; - //Init the new session-data - this.InitNewSession(state.Server); - //Restore session type - SessionType = SessionType.Web; - //generate new session-id and update the record in the store - _oldId = SessionID; - //Update the session-id - SessionID = UpdateId(state, _oldId); - //write update to server - result = Task.Run(ProcessUpgradeAsync); - } - else if (Flags.IsSet(UPGRADE_MSK | REGEN_ID_MSK)) - { - //generate new session-id and update the record in the store - _oldId = SessionID; - //Update the session-id - SessionID = UpdateId(state, _oldId); - //Update created time - Created = DateTimeOffset.UtcNow; - //write update to server - result = Task.Run(ProcessUpgradeAsync); - } - else if (Flags.IsSet(MODIFIED_MSK)) - { - //Send update to server - result = Task.Run(ProcessUpdateAsync); - } - - //Clear all flags - Flags.ClearAll(); - - return ValueTask.FromResult<Task?>(null); - } - } -} diff --git a/Libs/VNLib.Plugins.Essentials.Sessions.VNCache/WebSessionIdFactoryImpl.cs b/Libs/VNLib.Plugins.Essentials.Sessions.VNCache/WebSessionIdFactoryImpl.cs deleted file mode 100644 index 8dfda77..0000000 --- a/Libs/VNLib.Plugins.Essentials.Sessions.VNCache/WebSessionIdFactoryImpl.cs +++ /dev/null @@ -1,120 +0,0 @@ -/* -* Copyright (c) 2022 Vaughn Nugent -* -* Library: VNLib -* Package: VNLib.Plugins.Essentials.Sessions.VNCache -* File: WebSessionIdFactoryImpl.cs -* -* WebSessionIdFactoryImpl.cs is part of VNLib.Plugins.Essentials.Sessions.VNCache which is part of the larger -* VNLib collection of libraries and utilities. -* -* VNLib.Plugins.Essentials.Sessions.VNCache is free software: you can redistribute it and/or modify -* it under the terms of the GNU Affero General Public License as -* published by the Free Software Foundation, either version 3 of the -* License, or (at your option) any later version. -* -* VNLib.Plugins.Essentials.Sessions.VNCache is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU Affero General Public License for more details. -* -* You should have received a copy of the GNU Affero General Public License -* along with this program. If not, see https://www.gnu.org/licenses/. -*/ - -using System.Diagnostics.CodeAnalysis; - -using VNLib.Hashing; -using VNLib.Utils.Memory; -using VNLib.Utils.Extensions; -using VNLib.Plugins.Essentials.Extensions; -using VNLib.Net.Http; -using VNLib.Plugins.Essentials.Sessions.Runtime; - -namespace VNLib.Plugins.Essentials.Sessions.VNCache -{ - /// <summary> - /// <see cref="IWebSessionIdFactory"/> implementation, using - /// http cookies as session id storage - /// </summary> - internal sealed class WebSessionIdFactoryImpl : IWebSessionIdFactory - { - public TimeSpan ValidFor { get; } - - public string GenerateSessionId(IHttpEvent entity) - { - //Random hex hash - string cookie = RandomHash.GetRandomBase32(_tokenSize); - - //Set the session id cookie - entity.Server.SetCookie(SessionCookieName, cookie, ValidFor, secure: true, httpOnly: true); - - //return session-id value from cookie value - return ComputeSessionIdFromCookie(cookie); - } - - bool ISessionIdFactory.TryGetSessionId(IHttpEvent entity, [NotNullWhen(true)] out string? sessionId) - { - //Get authorization token and make sure its not too large to cause a buffer overflow - if (entity.Server.GetCookie(SessionCookieName, out string? cookie) && (cookie.Length + SessionIdPrefix.Length) <= _bufferSize) - { - //Compute session id from token - sessionId = ComputeSessionIdFromCookie(cookie); - - return true; - } - //Only add sessions for user-agents - else if(entity.Server.IsBrowser()) - { - //Get a new session id - sessionId = GenerateSessionId(entity); - - return true; - } - else - { - sessionId = null; - return false; - } - } - - private readonly string SessionCookieName; - private readonly string SessionIdPrefix; - private readonly int _bufferSize; - private readonly int _tokenSize; - - /// <summary> - /// Initialzies a new web session Id factory - /// </summary> - /// <param name="cookieSize">The size of the cookie in bytes</param> - /// <param name="sessionCookieName">The name of the session cookie</param> - /// <param name="sessionIdPrefix">The session-id internal prefix</param> - /// <param name="validFor">The time the session cookie is valid for</param> - public WebSessionIdFactoryImpl(uint cookieSize, string sessionCookieName, string sessionIdPrefix, TimeSpan validFor) - { - ValidFor = validFor; - SessionCookieName = sessionCookieName; - SessionIdPrefix = sessionIdPrefix; - _tokenSize = (int)cookieSize; - //Calc buffer size - _bufferSize = Math.Max(32, ((int)cookieSize * 3) + sessionIdPrefix.Length); - } - - - private string ComputeSessionIdFromCookie(string sessionId) - { - //Buffer to copy data to - using UnsafeMemoryHandle<char> buffer = Memory.UnsafeAlloc<char>(_bufferSize, true); - - //Writer to accumulate data - ForwardOnlyWriter<char> writer = new(buffer.Span); - - //Append prefix and session id - writer.Append(SessionIdPrefix); - writer.Append(sessionId); - - //Compute base64 hash of token and - return ManagedHash.ComputeBase64Hash(writer.AsSpan(), HashAlg.SHA256); - } - } -}
\ No newline at end of file diff --git a/Libs/VNLib.Plugins.Essentials.Sessions.VNCache/WebSessionProvider.cs b/Libs/VNLib.Plugins.Essentials.Sessions.VNCache/WebSessionProvider.cs deleted file mode 100644 index ebc3c3d..0000000 --- a/Libs/VNLib.Plugins.Essentials.Sessions.VNCache/WebSessionProvider.cs +++ /dev/null @@ -1,137 +0,0 @@ -/* -* Copyright (c) 2022 Vaughn Nugent -* -* Library: VNLib -* Package: VNLib.Plugins.Essentials.Sessions.VNCache -* File: WebSessionProvider.cs -* -* WebSessionProvider.cs is part of VNLib.Plugins.Essentials.Sessions.VNCache which is part of the larger -* VNLib collection of libraries and utilities. -* -* VNLib.Plugins.Essentials.Sessions.VNCache is free software: you can redistribute it and/or modify -* it under the terms of the GNU Affero General Public License as -* published by the Free Software Foundation, either version 3 of the -* License, or (at your option) any later version. -* -* VNLib.Plugins.Essentials.Sessions.VNCache is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU Affero General Public License for more details. -* -* You should have received a copy of the GNU Affero General Public License -* along with this program. If not, see https://www.gnu.org/licenses/. -*/ - -using System; - -using VNLib.Net.Http; -using VNLib.Plugins.Extensions.Loading; -using VNLib.Plugins.Sessions.Cache.Client; - -namespace VNLib.Plugins.Essentials.Sessions.VNCache -{ - /// <summary> - /// The implementation of a VNCache web based session - /// </summary> - [ConfigurationName("web")] - internal sealed class WebSessionProvider : SessionCacheClient, ISessionProvider - { - static readonly TimeSpan BackgroundUpdateTimeout = TimeSpan.FromSeconds(10); - - private readonly IWebSessionIdFactory factory; - private readonly uint MaxConnections; - - /// <summary> - /// Initializes a new <see cref="WebSessionProvider"/> - /// </summary> - /// <param name="client">The cache client to make cache operations against</param> - /// <param name="maxCacheItems">The max number of items to store in cache</param> - /// <param name="maxWaiting">The maxium number of waiting session events before 503s are sent</param> - /// <param name="factory">The session-id factory</param> - public WebSessionProvider(IRemoteCacheStore client, int maxCacheItems, uint maxWaiting, IWebSessionIdFactory factory) : base(client, maxCacheItems) - { - this.factory = factory; - MaxConnections = maxWaiting; - } - - private string UpdateSessionId(IHttpEvent entity, string oldId) - { - //Generate and set a new sessionid - string newid = factory.GenerateSessionId(entity); - //Aquire lock on cache - lock (CacheLock) - { - //Change the cache lookup id - if (CacheTable.Remove(oldId, out RemoteSession? session)) - { - CacheTable.Add(newid, session); - } - } - return newid; - } - - protected override RemoteSession SessionCtor(string sessionId) => new WebSession(sessionId, Store, BackgroundUpdateTimeout, UpdateSessionId); - - public async ValueTask<SessionHandle> GetSessionAsync(IHttpEvent entity, CancellationToken cancellationToken) - { - //Callback to close the session when the handle is closeed - static ValueTask HandleClosedAsync(ISession session, IHttpEvent entity) - { - return (session as SessionBase)!.UpdateAndRelease(true, entity); - } - - try - { - //Get session id - if (!factory.TryGetSessionId(entity, out string? sessionId)) - { - //Id not allowed/found, so do not attach a session - return SessionHandle.Empty; - } - - //Limit max number of waiting clients - if (WaitingConnections > MaxConnections) - { - //Set 503 for temporary unavail - entity.CloseResponse(System.Net.HttpStatusCode.ServiceUnavailable); - return new SessionHandle(null, FileProcessArgs.VirtualSkip, null); - } - - //Get session - RemoteSession session = await GetSessionAsync(entity, sessionId, cancellationToken); - - //If the session is new (not in cache), then overwrite the session id with a new one as user may have specified their own - if (session.IsNew) - { - session.RegenID(); - } - - //Make sure the session has not expired yet - if (session.Created.Add(factory.ValidFor) < DateTimeOffset.UtcNow) - { - //Invalidate the session, so its technically valid for this request, but will be cleared on this handle close cycle - session.Invalidate(); - //Clear basic login status - session.Token = null; - session.UserID = null; - session.Privilages = 0; - session.SetLoginToken(null); - } - - return new SessionHandle(session, HandleClosedAsync); - } - catch (OperationCanceledException) - { - throw; - } - catch (SessionException) - { - throw; - } - catch (Exception ex) - { - throw new SessionException("Exception raised while retreiving or loading Web session", ex); - } - } - } -} diff --git a/Libs/VNLib.Plugins.Essentials.Sessions.VNCache/WebSessionProviderEntry.cs b/Libs/VNLib.Plugins.Essentials.Sessions.VNCache/WebSessionProviderEntry.cs deleted file mode 100644 index 309db47..0000000 --- a/Libs/VNLib.Plugins.Essentials.Sessions.VNCache/WebSessionProviderEntry.cs +++ /dev/null @@ -1,82 +0,0 @@ -/* -* Copyright (c) 2022 Vaughn Nugent -* -* Library: VNLib -* Package: VNLib.Plugins.Essentials.Sessions.VNCache -* File: WebSessionProviderEntry.cs -* -* WebSessionProviderEntry.cs is part of VNLib.Plugins.Essentials.Sessions.VNCache which is part of the larger -* VNLib collection of libraries and utilities. -* -* VNLib.Plugins.Essentials.Sessions.VNCache is free software: you can redistribute it and/or modify -* it under the terms of the GNU Affero General Public License as -* published by the Free Software Foundation, either version 3 of the -* License, or (at your option) any later version. -* -* VNLib.Plugins.Essentials.Sessions.VNCache is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU Affero General Public License for more details. -* -* You should have received a copy of the GNU Affero General Public License -* along with this program. If not, see https://www.gnu.org/licenses/. -*/ - -using System.Text.Json; - -using VNLib.Net.Http; -using VNLib.Utils.Logging; -using VNLib.Utils.Extensions; -using VNLib.Data.Caching; -using VNLib.Plugins.Sessions.Cache.Client; -using VNLib.Plugins.Essentials.Sessions.Runtime; -using VNLib.Plugins.Extensions.Loading; -using VNLib.Plugins.Extensions.VNCache; - -namespace VNLib.Plugins.Essentials.Sessions.VNCache -{ - public sealed class WebSessionProviderEntry : IRuntimeSessionProvider - { - const string WEB_SESSION_CONFIG = "web"; - - private WebSessionProvider? _sessions; - - //Web sessions can always be provided so long as cache is loaded - bool IRuntimeSessionProvider.CanProcess(IHttpEvent entity) => _sessions != null; - - ValueTask<SessionHandle> ISessionProvider.GetSessionAsync(IHttpEvent entity, CancellationToken cancellationToken) - { - return _sessions!.GetSessionAsync(entity, cancellationToken); - } - - void IRuntimeSessionProvider.Load(PluginBase plugin, ILogProvider localized) - { - //Try get vncache config element - IReadOnlyDictionary<string, JsonElement> webSessionConfig = plugin.GetConfigForType<WebSessionProvider>(); - - uint cookieSize = webSessionConfig["cookie_size"].GetUInt32(); - string cookieName = webSessionConfig["cookie_name"].GetString() ?? throw new KeyNotFoundException($"Missing required element 'cookie_name' for config '{WEB_SESSION_CONFIG}'"); - string cachePrefix = webSessionConfig["cache_prefix"].GetString() ?? throw new KeyNotFoundException($"Missing required element 'cache_prefix' for config '{WEB_SESSION_CONFIG}'"); - int cacheLimit = (int)webSessionConfig["cache_size"].GetUInt32(); - uint maxConnections = webSessionConfig["max_waiting_connections"].GetUInt32(); - TimeSpan validFor = webSessionConfig["valid_for_sec"].GetTimeSpan(TimeParseType.Seconds); - - //Init id factory - WebSessionIdFactoryImpl idFactory = new(cookieSize, cookieName, cachePrefix, validFor); - - //Get shared global-cache - IGlobalCacheProvider globalCache = plugin.GetGlobalCache(); - - //Create cache store from global cache - GlobalCacheStore cacheStore = new(globalCache); - - //Init provider - _sessions = new(cacheStore, cacheLimit, maxConnections, idFactory); - - //Load and run cached sessions on deferred task lib - _ = plugin.DeferTask(() => _sessions.CleanupExpiredSessionsAsync(localized, plugin.UnloadToken), 1000); - - localized.Information("Session provider loaded"); - } - } -}
\ No newline at end of file diff --git a/Libs/VNLib.Plugins.Essentials.Sessions/MemorySession.cs b/Libs/VNLib.Plugins.Essentials.Sessions/MemorySession.cs deleted file mode 100644 index d365cd2..0000000 --- a/Libs/VNLib.Plugins.Essentials.Sessions/MemorySession.cs +++ /dev/null @@ -1,130 +0,0 @@ -/* -* Copyright (c) 2022 Vaughn Nugent -* -* Library: VNLib -* Package: VNLib.Plugins.Essentials.Sessions.Memory -* File: MemorySession.cs -* -* MemorySession.cs is part of VNLib.Plugins.Essentials.Sessions.Memory which is part of the larger -* VNLib collection of libraries and utilities. -* -* VNLib.Plugins.Essentials.Sessions.Memory is free software: you can redistribute it and/or modify -* it under the terms of the GNU Affero General Public License as -* published by the Free Software Foundation, either version 3 of the -* License, or (at your option) any later version. -* -* VNLib.Plugins.Essentials.Sessions.Memory is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU Affero General Public License for more details. -* -* You should have received a copy of the GNU Affero General Public License -* along with this program. If not, see https://www.gnu.org/licenses/. -*/ - -using System; -using System.Net; -using System.Threading.Tasks; -using System.Collections.Generic; -using VNLib.Plugins.Essentials.Extensions; - -using VNLib.Utils.Async; -using VNLib.Net.Http; -using VNLib.Utils.Memory.Caching; -using static VNLib.Plugins.Essentials.Sessions.ISessionExtensions; - -#nullable enable - -namespace VNLib.Plugins.Essentials.Sessions.Memory -{ - internal class MemorySession : SessionBase, ICacheable - { - private readonly Dictionary<string, string> DataStorage; - - private readonly Func<IHttpEvent, string, string> OnSessionUpdate; - private readonly AsyncQueue<MemorySession> ExpiredTable; - - public MemorySession(string sessionId, IPAddress ipAddress, Func<IHttpEvent, string, string> onSessionUpdate, AsyncQueue<MemorySession> expired) - { - //Set the initial is-new flag - DataStorage = new Dictionary<string, string>(10); - ExpiredTable = expired; - - OnSessionUpdate = onSessionUpdate; - //Get new session id - SessionID = sessionId; - UserIP = ipAddress; - SessionType = SessionType.Web; - Created = DateTimeOffset.UtcNow; - //Init - IsNew = true; - } - //Store in memory directly - public override IPAddress UserIP { get; protected set; } - - //Session type has no backing store, so safe to hard-code it's always web - - public override SessionType SessionType => SessionType.Web; - - protected override ValueTask<Task?> UpdateResource(bool isAsync, IHttpEvent state) - { - //if invalid is set, invalide the current session - if (Flags.IsSet(INVALID_MSK)) - { - //Clear storage, and regenerate the sessionid - DataStorage.Clear(); - //store new sessionid - SessionID = OnSessionUpdate(state, SessionID); - //Reset ip-address - UserIP = state.Server.GetTrustedIp(); - //Update created-time - Created = DateTimeOffset.UtcNow; - //Re-initialize the session to the state of the current connection - this.InitNewSession(state.Server); - //Modified flag doesnt matter since there is no write-back - - } - else if (Flags.IsSet(REGEN_ID_MSK)) - { - //Regen id without modifying the data store - SessionID = OnSessionUpdate(state, SessionID); - } - //Clear flags - Flags.ClearAll(); - //Memory session always completes - return ValueTask.FromResult<Task?>(null); - } - - protected override string IndexerGet(string key) - { - return DataStorage.GetValueOrDefault(key, string.Empty); - } - - protected override void IndexerSet(string key, string value) - { - //Check for special keys - switch (key) - { - //For tokens/login hashes, we can set the upgrade flag - case TOKEN_ENTRY: - case LOGIN_TOKEN_ENTRY: - Flags.Set(REGEN_ID_MSK); - break; - } - DataStorage[key] = value; - } - - - DateTime ICacheable.Expires { get; set; } - - void ICacheable.Evicted() - { - DataStorage.Clear(); - //Enque cleanup - _ = ExpiredTable.TryEnque(this); - } - - bool IEquatable<ICacheable>.Equals(ICacheable? other) => other is ISession ses && SessionID.Equals(ses.SessionID, StringComparison.Ordinal); - - } -} diff --git a/Libs/VNLib.Plugins.Essentials.Sessions/MemorySessionConfig.cs b/Libs/VNLib.Plugins.Essentials.Sessions/MemorySessionConfig.cs deleted file mode 100644 index 74199e6..0000000 --- a/Libs/VNLib.Plugins.Essentials.Sessions/MemorySessionConfig.cs +++ /dev/null @@ -1,58 +0,0 @@ -/* -* Copyright (c) 2022 Vaughn Nugent -* -* Library: VNLib -* Package: VNLib.Plugins.Essentials.Sessions.Memory -* File: MemorySessionConfig.cs -* -* MemorySessionConfig.cs is part of VNLib.Plugins.Essentials.Sessions.Memory which is part of the larger -* VNLib collection of libraries and utilities. -* -* VNLib.Plugins.Essentials.Sessions.Memory is free software: you can redistribute it and/or modify -* it under the terms of the GNU Affero General Public License as -* published by the Free Software Foundation, either version 3 of the -* License, or (at your option) any later version. -* -* VNLib.Plugins.Essentials.Sessions.Memory is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU Affero General Public License for more details. -* -* You should have received a copy of the GNU Affero General Public License -* along with this program. If not, see https://www.gnu.org/licenses/. -*/ - -using System; - -using VNLib.Utils.Logging; - -namespace VNLib.Plugins.Essentials.Sessions.Memory -{ - /// <summary> - /// Represents configration variables used to create and operate http sessions. - /// </summary> - public readonly struct MemorySessionConfig - { - /// <summary> - /// The name of the cookie to use for matching sessions - /// </summary> - public string SessionCookieID { get; init; } - /// <summary> - /// The size (in bytes) of the genreated SessionIds - /// </summary> - public uint SessionIdSizeBytes { get; init; } - /// <summary> - /// The amount of time a session is valid (within the backing store) - /// </summary> - public TimeSpan SessionTimeout { get; init; } - /// <summary> - /// The log for which all errors within the <see cref="SessionProvider"/> instance will be written to. - /// </summary> - public ILogProvider SessionLog { get; init; } - /// <summary> - /// The maximum number of sessions allowed to be cached in memory. If this value is exceed requests to this - /// server will be denied with a 503 error code - /// </summary> - public int MaxAllowedSessions { get; init; } - } -}
\ No newline at end of file diff --git a/Libs/VNLib.Plugins.Essentials.Sessions/MemorySessionEntrypoint.cs b/Libs/VNLib.Plugins.Essentials.Sessions/MemorySessionEntrypoint.cs deleted file mode 100644 index 07ae04b..0000000 --- a/Libs/VNLib.Plugins.Essentials.Sessions/MemorySessionEntrypoint.cs +++ /dev/null @@ -1,93 +0,0 @@ -/* -* Copyright (c) 2022 Vaughn Nugent -* -* Library: VNLib -* Package: VNLib.Plugins.Essentials.Sessions.Memory -* File: MemorySessionEntrypoint.cs -* -* MemorySessionEntrypoint.cs is part of VNLib.Plugins.Essentials.Sessions.Memory which is part of the larger -* VNLib collection of libraries and utilities. -* -* VNLib.Plugins.Essentials.Sessions.Memory is free software: you can redistribute it and/or modify -* it under the terms of the GNU Affero General Public License as -* published by the Free Software Foundation, either version 3 of the -* License, or (at your option) any later version. -* -* VNLib.Plugins.Essentials.Sessions.Memory is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU Affero General Public License for more details. -* -* You should have received a copy of the GNU Affero General Public License -* along with this program. If not, see https://www.gnu.org/licenses/. -*/ - -using System; -using System.Text.Json; -using System.Threading; -using System.Threading.Tasks; -using System.Collections.Generic; - -using VNLib.Net.Http; -using VNLib.Utils.Logging; -using VNLib.Utils.Extensions; -using VNLib.Plugins.Extensions.Loading.Events; -using VNLib.Plugins.Extensions.Loading; -using VNLib.Plugins.Essentials.Sessions.Runtime; - -#nullable enable - -namespace VNLib.Plugins.Essentials.Sessions.Memory -{ - public sealed class MemorySessionEntrypoint : IRuntimeSessionProvider, IIntervalScheduleable - { - const string WEB_SESSION_CONFIG = "web"; - - private MemorySessionStore? _sessions; - - bool IRuntimeSessionProvider.CanProcess(IHttpEvent entity) - { - //Web sessions can always be provided - return _sessions != null; - } - - public ValueTask<SessionHandle> GetSessionAsync(IHttpEvent entity, CancellationToken cancellationToken) - { - return _sessions!.GetSessionAsync(entity, cancellationToken); - } - - void IRuntimeSessionProvider.Load(PluginBase plugin, ILogProvider localized) - { - //Get websessions config element - - IReadOnlyDictionary<string, JsonElement> webSessionConfig = plugin.GetConfig(WEB_SESSION_CONFIG); - - MemorySessionConfig config = new() - { - SessionLog = localized, - MaxAllowedSessions = webSessionConfig["cache_size"].GetInt32(), - SessionIdSizeBytes = webSessionConfig["cookie_size"].GetUInt32(), - SessionTimeout = webSessionConfig["valid_for_sec"].GetTimeSpan(TimeParseType.Seconds), - SessionCookieID = webSessionConfig["cookie_name"].GetString() ?? throw new KeyNotFoundException($"Missing required element 'cookie_name' for config '{WEB_SESSION_CONFIG}'"), - }; - - _sessions = new(config); - - //Begin listening for expired records - _ = plugin.DeferTask(() => _sessions.CleanupExiredAsync(localized, plugin.UnloadToken)); - - //Schedule garbage collector - plugin.ScheduleInterval(this, TimeSpan.FromMinutes(1)); - - //Call cleanup on exit - _ = plugin.RegisterForUnload(_sessions.Cleanup); - } - - Task IIntervalScheduleable.OnIntervalAsync(ILogProvider log, CancellationToken cancellationToken) - { - //Cleanup expired sessions on interval - _sessions?.GC(); - return Task.CompletedTask; - } - } -} diff --git a/Libs/VNLib.Plugins.Essentials.Sessions/MemorySessionStore.cs b/Libs/VNLib.Plugins.Essentials.Sessions/MemorySessionStore.cs deleted file mode 100644 index 1af885e..0000000 --- a/Libs/VNLib.Plugins.Essentials.Sessions/MemorySessionStore.cs +++ /dev/null @@ -1,168 +0,0 @@ -/* -* Copyright (c) 2022 Vaughn Nugent -* -* Library: VNLib -* Package: VNLib.Plugins.Essentials.Sessions.Memory -* File: MemorySessionStore.cs -* -* MemorySessionStore.cs is part of VNLib.Plugins.Essentials.Sessions.Memory which is part of the larger -* VNLib collection of libraries and utilities. -* -* VNLib.Plugins.Essentials.Sessions.Memory is free software: you can redistribute it and/or modify -* it under the terms of the GNU Affero General Public License as -* published by the Free Software Foundation, either version 3 of the -* License, or (at your option) any later version. -* -* VNLib.Plugins.Essentials.Sessions.Memory is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU Affero General Public License for more details. -* -* You should have received a copy of the GNU Affero General Public License -* along with this program. If not, see https://www.gnu.org/licenses/. -*/ - -using System; -using System.Threading; -using System.Threading.Tasks; -using System.Collections.Generic; - -using VNLib.Net.Http; -using VNLib.Net.Http.Core; -using VNLib.Utils; -using VNLib.Utils.Async; -using VNLib.Utils.Logging; -using VNLib.Utils.Extensions; -using VNLib.Plugins.Essentials.Extensions; - -#nullable enable - -namespace VNLib.Plugins.Essentials.Sessions.Memory -{ - /// <summary> - /// An <see cref="ISessionProvider"/> for in-process-memory backed sessions - /// </summary> - internal sealed class MemorySessionStore : ISessionProvider - { - private readonly Dictionary<string, MemorySession> SessionsStore; - - internal readonly MemorySessionConfig Config; - internal readonly SessionIdFactory IdFactory; - internal readonly AsyncQueue<MemorySession> ExpiredSessions; - - public MemorySessionStore(MemorySessionConfig config) - { - Config = config; - SessionsStore = new(config.MaxAllowedSessions, StringComparer.Ordinal); - IdFactory = new(config.SessionIdSizeBytes, config.SessionCookieID, config.SessionTimeout); - ExpiredSessions = new(false, true); - } - - ///<inheritdoc/> - public async ValueTask<SessionHandle> GetSessionAsync(IHttpEvent entity, CancellationToken cancellationToken) - { - - static ValueTask SessionHandleClosedAsync(ISession session, IHttpEvent ev) - { - return (session as MemorySession)!.UpdateAndRelease(true, ev); - } - - //Try to get the id for the session - if (IdFactory.TryGetSessionId(entity, out string? sessionId)) - { - //Try to get the old record or evict it - ERRNO result = SessionsStore.TryGetOrEvictRecord(sessionId, out MemorySession? session); - if(result > 0) - { - //Valid, now wait for exclusive access - await session.WaitOneAsync(cancellationToken); - return new (session, SessionHandleClosedAsync); - } - else - { - //try to cleanup expired records - GC(); - //Make sure there is enough room to add a new session - if (SessionsStore.Count >= Config.MaxAllowedSessions) - { - entity.Server.SetNoCache(); - //Set 503 when full - entity.CloseResponse(System.Net.HttpStatusCode.ServiceUnavailable); - //Cannot service new session - return new(null, FileProcessArgs.VirtualSkip, null); - } - //Initialze a new session - session = new(sessionId, entity.Server.GetTrustedIp(), UpdateSessionId, ExpiredSessions); - //Increment the semaphore - (session as IWaitHandle).WaitOne(); - //store the session in cache while holding semaphore, and set its expiration - SessionsStore.StoreRecord(session.SessionID, session, Config.SessionTimeout); - //Init new session handle - return new (session, SessionHandleClosedAsync); - } - } - else - { - return SessionHandle.Empty; - } - } - - public async Task CleanupExiredAsync(ILogProvider log, CancellationToken token) - { - while (true) - { - try - { - //Wait for expired session and dispose it - using MemorySession session = await ExpiredSessions.DequeueAsync(token); - - //Obtain lock on session - await session.WaitOneAsync(CancellationToken.None); - - log.Verbose("Removed expired session {id}", session.SessionID); - } - catch (OperationCanceledException) - { - break; - } - catch (Exception ex) - { - log.Error(ex); - } - } - } - - private string UpdateSessionId(IHttpEvent entity, string oldId) - { - //Generate and set a new sessionid - string newid = IdFactory.GenerateSessionId(entity); - //Aquire lock on cache - lock (SessionsStore) - { - //Change the cache lookup id - if (SessionsStore.Remove(oldId, out MemorySession? session)) - { - SessionsStore.Add(newid, session); - } - } - return newid; - } - - /// <summary> - /// Evicts all sessions from the current store - /// </summary> - public void Cleanup() - { - //Expire all old records to cleanup all entires - this.SessionsStore.CollectRecords(DateTime.MaxValue); - } - /// <summary> - /// Collects all expired records from the current store - /// </summary> - public void GC() - { - //collect expired records - this.SessionsStore.CollectRecords(); - } - } -} diff --git a/Libs/VNLib.Plugins.Essentials.Sessions/SessionIdFactory.cs b/Libs/VNLib.Plugins.Essentials.Sessions/SessionIdFactory.cs deleted file mode 100644 index c221617..0000000 --- a/Libs/VNLib.Plugins.Essentials.Sessions/SessionIdFactory.cs +++ /dev/null @@ -1,84 +0,0 @@ -/* -* Copyright (c) 2022 Vaughn Nugent -* -* Library: VNLib -* Package: VNLib.Plugins.Essentials.Sessions.Memory -* File: SessionIdFactory.cs -* -* SessionIdFactory.cs is part of VNLib.Plugins.Essentials.Sessions.Memory which is part of the larger -* VNLib collection of libraries and utilities. -* -* VNLib.Plugins.Essentials.Sessions.Memory is free software: you can redistribute it and/or modify -* it under the terms of the GNU Affero General Public License as -* published by the Free Software Foundation, either version 3 of the -* License, or (at your option) any later version. -* -* VNLib.Plugins.Essentials.Sessions.Memory is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU Affero General Public License for more details. -* -* You should have received a copy of the GNU Affero General Public License -* along with this program. If not, see https://www.gnu.org/licenses/. -*/ - -using System; -using System.Diagnostics.CodeAnalysis; - -using VNLib.Hashing; -using VNLib.Net.Http; -using VNLib.Plugins.Essentials.Extensions; -using VNLib.Plugins.Essentials.Sessions.Runtime; - -#nullable enable - -namespace VNLib.Plugins.Essentials.Sessions.Memory -{ - internal sealed class SessionIdFactory : ISessionIdFactory - { - private readonly int IdSize; - private readonly string cookieName; - private readonly TimeSpan ValidFor; - - public SessionIdFactory(uint idSize, string cookieName, TimeSpan validFor) - { - IdSize = (int)idSize; - this.cookieName = cookieName; - ValidFor = validFor; - } - - public string GenerateSessionId(IHttpEvent entity) - { - //Random hex hash - string cookie = RandomHash.GetRandomBase32(IdSize); - - //Set the session id cookie - entity.Server.SetCookie(cookieName, cookie, ValidFor, secure: true, httpOnly: true); - - //return session-id value from cookie value - return cookie; - } - - public bool TryGetSessionId(IHttpEvent entity, [NotNullWhen(true)] out string? sessionId) - { - //Get authorization token and make sure its not too large to cause a buffer overflow - if (entity.Server.GetCookie(cookieName, out sessionId)) - { - return true; - } - //Only add sessions for user-agents - else if (entity.Server.IsBrowser()) - { - //Get a new session id - sessionId = GenerateSessionId(entity); - - return true; - } - else - { - sessionId = null; - return false; - } - } - } -} diff --git a/Libs/VNLib.Plugins.Essentials.Sessions/VNLib.Plugins.Essentials.Sessions.Memory.csproj b/Libs/VNLib.Plugins.Essentials.Sessions/VNLib.Plugins.Essentials.Sessions.Memory.csproj deleted file mode 100644 index 78fe298..0000000 --- a/Libs/VNLib.Plugins.Essentials.Sessions/VNLib.Plugins.Essentials.Sessions.Memory.csproj +++ /dev/null @@ -1,56 +0,0 @@ -<Project Sdk="Microsoft.NET.Sdk"> - - <PropertyGroup> - <TargetFramework>net6.0</TargetFramework> - <CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies> - <AssemblyName>VNLib.Plugins.Essentials.Sessions.Memory</AssemblyName> - <RootNamespace>VNLib.Plugins.Essentials.Sessions.Memory</RootNamespace> - <Authors>Vaughn Nugent</Authors> - <Copyright>Copyright © 2022 Vaughn Nugent</Copyright> - <PackageProjectUrl>https://www.vaughnnugent.com/resources</PackageProjectUrl> - <SignAssembly>True</SignAssembly> - <AssemblyOriginatorKeyFile>\\vaughnnugent.com\Internal\Folder Redirection\vman\Documents\Programming\Software\StrongNameingKey.snk</AssemblyOriginatorKeyFile> - </PropertyGroup> - - <!-- Resolve nuget dll files and store them in the output dir --> - <PropertyGroup> - <CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies> - <EnableDynamicLoading>true</EnableDynamicLoading> - <GenerateDocumentationFile>True</GenerateDocumentationFile> - <AnalysisLevel>latest-all</AnalysisLevel> - </PropertyGroup> - <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'"> - <Deterministic>False</Deterministic> - </PropertyGroup> - <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'"> - <Deterministic>False</Deterministic> - </PropertyGroup> - - <ProjectExtensions><VisualStudio><UserProperties /></VisualStudio></ProjectExtensions> - <ItemGroup> - <PackageReference Include="ErrorProne.NET.CoreAnalyzers" Version="0.1.2"> - <PrivateAssets>all</PrivateAssets> - <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> - </PackageReference> - <PackageReference Include="ErrorProne.NET.Structs" Version="0.1.2"> - <PrivateAssets>all</PrivateAssets> - <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> - </PackageReference> - </ItemGroup> - <ItemGroup> - <ProjectReference Include="..\..\..\..\VNLib\Essentials\src\VNLib.Plugins.Essentials.csproj" /> - <ProjectReference Include="..\..\..\..\VNLib\Http\src\VNLib.Net.Http.csproj" /> - <ProjectReference Include="..\..\..\Extensions\VNLib.Plugins.Extensions.Loading\VNLib.Plugins.Extensions.Loading.csproj" /> - <ProjectReference Include="..\..\..\PluginBase\VNLib.Plugins.PluginBase.csproj" /> - <ProjectReference Include="..\VNLib.Plugins.Essentials.Sessions.Runtime\VNLib.Plugins.Essentials.Sessions.Runtime.csproj" /> - </ItemGroup> - - <Target Name="PostBuild" AfterTargets="PostBuildEvent"> - <Exec Command="start xcopy "$(TargetDir)" "F:\Programming\Web Plugins\DevPlugins\RuntimeAssets\$(TargetName)" /E /Y /R" /> - </Target> - <Target Name="PreBuild" BeforeTargets="PreBuildEvent"> - <Exec Command="erase "F:\Programming\Web Plugins\DevPlugins\RuntimeAssets\$(TargetName)" /q > nul" /> - </Target> - - -</Project> diff --git a/Libs/VNLib.Plugins.Essentials.Sessions/VNLib.Plugins.Essentials.Sessions.Memory.xml b/Libs/VNLib.Plugins.Essentials.Sessions/VNLib.Plugins.Essentials.Sessions.Memory.xml deleted file mode 100644 index 0821935..0000000 --- a/Libs/VNLib.Plugins.Essentials.Sessions/VNLib.Plugins.Essentials.Sessions.Memory.xml +++ /dev/null @@ -1,78 +0,0 @@ -<?xml version="1.0"?> -<!-- -Copyright (c) 2022 Vaughn Nugent ---> -<doc> - <assembly> - <name>VNLib.Plugins.Essentials.Sessions.Memory</name> - </assembly> - <members> - <member name="T:VNLib.Plugins.Essentials.Sessions.Memory.MemorySessionStore"> - <summary> - An <see cref="T:VNLib.Plugins.Essentials.Sessions.ISessionProvider"/> for in-process-memory backed sessions - </summary> - </member> - <member name="M:VNLib.Plugins.Essentials.Sessions.Memory.MemorySessionStore.GetSessionAsync(VNLib.Net.Http.HttpEvent,System.Threading.CancellationToken)"> - <inheritdoc/> - </member> - <member name="P:VNLib.Plugins.Essentials.Sessions.Memory.MemorySessionStore.NewSessionID"> - <summary> - Gets a new unique sessionid for sessions - </summary> - </member> - <member name="M:VNLib.Plugins.Essentials.Sessions.Memory.MemorySessionStore.SetSessionCookie(VNLib.Net.Http.HttpEvent,VNLib.Plugins.Essentials.Sessions.Memory.MemorySession)"> - <summary> - Sets a standard session cookie for an entity/connection - </summary> - <param name="entity">The entity to set the cookie on</param> - <param name="session">The session attached to the </param> - </member> - <member name="M:VNLib.Plugins.Essentials.Sessions.Memory.MemorySessionStore.Cleanup"> - <summary> - Evicts all sessions from the current store - </summary> - </member> - <member name="M:VNLib.Plugins.Essentials.Sessions.Memory.MemorySessionStore.GC"> - <summary> - Collects all expired records from the current store - </summary> - </member> - <member name="T:VNLib.Plugins.Essentials.Sessions.Memory.MemSessionHandle"> - <summary> - Provides a one-time-use handle (similar to asyncReleaser, or openHandle) - that holds exclusive access to a session until it is released - </summary> - </member> - <member name="T:VNLib.Net.Sessions.MemorySessionConfig"> - <summary> - Represents configration variables used to create and operate http sessions. - </summary> - </member> - <member name="P:VNLib.Net.Sessions.MemorySessionConfig.SessionCookieID"> - <summary> - The name of the cookie to use for matching sessions - </summary> - </member> - <member name="P:VNLib.Net.Sessions.MemorySessionConfig.SessionIdSizeBytes"> - <summary> - The size (in bytes) of the genreated SessionIds - </summary> - </member> - <member name="P:VNLib.Net.Sessions.MemorySessionConfig.SessionTimeout"> - <summary> - The amount of time a session is valid (within the backing store) - </summary> - </member> - <member name="P:VNLib.Net.Sessions.MemorySessionConfig.SessionLog"> - <summary> - The log for which all errors within the <see cref="!:SessionProvider"/> instance will be written to. - </summary> - </member> - <member name="P:VNLib.Net.Sessions.MemorySessionConfig.MaxAllowedSessions"> - <summary> - The maximum number of sessions allowed to be cached in memory. If this value is exceed requests to this - server will be denied with a 503 error code - </summary> - </member> - </members> -</doc> diff --git a/Libs/VNLib.Plugins.Sessions.Cache.Client/Exceptions/MessageTooLargeException.cs b/Libs/VNLib.Plugins.Sessions.Cache.Client/Exceptions/MessageTooLargeException.cs deleted file mode 100644 index 5a096a6..0000000 --- a/Libs/VNLib.Plugins.Sessions.Cache.Client/Exceptions/MessageTooLargeException.cs +++ /dev/null @@ -1,51 +0,0 @@ -/* -* Copyright (c) 2022 Vaughn Nugent -* -* Library: VNLib -* Package: VNLib.Plugins.Sessions.Cache.Client -* File: MessageTooLargeException.cs -* -* MessageTooLargeException.cs is part of VNLib.Plugins.Sessions.Cache.Client which is part of the larger -* VNLib collection of libraries and utilities. -* -* VNLib.Plugins.Sessions.Cache.Client is free software: you can redistribute it and/or modify -* it under the terms of the GNU Affero General Public License as -* published by the Free Software Foundation, either version 3 of the -* License, or (at your option) any later version. -* -* VNLib.Plugins.Sessions.Cache.Client is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU Affero General Public License for more details. -* -* You should have received a copy of the GNU Affero General Public License -* along with this program. If not, see https://www.gnu.org/licenses/. -*/ - -using System; -using System.Runtime.Serialization; - -using VNLib.Net.Messaging.FBM; - -namespace VNLib.Plugins.Sessions.Cache.Client.Exceptions -{ - /// <summary> - /// Raised when a request message is too large to send to - /// the server and the server may close the connection. - /// </summary> - public class MessageTooLargeException : FBMException - { - ///<inheritdoc/> - public MessageTooLargeException() - { } - ///<inheritdoc/> - public MessageTooLargeException(string message) : base(message) - { } - ///<inheritdoc/> - public MessageTooLargeException(string message, Exception innerException) : base(message, innerException) - { } - ///<inheritdoc/> - protected MessageTooLargeException(SerializationInfo info, StreamingContext context) : base(info, context) - { } - } -} diff --git a/Libs/VNLib.Plugins.Sessions.Cache.Client/Exceptions/SessionStatusException.cs b/Libs/VNLib.Plugins.Sessions.Cache.Client/Exceptions/SessionStatusException.cs deleted file mode 100644 index bc6b217..0000000 --- a/Libs/VNLib.Plugins.Sessions.Cache.Client/Exceptions/SessionStatusException.cs +++ /dev/null @@ -1,46 +0,0 @@ -/* -* Copyright (c) 2022 Vaughn Nugent -* -* Library: VNLib -* Package: VNLib.Plugins.Sessions.Cache.Client -* File: SessionStatusException.cs -* -* SessionStatusException.cs is part of VNLib.Plugins.Sessions.Cache.Client which is part of the larger -* VNLib collection of libraries and utilities. -* -* VNLib.Plugins.Sessions.Cache.Client is free software: you can redistribute it and/or modify -* it under the terms of the GNU Affero General Public License as -* published by the Free Software Foundation, either version 3 of the -* License, or (at your option) any later version. -* -* VNLib.Plugins.Sessions.Cache.Client is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU Affero General Public License for more details. -* -* You should have received a copy of the GNU Affero General Public License -* along with this program. If not, see https://www.gnu.org/licenses/. -*/ - -using System; -using System.Runtime.Serialization; - -using VNLib.Plugins.Essentials.Sessions; - -namespace VNLib.Plugins.Sessions.Cache.Client.Exceptions -{ - /// <summary> - /// Raised when the status of the session is invalid and cannot be used - /// </summary> - public class SessionStatusException : SessionException - { - public SessionStatusException() - { } - public SessionStatusException(string message) : base(message) - { } - public SessionStatusException(string message, Exception innerException) : base(message, innerException) - { } - protected SessionStatusException(SerializationInfo info, StreamingContext context) : base(info, context) - { } - } -} diff --git a/Libs/VNLib.Plugins.Sessions.Cache.Client/Exceptions/SessionUpdateFailedException.cs b/Libs/VNLib.Plugins.Sessions.Cache.Client/Exceptions/SessionUpdateFailedException.cs deleted file mode 100644 index 6d572aa..0000000 --- a/Libs/VNLib.Plugins.Sessions.Cache.Client/Exceptions/SessionUpdateFailedException.cs +++ /dev/null @@ -1,41 +0,0 @@ -/* -* Copyright (c) 2022 Vaughn Nugent -* -* Library: VNLib -* Package: VNLib.Plugins.Sessions.Cache.Client -* File: SessionUpdateFailedException.cs -* -* SessionUpdateFailedException.cs is part of VNLib.Plugins.Sessions.Cache.Client which is part of the larger -* VNLib collection of libraries and utilities. -* -* VNLib.Plugins.Sessions.Cache.Client is free software: you can redistribute it and/or modify -* it under the terms of the GNU Affero General Public License as -* published by the Free Software Foundation, either version 3 of the -* License, or (at your option) any later version. -* -* VNLib.Plugins.Sessions.Cache.Client is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU Affero General Public License for more details. -* -* You should have received a copy of the GNU Affero General Public License -* along with this program. If not, see https://www.gnu.org/licenses/. -*/ - -using System; -using System.Runtime.Serialization; - -namespace VNLib.Plugins.Sessions.Cache.Client.Exceptions -{ - public class SessionUpdateFailedException : SessionStatusException - { - public SessionUpdateFailedException() - { } - public SessionUpdateFailedException(string message) : base(message) - { } - public SessionUpdateFailedException(string message, Exception innerException) : base(message, innerException) - { } - protected SessionUpdateFailedException(SerializationInfo info, StreamingContext context) : base(info, context) - { } - } -} diff --git a/Libs/VNLib.Plugins.Sessions.Cache.Client/GlobalCacheStore.cs b/Libs/VNLib.Plugins.Sessions.Cache.Client/GlobalCacheStore.cs deleted file mode 100644 index df3c564..0000000 --- a/Libs/VNLib.Plugins.Sessions.Cache.Client/GlobalCacheStore.cs +++ /dev/null @@ -1,64 +0,0 @@ -/* -* Copyright (c) 2022 Vaughn Nugent -* -* Library: VNLib -* Package: VNLib.Plugins.Sessions.Cache.Client -* File: GlobalCacheStore.cs -* -* GlobalCacheStore.cs is part of VNLib.Plugins.Sessions.Cache.Client which is part of the larger -* VNLib collection of libraries and utilities. -* -* VNLib.Plugins.Sessions.Cache.Client is free software: you can redistribute it and/or modify -* it under the terms of the GNU Affero General Public License as -* published by the Free Software Foundation, either version 3 of the -* License, or (at your option) any later version. -* -* VNLib.Plugins.Sessions.Cache.Client is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU Affero General Public License for more details. -* -* You should have received a copy of the GNU Affero General Public License -* along with this program. If not, see https://www.gnu.org/licenses/. -*/ - -using System; -using System.Threading; -using System.Threading.Tasks; - -using VNLib.Data.Caching; - -namespace VNLib.Plugins.Sessions.Cache.Client -{ - /// <summary> - /// A wrapper class to provide a <see cref="IRemoteCacheStore"/> from - /// a <see cref="IGlobalCacheProvider"/> client instance - /// </summary> - public sealed class GlobalCacheStore : IRemoteCacheStore - { - private readonly IGlobalCacheProvider _cache; - - public GlobalCacheStore(IGlobalCacheProvider globalCache) - { - _cache = globalCache ?? throw new ArgumentNullException(nameof(globalCache)); - } - - ///<inheritdoc/> - public Task AddOrUpdateObjectAsync<T>(string objectId, string? newId, T obj, CancellationToken cancellationToken = default) - { - return _cache.AddOrUpdateAsync(objectId, newId, obj, cancellationToken); - } - - ///<inheritdoc/> - public Task DeleteObjectAsync(string objectId, CancellationToken cancellationToken = default) - { - return _cache.DeleteAsync(objectId, cancellationToken); - } - - ///<inheritdoc/> - public Task<T?> GetObjectAsync<T>(string objectId, CancellationToken cancellationToken = default) - { - return _cache.GetAsync<T>(objectId, cancellationToken); - } - } -} diff --git a/Libs/VNLib.Plugins.Sessions.Cache.Client/IRemoteCacheStore.cs b/Libs/VNLib.Plugins.Sessions.Cache.Client/IRemoteCacheStore.cs deleted file mode 100644 index 2a8bd49..0000000 --- a/Libs/VNLib.Plugins.Sessions.Cache.Client/IRemoteCacheStore.cs +++ /dev/null @@ -1,48 +0,0 @@ -/* -* Copyright (c) 2022 Vaughn Nugent -* -* Library: VNLib -* Package: VNLib.Plugins.Sessions.Cache.Client -* File: IRemoteCacheStore.cs -* -* IRemoteCacheStore.cs is part of VNLib.Plugins.Sessions.Cache.Client which is part of the larger -* VNLib collection of libraries and utilities. -* -* VNLib.Plugins.Sessions.Cache.Client is free software: you can redistribute it and/or modify -* it under the terms of the GNU Affero General Public License as -* published by the Free Software Foundation, either version 3 of the -* License, or (at your option) any later version. -* -* VNLib.Plugins.Sessions.Cache.Client is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU Affero General Public License for more details. -* -* You should have received a copy of the GNU Affero General Public License -* along with this program. If not, see https://www.gnu.org/licenses/. -*/ - -using System.Threading; -using System.Threading.Tasks; - -namespace VNLib.Plugins.Sessions.Cache.Client -{ - /// <summary> - /// Represents an asynchronous interface to a remote cache store - /// </summary> - public interface IRemoteCacheStore - { - /// <summary> - /// Gets an object from the cache provider by key - /// </summary> - /// <typeparam name="T">The data type</typeparam> - /// <param name="objectId">The key/id of the object to recover</param> - /// <param name="cancellationToken">A token to cancel the operation</param> - /// <returns>A task that resolves the found object or null otherwise</returns> - Task<T?> GetObjectAsync<T>(string objectId, CancellationToken cancellationToken = default); - - Task AddOrUpdateObjectAsync<T>(string objectId, string? newId, T obj, CancellationToken cancellationToken = default); - - Task DeleteObjectAsync(string objectId, CancellationToken cancellationToken = default); - } -} diff --git a/Libs/VNLib.Plugins.Sessions.Cache.Client/RemoteSession.cs b/Libs/VNLib.Plugins.Sessions.Cache.Client/RemoteSession.cs deleted file mode 100644 index af2c969..0000000 --- a/Libs/VNLib.Plugins.Sessions.Cache.Client/RemoteSession.cs +++ /dev/null @@ -1,188 +0,0 @@ -/* -* Copyright (c) 2022 Vaughn Nugent -* -* Library: VNLib -* Package: VNLib.Plugins.Sessions.Cache.Client -* File: RemoteSession.cs -* -* RemoteSession.cs is part of VNLib.Plugins.Sessions.Cache.Client which is part of the larger -* VNLib collection of libraries and utilities. -* -* VNLib.Plugins.Sessions.Cache.Client is free software: you can redistribute it and/or modify -* it under the terms of the GNU Affero General Public License as -* published by the Free Software Foundation, either version 3 of the -* License, or (at your option) any later version. -* -* VNLib.Plugins.Sessions.Cache.Client is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU Affero General Public License for more details. -* -* You should have received a copy of the GNU Affero General Public License -* along with this program. If not, see https://www.gnu.org/licenses/. -*/ - -using System; -using System.Threading; -using System.Threading.Tasks; -using System.Collections.Generic; - -using Microsoft.VisualStudio.Threading; - -using VNLib.Net.Http; -using VNLib.Data.Caching.Exceptions; -using VNLib.Utils.Extensions; -using VNLib.Plugins.Essentials.Sessions; -using VNLib.Plugins.Essentials.Extensions; - -namespace VNLib.Plugins.Sessions.Cache.Client -{ - /// <summary> - /// Base class for cacheable lazy initialized session entires - /// that exist in a remote caching server - /// </summary> - public abstract class RemoteSession : SessionBase - { - protected const string CREATED_TIME_ENTRY = "__.i.ctime"; - - protected IRemoteCacheStore Client { get; } - protected TimeSpan UpdateTimeout { get; } - - private readonly AsyncLazyInitializer Initializer; - - /// <summary> - /// The lazy loaded data-store - /// </summary> - protected Dictionary<string, string>? DataStore; - - protected RemoteSession(string sessionId, IRemoteCacheStore client, TimeSpan backgroundTimeOut) - { - SessionID = sessionId; - UpdateTimeout = backgroundTimeOut; - Client = client; - Initializer = new(InitializeAsync, null); - } - - /// <summary> - /// The data initializer, loads the data store from the connected cache server - /// </summary> - /// <returns>A task that completes when the get operation completes</returns> - protected virtual async Task InitializeAsync() - { - //Setup timeout cancellation for the get, to cancel it - using CancellationTokenSource cts = new(UpdateTimeout); - //get or create a new session - DataStore = await Client.GetObjectAsync<Dictionary<string, string>>(SessionID, cancellationToken: cts.Token); - } - /// <summary> - /// Updates the current sessin agaisnt the cache store - /// </summary> - /// <returns>A task that complets when the update has completed</returns> - protected virtual async Task ProcessUpdateAsync() - { - //Setup timeout cancellation for the update, to cancel it - using CancellationTokenSource cts = new(UpdateTimeout); - await Client.AddOrUpdateObjectAsync(SessionID, null, DataStore, cts.Token); - } - /// <summary> - /// Delets the current session in the remote store - /// </summary> - /// <returns>A task that completes when instance has been deleted</returns> - protected virtual async Task ProcessDeleteAsync() - { - //Setup timeout cancellation for the update, to cancel it - using CancellationTokenSource cts = new(UpdateTimeout); - try - { - await Client.DeleteObjectAsync(SessionID, cts.Token); - } - catch (ObjectNotFoundException) - { - //This is fine, if the object does not exist, nothing to invalidate - } - } - - ///<inheritdoc/> - public override DateTimeOffset Created - { - get - { - //Deserialze the base32 ms - long unixMs = this.GetValueType<string, long>(CREATED_TIME_ENTRY); - //set created time from ms - return DateTimeOffset.FromUnixTimeMilliseconds(unixMs); - } - - protected set => this.SetValueType(CREATED_TIME_ENTRY, value.ToUnixTimeMilliseconds()); - } - - - ///<inheritdoc/> - protected override string IndexerGet(string key) - { - //Get the value at the key or an empty string as a default - return DataStore!.GetValueOrDefault(key, string.Empty); - } - ///<inheritdoc/> - protected override void IndexerSet(string key, string value) - { - //If the value is null, remove the key from the store - if (value == null) - { - //Set modified flag - IsModified |= DataStore!.Remove(key); - } - else - { - //Store the value at the specified key - DataStore![key] = value; - IsModified = true; - } - } - - - /* - * If the data-store is not found it means the session does not - * exist in cache, so its technically not dangerous to reuse, - * so the new mask needs to be set, but the old ID is going - * to be reused - */ - - /// <summary> - /// Waits for exclusive access to the session, and initializes - /// session data (loads it from the remote store) - /// </summary> - /// <param name="entity">The event to attach a session to</param> - /// <param name="cancellationToken">A token to cancel the operaion</param> - /// <returns></returns> - public virtual async Task WaitAndLoadAsync(IHttpEvent entity, CancellationToken cancellationToken) - { - //Wait for exclusive access - await base.WaitOneAsync(cancellationToken); - try - { - //Lazily initalize the current instance - await Initializer.InitializeAsync(cancellationToken); - //See if data-store is null (new session was created - if (DataStore == null) - { - //New session was created - DataStore = new(10); - //Set is-new flag - Flags.Set(IS_NEW_MSK); - //Set created time - Created = DateTimeOffset.UtcNow; - //Init ipaddress - UserIP = entity.Server.GetTrustedIp(); - //Set modified flag so session will be updated - IsModified = true; - } - } - catch - { - MainLock.Release(); - throw; - } - } - } -} diff --git a/Libs/VNLib.Plugins.Sessions.Cache.Client/SessionCacheClient.cs b/Libs/VNLib.Plugins.Sessions.Cache.Client/SessionCacheClient.cs deleted file mode 100644 index 800ad66..0000000 --- a/Libs/VNLib.Plugins.Sessions.Cache.Client/SessionCacheClient.cs +++ /dev/null @@ -1,242 +0,0 @@ -/* -* Copyright (c) 2022 Vaughn Nugent -* -* Library: VNLib -* Package: VNLib.Plugins.Sessions.Cache.Client -* File: SessionCacheClient.cs -* -* SessionCacheClient.cs is part of VNLib.Plugins.Sessions.Cache.Client which is part of the larger -* VNLib collection of libraries and utilities. -* -* VNLib.Plugins.Sessions.Cache.Client is free software: you can redistribute it and/or modify -* it under the terms of the GNU Affero General Public License as -* published by the Free Software Foundation, either version 3 of the -* License, or (at your option) any later version. -* -* VNLib.Plugins.Sessions.Cache.Client is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU Affero General Public License for more details. -* -* You should have received a copy of the GNU Affero General Public License -* along with this program. If not, see https://www.gnu.org/licenses/. -*/ - -using System; -using System.Threading; -using System.Threading.Tasks; -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; - -using VNLib.Net.Http; -using VNLib.Utils.Async; -using VNLib.Utils.Logging; -using VNLib.Utils.Memory.Caching; -using VNLib.Plugins.Essentials.Sessions; - -namespace VNLib.Plugins.Sessions.Cache.Client -{ - - /// <summary> - /// A client that allows access to sessions located on external servers - /// </summary> - public abstract class SessionCacheClient : ICacheHolder - { - public class LRUSessionStore<T> : LRUCache<string, T>, ICacheHolder where T : ISession - { - internal AsyncQueue<T> ExpiredSessions { get; } - - ///<inheritdoc/> - public override bool IsReadOnly => false; - ///<inheritdoc/> - protected override int MaxCapacity { get; } - - - public LRUSessionStore(int maxCapacity) : base(StringComparer.Ordinal) - { - MaxCapacity = maxCapacity; - ExpiredSessions = new (true, true); - } - - ///<inheritdoc/> - protected override bool CacheMiss(string key, [NotNullWhen(true)] out T? value) - { - value = default; - return false; - } - - ///<inheritdoc/> - protected override void Evicted(KeyValuePair<string, T> evicted) - { - //add to queue, the list lock should be held during this operatio - _ = ExpiredSessions.TryEnque(evicted.Value); - } - - ///<inheritdoc/> - public void CacheClear() - { - foreach (KeyValuePair<string, T> value in List) - { - Evicted(value); - } - Clear(); - } - - ///<inheritdoc/> - public void CacheHardClear() - { - CacheClear(); - } - } - - protected readonly LRUSessionStore<RemoteSession> CacheTable; - protected readonly object CacheLock; - protected readonly int MaxLoadedEntires; - - /// <summary> - /// The client used to communicate with the cache server - /// </summary> - protected IRemoteCacheStore Store { get; } - - /// <summary> - /// Initializes a new <see cref="SessionCacheClient"/> - /// </summary> - /// <param name="client"></param> - /// <param name="maxCacheItems">The maximum number of sessions to keep in memory</param> - protected SessionCacheClient(IRemoteCacheStore client, int maxCacheItems) - { - MaxLoadedEntires = maxCacheItems; - CacheLock = new(); - CacheTable = new(maxCacheItems); - Store = client; - } - - private ulong _waitingCount; - - /// <summary> - /// The number of pending connections waiting for results from the cache server - /// </summary> - public ulong WaitingConnections => _waitingCount; - - /// <summary> - /// Attempts to get a session from the cache identified by its sessionId asynchronously - /// </summary> - /// <param name="entity">The connection/request to attach the session to</param> - /// <param name="sessionId">The ID of the session to retrieve</param> - /// <param name="cancellationToken">A token to cancel the operation</param> - /// <returns>A <see cref="ValueTask"/> that resolves the remote session</returns> - /// <exception cref="SessionException"></exception> - public virtual async ValueTask<RemoteSession> GetSessionAsync(IHttpEvent entity, string sessionId, CancellationToken cancellationToken) - { - try - { - RemoteSession? session; - //Aquire lock on cache - lock (CacheLock) - { - //See if session is loaded into cache - if (!CacheTable.TryGetValue(sessionId, out session)) - { - //Init new record - session = SessionCtor(sessionId); - //Add to cache - CacheTable.Add(session.SessionID, session); - } - //Valid entry found in cache - } - - //Inc waiting count - Interlocked.Increment(ref _waitingCount); - - try - { - //Load session-data - await session.WaitAndLoadAsync(entity, cancellationToken); - return session; - } - catch - { - //Remove the invalid cached session - lock (CacheLock) - { - _ = CacheTable.Remove(sessionId); - } - throw; - } - finally - { - //Dec waiting count - Interlocked.Decrement(ref _waitingCount); - } - } - catch (SessionException) - { - throw; - } - catch (OperationCanceledException) - { - throw; - } - //Wrap exceptions - catch (Exception ex) - { - throw new SessionException("An unhandled exception was raised", ex); - } - } - - /// <summary> - /// Gets a new <see cref="RemoteSession"/> instances for the given sessionId, - /// and places it a the head of internal cache - /// </summary> - /// <param name="sessionId">The session identifier</param> - /// <returns>The new session for the given ID</returns> - protected abstract RemoteSession SessionCtor(string sessionId); - - /// <summary> - /// Begins waiting for expired sessions to be evicted from the cache table that - /// may have pending synchronization operations - /// </summary> - /// <param name="log"></param> - /// <param name="token"></param> - /// <returns></returns> - public async Task CleanupExpiredSessionsAsync(ILogProvider log, CancellationToken token) - { - while (true) - { - try - { - //Wait for expired session and dispose it - using RemoteSession session = await CacheTable.ExpiredSessions.DequeueAsync(token); - - //Obtain lock on session - await session.WaitOneAsync(CancellationToken.None); - - log.Verbose("Removed expired session {id}", session.SessionID); - } - catch (OperationCanceledException) - { - break; - } - catch(Exception ex) - { - log.Error(ex); - } - } - } - - ///<inheritdoc/> - public void CacheClear() - { - - } - ///<inheritdoc/> - public void CacheHardClear() - { - //Cleanup cache when disconnected - lock (CacheLock) - { - CacheTable.CacheHardClear(); - } - } - } -} diff --git a/Libs/VNLib.Plugins.Sessions.Cache.Client/VNLib.Plugins.Sessions.Cache.Client.csproj b/Libs/VNLib.Plugins.Sessions.Cache.Client/VNLib.Plugins.Sessions.Cache.Client.csproj deleted file mode 100644 index 8271ed2..0000000 --- a/Libs/VNLib.Plugins.Sessions.Cache.Client/VNLib.Plugins.Sessions.Cache.Client.csproj +++ /dev/null @@ -1,48 +0,0 @@ -<Project Sdk="Microsoft.NET.Sdk"> - - <PropertyGroup> - <TargetFramework>net6.0</TargetFramework> - <Authors>Vaughn Nugent</Authors> - <Copyright>Copyright © 2022 Vaughn Nugent</Copyright> - <Version>1.0.1.1</Version> - <SignAssembly>True</SignAssembly> - <AssemblyOriginatorKeyFile>\\vaughnnugent.com\Internal\Folder Redirection\vman\Documents\Programming\Software\StrongNameingKey.snk</AssemblyOriginatorKeyFile> - </PropertyGroup> - - <PropertyGroup> - <DocumentationFile></DocumentationFile> - <GenerateDocumentationFile>True</GenerateDocumentationFile> - <PackageProjectUrl>https://www.vaughnnugent.com/resources</PackageProjectUrl> - <AnalysisLevel>latest-all</AnalysisLevel> - <Nullable>enable</Nullable> - </PropertyGroup> - - <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'"> - <Deterministic>False</Deterministic> - </PropertyGroup> - - <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'"> - <Deterministic>False</Deterministic> - </PropertyGroup> - - <ItemGroup> - <PackageReference Include="ErrorProne.NET.CoreAnalyzers" Version="0.1.2"> - <PrivateAssets>all</PrivateAssets> - <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> - </PackageReference> - <PackageReference Include="ErrorProne.NET.Structs" Version="0.1.2"> - <PrivateAssets>all</PrivateAssets> - <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> - </PackageReference> - <PackageReference Include="Microsoft.VisualStudio.Threading" Version="17.4.27" /> - <PackageReference Include="RestSharp" Version="108.0.2" /> - </ItemGroup> - - <ItemGroup> - <ProjectReference Include="..\..\..\..\VNLib\Essentials\src\VNLib.Plugins.Essentials.csproj" /> - <ProjectReference Include="..\..\..\..\VNLib\Http\src\VNLib.Net.Http.csproj" /> - <ProjectReference Include="..\..\..\..\VNLib\Utils\src\VNLib.Utils.csproj" /> - <ProjectReference Include="..\..\..\DataCaching\VNLib.Data.Caching\VNLib.Data.Caching.csproj" /> - </ItemGroup> - -</Project> diff --git a/Libs/VNLib.Plugins.Sessions.Cache.Client/VNLib.Plugins.Sessions.Cache.Client.xml b/Libs/VNLib.Plugins.Sessions.Cache.Client/VNLib.Plugins.Sessions.Cache.Client.xml deleted file mode 100644 index c96515b..0000000 --- a/Libs/VNLib.Plugins.Sessions.Cache.Client/VNLib.Plugins.Sessions.Cache.Client.xml +++ /dev/null @@ -1,72 +0,0 @@ -<?xml version="1.0"?> -<!-- -Copyright (c) 2022 Vaughn Nugent ---> -<doc> - <assembly> - <name>VNLib.Plugins.Sessions.Cache.Client</name> - </assembly> - <members> - <member name="T:VNLib.Plugins.Sessions.Cache.Client.MessageTooLargeException"> - <summary> - Raised when a request message is too large to send to - the server and the server may close the connection. - </summary> - </member> - <member name="M:VNLib.Plugins.Sessions.Cache.Client.MessageTooLargeException.#ctor"> - <inheritdoc/> - </member> - <member name="M:VNLib.Plugins.Sessions.Cache.Client.MessageTooLargeException.#ctor(System.String)"> - <inheritdoc/> - </member> - <member name="M:VNLib.Plugins.Sessions.Cache.Client.MessageTooLargeException.#ctor(System.String,System.Exception)"> - <inheritdoc/> - </member> - <member name="M:VNLib.Plugins.Sessions.Cache.Client.MessageTooLargeException.#ctor(System.Runtime.Serialization.SerializationInfo,System.Runtime.Serialization.StreamingContext)"> - <inheritdoc/> - </member> - <member name="T:VNLib.Plugins.Sessions.Cache.Client.SessionClient"> - <summary> - A client that allows access to sessions located on external servers - </summary> - </member> - <member name="P:VNLib.Plugins.Sessions.Cache.Client.SessionClient.GetSessionId"> - <summary> - A callback that produces a session-id from the connection (or a new id if needed) - </summary> - </member> - <member name="P:VNLib.Plugins.Sessions.Cache.Client.SessionClient.NewSessionId"> - <summary> - A callback that produces a new session-id for the connection (and updates the client if necessary) - </summary> - </member> - <member name="M:VNLib.Plugins.Sessions.Cache.Client.SessionClient.#ctor(System.Int32,System.Int32,System.Int32,VNLib.Utils.Logging.ILogProvider,VNLib.Utils.Memory.PrivateHeap)"> - <summary> - Initializes a new <see cref="T:VNLib.Plugins.Sessions.Cache.Client.SessionClient"/> - </summary> - <param name="maxMessageSize">The maxium message size (in bytes) the client will allow receiving (maximum data size for sessions)</param> - <param name="recvBufferSize">The size (in bytes) of the client message receive buffer</param> - <param name="maxCacheItems">The maximum number of sessions to keep in memory</param> - <param name="log">A <see cref="T:VNLib.Utils.Logging.ILogProvider"/> to write log events to</param> - <param name="heap">The <see cref="T:VNLib.Utils.Memory.PrivateHeap"/> to allocate buffers from</param> - </member> - <member name="M:VNLib.Plugins.Sessions.Cache.Client.SessionClient.GetSessionAsync(VNLib.Net.Http.HttpEvent,System.Threading.CancellationToken)"> - <inheritdoc/> - </member> - <member name="M:VNLib.Plugins.Sessions.Cache.Client.SessionClient.CacheClear"> - <inheritdoc/> - </member> - <member name="M:VNLib.Plugins.Sessions.Cache.Client.SessionClient.CacheHardClear"> - <inheritdoc/> - </member> - <member name="M:VNLib.Plugins.Sessions.Cache.Client.SessionClient.OnConnected"> - <inheritdoc/> - </member> - <member name="M:VNLib.Plugins.Sessions.Cache.Client.SessionClient.OnError(VNLib.Net.Messaging.FBM.Client.FMBClientErrorEventArgs)"> - <inheritdoc/> - </member> - <member name="M:VNLib.Plugins.Sessions.Cache.Client.SessionClient.OnDisconnected"> - <inheritdoc/> - </member> - </members> -</doc> |