Skip to content

Commit d94c14f

Browse files
sklein94cesmarvin
authored andcommitted
Merge branch 'release/v1.19.0-1'
2 parents be6e166 + 1fc5ba6 commit d94c14f

File tree

14 files changed

+91
-40
lines changed

14 files changed

+91
-40
lines changed

CHANGELOG.md

+7
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
66

77
## [Unreleased]
88

9+
## [v1.19.0-1] - 2025-01-22
10+
### Changed
11+
- The user form is now only valid if all required fields are filled out [#183]
12+
913
## [v1.18.0-1] - 2025-01-17
1014
### Changed
1115
- The internal makefiles have been updated to standardize the versioning of the release notes.
@@ -17,6 +21,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1721
- Add new integration tests [#161]
1822
- New integration tests regarding user import, including the testing of the e-mail dispatch
1923
- Adjust Pipeline to include Mailhog for Testing
24+
-
25+
### Fixed
26+
- Fixed validation in user creation form to only allow saving when all required fields are filled out
2027

2128
## [v1.17.2-1] - 2024-11-22
2229
### Removed

Dockerfile

+1-1
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ FROM registry.cloudogu.com/official/java:8u432-1
7575
ARG TOMCAT_VERSION
7676

7777
LABEL NAME="official/usermgt" \
78-
VERSION="1.18.0-1" \
78+
VERSION="1.19.0-1" \
7979
maintainer="[email protected]"
8080

8181
# mark as webapp for nginx

Makefile

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Set these to the desired values
22
ARTIFACT_ID=usermgt
3-
VERSION=1.18.0-1
3+
VERSION=1.19.0-1
44
# overwrite ADDITIONAL_LDFLAGS to disable static compilation
55
# this should fix https://github.com/golang/go/issues/13470
66
ADDITIONAL_LDFLAGS=""

app/src/main/ui/src/components/users/UserForm.tsx

+37-8
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import {deprecated_Form as Form, Details} from "@cloudogu/ces-theme-tailwind";
22
import {Button, H2, ListWithSearchbar} from "@cloudogu/deprecated-ces-theme-tailwind";
33
import {TrashIcon} from "@heroicons/react/24/outline";
4+
import {useEffect, useState} from "react";
45
import {twMerge} from "tailwind-merge";
56
import {t} from "../../helpers/i18nHelpers";
67
import {useConfirmation} from "../../hooks/useConfirmation";
@@ -13,6 +14,7 @@ import HelpLink from "../helpLink";
1314
import type {Group} from "../../services/Groups";
1415
import type {User} from "../../services/Users";
1516
import type {NotifyFunction, UseFormHandlerFunctions} from "@cloudogu/deprecated-ces-theme-tailwind";
17+
import type {ChangeEvent} from "react";
1618

1719
const MAX_SEARCH_RESULTS = 10;
1820

@@ -31,9 +33,18 @@ export interface UserFormProps<T extends User> {
3133
export default function UserForm<T extends User>(props: UserFormProps<T>) {
3234
const {handler, notification, notify} = useUserFormHandler<T>(props.initialUser, (values: T) => props.onSubmit(values, notify, handler));
3335
const {open, setOpen: toggleModal, targetName: groupName, setTargetName: setGroupName} = useConfirmation();
36+
const [formDisabled, setFormDisabled] = useState(false);
37+
useEffect(() => hasEmptyRequiredFields(), []);
3438

3539
const {admin} = useApplicationContext().casUser;
3640

41+
const originalChangeFunction = handler.handleChange;
42+
43+
handler.handleChange = (e:ChangeEvent) => {
44+
originalChangeFunction(e);
45+
hasEmptyRequiredFields();
46+
};
47+
3748
const addGroup = (groupName: string): void => {
3849
if (handler.values.memberOf.indexOf(groupName) < 0) {
3950
const newGroups = [...handler.values.memberOf, groupName];
@@ -83,6 +94,24 @@ export default function UserForm<T extends User>(props: UserFormProps<T>) {
8394
/>
8495
);
8596

97+
const hasEmptyRequiredFields = (): void => {
98+
const form = document.forms.item(0);
99+
console.log("Check for null values");
100+
if (form) {
101+
const inputs: NodeListOf<HTMLInputElement> = form.querySelectorAll("input:required");
102+
for (const input of inputs) {
103+
if (!input.value) {
104+
setFormDisabled(true);
105+
return;
106+
}
107+
}
108+
setFormDisabled(false);
109+
return;
110+
}
111+
setFormDisabled(true);
112+
return;
113+
};
114+
86115
return (
87116
<>
88117
<ConfirmationDialog
@@ -103,27 +132,27 @@ export default function UserForm<T extends User>(props: UserFormProps<T>) {
103132
{t("users.externalUserWarning")}
104133
</span>
105134
)}
106-
<Form.ValidatedTextInput type={"text"} name={"username"} disabled={props.disableUsernameField ?? true} data-testid="username" placeholder={t("users.placeholder.username")} hint={t("users.hint.username")}>
135+
<Form.ValidatedTextInput required type={"text"} name={"username"} disabled={props.disableUsernameField ?? true} data-testid="username" placeholder={t("users.placeholder.username")} hint={t("users.hint.username")} >
107136
{t("editUser.labels.username")}
108137
</Form.ValidatedTextInput>
109-
<Form.ValidatedTextInput disabled={props.initialUser.external} type={"text"} name={"givenname"} data-testid="givenname" placeholder={t("users.placeholder.givenname")}>
138+
<Form.ValidatedTextInput required disabled={props.initialUser.external} type={"text"} name={"givenname"} data-testid="givenname" placeholder={t("users.placeholder.givenname")} >
110139
{t("editUser.labels.givenName")}
111140
</Form.ValidatedTextInput>
112-
<Form.ValidatedTextInput disabled={props.initialUser.external} type={"text"} name={"surname"} data-testid="surname" placeholder={t("users.placeholder.surname")}>
141+
<Form.ValidatedTextInput required disabled={props.initialUser.external} type={"text"} name={"surname"} data-testid="surname" placeholder={t("users.placeholder.surname")} >
113142
{t("editUser.labels.surname")}
114143
</Form.ValidatedTextInput>
115-
<Form.ValidatedTextInput disabled={props.initialUser.external} type={"text"} name={"displayName"} data-testid="displayName" placeholder={t("users.placeholder.displayName")} hint={t("users.hint.displayName")}>
144+
<Form.ValidatedTextInput required disabled={props.initialUser.external} type={"text"} name={"displayName"} data-testid="displayName" placeholder={t("users.placeholder.displayName")} hint={t("users.hint.displayName")} >
116145
{t("editUser.labels.displayName")}
117146
</Form.ValidatedTextInput>
118-
<Form.ValidatedTextInput disabled={props.initialUser.external} type={"text"} name={"mail"} data-testid="mail" placeholder={t("users.placeholder.mail")}>
147+
<Form.ValidatedTextInput required disabled={props.initialUser.external} type={"text"} name={"mail"} data-testid="mail" placeholder={t("users.placeholder.mail")} >
119148
{t("editUser.labels.email")}
120149
</Form.ValidatedTextInput>
121150
{!props.initialUser.external &&
122151
<>
123-
<Form.ValidatedTextInput disabled={props.initialUser.external} type={"password"} name={"password"} data-testid="password" placeholder={t("users.placeholder.password")}>
152+
<Form.ValidatedTextInput required disabled={props.initialUser.external} type={"password"} name={"password"} data-testid="password" placeholder={t("users.placeholder.password")} >
124153
{t("editUser.labels.password")}
125154
</Form.ValidatedTextInput>
126-
<Form.ValidatedTextInput disabled={props.initialUser.external} type={"password"} name={"confirmPassword"} data-testid="confirmPassword" placeholder={t("users.placeholder.confirmPassword")}>
155+
<Form.ValidatedTextInput required disabled={props.initialUser.external} type={"password"} name={"confirmPassword"} data-testid="confirmPassword" placeholder={t("users.placeholder.confirmPassword")} >
127156
{t("editUser.labels.confirmPassword")}
128157
</Form.ValidatedTextInput>
129158
</>
@@ -156,7 +185,7 @@ export default function UserForm<T extends User>(props: UserFormProps<T>) {
156185
)}
157186

158187
<div className={"my-4"}>
159-
<Button variant={"primary"} type={"submit"} disabled={!handler.dirty} data-testid="save-button">
188+
<Button variant={"primary"} type={"submit"} disabled={formDisabled} data-testid="save-button">
160189
{t("editUser.buttons.save")}
161190
</Button>
162191
{props.additionalButtons as JSX.Element}

app/src/main/ui/src/i18n/de.json

+7-7
Original file line numberDiff line numberDiff line change
@@ -21,15 +21,15 @@
2121
"editUser.errors.username.maxlength": "Darf höchstens 128 zeichen enthalten",
2222
"editUser.errors.username.minlength": "Muss mindestens 2 Zeichen enthalten",
2323
"editUser.errors.username.required": "Nutzername muss ausgefüllt sein.",
24-
"editUser.labels.confirmPassword": "Passwort bestätigen",
25-
"editUser.labels.displayName": "Anzeigename",
26-
"editUser.labels.email": "E-Mail",
24+
"editUser.labels.confirmPassword": "Passwort bestätigen*",
25+
"editUser.labels.displayName": "Anzeigename*",
26+
"editUser.labels.email": "E-Mail*",
2727
"editUser.labels.external": "Externer Account",
28-
"editUser.labels.givenName": "Vorname",
28+
"editUser.labels.givenName": "Vorname*",
2929
"editUser.labels.mustChangePassword": "Nutzer muss sein Passwort beim nächsten Login ändern",
30-
"editUser.labels.password": "Passwort",
31-
"editUser.labels.surname": "Nachname",
32-
"editUser.labels.username": "Nutzername",
30+
"editUser.labels.password": "Passwort*",
31+
"editUser.labels.surname": "Nachname*",
32+
"editUser.labels.username": "Nutzername*",
3333
"editUser.notification.error": "Die Account Informationen konnten nicht gespeichert werden. Bitte versuchen Sie es später erneut.",
3434
"editUser.notification.success": "Die Account Informationen wurden erfolgreich gespeichert.",
3535
"general.applicationName": "User Management",

app/src/main/ui/src/i18n/en.json

+7-7
Original file line numberDiff line numberDiff line change
@@ -21,15 +21,15 @@
2121
"editUser.errors.username.maxlength": "May contain a maximum of 128 characters",
2222
"editUser.errors.username.minlength": "Must contain at least 2 characters",
2323
"editUser.errors.username.required": "Username is required.",
24-
"editUser.labels.confirmPassword": "Confirm Password",
25-
"editUser.labels.displayName": "Display Name",
26-
"editUser.labels.email": "E-Mail",
24+
"editUser.labels.confirmPassword": "Confirm Password*",
25+
"editUser.labels.displayName": "Display Name*",
26+
"editUser.labels.email": "E-Mail*",
2727
"editUser.labels.external": "External account",
28-
"editUser.labels.givenName": "Given Name",
28+
"editUser.labels.givenName": "Given Name*",
2929
"editUser.labels.mustChangePassword": "The user must change the password at the next login",
30-
"editUser.labels.password": "Password",
31-
"editUser.labels.surname": "Surname",
32-
"editUser.labels.username": "Username",
30+
"editUser.labels.password": "Password*",
31+
"editUser.labels.surname": "Surname*",
32+
"editUser.labels.username": "Username*",
3333
"editUser.notification.error": "Account information could not be saved. Please try again later.",
3434
"editUser.notification.success": "Account information saved successfully.",
3535
"general.applicationName": "User Management",

docs/gui/release_notes_de.md

+4
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ Technische Details zu einem Release finden Sie im zugehörigen [Changelog](https
66

77
## [Unreleased]
88

9+
## [v1.19.0-1] - 2025-01-22
10+
* Alle Pflichtfelder im Formular zum Anlegen/Editieren von Nutzern sind jetzt als verpflichtend markiert und es ist
11+
nicht mehr möglich, das Formular abzusenden, wenn nicht alle verpflichtenden Felder ausgefüllt sind
12+
913
## [v1.18.0-1] - 2025-01-17
1014
### Changed
1115
* Die internen Makefiles wurden aktualisiert um die Versionierung der Release-Notes zu vereinheitlichen.

docs/gui/release_notes_en.md

+4
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ Technical details on a release can be found in the corresponding [Changelog](htt
66

77
## [Unreleased]
88

9+
## [v1.19.0-1] - 2025-01-22
10+
* All mandatory fields in the form for creating/editing users are now marked as mandatory and it is no longer possible to submit the form
11+
no longer possible to submit the form if not all mandatory fields are filled in
12+
913
## [v1.18.0-1] - 2025-01-17
1014
### Changed
1115
* The internal makefiles have been updated to standardize the versioning of the release notes.

dogu.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"Name": "official/usermgt",
3-
"Version": "1.18.0-1",
3+
"Version": "1.19.0-1",
44
"DisplayName": "User Management",
55
"Description": "User and Group Management.",
66
"Category": "Administration Apps",

integrationTests/cypress/e2e/password_policy.feature

+3-2
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,15 @@ Feature: Tests for the verification of the password policy
77

88
Scenario: a user deletes his password input is shown all password rules
99
When the user deletes his password input
10+
And the user enters an invalid password
1011
And the user clicks save
1112
Then the password entry is marked as invalid
1213
And all password rules are displayed
1314

14-
Scenario: a user enters a valid password is shown that his confirm entry is not filled out yet
15+
Scenario: a user enters a valid password is shown that his different confirm entry must match
1516
Given the user deletes his password input
1617
When the user enters a valid password
18+
And the user enters an invalid confirm-password
1719
And the user clicks save
1820
Then the password entry is marked as valid
1921
And the password-confirm entry is marked as invalid
@@ -22,7 +24,6 @@ Feature: Tests for the verification of the password policy
2224
Scenario: a user who entered a valid password, enters a valid confirm entry
2325
Given the user deletes his password input
2426
When the user enters a valid password
25-
And the user deletes his given name input
2627
And the user enters a valid confirm-password
2728
And the user clicks save
2829
Then the password entry is marked as valid

integrationTests/cypress/support/step_definitions/then.ts

-2
Original file line numberDiff line numberDiff line change
@@ -50,13 +50,11 @@ Then("all password rules are displayed", function () {
5050
cy.get('span[data-testid="password-input-error-2"]').should('be.visible')
5151
cy.get('span[data-testid="password-input-error-3"]').should('be.visible')
5252
cy.get('span[data-testid="password-input-error-4"]').should('be.visible')
53-
cy.get('span[data-testid="password-input-error-5"]').should('be.visible')
5453

5554
cy.get('div[data-testid="password-input-error-errors"]').contains('The password must contain at least 14 characters.')
5655
cy.get('div[data-testid="password-input-error-errors"]').contains('The password must contain at least one capital letter.')
5756
cy.get('div[data-testid="password-input-error-errors"]').contains('The password must contain at least one lower case letter.')
5857
cy.get('div[data-testid="password-input-error-errors"]').contains('The password must contain at least 1 number.')
59-
cy.get('div[data-testid="password-input-error-errors"]').contains('The password must contain at least 1 special character.')
6058
cy.get('div[data-testid="password-input-error-errors"]').contains('The password must not contain only spaces.')
6159
});
6260

integrationTests/cypress/support/step_definitions/when.ts

+14-6
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import '@bahmutov/cy-api'
2-
import { When } from "@badeball/cypress-cucumber-preprocessor";
2+
import {When} from "@badeball/cypress-cucumber-preprocessor";
33
import env from "@cloudogu/dogu-integration-test-library/lib/environment_variables";
44

55
//Implement all necessary steps fore dogu integration test library
@@ -220,7 +220,7 @@ When("the user uploads the file {string}", function (fileName: string) {
220220
})
221221

222222
When("the user clicks on the details regarding the {string} user import", function (importStatus: string) {
223-
cy.get('details[data-testid="'+ importStatus +'-import-details"]').click()
223+
cy.get('details[data-testid="' + importStatus + '-import-details"]').click()
224224
})
225225

226226
When("the user downloads the import overview", function () {
@@ -230,21 +230,21 @@ When("the user downloads the import overview", function () {
230230
When("the user opens the user import details page", function () {
231231
cy.visit('/usermgt/summaries')
232232
cy.clickWarpMenuCheckboxIfPossible()
233-
if(cy.get('tbody').find('tr:nth-of-type(1)').invoke('find',"td:nth-of-type(4)").should('exist')) {
233+
if (cy.get('tbody').find('tr:nth-of-type(1)').invoke('find', "td:nth-of-type(4)").should('exist')) {
234234
cy.get('tbody').find('tr:nth-of-type(1)').invoke('find', "td:nth-of-type(4)").find('button').click()
235235
cy.get('div').find('span').contains("Details").click()
236236
}
237237
})
238238

239239
When("the user opens the menu in the functions column", function () {
240-
cy.get('tbody').find('tr:nth-of-type(1)').invoke('find',"td:nth-of-type(4)").find( 'button').click()
240+
cy.get('tbody').find('tr:nth-of-type(1)').invoke('find', "td:nth-of-type(4)").find('button').click()
241241
})
242242

243243
When("deletes the entry for the user import", function () {
244244
cy.get('table').find('tr').then((row) => {
245245
let i: number = 1;
246-
for (i; i < row.length; i++){
247-
cy.get('tbody').find('tr:nth-of-type(1)').invoke('find',"td:nth-of-type(4)").find( 'button').click()
246+
for (i; i < row.length; i++) {
247+
cy.get('tbody').find('tr:nth-of-type(1)').invoke('find', "td:nth-of-type(4)").find('button').click()
248248
cy.get('div').find('span').contains("Delete").click()
249249
cy.get('button').find('span').contains("OK").click()
250250
}
@@ -283,3 +283,11 @@ When("the user {string} with password {string} logs in", function (username: str
283283
cy.visit("/cas/login")
284284
cy.login(username, password, env.GetMaxRetryCount())
285285
})
286+
287+
When("the user enters an invalid password", function () {
288+
cy.get('input[id="password"]').type(" ")
289+
})
290+
291+
When("the user enters an invalid confirm-password", function () {
292+
cy.get('input[id="confirmPassword"]').clear().type(" ")
293+
})

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,5 @@
33
"cypress": "^13.13.1",
44
"yarn": "^1.22.22"
55
},
6-
"version": "1.18.0-1"
6+
"version": "1.19.0-1"
77
}

yarn.lock

+4-4
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ asn1@~0.2.3:
107107
dependencies:
108108
safer-buffer "~2.1.0"
109109

110-
assert-plus@^1.0.0, [email protected]:
110+
[email protected], assert-plus@^1.0.0:
111111
version "1.0.0"
112112
resolved "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz"
113113
integrity sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==
@@ -401,7 +401,7 @@ end-of-stream@^1.1.0:
401401
dependencies:
402402
once "^1.4.0"
403403

404-
enquirer@^2.3.6, "enquirer@>= 2.3.0 < 3":
404+
enquirer@^2.3.6:
405405
version "2.4.1"
406406
resolved "https://registry.npmjs.org/enquirer/-/enquirer-2.4.1.tgz"
407407
integrity sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==
@@ -469,7 +469,7 @@ [email protected]:
469469
optionalDependencies:
470470
"@types/yauzl" "^2.9.1"
471471

472-
extsprintf@^1.2.0, extsprintf@1.3.0:
472+
extsprintf@1.3.0, extsprintf@^1.2.0:
473473
version "1.3.0"
474474
resolved "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz"
475475
integrity sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==
@@ -784,7 +784,7 @@ minimist@^1.2.8:
784784
resolved "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz"
785785
integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==
786786

787-
ms@^2.1.1, [email protected].2:
787+
[email protected].2, ms@^2.1.1:
788788
version "2.1.2"
789789
resolved "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz"
790790
integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==

0 commit comments

Comments
 (0)