-
Notifications
You must be signed in to change notification settings - Fork 77
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #135 from DuendeSoftware/brock/skip_response_handling
add a skip response handling metadata flag
- Loading branch information
Showing
9 changed files
with
380 additions
and
82 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
// Copyright (c) Duende Software. All rights reserved. | ||
// See LICENSE in the project root for license information. | ||
|
||
using Microsoft.Extensions.DependencyInjection; | ||
using System; | ||
using System.Linq; | ||
|
||
namespace Microsoft.AspNetCore.Builder; | ||
|
||
/// <summary> | ||
/// Extension methods for the BFF DI services | ||
/// </summary> | ||
static class DecoratorServiceCollectionExtensions | ||
{ | ||
internal static void AddTransientDecorator<TService, TImplementation>(this IServiceCollection services) | ||
where TService : class | ||
where TImplementation : class, TService | ||
{ | ||
services.AddDecorator<TService>(); | ||
services.AddTransient<TService, TImplementation>(); | ||
} | ||
|
||
internal static void AddDecorator<TService>(this IServiceCollection services) | ||
{ | ||
var registration = services.LastOrDefault(x => x.ServiceType == typeof(TService)); | ||
if (registration == null) | ||
{ | ||
throw new InvalidOperationException("Service type: " + typeof(TService).Name + " not registered."); | ||
} | ||
if (services.Any(x => x.ServiceType == typeof(Decorator<TService>))) | ||
{ | ||
throw new InvalidOperationException("Decorator already registered for type: " + typeof(TService).Name + "."); | ||
} | ||
|
||
services.Remove(registration); | ||
|
||
if (registration.ImplementationInstance != null) | ||
{ | ||
var type = registration.ImplementationInstance.GetType(); | ||
var innerType = typeof(Decorator<,>).MakeGenericType(typeof(TService), type); | ||
services.Add(new ServiceDescriptor(typeof(Decorator<TService>), innerType, ServiceLifetime.Transient)); | ||
services.Add(new ServiceDescriptor(type, registration.ImplementationInstance)); | ||
} | ||
else if (registration.ImplementationFactory != null) | ||
{ | ||
services.Add(new ServiceDescriptor(typeof(Decorator<TService>), provider => | ||
{ | ||
return new DisposableDecorator<TService>((TService)registration.ImplementationFactory(provider)); | ||
}, registration.Lifetime)); | ||
} | ||
else | ||
{ | ||
var type = registration.ImplementationType!; | ||
var innerType = typeof(Decorator<,>).MakeGenericType(typeof(TService), type); | ||
services.Add(new ServiceDescriptor(typeof(Decorator<TService>), innerType, ServiceLifetime.Transient)); | ||
services.Add(new ServiceDescriptor(type, type, registration.Lifetime)); | ||
} | ||
} | ||
|
||
} | ||
|
||
internal class Decorator<TService> | ||
{ | ||
public TService Instance { get; set; } | ||
|
||
public Decorator(TService instance) | ||
{ | ||
Instance = instance; | ||
} | ||
} | ||
|
||
internal class Decorator<TService, TImpl> : Decorator<TService> | ||
where TImpl : class, TService | ||
{ | ||
public Decorator(TImpl instance) : base(instance) | ||
{ | ||
} | ||
} | ||
|
||
internal class DisposableDecorator<TService> : Decorator<TService>, IDisposable | ||
{ | ||
public DisposableDecorator(TService instance) : base(instance) | ||
{ | ||
} | ||
|
||
public void Dispose() | ||
{ | ||
(Instance as IDisposable)?.Dispose(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
14 changes: 14 additions & 0 deletions
14
src/Duende.Bff/EndpointProcessing/BffApiSkipResponseHandlingAttribute.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
// Copyright (c) Duende Software. All rights reserved. | ||
// See LICENSE in the project root for license information. | ||
|
||
using System; | ||
|
||
namespace Duende.Bff; | ||
|
||
/// <summary> | ||
/// This attribute indicates that the BFF midleware will not override the HTTP response status code. | ||
/// </summary> | ||
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)] | ||
public class BffApiSkipResponseHandlingAttribute : Attribute, IBffApiSkipResponseHandling | ||
{ | ||
} |
94 changes: 94 additions & 0 deletions
94
src/Duende.Bff/EndpointProcessing/BffAuthenticationService.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
// Copyright (c) Duende Software. All rights reserved. | ||
// See LICENSE in the project root for license information. | ||
|
||
using Microsoft.AspNetCore.Authentication; | ||
using Microsoft.AspNetCore.Http; | ||
using System.Security.Claims; | ||
using System.Threading.Tasks; | ||
using Microsoft.Extensions.Logging; | ||
using Microsoft.AspNetCore.Builder; | ||
|
||
namespace Duende.Bff; | ||
|
||
// this decorates the real authentication service to detect when | ||
// Challenge of Forbid is being called for a BFF API endpoint | ||
internal class BffAuthenticationService : IAuthenticationService | ||
{ | ||
private readonly IAuthenticationService _inner; | ||
private readonly ILogger<BffAuthenticationService> _logger; | ||
|
||
public BffAuthenticationService( | ||
Decorator<IAuthenticationService> decorator, | ||
ILogger<BffAuthenticationService> logger) | ||
{ | ||
_inner = decorator.Instance; | ||
_logger = logger; | ||
} | ||
|
||
public Task SignInAsync(HttpContext context, string? scheme, ClaimsPrincipal principal, AuthenticationProperties? properties) | ||
{ | ||
return _inner.SignInAsync(context, scheme, principal, properties); | ||
} | ||
|
||
public Task SignOutAsync(HttpContext context, string? scheme, AuthenticationProperties? properties) | ||
{ | ||
return _inner.SignOutAsync(context, scheme, properties); | ||
} | ||
|
||
public Task<AuthenticateResult> AuthenticateAsync(HttpContext context, string? scheme) | ||
{ | ||
return _inner.AuthenticateAsync(context, scheme); | ||
} | ||
|
||
public async Task ChallengeAsync(HttpContext context, string? scheme, AuthenticationProperties? properties) | ||
{ | ||
await _inner.ChallengeAsync(context, scheme, properties); | ||
|
||
var endpoint = context.GetEndpoint(); | ||
if (endpoint != null) | ||
{ | ||
if (context.Response.StatusCode == 302) | ||
{ | ||
var isBffEndpoint = endpoint.Metadata.GetMetadata<IBffApiEndpoint>() != null; | ||
if (isBffEndpoint) | ||
{ | ||
var requireResponseHandling = endpoint.Metadata.GetMetadata<IBffApiSkipResponseHandling>() == null; | ||
if (requireResponseHandling) | ||
{ | ||
_logger.LogDebug("Challenge was called for a BFF API endpoint, BFF response handing changing status code to 401."); | ||
|
||
context.Response.StatusCode = 401; | ||
context.Response.Headers.Remove("Location"); | ||
context.Response.Headers.Remove("Set-Cookie"); | ||
} | ||
} | ||
} | ||
} | ||
} | ||
|
||
public async Task ForbidAsync(HttpContext context, string? scheme, AuthenticationProperties? properties) | ||
{ | ||
await _inner.ForbidAsync(context, scheme, properties); | ||
|
||
var endpoint = context.GetEndpoint(); | ||
if (endpoint != null) | ||
{ | ||
if (context.Response.StatusCode == 302) | ||
{ | ||
var isBffEndpoint = endpoint.Metadata.GetMetadata<IBffApiEndpoint>() != null; | ||
if (isBffEndpoint) | ||
{ | ||
var requireResponseHandling = endpoint.Metadata.GetMetadata<IBffApiSkipResponseHandling>() == null; | ||
if (requireResponseHandling) | ||
{ | ||
_logger.LogDebug("Forbid was called for a BFF API endpoint, BFF response handing changing status code to 403."); | ||
|
||
context.Response.StatusCode = 403; | ||
context.Response.Headers.Remove("Location"); | ||
context.Response.Headers.Remove("Set-Cookie"); | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} |
56 changes: 0 additions & 56 deletions
56
src/Duende.Bff/EndpointProcessing/BffAuthorizationMiddlewareResultHandler.cs
This file was deleted.
Oops, something went wrong.
11 changes: 11 additions & 0 deletions
11
src/Duende.Bff/EndpointProcessing/IBffApiSkipResponseHandling.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
// Copyright (c) Duende Software. All rights reserved. | ||
// See LICENSE in the project root for license information. | ||
|
||
namespace Duende.Bff; | ||
|
||
/// <summary> | ||
/// Indicates that the BFF midleware will not override the HTTP response status code. | ||
/// </summary> | ||
public interface IBffApiSkipResponseHandling | ||
{ | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.