From 2b699f77f45b3eb82073caf182a820c7aa9383e1 Mon Sep 17 00:00:00 2001 From: Maksim Eltyshev Date: Mon, 26 Jan 2026 21:18:32 +0100 Subject: [PATCH] feat: Add ability to unlink SSO from user --- .../UsersPane/ActionsStep.jsx | 34 +++++++++++++++++++ client/src/locales/ar-YE/core.js | 5 +++ client/src/locales/bg-BG/core.js | 5 +++ client/src/locales/ca-ES/core.js | 5 +++ client/src/locales/cs-CZ/core.js | 5 +++ client/src/locales/da-DK/core.js | 5 +++ client/src/locales/de-DE/core.js | 5 +++ client/src/locales/el-GR/core.js | 5 +++ client/src/locales/en-GB/core.js | 5 +++ client/src/locales/en-US/core.js | 5 +++ client/src/locales/es-ES/core.js | 5 +++ client/src/locales/et-EE/core.js | 5 +++ client/src/locales/fa-IR/core.js | 5 +++ client/src/locales/fi-FI/core.js | 5 +++ client/src/locales/fr-FR/core.js | 5 +++ client/src/locales/hu-HU/core.js | 5 +++ client/src/locales/id-ID/core.js | 5 +++ client/src/locales/it-IT/core.js | 5 +++ client/src/locales/ja-JP/core.js | 5 +++ client/src/locales/ko-KR/core.js | 5 +++ client/src/locales/nl-NL/core.js | 5 +++ client/src/locales/pl-PL/core.js | 5 +++ client/src/locales/pt-BR/core.js | 5 +++ client/src/locales/pt-PT/core.js | 5 +++ client/src/locales/ro-RO/core.js | 5 +++ client/src/locales/ru-RU/core.js | 5 +++ client/src/locales/sk-SK/core.js | 5 +++ client/src/locales/sr-Cyrl-RS/core.js | 5 +++ client/src/locales/sr-Latn-RS/core.js | 5 +++ client/src/locales/sv-SE/core.js | 5 +++ client/src/locales/tr-TR/core.js | 5 +++ client/src/locales/uk-UA/core.js | 5 +++ client/src/locales/uz-UZ/core.js | 5 +++ client/src/locales/vi-VN/core.js | 5 +++ client/src/locales/zh-CN/core.js | 5 +++ client/src/locales/zh-TW/core.js | 5 +++ server/api/controllers/users/update.js | 8 ++++- server/api/hooks/oidc/index.js | 27 ++++++++++----- server/utils/validators.js | 3 ++ 39 files changed, 238 insertions(+), 9 deletions(-) diff --git a/client/src/components/common/AdministrationModal/UsersPane/ActionsStep.jsx b/client/src/components/common/AdministrationModal/UsersPane/ActionsStep.jsx index 2b9732e2..862e451e 100644 --- a/client/src/components/common/AdministrationModal/UsersPane/ActionsStep.jsx +++ b/client/src/components/common/AdministrationModal/UsersPane/ActionsStep.jsx @@ -30,6 +30,7 @@ const StepTypes = { EDIT_PASSWORD: 'EDIT_PASSWORD', EDIT_ROLE: 'EDIT_ROLE', API_KEY: 'API_KEY', + UNLINK_SSO: 'UNLINK_SSO', ACTIVATE: 'ACTIVATE', DEACTIVATE: 'DEACTIVATE', DELETE: 'DELETE', @@ -58,6 +59,16 @@ const ActionsStep = React.memo(({ userId, onClose }) => { [userId, dispatch], ); + const handleUnlinkSsoConfirm = useCallback(() => { + dispatch( + entryActions.updateUser(userId, { + isSsoUser: false, + }), + ); + + onClose(); + }, [userId, onClose, dispatch]); + const handleActivateConfirm = useCallback(() => { dispatch( entryActions.updateUser(userId, { @@ -106,6 +117,10 @@ const ActionsStep = React.memo(({ userId, onClose }) => { openStep(StepTypes.API_KEY); }, [openStep]); + const handleUnlinkSsoClick = useCallback(() => { + openStep(StepTypes.UNLINK_SSO); + }, [openStep]); + const handleActivateClick = useCallback(() => { openStep(StepTypes.ACTIVATE); }, [openStep]); @@ -142,6 +157,17 @@ const ActionsStep = React.memo(({ userId, onClose }) => { ); case StepTypes.API_KEY: return ; + case StepTypes.UNLINK_SSO: + return ( + + ); case StepTypes.ACTIVATE: return ( { context: 'title', })} + {user.isSsoUser && !isCurrentUser && ( + + + {t('action.unlinkSso', { + context: 'title', + })} + + )} {!isCurrentUser && ( <> { sails.log.info('Initializing OIDC client'); let issuer; @@ -38,6 +50,8 @@ module.exports = function defineOidcHook(sails) { issuer = await openidClient.Issuer.discover(sails.config.custom.oidcIssuer); } catch (error) { sails.log.warn(`Error while initializing OIDC client: ${error}`); + + clientInitPromise = null; return null; } @@ -54,9 +68,10 @@ module.exports = function defineOidcHook(sails) { } client = new issuer.Client(metadata); - } + return client; + })(); - return client; + return clientInitPromise; }, async getBootstrap() { @@ -84,9 +99,5 @@ module.exports = function defineOidcHook(sails) { isEnabled() { return !!sails.config.custom.oidcIssuer; }, - - isActive() { - return client !== null; - }, }; }; diff --git a/server/utils/validators.js b/server/utils/validators.js index 695b80a6..f586cc35 100644 --- a/server/utils/validators.js +++ b/server/utils/validators.js @@ -13,6 +13,8 @@ const ID_REGEX = /^[1-9][0-9]*$/; const IDS_WITH_COMMA_REGEX = /^[1-9][0-9]*(,[1-9][0-9]*)*$/; const USERNAME_REGEX = /^[a-zA-Z0-9]+((_|\.)?[a-zA-Z0-9])*$/; +const is = (defaultValue) => (value) => value === defaultValue; + const isUrl = (value) => validator.isURL(value, { protocols: ['http', 'https'], @@ -61,6 +63,7 @@ module.exports = { IDS_WITH_COMMA_REGEX, USERNAME_REGEX, + is, isUrl, isIdInRange, isIdsWithCommaInRange,