Skip to content

Commit e50368b

Browse files
committed
Refactor KeycloakRealmResource and enhance validation
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.
1 parent e76fbca commit e50368b

File tree

2 files changed

+109
-17
lines changed

2 files changed

+109
-17
lines changed

src/Aspire.Hosting.Keycloak/KeycloakRealmResource.cs

Lines changed: 41 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -8,27 +8,51 @@ namespace Aspire.Hosting.Keycloak;
88
/// <summary>
99
/// Represents a Keycloak Realm resource.
1010
/// </summary>
11-
/// <param name="name">The name of the realm resource.</param>
12-
/// <param name="realmName">The name of the realm.</param>
13-
/// <param name="parent">The Keycloak server resource associated with this database.</param>
14-
public sealed class KeycloakRealmResource(string name, string realmName, KeycloakResource parent) : Resource(name), IResourceWithParent<KeycloakResource>, IResourceWithConnectionString
11+
public sealed class KeycloakRealmResource : Resource, IResourceWithParent<KeycloakResource>, IResourceWithConnectionString
1512
{
1613
private EndpointReference? _parentEndpoint;
17-
private EndpointReference ParentEndpoint => _parentEndpoint ??= new(Parent, "http");
14+
private EndpointReferenceExpression? _parentUrl;
15+
16+
/// <summary>
17+
/// Initializes a new instance of the <see cref="KeycloakRealmResource"/> class.
18+
/// </summary>
19+
/// <param name="name">The name of the realm resource.</param>
20+
/// <param name="realmName">The name of the realm.</param>
21+
/// <param name="parent">The Keycloak server resource associated with this database.</param>
22+
public KeycloakRealmResource(string name, string realmName, KeycloakResource parent) : base(name)
23+
{
24+
ArgumentException.ThrowIfNullOrWhiteSpace(realmName);
25+
ArgumentNullException.ThrowIfNull(parent);
26+
27+
RealmName = realmName;
28+
RealmPath = $"realms/{realmName}";
29+
Parent = parent;
30+
}
31+
32+
private EndpointReferenceExpression ParentUrl => _parentUrl ??= ParentEndpoint.Property(EndpointProperty.Url);
33+
34+
/// <summary>
35+
/// Gets the parent endpoint reference.
36+
/// </summary>
37+
public EndpointReference ParentEndpoint => _parentEndpoint ??= new(Parent, "http");
1838

1939
/// <inheritdoc/>
20-
public ReferenceExpression ConnectionStringExpression => ReferenceExpression.Create($"{ParentEndpoint.Property(EndpointProperty.Url)}/realms/{RealmName}/");
40+
public ReferenceExpression ConnectionStringExpression => ReferenceExpression.Create($"{ParentUrl}/{RealmPath}/");
41+
42+
/// <summary>
43+
/// Gets the base address of the realm.
44+
/// </summary>
45+
public string RealmPath { get; }
2146

2247
/// <summary>
2348
/// Gets the issuer expression for the Keycloak realm.
2449
/// </summary>
25-
public ReferenceExpression IssuerExpression => ReferenceExpression.Create(
26-
$"{ParentEndpoint.Property(EndpointProperty.Url)}/realms/{RealmName}");
50+
public ReferenceExpression IssuerUrlExpression => ReferenceExpression.Create($"{ParentUrl}/{RealmPath}");
2751

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

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

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

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

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

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

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

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

98122
/// <inheritdoc/>
99-
public KeycloakResource Parent { get; } = parent;
123+
public KeycloakResource Parent { get; }
100124

101125
/// <summary>
102126
/// Gets the name of the realm.
103127
/// </summary>
104-
public string RealmName { get; } = realmName;
128+
public string RealmName { get; }
105129
}

tests/Aspire.Hosting.Keycloak.Tests/KeycloakPublicApiTests.cs

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,49 @@ public void CtorKeycloakResourceShouldThrowWhenAdminPasswordIsNull()
3434
Assert.Equal(nameof(adminPassword), exception.ParamName);
3535
}
3636

37+
[Fact]
38+
public void CtorKeycloakRealmResourceShouldThrowWhenNameIsNull()
39+
{
40+
string name = null!;
41+
var realmName = "realm1";
42+
var builder = TestDistributedApplicationBuilder.Create();
43+
var adminPassword = builder.AddParameter("Password");
44+
var parent = new KeycloakResource("keycloak", default(ParameterResource?), adminPassword.Resource);
45+
46+
var action = () => new KeycloakRealmResource(name, realmName, parent);
47+
48+
var exception = Assert.Throws<ArgumentNullException>(action);
49+
Assert.Equal(nameof(name), exception.ParamName);
50+
}
51+
52+
[Fact]
53+
public void CtorMongoKeycloakRealmResourceShouldThrowWhenRealmNameIsNull()
54+
{
55+
var name = "keycloak";
56+
string realmName = null!;
57+
var builder = TestDistributedApplicationBuilder.Create();
58+
var adminPassword = builder.AddParameter("Password");
59+
var parent = new KeycloakResource("keycloak", default(ParameterResource?), adminPassword.Resource);
60+
61+
var action = () => new KeycloakRealmResource(name, realmName, parent);
62+
63+
var exception = Assert.Throws<ArgumentNullException>(action);
64+
Assert.Equal(nameof(realmName), exception.ParamName);
65+
}
66+
67+
[Fact]
68+
public void CtorMongoKeycloakRealmResourceShouldThrowWhenDatabaseParentIsNull()
69+
{
70+
var name = "keycloak";
71+
var realmName = "realm1";
72+
KeycloakResource parent = null!;
73+
74+
var action = () => new KeycloakRealmResource(name, realmName, parent);
75+
76+
var exception = Assert.Throws<ArgumentNullException>(action);
77+
Assert.Equal(nameof(parent), exception.ParamName);
78+
}
79+
3780
[Fact]
3881
public void AddKeycloakContainerShouldThrowWhenBuilderIsNull()
3982
{
@@ -195,4 +238,29 @@ public void WithRealmImportFileAddsBindMountAnnotation(bool? isReadOnly)
195238
Assert.Equal(ContainerMountType.BindMount, containerAnnotation.Type);
196239
Assert.Equal(isReadOnly ?? false, containerAnnotation.IsReadOnly);
197240
}
241+
242+
[Fact]
243+
public void AddRealmShouldThrowWhenBuilderIsNull()
244+
{
245+
IResourceBuilder<KeycloakResource> builder = null!;
246+
const string name = "realm1";
247+
248+
var action = () => builder.AddRealm(name);
249+
250+
var exception = Assert.Throws<ArgumentNullException>(action);
251+
Assert.Equal(nameof(builder), exception.ParamName);
252+
}
253+
254+
[Fact]
255+
public void AddRealmShouldThrowWhenNameIsNull()
256+
{
257+
var builderResource = TestDistributedApplicationBuilder.Create();
258+
var MongoDB = builderResource.AddKeycloak("realm1");
259+
string name = null!;
260+
261+
var action = () => MongoDB.AddRealm(name);
262+
263+
var exception = Assert.Throws<ArgumentNullException>(action);
264+
Assert.Equal(nameof(name), exception.ParamName);
265+
}
198266
}

0 commit comments

Comments
 (0)