diff options
Diffstat (limited to 'libs/VNLib.Plugins.Sessions.OAuth/src')
7 files changed, 79 insertions, 123 deletions
diff --git a/libs/VNLib.Plugins.Sessions.OAuth/src/Endpoints/AccessTokenEndpoint.cs b/libs/VNLib.Plugins.Sessions.OAuth/src/Endpoints/AccessTokenEndpoint.cs index 77cad38..dc0530f 100644 --- a/libs/VNLib.Plugins.Sessions.OAuth/src/Endpoints/AccessTokenEndpoint.cs +++ b/libs/VNLib.Plugins.Sessions.OAuth/src/Endpoints/AccessTokenEndpoint.cs @@ -46,7 +46,6 @@ namespace VNLib.Plugins.Sessions.OAuth.Endpoints /// Grants authorization to OAuth2 clients to protected resources /// with access tokens /// </summary> - [ConfigurationName(O2SessionProviderEntry.OAUTH2_CONFIG_KEY)] internal sealed class AccessTokenEndpoint : ResourceEndpointBase { private readonly IApplicationTokenFactory TokenFactory; @@ -60,14 +59,13 @@ namespace VNLib.Plugins.Sessions.OAuth.Endpoints DisableSessionsRequired = true }; - public AccessTokenEndpoint(PluginBase pbase, IConfigScope config) + public AccessTokenEndpoint(PluginBase pbase, IConfigScope config, IApplicationTokenFactory tokenFactory) { string? path = config["token_path"].GetString();; InitPathAndLog(path, pbase.Log); - //Get the session provider, as its a token factory - TokenFactory = pbase.GetOrCreateSingleton<OAuth2SessionProvider>(); + TokenFactory = tokenFactory; Applications = new(pbase.GetContextOptions(), pbase.GetOrCreateSingleton<ManagedPasswordHashing>()); diff --git a/libs/VNLib.Plugins.Sessions.OAuth/src/Endpoints/RevocationEndpoint.cs b/libs/VNLib.Plugins.Sessions.OAuth/src/Endpoints/RevocationEndpoint.cs index 45a8391..bdb4e4e 100644 --- a/libs/VNLib.Plugins.Sessions.OAuth/src/Endpoints/RevocationEndpoint.cs +++ b/libs/VNLib.Plugins.Sessions.OAuth/src/Endpoints/RevocationEndpoint.cs @@ -46,8 +46,7 @@ namespace VNLib.Plugins.Sessions.OAuth.Endpoints { //Revoke the access token, by invalidating it entity.Session.Invalidate(); - entity.CloseResponse(System.Net.HttpStatusCode.OK); - return VfReturnType.VirtualSkip; + return VirtualOk(entity); } } } diff --git a/libs/VNLib.Plugins.Sessions.OAuth/src/O2AuthenticationPluginEntry.cs b/libs/VNLib.Plugins.Sessions.OAuth/src/O2AuthenticationPluginEntry.cs index 5cd6aaf..1d74b7e 100644 --- a/libs/VNLib.Plugins.Sessions.OAuth/src/O2AuthenticationPluginEntry.cs +++ b/libs/VNLib.Plugins.Sessions.OAuth/src/O2AuthenticationPluginEntry.cs @@ -1,5 +1,5 @@ /* -* Copyright (c) 2022 Vaughn Nugent +* Copyright (c) 2023 Vaughn Nugent * * Library: VNLib * Package: VNLib.Plugins.Essentials.Sessions.OAuth @@ -23,9 +23,13 @@ */ using System; -using System.Collections.Generic; + +using System.ComponentModel.Design; using VNLib.Utils.Logging; +using VNLib.Plugins.Attributes; +using VNLib.Plugins.Essentials.Sessions; +using VNLib.Plugins.Extensions.Loading; namespace VNLib.Plugins.Sessions.OAuth { @@ -33,19 +37,20 @@ namespace VNLib.Plugins.Sessions.OAuth { public override string PluginName => "Essentials.Oauth.Authentication"; - private readonly O2SessionProviderEntry SessionProvider = new(); + private OAuth2SessionProvider? SessionProvider; + + + [ServiceConfigurator] + public void OnServicesLoading(IServiceContainer services) + { + //Expose the OAuth2 session provider as a service singleton + services.AddService<ISessionProvider>(SessionProvider!); + } protected override void OnLoad() { - try - { - //Load the session provider, that will only load the endpoints - SessionProvider.Load(this, Log); - } - catch(KeyNotFoundException kne) - { - Log.Error("Missing required configuration keys {err}", kne.Message); - } + SessionProvider = this.GetOrCreateSingleton<OAuth2SessionProvider>(); + Log.Information("Plugin loaded"); } protected override void OnUnLoad() diff --git a/libs/VNLib.Plugins.Sessions.OAuth/src/O2SessionProviderEntry.cs b/libs/VNLib.Plugins.Sessions.OAuth/src/O2SessionProviderEntry.cs deleted file mode 100644 index 0437541..0000000 --- a/libs/VNLib.Plugins.Sessions.OAuth/src/O2SessionProviderEntry.cs +++ /dev/null @@ -1,81 +0,0 @@ -/* -* Copyright (c) 2023 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; -using System.Threading; -using System.Threading.Tasks; - -using VNLib.Net.Http; -using VNLib.Utils.Logging; -using VNLib.Plugins.Sessions.OAuth.Endpoints; -using VNLib.Plugins.Essentials.Sessions; -using VNLib.Plugins.Extensions.Loading; -using VNLib.Plugins.Extensions.Loading.Routing; - -namespace VNLib.Plugins.Sessions.OAuth -{ - - public sealed class O2SessionProviderEntry : ISessionProvider - { - public const string OAUTH2_CONFIG_KEY = "oauth2"; - - private OAuth2SessionProvider? _sessions; - - public bool CanProcess(IHttpEvent entity) - { - //If authorization header is set try to process as oauth2 session - return _sessions != null && _sessions.IsConnected && entity.Server.Headers.HeaderSet(System.Net.HttpRequestHeader.Authorization); - } - - ValueTask<SessionHandle> ISessionProvider.GetSessionAsync(IHttpEvent entity, CancellationToken cancellationToken) - { - return _sessions!.GetSessionAsync(entity, cancellationToken); - } - - public void Load(PluginBase plugin, ILogProvider localized) - { - IConfigScope o2Config = plugin.GetConfig(OAUTH2_CONFIG_KEY); - - //Access token endpoint is optional - if (o2Config.ContainsKey("token_path")) - { - //Create token endpoint - plugin.Route<AccessTokenEndpoint>(); - } - - //Optional revocation endpoint - if (plugin.HasConfigForType<RevocationEndpoint>()) - { - //Route revocation endpoint - plugin.Route<RevocationEndpoint>(); - } - - //Init session provider - _sessions = plugin.GetOrCreateSingleton<OAuth2SessionProvider>(); - _sessions.SetLog(localized); - - localized.Information("Session provider loaded"); - } - } -}
\ No newline at end of file diff --git a/libs/VNLib.Plugins.Sessions.OAuth/src/OAuth2SessionProvider.cs b/libs/VNLib.Plugins.Sessions.OAuth/src/OAuth2SessionProvider.cs index b1b97b7..99bac29 100644 --- a/libs/VNLib.Plugins.Sessions.OAuth/src/OAuth2SessionProvider.cs +++ b/libs/VNLib.Plugins.Sessions.OAuth/src/OAuth2SessionProvider.cs @@ -5,8 +5,8 @@ * 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. +* 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 @@ -31,7 +31,6 @@ using System.Collections.Generic; using VNLib.Net.Http; using VNLib.Utils; using VNLib.Utils.Logging; -using VNLib.Data.Caching.Exceptions; using VNLib.Plugins.Essentials; using VNLib.Plugins.Essentials.Sessions; using VNLib.Plugins.Essentials.Oauth.Tokens; @@ -39,6 +38,8 @@ using VNLib.Plugins.Essentials.Oauth.Applications; using VNLib.Plugins.Extensions.Loading; using VNLib.Plugins.Extensions.Loading.Sql; using VNLib.Plugins.Extensions.Loading.Events; +using VNLib.Plugins.Extensions.Loading.Routing; +using VNLib.Plugins.Sessions.OAuth.Endpoints; using static VNLib.Plugins.Essentials.Oauth.OauthSessionExtensions; namespace VNLib.Plugins.Sessions.OAuth @@ -47,9 +48,12 @@ namespace VNLib.Plugins.Sessions.OAuth /// <summary> /// Provides OAuth2 session management /// </summary> - [ConfigurationName(O2SessionProviderEntry.OAUTH2_CONFIG_KEY)] - internal sealed class OAuth2SessionProvider : ISessionProvider, ITokenManager, IApplicationTokenFactory, IIntervalScheduleable - { + [ExternService] + [ConfigurationName(OAUTH2_CONFIG_KEY)] + public sealed class OAuth2SessionProvider : ISessionProvider, ITokenManager, IApplicationTokenFactory, IIntervalScheduleable + { + public const string OAUTH2_CONFIG_KEY = "oauth2"; + private static readonly SessionHandle Skip = new(null, FileProcessArgs.VirtualSkip, null); private readonly OAuth2SessionStore _sessions; @@ -60,8 +64,6 @@ namespace VNLib.Plugins.Sessions.OAuth private uint _waitingConnections; - public bool IsConnected => _sessions.IsConnected; - public OAuth2SessionProvider(PluginBase plugin, IConfigScope config) { _sessions = plugin.GetOrCreateSingleton<OAuth2SessionStore>(); @@ -71,9 +73,40 @@ namespace VNLib.Plugins.Sessions.OAuth //Schedule interval plugin.ScheduleInterval(this, TimeSpan.FromMinutes(2)); + + IConfigScope o2Config = plugin.GetConfig(OAUTH2_CONFIG_KEY); + + /* + * Route built-in oauth2 endpoints + */ + if (o2Config.ContainsKey("token_path")) + { + /* + * Access token endpoint requires this instance as a token manager + * which would cause a circular dependency, so it needs to be routed + * manually + */ + AccessTokenEndpoint tokenEndpoint = new(plugin, o2Config, this); + //Create token endpoint + plugin.Route(tokenEndpoint); + } + + //Optional revocation endpoint + if (plugin.HasConfigForType<RevocationEndpoint>()) + { + //Route revocation endpoint + plugin.Route<RevocationEndpoint>(); + } } - public void SetLog(ILogProvider log) => _sessions.SetLog(log); + /* + * Called when + */ + public bool CanProcess(IHttpEvent entity) + { + //If authorization header is set try to process as oauth2 session + return _sessions.IsConnected && entity.Server.Headers.HeaderSet(HttpRequestHeader.Authorization); + } public ValueTask<SessionHandle> GetSessionAsync(IHttpEvent entity, CancellationToken cancellationToken) { @@ -217,9 +250,6 @@ namespace VNLib.Plugins.Sessions.OAuth //Remove tokens by thier object id from cache await _sessions.DeleteTokenAsync(token.Id, cancellationToken); } - //Ignore if the object has already been removed - catch (ObjectNotFoundException) - {} catch (Exception ex) { #pragma warning disable CA1508 // Avoid dead conditional code diff --git a/libs/VNLib.Plugins.Sessions.OAuth/src/OAuth2SessionStore.cs b/libs/VNLib.Plugins.Sessions.OAuth/src/OAuth2SessionStore.cs index 8c65bc8..6cf25c4 100644 --- a/libs/VNLib.Plugins.Sessions.OAuth/src/OAuth2SessionStore.cs +++ b/libs/VNLib.Plugins.Sessions.OAuth/src/OAuth2SessionStore.cs @@ -34,19 +34,25 @@ using VNLib.Plugins.Sessions.Cache.Client; using VNLib.Plugins.Extensions.VNCache; using VNLib.Plugins.Extensions.Loading; - namespace VNLib.Plugins.Sessions.OAuth { - [ConfigurationName(O2SessionProviderEntry.OAUTH2_CONFIG_KEY)] + [ConfigurationName(OAuth2SessionProvider.OAUTH2_CONFIG_KEY)] internal sealed class OAuth2SessionStore : SessionStore<OAuth2Session> { const int MAX_SESSION_BUFFER_SIZE = 16 * 1024; private ILogProvider _log; + ///<inheritdoc/> protected override ISessionIdFactory IdFactory { get; } + + ///<inheritdoc/> protected override IRemoteCacheStore Cache { get; } + + ///<inheritdoc/> protected override ISessionFactory<OAuth2Session> SessionFactory { get; } + + ///<inheritdoc/> protected override ILogProvider Log => _log; public bool IsConnected => Cache.IsConnected; @@ -56,11 +62,16 @@ namespace VNLib.Plugins.Sessions.OAuth OAuth2SessionConfig o2Conf = config.DeserialzeAndValidate<OAuth2SessionConfig>(); //Get global cache - IGlobalCacheProvider cache = plugin.GetOrCreateSingleton<VnGlobalCache>() + IGlobalCacheProvider? cache = plugin.GetDefaultGlobalCache()? .GetPrefixedCache(o2Conf.CachePrefix, HashAlg.SHA256); - //Create remote cache - Cache = new GlobalCacheStore(cache, MAX_SESSION_BUFFER_SIZE); + _ = cache ?? throw new MissingDependencyException("A global cache provider is required to store OAuth2 sessions. Please configure a cache provider"); + + //Get debug log if enabled + ILogProvider? sessDebugLog = plugin.HostArgs.HasArgument("--debug-sessions") ? plugin.Log.CreateScope("OAuth2-Sessions") : null; + + //Create cache store from global cache + Cache = new GlobalCacheStore(cache, MAX_SESSION_BUFFER_SIZE, sessDebugLog); IdFactory = plugin.GetOrCreateSingleton<OAuth2TokenFactory>(); @@ -70,12 +81,6 @@ namespace VNLib.Plugins.Sessions.OAuth _log = plugin.Log; } - public void SetLog(ILogProvider log) - { - _log = log; - } - - public Task DeleteTokenAsync(string token, CancellationToken cancellation) { return Cache.DeleteObjectAsync(token, cancellation); diff --git a/libs/VNLib.Plugins.Sessions.OAuth/src/OAuth2TokenFactory.cs b/libs/VNLib.Plugins.Sessions.OAuth/src/OAuth2TokenFactory.cs index b452e29..b97abae 100644 --- a/libs/VNLib.Plugins.Sessions.OAuth/src/OAuth2TokenFactory.cs +++ b/libs/VNLib.Plugins.Sessions.OAuth/src/OAuth2TokenFactory.cs @@ -33,7 +33,7 @@ using VNLib.Plugins.Essentials.Extensions; namespace VNLib.Plugins.Sessions.OAuth { - [ConfigurationName(O2SessionProviderEntry.OAUTH2_CONFIG_KEY)] + [ConfigurationName(OAuth2SessionProvider.OAUTH2_CONFIG_KEY)] internal sealed class OAuth2TokenFactory : ISessionIdFactory, IOauthSessionIdFactory { private readonly OAuth2SessionConfig _config; |