Merge pull request #65 from stephb9959/main

Version 2.4.0
This commit is contained in:
Charles
2021-11-15 16:58:40 -05:00
committed by GitHub
12 changed files with 159 additions and 71 deletions

18
package-lock.json generated
View File

@@ -1,12 +1,12 @@
{ {
"name": "ucentral-client", "name": "ucentral-client",
"version": "2.3.20", "version": "2.4.0",
"lockfileVersion": 2, "lockfileVersion": 2,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "ucentral-client", "name": "ucentral-client",
"version": "2.3.20", "version": "2.4.0",
"dependencies": { "dependencies": {
"@coreui/coreui": "^3.4.0", "@coreui/coreui": "^3.4.0",
"@coreui/icons": "^2.0.1", "@coreui/icons": "^2.0.1",
@@ -32,7 +32,7 @@
"react-tooltip": "^4.2.21", "react-tooltip": "^4.2.21",
"react-widgets": "^5.1.1", "react-widgets": "^5.1.1",
"sass": "^1.35.1", "sass": "^1.35.1",
"ucentral-libs": "^1.0.29", "ucentral-libs": "^1.0.30",
"uuid": "^8.3.2" "uuid": "^8.3.2"
}, },
"devDependencies": { "devDependencies": {
@@ -14842,9 +14842,9 @@
} }
}, },
"node_modules/ucentral-libs": { "node_modules/ucentral-libs": {
"version": "1.0.29", "version": "1.0.30",
"resolved": "https://registry.npmjs.org/ucentral-libs/-/ucentral-libs-1.0.29.tgz", "resolved": "https://registry.npmjs.org/ucentral-libs/-/ucentral-libs-1.0.30.tgz",
"integrity": "sha512-yeuzfdk15YqUW7/BdAdR8PxF0IIGxubTfkJQBloZqOFWIfBb/b//lDWjQUKj2DkgxxAW3rY3XbmzwCU9UJuMPg==", "integrity": "sha512-rYSMUZZ6zVa3PokYUlvAydDfYQE0fUTWY8KClbCsuq8S9N+KwSlGsHVZRw/Vax9OyKh6jLBTWcRnQYkx9DG49g==",
"dependencies": { "dependencies": {
"@coreui/coreui": "^3.4.0", "@coreui/coreui": "^3.4.0",
"@coreui/icons": "^2.0.1", "@coreui/icons": "^2.0.1",
@@ -27716,9 +27716,9 @@
} }
}, },
"ucentral-libs": { "ucentral-libs": {
"version": "1.0.29", "version": "1.0.30",
"resolved": "https://registry.npmjs.org/ucentral-libs/-/ucentral-libs-1.0.29.tgz", "resolved": "https://registry.npmjs.org/ucentral-libs/-/ucentral-libs-1.0.30.tgz",
"integrity": "sha512-yeuzfdk15YqUW7/BdAdR8PxF0IIGxubTfkJQBloZqOFWIfBb/b//lDWjQUKj2DkgxxAW3rY3XbmzwCU9UJuMPg==", "integrity": "sha512-rYSMUZZ6zVa3PokYUlvAydDfYQE0fUTWY8KClbCsuq8S9N+KwSlGsHVZRw/Vax9OyKh6jLBTWcRnQYkx9DG49g==",
"requires": { "requires": {
"@coreui/coreui": "^3.4.0", "@coreui/coreui": "^3.4.0",
"@coreui/icons": "^2.0.1", "@coreui/icons": "^2.0.1",

View File

@@ -1,6 +1,6 @@
{ {
"name": "ucentral-client", "name": "ucentral-client",
"version": "2.3.20", "version": "2.4.0",
"dependencies": { "dependencies": {
"@coreui/coreui": "^3.4.0", "@coreui/coreui": "^3.4.0",
"@coreui/icons": "^2.0.1", "@coreui/icons": "^2.0.1",
@@ -26,7 +26,7 @@
"react-tooltip": "^4.2.21", "react-tooltip": "^4.2.21",
"react-widgets": "^5.1.1", "react-widgets": "^5.1.1",
"sass": "^1.35.1", "sass": "^1.35.1",
"ucentral-libs": "^1.0.29", "ucentral-libs": "^1.0.30",
"uuid": "^8.3.2" "uuid": "^8.3.2"
}, },
"main": "index.js", "main": "index.js",

View File

@@ -686,7 +686,7 @@
"check_phone": "Bitte überprüfen Sie Ihr Telefon auf Ihren Validierungscode", "check_phone": "Bitte überprüfen Sie Ihr Telefon auf Ihren Validierungscode",
"confirm_new_password": "Bestätige neues Passwort", "confirm_new_password": "Bestätige neues Passwort",
"create": "Benutzer erstellen", "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", "create_success": "Benutzer erfolgreich erstellt",
"creating": "Benutzer erstellen ...", "creating": "Benutzer erstellen ...",
"delete_avatar": "Avatar löschen", "delete_avatar": "Avatar löschen",

View File

@@ -686,7 +686,7 @@
"check_phone": "Please check your phone for your validation code", "check_phone": "Please check your phone for your validation code",
"confirm_new_password": "Confirm New Password", "confirm_new_password": "Confirm New Password",
"create": "Create User", "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", "create_success": "User Created Successfully",
"creating": "Creating User...", "creating": "Creating User...",
"delete_avatar": "Delete Avatar", "delete_avatar": "Delete Avatar",

View File

@@ -686,7 +686,7 @@
"check_phone": "Por favor revise su teléfono para su código de validación", "check_phone": "Por favor revise su teléfono para su código de validación",
"confirm_new_password": "confirmar nueva contraseña", "confirm_new_password": "confirmar nueva contraseña",
"create": "Crear usuario", "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", "create_success": "Usuario creado con éxito",
"creating": "Creando usuario ...", "creating": "Creando usuario ...",
"delete_avatar": "Eliminar avatar", "delete_avatar": "Eliminar avatar",

View File

@@ -686,7 +686,7 @@
"check_phone": "Veuillez vérifier votre téléphone pour votre code de validation", "check_phone": "Veuillez vérifier votre téléphone pour votre code de validation",
"confirm_new_password": "Confirmer le nouveau mot de passe", "confirm_new_password": "Confirmer le nouveau mot de passe",
"create": "Créer un utilisateur", "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", "create_success": "L'utilisateur a été créé avec succès",
"creating": "Création de l'utilisateur...", "creating": "Création de l'utilisateur...",
"delete_avatar": "Supprimer l'avatar", "delete_avatar": "Supprimer l'avatar",

View File

@@ -686,7 +686,7 @@
"check_phone": "Por favor, verifique o seu telefone para o seu código de validação", "check_phone": "Por favor, verifique o seu telefone para o seu código de validação",
"confirm_new_password": "confirme a nova senha", "confirm_new_password": "confirme a nova senha",
"create": "Criar usuário", "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", "create_success": "Usuário criado com sucesso",
"creating": "Criando usuário ...", "creating": "Criando usuário ...",
"delete_avatar": "Apagar Avatar", "delete_avatar": "Apagar Avatar",

View File

@@ -27,7 +27,7 @@ const initialState = {
error: false, error: false,
}, },
userRole: { userRole: {
value: 'admin', value: 'accounting',
error: false, error: false,
}, },
notes: { notes: {
@@ -42,16 +42,11 @@ const initialState = {
}, },
}; };
const CreateUserModal = ({ show, toggle, getUsers }) => { const CreateUserModal = ({ show, toggle, getUsers, policies }) => {
const { t } = useTranslation(); const { t } = useTranslation();
const { currentToken, endpoints } = useAuth(); const { currentToken, endpoints } = useAuth();
const { addToast } = useToast(); const { addToast } = useToast();
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const [policies, setPolicies] = useState({
passwordPolicy: '',
passwordPattern: '',
accessPolicy: '',
});
const [formFields, updateFieldWithId, updateField, setFormFields] = useFormFields(initialState); const [formFields, updateFieldWithId, updateField, setFormFields] = useFormFields(initialState);
const toggleChange = () => { const toggleChange = () => {
@@ -107,10 +102,10 @@ const CreateUserModal = ({ show, toggle, getUsers }) => {
}); });
toggle(); toggle();
}) })
.catch(() => { .catch((e) => {
addToast({ addToast({
title: t('common.error'), title: t('common.error'),
body: t('user.create_failure'), body: t('user.create_failure', { error: e.response?.data?.ErrorDescription }),
color: 'danger', color: 'danger',
autohide: true, autohide: true,
}); });
@@ -122,23 +117,6 @@ const CreateUserModal = ({ show, toggle, getUsers }) => {
setLoading(false); 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(() => { useEffect(() => {
setFormFields(initialState); setFormFields(initialState);
}, [show]); }, [show]);
@@ -177,6 +155,7 @@ CreateUserModal.propTypes = {
show: PropTypes.bool.isRequired, show: PropTypes.bool.isRequired,
toggle: PropTypes.func.isRequired, toggle: PropTypes.func.isRequired,
getUsers: PropTypes.func.isRequired, getUsers: PropTypes.func.isRequired,
policies: PropTypes.instanceOf(Object).isRequired,
}; };
export default React.memo(CreateUserModal); export default React.memo(CreateUserModal);

View File

@@ -36,7 +36,7 @@ const initialState = {
editable: true, editable: true,
}, },
userRole: { userRole: {
value: '', value: 'accounting',
error: false, error: false,
editable: true, 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 { t } = useTranslation();
const { currentToken, endpoints } = useAuth(); const { currentToken, endpoints } = useAuth();
const { addToast } = useToast(); const { addToast } = useToast();
@@ -54,23 +54,6 @@ const EditUserModal = ({ show, toggle, userId, getUsers }) => {
const [initialUser, setInitialUser] = useState({}); const [initialUser, setInitialUser] = useState({});
const [editing, setEditing] = useState(false); const [editing, setEditing] = useState(false);
const [user, updateWithId, updateWithKey, setUser] = useUser(initialState); 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 getUser = () => {
const options = { const options = {
@@ -209,9 +192,6 @@ const EditUserModal = ({ show, toggle, userId, getUsers }) => {
if (userId) { if (userId) {
getUser(); getUser();
} }
if (policies.passwordPattern.length === 0) {
getPasswordPolicy();
}
}, [userId]); }, [userId]);
useEffect(() => { useEffect(() => {
@@ -243,6 +223,7 @@ EditUserModal.propTypes = {
show: PropTypes.bool.isRequired, show: PropTypes.bool.isRequired,
toggle: PropTypes.func.isRequired, toggle: PropTypes.func.isRequired,
getUsers: PropTypes.func.isRequired, getUsers: PropTypes.func.isRequired,
policies: PropTypes.instanceOf(Object).isRequired,
}; };
export default React.memo(EditUserModal); export default React.memo(EditUserModal);

View File

@@ -133,7 +133,7 @@ const Login = () => {
const onKeyDown = (event, action) => { const onKeyDown = (event, action) => {
if (event.code === 'Enter') { 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 = () => { const sendForgotPasswordEmail = () => {
setForgotResponse(initialResponseState); setForgotResponse(initialResponseState);
@@ -446,6 +547,7 @@ const Login = () => {
toggleForgotPassword={toggleForgotPassword} toggleForgotPassword={toggleForgotPassword}
formType={formType} formType={formType}
onKeyDown={onKeyDown} onKeyDown={onKeyDown}
submitForm={submitForm}
sendForgotPasswordEmail={sendForgotPasswordEmail} sendForgotPasswordEmail={sendForgotPasswordEmail}
changePasswordResponse={changePasswordResponse} changePasswordResponse={changePasswordResponse}
cancelPasswordChange={cancelPasswordChange} cancelPasswordChange={cancelPasswordChange}

View File

@@ -20,6 +20,23 @@ const UserListPage = () => {
const [loading, setLoading] = useState(true); const [loading, setLoading] = useState(true);
const [deleteLoading, setDeleteLoading] = useState(false); const [deleteLoading, setDeleteLoading] = useState(false);
const [usersPerPage, setUsersPerPage] = useState(getItem('devicesPerPage') || '10'); 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 = () => { const toggleCreateModal = () => {
setShowCreateModal(!showCreateModal); setShowCreateModal(!showCreateModal);
@@ -180,6 +197,7 @@ const UserListPage = () => {
useEffect(() => { useEffect(() => {
getUsers(); getUsers();
getPasswordPolicy();
}, []); }, []);
useEffect(() => { useEffect(() => {
@@ -193,7 +211,7 @@ const UserListPage = () => {
<div> <div>
<UserListTable <UserListTable
t={t} t={t}
users={usersToDisplay} users={usersToDisplay.sort((a, b) => a.email > b.email)}
loading={loading} loading={loading}
usersPerPage={usersPerPage} usersPerPage={usersPerPage}
setUsersPerPage={updateUsersPerPage} setUsersPerPage={updateUsersPerPage}
@@ -206,12 +224,18 @@ const UserListPage = () => {
toggleEdit={toggleEditModal} toggleEdit={toggleEditModal}
refreshUsers={getUsers} refreshUsers={getUsers}
/> />
<CreateUserModal show={showCreateModal} toggle={toggleCreateModal} getUsers={getUsers} /> <CreateUserModal
show={showCreateModal}
toggle={toggleCreateModal}
getUsers={getUsers}
policies={policies}
/>
<EditUserModal <EditUserModal
show={showEditModal} show={showEditModal}
toggle={toggleEditModal} toggle={toggleEditModal}
userId={userToEdit} userId={userToEdit}
getUsers={getUsers} getUsers={getUsers}
policies={policies}
/> />
</div> </div>
); );

View File

@@ -20,10 +20,12 @@ axiosInstance.interceptors.response.use(
case 401: case 401:
break; break;
case 403: case 403:
if (error.response.data?.ErrorCode === 9) {
localStorage.removeItem('access_token'); localStorage.removeItem('access_token');
localStorage.removeItem('gateway_endpoints'); localStorage.removeItem('gateway_endpoints');
sessionStorage.clear(); sessionStorage.clear();
window.location.href = '/'; window.location.href = '/';
}
break; break;
default: default:
break; break;