mirror of
https://github.com/appen-isen/fermentardoise.git
synced 2026-01-18 16:17:27 +01:00
Nano index (#8)
* begin appearance * update * update * add profile page * begin * add login and logout functionality * update index * update
This commit is contained in:
134
api.php
Normal file
134
api.php
Normal file
@@ -0,0 +1,134 @@
|
|||||||
|
<?php
|
||||||
|
ini_set('display_errors', 1);
|
||||||
|
ini_set('display_startup_errors', 1);
|
||||||
|
error_reporting(E_ALL);
|
||||||
|
|
||||||
|
require_once 'resources/config.php';
|
||||||
|
require_once 'resources/database.php';
|
||||||
|
require_once LIBRARY_PATH . '/redirect.php';
|
||||||
|
require_once LIBRARY_PATH . '/exceptions.php';
|
||||||
|
|
||||||
|
$pathInfo = explode('/', trim($_SERVER['PATH_INFO'], '/\\'));
|
||||||
|
|
||||||
|
header('content-type: application/json; charset=utf-8');
|
||||||
|
|
||||||
|
$db = new Database();
|
||||||
|
|
||||||
|
function getAuthorizationToken(): ?string{
|
||||||
|
|
||||||
|
$headers = getallheaders();
|
||||||
|
|
||||||
|
$authorization = $headers['Authorization'];
|
||||||
|
|
||||||
|
if (!isset($authorization)) {
|
||||||
|
APIErrors::invalidHeader();
|
||||||
|
}
|
||||||
|
|
||||||
|
$authorization = explode(' ', trim($authorization), 2)[1];
|
||||||
|
|
||||||
|
if (empty($authorization)) {
|
||||||
|
APIErrors::invalidGrant();
|
||||||
|
}
|
||||||
|
return $authorization;
|
||||||
|
}
|
||||||
|
|
||||||
|
class APIErrors{
|
||||||
|
|
||||||
|
public static function invalidGrant()
|
||||||
|
{
|
||||||
|
http_response_code(400);
|
||||||
|
die(json_encode(array(
|
||||||
|
'error' => 'invalid_grant',
|
||||||
|
'error_description' => 'The authorization code is invalid or expired.'
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function invalidHeader()
|
||||||
|
{
|
||||||
|
http_response_code(400);
|
||||||
|
die(json_encode(array(
|
||||||
|
'error' => 'invalid_header',
|
||||||
|
'error_description' => 'The request is missing the Authorization header or the Authorization header is invalid.'
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function invalidRequest()
|
||||||
|
{
|
||||||
|
http_response_code(400);
|
||||||
|
die(json_encode(array(
|
||||||
|
'error' => 'invalid_request',
|
||||||
|
'error_description' => 'The request is missing a parameter, uses an unsupported parameter, uses an invalid parameter or repeats a parameter.'
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function invalidCredential()
|
||||||
|
{
|
||||||
|
http_response_code(400);
|
||||||
|
die(json_encode(array(
|
||||||
|
'error' => 'invalid_credential',
|
||||||
|
'error_description' => 'The request has error(s) in the credentials gave.'
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function internalError()
|
||||||
|
{
|
||||||
|
http_response_code(500);
|
||||||
|
die();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch ($pathInfo[0] . $_SERVER['REQUEST_METHOD']) {
|
||||||
|
case 'login' . 'POST':
|
||||||
|
$email = $_POST['email'];
|
||||||
|
$password = $_POST['pwd'];
|
||||||
|
|
||||||
|
if (!isset($email) || !isset($password)) {
|
||||||
|
APIErrors::invalidRequest();
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$result = $db->connectUser($email, $password, time()+14400);
|
||||||
|
} catch (AuthenticationException $_) {
|
||||||
|
APIErrors::invalidGrant();
|
||||||
|
}
|
||||||
|
|
||||||
|
http_response_code(200);
|
||||||
|
die(json_encode(array(
|
||||||
|
/*'access_token' => $access_token,
|
||||||
|
'created_at' => time(),
|
||||||
|
'token_type' => 'bearer'*/
|
||||||
|
'pass' => 'OK'
|
||||||
|
)));
|
||||||
|
|
||||||
|
case 'logout' . 'POST':
|
||||||
|
$authorization = getAuthorizationToken();
|
||||||
|
|
||||||
|
try {
|
||||||
|
$db->disconnectUser($authorization);
|
||||||
|
} catch (AuthenticationException $_) {
|
||||||
|
APIErrors::invalidGrant();
|
||||||
|
}
|
||||||
|
|
||||||
|
http_response_code(200);
|
||||||
|
die(json_encode(array(
|
||||||
|
'message' => 'Authorization code delete successfully.'
|
||||||
|
)));
|
||||||
|
case 'getEmail' . 'POST':
|
||||||
|
$authorization = getAuthorizationToken();
|
||||||
|
try {
|
||||||
|
$result = $db->getEmail($authorization);
|
||||||
|
} catch (AuthenticationException $_) {
|
||||||
|
APIErrors::invalidGrant();
|
||||||
|
}
|
||||||
|
|
||||||
|
http_response_code(200);
|
||||||
|
die(json_encode($result));
|
||||||
|
|
||||||
|
case 'test' . 'GET' :
|
||||||
|
http_response_code(200);
|
||||||
|
die(json_encode(test()));
|
||||||
|
default:
|
||||||
|
http_response_code(404);
|
||||||
|
die();
|
||||||
|
}
|
||||||
|
?>
|
||||||
0
fermentardoise.session.sql
Normal file
0
fermentardoise.session.sql
Normal file
30
index.html
30
index.html
@@ -1,14 +1,18 @@
|
|||||||
<!doctype html>
|
<!doctype html>
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<script src="https://cdn.tailwindcss.com"></script>
|
<!-- JQuery integration -->
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/jquery@3.6.0/dist/jquery.min.js"></script>
|
||||||
|
<script src="https://cdn.tailwindcss.com"></script>
|
||||||
|
<script src="public_html/js/utils.js"></script>
|
||||||
|
<script src="public_html/js/home.js" defer></script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<header class="fixed flex justify-between p-2.5 bg-lime-600 top-0 w-full">
|
<header class="fixed flex justify-between p-2.5 bg-lime-600 top-0 w-full">
|
||||||
<h2 class="text-white">Fermentardoise</h2>
|
<h2 class="text-white">Fermentardoise</h2>
|
||||||
<div class="flex gap-x-5 flex-wrap">
|
<div class="flex gap-x-5 flex-wrap" id="header-right">
|
||||||
<div class="lg:inline-flex sm:max-lg:flex-col space-x-3 sm:max-lg:space-y-2 group">
|
<div class="lg:inline-flex sm:max-lg:flex-col space-x-3 sm:max-lg:space-y-2 group">
|
||||||
<input type="search" name="" id=""
|
<input type="search" name="" id=""
|
||||||
class="bg-green-500 rounded-full align-middle hidden scale-0 px-2.5
|
class="bg-green-500 rounded-full align-middle hidden scale-0 px-2.5
|
||||||
@@ -16,17 +20,17 @@
|
|||||||
focus:scale-100 group-focus-within:scale-100 focus:flex group-focus-within:flex"
|
focus:scale-100 group-focus-within:scale-100 focus:flex group-focus-within:flex"
|
||||||
placeholder="Rechercher">
|
placeholder="Rechercher">
|
||||||
<button class="bg-slate-400 rounded-xl px-1.5 focus:ring focus:ring-red-600">
|
<button class="bg-slate-400 rounded-xl px-1.5 focus:ring focus:ring-red-600">
|
||||||
rechercher
|
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" d="M21 21l-5.197-5.197m0 0A7.5 7.5 0 105.196 5.196a7.5 7.5 0 0010.607 10.607z" />
|
||||||
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
<a href="login.html" class="rounded-full border bg-rose-600 text-white px-1.5 hover:bg-green-500">
|
|
||||||
Connexion
|
|
||||||
</a>
|
|
||||||
</div>
|
</div>
|
||||||
<input type="search" name="" id=""
|
<a href="login.html" class="rounded-full border bg-rose-600 text-white px-1.5 hover:bg-green-500" id="connexion">
|
||||||
class="bg-green-500 rounded-full align-middle hidden scale-0 px-2.5
|
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
|
||||||
text-white placeholder:text-white
|
<path stroke-linecap="round" stroke-linejoin="round" d="M15.75 9V5.25A2.25 2.25 0 0013.5 3h-6a2.25 2.25 0 00-2.25 2.25v13.5A2.25 2.25 0 007.5 21h6a2.25 2.25 0 002.25-2.25V15m3 0l3-3m0 0l-3-3m3 3H9" />
|
||||||
sm:max-lg:focus:scale-100 sm:max-lg:peer-focus-within:scale-100 sm:max-lg:focus:flex sm:max-lg:peer-focus-within:flex"
|
</svg>
|
||||||
placeholder="Rechercher">
|
</a>
|
||||||
|
<div id="profile-info" class="group"></div>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
<main class="flex justify-center w-full h-screen">
|
<main class="flex justify-center w-full h-screen">
|
||||||
|
|||||||
31
login.html
31
login.html
@@ -1,9 +1,14 @@
|
|||||||
<!doctype html>
|
<!doctype html>
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<script src="https://cdn.tailwindcss.com"></script>
|
<!-- JQuery integration -->
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/jquery@3.6.0/dist/jquery.min.js"></script>
|
||||||
|
<script src="https://cdn.tailwindcss.com"></script>
|
||||||
|
<!-- Initialisation Personal Script -->
|
||||||
|
<script src="public_html/js/utils.js" defer></script>
|
||||||
|
<script src="public_html/js/login.js" defer></script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<header class="fixed flex justify-between p-2.5 bg-lime-600 top-0 w-full">
|
<header class="fixed flex justify-between p-2.5 bg-lime-600 top-0 w-full">
|
||||||
@@ -15,19 +20,25 @@
|
|||||||
focus:scale-100 group-focus-within:scale-100 focus:flex group-focus-within:flex"
|
focus:scale-100 group-focus-within:scale-100 focus:flex group-focus-within:flex"
|
||||||
placeholder="Rechercher">
|
placeholder="Rechercher">
|
||||||
<button class="bg-slate-400 rounded-xl px-1.5 focus:ring focus:ring-red-600">
|
<button class="bg-slate-400 rounded-xl px-1.5 focus:ring focus:ring-red-600">
|
||||||
rechercher
|
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" d="M21 21l-5.197-5.197m0 0A7.5 7.5 0 105.196 5.196a7.5 7.5 0 0010.607 10.607z" />
|
||||||
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
<main class="flex justify-center w-full h-[70vh]">
|
<main class="flex justify-center w-full h-[70vh] flex-col">
|
||||||
<div class="bg-blue-300 self-center flex flex-col p-3.5 w-[60vw] space-y-2.5">
|
<div class="bg-amber-500 mx-80 mb-20 rounded-lg text-center text-lg hidden" id="error-zone">
|
||||||
|
Mail ou mot de passe non reconnu !
|
||||||
|
</div>
|
||||||
|
<form class="bg-blue-300 self-center flex flex-col p-3.5 w-[60vw] space-y-2.5" id ="form_login">
|
||||||
<h3>Connexion</h3>
|
<h3>Connexion</h3>
|
||||||
|
<div class="items-center" id="error-zone"></div>
|
||||||
<div class="flex flex-col rounded-md shadow-sm">
|
<div class="flex flex-col rounded-md shadow-sm">
|
||||||
<input type="email" name="" id="" placeholder="Mail" autocomplete="email" required
|
<input type="email" name="" id="email" placeholder="Mail" autocomplete="email" required
|
||||||
class="rounded-t-md border border-gray-500 px-2 py-1 w-full ring-0 placeholder:text-zinc-400
|
class="rounded-t-md border border-gray-500 px-2 py-1 w-full ring-0 placeholder:text-zinc-400
|
||||||
focus:outline-none focus:border-emerald-600 focus:ring-1 focus:ring-emerald-600 focus:z-10">
|
focus:outline-none focus:border-emerald-600 focus:ring-1 focus:ring-emerald-600 focus:z-10">
|
||||||
|
|
||||||
<input type="password" name="" id="" placeholder="Mot de passe" autocomplete="current-password" required
|
<input type="password" name="" id="pwd" placeholder="Mot de passe" autocomplete="current-password" required
|
||||||
class="rounded-b-md border border-gray-500 px-2 py-1 w-full ring-0 placeholder:text-zinc-400
|
class="rounded-b-md border border-gray-500 px-2 py-1 w-full ring-0 placeholder:text-zinc-400
|
||||||
focus:outline-none focus:border-emerald-600 focus:ring-1 focus:ring-emerald-600 focus:z-10">
|
focus:outline-none focus:border-emerald-600 focus:ring-1 focus:ring-emerald-600 focus:z-10">
|
||||||
</div>
|
</div>
|
||||||
@@ -41,10 +52,10 @@
|
|||||||
<a href="" class="font-medium text-indigo-600 hover:text-indigo-500">Mot de passe oublié ?</a>
|
<a href="" class="font-medium text-indigo-600 hover:text-indigo-500">Mot de passe oublié ?</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<button type="submit" class="w-full bg-slate-200 rounded-md py-1 px-2">
|
<button id="login_button" type="submit" class="w-full bg-slate-200 rounded-md py-1 px-2">
|
||||||
Connexion
|
Connexion
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</form>
|
||||||
</main>
|
</main>
|
||||||
<footer class="fixed bottom-0 w-full text-center bg-lime-300 py-2">
|
<footer class="fixed bottom-0 w-full text-center bg-lime-300 py-2">
|
||||||
Made with ❤️ & 🍺 by Appen
|
Made with ❤️ & 🍺 by Appen
|
||||||
|
|||||||
25
public_html/html_elements/profile.html
Normal file
25
public_html/html_elements/profile.html
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
<button type="button" class="inline-flex items-center gap-x-1 text-sm font-semibold leading-6 text-gray-900" id="profile">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" d="M17.982 18.725A7.488 7.488 0 0012 15.75a7.488 7.488 0 00-5.982 2.975m11.963 0a9 9 0 10-11.963 0m11.963 0A8.966 8.966 0 0112 21a8.966 8.966 0 01-5.982-2.275M15 9.75a3 3 0 11-6 0 3 3 0 016 0z" />
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
<div id="deconnexion" class="group-focus-within:visible invisible absolute right-0 mt-5 z-10 flex w-50 max-w-50 px-4">
|
||||||
|
<div class="w-sm max-w-sm flex-auto overflow-hidden rounded-3xl bg-white text-sm leading-6 shadow-lg ring-1 ring-gray-900/5">
|
||||||
|
<div class=" static p-4">
|
||||||
|
<p id="profil_name" class="relative -top-1.5 text-center"></p>
|
||||||
|
<div class="group relative flex rounded-lg gap-x-5 hover:bg-gray-50 align-middle">
|
||||||
|
<div class="mt-1 flex flex-none items-center justify-center rounded-lg group-hover:bg-gray-50 bg-white">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" d="M15.75 9V5.25A2.25 2.25 0 0013.5 3h-6a2.25 2.25 0 00-2.25 2.25v13.5A2.25 2.25 0 007.5 21h6a2.25 2.25 0 002.25-2.25V15M12 9l-3 3m0 0l3 3m-3-3h12.75" />
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<button type="button" class="font-semibold text-gray-900" id="deconnect">
|
||||||
|
Déconnexion
|
||||||
|
<span class="absolute inset-0"></span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
3
public_html/img/profile.svg
Normal file
3
public_html/img/profile.svg
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" d="M17.982 18.725A7.488 7.488 0 0012 15.75a7.488 7.488 0 00-5.982 2.975m11.963 0a9 9 0 10-11.963 0m11.963 0A8.966 8.966 0 0112 21a8.966 8.966 0 01-5.982-2.275M15 9.75a3 3 0 11-6 0 3 3 0 016 0z" />
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 389 B |
29
public_html/js/home.js
Normal file
29
public_html/js/home.js
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
function profile(){
|
||||||
|
if(getCookie('fermentardoise_session') != ""){
|
||||||
|
$("#connexion").remove();
|
||||||
|
$("#profile-info").load("public_html/html_elements/profile.html", () => {
|
||||||
|
$.ajax({
|
||||||
|
type: 'POST',
|
||||||
|
url: 'api.php/getEmail',
|
||||||
|
headers: {
|
||||||
|
Authorization: 'Bearer ' + getCookie('fermentardoise_session')
|
||||||
|
}
|
||||||
|
}).done((data) => {
|
||||||
|
$("#profil_name").append(data);
|
||||||
|
});
|
||||||
|
$("#deconnect").click(() => {
|
||||||
|
$.ajax({
|
||||||
|
type: 'POST',
|
||||||
|
url: 'api.php/logout',
|
||||||
|
headers: {
|
||||||
|
Authorization: 'Bearer ' + getCookie('fermentardoise_session')
|
||||||
|
}
|
||||||
|
}).done((data) => {
|
||||||
|
window.location.replace("index.html");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
profile();
|
||||||
29
public_html/js/login.js
Normal file
29
public_html/js/login.js
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
function listener_login() {
|
||||||
|
if(getCookie('fermentardoise_session') != ""){
|
||||||
|
window.location.replace("index.html");
|
||||||
|
}
|
||||||
|
document.getElementById("form_login").addEventListener("submit", function (event) {
|
||||||
|
event.preventDefault();
|
||||||
|
event.stopImmediatePropagation();
|
||||||
|
console.log("connexion");
|
||||||
|
let mail = document.getElementById("email").value;
|
||||||
|
let upwd = document.getElementById("pwd").value;
|
||||||
|
$.ajax({
|
||||||
|
type: 'POST',
|
||||||
|
url: 'api.php/login',
|
||||||
|
data: {
|
||||||
|
email: mail,
|
||||||
|
pwd: upwd,
|
||||||
|
},
|
||||||
|
statusCode: {
|
||||||
|
400: function () {
|
||||||
|
$("#error-zone").removeClass("hidden");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}).done(() => {
|
||||||
|
window.location.replace("index.html");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
listener_login();
|
||||||
42
public_html/js/utils.js
Normal file
42
public_html/js/utils.js
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
function createCookie(name, value, timestamp) {
|
||||||
|
var expires;
|
||||||
|
if (timestamp) {
|
||||||
|
var date = new Date(timestamp);
|
||||||
|
expires = "; expires=" + date.toGMTString();
|
||||||
|
} else {
|
||||||
|
expires = "";
|
||||||
|
}
|
||||||
|
document.cookie = name + "=" + value + expires + "; path=/";
|
||||||
|
}
|
||||||
|
|
||||||
|
function getCookie(c_name) {
|
||||||
|
let c_start;
|
||||||
|
let c_end;
|
||||||
|
if (document.cookie.length > 0) {
|
||||||
|
c_start = document.cookie.indexOf(c_name + "=");
|
||||||
|
if (c_start != -1) {
|
||||||
|
c_start = c_start + c_name.length + 1;
|
||||||
|
c_end = document.cookie.indexOf(";", c_start);
|
||||||
|
if (c_end == -1) {
|
||||||
|
c_end = document.cookie.length;
|
||||||
|
}
|
||||||
|
return unescape(document.cookie.substring(c_start, c_end));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
function get_cookie(name){
|
||||||
|
return document.cookie.split(';').some(c => {
|
||||||
|
return c.trim().startsWith(name + '=');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function deleteCookie( name, path, domain ) {
|
||||||
|
if( get_cookie( name ) ) {
|
||||||
|
document.cookie = name + "=" +
|
||||||
|
((path) ? ";path="+path:"")+
|
||||||
|
((domain)?";domain="+domain:"") +
|
||||||
|
";expires=Thu, 01 Jan 1970 00:00:01 GMT";
|
||||||
|
}
|
||||||
|
}
|
||||||
241
resources/database.php
Normal file
241
resources/database.php
Normal file
@@ -0,0 +1,241 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* PHP Version 8.2.1
|
||||||
|
*/
|
||||||
|
ini_set('display_errors', 1);
|
||||||
|
ini_set('display_startup_errors', 1);
|
||||||
|
error_reporting(E_ALL);
|
||||||
|
|
||||||
|
require_once 'config.php';
|
||||||
|
require_once 'library/exceptions.php';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Collection of methods to communicate with the database.
|
||||||
|
*/
|
||||||
|
class Database {
|
||||||
|
protected $PDO;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Connect to the PostgreSQL database.
|
||||||
|
*
|
||||||
|
* @throws PDOException Error thrown if the connection
|
||||||
|
* to the database failed.
|
||||||
|
*/
|
||||||
|
public function __construct(){
|
||||||
|
$this->PDO = new PDO(
|
||||||
|
'pgsql:host=' . DB_SERVER . ';port=' . DB_PORT . ';dbname=' . DB_NAME,
|
||||||
|
DB_USER,
|
||||||
|
DB_PASSWORD
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the password hash of a user.
|
||||||
|
*
|
||||||
|
* @param string $eamil
|
||||||
|
*
|
||||||
|
* @return ?string if the password hash exists.
|
||||||
|
*/
|
||||||
|
public function getUserPasswordHash(string $email): ?string{
|
||||||
|
$email = strtolower($email);
|
||||||
|
|
||||||
|
$request = 'SELECT password_hash from users where email = :email';
|
||||||
|
|
||||||
|
$statement = $this->PDO->prepare($request);
|
||||||
|
$statement->bindParam(':email', $email);
|
||||||
|
$statement->execute();
|
||||||
|
|
||||||
|
$result = $statement->fetch(PDO::FETCH_OBJ);
|
||||||
|
|
||||||
|
if (!$result) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $result->password_hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verifies the user credentials.
|
||||||
|
*
|
||||||
|
* @param string $email
|
||||||
|
* @param string $password
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function verifyUserCredentials(string $email, string $password): bool {
|
||||||
|
$password_hash = $this->getUserPasswordHash($email);
|
||||||
|
|
||||||
|
return !empty($password_hash) && password_verify($password, $password_hash);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verifies the user access token.
|
||||||
|
*
|
||||||
|
* @param string $access_token
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function verifyUserAccessToken(string $access_token): bool {
|
||||||
|
$request = 'SELECT * from users where access_token = :access_token';
|
||||||
|
|
||||||
|
$statement = $this->PDO->prepare($request);
|
||||||
|
$statement->bindParam(':access_token', $access_token);
|
||||||
|
$statement->execute();
|
||||||
|
|
||||||
|
$result = $statement->fetch(PDO::FETCH_OBJ);
|
||||||
|
|
||||||
|
return !empty($result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Connects the user by returning its unique id if the
|
||||||
|
* credentials are valid.
|
||||||
|
*
|
||||||
|
* @param string $email
|
||||||
|
* @param string $password
|
||||||
|
* @param int $session_expire (optional) The lifetime of the session cookie in seconds.
|
||||||
|
*
|
||||||
|
* @throws AuthenticationException If the authentication failed.
|
||||||
|
*/
|
||||||
|
public function connectUser(string $email, string $password, int $session_expire = 0): bool {
|
||||||
|
// test if the credentials are correct
|
||||||
|
if (!$this->verifyUserCredentials($email, $password)) {
|
||||||
|
throw new AuthenticationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
// make email lowercase in case the user used uppercase letters
|
||||||
|
$email = strtolower($email);
|
||||||
|
|
||||||
|
// create a unique token used to identify the user
|
||||||
|
$access_token = hash('sha256', $email . $password . microtime(true));
|
||||||
|
|
||||||
|
// Set session hash on the user
|
||||||
|
$request = 'UPDATE users SET access_token = :access_token
|
||||||
|
WHERE email = :email';
|
||||||
|
|
||||||
|
$statement = $this->PDO->prepare($request);
|
||||||
|
$statement->bindParam(':email', $email);
|
||||||
|
$statement->bindParam(':access_token', $access_token);
|
||||||
|
$success = $statement->execute();
|
||||||
|
|
||||||
|
// Throw an exception if the update failed
|
||||||
|
if (!$success) {
|
||||||
|
throw new Exception('Failed to connect user.');
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($session_expire > 0) {
|
||||||
|
$session_expire = time() + $session_expire;
|
||||||
|
}
|
||||||
|
|
||||||
|
// set the session cookie
|
||||||
|
return setcookie(
|
||||||
|
ACCESS_TOKEN_NAME,
|
||||||
|
$access_token,
|
||||||
|
$session_expire,
|
||||||
|
"/"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an access token if the credentials are valid
|
||||||
|
* the functionality is the same as connectUser but
|
||||||
|
* this function is related to AJAX request
|
||||||
|
*
|
||||||
|
* @param string $email
|
||||||
|
* @param string $password
|
||||||
|
*
|
||||||
|
* @return string the access_token
|
||||||
|
*/
|
||||||
|
public function getUserAccessToken(string $email, string $password): ?string {
|
||||||
|
if (!$this->verifyUserCredentials($email, $password)) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
$email = strtolower($email);
|
||||||
|
|
||||||
|
$access_token = hash('sha256', $email . $password . time());
|
||||||
|
|
||||||
|
//Set the access token to the user
|
||||||
|
$request = 'UPDATE users set access_token = :access_token where email = :email';
|
||||||
|
|
||||||
|
$statement = $this->PDO->prepare($request);
|
||||||
|
$statement->bindParam(':access_token', $access_token);
|
||||||
|
$statement->bindParam(':email', $email);
|
||||||
|
$statement->execute();
|
||||||
|
|
||||||
|
return $access_token;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disconnects the user by deleting the access token.
|
||||||
|
*
|
||||||
|
* @param string $access_token
|
||||||
|
*
|
||||||
|
* @throws AuthenticationException If the access token is invalid.
|
||||||
|
*/
|
||||||
|
public function disconnectUser($access_token): bool{
|
||||||
|
if (!$this->verifyUserAccessToken($access_token)) {
|
||||||
|
throw new AuthenticationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
// remove access token from the user
|
||||||
|
$request = 'UPDATE users SET access_token = NULL
|
||||||
|
WHERE access_token = :access_token';
|
||||||
|
|
||||||
|
$statement = $this->PDO->prepare($request);
|
||||||
|
$statement->bindParam(':access_token', $access_token);
|
||||||
|
$success = $statement->execute();
|
||||||
|
|
||||||
|
// delete the session cookie
|
||||||
|
unset($_COOKIE[ACCESS_TOKEN_NAME]);
|
||||||
|
setcookie(ACCESS_TOKEN_NAME, '', -1, '/');
|
||||||
|
return $success;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the user email
|
||||||
|
*
|
||||||
|
* @param string $access_token
|
||||||
|
*
|
||||||
|
* @return ?string
|
||||||
|
*/
|
||||||
|
public function getEmail(string $access_token): ?string{
|
||||||
|
if (!$this->verifyUserAccessToken($access_token)) {
|
||||||
|
throw new AuthenticationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
$request = 'SELECT email from users where access_token = :access_token';
|
||||||
|
|
||||||
|
$statement = $this->PDO->prepare($request);
|
||||||
|
$statement->bindParam(':access_token', $access_token);
|
||||||
|
$statement->execute();
|
||||||
|
|
||||||
|
$result = $statement->fetch(PDO::FETCH_OBJ);
|
||||||
|
|
||||||
|
return $result->email;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function test(){
|
||||||
|
if (!isset($_COOKIE[ACCESS_TOKEN_NAME])) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$access_token = $_COOKIE[ACCESS_TOKEN_NAME];
|
||||||
|
|
||||||
|
if (!$this->verifyUserAccessToken($access_token)) {
|
||||||
|
throw new AuthenticationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
$request = 'SELECT * from users where access_token = :access_token';
|
||||||
|
|
||||||
|
$statement = $this->PDO->prepare($request);
|
||||||
|
$statement->bindParam(':access_token', $access_token);
|
||||||
|
$statement->execute();
|
||||||
|
|
||||||
|
$result = $statement->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
?>
|
||||||
@@ -12,8 +12,9 @@ DROP TABLE IF EXISTS payments CASCADE;
|
|||||||
-- Table users
|
-- Table users
|
||||||
CREATE TABLE users (
|
CREATE TABLE users (
|
||||||
id SERIAL PRIMARY KEY,
|
id SERIAL PRIMARY KEY,
|
||||||
login VARCHAR(255) NOT NULL,
|
email VARCHAR(255) NOT NULL,
|
||||||
password_hash VARCHAR(50) NOT NULL
|
password_hash VARCHAR(64) NOT NULL,
|
||||||
|
access_token VARCHAR(64)
|
||||||
);
|
);
|
||||||
|
|
||||||
-- Table clients
|
-- Table clients
|
||||||
|
|||||||
Reference in New Issue
Block a user