Add form to delete account

Move service-specific deletion code to functions
This commit is contained in:
Miraty 2022-06-18 04:22:05 +02:00
parent 4cafad3310
commit 6dbc63a36a
25 changed files with 203 additions and 150 deletions

View file

@ -13,12 +13,8 @@ define("PLACEHOLDER_IPV4", "203.0.113.42"); // From RFC5737: IPv4 Address Blocks
// Page titles definition
require "pages.php";
require CONF['common']['root_path'] . "/fn/common.php";
// Service-specific functions and constants
if (SERVICE === "reg" OR SERVICE === "ns")
require CONF['common']['root_path'] . "/fn/dns.php";
if (SERVICE !== "")
require CONF['common']['root_path'] . "/fn/" . SERVICE . ".php";
foreach (array_diff(scandir(CONF['common']['root_path'] . "/fn"), array('..', '.')) as $file)
require CONF['common']['root_path'] . '/fn/' . $file;
function success($msg = '') {
if ($msg !== '')

View file

@ -6,6 +6,7 @@ define('TITLES', [
'index' => '<span aria-hidden="true">🔐 </span>Authentification',
'login' => 'Se connecter',
'register' => 'Créer un compte',
'unregister' => 'Supprimer son compte',
'password' => 'Changer la clé de passe',
'logout' => 'Déconnexion',
],
@ -48,6 +49,7 @@ define('DESCRIPTIONS', [
'index' => 'Gérer son compte',
'login' => 'Démarrer une nouvelle session avec un compte existant',
'register' => 'Créer un nouveau compte Niver',
'unregister' => 'Effacer les données liées à son compte',
'password' => 'Changer la chaîne de caractères permettant de vous authentifier.',
'logout' => 'Terminer la session et effacer ses cookies',
],

View file

@ -29,7 +29,7 @@ function hashPassword($password) {
}
function userExist($username) {
return $username === query('select', 'users', ['username' => $username], 'username')[0];
return isset(query('select', 'users', ['username' => $username], 'username')[0]);
}
function checkPassword($username, $password) {

View file

@ -1,22 +1,42 @@
<?php
function knotcExec($suffix, $cmd) {
$action = checkAction($_POST['action']);
exec(CONF['dns']['knotc_path'] . " zone-begin " . $suffix, $output['begin'], $code['begin']);
function knotcConfExec($cmds) {
exec(CONF['dns']['knotc_path'] . " conf-begin", $output['begin'], $code['begin']);
if ($code['begin'] !== 0)
serverError("<code>knotc</code> failed with exit code <samp>" . $code['begin'] . "</samp>: <samp>" . $output['begin'][0] . "</samp>.");
serverError("knotcConfExec: <code>knotc</code> failed with exit code <samp>" . $code['begin'] . "</samp>: <samp>" . $output['begin'][0] . "</samp>.");
exec(CONF['dns']['knotc_path'] . " zone-" . $action . "set " . $suffix . " " . implode(" ", $cmd), $output['op'], $code['op']);
if ($code['op'] !== 0) {
exec(CONF['dns']['knotc_path'] . " zone-abort " . $suffix);
serverError("<code>knotc</code> failed with exit code <samp>" . $code['op'] . "</samp>: <samp>" . $output['op'][0] . "</samp>.");
foreach ($cmds as $cmd) {
exec(CONF['dns']['knotc_path'] . " conf-" . $cmd, $output['op'], $code['op']);
if ($code['op'] !== 0) {
exec(CONF['dns']['knotc_path'] . " conf-abort");
serverError("knotcConfExec: <code>knotc</code> failed with exit code <samp>" . $code['op'] . "</samp>: <samp>" . $output['op'][0] . "</samp>.");
}
}
exec(CONF['dns']['knotc_path'] . " zone-commit " . $suffix, $output['commit'], $code['commit']);
exec(CONF['dns']['knotc_path'] . " conf-commit", $output['commit'], $code['commit']);
if ($code['commit'] !== 0) {
exec(CONF['dns']['knotc_path'] . " zone-abort " . $suffix);
serverError("<code>knotc</code> failed with exit code <samp>" . $code['commit'] . "</samp>: <samp>" . $output['commit'][0] . "</samp>.");
exec(CONF['dns']['knotc_path'] . " conf-abort");
serverError("knotcConfExec: <code>knotc</code> failed with exit code <samp>" . $code['commit'] . "</samp>: <samp>" . $output['commit'][0] . "</samp>.");
}
}
function knotcZoneExec($zone, $cmd) {
$action = checkAction($_POST['action']);
exec(CONF['dns']['knotc_path'] . " zone-begin " . $zone, $output['begin'], $code['begin']);
if ($code['begin'] !== 0)
serverError("knotcZoneExec: <code>knotc</code> failed with exit code <samp>" . $code['begin'] . "</samp>: <samp>" . $output['begin'][0] . "</samp>.");
exec(CONF['dns']['knotc_path'] . " zone-" . $action . "set " . $zone . " " . implode(" ", $cmd), $output['op'], $code['op']);
if ($code['op'] !== 0) {
exec(CONF['dns']['knotc_path'] . " zone-abort " . $zone);
serverError("knotcZoneExec: <code>knotc</code> failed with exit code <samp>" . $code['op'] . "</samp>: <samp>" . $output['op'][0] . "</samp>.");
}
exec(CONF['dns']['knotc_path'] . " zone-commit " . $zone, $output['commit'], $code['commit']);
if ($code['commit'] !== 0) {
exec(CONF['dns']['knotc_path'] . " zone-abort " . $zone);
serverError("knotcZoneExec: <code>knotc</code> failed with exit code <samp>" . $code['commit'] . "</samp>: <samp>" . $output['commit'][0] . "</samp>.");
}
}

View file

@ -20,18 +20,16 @@ function addSite($username, $siteDir, $domain, $domainType, $protocol) {
$op = $db->prepare("INSERT INTO sites(username, site_dir, domain, domain_type, protocol, creation_date, le_enabled) VALUES(:username, :site_dir, :domain, :domain_type, :protocol, :creation_date, :le_enabled)");
if ($domainType === "dns" AND $protocol === "http")
$le_enabled = 0;
else
$le_enabled = NULL;
$op->bindValue(':username', $username);
$op->bindValue(':site_dir', $siteDir);
$op->bindValue(':domain', $domain);
$op->bindValue(':domain_type', $domainType);
$op->bindValue(':protocol', $protocol);
$op->bindValue(':creation_date', date("Y-m-d H:i:s"));
$op->bindValue(':le_enabled', $le_enabled);
if ($domainType === "dns" AND $protocol === "http")
$op->bindValue(':le_enabled', 0);
else
$op->bindValue(':le_enabled', NULL);
$op->execute();
}
@ -47,3 +45,51 @@ function dirsStatuses($username, $domainType, $protocol) {
$dirs[$fsDir] = in_array($fsDir, $dbDirs);
return $dirs;
}
function htDeleteSite($dir, $domainType, $protocol) {
if ($domainType === 'onion') {
// Delete Tor config
$torConf = file_get_contents(CONF['ht']['tor_config_path']);
if ($torConf === false)
serverError("Failed to read current Tor configuration.");
$torConf = str_replace('HiddenServiceDir ' . CONF['ht']['tor_keys_path'] . '/' . $dir . '/
HiddenServicePort 80 [::1]:' . CONF['ht']['internal_onion_http_port'] . '
', '', $torConf);
if (file_put_contents(CONF['ht']['tor_config_path'], $torConf) === false)
serverError("Failed to write new Tor configuration.");
// Reload Tor
exec(CONF['ht']['sudo_path'] . " " . CONF['ht']['systemctl_path'] . " reload " . CONF['ht']['tor_service'], $output, $code);
if ($code !== 0)
serverError("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'] . "/" . $dir, $output, $code);
if ($code !== 0)
serverError("Failed to delete Tor keys.");
}
// Delete Nginx config
$domain = query('select', 'sites', [
'username' => $_SESSION['username'],
'domain_type' => $domainType,
'protocol' => $protocol,
'site_dir' => $dir,
], 'domain')[0];
if (unlink(CONF['ht']['nginx_config_path'] . '/' . $domain . '.conf') !== true)
serverError("Failed to delete Nginx configuration.");
// Reload Nginx
exec(CONF['ht']['sudo_path'] . ' ' . CONF['ht']['systemctl_path'] . ' reload nginx', result_code: $code);
if ($code !== 0)
serverError("Failed to reload Nginx.");
// Delete from database
query('delete', 'sites', [
'username' => $_SESSION['username'],
'domain_type' => $domainType,
'protocol' => $protocol,
'site_dir' => $dir,
]);
}

View file

@ -35,3 +35,21 @@ function nsCheckZonePossession($zone) {
if (!in_array($zone, query('select', 'zones', ['username' => $_SESSION['username']], 'zone'), true))
userError("You don't own this zone on the nameserver.");
}
function nsDeleteZone($zone) {
// Remove from Knot configuration
knotcConfExec(["unset 'zone[$zone]'"]);
// Remove Knot zone file
if(unlink(CONF['ns']['knot_zones_path'] . '/' . $zone . 'zone') !== true)
serverError("Failed to remove Knot zone file.");
// Remove Knot related data
exec(CONF['dns']['knotc_path'] . " zone-purge " . $zone);
// Remove from database
query('delete', 'zones', [
'zone' => $zone,
'username' => $_SESSION['username'],
]);
}

View file

@ -12,3 +12,19 @@ function regCheckDomainPossession($domain) {
function regIsFree($domain) {
return empty(query('select', 'registry', ['domain' => $domain], 'domain'));
}
function regDeleteDomain($domain) {
// Delete domain from registry file
$regFile = file_get_contents(CONF['reg']['registry_file']);
if ($regFile === false)
serverError("Failed to read current registry File.");
$regFile = preg_replace("#[^\n]{0,1024}" . $domain . " {0,1024}[^\n]{0,1024}\n#", "", $regFile);
if (file_put_contents(CONF['reg']['registry_file'], $regFile) === false)
serverError("Failed to write new registry file.");
// Delete from Niver's database
query('delete', 'registry', [
'domain' => $domain,
'username' => $_SESSION['username'],
]);
}

View file

@ -0,0 +1,49 @@
<?php require "../../common/html.php"; ?>
<form method="post">
<input type="checkbox" name="delete" id="delete">
<label for="delete">Supprimer mon compte et toutes ses données</label>
<br>
<input type="submit">
</form>
<?php
switchToFormProcess();
if (!isset($_POST['delete']))
userError("Il faut confirmer la suppression du compte");
foreach (query('select', 'registry', ['username' => $_SESSION['username']], 'domain') as $domain)
regDeleteDomain($domain);
foreach (query('select', 'zones', ['username' => $_SESSION['username']], 'zone') as $zone)
nsDeleteZone($zone);
foreach (query('select', 'sites', [
'username' => $_SESSION['username'],
'domain_type' => 'onion',
'protocol' => 'http',
], 'site_dir') as $dir)
htDeleteSite($dir, domainType: 'onion', protocol: 'http');
foreach (query('select', 'sites', [
'username' => $_SESSION['username'],
'domain_type' => 'dns',
'protocol' => 'http',
], 'site_dir') as $dir)
htDeleteSite($dir, domainType: 'dns', protocol: 'http');
// PHP rmdir() only works on empty directories
$dirObj = new RecursiveDirectoryIterator(CONF['ht']['ht_path'] . "/" . $_SESSION['username'], RecursiveDirectoryIterator::SKIP_DOTS);
$files = new RecursiveIteratorIterator($dirObj, RecursiveIteratorIterator::CHILD_FIRST);
foreach ($files as $path)
$path->isDir() && !$path->isLink() ? rmdir($path->getPathname()) : unlink($path->getPathname());
if (rmdir(CONF['ht']['ht_path'] . '/' . $_SESSION['username']) !== true)
serverError("Unable to delete user's hypertext directory.");
query('delete', 'users', ['username' => $_SESSION['username']]);
redir();
success("Compte supprimé.");

View file

@ -5,9 +5,9 @@
</p>
<form method="post">
<label for="dir">Dossier ciblé</label><br>
<select required="" name="dir" id="dir">
<option value="" disabled="" selected="">---</option>
<label for="dir">Dossier ciblé</label><br>
<select required="" name="dir" id="dir">
<option value="" disabled="" selected="">---</option>
<?php
@ -22,9 +22,9 @@ if (isset($_SESSION['username'])) {
?>
</select>
<br>
<input value="Valider" type="submit">
</select>
<br>
<input value="Valider" type="submit">
</form>
<?php
@ -34,27 +34,6 @@ switchToFormProcess();
if ($dirsStatuses[$_POST['dir']] !== true)
userError("Wrong value for <code>dir</code>.");
// Delete Nginx config
$onion = query('select', 'sites', [
'username' => $_SESSION['username'],
'domain_type' => 'dns',
'protocol' => 'http',
'site_dir' => $_POST['dir'],
], 'domain')[0];
if (unlink(CONF['ht']['nginx_config_path'] . "/" . $onion . ".conf") !== true)
serverError("Failed to delete Nginx configuration.");
htDeleteSite($_POST['dir'], domainType: 'dns', protocol: 'http');
// Reload Nginx
exec(CONF['ht']['sudo_path'] . " " . CONF['ht']['systemctl_path'] . " reload nginx", result_code: $code);
if ($code !== 0)
serverError("Failed to reload Nginx.");
// Delete from database
query('delete', 'sites', [
'username' => $_SESSION['username'],
'domain_type' => 'dns',
'protocol' => 'http',
'site_dir' => $_POST['dir'],
]);
success("Accès retiré avec succès.");
success("Accès retiré.");

View file

@ -34,47 +34,6 @@ switchToFormProcess();
if ($dirsStatuses[$_POST['dir']] !== true)
userError("Wrong value for <code>dir</code>.");
// Delete Tor config
$torConf = file_get_contents(CONF['ht']['tor_config_path']);
if ($torConf === false)
serverError("Failed to read current Tor configuration.");
$torConf = str_replace("HiddenServiceDir " . CONF['ht']['tor_keys_path'] . "/" . $_POST['dir'] . "/
HiddenServicePort 80 [::1]:" . CONF['ht']['internal_onion_http_port'] . "
", "", $torConf);
if (file_put_contents(CONF['ht']['tor_config_path'], $torConf) === false)
serverError("Failed to write new Tor configuration.");
htDeleteSite($_POST['dir'], domainType: 'onion', protocol: 'http');
// Reload Tor
exec(CONF['ht']['sudo_path'] . " " . CONF['ht']['systemctl_path'] . " reload " . CONF['ht']['tor_service'], $output, $code);
if ($code !== 0)
serverError("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'] . "/" . $_POST['dir'], $output, $code);
if ($code !== 0)
serverError("Failed to delete Tor keys.");
// Delete Nginx config
$onion = query('select', 'sites', [
'username' => $_SESSION['username'],
'domain_type' => 'onion',
'protocol' => 'http',
'site_dir' => $_POST['dir'],
], 'domain')[0];
if (unlink(CONF['ht']['nginx_config_path'] . "/" . $onion . ".conf") !== true)
serverError("Failed to delete Nginx configuration.");
// Reload Nginx
exec(CONF['ht']['sudo_path'] . " " . CONF['ht']['systemctl_path'] . " reload nginx", result_code: $code);
if ($code !== 0)
serverError("Failed to reload Nginx.");
// Delete from database
query('delete', 'sites', [
'username' => $_SESSION['username'],
'domain_type' => 'onion',
'protocol' => 'http',
'site_dir' => $_POST['dir'],
]);
success("Accès retiré avec succès.");
success("Accès retiré.");

View file

@ -36,7 +36,7 @@ if (!(preg_match("/^[a-z]{1,127}$/", $_POST['tag'])))
if (!(preg_match("/^[a-z0-9.-]{1,255}$/", $_POST['value'])))
userError("Wrong value for <code>value</code>.");
knotcExec($_POST['zone'], array(
knotcZoneExec($_POST['zone'], array(
$values['domain'],
$values['ttl'],
"CAA",

View file

@ -20,7 +20,7 @@ $values = nsParseCommonRequirements();
$record = checkIpFormat($_POST['ip']);
knotcExec($_POST['zone'], array(
knotcZoneExec($_POST['zone'], array(
$values['domain'],
$values['ttl'],
$record,

View file

@ -31,7 +31,7 @@ if (!($_POST['priority'] >= 0 AND $_POST['priority'] <= 255))
$_POST['host'] = formatAbsoluteDomain($_POST['host']);
knotcExec($_POST['zone'], array(
knotcZoneExec($_POST['zone'], array(
$values['domain'],
$values['ttl'],
"MX",

View file

@ -18,7 +18,7 @@ $values = nsParseCommonRequirements();
$_POST['ns'] = formatAbsoluteDomain($_POST['ns']);
knotcExec($_POST['zone'], array(
knotcZoneExec($_POST['zone'], array(
$values['domain'],
$values['ttl'],
"NS",

View file

@ -49,7 +49,7 @@ if (!($_POST['port'] >= 0 AND $_POST['port'] <= 65535))
$_POST['target'] = formatAbsoluteDomain($_POST['target']);
knotcExec($_POST['zone'], array(
knotcZoneExec($_POST['zone'], array(
$values['domain'],
$values['ttl'],
"SRV",

View file

@ -48,7 +48,7 @@ if (!($_POST['type'] === "2"))
if (!(preg_match("/^[a-z0-9]{64}$/", $_POST['fp'])))
userError("Wrong value for <code>fp</code>.");
knotcExec($_POST['zone'], array(
knotcZoneExec($_POST['zone'], array(
$values['domain'],
$values['ttl'],
"SSHFP",

View file

@ -60,7 +60,7 @@ if (!($_POST['type'] >= 0 AND $_POST['type'] <= 2))
if (!(preg_match("/^[a-zA-Z0-9.-]{1,1024}$/", $_POST['content'])))
userError("Wrong value for <code>content</code>.");
knotcExec($_POST['zone'], array(
knotcZoneExec($_POST['zone'], array(
$values['domain'],
$values['ttl'],
"TLSA",

View file

@ -19,7 +19,7 @@ $values = nsParseCommonRequirements();
if (!(preg_match("/^[a-zA-Z0-9 =:!%$+\/\()[\]_-]{5,8192}$/", $_POST['txt'])))
userError("Wrong value for <code>txt</code>.");
knotcExec($_POST['zone'], array(
knotcZoneExec($_POST['zone'], array(
$values['domain'],
$values['ttl'],
"TXT",

View file

@ -29,9 +29,9 @@ if (is_int(file_put_contents($knotZonePath, $knotZone)) !== true)
if (chmod($knotZonePath, 0660) !== true)
serverError("Failed to chmod new zone file.");
exec(CONF['dns']['knotc_path'] . " conf-begin");
exec(CONF['dns']['knotc_path'] . " conf-set 'zone[" . $_POST['domain'] . "]'");
exec(CONF['dns']['knotc_path'] . " conf-set 'zone[" . $_POST['domain'] . "].template' 'niver'");
exec(CONF['dns']['knotc_path'] . " conf-commit");
knotcConfExec([
"set 'zone[" . $_POST['domain'] . "]'",
"set 'zone[" . $_POST['domain'] . "].template' 'niver'",
]);
success("La requête a été traitée.");

View file

@ -20,22 +20,6 @@ switchToFormProcess();
nsCheckZonePossession($_POST['zone']);
// Remove from Knot configuration
exec(CONF['dns']['knotc_path'] . " conf-begin");
exec(CONF['dns']['knotc_path'] . " conf-unset 'zone[" . $_POST['zone'] . "]'");
exec(CONF['dns']['knotc_path'] . " conf-commit");
// Remove Knot zone file
if(unlink(CONF['ns']['knot_zones_path'] . "/" . $_POST['zone'] . "zone") !== true)
serverError("Failed to remove Knot zone file.");
// Remove Knot related data
exec(CONF['dns']['knotc_path'] . " zone-purge " . $_POST['zone']);
// Remove from database
query('delete', 'zones', [
'zone' => $_POST['zone'],
'username' => $_SESSION['username'],
]);
nsDeleteZone($_POST['zone']);
success("Zone effacée.");

View file

@ -86,7 +86,7 @@ regCheckDomainPossession($_POST['zone']);
$action = checkAction($_POST['action']);
knotcExec(CONF['reg']['registry'], array(
knotcZoneExec(CONF['reg']['registry'], array(
$_POST['zone'],
CONF['reg']['ttl'],
"DS",

View file

@ -45,7 +45,7 @@ $domain = formatAbsoluteDomain(formatEndWithDot($_POST['subdomain']) . $_POST['s
$record = checkIpFormat($_POST['ip']);
knotcExec(CONF['reg']['registry'], array(
knotcZoneExec(CONF['reg']['registry'], array(
$domain,
CONF['reg']['ttl'],
$record,

View file

@ -13,8 +13,8 @@
<option value="" disabled="" selected="">---</option>
<?php
if (isset($_SESSION['username']))
foreach(regListUserDomains($_SESSION['username']) as $suffix)
echo " <option value='" . $suffix . "'>." . $suffix . "</option>\n";
foreach(regListUserDomains($_SESSION['username']) as $domain)
echo " <option value='" . $domain . "'>" . $domain . "</option>\n";
?>
</select>
@ -33,7 +33,7 @@ switchToFormProcess();
regCheckDomainPossession($_POST['domain']);
$_POST['ns'] = formatAbsoluteDomain($_POST['ns']);
knotcExec(CONF['reg']['registry'], array(
knotcZoneExec(CONF['reg']['registry'], array(
$_POST['domain'],
CONF['reg']['ttl'],
"NS",

View file

@ -7,8 +7,8 @@
<option value="" disabled="" selected="">---</option>
<?php
if (isset($_SESSION['username']))
foreach(regListUserDomains($_SESSION['username']) as $suffix)
echo " <option value='" . $suffix . "'>." . $suffix . "</option>\n";
foreach(regListUserDomains($_SESSION['username']) as $domain)
echo " <option value='" . $domain . "'>" . $domain . "</option>\n";
?>
</select>
@ -22,21 +22,6 @@ switchToFormProcess();
regCheckDomainPossession($_POST['domain']);
// Add Tor config
$regFile = file_get_contents(CONF['reg']['registry_file']);
if ($regFile === false)
serverError("Failed to read current registry File.");
$regFile = preg_replace("#[^\n]{0,1024}" . $_POST['domain'] . " {0,1024}[^\n]{0,1024}\n#", "", $regFile);
if (file_put_contents(CONF['reg']['registry_file'], $regFile) === false)
serverError("Failed to write new registry file.");
// Remove from Niver's database
$db = new PDO('sqlite:' . DB_PATH);
$stmt = $db->prepare("DELETE FROM registry WHERE domain = :domain AND username = :username");
$stmt->bindValue(':domain', $_POST['domain']);
$stmt->bindValue(':username', $_SESSION['username']);
$stmt->execute();
regDeleteDomain($_POST['domain']);
success("Domaine effacé du registre.");

View file

@ -1,7 +1,6 @@
<?php
require "common/init.php";
require "fn/auth.php";
$authData = json_decode(file_get_contents("php://input"), true);
$user = json_decode($authData['user'], true);