diff --git a/src/Gemstone.Web/Security/APIAccessHandler.cs b/src/Gemstone.Web/Security/APIAccessHandler.cs
new file mode 100644
index 00000000..a32de207
--- /dev/null
+++ b/src/Gemstone.Web/Security/APIAccessHandler.cs
@@ -0,0 +1,74 @@
+//******************************************************************************************************
+// ControllerAccessHandler.cs - Gbtc
+//
+// Copyright © 2025, Grid Protection Alliance. All Rights Reserved.
+//
+// Licensed to the Grid Protection Alliance (GPA) under one or more contributor license agreements. See
+// the NOTICE file distributed with this work for additional information regarding copyright ownership.
+// The GPA licenses this file to you under the MIT License (MIT), the "License"; you may not use this
+// file except in compliance with the License. You may obtain a copy of the License at:
+//
+// http://opensource.org/licenses/MIT
+//
+// Unless agreed to in writing, the subject software distributed under the License is distributed on an
+// "AS-IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. Refer to the
+// License for the specific language governing permissions and limitations.
+//
+// Code Modification History:
+// ----------------------------------------------------------------------------------------------------
+// 07/29/2025 - Stephen C. Wills
+// Generated original version of source code.
+//
+//******************************************************************************************************
+
+using System;
+using System.Collections.Generic;
+using System.Reflection;
+using System.Security.Claims;
+using System.Threading.Tasks;
+using Gemstone.Reflection.MemberInfoExtensions;
+using Gemstone.Security;
+using Gemstone.Security.AccessControl;
+using Microsoft.AspNetCore.Authorization;
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Http.Features;
+using Microsoft.AspNetCore.Mvc.Controllers;
+using Microsoft.AspNetCore.Routing;
+
+namespace Gemstone.Web.Security;
+
+///
+/// Authorization handler for access to rest api actions.
+///
+public class APIAccessHandler: GemstoneAccessHandler
+{
+ ///
+ protected override string ResourceType => "API";
+
+ /// #ToDo - Remove Support for Gemstone.ResourceAccess.Default
+}
+
+///
+/// Requirement to be handled by the .
+///
+public class APIAccessRequirement : IAuthorizationRequirement
+{
+}
+
+///
+/// Defines extension methods for the .
+///
+public static class APIAccessHandlerExtensions
+{
+ private static APIAccessRequirement Requirement { get; } = new();
+
+ ///
+ /// Adds the to the policy.
+ ///
+ /// The policy builder
+ /// The policy builder.
+ public static AuthorizationPolicyBuilder RequireAPIAccess(this AuthorizationPolicyBuilder builder)
+ {
+ return builder.AddRequirements(Requirement);
+ }
+}
diff --git a/src/Gemstone.Web/Security/ControllerAccessHandler.cs b/src/Gemstone.Web/Security/ControllerAccessHandler.cs
index 3d642779..18d23de2 100644
--- a/src/Gemstone.Web/Security/ControllerAccessHandler.cs
+++ b/src/Gemstone.Web/Security/ControllerAccessHandler.cs
@@ -36,135 +36,12 @@ namespace Gemstone.Web.Security;
///
/// Authorization handler for access to controller actions.
///
-public class ControllerAccessHandler : AuthorizationHandler
+public class ControllerAccessHandler : GemstoneAccessHandler
{
- #region [ Members ]
-
- // Nested Types
- private enum Permission
- {
- Allow,
- Deny,
- Neither
- }
-
- private class ContextWrapper(AuthorizationHandlerContext context, ControllerAccessRequirement requirement, HttpContext httpContext, Endpoint endpoint, ControllerActionDescriptor descriptor)
- {
- private AuthorizationHandlerContext Context { get; } = context;
- private ControllerAccessRequirement Requirement { get; } = requirement;
-
- public ClaimsPrincipal User { get; } = context.User;
- public Endpoint Endpoint { get; } = endpoint;
- public ControllerActionDescriptor Descriptor { get; } = descriptor;
- public string HttpMethod => httpContext.Request.Method;
-
- public bool Succeed()
- {
- Context.Succeed(Requirement);
- return true;
- }
-
- public bool Fail(AuthorizationFailureReason reason)
- {
- Context.Fail(reason);
- return true;
- }
- }
-
- #endregion
-
- #region [ Methods ]
-
+
///
- protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, ControllerAccessRequirement requirement)
- {
- if (context.Resource is not HttpContext httpContext)
- return Task.CompletedTask;
-
- IEndpointFeature? endpointFeature = httpContext.Features.Get();
- Endpoint? endpoint = endpointFeature?.Endpoint;
-
- if (endpoint is null)
- return Task.CompletedTask;
-
- ControllerActionDescriptor? descriptor = endpoint.Metadata
- .GetMetadata();
-
- if (descriptor is null)
- return Task.CompletedTask;
-
- ContextWrapper wrapper = new(context, requirement, httpContext, endpoint, descriptor);
-
- if (HandleResourceActionPermission(wrapper))
- return Task.CompletedTask;
-
- HandleResourceAccessPermission(wrapper);
- return Task.CompletedTask;
- }
-
- private bool HandleResourceActionPermission(ContextWrapper wrapper)
- {
- IRouteNameMetadata? routeNameMetadata = wrapper.Endpoint.Metadata
- .GetMetadata();
-
- string? routeName = routeNameMetadata?.RouteName;
-
- string resource = wrapper.Descriptor.ControllerName;
- string action = routeName ?? wrapper.Descriptor.ActionName;
- string claimValue = $"Controller {resource} {action}";
- Permission permission = GetResourceActionPermission(wrapper.User, claimValue);
-
- return
- (permission == Permission.Deny && fail()) ||
- (permission == Permission.Allow && succeed());
-
- bool succeed() =>
- wrapper.Succeed();
-
- bool fail()
- {
- AuthorizationFailureReason reason = ToFailureReason(claimValue);
- return wrapper.Fail(reason);
- }
- }
-
- private AuthorizationFailureReason ToFailureReason(string claim)
- {
- return new AuthorizationFailureReason(this, $"{claim} permission denied");
- }
-
- #endregion
-
- #region [ Static ]
-
- // Static Methods
-
- private static Permission GetResourceActionPermission(ClaimsPrincipal user, string claimValue)
- {
- string allowClaim = $"Gemstone.ResourceAction.Allow";
- string denyClaim = $"Gemstone.ResourceAction.Deny";
-
- if (user.HasClaim(denyClaim, claimValue))
- return Permission.Deny;
-
- return user.HasClaim(allowClaim, claimValue)
- ? Permission.Allow
- : Permission.Neither;
- }
-
- private static void HandleResourceAccessPermission(ContextWrapper wrapper)
- {
- IReadOnlyList accessAttributes = wrapper.Endpoint.Metadata
- .GetOrderedMetadata();
-
- string resourceName = accessAttributes.GetResourceName(wrapper.Descriptor);
- ResourceAccessType access = accessAttributes.GetAccessType(wrapper.HttpMethod);
-
- if (wrapper.User.HasAccessTo("Controller", resourceName, access))
- wrapper.Succeed();
- }
+ protected override string ResourceType => "Controller";
- #endregion
}
///
diff --git a/src/Gemstone.Web/Security/GemstoneAccessHandler.cs b/src/Gemstone.Web/Security/GemstoneAccessHandler.cs
new file mode 100644
index 00000000..7881c39c
--- /dev/null
+++ b/src/Gemstone.Web/Security/GemstoneAccessHandler.cs
@@ -0,0 +1,170 @@
+//******************************************************************************************************
+// GemstoneAccessHandler.cs - Gbtc
+//
+// Copyright © 2026, Grid Protection Alliance. All Rights Reserved.
+//
+// Licensed to the Grid Protection Alliance (GPA) under one or more contributor license agreements. See
+// the NOTICE file distributed with this work for additional information regarding copyright ownership.
+// The GPA licenses this file to you under the MIT License (MIT), the "License"; you may not use this
+// file except in compliance with the License. You may obtain a copy of the License at:
+//
+// http://opensource.org/licenses/MIT
+//
+// Unless agreed to in writing, the subject software distributed under the License is distributed on an
+// "AS-IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. Refer to the
+// License for the specific language governing permissions and limitations.
+//
+// Code Modification History:
+// ----------------------------------------------------------------------------------------------------
+// 07/09/2026 - C. Lackner
+// Generated original version of source code.
+//
+//******************************************************************************************************
+
+using System.Collections.Generic;
+using System.Security.Claims;
+using System.Threading.Tasks;
+using Gemstone.Security;
+using Gemstone.Security.AccessControl;
+using Microsoft.AspNetCore.Authorization;
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Http.Features;
+using Microsoft.AspNetCore.Mvc.Controllers;
+using Microsoft.AspNetCore.Routing;
+
+namespace Gemstone.Web.Security;
+
+///
+/// Authorization handler for access to generic Resources.
+///
+public class GemstoneAccessHandler : AuthorizationHandler where T : IAuthorizationRequirement
+{
+ #region [ Members ]
+
+ protected virtual string ResourceType { get; }
+
+ // Nested Types
+ private enum Permission
+ {
+ Allow,
+ Deny,
+ Neither
+ }
+
+ private class ContextWrapper(AuthorizationHandlerContext context, T requirement, HttpContext httpContext, Endpoint endpoint, ControllerActionDescriptor descriptor) where T : IAuthorizationRequirement
+ {
+ private AuthorizationHandlerContext Context { get; } = context;
+ private T Requirement { get; } = requirement;
+
+ public ClaimsPrincipal User { get; } = context.User;
+ public Endpoint Endpoint { get; } = endpoint;
+ public ControllerActionDescriptor Descriptor { get; } = descriptor;
+ public string HttpMethod => httpContext.Request.Method;
+
+ public bool Succeed()
+ {
+ Context.Succeed(Requirement);
+ return true;
+ }
+
+ public bool Fail(AuthorizationFailureReason reason)
+ {
+ Context.Fail(reason);
+ return true;
+ }
+ }
+
+
+ #endregion
+
+ #region [ Methods ]
+
+ ///
+ protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, T requirement)
+ {
+ if (context.Resource is not HttpContext httpContext)
+ return Task.CompletedTask;
+
+ IEndpointFeature? endpointFeature = httpContext.Features.Get();
+ Endpoint? endpoint = endpointFeature?.Endpoint;
+
+ if (endpoint is null)
+ return Task.CompletedTask;
+
+ ControllerActionDescriptor? descriptor = endpoint.Metadata
+ .GetMetadata();
+
+ if (descriptor is null)
+ return Task.CompletedTask;
+
+ ContextWrapper wrapper = new(context, requirement, httpContext, endpoint, descriptor);
+
+ if (HandleResourceActionPermission(wrapper))
+ return Task.CompletedTask;
+
+ HandleResourceAccessPermission(wrapper, ResourceType);
+ return Task.CompletedTask;
+ }
+
+ private bool HandleResourceActionPermission(ContextWrapper wrapper)
+ {
+ IRouteNameMetadata? routeNameMetadata = wrapper.Endpoint.Metadata
+ .GetMetadata();
+
+ string? routeName = routeNameMetadata?.RouteName;
+
+ string resource = wrapper.Descriptor.ControllerName;
+ string action = routeName ?? wrapper.Descriptor.ActionName;
+ string claimValue = $"{ResourceType} {resource} {action}";
+ Permission permission = GetResourceActionPermission(wrapper.User, claimValue);
+
+ return
+ (permission == Permission.Deny && fail()) ||
+ (permission == Permission.Allow && succeed());
+
+ bool succeed() =>
+ wrapper.Succeed();
+
+ bool fail()
+ {
+ AuthorizationFailureReason reason = ToFailureReason(claimValue);
+ return wrapper.Fail(reason);
+ }
+ }
+
+ private AuthorizationFailureReason ToFailureReason(string claim)
+ {
+ return new AuthorizationFailureReason(this, $"{claim} permission denied");
+ }
+
+ #endregion
+
+ #region [ Static ]
+
+ // Static Methods
+
+ private static Permission GetResourceActionPermission(ClaimsPrincipal user, string claimValue)
+ {
+
+ if (user.HasClaim(GemstoneClaimTypes.DenyClaim, claimValue))
+ return Permission.Deny;
+
+ return user.HasClaim(GemstoneClaimTypes.AllowClaim, claimValue)
+ ? Permission.Allow
+ : Permission.Neither;
+ }
+
+ private static void HandleResourceAccessPermission(ContextWrapper wrapper, string resourceType)
+ {
+ IReadOnlyList accessAttributes = wrapper.Endpoint.Metadata
+ .GetOrderedMetadata();
+
+ string resourceName = accessAttributes.GetResourceName(wrapper.Descriptor);
+ ResourceAccessType access = accessAttributes.GetAccessType(wrapper.HttpMethod);
+
+ if (wrapper.User.HasAccessTo(resourceType, resourceName, access))
+ wrapper.Succeed();
+ }
+
+ #endregion
+}