init.php + jobs + job to delete old testing accounts
This commit is contained in:
parent
f05a55a7fa
commit
e4ae765486
15 changed files with 124 additions and 113 deletions
|
@ -2,6 +2,9 @@
|
|||
|
||||
## Program flow
|
||||
|
||||
`init.php`
|
||||
: Initializes common values
|
||||
|
||||
`router.php`
|
||||
: Receives every external HTTP request from the web server, executes actions required in any case, executes matching code in `pg-act` if appropriate, and calls `view.php` either way.
|
||||
|
||||
|
@ -21,12 +24,12 @@ The `output` function is used to return success or error messages and stop proce
|
|||
`fn/`
|
||||
: Functions, grouped by concerned service
|
||||
|
||||
`jobs/`
|
||||
: CLI scripts
|
||||
|
||||
`sftpgo-auth.php`
|
||||
: When someone tries to log in over SFTP, SFTPGo sends username and password to this script, which queries the database and replies whether authentication succeeded or not.
|
||||
|
||||
`check.php`
|
||||
: This file is not part of the normal program execution, it is meant to be run by developers to test that the current setup is working.
|
||||
|
||||
`DOCS/`
|
||||
: Documentation (some important or standard files may be directly in the root)
|
||||
|
||||
|
|
33
fn/auth.php
33
fn/auth.php
|
@ -87,6 +87,39 @@ function setupDisplayUsername($display_username) {
|
|||
$_SESSION['display-username-cyphertext'] = $cyphertext;
|
||||
}
|
||||
|
||||
function authDeleteUser($user_id) {
|
||||
$user_services = explode(',', query('select', 'users', ['id' => $user_id], 'services')[0]);
|
||||
|
||||
foreach (SERVICES_USER as $service)
|
||||
if (in_array($service, $user_services, true) AND CONF['common']['services'][$service] !== 'enabled')
|
||||
output(503, sprintf(_('Your account can\'t be deleted because the %s service is currently unavailable.'), '<em>' . PAGES[$service]['index']['title'] . '</em>'));
|
||||
|
||||
if (in_array('reg', $user_services, true))
|
||||
foreach (query('select', 'registry', ['username' => $user_id], 'domain') as $domain)
|
||||
regDeleteDomain($domain, $user_id);
|
||||
|
||||
if (in_array('ns', $user_services, true))
|
||||
foreach (query('select', 'zones', ['username' => $user_id], 'zone') as $zone)
|
||||
nsDeleteZone($zone, $user_id);
|
||||
|
||||
if (in_array('ht', $user_services, true)) {
|
||||
foreach (query('select', 'sites', ['username' => $user_id]) as $site)
|
||||
htDeleteSite($site['address'], $site['type'], $user_id);
|
||||
|
||||
exec(CONF['ht']['sudo_path'] . ' -u ' . CONF['ht']['tor_user'] . ' ' . CONF['ht']['rm_path'] . ' -r ' . CONF['ht']['tor_keys_path'] . '/' . $user_id, result_code: $code);
|
||||
if ($code !== 0)
|
||||
output(500, 'Can\'t remove Tor keys directory.');
|
||||
|
||||
removeDirectory(CONF['ht']['tor_config_path'] . '/' . $user_id);
|
||||
|
||||
exec(CONF['ht']['sudo_path'] . ' -u ' . CONF['ht']['sftpgo_user'] . ' ' . CONF['ht']['rm_path'] . ' -r ' . CONF['ht']['ht_path'] . '/fs/' . $user_id, result_code: $code);
|
||||
if ($code !== 0)
|
||||
output(500, 'Can\'t remove user\'s directory.');
|
||||
}
|
||||
|
||||
query('delete', 'users', ['id' => $user_id]);
|
||||
}
|
||||
|
||||
function rateLimit() {
|
||||
if (PAGE_METADATA['tokens_account_cost'] ?? 0 > 0)
|
||||
rateLimitAccount(PAGE_METADATA['tokens_account_cost']);
|
||||
|
|
|
@ -10,7 +10,10 @@ function output($code, $msg = '', $logs = [''], $data = []) {
|
|||
4 => '<p><output>' . _('<strong>User error</strong>: ') . '<em>' . $msg . '</em></output></p>' . LF,
|
||||
5 => '<p><output>' . _('<strong>Server error</strong>: ') . '<em>' . $msg . '</em></output></p>' . LF,
|
||||
};
|
||||
displayPage(array_merge(['final_message' => $final_message], $data));
|
||||
if (is_callable('displayPage'))
|
||||
displayPage(array_merge(['final_message' => $final_message], $data));
|
||||
echo $final_message;
|
||||
exit();
|
||||
}
|
||||
|
||||
function insert($table, $values) {
|
||||
|
|
10
fn/ht.php
10
fn/ht.php
|
@ -76,17 +76,17 @@ function htRelativeSymlink($target, $name) {
|
|||
output(500, 'Unable to create symlink.');
|
||||
}
|
||||
|
||||
function htDeleteSite($address, $type) {
|
||||
function htDeleteSite($address, $type, $user_id) {
|
||||
|
||||
if ($type === 'onion') {
|
||||
$dir = query('select', 'sites', [
|
||||
'username' => $_SESSION['id'],
|
||||
'username' => $user_id,
|
||||
'address' => $address,
|
||||
'type' => $type,
|
||||
], 'site_dir')[0];
|
||||
|
||||
// Delete Tor config
|
||||
if (unlink(CONF['ht']['tor_config_path'] . '/' . $_SESSION['id'] . '/' . $dir) !== true)
|
||||
if (unlink(CONF['ht']['tor_config_path'] . '/' . $user_id . '/' . $dir) !== true)
|
||||
output(500, 'Failed to delete Tor configuration.');
|
||||
|
||||
// Reload Tor
|
||||
|
@ -95,7 +95,7 @@ function htDeleteSite($address, $type) {
|
|||
output(500, 'Failed to reload Tor.');
|
||||
|
||||
// Delete Tor keys
|
||||
exec(CONF['ht']['sudo_path'] . ' -u ' . CONF['ht']['tor_user'] . ' ' . CONF['ht']['rm_path'] . ' -r ' . CONF['ht']['tor_keys_path'] . '/' . $_SESSION['id'] . '/' . $dir, result_code: $code);
|
||||
exec(CONF['ht']['sudo_path'] . ' -u ' . CONF['ht']['tor_user'] . ' ' . CONF['ht']['rm_path'] . ' -r ' . CONF['ht']['tor_keys_path'] . '/' . $user_id . '/' . $dir, result_code: $code);
|
||||
if ($code !== 0)
|
||||
output(500, 'Failed to delete Tor keys.');
|
||||
}
|
||||
|
@ -117,7 +117,7 @@ function htDeleteSite($address, $type) {
|
|||
output(500, 'Unable to delete symlink.');
|
||||
|
||||
query('delete', 'sites', [
|
||||
'username' => $_SESSION['id'],
|
||||
'username' => $user_id,
|
||||
'type' => $type,
|
||||
'address' => $address,
|
||||
]);
|
||||
|
|
|
@ -48,7 +48,7 @@ function nsCheckZonePossession($zone) {
|
|||
output(403, 'You don\'t own this zone on the name server.');
|
||||
}
|
||||
|
||||
function nsDeleteZone($zone) {
|
||||
function nsDeleteZone($zone, $user_id) {
|
||||
// Remove from Knot configuration
|
||||
knotcConfExec(["unset 'zone[$zone]'"]);
|
||||
|
||||
|
@ -64,6 +64,6 @@ function nsDeleteZone($zone) {
|
|||
// Remove from database
|
||||
query('delete', 'zones', [
|
||||
'zone' => $zone,
|
||||
'username' => $_SESSION['id'],
|
||||
'username' => $user_id,
|
||||
]);
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@ function regCheckDomainPossession($domain) {
|
|||
output(403, 'You don\'t own this domain on the registry.');
|
||||
}
|
||||
|
||||
function regDeleteDomain($domain) {
|
||||
function regDeleteDomain($domain, $user_id) {
|
||||
// Delete domain from registry file
|
||||
$path = CONF['reg']['suffixes_path'] . '/' . regParseDomain($domain)['suffix'] . 'zone';
|
||||
$content = file_get_contents($path);
|
||||
|
@ -28,7 +28,7 @@ function regDeleteDomain($domain) {
|
|||
|
||||
$conditions = [
|
||||
'domain' => $domain,
|
||||
'username' => $_SESSION['id'],
|
||||
'username' => $user_id,
|
||||
];
|
||||
|
||||
insert('registry-history', [
|
||||
|
|
48
init.php
Normal file
48
init.php
Normal file
|
@ -0,0 +1,48 @@
|
|||
<?php
|
||||
umask(0077);
|
||||
|
||||
set_error_handler(function ($level, $message, $file = '', $line = 0) {
|
||||
throw new ErrorException($message, 0, $level, $file, $line);
|
||||
});
|
||||
set_exception_handler(function ($e) {
|
||||
error_log($e);
|
||||
http_response_code(500);
|
||||
echo '<h1>Error</h1>An error occured.';
|
||||
});
|
||||
register_shutdown_function(function () { // Also catch fatal errors
|
||||
if (($error = error_get_last()) !== NULL)
|
||||
throw new ErrorException($error['message'], 0, $error['type'], $error['file'], $error['line']);
|
||||
});
|
||||
|
||||
const ROOT_PATH = __DIR__;
|
||||
define('CONF', parse_ini_file(ROOT_PATH . '/config.ini', true, INI_SCANNER_TYPED));
|
||||
|
||||
define('DB', new PDO('sqlite:' . ROOT_PATH . '/db/servnest.db'));
|
||||
DB->exec('PRAGMA foreign_keys = ON;');
|
||||
DB->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
||||
|
||||
date_default_timezone_set('UTC');
|
||||
|
||||
foreach (explode(',', preg_replace('/[A-Z0-9]|q=|;|-|\./', '', $_SERVER['HTTP_ACCEPT_LANGUAGE'] ?? '')) as $client_locale)
|
||||
if (in_array($client_locale, array_diff(scandir(ROOT_PATH . '/locales'), ['..', '.']), true)) {
|
||||
$locale = $client_locale;
|
||||
break;
|
||||
}
|
||||
define('LOCALE', $locale ?? 'en');
|
||||
putenv('LANG=C.UTF-8');
|
||||
setlocale(LC_MESSAGES, 'C.UTF-8');
|
||||
bindtextdomain('messages', ROOT_PATH . '/locales/' . LOCALE);
|
||||
header('Content-Language: ' . LOCALE);
|
||||
|
||||
const SERVICES_USER = ['reg', 'ns', 'ht'];
|
||||
|
||||
const LF = "\n";
|
||||
|
||||
const PLACEHOLDER_DOMAIN = 'example'; // From RFC2606: Reserved Top Level DNS Names > 2. TLDs for Testing, & Documentation Examples
|
||||
const PLACEHOLDER_IPV6 = '2001:db8::3'; // From RFC3849: IPv6 Address Prefix Reserved for Documentation
|
||||
const PLACEHOLDER_IPV4 = '203.0.113.42'; // From RFC5737: IPv4 Address Blocks Reserved for Documentation
|
||||
|
||||
foreach (array_diff(scandir(ROOT_PATH . '/fn'), ['..', '.']) as $file)
|
||||
require ROOT_PATH . '/fn/' . $file;
|
||||
|
||||
require ROOT_PATH . '/pages.php';
|
|
@ -1,7 +1,6 @@
|
|||
<?php
|
||||
umask(0077);
|
||||
const ROOT_PATH = __DIR__;
|
||||
define('CONF', parse_ini_file(ROOT_PATH . '/config.ini', true, INI_SCANNER_TYPED));
|
||||
<?php // Test that the current setup is working
|
||||
|
||||
require 'init.php';
|
||||
|
||||
const SFTP = '/usr/bin/sftp';
|
||||
const SSHPASS = '/usr/bin/sshpass';
|
||||
|
@ -13,16 +12,10 @@ const SUFFIX = 'test.servnest.test.';
|
|||
|
||||
const TOR_PROXY = 'socks5h://127.0.0.1:9050';
|
||||
|
||||
const LF = "\n";
|
||||
|
||||
exec(CONF['dns']['kdig_path'] . ' torproject.org AAAA', $output, $return_code);
|
||||
if (preg_match('/^;; Flags: qr rd ra ad;/Dm', implode("\n", $output)) !== 1)
|
||||
exit('Unable to do a DNSSEC-validated DNS query.' . LF);
|
||||
|
||||
if (CONF['common']['services']['ns'] === 'rest') {
|
||||
echo 'a';
|
||||
}
|
||||
|
||||
define('COOKIE_FILE', sys_get_temp_dir() . '/cookie-' . bin2hex(random_bytes(16)) . '.txt');
|
||||
|
||||
function curlTest($address, $post = [], $tor = false) {
|
10
jobs/delete-old-testing.php
Normal file
10
jobs/delete-old-testing.php
Normal file
|
@ -0,0 +1,10 @@
|
|||
<?php
|
||||
require 'init.php';
|
||||
|
||||
const MAX_TESTING_ACCOUNT_AGE = 86400 * 10;
|
||||
|
||||
foreach (query('select', 'users', ['type' => 'testing']) as $account) {
|
||||
$account_age = time() - date_create_immutable_from_format('Y-m-d H:i:s', $account['registration_date'])->getTimestamp();
|
||||
if ($account_age > MAX_TESTING_ACCOUNT_AGE)
|
||||
authDeleteUser($account['id']);
|
||||
}
|
|
@ -6,36 +6,7 @@ if (checkPassword($_SESSION['id'], $_POST['current-password']) !== true)
|
|||
if (!isset($_POST['delete']))
|
||||
output(403, _('Account deletion must be confirmed.'));
|
||||
|
||||
$user_services = explode(',', query('select', 'users', ['id' => $_SESSION['id']], 'services')[0]);
|
||||
|
||||
foreach (SERVICES_USER as $service)
|
||||
if (in_array($service, $user_services, true) AND CONF['common']['services'][$service] !== 'enabled')
|
||||
output(503, sprintf(_('Your account can\'t be deleted because the %s service is currently unavailable.'), '<em>' . PAGES[$service]['index']['title'] . '</em>'));
|
||||
|
||||
if (in_array('reg', $user_services, true))
|
||||
foreach (query('select', 'registry', ['username' => $_SESSION['id']], 'domain') as $domain)
|
||||
regDeleteDomain($domain);
|
||||
|
||||
if (in_array('ns', $user_services, true))
|
||||
foreach (query('select', 'zones', ['username' => $_SESSION['id']], 'zone') as $zone)
|
||||
nsDeleteZone($zone);
|
||||
|
||||
if (in_array('ht', $user_services, true)) {
|
||||
foreach (query('select', 'sites', ['username' => $_SESSION['id']]) as $site)
|
||||
htDeleteSite($site['address'], $site['type']);
|
||||
|
||||
exec(CONF['ht']['sudo_path'] . ' -u ' . CONF['ht']['tor_user'] . ' ' . CONF['ht']['rm_path'] . ' -r ' . 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['id']);
|
||||
|
||||
exec(CONF['ht']['sudo_path'] . ' -u ' . CONF['ht']['sftpgo_user'] . ' ' . CONF['ht']['rm_path'] . ' -r ' . CONF['ht']['ht_path'] . '/fs/' . $_SESSION['id'], result_code: $code);
|
||||
if ($code !== 0)
|
||||
output(500, 'Can\'t remove user\'s directory.');
|
||||
}
|
||||
|
||||
query('delete', 'users', ['id' => $_SESSION['id']]);
|
||||
authDeleteUser($_SESSION['id']);
|
||||
|
||||
logout();
|
||||
|
||||
|
|
|
@ -6,6 +6,6 @@ if (preg_match('/^(?<type>subpath|subdomain|onion|dns):(?<address>[a-z0-9._-]{1,
|
|||
if (isset(query('select', 'sites', ['username' => $_SESSION['id'], 'address' => $site['address'], 'type' => $site['type']], 'address')[0]) !== true)
|
||||
output(403, 'Unavailable value for <code>site</code>.');
|
||||
|
||||
htDeleteSite($site['address'], $site['type']);
|
||||
htDeleteSite($site['address'], $site['type'], $_SESSION['id']);
|
||||
|
||||
output(200, _('Access removed.'));
|
||||
|
|
|
@ -2,6 +2,6 @@
|
|||
|
||||
nsCheckZonePossession($_POST['zone']);
|
||||
|
||||
nsDeleteZone($_POST['zone']);
|
||||
nsDeleteZone($_POST['zone'], $_SESSION['id']);
|
||||
|
||||
output(200, _('Zone deleted.'));
|
||||
|
|
|
@ -2,6 +2,6 @@
|
|||
|
||||
regCheckDomainPossession($_POST['domain']);
|
||||
|
||||
regDeleteDomain($_POST['domain']);
|
||||
regDeleteDomain($_POST['domain'], $_SESSION['id']);
|
||||
|
||||
output(200, _('Domain unregistered.'));
|
||||
|
|
62
router.php
62
router.php
|
@ -1,54 +1,5 @@
|
|||
<?php
|
||||
umask(0077);
|
||||
|
||||
set_error_handler(function ($level, $message, $file = '', $line = 0) {
|
||||
throw new ErrorException($message, 0, $level, $file, $line);
|
||||
});
|
||||
set_exception_handler(function ($e) {
|
||||
error_log($e);
|
||||
http_response_code(500);
|
||||
echo '<h1>Error</h1>An error occured.';
|
||||
});
|
||||
register_shutdown_function(function () { // Also catch fatal errors
|
||||
if (($error = error_get_last()) !== NULL)
|
||||
throw new ErrorException($error['message'], 0, $error['type'], $error['file'], $error['line']);
|
||||
});
|
||||
|
||||
const ROOT_PATH = __DIR__;
|
||||
define('CONF', parse_ini_file(ROOT_PATH . '/config.ini', true, INI_SCANNER_TYPED));
|
||||
|
||||
define('DB', new PDO('sqlite:' . ROOT_PATH . '/db/servnest.db'));
|
||||
DB->exec('PRAGMA foreign_keys = ON;');
|
||||
DB->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
||||
|
||||
date_default_timezone_set('UTC');
|
||||
|
||||
foreach (explode(',', preg_replace('/[A-Z0-9]|q=|;|-|\./', '', $_SERVER['HTTP_ACCEPT_LANGUAGE'] ?? '')) as $client_locale)
|
||||
if (in_array($client_locale, array_diff(scandir(ROOT_PATH . '/locales'), ['..', '.']), true)) {
|
||||
$locale = $client_locale;
|
||||
break;
|
||||
}
|
||||
define('LOCALE', $locale ?? 'en');
|
||||
putenv('LANG=C.UTF-8');
|
||||
setlocale(LC_MESSAGES, 'C.UTF-8');
|
||||
bindtextdomain('messages', ROOT_PATH . '/locales/' . LOCALE);
|
||||
header('Content-Language: ' . LOCALE);
|
||||
|
||||
const SERVICES_USER = ['reg', 'ns', 'ht'];
|
||||
|
||||
const LF = "\n";
|
||||
|
||||
const PLACEHOLDER_DOMAIN = 'example'; // From RFC2606: Reserved Top Level DNS Names > 2. TLDs for Testing, & Documentation Examples
|
||||
const PLACEHOLDER_IPV6 = '2001:db8::3'; // From RFC3849: IPv6 Address Prefix Reserved for Documentation
|
||||
const PLACEHOLDER_IPV4 = '203.0.113.42'; // From RFC5737: IPv4 Address Blocks Reserved for Documentation
|
||||
|
||||
foreach (array_diff(scandir(ROOT_PATH . '/fn'), ['..', '.']) as $file)
|
||||
require ROOT_PATH . '/fn/' . $file;
|
||||
|
||||
require ROOT_PATH . '/pages.php';
|
||||
|
||||
if ($_SERVER['REQUEST_URI'] === '/sftpgo-auth.php')
|
||||
return;
|
||||
require 'init.php';
|
||||
|
||||
$pageAddress = substr($_SERVER['REQUEST_URI'], strlen(CONF['common']['prefix']) + 1);
|
||||
if (strpos($pageAddress, '?') !== false) {
|
||||
|
@ -113,6 +64,9 @@ if (isset($_COOKIE[SESSION_COOKIE_NAME]))
|
|||
startSession(); // Resume session
|
||||
|
||||
if (isset($_SESSION['id'])) {
|
||||
if (!isset(query('select', 'users', ['id' => $_SESSION['id']], 'id')[0]))
|
||||
output(403, _('This account doesn\'t exist anymore. Log out to end this ghost session.'));
|
||||
|
||||
// Decrypt display username
|
||||
if (!isset($_COOKIE['display-username-decryption-key']))
|
||||
output(403, 'The display username decryption key has not been sent.');
|
||||
|
@ -157,12 +111,8 @@ if ($_POST !== []) {
|
|||
if (!in_array($_SERVER['HTTP_SEC_FETCH_SITE'], ['none', 'same-origin'], true))
|
||||
output(403, 'The <code>Sec-Fetch-Site</code> HTTP header must be <code>same-origin</code> or <code>none</code> when submitting a POST request to prevent Cross-Site Request Forgery (<abbr>CSRF</abbr>).');
|
||||
|
||||
if (PAGE_METADATA['require-login'] ?? true !== false) {
|
||||
if (isset($_SESSION['id']) !== true)
|
||||
output(403, _('You need to be logged in to do this.'));
|
||||
if (isset(query('select', 'users', ['id' => $_SESSION['id']], 'id')[0]) !== true)
|
||||
output(403, _('This account doesn\'t exist anymore. Log out to end this ghost session.'));
|
||||
}
|
||||
if (PAGE_METADATA['require-login'] ?? true AND !isset($_SESSION['id']))
|
||||
output(403, _('You need to be logged in to do this.'));
|
||||
|
||||
if (file_exists(ROOT_PATH . '/pg-act/' . PAGE_ADDRESS . '.php'))
|
||||
require ROOT_PATH . '/pg-act/' . PAGE_ADDRESS . '.php';
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
const DEBUG = false;
|
||||
!DEBUG or ob_start();
|
||||
|
||||
require 'router.php';
|
||||
require 'init.php';
|
||||
|
||||
function deny($reason) {
|
||||
!DEBUG or file_put_contents(ROOT_PATH . '/db/debug.txt', ob_get_contents() . $reason . LF);
|
||||
|
|
Loading…
Reference in a new issue