Internal ID, Argon2 for usernames, username changes
This commit is contained in:
parent
05db184fa6
commit
f15681999b
31 changed files with 177 additions and 124 deletions
|
@ -1,41 +1,43 @@
|
|||
BEGIN TRANSACTION;
|
||||
CREATE TABLE IF NOT EXISTS "params" (
|
||||
"name" TEXT NOT NULL UNIQUE,
|
||||
"value" TEXT NOT NULL
|
||||
);
|
||||
CREATE TABLE IF NOT EXISTS "registry" (
|
||||
"id" INTEGER NOT NULL UNIQUE,
|
||||
"domain" TEXT NOT NULL UNIQUE,
|
||||
"username" TEXT NOT NULL,
|
||||
"last_renewal" TEXT NOT NULL,
|
||||
PRIMARY KEY("id" AUTOINCREMENT)
|
||||
);
|
||||
CREATE TABLE IF NOT EXISTS "zones" (
|
||||
"id" INTEGER NOT NULL UNIQUE,
|
||||
"zone" TEXT NOT NULL UNIQUE,
|
||||
"username" TEXT NOT NULL,
|
||||
PRIMARY KEY("id" AUTOINCREMENT)
|
||||
"value" TEXT NOT NULL,
|
||||
PRIMARY KEY("name")
|
||||
);
|
||||
INSERT INTO "params"("name", "value") VALUES("instance_bucket_tokens", "0");
|
||||
INSERT INTO "params"("name", "value") VALUES("instance_bucket_last_update", "0");
|
||||
INSERT INTO "params"("name", "value") VALUES("username_salt", "00000000000000000000000000000000"); -- Should be unique and secret ; generate one using `openssl rand -hex 16` ; can't be changed without breaking current accounts login
|
||||
CREATE TABLE IF NOT EXISTS "users" (
|
||||
"id" INTEGER NOT NULL UNIQUE,
|
||||
"id" TEXT NOT NULL UNIQUE,
|
||||
"username" TEXT NOT NULL UNIQUE,
|
||||
"password" TEXT NOT NULL,
|
||||
"registration_date" TEXT NOT NULL,
|
||||
"bucket_tokens" INTEGER NOT NULL,
|
||||
"bucket_last_update" INTEGER NOT NULL,
|
||||
"type" TEXT NOT NULL,
|
||||
PRIMARY KEY("id" AUTOINCREMENT)
|
||||
PRIMARY KEY("id")
|
||||
);
|
||||
CREATE TABLE IF NOT EXISTS "registry" (
|
||||
"domain" TEXT NOT NULL UNIQUE,
|
||||
"username" TEXT NOT NULL,
|
||||
"last_renewal" TEXT NOT NULL,
|
||||
PRIMARY KEY("domain"),
|
||||
FOREIGN KEY("username") REFERENCES "users"("id")
|
||||
);
|
||||
CREATE TABLE IF NOT EXISTS "zones" (
|
||||
"zone" TEXT NOT NULL UNIQUE,
|
||||
"username" TEXT NOT NULL,
|
||||
PRIMARY KEY("zone"),
|
||||
FOREIGN KEY("username") REFERENCES "users"("id")
|
||||
);
|
||||
CREATE TABLE IF NOT EXISTS "sites" (
|
||||
"id" INTEGER NOT NULL UNIQUE,
|
||||
"username" TEXT NOT NULL,
|
||||
"site_dir" TEXT NOT NULL,
|
||||
"domain" TEXT NOT NULL UNIQUE,
|
||||
"domain_type" TEXT NOT NULL,
|
||||
"protocol" TEXT NOT NULL,
|
||||
"creation_date" TEXT NOT NULL,
|
||||
PRIMARY KEY("id" AUTOINCREMENT)
|
||||
PRIMARY KEY("domain"),
|
||||
FOREIGN KEY("username") REFERENCES "users"("id")
|
||||
);
|
||||
INSERT INTO "params"("name", "value") VALUES("instance_bucket_tokens", "0");
|
||||
INSERT INTO "params"("name", "value") VALUES("instance_bucket_last_update", "0");
|
||||
COMMIT;
|
||||
|
|
34
fn/auth.php
34
fn/auth.php
|
@ -14,38 +14,42 @@ const OPTIONS_PASSWORD = [
|
|||
'threads' => 64,
|
||||
];
|
||||
|
||||
function checkUsernameFormat($username) {
|
||||
if (preg_match('/' . USERNAME_REGEX . '/Du', $username) !== 1)
|
||||
output(403, 'Username malformed.');
|
||||
}
|
||||
|
||||
function checkPasswordFormat($password) {
|
||||
if (preg_match('/' . PASSWORD_REGEX . '/Du', $password) !== 1)
|
||||
output(403, 'Password malformed.');
|
||||
}
|
||||
|
||||
function checkUsernameFormat($username) {
|
||||
if (preg_match('/' . USERNAME_REGEX . '/Du', $username) !== 1)
|
||||
output(403, 'Username malformed.');
|
||||
function hashUsername($username) {
|
||||
return base64_encode(sodium_crypto_pwhash(32, $username, hex2bin(query('select', 'params', ['name' => 'username_salt'], 'value')[0]), 2**10, 2**14, SODIUM_CRYPTO_PWHASH_ALG_ARGON2ID13));
|
||||
}
|
||||
|
||||
function hashPassword($password) {
|
||||
return password_hash($password, ALGO_PASSWORD, OPTIONS_PASSWORD);
|
||||
}
|
||||
|
||||
function userExist($username) {
|
||||
return isset(query('select', 'users', ['username' => $username], 'username')[0]);
|
||||
function usernameExists($username) {
|
||||
return isset(query('select', 'users', ['username' => $username], 'id')[0]);
|
||||
}
|
||||
|
||||
function checkPassword($username, $password) {
|
||||
return password_verify($password, query('select', 'users', ['username' => $username], 'password')[0]);
|
||||
function checkPassword($id, $password) {
|
||||
return password_verify($password, query('select', 'users', ['id' => $id], 'password')[0]);
|
||||
}
|
||||
|
||||
function outdatedPasswordHash($username) {
|
||||
return password_needs_rehash(query('select', 'users', ['username' => $username], 'password')[0], ALGO_PASSWORD, OPTIONS_PASSWORD);
|
||||
function outdatedPasswordHash($id) {
|
||||
return password_needs_rehash(query('select', 'users', ['id' => $id], 'password')[0], ALGO_PASSWORD, OPTIONS_PASSWORD);
|
||||
}
|
||||
|
||||
function changePassword($username, $password) {
|
||||
function changePassword($id, $password) {
|
||||
$db = new PDO('sqlite:' . DB_PATH);
|
||||
|
||||
$stmt = $db->prepare('UPDATE users SET password = :password WHERE username = :username');
|
||||
$stmt = $db->prepare('UPDATE users SET password = :password WHERE id = :id');
|
||||
|
||||
$stmt->bindValue(':username', $username);
|
||||
$stmt->bindValue(':id', $id);
|
||||
$stmt->bindValue(':password', hashPassword($password));
|
||||
|
||||
$stmt->execute();
|
||||
|
@ -61,7 +65,7 @@ function rateLimit() {
|
|||
|
||||
function rateLimitAccount($requestedTokens) {
|
||||
// Get
|
||||
$userData = query('select', 'users', ['username' => $_SESSION['username']]);
|
||||
$userData = query('select', 'users', ['id' => $_SESSION['id']]);
|
||||
$tokens = $userData[0]['bucket_tokens'];
|
||||
$bucketLastUpdate = $userData[0]['bucket_last_update'];
|
||||
|
||||
|
@ -75,8 +79,8 @@ function rateLimitAccount($requestedTokens) {
|
|||
|
||||
// Update
|
||||
$db = new PDO('sqlite:' . DB_PATH);
|
||||
$stmt = $db->prepare('UPDATE users SET bucket_tokens = :bucket_tokens, bucket_last_update = :bucket_last_update WHERE username = :username');
|
||||
$stmt->bindValue(':username', $_SESSION['username']);
|
||||
$stmt = $db->prepare('UPDATE users SET bucket_tokens = :bucket_tokens, bucket_last_update = :bucket_last_update WHERE id = :id');
|
||||
$stmt->bindValue(':id', $_SESSION['id']);
|
||||
$stmt->bindValue(':bucket_tokens', $tokens);
|
||||
$stmt->bindValue(':bucket_last_update', time());
|
||||
$stmt->execute();
|
||||
|
|
|
@ -20,11 +20,11 @@ function output($code, $msg = '', $logs = ['']) {
|
|||
function processForm($requireLogin = true) {
|
||||
if (http_response_code() !== 200)
|
||||
return false;
|
||||
if (empty($_POST) AND $requireLogin AND !isset($_SESSION['username']))
|
||||
if (empty($_POST) AND $requireLogin AND !isset($_SESSION['id']))
|
||||
echo '<p>Ce formulaire ne sera pas accepté car il faut <a class="auth" href="' . redirUrl('auth/login') . '">se connecter</a> avant.</p>';
|
||||
if (empty($_POST))
|
||||
return false;
|
||||
if ($requireLogin AND !isset($_SESSION['username']))
|
||||
if ($requireLogin AND !isset($_SESSION['id']))
|
||||
output(403, 'Vous devez être connecté·e pour effectuer cette action.');
|
||||
return true;
|
||||
}
|
||||
|
@ -152,11 +152,11 @@ if (!file_exists(SECRET_KEY_FILE)) {
|
|||
define('SECRET_KEY', file_get_contents(SECRET_KEY_FILE));
|
||||
function getAuthToken() {
|
||||
$salt = bin2hex(random_bytes(4));
|
||||
$hash = hash_hmac('sha256', $salt . ($_SESSION['username'] ?? ''), SECRET_KEY);
|
||||
$hash = hash_hmac('sha256', $salt . ($_SESSION['id'] ?? ''), SECRET_KEY);
|
||||
return $salt . '-' . substr($hash, 0, 32);
|
||||
}
|
||||
function checkAuthToken($salt, $hash) {
|
||||
$correctProof = substr(hash_hmac('sha256', $salt . $_SESSION['username'], SECRET_KEY), 0, 32);
|
||||
$correctProof = substr(hash_hmac('sha256', $salt . $_SESSION['id'], SECRET_KEY), 0, 32);
|
||||
if (hash_equals($correctProof, $hash) !== true)
|
||||
output(403, 'Preuve incorrecte');
|
||||
}
|
||||
|
|
14
fn/ht.php
14
fn/ht.php
|
@ -33,15 +33,15 @@ function addSite($username, $siteDir, $domain, $domainType, $protocol) {
|
|||
}
|
||||
|
||||
function dirsStatuses($domainType, $protocol) {
|
||||
if (isset($_SESSION['username']) !== true)
|
||||
if (isset($_SESSION['id']) !== true)
|
||||
return [];
|
||||
$dbDirs = query('select', 'sites', [
|
||||
'username' => $_SESSION['username'],
|
||||
'username' => $_SESSION['id'],
|
||||
'domain_type' => $domainType,
|
||||
'protocol' => $protocol,
|
||||
], 'site_dir');
|
||||
$dirs = [];
|
||||
foreach (listFsDirs($_SESSION['username']) as $fsDir)
|
||||
foreach (listFsDirs($_SESSION['id']) as $fsDir)
|
||||
$dirs[$fsDir] = in_array($fsDir, $dbDirs);
|
||||
return $dirs;
|
||||
}
|
||||
|
@ -50,7 +50,7 @@ function htDeleteSite($dir, $domainType, $protocol) {
|
|||
|
||||
if ($domainType === 'onion') {
|
||||
// Delete Tor config
|
||||
if (unlink(CONF['ht']['tor_config_path'] . '/' . $_SESSION['username'] . '/' . $dir) !== true)
|
||||
if (unlink(CONF['ht']['tor_config_path'] . '/' . $_SESSION['id'] . '/' . $dir) !== true)
|
||||
output(500, 'Failed to delete Tor configuration.');
|
||||
|
||||
// Reload Tor
|
||||
|
@ -59,14 +59,14 @@ function htDeleteSite($dir, $domainType, $protocol) {
|
|||
output(500, 'Failed to reload Tor.');
|
||||
|
||||
// Delete Tor keys
|
||||
exec(CONF['ht']['sudo_path'] . ' -u ' . CONF['ht']['tor_user'] . ' ' . CONF['ht']['rm_path'] . ' --recursive ' . CONF['ht']['tor_keys_path'] . '/' . $_SESSION['username'] . '/' . $dir, $output, $code);
|
||||
exec(CONF['ht']['sudo_path'] . ' -u ' . CONF['ht']['tor_user'] . ' ' . CONF['ht']['rm_path'] . ' --recursive ' . CONF['ht']['tor_keys_path'] . '/' . $_SESSION['id'] . '/' . $dir, $output, $code);
|
||||
if ($code !== 0)
|
||||
output(500, 'Failed to delete Tor keys.');
|
||||
}
|
||||
|
||||
// Delete Nginx config
|
||||
$domain = query('select', 'sites', [
|
||||
'username' => $_SESSION['username'],
|
||||
'username' => $_SESSION['id'],
|
||||
'domain_type' => $domainType,
|
||||
'protocol' => $protocol,
|
||||
'site_dir' => $dir,
|
||||
|
@ -88,7 +88,7 @@ function htDeleteSite($dir, $domainType, $protocol) {
|
|||
|
||||
// Delete from database
|
||||
query('delete', 'sites', [
|
||||
'username' => $_SESSION['username'],
|
||||
'username' => $_SESSION['id'],
|
||||
'domain_type' => $domainType,
|
||||
'protocol' => $protocol,
|
||||
'site_dir' => $dir,
|
||||
|
|
|
@ -22,7 +22,7 @@ function nsCommonRequirements() {
|
|||
AND isset($_POST['zone'])
|
||||
AND isset($_POST['ttl-value'])
|
||||
AND isset($_POST['ttl-multiplier'])
|
||||
AND isset($_SESSION['username'])
|
||||
AND isset($_SESSION['id'])
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -51,7 +51,7 @@ function nsListUserZones($username) {
|
|||
function nsCheckZonePossession($zone) {
|
||||
checkAbsoluteDomainFormat($zone);
|
||||
|
||||
if (!in_array($zone, query('select', 'zones', ['username' => $_SESSION['username']], 'zone'), true))
|
||||
if (!in_array($zone, query('select', 'zones', ['username' => $_SESSION['id']], 'zone'), true))
|
||||
output(403, 'You don\'t own this zone on the nameserver.');
|
||||
}
|
||||
|
||||
|
@ -69,6 +69,6 @@ function nsDeleteZone($zone) {
|
|||
// Remove from database
|
||||
query('delete', 'zones', [
|
||||
'zone' => $zone,
|
||||
'username' => $_SESSION['username'],
|
||||
'username' => $_SESSION['id'],
|
||||
]);
|
||||
}
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
<?php
|
||||
|
||||
define('SUBDOMAIN_REGEX', '^[a-z0-9]{4,63}$');
|
||||
const SUBDOMAIN_REGEX = '^[a-z0-9]{4,63}$';
|
||||
|
||||
function regListUserDomains($username) {
|
||||
return query('select', 'registry', ['username' => $username], 'domain');
|
||||
}
|
||||
|
||||
function regCheckDomainPossession($domain) {
|
||||
if (in_array($domain, regListUserDomains($_SESSION['username']), true) !== true)
|
||||
if (in_array($domain, regListUserDomains($_SESSION['id']), true) !== true)
|
||||
output(403, 'You don\'t own this domain.');
|
||||
}
|
||||
|
||||
|
@ -23,6 +23,6 @@ function regDeleteDomain($domain) {
|
|||
// Delete from Niver's database
|
||||
query('delete', 'registry', [
|
||||
'domain' => $domain,
|
||||
'username' => $_SESSION['username'],
|
||||
'username' => $_SESSION['id'],
|
||||
]);
|
||||
}
|
||||
|
|
|
@ -18,8 +18,8 @@
|
|||
<select required="" name="zone" id="zone">
|
||||
<option value="" disabled="" selected="">-</option>
|
||||
<?php
|
||||
if (isset($_SESSION['username']))
|
||||
foreach (nsListUserZones($_SESSION['username']) as $zone)
|
||||
if (isset($_SESSION['id']))
|
||||
foreach (nsListUserZones($_SESSION['id']) as $zone)
|
||||
echo "<option value='" . $zone . "'>" . $zone . "</option>";
|
||||
?>
|
||||
|
||||
|
|
|
@ -26,6 +26,10 @@ define('PAGES', [
|
|||
'title' => 'Changer la clé de passe',
|
||||
'description' => 'Changer la chaîne de caractères permettant de vous authentifier.',
|
||||
],
|
||||
'username' => [
|
||||
'title' => 'Changer l\'identifiant',
|
||||
'description' => 'Changer la chaîne de caractères permettant d\'identifier votre compte.',
|
||||
],
|
||||
'logout' => [
|
||||
'title' => 'Déconnexion',
|
||||
'description' => 'Terminer la session et effacer ses cookies',
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<?php displayIndex(); ?>
|
||||
<p>
|
||||
<?php if (isset($_SESSION['username'])) { ?>
|
||||
Vous utilisez actuellement un compte <?= (($_SESSION['type'] === 'trusted') ? 'confiancé' : 'de test') ?>.
|
||||
<?php if (isset($_SESSION['id'])) { ?>
|
||||
Vous utilisez actuellement un compte <?= (($_SESSION['type'] === 'trusted') ? 'confiancé' : 'de test') ?>. Son identifiant interne est <code><?= $_SESSION['id'] ?></code>.
|
||||
<?php } else { ?>
|
||||
Vous n'utilisez actuellement aucun compte.
|
||||
<?php } ?>
|
||||
|
@ -10,7 +10,7 @@
|
|||
<h2>Types de comptes</h2>
|
||||
|
||||
<dl>
|
||||
<dt>De test</dt>
|
||||
<dt><span aria-hidden="true">⏳ </span>De test</dt>
|
||||
<dd>
|
||||
C'est le type de compte par défaut, avec des fonctionnalités limitées pour éviter les abus :
|
||||
<ul>
|
||||
|
@ -19,7 +19,7 @@
|
|||
<li>Certificat Let's Encrypt de test</li>
|
||||
</ul>
|
||||
</dd>
|
||||
<dt>Confiancé</dt>
|
||||
<dt><span aria-hidden="true">👤 </span>Confiancé</dt>
|
||||
<dd>
|
||||
C'est originellement un compte de test mais qui a été confiancé par ane administrataire, et qui a pour but d'être utilisé de façon stable :
|
||||
<ul>
|
||||
|
|
|
@ -5,20 +5,22 @@ if (processForm(false)) {
|
|||
|
||||
checkUsernameFormat($_POST['username']);
|
||||
|
||||
$internal_username = hash('sha256', $_POST['username']);
|
||||
$username = hashUsername($_POST['username']);
|
||||
|
||||
if (userExist($internal_username) !== true)
|
||||
if (usernameExists($username) !== true)
|
||||
output(403, 'Connexion impossible : ce compte n\'existe pas.');
|
||||
|
||||
if (checkPassword($internal_username, $_POST['password']) !== true)
|
||||
$id = query('select', 'users', ['username' => $username], 'id')[0];
|
||||
|
||||
if (checkPassword($id, $_POST['password']) !== true)
|
||||
output(403, 'Connexion impossible : clé de passe invalide.');
|
||||
|
||||
$_SESSION['username'] = $internal_username;
|
||||
$_SESSION['id'] = $id;
|
||||
$_SESSION['display-username'] = htmlspecialchars($_POST['username']);
|
||||
$_SESSION['type'] = query('select', 'users', ['username' => $internal_username], 'type')[0];
|
||||
$_SESSION['type'] = query('select', 'users', ['id' => $id], 'type')[0];
|
||||
|
||||
if (outdatedPasswordHash($internal_username))
|
||||
changePassword($internal_username, $_POST['password']);
|
||||
if (outdatedPasswordHash($id))
|
||||
changePassword($id, $_POST['password']);
|
||||
|
||||
redir();
|
||||
}
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
<?php
|
||||
|
||||
if (processForm()) {
|
||||
checkPasswordFormat($_POST['newPassword']);
|
||||
checkPasswordFormat($_POST['new-password']);
|
||||
|
||||
if (checkPassword($_SESSION['username'], $_POST['currentPassword']) !== true)
|
||||
if (checkPassword($_SESSION['id'], $_POST['current-password']) !== true)
|
||||
output(403, 'Changement impossible : clé de passe invalide.');
|
||||
|
||||
changePassword($_SESSION['username'], $_POST['newPassword']);
|
||||
changePassword($_SESSION['id'], $_POST['new-password']);
|
||||
|
||||
output(200, 'Clé de passe changée.');
|
||||
}
|
||||
|
@ -18,11 +18,11 @@ if (processForm()) {
|
|||
</p>
|
||||
|
||||
<form method="post">
|
||||
<label for="currentPassword">Clé de passe actuelle</label><br>
|
||||
<input required="" autocomplete="current-password" minlength="8" maxlength="1024" pattern="<?= PASSWORD_REGEX ?>" id="currentPassword" name="currentPassword" type="password" placeholder="<?= PLACEHOLDER_PASSWORD ?>"><br>
|
||||
<label for="current-password">Clé de passe actuelle</label><br>
|
||||
<input required="" autocomplete="current-password" minlength="8" maxlength="1024" pattern="<?= PASSWORD_REGEX ?>" id="current-password" name="current-password" type="password" placeholder="<?= PLACEHOLDER_PASSWORD ?>"><br>
|
||||
|
||||
<label for="newPassword">Nouvelle clé de passe</label><br>
|
||||
<input required="" autocomplete="new-password" minlength="8" maxlength="1024" pattern="<?= PASSWORD_REGEX ?>" id="newPassword" name="newPassword" type="password" placeholder="<?= PLACEHOLDER_PASSWORD ?>"><br>
|
||||
<label for="new-password">Nouvelle clé de passe</label><br>
|
||||
<input required="" autocomplete="new-password" minlength="8" maxlength="1024" pattern="<?= PASSWORD_REGEX ?>" id="new-password" name="new-password" type="password" placeholder="<?= PLACEHOLDER_PASSWORD ?>"><br>
|
||||
|
||||
<input type="submit">
|
||||
</form>
|
||||
|
|
|
@ -5,15 +5,18 @@ if (processForm(false)) {
|
|||
|
||||
checkUsernameFormat($_POST['username']);
|
||||
|
||||
$internal_username = hash('sha256', $_POST['username']);
|
||||
$username = hashUsername($_POST['username']);
|
||||
|
||||
if (userExist($internal_username) !== false)
|
||||
if (usernameExists($username) !== false)
|
||||
output(403, 'Ce nom de compte est déjà utilisé.');
|
||||
|
||||
rateLimit();
|
||||
|
||||
$id = hash('sha256', random_bytes(32));
|
||||
|
||||
insert('users', [
|
||||
'username' => $internal_username,
|
||||
'id' => $id,
|
||||
'username' => $username,
|
||||
'password' => hashPassword($_POST['password']),
|
||||
'registration_date' => date('Y-m-d H:i:s'),
|
||||
'bucket_tokens' => 0,
|
||||
|
@ -23,22 +26,22 @@ if (processForm(false)) {
|
|||
|
||||
// Setup SFTP directory
|
||||
umask(0002);
|
||||
if (mkdir(CONF['ht']['ht_path'] . '/' . $internal_username, 0775) !== true)
|
||||
if (mkdir(CONF['ht']['ht_path'] . '/' . $id, 0775) !== true)
|
||||
output(500, 'Can\'t create user directory.');
|
||||
exec(CONF['ht']['sudo_path'] . ' ' . CONF['ht']['chgrp_path'] . ' ' . CONF['ht']['sftpgo_group'] . ' ' . CONF['ht']['ht_path'] . '/' . $internal_username . ' --no-dereference', result_code: $code);
|
||||
exec(CONF['ht']['sudo_path'] . ' ' . CONF['ht']['chgrp_path'] . ' ' . CONF['ht']['sftpgo_group'] . ' ' . CONF['ht']['ht_path'] . '/' . $id . ' --no-dereference', result_code: $code);
|
||||
if ($code !== 0)
|
||||
output(500, 'Can\'t change user directory group.');
|
||||
|
||||
// Setup Tor config directory
|
||||
if (mkdir(CONF['ht']['tor_config_path'] . '/' . $internal_username, 0755) !== true)
|
||||
if (mkdir(CONF['ht']['tor_config_path'] . '/' . $id, 0755) !== true)
|
||||
output(500, 'Can\'t create Tor config directory.');
|
||||
|
||||
// Setup Tor keys directory
|
||||
exec(CONF['ht']['sudo_path'] . ' -u ' . CONF['ht']['tor_user'] . ' ' . CONF['ht']['mkdir_path'] . ' --mode=0700 ' . CONF['ht']['tor_keys_path'] . '/' . $internal_username, result_code: $code);
|
||||
exec(CONF['ht']['sudo_path'] . ' -u ' . CONF['ht']['tor_user'] . ' ' . CONF['ht']['mkdir_path'] . ' --mode=0700 ' . CONF['ht']['tor_keys_path'] . '/' . $id, result_code: $code);
|
||||
if ($code !== 0)
|
||||
output(500, 'Can\'t create Tor keys directory.');
|
||||
|
||||
$_SESSION['username'] = $internal_username;
|
||||
$_SESSION['id'] = $id;
|
||||
$_SESSION['display-username'] = htmlspecialchars($_POST['username']);
|
||||
$_SESSION['type'] = 'testing';
|
||||
|
||||
|
|
|
@ -4,37 +4,37 @@ if (processForm()) {
|
|||
if (!isset($_POST['delete']))
|
||||
output(403, 'Il faut confirmer la suppression du compte');
|
||||
|
||||
foreach (query('select', 'registry', ['username' => $_SESSION['username']], 'domain') as $domain)
|
||||
foreach (query('select', 'registry', ['username' => $_SESSION['id']], 'domain') as $domain)
|
||||
regDeleteDomain($domain);
|
||||
|
||||
foreach (query('select', 'zones', ['username' => $_SESSION['username']], 'zone') as $zone)
|
||||
foreach (query('select', 'zones', ['username' => $_SESSION['id']], 'zone') as $zone)
|
||||
nsDeleteZone($zone);
|
||||
|
||||
foreach (query('select', 'sites', [
|
||||
'username' => $_SESSION['username'],
|
||||
'username' => $_SESSION['id'],
|
||||
'domain_type' => 'onion',
|
||||
'protocol' => 'http',
|
||||
], 'site_dir') as $dir)
|
||||
htDeleteSite($dir, domainType: 'onion', protocol: 'http');
|
||||
|
||||
foreach (query('select', 'sites', [
|
||||
'username' => $_SESSION['username'],
|
||||
'username' => $_SESSION['id'],
|
||||
'domain_type' => 'dns',
|
||||
'protocol' => 'http',
|
||||
], 'site_dir') as $dir)
|
||||
htDeleteSite($dir, domainType: 'dns', protocol: 'http');
|
||||
|
||||
exec(CONF['ht']['sudo_path'] . ' -u ' . CONF['ht']['tor_user'] . ' ' . CONF['ht']['rm_path'] . ' --recursive ' . CONF['ht']['tor_keys_path'] . '/' . $_SESSION['username'], result_code: $code);
|
||||
exec(CONF['ht']['sudo_path'] . ' -u ' . CONF['ht']['tor_user'] . ' ' . CONF['ht']['rm_path'] . ' --recursive ' . CONF['ht']['tor_keys_path'] . '/' . $_SESSION['id'], result_code: $code);
|
||||
if ($code !== 0)
|
||||
output(500, 'Can\'t remove Tor keys directory.');
|
||||
|
||||
removeDirectory(CONF['ht']['tor_config_path'] . '/' . $_SESSION['username']);
|
||||
removeDirectory(CONF['ht']['tor_config_path'] . '/' . $_SESSION['id']);
|
||||
|
||||
exec(CONF['ht']['sudo_path'] . ' -u ' . CONF['ht']['sftpgo_user'] . ' ' . CONF['ht']['rm_path'] . ' --recursive ' . CONF['ht']['ht_path'] . '/' . $_SESSION['username'], result_code: $code);
|
||||
exec(CONF['ht']['sudo_path'] . ' -u ' . CONF['ht']['sftpgo_user'] . ' ' . CONF['ht']['rm_path'] . ' --recursive ' . CONF['ht']['ht_path'] . '/' . $_SESSION['id'], result_code: $code);
|
||||
if ($code !== 0)
|
||||
output(500, 'Can\'t remove user\'s directory.');
|
||||
|
||||
query('delete', 'users', ['username' => $_SESSION['username']]);
|
||||
query('delete', 'users', ['id' => $_SESSION['id']]);
|
||||
|
||||
require 'logout.php';
|
||||
|
||||
|
|
36
pages/auth/username.php
Normal file
36
pages/auth/username.php
Normal file
|
@ -0,0 +1,36 @@
|
|||
<?php
|
||||
|
||||
if (processForm()) {
|
||||
checkUsernameFormat($_POST['new-username']);
|
||||
|
||||
$username = hashUsername($_POST['new-username']);
|
||||
|
||||
if (usernameExists($username) !== false)
|
||||
output(403, 'Ce nom de compte est déjà utilisé.');
|
||||
|
||||
$db = new PDO('sqlite:' . DB_PATH);
|
||||
|
||||
$stmt = $db->prepare('UPDATE users SET username = :username WHERE id = :id');
|
||||
|
||||
$stmt->bindValue(':id', $_SESSION['id']);
|
||||
$stmt->bindValue(':username', $username);
|
||||
|
||||
$stmt->execute();
|
||||
|
||||
$_SESSION['display-username'] = htmlspecialchars($_POST['new-username']);
|
||||
|
||||
output(200, 'Identifiant changé.');
|
||||
}
|
||||
|
||||
?>
|
||||
|
||||
<p>
|
||||
Vous pouvez ici changer l'identifiant permettant d'accéder à votre compte Niver.
|
||||
</p>
|
||||
|
||||
<form method="post">
|
||||
<label for="new-username">Nouvel identifiant</label><br>
|
||||
<input required="" autocomplete="new-username" minlength="1" maxlength="1024" pattern="<?= USERNAME_REGEX ?>" id="new-username" name="new-username" type="text" placeholder="<?= PLACEHOLDER_USERNAME ?>"><br>
|
||||
|
||||
<input type="submit">
|
||||
</form>
|
|
@ -31,7 +31,7 @@ if (processForm()) {
|
|||
|
||||
rateLimit();
|
||||
|
||||
addSite($_SESSION['username'], $_POST['dir'], $_POST['domain'], 'dns', 'http');
|
||||
addSite($_SESSION['id'], $_POST['dir'], $_POST['domain'], 'dns', 'http');
|
||||
|
||||
exec('2>&1 ' . CONF['ht']['sudo_path'] . ' ' . CONF['ht']['certbot_path'] . ' certonly' . (($_SESSION['type'] === 'trusted') ? '' : ' --test-cert') . ' --key-type rsa --rsa-key-size 3072 --webroot --webroot-path /srv/niver/acme --domain ' . $_POST['domain'], $output, $returnCode);
|
||||
if ($returnCode !== 0)
|
||||
|
@ -41,7 +41,7 @@ if (processForm()) {
|
|||
listen [' . CONF['ht']['ipv6_listen_address'] . ']:' . CONF['ht']['https_port'] . ' ssl http2;
|
||||
listen ' . CONF['ht']['ipv4_listen_address'] . ':' . CONF['ht']['https_port'] . ' ssl http2;
|
||||
server_name ' . $_POST['domain'] . ';
|
||||
root ' . CONF['ht']['ht_path'] . '/' . $_SESSION['username'] . '/' . $_POST['dir'] . ';
|
||||
root ' . CONF['ht']['ht_path'] . '/' . $_SESSION['id'] . '/' . $_POST['dir'] . ';
|
||||
|
||||
ssl_certificate /etc/letsencrypt/live/' . $_POST['domain'] . '/fullchain.pem;
|
||||
ssl_certificate_key /etc/letsencrypt/live/' . $_POST['domain'] . '/privkey.pem;
|
||||
|
|
|
@ -7,10 +7,10 @@ if (processForm()) {
|
|||
rateLimit();
|
||||
|
||||
// Add Tor config
|
||||
$torConf = 'HiddenServiceDir ' . CONF['ht']['tor_keys_path'] . '/' . $_SESSION['username'] . '/' . $_POST['dir'] . '/
|
||||
$torConf = 'HiddenServiceDir ' . CONF['ht']['tor_keys_path'] . '/' . $_SESSION['id'] . '/' . $_POST['dir'] . '/
|
||||
HiddenServicePort 80 [::1]:' . CONF['ht']['internal_onion_http_port'] . '
|
||||
';
|
||||
if (file_put_contents(CONF['ht']['tor_config_path'] . '/' . $_SESSION['username'] . '/' . $_POST['dir'], $torConf) === false)
|
||||
if (file_put_contents(CONF['ht']['tor_config_path'] . '/' . $_SESSION['id'] . '/' . $_POST['dir'], $torConf) === false)
|
||||
output(500, 'Failed to write new Tor configuration.');
|
||||
|
||||
// Reload Tor
|
||||
|
@ -19,19 +19,19 @@ if (processForm()) {
|
|||
output(500, 'Failed to reload Tor.');
|
||||
|
||||
// Get the address generated by Tor
|
||||
exec(CONF['ht']['sudo_path'] . ' -u ' . CONF['ht']['tor_user'] . ' ' . CONF['ht']['cat_path'] . ' ' . CONF['ht']['tor_keys_path'] . '/' . $_SESSION['username'] . '/' . $_POST['dir'] . '/hostname', $output);
|
||||
exec(CONF['ht']['sudo_path'] . ' -u ' . CONF['ht']['tor_user'] . ' ' . CONF['ht']['cat_path'] . ' ' . CONF['ht']['tor_keys_path'] . '/' . $_SESSION['id'] . '/' . $_POST['dir'] . '/hostname', $output);
|
||||
$onion = $output[0];
|
||||
if (preg_match('/^[0-9a-z]{56}\.onion$/D', $onion) !== 1)
|
||||
output(500, 'No onion address found.');
|
||||
|
||||
// Store it in the database
|
||||
addSite($_SESSION['username'], $_POST['dir'], $onion, 'onion', 'http');
|
||||
addSite($_SESSION['id'], $_POST['dir'], $onion, 'onion', 'http');
|
||||
|
||||
// Add Nginx config
|
||||
$nginxConf = 'server {
|
||||
listen [::1]:' . CONF['ht']['internal_onion_http_port'] . ';
|
||||
server_name ' . $onion . ';
|
||||
root ' . CONF['ht']['ht_path'] . '/' . $_SESSION['username'] . '/' . $_POST['dir'] . ';
|
||||
root ' . CONF['ht']['ht_path'] . '/' . $_SESSION['id'] . '/' . $_POST['dir'] . ';
|
||||
|
||||
include inc/ht-onion.conf;
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
<dl>
|
||||
<?php
|
||||
|
||||
$sites = query('select', 'sites', ['username' => $_SESSION['username'] ?? '']);
|
||||
$sites = query('select', 'sites', ['username' => $_SESSION['id'] ?? '']);
|
||||
if ($sites === [])
|
||||
echo ' <p>Ce compte n\'héberge aucun site sur cette instance.<p>' . LF;
|
||||
else {
|
||||
|
@ -71,7 +71,7 @@ echo (($quotaSize >> 30) >= 1) ? $quotaSize >> 30 . ' ' . linkToDocs('units', '<
|
|||
<section>
|
||||
<h3>Se connecter au serveur</h3>
|
||||
|
||||
<a href="sftp://<?= isset($_SESSION['username']) ? $_SESSION['username'] : '<username>'; ?>@<?= CONF['ht']['sftp_domain'] ?>:<?= CONF['ht']['public_sftp_port'] ?>/">sftp://<?= isset($_SESSION['username']) ? $_SESSION['username'] : '<username>'; ?>@<?= CONF['ht']['sftp_domain'] ?>:<?= CONF['ht']['public_sftp_port'] ?>/</a>
|
||||
<a href="sftp://<?= isset($_SESSION['display-username']) ? $_SESSION['display-username'] : '<username>'; ?>@<?= CONF['ht']['sftp_domain'] ?>:<?= CONF['ht']['public_sftp_port'] ?>/">sftp://<?= isset($_SESSION['display-username']) ? $_SESSION['display-username'] : '<username>'; ?>@<?= CONF['ht']['sftp_domain'] ?>:<?= CONF['ht']['public_sftp_port'] ?>/</a>
|
||||
|
||||
<dl>
|
||||
<dt>Serveur</dt>
|
||||
|
@ -88,7 +88,7 @@ echo (($quotaSize >> 30) >= 1) ? $quotaSize >> 30 . ' ' . linkToDocs('units', '<
|
|||
</dd>
|
||||
<dt>Utilisataire</dt>
|
||||
<dd>
|
||||
<code><?= isset($_SESSION['username']) ? $_SESSION['username'] : '<username>'; ?></code>
|
||||
<code><?= isset($_SESSION['display-username']) ? $_SESSION['display-username'] : '<username>'; ?></code>
|
||||
</dd>
|
||||
<dt>Clé de passe</dt>
|
||||
<dd>
|
||||
|
|
|
@ -70,8 +70,8 @@ if (processForm() AND isset($_POST['zone-content'])) { // Update zone
|
|||
<select required="" name="zone" id="zone">
|
||||
<option value="" disabled="" selected="">-</option>
|
||||
<?php
|
||||
if (isset($_SESSION['username']))
|
||||
foreach (nsListUserZones($_SESSION['username']) as $zone)
|
||||
if (isset($_SESSION['id']))
|
||||
foreach (nsListUserZones($_SESSION['id']) as $zone)
|
||||
echo ' <option value="' . $zone . '">' . $zone . '</option>' . LF;
|
||||
?>
|
||||
</select>
|
||||
|
|
|
@ -19,7 +19,7 @@ foreach (CONF['ns']['servers'] as $server)
|
|||
|
||||
<?php
|
||||
|
||||
$zones = query('select', 'zones', ['username' => $_SESSION['username'] ?? ''], 'zone');
|
||||
$zones = query('select', 'zones', ['username' => $_SESSION['id'] ?? ''], 'zone');
|
||||
if ($zones === [])
|
||||
echo '<p>Ce compte n\'héberge aucune zone sur cette instance.<p>' . LF;
|
||||
else {
|
||||
|
|
|
@ -13,8 +13,8 @@
|
|||
<select required="" name="zone" id="zone">
|
||||
<option value="" disabled="" selected="">-</option>
|
||||
<?php
|
||||
if (isset($_SESSION['username']))
|
||||
foreach (nsListUserZones($_SESSION['username']) as $zone)
|
||||
if (isset($_SESSION['id']))
|
||||
foreach (nsListUserZones($_SESSION['id']) as $zone)
|
||||
echo ' <option value="' . $zone . '">' . $zone . '</option>' . LF;
|
||||
?>
|
||||
</select>
|
||||
|
|
|
@ -22,7 +22,7 @@ if (processForm()) {
|
|||
|
||||
insert('zones', [
|
||||
'zone' => $_POST['domain'],
|
||||
'username' => $_SESSION['username'],
|
||||
'username' => $_SESSION['id'],
|
||||
]);
|
||||
|
||||
$knotZonePath = CONF['ns']['knot_zones_path'] . '/' . $_POST['domain'] . 'zone';
|
||||
|
|
|
@ -15,8 +15,8 @@ if (processForm()) {
|
|||
<select required="" name="zone" id="zone">
|
||||
<option value="" disabled="" selected="">-</option>
|
||||
<?php
|
||||
if (isset($_SESSION['username']))
|
||||
foreach (nsListUserZones($_SESSION['username']) as $zone)
|
||||
if (isset($_SESSION['id']))
|
||||
foreach (nsListUserZones($_SESSION['id']) as $zone)
|
||||
echo ' <option value="' . $zone . '">' . $zone . '</option>' . LF;
|
||||
?>
|
||||
</select>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<?php
|
||||
|
||||
if (isset($_SESSION['username']))
|
||||
$domains = regListUserDomains($_SESSION['username']);
|
||||
if (isset($_SESSION['id']))
|
||||
$domains = regListUserDomains($_SESSION['id']);
|
||||
else
|
||||
$domains = [];
|
||||
|
||||
|
|
|
@ -42,8 +42,8 @@ if (processForm()) {
|
|||
<select required="" name="suffix" id="suffix">
|
||||
<option value="" disabled="" selected="">---</option>
|
||||
<?php
|
||||
if (isset($_SESSION['username']))
|
||||
foreach(regListUserDomains($_SESSION['username']) as $suffix)
|
||||
if (isset($_SESSION['id']))
|
||||
foreach(regListUserDomains($_SESSION['id']) as $suffix)
|
||||
echo ' <option value="' . $suffix . '">' . $suffix . '</option>' . LF;
|
||||
?>
|
||||
</select>
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
<?php
|
||||
|
||||
$domains = query('select', 'registry', ['username' => $_SESSION['username'] ?? ''], 'domain');
|
||||
$domains = query('select', 'registry', ['username' => $_SESSION['id'] ?? ''], 'domain');
|
||||
if ($domains === [])
|
||||
echo '<p>Ce compte n\'a aucun domaine enregistré sur <code>' . CONF['reg']['registry'] . '</code><p>' . LF;
|
||||
else {
|
||||
|
|
|
@ -32,8 +32,8 @@ if (processForm()) {
|
|||
<select required="" name="domain" id="domain">
|
||||
<option value="" disabled="" selected="">---</option>
|
||||
<?php
|
||||
if (isset($_SESSION['username']))
|
||||
foreach (regListUserDomains($_SESSION['username']) as $domain)
|
||||
if (isset($_SESSION['id']))
|
||||
foreach (regListUserDomains($_SESSION['id']) as $domain)
|
||||
echo ' <option value="' . $domain . '">' . $domain . '</option>' . LF;
|
||||
?>
|
||||
</select>
|
||||
|
|
|
@ -3,8 +3,8 @@
|
|||
<select required="" name="domain" id="domain">
|
||||
<option value="" disabled="" selected="">-</option>
|
||||
<?php
|
||||
if (isset($_SESSION['username']))
|
||||
foreach (regListUserDomains($_SESSION['username']) as $domain)
|
||||
if (isset($_SESSION['id']))
|
||||
foreach (regListUserDomains($_SESSION['id']) as $domain)
|
||||
echo ' <option value="' . $domain . '">' . $domain . '</option>' . LF;
|
||||
?>
|
||||
</select>
|
||||
|
|
|
@ -16,7 +16,7 @@ if (processForm()) {
|
|||
|
||||
insert('registry', [
|
||||
'domain' => $domain,
|
||||
'username' => $_SESSION['username'],
|
||||
'username' => $_SESSION['id'],
|
||||
'last_renewal' => date('Y-m-d H:i:s'),
|
||||
]);
|
||||
|
||||
|
|
|
@ -20,8 +20,8 @@ if (processForm()) {
|
|||
<select required="" name="domain" id="domain">
|
||||
<option value="" disabled="" selected="">---</option>
|
||||
<?php
|
||||
if (isset($_SESSION['username']))
|
||||
foreach(regListUserDomains($_SESSION['username']) as $domain)
|
||||
if (isset($_SESSION['id']))
|
||||
foreach(regListUserDomains($_SESSION['id']) as $domain)
|
||||
echo ' <option value="' . $domain . '">' . $domain . '</option>' . LF;
|
||||
?>
|
||||
</select>
|
||||
|
|
|
@ -91,7 +91,7 @@ foreach (glob('css/*.css') as $cssPath)
|
|||
<body>
|
||||
<header>
|
||||
<p>
|
||||
<?php if (isset($_SESSION['username'])) { ?>
|
||||
<?php if (isset($_SESSION['id'])) { ?>
|
||||
<?= ($_SESSION['type'] === 'trusted') ? '<span title="Compte confiancé">👤 </span>' : '<span title="Compte de test">⏳ </span>' ?><strong><?= $_SESSION['display-username'] ?></strong> <a class="auth" href="<?= CONF['common']['prefix'] ?>/auth/logout">Se déconnecter</a>
|
||||
<?php } else { ?>
|
||||
<span aria-hidden="true">👻 </span><em>Anonyme</em> <a class="auth" href="<?= redirUrl('auth/login') ?>">Se connecter</a>
|
||||
|
|
|
@ -4,15 +4,17 @@ require 'router.php';
|
|||
|
||||
$auth_data = json_decode(file_get_contents('php://input'), true);
|
||||
|
||||
$internal_username = hash('sha256', $auth_data['username']);
|
||||
$username = hashUsername($auth_data['username']);
|
||||
|
||||
if (userExist($internal_username) === true AND checkPassword($internal_username, $auth_data['password']) === true) {
|
||||
$id = query('select', 'users', ['username' => $username], 'id')[0];
|
||||
|
||||
if (usernameExists($username) === true AND checkPassword($id, $auth_data['password']) === true) {
|
||||
echo '
|
||||
{
|
||||
"status": 1,
|
||||
"username": ' . json_encode($auth_data['username']) . ',
|
||||
"home_dir": "' . CONF['ht']['ht_path'] . '/' . $internal_username . '",
|
||||
"quota_size": ' . ((query('select', 'users', ['username' => $internal_username], 'type')[0] === 'trusted') ? CONF['ht']['user_quota_trusted'] : CONF['ht']['user_quota_testing']) . ',
|
||||
"home_dir": "' . CONF['ht']['ht_path'] . '/' . $id . '",
|
||||
"quota_size": ' . ((query('select', 'users', ['id' => $id], 'type')[0] === 'trusted') ? CONF['ht']['user_quota_trusted'] : CONF['ht']['user_quota_testing']) . ',
|
||||
"permissions": {
|
||||
"/": [
|
||||
"*"
|
||||
|
|
Loading…
Reference in a new issue