diff --git a/src/Gemstone.Security/AccessControl/ResourceAccessType.cs b/src/Gemstone.Security/AccessControl/ResourceAccessType.cs index baeca622..98880c56 100644 --- a/src/Gemstone.Security/AccessControl/ResourceAccessType.cs +++ b/src/Gemstone.Security/AccessControl/ResourceAccessType.cs @@ -94,21 +94,17 @@ public static bool HasAccessTo(this ClaimsPrincipal user, string resourceType, s { ThrowIfNotValid(access); - const string AllowClaim = "Gemstone.ResourceAccess.Allow"; - const string DenyClaim = "Gemstone.ResourceAccess.Deny"; - const string BaseClaim = "Gemstone.ResourceAccess.Default"; - if (access == ResourceAccessType.None) return false; string claimValue = $"{resourceType} {resourceName} {access}"; bool IsDenied() => - user.HasClaim(DenyClaim, claimValue); + user.HasClaim(GemstoneClaimTypes.DenyClaim, claimValue); bool IsAllowed() => - user.HasClaim(AllowClaim, claimValue) || - user.HasClaim(BaseClaim, $"{access}"); + user.HasClaim(GemstoneClaimTypes.AllowClaim, claimValue) || + user.HasClaim(GemstoneClaimTypes.BaseClaim, $"{access}"); return !IsDenied() && IsAllowed(); } diff --git a/src/Gemstone.Security/AuthenticationProviders/APIAuthenticationProvider.cs b/src/Gemstone.Security/AuthenticationProviders/APIAuthenticationProvider.cs new file mode 100644 index 00000000..109a3017 --- /dev/null +++ b/src/Gemstone.Security/AuthenticationProviders/APIAuthenticationProvider.cs @@ -0,0 +1,85 @@ +//****************************************************************************************************** +// APIAUthenticationProvider.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; +using System.Security.Claims; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Http; + +namespace Gemstone.Security.AuthenticationProviders; + +/// +/// Options for the class. +/// +public class APIAuthenticationProviderOptions +{ + /// + /// Function that validates a Token + /// + public Func? ValidateToken { get; set; } + +} + +public class APIToken +{ + public string Name { get; set; } + public DateTime Expiration { get; set; } + + public Claim[] Claims { get; set; } +} + + +public class APIAuthenticationProvider +{ + private readonly RequestDelegate _next; + private readonly APIAuthenticationProviderOptions Settings; + public APIAuthenticationProvider(RequestDelegate next, APIAuthenticationProviderOptions options) + { + _next = next; + Settings = options; + } + public async Task InvokeAsync(HttpContext context) + { + if (context.Request.Headers.TryGetValue("Authorization", out var authHeader)) + { + string bearerToken = authHeader.ToString(); + + if (bearerToken.StartsWith("Bearer ", System.StringComparison.OrdinalIgnoreCase)) + { + string token = bearerToken.Substring("Bearer ".Length).Trim(); + APIToken? resolvedToken = Settings.ValidateToken?.Invoke(token); + if (resolvedToken == null || resolvedToken.Expiration < DateTime.UtcNow) + { + await _next(context); + return; + } + ClaimsIdentity identity = new("APIAuthentication"); + identity.AddClaim(new(ClaimTypes.Name, resolvedToken.Name)); + identity.AddClaims(resolvedToken.Claims); + context.User = new(identity); + await _next(context); + } + } + } +} + diff --git a/src/Gemstone.Security/AuthenticationProviders/IAuthenticationBuilder.cs b/src/Gemstone.Security/AuthenticationProviders/IAuthenticationBuilder.cs index 669af0dd..a2e2d02c 100644 --- a/src/Gemstone.Security/AuthenticationProviders/IAuthenticationBuilder.cs +++ b/src/Gemstone.Security/AuthenticationProviders/IAuthenticationBuilder.cs @@ -96,9 +96,6 @@ public IEnumerable GetProviderIdentities() public IEnumerable GetAssignedClaims(string providerIdentity, ClaimsPrincipal principal) { - const string ProviderIdentityClaim = "Gemstone.ProviderIdentity"; - const string UserIdentityClaim = "Gemstone.UserIdentity"; - IAuthenticationProvider? provider = ProviderLookup(providerIdentity); if (provider is null) @@ -106,11 +103,13 @@ public IEnumerable GetAssignedClaims(string providerIdentity, ClaimsPrinc string userIdentity = provider.GetIdentity(principal); - IEnumerable providerClaims = Setup - .GetProviderClaims(providerIdentity) - .Join(principal.Claims, ToKey, ToKey, (providerClaim, _) => providerClaim.Assigned) - .Prepend(new(UserIdentityClaim, userIdentity)) - .Prepend(new(ProviderIdentityClaim, providerIdentity)); + IEnumerable principalClaims = principal.Claims + .Append(new(GemstoneClaimTypes.AllUsers,string.Empty)); + + IEnumerable providerClaims = Setup.GetProviderClaims(providerIdentity) + .Join(principalClaims, ToKey, ToKey, (providerClaim, _) => providerClaim.Assigned) + .Prepend(new(GemstoneClaimTypes.UserIdentity, userIdentity)) + .Prepend(new(GemstoneClaimTypes.ProviderIdentity, providerIdentity)); return providerClaims; } diff --git a/src/Gemstone.Security/AuthenticationProviders/OAuthAuthenticationProvider.cs b/src/Gemstone.Security/AuthenticationProviders/OAuthAuthenticationProvider.cs index b5266466..afa14dda 100644 --- a/src/Gemstone.Security/AuthenticationProviders/OAuthAuthenticationProvider.cs +++ b/src/Gemstone.Security/AuthenticationProviders/OAuthAuthenticationProvider.cs @@ -127,7 +127,7 @@ public string GetIdentity(ClaimsPrincipal principal) .Claims .Select(claim => claim.Type) .Distinct() - .Select(type => new ClaimType(type)).Prepend(new ClaimType("Gemstone.AllUsers")).ToArray(); + .Select(type => new ClaimType(type)).ToArray(); string? identity = principal .FindFirst(Options.UserIdClaim ?? "sub")? @@ -158,7 +158,7 @@ private static ClaimType[] ClaimTypes { get; set; - } = [new ClaimType("Gemstone.AllUsers")]; + } = [new ClaimType(GemstoneClaimTypes.AllUsers), new (GemstoneClaimTypes.UserIdentity)]; // Static Methods diff --git a/src/Gemstone.Security/GemstoneClaimTypes.cs b/src/Gemstone.Security/GemstoneClaimTypes.cs new file mode 100644 index 00000000..06a18e71 --- /dev/null +++ b/src/Gemstone.Security/GemstoneClaimTypes.cs @@ -0,0 +1,63 @@ +//****************************************************************************************************** +// GemstoneClaimTypes.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: +// ---------------------------------------------------------------------------------------------------- +// 10/16/2019 - J. Ritchie Carroll +// Generated original version of source code. +// +//****************************************************************************************************** + +using System.Runtime.CompilerServices; + +namespace Gemstone.Security +{ + /// + /// The Claim Types used bu the namespace + /// + public static class GemstoneClaimTypes + { + /// + /// Holds the unique identifier for the User. + /// + public const string UserIdentity = "Gemstone.UserIdentity"; + /// + /// Holds the unique identifier for the Authentication Provider. + /// + public const string ProviderIdentity = "Gemstone.ProviderIdentity"; + + /// + /// Is used in Matrching Claims to match any user, regardless of claims they have + /// + public const string AllUsers = "Gemstone.AllUsers"; + + /// + /// Allows a user to access a resource. + /// + public const string AllowClaim = "Gemstone.ResourceAccess.Allow"; + + /// + /// Denies a user access to a resource. + /// + public const string DenyClaim = "Gemstone.ResourceAccess.Deny"; + + /// + /// Allows access to the value as the default access level. + /// + public const string BaseClaim = "Gemstone.ResourceAccess.Default"; + + } +}