diff options
Diffstat (limited to 'plugins/VNLib.Plugins.Essentials.Accounts/src/AccountsEntryPoint.cs')
-rw-r--r-- | plugins/VNLib.Plugins.Essentials.Accounts/src/AccountsEntryPoint.cs | 234 |
1 files changed, 178 insertions, 56 deletions
diff --git a/plugins/VNLib.Plugins.Essentials.Accounts/src/AccountsEntryPoint.cs b/plugins/VNLib.Plugins.Essentials.Accounts/src/AccountsEntryPoint.cs index f61647f..96b56b4 100644 --- a/plugins/VNLib.Plugins.Essentials.Accounts/src/AccountsEntryPoint.cs +++ b/plugins/VNLib.Plugins.Essentials.Accounts/src/AccountsEntryPoint.cs @@ -23,20 +23,21 @@ */ using System; -using System.Linq; -using System.Collections.Generic; +using System.Text.Json; using System.ComponentModel.Design; +using VNLib.Utils; using VNLib.Utils.Memory; using VNLib.Utils.Logging; using VNLib.Plugins.Attributes; using VNLib.Plugins.Essentials.Users; using VNLib.Plugins.Essentials.Middleware; +using VNLib.Plugins.Essentials.Accounts.MFA; using VNLib.Plugins.Essentials.Accounts.Endpoints; +using VNLib.Plugins.Essentials.Accounts.SecurityProvider; using VNLib.Plugins.Extensions.Loading; using VNLib.Plugins.Extensions.Loading.Users; using VNLib.Plugins.Extensions.Loading.Routing; -using VNLib.Plugins.Essentials.Accounts.SecurityProvider; namespace VNLib.Plugins.Essentials.Accounts { @@ -45,6 +46,8 @@ namespace VNLib.Plugins.Essentials.Accounts public override string PluginName => "Essentials.Accounts"; + private bool SetupMode => PluginConfig.TryGetProperty("setup_mode", out JsonElement el) && el.GetBoolean(); + private AccountSecProvider? _securityProvider; [ServiceConfigurator] @@ -83,7 +86,7 @@ namespace VNLib.Plugins.Essentials.Accounts if (this.HasConfigForType<PasswordChangeEndpoint>()) { this.Route<PasswordChangeEndpoint>(); - } + } if (this.HasConfigForType<MFAEndpoint>()) { @@ -104,6 +107,11 @@ namespace VNLib.Plugins.Essentials.Accounts Log.Information("Configuring the account security provider service"); } + if (SetupMode) + { + Log.Warn("Setup mode is enabled, this is not recommended for production use"); + } + //Write loaded to log Log.Information("Plugin loaded"); } @@ -118,90 +126,115 @@ namespace VNLib.Plugins.Essentials.Accounts protected override async void ProcessHostCommand(string cmd) { - //Only process commands if the plugin is in debug mode - if (!this.IsDebug()) + //Only process commands if the plugin is in setup mode + if (!SetupMode) { return; } try { + //Create argument parser + ArgumentList args = new(cmd.Split(' ')); + IUserManager Users = this.GetOrCreateSingleton<UserManager>(); IPasswordHashingProvider Passwords = this.GetOrCreateSingleton<ManagedPasswordHashing>(); - //get args as a list - List<string> args = cmd.Split(' ').ToList(); + string? username = args.GetArgument("-u"); + string? password = args.GetArgument("-p"); + if (args.Count < 3) { - Log.Warn("No command specified"); + Log.Warn("Not enough arguments, use the help command to view available commands"); + return; } - switch (args[2].ToLower()) + + switch (args[2].ToLower(null)) { + case "help": + const string help = @" + +Command help for {name} + +Usage: p {name} <command> [options] + +Commands: + create -u <username> -p <password> Create a new user + reset-password -u <username> -p <password> -l <priv level> Reset a user's password + delete -u <username> Delete a user + 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 + help Display this help message +"; + Log.Information(help, PluginName); + break; //Create new user - case "create": + case "create": { - int uid = args.IndexOf("-u"); - int pwd = args.IndexOf("-p"); - if (uid < 0 || pwd < 0) + if (username == null || password == null) { Log.Warn("You are missing required argument values. Format 'create -u <username> -p <password>'"); - return; + break; } - string username = args[uid + 1].Trim(); - string randomUserId = AccountUtil.GetRandomUserId(); - //Password as privatestring DANGEROUS to refs - using (PrivateString password = (PrivateString)args[pwd + 1].Trim()!) + + string? privilege = args.GetArgument("-l"); + + if(!ulong.TryParse(privilege, out ulong privLevel)) { - //Hash the password - using PrivateString passHash = Passwords.Hash(password); - //Create the user - using IUser user = await Users.CreateUserAsync(randomUserId, username, AccountUtil.MINIMUM_LEVEL, passHash); - //Set active flag - user.Status = UserStatus.Active; - //Set local account - user.SetAccountOrigin(AccountUtil.LOCAL_ACCOUNT_ORIGIN); - - await user.ReleaseAsync(); + privLevel = AccountUtil.MINIMUM_LEVEL; } - Log.Information("Successfully created user {id}", username); + //Hash the password + using PrivateString passHash = Passwords.Hash(password); + //Create the user + using IUser user = await Users.CreateUserAsync(username, passHash, privLevel); + + //Set active flag + user.Status = UserStatus.Active; + //Set local account + user.SetAccountOrigin(AccountUtil.LOCAL_ACCOUNT_ORIGIN); + + await user.ReleaseAsync(); + + Log.Information("Successfully created user {id}", username); } break; - case "reset": + case "reset-password": { - int uid = args.IndexOf("-u"); - int pwd = args.IndexOf("-p"); - if (uid < 0 || pwd < 0) + if (username == null || password == null) { - Log.Warn("You are missing required argument values. Format 'reset -u <username> -p <password>'"); - return; + Log.Warn("You are missing required argument values. Format 'create -u <username> -p <password>'"); + break; } - string username = args[uid + 1].Trim(); - //Password as privatestring DANGEROUS to refs - using (PrivateString password = (PrivateString)args[pwd + 1].Trim()!) + + //Hash the password + using PrivateString passHash = Passwords.Hash(password); + + //Get the user + using IUser? user = await Users.GetUserFromEmailAsync(username); + + if(user == null) { - //Hash the password - using PrivateString passHash = Passwords.Hash(password); - //Get the user - using IUser? user = await Users.GetUserFromEmailAsync(username); - - if(user == null) - { - Log.Warn("The specified user does not exist"); - break; - } - - //Set the password - await Users.UpdatePassAsync(user, passHash); + Log.Warn("The specified user does not exist"); + break; } + + //Set the password + await Users.UpdatePassAsync(user, passHash); + Log.Information("Successfully reset password for {id}", username); } break; case "delete": { - //get user-id - string userId = args[3].Trim(); + if(username == null) + { + Log.Warn("You are missing required argument values. Format 'delete -u <username>'"); + break; + } + //Get user - using IUser? user = await Users.GetUserFromEmailAsync(userId); + using IUser? user = await Users.GetUserFromEmailAsync(username); if (user == null) { @@ -213,10 +246,99 @@ namespace VNLib.Plugins.Essentials.Accounts user.Delete(); //Release user await user.ReleaseAsync(); + + Log.Information("Successfully deleted user {id}", username); + } + break; + case "disable-mfa": + { + if (username == null) + { + Log.Warn("You are missing required argument values. Format 'disable-mfa -u <username>'"); + break; + } + + //Get user + using IUser? user = await Users.GetUserFromEmailAsync(username); + + if (user == null) + { + Log.Warn("The specified user does not exist"); + break; + } + + user.MFADisable(); + await user.ReleaseAsync(); + + Log.Information("Successfully disabled MFA for {id}", username); + } + break; + case "enable-totp": + { + string? secret = args.GetArgument("-s"); + + if (username == null || secret == null) + { + Log.Warn("You are missing required argument values. Format 'enable-totp -u <username> -s <secret>'"); + break; + } + + //Get user + using IUser? user = await Users.GetUserFromEmailAsync(username); + + if (user == null) + { + Log.Warn("The specified user does not exist"); + break; + } + + try + { + byte[] sec = VnEncoding.FromBase32String(secret) ?? throw new Exception(""); + } + catch + { + Log.Error("Your TOTP secret is not valid base32"); + break; + } + + //Update the totp secret and flush changes + user.MFASetTOTPSecret(secret); + await user.ReleaseAsync(); + + Log.Information("Successfully set TOTP secret for {id}", username); + } + break; + case "set-privilege": + { + if (username == null) + { + Log.Warn("You are missing required argument values. Format 'set-privilege -u <username> -l <privilege level>'"); + break; + } + + string? privilege = args.GetArgument("-l"); + if (!ulong.TryParse(privilege, out ulong privLevel)) + { + Log.Warn("You are missing required argument values. Format 'set-privilege -u <username> -l <privilege level>'"); + break; + } + + //Get user + using IUser? user = await Users.GetUserFromEmailAsync(username); + if (user == null) + { + Log.Warn("The specified user does not exist"); + break; + } + + user.Privileges = privLevel; + await user.ReleaseAsync(); + Log.Information("Successfully set privilege level for {id}", username); } break; default: - Log.Warn("Uknown command"); + Log.Warn("Uknown command, use the help command"); break; } } |