Ajout page d'authentification

This commit is contained in:
wpetit 2020-02-19 12:21:04 +01:00
parent 72e328ea1c
commit 3aa8ab987b
9 changed files with 203 additions and 14 deletions

View File

@ -2,6 +2,7 @@
namespace App\Controller; namespace App\Controller;
use App\Http\DataResponse; use App\Http\DataResponse;
use App\Http\ErrorResponse;
use Symfony\Bundle\FrameworkBundle\Controller\Controller; use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Request;
@ -12,10 +13,19 @@ class SecurityController extends Controller
/** /**
* @Route("/api/v1/login", name="api_v1_login", methods={"POST"}) * @Route("/api/v1/login", name="api_v1_login", methods={"POST"})
*/ */
public function login(Request $request) public function login()
{ {
$user = $this->getUser(); $user = $this->getUser();
if ($user == null) {
return new ErrorResponse(
0,
"Identifiants invalides",
null,
401,
);
}
return new DataResponse([ return new DataResponse([
'username' => $user->getUsername(), 'username' => $user->getUsername(),
'roles' => $user->getRoles(), 'roles' => $user->getRoles(),

View File

@ -5107,7 +5107,8 @@
}, },
"ansi-regex": { "ansi-regex": {
"version": "2.1.1", "version": "2.1.1",
"bundled": true "bundled": true,
"optional": true
}, },
"aproba": { "aproba": {
"version": "1.2.0", "version": "1.2.0",
@ -5125,11 +5126,13 @@
}, },
"balanced-match": { "balanced-match": {
"version": "1.0.0", "version": "1.0.0",
"bundled": true "bundled": true,
"optional": true
}, },
"brace-expansion": { "brace-expansion": {
"version": "1.1.11", "version": "1.1.11",
"bundled": true, "bundled": true,
"optional": true,
"requires": { "requires": {
"balanced-match": "^1.0.0", "balanced-match": "^1.0.0",
"concat-map": "0.0.1" "concat-map": "0.0.1"
@ -5142,15 +5145,18 @@
}, },
"code-point-at": { "code-point-at": {
"version": "1.1.0", "version": "1.1.0",
"bundled": true "bundled": true,
"optional": true
}, },
"concat-map": { "concat-map": {
"version": "0.0.1", "version": "0.0.1",
"bundled": true "bundled": true,
"optional": true
}, },
"console-control-strings": { "console-control-strings": {
"version": "1.1.0", "version": "1.1.0",
"bundled": true "bundled": true,
"optional": true
}, },
"core-util-is": { "core-util-is": {
"version": "1.0.2", "version": "1.0.2",
@ -5253,7 +5259,8 @@
}, },
"inherits": { "inherits": {
"version": "2.0.4", "version": "2.0.4",
"bundled": true "bundled": true,
"optional": true
}, },
"ini": { "ini": {
"version": "1.3.5", "version": "1.3.5",
@ -5263,6 +5270,7 @@
"is-fullwidth-code-point": { "is-fullwidth-code-point": {
"version": "1.0.0", "version": "1.0.0",
"bundled": true, "bundled": true,
"optional": true,
"requires": { "requires": {
"number-is-nan": "^1.0.0" "number-is-nan": "^1.0.0"
} }
@ -5275,17 +5283,20 @@
"minimatch": { "minimatch": {
"version": "3.0.4", "version": "3.0.4",
"bundled": true, "bundled": true,
"optional": true,
"requires": { "requires": {
"brace-expansion": "^1.1.7" "brace-expansion": "^1.1.7"
} }
}, },
"minimist": { "minimist": {
"version": "0.0.8", "version": "0.0.8",
"bundled": true "bundled": true,
"optional": true
}, },
"minipass": { "minipass": {
"version": "2.9.0", "version": "2.9.0",
"bundled": true, "bundled": true,
"optional": true,
"requires": { "requires": {
"safe-buffer": "^5.1.2", "safe-buffer": "^5.1.2",
"yallist": "^3.0.0" "yallist": "^3.0.0"
@ -5302,6 +5313,7 @@
"mkdirp": { "mkdirp": {
"version": "0.5.1", "version": "0.5.1",
"bundled": true, "bundled": true,
"optional": true,
"requires": { "requires": {
"minimist": "0.0.8" "minimist": "0.0.8"
} }
@ -5382,7 +5394,8 @@
}, },
"number-is-nan": { "number-is-nan": {
"version": "1.0.1", "version": "1.0.1",
"bundled": true "bundled": true,
"optional": true
}, },
"object-assign": { "object-assign": {
"version": "4.1.1", "version": "4.1.1",
@ -5392,6 +5405,7 @@
"once": { "once": {
"version": "1.4.0", "version": "1.4.0",
"bundled": true, "bundled": true,
"optional": true,
"requires": { "requires": {
"wrappy": "1" "wrappy": "1"
} }
@ -5467,7 +5481,8 @@
}, },
"safe-buffer": { "safe-buffer": {
"version": "5.1.2", "version": "5.1.2",
"bundled": true "bundled": true,
"optional": true
}, },
"safer-buffer": { "safer-buffer": {
"version": "2.1.2", "version": "2.1.2",
@ -5497,6 +5512,7 @@
"string-width": { "string-width": {
"version": "1.0.2", "version": "1.0.2",
"bundled": true, "bundled": true,
"optional": true,
"requires": { "requires": {
"code-point-at": "^1.0.0", "code-point-at": "^1.0.0",
"is-fullwidth-code-point": "^1.0.0", "is-fullwidth-code-point": "^1.0.0",
@ -5514,6 +5530,7 @@
"strip-ansi": { "strip-ansi": {
"version": "3.0.1", "version": "3.0.1",
"bundled": true, "bundled": true,
"optional": true,
"requires": { "requires": {
"ansi-regex": "^2.0.0" "ansi-regex": "^2.0.0"
} }
@ -5552,11 +5569,13 @@
}, },
"wrappy": { "wrappy": {
"version": "1.0.2", "version": "1.0.2",
"bundled": true "bundled": true,
"optional": true
}, },
"yallist": { "yallist": {
"version": "3.1.1", "version": "3.1.1",
"bundled": true "bundled": true,
"optional": true
} }
} }
}, },

View 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 }
}

View File

@ -3,6 +3,7 @@ import { hot } from 'react-hot-loader'
import { HashRouter } from 'react-router-dom' // ou BrowserRouter import { HashRouter } from 'react-router-dom' // ou BrowserRouter
import { Route, Switch, Redirect } from 'react-router' import { Route, Switch, Redirect } from 'react-router'
import HomePage from './pages/home'; import HomePage from './pages/home';
import { ConnectedLoginPage as LoginPage } from './pages/login';
class App extends Component { class App extends Component {
render () { render () {
@ -10,6 +11,7 @@ class App extends Component {
<Fragment> <Fragment>
<HashRouter> <HashRouter>
<Switch> <Switch>
<Route path='/login' exact component={LoginPage} />
<Route path='/home' exact component={HomePage} /> <Route path='/home' exact component={HomePage} />
<Route component={() => <Redirect to="/home" />} /> <Route component={() => <Redirect to="/home" />} />
</Switch> </Switch>

View 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);

View 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,
},
}
};

View 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())
}

View File

@ -1,7 +1,9 @@
import { all, takeLatest } from 'redux-saga/effects'; import { all, takeLatest } from 'redux-saga/effects';
import { LOGIN_REQUEST } from '../actions/auth.actions';
import { loginSaga} from './auth.sagas';
export default function* rootSaga() { export default function* rootSaga() {
yield all([ yield all([
takeLatest(LOGIN_REQUEST, loginSaga),
]); ]);
} }

View File

@ -1,11 +1,12 @@
import { createStore, applyMiddleware, combineReducers, compose } from 'redux' import { createStore, applyMiddleware, combineReducers, compose } from 'redux'
import createSagaMiddleware from 'redux-saga' import createSagaMiddleware from 'redux-saga'
import rootSaga from '../sagas/root' import rootSaga from '../sagas/root'
import { sessionReducer } from '../reducers/session.reducers';
const sagaMiddleware = createSagaMiddleware() const sagaMiddleware = createSagaMiddleware()
const rootReducer = combineReducers({ const rootReducer = combineReducers({
// Ajouter vos reducers ici session: sessionReducer,
}); });
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose; const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;