Skip to content

Commit

Permalink
Merge pull request #94 from anct-cnum/Verification_MDP_3_tentatives
Browse files Browse the repository at this point in the history
Ajout de la vérification de tentative de MDP
  • Loading branch information
MorganP-projects authored Jan 11, 2024
2 parents 9a32bf9 + 7fca6dc commit 1dfddb6
Show file tree
Hide file tree
Showing 6 changed files with 193 additions and 4 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"version": "0.1.0",
"private": true,
"dependencies": {
"@gouvfr/dsfr": "^1.9.0",
"@gouvfr/dsfr": "^1.11.0",
"@reduxjs/toolkit": "^1.9.5",
"axios": "^1.4.0",
"dayjs": "^1.11.9",
Expand Down
34 changes: 33 additions & 1 deletion src/actions/user.actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,15 @@ import { userService } from '../services/user.service.js';
export const userActions = {
login,
logout,
clearErrorConnexion,
verifyToken,
verifyPrefetToken,
choosePassword,
inviteAccountsPrefet,
forgottenPassword,
updateInfosCandidat,
confirmUserEmail
confirmUserEmail,
verifyCode,
};

function login(username, password) {
Expand Down Expand Up @@ -52,6 +54,10 @@ function logout() {
return { type: 'LOGOUT' };
}

function clearErrorConnexion() {
return { type: 'CLEAR_ERROR' };
}

function verifyToken(token) {
return dispatch => {
dispatch(request(token));
Expand Down Expand Up @@ -207,6 +213,7 @@ function updateInfosCandidat({ id, infos }) {
return { type: 'UPDATE_USER_EMAIL_FAILURE', error };
}
}

function confirmUserEmail(token) {
return dispatch => {
dispatch(request());
Expand All @@ -229,3 +236,28 @@ function confirmUserEmail(token) {
return { type: 'CONFIRMATION_UPDATE_USER_EMAIL_FAILURE', error };
}
}

function verifyCode(code, email) {
return dispatch => {
dispatch(request());
userService.verifyCode(code, email)
.then(
result => {
dispatch(success(result));
},
error => {
dispatch(failure(error));
}
);
};

function request() {
return { type: 'VERIFY_CODE_CONNEXION_REQUEST' };
}
function success(result) {
return { type: 'VERIFY_CODE_CONNEXION_SUCCESS', result };
}
function failure(error) {
return { type: 'VERIFY_CODE_CONNEXION_FAILURE', error };
}
}
29 changes: 29 additions & 0 deletions src/components/anonymous/Login.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import Header from '../common/Header';
import ModalResetPassword from './ModalResetPassword';
import Spinner from '../common/Spinner';
import Alerte from '../common/Alerte';
import ModalVerifyCode from './ModalVerifyCode';

function Login() {

Expand All @@ -16,13 +17,16 @@ function Login() {

const [submitted, setSubmitted] = useState(false);
const [showModalResetPassword, setShowModalResetPassword] = useState(false);
const [showModalVerifyCode, setShowModalVerifyCode] = useState(false);
const [countAttempt, setCountAttempt] = useState(3);
const { username, password } = inputs;
const loggingIn = useSelector(state => state.authentication.loggingIn);
const error = useSelector(state => state.authentication.error);
const deleteError = useSelector(state => state.conseiller?.deleteCandidatureError);
const errorEmail = useSelector(state => state.motDePasseOublie.error);
const validEmail = useSelector(state => state.motDePasseOublie.success);
const loading = useSelector(state => state.motDePasseOublie?.loading);
const messageCodeVerified = useSelector(state => state.authentication?.messageCodeVerified);

const dispatch = useDispatch();
const location = useLocation();
Expand All @@ -46,6 +50,10 @@ function Login() {
useEffect(() => {
if (error?.resetPasswordCnil) {
setShowModalResetPassword(true);
} else if (error?.attemptFail) {
setCountAttempt(3 - error?.attemptFail);
} else if (error?.openPopinVerifyCode) {
setShowModalVerifyCode(true);
}
}, [error]);

Expand All @@ -66,6 +74,16 @@ function Login() {
}
}, [loading]);

useEffect(() => {
if (messageCodeVerified) {
dispatch(userActions.clearErrorConnexion());
dispatch(alerteEtSpinnerActions.getMessageAlerte({
type: 'success',
message: messageCodeVerified,
status: null, description: null
}));
}
}, [messageCodeVerified]);
return (
<div>
<Header />
Expand All @@ -74,6 +92,9 @@ function Login() {
{showModalResetPassword &&
<ModalResetPassword username={username} setShowModalResetPassword={setShowModalResetPassword} />
}
{showModalVerifyCode &&
<ModalVerifyCode setShowModalVerifyCode={setShowModalVerifyCode} email={username}/>
}
<div className="fr-container fr-mt-3w fr-mb-5w">
<div className="fr-grid-row">
<div className="fr-col-offset-3"></div>
Expand Down Expand Up @@ -106,6 +127,14 @@ function Login() {
<div className="invalid">Mot de passe requis</div>
}
</div>
{error?.attemptFail < 3 &&
<div className="fr-mb-2w" style={{ color: 'red' }}>Erreur de mot de passe, il ne vous reste plus que&nbsp;
<b>{countAttempt}&nbsp;{countAttempt > 1 ? 'essais' : 'essai' }</b>.
</div>
}
{error?.attemptFail === 3 &&
<div className="fr-mb-2w" style={{ color: 'red' }}>Votre compte est bloqu&eacute; pour les <b>10 prochaines minutes</b>.</div>
}
{loggingIn && <span>Connexion en cours...</span>}
<button className="fr-btn" onClick={handleSubmit}>Se connecter</button>
<p className="fr-mt-3w">
Expand Down
81 changes: 81 additions & 0 deletions src/components/anonymous/ModalVerifyCode.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import React, { useEffect, useState } from 'react';
import PropTypes from 'prop-types';
import { userActions } from '../../actions';
import { useDispatch, useSelector } from 'react-redux';

const ModalVerifyCode = ({ setShowModalVerifyCode, email }) => {
const dispatch = useDispatch();
const [code, setCode] = useState('');
const messageCodeVerified = useSelector(state => state.authentication?.messageCodeVerified);
const errorVerifyingCode = useSelector(state => state.authentication?.errorVerifyingCode);

function handleSendCode() {
dispatch(userActions.verifyCode(code, email));
}

function handleChange(e) {
setCode(e.target?.value);
}

useEffect(() => {
if (messageCodeVerified) {
window.scrollTo({ top: 0, behavior: 'smooth' });
setShowModalVerifyCode(false);
}
}, [messageCodeVerified]);

return (
<dialog aria-labelledby="fr-modal-reset-password" role="dialog" id="fr-modal-reset-password" className="fr-modal modalOpened">
<div className="fr-container fr-container--fluid fr-container-md">
<div className="fr-grid-row fr-grid-row--center">
<div className="fr-col-12 fr-col-md-8 fr-col-lg-6">
<div className="fr-modal__body">
<div className="fr-modal__header">
</div>
<div className="fr-modal__content">
<h1 id="fr-modal-title-modal-1" className="fr-modal__title">
<span className="fr-icon-arrow-right-line fr-icon--lg"></span>
V&eacute;rification du compte conseiller
</h1>
<p>
Conform&eacute;ment aux r&egrave;gles de s&eacute;curit&eacute; des mots de passe,
un code de v&eacute;rification vous a &eacute;t&eacute; envoy&eacute; sur votre
adresse email personnelle.
Merci de le renseigner ici.
</p>

<div className="fr-grid-row fr-mt-3w">
<label className={`fr-label code ${errorVerifyingCode ? 'invalid' : ''}`} htmlFor="code">
Code de v&eacute;rification
</label>
<input
id="code"
name="code"
type="number"
value={code}
onChange={handleChange}
className={`fr-input fr-input-custom ${errorVerifyingCode ? ' fr-input--error' : ''}`} />
{errorVerifyingCode &&
<div className="invalid">{errorVerifyingCode}</div>
}
</div>
</div>
<div className="fr-modal__footer">
<button className="fr-btn fr-ml-auto" onClick={handleSendCode}>
V&eacute;rifier le code
</button>
</div>
</div>
</div>
</div>
</div>
</dialog>
);
};

ModalVerifyCode.propTypes = {
setShowModalVerifyCode: PropTypes.func,
email: PropTypes.string
};

export default ModalVerifyCode;
25 changes: 24 additions & 1 deletion src/reducers/authenticationReducer.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
let user = JSON.parse(localStorage.getItem('user'));
const initialState = user ? { loggedIn: true, user } : {};
const initialState = user ? { loggedIn: true, user, error: null } : {};

export default function authentication(state = initialState, action) {
switch (action.type) {
Expand All @@ -17,8 +17,31 @@ export default function authentication(state = initialState, action) {
return {
error: action.error
};
case 'CLEAR_ERROR':
return {
error: null
};
case 'LOGOUT':
return {};
case 'VERIFY_CODE_CONNEXION_REQUEST':
return {
...state,
verifyingCode: true,
messageCodeVerified: '',
errorVerifyingCode: '',
};
case 'VERIFY_CODE_CONNEXION_SUCCESS':
return {
...state,
verifyingCode: false,
messageCodeVerified: action.result.messageVerificationCode,
};
case 'VERIFY_CODE_CONNEXION_FAILURE':
return {
...state,
verifyingCode: false,
errorVerifyingCode: action.error,
};
default:
return state;
}
Expand Down
26 changes: 25 additions & 1 deletion src/services/user.service.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ export const userService = {
choosePassword,
updateInfosCandidat,
confirmUserEmail,
sendForgottenPasswordEmail
sendForgottenPasswordEmail,
verifyCode,
};

function login(username, password) {
Expand Down Expand Up @@ -105,6 +106,23 @@ function sendForgottenPasswordEmail(username) {
return fetch(uri, requestOptions).then(handleResponse);
}

function verifyCode(code, email) {
const apiUrlRoot = process.env.REACT_APP_API;

const requestOptions = {
method: 'PATCH',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
code, email
})
};

let uri = `${apiUrlRoot}/users/verify-code`;
return fetch(uri, requestOptions).then(handleResponse);
}

function handleResponse(response) {
return response.text().then(text => {
const data = text && JSON.parse(text);
Expand All @@ -116,6 +134,12 @@ function handleResponse(response) {
if (data?.data?.resetPasswordCnil && data.message === 'RESET_PASSWORD_CNIL') {
return Promise.reject({ resetPasswordCnil: true });
}
if (data?.data?.attemptFail && (data.message === 'ERROR_ATTEMPT_LOGIN')) {
return Promise.reject({ attemptFail: data?.data?.attemptFail });
}
if (data?.data?.openPopinVerifyCode && data.message === 'PROCESS_LOGIN_UNBLOCKED') {
return Promise.reject({ openPopinVerifyCode: data?.data?.openPopinVerifyCode });
}
const error = (data && data.message) || response.statusText;
return Promise.reject(error);
}
Expand Down

0 comments on commit 1dfddb6

Please sign in to comment.