From 347b75cb4da51776b7ef346778a237462e93c52d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Chalet?= Date: Thu, 16 Mar 2017 03:42:00 +0100 Subject: [PATCH] Add a new OpenIddictApplicationManager.UpdateAsync overload accepting a client secret and rework the validation logic --- .../Managers/OpenIddictApplicationManager.cs | 96 ++++++++++++------- .../Stores/IOpenIddictApplicationStore.cs | 2 +- .../Stores/OpenIddictApplicationStore.cs | 8 +- 3 files changed, 67 insertions(+), 39 deletions(-) diff --git a/src/OpenIddict.Core/Managers/OpenIddictApplicationManager.cs b/src/OpenIddict.Core/Managers/OpenIddictApplicationManager.cs index 1c6340fb..9e917f21 100644 --- a/src/OpenIddict.Core/Managers/OpenIddictApplicationManager.cs +++ b/src/OpenIddict.Core/Managers/OpenIddictApplicationManager.cs @@ -47,34 +47,16 @@ namespace OpenIddict.Core /// A that can be used to monitor the asynchronous operation, /// whose result returns the unique identifier associated with the application. /// - public virtual async Task CreateAsync([NotNull] TApplication application, CancellationToken cancellationToken) + public virtual Task CreateAsync([NotNull] TApplication application, CancellationToken cancellationToken) { - if (application == null) - { - throw new ArgumentNullException(nameof(application)); - } - - if (!string.IsNullOrEmpty(await Store.GetHashedSecretAsync(application, cancellationToken))) - { - throw new ArgumentException("The client secret hash cannot be directly set on the application entity. " + - "To create a confidential application, use the CreateAsync() overload accepting a secret parameter."); - } - - // If no client type was specified, assume it's a public application. - if (string.IsNullOrEmpty(await Store.GetClientTypeAsync(application, cancellationToken))) - { - await Store.SetClientTypeAsync(application, OpenIddictConstants.ClientTypes.Public, cancellationToken); - } - - await ValidateAsync(application, cancellationToken); - return await Store.CreateAsync(application, cancellationToken); + return CreateAsync(application, /* secret: */ null, cancellationToken); } /// - /// Creates a new confidential application, using the specified client secret. + /// Creates a new application. /// /// The application to create. - /// The client secret associated with the application. + /// The client secret associated with the application, if applicable. /// The that can be used to abort the operation. /// /// A that can be used to monitor the asynchronous operation, @@ -82,7 +64,7 @@ namespace OpenIddict.Core /// public virtual async Task CreateAsync( [NotNull] TApplication application, - [NotNull] string secret, CancellationToken cancellationToken) + [CanBeNull] string secret, CancellationToken cancellationToken) { if (application == null) { @@ -94,16 +76,32 @@ namespace OpenIddict.Core throw new ArgumentException("The client secret hash cannot be directly set on the application entity."); } + // If no client type was specified, assume it's a public application if no secret was provided. var type = await Store.GetClientTypeAsync(application, cancellationToken); - if (!string.IsNullOrEmpty(type) && - !string.Equals(type, OpenIddictConstants.ClientTypes.Confidential, StringComparison.OrdinalIgnoreCase)) + if (string.IsNullOrEmpty(type)) + { + await Store.SetClientTypeAsync(application, string.IsNullOrEmpty(secret) ? + OpenIddictConstants.ClientTypes.Public : + OpenIddictConstants.ClientTypes.Confidential, cancellationToken); + } + + if (string.IsNullOrEmpty(secret)) { - throw new InvalidOperationException("The client type must be set to 'confidential' when creating an application with a client secret." + - "To create a public application, use the CreateAsync() overload that doesn't take a secret parameter."); + // If the client is a confidential application, throw an + // exception as the client secret is required in this case. + if (string.Equals(type, OpenIddictConstants.ClientTypes.Confidential, StringComparison.OrdinalIgnoreCase)) + { + throw new InvalidOperationException("A client secret must be provided when creating a confidential application."); + } + + await Store.SetHashedSecretAsync(application, null, cancellationToken); + } + + else + { + await Store.SetHashedSecretAsync(application, Crypto.HashPassword(secret), cancellationToken); } - await Store.SetClientTypeAsync(application, OpenIddictConstants.ClientTypes.Confidential, cancellationToken); - await Store.SetHashedSecretAsync(application, Crypto.HashPassword(secret), cancellationToken); await ValidateAsync(application, cancellationToken); return await Store.CreateAsync(application, cancellationToken); @@ -347,7 +345,7 @@ namespace OpenIddict.Core /// /// A that can be used to monitor the asynchronous operation. /// - public virtual async Task SetClientSecretAsync([NotNull] TApplication application, [NotNull] string secret, CancellationToken cancellationToken) + public virtual async Task SetClientSecretAsync([NotNull] TApplication application, [CanBeNull] string secret, CancellationToken cancellationToken) { if (application == null) { @@ -356,10 +354,14 @@ namespace OpenIddict.Core if (string.IsNullOrEmpty(secret)) { - throw new ArgumentException("The client secret cannot be null or empty.", nameof(secret)); + await Store.SetHashedSecretAsync(application, null, cancellationToken); + } + + else + { + await Store.SetHashedSecretAsync(application, Crypto.HashPassword(secret), cancellationToken); } - await Store.SetHashedSecretAsync(application, Crypto.HashPassword(secret), cancellationToken); await UpdateAsync(application, cancellationToken); } @@ -382,6 +384,36 @@ namespace OpenIddict.Core await Store.UpdateAsync(application, cancellationToken); } + /// + /// Updates an existing application. + /// + /// The application to update. + /// The client secret associated with the application. + /// The that can be used to abort the operation. + /// + /// A that can be used to monitor the asynchronous operation. + /// + public virtual async Task UpdateAsync([NotNull] TApplication application, + [CanBeNull] string secret, CancellationToken cancellationToken) + { + if (application == null) + { + throw new ArgumentNullException(nameof(application)); + } + + if (string.IsNullOrEmpty(secret)) + { + await Store.SetHashedSecretAsync(application, null, cancellationToken); + } + + else + { + await Store.SetHashedSecretAsync(application, Crypto.HashPassword(secret), cancellationToken); + } + + await UpdateAsync(application, cancellationToken); + } + /// /// Validates the application to ensure it's in a consistent state. /// diff --git a/src/OpenIddict.Core/Stores/IOpenIddictApplicationStore.cs b/src/OpenIddict.Core/Stores/IOpenIddictApplicationStore.cs index c0e77d39..1a212ff0 100644 --- a/src/OpenIddict.Core/Stores/IOpenIddictApplicationStore.cs +++ b/src/OpenIddict.Core/Stores/IOpenIddictApplicationStore.cs @@ -178,7 +178,7 @@ namespace OpenIddict.Core /// /// A that can be used to monitor the asynchronous operation. /// - Task SetHashedSecretAsync([NotNull] TApplication application, [NotNull] string hash, CancellationToken cancellationToken); + Task SetHashedSecretAsync([NotNull] TApplication application, [CanBeNull] string hash, CancellationToken cancellationToken); /// /// Updates an existing application. diff --git a/src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictApplicationStore.cs b/src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictApplicationStore.cs index 78e34f7d..264489db 100644 --- a/src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictApplicationStore.cs +++ b/src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictApplicationStore.cs @@ -368,18 +368,14 @@ namespace OpenIddict.EntityFrameworkCore /// /// A that can be used to monitor the asynchronous operation. /// - public virtual Task SetHashedSecretAsync([NotNull] TApplication application, [NotNull] string hash, CancellationToken cancellationToken) + public virtual Task SetHashedSecretAsync([NotNull] TApplication application, + [CanBeNull] string hash, CancellationToken cancellationToken) { if (application == null) { throw new ArgumentNullException(nameof(application)); } - if (string.IsNullOrEmpty(hash)) - { - throw new ArgumentException("The client secret hash cannot be null or empty.", nameof(hash)); - } - application.ClientSecret = hash; return Task.FromResult(0);