Skip to content

Commit 5dc3f40

Browse files
committed
user: make passwordRef optional
Keystone allows creating passwordless users for authentication via federation, application credentials, or other means. Make passwordRef optional so ORC can create users without a password. A CEL validation on UserResourceSpec prevents removing passwordRef once set, since Keystone does not support clearing a password via the API. The field remains mutable (can be changed to a different Secret).
1 parent b6fde2a commit 5dc3f40

17 files changed

Lines changed: 49 additions & 66 deletions

File tree

api/v1alpha1/user_types.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ limitations under the License.
1717
package v1alpha1
1818

1919
// UserResourceSpec contains the desired state of the resource.
20+
// +kubebuilder:validation:XValidation:rule="!has(oldSelf.passwordRef) || has(self.passwordRef)",message="passwordRef may not be removed once set"
2021
type UserResourceSpec struct {
2122
// name will be the name of the created resource. If not specified, the
2223
// name of the ORC object will be used.
@@ -45,8 +46,9 @@ type UserResourceSpec struct {
4546

4647
// passwordRef is a reference to a Secret containing the password
4748
// for this user. The Secret must contain a key named "password".
48-
// +required
49-
PasswordRef KubernetesNameRef `json:"passwordRef,omitempty"`
49+
// If not specified, the user is created without a password.
50+
// +optional
51+
PasswordRef *KubernetesNameRef `json:"passwordRef,omitempty"`
5052
}
5153

5254
// UserFilter defines an existing resource by its properties

api/v1alpha1/zz_generated.deepcopy.go

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

cmd/models-schema/zz_generated.openapi.go

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

config/crd/bases/openstack.k-orc.cloud_users.yaml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -190,12 +190,14 @@ spec:
190190
description: |-
191191
passwordRef is a reference to a Secret containing the password
192192
for this user. The Secret must contain a key named "password".
193+
If not specified, the user is created without a password.
193194
maxLength: 253
194195
minLength: 1
195196
type: string
196-
required:
197-
- passwordRef
198197
type: object
198+
x-kubernetes-validations:
199+
- message: passwordRef may not be removed once set
200+
rule: '!has(oldSelf.passwordRef) || has(self.passwordRef)'
199201
required:
200202
- cloudCredentialsRef
201203
type: object

internal/controllers/user/actuator.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -140,10 +140,10 @@ func (actuator userActuator) CreateResource(ctx context.Context, obj orcObjectPT
140140
}
141141

142142
var password string
143-
{
143+
if resource.PasswordRef != nil {
144144
secret, secretReconcileStatus := dependency.FetchDependency(
145145
ctx, actuator.k8sClient, obj.Namespace,
146-
&resource.PasswordRef, "Secret",
146+
resource.PasswordRef, "Secret",
147147
func(*corev1.Secret) bool { return true },
148148
)
149149
reconcileStatus = reconcileStatus.WithReconcileStatus(secretReconcileStatus)
@@ -189,11 +189,11 @@ func (actuator userActuator) DeleteResource(ctx context.Context, _ orcObjectPT,
189189
func (actuator userActuator) reconcilePassword(ctx context.Context, obj orcObjectPT, osResource *osResourceT) progress.ReconcileStatus {
190190
log := ctrl.LoggerFrom(ctx)
191191
resource := obj.Spec.Resource
192-
if resource == nil {
192+
if resource == nil || resource.PasswordRef == nil {
193193
return nil
194194
}
195195

196-
currentRef := string(resource.PasswordRef)
196+
currentRef := string(*resource.PasswordRef)
197197
var lastAppliedRef string
198198
if obj.Status.Resource != nil {
199199
lastAppliedRef = obj.Status.Resource.AppliedPasswordRef
@@ -206,7 +206,7 @@ func (actuator userActuator) reconcilePassword(ctx context.Context, obj orcObjec
206206
// Read the password from the referenced Secret
207207
secret, secretRS := dependency.FetchDependency(
208208
ctx, actuator.k8sClient, obj.Namespace,
209-
&resource.PasswordRef, "Secret",
209+
resource.PasswordRef, "Secret",
210210
func(*corev1.Secret) bool { return true },
211211
)
212212
if secretRS != nil {

internal/controllers/user/controller.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -91,10 +91,10 @@ var passwordDependency = dependency.NewDependency[*orcv1alpha1.UserList, *corev1
9191
"spec.resource.passwordRef",
9292
func(user *orcv1alpha1.User) []string {
9393
resource := user.Spec.Resource
94-
if resource == nil {
94+
if resource == nil || resource.PasswordRef == nil {
9595
return nil
9696
}
97-
return []string{string(resource.PasswordRef)}
97+
return []string{string(*resource.PasswordRef)}
9898
},
9999
)
100100

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,4 @@
11
---
2-
apiVersion: v1
3-
kind: Secret
4-
metadata:
5-
name: user-create-minimal
6-
type: Opaque
7-
stringData:
8-
password: "TestPassword"
9-
---
102
apiVersion: openstack.k-orc.cloud/v1alpha1
113
kind: User
124
metadata:
@@ -16,5 +8,4 @@ spec:
168
cloudName: openstack-admin
179
secretName: openstack-clouds
1810
managementPolicy: managed
19-
resource:
20-
passwordRef: user-create-minimal
11+
resource: {}

internal/controllers/user/tests/user-create-minimal/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
## Step 00
44

5-
Create a minimal User, that sets only the required fields, and verify that the observed state corresponds to the spec.
5+
Create a minimal User without a password, and verify that the observed state corresponds to the spec.
66

77
Also validate that the OpenStack resource uses the name of the ORC object when no name is explicitly specified.
88

internal/controllers/user/tests/user-import-dependency/00-import-resource.yaml

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,4 @@
11
---
2-
apiVersion: v1
3-
kind: Secret
4-
metadata:
5-
name: user-import-dependency-password
6-
type: Opaque
7-
stringData:
8-
password: "TestPassword"
9-
---
102
apiVersion: openstack.k-orc.cloud/v1alpha1
113
kind: Domain
124
metadata:

internal/controllers/user/tests/user-import-dependency/01-create-trap-resource.yaml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,5 +21,4 @@ spec:
2121
secretName: openstack-clouds
2222
managementPolicy: managed
2323
resource:
24-
domainRef: user-import-dependency-not-this-one
25-
passwordRef: user-import-dependency-password
24+
domainRef: user-import-dependency-not-this-one

0 commit comments

Comments
 (0)