Browse Source

Allow built-in scope validation to be partially executed when the degraded mode is enabled

pull/1088/head
Kévin Chalet 5 years ago
parent
commit
f54ba7f6dd
  1. 4
      src/OpenIddict.Abstractions/Resources/OpenIddictResources.resx
  2. 16
      src/OpenIddict.Server/OpenIddictServerHandlers.Authentication.cs
  3. 16
      src/OpenIddict.Server/OpenIddictServerHandlers.Device.cs
  4. 16
      src/OpenIddict.Server/OpenIddictServerHandlers.Exchange.cs
  5. 32
      test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Authentication.cs
  6. 4
      test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Exchange.cs

4
src/OpenIddict.Abstractions/Resources/OpenIddictResources.resx

@ -1776,6 +1776,10 @@ To register the OpenIddict core services, reference the 'OpenIddict.Core' packag
<value>The token shouldn't be null or empty at this point.</value> <value>The token shouldn't be null or empty at this point.</value>
<comment>{Locked}</comment> <comment>{Locked}</comment>
</data> </data>
<data name="ID5011" xml:space="preserve">
<value>The OpenIddict Core services should be registered.</value>
<comment>{Locked}</comment>
</data>
<data name="ID7000" xml:space="preserve"> <data name="ID7000" xml:space="preserve">
<value>An error occurred while validating the token '{Token}'.</value> <value>An error occurred while validating the token '{Token}'.</value>
<comment>{Locked}</comment> <comment>{Locked}</comment>

16
src/OpenIddict.Server/OpenIddictServerHandlers.Authentication.cs

