Check config perms, validate boolean inputs
This commit is contained in:
parent
5a99441a0c
commit
fbce900475
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"name": "fwdekker/death-notifier",
|
"name": "fwdekker/death-notifier",
|
||||||
"description": "Get notified when a famous person dies.",
|
"description": "Get notified when a famous person dies.",
|
||||||
"version": "0.17.2", "_comment_version": "Also update version in `package.json`!",
|
"version": "0.17.3", "_comment_version": "Also update version in `package.json`!",
|
||||||
"type": "project",
|
"type": "project",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"homepage": "https://git.fwdekker.com/tools/death-notifier",
|
"homepage": "https://git.fwdekker.com/tools/death-notifier",
|
||||||
|
|
Binary file not shown.
Binary file not shown.
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "death-notifier",
|
"name": "death-notifier",
|
||||||
"version": "0.17.2", "_comment_version": "Also update version in `composer.json`!",
|
"version": "0.17.3", "_comment_version": "Also update version in `composer.json`!",
|
||||||
"description": "Get notified when a famous person dies.",
|
"description": "Get notified when a famous person dies.",
|
||||||
"author": "Florine W. Dekker",
|
"author": "Florine W. Dekker",
|
||||||
"browser": "dist/bundle.js",
|
"browser": "dist/bundle.js",
|
||||||
|
|
|
@ -3,33 +3,37 @@
|
||||||
# TODO: Add i18n
|
# TODO: Add i18n
|
||||||
|
|
||||||
[admin]
|
[admin]
|
||||||
# bcrypt hash of password to use the CLI of `api.php`. If set to its default value, or if empty, the CLI is disabled
|
# bcrypt hash of password to use the CLI of `api.php`. If set to its default value, or if empty, the CLI is disabled.
|
||||||
cli_secret = REPLACE THIS WITH A SECRET VALUE
|
cli_secret = REPLACE THIS WITH A SECRET VALUE
|
||||||
|
|
||||||
[database]
|
[database]
|
||||||
# Relative path to SQLite database
|
# Relative path to SQLite database.
|
||||||
filename = .death-notifier.db
|
filename = .death-notifier.db
|
||||||
|
|
||||||
[logger]
|
[logger]
|
||||||
# File to store logs in
|
# File to store logs in.
|
||||||
file = .death-notifier.log
|
file = .death-notifier.log
|
||||||
# Log level. See https://seldaek.github.io/monolog/doc/01-usage.html#log-levels
|
# Log level. See https://seldaek.github.io/monolog/doc/01-usage.html#log-levels
|
||||||
level = 200
|
level = 200
|
||||||
|
|
||||||
[mail]
|
[mail]
|
||||||
# Host name of SMTP server to send mail through
|
# Host name of SMTP server to send mail through.
|
||||||
host = TODO
|
host = TODO
|
||||||
# Port of SMTP server to send mail through
|
# Port of SMTP server to send mail through.
|
||||||
port = TODO
|
port = TODO
|
||||||
# Username to authenticate with at SMTP server
|
# Username to authenticate with at SMTP server.
|
||||||
username = TODO
|
username = TODO
|
||||||
# Password to authenticate with at SMTP server
|
# Password to authenticate with at SMTP server.
|
||||||
password = TODO
|
password = TODO
|
||||||
# Name to show to recipient
|
# Name to show to recipient.
|
||||||
from_name = TODO
|
from_name = TODO
|
||||||
|
|
||||||
|
[security]
|
||||||
|
# `true` if and only if insecure file permissions for config files should be tolerated.
|
||||||
|
allow_config_insecure_permissions = false
|
||||||
|
|
||||||
[server]
|
[server]
|
||||||
# The path to the main page. Going to this path in your browser should show the contents of `index.html`
|
# The path to the directory containing the site's main page.
|
||||||
base_path = https://example.com/death-notifier/
|
base_path = https://example.com/death-notifier/
|
||||||
# The message to display at the top of all pages. An empty string hides the message
|
# The message to display at the top of all pages. A blank string hides the message.
|
||||||
global_message =
|
global_message =
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
namespace com\fwdekker\deathnotifier;
|
namespace com\fwdekker\deathnotifier;
|
||||||
|
|
||||||
|
use Error;
|
||||||
use InvalidArgumentException;
|
use InvalidArgumentException;
|
||||||
|
|
||||||
|
|
||||||
|
@ -40,7 +41,6 @@ class Config
|
||||||
*/
|
*/
|
||||||
private static function read_config(): array
|
private static function read_config(): array
|
||||||
{
|
{
|
||||||
// TODO: Check permissions, return `null` if too permissive
|
|
||||||
$config = parse_ini_file("config.default.ini.php", process_sections: true, scanner_mode: INI_SCANNER_TYPED);
|
$config = parse_ini_file("config.default.ini.php", process_sections: true, scanner_mode: INI_SCANNER_TYPED);
|
||||||
if ($config === false) throw new InvalidArgumentException("Invalid `config.default.ini.php` file.");
|
if ($config === false) throw new InvalidArgumentException("Invalid `config.default.ini.php` file.");
|
||||||
|
|
||||||
|
@ -49,6 +49,20 @@ class Config
|
||||||
if ($config_custom === false) throw new InvalidArgumentException("Invalid `config.ini.php` file.");
|
if ($config_custom === false) throw new InvalidArgumentException("Invalid `config.ini.php` file.");
|
||||||
|
|
||||||
$config = array_replace_recursive($config, $config_custom);
|
$config = array_replace_recursive($config, $config_custom);
|
||||||
|
|
||||||
|
// Check file permissions
|
||||||
|
if (!$config["security"]["allow_config_insecure_permissions"]) {
|
||||||
|
$perms = fileperms("config.ini.php");
|
||||||
|
if ($perms === false)
|
||||||
|
throw new Error("Failed to read file permissions for `config.ini.php` even though the file exists.");
|
||||||
|
|
||||||
|
$perms = substr(decoct($perms), 3);
|
||||||
|
if ($perms !== "600")
|
||||||
|
throw new InvalidArgumentException(
|
||||||
|
"Insecure file permissions for `config.ini.php`. " .
|
||||||
|
"Should be 600 but was $perms."
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return $config;
|
return $config;
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
namespace com\fwdekker\deathnotifier\user;
|
namespace com\fwdekker\deathnotifier\user;
|
||||||
|
|
||||||
use com\fwdekker\deathnotifier\Action;
|
use com\fwdekker\deathnotifier\Action;
|
||||||
|
use com\fwdekker\deathnotifier\validator\IsBooleanRule;
|
||||||
use com\fwdekker\deathnotifier\validator\IsValidCsrfTokenRule;
|
use com\fwdekker\deathnotifier\validator\IsValidCsrfTokenRule;
|
||||||
use com\fwdekker\deathnotifier\UnexpectedException;
|
use com\fwdekker\deathnotifier\UnexpectedException;
|
||||||
use com\fwdekker\deathnotifier\validator\IsSetRule;
|
use com\fwdekker\deathnotifier\validator\IsSetRule;
|
||||||
|
@ -51,18 +52,17 @@ class ToggleNotificationsAction extends Action
|
||||||
(new SessionRuleSet(validate_logged_in: true))->check($_SESSION);
|
(new SessionRuleSet(validate_logged_in: true))->check($_SESSION);
|
||||||
(new RuleSet([
|
(new RuleSet([
|
||||||
"token" => [new IsValidCsrfTokenRule()],
|
"token" => [new IsValidCsrfTokenRule()],
|
||||||
// TODO: Do we need an IsBooleanRule? (check the `== true` below)
|
"enable_notifications" => [new IsBooleanRule()],
|
||||||
"enable_notifications" => [new IsSetRule()],
|
|
||||||
]))->check($inputs);
|
]))->check($inputs);
|
||||||
|
|
||||||
$this->user_list->transaction(function () use ($inputs) {
|
$this->user_list->transaction(function () use ($inputs) {
|
||||||
$user_data = $this->user_list->get_user_by_uuid($_SESSION["uuid"]);
|
$user_data = $this->user_list->get_user_by_uuid($_SESSION["uuid"]);
|
||||||
if ($user_data === null)
|
if ($user_data === null)
|
||||||
throw new UnexpectedException("Failed to retrieve user data. Refresh the page and try again.");
|
throw new UnexpectedException("Failed to retrieve user data. Refresh the page and try again.");
|
||||||
if ($user_data["email_verification_token"] === null)
|
if ($inputs["enable_notifications"] && $user_data["email_verification_token"] !== null)
|
||||||
throw new InvalidInputException("Please verify your email address before enabling notifications.");
|
throw new InvalidInputException("Please verify your email address before toggling notifications.");
|
||||||
|
|
||||||
$this->user_list->set_notifications_enabled($_SESSION["uuid"], $inputs["enable_notifications"] == true);
|
$this->user_list->set_notifications_enabled($_SESSION["uuid"], $inputs["enable_notifications"]);
|
||||||
});
|
});
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
|
|
@ -0,0 +1,27 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace com\fwdekker\deathnotifier\validator;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validates that the input is a boolean.
|
||||||
|
*/
|
||||||
|
class IsBooleanRule extends Rule
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Validates that the input is a boolean.
|
||||||
|
*
|
||||||
|
* @param array<int|string, mixed> $inputs the list of inputs in which the value at {@see $key} should be checked
|
||||||
|
* @param string $key the key in {@see $inputs} of the input to check
|
||||||
|
* @return void if the checked input is a boolean
|
||||||
|
* @throws InvalidInputException if the checked input is not a boolean
|
||||||
|
*/
|
||||||
|
public function check(array $inputs, string $key): void
|
||||||
|
{
|
||||||
|
if (!isset($inputs[$key]) || !is_bool($inputs[$key]))
|
||||||
|
throw new InvalidInputException(
|
||||||
|
$this->override_message ?? "Field '" . htmlentities($key) . "' must be a boolean.",
|
||||||
|
$key
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue