diff --git a/Bornlogic.IdentityServer/Extensions/IClientStoreExtensions.cs b/Bornlogic.IdentityServer/Extensions/IClientStoreExtensions.cs index 207e0024d..5cf423fe3 100644 --- a/Bornlogic.IdentityServer/Extensions/IClientStoreExtensions.cs +++ b/Bornlogic.IdentityServer/Extensions/IClientStoreExtensions.cs @@ -33,7 +33,7 @@ public static async Task FindEnabledClientByIdAsync(this IClientStore st if (clientUserRoleService != null && !string.IsNullOrEmpty(userID)) { - var userHasLoginByPassRoleInClient = await clientUserRoleService.UserHasLoginByPassRoleInClient(userID, client); + var userHasLoginByPassRoleInClient = await clientUserRoleService.UserHasLoginByPassRoleInClient(userID, client, null); if (userHasLoginByPassRoleInClient) { diff --git a/Bornlogic.IdentityServer/Models/ClientRoleOptions.cs b/Bornlogic.IdentityServer/Models/ClientRoleOptions.cs new file mode 100644 index 000000000..94688c250 --- /dev/null +++ b/Bornlogic.IdentityServer/Models/ClientRoleOptions.cs @@ -0,0 +1,7 @@ +namespace Bornlogic.IdentityServer.Models +{ + public class ClientRoleOptions + { + public string[] ValidUserRolesToBypassClientScopeValidation { get; set; } + } +} diff --git a/Bornlogic.IdentityServer/Services/Default/DefaultClientUserRoleService.cs b/Bornlogic.IdentityServer/Services/Default/DefaultClientUserRoleService.cs index 53f508237..61a219161 100644 --- a/Bornlogic.IdentityServer/Services/Default/DefaultClientUserRoleService.cs +++ b/Bornlogic.IdentityServer/Services/Default/DefaultClientUserRoleService.cs @@ -4,7 +4,7 @@ namespace Bornlogic.IdentityServer.Services.Default { public class DefaultClientUserRoleService : IClientUserRoleService { - public Task UserHasLoginByPassRoleInClient(string userID, Client client) + public Task UserHasLoginByPassRoleInClient(string userID, Client client, string[] validRoles) { return Task.FromResult(false); } diff --git a/Bornlogic.IdentityServer/Services/IClientUserRoleService.cs b/Bornlogic.IdentityServer/Services/IClientUserRoleService.cs index 1a8f1bb92..d8dfff529 100644 --- a/Bornlogic.IdentityServer/Services/IClientUserRoleService.cs +++ b/Bornlogic.IdentityServer/Services/IClientUserRoleService.cs @@ -6,6 +6,6 @@ namespace Bornlogic.IdentityServer.Services { public interface IClientUserRoleService { - Task UserHasLoginByPassRoleInClient(string userID, Client client); + Task UserHasLoginByPassRoleInClient(string userID, Client client, string[] validRoles); } } diff --git a/Bornlogic.IdentityServer/Validation/Default/AuthorizeRequestValidator.cs b/Bornlogic.IdentityServer/Validation/Default/AuthorizeRequestValidator.cs index 1ded56670..488baf76b 100644 --- a/Bornlogic.IdentityServer/Validation/Default/AuthorizeRequestValidator.cs +++ b/Bornlogic.IdentityServer/Validation/Default/AuthorizeRequestValidator.cs @@ -621,7 +621,8 @@ private async Task ValidateScopeAsync(Validate { Client = request.Client, Scopes = request.RequestedScopes, - RequiredRequestScopes = request.Raw.Get("required_scope")?.Split(' ') ?? Array.Empty() + RequiredRequestScopes = request.Raw.Get("required_scope")?.Split(' ') ?? Array.Empty(), + Subject = request.Subject }); if (!validatedResources.Succeeded) diff --git a/Bornlogic.IdentityServer/Validation/Default/DefaultResourceValidator.cs b/Bornlogic.IdentityServer/Validation/Default/DefaultResourceValidator.cs index c00867aed..b5864151d 100644 --- a/Bornlogic.IdentityServer/Validation/Default/DefaultResourceValidator.cs +++ b/Bornlogic.IdentityServer/Validation/Default/DefaultResourceValidator.cs @@ -3,10 +3,13 @@ using Bornlogic.IdentityServer.Extensions; +using Bornlogic.IdentityServer.Models; +using Bornlogic.IdentityServer.Services; using Bornlogic.IdentityServer.Storage.Models; using Bornlogic.IdentityServer.Storage.Stores; using Bornlogic.IdentityServer.Validation.Models; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; namespace Bornlogic.IdentityServer.Validation.Default { @@ -16,6 +19,8 @@ namespace Bornlogic.IdentityServer.Validation.Default public class DefaultResourceValidator : IResourceValidator { private readonly ILogger _logger; + private readonly IClientUserRoleService _clientUserRoleService; + private readonly IOptions _clientRoleOptions; private readonly IScopeParser _scopeParser; private readonly IResourceStore _store; @@ -25,9 +30,11 @@ public class DefaultResourceValidator : IResourceValidator /// The store. /// /// The logger. - public DefaultResourceValidator(IResourceStore store, IScopeParser scopeParser, ILogger logger) + public DefaultResourceValidator(IResourceStore store, IScopeParser scopeParser, ILogger logger, IClientUserRoleService clientUserRoleService, IOptions clientRoleOptions) { _logger = logger; + _clientUserRoleService = clientUserRoleService; + _clientRoleOptions = clientRoleOptions; _scopeParser = scopeParser; _store = store; } @@ -65,20 +72,30 @@ public virtual async Task ValidateRequestedResourcesAs return result; } - var scopeNames = parsedScopesResult.ParsedScopes.Select(x => x.ParsedName).Distinct().ToArray(); - var resourcesFromStore = await _store.FindEnabledResourcesByScopeAsync(scopeNames); + var subjectIdOrDefault = request.Subject?.GetSubjectIdOrDefault(); - foreach (var scope in parsedScopesResult.ParsedScopes) + if (!string.IsNullOrEmpty(subjectIdOrDefault)) { - await ValidateScopeAsync(request.Client, resourcesFromStore, scope, result, request.RequiredRequestScopes.Any(a => a == scope.ParsedName)); - } + var hasRoleToBypassScopeValidation = await _clientUserRoleService.UserHasLoginByPassRoleInClient(subjectIdOrDefault, request.Client, _clientRoleOptions?.Value?.ValidUserRolesToBypassClientScopeValidation); - var requiredRequestScopeNames = parsedRequiredRequestScopesResult.ParsedScopes.Select(x => x.ParsedName).Distinct().ToArray(); - var requiredRequestResourcesFromStore = await _store.FindEnabledResourcesByScopeAsync(requiredRequestScopeNames); + if (!hasRoleToBypassScopeValidation) + { + var scopeNames = parsedScopesResult.ParsedScopes.Select(x => x.ParsedName).Distinct().ToArray(); + var resourcesFromStore = await _store.FindEnabledResourcesByScopeAsync(scopeNames); - foreach (var scope in parsedRequiredRequestScopesResult.ParsedScopes) - { - await ValidateRequestRequiredScopeAsync(request.Client, requiredRequestResourcesFromStore, scope, result); + foreach (var scope in parsedScopesResult.ParsedScopes) + { + await ValidateScopeAsync(request.Client, resourcesFromStore, scope, result, request.RequiredRequestScopes.Any(a => a == scope.ParsedName)); + } + + var requiredRequestScopeNames = parsedRequiredRequestScopesResult.ParsedScopes.Select(x => x.ParsedName).Distinct().ToArray(); + var requiredRequestResourcesFromStore = await _store.FindEnabledResourcesByScopeAsync(requiredRequestScopeNames); + + foreach (var scope in parsedRequiredRequestScopesResult.ParsedScopes) + { + await ValidateRequestRequiredScopeAsync(request.Client, requiredRequestResourcesFromStore, scope, result); + } + } } if (result.InvalidScopes.Count > 0) diff --git a/Bornlogic.IdentityServer/Validation/Default/DeviceAuthorizationRequestValidator.cs b/Bornlogic.IdentityServer/Validation/Default/DeviceAuthorizationRequestValidator.cs index e7f244c86..46a2166b7 100644 --- a/Bornlogic.IdentityServer/Validation/Default/DeviceAuthorizationRequestValidator.cs +++ b/Bornlogic.IdentityServer/Validation/Default/DeviceAuthorizationRequestValidator.cs @@ -151,7 +151,8 @@ private async Task ValidateScopeAsyn ////////////////////////////////////////////////////////// var validatedResources = await _resourceValidator.ValidateRequestedResourcesAsync(new ResourceValidationRequest{ Client = request.Client, - Scopes = request.RequestedScopes + Scopes = request.RequestedScopes, + Subject = request.Subject }); if (!validatedResources.Succeeded) diff --git a/Bornlogic.IdentityServer/Validation/Default/TokenRequestValidator.cs b/Bornlogic.IdentityServer/Validation/Default/TokenRequestValidator.cs index b4ba4e247..836379cc8 100644 --- a/Bornlogic.IdentityServer/Validation/Default/TokenRequestValidator.cs +++ b/Bornlogic.IdentityServer/Validation/Default/TokenRequestValidator.cs @@ -721,7 +721,8 @@ private async Task ValidateRequestedScopesAsync(NameValueCollection parame var resourceValidationResult = await _resourceValidator.ValidateRequestedResourcesAsync(new ResourceValidationRequest { Client = _validatedRequest.Client, - Scopes = requestedScopes + Scopes = requestedScopes, + Subject = _validatedRequest.Subject }); if (!resourceValidationResult.Succeeded) diff --git a/Bornlogic.IdentityServer/Validation/Models/ResourceValidationRequest.cs b/Bornlogic.IdentityServer/Validation/Models/ResourceValidationRequest.cs index 4f4529bd3..f3fd046b0 100644 --- a/Bornlogic.IdentityServer/Validation/Models/ResourceValidationRequest.cs +++ b/Bornlogic.IdentityServer/Validation/Models/ResourceValidationRequest.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. +using System.Security.Claims; using Bornlogic.IdentityServer.Storage.Models; namespace Bornlogic.IdentityServer.Validation.Models @@ -16,6 +17,8 @@ public class ResourceValidationRequest /// public Client Client { get; set; } + public ClaimsPrincipal Subject { get; set; } + /// /// The requested scope values. ///