diff --git a/package-lock.json b/package-lock.json index 18526aa..820ea9c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "ucentral-client", - "version": "2.3.20", + "version": "2.4.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "ucentral-client", - "version": "2.3.20", + "version": "2.4.0", "dependencies": { "@coreui/coreui": "^3.4.0", "@coreui/icons": "^2.0.1", @@ -32,7 +32,7 @@ "react-tooltip": "^4.2.21", "react-widgets": "^5.1.1", "sass": "^1.35.1", - "ucentral-libs": "^1.0.29", + "ucentral-libs": "^1.0.30", "uuid": "^8.3.2" }, "devDependencies": { @@ -14842,9 +14842,9 @@ } }, "node_modules/ucentral-libs": { - "version": "1.0.29", - "resolved": "https://registry.npmjs.org/ucentral-libs/-/ucentral-libs-1.0.29.tgz", - "integrity": "sha512-yeuzfdk15YqUW7/BdAdR8PxF0IIGxubTfkJQBloZqOFWIfBb/b//lDWjQUKj2DkgxxAW3rY3XbmzwCU9UJuMPg==", + "version": "1.0.30", + "resolved": "https://registry.npmjs.org/ucentral-libs/-/ucentral-libs-1.0.30.tgz", + "integrity": "sha512-rYSMUZZ6zVa3PokYUlvAydDfYQE0fUTWY8KClbCsuq8S9N+KwSlGsHVZRw/Vax9OyKh6jLBTWcRnQYkx9DG49g==", "dependencies": { "@coreui/coreui": "^3.4.0", "@coreui/icons": "^2.0.1", @@ -27716,9 +27716,9 @@ } }, "ucentral-libs": { - "version": "1.0.29", - "resolved": "https://registry.npmjs.org/ucentral-libs/-/ucentral-libs-1.0.29.tgz", - "integrity": "sha512-yeuzfdk15YqUW7/BdAdR8PxF0IIGxubTfkJQBloZqOFWIfBb/b//lDWjQUKj2DkgxxAW3rY3XbmzwCU9UJuMPg==", + "version": "1.0.30", + "resolved": "https://registry.npmjs.org/ucentral-libs/-/ucentral-libs-1.0.30.tgz", + "integrity": "sha512-rYSMUZZ6zVa3PokYUlvAydDfYQE0fUTWY8KClbCsuq8S9N+KwSlGsHVZRw/Vax9OyKh6jLBTWcRnQYkx9DG49g==", "requires": { "@coreui/coreui": "^3.4.0", "@coreui/icons": "^2.0.1", diff --git a/package.json b/package.json index f388484..387b940 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ucentral-client", - "version": "2.3.20", + "version": "2.4.0", "dependencies": { "@coreui/coreui": "^3.4.0", "@coreui/icons": "^2.0.1", @@ -26,7 +26,7 @@ "react-tooltip": "^4.2.21", "react-widgets": "^5.1.1", "sass": "^1.35.1", - "ucentral-libs": "^1.0.29", + "ucentral-libs": "^1.0.30", "uuid": "^8.3.2" }, "main": "index.js", diff --git a/public/locales/de/translation.json b/public/locales/de/translation.json index a634fe1..dfc1763 100644 --- a/public/locales/de/translation.json +++ b/public/locales/de/translation.json @@ -686,7 +686,7 @@ "check_phone": "Bitte überprüfen Sie Ihr Telefon auf Ihren Validierungscode", "confirm_new_password": "Bestätige neues Passwort", "create": "Benutzer erstellen", - "create_failure": "Fehler beim Erstellen des Benutzers. Bitte stellen Sie sicher, dass diese E-Mail-Adresse nicht bereits mit einem Konto verknüpft ist.", + "create_failure": "Fehler beim Erstellen des Benutzers: {{error}}", "create_success": "Benutzer erfolgreich erstellt", "creating": "Benutzer erstellen ...", "delete_avatar": "Avatar löschen", diff --git a/public/locales/en/translation.json b/public/locales/en/translation.json index b696d83..600adfc 100644 --- a/public/locales/en/translation.json +++ b/public/locales/en/translation.json @@ -686,7 +686,7 @@ "check_phone": "Please check your phone for your validation code", "confirm_new_password": "Confirm New Password", "create": "Create User", - "create_failure": "Error while creating user. Please make sure this email address is not already linked to an account.", + "create_failure": "Error while creating user: {{error}}", "create_success": "User Created Successfully", "creating": "Creating User...", "delete_avatar": "Delete Avatar", diff --git a/public/locales/es/translation.json b/public/locales/es/translation.json index df886e8..cf7f8eb 100644 --- a/public/locales/es/translation.json +++ b/public/locales/es/translation.json @@ -686,7 +686,7 @@ "check_phone": "Por favor revise su teléfono para su código de validación", "confirm_new_password": "confirmar nueva contraseña", "create": "Crear usuario", - "create_failure": "Error al crear usuario. Asegúrese de que esta dirección de correo electrónico no esté vinculada a una cuenta.", + "create_failure": "Error al crear el usuario: {{error}}", "create_success": "Usuario creado con éxito", "creating": "Creando usuario ...", "delete_avatar": "Eliminar avatar", diff --git a/public/locales/fr/translation.json b/public/locales/fr/translation.json index 49f93bd..b710f36 100644 --- a/public/locales/fr/translation.json +++ b/public/locales/fr/translation.json @@ -686,7 +686,7 @@ "check_phone": "Veuillez vérifier votre téléphone pour votre code de validation", "confirm_new_password": "Confirmer le nouveau mot de passe", "create": "Créer un utilisateur", - "create_failure": "Erreur lors de la création de l'utilisateur. Veuillez vous assurer que cette adresse e-mail n'est pas déjà liée à un compte.", + "create_failure": "Erreur lors de la création de l'utilisateur : {{error}}", "create_success": "L'utilisateur a été créé avec succès", "creating": "Création de l'utilisateur...", "delete_avatar": "Supprimer l'avatar", diff --git a/public/locales/pt/translation.json b/public/locales/pt/translation.json index 4575d7e..7b40f4b 100644 --- a/public/locales/pt/translation.json +++ b/public/locales/pt/translation.json @@ -686,7 +686,7 @@ "check_phone": "Por favor, verifique o seu telefone para o seu código de validação", "confirm_new_password": "confirme a nova senha", "create": "Criar usuário", - "create_failure": "Erro ao criar usuário. Certifique-se de que este endereço de e-mail ainda não esteja vinculado a uma conta.", + "create_failure": "Erro ao criar usuário: {{error}}", "create_success": "Usuário criado com sucesso", "creating": "Criando usuário ...", "delete_avatar": "Apagar Avatar", diff --git a/src/components/CreateUserModal/index.js b/src/components/CreateUserModal/index.js index eff7b98..450650b 100644 --- a/src/components/CreateUserModal/index.js +++ b/src/components/CreateUserModal/index.js @@ -27,7 +27,7 @@ const initialState = { error: false, }, userRole: { - value: 'admin', + value: 'accounting', error: false, }, notes: { @@ -42,16 +42,11 @@ const initialState = { }, }; -const CreateUserModal = ({ show, toggle, getUsers }) => { +const CreateUserModal = ({ show, toggle, getUsers, policies }) => { const { t } = useTranslation(); const { currentToken, endpoints } = useAuth(); const { addToast } = useToast(); const [loading, setLoading] = useState(false); - const [policies, setPolicies] = useState({ - passwordPolicy: '', - passwordPattern: '', - accessPolicy: '', - }); const [formFields, updateFieldWithId, updateField, setFormFields] = useFormFields(initialState); const toggleChange = () => { @@ -107,10 +102,10 @@ const CreateUserModal = ({ show, toggle, getUsers }) => { }); toggle(); }) - .catch(() => { + .catch((e) => { addToast({ title: t('common.error'), - body: t('user.create_failure'), + body: t('user.create_failure', { error: e.response?.data?.ErrorDescription }), color: 'danger', autohide: true, }); @@ -122,23 +117,6 @@ const CreateUserModal = ({ show, toggle, getUsers }) => { setLoading(false); } }; - - const getPasswordPolicy = () => { - axiosInstance - .post(`${endpoints.owsec}/api/v1/oauth2?requirements=true`, {}) - .then((response) => { - const newPolicies = response.data; - newPolicies.accessPolicy = `${endpoints.owsec}${newPolicies.accessPolicy}`; - newPolicies.passwordPolicy = `${endpoints.owsec}${newPolicies.passwordPolicy}`; - setPolicies(response.data); - }) - .catch(() => {}); - }; - - useEffect(() => { - if (policies.passwordPattern.length === 0) getPasswordPolicy(); - }, []); - useEffect(() => { setFormFields(initialState); }, [show]); @@ -177,6 +155,7 @@ CreateUserModal.propTypes = { show: PropTypes.bool.isRequired, toggle: PropTypes.func.isRequired, getUsers: PropTypes.func.isRequired, + policies: PropTypes.instanceOf(Object).isRequired, }; export default React.memo(CreateUserModal); diff --git a/src/components/EditUserModal/index.js b/src/components/EditUserModal/index.js index 2996f00..96af348 100644 --- a/src/components/EditUserModal/index.js +++ b/src/components/EditUserModal/index.js @@ -36,7 +36,7 @@ const initialState = { editable: true, }, userRole: { - value: '', + value: 'accounting', error: false, editable: true, }, @@ -46,7 +46,7 @@ const initialState = { }, }; -const EditUserModal = ({ show, toggle, userId, getUsers }) => { +const EditUserModal = ({ show, toggle, userId, getUsers, policies }) => { const { t } = useTranslation(); const { currentToken, endpoints } = useAuth(); const { addToast } = useToast(); @@ -54,23 +54,6 @@ const EditUserModal = ({ show, toggle, userId, getUsers }) => { const [initialUser, setInitialUser] = useState({}); const [editing, setEditing] = useState(false); const [user, updateWithId, updateWithKey, setUser] = useUser(initialState); - const [policies, setPolicies] = useState({ - passwordPolicy: '', - passwordPattern: '', - accessPolicy: '', - }); - - const getPasswordPolicy = () => { - axiosInstance - .post(`${endpoints.owsec}/api/v1/oauth2?requirements=true`, {}) - .then((response) => { - const newPolicies = response.data; - newPolicies.accessPolicy = `${endpoints.owsec}${newPolicies.accessPolicy}`; - newPolicies.passwordPolicy = `${endpoints.owsec}${newPolicies.passwordPolicy}`; - setPolicies(response.data); - }) - .catch(() => {}); - }; const getUser = () => { const options = { @@ -209,9 +192,6 @@ const EditUserModal = ({ show, toggle, userId, getUsers }) => { if (userId) { getUser(); } - if (policies.passwordPattern.length === 0) { - getPasswordPolicy(); - } }, [userId]); useEffect(() => { @@ -243,6 +223,7 @@ EditUserModal.propTypes = { show: PropTypes.bool.isRequired, toggle: PropTypes.func.isRequired, getUsers: PropTypes.func.isRequired, + policies: PropTypes.instanceOf(Object).isRequired, }; export default React.memo(EditUserModal); diff --git a/src/pages/LoginPage/index.js b/src/pages/LoginPage/index.js index cd2b4a1..a374c39 100644 --- a/src/pages/LoginPage/index.js +++ b/src/pages/LoginPage/index.js @@ -133,7 +133,7 @@ const Login = () => { const onKeyDown = (event, action) => { if (event.code === 'Enter') { - action(); + action(event); } }; @@ -305,6 +305,107 @@ const Login = () => { } }; + const submitForm = (event) => { + event.preventDefault(); + setLoginResponse(initialResponseState); + + setLoading(true); + let token = ''; + + const parameters = { + userId: event.target?.username?.value, + password: event.target?.password?.value, + }; + + if (formType === 'change-password') { + parameters.newPassword = fields.newpassword.value; + } + + axiosInstance + .post(`${fields.ucentralsecurl.value}/api/v1/oauth2`, parameters) + .then((response) => { + // If there's MFA to do + if (response.data.method && response.data.created) { + setFormType(`validation-${response.data.method}-${response.data.uuid}`); + return null; + } + if (response.data.userMustChangePassword) { + setFormType('change-password'); + return null; + } + setItem('access_token', response.data.access_token); + token = response.data.access_token; + return axiosInstance.get(`${fields.ucentralsecurl.value}/api/v1/systemEndpoints`, { + headers: { + Accept: 'application/json', + Authorization: `Bearer ${response.data.access_token}`, + }, + }); + }) + .then((response) => { + if (response) { + const endpoints = { + owsec: fields.ucentralsecurl.value, + }; + for (const endpoint of response.data.endpoints) { + endpoints[endpoint.type] = endpoint.uri; + } + if (endpoints.owgw) getGatewayUIUrl(token, endpoints.owgw); + if (endpoints.owprov) getProvUIUrl(token, endpoints.owprov); + setItem('gateway_endpoints', JSON.stringify(endpoints)); + setEndpoints(endpoints); + setCurrentToken(token); + } + }) + .catch((error) => { + if (formType === 'change-password') { + if (error.response?.data?.ErrorCode === 3) { + setChangeResponse({ + text: t('login.previously_used'), + error: true, + tried: true, + }); + } else if (error.response?.data?.ErrorCode === 5) { + setChangeResponse({ + text: t('common.invalid_password'), + error: true, + tried: true, + }); + } else { + setChangeResponse({ + text: t('login.change_password_error'), + error: true, + tried: true, + }); + } + } else if (error.response.status === 403) { + if (error.response?.data?.ErrorCode === 1) setFormType('change-password'); + else if (error.response?.data?.ErrorCode === 2) { + setLoginResponse({ + text: t('common.invalid_credentials'), + error: true, + tried: true, + }); + } else { + setLoginResponse({ + text: t('login.login_error'), + error: true, + tried: true, + }); + } + } else { + setLoginResponse({ + text: t('login.login_error'), + error: true, + tried: true, + }); + } + }) + .finally(() => { + setLoading(false); + }); + }; + const sendForgotPasswordEmail = () => { setForgotResponse(initialResponseState); @@ -446,6 +547,7 @@ const Login = () => { toggleForgotPassword={toggleForgotPassword} formType={formType} onKeyDown={onKeyDown} + submitForm={submitForm} sendForgotPasswordEmail={sendForgotPasswordEmail} changePasswordResponse={changePasswordResponse} cancelPasswordChange={cancelPasswordChange} diff --git a/src/pages/UserListPage/index.js b/src/pages/UserListPage/index.js index 55a7dd1..2e7796a 100644 --- a/src/pages/UserListPage/index.js +++ b/src/pages/UserListPage/index.js @@ -20,6 +20,23 @@ const UserListPage = () => { const [loading, setLoading] = useState(true); const [deleteLoading, setDeleteLoading] = useState(false); const [usersPerPage, setUsersPerPage] = useState(getItem('devicesPerPage') || '10'); + const [policies, setPolicies] = useState({ + passwordPolicy: '', + passwordPattern: '', + accessPolicy: '', + }); + + const getPasswordPolicy = () => { + axiosInstance + .post(`${endpoints.owsec}/api/v1/oauth2?requirements=true`, {}) + .then((response) => { + const newPolicies = response.data; + newPolicies.accessPolicy = `${endpoints.owsec}${newPolicies.accessPolicy}`; + newPolicies.passwordPolicy = `${endpoints.owsec}${newPolicies.passwordPolicy}`; + setPolicies(response.data); + }) + .catch(() => {}); + }; const toggleCreateModal = () => { setShowCreateModal(!showCreateModal); @@ -180,6 +197,7 @@ const UserListPage = () => { useEffect(() => { getUsers(); + getPasswordPolicy(); }, []); useEffect(() => { @@ -193,7 +211,7 @@ const UserListPage = () => {