From f510a25ea2a609a2aba918cc5450ff2ce13a1663 Mon Sep 17 00:00:00 2001 From: Andrea Lamparelli Date: Thu, 19 Dec 2024 17:42:59 +0100 Subject: [PATCH] Make user pwd not required with oidc Signed-off-by: Andrea Lamparelli --- .../horreum/svc/user/KeycloakUserBackend.java | 13 +- .../src/domain/admin/Administrators.tsx | 125 +----------------- horreum-web/src/domain/admin/Teams.tsx | 2 +- horreum-web/src/domain/user/ManagedTeams.tsx | 2 +- horreum-web/src/domain/user/NewUserModal.tsx | 60 +++++---- 5 files changed, 50 insertions(+), 152 deletions(-) diff --git a/horreum-backend/src/main/java/io/hyperfoil/tools/horreum/svc/user/KeycloakUserBackend.java b/horreum-backend/src/main/java/io/hyperfoil/tools/horreum/svc/user/KeycloakUserBackend.java index 12f2489a2..0201afba7 100644 --- a/horreum-backend/src/main/java/io/hyperfoil/tools/horreum/svc/user/KeycloakUserBackend.java +++ b/horreum-backend/src/main/java/io/hyperfoil/tools/horreum/svc/user/KeycloakUserBackend.java @@ -196,11 +196,14 @@ private static UserRepresentation convertUserRepresentation(UserService.NewUser rep.setLastName(user.user.lastName); rep.setEnabled(true); - CredentialRepresentation credentials = new CredentialRepresentation(); - credentials.setType(CredentialRepresentation.PASSWORD); - credentials.setTemporary(true); - credentials.setValue(user.password); - rep.setCredentials(List.of(credentials)); + if (user.password != null && !user.password.isBlank()) { + CredentialRepresentation credentials = new CredentialRepresentation(); + credentials.setType(CredentialRepresentation.PASSWORD); + credentials.setTemporary(true); + credentials.setValue(user.password); + rep.setCredentials(List.of(credentials)); + } + return rep; } diff --git a/horreum-web/src/domain/admin/Administrators.tsx b/horreum-web/src/domain/admin/Administrators.tsx index 9dc1809da..191eb72b1 100644 --- a/horreum-web/src/domain/admin/Administrators.tsx +++ b/horreum-web/src/domain/admin/Administrators.tsx @@ -3,21 +3,17 @@ import { useSelector } from "react-redux" import { Button, DualListSelector, Form, - FormGroup, - HelperText, - HelperTextItem, - FormHelperText, - Modal, - Spinner, - TextInput } from "@patternfly/react-core" + FormGroup +} from "@patternfly/react-core" import { TabFunctionsRef } from "../../components/SavedTabs" import {userApi, UserData} from "../../api" import UserSearch from "../../components/UserSearch" -import { isAdminSelector, userName } from "../../auth" -import { noop } from "../../utils" +import {isAdminSelector, userName} from "../../auth" import {AppContext} from "../../context/appContext"; import {AppContextType} from "../../context/@types/appContextTypes"; +import NewUserModal from "../user/NewUserModal"; +import {noop} from "../../utils"; function userElement(u: UserData) { @@ -114,117 +110,8 @@ export default function Administrators(props: AdministratorsProps) { setCreateNewUser(false)} - onCreate={(user, password) => { - return userApi.createUser({ user, password }).then( - () => { - alerting.dispatchInfo( - "USER_CREATED", - "User created", - "User was successfully created", - 3000 - ) - }, - error => alerting.dispatchError(error, "USER_NOT_CREATED", "Failed to create new user.") - ) - }} + onCreate={noop} /> ) } - -type NewUserModalProps = { - isOpen: boolean - onClose(): void - onCreate(user: UserData, password: string): Promise -} - -function NewUserModal(props: NewUserModalProps) { - const [username, setUsername] = useState() - const [password, setPassword] = useState() - const [email, setEmail] = useState() - const [firstName, setFirstName] = useState() - const [lastName, setLastName] = useState() - const [creating, setCreating] = useState(false) - const valid = username && password && email && /^.+@.+\..+$/.test(email) - useEffect(() => { - setUsername(undefined) - setPassword("") - setEmail("") - setFirstName("") - setLastName("") - }, [props.isOpen]) - return ( - { - setCreating(true) - props - .onCreate({ id: "", username: username || "", email, firstName, lastName }, password || "") - .catch(noop) - .finally(() => { - setCreating(false) - props.onClose() - }) - }} - > - Create - , - , - ]} - > - {creating ? ( - - ) : ( -
- - setUsername(val)} - validated={username ? "default" : "error"} - /> - - - setPassword(val)} - validated={password ? "default" : "error"} - /> - - - This password is only temporary and the user will change it during first login. - - - - - setEmail(val)} - validated={email && /^.+@.+\..+$/.test(email) ? "default" : "error"} - /> - - - setFirstName(val)} /> - - - setLastName(val)} /> - -
- )} -
- ) -} diff --git a/horreum-web/src/domain/admin/Teams.tsx b/horreum-web/src/domain/admin/Teams.tsx index a3bc05af0..59bbc970a 100644 --- a/horreum-web/src/domain/admin/Teams.tsx +++ b/horreum-web/src/domain/admin/Teams.tsx @@ -207,7 +207,7 @@ export default function Teams(props: TeamsProps) { team={selected.name} isOpen={newUserModalOpen} onClose={() => setNewUserModalOpen(false)} - onCreate={(user, roles) => { + onCreate={(user, _, roles) => { if (teamFuncsRef.current) { teamFuncsRef.current.addMember(user, roles) } diff --git a/horreum-web/src/domain/user/ManagedTeams.tsx b/horreum-web/src/domain/user/ManagedTeams.tsx index 3d7e04ebb..ce26fd72a 100644 --- a/horreum-web/src/domain/user/ManagedTeams.tsx +++ b/horreum-web/src/domain/user/ManagedTeams.tsx @@ -90,7 +90,7 @@ export default function ManagedTeams(props: ManagedTeamsProps) { team={team.key} isOpen={createNewUser} onClose={() => setCreateNewUser(false)} - onCreate={(user, roles) => { + onCreate={(user, _, roles) => { teamMembersFuncs.current && teamMembersFuncs.current.addMember(user, roles) }} /> diff --git a/horreum-web/src/domain/user/NewUserModal.tsx b/horreum-web/src/domain/user/NewUserModal.tsx index 910f64e06..0c339caee 100644 --- a/horreum-web/src/domain/user/NewUserModal.tsx +++ b/horreum-web/src/domain/user/NewUserModal.tsx @@ -17,13 +17,15 @@ import {userApi, UserData} from "../../api" import { getRoles } from "./TeamMembers" import {AppContext} from "../../context/appContext"; import {AppContextType} from "../../context/@types/appContextTypes"; +import {useSelector} from "react-redux"; +import {oidcSelector} from "../../auth"; type NewUserModalProps = { - team: string + team?: string isOpen: boolean onClose(): void - onCreate(user: UserData, roles: string[]): void + onCreate(user: UserData, password: string | undefined, roles: string[]): void } export default function NewUserModal(props: NewUserModalProps) { @@ -38,7 +40,8 @@ export default function NewUserModal(props: NewUserModalProps) { const [tester, setTester] = useState(true) const [uploader, setUploader] = useState(false) const [manager, setManager] = useState(false) - const valid = username && password && email && /^.+@.+\..+$/.test(email) + const isOidc = useSelector(oidcSelector) !== undefined; + const valid = username && (password || isOidc) && email && /^.+@.+\..+$/.test(email) useEffect(() => { setUsername(undefined) setPassword("") @@ -61,10 +64,10 @@ export default function NewUserModal(props: NewUserModalProps) { onClick={() => { setCreating(true) const user = { id: "", username: username || "", email, firstName, lastName } - const roles = getRoles(viewer, tester, uploader, manager) + const roles = props.team ? getRoles(viewer, tester, uploader, manager) : [] userApi.createUser({ user, password, team: props.team, roles }) .then(() => { - props.onCreate(user, roles) + props.onCreate(user, password, roles) alerting.dispatchInfo( "USER_CREATED", "User created", @@ -94,6 +97,7 @@ export default function NewUserModal(props: NewUserModalProps) {
setUsername(val)} @@ -101,15 +105,16 @@ export default function NewUserModal(props: NewUserModalProps) { /> setPassword(val)} - validated={password ? "default" : "error"} + validated={(password || isOidc) ? "default" : "error"} /> @@ -119,6 +124,7 @@ export default function NewUserModal(props: NewUserModalProps) { - setFirstName(val)} /> + setFirstName(val)} /> - setLastName(val)} /> - - - - - setViewer(val)} label="Viewer" /> - - - setTester(val)} label="Tester" /> - - - setUploader(val)} label="Uploader" /> - - - setManager(val)} label="Manager" /> - - + setLastName(val)} /> + {props.team && ( + + + + setViewer(val)} label="Viewer" /> + + + setTester(val)} label="Tester" /> + + + setUploader(val)} label="Uploader" /> + + + setManager(val)} label="Manager" /> + + + + )}
)}