diff options
Diffstat (limited to 'plugins/VNLib.Plugins.Essentials.Accounts')
5 files changed, 80 insertions, 11 deletions
diff --git a/plugins/VNLib.Plugins.Essentials.Accounts/README.md b/plugins/VNLib.Plugins.Essentials.Accounts/README.md index 8fce3e1..13f2dad 100644 --- a/plugins/VNLib.Plugins.Essentials.Accounts/README.md +++ b/plugins/VNLib.Plugins.Essentials.Accounts/README.md @@ -1,9 +1,18 @@ -# VNLib.Plugins.Essentials.Accounts +# VNLib.Plugins.Essentials.Accounts +*An Essentials web plugin that provides endpoints for authenticating, registering, resetting, local user accounts including multi-factor authentication using TOTP (for now).* -An essentials web plugin that provides endpoints for authenticating, registering, resetting, local user accounts including multi-factor authentication using TOTP (for now). +### Dependency notice +This library uses some external dependencies: VaultSharp, Serilog, libargon2, and FluentValidation. -#### Builds -Debug build w/ symbols & xml docs, release builds, NuGet packages, and individually packaged source code are available on my [website](https://www.vaughnnugent.com/resources/software). All tar-gzip (.tgz) files will have an associated .sha384 appended checksum of the desired download file. +## Builds +Debug build w/ symbols & xml docs, release builds, NuGet packages, and individually packaged source code are available on my website (link below). -## License +## Docs and Guides +Documentation, specifications, and setup guides are available on my website. + +[Docs and Articles](https://www.vaughnnugent.com/resources/software/articles?tags=docs,_VNLib.Plugins.Essentials.Accounts) +[Builds and Source](https://www.vaughnnugent.com/resources/software/modules/Plugins.Essentials) +[Nuget Feeds](https://www.vaughnnugent.com/resources/software/modules) + +## License Source files in for this project are licensed to you under the GNU Affero General Public License (or any later version). See the LICENSE files for more information.
\ No newline at end of file diff --git a/plugins/VNLib.Plugins.Essentials.Accounts/src/AccountsEntryPoint.cs b/plugins/VNLib.Plugins.Essentials.Accounts/src/AccountsEntryPoint.cs index 96b56b4..5f171cd 100644 --- a/plugins/VNLib.Plugins.Essentials.Accounts/src/AccountsEntryPoint.cs +++ b/plugins/VNLib.Plugins.Essentials.Accounts/src/AccountsEntryPoint.cs @@ -26,6 +26,8 @@ using System; using System.Text.Json; using System.ComponentModel.Design; +using FluentValidation.Results; + using VNLib.Utils; using VNLib.Utils.Memory; using VNLib.Utils.Logging; @@ -46,14 +48,14 @@ namespace VNLib.Plugins.Essentials.Accounts public override string PluginName => "Essentials.Accounts"; - private bool SetupMode => PluginConfig.TryGetProperty("setup_mode", out JsonElement el) && el.GetBoolean(); + private bool SetupMode => HostArgs.HasArgument("--account-setup"); private AccountSecProvider? _securityProvider; [ServiceConfigurator] public void ConfigureServices(IServiceContainer services) { - //Export the build in security provider + //Export the built in security provider and add it as a middleware item as well if (_securityProvider != null) { services.AddService(typeof(IAccountSecurityProvider), _securityProvider); @@ -164,6 +166,7 @@ Commands: disable-mfa -u <username> Disable a user's MFA configuration enable-totp -u <username> -s <base32 secret> Enable TOTP MFA for a user set-privilege -u <username> -l <priv level> Set a user's privilege level + add-pubkey -u <username> Add a JWK public key to a user's profile help Display this help message "; Log.Information(help, PluginName); @@ -309,6 +312,58 @@ Commands: Log.Information("Successfully set TOTP secret for {id}", username); } break; + case "add-pubkey": + { + + if (string.IsNullOrWhiteSpace(username)) + { + Log.Warn("You are missing required argument values. Format 'add-pubkey -u <username>"); + break; + } + + Console.WriteLine("Enter public key JWK..."); + + //Wait for pubkey + string? pubkeyJwk = Console.ReadLine(); + + if(string.IsNullOrWhiteSpace(pubkeyJwk)) + { + Log.Warn("No public key supplied."); + break; + } + + //Get user + using IUser? user = await Users.GetUserFromEmailAsync(username); + + if (user == null) + { + Log.Warn("The specified user does not exist"); + break; + } + + PkiAuthPublicKey? pubkey = JsonSerializer.Deserialize<PkiAuthPublicKey>(pubkeyJwk); + if (pubkey == null) + { + Log.Error("You public key is not a JSON object"); + break; + } + + //Validate + ValidationResult res = PkiLoginEndpoint.UserJwkValidator.Validate(pubkey); + if (!res.IsValid) + { + Log.Error("The public key JWK is not valid:\n{errors}", res.ToDictionary()); + break; + } + + + //Add/update the public key and flush changes + user.PKIAddPublicKey(pubkey); + await user.ReleaseAsync(); + + Log.Information("Successfully set TOTP secret for {id}", username); + } + break; case "set-privilege": { if (username == null) diff --git a/plugins/VNLib.Plugins.Essentials.Accounts/src/Endpoints/LoginEndpoint.cs b/plugins/VNLib.Plugins.Essentials.Accounts/src/Endpoints/LoginEndpoint.cs index b01cc3d..66a099e 100644 --- a/plugins/VNLib.Plugins.Essentials.Accounts/src/Endpoints/LoginEndpoint.cs +++ b/plugins/VNLib.Plugins.Essentials.Accounts/src/Endpoints/LoginEndpoint.cs @@ -80,8 +80,8 @@ namespace VNLib.Plugins.Essentials.Accounts.Endpoints public LoginEndpoint(PluginBase pbase, IConfigScope config) { string path = config.GetRequiredProperty("path", p => p.GetString()!); - TimeSpan duration = config["failed_count_timeout_sec"].GetTimeSpan(TimeParseType.Seconds); - uint maxLogins = config["failed_count_max"].GetUInt32(); + TimeSpan duration = config["failed_attempt_timeout_sec"].GetTimeSpan(TimeParseType.Seconds); + uint maxLogins = config["max_login_attempts"].GetUInt32(); InitPathAndLog(path, pbase.Log); diff --git a/plugins/VNLib.Plugins.Essentials.Accounts/src/Endpoints/PkiLoginEndpoint.cs b/plugins/VNLib.Plugins.Essentials.Accounts/src/Endpoints/PkiLoginEndpoint.cs index 6d9e049..0abe657 100644 --- a/plugins/VNLib.Plugins.Essentials.Accounts/src/Endpoints/PkiLoginEndpoint.cs +++ b/plugins/VNLib.Plugins.Essentials.Accounts/src/Endpoints/PkiLoginEndpoint.cs @@ -63,7 +63,11 @@ namespace VNLib.Plugins.Essentials.Accounts.Endpoints private static JwtLoginValidator LwValidator { get; } = new(); private static IValidator<AuthenticationInfo> AuthValidator { get; } = AuthenticationInfo.GetValidator(); - private static IValidator<PkiAuthPublicKey> UserJwkValidator { get; } = GetKeyValidator(); + + /// <summary> + /// A validator used to validate <see cref="PkiAuthPublicKey"/> instances + /// </summary> + public static IValidator<PkiAuthPublicKey> UserJwkValidator { get; } = GetKeyValidator(); private readonly JwtEndpointConfig _config; private readonly IUserManager _users; diff --git a/plugins/VNLib.Plugins.Essentials.Accounts/src/SecurityProvider/AccountSecProvider.cs b/plugins/VNLib.Plugins.Essentials.Accounts/src/SecurityProvider/AccountSecProvider.cs index 878a63a..d9c1703 100644 --- a/plugins/VNLib.Plugins.Essentials.Accounts/src/SecurityProvider/AccountSecProvider.cs +++ b/plugins/VNLib.Plugins.Essentials.Accounts/src/SecurityProvider/AccountSecProvider.cs @@ -58,6 +58,7 @@ namespace VNLib.Plugins.Essentials.Accounts.SecurityProvider { [ConfigurationName("account_security", Required = false)] + [MiddlewareImpl(MiddlewareImplOptions.SecurityCritical)] internal class AccountSecProvider : IAccountSecurityProvider, IHttpMiddleware { private const int PUB_KEY_JWT_NONCE_SIZE = 16; @@ -125,7 +126,7 @@ namespace VNLib.Plugins.Essentials.Accounts.SecurityProvider //If the session stored a user-agent, make sure it matches the connection if (session.UserAgent != null && !session.UserAgent.Equals(entity.Server.UserAgent, StringComparison.Ordinal)) { - _logger.Debug("Denied authorized connection from {ip} because user-agent changed"); + _logger.Debug("Denied authorized connection from {ip} because user-agent changed", entity.TrustedRemoteIp); return ValueTask.FromResult(FileProcessArgs.Deny); } } |