aboutsummaryrefslogtreecommitdiff
path: root/plugins/VNLib.Plugins.Essentials.Accounts
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/VNLib.Plugins.Essentials.Accounts')
-rw-r--r--plugins/VNLib.Plugins.Essentials.Accounts/README.md19
-rw-r--r--plugins/VNLib.Plugins.Essentials.Accounts/src/AccountsEntryPoint.cs59
-rw-r--r--plugins/VNLib.Plugins.Essentials.Accounts/src/Endpoints/LoginEndpoint.cs4
-rw-r--r--plugins/VNLib.Plugins.Essentials.Accounts/src/Endpoints/PkiLoginEndpoint.cs6
-rw-r--r--plugins/VNLib.Plugins.Essentials.Accounts/src/SecurityProvider/AccountSecProvider.cs3
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);
}
}