Browse Source

Introduce OpenIddictServerBuilder.SetRequestCachingPolicy()

pull/662/head
Kévin Chalet 8 years ago
parent
commit
aa79131335
  1. 5
      src/OpenIddict.Core/Managers/OpenIddictScopeManager.cs
  2. 5
      src/OpenIddict.Server/Internal/OpenIddictServerInitializer.cs
  3. 7
      src/OpenIddict.Server/Internal/OpenIddictServerProvider.Authentication.cs
  4. 7
      src/OpenIddict.Server/Internal/OpenIddictServerProvider.Session.cs
  5. 18
      src/OpenIddict.Server/OpenIddictServerBuilder.cs
  6. 10
      src/OpenIddict.Server/OpenIddictServerOptions.cs
  7. 24
      test/OpenIddict.Server.Tests/Internal/OpenIddictServerInitializerTests.cs
  8. 11
      test/OpenIddict.Server.Tests/Internal/OpenIddictServerProviderTests.Authentication.cs
  9. 11
      test/OpenIddict.Server.Tests/Internal/OpenIddictServerProviderTests.Session.cs
  10. 49
      test/OpenIddict.Server.Tests/OpenIddictServerBuilderTests.cs

5
src/OpenIddict.Core/Managers/OpenIddictScopeManager.cs

