Skip to content

Commit

Permalink
Allow lookup of ResourceServerRef by identifier or by id.
Browse files Browse the repository at this point in the history
  • Loading branch information
wasabii committed Jan 7, 2025
1 parent fd3a1bf commit 189671b
Show file tree
Hide file tree
Showing 9 changed files with 73 additions and 65 deletions.
4 changes: 4 additions & 0 deletions src/Alethic.Auth0.Operator.Core/Models/V1ResourceServerRef.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ public class V1ResourceServerRef
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public string? Id { get; set; }

[JsonPropertyName("identifier")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public string? Identifier { get; set; }

/// <inheritdoc />
public override string ToString()
{
Expand Down
16 changes: 11 additions & 5 deletions src/Alethic.Auth0.Operator/Controllers/V1ClientGrantController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ public V1ClientGrantController(IKubernetesClient kube, EntityRequeue<V1ClientGra
protected override string EntityTypeName => "ClientGrant";

/// <inheritdoc />
protected override async Task<IDictionary?> GetApi(IManagementApiClient api, string id, string defaultNamespace,CancellationToken cancellationToken)
protected override async Task<IDictionary?> GetApi(IManagementApiClient api, string id, string defaultNamespace, CancellationToken cancellationToken)
{
var list = await api.ClientGrants.GetAllAsync(new GetClientGrantsRequest(), cancellationToken: cancellationToken);
var self = list.FirstOrDefault(i => i.Id == id);
Expand All @@ -65,13 +65,13 @@ public V1ClientGrantController(IKubernetesClient kube, EntityRequeue<V1ClientGra
{
if (conf.ClientRef is null)
throw new InvalidOperationException("ClientRef is required.");
var clientId = await ResolveClientRefToId(conf.ClientRef, defaultNamespace, cancellationToken);
var clientId = await ResolveClientRefToId(api, conf.ClientRef, defaultNamespace, cancellationToken);
if (string.IsNullOrWhiteSpace(clientId))
throw new InvalidOperationException();

if (conf.Audience is null)
throw new InvalidOperationException("Audience is required.");
var audience = await ResolveResourceServerRefToIdentifier(conf.Audience, defaultNamespace, cancellationToken);
var audience = await ResolveResourceServerRefToIdentifier(api, conf.Audience, defaultNamespace, cancellationToken);
if (string.IsNullOrWhiteSpace(audience))
throw new InvalidOperationException();

Expand All @@ -97,8 +97,8 @@ protected override async Task<string> CreateApi(IManagementApiClient api, Client
req.AllowAnyOrganization = conf.AllowAnyOrganization;
req.OrganizationUsage = Convert(conf.OrganizationUsage);
req.Scope = conf.Scopes?.ToList();
req.ClientId = await ResolveClientRefToId(conf.ClientRef, defaultNamespace, cancellationToken);
req.Audience = await ResolveResourceServerRefToIdentifier(conf.Audience, defaultNamespace, cancellationToken);
req.ClientId = await ResolveClientRefToId(api, conf.ClientRef, defaultNamespace, cancellationToken);
req.Audience = await ResolveResourceServerRefToIdentifier(api, conf.Audience, defaultNamespace, cancellationToken);

var self = await api.ClientGrants.CreateAsync(req, cancellationToken);
if (self is null)
Expand All @@ -124,6 +124,12 @@ protected override Task DeleteApi(IManagementApiClient api, string id, Cancellat
return api.ClientGrants.DeleteAsync(id, cancellationToken);
}

/// <summary>
/// Converts a from a local model type to a request type.
/// </summary>
/// <param name="organizationUsage"></param>
/// <returns></returns>
/// <exception cref="InvalidOperationException"></exception>
global::Auth0.ManagementApi.Models.OrganizationUsage? Convert(global::Alethic.Auth0.Operator.Core.Models.OrganizationUsage? organizationUsage)
{
return organizationUsage switch
Expand Down
29 changes: 8 additions & 21 deletions src/Alethic.Auth0.Operator/Controllers/V1ConnectionController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ public V1ConnectionController(IKubernetesClient kube, EntityRequeue<V1Connection
/// <param name="cancellationToken"></param>
/// <returns></returns>
/// <exception cref="InvalidOperationException"></exception>
async Task<string[]?> ResolveClientRefsToId(V1ClientRef[]? refs, string defaultNamespace, CancellationToken cancellationToken)
async Task<string[]?> ResolveClientRefsToIds(IManagementApiClient api, V1ClientRef[]? refs, string defaultNamespace, CancellationToken cancellationToken)
{
if (refs is null)
return Array.Empty<string>();
Expand All @@ -122,24 +122,11 @@ public V1ConnectionController(IKubernetesClient kube, EntityRequeue<V1Connection

foreach (var i in refs)
{
if (i.Id is { } id)
{
l.Add(id);
}
else
{
Logger.LogDebug("Attempting to resolve ClientRef {Namespace}/{Name}.", i.Namespace, i.Name);

var client = await ResolveClientRef(i, defaultNamespace, cancellationToken);
if (client is null)
throw new InvalidOperationException($"Could not resolve ClientRef {i}.");

if (client.Status.Id is null)
throw new RetryException($"Referenced Client {client.Namespace()}/{client.Name()} has not been reconciled.");

Logger.LogDebug("Resolved ClientRef {Namespace}/{Name} to {Id}.", i.Namespace, i.Name, client.Status.Id);
l.Add(client.Status.Id);
}
var r = await ResolveClientRefToId(api, i, defaultNamespace, cancellationToken);
if (r is null)
throw new InvalidOperationException();

l.Add(r);
}

return l.ToArray();
Expand All @@ -157,7 +144,7 @@ protected override async Task<string> CreateApi(IManagementApiClient api, Connec
req.Realms = conf.Realms;
req.IsDomainConnection = conf.IsDomainConnection ?? false;
req.ShowAsButton = conf.ShowAsButton;
req.EnabledClients = await ResolveClientRefsToId(conf.EnabledClients, defaultNamespace, cancellationToken);
req.EnabledClients = await ResolveClientRefsToIds(api, conf.EnabledClients, defaultNamespace, cancellationToken);

var self = await api.Connections.CreateAsync(TransformToNewtonsoftJson<ConnectionConf, ConnectionCreateRequest>(conf), cancellationToken);
if (self is null)
Expand All @@ -176,7 +163,7 @@ protected override async Task UpdateApi(IManagementApiClient api, string id, Con
req.Realms = conf.Realms;
req.IsDomainConnection = conf.IsDomainConnection ?? false;
req.ShowAsButton = conf.ShowAsButton;
req.EnabledClients = await ResolveClientRefsToId(conf.EnabledClients, defaultNamespace, cancellationToken);
req.EnabledClients = await ResolveClientRefsToIds(api, conf.EnabledClients, defaultNamespace, cancellationToken);

await api.Connections.UpdateAsync(id, req, cancellationToken);
}
Expand Down
81 changes: 42 additions & 39 deletions src/Alethic.Auth0.Operator/Controllers/V1Controller.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
using KubeOps.Abstractions.Entities;
using KubeOps.Abstractions.Queue;
using KubeOps.KubernetesClient;
using KubeOps.KubernetesClient.LabelSelectors;

using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Logging;
Expand Down Expand Up @@ -102,7 +103,6 @@ public async Task<IManagementApiClient> GetTenantApiClientAsync(V1TenantRef tena
/// <param name="defaultNamespace"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
[return: NotNullIfNotNull(nameof(tenantRef))]
public async Task<V1Tenant?> ResolveTenantRef(V1TenantRef? tenantRef, string defaultNamespace, CancellationToken cancellationToken)
{
if (tenantRef is null)
Expand All @@ -125,12 +125,12 @@ public async Task<IManagementApiClient> GetTenantApiClientAsync(V1TenantRef tena
/// <summary>
/// Attempts to resolve the client document referenced by the client reference.
/// </summary>
/// <param name="api"></param>
/// <param name="clientRef"></param>
/// <param name="defaultNamespace"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
[return: NotNullIfNotNull(nameof(clientRef))]
public async Task<V1Client?> ResolveClientRef(V1ClientRef? clientRef, string defaultNamespace, CancellationToken cancellationToken)
public async Task<V1Client?> ResolveClientRef(IManagementApiClient api, V1ClientRef? clientRef, string defaultNamespace, CancellationToken cancellationToken)
{
if (clientRef is null)
return null;
Expand All @@ -152,54 +152,50 @@ public async Task<IManagementApiClient> GetTenantApiClientAsync(V1TenantRef tena
/// <summary>
/// Attempts to resolve the client reference to client ID.
/// </summary>
/// <param name="api"></param>
/// <param name="clientRef"></param>
/// <param name="defaultNamespace"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
[return: NotNullIfNotNull(nameof(clientRef))]
protected async Task<string?> ResolveClientRefToId(V1ClientRef? clientRef, string defaultNamespace, CancellationToken cancellationToken)
protected async Task<string?> ResolveClientRefToId(IManagementApiClient api, V1ClientRef? clientRef, string defaultNamespace, CancellationToken cancellationToken)
{
if (clientRef is null)
return null;

if (clientRef.Id is { } id && string.IsNullOrWhiteSpace(id) == false)
{
return id;
}
else
{
Logger.LogDebug("Attempting to resolve ClientRef {Namespace}/{Name}.", clientRef.Namespace, clientRef.Name);

var client = await ResolveClientRef(clientRef, defaultNamespace, cancellationToken);
if (client is null)
throw new InvalidOperationException($"Could not resolve ClientRef {clientRef}.");
if (string.IsNullOrWhiteSpace(client.Status.Id))
throw new RetryException($"Referenced Client {client.Namespace()}/{client.Name()} has not been reconciled.");
Logger.LogDebug("Attempting to resolve ClientRef {Namespace}/{Name}.", clientRef.Namespace, clientRef.Name);

Logger.LogDebug("Resolved ClientRef {Namespace}/{Name} to {Id}.", clientRef.Namespace, clientRef.Name, client.Status.Id);
return client.Status.Id;
}
var client = await ResolveClientRef(api, clientRef, defaultNamespace, cancellationToken);
if (client is null)
throw new InvalidOperationException($"Could not resolve ClientRef {clientRef}.");
if (string.IsNullOrWhiteSpace(client.Status.Id))
throw new RetryException($"Referenced Client {client.Namespace()}/{client.Name()} has not been reconciled.");

Logger.LogDebug("Resolved ClientRef {Namespace}/{Name} to {Id}.", clientRef.Namespace, clientRef.Name, client.Status.Id);
return client.Status.Id;
}

/// <summary>
/// Attempts to resolve the resource server document referenced by the resource server reference.
/// </summary>
/// <param name="api"></param>
/// <param name="resourceServerRef"></param>
/// <param name="defaultNamespace"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
[return: NotNullIfNotNull(nameof(resourceServerRef))]
public async Task<V1ResourceServer?> ResolveResourceServerRef(V1ResourceServerRef? resourceServerRef, string defaultNamespace, CancellationToken cancellationToken)
public async Task<V1ResourceServer?> ResolveResourceServerRef(IManagementApiClient api, V1ResourceServerRef? resourceServerRef, string defaultNamespace, CancellationToken cancellationToken)
{
if (resourceServerRef is null)
return null;

if (string.IsNullOrWhiteSpace(resourceServerRef.Name))
throw new InvalidOperationException($"ResourceServer reference has no name.");

var ns = resourceServerRef.Namespace ?? defaultNamespace;
if (string.IsNullOrWhiteSpace(ns))
throw new InvalidOperationException($"ResourceServer reference has no discovered namesace.");
throw new InvalidOperationException($"ResourceServer reference has no namespace.");

if (string.IsNullOrWhiteSpace(resourceServerRef.Name))
throw new InvalidOperationException($"ResourceServer reference has no name.");

var resourceServer = await _kube.GetAsync<V1ResourceServer>(resourceServerRef.Name, ns, cancellationToken);
if (resourceServer is null)
Expand All @@ -211,34 +207,41 @@ public async Task<IManagementApiClient> GetTenantApiClientAsync(V1TenantRef tena
/// <summary>
/// Attempts to resolve the list of client references to client IDs.
/// </summary>
/// <param name="refs"></param>
/// <param name="api"></param>
/// <param name="reference"></param>
/// <param name="defaultNamespace"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
[return: NotNullIfNotNull(nameof(reference))]
protected async Task<string?> ResolveResourceServerRefToIdentifier(V1ResourceServerRef? reference, string defaultNamespace, CancellationToken cancellationToken)
protected async Task<string?> ResolveResourceServerRefToIdentifier(IManagementApiClient api, V1ResourceServerRef? reference, string defaultNamespace, CancellationToken cancellationToken)
{
if (reference is null)
return null;

// identifier is specified directly by reference
if (reference.Identifier is { } identifier && string.IsNullOrWhiteSpace(identifier) == false)
return identifier;

// id is specified by reference, lookup identifier
if (reference.Id is { } id && string.IsNullOrWhiteSpace(id) == false)
{
return id;
var self = await api.ResourceServers.GetAsync(id, cancellationToken);
if (self is null)
throw new InvalidOperationException($"Failed to resolve ResourceServer reference {id}.");

return self.Identifier;
}
else
{
Logger.LogDebug("Attempting to resolve ResourceServer reference {Namespace}/{Name}.", reference.Namespace, reference.Name);

var resourceServer = await ResolveResourceServerRef(reference, defaultNamespace, cancellationToken);
if (resourceServer is null)
throw new InvalidOperationException($"Could not resolve ResourceServerRef {reference}.");
Logger.LogDebug("Attempting to resolve ResourceServer reference {Namespace}/{Name}.", reference.Namespace, reference.Name);

var resourceServer = await ResolveResourceServerRef(api, reference, defaultNamespace, cancellationToken);
if (resourceServer is null)
throw new InvalidOperationException($"Could not resolve ResourceServerRef {reference}.");

if (resourceServer.Status.Identifier is null)
throw new InvalidOperationException($"Referenced ResourceServer {resourceServer.Namespace()}/{resourceServer.Name()} has not been reconcilled.");
if (resourceServer.Status.Identifier is null)
throw new InvalidOperationException($"Referenced ResourceServer {resourceServer.Namespace()}/{resourceServer.Name()} has not been reconcilled.");

Logger.LogDebug("Resolved ResourceServer reference {Namespace}/{Name} to {Identifier}.", reference.Namespace, reference.Name, resourceServer.Status.Identifier);
return resourceServer.Status.Identifier;
}
Logger.LogDebug("Resolved ResourceServer reference {Namespace}/{Name} to {Identifier}.", reference.Namespace, reference.Name, resourceServer.Status.Identifier);
return resourceServer.Status.Identifier;
}

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,9 @@ spec:
id:
nullable: true
type: string
identifier:
nullable: true
type: string
type: object
organization_usage:
enum:
Expand All @@ -91,6 +94,7 @@ spec:
type: object
required:
- tenantRef
- conf
type: object
type: object
served: true
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -571,6 +571,7 @@ spec:
type: object
required:
- tenantRef
- conf
type: object
type: object
served: true
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ spec:
type: object
required:
- tenantRef
- conf
type: object
type: object
served: true
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,7 @@ spec:
type: object
required:
- tenantRef
- conf
type: object
type: object
served: true
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,7 @@ spec:
required:
- name
- auth
- conf
type: object
type: object
served: true
Expand Down

0 comments on commit 189671b

Please sign in to comment.