21 changed files with 4121 additions and 1 deletions
@ -0,0 +1,21 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
|
|||
<Import Project="..\..\build\packages.props" /> |
|||
|
|||
<PropertyGroup> |
|||
<TargetFrameworks>net451;netstandard1.5</TargetFrameworks> |
|||
<SignAssembly>false</SignAssembly> |
|||
<NetStandardImplicitPackageVersion>1.6.1</NetStandardImplicitPackageVersion> |
|||
</PropertyGroup> |
|||
|
|||
<PropertyGroup> |
|||
<Description>Document-oriented entities for the MongoDB stores.</Description> |
|||
<Authors>Kévin Chalet</Authors> |
|||
<PackageTags>aspnetcore;authentication;jwt;openidconnect;openiddict;security</PackageTags> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<PackageReference Include="MongoDB.Bson" Version="$(MongoDbVersion)" /> |
|||
</ItemGroup> |
|||
|
|||
</Project> |
|||
@ -0,0 +1,91 @@ |
|||
/* |
|||
* 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; |
|||
using MongoDB.Bson; |
|||
using MongoDB.Bson.Serialization.Attributes; |
|||
|
|||
namespace OpenIddict.MongoDb.Models |
|||
{ |
|||
/// <summary>
|
|||
/// Represents an OpenIddict application.
|
|||
/// </summary>
|
|||
public class OpenIddictApplication |
|||
{ |
|||
/// <summary>
|
|||
/// Gets or sets the client identifier
|
|||
/// associated with the current application.
|
|||
/// </summary>
|
|||
[BsonElement("client_id"), BsonRequired] |
|||
public virtual string ClientId { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the client secret associated with the current application.
|
|||
/// Note: depending on the application manager used to create this instance,
|
|||
/// this property may be hashed or encrypted for security reasons.
|
|||
/// </summary>
|
|||
[BsonElement("client_secret"), BsonIgnoreIfNull] |
|||
public virtual string ClientSecret { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the concurrency token.
|
|||
/// </summary>
|
|||
[BsonElement("concurrency_token"), BsonRequired] |
|||
public virtual string ConcurrencyToken { get; set; } = Guid.NewGuid().ToString(); |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the consent type
|
|||
/// associated with the current application.
|
|||
/// </summary>
|
|||
[BsonElement("consent_type"), BsonIgnoreIfNull] |
|||
public virtual string ConsentType { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the display name
|
|||
/// associated with the current application.
|
|||
/// </summary>
|
|||
[BsonElement("display_name"), BsonIgnoreIfNull] |
|||
public virtual string DisplayName { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the unique identifier
|
|||
/// associated with the current application.
|
|||
/// </summary>
|
|||
[BsonId, BsonRequired] |
|||
public virtual ObjectId Id { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the permissions associated with the current application.
|
|||
/// </summary>
|
|||
[BsonElement("permissions"), BsonIgnoreIfDefault] |
|||
public virtual string[] Permissions { get; set; } = new string[0]; |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the logout callback URLs associated with the current application.
|
|||
/// </summary>
|
|||
[BsonElement("post_logout_redirect_uris"), BsonIgnoreIfDefault] |
|||
public virtual string[] PostLogoutRedirectUris { get; set; } = new string[0]; |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the additional properties associated with the current application.
|
|||
/// </summary>
|
|||
[BsonExtraElements] |
|||
public virtual BsonDocument Properties { get; set; } = new BsonDocument(); |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the callback URLs associated with the current application.
|
|||
/// </summary>
|
|||
[BsonElement("redirect_uris"), BsonIgnoreIfDefault] |
|||
public virtual string[] RedirectUris { get; set; } = new string[0]; |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the application type
|
|||
/// associated with the current application.
|
|||
/// </summary>
|
|||
[BsonElement("type"), BsonRequired] |
|||
public virtual string Type { get; set; } |
|||
} |
|||
} |
|||
@ -0,0 +1,68 @@ |
|||
/* |
|||
* 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; |
|||
using MongoDB.Bson; |
|||
using MongoDB.Bson.Serialization.Attributes; |
|||
|
|||
namespace OpenIddict.MongoDb.Models |
|||
{ |
|||
/// <summary>
|
|||
/// Represents an OpenIddict authorization.
|
|||
/// </summary>
|
|||
public class OpenIddictAuthorization |
|||
{ |
|||
/// <summary>
|
|||
/// Gets or sets the identifier of the application
|
|||
/// associated with the current authorization.
|
|||
/// </summary>
|
|||
[BsonElement("application_id"), BsonIgnoreIfDefault] |
|||
public virtual ObjectId ApplicationId { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the concurrency token.
|
|||
/// </summary>
|
|||
[BsonElement("concurrency_token"), BsonRequired] |
|||
public virtual string ConcurrencyToken { get; set; } = Guid.NewGuid().ToString(); |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the unique identifier
|
|||
/// associated with the current authorization.
|
|||
/// </summary>
|
|||
[BsonId, BsonRequired] |
|||
public virtual ObjectId Id { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the additional properties associated with the current authorization.
|
|||
/// </summary>
|
|||
[BsonExtraElements] |
|||
public virtual BsonDocument Properties { get; set; } = new BsonDocument(); |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the scopes associated with the current authorization.
|
|||
/// </summary>
|
|||
[BsonElement("scopes"), BsonIgnoreIfDefault] |
|||
public virtual string[] Scopes { get; set; } = new string[0]; |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the status of the current authorization.
|
|||
/// </summary>
|
|||
[BsonElement("status"), BsonRequired] |
|||
public virtual string Status { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the subject associated with the current authorization.
|
|||
/// </summary>
|
|||
[BsonElement("subject"), BsonRequired] |
|||
public virtual string Subject { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the type of the current authorization.
|
|||
/// </summary>
|
|||
[BsonElement("type"), BsonRequired] |
|||
public virtual string Type { get; set; } |
|||
} |
|||
} |
|||
@ -0,0 +1,64 @@ |
|||
/* |
|||
* 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; |
|||
using MongoDB.Bson; |
|||
using MongoDB.Bson.Serialization.Attributes; |
|||
|
|||
namespace OpenIddict.MongoDb.Models |
|||
{ |
|||
/// <summary>
|
|||
/// Represents an OpenIddict scope.
|
|||
/// </summary>
|
|||
public class OpenIddictScope |
|||
{ |
|||
/// <summary>
|
|||
/// Gets or sets the concurrency token.
|
|||
/// </summary>
|
|||
[BsonElement("concurrency_token"), BsonRequired] |
|||
public virtual string ConcurrencyToken { get; set; } = Guid.NewGuid().ToString(); |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the public description
|
|||
/// associated with the current scope.
|
|||
/// </summary>
|
|||
[BsonElement("description"), BsonIgnoreIfNull] |
|||
public virtual string Description { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the display name
|
|||
/// associated with the current scope.
|
|||
/// </summary>
|
|||
[BsonElement("display_name"), BsonIgnoreIfNull] |
|||
public virtual string DisplayName { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the unique identifier
|
|||
/// associated with the current scope.
|
|||
/// </summary>
|
|||
[BsonId, BsonRequired] |
|||
public virtual ObjectId Id { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the unique name
|
|||
/// associated with the current scope.
|
|||
/// </summary>
|
|||
[BsonElement("name"), BsonRequired] |
|||
public virtual string Name { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the additional properties associated with the current scope.
|
|||
/// </summary>
|
|||
[BsonExtraElements] |
|||
public virtual BsonDocument Properties { get; set; } = new BsonDocument(); |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the resources associated with the current scope.
|
|||
/// </summary>
|
|||
[BsonElement("resources"), BsonIgnoreIfDefault] |
|||
public virtual string[] Resources { get; set; } = new string[0]; |
|||
} |
|||
} |
|||
@ -0,0 +1,98 @@ |
|||
/* |
|||
* 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; |
|||
using MongoDB.Bson; |
|||
using MongoDB.Bson.Serialization.Attributes; |
|||
|
|||
namespace OpenIddict.MongoDb.Models |
|||
{ |
|||
/// <summary>
|
|||
/// Represents an OpenIddict token.
|
|||
/// </summary>
|
|||
public class OpenIddictToken |
|||
{ |
|||
/// <summary>
|
|||
/// Gets or sets the identifier of the application associated with the current token.
|
|||
/// </summary>
|
|||
[BsonElement("application_id"), BsonIgnoreIfDefault] |
|||
public virtual ObjectId ApplicationId { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the identifier of the authorization associated with the current token.
|
|||
/// </summary>
|
|||
[BsonElement("authorization_id"), BsonIgnoreIfDefault] |
|||
public virtual ObjectId AuthorizationId { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the concurrency token.
|
|||
/// </summary>
|
|||
[BsonElement("concurrency_token"), BsonRequired] |
|||
public virtual string ConcurrencyToken { get; set; } = Guid.NewGuid().ToString(); |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the date on which the token
|
|||
/// will start to be considered valid.
|
|||
/// </summary>
|
|||
[BsonElement("creation_date"), BsonIgnoreIfNull] |
|||
public virtual DateTime? CreationDate { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the date on which the token
|
|||
/// will no longer be considered valid.
|
|||
/// </summary>
|
|||
[BsonElement("expiration_date"), BsonIgnoreIfNull] |
|||
public virtual DateTime? ExpirationDate { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the unique identifier
|
|||
/// associated with the current token.
|
|||
/// </summary>
|
|||
[BsonId, BsonRequired] |
|||
public virtual ObjectId Id { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the payload of the current token, if applicable.
|
|||
/// Note: this property is only used for reference tokens
|
|||
/// and may be encrypted for security reasons.
|
|||
/// </summary>
|
|||
[BsonElement("payload"), BsonIgnoreIfNull] |
|||
public virtual string Payload { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the additional properties associated with the current token.
|
|||
/// </summary>
|
|||
[BsonExtraElements] |
|||
public virtual BsonDocument Properties { get; set; } = new BsonDocument(); |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the reference identifier associated
|
|||
/// with the current token, if applicable.
|
|||
/// Note: this property is only used for reference tokens
|
|||
/// and may be hashed or encrypted for security reasons.
|
|||
/// </summary>
|
|||
[BsonElement("reference_id"), BsonIgnoreIfNull] |
|||
public virtual string ReferenceId { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the status of the current token.
|
|||
/// </summary>
|
|||
[BsonElement("status"), BsonRequired] |
|||
public virtual string Status { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the subject associated with the current token.
|
|||
/// </summary>
|
|||
[BsonElement("subject"), BsonRequired] |
|||
public virtual string Subject { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the type of the current token.
|
|||
/// </summary>
|
|||
[BsonElement("type"), BsonRequired] |
|||
public virtual string Type { get; set; } |
|||
} |
|||
} |
|||
@ -0,0 +1,27 @@ |
|||
/* |
|||
* 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.Threading; |
|||
using System.Threading.Tasks; |
|||
using MongoDB.Driver; |
|||
|
|||
namespace OpenIddict.MongoDb |
|||
{ |
|||
/// <summary>
|
|||
/// Exposes the MongoDB database used by the OpenIddict stores.
|
|||
/// </summary>
|
|||
public interface IOpenIddictMongoDbContext |
|||
{ |
|||
/// <summary>
|
|||
/// Gets the <see cref="IMongoDatabase"/>.
|
|||
/// </summary>
|
|||
/// <returns>
|
|||
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the
|
|||
/// asynchronous operation, whose result returns the MongoDB database.
|
|||
/// </returns>
|
|||
ValueTask<IMongoDatabase> GetDatabaseAsync(CancellationToken cancellationToken); |
|||
} |
|||
} |
|||
@ -0,0 +1,27 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
|
|||
<Import Project="..\..\build\packages.props" /> |
|||
|
|||
<PropertyGroup> |
|||
<TargetFrameworks>net451;netstandard1.5</TargetFrameworks> |
|||
<SignAssembly>false</SignAssembly> |
|||
<NetStandardImplicitPackageVersion>1.6.1</NetStandardImplicitPackageVersion> |
|||
</PropertyGroup> |
|||
|
|||
<PropertyGroup> |
|||
<Description>MongoDB stores for OpenIddict.</Description> |
|||
<Authors>Kévin Chalet</Authors> |
|||
<PackageTags>aspnetcore;authentication;jwt;openidconnect;openiddict;security</PackageTags> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<ProjectReference Include="..\OpenIddict.Core\OpenIddict.Core.csproj" /> |
|||
<ProjectReference Include="..\OpenIddict.MongoDb.Models\OpenIddict.MongoDb.Models.csproj" /> |
|||
</ItemGroup> |
|||
|
|||
<ItemGroup> |
|||
<PackageReference Include="JetBrains.Annotations" Version="$(JetBrainsVersion)" PrivateAssets="All" /> |
|||
<PackageReference Include="MongoDB.Driver" Version="$(MongoDbVersion)" /> |
|||
</ItemGroup> |
|||
|
|||
</Project> |
|||
@ -0,0 +1,184 @@ |
|||
/* |
|||
* 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; |
|||
using System.ComponentModel; |
|||
using JetBrains.Annotations; |
|||
using MongoDB.Driver; |
|||
using OpenIddict.Core; |
|||
using OpenIddict.MongoDb; |
|||
using OpenIddict.MongoDb.Models; |
|||
|
|||
namespace Microsoft.Extensions.DependencyInjection |
|||
{ |
|||
/// <summary>
|
|||
/// Exposes the necessary methods required to configure the OpenIddict MongoDB services.
|
|||
/// </summary>
|
|||
public class OpenIddictMongoDbBuilder |
|||
{ |
|||
/// <summary>
|
|||
/// Initializes a new instance of <see cref="OpenIddictMongoDbBuilder"/>.
|
|||
/// </summary>
|
|||
/// <param name="services">The services collection.</param>
|
|||
public OpenIddictMongoDbBuilder([NotNull] IServiceCollection services) |
|||
{ |
|||
if (services == null) |
|||
{ |
|||
throw new ArgumentNullException(nameof(services)); |
|||
} |
|||
|
|||
Services = services; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the services collection.
|
|||
/// </summary>
|
|||
[EditorBrowsable(EditorBrowsableState.Never)] |
|||
public IServiceCollection Services { get; } |
|||
|
|||
/// <summary>
|
|||
/// Amends the default OpenIddict MongoDB configuration.
|
|||
/// </summary>
|
|||
/// <param name="configuration">The delegate used to configure the OpenIddict options.</param>
|
|||
/// <remarks>This extension can be safely called multiple times.</remarks>
|
|||
/// <returns>The <see cref="OpenIddictMongoDbBuilder"/>.</returns>
|
|||
public OpenIddictMongoDbBuilder Configure([NotNull] Action<OpenIddictMongoDbOptions> configuration) |
|||
{ |
|||
if (configuration == null) |
|||
{ |
|||
throw new ArgumentNullException(nameof(configuration)); |
|||
} |
|||
|
|||
Services.Configure(configuration); |
|||
|
|||
return this; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Configures the MongoDB stores to use the specified database
|
|||
/// instead of retrieving it from the dependency injection container.
|
|||
/// </summary>
|
|||
/// <param name="database">The <see cref="IMongoDatabase"/>.</param>
|
|||
/// <returns>The <see cref="OpenIddictMongoDbBuilder"/>.</returns>
|
|||
public OpenIddictMongoDbBuilder UseDatabase([NotNull] IMongoDatabase database) |
|||
{ |
|||
if (database == null) |
|||
{ |
|||
throw new ArgumentNullException(nameof(database)); |
|||
} |
|||
|
|||
return Configure(options => options.Database = database); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Configures OpenIddict to use the specified entity as the default application entity.
|
|||
/// </summary>
|
|||
/// <returns>The <see cref="OpenIddictMongoDbBuilder"/>.</returns>
|
|||
public OpenIddictMongoDbBuilder ReplaceDefaultApplicationEntity<TApplication>() |
|||
where TApplication : OpenIddictApplication, new() |
|||
{ |
|||
Services.Configure<OpenIddictCoreOptions>(options => options.DefaultApplicationType = typeof(TApplication)); |
|||
|
|||
return this; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Configures OpenIddict to use the specified entity as the default authorization entity.
|
|||
/// </summary>
|
|||
/// <returns>The <see cref="OpenIddictMongoDbBuilder"/>.</returns>
|
|||
public OpenIddictMongoDbBuilder ReplaceDefaultAuthorizationEntity<TAuthorization>() |
|||
where TAuthorization : OpenIddictAuthorization, new() |
|||
{ |
|||
Services.Configure<OpenIddictCoreOptions>(options => options.DefaultAuthorizationType = typeof(TAuthorization)); |
|||
|
|||
return this; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Configures OpenIddict to use the specified entity as the default scope entity.
|
|||
/// </summary>
|
|||
/// <returns>The <see cref="OpenIddictMongoDbBuilder"/>.</returns>
|
|||
public OpenIddictMongoDbBuilder ReplaceDefaultScopeEntity<TScope>() |
|||
where TScope : OpenIddictScope, new() |
|||
{ |
|||
Services.Configure<OpenIddictCoreOptions>(options => options.DefaultScopeType = typeof(TScope)); |
|||
|
|||
return this; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Configures OpenIddict to use the specified entity as the default token entity.
|
|||
/// </summary>
|
|||
/// <returns>The <see cref="OpenIddictMongoDbBuilder"/>.</returns>
|
|||
public OpenIddictMongoDbBuilder ReplaceDefaultTokenEntity<TToken>() |
|||
where TToken : OpenIddictToken, new() |
|||
{ |
|||
Services.Configure<OpenIddictCoreOptions>(options => options.DefaultTokenType = typeof(TToken)); |
|||
|
|||
return this; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Replaces the default applications collection name (by default, openiddict.applications).
|
|||
/// </summary>
|
|||
/// <param name="name">The collection name</param>
|
|||
/// <returns>The <see cref="OpenIddictMongoDbBuilder"/>.</returns>
|
|||
public OpenIddictMongoDbBuilder SetApplicationsCollectionName([NotNull] string name) |
|||
{ |
|||
if (string.IsNullOrEmpty(name)) |
|||
{ |
|||
throw new ArgumentException("The collection name cannot be null or empty.", nameof(name)); |
|||
} |
|||
|
|||
return Configure(options => options.ApplicationsCollectionName = name); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Replaces the default authorizations collection name (by default, openiddict.authorizations).
|
|||
/// </summary>
|
|||
/// <param name="name">The collection name</param>
|
|||
/// <returns>The <see cref="OpenIddictMongoDbBuilder"/>.</returns>
|
|||
public OpenIddictMongoDbBuilder SetAuthorizationsCollectionName([NotNull] string name) |
|||
{ |
|||
if (string.IsNullOrEmpty(name)) |
|||
{ |
|||
throw new ArgumentException("The collection name cannot be null or empty.", nameof(name)); |
|||
} |
|||
|
|||
return Configure(options => options.AuthorizationsCollectionName = name); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Replaces the default scopes collection name (by default, openiddict.scopes).
|
|||
/// </summary>
|
|||
/// <param name="name">The collection name</param>
|
|||
/// <returns>The <see cref="OpenIddictMongoDbBuilder"/>.</returns>
|
|||
public OpenIddictMongoDbBuilder SetScopesCollectionName([NotNull] string name) |
|||
{ |
|||
if (string.IsNullOrEmpty(name)) |
|||
{ |
|||
throw new ArgumentException("The collection name cannot be null or empty.", nameof(name)); |
|||
} |
|||
|
|||
return Configure(options => options.ScopesCollectionName = name); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Replaces the default tokens collection name (by default, openiddict.tokens).
|
|||
/// </summary>
|
|||
/// <param name="name">The collection name</param>
|
|||
/// <returns>The <see cref="OpenIddictMongoDbBuilder"/>.</returns>
|
|||
public OpenIddictMongoDbBuilder SetTokensCollectionName([NotNull] string name) |
|||
{ |
|||
if (string.IsNullOrEmpty(name)) |
|||
{ |
|||
throw new ArgumentException("The collection name cannot be null or empty.", nameof(name)); |
|||
} |
|||
|
|||
return Configure(options => options.TokensCollectionName = name); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,113 @@ |
|||
/* |
|||
* 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; |
|||
using System.Text; |
|||
using System.Threading; |
|||
using System.Threading.Tasks; |
|||
using JetBrains.Annotations; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Microsoft.Extensions.Options; |
|||
using MongoDB.Driver; |
|||
using OpenIddict.MongoDb.Models; |
|||
|
|||
namespace OpenIddict.MongoDb |
|||
{ |
|||
/// <summary>
|
|||
/// Exposes the MongoDB database used by the OpenIddict stores.
|
|||
/// </summary>
|
|||
public class OpenIddictMongoDbContext : IOpenIddictMongoDbContext |
|||
{ |
|||
private readonly IOptionsMonitor<OpenIddictMongoDbOptions> _options; |
|||
private readonly IServiceProvider _provider; |
|||
private IMongoDatabase _database; |
|||
|
|||
public OpenIddictMongoDbContext( |
|||
[NotNull] IOptionsMonitor<OpenIddictMongoDbOptions> options, |
|||
[NotNull] IServiceProvider provider) |
|||
{ |
|||
_options = options; |
|||
_provider = provider; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the <see cref="IMongoDatabase"/>.
|
|||
/// </summary>
|
|||
/// <returns>
|
|||
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the
|
|||
/// asynchronous operation, whose result returns the MongoDB database.
|
|||
/// </returns>
|
|||
public ValueTask<IMongoDatabase> GetDatabaseAsync(CancellationToken cancellationToken) |
|||
{ |
|||
if (_database != null) |
|||
{ |
|||
return new ValueTask<IMongoDatabase>(_database); |
|||
} |
|||
|
|||
var options = _options.CurrentValue; |
|||
if (options == null) |
|||
{ |
|||
throw new InvalidOperationException("The OpenIddict MongoDB options cannot be retrieved."); |
|||
} |
|||
|
|||
async Task<IMongoDatabase> ExecuteAsync() |
|||
{ |
|||
var database = options.Database; |
|||
if (database == null) |
|||
{ |
|||
database = _provider.GetService<IMongoDatabase>(); |
|||
} |
|||
|
|||
if (database == null) |
|||
{ |
|||
throw new InvalidOperationException(new StringBuilder() |
|||
.AppendLine("No suitable MongoDB database service can be found.") |
|||
.Append("To configure the OpenIddict MongoDB stores to use a specific database, use ") |
|||
.Append("'services.AddOpenIddict().AddCore().UseMongoDb().UseDatabase()' or register an ") |
|||
.Append("'IMongoDatabase' in the dependency injection container in 'ConfigureServices()'.") |
|||
.ToString()); |
|||
} |
|||
|
|||
// Note: the cancellation token passed as a parameter is deliberately not used here to ensure
|
|||
// the cancellation of a single store operation doesn't prevent the indexes from being created.
|
|||
var applications = database.GetCollection<OpenIddictApplication>(options.ApplicationsCollectionName); |
|||
await applications.Indexes.CreateOneAsync( |
|||
Builders<OpenIddictApplication>.IndexKeys.Ascending(application => application.ClientId), |
|||
new CreateIndexOptions |
|||
{ |
|||
Unique = true |
|||
}); |
|||
|
|||
await applications.Indexes.CreateOneAsync( |
|||
Builders<OpenIddictApplication>.IndexKeys.Ascending(application => application.PostLogoutRedirectUris)); |
|||
|
|||
await applications.Indexes.CreateOneAsync( |
|||
Builders<OpenIddictApplication>.IndexKeys.Ascending(application => application.RedirectUris)); |
|||
|
|||
var scopes = database.GetCollection<OpenIddictScope>(options.ScopesCollectionName); |
|||
await scopes.Indexes.CreateOneAsync( |
|||
Builders<OpenIddictScope>.IndexKeys.Ascending(scope => scope.Name), |
|||
new CreateIndexOptions |
|||
{ |
|||
Unique = true |
|||
}); |
|||
|
|||
var tokens = database.GetCollection<OpenIddictToken>(options.TokensCollectionName); |
|||
await tokens.Indexes.CreateOneAsync( |
|||
Builders<OpenIddictToken>.IndexKeys.Ascending(token => token.ReferenceId), |
|||
new CreateIndexOptions<OpenIddictToken> |
|||
{ |
|||
PartialFilterExpression = Builders<OpenIddictToken>.Filter.Exists(token => token.ReferenceId), |
|||
Unique = true |
|||
}); |
|||
|
|||
return _database = database; |
|||
} |
|||
|
|||
return new ValueTask<IMongoDatabase>(ExecuteAsync()); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,81 @@ |
|||
/* |
|||
* 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; |
|||
using JetBrains.Annotations; |
|||
using Microsoft.Extensions.DependencyInjection.Extensions; |
|||
using Microsoft.Extensions.Options; |
|||
using OpenIddict.MongoDb; |
|||
using OpenIddict.MongoDb.Models; |
|||
|
|||
namespace Microsoft.Extensions.DependencyInjection |
|||
{ |
|||
public static class OpenIddictMongoDbExtensions |
|||
{ |
|||
/// <summary>
|
|||
/// Registers the MongoDB stores services in the DI container and
|
|||
/// configures OpenIddict to use the MongoDB entities by default.
|
|||
/// </summary>
|
|||
/// <param name="builder">The services builder used by OpenIddict to register new services.</param>
|
|||
/// <remarks>This extension can be safely called multiple times.</remarks>
|
|||
/// <returns>The <see cref="OpenIddictMongoDbBuilder"/>.</returns>
|
|||
public static OpenIddictMongoDbBuilder UseMongoDb([NotNull] this OpenIddictCoreBuilder builder) |
|||
{ |
|||
if (builder == null) |
|||
{ |
|||
throw new ArgumentNullException(nameof(builder)); |
|||
} |
|||
|
|||
builder.SetDefaultApplicationEntity<OpenIddictApplication>() |
|||
.SetDefaultAuthorizationEntity<OpenIddictAuthorization>() |
|||
.SetDefaultScopeEntity<OpenIddictScope>() |
|||
.SetDefaultTokenEntity<OpenIddictToken>(); |
|||
|
|||
// Note: the Mongo stores/resolvers don't depend on scoped/transient services and thus
|
|||
// can be safely registered as singleton services and shared/reused across requests.
|
|||
builder.ReplaceApplicationStoreResolver<OpenIddictApplicationStoreResolver>(ServiceLifetime.Singleton) |
|||
.ReplaceAuthorizationStoreResolver<OpenIddictAuthorizationStoreResolver>(ServiceLifetime.Singleton) |
|||
.ReplaceScopeStoreResolver<OpenIddictScopeStoreResolver>(ServiceLifetime.Singleton) |
|||
.ReplaceTokenStoreResolver<OpenIddictTokenStoreResolver>(ServiceLifetime.Singleton); |
|||
|
|||
builder.Services.TryAddSingleton(typeof(OpenIddictApplicationStore<>)); |
|||
builder.Services.TryAddSingleton(typeof(OpenIddictAuthorizationStore<>)); |
|||
builder.Services.TryAddSingleton(typeof(OpenIddictScopeStore<>)); |
|||
builder.Services.TryAddSingleton(typeof(OpenIddictTokenStore<>)); |
|||
|
|||
builder.Services.TryAddSingleton<IOpenIddictMongoDbContext, OpenIddictMongoDbContext>(); |
|||
|
|||
return new OpenIddictMongoDbBuilder(builder.Services); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Registers the MongoDB stores services in the DI container and
|
|||
/// configures OpenIddict to use the MongoDB entities by default.
|
|||
/// </summary>
|
|||
/// <param name="builder">The services builder used by OpenIddict to register new services.</param>
|
|||
/// <param name="configuration">The configuration delegate used to configure the MongoDB services.</param>
|
|||
/// <remarks>This extension can be safely called multiple times.</remarks>
|
|||
/// <returns>The <see cref="OpenIddictCoreBuilder"/>.</returns>
|
|||
public static OpenIddictCoreBuilder UseMongoDb( |
|||
[NotNull] this OpenIddictCoreBuilder builder, |
|||
[NotNull] Action<OpenIddictMongoDbBuilder> configuration) |
|||
{ |
|||
if (builder == null) |
|||
{ |
|||
throw new ArgumentNullException(nameof(builder)); |
|||
} |
|||
|
|||
if (configuration == null) |
|||
{ |
|||
throw new ArgumentNullException(nameof(configuration)); |
|||
} |
|||
|
|||
configuration(builder.UseMongoDb()); |
|||
|
|||
return builder; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,42 @@ |
|||
/* |
|||
* 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 MongoDB.Driver; |
|||
|
|||
namespace OpenIddict.MongoDb |
|||
{ |
|||
/// <summary>
|
|||
/// Provides various settings needed to configure the OpenIddict MongoDB integration.
|
|||
/// </summary>
|
|||
public class OpenIddictMongoDbOptions |
|||
{ |
|||
/// <summary>
|
|||
/// Gets or sets the name of the applications collection (by default, openiddict.applications).
|
|||
/// </summary>
|
|||
public string ApplicationsCollectionName { get; set; } = "openiddict.applications"; |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the name of the authorizations collection (by default, openiddict.authorizations).
|
|||
/// </summary>
|
|||
public string AuthorizationsCollectionName { get; set; } = "openiddict.authorizations"; |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the <see cref="IMongoDatabase"/> used by the OpenIddict stores.
|
|||
/// If no value is explicitly set, the database is resolved from the DI container.
|
|||
/// </summary>
|
|||
public IMongoDatabase Database { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the name of the scopes collection (by default, openiddict.scopes).
|
|||
/// </summary>
|
|||
public string ScopesCollectionName { get; set; } = "openiddict.scopes"; |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the name of the tokens collection (by default, openiddict.tokens).
|
|||
/// </summary>
|
|||
public string TokensCollectionName { get; set; } = "openiddict.tokens"; |
|||
} |
|||
} |
|||
@ -0,0 +1,57 @@ |
|||
/* |
|||
* 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; |
|||
using System.Reflection; |
|||
using System.Text; |
|||
using JetBrains.Annotations; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using OpenIddict.Abstractions; |
|||
using OpenIddict.MongoDb.Models; |
|||
|
|||
namespace OpenIddict.MongoDb |
|||
{ |
|||
/// <summary>
|
|||
/// Exposes a method allowing to resolve an application store.
|
|||
/// </summary>
|
|||
public class OpenIddictApplicationStoreResolver : IOpenIddictApplicationStoreResolver |
|||
{ |
|||
private readonly IServiceProvider _provider; |
|||
|
|||
public OpenIddictApplicationStoreResolver([NotNull] IServiceProvider provider) |
|||
{ |
|||
_provider = provider; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Returns an application store compatible with the specified application type or throws an
|
|||
/// <see cref="InvalidOperationException"/> if no store can be built using the specified type.
|
|||
/// </summary>
|
|||
/// <typeparam name="TApplication">The type of the Application entity.</typeparam>
|
|||
/// <returns>An <see cref="IOpenIddictApplicationStore{TApplication}"/>.</returns>
|
|||
public IOpenIddictApplicationStore<TApplication> Get<TApplication>() where TApplication : class |
|||
{ |
|||
var store = _provider.GetService<IOpenIddictApplicationStore<TApplication>>(); |
|||
if (store != null) |
|||
{ |
|||
return store; |
|||
} |
|||
|
|||
if (!typeof(OpenIddictApplication).IsAssignableFrom(typeof(TApplication))) |
|||
{ |
|||
throw new InvalidOperationException(new StringBuilder() |
|||
.AppendLine("The specified application type is not compatible with the MongoDB stores.") |
|||
.Append("When enabling the MongoDB stores, make sure you use the built-in 'OpenIddictApplication' ") |
|||
.Append("entity (from the 'OpenIddict.MongoDb.Models' package) or a custom entity ") |
|||
.Append("that inherits from the 'OpenIddictApplication' entity.") |
|||
.ToString()); |
|||
} |
|||
|
|||
return (IOpenIddictApplicationStore<TApplication>) _provider.GetRequiredService( |
|||
typeof(OpenIddictApplicationStore<>).MakeGenericType(typeof(TApplication))); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,57 @@ |
|||
/* |
|||
* 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; |
|||
using System.Reflection; |
|||
using System.Text; |
|||
using JetBrains.Annotations; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using OpenIddict.Abstractions; |
|||
using OpenIddict.MongoDb.Models; |
|||
|
|||
namespace OpenIddict.MongoDb |
|||
{ |
|||
/// <summary>
|
|||
/// Exposes a method allowing to resolve an authorization store.
|
|||
/// </summary>
|
|||
public class OpenIddictAuthorizationStoreResolver : IOpenIddictAuthorizationStoreResolver |
|||
{ |
|||
private readonly IServiceProvider _provider; |
|||
|
|||
public OpenIddictAuthorizationStoreResolver([NotNull] IServiceProvider provider) |
|||
{ |
|||
_provider = provider; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Returns an authorization store compatible with the specified authorization type or throws an
|
|||
/// <see cref="InvalidOperationException"/> if no store can be built using the specified type.
|
|||
/// </summary>
|
|||
/// <typeparam name="TAuthorization">The type of the Authorization entity.</typeparam>
|
|||
/// <returns>An <see cref="IOpenIddictAuthorizationStore{TAuthorization}"/>.</returns>
|
|||
public IOpenIddictAuthorizationStore<TAuthorization> Get<TAuthorization>() where TAuthorization : class |
|||
{ |
|||
var store = _provider.GetService<IOpenIddictAuthorizationStore<TAuthorization>>(); |
|||
if (store != null) |
|||
{ |
|||
return store; |
|||
} |
|||
|
|||
if (!typeof(OpenIddictAuthorization).IsAssignableFrom(typeof(TAuthorization))) |
|||
{ |
|||
throw new InvalidOperationException(new StringBuilder() |
|||
.AppendLine("The specified authorization type is not compatible with the MongoDB stores.") |
|||
.Append("When enabling the MongoDB stores, make sure you use the built-in 'OpenIddictAuthorization' ") |
|||
.Append("entity (from the 'OpenIddict.MongoDb.Models' package) or a custom entity ") |
|||
.Append("that inherits from the 'OpenIddictAuthorization' entity.") |
|||
.ToString()); |
|||
} |
|||
|
|||
return (IOpenIddictAuthorizationStore<TAuthorization>) _provider.GetRequiredService( |
|||
typeof(OpenIddictAuthorizationStore<>).MakeGenericType(typeof(TAuthorization))); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,57 @@ |
|||
/* |
|||
* 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; |
|||
using System.Reflection; |
|||
using System.Text; |
|||
using JetBrains.Annotations; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using OpenIddict.Abstractions; |
|||
using OpenIddict.MongoDb.Models; |
|||
|
|||
namespace OpenIddict.MongoDb |
|||
{ |
|||
/// <summary>
|
|||
/// Exposes a method allowing to resolve a scope store.
|
|||
/// </summary>
|
|||
public class OpenIddictScopeStoreResolver : IOpenIddictScopeStoreResolver |
|||
{ |
|||
private readonly IServiceProvider _provider; |
|||
|
|||
public OpenIddictScopeStoreResolver([NotNull] IServiceProvider provider) |
|||
{ |
|||
_provider = provider; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Returns a scope store compatible with the specified scope type or throws an
|
|||
/// <see cref="InvalidOperationException"/> if no store can be built using the specified type.
|
|||
/// </summary>
|
|||
/// <typeparam name="TScope">The type of the Scope entity.</typeparam>
|
|||
/// <returns>An <see cref="IOpenIddictScopeStore{TScope}"/>.</returns>
|
|||
public IOpenIddictScopeStore<TScope> Get<TScope>() where TScope : class |
|||
{ |
|||
var store = _provider.GetService<IOpenIddictScopeStore<TScope>>(); |
|||
if (store != null) |
|||
{ |
|||
return store; |
|||
} |
|||
|
|||
if (!typeof(OpenIddictScope).IsAssignableFrom(typeof(TScope))) |
|||
{ |
|||
throw new InvalidOperationException(new StringBuilder() |
|||
.AppendLine("The specified scope type is not compatible with the MongoDB stores.") |
|||
.Append("When enabling the MongoDB stores, make sure you use the built-in 'OpenIddictScope' ") |
|||
.Append("entity (from the 'OpenIddict.MongoDb.Models' package) or a custom entity ") |
|||
.Append("that inherits from the 'OpenIddictScope' entity.") |
|||
.ToString()); |
|||
} |
|||
|
|||
return (IOpenIddictScopeStore<TScope>) _provider.GetRequiredService( |
|||
typeof(OpenIddictScopeStore<>).MakeGenericType(typeof(TScope))); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,57 @@ |
|||
/* |
|||
* 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; |
|||
using System.Reflection; |
|||
using System.Text; |
|||
using JetBrains.Annotations; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using OpenIddict.Abstractions; |
|||
using OpenIddict.MongoDb.Models; |
|||
|
|||
namespace OpenIddict.MongoDb |
|||
{ |
|||
/// <summary>
|
|||
/// Exposes a method allowing to resolve a token store.
|
|||
/// </summary>
|
|||
public class OpenIddictTokenStoreResolver : IOpenIddictTokenStoreResolver |
|||
{ |
|||
private readonly IServiceProvider _provider; |
|||
|
|||
public OpenIddictTokenStoreResolver([NotNull] IServiceProvider provider) |
|||
{ |
|||
_provider = provider; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Returns a token store compatible with the specified token type or throws an
|
|||
/// <see cref="InvalidOperationException"/> if no store can be built using the specified type.
|
|||
/// </summary>
|
|||
/// <typeparam name="TToken">The type of the Token entity.</typeparam>
|
|||
/// <returns>An <see cref="IOpenIddictTokenStore{TToken}"/>.</returns>
|
|||
public IOpenIddictTokenStore<TToken> Get<TToken>() where TToken : class |
|||
{ |
|||
var store = _provider.GetService<IOpenIddictTokenStore<TToken>>(); |
|||
if (store != null) |
|||
{ |
|||
return store; |
|||
} |
|||
|
|||
if (!typeof(OpenIddictToken).IsAssignableFrom(typeof(TToken))) |
|||
{ |
|||
throw new InvalidOperationException(new StringBuilder() |
|||
.AppendLine("The specified token type is not compatible with the MongoDB stores.") |
|||
.Append("When enabling the MongoDB stores, make sure you use the built-in 'OpenIddictToken' ") |
|||
.Append("entity (from the 'OpenIddict.MongoDb.Models' package) or a custom entity ") |
|||
.Append("that inherits from the 'OpenIddictToken' entity.") |
|||
.ToString()); |
|||
} |
|||
|
|||
return (IOpenIddictTokenStore<TToken>) _provider.GetRequiredService( |
|||
typeof(OpenIddictTokenStore<>).MakeGenericType(typeof(TToken))); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,813 @@ |
|||
/* |
|||
* 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; |
|||
using System.Collections.Generic; |
|||
using System.Collections.Immutable; |
|||
using System.Linq; |
|||
using System.Text; |
|||
using System.Threading; |
|||
using System.Threading.Tasks; |
|||
using JetBrains.Annotations; |
|||
using Microsoft.Extensions.Caching.Memory; |
|||
using Microsoft.Extensions.Options; |
|||
using MongoDB.Bson; |
|||
using MongoDB.Driver; |
|||
using MongoDB.Driver.Linq; |
|||
using Newtonsoft.Json.Linq; |
|||
using OpenIddict.Abstractions; |
|||
using OpenIddict.MongoDb.Models; |
|||
|
|||
namespace OpenIddict.MongoDb |
|||
{ |
|||
/// <summary>
|
|||
/// Provides methods allowing to manage the applications stored in a database.
|
|||
/// </summary>
|
|||
/// <typeparam name="TApplication">The type of the Application entity.</typeparam>
|
|||
public class OpenIddictApplicationStore<TApplication> : IOpenIddictApplicationStore<TApplication> |
|||
where TApplication : OpenIddictApplication, new() |
|||
{ |
|||
public OpenIddictApplicationStore( |
|||
[NotNull] IMemoryCache cache, |
|||
[NotNull] IOpenIddictMongoDbContext context, |
|||
[NotNull] IOptionsMonitor<OpenIddictMongoDbOptions> options) |
|||
{ |
|||
Cache = cache; |
|||
Context = context; |
|||
Options = options; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the memory cached associated with the current store.
|
|||
/// </summary>
|
|||
protected IMemoryCache Cache { get; } |
|||
|
|||
/// <summary>
|
|||
/// Gets the database context associated with the current store.
|
|||
/// </summary>
|
|||
protected IOpenIddictMongoDbContext Context { get; } |
|||
|
|||
/// <summary>
|
|||
/// Gets the options associated with the current store.
|
|||
/// </summary>
|
|||
protected IOptionsMonitor<OpenIddictMongoDbOptions> Options { get; } |
|||
|
|||
/// <summary>
|
|||
/// Determines the number of applications that exist in the database.
|
|||
/// </summary>
|
|||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
|
|||
/// <returns>
|
|||
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation,
|
|||
/// whose result returns the number of applications in the database.
|
|||
/// </returns>
|
|||
public virtual async Task<long> CountAsync(CancellationToken cancellationToken) |
|||
{ |
|||
var database = await Context.GetDatabaseAsync(cancellationToken); |
|||
var collection = database.GetCollection<TApplication>(Options.CurrentValue.ApplicationsCollectionName); |
|||
|
|||
return await collection.CountAsync(FilterDefinition<TApplication>.Empty); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Determines the number of applications that match the specified query.
|
|||
/// </summary>
|
|||
/// <typeparam name="TResult">The result type.</typeparam>
|
|||
/// <param name="query">The query to execute.</param>
|
|||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
|
|||
/// <returns>
|
|||
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation,
|
|||
/// whose result returns the number of applications that match the specified query.
|
|||
/// </returns>
|
|||
public virtual async Task<long> CountAsync<TResult>([NotNull] Func<IQueryable<TApplication>, IQueryable<TResult>> query, CancellationToken cancellationToken) |
|||
{ |
|||
if (query == null) |
|||
{ |
|||
throw new ArgumentNullException(nameof(query)); |
|||
} |
|||
|
|||
var database = await Context.GetDatabaseAsync(cancellationToken); |
|||
var collection = database.GetCollection<TApplication>(Options.CurrentValue.ApplicationsCollectionName); |
|||
|
|||
return await ((IMongoQueryable<TApplication>) query(collection.AsQueryable())).LongCountAsync(); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Creates a new application.
|
|||
/// </summary>
|
|||
/// <param name="application">The application to create.</param>
|
|||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
|
|||
/// <returns>
|
|||
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation.
|
|||
/// </returns>
|
|||
public virtual async Task CreateAsync([NotNull] TApplication application, CancellationToken cancellationToken) |
|||
{ |
|||
if (application == null) |
|||
{ |
|||
throw new ArgumentNullException(nameof(application)); |
|||
} |
|||
|
|||
var database = await Context.GetDatabaseAsync(cancellationToken); |
|||
var collection = database.GetCollection<TApplication>(Options.CurrentValue.ApplicationsCollectionName); |
|||
|
|||
await collection.InsertOneAsync(application, null, cancellationToken); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Removes an existing application.
|
|||
/// </summary>
|
|||
/// <param name="application">The application to delete.</param>
|
|||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
|
|||
/// <returns>
|
|||
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation.
|
|||
/// </returns>
|
|||
public virtual async Task DeleteAsync([NotNull] TApplication application, CancellationToken cancellationToken) |
|||
{ |
|||
if (application == null) |
|||
{ |
|||
throw new ArgumentNullException(nameof(application)); |
|||
} |
|||
|
|||
var database = await Context.GetDatabaseAsync(cancellationToken); |
|||
var collection = database.GetCollection<TApplication>(Options.CurrentValue.ApplicationsCollectionName); |
|||
|
|||
if ((await collection.DeleteOneAsync(entity => |
|||
entity.Id == application.Id && |
|||
entity.ConcurrencyToken == application.ConcurrencyToken)).DeletedCount == 0) |
|||
{ |
|||
throw new OpenIddictException(OpenIddictConstants.Exceptions.ConcurrencyError, new StringBuilder() |
|||
.AppendLine("The application was concurrently updated and cannot be persisted in its current state.") |
|||
.Append("Reload the application from the database and retry the operation.") |
|||
.ToString()); |
|||
} |
|||
|
|||
// Delete the authorizations associated with the application.
|
|||
await database.GetCollection<OpenIddictAuthorization>(Options.CurrentValue.AuthorizationsCollectionName) |
|||
.DeleteManyAsync(authorization => authorization.ApplicationId == application.Id, cancellationToken); |
|||
|
|||
// Delete the tokens associated with the application.
|
|||
await database.GetCollection<OpenIddictToken>(Options.CurrentValue.TokensCollectionName) |
|||
.DeleteManyAsync(token => token.ApplicationId == application.Id, cancellationToken); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Retrieves an application using its client identifier.
|
|||
/// </summary>
|
|||
/// <param name="identifier">The client identifier associated with the application.</param>
|
|||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
|
|||
/// <returns>
|
|||
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation,
|
|||
/// whose result returns the client application corresponding to the identifier.
|
|||
/// </returns>
|
|||
public virtual async Task<TApplication> FindByClientIdAsync([NotNull] string identifier, CancellationToken cancellationToken) |
|||
{ |
|||
if (string.IsNullOrEmpty(identifier)) |
|||
{ |
|||
throw new ArgumentException("The identifier cannot be null or empty.", nameof(identifier)); |
|||
} |
|||
|
|||
var database = await Context.GetDatabaseAsync(cancellationToken); |
|||
var collection = database.GetCollection<TApplication>(Options.CurrentValue.ApplicationsCollectionName); |
|||
|
|||
return await collection.Find(application => application.ClientId == identifier).FirstOrDefaultAsync(cancellationToken); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Retrieves an application using its unique identifier.
|
|||
/// </summary>
|
|||
/// <param name="identifier">The unique identifier associated with the application.</param>
|
|||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
|
|||
/// <returns>
|
|||
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation,
|
|||
/// whose result returns the client application corresponding to the identifier.
|
|||
/// </returns>
|
|||
public virtual async Task<TApplication> FindByIdAsync([NotNull] string identifier, CancellationToken cancellationToken) |
|||
{ |
|||
if (string.IsNullOrEmpty(identifier)) |
|||
{ |
|||
throw new ArgumentException("The identifier cannot be null or empty.", nameof(identifier)); |
|||
} |
|||
|
|||
var database = await Context.GetDatabaseAsync(cancellationToken); |
|||
var collection = database.GetCollection<TApplication>(Options.CurrentValue.ApplicationsCollectionName); |
|||
|
|||
return await collection.Find(application => application.Id == ObjectId.Parse(identifier)).FirstOrDefaultAsync(cancellationToken); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Retrieves all the applications associated with the specified post_logout_redirect_uri.
|
|||
/// </summary>
|
|||
/// <param name="address">The post_logout_redirect_uri associated with the applications.</param>
|
|||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
|
|||
/// <returns>
|
|||
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation, whose result
|
|||
/// returns the client applications corresponding to the specified post_logout_redirect_uri.
|
|||
/// </returns>
|
|||
public virtual async Task<ImmutableArray<TApplication>> FindByPostLogoutRedirectUriAsync([NotNull] string address, CancellationToken cancellationToken) |
|||
{ |
|||
if (string.IsNullOrEmpty(address)) |
|||
{ |
|||
throw new ArgumentException("The address cannot be null or empty.", nameof(address)); |
|||
} |
|||
|
|||
var database = await Context.GetDatabaseAsync(cancellationToken); |
|||
var collection = database.GetCollection<TApplication>(Options.CurrentValue.ApplicationsCollectionName); |
|||
|
|||
return ImmutableArray.CreateRange(await collection.Find(application => |
|||
application.PostLogoutRedirectUris.Contains(address)).ToListAsync(cancellationToken)); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Retrieves all the applications associated with the specified redirect_uri.
|
|||
/// </summary>
|
|||
/// <param name="address">The redirect_uri associated with the applications.</param>
|
|||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
|
|||
/// <returns>
|
|||
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation, whose result
|
|||
/// returns the client applications corresponding to the specified redirect_uri.
|
|||
/// </returns>
|
|||
public virtual async Task<ImmutableArray<TApplication>> FindByRedirectUriAsync([NotNull] string address, CancellationToken cancellationToken) |
|||
{ |
|||
if (string.IsNullOrEmpty(address)) |
|||
{ |
|||
throw new ArgumentException("The address cannot be null or empty.", nameof(address)); |
|||
} |
|||
|
|||
var database = await Context.GetDatabaseAsync(cancellationToken); |
|||
var collection = database.GetCollection<TApplication>(Options.CurrentValue.ApplicationsCollectionName); |
|||
|
|||
return ImmutableArray.CreateRange(await collection.Find(application => |
|||
application.RedirectUris.Contains(address)).ToListAsync(cancellationToken)); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Executes the specified query and returns the first element.
|
|||
/// </summary>
|
|||
/// <typeparam name="TState">The state type.</typeparam>
|
|||
/// <typeparam name="TResult">The result type.</typeparam>
|
|||
/// <param name="query">The query to execute.</param>
|
|||
/// <param name="state">The optional state.</param>
|
|||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
|
|||
/// <returns>
|
|||
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation,
|
|||
/// whose result returns the first element returned when executing the query.
|
|||
/// </returns>
|
|||
public virtual async Task<TResult> GetAsync<TState, TResult>( |
|||
[NotNull] Func<IQueryable<TApplication>, TState, IQueryable<TResult>> query, |
|||
[CanBeNull] TState state, CancellationToken cancellationToken) |
|||
{ |
|||
if (query == null) |
|||
{ |
|||
throw new ArgumentNullException(nameof(query)); |
|||
} |
|||
|
|||
var database = await Context.GetDatabaseAsync(cancellationToken); |
|||
var collection = database.GetCollection<TApplication>(Options.CurrentValue.ApplicationsCollectionName); |
|||
|
|||
return await ((IMongoQueryable<TResult>) query(collection.AsQueryable(), state)).FirstOrDefaultAsync(cancellationToken); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Retrieves the client identifier associated with an application.
|
|||
/// </summary>
|
|||
/// <param name="application">The application.</param>
|
|||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
|
|||
/// <returns>
|
|||
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
|
|||
/// whose result returns the client identifier associated with the application.
|
|||
/// </returns>
|
|||
public virtual ValueTask<string> GetClientIdAsync([NotNull] TApplication application, CancellationToken cancellationToken) |
|||
{ |
|||
if (application == null) |
|||
{ |
|||
throw new ArgumentNullException(nameof(application)); |
|||
} |
|||
|
|||
return new ValueTask<string>(application.ClientId); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Retrieves the client secret associated with an application.
|
|||
/// Note: depending on the manager used to create the application,
|
|||
/// the client secret may be hashed for security reasons.
|
|||
/// </summary>
|
|||
/// <param name="application">The application.</param>
|
|||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
|
|||
/// <returns>
|
|||
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
|
|||
/// whose result returns the client secret associated with the application.
|
|||
/// </returns>
|
|||
public virtual ValueTask<string> GetClientSecretAsync([NotNull] TApplication application, CancellationToken cancellationToken) |
|||
{ |
|||
if (application == null) |
|||
{ |
|||
throw new ArgumentNullException(nameof(application)); |
|||
} |
|||
|
|||
return new ValueTask<string>(application.ClientSecret); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Retrieves the client type associated with an application.
|
|||
/// </summary>
|
|||
/// <param name="application">The application.</param>
|
|||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
|
|||
/// <returns>
|
|||
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
|
|||
/// whose result returns the client type of the application (by default, "public").
|
|||
/// </returns>
|
|||
public virtual ValueTask<string> GetClientTypeAsync([NotNull] TApplication application, CancellationToken cancellationToken) |
|||
{ |
|||
if (application == null) |
|||
{ |
|||
throw new ArgumentNullException(nameof(application)); |
|||
} |
|||
|
|||
return new ValueTask<string>(application.Type); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Retrieves the consent type associated with an application.
|
|||
/// </summary>
|
|||
/// <param name="application">The application.</param>
|
|||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
|
|||
/// <returns>
|
|||
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
|
|||
/// whose result returns the consent type of the application (by default, "explicit").
|
|||
/// </returns>
|
|||
public virtual ValueTask<string> GetConsentTypeAsync([NotNull] TApplication application, CancellationToken cancellationToken) |
|||
{ |
|||
if (application == null) |
|||
{ |
|||
throw new ArgumentNullException(nameof(application)); |
|||
} |
|||
|
|||
return new ValueTask<string>(application.ConsentType); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Retrieves the display name associated with an application.
|
|||
/// </summary>
|
|||
/// <param name="application">The application.</param>
|
|||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
|
|||
/// <returns>
|
|||
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
|
|||
/// whose result returns the display name associated with the application.
|
|||
/// </returns>
|
|||
public virtual ValueTask<string> GetDisplayNameAsync([NotNull] TApplication application, CancellationToken cancellationToken) |
|||
{ |
|||
if (application == null) |
|||
{ |
|||
throw new ArgumentNullException(nameof(application)); |
|||
} |
|||
|
|||
return new ValueTask<string>(application.DisplayName); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Retrieves the unique identifier associated with an application.
|
|||
/// </summary>
|
|||
/// <param name="application">The application.</param>
|
|||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
|
|||
/// <returns>
|
|||
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
|
|||
/// whose result returns the unique identifier associated with the application.
|
|||
/// </returns>
|
|||
public virtual ValueTask<string> GetIdAsync([NotNull] TApplication application, CancellationToken cancellationToken) |
|||
{ |
|||
if (application == null) |
|||
{ |
|||
throw new ArgumentNullException(nameof(application)); |
|||
} |
|||
|
|||
return new ValueTask<string>(application.Id.ToString()); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Retrieves the permissions associated with an application.
|
|||
/// </summary>
|
|||
/// <param name="application">The application.</param>
|
|||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
|
|||
/// <returns>
|
|||
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
|
|||
/// whose result returns all the permissions associated with the application.
|
|||
/// </returns>
|
|||
public virtual ValueTask<ImmutableArray<string>> GetPermissionsAsync([NotNull] TApplication application, CancellationToken cancellationToken) |
|||
{ |
|||
if (application == null) |
|||
{ |
|||
throw new ArgumentNullException(nameof(application)); |
|||
} |
|||
|
|||
if (application.Permissions == null || application.Permissions.Length == 0) |
|||
{ |
|||
return new ValueTask<ImmutableArray<string>>(ImmutableArray.Create<string>()); |
|||
} |
|||
|
|||
return new ValueTask<ImmutableArray<string>>(application.Permissions.ToImmutableArray()); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Retrieves the logout callback addresses associated with an application.
|
|||
/// </summary>
|
|||
/// <param name="application">The application.</param>
|
|||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
|
|||
/// <returns>
|
|||
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
|
|||
/// whose result returns all the post_logout_redirect_uri associated with the application.
|
|||
/// </returns>
|
|||
public virtual ValueTask<ImmutableArray<string>> GetPostLogoutRedirectUrisAsync([NotNull] TApplication application, CancellationToken cancellationToken) |
|||
{ |
|||
if (application == null) |
|||
{ |
|||
throw new ArgumentNullException(nameof(application)); |
|||
} |
|||
|
|||
if (application.PostLogoutRedirectUris == null || application.PostLogoutRedirectUris.Length == 0) |
|||
{ |
|||
return new ValueTask<ImmutableArray<string>>(ImmutableArray.Create<string>()); |
|||
} |
|||
|
|||
return new ValueTask<ImmutableArray<string>>(application.PostLogoutRedirectUris.ToImmutableArray()); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Retrieves the additional properties associated with an application.
|
|||
/// </summary>
|
|||
/// <param name="application">The application.</param>
|
|||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
|
|||
/// <returns>
|
|||
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
|
|||
/// whose result returns all the additional properties associated with the application.
|
|||
/// </returns>
|
|||
public virtual ValueTask<JObject> GetPropertiesAsync([NotNull] TApplication application, CancellationToken cancellationToken) |
|||
{ |
|||
if (application == null) |
|||
{ |
|||
throw new ArgumentNullException(nameof(application)); |
|||
} |
|||
|
|||
if (application.Properties == null) |
|||
{ |
|||
return new ValueTask<JObject>(new JObject()); |
|||
} |
|||
|
|||
return new ValueTask<JObject>(JObject.FromObject(application.Properties.ToDictionary())); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Retrieves the callback addresses associated with an application.
|
|||
/// </summary>
|
|||
/// <param name="application">The application.</param>
|
|||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
|
|||
/// <returns>
|
|||
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
|
|||
/// whose result returns all the redirect_uri associated with the application.
|
|||
/// </returns>
|
|||
public virtual ValueTask<ImmutableArray<string>> GetRedirectUrisAsync([NotNull] TApplication application, CancellationToken cancellationToken) |
|||
{ |
|||
if (application == null) |
|||
{ |
|||
throw new ArgumentNullException(nameof(application)); |
|||
} |
|||
|
|||
if (application.RedirectUris == null || application.RedirectUris.Length == 0) |
|||
{ |
|||
return new ValueTask<ImmutableArray<string>>(ImmutableArray.Create<string>()); |
|||
} |
|||
|
|||
return new ValueTask<ImmutableArray<string>>(application.RedirectUris.ToImmutableArray()); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Instantiates a new application.
|
|||
/// </summary>
|
|||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
|
|||
/// <returns>
|
|||
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
|
|||
/// whose result returns the instantiated application, that can be persisted in the database.
|
|||
/// </returns>
|
|||
public virtual ValueTask<TApplication> InstantiateAsync(CancellationToken cancellationToken) |
|||
=> new ValueTask<TApplication>(new TApplication()); |
|||
|
|||
/// <summary>
|
|||
/// Executes the specified query and returns all the corresponding elements.
|
|||
/// </summary>
|
|||
/// <param name="count">The number of results to return.</param>
|
|||
/// <param name="offset">The number of results to skip.</param>
|
|||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
|
|||
/// <returns>
|
|||
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation,
|
|||
/// whose result returns all the elements returned when executing the specified query.
|
|||
/// </returns>
|
|||
public virtual async Task<ImmutableArray<TApplication>> ListAsync( |
|||
[CanBeNull] int? count, [CanBeNull] int? offset, CancellationToken cancellationToken) |
|||
{ |
|||
var database = await Context.GetDatabaseAsync(cancellationToken); |
|||
var collection = database.GetCollection<TApplication>(Options.CurrentValue.ApplicationsCollectionName); |
|||
|
|||
var query = (IMongoQueryable<TApplication>) collection.AsQueryable().OrderBy(application => application.Id); |
|||
|
|||
if (offset.HasValue) |
|||
{ |
|||
query = query.Skip(offset.Value); |
|||
} |
|||
|
|||
if (count.HasValue) |
|||
{ |
|||
query = query.Take(count.Value); |
|||
} |
|||
|
|||
return ImmutableArray.CreateRange(await query.ToListAsync(cancellationToken)); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Executes the specified query and returns all the corresponding elements.
|
|||
/// </summary>
|
|||
/// <typeparam name="TState">The state type.</typeparam>
|
|||
/// <typeparam name="TResult">The result type.</typeparam>
|
|||
/// <param name="query">The query to execute.</param>
|
|||
/// <param name="state">The optional state.</param>
|
|||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
|
|||
/// <returns>
|
|||
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation,
|
|||
/// whose result returns all the elements returned when executing the specified query.
|
|||
/// </returns>
|
|||
public virtual async Task<ImmutableArray<TResult>> ListAsync<TState, TResult>( |
|||
[NotNull] Func<IQueryable<TApplication>, TState, IQueryable<TResult>> query, |
|||
[CanBeNull] TState state, CancellationToken cancellationToken) |
|||
{ |
|||
if (query == null) |
|||
{ |
|||
throw new ArgumentNullException(nameof(query)); |
|||
} |
|||
|
|||
var database = await Context.GetDatabaseAsync(cancellationToken); |
|||
var collection = database.GetCollection<TApplication>(Options.CurrentValue.ApplicationsCollectionName); |
|||
|
|||
return ImmutableArray.CreateRange( |
|||
await ((IMongoQueryable<TResult>) query(collection.AsQueryable(), state)).ToListAsync(cancellationToken)); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Sets the client identifier associated with an application.
|
|||
/// </summary>
|
|||
/// <param name="application">The application.</param>
|
|||
/// <param name="identifier">The client identifier associated with the application.</param>
|
|||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
|
|||
/// <returns>
|
|||
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation.
|
|||
/// </returns>
|
|||
public virtual Task SetClientIdAsync([NotNull] TApplication application, |
|||
[CanBeNull] string identifier, CancellationToken cancellationToken) |
|||
{ |
|||
if (application == null) |
|||
{ |
|||
throw new ArgumentNullException(nameof(application)); |
|||
} |
|||
|
|||
application.ClientId = identifier; |
|||
|
|||
return Task.FromResult(0); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Sets the client secret associated with an application.
|
|||
/// Note: depending on the manager used to create the application,
|
|||
/// the client secret may be hashed for security reasons.
|
|||
/// </summary>
|
|||
/// <param name="application">The application.</param>
|
|||
/// <param name="secret">The client secret associated with the application.</param>
|
|||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
|
|||
/// <returns>
|
|||
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation.
|
|||
/// </returns>
|
|||
public virtual Task SetClientSecretAsync([NotNull] TApplication application, |
|||
[CanBeNull] string secret, CancellationToken cancellationToken) |
|||
{ |
|||
if (application == null) |
|||
{ |
|||
throw new ArgumentNullException(nameof(application)); |
|||
} |
|||
|
|||
application.ClientSecret = secret; |
|||
|
|||
return Task.FromResult(0); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Sets the client type associated with an application.
|
|||
/// </summary>
|
|||
/// <param name="application">The application.</param>
|
|||
/// <param name="type">The client type associated with the application.</param>
|
|||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
|
|||
/// <returns>
|
|||
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation.
|
|||
/// </returns>
|
|||
public virtual Task SetClientTypeAsync([NotNull] TApplication application, |
|||
[CanBeNull] string type, CancellationToken cancellationToken) |
|||
{ |
|||
if (application == null) |
|||
{ |
|||
throw new ArgumentNullException(nameof(application)); |
|||
} |
|||
|
|||
application.Type = type; |
|||
|
|||
return Task.FromResult(0); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Sets the consent type associated with an application.
|
|||
/// </summary>
|
|||
/// <param name="application">The application.</param>
|
|||
/// <param name="type">The consent type associated with the application.</param>
|
|||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
|
|||
/// <returns>
|
|||
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation.
|
|||
/// </returns>
|
|||
public virtual Task SetConsentTypeAsync([NotNull] TApplication application, |
|||
[CanBeNull] string type, CancellationToken cancellationToken) |
|||
{ |
|||
if (application == null) |
|||
{ |
|||
throw new ArgumentNullException(nameof(application)); |
|||
} |
|||
|
|||
application.ConsentType = type; |
|||
|
|||
return Task.FromResult(0); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Sets the display name associated with an application.
|
|||
/// </summary>
|
|||
/// <param name="application">The application.</param>
|
|||
/// <param name="name">The display name associated with the application.</param>
|
|||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
|
|||
/// <returns>
|
|||
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation.
|
|||
/// </returns>
|
|||
public virtual Task SetDisplayNameAsync([NotNull] TApplication application, |
|||
[CanBeNull] string name, CancellationToken cancellationToken) |
|||
{ |
|||
if (application == null) |
|||
{ |
|||
throw new ArgumentNullException(nameof(application)); |
|||
} |
|||
|
|||
application.DisplayName = name; |
|||
|
|||
return Task.FromResult(0); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Sets the permissions associated with an application.
|
|||
/// </summary>
|
|||
/// <param name="application">The application.</param>
|
|||
/// <param name="permissions">The permissions associated with the application </param>
|
|||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
|
|||
/// <returns>
|
|||
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation.
|
|||
/// </returns>
|
|||
public virtual Task SetPermissionsAsync([NotNull] TApplication application, ImmutableArray<string> permissions, CancellationToken cancellationToken) |
|||
{ |
|||
if (application == null) |
|||
{ |
|||
throw new ArgumentNullException(nameof(application)); |
|||
} |
|||
|
|||
if (permissions.IsDefaultOrEmpty) |
|||
{ |
|||
application.Permissions = null; |
|||
|
|||
return Task.FromResult(0); |
|||
} |
|||
|
|||
application.Permissions = permissions.ToArray(); |
|||
|
|||
return Task.FromResult(0); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Sets the logout callback addresses associated with an application.
|
|||
/// </summary>
|
|||
/// <param name="application">The application.</param>
|
|||
/// <param name="addresses">The logout callback addresses associated with the application </param>
|
|||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
|
|||
/// <returns>
|
|||
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation.
|
|||
/// </returns>
|
|||
public virtual Task SetPostLogoutRedirectUrisAsync([NotNull] TApplication application, |
|||
ImmutableArray<string> addresses, CancellationToken cancellationToken) |
|||
{ |
|||
if (application == null) |
|||
{ |
|||
throw new ArgumentNullException(nameof(application)); |
|||
} |
|||
|
|||
if (addresses.IsDefaultOrEmpty) |
|||
{ |
|||
application.PostLogoutRedirectUris = null; |
|||
|
|||
return Task.FromResult(0); |
|||
} |
|||
|
|||
application.PostLogoutRedirectUris = addresses.ToArray(); |
|||
|
|||
return Task.FromResult(0); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Sets the additional properties associated with an application.
|
|||
/// </summary>
|
|||
/// <param name="application">The application.</param>
|
|||
/// <param name="properties">The additional properties associated with the application.</param>
|
|||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
|
|||
/// <returns>
|
|||
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation.
|
|||
/// </returns>
|
|||
public virtual Task SetPropertiesAsync([NotNull] TApplication application, [CanBeNull] JObject properties, CancellationToken cancellationToken) |
|||
{ |
|||
if (application == null) |
|||
{ |
|||
throw new ArgumentNullException(nameof(application)); |
|||
} |
|||
|
|||
if (properties == null) |
|||
{ |
|||
application.Properties = null; |
|||
|
|||
return Task.FromResult(0); |
|||
} |
|||
|
|||
application.Properties = new BsonDocument(properties.ToObject<IDictionary<string, object>>()); |
|||
|
|||
return Task.FromResult(0); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Sets the callback addresses associated with an application.
|
|||
/// </summary>
|
|||
/// <param name="application">The application.</param>
|
|||
/// <param name="addresses">The callback addresses associated with the application </param>
|
|||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
|
|||
/// <returns>
|
|||
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation.
|
|||
/// </returns>
|
|||
public virtual Task SetRedirectUrisAsync([NotNull] TApplication application, |
|||
ImmutableArray<string> addresses, CancellationToken cancellationToken) |
|||
{ |
|||
if (application == null) |
|||
{ |
|||
throw new ArgumentNullException(nameof(application)); |
|||
} |
|||
|
|||
if (addresses.IsDefaultOrEmpty) |
|||
{ |
|||
application.RedirectUris = null; |
|||
|
|||
return Task.FromResult(0); |
|||
} |
|||
|
|||
application.RedirectUris = addresses.ToArray(); |
|||
|
|||
return Task.FromResult(0); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Updates an existing application.
|
|||
/// </summary>
|
|||
/// <param name="application">The application to update.</param>
|
|||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
|
|||
/// <returns>
|
|||
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation.
|
|||
/// </returns>
|
|||
public virtual async Task UpdateAsync([NotNull] TApplication application, CancellationToken cancellationToken) |
|||
{ |
|||
if (application == null) |
|||
{ |
|||
throw new ArgumentNullException(nameof(application)); |
|||
} |
|||
|
|||
// Generate a new concurrency token and attach it
|
|||
// to the application before persisting the changes.
|
|||
application.ConcurrencyToken = Guid.NewGuid().ToString(); |
|||
|
|||
var database = await Context.GetDatabaseAsync(cancellationToken); |
|||
var collection = database.GetCollection<TApplication>(Options.CurrentValue.ApplicationsCollectionName); |
|||
|
|||
if ((await collection.ReplaceOneAsync(entity => |
|||
entity.Id == application.Id && |
|||
entity.ConcurrencyToken == application.ConcurrencyToken, application, null, cancellationToken)).MatchedCount == 0) |
|||
{ |
|||
throw new OpenIddictException(OpenIddictConstants.Exceptions.ConcurrencyError, new StringBuilder() |
|||
.AppendLine("The application was concurrently updated and cannot be persisted in its current state.") |
|||
.Append("Reload the application from the database and retry the operation.") |
|||
.ToString()); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,764 @@ |
|||
/* |
|||
* 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; |
|||
using System.Collections.Generic; |
|||
using System.Collections.Immutable; |
|||
using System.Linq; |
|||
using System.Text; |
|||
using System.Threading; |
|||
using System.Threading.Tasks; |
|||
using JetBrains.Annotations; |
|||
using Microsoft.Extensions.Caching.Memory; |
|||
using Microsoft.Extensions.Options; |
|||
using MongoDB.Bson; |
|||
using MongoDB.Driver; |
|||
using MongoDB.Driver.Linq; |
|||
using Newtonsoft.Json.Linq; |
|||
using OpenIddict.Abstractions; |
|||
using OpenIddict.MongoDb.Models; |
|||
|
|||
namespace OpenIddict.MongoDb |
|||
{ |
|||
/// <summary>
|
|||
/// Provides methods allowing to manage the authorizations stored in a database.
|
|||
/// </summary>
|
|||
/// <typeparam name="TAuthorization">The type of the Authorization entity.</typeparam>
|
|||
public class OpenIddictAuthorizationStore<TAuthorization> : IOpenIddictAuthorizationStore<TAuthorization> |
|||
where TAuthorization : OpenIddictAuthorization, new() |
|||
{ |
|||
public OpenIddictAuthorizationStore( |
|||
[NotNull] IMemoryCache cache, |
|||
[NotNull] IOpenIddictMongoDbContext context, |
|||
[NotNull] IOptionsMonitor<OpenIddictMongoDbOptions> options) |
|||
{ |
|||
Cache = cache; |
|||
Context = context; |
|||
Options = options; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the memory cached associated with the current store.
|
|||
/// </summary>
|
|||
protected IMemoryCache Cache { get; } |
|||
|
|||
/// <summary>
|
|||
/// Gets the database context associated with the current store.
|
|||
/// </summary>
|
|||
protected IOpenIddictMongoDbContext Context { get; } |
|||
|
|||
/// <summary>
|
|||
/// Gets the options associated with the current store.
|
|||
/// </summary>
|
|||
protected IOptionsMonitor<OpenIddictMongoDbOptions> Options { get; } |
|||
|
|||
/// <summary>
|
|||
/// Determines the number of authorizations that exist in the database.
|
|||
/// </summary>
|
|||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
|
|||
/// <returns>
|
|||
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation,
|
|||
/// whose result returns the number of authorizations in the database.
|
|||
/// </returns>
|
|||
public virtual async Task<long> CountAsync(CancellationToken cancellationToken) |
|||
{ |
|||
var database = await Context.GetDatabaseAsync(cancellationToken); |
|||
var collection = database.GetCollection<TAuthorization>(Options.CurrentValue.AuthorizationsCollectionName); |
|||
|
|||
return await collection.CountAsync(FilterDefinition<TAuthorization>.Empty); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Determines the number of authorizations that match the specified query.
|
|||
/// </summary>
|
|||
/// <typeparam name="TResult">The result type.</typeparam>
|
|||
/// <param name="query">The query to execute.</param>
|
|||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
|
|||
/// <returns>
|
|||
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation,
|
|||
/// whose result returns the number of authorizations that match the specified query.
|
|||
/// </returns>
|
|||
public virtual async Task<long> CountAsync<TResult>([NotNull] Func<IQueryable<TAuthorization>, IQueryable<TResult>> query, CancellationToken cancellationToken) |
|||
{ |
|||
if (query == null) |
|||
{ |
|||
throw new ArgumentNullException(nameof(query)); |
|||
} |
|||
|
|||
var database = await Context.GetDatabaseAsync(cancellationToken); |
|||
var collection = database.GetCollection<TAuthorization>(Options.CurrentValue.AuthorizationsCollectionName); |
|||
|
|||
return await ((IMongoQueryable<TAuthorization>) query(collection.AsQueryable())).LongCountAsync(); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Creates a new authorization.
|
|||
/// </summary>
|
|||
/// <param name="authorization">The authorization to create.</param>
|
|||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
|
|||
/// <returns>
|
|||
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation.
|
|||
/// </returns>
|
|||
public virtual async Task CreateAsync([NotNull] TAuthorization authorization, CancellationToken cancellationToken) |
|||
{ |
|||
if (authorization == null) |
|||
{ |
|||
throw new ArgumentNullException(nameof(authorization)); |
|||
} |
|||
|
|||
var database = await Context.GetDatabaseAsync(cancellationToken); |
|||
var collection = database.GetCollection<TAuthorization>(Options.CurrentValue.AuthorizationsCollectionName); |
|||
|
|||
await collection.InsertOneAsync(authorization, null, cancellationToken); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Removes an existing authorization.
|
|||
/// </summary>
|
|||
/// <param name="authorization">The authorization to delete.</param>
|
|||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
|
|||
/// <returns>
|
|||
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation.
|
|||
/// </returns>
|
|||
public virtual async Task DeleteAsync([NotNull] TAuthorization authorization, CancellationToken cancellationToken) |
|||
{ |
|||
if (authorization == null) |
|||
{ |
|||
throw new ArgumentNullException(nameof(authorization)); |
|||
} |
|||
|
|||
var database = await Context.GetDatabaseAsync(cancellationToken); |
|||
var collection = database.GetCollection<TAuthorization>(Options.CurrentValue.AuthorizationsCollectionName); |
|||
|
|||
if ((await collection.DeleteOneAsync(entity => |
|||
entity.Id == authorization.Id && |
|||
entity.ConcurrencyToken == authorization.ConcurrencyToken)).DeletedCount == 0) |
|||
{ |
|||
throw new OpenIddictException(OpenIddictConstants.Exceptions.ConcurrencyError, new StringBuilder() |
|||
.AppendLine("The authorization was concurrently updated and cannot be persisted in its current state.") |
|||
.Append("Reload the authorization from the database and retry the operation.") |
|||
.ToString()); |
|||
} |
|||
|
|||
// Delete the tokens associated with the authorization.
|
|||
await database.GetCollection<OpenIddictToken>(Options.CurrentValue.TokensCollectionName) |
|||
.DeleteManyAsync(token => token.AuthorizationId == authorization.Id, cancellationToken); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Retrieves the authorizations corresponding to the specified
|
|||
/// subject and associated with the application identifier.
|
|||
/// </summary>
|
|||
/// <param name="subject">The subject associated with the authorization.</param>
|
|||
/// <param name="client">The client associated with the authorization.</param>
|
|||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
|
|||
/// <returns>
|
|||
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation,
|
|||
/// whose result returns the authorizations corresponding to the subject/client.
|
|||
/// </returns>
|
|||
public virtual async Task<ImmutableArray<TAuthorization>> FindAsync( |
|||
[NotNull] string subject, [NotNull] string client, CancellationToken cancellationToken) |
|||
{ |
|||
if (string.IsNullOrEmpty(subject)) |
|||
{ |
|||
throw new ArgumentException("The subject cannot be null or empty.", nameof(subject)); |
|||
} |
|||
|
|||
if (string.IsNullOrEmpty(client)) |
|||
{ |
|||
throw new ArgumentException("The client cannot be null or empty.", nameof(client)); |
|||
} |
|||
|
|||
var database = await Context.GetDatabaseAsync(cancellationToken); |
|||
var collection = database.GetCollection<TAuthorization>(Options.CurrentValue.AuthorizationsCollectionName); |
|||
|
|||
return ImmutableArray.CreateRange(await collection.Find(authorization => |
|||
authorization.Subject == subject && |
|||
authorization.ApplicationId == ObjectId.Parse(client)).ToListAsync(cancellationToken)); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Retrieves the authorizations matching the specified parameters.
|
|||
/// </summary>
|
|||
/// <param name="subject">The subject associated with the authorization.</param>
|
|||
/// <param name="client">The client associated with the authorization.</param>
|
|||
/// <param name="status">The authorization status.</param>
|
|||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
|
|||
/// <returns>
|
|||
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation,
|
|||
/// whose result returns the authorizations corresponding to the criteria.
|
|||
/// </returns>
|
|||
public virtual async Task<ImmutableArray<TAuthorization>> FindAsync( |
|||
[NotNull] string subject, [NotNull] string client, |
|||
[NotNull] string status, CancellationToken cancellationToken) |
|||
{ |
|||
if (string.IsNullOrEmpty(subject)) |
|||
{ |
|||
throw new ArgumentException("The subject cannot be null or empty.", nameof(subject)); |
|||
} |
|||
|
|||
if (string.IsNullOrEmpty(client)) |
|||
{ |
|||
throw new ArgumentException("The client identifier cannot be null or empty.", nameof(client)); |
|||
} |
|||
|
|||
if (string.IsNullOrEmpty(status)) |
|||
{ |
|||
throw new ArgumentException("The status cannot be null or empty.", nameof(status)); |
|||
} |
|||
|
|||
var database = await Context.GetDatabaseAsync(cancellationToken); |
|||
var collection = database.GetCollection<TAuthorization>(Options.CurrentValue.AuthorizationsCollectionName); |
|||
|
|||
return ImmutableArray.CreateRange(await collection.Find(authorization => |
|||
authorization.Subject == subject && |
|||
authorization.ApplicationId == ObjectId.Parse(client) && |
|||
authorization.Status == status).ToListAsync(cancellationToken)); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Retrieves the authorizations matching the specified parameters.
|
|||
/// </summary>
|
|||
/// <param name="subject">The subject associated with the authorization.</param>
|
|||
/// <param name="client">The client associated with the authorization.</param>
|
|||
/// <param name="status">The authorization status.</param>
|
|||
/// <param name="type">The authorization type.</param>
|
|||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
|
|||
/// <returns>
|
|||
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation,
|
|||
/// whose result returns the authorizations corresponding to the criteria.
|
|||
/// </returns>
|
|||
public virtual async Task<ImmutableArray<TAuthorization>> FindAsync( |
|||
[NotNull] string subject, [NotNull] string client, |
|||
[NotNull] string status, [NotNull] string type, CancellationToken cancellationToken) |
|||
{ |
|||
if (string.IsNullOrEmpty(subject)) |
|||
{ |
|||
throw new ArgumentException("The subject cannot be null or empty.", nameof(subject)); |
|||
} |
|||
|
|||
if (string.IsNullOrEmpty(client)) |
|||
{ |
|||
throw new ArgumentException("The client identifier cannot be null or empty.", nameof(client)); |
|||
} |
|||
|
|||
if (string.IsNullOrEmpty(status)) |
|||
{ |
|||
throw new ArgumentException("The status cannot be null or empty.", nameof(status)); |
|||
} |
|||
|
|||
if (string.IsNullOrEmpty(type)) |
|||
{ |
|||
throw new ArgumentException("The type cannot be null or empty.", nameof(type)); |
|||
} |
|||
|
|||
var database = await Context.GetDatabaseAsync(cancellationToken); |
|||
var collection = database.GetCollection<TAuthorization>(Options.CurrentValue.AuthorizationsCollectionName); |
|||
|
|||
return ImmutableArray.CreateRange(await collection.Find(authorization => |
|||
authorization.Subject == subject && |
|||
authorization.ApplicationId == ObjectId.Parse(client) && |
|||
authorization.Status == status && |
|||
authorization.Type == type).ToListAsync(cancellationToken)); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Retrieves an authorization using its unique identifier.
|
|||
/// </summary>
|
|||
/// <param name="identifier">The unique identifier associated with the authorization.</param>
|
|||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
|
|||
/// <returns>
|
|||
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation,
|
|||
/// whose result returns the authorization corresponding to the identifier.
|
|||
/// </returns>
|
|||
public virtual async Task<TAuthorization> FindByIdAsync([NotNull] string identifier, CancellationToken cancellationToken) |
|||
{ |
|||
if (string.IsNullOrEmpty(identifier)) |
|||
{ |
|||
throw new ArgumentException("The identifier cannot be null or empty.", nameof(identifier)); |
|||
} |
|||
|
|||
var database = await Context.GetDatabaseAsync(cancellationToken); |
|||
var collection = database.GetCollection<TAuthorization>(Options.CurrentValue.AuthorizationsCollectionName); |
|||
|
|||
return await collection.Find(authorization => authorization.Id == ObjectId.Parse(identifier)).FirstOrDefaultAsync(cancellationToken); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Retrieves all the authorizations corresponding to the specified subject.
|
|||
/// </summary>
|
|||
/// <param name="subject">The subject associated with the authorization.</param>
|
|||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
|
|||
/// <returns>
|
|||
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation,
|
|||
/// whose result returns the authorizations corresponding to the specified subject.
|
|||
/// </returns>
|
|||
public virtual async Task<ImmutableArray<TAuthorization>> FindBySubjectAsync( |
|||
[NotNull] string subject, CancellationToken cancellationToken) |
|||
{ |
|||
if (string.IsNullOrEmpty(subject)) |
|||
{ |
|||
throw new ArgumentException("The subject cannot be null or empty.", nameof(subject)); |
|||
} |
|||
|
|||
var database = await Context.GetDatabaseAsync(cancellationToken); |
|||
var collection = database.GetCollection<TAuthorization>(Options.CurrentValue.AuthorizationsCollectionName); |
|||
|
|||
return ImmutableArray.CreateRange(await collection.Find(authorization => authorization.Subject == subject).ToListAsync(cancellationToken)); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Retrieves the optional application identifier associated with an authorization.
|
|||
/// </summary>
|
|||
/// <param name="authorization">The authorization.</param>
|
|||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
|
|||
/// <returns>
|
|||
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
|
|||
/// whose result returns the application identifier associated with the authorization.
|
|||
/// </returns>
|
|||
public virtual ValueTask<string> GetApplicationIdAsync([NotNull] TAuthorization authorization, CancellationToken cancellationToken) |
|||
{ |
|||
if (authorization == null) |
|||
{ |
|||
throw new ArgumentNullException(nameof(authorization)); |
|||
} |
|||
|
|||
return new ValueTask<string>(authorization.ApplicationId.ToString()); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Executes the specified query and returns the first element.
|
|||
/// </summary>
|
|||
/// <typeparam name="TState">The state type.</typeparam>
|
|||
/// <typeparam name="TResult">The result type.</typeparam>
|
|||
/// <param name="query">The query to execute.</param>
|
|||
/// <param name="state">The optional state.</param>
|
|||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
|
|||
/// <returns>
|
|||
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation,
|
|||
/// whose result returns the first element returned when executing the query.
|
|||
/// </returns>
|
|||
public virtual async Task<TResult> GetAsync<TState, TResult>( |
|||
[NotNull] Func<IQueryable<TAuthorization>, TState, IQueryable<TResult>> query, |
|||
[CanBeNull] TState state, CancellationToken cancellationToken) |
|||
{ |
|||
if (query == null) |
|||
{ |
|||
throw new ArgumentNullException(nameof(query)); |
|||
} |
|||
|
|||
var database = await Context.GetDatabaseAsync(cancellationToken); |
|||
var collection = database.GetCollection<TAuthorization>(Options.CurrentValue.AuthorizationsCollectionName); |
|||
|
|||
return await ((IMongoQueryable<TResult>) query(collection.AsQueryable(), state)).FirstOrDefaultAsync(cancellationToken); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Retrieves the unique identifier associated with an authorization.
|
|||
/// </summary>
|
|||
/// <param name="authorization">The authorization.</param>
|
|||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
|
|||
/// <returns>
|
|||
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
|
|||
/// whose result returns the unique identifier associated with the authorization.
|
|||
/// </returns>
|
|||
public virtual ValueTask<string> GetIdAsync([NotNull] TAuthorization authorization, CancellationToken cancellationToken) |
|||
{ |
|||
if (authorization == null) |
|||
{ |
|||
throw new ArgumentNullException(nameof(authorization)); |
|||
} |
|||
|
|||
return new ValueTask<string>(authorization.Id.ToString()); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Retrieves the additional properties associated with an authorization.
|
|||
/// </summary>
|
|||
/// <param name="authorization">The authorization.</param>
|
|||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
|
|||
/// <returns>
|
|||
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
|
|||
/// whose result returns all the additional properties associated with the authorization.
|
|||
/// </returns>
|
|||
public virtual ValueTask<JObject> GetPropertiesAsync([NotNull] TAuthorization authorization, CancellationToken cancellationToken) |
|||
{ |
|||
if (authorization == null) |
|||
{ |
|||
throw new ArgumentNullException(nameof(authorization)); |
|||
} |
|||
|
|||
if (authorization.Properties == null) |
|||
{ |
|||
return new ValueTask<JObject>(new JObject()); |
|||
} |
|||
|
|||
return new ValueTask<JObject>(JObject.FromObject(authorization.Properties.ToDictionary())); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Retrieves the scopes associated with an authorization.
|
|||
/// </summary>
|
|||
/// <param name="authorization">The authorization.</param>
|
|||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
|
|||
/// <returns>
|
|||
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
|
|||
/// whose result returns the scopes associated with the specified authorization.
|
|||
/// </returns>
|
|||
public virtual ValueTask<ImmutableArray<string>> GetScopesAsync([NotNull] TAuthorization authorization, CancellationToken cancellationToken) |
|||
{ |
|||
if (authorization == null) |
|||
{ |
|||
throw new ArgumentNullException(nameof(authorization)); |
|||
} |
|||
|
|||
if (authorization.Scopes == null || authorization.Scopes.Length == 0) |
|||
{ |
|||
return new ValueTask<ImmutableArray<string>>(ImmutableArray.Create<string>()); |
|||
} |
|||
|
|||
return new ValueTask<ImmutableArray<string>>(authorization.Scopes.ToImmutableArray()); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Retrieves the status associated with an authorization.
|
|||
/// </summary>
|
|||
/// <param name="authorization">The authorization.</param>
|
|||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
|
|||
/// <returns>
|
|||
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
|
|||
/// whose result returns the status associated with the specified authorization.
|
|||
/// </returns>
|
|||
public virtual ValueTask<string> GetStatusAsync([NotNull] TAuthorization authorization, CancellationToken cancellationToken) |
|||
{ |
|||
if (authorization == null) |
|||
{ |
|||
throw new ArgumentNullException(nameof(authorization)); |
|||
} |
|||
|
|||
return new ValueTask<string>(authorization.Status); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Retrieves the subject associated with an authorization.
|
|||
/// </summary>
|
|||
/// <param name="authorization">The authorization.</param>
|
|||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
|
|||
/// <returns>
|
|||
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
|
|||
/// whose result returns the subject associated with the specified authorization.
|
|||
/// </returns>
|
|||
public virtual ValueTask<string> GetSubjectAsync([NotNull] TAuthorization authorization, CancellationToken cancellationToken) |
|||
{ |
|||
if (authorization == null) |
|||
{ |
|||
throw new ArgumentNullException(nameof(authorization)); |
|||
} |
|||
|
|||
return new ValueTask<string>(authorization.Subject); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Retrieves the type associated with an authorization.
|
|||
/// </summary>
|
|||
/// <param name="authorization">The authorization.</param>
|
|||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
|
|||
/// <returns>
|
|||
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
|
|||
/// whose result returns the type associated with the specified authorization.
|
|||
/// </returns>
|
|||
public virtual ValueTask<string> GetTypeAsync([NotNull] TAuthorization authorization, CancellationToken cancellationToken) |
|||
{ |
|||
if (authorization == null) |
|||
{ |
|||
throw new ArgumentNullException(nameof(authorization)); |
|||
} |
|||
|
|||
return new ValueTask<string>(authorization.Type); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Instantiates a new authorization.
|
|||
/// </summary>
|
|||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
|
|||
/// <returns>
|
|||
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
|
|||
/// whose result returns the instantiated authorization, that can be persisted in the database.
|
|||
/// </returns>
|
|||
public virtual ValueTask<TAuthorization> InstantiateAsync(CancellationToken cancellationToken) |
|||
=> new ValueTask<TAuthorization>(new TAuthorization()); |
|||
|
|||
/// <summary>
|
|||
/// Executes the specified query and returns all the corresponding elements.
|
|||
/// </summary>
|
|||
/// <param name="count">The number of results to return.</param>
|
|||
/// <param name="offset">The number of results to skip.</param>
|
|||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
|
|||
/// <returns>
|
|||
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation,
|
|||
/// whose result returns all the elements returned when executing the specified query.
|
|||
/// </returns>
|
|||
public virtual async Task<ImmutableArray<TAuthorization>> ListAsync( |
|||
[CanBeNull] int? count, [CanBeNull] int? offset, CancellationToken cancellationToken) |
|||
{ |
|||
var database = await Context.GetDatabaseAsync(cancellationToken); |
|||
var collection = database.GetCollection<TAuthorization>(Options.CurrentValue.AuthorizationsCollectionName); |
|||
|
|||
var query = (IMongoQueryable<TAuthorization>) collection.AsQueryable().OrderBy(authorization => authorization.Id); |
|||
|
|||
if (offset.HasValue) |
|||
{ |
|||
query = query.Skip(offset.Value); |
|||
} |
|||
|
|||
if (count.HasValue) |
|||
{ |
|||
query = query.Take(count.Value); |
|||
} |
|||
|
|||
return ImmutableArray.CreateRange(await query.ToListAsync(cancellationToken)); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Executes the specified query and returns all the corresponding elements.
|
|||
/// </summary>
|
|||
/// <typeparam name="TState">The state type.</typeparam>
|
|||
/// <typeparam name="TResult">The result type.</typeparam>
|
|||
/// <param name="query">The query to execute.</param>
|
|||
/// <param name="state">The optional state.</param>
|
|||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
|
|||
/// <returns>
|
|||
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation,
|
|||
/// whose result returns all the elements returned when executing the specified query.
|
|||
/// </returns>
|
|||
public virtual async Task<ImmutableArray<TResult>> ListAsync<TState, TResult>( |
|||
[NotNull] Func<IQueryable<TAuthorization>, TState, IQueryable<TResult>> query, |
|||
[CanBeNull] TState state, CancellationToken cancellationToken) |
|||
{ |
|||
if (query == null) |
|||
{ |
|||
throw new ArgumentNullException(nameof(query)); |
|||
} |
|||
|
|||
var database = await Context.GetDatabaseAsync(cancellationToken); |
|||
var collection = database.GetCollection<TAuthorization>(Options.CurrentValue.AuthorizationsCollectionName); |
|||
|
|||
return ImmutableArray.CreateRange( |
|||
await ((IMongoQueryable<TResult>) query(collection.AsQueryable(), state)).ToListAsync(cancellationToken)); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Removes the ad-hoc authorizations that are marked as invalid or have no valid token attached.
|
|||
/// </summary>
|
|||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
|
|||
/// <returns>
|
|||
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation.
|
|||
/// </returns>
|
|||
public virtual async Task PruneAsync(CancellationToken cancellationToken) |
|||
{ |
|||
var database = await Context.GetDatabaseAsync(cancellationToken); |
|||
var collection = database.GetCollection<TAuthorization>(Options.CurrentValue.AuthorizationsCollectionName); |
|||
|
|||
var identifiers = |
|||
await (from authorization in collection.AsQueryable() |
|||
join token in database.GetCollection<OpenIddictToken>(Options.CurrentValue.TokensCollectionName).AsQueryable() |
|||
on authorization.Id equals token.AuthorizationId into tokens |
|||
where authorization.Status != OpenIddictConstants.Statuses.Valid || |
|||
(authorization.Type == OpenIddictConstants.AuthorizationTypes.AdHoc && |
|||
!tokens.Any(token => token.Status == OpenIddictConstants.Statuses.Valid)) |
|||
orderby authorization.Id |
|||
select authorization.Id).ToListAsync(cancellationToken); |
|||
|
|||
await collection.DeleteManyAsync(authorization => identifiers.Contains(authorization.Id)); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Sets the application identifier associated with an authorization.
|
|||
/// </summary>
|
|||
/// <param name="authorization">The authorization.</param>
|
|||
/// <param name="identifier">The unique identifier associated with the client application.</param>
|
|||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
|
|||
/// <returns>
|
|||
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation.
|
|||
/// </returns>
|
|||
public virtual Task SetApplicationIdAsync([NotNull] TAuthorization authorization, |
|||
[CanBeNull] string identifier, CancellationToken cancellationToken) |
|||
{ |
|||
if (authorization == null) |
|||
{ |
|||
throw new ArgumentNullException(nameof(authorization)); |
|||
} |
|||
|
|||
if (!string.IsNullOrEmpty(identifier)) |
|||
{ |
|||
authorization.ApplicationId = ObjectId.Parse(identifier); |
|||
} |
|||
|
|||
else |
|||
{ |
|||
authorization.ApplicationId = ObjectId.Empty; |
|||
} |
|||
|
|||
return Task.FromResult(0); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Sets the additional properties associated with an authorization.
|
|||
/// </summary>
|
|||
/// <param name="authorization">The authorization.</param>
|
|||
/// <param name="properties">The additional properties associated with the authorization.</param>
|
|||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
|
|||
/// <returns>
|
|||
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation.
|
|||
/// </returns>
|
|||
public virtual Task SetPropertiesAsync([NotNull] TAuthorization authorization, [CanBeNull] JObject properties, CancellationToken cancellationToken) |
|||
{ |
|||
if (authorization == null) |
|||
{ |
|||
throw new ArgumentNullException(nameof(authorization)); |
|||
} |
|||
|
|||
if (properties == null) |
|||
{ |
|||
authorization.Properties = null; |
|||
|
|||
return Task.FromResult(0); |
|||
} |
|||
|
|||
authorization.Properties = new BsonDocument(properties.ToObject<IDictionary<string, object>>()); |
|||
|
|||
return Task.FromResult(0); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Sets the scopes associated with an authorization.
|
|||
/// </summary>
|
|||
/// <param name="authorization">The authorization.</param>
|
|||
/// <param name="scopes">The scopes associated with the authorization.</param>
|
|||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
|
|||
/// <returns>
|
|||
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation.
|
|||
/// </returns>
|
|||
public virtual Task SetScopesAsync([NotNull] TAuthorization authorization, |
|||
ImmutableArray<string> scopes, CancellationToken cancellationToken) |
|||
{ |
|||
if (authorization == null) |
|||
{ |
|||
throw new ArgumentNullException(nameof(authorization)); |
|||
} |
|||
|
|||
if (scopes.IsDefaultOrEmpty) |
|||
{ |
|||
authorization.Scopes = null; |
|||
|
|||
return Task.FromResult(0); |
|||
} |
|||
|
|||
authorization.Scopes = scopes.ToArray(); |
|||
|
|||
return Task.FromResult(0); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Sets the status associated with an authorization.
|
|||
/// </summary>
|
|||
/// <param name="authorization">The authorization.</param>
|
|||
/// <param name="status">The status associated with the authorization.</param>
|
|||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
|
|||
/// <returns>
|
|||
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation.
|
|||
/// </returns>
|
|||
public virtual Task SetStatusAsync([NotNull] TAuthorization authorization, |
|||
[CanBeNull] string status, CancellationToken cancellationToken) |
|||
{ |
|||
if (authorization == null) |
|||
{ |
|||
throw new ArgumentNullException(nameof(authorization)); |
|||
} |
|||
|
|||
authorization.Status = status; |
|||
|
|||
return Task.FromResult(0); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Sets the subject associated with an authorization.
|
|||
/// </summary>
|
|||
/// <param name="authorization">The authorization.</param>
|
|||
/// <param name="subject">The subject associated with the authorization.</param>
|
|||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
|
|||
/// <returns>
|
|||
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation.
|
|||
/// </returns>
|
|||
public virtual Task SetSubjectAsync([NotNull] TAuthorization authorization, |
|||
[CanBeNull] string subject, CancellationToken cancellationToken) |
|||
{ |
|||
if (authorization == null) |
|||
{ |
|||
throw new ArgumentNullException(nameof(authorization)); |
|||
} |
|||
|
|||
authorization.Subject = subject; |
|||
|
|||
return Task.FromResult(0); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Sets the type associated with an authorization.
|
|||
/// </summary>
|
|||
/// <param name="authorization">The authorization.</param>
|
|||
/// <param name="type">The type associated with the authorization.</param>
|
|||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
|
|||
/// <returns>
|
|||
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation.
|
|||
/// </returns>
|
|||
public virtual Task SetTypeAsync([NotNull] TAuthorization authorization, |
|||
[CanBeNull] string type, CancellationToken cancellationToken) |
|||
{ |
|||
if (authorization == null) |
|||
{ |
|||
throw new ArgumentNullException(nameof(authorization)); |
|||
} |
|||
|
|||
authorization.Type = type; |
|||
|
|||
return Task.FromResult(0); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Updates an existing authorization.
|
|||
/// </summary>
|
|||
/// <param name="authorization">The authorization to update.</param>
|
|||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
|
|||
/// <returns>
|
|||
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation.
|
|||
/// </returns>
|
|||
public virtual async Task UpdateAsync([NotNull] TAuthorization authorization, CancellationToken cancellationToken) |
|||
{ |
|||
if (authorization == null) |
|||
{ |
|||
throw new ArgumentNullException(nameof(authorization)); |
|||
} |
|||
|
|||
// Generate a new concurrency token and attach it
|
|||
// to the authorization before persisting the changes.
|
|||
authorization.ConcurrencyToken = Guid.NewGuid().ToString(); |
|||
|
|||
var database = await Context.GetDatabaseAsync(cancellationToken); |
|||
var collection = database.GetCollection<TAuthorization>(Options.CurrentValue.AuthorizationsCollectionName); |
|||
|
|||
if ((await collection.ReplaceOneAsync(entity => |
|||
entity.Id == authorization.Id && |
|||
entity.ConcurrencyToken == authorization.ConcurrencyToken, authorization, null, cancellationToken)).MatchedCount == 0) |
|||
{ |
|||
throw new OpenIddictException(OpenIddictConstants.Exceptions.ConcurrencyError, new StringBuilder() |
|||
.AppendLine("The authorization was concurrently updated and cannot be persisted in its current state.") |
|||
.Append("Reload the authorization from the database and retry the operation.") |
|||
.ToString()); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,610 @@ |
|||
/* |
|||
* 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; |
|||
using System.Collections.Generic; |
|||
using System.Collections.Immutable; |
|||
using System.Linq; |
|||
using System.Text; |
|||
using System.Threading; |
|||
using System.Threading.Tasks; |
|||
using JetBrains.Annotations; |
|||
using Microsoft.Extensions.Caching.Memory; |
|||
using Microsoft.Extensions.Options; |
|||
using MongoDB.Bson; |
|||
using MongoDB.Driver; |
|||
using MongoDB.Driver.Linq; |
|||
using Newtonsoft.Json.Linq; |
|||
using OpenIddict.Abstractions; |
|||
using OpenIddict.MongoDb.Models; |
|||
|
|||
namespace OpenIddict.MongoDb |
|||
{ |
|||
/// <summary>
|
|||
/// Provides methods allowing to manage the scopes stored in a database.
|
|||
/// </summary>
|
|||
/// <typeparam name="TScope">The type of the Scope entity.</typeparam>
|
|||
public class OpenIddictScopeStore<TScope> : IOpenIddictScopeStore<TScope> |
|||
where TScope : OpenIddictScope, new() |
|||
{ |
|||
public OpenIddictScopeStore( |
|||
[NotNull] IMemoryCache cache, |
|||
[NotNull] IOpenIddictMongoDbContext context, |
|||
[NotNull] IOptionsMonitor<OpenIddictMongoDbOptions> options) |
|||
{ |
|||
Cache = cache; |
|||
Context = context; |
|||
Options = options; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the memory cached associated with the current store.
|
|||
/// </summary>
|
|||
protected IMemoryCache Cache { get; } |
|||
|
|||
/// <summary>
|
|||
/// Gets the database context associated with the current store.
|
|||
/// </summary>
|
|||
protected IOpenIddictMongoDbContext Context { get; } |
|||
|
|||
/// <summary>
|
|||
/// Gets the options associated with the current store.
|
|||
/// </summary>
|
|||
protected IOptionsMonitor<OpenIddictMongoDbOptions> Options { get; } |
|||
|
|||
/// <summary>
|
|||
/// Determines the number of scopes that exist in the database.
|
|||
/// </summary>
|
|||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
|
|||
/// <returns>
|
|||
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation,
|
|||
/// whose result returns the number of scopes in the database.
|
|||
/// </returns>
|
|||
public virtual async Task<long> CountAsync(CancellationToken cancellationToken) |
|||
{ |
|||
var database = await Context.GetDatabaseAsync(cancellationToken); |
|||
var collection = database.GetCollection<TScope>(Options.CurrentValue.ScopesCollectionName); |
|||
|
|||
return await collection.CountAsync(FilterDefinition<TScope>.Empty); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Determines the number of scopes that match the specified query.
|
|||
/// </summary>
|
|||
/// <typeparam name="TResult">The result type.</typeparam>
|
|||
/// <param name="query">The query to execute.</param>
|
|||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
|
|||
/// <returns>
|
|||
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation,
|
|||
/// whose result returns the number of scopes that match the specified query.
|
|||
/// </returns>
|
|||
public virtual async Task<long> CountAsync<TResult>([NotNull] Func<IQueryable<TScope>, IQueryable<TResult>> query, CancellationToken cancellationToken) |
|||
{ |
|||
if (query == null) |
|||
{ |
|||
throw new ArgumentNullException(nameof(query)); |
|||
} |
|||
|
|||
var database = await Context.GetDatabaseAsync(cancellationToken); |
|||
var collection = database.GetCollection<TScope>(Options.CurrentValue.ScopesCollectionName); |
|||
|
|||
return await ((IMongoQueryable<TScope>) query(collection.AsQueryable())).LongCountAsync(); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Creates a new scope.
|
|||
/// </summary>
|
|||
/// <param name="scope">The scope to create.</param>
|
|||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
|
|||
/// <returns>
|
|||
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation.
|
|||
/// </returns>
|
|||
public virtual async Task CreateAsync([NotNull] TScope scope, CancellationToken cancellationToken) |
|||
{ |
|||
if (scope == null) |
|||
{ |
|||
throw new ArgumentNullException(nameof(scope)); |
|||
} |
|||
|
|||
var database = await Context.GetDatabaseAsync(cancellationToken); |
|||
var collection = database.GetCollection<TScope>(Options.CurrentValue.ScopesCollectionName); |
|||
|
|||
await collection.InsertOneAsync(scope, null, cancellationToken); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Removes an existing scope.
|
|||
/// </summary>
|
|||
/// <param name="scope">The scope to delete.</param>
|
|||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
|
|||
/// <returns>
|
|||
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation.
|
|||
/// </returns>
|
|||
public virtual async Task DeleteAsync([NotNull] TScope scope, CancellationToken cancellationToken) |
|||
{ |
|||
if (scope == null) |
|||
{ |
|||
throw new ArgumentNullException(nameof(scope)); |
|||
} |
|||
|
|||
var database = await Context.GetDatabaseAsync(cancellationToken); |
|||
var collection = database.GetCollection<TScope>(Options.CurrentValue.ScopesCollectionName); |
|||
|
|||
if ((await collection.DeleteOneAsync(entity => |
|||
entity.Id == scope.Id && |
|||
entity.ConcurrencyToken == scope.ConcurrencyToken)).DeletedCount == 0) |
|||
{ |
|||
throw new OpenIddictException(OpenIddictConstants.Exceptions.ConcurrencyError, new StringBuilder() |
|||
.AppendLine("The scope was concurrently updated and cannot be persisted in its current state.") |
|||
.Append("Reload the scope from the database and retry the operation.") |
|||
.ToString()); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Retrieves a scope using its unique identifier.
|
|||
/// </summary>
|
|||
/// <param name="identifier">The unique identifier associated with the scope.</param>
|
|||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
|
|||
/// <returns>
|
|||
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation,
|
|||
/// whose result returns the scope corresponding to the identifier.
|
|||
/// </returns>
|
|||
public virtual async Task<TScope> FindByIdAsync([NotNull] string identifier, CancellationToken cancellationToken) |
|||
{ |
|||
if (string.IsNullOrEmpty(identifier)) |
|||
{ |
|||
throw new ArgumentException("The identifier cannot be null or empty.", nameof(identifier)); |
|||
} |
|||
|
|||
var database = await Context.GetDatabaseAsync(cancellationToken); |
|||
var collection = database.GetCollection<TScope>(Options.CurrentValue.ScopesCollectionName); |
|||
|
|||
return await collection.Find(scope => scope.Id == ObjectId.Parse(identifier)).FirstOrDefaultAsync(cancellationToken); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Retrieves a scope using its name.
|
|||
/// </summary>
|
|||
/// <param name="name">The name associated with the scope.</param>
|
|||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
|
|||
/// <returns>
|
|||
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation,
|
|||
/// whose result returns the scope corresponding to the specified name.
|
|||
/// </returns>
|
|||
public virtual async Task<TScope> FindByNameAsync([NotNull] string name, CancellationToken cancellationToken) |
|||
{ |
|||
if (string.IsNullOrEmpty(name)) |
|||
{ |
|||
throw new ArgumentException("The scope name cannot be null or empty.", nameof(name)); |
|||
} |
|||
|
|||
var database = await Context.GetDatabaseAsync(cancellationToken); |
|||
var collection = database.GetCollection<TScope>(Options.CurrentValue.ScopesCollectionName); |
|||
|
|||
return await collection.Find(scope => scope.Name == name).FirstOrDefaultAsync(cancellationToken); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Retrieves a list of scopes using their name.
|
|||
/// </summary>
|
|||
/// <param name="names">The names associated with the scopes.</param>
|
|||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
|
|||
/// <returns>
|
|||
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation,
|
|||
/// whose result returns the scopes corresponding to the specified names.
|
|||
/// </returns>
|
|||
public virtual async Task<ImmutableArray<TScope>> FindByNamesAsync( |
|||
ImmutableArray<string> names, CancellationToken cancellationToken) |
|||
{ |
|||
if (names.Any(name => string.IsNullOrEmpty(name))) |
|||
{ |
|||
throw new ArgumentException("Scope names cannot be null or empty.", nameof(names)); |
|||
} |
|||
|
|||
var database = await Context.GetDatabaseAsync(cancellationToken); |
|||
var collection = database.GetCollection<TScope>(Options.CurrentValue.ScopesCollectionName); |
|||
|
|||
return ImmutableArray.CreateRange(await collection.Find(scope => names.Contains(scope.Name)).ToListAsync(cancellationToken)); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Retrieves all the scopes that contain the specified resource.
|
|||
/// </summary>
|
|||
/// <param name="resource">The resource associated with the scopes.</param>
|
|||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
|
|||
/// <returns>
|
|||
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation,
|
|||
/// whose result returns the scopes associated with the specified resource.
|
|||
/// </returns>
|
|||
public virtual async Task<ImmutableArray<TScope>> FindByResourceAsync( |
|||
[NotNull] string resource, CancellationToken cancellationToken) |
|||
{ |
|||
if (string.IsNullOrEmpty(resource)) |
|||
{ |
|||
throw new ArgumentException("The resource cannot be null or empty.", nameof(resource)); |
|||
} |
|||
|
|||
var database = await Context.GetDatabaseAsync(cancellationToken); |
|||
var collection = database.GetCollection<TScope>(Options.CurrentValue.ScopesCollectionName); |
|||
|
|||
return ImmutableArray.CreateRange(await collection.Find(scope => scope.Resources.Contains(resource)).ToListAsync(cancellationToken)); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Executes the specified query and returns the first element.
|
|||
/// </summary>
|
|||
/// <typeparam name="TState">The state type.</typeparam>
|
|||
/// <typeparam name="TResult">The result type.</typeparam>
|
|||
/// <param name="query">The query to execute.</param>
|
|||
/// <param name="state">The optional state.</param>
|
|||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
|
|||
/// <returns>
|
|||
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation,
|
|||
/// whose result returns the first element returned when executing the query.
|
|||
/// </returns>
|
|||
public virtual async Task<TResult> GetAsync<TState, TResult>( |
|||
[NotNull] Func<IQueryable<TScope>, TState, IQueryable<TResult>> query, |
|||
[CanBeNull] TState state, CancellationToken cancellationToken) |
|||
{ |
|||
if (query == null) |
|||
{ |
|||
throw new ArgumentNullException(nameof(query)); |
|||
} |
|||
|
|||
var database = await Context.GetDatabaseAsync(cancellationToken); |
|||
var collection = database.GetCollection<TScope>(Options.CurrentValue.ScopesCollectionName); |
|||
|
|||
return await ((IMongoQueryable<TResult>) query(collection.AsQueryable(), state)).FirstOrDefaultAsync(cancellationToken); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Retrieves the description associated with a scope.
|
|||
/// </summary>
|
|||
/// <param name="scope">The scope.</param>
|
|||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
|
|||
/// <returns>
|
|||
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
|
|||
/// whose result returns the description associated with the specified scope.
|
|||
/// </returns>
|
|||
public virtual ValueTask<string> GetDescriptionAsync([NotNull] TScope scope, CancellationToken cancellationToken) |
|||
{ |
|||
if (scope == null) |
|||
{ |
|||
throw new ArgumentNullException(nameof(scope)); |
|||
} |
|||
|
|||
return new ValueTask<string>(scope.Description); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Retrieves the display name associated with a scope.
|
|||
/// </summary>
|
|||
/// <param name="scope">The scope.</param>
|
|||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
|
|||
/// <returns>
|
|||
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
|
|||
/// whose result returns the display name associated with the scope.
|
|||
/// </returns>
|
|||
public virtual ValueTask<string> GetDisplayNameAsync([NotNull] TScope scope, CancellationToken cancellationToken) |
|||
{ |
|||
if (scope == null) |
|||
{ |
|||
throw new ArgumentNullException(nameof(scope)); |
|||
} |
|||
|
|||
return new ValueTask<string>(scope.DisplayName); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Retrieves the unique identifier associated with a scope.
|
|||
/// </summary>
|
|||
/// <param name="scope">The scope.</param>
|
|||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
|
|||
/// <returns>
|
|||
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
|
|||
/// whose result returns the unique identifier associated with the scope.
|
|||
/// </returns>
|
|||
public virtual ValueTask<string> GetIdAsync([NotNull] TScope scope, CancellationToken cancellationToken) |
|||
{ |
|||
if (scope == null) |
|||
{ |
|||
throw new ArgumentNullException(nameof(scope)); |
|||
} |
|||
|
|||
return new ValueTask<string>(scope.Id.ToString()); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Retrieves the name associated with a scope.
|
|||
/// </summary>
|
|||
/// <param name="scope">The scope.</param>
|
|||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
|
|||
/// <returns>
|
|||
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
|
|||
/// whose result returns the name associated with the specified scope.
|
|||
/// </returns>
|
|||
public virtual ValueTask<string> GetNameAsync([NotNull] TScope scope, CancellationToken cancellationToken) |
|||
{ |
|||
if (scope == null) |
|||
{ |
|||
throw new ArgumentNullException(nameof(scope)); |
|||
} |
|||
|
|||
return new ValueTask<string>(scope.Name); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Retrieves the additional properties associated with a scope.
|
|||
/// </summary>
|
|||
/// <param name="scope">The scope.</param>
|
|||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
|
|||
/// <returns>
|
|||
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
|
|||
/// whose result returns all the additional properties associated with the scope.
|
|||
/// </returns>
|
|||
public virtual ValueTask<JObject> GetPropertiesAsync([NotNull] TScope scope, CancellationToken cancellationToken) |
|||
{ |
|||
if (scope == null) |
|||
{ |
|||
throw new ArgumentNullException(nameof(scope)); |
|||
} |
|||
|
|||
if (scope.Properties == null) |
|||
{ |
|||
return new ValueTask<JObject>(new JObject()); |
|||
} |
|||
|
|||
return new ValueTask<JObject>(JObject.FromObject(scope.Properties.ToDictionary())); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Retrieves the resources associated with a scope.
|
|||
/// </summary>
|
|||
/// <param name="scope">The scope.</param>
|
|||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
|
|||
/// <returns>
|
|||
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
|
|||
/// whose result returns all the resources associated with the scope.
|
|||
/// </returns>
|
|||
public virtual ValueTask<ImmutableArray<string>> GetResourcesAsync([NotNull] TScope scope, CancellationToken cancellationToken) |
|||
{ |
|||
if (scope == null) |
|||
{ |
|||
throw new ArgumentNullException(nameof(scope)); |
|||
} |
|||
|
|||
if (scope.Resources == null || scope.Resources.Length == 0) |
|||
{ |
|||
return new ValueTask<ImmutableArray<string>>(ImmutableArray.Create<string>()); |
|||
} |
|||
|
|||
return new ValueTask<ImmutableArray<string>>(scope.Resources.ToImmutableArray()); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Instantiates a new scope.
|
|||
/// </summary>
|
|||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
|
|||
/// <returns>
|
|||
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
|
|||
/// whose result returns the instantiated scope, that can be persisted in the database.
|
|||
/// </returns>
|
|||
public virtual ValueTask<TScope> InstantiateAsync(CancellationToken cancellationToken) |
|||
=> new ValueTask<TScope>(new TScope()); |
|||
|
|||
/// <summary>
|
|||
/// Executes the specified query and returns all the corresponding elements.
|
|||
/// </summary>
|
|||
/// <param name="count">The number of results to return.</param>
|
|||
/// <param name="offset">The number of results to skip.</param>
|
|||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
|
|||
/// <returns>
|
|||
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation,
|
|||
/// whose result returns all the elements returned when executing the specified query.
|
|||
/// </returns>
|
|||
public virtual async Task<ImmutableArray<TScope>> ListAsync( |
|||
[CanBeNull] int? count, [CanBeNull] int? offset, CancellationToken cancellationToken) |
|||
{ |
|||
var database = await Context.GetDatabaseAsync(cancellationToken); |
|||
var collection = database.GetCollection<TScope>(Options.CurrentValue.ScopesCollectionName); |
|||
|
|||
var query = (IMongoQueryable<TScope>) collection.AsQueryable().OrderBy(scope => scope.Id); |
|||
|
|||
if (offset.HasValue) |
|||
{ |
|||
query = query.Skip(offset.Value); |
|||
} |
|||
|
|||
if (count.HasValue) |
|||
{ |
|||
query = query.Take(count.Value); |
|||
} |
|||
|
|||
return ImmutableArray.CreateRange(await query.ToListAsync(cancellationToken)); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Executes the specified query and returns all the corresponding elements.
|
|||
/// </summary>
|
|||
/// <typeparam name="TState">The state type.</typeparam>
|
|||
/// <typeparam name="TResult">The result type.</typeparam>
|
|||
/// <param name="query">The query to execute.</param>
|
|||
/// <param name="state">The optional state.</param>
|
|||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
|
|||
/// <returns>
|
|||
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation,
|
|||
/// whose result returns all the elements returned when executing the specified query.
|
|||
/// </returns>
|
|||
public virtual async Task<ImmutableArray<TResult>> ListAsync<TState, TResult>( |
|||
[NotNull] Func<IQueryable<TScope>, TState, IQueryable<TResult>> query, |
|||
[CanBeNull] TState state, CancellationToken cancellationToken) |
|||
{ |
|||
if (query == null) |
|||
{ |
|||
throw new ArgumentNullException(nameof(query)); |
|||
} |
|||
|
|||
var database = await Context.GetDatabaseAsync(cancellationToken); |
|||
var collection = database.GetCollection<TScope>(Options.CurrentValue.ScopesCollectionName); |
|||
|
|||
return ImmutableArray.CreateRange( |
|||
await ((IMongoQueryable<TResult>) query(collection.AsQueryable(), state)).ToListAsync(cancellationToken)); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Sets the description associated with a scope.
|
|||
/// </summary>
|
|||
/// <param name="scope">The scope.</param>
|
|||
/// <param name="description">The description associated with the authorization.</param>
|
|||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
|
|||
/// <returns>
|
|||
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation.
|
|||
/// </returns>
|
|||
public virtual Task SetDescriptionAsync([NotNull] TScope scope, [CanBeNull] string description, CancellationToken cancellationToken) |
|||
{ |
|||
if (scope == null) |
|||
{ |
|||
throw new ArgumentNullException(nameof(scope)); |
|||
} |
|||
|
|||
scope.Description = description; |
|||
|
|||
return Task.FromResult(0); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Sets the display name associated with a scope.
|
|||
/// </summary>
|
|||
/// <param name="scope">The scope.</param>
|
|||
/// <param name="name">The display name associated with the scope.</param>
|
|||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
|
|||
/// <returns>
|
|||
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation.
|
|||
/// </returns>
|
|||
public virtual Task SetDisplayNameAsync([NotNull] TScope scope, [CanBeNull] string name, CancellationToken cancellationToken) |
|||
{ |
|||
if (scope == null) |
|||
{ |
|||
throw new ArgumentNullException(nameof(scope)); |
|||
} |
|||
|
|||
scope.DisplayName = name; |
|||
|
|||
return Task.FromResult(0); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Sets the name associated with a scope.
|
|||
/// </summary>
|
|||
/// <param name="scope">The scope.</param>
|
|||
/// <param name="name">The name associated with the authorization.</param>
|
|||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
|
|||
/// <returns>
|
|||
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation.
|
|||
/// </returns>
|
|||
public virtual Task SetNameAsync([NotNull] TScope scope, [CanBeNull] string name, CancellationToken cancellationToken) |
|||
{ |
|||
if (scope == null) |
|||
{ |
|||
throw new ArgumentNullException(nameof(scope)); |
|||
} |
|||
|
|||
scope.Name = name; |
|||
|
|||
return Task.FromResult(0); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Sets the additional properties associated with a scope.
|
|||
/// </summary>
|
|||
/// <param name="scope">The scope.</param>
|
|||
/// <param name="properties">The additional properties associated with the scope.</param>
|
|||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
|
|||
/// <returns>
|
|||
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation.
|
|||
/// </returns>
|
|||
public virtual Task SetPropertiesAsync([NotNull] TScope scope, [CanBeNull] JObject properties, CancellationToken cancellationToken) |
|||
{ |
|||
if (scope == null) |
|||
{ |
|||
throw new ArgumentNullException(nameof(scope)); |
|||
} |
|||
|
|||
if (properties == null) |
|||
{ |
|||
scope.Properties = null; |
|||
|
|||
return Task.FromResult(0); |
|||
} |
|||
|
|||
scope.Properties = new BsonDocument(properties.ToObject<IDictionary<string, object>>()); |
|||
|
|||
return Task.FromResult(0); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Sets the resources associated with a scope.
|
|||
/// </summary>
|
|||
/// <param name="scope">The scope.</param>
|
|||
/// <param name="resources">The resources associated with the scope.</param>
|
|||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
|
|||
/// <returns>
|
|||
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation.
|
|||
/// </returns>
|
|||
public virtual Task SetResourcesAsync([NotNull] TScope scope, ImmutableArray<string> resources, CancellationToken cancellationToken) |
|||
{ |
|||
if (scope == null) |
|||
{ |
|||
throw new ArgumentNullException(nameof(scope)); |
|||
} |
|||
|
|||
if (resources.IsDefaultOrEmpty) |
|||
{ |
|||
scope.Resources = null; |
|||
|
|||
return Task.FromResult(0); |
|||
} |
|||
|
|||
scope.Resources = resources.ToArray(); |
|||
|
|||
return Task.FromResult(0); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Updates an existing scope.
|
|||
/// </summary>
|
|||
/// <param name="scope">The scope to update.</param>
|
|||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
|
|||
/// <returns>
|
|||
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation.
|
|||
/// </returns>
|
|||
public virtual async Task UpdateAsync([NotNull] TScope scope, CancellationToken cancellationToken) |
|||
{ |
|||
if (scope == null) |
|||
{ |
|||
throw new ArgumentNullException(nameof(scope)); |
|||
} |
|||
|
|||
// Generate a new concurrency token and attach it
|
|||
// to the scope before persisting the changes.
|
|||
scope.ConcurrencyToken = Guid.NewGuid().ToString(); |
|||
|
|||
var database = await Context.GetDatabaseAsync(cancellationToken); |
|||
var collection = database.GetCollection<TScope>(Options.CurrentValue.ScopesCollectionName); |
|||
|
|||
if ((await collection.ReplaceOneAsync(entity => |
|||
entity.Id == scope.Id && |
|||
entity.ConcurrencyToken == scope.ConcurrencyToken, scope, null, cancellationToken)).MatchedCount == 0) |
|||
{ |
|||
throw new OpenIddictException(OpenIddictConstants.Exceptions.ConcurrencyError, new StringBuilder() |
|||
.AppendLine("The scope was concurrently updated and cannot be persisted in its current state.") |
|||
.Append("Reload the scope from the database and retry the operation.") |
|||
.ToString()); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,874 @@ |
|||
/* |
|||
* 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; |
|||
using System.Collections.Generic; |
|||
using System.Collections.Immutable; |
|||
using System.Linq; |
|||
using System.Text; |
|||
using System.Threading; |
|||
using System.Threading.Tasks; |
|||
using JetBrains.Annotations; |
|||
using Microsoft.Extensions.Caching.Memory; |
|||
using Microsoft.Extensions.Options; |
|||
using MongoDB.Bson; |
|||
using MongoDB.Driver; |
|||
using MongoDB.Driver.Linq; |
|||
using Newtonsoft.Json.Linq; |
|||
using OpenIddict.Abstractions; |
|||
using OpenIddict.MongoDb.Models; |
|||
|
|||
namespace OpenIddict.MongoDb |
|||
{ |
|||
/// <summary>
|
|||
/// Provides methods allowing to manage the tokens stored in a database.
|
|||
/// </summary>
|
|||
/// <typeparam name="TToken">The type of the Token entity.</typeparam>
|
|||
public class OpenIddictTokenStore<TToken> : IOpenIddictTokenStore<TToken> |
|||
where TToken : OpenIddictToken, new() |
|||
{ |
|||
public OpenIddictTokenStore( |
|||
[NotNull] IMemoryCache cache, |
|||
[NotNull] IOpenIddictMongoDbContext context, |
|||
[NotNull] IOptionsMonitor<OpenIddictMongoDbOptions> options) |
|||
{ |
|||
Cache = cache; |
|||
Context = context; |
|||
Options = options; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the memory cached associated with the current store.
|
|||
/// </summary>
|
|||
protected IMemoryCache Cache { get; } |
|||
|
|||
/// <summary>
|
|||
/// Gets the database context associated with the current store.
|
|||
/// </summary>
|
|||
protected IOpenIddictMongoDbContext Context { get; } |
|||
|
|||
/// <summary>
|
|||
/// Gets the options associated with the current store.
|
|||
/// </summary>
|
|||
protected IOptionsMonitor<OpenIddictMongoDbOptions> Options { get; } |
|||
|
|||
/// <summary>
|
|||
/// Determines the number of tokens that exist in the database.
|
|||
/// </summary>
|
|||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
|
|||
/// <returns>
|
|||
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation,
|
|||
/// whose result returns the number of applications in the database.
|
|||
/// </returns>
|
|||
public virtual async Task<long> CountAsync(CancellationToken cancellationToken) |
|||
{ |
|||
var database = await Context.GetDatabaseAsync(cancellationToken); |
|||
var collection = database.GetCollection<TToken>(Options.CurrentValue.TokensCollectionName); |
|||
|
|||
return await collection.CountAsync(FilterDefinition<TToken>.Empty); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Determines the number of tokens that match the specified query.
|
|||
/// </summary>
|
|||
/// <typeparam name="TResult">The result type.</typeparam>
|
|||
/// <param name="query">The query to execute.</param>
|
|||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
|
|||
/// <returns>
|
|||
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation,
|
|||
/// whose result returns the number of tokens that match the specified query.
|
|||
/// </returns>
|
|||
public virtual async Task<long> CountAsync<TResult>([NotNull] Func<IQueryable<TToken>, IQueryable<TResult>> query, CancellationToken cancellationToken) |
|||
{ |
|||
if (query == null) |
|||
{ |
|||
throw new ArgumentNullException(nameof(query)); |
|||
} |
|||
|
|||
var database = await Context.GetDatabaseAsync(cancellationToken); |
|||
var collection = database.GetCollection<TToken>(Options.CurrentValue.TokensCollectionName); |
|||
|
|||
return await ((IMongoQueryable<TToken>) query(collection.AsQueryable())).LongCountAsync(); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Creates a new token.
|
|||
/// </summary>
|
|||
/// <param name="token">The token to create.</param>
|
|||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
|
|||
/// <returns>
|
|||
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation.
|
|||
/// </returns>
|
|||
public virtual async Task CreateAsync([NotNull] TToken token, CancellationToken cancellationToken) |
|||
{ |
|||
if (token == null) |
|||
{ |
|||
throw new ArgumentNullException(nameof(token)); |
|||
} |
|||
|
|||
var database = await Context.GetDatabaseAsync(cancellationToken); |
|||
var collection = database.GetCollection<TToken>(Options.CurrentValue.TokensCollectionName); |
|||
|
|||
await collection.InsertOneAsync(token, null, cancellationToken); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Removes a token.
|
|||
/// </summary>
|
|||
/// <param name="token">The token to delete.</param>
|
|||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
|
|||
/// <returns>
|
|||
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation.
|
|||
/// </returns>
|
|||
public virtual async Task DeleteAsync([NotNull] TToken token, CancellationToken cancellationToken) |
|||
{ |
|||
if (token == null) |
|||
{ |
|||
throw new ArgumentNullException(nameof(token)); |
|||
} |
|||
|
|||
var database = await Context.GetDatabaseAsync(cancellationToken); |
|||
var collection = database.GetCollection<TToken>(Options.CurrentValue.TokensCollectionName); |
|||
|
|||
if ((await collection.DeleteOneAsync(entity => |
|||
entity.Id == token.Id && |
|||
entity.ConcurrencyToken == token.ConcurrencyToken)).DeletedCount == 0) |
|||
{ |
|||
throw new OpenIddictException(OpenIddictConstants.Exceptions.ConcurrencyError, new StringBuilder() |
|||
.AppendLine("The token was concurrently updated and cannot be persisted in its current state.") |
|||
.Append("Reload the token from the database and retry the operation.") |
|||
.ToString()); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Retrieves the list of tokens corresponding to the specified application identifier.
|
|||
/// </summary>
|
|||
/// <param name="identifier">The application identifier associated with the tokens.</param>
|
|||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
|
|||
/// <returns>
|
|||
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation,
|
|||
/// whose result returns the tokens corresponding to the specified application.
|
|||
/// </returns>
|
|||
public virtual async Task<ImmutableArray<TToken>> FindByApplicationIdAsync([NotNull] string identifier, CancellationToken cancellationToken) |
|||
{ |
|||
if (string.IsNullOrEmpty(identifier)) |
|||
{ |
|||
throw new ArgumentException("The identifier cannot be null or empty.", nameof(identifier)); |
|||
} |
|||
|
|||
var database = await Context.GetDatabaseAsync(cancellationToken); |
|||
var collection = database.GetCollection<TToken>(Options.CurrentValue.TokensCollectionName); |
|||
|
|||
return ImmutableArray.CreateRange(await collection.Find(token => token.ApplicationId == ObjectId.Parse(identifier)).ToListAsync(cancellationToken)); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Retrieves the list of tokens corresponding to the specified authorization identifier.
|
|||
/// </summary>
|
|||
/// <param name="identifier">The authorization identifier associated with the tokens.</param>
|
|||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
|
|||
/// <returns>
|
|||
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation,
|
|||
/// whose result returns the tokens corresponding to the specified authorization.
|
|||
/// </returns>
|
|||
public virtual async Task<ImmutableArray<TToken>> FindByAuthorizationIdAsync([NotNull] string identifier, CancellationToken cancellationToken) |
|||
{ |
|||
if (string.IsNullOrEmpty(identifier)) |
|||
{ |
|||
throw new ArgumentException("The identifier cannot be null or empty.", nameof(identifier)); |
|||
} |
|||
|
|||
var database = await Context.GetDatabaseAsync(cancellationToken); |
|||
var collection = database.GetCollection<TToken>(Options.CurrentValue.TokensCollectionName); |
|||
|
|||
return ImmutableArray.CreateRange(await collection.Find(token => token.AuthorizationId == ObjectId.Parse(identifier)).ToListAsync(cancellationToken)); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Retrieves a token using its unique identifier.
|
|||
/// </summary>
|
|||
/// <param name="identifier">The unique identifier associated with the token.</param>
|
|||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
|
|||
/// <returns>
|
|||
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation,
|
|||
/// whose result returns the token corresponding to the unique identifier.
|
|||
/// </returns>
|
|||
public virtual async Task<TToken> FindByIdAsync([NotNull] string identifier, CancellationToken cancellationToken) |
|||
{ |
|||
if (string.IsNullOrEmpty(identifier)) |
|||
{ |
|||
throw new ArgumentException("The identifier cannot be null or empty.", nameof(identifier)); |
|||
} |
|||
|
|||
var database = await Context.GetDatabaseAsync(cancellationToken); |
|||
var collection = database.GetCollection<TToken>(Options.CurrentValue.TokensCollectionName); |
|||
|
|||
return await collection.Find(token => token.Id == ObjectId.Parse(identifier)).FirstOrDefaultAsync(cancellationToken); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Retrieves the list of tokens corresponding to the specified reference identifier.
|
|||
/// Note: the reference identifier may be hashed or encrypted for security reasons.
|
|||
/// </summary>
|
|||
/// <param name="identifier">The reference identifier associated with the tokens.</param>
|
|||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
|
|||
/// <returns>
|
|||
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation,
|
|||
/// whose result returns the tokens corresponding to the specified reference identifier.
|
|||
/// </returns>
|
|||
public virtual async Task<TToken> FindByReferenceIdAsync([NotNull] string identifier, CancellationToken cancellationToken) |
|||
{ |
|||
if (string.IsNullOrEmpty(identifier)) |
|||
{ |
|||
throw new ArgumentException("The identifier cannot be null or empty.", nameof(identifier)); |
|||
} |
|||
|
|||
var database = await Context.GetDatabaseAsync(cancellationToken); |
|||
var collection = database.GetCollection<TToken>(Options.CurrentValue.TokensCollectionName); |
|||
|
|||
return await collection.Find(token => token.ReferenceId == identifier).FirstOrDefaultAsync(cancellationToken); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Retrieves the list of tokens corresponding to the specified subject.
|
|||
/// </summary>
|
|||
/// <param name="subject">The subject associated with the tokens.</param>
|
|||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
|
|||
/// <returns>
|
|||
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation,
|
|||
/// whose result returns the tokens corresponding to the specified subject.
|
|||
/// </returns>
|
|||
public virtual async Task<ImmutableArray<TToken>> FindBySubjectAsync([NotNull] string subject, CancellationToken cancellationToken) |
|||
{ |
|||
if (string.IsNullOrEmpty(subject)) |
|||
{ |
|||
throw new ArgumentException("The subject cannot be null or empty.", nameof(subject)); |
|||
} |
|||
|
|||
var database = await Context.GetDatabaseAsync(cancellationToken); |
|||
var collection = database.GetCollection<TToken>(Options.CurrentValue.TokensCollectionName); |
|||
|
|||
return ImmutableArray.CreateRange(await collection.Find(token => token.Subject == subject).ToListAsync(cancellationToken)); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Retrieves the optional application identifier associated with a token.
|
|||
/// </summary>
|
|||
/// <param name="token">The token.</param>
|
|||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
|
|||
/// <returns>
|
|||
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
|
|||
/// whose result returns the application identifier associated with the token.
|
|||
/// </returns>
|
|||
public virtual ValueTask<string> GetApplicationIdAsync([NotNull] TToken token, CancellationToken cancellationToken) |
|||
{ |
|||
if (token == null) |
|||
{ |
|||
throw new ArgumentNullException(nameof(token)); |
|||
} |
|||
|
|||
return new ValueTask<string>(token.ApplicationId.ToString()); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Executes the specified query and returns the first element.
|
|||
/// </summary>
|
|||
/// <typeparam name="TState">The state type.</typeparam>
|
|||
/// <typeparam name="TResult">The result type.</typeparam>
|
|||
/// <param name="query">The query to execute.</param>
|
|||
/// <param name="state">The optional state.</param>
|
|||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
|
|||
/// <returns>
|
|||
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation,
|
|||
/// whose result returns the first element returned when executing the query.
|
|||
/// </returns>
|
|||
public virtual async Task<TResult> GetAsync<TState, TResult>( |
|||
[NotNull] Func<IQueryable<TToken>, TState, IQueryable<TResult>> query, |
|||
[CanBeNull] TState state, CancellationToken cancellationToken) |
|||
{ |
|||
if (query == null) |
|||
{ |
|||
throw new ArgumentNullException(nameof(query)); |
|||
} |
|||
|
|||
var database = await Context.GetDatabaseAsync(cancellationToken); |
|||
var collection = database.GetCollection<TToken>(Options.CurrentValue.TokensCollectionName); |
|||
|
|||
return await ((IMongoQueryable<TResult>) query(collection.AsQueryable(), state)).FirstOrDefaultAsync(cancellationToken); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Retrieves the optional authorization identifier associated with a token.
|
|||
/// </summary>
|
|||
/// <param name="token">The token.</param>
|
|||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
|
|||
/// <returns>
|
|||
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
|
|||
/// whose result returns the authorization identifier associated with the token.
|
|||
/// </returns>
|
|||
public virtual ValueTask<string> GetAuthorizationIdAsync([NotNull] TToken token, CancellationToken cancellationToken) |
|||
{ |
|||
if (token == null) |
|||
{ |
|||
throw new ArgumentNullException(nameof(token)); |
|||
} |
|||
|
|||
return new ValueTask<string>(token.AuthorizationId.ToString()); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Retrieves the creation date associated with a token.
|
|||
/// </summary>
|
|||
/// <param name="token">The token.</param>
|
|||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
|
|||
/// <returns>
|
|||
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
|
|||
/// whose result returns the creation date associated with the specified token.
|
|||
/// </returns>
|
|||
public virtual ValueTask<DateTimeOffset?> GetCreationDateAsync([NotNull] TToken token, CancellationToken cancellationToken) |
|||
{ |
|||
if (token == null) |
|||
{ |
|||
throw new ArgumentNullException(nameof(token)); |
|||
} |
|||
|
|||
return new ValueTask<DateTimeOffset?>(token.CreationDate); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Retrieves the expiration date associated with a token.
|
|||
/// </summary>
|
|||
/// <param name="token">The token.</param>
|
|||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
|
|||
/// <returns>
|
|||
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
|
|||
/// whose result returns the expiration date associated with the specified token.
|
|||
/// </returns>
|
|||
public virtual ValueTask<DateTimeOffset?> GetExpirationDateAsync([NotNull] TToken token, CancellationToken cancellationToken) |
|||
{ |
|||
if (token == null) |
|||
{ |
|||
throw new ArgumentNullException(nameof(token)); |
|||
} |
|||
|
|||
return new ValueTask<DateTimeOffset?>(token.ExpirationDate); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Retrieves the unique identifier associated with a token.
|
|||
/// </summary>
|
|||
/// <param name="token">The token.</param>
|
|||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
|
|||
/// <returns>
|
|||
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
|
|||
/// whose result returns the unique identifier associated with the token.
|
|||
/// </returns>
|
|||
public virtual ValueTask<string> GetIdAsync([NotNull] TToken token, CancellationToken cancellationToken) |
|||
{ |
|||
if (token == null) |
|||
{ |
|||
throw new ArgumentNullException(nameof(token)); |
|||
} |
|||
|
|||
return new ValueTask<string>(token.Id.ToString()); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Retrieves the payload associated with a token.
|
|||
/// </summary>
|
|||
/// <param name="token">The token.</param>
|
|||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
|
|||
/// <returns>
|
|||
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
|
|||
/// whose result returns the payload associated with the specified token.
|
|||
/// </returns>
|
|||
public virtual ValueTask<string> GetPayloadAsync([NotNull] TToken token, CancellationToken cancellationToken) |
|||
{ |
|||
if (token == null) |
|||
{ |
|||
throw new ArgumentNullException(nameof(token)); |
|||
} |
|||
|
|||
return new ValueTask<string>(token.Payload); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Retrieves the additional properties associated with a token.
|
|||
/// </summary>
|
|||
/// <param name="token">The token.</param>
|
|||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
|
|||
/// <returns>
|
|||
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
|
|||
/// whose result returns all the additional properties associated with the token.
|
|||
/// </returns>
|
|||
public virtual ValueTask<JObject> GetPropertiesAsync([NotNull] TToken token, CancellationToken cancellationToken) |
|||
{ |
|||
if (token == null) |
|||
{ |
|||
throw new ArgumentNullException(nameof(token)); |
|||
} |
|||
|
|||
if (token.Properties == null) |
|||
{ |
|||
return new ValueTask<JObject>(new JObject()); |
|||
} |
|||
|
|||
return new ValueTask<JObject>(JObject.FromObject(token.Properties.ToDictionary())); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Retrieves the reference identifier associated with a token.
|
|||
/// Note: depending on the manager used to create the token,
|
|||
/// the reference identifier may be hashed for security reasons.
|
|||
/// </summary>
|
|||
/// <param name="token">The token.</param>
|
|||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
|
|||
/// <returns>
|
|||
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
|
|||
/// whose result returns the reference identifier associated with the specified token.
|
|||
/// </returns>
|
|||
public virtual ValueTask<string> GetReferenceIdAsync([NotNull] TToken token, CancellationToken cancellationToken) |
|||
{ |
|||
if (token == null) |
|||
{ |
|||
throw new ArgumentNullException(nameof(token)); |
|||
} |
|||
|
|||
return new ValueTask<string>(token.ReferenceId); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Retrieves the status associated with a token.
|
|||
/// </summary>
|
|||
/// <param name="token">The token.</param>
|
|||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
|
|||
/// <returns>
|
|||
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
|
|||
/// whose result returns the status associated with the specified token.
|
|||
/// </returns>
|
|||
public virtual ValueTask<string> GetStatusAsync([NotNull] TToken token, CancellationToken cancellationToken) |
|||
{ |
|||
if (token == null) |
|||
{ |
|||
throw new ArgumentNullException(nameof(token)); |
|||
} |
|||
|
|||
return new ValueTask<string>(token.Status); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Retrieves the subject associated with a token.
|
|||
/// </summary>
|
|||
/// <param name="token">The token.</param>
|
|||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
|
|||
/// <returns>
|
|||
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
|
|||
/// whose result returns the subject associated with the specified token.
|
|||
/// </returns>
|
|||
public virtual ValueTask<string> GetSubjectAsync([NotNull] TToken token, CancellationToken cancellationToken) |
|||
{ |
|||
if (token == null) |
|||
{ |
|||
throw new ArgumentNullException(nameof(token)); |
|||
} |
|||
|
|||
return new ValueTask<string>(token.Subject); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Retrieves the token type associated with a token.
|
|||
/// </summary>
|
|||
/// <param name="token">The token.</param>
|
|||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
|
|||
/// <returns>
|
|||
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
|
|||
/// whose result returns the token type associated with the specified token.
|
|||
/// </returns>
|
|||
public virtual ValueTask<string> GetTokenTypeAsync([NotNull] TToken token, CancellationToken cancellationToken) |
|||
{ |
|||
if (token == null) |
|||
{ |
|||
throw new ArgumentNullException(nameof(token)); |
|||
} |
|||
|
|||
return new ValueTask<string>(token.Type); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Instantiates a new token.
|
|||
/// </summary>
|
|||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
|
|||
/// <returns>
|
|||
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
|
|||
/// whose result returns the instantiated token, that can be persisted in the database.
|
|||
/// </returns>
|
|||
public virtual ValueTask<TToken> InstantiateAsync(CancellationToken cancellationToken) |
|||
=> new ValueTask<TToken>(new TToken()); |
|||
|
|||
/// <summary>
|
|||
/// Executes the specified query and returns all the corresponding elements.
|
|||
/// </summary>
|
|||
/// <param name="count">The number of results to return.</param>
|
|||
/// <param name="offset">The number of results to skip.</param>
|
|||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
|
|||
/// <returns>
|
|||
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation,
|
|||
/// whose result returns all the elements returned when executing the specified query.
|
|||
/// </returns>
|
|||
public virtual async Task<ImmutableArray<TToken>> ListAsync( |
|||
[CanBeNull] int? count, [CanBeNull] int? offset, CancellationToken cancellationToken) |
|||
{ |
|||
var database = await Context.GetDatabaseAsync(cancellationToken); |
|||
var collection = database.GetCollection<TToken>(Options.CurrentValue.TokensCollectionName); |
|||
|
|||
var query = (IMongoQueryable<TToken>) collection.AsQueryable().OrderBy(token => token.Id); |
|||
|
|||
if (offset.HasValue) |
|||
{ |
|||
query = query.Skip(offset.Value); |
|||
} |
|||
|
|||
if (count.HasValue) |
|||
{ |
|||
query = query.Take(count.Value); |
|||
} |
|||
|
|||
return ImmutableArray.CreateRange(await query.ToListAsync(cancellationToken)); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Executes the specified query and returns all the corresponding elements.
|
|||
/// </summary>
|
|||
/// <typeparam name="TState">The state type.</typeparam>
|
|||
/// <typeparam name="TResult">The result type.</typeparam>
|
|||
/// <param name="query">The query to execute.</param>
|
|||
/// <param name="state">The optional state.</param>
|
|||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
|
|||
/// <returns>
|
|||
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation,
|
|||
/// whose result returns all the elements returned when executing the specified query.
|
|||
/// </returns>
|
|||
public virtual async Task<ImmutableArray<TResult>> ListAsync<TState, TResult>( |
|||
[NotNull] Func<IQueryable<TToken>, TState, IQueryable<TResult>> query, |
|||
[CanBeNull] TState state, CancellationToken cancellationToken) |
|||
{ |
|||
if (query == null) |
|||
{ |
|||
throw new ArgumentNullException(nameof(query)); |
|||
} |
|||
|
|||
var database = await Context.GetDatabaseAsync(cancellationToken); |
|||
var collection = database.GetCollection<TToken>(Options.CurrentValue.TokensCollectionName); |
|||
|
|||
return ImmutableArray.CreateRange( |
|||
await ((IMongoQueryable<TResult>) query(collection.AsQueryable(), state)).ToListAsync(cancellationToken)); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Removes the tokens that are marked as expired or invalid.
|
|||
/// </summary>
|
|||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
|
|||
/// <returns>
|
|||
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation.
|
|||
/// </returns>
|
|||
public virtual async Task PruneAsync(CancellationToken cancellationToken) |
|||
{ |
|||
var database = await Context.GetDatabaseAsync(cancellationToken); |
|||
var collection = database.GetCollection<TToken>(Options.CurrentValue.TokensCollectionName); |
|||
|
|||
await collection.DeleteManyAsync(token => token.ExpirationDate < DateTimeOffset.UtcNow || |
|||
token.Status != OpenIddictConstants.Statuses.Valid, cancellationToken); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Sets the application identifier associated with a token.
|
|||
/// </summary>
|
|||
/// <param name="token">The token.</param>
|
|||
/// <param name="identifier">The unique identifier associated with the client application.</param>
|
|||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
|
|||
/// <returns>
|
|||
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation.
|
|||
/// </returns>
|
|||
public virtual Task SetApplicationIdAsync([NotNull] TToken token, |
|||
[CanBeNull] string identifier, CancellationToken cancellationToken) |
|||
{ |
|||
if (token == null) |
|||
{ |
|||
throw new ArgumentNullException(nameof(token)); |
|||
} |
|||
|
|||
if (!string.IsNullOrEmpty(identifier)) |
|||
{ |
|||
token.ApplicationId = ObjectId.Parse(identifier); |
|||
} |
|||
|
|||
else |
|||
{ |
|||
token.ApplicationId = ObjectId.Empty; |
|||
} |
|||
|
|||
return Task.FromResult(0); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Sets the authorization identifier associated with a token.
|
|||
/// </summary>
|
|||
/// <param name="token">The token.</param>
|
|||
/// <param name="identifier">The unique identifier associated with the authorization.</param>
|
|||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
|
|||
/// <returns>
|
|||
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation.
|
|||
/// </returns>
|
|||
public virtual Task SetAuthorizationIdAsync([NotNull] TToken token, |
|||
[CanBeNull] string identifier, CancellationToken cancellationToken) |
|||
{ |
|||
if (token == null) |
|||
{ |
|||
throw new ArgumentNullException(nameof(token)); |
|||
} |
|||
|
|||
if (!string.IsNullOrEmpty(identifier)) |
|||
{ |
|||
token.AuthorizationId = ObjectId.Parse(identifier); |
|||
} |
|||
|
|||
else |
|||
{ |
|||
token.AuthorizationId = ObjectId.Empty; |
|||
} |
|||
|
|||
return Task.FromResult(0); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Sets the creation date associated with a token.
|
|||
/// </summary>
|
|||
/// <param name="token">The token.</param>
|
|||
/// <param name="date">The creation date.</param>
|
|||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
|
|||
/// <returns>
|
|||
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation.
|
|||
/// </returns>
|
|||
public virtual Task SetCreationDateAsync([NotNull] TToken token, |
|||
[CanBeNull] DateTimeOffset? date, CancellationToken cancellationToken) |
|||
{ |
|||
if (token == null) |
|||
{ |
|||
throw new ArgumentNullException(nameof(token)); |
|||
} |
|||
|
|||
token.CreationDate = date?.UtcDateTime; |
|||
|
|||
return Task.FromResult(0); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Sets the expiration date associated with a token.
|
|||
/// </summary>
|
|||
/// <param name="token">The token.</param>
|
|||
/// <param name="date">The expiration date.</param>
|
|||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
|
|||
/// <returns>
|
|||
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation.
|
|||
/// </returns>
|
|||
public virtual Task SetExpirationDateAsync([NotNull] TToken token, |
|||
[CanBeNull] DateTimeOffset? date, CancellationToken cancellationToken) |
|||
{ |
|||
if (token == null) |
|||
{ |
|||
throw new ArgumentNullException(nameof(token)); |
|||
} |
|||
|
|||
token.ExpirationDate = date?.UtcDateTime; |
|||
|
|||
return Task.FromResult(0); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Sets the payload associated with a token.
|
|||
/// </summary>
|
|||
/// <param name="token">The token.</param>
|
|||
/// <param name="payload">The payload associated with the token.</param>
|
|||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
|
|||
/// <returns>
|
|||
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation.
|
|||
/// </returns>
|
|||
public virtual Task SetPayloadAsync([NotNull] TToken token, [CanBeNull] string payload, CancellationToken cancellationToken) |
|||
{ |
|||
if (token == null) |
|||
{ |
|||
throw new ArgumentNullException(nameof(token)); |
|||
} |
|||
|
|||
token.Payload = payload; |
|||
|
|||
return Task.FromResult(0); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Sets the additional properties associated with a token.
|
|||
/// </summary>
|
|||
/// <param name="token">The token.</param>
|
|||
/// <param name="properties">The additional properties associated with the token.</param>
|
|||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
|
|||
/// <returns>
|
|||
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation.
|
|||
/// </returns>
|
|||
public virtual Task SetPropertiesAsync([NotNull] TToken token, [CanBeNull] JObject properties, CancellationToken cancellationToken) |
|||
{ |
|||
if (token == null) |
|||
{ |
|||
throw new ArgumentNullException(nameof(token)); |
|||
} |
|||
|
|||
if (properties == null) |
|||
{ |
|||
token.Properties = null; |
|||
|
|||
return Task.FromResult(0); |
|||
} |
|||
|
|||
token.Properties = new BsonDocument(properties.ToObject<IDictionary<string, object>>()); |
|||
|
|||
return Task.FromResult(0); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Sets the reference identifier associated with a token.
|
|||
/// Note: depending on the manager used to create the token,
|
|||
/// the reference identifier may be hashed for security reasons.
|
|||
/// </summary>
|
|||
/// <param name="token">The token.</param>
|
|||
/// <param name="identifier">The reference identifier associated with the token.</param>
|
|||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
|
|||
/// <returns>
|
|||
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation.
|
|||
/// </returns>
|
|||
public virtual Task SetReferenceIdAsync([NotNull] TToken token, [CanBeNull] string identifier, CancellationToken cancellationToken) |
|||
{ |
|||
if (token == null) |
|||
{ |
|||
throw new ArgumentNullException(nameof(token)); |
|||
} |
|||
|
|||
token.ReferenceId = identifier; |
|||
|
|||
return Task.FromResult(0); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Sets the status associated with a token.
|
|||
/// </summary>
|
|||
/// <param name="token">The token.</param>
|
|||
/// <param name="status">The status associated with the authorization.</param>
|
|||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
|
|||
/// <returns>
|
|||
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation.
|
|||
/// </returns>
|
|||
public virtual Task SetStatusAsync([NotNull] TToken token, [CanBeNull] string status, CancellationToken cancellationToken) |
|||
{ |
|||
if (token == null) |
|||
{ |
|||
throw new ArgumentNullException(nameof(token)); |
|||
} |
|||
|
|||
if (string.IsNullOrEmpty(status)) |
|||
{ |
|||
throw new ArgumentException("The status cannot be null or empty.", nameof(status)); |
|||
} |
|||
|
|||
token.Status = status; |
|||
|
|||
return Task.FromResult(0); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Sets the subject associated with a token.
|
|||
/// </summary>
|
|||
/// <param name="token">The token.</param>
|
|||
/// <param name="subject">The subject associated with the token.</param>
|
|||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
|
|||
/// <returns>
|
|||
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation.
|
|||
/// </returns>
|
|||
public virtual Task SetSubjectAsync([NotNull] TToken token, [CanBeNull] string subject, CancellationToken cancellationToken) |
|||
{ |
|||
if (token == null) |
|||
{ |
|||
throw new ArgumentNullException(nameof(token)); |
|||
} |
|||
|
|||
if (string.IsNullOrEmpty(subject)) |
|||
{ |
|||
throw new ArgumentException("The subject cannot be null or empty.", nameof(subject)); |
|||
} |
|||
|
|||
token.Subject = subject; |
|||
|
|||
return Task.FromResult(0); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Sets the token type associated with a token.
|
|||
/// </summary>
|
|||
/// <param name="token">The token.</param>
|
|||
/// <param name="type">The token type associated with the token.</param>
|
|||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
|
|||
/// <returns>
|
|||
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation.
|
|||
/// </returns>
|
|||
public virtual Task SetTokenTypeAsync([NotNull] TToken token, [CanBeNull] string type, CancellationToken cancellationToken) |
|||
{ |
|||
if (token == null) |
|||
{ |
|||
throw new ArgumentNullException(nameof(token)); |
|||
} |
|||
|
|||
if (string.IsNullOrEmpty(type)) |
|||
{ |
|||
throw new ArgumentException("The token type cannot be null or empty.", nameof(type)); |
|||
} |
|||
|
|||
token.Type = type; |
|||
|
|||
return Task.FromResult(0); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Updates an existing token.
|
|||
/// </summary>
|
|||
/// <param name="token">The token to update.</param>
|
|||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
|
|||
/// <returns>
|
|||
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation.
|
|||
/// </returns>
|
|||
public virtual async Task UpdateAsync([NotNull] TToken token, CancellationToken cancellationToken) |
|||
{ |
|||
if (token == null) |
|||
{ |
|||
throw new ArgumentNullException(nameof(token)); |
|||
} |
|||
|
|||
// Generate a new concurrency token and attach it
|
|||
// to the token before persisting the changes.
|
|||
token.ConcurrencyToken = Guid.NewGuid().ToString(); |
|||
|
|||
var database = await Context.GetDatabaseAsync(cancellationToken); |
|||
var collection = database.GetCollection<TToken>(Options.CurrentValue.TokensCollectionName); |
|||
|
|||
if ((await collection.ReplaceOneAsync(entity => |
|||
entity.Id == token.Id && |
|||
entity.ConcurrencyToken == token.ConcurrencyToken, token, null, cancellationToken)).MatchedCount == 0) |
|||
{ |
|||
throw new OpenIddictException(OpenIddictConstants.Exceptions.ConcurrencyError, new StringBuilder() |
|||
.AppendLine("The token was concurrently updated and cannot be persisted in its current state.") |
|||
.Append("Reload the token from the database and retry the operation.") |
|||
.ToString()); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
Loading…
Reference in new issue