@ -211,6 +211,11 @@ namespace OpenIddict.Core
public virtual async Task<ImmutableArray<TScope>> FindByNamesAsync(
ImmutableArray<string> names, CancellationToken cancellationToken = default)
{
if (names.IsDefaultOrEmpty)
{
return ImmutableArray.Create<TScope>();
}
if (names.Any(name => string.IsNullOrEmpty(name)))
{
throw new ArgumentException("Scope names cannot be null or empty.", nameof(names));

5
src/OpenIddict.Server/Internal/OpenIddictServerInitializer.cs

@ -152,6 +152,11 @@ namespace OpenIddict.Server.Internal
"The token endpoint must be enabled to use the authorization code, client credentials, password and refresh token flows.");
}
if (options.EnableRequestCaching && options.RequestCachingPolicy == null)
{
throw new InvalidOperationException("A caching policy must be specified when enabling request caching.");
}
if (options.RevocationEndpointPath.HasValue && options.DisableTokenStorage)
{
throw new InvalidOperationException("The revocation endpoint cannot be enabled when token storage is disabled.");

7
src/OpenIddict.Server/Internal/OpenIddictServerProvider.Authentication.cs

@ -15,7 +15,6 @@ using AspNet.Security.OpenIdConnect.Server;
using JetBrains.Annotations;
using Microsoft.AspNetCore.Diagnostics;
using Microsoft.AspNetCore.WebUtilities;
using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.Logging;
using Microsoft.IdentityModel.Tokens;
using Newtonsoft.Json;
@ -469,11 +468,7 @@ namespace OpenIddict.Server.Internal
// to avoid collisions with the other types of cached requests.
var key = OpenIddictConstants.Environment.AuthorizationRequest + context.Request.RequestId;
await options.Cache.SetAsync(key, stream.ToArray(), new DistributedCacheEntryOptions
{
AbsoluteExpiration = context.Options.SystemClock.UtcNow + TimeSpan.FromMinutes(30),
SlidingExpiration = TimeSpan.FromMinutes(10)
});
await options.Cache.SetAsync(key, stream.ToArray(), options.RequestCachingPolicy);
// Create a new authorization request containing only the request_id parameter.
var address = QueryHelpers.AddQueryString(

7
src/OpenIddict.Server/Internal/OpenIddictServerProvider.Session.cs

@ -12,7 +12,6 @@ using AspNet.Security.OpenIdConnect.Server;
using JetBrains.Annotations;
using Microsoft.AspNetCore.Diagnostics;
using Microsoft.AspNetCore.WebUtilities;
using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.Logging;
using Microsoft.IdentityModel.Tokens;
using Newtonsoft.Json;
@ -187,11 +186,7 @@ namespace OpenIddict.Server.Internal
// to avoid collisions with the other types of cached requests.
var key = OpenIddictConstants.Environment.LogoutRequest + context.Request.RequestId;
await options.Cache.SetAsync(key, stream.ToArray(), new DistributedCacheEntryOptions
{
AbsoluteExpiration = context.Options.SystemClock.UtcNow + TimeSpan.FromMinutes(30),
SlidingExpiration = TimeSpan.FromMinutes(10)
});
await options.Cache.SetAsync(key, stream.ToArray(), options.RequestCachingPolicy);
// Create a new logout request containing only the request_id parameter.
var address = QueryHelpers.AddQueryString(

18
src/OpenIddict.Server/OpenIddictServerBuilder.cs

@ -18,6 +18,7 @@ using AspNet.Security.OpenIdConnect.Primitives;
using JetBrains.Annotations;
using Microsoft.AspNetCore.DataProtection;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Caching.Distributed;
using Microsoft.IdentityModel.Tokens;
using OpenIddict.Server;
@ -741,6 +742,23 @@ namespace Microsoft.Extensions.DependencyInjection
public OpenIddictServerBuilder SetRefreshTokenLifetime([CanBeNull] TimeSpan? lifetime)
=> Configure(options => options.RefreshTokenLifetime = lifetime);
/// <summary>
/// Sets the caching policy used to determine how long the authorization and
/// end session requests should be cached by the distributed cache implementation.
/// Note: the specified policy is only used when request caching is explicitly enabled.
/// </summary>
/// <param name="policy">The request caching policy.</param>
/// <returns>The <see cref="OpenIddictServerBuilder"/>.</returns>
public OpenIddictServerBuilder SetRequestCachingPolicy([NotNull] DistributedCacheEntryOptions policy)
{
if (policy == null)
{
throw new ArgumentNullException(nameof(policy));
}
return Configure(options => options.RequestCachingPolicy = policy);
}
/// <summary>
/// Sets the issuer address, which is used as the base address
/// for the endpoint URIs returned from the discovery endpoint.

10
src/OpenIddict.Server/OpenIddictServerOptions.cs

@ -113,6 +113,16 @@ namespace OpenIddict.Server
/// </summary>
public RandomNumberGenerator RandomNumberGenerator { get; set; } = RandomNumberGenerator.Create();
/// <summary>
/// Gets or sets the caching policy used to determine how long the authorization
/// and end session requests should be cached by the distributed cache implementation.
/// </summary>
public DistributedCacheEntryOptions RequestCachingPolicy { get; set; } = new DistributedCacheEntryOptions
{
AbsoluteExpirationRelativeToNow = TimeSpan.FromHours(1),
SlidingExpiration = TimeSpan.FromMinutes(30)
};
/// <summary>
/// Gets the OAuth2/OpenID Connect scopes enabled for this application.
/// </summary>

24
test/OpenIddict.Server.Tests/Internal/OpenIddictServerInitializerTests.cs

@ -164,6 +164,30 @@ namespace OpenIddict.Server.Internal.Tests
"client credentials, password and refresh token flows.", exception.Message);
}
[Fact]
public async Task PostConfigure_ThrowsAnExceptionWhenCachingPolicyIsNullAndRequestCachingEnabled()
{
// Arrange
var server = CreateAuthorizationServer(builder =>
{
builder.EnableAuthorizationEndpoint("/connect/authorize")
.AllowImplicitFlow()
.EnableRequestCaching();
builder.Configure(options => options.RequestCachingPolicy = null);
});
var client = new OpenIdConnectClient(server.CreateClient());
// Act and assert
var exception = await Assert.ThrowsAsync<InvalidOperationException>(delegate
{
return client.GetAsync("/");
});
Assert.Equal("A caching policy must be specified when enabling request caching.", exception.Message);
}
[Fact]
public async Task PostConfigure_ThrowsAnExceptionWhenTokenStorageIsDisabled()
{

11
test/OpenIddict.Server.Tests/Internal/OpenIddictServerProviderTests.Authentication.cs

@ -4,6 +4,7 @@
* the license and the contributors participating to this project.
*/
using System;
using System.Collections.Immutable;
using System.IO;
using System.Security.Cryptography;
@ -828,6 +829,12 @@ namespace OpenIddict.Server.Internal.Tests
builder.EnableRequestCaching();
builder.SetRequestCachingPolicy(new DistributedCacheEntryOptions
{
AbsoluteExpirationRelativeToNow = TimeSpan.FromDays(42),
SlidingExpiration = TimeSpan.FromSeconds(42)
});
builder.Configure(options => options.RandomNumberGenerator = generator.Object);
});
@ -850,7 +857,9 @@ namespace OpenIddict.Server.Internal.Tests
cache.Verify(mock => mock.SetAsync(
OpenIddictConstants.Environment.AuthorizationRequest + identifier,
It.IsAny<byte[]>(),
It.IsAny<DistributedCacheEntryOptions>(),
It.Is<DistributedCacheEntryOptions>(options =>
options.AbsoluteExpirationRelativeToNow == TimeSpan.FromDays(42) &&
options.SlidingExpiration == TimeSpan.FromSeconds(42)),
It.IsAny<CancellationToken>()), Times.Once());
generator.Verify(mock => mock.GetBytes(It.Is<byte[]>(bytes => bytes.Length == 256 / 8)), Times.Once());