@ -1080,13 +1080,15 @@ namespace OpenIddict.Server
/// <summary> /// <summary>
/// Contains the logic responsible of rejecting authorization requests that use unregistered scopes. /// Contains the logic responsible of rejecting authorization requests that use unregistered scopes.
/// Note: this handler is not used when the degraded mode is enabled or when scope validation is disabled. /// Note: this handler partially works with the degraded mode but is not used when scope validation is disabled.
/// </summary> /// </summary>
public class ValidateScopes : IOpenIddictServerHandler<ValidateAuthorizationRequestContext> public class ValidateScopes : IOpenIddictServerHandler<ValidateAuthorizationRequestContext>
{ {
private readonly IOpenIddictScopeManager _scopeManager; private readonly IOpenIddictScopeManager? _scopeManager;
public ValidateScopes() => throw new InvalidOperationException(SR.GetResourceString(SR.ID1015)); public ValidateScopes()
{
}
public ValidateScopes(IOpenIddictScopeManager scopeManager) public ValidateScopes(IOpenIddictScopeManager scopeManager)
=> _scopeManager = scopeManager; => _scopeManager = scopeManager;
@ -1097,7 +1099,6 @@ namespace OpenIddict.Server
public static OpenIddictServerHandlerDescriptor Descriptor { get; } public static OpenIddictServerHandlerDescriptor Descriptor { get; }
= OpenIddictServerHandlerDescriptor.CreateBuilder<ValidateAuthorizationRequestContext>() = OpenIddictServerHandlerDescriptor.CreateBuilder<ValidateAuthorizationRequestContext>()
.AddFilter<RequireScopeValidationEnabled>() .AddFilter<RequireScopeValidationEnabled>()
.AddFilter<RequireDegradedModeDisabled>()
.UseScopedHandler<ValidateScopes>() .UseScopedHandler<ValidateScopes>()
.SetOrder(ValidateClientRedirectUri.Descriptor.Order + 1_000) .SetOrder(ValidateClientRedirectUri.Descriptor.Order + 1_000)
.SetType(OpenIddictServerHandlerType.BuiltIn) .SetType(OpenIddictServerHandlerType.BuiltIn)
@ -1115,8 +1116,13 @@ namespace OpenIddict.Server
var scopes = new HashSet<string>(context.Request.GetScopes(), StringComparer.Ordinal); var scopes = new HashSet<string>(context.Request.GetScopes(), StringComparer.Ordinal);
scopes.ExceptWith(context.Options.Scopes); scopes.ExceptWith(context.Options.Scopes);
if (scopes.Count != 0) // Note: the remaining scopes are only checked if the degraded mode was not enabled,
// as this requires using the scope manager, which is never used with the degraded mode,
// even if the service was registered and resolved from the dependency injection container.
if (scopes.Count != 0 && !context.Options.EnableDegradedMode)
{ {
Debug.Assert(_scopeManager != null, SR.GetResourceString(SR.ID5011));
await foreach (var scope in _scopeManager.FindByNamesAsync(scopes.ToImmutableArray())) await foreach (var scope in _scopeManager.FindByNamesAsync(scopes.ToImmutableArray()))
{ {
var name = await _scopeManager.GetNameAsync(scope); var name = await _scopeManager.GetNameAsync(scope);

16
src/OpenIddict.Server/OpenIddictServerHandlers.Device.cs

@ -364,13 +364,15 @@ namespace OpenIddict.Server
/// <summary> /// <summary>
/// Contains the logic responsible of rejecting authorization requests that use unregistered scopes. /// Contains the logic responsible of rejecting authorization requests that use unregistered scopes.
/// Note: this handler is not used when the degraded mode is enabled or when scope validation is disabled. /// Note: this handler partially works with the degraded mode but is not used when scope validation is disabled.
/// </summary> /// </summary>
public class ValidateScopes : IOpenIddictServerHandler<ValidateDeviceRequestContext> public class ValidateScopes : IOpenIddictServerHandler<ValidateDeviceRequestContext>
{ {
private readonly IOpenIddictScopeManager _scopeManager; private readonly IOpenIddictScopeManager? _scopeManager;
public ValidateScopes() => throw new InvalidOperationException(SR.GetResourceString(SR.ID1015)); public ValidateScopes()
{
}
public ValidateScopes(IOpenIddictScopeManager scopeManager) public ValidateScopes(IOpenIddictScopeManager scopeManager)
=> _scopeManager = scopeManager; => _scopeManager = scopeManager;
@ -381,7 +383,6 @@ namespace OpenIddict.Server
public static OpenIddictServerHandlerDescriptor Descriptor { get; } public static OpenIddictServerHandlerDescriptor Descriptor { get; }
= OpenIddictServerHandlerDescriptor.CreateBuilder<ValidateDeviceRequestContext>() = OpenIddictServerHandlerDescriptor.CreateBuilder<ValidateDeviceRequestContext>()
.AddFilter<RequireScopeValidationEnabled>() .AddFilter<RequireScopeValidationEnabled>()
.AddFilter<RequireDegradedModeDisabled>()
.UseScopedHandler<ValidateScopes>() .UseScopedHandler<ValidateScopes>()
.SetOrder(ValidateClientIdParameter.Descriptor.Order + 1_000) .SetOrder(ValidateClientIdParameter.Descriptor.Order + 1_000)
.SetType(OpenIddictServerHandlerType.BuiltIn) .SetType(OpenIddictServerHandlerType.BuiltIn)
@ -399,8 +400,13 @@ namespace OpenIddict.Server
var scopes = new HashSet<string>(context.Request.GetScopes(), StringComparer.Ordinal); var scopes = new HashSet<string>(context.Request.GetScopes(), StringComparer.Ordinal);
scopes.ExceptWith(context.Options.Scopes); scopes.ExceptWith(context.Options.Scopes);
if (scopes.Count != 0) // Note: the remaining scopes are only checked if the degraded mode was not enabled,
// as this requires using the scope manager, which is never used with the degraded mode,
// even if the service was registered and resolved from the dependency injection container.
if (scopes.Count != 0 && !context.Options.EnableDegradedMode)
{ {
Debug.Assert(_scopeManager != null, SR.GetResourceString(SR.ID5011));
await foreach (var scope in _scopeManager.FindByNamesAsync(scopes.ToImmutableArray())) await foreach (var scope in _scopeManager.FindByNamesAsync(scopes.ToImmutableArray()))
{ {
var name = await _scopeManager.GetNameAsync(scope); var name = await _scopeManager.GetNameAsync(scope);

16
src/OpenIddict.Server/OpenIddictServerHandlers.Exchange.cs

@ -645,13 +645,15 @@ namespace OpenIddict.Server
/// <summary> /// <summary>
/// Contains the logic responsible of rejecting authorization requests that use unregistered scopes. /// Contains the logic responsible of rejecting authorization requests that use unregistered scopes.
/// Note: this handler is not used when the degraded mode is enabled or when scope validation is disabled. /// Note: this handler partially works with the degraded mode but is not used when scope validation is disabled.
/// </summary> /// </summary>
public class ValidateScopes : IOpenIddictServerHandler<ValidateTokenRequestContext> public class ValidateScopes : IOpenIddictServerHandler<ValidateTokenRequestContext>
{ {
private readonly IOpenIddictScopeManager _scopeManager; private readonly IOpenIddictScopeManager? _scopeManager;
public ValidateScopes() => throw new InvalidOperationException(SR.GetResourceString(SR.ID1015)); public ValidateScopes()
{
}
public ValidateScopes(IOpenIddictScopeManager scopeManager) public ValidateScopes(IOpenIddictScopeManager scopeManager)
=> _scopeManager = scopeManager; => _scopeManager = scopeManager;
@ -662,7 +664,6 @@ namespace OpenIddict.Server
public static OpenIddictServerHandlerDescriptor Descriptor { get; } public static OpenIddictServerHandlerDescriptor Descriptor { get; }
= OpenIddictServerHandlerDescriptor.CreateBuilder<ValidateTokenRequestContext>() = OpenIddictServerHandlerDescriptor.CreateBuilder<ValidateTokenRequestContext>()
.AddFilter<RequireScopeValidationEnabled>() .AddFilter<RequireScopeValidationEnabled>()
.AddFilter<RequireDegradedModeDisabled>()
.UseScopedHandler<ValidateScopes>() .UseScopedHandler<ValidateScopes>()
.SetOrder(ValidatePasswordParameters.Descriptor.Order + 1_000) .SetOrder(ValidatePasswordParameters.Descriptor.Order + 1_000)
.SetType(OpenIddictServerHandlerType.BuiltIn) .SetType(OpenIddictServerHandlerType.BuiltIn)
@ -680,8 +681,13 @@ namespace OpenIddict.Server
var scopes = new HashSet<string>(context.Request.GetScopes(), StringComparer.Ordinal); var scopes = new HashSet<string>(context.Request.GetScopes(), StringComparer.Ordinal);
scopes.ExceptWith(context.Options.Scopes); scopes.ExceptWith(context.Options.Scopes);
if (scopes.Count != 0) // Note: the remaining scopes are only checked if the degraded mode was not enabled,
// as this requires using the scope manager, which is never used with the degraded mode,
// even if the service was registered and resolved from the dependency injection container.
if (scopes.Count != 0 && !context.Options.EnableDegradedMode)
{ {
Debug.Assert(_scopeManager != null, SR.GetResourceString(SR.ID5011));
await foreach (var scope in _scopeManager.FindByNamesAsync(scopes.ToImmutableArray())) await foreach (var scope in _scopeManager.FindByNamesAsync(scopes.ToImmutableArray()))
{ {
var name = await _scopeManager.GetNameAsync(scope); var name = await _scopeManager.GetNameAsync(scope);

32
test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Authentication.cs

@ -615,38 +615,8 @@ namespace OpenIddict.Server.IntegrationTests
// Arrange // Arrange
await using var server = await CreateServerAsync(options => await using var server = await CreateServerAsync(options =>
{ {
options.EnableDegradedMode();
options.RegisterScopes("registered_scope"); options.RegisterScopes("registered_scope");
options.SetRevocationEndpointUris(Array.Empty<Uri>());
options.DisableTokenStorage();
options.DisableSlidingRefreshTokenExpiration();
options.Services.AddSingleton(CreateApplicationManager(mock =>
{
var application = new OpenIddictApplication();
mock.Setup(manager => manager.FindByClientIdAsync("Fabrikam", It.IsAny<CancellationToken>()))
.ReturnsAsync(application);
mock.Setup(manager => manager.ValidateRedirectUriAsync(application, "http://www.fabrikam.com/path", It.IsAny<CancellationToken>()))
.ReturnsAsync(true);
mock.Setup(manager => manager.HasClientTypeAsync(application, ClientTypes.Public, It.IsAny<CancellationToken>()))
.ReturnsAsync(true);
}));
options.Services.AddSingleton(CreateApplicationManager(mock =>
{
var application = new OpenIddictApplication();
mock.Setup(manager => manager.FindByClientIdAsync("Fabrikam", It.IsAny<CancellationToken>()))
.ReturnsAsync(application);
mock.Setup(manager => manager.ValidateRedirectUriAsync(application, "http://www.fabrikam.com/path", It.IsAny<CancellationToken>()))
.ReturnsAsync(true);
mock.Setup(manager => manager.HasClientTypeAsync(application, ClientTypes.Public, It.IsAny<CancellationToken>()))
.ReturnsAsync(true);
}));
options.AddEventHandler<HandleAuthorizationRequestContext>(builder => options.AddEventHandler<HandleAuthorizationRequestContext>(builder =>
builder.UseInlineHandler(context => builder.UseInlineHandler(context =>

4
test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Exchange.cs

@ -886,6 +886,7 @@ namespace OpenIddict.Server.IntegrationTests
await using var server = await CreateServerAsync(options => await using var server = await CreateServerAsync(options =>
{ {
options.EnableDegradedMode(); options.EnableDegradedMode();
options.RegisterScopes(Scopes.Phone, Scopes.Profile);
options.AddEventHandler<ProcessAuthenticationContext>(builder => options.AddEventHandler<ProcessAuthenticationContext>(builder =>
{ {
@ -930,6 +931,7 @@ namespace OpenIddict.Server.IntegrationTests
await using var server = await CreateServerAsync(options => await using var server = await CreateServerAsync(options =>
{ {
options.EnableDegradedMode(); options.EnableDegradedMode();
options.RegisterScopes(Scopes.Phone, Scopes.Profile);
options.AddEventHandler<ProcessAuthenticationContext>(builder => options.AddEventHandler<ProcessAuthenticationContext>(builder =>
{ {
@ -974,6 +976,7 @@ namespace OpenIddict.Server.IntegrationTests
await using var server = await CreateServerAsync(options => await using var server = await CreateServerAsync(options =>
{ {
options.EnableDegradedMode(); options.EnableDegradedMode();
options.RegisterScopes(Scopes.Phone, Scopes.Profile);
options.AddEventHandler<ProcessAuthenticationContext>(builder => options.AddEventHandler<ProcessAuthenticationContext>(builder =>
{ {
@ -1016,6 +1019,7 @@ namespace OpenIddict.Server.IntegrationTests
await using var server = await CreateServerAsync(options => await using var server = await CreateServerAsync(options =>
{ {
options.EnableDegradedMode(); options.EnableDegradedMode();
options.RegisterScopes(Scopes.Phone, Scopes.Profile);
options.AddEventHandler<ProcessAuthenticationContext>(builder => options.AddEventHandler<ProcessAuthenticationContext>(builder =>
{ {

Loading…
Cancel
Save