aboutsummaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorGravatar Dag <me@dvikan.no> 2024-01-25 18:20:02 +0100
committerGravatar GitHub <noreply@github.com> 2024-01-25 18:20:02 +0100
commite58c867a82f4c884eaeda93519ed8464d052837c (patch)
tree481e222f4b2f6987a89eae1f5ed8d2255d8223d4 /lib
parentd08d13f2c87b24fadb92e31c50dedc6e56c3c088 (diff)
downloadrss-bridge-e58c867a82f4c884eaeda93519ed8464d052837c.tar.gz
rss-bridge-e58c867a82f4c884eaeda93519ed8464d052837c.tar.zst
rss-bridge-e58c867a82f4c884eaeda93519ed8464d052837c.zip
feat: token authentication (#3927)
Diffstat (limited to 'lib')
-rw-r--r--lib/BridgeCard.php39
-rw-r--r--lib/RssBridge.php18
-rw-r--r--lib/http.php14
3 files changed, 49 insertions, 22 deletions
diff --git a/lib/BridgeCard.php b/lib/BridgeCard.php
index 6b812740..e5456f33 100644
--- a/lib/BridgeCard.php
+++ b/lib/BridgeCard.php
@@ -2,14 +2,7 @@
final class BridgeCard
{
- /**
- * Render bridge card
- *
- * @param class-string<BridgeAbstract> $bridgeClassName The bridge name
- * @param bool $isActive Indicates if the bridge is active or not
- * @return string The bridge card
- */
- public static function render($bridgeClassName, $isActive = true)
+ public static function render(string $bridgeClassName, Request $request): string
{
$bridgeFactory = new BridgeFactory();
@@ -47,19 +40,21 @@ final class BridgeCard
<h2><a href="{$uri}">{$name}</a></h2>
<p class="description">{$description}</p>
+
<input type="checkbox" class="showmore-box" id="showmore-{$bridgeClassName}" />
<label class="showmore" for="showmore-{$bridgeClassName}">Show more</label>
CARD;
- // If we don't have any parameter for the bridge, we print a generic form to load it.
+ $token = $request->attribute('token');
+
if (count($contexts) === 0) {
// The bridge has zero parameters
- $card .= self::renderForm($bridgeClassName, $isActive);
+ $card .= self::renderForm($bridgeClassName, '', [], $token);
} elseif (count($contexts) === 1 && array_key_exists('global', $contexts)) {
// The bridge has a single context with key 'global'
- $card .= self::renderForm($bridgeClassName, $isActive, '', $contexts['global']);
+ $card .= self::renderForm($bridgeClassName, '', $contexts['global'], $token);
} else {
// The bridge has one or more contexts (named or unnamed)
foreach ($contexts as $contextName => $contextParameters) {
@@ -77,7 +72,7 @@ final class BridgeCard
$card .= '<h5>' . $contextName . '</h5>' . PHP_EOL;
}
- $card .= self::renderForm($bridgeClassName, $isActive, $contextName, $contextParameters);
+ $card .= self::renderForm($bridgeClassName, $contextName, $contextParameters, $token);
}
}
@@ -99,17 +94,21 @@ final class BridgeCard
private static function renderForm(
string $bridgeClassName,
- bool $isActive = false,
- string $contextName = '',
- array $contextParameters = []
+ string $contextName,
+ array $contextParameters,
+ ?string $token
) {
$form = <<<EOD
- <form method="GET" action="?">
+ <form method="GET" action="?" class="bridge-form">
<input type="hidden" name="action" value="display" />
<input type="hidden" name="bridge" value="{$bridgeClassName}" />
-
EOD;
+ if ($token) {
+ // todo: maybe escape the token?
+ $form .= sprintf('<input type="hidden" name="token" value="%s" />', $token);
+ }
+
if (!empty($contextName)) {
$form .= sprintf('<input type="hidden" name="context" value="%s" />', $contextName);
}
@@ -167,11 +166,7 @@ final class BridgeCard
$form .= '</div>';
}
- if ($isActive) {
- $form .= '<button type="submit" name="format" formtarget="_blank" value="Html">Generate feed</button>';
- } else {
- $form .= '<span style="font-weight: bold;">Inactive</span>';
- }
+ $form .= '<button type="submit" name="format" formtarget="_blank" value="Html">Generate feed</button>';
return $form . '</form>' . PHP_EOL;
}
diff --git a/lib/RssBridge.php b/lib/RssBridge.php
index c8d11596..1bb5f5ea 100644
--- a/lib/RssBridge.php
+++ b/lib/RssBridge.php
@@ -47,6 +47,7 @@ final class RssBridge
]), 503);
}
+ // HTTP Basic auth check
if (Configuration::getConfig('authentication', 'enable')) {
if (Configuration::getConfig('authentication', 'password') === '') {
return new Response('The authentication password cannot be the empty string', 500);
@@ -71,6 +72,23 @@ final class RssBridge
// At this point the username and password was correct
}
+ // Add token as attribute to request
+ $request = $request->withAttribute('token', $request->get('token'));
+
+ // Token authentication check
+ if (Configuration::getConfig('authentication', 'token')) {
+ if (! $request->attribute('token')) {
+ return new Response(render(__DIR__ . '/../templates/token.html.php', [
+ 'message' => '',
+ ]), 401);
+ }
+ if (! hash_equals(Configuration::getConfig('authentication', 'token'), $request->attribute('token'))) {
+ return new Response(render(__DIR__ . '/../templates/token.html.php', [
+ 'message' => 'Invalid token',
+ ]), 401);
+ }
+ }
+
$action = $request->get('action', 'Frontpage');
$actionName = strtolower($action) . 'Action';
$actionName = implode(array_map('ucfirst', explode('-', $actionName)));
diff --git a/lib/http.php b/lib/http.php
index d53909b4..e4f9bf48 100644
--- a/lib/http.php
+++ b/lib/http.php
@@ -170,6 +170,7 @@ final class Request
{
private array $get;
private array $server;
+ private array $attributes;
private function __construct()
{
@@ -180,6 +181,7 @@ final class Request
$self = new self();
$self->get = $_GET;
$self->server = $_SERVER;
+ $self->attributes = [];
return $self;
}
@@ -200,6 +202,18 @@ final class Request
return $this->server[$key] ?? $default;
}
+ public function withAttribute(string $name, $value = true): self
+ {
+ $clone = clone $this;
+ $clone->attributes[$name] = $value;
+ return $clone;
+ }
+
+ public function attribute(string $key, $default = null)
+ {
+ return $this->attributes[$key] ?? $default;
+ }
+
public function toArray(): array
{
return $this->get;