From 98651b9e34ed5e6103bd30db7a57ee5ce54e0f0a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Chalet?= Date: Mon, 29 Jan 2018 19:52:34 +0100 Subject: [PATCH] Update OpenIddictApplication.PostLogoutRedirectUris/RedirectUris and OpenIddictAuthorization.Scopes to be stored as JSON arrays --- .../Stores/OpenIddictApplicationStore.cs | 98 +++++-------------- .../Stores/OpenIddictAuthorizationStore.cs | 18 +--- .../OpenIddictApplication.cs | 10 +- .../OpenIddictAuthorization.cs | 4 +- 4 files changed, 30 insertions(+), 100 deletions(-) diff --git a/src/OpenIddict.Core/Stores/OpenIddictApplicationStore.cs b/src/OpenIddict.Core/Stores/OpenIddictApplicationStore.cs index 1e9002a9..7596af58 100644 --- a/src/OpenIddict.Core/Stores/OpenIddictApplicationStore.cs +++ b/src/OpenIddict.Core/Stores/OpenIddictApplicationStore.cs @@ -140,40 +140,27 @@ namespace OpenIddict.Core throw new ArgumentException("The address cannot be null or empty.", nameof(address)); } - // To optimize the efficiency of the query, only applications whose stringified - // LogoutRedirectUris property contains the specified address are returned. Once the - // applications are retrieved, the LogoutRedirectUri property is manually split. + // To optimize the efficiency of the query a bit, only applications whose stringified + // PostLogoutRedirectUris contains the specified URL are returned. Once the applications + // are retrieved, a second pass is made to ensure only valid elements are returned. + // Implementers that use this method in a hot path may want to override this method + // to use SQL Server 2016 functions like JSON_VALUE to make the query more efficient. IQueryable Query(IQueryable applications, string uri) => from application in applications where application.PostLogoutRedirectUris.Contains(uri) select application; - var candidates = await ListAsync((applications, uri) => Query(applications, uri), address, cancellationToken); - if (candidates.IsDefaultOrEmpty) - { - return ImmutableArray.Create(); - } + var builder = ImmutableArray.CreateBuilder(); - var builder = ImmutableArray.CreateBuilder(0); - - foreach (var candidate in candidates) + foreach (var application in await ListAsync((applications, uri) => Query(applications, uri), address, cancellationToken)) { - var uris = candidate.PostLogoutRedirectUris?.Split( - new[] { OpenIddictConstants.Separators.Space }, - StringSplitOptions.RemoveEmptyEntries); - - if (uris == null) - { - continue; - } - - foreach (var uri in uris) + foreach (var uri in await GetPostLogoutRedirectUrisAsync(application, cancellationToken)) { // Note: the post_logout_redirect_uri must be compared // using case-sensitive "Simple String Comparison". if (string.Equals(uri, address, StringComparison.Ordinal)) { - builder.Add(candidate); + builder.Add(application); break; } @@ -199,40 +186,27 @@ namespace OpenIddict.Core throw new ArgumentException("The address cannot be null or empty.", nameof(address)); } - // To optimize the efficiency of the query, only applications whose stringified - // RedirectUris property contains the specified address are returned. Once the - // applications are retrieved, the RedirectUri property is manually split. + // To optimize the efficiency of the query a bit, only applications whose stringified + // RedirectUris property contains the specified URL are returned. Once the applications + // are retrieved, a second pass is made to ensure only valid elements are returned. + // Implementers that use this method in a hot path may want to override this method + // to use SQL Server 2016 functions like JSON_VALUE to make the query more efficient. IQueryable Query(IQueryable applications, string uri) => from application in applications where application.RedirectUris.Contains(uri) select application; - var candidates = await ListAsync((applications, uri) => Query(applications, uri), address, cancellationToken); - if (candidates.IsDefaultOrEmpty) - { - return ImmutableArray.Create(); - } + var builder = ImmutableArray.CreateBuilder(); - var builder = ImmutableArray.CreateBuilder(0); - - foreach (var candidate in candidates) + foreach (var application in await ListAsync((applications, uri) => Query(applications, uri), address, cancellationToken)) { - var uris = candidate.RedirectUris?.Split( - new[] { OpenIddictConstants.Separators.Space }, - StringSplitOptions.RemoveEmptyEntries); - - if (uris == null) - { - continue; - } - - foreach (var uri in uris) + foreach (var uri in await GetRedirectUrisAsync(application, cancellationToken)) { // Note: the redirect_uri must be compared using case-sensitive "Simple String Comparison". // See http://openid.net/specs/openid-connect-core-1_0.html#AuthRequest for more information. if (string.Equals(uri, address, StringComparison.Ordinal)) { - builder.Add(candidate); + builder.Add(application); break; } @@ -400,11 +374,7 @@ namespace OpenIddict.Core return Task.FromResult(ImmutableArray.Create()); } - var uris = application.PostLogoutRedirectUris.Split( - new[] { OpenIddictConstants.Separators.Space }, - StringSplitOptions.RemoveEmptyEntries); - - return Task.FromResult(ImmutableArray.Create(uris)); + return Task.FromResult(JArray.Parse(application.PostLogoutRedirectUris).Select(element => (string) element).ToImmutableArray()); } /// @@ -452,11 +422,7 @@ namespace OpenIddict.Core return Task.FromResult(ImmutableArray.Create()); } - var uris = application.RedirectUris.Split( - new[] { OpenIddictConstants.Separators.Space }, - StringSplitOptions.RemoveEmptyEntries); - - return Task.FromResult(ImmutableArray.Create(uris)); + return Task.FromResult(JArray.Parse(application.RedirectUris).Select(element => (string) element).ToImmutableArray()); } /// @@ -659,17 +625,7 @@ namespace OpenIddict.Core return Task.CompletedTask; } - if (addresses.Any(address => string.IsNullOrEmpty(address))) - { - throw new ArgumentException("Callback addresses cannot be null or empty.", nameof(addresses)); - } - - if (addresses.Any(address => address.Contains(OpenIddictConstants.Separators.Space))) - { - throw new ArgumentException("Callback addresses cannot contain spaces.", nameof(addresses)); - } - - application.PostLogoutRedirectUris = string.Join(OpenIddictConstants.Separators.Space, addresses); + application.PostLogoutRedirectUris = new JArray(addresses.ToArray()).ToString(Formatting.None); return Task.CompletedTask; } @@ -726,17 +682,7 @@ namespace OpenIddict.Core return Task.CompletedTask; } - if (addresses.Any(address => string.IsNullOrEmpty(address))) - { - throw new ArgumentException("Callback addresses cannot be null or empty.", nameof(addresses)); - } - - if (addresses.Any(address => address.Contains(OpenIddictConstants.Separators.Space))) - { - throw new ArgumentException("Callback addresses cannot contain spaces.", nameof(addresses)); - } - - application.RedirectUris = string.Join(OpenIddictConstants.Separators.Space, addresses); + application.RedirectUris = new JArray(addresses.ToArray()).ToString(Formatting.None); return Task.CompletedTask; } diff --git a/src/OpenIddict.Core/Stores/OpenIddictAuthorizationStore.cs b/src/OpenIddict.Core/Stores/OpenIddictAuthorizationStore.cs index da5493fd..520053ba 100644 --- a/src/OpenIddict.Core/Stores/OpenIddictAuthorizationStore.cs +++ b/src/OpenIddict.Core/Stores/OpenIddictAuthorizationStore.cs @@ -246,11 +246,7 @@ namespace OpenIddict.Core return Task.FromResult(ImmutableArray.Create()); } - var scopes = authorization.Scopes.Split( - new[] { OpenIddictConstants.Separators.Space }, - StringSplitOptions.RemoveEmptyEntries); - - return Task.FromResult(ImmutableArray.Create(scopes)); + return Task.FromResult(JArray.Parse(authorization.Scopes).Select(element => (string) element).ToImmutableArray()); } /// @@ -465,17 +461,7 @@ namespace OpenIddict.Core return Task.CompletedTask; } - if (scopes.Any(scope => string.IsNullOrEmpty(scope))) - { - throw new ArgumentException("Scopes cannot be null or empty.", nameof(authorization)); - } - - if (scopes.Any(scope => scope.Contains(OpenIddictConstants.Separators.Space))) - { - throw new ArgumentException("Scopes cannot contain spaces.", nameof(authorization)); - } - - authorization.Scopes = string.Join(OpenIddictConstants.Separators.Space, scopes); + authorization.Scopes = new JArray(scopes.ToArray()).ToString(Formatting.None); return Task.CompletedTask; } diff --git a/src/OpenIddict.Models/OpenIddictApplication.cs b/src/OpenIddict.Models/OpenIddictApplication.cs index f5d6b0d6..99ae4588 100644 --- a/src/OpenIddict.Models/OpenIddictApplication.cs +++ b/src/OpenIddict.Models/OpenIddictApplication.cs @@ -75,9 +75,8 @@ namespace OpenIddict.Models public virtual string Permissions { get; set; } /// - /// Gets or sets the logout callback URLs - /// associated with the current application, - /// stored as a unique space-separated string. + /// Gets or sets the logout callback URLs associated with + /// the current application, serialized as a JSON array. /// public virtual string PostLogoutRedirectUris { get; set; } @@ -88,9 +87,8 @@ namespace OpenIddict.Models public virtual string Properties { get; set; } /// - /// Gets or sets the callback URLs - /// associated with the current application, - /// stored as a unique space-separated string. + /// Gets or sets the callback URLs associated with the + /// current application, serialized as a JSON array. /// public virtual string RedirectUris { get; set; } diff --git a/src/OpenIddict.Models/OpenIddictAuthorization.cs b/src/OpenIddict.Models/OpenIddictAuthorization.cs index 273b43a1..b975e9be 100644 --- a/src/OpenIddict.Models/OpenIddictAuthorization.cs +++ b/src/OpenIddict.Models/OpenIddictAuthorization.cs @@ -56,8 +56,8 @@ namespace OpenIddict.Models public virtual string Properties { get; set; } /// - /// Gets or sets the space-delimited scopes - /// associated with the current authorization. + /// Gets or sets the scopes associated with the current + /// authorization, serialized as a JSON array. /// public virtual string Scopes { get; set; }