Skip to content

Commit 369d067

Browse files
committed
feat: Add support for list of attributes with the same key
Signed-off-by: Douglass Kirkley <doug.kirkley@gmail.com>
1 parent c22c55a commit 369d067

21 files changed

+274
-82
lines changed

api/common/attributes.go

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
package common
2+
3+
import (
4+
"encoding/json"
5+
"fmt"
6+
)
7+
8+
// UserAttributes is a map of user attributes. It supports both string and []string values for attributes.
9+
// +kubebuilder:validation:Type=object
10+
// +kubebuilder:pruning:PreserveUnknownFields
11+
type UserAttributes map[string][]string
12+
13+
// UnmarshalJSON implements json.Unmarshaler.
14+
func (a *UserAttributes) UnmarshalJSON(data []byte) error {
15+
var raw map[string]interface{}
16+
if err := json.Unmarshal(data, &raw); err != nil {
17+
return err
18+
}
19+
20+
if *a == nil {
21+
*a = make(UserAttributes)
22+
}
23+
24+
result := *a
25+
26+
for k, v := range raw {
27+
switch value := v.(type) {
28+
case string:
29+
result[k] = []string{value}
30+
case []interface{}:
31+
var strSlice []string
32+
33+
for _, item := range value {
34+
if str, ok := item.(string); ok {
35+
strSlice = append(strSlice, str)
36+
} else {
37+
return fmt.Errorf("attribute '%s' contains a non-string value in the list", k)
38+
}
39+
}
40+
41+
result[k] = strSlice
42+
default:
43+
return fmt.Errorf("unsupported type for attribute '%s': %T", k, v)
44+
}
45+
}
46+
47+
return nil
48+
}
49+
50+
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
51+
func (a *UserAttributes) DeepCopyInto(out *UserAttributes) {
52+
if *a == nil {
53+
*out = nil
54+
return
55+
}
56+
*out = make(UserAttributes, len(*a))
57+
for key, val := range *a {
58+
if val == nil {
59+
(*out)[key] = nil
60+
} else {
61+
// Create a new slice and copy the elements.
62+
inVal := val
63+
outVal := make([]string, len(inVal))
64+
copy(outVal, inVal)
65+
(*out)[key] = outVal
66+
}
67+
}
68+
}
69+
70+
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new UserAttributes.
71+
func (a *UserAttributes) DeepCopy() *UserAttributes {
72+
if a == nil {
73+
return nil
74+
}
75+
out := new(UserAttributes)
76+
a.DeepCopyInto(out)
77+
return out
78+
}

api/v1/keycloakclient_types.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -209,7 +209,7 @@ type ServiceAccount struct {
209209
// Attributes is a map of service account attributes.
210210
// +nullable
211211
// +optional
212-
Attributes map[string]string `json:"attributes,omitempty"`
212+
Attributes common.UserAttributes `json:"attributes,omitempty"`
213213

214214
// Groups is a list of groups assigned to service account
215215
// +nullable

api/v1/keycloakrealmuser_types.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ type KeycloakRealmUserSpec struct {
5858
// Attributes is a map of user attributes.
5959
// +nullable
6060
// +optional
61-
Attributes map[string]string `json:"attributes,omitempty"`
61+
Attributes common.UserAttributes `json:"attributes,omitempty"`
6262

6363
// ReconciliationStrategy is a strategy for reconciliation. Possible values: full, create-only.
6464
// Default value: full. If set to create-only, user will be created only if it does not exist. If user exists, it will not be updated.

api/v1/zz_generated.deepcopy.go

Lines changed: 2 additions & 14 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

config/crd/bases/v1.edp.epam.com_keycloakclients.yaml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -587,11 +587,10 @@ spec:
587587
nullable: true
588588
properties:
589589
attributes:
590-
additionalProperties:
591-
type: string
592590
description: Attributes is a map of service account attributes.
593591
nullable: true
594592
type: object
593+
x-kubernetes-preserve-unknown-fields: true
595594
clientRoles:
596595
description: ClientRoles is a list of client roles assigned to
597596
service account.

config/crd/bases/v1.edp.epam.com_keycloakrealmusers.yaml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,11 +45,10 @@ spec:
4545
description: KeycloakRealmUserSpec defines the desired state of KeycloakRealmUser.
4646
properties:
4747
attributes:
48-
additionalProperties:
49-
type: string
5048
description: Attributes is a map of user attributes.
5149
nullable: true
5250
type: object
51+
x-kubernetes-preserve-unknown-fields: true
5352
clientRoles:
5453
description: ClientRoles is a list of client roles assigned to user.
5554
items:

deploy-templates/crds/v1.edp.epam.com_keycloakclients.yaml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -587,11 +587,10 @@ spec:
587587
nullable: true
588588
properties:
589589
attributes:
590-
additionalProperties:
591-
type: string
592590
description: Attributes is a map of service account attributes.
593591
nullable: true
594592
type: object
593+
x-kubernetes-preserve-unknown-fields: true
595594
clientRoles:
596595
description: ClientRoles is a list of client roles assigned to
597596
service account.

deploy-templates/crds/v1.edp.epam.com_keycloakrealmusers.yaml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,11 +45,10 @@ spec:
4545
description: KeycloakRealmUserSpec defines the desired state of KeycloakRealmUser.
4646
properties:
4747
attributes:
48-
additionalProperties:
49-
type: string
5048
description: Attributes is a map of user attributes.
5149
nullable: true
5250
type: object
51+
x-kubernetes-preserve-unknown-fields: true
5352
clientRoles:
5453
description: ClientRoles is a list of client roles assigned to user.
5554
items:

docs/api.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3342,7 +3342,7 @@ ServiceAccount is a service account configuration.
33423342
</thead>
33433343
<tbody><tr>
33443344
<td><b>attributes</b></td>
3345-
<td>map[string]string</td>
3345+
<td>object</td>
33463346
<td>
33473347
Attributes is a map of service account attributes.<br/>
33483348
</td>
@@ -6367,7 +6367,7 @@ KeycloakRealmUserSpec defines the desired state of KeycloakRealmUser.
63676367
<td>true</td>
63686368
</tr><tr>
63696369
<td><b>attributes</b></td>
6370-
<td>map[string]string</td>
6370+
<td>object</td>
63716371
<td>
63726372
Attributes is a map of user attributes.<br/>
63736373
</td>

internal/controller/keycloakclient/chain/service_account_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@ func TestServiceAccount_Serve(t *testing.T) {
2020
},
2121
ServiceAccount: &keycloakApi.ServiceAccount{
2222
Enabled: true,
23-
Attributes: map[string]string{
24-
"foo": "bar",
23+
Attributes: common.UserAttributes{
24+
"foo": {"bar"},
2525
},
2626
ClientRoles: []keycloakApi.ClientRole{
2727
{

0 commit comments

Comments
 (0)