/* * Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0) * See https://github.com/openiddict/openiddict-core for more information concerning * the license and the contributors participating to this project. */ using System.Collections.Immutable; using System.ComponentModel.DataAnnotations; using System.Runtime.CompilerServices; using System.Security.Claims; using System.Text; using System.Text.Json; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using static OpenIddict.Abstractions.OpenIddictExceptions; namespace OpenIddict.Core; /// /// Provides methods allowing to manage the authorizations stored in the store. /// /// /// Applications that do not want to depend on a specific entity type can use the non-generic /// instead, for which the actual entity type /// is resolved at runtime based on the default entity type registered in the core options. /// /// The type of the Authorization entity. public class OpenIddictAuthorizationManager : IOpenIddictAuthorizationManager where TAuthorization : class { public OpenIddictAuthorizationManager( IOpenIddictAuthorizationCache cache!!, ILogger> logger!!, IOptionsMonitor options!!, IOpenIddictAuthorizationStoreResolver resolver!!) { Cache = cache; Logger = logger; Options = options; Store = resolver.Get(); } /// /// Gets the cache associated with the current manager. /// protected IOpenIddictAuthorizationCache Cache { get; } /// /// Gets the logger associated with the current manager. /// protected ILogger Logger { get; } /// /// Gets the options associated with the current manager. /// protected IOptionsMonitor Options { get; } /// /// Gets the store associated with the current manager. /// protected IOpenIddictAuthorizationStore Store { get; } /// /// Determines the number of authorizations that exist in the database. /// /// The that can be used to abort the operation. /// /// A that can be used to monitor the asynchronous operation, /// whose result returns the number of authorizations in the database. /// public virtual ValueTask CountAsync(CancellationToken cancellationToken = default) => Store.CountAsync(cancellationToken); /// /// Determines the number of authorizations that match the specified query. /// /// The result type. /// The query to execute. /// The that can be used to abort the operation. /// /// A that can be used to monitor the asynchronous operation, /// whose result returns the number of authorizations that match the specified query. /// public virtual ValueTask CountAsync( Func, IQueryable> query!!, CancellationToken cancellationToken = default) => Store.CountAsync(query, cancellationToken); /// /// Creates a new authorization. /// /// The application to create. /// The that can be used to abort the operation. /// /// A that can be used to monitor the asynchronous operation. /// public virtual async ValueTask CreateAsync(TAuthorization authorization!!, CancellationToken cancellationToken = default) { // If no status was explicitly specified, assume that the authorization is valid. if (string.IsNullOrEmpty(await Store.GetStatusAsync(authorization, cancellationToken))) { await Store.SetStatusAsync(authorization, Statuses.Valid, cancellationToken); } var results = await GetValidationResultsAsync(authorization, cancellationToken); if (results.Any(result => result != ValidationResult.Success)) { var builder = new StringBuilder(); builder.AppendLine(SR.GetResourceString(SR.ID0219)); builder.AppendLine(); foreach (var result in results) { builder.AppendLine(result.ErrorMessage); } throw new OpenIddictExceptions.ValidationException(builder.ToString(), results); } await Store.CreateAsync(authorization, cancellationToken); if (!Options.CurrentValue.DisableEntityCaching) { await Cache.AddAsync(authorization, cancellationToken); } async Task> GetValidationResultsAsync( TAuthorization authorization, CancellationToken cancellationToken) { var builder = ImmutableArray.CreateBuilder(); await foreach (var result in ValidateAsync(authorization, cancellationToken)) { builder.Add(result); } return builder.ToImmutable(); } } /// /// Creates a new authorization based on the specified descriptor. /// /// The authorization descriptor. /// The that can be used to abort the operation. /// /// A that can be used to monitor the asynchronous operation, whose result returns the authorization. /// public virtual async ValueTask CreateAsync( OpenIddictAuthorizationDescriptor descriptor!!, CancellationToken cancellationToken = default) { var authorization = await Store.InstantiateAsync(cancellationToken); if (authorization is null) { throw new InvalidOperationException(SR.GetResourceString(SR.ID0220)); } await PopulateAsync(authorization, descriptor, cancellationToken); await CreateAsync(authorization, cancellationToken); return authorization; } /// /// Creates a new permanent authorization based on the specified parameters. /// /// The principal associated with the authorization. /// The subject associated with the authorization. /// The client associated with the authorization. /// The authorization type. /// The minimal scopes associated with the authorization. /// The that can be used to abort the operation. /// /// A that can be used to monitor the asynchronous operation, whose result returns the authorization. /// public virtual ValueTask CreateAsync( ClaimsPrincipal principal!!, string subject, string client, string type, ImmutableArray scopes, CancellationToken cancellationToken = default) { if (string.IsNullOrEmpty(subject)) { throw new ArgumentException(SR.GetResourceString(SR.ID0198), nameof(subject)); } if (string.IsNullOrEmpty(client)) { throw new ArgumentException(SR.GetResourceString(SR.ID0124), nameof(client)); } if (string.IsNullOrEmpty(type)) { throw new ArgumentException(SR.GetResourceString(SR.ID0200), nameof(type)); } var descriptor = new OpenIddictAuthorizationDescriptor { ApplicationId = client, CreationDate = DateTimeOffset.UtcNow, Principal = principal, Status = Statuses.Valid, Subject = subject, Type = type }; descriptor.Scopes.UnionWith(scopes); return CreateAsync(descriptor, cancellationToken); } /// /// Removes an existing authorization. /// /// The authorization to delete. /// The that can be used to abort the operation. /// /// A that can be used to monitor the asynchronous operation. /// public virtual async ValueTask DeleteAsync(TAuthorization authorization!!, CancellationToken cancellationToken = default) { if (!Options.CurrentValue.DisableEntityCaching) { await Cache.RemoveAsync(authorization, cancellationToken); } await Store.DeleteAsync(authorization, cancellationToken); } /// /// Retrieves the authorizations corresponding to the specified /// subject and associated with the application identifier. /// /// The subject associated with the authorization. /// The client associated with the authorization. /// The that can be used to abort the operation. /// The authorizations corresponding to the subject/client. public virtual IAsyncEnumerable FindAsync( string subject, string client, CancellationToken cancellationToken = default) { if (string.IsNullOrEmpty(subject)) { throw new ArgumentException(SR.GetResourceString(SR.ID0198), nameof(subject)); } if (string.IsNullOrEmpty(client)) { throw new ArgumentException(SR.GetResourceString(SR.ID0124), nameof(client)); } var authorizations = Options.CurrentValue.DisableEntityCaching ? Store.FindAsync(subject, client, cancellationToken) : Cache.FindAsync(subject, client, cancellationToken); // SQL engines like Microsoft SQL Server or MySQL are known to use case-insensitive lookups by default. // To ensure a case-sensitive comparison is enforced independently of the database/table/query collation // used by the store, a second pass using string.Equals(StringComparison.Ordinal) is manually made here. if (Options.CurrentValue.DisableAdditionalFiltering) { return authorizations; } // SQL engines like Microsoft SQL Server or MySQL are known to use case-insensitive lookups by default. // To ensure a case-sensitive comparison is enforced independently of the database/table/query collation // used by the store, a second pass using string.Equals(StringComparison.Ordinal) is manually made here. return ExecuteAsync(cancellationToken); async IAsyncEnumerable ExecuteAsync([EnumeratorCancellation] CancellationToken cancellationToken) { await foreach (var authorization in authorizations) { if (string.Equals(await Store.GetSubjectAsync(authorization, cancellationToken), subject, StringComparison.Ordinal)) { yield return authorization; } } } } /// /// Retrieves the authorizations matching the specified parameters. /// /// The subject associated with the authorization. /// The client associated with the authorization. /// The authorization status. /// The that can be used to abort the operation. /// The authorizations corresponding to the criteria. public virtual IAsyncEnumerable FindAsync( string subject, string client, string status, CancellationToken cancellationToken = default) { if (string.IsNullOrEmpty(subject)) { throw new ArgumentException(SR.GetResourceString(SR.ID0198), nameof(subject)); } if (string.IsNullOrEmpty(client)) { throw new ArgumentException(SR.GetResourceString(SR.ID0124), nameof(client)); } if (string.IsNullOrEmpty(status)) { throw new ArgumentException(SR.GetResourceString(SR.ID0199), nameof(status)); } var authorizations = Options.CurrentValue.DisableEntityCaching ? Store.FindAsync(subject, client, status, cancellationToken) : Cache.FindAsync(subject, client, status, cancellationToken); if (Options.CurrentValue.DisableAdditionalFiltering) { return authorizations; } // SQL engines like Microsoft SQL Server or MySQL are known to use case-insensitive lookups by default. // To ensure a case-sensitive comparison is enforced independently of the database/table/query collation // used by the store, a second pass using string.Equals(StringComparison.Ordinal) is manually made here. return ExecuteAsync(cancellationToken); async IAsyncEnumerable ExecuteAsync([EnumeratorCancellation] CancellationToken cancellationToken) { await foreach (var authorization in authorizations) { if (string.Equals(await Store.GetSubjectAsync(authorization, cancellationToken), subject, StringComparison.Ordinal)) { yield return authorization; } } } } /// /// Retrieves the authorizations matching the specified parameters. /// /// The subject associated with the authorization. /// The client associated with the authorization. /// The authorization status. /// The authorization type. /// The that can be used to abort the operation. /// The authorizations corresponding to the criteria. public virtual IAsyncEnumerable FindAsync( string subject, string client, string status, string type, CancellationToken cancellationToken = default) { if (string.IsNullOrEmpty(subject)) { throw new ArgumentException(SR.GetResourceString(SR.ID0198), nameof(subject)); } if (string.IsNullOrEmpty(client)) { throw new ArgumentException(SR.GetResourceString(SR.ID0124), nameof(client)); } if (string.IsNullOrEmpty(status)) { throw new ArgumentException(SR.GetResourceString(SR.ID0199), nameof(status)); } if (string.IsNullOrEmpty(type)) { throw new ArgumentException(SR.GetResourceString(SR.ID0200), nameof(type)); } var authorizations = Options.CurrentValue.DisableEntityCaching ? Store.FindAsync(subject, client, status, type, cancellationToken) : Cache.FindAsync(subject, client, status, type, cancellationToken); if (Options.CurrentValue.DisableAdditionalFiltering) { return authorizations; } // SQL engines like Microsoft SQL Server or MySQL are known to use case-insensitive lookups by default. // To ensure a case-sensitive comparison is enforced independently of the database/table/query collation // used by the store, a second pass using string.Equals(StringComparison.Ordinal) is manually made here. return ExecuteAsync(cancellationToken); async IAsyncEnumerable ExecuteAsync([EnumeratorCancellation] CancellationToken cancellationToken) { await foreach (var authorization in authorizations) { if (string.Equals(await Store.GetSubjectAsync(authorization, cancellationToken), subject, StringComparison.Ordinal)) { yield return authorization; } } } } /// /// Retrieves the authorizations matching the specified parameters. /// /// The subject associated with the authorization. /// The client associated with the authorization. /// The authorization status. /// The authorization type. /// The minimal scopes associated with the authorization. /// The that can be used to abort the operation. /// The authorizations corresponding to the criteria. public virtual IAsyncEnumerable FindAsync( string subject, string client, string status, string type, ImmutableArray scopes, CancellationToken cancellationToken = default) { if (string.IsNullOrEmpty(subject)) { throw new ArgumentException(SR.GetResourceString(SR.ID0198), nameof(subject)); } if (string.IsNullOrEmpty(client)) { throw new ArgumentException(SR.GetResourceString(SR.ID0124), nameof(client)); } if (string.IsNullOrEmpty(status)) { throw new ArgumentException(SR.GetResourceString(SR.ID0199), nameof(status)); } if (string.IsNullOrEmpty(type)) { throw new ArgumentException(SR.GetResourceString(SR.ID0200), nameof(type)); } var authorizations = Options.CurrentValue.DisableEntityCaching ? Store.FindAsync(subject, client, status, type, scopes, cancellationToken) : Cache.FindAsync(subject, client, status, type, scopes, cancellationToken); if (Options.CurrentValue.DisableAdditionalFiltering) { return authorizations; } // SQL engines like Microsoft SQL Server or MySQL are known to use case-insensitive lookups by default. // To ensure a case-sensitive comparison is enforced independently of the database/table/query collation // used by the store, a second pass using string.Equals(StringComparison.Ordinal) is manually made here. return ExecuteAsync(cancellationToken); async IAsyncEnumerable ExecuteAsync([EnumeratorCancellation] CancellationToken cancellationToken) { await foreach (var authorization in authorizations) { if (!string.Equals(await Store.GetSubjectAsync(authorization, cancellationToken), subject, StringComparison.Ordinal)) { continue; } if (!await HasScopesAsync(authorization, scopes, cancellationToken)) { continue; } yield return authorization; } } } /// /// Retrieves the list of authorizations corresponding to the specified application identifier. /// /// The application identifier associated with the authorizations. /// The that can be used to abort the operation. /// The authorizations corresponding to the specified application. public virtual IAsyncEnumerable FindByApplicationIdAsync( string identifier, CancellationToken cancellationToken = default) { if (string.IsNullOrEmpty(identifier)) { throw new ArgumentException(SR.GetResourceString(SR.ID0195), nameof(identifier)); } var authorizations = Options.CurrentValue.DisableEntityCaching ? Store.FindByApplicationIdAsync(identifier, cancellationToken) : Cache.FindByApplicationIdAsync(identifier, cancellationToken); if (Options.CurrentValue.DisableAdditionalFiltering) { return authorizations; } // SQL engines like Microsoft SQL Server or MySQL are known to use case-insensitive lookups by default. // To ensure a case-sensitive comparison is enforced independently of the database/table/query collation // used by the store, a second pass using string.Equals(StringComparison.Ordinal) is manually made here. return ExecuteAsync(cancellationToken); async IAsyncEnumerable ExecuteAsync([EnumeratorCancellation] CancellationToken cancellationToken) { await foreach (var authorization in authorizations) { if (string.Equals(await Store.GetApplicationIdAsync(authorization, cancellationToken), identifier, StringComparison.Ordinal)) { yield return authorization; } } } } /// /// Retrieves an authorization using its unique identifier. /// /// The unique identifier associated with the authorization. /// The that can be used to abort the operation. /// /// A that can be used to monitor the asynchronous operation, /// whose result returns the authorization corresponding to the identifier. /// public virtual async ValueTask FindByIdAsync(string identifier, CancellationToken cancellationToken = default) { if (string.IsNullOrEmpty(identifier)) { throw new ArgumentException(SR.GetResourceString(SR.ID0195), nameof(identifier)); } var authorization = Options.CurrentValue.DisableEntityCaching ? await Store.FindByIdAsync(identifier, cancellationToken) : await Cache.FindByIdAsync(identifier, cancellationToken); if (authorization is null) { return null; } // SQL engines like Microsoft SQL Server or MySQL are known to use case-insensitive lookups by default. // To ensure a case-sensitive comparison is enforced independently of the database/table/query collation // used by the store, a second pass using string.Equals(StringComparison.Ordinal) is manually made here. if (!Options.CurrentValue.DisableAdditionalFiltering && !string.Equals(await Store.GetIdAsync(authorization, cancellationToken), identifier, StringComparison.Ordinal)) { return null; } return authorization; } /// /// Retrieves all the authorizations corresponding to the specified subject. /// /// The subject associated with the authorization. /// The that can be used to abort the operation. /// The authorizations corresponding to the specified subject. public virtual IAsyncEnumerable FindBySubjectAsync( string subject, CancellationToken cancellationToken = default) { if (string.IsNullOrEmpty(subject)) { throw new ArgumentException(SR.GetResourceString(SR.ID0198), nameof(subject)); } var authorizations = Options.CurrentValue.DisableEntityCaching ? Store.FindBySubjectAsync(subject, cancellationToken) : Cache.FindBySubjectAsync(subject, cancellationToken); if (Options.CurrentValue.DisableAdditionalFiltering) { return authorizations; } // SQL engines like Microsoft SQL Server or MySQL are known to use case-insensitive lookups by default. // To ensure a case-sensitive comparison is enforced independently of the database/table/query collation // used by the store, a second pass using string.Equals(StringComparison.Ordinal) is manually made here. return ExecuteAsync(cancellationToken); async IAsyncEnumerable ExecuteAsync([EnumeratorCancellation] CancellationToken cancellationToken) { await foreach (var authorization in authorizations) { if (string.Equals(await Store.GetSubjectAsync(authorization, cancellationToken), subject, StringComparison.Ordinal)) { yield return authorization; } } } } /// /// Retrieves the optional application identifier associated with an authorization. /// /// The authorization. /// The that can be used to abort the operation. /// /// A that can be used to monitor the asynchronous operation, /// whose result returns the application identifier associated with the authorization. /// public virtual ValueTask GetApplicationIdAsync( TAuthorization authorization!!, CancellationToken cancellationToken = default) => Store.GetApplicationIdAsync(authorization, cancellationToken); /// /// Executes the specified query and returns the first element. /// /// The result type. /// The query to execute. /// The that can be used to abort the operation. /// /// A that can be used to monitor the asynchronous operation, /// whose result returns the first element returned when executing the query. /// public virtual ValueTask GetAsync( Func, IQueryable> query!!, CancellationToken cancellationToken = default) => GetAsync(static (authorizations, query) => query(authorizations), query, cancellationToken); /// /// Executes the specified query and returns the first element. /// /// The state type. /// The result type. /// The query to execute. /// The optional state. /// The that can be used to abort the operation. /// /// A that can be used to monitor the asynchronous operation, /// whose result returns the first element returned when executing the query. /// public virtual ValueTask GetAsync( Func, TState, IQueryable> query!!, TState state, CancellationToken cancellationToken = default) => Store.GetAsync(query, state, cancellationToken); /// /// Retrieves the creation date associated with an authorization. /// /// The authorization. /// The that can be used to abort the operation. /// /// A that can be used to monitor the asynchronous operation, /// whose result returns the creation date associated with the specified authorization. /// public virtual ValueTask GetCreationDateAsync( TAuthorization authorization!!, CancellationToken cancellationToken = default) => Store.GetCreationDateAsync(authorization, cancellationToken); /// /// Retrieves the unique identifier associated with an authorization. /// /// The authorization. /// The that can be used to abort the operation. /// /// A that can be used to monitor the asynchronous operation, /// whose result returns the unique identifier associated with the authorization. /// public virtual ValueTask GetIdAsync(TAuthorization authorization!!, CancellationToken cancellationToken = default) => Store.GetIdAsync(authorization, cancellationToken); /// /// Retrieves the additional properties associated with an authorization. /// /// The authorization. /// The that can be used to abort the operation. /// /// A that can be used to monitor the asynchronous operation, /// whose result returns all the additional properties associated with the authorization. /// public virtual ValueTask> GetPropertiesAsync( TAuthorization authorization!!, CancellationToken cancellationToken = default) => Store.GetPropertiesAsync(authorization, cancellationToken); /// /// Retrieves the scopes associated with an authorization. /// /// The authorization. /// The that can be used to abort the operation. /// /// A that can be used to monitor the asynchronous operation, /// whose result returns the scopes associated with the specified authorization. /// public virtual ValueTask> GetScopesAsync( TAuthorization authorization!!, CancellationToken cancellationToken = default) => Store.GetScopesAsync(authorization, cancellationToken); /// /// Retrieves the status associated with an authorization. /// /// The authorization. /// The that can be used to abort the operation. /// /// A that can be used to monitor the asynchronous operation, /// whose result returns the status associated with the specified authorization. /// public virtual ValueTask GetStatusAsync( TAuthorization authorization!!, CancellationToken cancellationToken = default) => Store.GetStatusAsync(authorization, cancellationToken); /// /// Retrieves the subject associated with an authorization. /// /// The authorization. /// The that can be used to abort the operation. /// /// A that can be used to monitor the asynchronous operation, /// whose result returns the subject associated with the specified authorization. /// public virtual ValueTask GetSubjectAsync( TAuthorization authorization!!, CancellationToken cancellationToken = default) => Store.GetSubjectAsync(authorization, cancellationToken); /// /// Retrieves the type associated with an authorization. /// /// The authorization. /// The that can be used to abort the operation. /// /// A that can be used to monitor the asynchronous operation, /// whose result returns the type associated with the specified authorization. /// public virtual ValueTask GetTypeAsync( TAuthorization authorization!!, CancellationToken cancellationToken = default) => Store.GetTypeAsync(authorization, cancellationToken); /// /// Determines whether the specified scopes are included in the authorization. /// /// The authorization. /// The scopes. /// The that can be used to abort the operation. /// true if the scopes are included in the authorization, false otherwise. public virtual async ValueTask HasScopesAsync(TAuthorization authorization!!, ImmutableArray scopes, CancellationToken cancellationToken = default) => new HashSet(await Store.GetScopesAsync( authorization, cancellationToken), StringComparer.Ordinal).IsSupersetOf(scopes); /// /// Determines whether a given authorization has the specified status. /// /// The authorization. /// The expected status. /// The that can be used to abort the operation. /// true if the authorization has the specified status, false otherwise. public virtual async ValueTask HasStatusAsync(TAuthorization authorization!!, string status, CancellationToken cancellationToken = default) { if (string.IsNullOrEmpty(status)) { throw new ArgumentException(SR.GetResourceString(SR.ID0199), nameof(status)); } return string.Equals(await Store.GetStatusAsync(authorization, cancellationToken), status, StringComparison.OrdinalIgnoreCase); } /// /// Determines whether a given authorization has the specified type. /// /// The authorization. /// The expected type. /// The that can be used to abort the operation. /// true if the authorization has the specified type, false otherwise. public virtual async ValueTask HasTypeAsync( TAuthorization authorization!!, string type, CancellationToken cancellationToken = default) { if (string.IsNullOrEmpty(type)) { throw new ArgumentException(SR.GetResourceString(SR.ID0200), nameof(type)); } return string.Equals(await Store.GetTypeAsync(authorization, cancellationToken), type, StringComparison.OrdinalIgnoreCase); } /// /// Executes the specified query and returns all the corresponding elements. /// /// The number of results to return. /// The number of results to skip. /// The that can be used to abort the operation. /// All the elements returned when executing the specified query. public virtual IAsyncEnumerable ListAsync( int? count = null, int? offset = null, CancellationToken cancellationToken = default) => Store.ListAsync(count, offset, cancellationToken); /// /// Executes the specified query and returns all the corresponding elements. /// /// The result type. /// The query to execute. /// The that can be used to abort the operation. /// All the elements returned when executing the specified query. public virtual IAsyncEnumerable ListAsync( Func, IQueryable> query!!, CancellationToken cancellationToken = default) => ListAsync(static (authorizations, query) => query(authorizations), query, cancellationToken); /// /// Executes the specified query and returns all the corresponding elements. /// /// The state type. /// The result type. /// The query to execute. /// The optional state. /// The that can be used to abort the operation. /// All the elements returned when executing the specified query. public virtual IAsyncEnumerable ListAsync( Func, TState, IQueryable> query!!, TState state, CancellationToken cancellationToken = default) => Store.ListAsync(query, state, cancellationToken); /// /// Populates the authorization using the specified descriptor. /// /// The authorization. /// The descriptor. /// The that can be used to abort the operation. /// /// A that can be used to monitor the asynchronous operation. /// public virtual async ValueTask PopulateAsync(TAuthorization authorization!!, OpenIddictAuthorizationDescriptor descriptor!!, CancellationToken cancellationToken = default) { await Store.SetApplicationIdAsync(authorization, descriptor.ApplicationId, cancellationToken); await Store.SetCreationDateAsync(authorization, descriptor.CreationDate, cancellationToken); await Store.SetPropertiesAsync(authorization, descriptor.Properties.ToImmutableDictionary(), cancellationToken); await Store.SetScopesAsync(authorization, descriptor.Scopes.ToImmutableArray(), cancellationToken); await Store.SetStatusAsync(authorization, descriptor.Status, cancellationToken); await Store.SetSubjectAsync(authorization, descriptor.Subject, cancellationToken); await Store.SetTypeAsync(authorization, descriptor.Type, cancellationToken); } /// /// Populates the specified descriptor using the properties exposed by the authorization. /// /// The descriptor. /// The authorization. /// The that can be used to abort the operation. /// /// A that can be used to monitor the asynchronous operation. /// public virtual async ValueTask PopulateAsync( OpenIddictAuthorizationDescriptor descriptor!!, TAuthorization authorization!!, CancellationToken cancellationToken = default) { descriptor.ApplicationId = await Store.GetApplicationIdAsync(authorization, cancellationToken); descriptor.CreationDate = await Store.GetCreationDateAsync(authorization, cancellationToken); descriptor.Scopes.Clear(); descriptor.Scopes.UnionWith(await Store.GetScopesAsync(authorization, cancellationToken)); descriptor.Status = await Store.GetStatusAsync(authorization, cancellationToken); descriptor.Subject = await Store.GetSubjectAsync(authorization, cancellationToken); descriptor.Type = await Store.GetTypeAsync(authorization, cancellationToken); descriptor.Properties.Clear(); foreach (var pair in await Store.GetPropertiesAsync(authorization, cancellationToken)) { descriptor.Properties.Add(pair.Key, pair.Value); } } /// /// Removes the authorizations that are marked as invalid and the ad-hoc ones that have no token attached. /// Only authorizations created before the specified are removed. /// /// /// To ensure ad-hoc authorizations that no longer have any valid/non-expired token /// attached are correctly removed, the tokens should always be pruned first. /// /// The date before which authorizations are not pruned. /// The that can be used to abort the operation. /// /// A that can be used to monitor the asynchronous operation. /// public virtual ValueTask PruneAsync(DateTimeOffset threshold, CancellationToken cancellationToken = default) => Store.PruneAsync(threshold, cancellationToken); /// /// Tries to revoke an authorization. /// /// The authorization to revoke. /// The that can be used to abort the operation. /// true if the authorization was successfully revoked, false otherwise. public virtual async ValueTask TryRevokeAsync(TAuthorization authorization!!, CancellationToken cancellationToken = default) { var status = await Store.GetStatusAsync(authorization, cancellationToken); if (string.Equals(status, Statuses.Revoked, StringComparison.OrdinalIgnoreCase)) { return true; } await Store.SetStatusAsync(authorization, Statuses.Revoked, cancellationToken); try { await UpdateAsync(authorization, cancellationToken); Logger.LogInformation(SR.GetResourceString(SR.ID6164), await Store.GetIdAsync(authorization, cancellationToken)); return true; } catch (ConcurrencyException exception) { Logger.LogDebug(exception, SR.GetResourceString(SR.ID6165), await Store.GetIdAsync(authorization, cancellationToken)); return false; } catch (Exception exception) { Logger.LogWarning(exception, SR.GetResourceString(SR.ID6166), await Store.GetIdAsync(authorization, cancellationToken)); return false; } } /// /// Updates an existing authorization. /// /// The authorization to update. /// The that can be used to abort the operation. /// /// A that can be used to monitor the asynchronous operation. /// public virtual async ValueTask UpdateAsync(TAuthorization authorization!!, CancellationToken cancellationToken = default) { var results = await GetValidationResultsAsync(authorization, cancellationToken); if (results.Any(result => result != ValidationResult.Success)) { var builder = new StringBuilder(); builder.AppendLine(SR.GetResourceString(SR.ID0221)); builder.AppendLine(); foreach (var result in results) { builder.AppendLine(result.ErrorMessage); } throw new OpenIddictExceptions.ValidationException(builder.ToString(), results); } await Store.UpdateAsync(authorization, cancellationToken); if (!Options.CurrentValue.DisableEntityCaching) { await Cache.RemoveAsync(authorization, cancellationToken); await Cache.AddAsync(authorization, cancellationToken); } async Task> GetValidationResultsAsync( TAuthorization authorization, CancellationToken cancellationToken) { var builder = ImmutableArray.CreateBuilder(); await foreach (var result in ValidateAsync(authorization, cancellationToken)) { builder.Add(result); } return builder.ToImmutable(); } } /// /// Updates an existing authorization. /// /// The authorization to update. /// The descriptor used to update the authorization. /// The that can be used to abort the operation. /// /// A that can be used to monitor the asynchronous operation. /// public virtual async ValueTask UpdateAsync(TAuthorization authorization!!, OpenIddictAuthorizationDescriptor descriptor!!, CancellationToken cancellationToken = default) { await PopulateAsync(authorization, descriptor, cancellationToken); await UpdateAsync(authorization, cancellationToken); } /// /// Validates the authorization to ensure it's in a consistent state. /// /// The authorization. /// The that can be used to abort the operation. /// The validation error encountered when validating the authorization. public virtual async IAsyncEnumerable ValidateAsync( TAuthorization authorization!!, [EnumeratorCancellation] CancellationToken cancellationToken = default) { var type = await Store.GetTypeAsync(authorization, cancellationToken); if (string.IsNullOrEmpty(type)) { yield return new ValidationResult(SR.GetResourceString(SR.ID2116)); } else if (!string.Equals(type, AuthorizationTypes.AdHoc, StringComparison.OrdinalIgnoreCase) && !string.Equals(type, AuthorizationTypes.Permanent, StringComparison.OrdinalIgnoreCase)) { yield return new ValidationResult(SR.GetResourceString(SR.ID2117)); } if (string.IsNullOrEmpty(await Store.GetStatusAsync(authorization, cancellationToken))) { yield return new ValidationResult(SR.GetResourceString(SR.ID2038)); } // Ensure that the scopes are not null or empty and do not contain spaces. foreach (var scope in await Store.GetScopesAsync(authorization, cancellationToken)) { if (string.IsNullOrEmpty(scope)) { yield return new ValidationResult(SR.GetResourceString(SR.ID2039)); break; } if (scope.Contains(Separators.Space[0])) { yield return new ValidationResult(SR.GetResourceString(SR.ID2042)); break; } } } /// ValueTask IOpenIddictAuthorizationManager.CountAsync(CancellationToken cancellationToken) => CountAsync(cancellationToken); /// ValueTask IOpenIddictAuthorizationManager.CountAsync(Func, IQueryable> query, CancellationToken cancellationToken) => CountAsync(query, cancellationToken); /// async ValueTask IOpenIddictAuthorizationManager.CreateAsync(ClaimsPrincipal principal, string subject, string client, string type, ImmutableArray scopes, CancellationToken cancellationToken) => await CreateAsync(principal, subject, client, type, scopes, cancellationToken); /// async ValueTask IOpenIddictAuthorizationManager.CreateAsync(OpenIddictAuthorizationDescriptor descriptor, CancellationToken cancellationToken) => await CreateAsync(descriptor, cancellationToken); /// ValueTask IOpenIddictAuthorizationManager.CreateAsync(object authorization, CancellationToken cancellationToken) => CreateAsync((TAuthorization) authorization, cancellationToken); /// ValueTask IOpenIddictAuthorizationManager.DeleteAsync(object authorization, CancellationToken cancellationToken) => DeleteAsync((TAuthorization) authorization, cancellationToken); /// IAsyncEnumerable IOpenIddictAuthorizationManager.FindAsync(string subject, string client, CancellationToken cancellationToken) => FindAsync(subject, client, cancellationToken); /// IAsyncEnumerable IOpenIddictAuthorizationManager.FindAsync(string subject, string client, string status, CancellationToken cancellationToken) => FindAsync(subject, client, status, cancellationToken); /// IAsyncEnumerable IOpenIddictAuthorizationManager.FindAsync(string subject, string client, string status, string type, CancellationToken cancellationToken) => FindAsync(subject, client, status, type, cancellationToken); /// IAsyncEnumerable IOpenIddictAuthorizationManager.FindAsync(string subject, string client, string status, string type, ImmutableArray scopes, CancellationToken cancellationToken) => FindAsync(subject, client, status, type, scopes, cancellationToken); /// IAsyncEnumerable IOpenIddictAuthorizationManager.FindByApplicationIdAsync(string identifier, CancellationToken cancellationToken) => FindByApplicationIdAsync(identifier, cancellationToken); /// async ValueTask IOpenIddictAuthorizationManager.FindByIdAsync(string identifier, CancellationToken cancellationToken) => await FindByIdAsync(identifier, cancellationToken); /// IAsyncEnumerable IOpenIddictAuthorizationManager.FindBySubjectAsync(string subject, CancellationToken cancellationToken) => FindBySubjectAsync(subject, cancellationToken); /// ValueTask IOpenIddictAuthorizationManager.GetApplicationIdAsync(object authorization, CancellationToken cancellationToken) => GetApplicationIdAsync((TAuthorization) authorization, cancellationToken); /// ValueTask IOpenIddictAuthorizationManager.GetAsync(Func, IQueryable> query, CancellationToken cancellationToken) where TResult : default => GetAsync(query, cancellationToken); /// ValueTask IOpenIddictAuthorizationManager.GetAsync(Func, TState, IQueryable> query, TState state, CancellationToken cancellationToken) where TResult : default => GetAsync(query, state, cancellationToken); ValueTask IOpenIddictAuthorizationManager.GetCreationDateAsync(object authorization, CancellationToken cancellationToken) => GetCreationDateAsync((TAuthorization) authorization, cancellationToken); /// ValueTask IOpenIddictAuthorizationManager.GetIdAsync(object authorization, CancellationToken cancellationToken) => GetIdAsync((TAuthorization) authorization, cancellationToken); /// ValueTask> IOpenIddictAuthorizationManager.GetPropertiesAsync(object authorization, CancellationToken cancellationToken) => GetPropertiesAsync((TAuthorization) authorization, cancellationToken); /// ValueTask> IOpenIddictAuthorizationManager.GetScopesAsync(object authorization, CancellationToken cancellationToken) => GetScopesAsync((TAuthorization) authorization, cancellationToken); /// ValueTask IOpenIddictAuthorizationManager.GetStatusAsync(object authorization, CancellationToken cancellationToken) => GetStatusAsync((TAuthorization) authorization, cancellationToken); /// ValueTask IOpenIddictAuthorizationManager.GetSubjectAsync(object authorization, CancellationToken cancellationToken) => GetSubjectAsync((TAuthorization) authorization, cancellationToken); /// ValueTask IOpenIddictAuthorizationManager.GetTypeAsync(object authorization, CancellationToken cancellationToken) => GetTypeAsync((TAuthorization) authorization, cancellationToken); /// ValueTask IOpenIddictAuthorizationManager.HasScopesAsync(object authorization, ImmutableArray scopes, CancellationToken cancellationToken) => HasScopesAsync((TAuthorization) authorization, scopes, cancellationToken); /// ValueTask IOpenIddictAuthorizationManager.HasStatusAsync(object authorization, string status, CancellationToken cancellationToken) => HasStatusAsync((TAuthorization) authorization, status, cancellationToken); /// ValueTask IOpenIddictAuthorizationManager.HasTypeAsync(object authorization, string type, CancellationToken cancellationToken) => HasTypeAsync((TAuthorization) authorization, type, cancellationToken); /// IAsyncEnumerable IOpenIddictAuthorizationManager.ListAsync(int? count, int? offset, CancellationToken cancellationToken) => ListAsync(count, offset, cancellationToken); /// IAsyncEnumerable IOpenIddictAuthorizationManager.ListAsync(Func, IQueryable> query, CancellationToken cancellationToken) => ListAsync(query, cancellationToken); /// IAsyncEnumerable IOpenIddictAuthorizationManager.ListAsync(Func, TState, IQueryable> query, TState state, CancellationToken cancellationToken) => ListAsync(query, state, cancellationToken); /// ValueTask IOpenIddictAuthorizationManager.PopulateAsync(OpenIddictAuthorizationDescriptor descriptor, object authorization, CancellationToken cancellationToken) => PopulateAsync(descriptor, (TAuthorization) authorization, cancellationToken); /// ValueTask IOpenIddictAuthorizationManager.PopulateAsync(object authorization, OpenIddictAuthorizationDescriptor descriptor, CancellationToken cancellationToken) => PopulateAsync((TAuthorization) authorization, descriptor, cancellationToken); /// ValueTask IOpenIddictAuthorizationManager.PruneAsync(DateTimeOffset threshold, CancellationToken cancellationToken) => PruneAsync(threshold, cancellationToken); /// ValueTask IOpenIddictAuthorizationManager.TryRevokeAsync(object authorization, CancellationToken cancellationToken) => TryRevokeAsync((TAuthorization) authorization, cancellationToken); /// ValueTask IOpenIddictAuthorizationManager.UpdateAsync(object authorization, CancellationToken cancellationToken) => UpdateAsync((TAuthorization) authorization, cancellationToken); /// ValueTask IOpenIddictAuthorizationManager.UpdateAsync(object authorization, OpenIddictAuthorizationDescriptor descriptor, CancellationToken cancellationToken) => UpdateAsync((TAuthorization) authorization, descriptor, cancellationToken); /// IAsyncEnumerable IOpenIddictAuthorizationManager.ValidateAsync(object authorization, CancellationToken cancellationToken) => ValidateAsync((TAuthorization) authorization, cancellationToken); }