aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLibravatar vnugent <public@vaughnnugent.com>2023-12-20 19:06:39 -0500
committerLibravatar vnugent <public@vaughnnugent.com>2023-12-20 19:06:39 -0500
commit3c2341cbff73f5f8c3748ae60b406e8aa4d7e129 (patch)
tree33c060304f05574f1e6b171cb4c5e8e5eeff85ad
parent549e4e4b4a2caa28655b8924b0868052c84b2044 (diff)
add scopes endpoint and re-add password protection for applications
-rw-r--r--Plugins/OAuth2ClientApplications/src/Endpoints/ApplicationEndpoint.cs64
-rw-r--r--Plugins/OAuth2ClientApplications/src/Endpoints/ScopesEndpoint.cs61
-rw-r--r--Plugins/OAuth2ClientApplications/src/OAuth2ClientAppsEntryPoint.cs1
3 files changed, 119 insertions, 7 deletions
diff --git a/Plugins/OAuth2ClientApplications/src/Endpoints/ApplicationEndpoint.cs b/Plugins/OAuth2ClientApplications/src/Endpoints/ApplicationEndpoint.cs
index a548ae0..4b770ca 100644
--- a/Plugins/OAuth2ClientApplications/src/Endpoints/ApplicationEndpoint.cs
+++ b/Plugins/OAuth2ClientApplications/src/Endpoints/ApplicationEndpoint.cs
@@ -43,10 +43,14 @@ using VNLib.Plugins.Extensions.Validation;
using VNLib.Plugins.Extensions.Loading;
using VNLib.Plugins.Extensions.Loading.Sql;
using VNLib.Plugins.Extensions.Data.Extensions;
+using VNLib.Plugins.Essentials.Users;
+using VNLib.Plugins.Extensions.Loading.Users;
using static VNLib.Plugins.Essentials.Statics;
+
namespace OAuth2ClientApplications.Endpoints
{
+
[ConfigurationName("applications")]
internal sealed class ApplicationEndpoint : ProtectedWebEndpoint
{
@@ -54,6 +58,7 @@ namespace OAuth2ClientApplications.Endpoints
private readonly ApplicationStore Applications;
private readonly int MaxAppsPerUser;
private readonly string MaxAppOverloadMessage;
+ private readonly IUserManager Users;
private static readonly UserAppValidator Validator = new();
@@ -68,6 +73,8 @@ namespace OAuth2ClientApplications.Endpoints
//Load apps
Applications = new(plugin.GetContextOptions(), plugin.GetOrCreateSingleton<ManagedPasswordHashing>());
+ Users = plugin.GetOrCreateSingleton<UserManager>();
+
//Complie overload message
MaxAppOverloadMessage = $"You have reached the limit of {MaxAppsPerUser} applications, this application cannot be created";
}
@@ -116,6 +123,7 @@ namespace OAuth2ClientApplications.Endpoints
webm.Result = "OAuth is only available for internal user accounts";
return VirtualClose(entity, webm, HttpStatusCode.Forbidden);
}
+
if (entity.QueryArgs.IsArgumentSet("action", "create"))
{
return await CreateAppAsync(entity);
@@ -134,11 +142,17 @@ namespace OAuth2ClientApplications.Endpoints
//Update message will include a challenge and an app id
string? appId = update.RootElement.GetPropString("Id");
- if (string.IsNullOrWhiteSpace(appId))
+ if (webm.Assert(!string.IsNullOrWhiteSpace(appId), "Application with the specified id does not exist"))
{
- return VfReturnType.NotFound;
+ return VirtualClose(entity, webm, HttpStatusCode.NotFound);
}
-
+
+ //validate the user's password
+ if (await ValidateUserPassword(entity, update, webm) == false)
+ {
+ return VirtualClose(entity, webm, HttpStatusCode.Unauthorized);
+ }
+
//Update the app's secret
using PrivateString? secret = await Applications.UpdateSecretAsync(entity.Session.UserID, appId);
@@ -173,13 +187,19 @@ namespace OAuth2ClientApplications.Endpoints
}
//Update message will include a challenge and an app id
- string? appId = update.RootElement.GetPropString("Id");
-
+ string? appId = update.RootElement.GetPropString("Id");
+
if (string.IsNullOrWhiteSpace(appId))
{
return VfReturnType.NotFound;
- }
-
+ }
+
+ //validate the password
+ if(await ValidateUserPassword(entity, update, webm) == false)
+ {
+ return VirtualClose(entity, webm, HttpStatusCode.Unauthorized);
+ }
+
//Try to delete the app
if (await Applications.DeleteAsync(appId, entity.Session.UserID))
{
@@ -307,6 +327,36 @@ namespace OAuth2ClientApplications.Endpoints
return VfReturnType.VirtualSkip;
}
+ private async Task<bool> ValidateUserPassword(HttpEntity entity, JsonDocument request, WebMessage webm)
+ {
+ //Get password from request and capture it as a private string
+ using PrivateString? rawPassword = PrivateString.ToPrivateString(request.RootElement.GetPropString("password"), true);
+
+ if (webm.Assert(rawPassword != null, "Please enter your account password"))
+ {
+ //Must sent a 401 to indicate that the password is required
+ return false;
+ }
+
+ //Get the current user from the store
+ using IUser? user = await Users.GetUserFromIDAsync(entity.Session.UserID, entity.EventCancellation);
+
+ if (webm.Assert(user != null, "Please check your password"))
+ {
+ return false;
+ }
+
+ //Validate the password against the user
+ bool isPasswordValid = await Users.ValidatePasswordAsync(user, rawPassword, PassValidateFlags.None, entity.EventCancellation) == UserPassValResult.Success;
+
+ if (webm.Assert(isPasswordValid, "Please check your password"))
+ {
+ return false;
+ }
+
+ return true;
+ }
+
private static string ParsePermissions(string permissions)
{
StringBuilder builder = new();
diff --git a/Plugins/OAuth2ClientApplications/src/Endpoints/ScopesEndpoint.cs b/Plugins/OAuth2ClientApplications/src/Endpoints/ScopesEndpoint.cs
new file mode 100644
index 0000000..b89b591
--- /dev/null
+++ b/Plugins/OAuth2ClientApplications/src/Endpoints/ScopesEndpoint.cs
@@ -0,0 +1,61 @@
+/*
+* Copyright (c) 2023 Vaughn Nugent
+*
+* Library: VNLib
+* Package: OAuth2ClientApplications
+* File: ScopesEndpoint.cs
+*
+* ScopesEndpoint.cs is part of OAuth2ClientApplications which is part of the larger
+* VNLib collection of libraries and utilities.
+*
+* OAuth2ClientApplications 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.
+*
+* OAuth2ClientApplications 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.Linq;
+
+using VNLib.Plugins;
+using VNLib.Plugins.Essentials;
+using VNLib.Plugins.Essentials.Endpoints;
+using VNLib.Plugins.Extensions.Loading;
+
+
+namespace OAuth2ClientApplications.Endpoints
+{
+ [ConfigurationName("scopes")]
+ internal sealed class ScopesEndpoint : UnprotectedWebEndpoint
+ {
+
+ private readonly string[] _permissions;
+
+ public ScopesEndpoint(PluginBase plugin, IConfigScope config)
+ {
+ //Get configuration variables from plugin
+ string? path = config["path"].GetString();
+
+ //Get scope permissions
+ _permissions = config["scopes"].EnumerateArray()
+ .Select(p => p.GetString()!)
+ .Where(p => p!= null)
+ .ToArray();
+
+ InitPathAndLog(path, plugin.Log);
+ }
+
+ protected override VfReturnType Get(HttpEntity entity)
+ {
+ //Return the permissions/scopes array
+ return VirtualOkJson(entity, _permissions);
+ }
+ }
+} \ No newline at end of file
diff --git a/Plugins/OAuth2ClientApplications/src/OAuth2ClientAppsEntryPoint.cs b/Plugins/OAuth2ClientApplications/src/OAuth2ClientAppsEntryPoint.cs
index 3390a77..6ad190e 100644
--- a/Plugins/OAuth2ClientApplications/src/OAuth2ClientAppsEntryPoint.cs
+++ b/Plugins/OAuth2ClientApplications/src/OAuth2ClientAppsEntryPoint.cs
@@ -40,6 +40,7 @@ namespace OAuth2ClientApplications
{
//Route the applications endpoint
this.Route<ApplicationEndpoint>();
+ this.Route<ScopesEndpoint>();
Log.Information("Plugin Loaded");
}