Skip to content

Commit

Permalink
Refactor KeycloakRealmResource and enhance validation
Browse files Browse the repository at this point in the history
The `KeycloakRealmResource` class has been refactored to include null checks in its constructor for parameters `name`, `realmName`, and `parent`. A private field `_parentEndpoint` and a property `ParentEndpoint` have been added. The `Parent` and `RealmName` properties now rely on the constructor for initialization.

New unit tests in `KeycloakPublicApiTests.cs` ensure that the constructor throws an `ArgumentNullException` for null parameters. Additional tests validate that the `AddRealm` method correctly handles null values for the builder and realm name, improving input validation across the API.
  • Loading branch information
paulomorgado committed Feb 7, 2025
1 parent e76fbca commit e50368b
Show file tree
Hide file tree
Showing 2 changed files with 109 additions and 17 deletions.
58 changes: 41 additions & 17 deletions src/Aspire.Hosting.Keycloak/KeycloakRealmResource.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,27 +8,51 @@ namespace Aspire.Hosting.Keycloak;
/// <summary>
/// Represents a Keycloak Realm resource.
/// </summary>
/// <param name="name">The name of the realm resource.</param>
/// <param name="realmName">The name of the realm.</param>
/// <param name="parent">The Keycloak server resource associated with this database.</param>
public sealed class KeycloakRealmResource(string name, string realmName, KeycloakResource parent) : Resource(name), IResourceWithParent<KeycloakResource>, IResourceWithConnectionString
public sealed class KeycloakRealmResource : Resource, IResourceWithParent<KeycloakResource>, IResourceWithConnectionString
{
private EndpointReference? _parentEndpoint;
private EndpointReference ParentEndpoint => _parentEndpoint ??= new(Parent, "http");
private EndpointReferenceExpression? _parentUrl;

/// <summary>
/// Initializes a new instance of the <see cref="KeycloakRealmResource"/> class.
/// </summary>
/// <param name="name">The name of the realm resource.</param>
/// <param name="realmName">The name of the realm.</param>
/// <param name="parent">The Keycloak server resource associated with this database.</param>
public KeycloakRealmResource(string name, string realmName, KeycloakResource parent) : base(name)
{
ArgumentException.ThrowIfNullOrWhiteSpace(realmName);
ArgumentNullException.ThrowIfNull(parent);

RealmName = realmName;
RealmPath = $"realms/{realmName}";
Parent = parent;
}

private EndpointReferenceExpression ParentUrl => _parentUrl ??= ParentEndpoint.Property(EndpointProperty.Url);

/// <summary>
/// Gets the parent endpoint reference.
/// </summary>
public EndpointReference ParentEndpoint => _parentEndpoint ??= new(Parent, "http");

/// <inheritdoc/>
public ReferenceExpression ConnectionStringExpression => ReferenceExpression.Create($"{ParentEndpoint.Property(EndpointProperty.Url)}/realms/{RealmName}/");
public ReferenceExpression ConnectionStringExpression => ReferenceExpression.Create($"{ParentUrl}/{RealmPath}/");

/// <summary>
/// Gets the base address of the realm.
/// </summary>
public string RealmPath { get; }

/// <summary>
/// Gets the issuer expression for the Keycloak realm.
/// </summary>
public ReferenceExpression IssuerExpression => ReferenceExpression.Create(
$"{ParentEndpoint.Property(EndpointProperty.Url)}/realms/{RealmName}");
public ReferenceExpression IssuerUrlExpression => ReferenceExpression.Create($"{ParentUrl}/{RealmPath}");

/// <summary>
/// Gets or sets the metadata address for the Keycloak realm.
/// </summary>
public string MetadataAddress { get; set; } = ".well-known/openid-configuration";
public string MetadataAddress => ".well-known/openid-configuration";

/// <summary>
/// Gets the metadata address expression for the Keycloak realm.
Expand All @@ -38,7 +62,7 @@ public sealed class KeycloakRealmResource(string name, string realmName, Keycloa
/// <summary>
/// Gets or sets the 'authorization_endpoint' for the Keycloak realm.
/// </summary>
public string AuthorizationEndpoint { get; set; } = "protocol/openid-connect/auth";
public string AuthorizationEndpoint => "protocol/openid-connect/auth";

/// <summary>
/// Gets the 'authorization_endpoint' expression for the Keycloak realm.
Expand All @@ -48,7 +72,7 @@ public sealed class KeycloakRealmResource(string name, string realmName, Keycloa
/// <summary>
/// Gets or sets the 'token_endpoint' for the Keycloak realm.
/// </summary>
public string TokenEndpoint { get; set; } = "protocol/openid-connect/token";
public string TokenEndpoint => "protocol/openid-connect/token";

/// <summary>
/// Gets the 'token_endpoint' expression for the Keycloak realm.
Expand All @@ -58,7 +82,7 @@ public sealed class KeycloakRealmResource(string name, string realmName, Keycloa
/// <summary>
/// Gets or sets the 'introspection_endpoint' for the Keycloak realm.
/// </summary>
public string IntrospectionEndpoint { get; set; } = "protocol/openid-connect/token/introspect";
public string IntrospectionEndpoint => "protocol/openid-connect/token/introspect";

/// <summary>
/// Gets the 'introspection_endpoint' expression for the Keycloak realm.
Expand All @@ -68,7 +92,7 @@ public sealed class KeycloakRealmResource(string name, string realmName, Keycloa
/// <summary>
/// Gets or sets 'user_info_endpoint' for the Keycloak realm.
/// </summary>
public string UserInfoEndpoint { get; set; } = "protocol/openid-connect/userinfo";
public string UserInfoEndpoint => "protocol/openid-connect/userinfo";

/// <summary>
/// Gets 'user_info_endpoint' expression for the Keycloak realm.
Expand All @@ -78,7 +102,7 @@ public sealed class KeycloakRealmResource(string name, string realmName, Keycloa
/// <summary>
/// Gets or sets the 'end_session_endpoint' for the Keycloak realm.
/// </summary>
public string EndSessionEndpoint { get; set; } = "protocol/openid-connect/logout";
public string EndSessionEndpoint => "protocol/openid-connect/logout";

/// <summary>
/// Gets the 'end_session_endpoint' expression for the Keycloak realm.
Expand All @@ -88,18 +112,18 @@ public sealed class KeycloakRealmResource(string name, string realmName, Keycloa
/// <summary>
/// Gets or sets the 'registration_endpoint' for the Keycloak realm.
/// </summary>
public string RegistrationEndpoint { get; set; } = "clients-registrations/openid-connect";
public string RegistrationEndpoint => "clients-registrations/openid-connect";

/// <summary>
/// Gets the 'registration_endpoint' expression for the Keycloak realm.
/// </summary>
public ReferenceExpression RegistrationEndpointExpression => ReferenceExpression.Create($"{ConnectionStringExpression}{RegistrationEndpoint}");

/// <inheritdoc/>
public KeycloakResource Parent { get; } = parent;
public KeycloakResource Parent { get; }

/// <summary>
/// Gets the name of the realm.
/// </summary>
public string RealmName { get; } = realmName;
public string RealmName { get; }
}
68 changes: 68 additions & 0 deletions tests/Aspire.Hosting.Keycloak.Tests/KeycloakPublicApiTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,49 @@ public void CtorKeycloakResourceShouldThrowWhenAdminPasswordIsNull()
Assert.Equal(nameof(adminPassword), exception.ParamName);
}

[Fact]
public void CtorKeycloakRealmResourceShouldThrowWhenNameIsNull()
{
string name = null!;
var realmName = "realm1";
var builder = TestDistributedApplicationBuilder.Create();
var adminPassword = builder.AddParameter("Password");
var parent = new KeycloakResource("keycloak", default(ParameterResource?), adminPassword.Resource);

var action = () => new KeycloakRealmResource(name, realmName, parent);

var exception = Assert.Throws<ArgumentNullException>(action);
Assert.Equal(nameof(name), exception.ParamName);
}

[Fact]
public void CtorMongoKeycloakRealmResourceShouldThrowWhenRealmNameIsNull()
{
var name = "keycloak";
string realmName = null!;
var builder = TestDistributedApplicationBuilder.Create();
var adminPassword = builder.AddParameter("Password");
var parent = new KeycloakResource("keycloak", default(ParameterResource?), adminPassword.Resource);

var action = () => new KeycloakRealmResource(name, realmName, parent);

var exception = Assert.Throws<ArgumentNullException>(action);
Assert.Equal(nameof(realmName), exception.ParamName);
}

[Fact]
public void CtorMongoKeycloakRealmResourceShouldThrowWhenDatabaseParentIsNull()
{
var name = "keycloak";
var realmName = "realm1";
KeycloakResource parent = null!;

var action = () => new KeycloakRealmResource(name, realmName, parent);

var exception = Assert.Throws<ArgumentNullException>(action);
Assert.Equal(nameof(parent), exception.ParamName);
}

[Fact]
public void AddKeycloakContainerShouldThrowWhenBuilderIsNull()
{
Expand Down Expand Up @@ -195,4 +238,29 @@ public void WithRealmImportFileAddsBindMountAnnotation(bool? isReadOnly)
Assert.Equal(ContainerMountType.BindMount, containerAnnotation.Type);
Assert.Equal(isReadOnly ?? false, containerAnnotation.IsReadOnly);
}

[Fact]
public void AddRealmShouldThrowWhenBuilderIsNull()
{
IResourceBuilder<KeycloakResource> builder = null!;
const string name = "realm1";

var action = () => builder.AddRealm(name);

var exception = Assert.Throws<ArgumentNullException>(action);
Assert.Equal(nameof(builder), exception.ParamName);
}

[Fact]
public void AddRealmShouldThrowWhenNameIsNull()
{
var builderResource = TestDistributedApplicationBuilder.Create();
var MongoDB = builderResource.AddKeycloak("realm1");
string name = null!;

var action = () => MongoDB.AddRealm(name);

var exception = Assert.Throws<ArgumentNullException>(action);
Assert.Equal(nameof(name), exception.ParamName);
}
}

0 comments on commit e50368b

Please sign in to comment.