✨ Ajoute le routeur et la page d'instanciation de la phrase de passe
This commit is contained in:
parent
c2b60b4b6a
commit
a8116aa5a2
29
.eslintrc.json
Normal file
29
.eslintrc.json
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
{
|
||||||
|
"env": {
|
||||||
|
"browser": true,
|
||||||
|
"es2021": true
|
||||||
|
},
|
||||||
|
"extends": [
|
||||||
|
"eslint:recommended",
|
||||||
|
"plugin:react/recommended",
|
||||||
|
"plugin:@typescript-eslint/recommended",
|
||||||
|
"plugin:react-hooks/recommended"
|
||||||
|
],
|
||||||
|
"parser": "@typescript-eslint/parser",
|
||||||
|
"parserOptions": {
|
||||||
|
"ecmaFeatures": {
|
||||||
|
"jsx": true
|
||||||
|
},
|
||||||
|
"ecmaVersion": "latest",
|
||||||
|
"sourceType": "module"
|
||||||
|
},
|
||||||
|
"plugins": [
|
||||||
|
"react",
|
||||||
|
"@typescript-eslint",
|
||||||
|
"react-hooks"
|
||||||
|
],
|
||||||
|
"rules": {
|
||||||
|
"react-hooks/rules-of-hooks": "error",
|
||||||
|
"react-hooks/exhaustive-deps": "warn"
|
||||||
|
}
|
||||||
|
}
|
@ -19,6 +19,7 @@ class UserController extends Controller
|
|||||||
$user = User::where('id', Auth::user()->getAuthIdentifier())->firstOrFail();
|
$user = User::where('id', Auth::user()->getAuthIdentifier())->firstOrFail();
|
||||||
$user->checkword = $validated['checkword'];
|
$user->checkword = $validated['checkword'];
|
||||||
$user->save();
|
$user->save();
|
||||||
|
Auth::setUser($user);
|
||||||
|
|
||||||
return response()->json(['success' => true]);
|
return response()->json(['success' => true]);
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
namespace App\Http;
|
namespace App\Http;
|
||||||
|
|
||||||
|
use App\Http\Middleware\CheckExistingPassphrase;
|
||||||
use Illuminate\Foundation\Http\Kernel as HttpKernel;
|
use Illuminate\Foundation\Http\Kernel as HttpKernel;
|
||||||
|
|
||||||
class Kernel extends HttpKernel
|
class Kernel extends HttpKernel
|
||||||
@ -54,6 +55,7 @@ class Kernel extends HttpKernel
|
|||||||
* @var array<string, class-string|string>
|
* @var array<string, class-string|string>
|
||||||
*/
|
*/
|
||||||
protected $routeMiddleware = [
|
protected $routeMiddleware = [
|
||||||
|
'auth.passphrase' => CheckExistingPassphrase::class,
|
||||||
'auth' => \App\Http\Middleware\Authenticate::class,
|
'auth' => \App\Http\Middleware\Authenticate::class,
|
||||||
'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
|
'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
|
||||||
'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class,
|
'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class,
|
||||||
|
30
app/Http/Middleware/CheckExistingPassphrase.php
Normal file
30
app/Http/Middleware/CheckExistingPassphrase.php
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Middleware;
|
||||||
|
|
||||||
|
use Closure;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Facades\Auth;
|
||||||
|
|
||||||
|
class CheckExistingPassphrase
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Handle an incoming request.
|
||||||
|
*
|
||||||
|
* @param \Illuminate\Http\Request $request
|
||||||
|
* @param \Closure(\Illuminate\Http\Request): (\Illuminate\Http\Response|\Illuminate\Http\RedirectResponse) $next
|
||||||
|
* @return \Illuminate\Http\Response|\Illuminate\Http\RedirectResponse
|
||||||
|
*/
|
||||||
|
public function handle(Request $request, Closure $next)
|
||||||
|
{
|
||||||
|
if (!$request->routeIs('user.*') && empty(Auth::user()->checkword)) {
|
||||||
|
return redirect(route('user.first'));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty(Auth::user()->checkword) && $request->routeIs('user.first')) {
|
||||||
|
return redirect(route('pages.index'));
|
||||||
|
}
|
||||||
|
|
||||||
|
return $next($request);
|
||||||
|
}
|
||||||
|
}
|
2387
package-lock.json
generated
2387
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -13,10 +13,14 @@
|
|||||||
"@babel/preset-react": "^7.13.13",
|
"@babel/preset-react": "^7.13.13",
|
||||||
"@popperjs/core": "^2.10.2",
|
"@popperjs/core": "^2.10.2",
|
||||||
"@tailwindcss/forms": "^0.4.0",
|
"@tailwindcss/forms": "^0.4.0",
|
||||||
|
"@typescript-eslint/eslint-plugin": "^5.12.1",
|
||||||
|
"@typescript-eslint/parser": "^5.12.1",
|
||||||
"alpinejs": "^3.4.2",
|
"alpinejs": "^3.4.2",
|
||||||
"autoprefixer": "^10.1.0",
|
"autoprefixer": "^10.1.0",
|
||||||
"axios": "^0.21.4",
|
"axios": "^0.21.4",
|
||||||
"bootstrap": "^5.1.3",
|
"bootstrap": "^5.1.3",
|
||||||
|
"eslint": "^8.9.0",
|
||||||
|
"eslint-plugin-react": "^7.29.0",
|
||||||
"laravel-mix": "^6.0.6",
|
"laravel-mix": "^6.0.6",
|
||||||
"lodash": "^4.17.19",
|
"lodash": "^4.17.19",
|
||||||
"postcss": "^8.2.1",
|
"postcss": "^8.2.1",
|
||||||
@ -36,8 +40,10 @@
|
|||||||
"@mui/icons-material": "^5.4.1",
|
"@mui/icons-material": "^5.4.1",
|
||||||
"@mui/material": "^5.4.1",
|
"@mui/material": "^5.4.1",
|
||||||
"@uiw/react-md-editor": "^3.9.4",
|
"@uiw/react-md-editor": "^3.9.4",
|
||||||
|
"eslint-plugin-react-hooks": "^4.3.0",
|
||||||
"react-crypt-gsm": "^1.0.4",
|
"react-crypt-gsm": "^1.0.4",
|
||||||
"react-query": "^3.34.12",
|
"react-query": "^3.34.12",
|
||||||
|
"react-router-dom": "^5.3.0",
|
||||||
"storage-encryption": "^1.0.16"
|
"storage-encryption": "^1.0.16"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
3395
public/js/app.js
vendored
3395
public/js/app.js
vendored
File diff suppressed because it is too large
Load Diff
@ -12,5 +12,5 @@ require('./bootstrap');
|
|||||||
* or customize the JavaScript scaffolding to fit your unique needs.
|
* or customize the JavaScript scaffolding to fit your unique needs.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
require('./components/pages/App');
|
require('./components/pages/AppWrapper');
|
||||||
require('./components/user/First');
|
require('./components/user/First');
|
||||||
|
@ -1,11 +1,17 @@
|
|||||||
import ReactDOM from 'react-dom';
|
|
||||||
import PageForm from "./Form";
|
import PageForm from "./Form";
|
||||||
import Pages from "./List";
|
import Pages from "./List";
|
||||||
import Prompt from "./Prompt";
|
import Prompt from "./Prompt";
|
||||||
import {useState} from "react";
|
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import {Divider, Paper} from "@mui/material";
|
import {Alert, AlertTitle, Divider} from "@mui/material";
|
||||||
import {EncryptStorage} from "storage-encryption";
|
import {EncryptStorage} from "storage-encryption";
|
||||||
|
import {
|
||||||
|
Switch,
|
||||||
|
Route,
|
||||||
|
Link,
|
||||||
|
Redirect,
|
||||||
|
withRouter,
|
||||||
|
useHistory
|
||||||
|
} from "react-router-dom";
|
||||||
|
|
||||||
interface List {
|
interface List {
|
||||||
id: string;
|
id: string;
|
||||||
@ -13,9 +19,9 @@ interface List {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const app = document.getElementById('app');
|
const app = document.getElementById('app');
|
||||||
const word = "shikiryu";
|
const word = "shikiryu"; // FIXME should be in db and ≠ between users
|
||||||
|
|
||||||
let sessionPassphrase = sessionStorage.getItem("key");
|
const sessionPassphrase = sessionStorage.getItem("key");
|
||||||
let pages: List[] = [];
|
let pages: List[] = [];
|
||||||
let getPageContentUrl,
|
let getPageContentUrl,
|
||||||
postUrl,
|
postUrl,
|
||||||
@ -30,42 +36,120 @@ if (app) {
|
|||||||
removeUrl = "" + app.getAttribute('data-remove');
|
removeUrl = "" + app.getAttribute('data-remove');
|
||||||
csrf = "" + app.getAttribute('data-csrf');
|
csrf = "" + app.getAttribute('data-csrf');
|
||||||
checkword = "" + app.getAttribute('data-checkword');
|
checkword = "" + app.getAttribute('data-checkword');
|
||||||
ReactDOM.render(<App/>, app);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function App() {
|
function App() {
|
||||||
const [listPages, setListPages] = useState(pages);
|
const [listPages, setListPages] = React.useState(pages);
|
||||||
const [passphrase, setPassphrase] = useState(sessionPassphrase);
|
const [passphrase, setPassphrase] = React.useState(sessionPassphrase);
|
||||||
|
const history = useHistory();
|
||||||
|
const [user, setUser] = React.useState(false);
|
||||||
|
const [error, isError] = React.useState(false);
|
||||||
|
|
||||||
const updatePassphrase = function(newPassphrase) {
|
const signin = cb => {
|
||||||
setPassphrase(newPassphrase);
|
const isAuthenticated = checkPassphrase();
|
||||||
return result(checkPassphrase());
|
setUser(isAuthenticated);
|
||||||
|
if (isAuthenticated) {
|
||||||
|
isError(false);
|
||||||
|
cb();
|
||||||
|
} else {
|
||||||
|
isError(true);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const checkPassphrase = function() {
|
const signout = cb => {
|
||||||
|
setUser(false);
|
||||||
|
cb();
|
||||||
|
};
|
||||||
|
|
||||||
|
function checkPassphrase() {
|
||||||
if (checkword === "" || checkword === null || checkword === "null") {
|
if (checkword === "" || checkword === null || checkword === "null") {
|
||||||
console.error("checkword is empty !");
|
console.error("checkword is empty !");
|
||||||
// return (<Redirect to="/first" />);
|
return false;
|
||||||
return false; // TODO redirect to first
|
|
||||||
}
|
}
|
||||||
localStorage.setItem("checkword", checkword);
|
localStorage.setItem("checkword", checkword);
|
||||||
|
|
||||||
const key = ""+sessionStorage.getItem("key");
|
const key = ""+sessionStorage.getItem("key");
|
||||||
if (key === "" || key === null || key === "null") {
|
if (key === "" || key === null || key === "null") {
|
||||||
console.error("key is empty 🤔 !");
|
console.error("key is empty 🤔 !");
|
||||||
// return (<Redirect to="/first" />);
|
return false;
|
||||||
return false; // TODO redirect to first ?
|
|
||||||
}
|
}
|
||||||
let encryptStorage = new EncryptStorage(key);
|
const encryptStorage = new EncryptStorage(key);
|
||||||
const decrypted_word = encryptStorage.decrypt("checkword");
|
const decrypted_word = encryptStorage.decrypt("checkword");
|
||||||
|
|
||||||
return decrypted_word === word;
|
return decrypted_word === word;
|
||||||
};
|
}
|
||||||
|
|
||||||
const result = function(correct) {
|
const Error = function() {
|
||||||
let content;
|
if (error) {
|
||||||
if (correct === true) {
|
return (
|
||||||
content = <div className="col-md-8">
|
<Alert severity="error">
|
||||||
|
<AlertTitle>Erreur</AlertTitle>
|
||||||
|
La phrase de passe ne correspond pas.
|
||||||
|
</Alert>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return (<div></div>);
|
||||||
|
}
|
||||||
|
|
||||||
|
const AuthButton = function() {
|
||||||
|
const history = useHistory();
|
||||||
|
|
||||||
|
return user ? (
|
||||||
|
<p>
|
||||||
|
Welcome!{" "}
|
||||||
|
<button
|
||||||
|
onClick={() => {
|
||||||
|
signout(() => history.push("/"));
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Sign out
|
||||||
|
</button>
|
||||||
|
</p>
|
||||||
|
) : (
|
||||||
|
<p>You are not logged in.</p>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function updatePassphrase(newPassphrase) {
|
||||||
|
setPassphrase(newPassphrase);
|
||||||
|
if (checkPassphrase()) {
|
||||||
|
isError(false);
|
||||||
|
setUser(true);
|
||||||
|
signin(() => {
|
||||||
|
history.push({ pathname: "/diary/public/pages" });
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
isError(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function PrivateRoute({ children, ...rest }) {
|
||||||
|
return (
|
||||||
|
<Route
|
||||||
|
{...rest}
|
||||||
|
render={({ location }) =>
|
||||||
|
user ? (
|
||||||
|
children
|
||||||
|
) : (
|
||||||
|
<Redirect
|
||||||
|
to={{
|
||||||
|
pathname: "/diary/public/pass",
|
||||||
|
state: { from: location }
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function PromptPage() {
|
||||||
|
return (<Prompt open={true} setOpen={updatePassphrase}/>);
|
||||||
|
}
|
||||||
|
|
||||||
|
function ListPage() {
|
||||||
|
return (
|
||||||
|
<div className="col-md-8">
|
||||||
<Pages
|
<Pages
|
||||||
pages={listPages}
|
pages={listPages}
|
||||||
url={getPageContentUrl}
|
url={getPageContentUrl}
|
||||||
@ -76,24 +160,29 @@ export default function App() {
|
|||||||
<Divider/>
|
<Divider/>
|
||||||
<PageForm setListPages={setListPages} csrf={csrf} url={postUrl} passphrase={passphrase}/>
|
<PageForm setListPages={setListPages} csrf={csrf} url={postUrl} passphrase={passphrase}/>
|
||||||
</div>
|
</div>
|
||||||
} else if (correct === false) {
|
);
|
||||||
content = <div className="col-md-8">
|
|
||||||
<Prompt open={true} setOpen={updatePassphrase}/>
|
|
||||||
</div>;
|
|
||||||
} else {
|
|
||||||
return correct;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<div className="container">
|
<AuthButton />
|
||||||
<div className="row justify-content-center">
|
<Error/>
|
||||||
{ content }
|
<ul>
|
||||||
</div>
|
<li>
|
||||||
</div>
|
<Link to="/diary/public/pages">Voir vos pages</Link>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<Switch>
|
||||||
|
<Route path="/diary/public/pass">
|
||||||
|
<PromptPage />
|
||||||
|
</Route>
|
||||||
|
<PrivateRoute path="/diary/public/pages">
|
||||||
|
<ListPage />
|
||||||
|
</PrivateRoute>
|
||||||
|
</Switch>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
return result(checkPassphrase());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export default withRouter(App);
|
||||||
|
17
resources/js/components/pages/AppWrapper.tsx
Normal file
17
resources/js/components/pages/AppWrapper.tsx
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
import * as React from 'react';
|
||||||
|
import ReactDOM from 'react-dom';
|
||||||
|
import { BrowserRouter as Router} from "react-router-dom";
|
||||||
|
import App from "./App";
|
||||||
|
|
||||||
|
const app = document.getElementById('app');
|
||||||
|
if (app) {
|
||||||
|
ReactDOM.render(<AppWrapper/>, app);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function AppWrapper() {
|
||||||
|
return (
|
||||||
|
<Router>
|
||||||
|
<App />
|
||||||
|
</Router>
|
||||||
|
)
|
||||||
|
}
|
@ -24,15 +24,19 @@ export default function FirstPage() {
|
|||||||
encryptStorage = new EncryptStorage(passphrase);
|
encryptStorage = new EncryptStorage(passphrase);
|
||||||
encryptStorage.encrypt("checkword", word);
|
encryptStorage.encrypt("checkword", word);
|
||||||
let encryptedFormData = new FormData();
|
let encryptedFormData = new FormData();
|
||||||
encryptedFormData.append("checkword", ""+localStorage.getItem("key"));
|
encryptedFormData.append("checkword", ""+localStorage.getItem("checkword"));
|
||||||
encryptedFormData.append('_token', csrf);
|
encryptedFormData.append('_token', csrf);
|
||||||
|
|
||||||
let response = await fetch(url, {
|
const response = await fetch(url, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
body: encryptedFormData
|
body: encryptedFormData
|
||||||
});
|
});
|
||||||
|
|
||||||
const json = await response.json(); // TODO redirect if success
|
const json = await response.json(); // TODO redirect if success
|
||||||
|
|
||||||
|
if (json.success) {
|
||||||
|
location.href = "/diary/public/pages"
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const updatePassphrase = (e: React.ChangeEvent<HTMLInputElement>) => {
|
const updatePassphrase = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
|
@ -20,10 +20,10 @@ Route::get('/', function () {
|
|||||||
});
|
});
|
||||||
|
|
||||||
Route::resource('pages', PageController::class)
|
Route::resource('pages', PageController::class)
|
||||||
->middleware(['auth']);
|
->middleware(['auth', 'auth.passphrase']);
|
||||||
|
|
||||||
Route::get('/first', [UserController::class, 'first'])->name('user.first');
|
Route::get('/first', [UserController::class, 'first'])->middleware(['auth', 'auth.passphrase'])->name('user.first');
|
||||||
Route::post('/first', [UserController::class, 'storeFirst'])->name('user.storeFirst');
|
Route::post('/first', [UserController::class, 'storeFirst'])->middleware(['auth', 'auth.passphrase'])->name('user.storeFirst');
|
||||||
|
|
||||||
Route::get('/dashboard', function () {
|
Route::get('/dashboard', function () {
|
||||||
return view('dashboard');
|
return view('dashboard');
|
||||||
|
Loading…
Reference in New Issue
Block a user