Ajout page d'authentification
This commit is contained in:
parent
72e328ea1c
commit
3aa8ab987b
@ -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(),
|
||||
|
41
frontend/package-lock.json
generated
41
frontend/package-lock.json
generated
@ -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
|
||||
}
|
||||
}
|
||||
},
|
||||
|
31
frontend/src/actions/auth.actions.js
Normal file
31
frontend/src/actions/auth.actions.js
Normal file
@ -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 }
|
||||
}
|
@ -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 {
|
||||
<Fragment>
|
||||
<HashRouter>
|
||||
<Switch>
|
||||
<Route path='/login' exact component={LoginPage} />
|
||||
<Route path='/home' exact component={HomePage} />
|
||||
<Route component={() => <Redirect to="/home" />} />
|
||||
</Switch>
|
||||
|
67
frontend/src/pages/login.js
Normal file
67
frontend/src/pages/login.js
Normal file
@ -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 (
|
||||
<Page>
|
||||
<div className="section">
|
||||
<div className="columns">
|
||||
<div className="column is-4 is-offset-4">
|
||||
<div className="box">
|
||||
<div className="field">
|
||||
<label className="label">Nom d'utilisateur</label>
|
||||
<div className="control">
|
||||
<input
|
||||
value={formData.username}
|
||||
onChange={onUsernameChange}
|
||||
type="text" className="input" />
|
||||
</div>
|
||||
</div>
|
||||
<div className="field">
|
||||
<label className="label">Mot de passe</label>
|
||||
<div className="control">
|
||||
<input
|
||||
value={formData.password}
|
||||
onChange={onPasswordChange}
|
||||
className="input" type="password" />
|
||||
</div>
|
||||
</div>
|
||||
<div className="field is-grouped is-grouped-right">
|
||||
<p className="control">
|
||||
<button onClick={onSubmit}
|
||||
className="button is-primary">
|
||||
Se connecter
|
||||
</button>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Page>
|
||||
);
|
||||
}
|
||||
|
||||
function mapStateToProps({ session }) {
|
||||
return { isLoggedIn: session.isLoggedIn };
|
||||
}
|
||||
|
||||
export const ConnectedLoginPage = connect(mapStateToProps)(LoginPage);
|
24
frontend/src/reducers/session.reducers.js
Normal file
24
frontend/src/reducers/session.reducers.js
Normal file
@ -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,
|
||||
},
|
||||
}
|
||||
};
|
33
frontend/src/sagas/auth.sagas.js
Normal file
33
frontend/src/sagas/auth.sagas.js
Normal file
@ -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())
|
||||
}
|
@ -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),
|
||||
]);
|
||||
}
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user