/// <inheritdoc/>
        public virtual async Task <ResourceValidationResult> ValidateRequestedResourcesAsync(ResourceValidationRequest request)
        {
            if (request == null)
            {
                throw new ArgumentNullException(nameof(request));
            }

            var parsedScopesResult = _scopeParser.ParseScopeValues(request.Scopes);

            var result = new ResourceValidationResult();

            if (!parsedScopesResult.Succeeded)
            {
                foreach (var invalidScope in parsedScopesResult.Errors)
                {
                    _logger.LogError("Invalid parsed scope {scope}, message: {error}", invalidScope.RawValue, invalidScope.Error);
                    result.InvalidScopes.Add(invalidScope.RawValue);
                }

                return(result);
            }

            var scopeNames         = parsedScopesResult.ParsedScopes.Select(x => x.ParsedName).Distinct().ToArray();
            var resourcesFromStore = await _store.FindEnabledResourcesByScopeAsync(scopeNames);

            foreach (var scope in parsedScopesResult.ParsedScopes)
            {
                await ValidateScopeAsync(request.Client, resourcesFromStore, scope, result);
            }

            if (result.InvalidScopes.Count > 0)
            {
                result.Resources.IdentityResources.Clear();
                result.Resources.ApiResources.Clear();
                result.Resources.ApiScopes.Clear();
                result.ParsedScopes.Clear();
            }

            return(result);
        }
        /// <summary>
        /// Validates that the requested scopes is contained in the store, and the client is allowed to request it.
        /// </summary>
        /// <param name="client"></param>
        /// <param name="resourcesFromStore"></param>
        /// <param name="requestedScope"></param>
        /// <param name="result"></param>
        /// <returns></returns>
        protected virtual async Task ValidateScopeAsync(
            Client client,
            Resources resourcesFromStore,
            ParsedScopeValue requestedScope,
            ResourceValidationResult result)
        {
            if (requestedScope.ParsedName == IdentityServerConstants.StandardScopes.OfflineAccess)
            {
                if (await IsClientAllowedOfflineAccessAsync(client))
                {
                    result.Resources.OfflineAccess = true;
                    result.ParsedScopes.Add(new ParsedScopeValue(IdentityServerConstants.StandardScopes.OfflineAccess));
                }
                else
                {
                    result.InvalidScopes.Add(IdentityServerConstants.StandardScopes.OfflineAccess);
                }
            }
            else
            {
                var identity = resourcesFromStore.FindIdentityResourcesByScope(requestedScope.ParsedName);
                if (identity != null)
                {
                    if (await IsClientAllowedIdentityResourceAsync(client, identity))
                    {
                        result.ParsedScopes.Add(requestedScope);
                        result.Resources.IdentityResources.Add(identity);
                    }
                    else
                    {
                        result.InvalidScopes.Add(requestedScope.RawValue);
                    }
                }
                else
                {
                    var apiScope = resourcesFromStore.FindApiScope(requestedScope.ParsedName);
                    if (apiScope != null)
                    {
                        if (await IsClientAllowedApiScopeAsync(client, apiScope))
                        {
                            result.ParsedScopes.Add(requestedScope);
                            result.Resources.ApiScopes.Add(apiScope);

                            var apis = resourcesFromStore.FindApiResourcesByScope(apiScope.Name);
                            foreach (var api in apis)
                            {
                                result.Resources.ApiResources.Add(api);
                            }
                        }
                        else
                        {
                            result.InvalidScopes.Add(requestedScope.RawValue);
                        }
                    }
                    else
                    {
                        _logger.LogError("Scope {scope} not found in store.", requestedScope.ParsedName);
                        result.InvalidScopes.Add(requestedScope.RawValue);
                    }
                }
            }
        }
示例#3
0
        /// <inheritdoc/>
        public virtual async Task <ResourceValidationResult> ValidateRequestedResourcesAsync(ResourceValidationRequest request)
        {
            if (request == null)
            {
                throw new ArgumentNullException(nameof(request));
            }

            var result = new ResourceValidationResult();

            var parsedScopesResult = _scopeParser.ParseScopeValues(request.Scopes);

            if (!parsedScopesResult.Succeeded)
            {
                foreach (var invalidScope in parsedScopesResult.Errors)
                {
                    _logger.LogError("Invalid parsed scope {scope}, message: {error}", invalidScope.RawValue, invalidScope.Error);
                    result.InvalidScopes.Add(invalidScope.RawValue);
                }

                return(result);
            }

            var scopeNames = parsedScopesResult.ParsedScopes.Select(x => x.ParsedName).Distinct().ToArray();
            // todo: this API might want to pass resource indicators to better filter
            var scopeResourcesFromStore = await _store.FindEnabledResourcesByScopeAsync(scopeNames);

            if (request.ResourceIndicators?.Any() == true)
            {
                // remove isolated API resources not included in the requested resource indicators
                scopeResourcesFromStore.ApiResources = scopeResourcesFromStore.ApiResources
                                                       .Where(x =>
                                                              // only allow non-isolated resources if the request could produce multiple access
                                                              // tokens. this will happen if the request is for a RT, so check for offline_access
                                                              (request.IncludeNonIsolatedApiResources && !x.RequireResourceIndicator) ||
                                                              request.ResourceIndicators.Contains(x.Name))
                                                       .ToHashSet();

                if (!request.IncludeNonIsolatedApiResources)
                {
                    // filter API scopes that don't match the resources requested
                    var allResourceScopes = scopeResourcesFromStore.ApiResources.SelectMany(x => x.Scopes).ToArray();
                    scopeResourcesFromStore.ApiScopes =
                        scopeResourcesFromStore.ApiScopes.Where(x => allResourceScopes.Contains(x.Name))
                        .ToHashSet();
                }

                // find requested resource indicators not matched by scope
                var matchedApiResourceNames            = scopeResourcesFromStore.ApiResources.Select(x => x.Name).ToArray();
                var invalidRequestedResourceIndicators = request.ResourceIndicators.Except(matchedApiResourceNames);
                if (invalidRequestedResourceIndicators.Any())
                {
                    foreach (var invalid in invalidRequestedResourceIndicators)
                    {
                        _logger.LogError("Invalid resource identifier {resource}. It is either not found, not enabled, or does not support any of the requested scopes.", invalid);
                        result.InvalidResourceIndicators.Add(invalid);
                    }

                    return(result);
                }
            }
            else
            {
                // no resource indicators, so filter all API resources marked as isolated
                scopeResourcesFromStore.ApiResources = scopeResourcesFromStore.ApiResources.Where(x => !x.RequireResourceIndicator).ToHashSet();
            }


            foreach (var scope in parsedScopesResult.ParsedScopes)
            {
                await ValidateScopeAsync(request.Client, scopeResourcesFromStore, scope, result);
            }


            if (result.InvalidScopes.Count > 0 || result.InvalidResourceIndicators.Count > 0)
            {
                result.Resources.IdentityResources.Clear();
                result.Resources.ApiResources.Clear();
                result.Resources.ApiScopes.Clear();
                result.ParsedScopes.Clear();
            }


            return(result);
        }