diff --git a/backend/src/Controller/SecurityController.php b/backend/src/Controller/SecurityController.php index 18c060a..360460b 100644 --- a/backend/src/Controller/SecurityController.php +++ b/backend/src/Controller/SecurityController.php @@ -2,6 +2,7 @@ namespace App\Controller; use App\Http\DataResponse; +use App\Http\ErrorResponse; use Symfony\Bundle\FrameworkBundle\Controller\Controller; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Request; @@ -12,10 +13,19 @@ class SecurityController extends Controller /** * @Route("/api/v1/login", name="api_v1_login", methods={"POST"}) */ - public function login(Request $request) + public function login() { $user = $this->getUser(); + if ($user == null) { + return new ErrorResponse( + 0, + "Identifiants invalides", + null, + 401, + ); + } + return new DataResponse([ 'username' => $user->getUsername(), 'roles' => $user->getRoles(), diff --git a/frontend/package-lock.json b/frontend/package-lock.json index fe47878..b460cc7 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -5107,7 +5107,8 @@ }, "ansi-regex": { "version": "2.1.1", - "bundled": true + "bundled": true, + "optional": true }, "aproba": { "version": "1.2.0", @@ -5125,11 +5126,13 @@ }, "balanced-match": { "version": "1.0.0", - "bundled": true + "bundled": true, + "optional": true }, "brace-expansion": { "version": "1.1.11", "bundled": true, + "optional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -5142,15 +5145,18 @@ }, "code-point-at": { "version": "1.1.0", - "bundled": true + "bundled": true, + "optional": true }, "concat-map": { "version": "0.0.1", - "bundled": true + "bundled": true, + "optional": true }, "console-control-strings": { "version": "1.1.0", - "bundled": true + "bundled": true, + "optional": true }, "core-util-is": { "version": "1.0.2", @@ -5253,7 +5259,8 @@ }, "inherits": { "version": "2.0.4", - "bundled": true + "bundled": true, + "optional": true }, "ini": { "version": "1.3.5", @@ -5263,6 +5270,7 @@ "is-fullwidth-code-point": { "version": "1.0.0", "bundled": true, + "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -5275,17 +5283,20 @@ "minimatch": { "version": "3.0.4", "bundled": true, + "optional": true, "requires": { "brace-expansion": "^1.1.7" } }, "minimist": { "version": "0.0.8", - "bundled": true + "bundled": true, + "optional": true }, "minipass": { "version": "2.9.0", "bundled": true, + "optional": true, "requires": { "safe-buffer": "^5.1.2", "yallist": "^3.0.0" @@ -5302,6 +5313,7 @@ "mkdirp": { "version": "0.5.1", "bundled": true, + "optional": true, "requires": { "minimist": "0.0.8" } @@ -5382,7 +5394,8 @@ }, "number-is-nan": { "version": "1.0.1", - "bundled": true + "bundled": true, + "optional": true }, "object-assign": { "version": "4.1.1", @@ -5392,6 +5405,7 @@ "once": { "version": "1.4.0", "bundled": true, + "optional": true, "requires": { "wrappy": "1" } @@ -5467,7 +5481,8 @@ }, "safe-buffer": { "version": "5.1.2", - "bundled": true + "bundled": true, + "optional": true }, "safer-buffer": { "version": "2.1.2", @@ -5497,6 +5512,7 @@ "string-width": { "version": "1.0.2", "bundled": true, + "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -5514,6 +5530,7 @@ "strip-ansi": { "version": "3.0.1", "bundled": true, + "optional": true, "requires": { "ansi-regex": "^2.0.0" } @@ -5552,11 +5569,13 @@ }, "wrappy": { "version": "1.0.2", - "bundled": true + "bundled": true, + "optional": true }, "yallist": { "version": "3.1.1", - "bundled": true + "bundled": true, + "optional": true } } }, diff --git a/frontend/src/actions/auth.actions.js b/frontend/src/actions/auth.actions.js new file mode 100644 index 0000000..2a22de1 --- /dev/null +++ b/frontend/src/actions/auth.actions.js @@ -0,0 +1,31 @@ +export const LOGIN_REQUEST = 'LOGIN_REQUEST' +export const LOGIN_SUCCESS = 'LOGIN_SUCCESS'; +export const LOGIN_FAILURE = 'LOGIN_FAILURE'; + +export function login(username, password) { + return { type: LOGIN_REQUEST, username, password } +} + +export function loginFailure(username, error) { + return { type: LOGIN_FAILURE, username, error } +} + +export function loginSuccess(username) { + return { type: LOGIN_SUCCESS, username } +} + +export const LOGOUT_REQUEST = 'LOGOUT_REQUEST' +export const LOGOUT_SUCCESS = 'LOGOUT_SUCCESS'; +export const LOGOUT_FAILURE = 'LOGOUT_FAILURE'; + +export function logout(username, password) { + return { type: LOGOUT_REQUEST, username, password } +} + +export function logoutFailure(username, error) { + return { type: LOGOUT_FAILURE, username, error } +} + +export function logoutSuccess(username) { + return { type: LOGOUT_SUCCESS, username } +} \ No newline at end of file diff --git a/frontend/src/app.js b/frontend/src/app.js index 886df5a..c7e5e20 100644 --- a/frontend/src/app.js +++ b/frontend/src/app.js @@ -3,6 +3,7 @@ import { hot } from 'react-hot-loader' import { HashRouter } from 'react-router-dom' // ou BrowserRouter import { Route, Switch, Redirect } from 'react-router' import HomePage from './pages/home'; +import { ConnectedLoginPage as LoginPage } from './pages/login'; class App extends Component { render () { @@ -10,6 +11,7 @@ class App extends Component { + } /> diff --git a/frontend/src/pages/login.js b/frontend/src/pages/login.js new file mode 100644 index 0000000..b4149c6 --- /dev/null +++ b/frontend/src/pages/login.js @@ -0,0 +1,67 @@ +import React, { useState, useEffect } from 'react' +import Page from './page'; +import { login } from '../actions/auth.actions'; +import { connect } from 'react-redux'; + +export function LoginPage({ dispatch, isLoggedIn, history }) { + + const [ formData, setFormData ] = useState({username: '', password: ''}); + const onUsernameChange = evt => setFormData({ ...formData, username: evt.target.value }); + const onPasswordChange = evt => setFormData({ ...formData, password: evt.target.value }); + const onSubmit = evt => { + evt.preventDefault(); + const { username, password } = formData; + dispatch(login(username, password)); + } + + useEffect(() => { + if (isLoggedIn) history.push('/home'); + }, [isLoggedIn]); + + + return ( + +
+
+
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+

+ +

+
+ +
+
+
+
+
+ ); +} + +function mapStateToProps({ session }) { + return { isLoggedIn: session.isLoggedIn }; +} + +export const ConnectedLoginPage = connect(mapStateToProps)(LoginPage); \ No newline at end of file diff --git a/frontend/src/reducers/session.reducers.js b/frontend/src/reducers/session.reducers.js new file mode 100644 index 0000000..eeb15a8 --- /dev/null +++ b/frontend/src/reducers/session.reducers.js @@ -0,0 +1,24 @@ +import { LOGIN_SUCCESS } from "../actions/auth.actions"; + +const initialState = { + isLoggedIn: false, + user: null, +}; + +export function sessionReducer(state = initialState, action) { + switch(action.type) { + case LOGIN_SUCCESS: + return handleLoginSuccess(state, action); + }; + return state; +} + +function handleLoginSuccess(state, action) { + return { + ...state.user, + isLoggedIn: true, + user: { + username: action.username, + }, + } +}; \ No newline at end of file diff --git a/frontend/src/sagas/auth.sagas.js b/frontend/src/sagas/auth.sagas.js new file mode 100644 index 0000000..f7d6435 --- /dev/null +++ b/frontend/src/sagas/auth.sagas.js @@ -0,0 +1,33 @@ +import { call, put } from 'redux-saga/effects'; +import { loginFailure, loginSuccess } from '../actions/auth.actions'; + +export function* loginSaga(action) { + let result; + try { + result = yield call(doLogin, action.username, action.password); + } catch(err) { + yield put(loginFailure(action.username, err)); + } + + if ('error' in result) { + yield put(loginFailure(action.username, result.error)); + return + } + + yield put(loginSuccess(action.username)); +} + +function doLogin(username, password) { + return fetch('http://localhost:8001/api/v1/login', { + method: 'POST', + body: JSON.stringify({ + username: username, + password: password + }), + headers: { + 'Content-Type': 'application/json', + }, + mode: 'cors', + credentials: 'include' + }).then(res => res.json()) +} \ No newline at end of file diff --git a/frontend/src/sagas/root.js b/frontend/src/sagas/root.js index 879634f..3e561d1 100644 --- a/frontend/src/sagas/root.js +++ b/frontend/src/sagas/root.js @@ -1,7 +1,9 @@ import { all, takeLatest } from 'redux-saga/effects'; +import { LOGIN_REQUEST } from '../actions/auth.actions'; +import { loginSaga} from './auth.sagas'; export default function* rootSaga() { yield all([ - + takeLatest(LOGIN_REQUEST, loginSaga), ]); } diff --git a/frontend/src/store/store.js b/frontend/src/store/store.js index 521df0a..71b924b 100644 --- a/frontend/src/store/store.js +++ b/frontend/src/store/store.js @@ -1,11 +1,12 @@ import { createStore, applyMiddleware, combineReducers, compose } from 'redux' import createSagaMiddleware from 'redux-saga' import rootSaga from '../sagas/root' +import { sessionReducer } from '../reducers/session.reducers'; const sagaMiddleware = createSagaMiddleware() const rootReducer = combineReducers({ - // Ajouter vos reducers ici + session: sessionReducer, }); const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;