11
test/OpenIddict.Server.Tests/Internal/OpenIddictServerProviderTests.Session.cs

@ -4,6 +4,7 @@
* the license and the contributors participating to this project.
*/
using System;
using System.Collections.Immutable;
using System.Security.Cryptography;
using System.Threading;
@ -136,6 +137,12 @@ namespace OpenIddict.Server.Internal.Tests
builder.EnableRequestCaching();
builder.SetRequestCachingPolicy(new DistributedCacheEntryOptions
{
AbsoluteExpirationRelativeToNow = TimeSpan.FromDays(42),
SlidingExpiration = TimeSpan.FromSeconds(42)
});
builder.Configure(options => options.RandomNumberGenerator = generator.Object);
});
@ -156,7 +163,9 @@ namespace OpenIddict.Server.Internal.Tests
cache.Verify(mock => mock.SetAsync(
OpenIddictConstants.Environment.LogoutRequest + identifier,
It.IsAny<byte[]>(),
It.IsAny<DistributedCacheEntryOptions>(),
It.Is<DistributedCacheEntryOptions>(options =>
options.AbsoluteExpirationRelativeToNow == TimeSpan.FromDays(42) &&
options.SlidingExpiration == TimeSpan.FromSeconds(42)),
It.IsAny<CancellationToken>()), Times.Once());
generator.Verify(mock => mock.GetBytes(It.Is<byte[]>(bytes => bytes.Length == 256 / 8)), Times.Once());

49
test/OpenIddict.Server.Tests/OpenIddictServerBuilderTests.cs

@ -12,6 +12,7 @@ using System.Threading.Tasks;
using AspNet.Security.OpenIdConnect.Primitives;
using Microsoft.AspNetCore.DataProtection;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Microsoft.IdentityModel.Tokens;
@ -713,6 +714,54 @@ namespace OpenIddict.Server.Tests
Assert.Null(options.RefreshTokenLifetime);
}
[Fact]
public void SetRequestCachingPolicy_ThrowsAnExceptionForNullPolicy()
{
// Arrange
var services = CreateServices();
var builder = CreateBuilder(services);
// Act and assert
var exception = Assert.Throws<ArgumentNullException>(() => builder.SetRequestCachingPolicy(null));
Assert.Equal("policy", exception.ParamName);
}
[Fact]
public void SetRequestCachingPolicy_PolicyIsUpdated()
{
// Arrange
var services = CreateServices();
var builder = CreateBuilder(services);
var policy = new DistributedCacheEntryOptions
{
AbsoluteExpirationRelativeToNow = TimeSpan.FromDays(42),
SlidingExpiration = TimeSpan.FromSeconds(42)
};
// Act
builder.SetRequestCachingPolicy(policy);
var options = GetOptions(services);
// Assert
Assert.Same(policy, options.RequestCachingPolicy);
}
[Fact]
public void SetIssuer_ThrowsAnExceptionForNullIssuer()
{
// Arrange
var services = CreateServices();
var builder = CreateBuilder(services);
// Act and assert
var exception = Assert.Throws<ArgumentNullException>(() => builder.SetIssuer(null));
Assert.Equal("address", exception.ParamName);
}
[Fact]
public void SetIssuer_AddressIsReplaced()
{

Loading…
Cancel
Save