Skip to content

Commit

Permalink
Handle per-host roleprovider config
Browse files Browse the repository at this point in the history
  • Loading branch information
donaldgray committed Jan 19, 2024
1 parent 297d6e3 commit af59463
Show file tree
Hide file tree
Showing 3 changed files with 98 additions and 27 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
using IIIFAuth2.API.Models.Domain;

namespace IIIFAuth2.API.Tests.Models.Domain;

public class RoleProviderConfigurationTests
{
[Fact]
public void GetConfiguration_ReturnsDefault_IfNoHostSpecificConfig()
{
// Arrange
var defaultConfig = new ClickthroughConfiguration { GestureTitle = "test" };
var roleProviderConfig = new RoleProviderConfiguration
{
{ RoleProviderConfiguration.DefaultKey, defaultConfig }
};

// Act
var actual = roleProviderConfig.GetConfiguration("localhost");

// Assert
actual.Should().Be(defaultConfig);
}

[Fact]
public void GetConfiguration_ReturnsDefault_IfHostSpecificConfigNotFound()
{
// Arrange
var defaultConfig = new ClickthroughConfiguration { GestureTitle = "test" };
var hostConfig = new ClickthroughConfiguration { GestureTitle = "anotherTest" };
var roleProviderConfig = new RoleProviderConfiguration
{
{ RoleProviderConfiguration.DefaultKey, defaultConfig },
{ "test.example", hostConfig }
};

// Act
var actual = roleProviderConfig.GetConfiguration("localhost");

// Assert
actual.Should().Be(defaultConfig);
}

[Fact]
public void GetConfiguration_ReturnsHostSpecific_IfFound()
{
// Arrange
var defaultConfig = new ClickthroughConfiguration { GestureTitle = "test" };
var hostConfig = new ClickthroughConfiguration { GestureTitle = "anotherTest" };
var roleProviderConfig = new RoleProviderConfiguration
{
{ RoleProviderConfiguration.DefaultKey, defaultConfig },
{ "test.example", hostConfig }
};

// Act
var actual = roleProviderConfig.GetConfiguration("test.example");

// Assert
actual.Should().Be(hostConfig);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using IIIFAuth2.API.Data.Entities;
using IIIFAuth2.API.Infrastructure.Auth.RoleProvisioning.Oidc;
using IIIFAuth2.API.Models.Domain;
using IIIFAuth2.API.Utils;

namespace IIIFAuth2.API.Infrastructure.Auth.RoleProvisioning;

Expand All @@ -13,17 +14,20 @@ public class RoleProviderService
private readonly AuthServicesContext dbContext;
private readonly ClickThroughProviderHandler clickthroughRoleHandler;
private readonly OidcRoleProviderHandler oidcRoleProviderHandler;
private readonly IHttpContextAccessor httpContextAccessor;
private readonly ILogger<RoleProviderService> logger;

public RoleProviderService(
AuthServicesContext dbContext,
ClickThroughProviderHandler clickthroughRoleHandler,
OidcRoleProviderHandler oidcRoleProviderHandler,
IHttpContextAccessor httpContextAccessor,
ILogger<RoleProviderService> logger)
{
this.dbContext = dbContext;
this.clickthroughRoleHandler = clickthroughRoleHandler;
this.oidcRoleProviderHandler = oidcRoleProviderHandler;
this.httpContextAccessor = httpContextAccessor;
this.logger = logger;
}

Expand All @@ -36,17 +40,9 @@ public RoleProviderService(
var accessService = await GetAccessServices(customerId, accessServiceName);
if (accessService == null) return null;

var roleProvider = accessService.RoleProvider;
if (roleProvider == null)
{
logger.LogWarning(
"AccessService '{AccessServiceId}' ({CustomerId}:{AccessServiceName}) has no RoleProvider",
accessService.Id, customerId, accessService.Name);
return null;
}
var providerConfiguration = GetProviderConfigurationForHost(accessService);
if (providerConfiguration == null) return null;

// TODO - does this need to be smarter? How does it look up the key? Hostname?
var providerConfiguration = roleProvider.Configuration.GetDefaultConfiguration();
switch (providerConfiguration.Config)
{
case RoleProviderType.Clickthrough:
Expand Down Expand Up @@ -74,17 +70,10 @@ public RoleProviderService(
{
var accessService = await GetAccessServices(customerId, accessServiceName);
if (accessService == null) return null;

var providerConfiguration = GetProviderConfigurationForHost(accessService);
if (providerConfiguration == null) return null;

var roleProvider = accessService.RoleProvider;
if (roleProvider == null)
{
logger.LogWarning(
"AccessService '{AccessServiceId}' ({CustomerId}:{AccessServiceName}) has no RoleProvider",
accessService.Id, customerId, accessService.Name);
return null;
}

var providerConfiguration = roleProvider.Configuration.GetDefaultConfiguration();
var result = await oidcRoleProviderHandler.HandleLoginCallback(customerId, roleProvisionToken, authCode,
accessService, providerConfiguration, cancellationToken);
return result;
Expand All @@ -98,4 +87,23 @@ public RoleProviderService(
.SingleOrDefault(s => s.Name.Equals(accessServiceName, StringComparison.OrdinalIgnoreCase));
return accessService;
}

private IProviderConfiguration? GetProviderConfigurationForHost(AccessService accessService)
{
var roleProvider = accessService.RoleProvider;
if (roleProvider == null)
{
logger.LogWarning(
"AccessService '{AccessServiceId}' ({CustomerId}:{AccessServiceName}) has no RoleProvider",
accessService.Id, accessService.Customer, accessService.Name);
return null;
}

var currentRequest = httpContextAccessor.SafeHttpContext().Request;
var host = currentRequest.Host.Value;
logger.LogTrace("Getting provider configuration for host {Host}", host);

var providerConfiguration = roleProvider.Configuration.GetConfiguration(host);
return providerConfiguration;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,19 @@ namespace IIIFAuth2.API.Models.Domain;

/// <summary>
/// A collection of <see cref="IProviderConfiguration"/> objects, keyed as dictionary.
/// A key of "default" will always be present. Further keys that match incoming host can be supplied, this allows for
/// different configurations for different hosts.
/// </summary>
/// <remarks>
/// The key will always be "default" but is a placeholder to allow more flexible configuration in the future. For auth1
/// it stored hostname as different RoleProviders could be used per host
/// </remarks>
public class RoleProviderConfiguration : Dictionary<string, IProviderConfiguration>
{
public const string DefaultKey = "default";

public IProviderConfiguration GetDefaultConfiguration()
=> this[DefaultKey];

/// <summary>
/// Get the most appropriate <see cref="IProviderConfiguration"/> object. This will be host-specific, if found,
/// or fallback to Default config.
/// </summary>
public IProviderConfiguration GetConfiguration(string host)
=> TryGetValue(host, out var hostConfiguration) ? hostConfiguration : this[DefaultKey];
}

/// <summary>
Expand Down

0 comments on commit af59463

Please sign in to comment.