From c7b16397101fd534c41ede5ae7c55e7e120c06f7 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Isma=C3=ABl=20Bouya?= Date: Thu, 29 Dec 2022 08:00:22 +0100 Subject: [PATCH] Switch to colemna --- flakes/backports/flake.lock | 12 +- flakes/backports/flake.nix | 4 + modules/private/system.nix | 10 +- modules/private/system/backup-2.nix | 1 - modules/private/system/dilion.nix | 1 - modules/private/system/eldiron.nix | 1 - modules/private/system/monitoring-1.nix | 1 - modules/private/system/quatresaisons.nix | 1 - .../private/websites/tools/tools/shaarli.nix | 2 +- nixops/Makefile | 18 +- nixops/default.nix | 8 +- nixops/scripts/with_env | 5 + overlays/shaarli/default.nix | 14 +- pkgs/shaarli/default.nix | 24 + pkgs/shaarli/shaarli_ldap.patch | 425 ++++++++++++++++++ shell.nix | 2 +- 16 files changed, 483 insertions(+), 46 deletions(-) create mode 100644 pkgs/shaarli/default.nix create mode 100644 pkgs/shaarli/shaarli_ldap.patch diff --git a/flakes/backports/flake.lock b/flakes/backports/flake.lock index a16b090..b5ecf00 100644 --- a/flakes/backports/flake.lock +++ b/flakes/backports/flake.lock @@ -2,11 +2,11 @@ "nodes": { "flake-utils": { "locked": { - "lastModified": 1659877975, - "narHash": "sha256-zllb8aq3YO3h8B/U0/J1WBgAL8EX5yWf5pMj3G0NAmc=", + "lastModified": 1667395993, + "narHash": "sha256-nuEHfE/LcWyuSWnS8t12N1wc105Qtau+/OdUAjtQ0rA=", "owner": "numtide", "repo": "flake-utils", - "rev": "c0e246b9b83f637f4681389ecabcb2681b4f3af0", + "rev": "5aed5285a952e0b949eb3ba02c12fa4fcfef535f", "type": "github" }, "original": { @@ -17,11 +17,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1663087123, - "narHash": "sha256-cNIRkF/J4mRxDtNYw+9/fBNq/NOA2nCuPOa3EdIyeDs=", + "lastModified": 1672080458, + "narHash": "sha256-Ukjn8YUwZevxDPaVUmTx2sf9bCcIJSasmLz+xjGBKrs=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "9608ace7009ce5bc3aeb940095e01553e635cbc7", + "rev": "1eb875e811dd59e21e77f6337f2c1592889b48b3", "type": "github" }, "original": { diff --git a/flakes/backports/flake.nix b/flakes/backports/flake.nix index 463639f..d23a7ae 100644 --- a/flakes/backports/flake.nix +++ b/flakes/backports/flake.nix @@ -13,6 +13,7 @@ khard = pkgs.khard; khal = pkgs.khal; go-task = pkgs.go-task; + colmena = pkgs.colmena; }; legacyPackages = packages; apps = { @@ -20,6 +21,7 @@ khard = flake-utils.lib.mkApp { drv = packages.khard; name = "khard"; }; khal = flake-utils.lib.mkApp { drv = packages.khal; name = "khal"; }; go-task = flake-utils.lib.mkApp { drv = packages.go-task; name = "go-task"; }; + colmena = flake-utils.lib.mkApp { drv = packages.colmena; name = "colmena"; }; }; } ) // rec { @@ -33,12 +35,14 @@ khard = final: prev: { khard = self.packages."${final.system}".khard; }; khal = final: prev: { khal = self.packages."${final.system}".khal; }; go-task = final: prev: { go-task = self.packages."${final.system}".go-task; }; + colmena = final: prev: { colmena = self.packages."${final.system}".colmena; }; }; overlay = final: prev: ({} // overlays.ntfy-sh final prev // overlays.khard final prev // overlays.khal final prev // overlays.go-task final prev + // overlays.colmena final prev ); nixosModules = { diff --git a/modules/private/system.nix b/modules/private/system.nix index d0943ab..1eea0f3 100644 --- a/modules/private/system.nix +++ b/modules/private/system.nix @@ -1,11 +1,11 @@ { pkgs, lib, config, name, nodes, ... }: { config = { - deployment.secrets."secret_vars.yml" = { - source = builtins.toString ../../nixops/secrets/vars.yml; - destination = config.secrets.secretsVars; - owner.user = "root"; - owner.group = "root"; + networking.hostName = name; + deployment.keys."vars.yml" = { + keyFile = builtins.toString ../../nixops/secrets/vars.yml; + user = "root"; + group = "root"; permissions = "0400"; }; diff --git a/modules/private/system/backup-2.nix b/modules/private/system/backup-2.nix index 83caf68..4510574 100644 --- a/modules/private/system/backup-2.nix +++ b/modules/private/system/backup-2.nix @@ -3,7 +3,6 @@ deployment = { targetUser = "root"; targetHost = lib.head config.hostEnv.ips.main.ip4; - substituteOnDestination = true; }; # ssh-keyscan backup-2 | nix-shell -p ssh-to-age --run ssh-to-age secrets.ageKeys = [ "age1kk3nr27qu42j28mcfdag5lhq0zu2pky7gfanvne8l4z2ctevjpgskmw0sr" ]; diff --git a/modules/private/system/dilion.nix b/modules/private/system/dilion.nix index 569c088..1b51682 100644 --- a/modules/private/system/dilion.nix +++ b/modules/private/system/dilion.nix @@ -3,7 +3,6 @@ deployment = { targetUser = "root"; targetHost = lib.head config.hostEnv.ips.main.ip4; - substituteOnDestination = true; }; # ssh-keyscan dilion | nix-shell -p ssh-to-age --run ssh-to-age secrets.ageKeys = [ "age1x49n6qa0arkdpq8530s7umgm0gqkq90exv4jep97q30rfnzknpaqate06a" ]; diff --git a/modules/private/system/eldiron.nix b/modules/private/system/eldiron.nix index 6ae3875..091c9f4 100644 --- a/modules/private/system/eldiron.nix +++ b/modules/private/system/eldiron.nix @@ -3,7 +3,6 @@ deployment = { targetUser = "root"; targetHost = lib.head config.hostEnv.ips.main.ip4; - substituteOnDestination = true; }; # ssh-keyscan eldiron | nix-shell -p ssh-to-age --run ssh-to-age secrets.ageKeys = [ "age1dxr5lhvtnjssfaqpnf6qx80h8gfwkxg3tdf35m6n9wljmk7wadfs3kmahj" ]; diff --git a/modules/private/system/monitoring-1.nix b/modules/private/system/monitoring-1.nix index c45835f..71eee33 100644 --- a/modules/private/system/monitoring-1.nix +++ b/modules/private/system/monitoring-1.nix @@ -3,7 +3,6 @@ deployment = { targetUser = "root"; targetHost = lib.head config.hostEnv.ips.main.ip4; - substituteOnDestination = true; }; # ssh-keyscan monitoring-1 | nix-shell -p ssh-to-age --run ssh-to-age secrets.ageKeys = [ "age1dn4lzhgxusqrpjjnzm7w8ml39ptf326htuzmpqdqs2gg3wq7cqzqxuvx8k" ]; diff --git a/modules/private/system/quatresaisons.nix b/modules/private/system/quatresaisons.nix index ed6f129..2938a34 100644 --- a/modules/private/system/quatresaisons.nix +++ b/modules/private/system/quatresaisons.nix @@ -200,7 +200,6 @@ in deployment = { targetUser = "root"; targetHost = lib.head config.hostEnv.ips.main.ip4; - substituteOnDestination = true; }; # ssh-keyscan quatresaison | nix-shell -p ssh-to-age --run ssh-to-age secrets.ageKeys = [ "age1yz8u6xvh2fltvyp96ep8crce3qx4tuceyhun6pwddfe0uvcrkarscxl7e7" ]; diff --git a/modules/private/websites/tools/tools/shaarli.nix b/modules/private/websites/tools/tools/shaarli.nix index e7f106c..79fbbc9 100644 --- a/modules/private/websites/tools/tools/shaarli.nix +++ b/modules/private/websites/tools/tools/shaarli.nix @@ -6,7 +6,7 @@ in rec { install -m 0755 -o ${apache.user} -g ${apache.group} -d ${varDir} \ ${varDir}/cache ${varDir}/pagecache ${varDir}/tmp ${varDir}/data ''; - webRoot = shaarli varDir; + webRoot = shaarli.override { inherit varDir; }; apache = rec { user = "wwwrun"; group = "wwwrun"; diff --git a/nixops/Makefile b/nixops/Makefile index fb9da4c..11c5a0f 100644 --- a/nixops/Makefile +++ b/nixops/Makefile @@ -18,6 +18,8 @@ setup: ###### Morph regular tasks PROFILE=/nix/var/nix/profiles/per-user/immae/morph/immaeEu TARGET ?= +COMMON_COLEMNA_ARGS = -v +#Only enabled in colemna 0.4: --nix-option allow-unsafe-native-code-during-evaluation true --nix-option allow-import-from-derivation true --nix-option substituters https://cache.nixos.org/ MORPH_ARGS ?= ifdef TARGET # multiple targets: --on="{machine1,machine2}" (works with * glob too) @@ -51,27 +53,27 @@ ssh-4c: ./scripts/with_env bash -c 'ssh -i $$SSH_IDENTITY_FILE root@quatresaisons $(SSH_ARGS)' debug: - ./scripts/with_env morph build --show-trace default.nix $(MORPH_ARGS) + ./scripts/with_env colmena build $(COMMON_COLEMNA_ARGS) --show-trace -f default.nix $(MORPH_ARGS) build: - ./scripts/with_env morph build default.nix $(MORPH_ARGS) + ./scripts/with_env colmena build $(COMMON_COLEMNA_ARGS) -f default.nix $(MORPH_ARGS) -dry-run: - ./scripts/with_env morph build --dry-run default.nix $(MORPH_ARGS) +#dry-run: +# ./scripts/with_env morph build -v --dry-run default.nix $(MORPH_ARGS) upload: - ./scripts/with_env morph push default.nix $(MORPH_ARGS) + ./scripts/with_env colmena apply $(COMMON_COLEMNA_ARGS) push -f default.nix $(MORPH_ARGS) deploy: - ./scripts/with_env morph deploy default.nix switch --keep-result --upload-secrets $(MORPH_ARGS) + ./scripts/with_env colmena apply $(COMMON_COLEMNA_ARGS) switch -f default.nix --keep-result $(MORPH_ARGS) nix-env -p $(PROFILE) --set .gcroots/default.nix next-boot: - ./scripts/with_env morph deploy default.nix boot --keep-result --upload-secrets $(MORPH_ARGS) + ./scripts/with_env colmena apply -v boot -f default.nix --keep-result $(MORPH_ARGS) nix-env -p $(PROFILE) --set .gcroots/default.nix deploy-reboot: - ./scripts/with_env morph deploy default.nix boot --reboot --upload-secrets $(MORPH_ARGS) + ./scripts/with_env colmena apply $(COMMON_COLEMNA_ARGS) boot -f default.nix --reboot $(MORPH_ARGS) .PHONY: ssh-eldiron ssh-dilion ssh-monitoring-1 ssh-backup-2 debug build upload deploy deploy-reboot diff --git a/nixops/default.nix b/nixops/default.nix index d2c446d..96296df 100644 --- a/nixops/default.nix +++ b/nixops/default.nix @@ -1,11 +1,5 @@ { - network = { - nixConfig = { - substituters = "https://cache.nixos.org/"; - allow-unsafe-native-code-during-evaluation = "true"; - allow-import-from-derivation = "true"; - }; - }; + meta.nixpkgs = import { overlays = []; }; dilion = import ../modules/private/system/dilion.nix; eldiron = import ../modules/private/system/eldiron.nix; backup-2 = import ../modules/private/system/backup-2.nix; diff --git a/nixops/scripts/with_env b/nixops/scripts/with_env index c570ccf..8bc26ed 100755 --- a/nixops/scripts/with_env +++ b/nixops/scripts/with_env @@ -17,6 +17,11 @@ trap finish EXIT sops -d secrets/vars.yml | yq -r .ssl_keys.nix_repository > $TEMP/id_ed25519 +cat > $TEMP/ssh_config < +Date: Sun Feb 3 20:58:18 2019 +0100 + + Add ldap connection + +diff --git a/.htaccess b/.htaccess +index 4c00427..5acd708 100644 +--- a/.htaccess ++++ b/.htaccess +@@ -6,10 +6,23 @@ RewriteEngine On + # Prevent accessing subdirectories not managed by SCM + RewriteRule ^(.git|doxygen|vendor) - [F] + ++RewriteCond %{REQUEST_URI}::$1 ^(/.+)/(.*)::\2$ ++RewriteRule ^(.*) - [E=BASE:%1] ++ ++RewriteCond %{ENV:REDIRECT_BASE} (.+) ++RewriteRule .* - [E=BASE:%1] ++ + # Forward the "Authorization" HTTP header + RewriteCond %{HTTP:Authorization} ^(.*) + RewriteRule .* - [e=HTTP_AUTHORIZATION:%1] + ++RewriteCond %{REQUEST_FILENAME} !-f ++RewriteCond %{REQUEST_FILENAME} !-d ++RewriteRule ^((?!api/)[^/]*)/?(.*)$ $2?%{QUERY_STRING} [E=USERSPACE:$1] ++ ++RewriteCond %{ENV:REDIRECT_USERSPACE} (.+) ++RewriteRule .* - [E=USERSPACE:%1] ++ + # REST API + RewriteCond %{REQUEST_FILENAME} !-f + RewriteCond %{REQUEST_FILENAME} !-d +diff --git a/application/ApplicationUtils.php b/application/ApplicationUtils.php +index 911873a..f21a1ef 100644 +--- a/application/ApplicationUtils.php ++++ b/application/ApplicationUtils.php +@@ -191,6 +191,9 @@ public static function checkResourcePermissions($conf) + $conf->get('resource.page_cache'), + $conf->get('resource.raintpl_tmp'), + ) as $path) { ++ if (! is_dir($path)) { ++ mkdir($path, 0755, true); ++ } + if (! is_readable(realpath($path))) { + $errors[] = '"'.$path.'" '. t('directory is not readable'); + } +diff --git a/application/config/ConfigManager.php b/application/config/ConfigManager.php +index 32aaea4..99efc15 100644 +--- a/application/config/ConfigManager.php ++++ b/application/config/ConfigManager.php +@@ -21,6 +21,11 @@ class ConfigManager + + public static $DEFAULT_PLUGINS = array('qrcode'); + ++ /** ++ * @var string User space. ++ */ ++ protected $userSpace; ++ + /** + * @var string Config folder. + */ +@@ -41,12 +46,36 @@ class ConfigManager + * + * @param string $configFile Configuration file path without extension. + */ +- public function __construct($configFile = 'data/config') ++ public function __construct($configFile = null, $userSpace = null) + { +- $this->configFile = $configFile; ++ $this->userSpace = $this->findLDAPUser($userSpace); ++ if ($configFile !== null) { ++ $this->configFile = $configFile; ++ } else { ++ $this->configFile = ($this->userSpace === null) ? 'data/config' : 'data/' . $this->userSpace . '/config'; ++ } + $this->initialize(); + } + ++ public function findLDAPUser($login, $password = null) { ++ $connect = ldap_connect(getenv('SHAARLI_LDAP_HOST')); ++ ldap_set_option($connect, LDAP_OPT_PROTOCOL_VERSION, 3); ++ if (!$connect || !ldap_bind($connect, getenv('SHAARLI_LDAP_DN'), getenv('SHAARLI_LDAP_PASSWORD'))) { ++ return false; ++ } ++ ++ $search_query = str_replace('%login%', ldap_escape($login), getenv('SHAARLI_LDAP_FILTER')); ++ ++ $search = ldap_search($connect, getenv('SHAARLI_LDAP_BASE'), $search_query); ++ $info = ldap_get_entries($connect, $search); ++ ++ if (ldap_count_entries($connect, $search) == 1 && (is_null($password) || ldap_bind($connect, $info[0]["dn"], $password))) { ++ return $login; ++ } else { ++ return null; ++ } ++ } ++ + /** + * Reset the ConfigManager instance. + */ +@@ -269,6 +298,16 @@ public function getConfigFileExt() + return $this->configFile . $this->configIO->getExtension(); + } + ++ /** ++ * Get the current userspace. ++ * ++ * @return mixed User space. ++ */ ++ public function getUserSpace() ++ { ++ return $this->userSpace; ++ } ++ + /** + * Recursive function which find asked setting in the loaded config. + * +@@ -342,19 +381,31 @@ protected static function removeConfig($settings, &$conf) + */ + protected function setDefaultValues() + { +- $this->setEmpty('resource.data_dir', 'data'); +- $this->setEmpty('resource.config', 'data/config.php'); +- $this->setEmpty('resource.datastore', 'data/datastore.php'); +- $this->setEmpty('resource.ban_file', 'data/ipbans.php'); +- $this->setEmpty('resource.updates', 'data/updates.txt'); +- $this->setEmpty('resource.log', 'data/log.txt'); +- $this->setEmpty('resource.update_check', 'data/lastupdatecheck.txt'); +- $this->setEmpty('resource.history', 'data/history.php'); ++ if ($this->userSpace === null) { ++ $data = 'data'; ++ $tmp = 'tmp'; ++ $cache = 'cache'; ++ $pagecache = 'pagecache'; ++ } else { ++ $data = 'data/' . ($this->userSpace); ++ $tmp = 'tmp/' . ($this->userSpace); ++ $cache = 'cache/' . ($this->userSpace); ++ $pagecache = 'pagecache/' . ($this->userSpace); ++ } ++ ++ $this->setEmpty('resource.data_dir', $data); ++ $this->setEmpty('resource.config', $data . '/config.php'); ++ $this->setEmpty('resource.datastore', $data . '/datastore.php'); ++ $this->setEmpty('resource.ban_file', $data . '/ipbans.php'); ++ $this->setEmpty('resource.updates', $data . '/updates.txt'); ++ $this->setEmpty('resource.log', $data . '/log.txt'); ++ $this->setEmpty('resource.update_check', $data . '/lastupdatecheck.txt'); ++ $this->setEmpty('resource.history', $data . '/history.php'); + $this->setEmpty('resource.raintpl_tpl', 'tpl/'); + $this->setEmpty('resource.theme', 'default'); +- $this->setEmpty('resource.raintpl_tmp', 'tmp/'); +- $this->setEmpty('resource.thumbnails_cache', 'cache'); +- $this->setEmpty('resource.page_cache', 'pagecache'); ++ $this->setEmpty('resource.raintpl_tmp', $tmp); ++ $this->setEmpty('resource.thumbnails_cache', $cache); ++ $this->setEmpty('resource.page_cache', $pagecache); + + $this->setEmpty('security.ban_after', 4); + $this->setEmpty('security.ban_duration', 1800); +diff --git a/application/security/LoginManager.php b/application/security/LoginManager.php +index d6784d6..bdfaca7 100644 +--- a/application/security/LoginManager.php ++++ b/application/security/LoginManager.php +@@ -32,6 +32,9 @@ class LoginManager + /** @var string User sign-in token depending on remote IP and credentials */ + protected $staySignedInToken = ''; + ++ protected $lastErrorReason = ''; ++ protected $lastErrorIsBanishable = false; ++ + /** + * Constructor + * +@@ -83,7 +86,7 @@ public function getStaySignedInToken() + */ + public function checkLoginState($cookie, $clientIpId) + { +- if (! $this->configManager->exists('credentials.login')) { ++ if (! $this->configManager->exists('credentials.login') || (isset($_SESSION['username']) && $_SESSION['username'] && $this->configManager->get('credentials.login') !== $_SESSION['username'])) { + // Shaarli is not configured yet + $this->isLoggedIn = false; + return; +@@ -133,20 +136,40 @@ public function isLoggedIn() + */ + public function checkCredentials($remoteIp, $clientIpId, $login, $password) + { +- $hash = sha1($password . $login . $this->configManager->get('credentials.salt')); ++ $this->lastErrorIsBanishable = false; ++ ++ if ($this->configManager->getUserSpace() !== null && $this->configManager->getUserSpace() !== $login) { ++ logm($this->configManager->get('resource.log'), ++ $remoteIp, ++ 'Trying to login to wrong user space'); ++ $this->lastErrorReason = 'You’re trying to access the wrong account.'; ++ return false; ++ } + +- if ($login != $this->configManager->get('credentials.login') +- || $hash != $this->configManager->get('credentials.hash') +- ) { ++ logm($this->configManager->get('resource.log'), ++ $remoteIp, ++ 'Trying LDAP connection'); ++ $result = $this->configManager->findLDAPUser($login, $password); ++ if ($result === false) { + logm( + $this->configManager->get('resource.log'), + $remoteIp, +- 'Login failed for user ' . $login ++ 'Impossible to connect to LDAP' + ); ++ $this->lastErrorReason = 'Server error.'; ++ return false; ++ } else if (is_null($result)) { ++ logm( ++ $this->configManager->get('resource.log'), ++ $remoteIp, ++ 'Login failed for user ' . $login ++ ); ++ $this->lastErrorIsBanishable = true; ++ $this->lastErrorReason = 'Wrong login/password.'; + return false; + } + +- $this->sessionManager->storeLoginInfo($clientIpId); ++ $this->sessionManager->storeLoginInfo($clientIpId, $login); + logm( + $this->configManager->get('resource.log'), + $remoteIp, +@@ -187,6 +210,10 @@ protected function writeBanFile() + */ + public function handleFailedLogin($server) + { ++ if (!$this->lastErrorIsBanishable) { ++ return $this->lastErrorReason ?: 'Error during login.'; ++ }; ++ + $ip = $server['REMOTE_ADDR']; + $trusted = $this->configManager->get('security.trusted_proxies', []); + +@@ -215,6 +242,7 @@ public function handleFailedLogin($server) + ); + } + $this->writeBanFile(); ++ return $this->lastErrorReason ?: 'Error during login.'; + } + + /** +diff --git a/application/security/SessionManager.php b/application/security/SessionManager.php +index b8b8ab8..5eb4aac 100644 +--- a/application/security/SessionManager.php ++++ b/application/security/SessionManager.php +@@ -111,10 +111,10 @@ public static function checkId($sessionId) + * + * @param string $clientIpId Client IP address identifier + */ +- public function storeLoginInfo($clientIpId) ++ public function storeLoginInfo($clientIpId, $login = null) + { + $this->session['ip'] = $clientIpId; +- $this->session['username'] = $this->conf->get('credentials.login'); ++ $this->session['username'] = $login ?: $this->conf->get('credentials.login'); + $this->extendTimeValidityBy(self::$SHORT_TIMEOUT); + } + +diff --git a/index.php b/index.php +index 4b86a3e..58ae2dd 100644 +--- a/index.php ++++ b/index.php +@@ -121,7 +121,32 @@ + $_COOKIE['shaarli'] = session_id(); + } + +-$conf = new ConfigManager(); ++$folderBase = getenv("BASE"); ++ ++if (getenv("USERSPACE")) { ++ if (isset($_GET["do"]) && $_GET["do"] == "login") { ++ header("Location: $folderBase/?do=login"); ++ exit; ++ } ++ $userspace = preg_replace("/[^-_A-Za-z0-9]/", '', getenv("USERSPACE")); ++} else if (isset($_SESSION["username"]) && $_SESSION["username"]) { ++ header("Location: " . $folderBase . "/" . $_SESSION["username"] . "?"); ++ exit; ++} else if (!isset($_GET["do"]) || $_GET["do"] != "login") { ++ header("Location: $folderBase/?do=login"); ++ exit; ++} ++ ++if (!isset($userspace) && isset($_POST["login"])) { ++ $userspace = preg_replace("/[^-_A-Za-z0-9]/", '', $_POST["login"]); ++ error_log("debugImmae: setting userspace from POST: " . $userspace); ++} ++ ++if (isset($userspace)) { ++ $conf = new ConfigManager(null, $userspace); ++} else { ++ $conf = new ConfigManager(); ++} + $sessionManager = new SessionManager($_SESSION, $conf); + $loginManager = new LoginManager($GLOBALS, $conf, $sessionManager); + $loginManager->generateStaySignedInToken($_SERVER['REMOTE_ADDR']); +@@ -175,7 +200,7 @@ + } + + // Display the installation form if no existing config is found +- install($conf, $sessionManager, $loginManager); ++ install($conf, $sessionManager, $loginManager, $userspace); + } + + $loginManager->checkLoginState($_COOKIE, $clientIpId); +@@ -205,6 +230,7 @@ function isLoggedIn() + && $loginManager->checkCredentials($_SERVER['REMOTE_ADDR'], $clientIpId, $_POST['login'], $_POST['password']) + ) { + $loginManager->handleSuccessfulLogin($_SERVER); ++ $userspace = $_POST['login']; + + $cookiedir = ''; + if (dirname($_SERVER['SCRIPT_NAME']) != '/') { +@@ -241,25 +267,25 @@ function isLoggedIn() + $uri .= '&'.$param.'='.urlencode($_GET[$param]); + } + } +- header('Location: '. $uri); ++ header('Location: '. $userspace . $uri); + exit; + } + + if (isset($_GET['edit_link'])) { +- header('Location: ?edit_link='. escape($_GET['edit_link'])); ++ header('Location: ' . $userspace . '?edit_link='. escape($_GET['edit_link'])); + exit; + } + + if (isset($_POST['returnurl'])) { + // Prevent loops over login screen. + if (strpos($_POST['returnurl'], 'do=login') === false) { +- header('Location: '. generateLocation($_POST['returnurl'], $_SERVER['HTTP_HOST'])); ++ header('Location: ' . generateLocation($_POST['returnurl'], $_SERVER['HTTP_HOST'])); + exit; + } + } +- header('Location: ?'); exit; ++ header('Location: '. $userspace . '?'); exit; + } else { +- $loginManager->handleFailedLogin($_SERVER); ++ $errorReason = $loginManager->handleFailedLogin($_SERVER); + $redir = '&username='. urlencode($_POST['login']); + if (isset($_GET['post'])) { + $redir .= '&post=' . urlencode($_GET['post']); +@@ -270,7 +296,7 @@ function isLoggedIn() + } + } + // Redirect to login screen. +- echo ''; ++ echo ''; + exit; + } + } +@@ -1719,7 +1745,7 @@ function buildLinkList($PAGE, $LINKSDB, $conf, $pluginManager, $loginManager) + * @param SessionManager $sessionManager SessionManager instance + * @param LoginManager $loginManager LoginManager instance + */ +-function install($conf, $sessionManager, $loginManager) { ++function install($conf, $sessionManager, $loginManager, $userspace) { + // On free.fr host, make sure the /sessions directory exists, otherwise login will not work. + if (endsWith($_SERVER['HTTP_HOST'],'.free.fr') && !is_dir($_SERVER['DOCUMENT_ROOT'].'/sessions')) mkdir($_SERVER['DOCUMENT_ROOT'].'/sessions',0705); + +@@ -1755,7 +1781,7 @@ function install($conf, $sessionManager, $loginManager) { + } + + +- if (!empty($_POST['setlogin']) && !empty($_POST['setpassword'])) ++ if (true) + { + $tz = 'UTC'; + if (!empty($_POST['continent']) && !empty($_POST['city']) +@@ -1764,15 +1790,15 @@ function install($conf, $sessionManager, $loginManager) { + $tz = $_POST['continent'].'/'.$_POST['city']; + } + $conf->set('general.timezone', $tz); +- $login = $_POST['setlogin']; +- $conf->set('credentials.login', $login); ++ $conf->set('credentials.login', $userspace); + $salt = sha1(uniqid('', true) .'_'. mt_rand()); + $conf->set('credentials.salt', $salt); +- $conf->set('credentials.hash', sha1($_POST['setpassword'] . $login . $salt)); ++ $hash = sha1(uniqid('', true) .'_'. mt_rand()); ++ $conf->set('credentials.hash', $hash); + if (!empty($_POST['title'])) { + $conf->set('general.title', escape($_POST['title'])); + } else { +- $conf->set('general.title', 'Shared links on '.escape(index_url($_SERVER))); ++ $conf->set('general.title', ucwords(str_replace("_", " ", $userspace))); + } + $conf->set('translation.language', escape($_POST['language'])); + $conf->set('updates.check_updates', !empty($_POST['updateCheck'])); +@@ -1841,7 +1867,12 @@ function install($conf, $sessionManager, $loginManager) { + $app = new \Slim\App($container); + + // REST API routes +-$app->group('/api/v1', function() { ++if (isset($userspace)) { ++ $mountpoint = '/' . $userspace . '/api/v1'; ++} else { ++ $mountpoint = '/api/v1'; ++} ++$app->group($mountpoint, function() { + $this->get('/info', '\Shaarli\Api\Controllers\Info:getInfo')->setName('getInfo'); + $this->get('/links', '\Shaarli\Api\Controllers\Links:getLinks')->setName('getLinks'); + $this->get('/links/{id:[\d]+}', '\Shaarli\Api\Controllers\Links:getLink')->setName('getLink'); +@@ -1860,7 +1891,7 @@ function install($conf, $sessionManager, $loginManager) { + $response = $app->run(true); + // Hack to make Slim and Shaarli router work together: + // If a Slim route isn't found and NOT API call, we call renderPage(). +-if ($response->getStatusCode() == 404 && strpos($_SERVER['REQUEST_URI'], '/api/v1') === false) { ++if ($response->getStatusCode() == 404 && strpos($_SERVER['REQUEST_URI'], $mountpoint) === false) { + // We use UTF-8 for proper international characters handling. + header('Content-Type: text/html; charset=utf-8'); + renderPage($conf, $pluginManager, $linkDb, $history, $sessionManager, $loginManager); diff --git a/shell.nix b/shell.nix index 5da8687..0512c54 100644 --- a/shell.nix +++ b/shell.nix @@ -1,4 +1,4 @@ { pkgs ? import { overlays = builtins.attrValues (import ./overlays); } }: pkgs.mkShell { - buildInputs = [ pkgs.nixUnstable pkgs.python3 pkgs.sops pkgs.morph pkgs.niv pkgs.curl pkgs.shellcheck pkgs.jq pkgs.gnumake pkgs.yq ]; + buildInputs = [ pkgs.nixUnstable pkgs.python3 pkgs.colmena pkgs.sops pkgs.morph pkgs.niv pkgs.curl pkgs.shellcheck pkgs.jq pkgs.gnumake pkgs.yq ]; } -- 2.41.0