diff options
| author | winter | 2024-12-14 21:24:48 +0000 |
|---|---|---|
| committer | winter | 2024-12-14 21:45:59 +0000 |
| commit | 2f4c1de3509141880df019ef1a6cc65981b3f6ea (patch) | |
| tree | 0021f3a860d6c80869d4af5d366393f1d0eea288 /Digitigrade | |
| parent | 0c6497ff02b8118bcefee37cc6fe0cbc02df4bb4 (diff) | |
implement outbound auth? untested
untested because i don't have another instance to test it against ....
Diffstat (limited to 'Digitigrade')
| -rw-r--r-- | Digitigrade/Job.php | 2 | ||||
| -rw-r--r-- | Digitigrade/Job/RefreshOutboundAuthToken.php | 29 | ||||
| -rw-r--r-- | Digitigrade/Job/RequestOutboundAuthToken.php | 28 | ||||
| -rw-r--r-- | Digitigrade/Job/UpdateInstanceInfo.php | 5 | ||||
| -rw-r--r-- | Digitigrade/JobQueue.php | 2 | ||||
| -rw-r--r-- | Digitigrade/Model.php | 7 | ||||
| -rw-r--r-- | Digitigrade/Model/FetchableModel.php | 8 | ||||
| -rw-r--r-- | Digitigrade/Model/Instance.php | 69 | ||||
| -rw-r--r-- | Digitigrade/Model/InstanceAuth.php | 21 |
9 files changed, 161 insertions, 10 deletions
diff --git a/Digitigrade/Job.php b/Digitigrade/Job.php index 4fc799a..146e4b2 100644 --- a/Digitigrade/Job.php +++ b/Digitigrade/Job.php @@ -30,7 +30,7 @@ abstract class Job implements \JsonSerializable { public abstract function run(); /** - * Submits the job to the global job queue with default options. + * Submits the job to the global job queue with appropriate options. * @return void */ public function submit() { diff --git a/Digitigrade/Job/RefreshOutboundAuthToken.php b/Digitigrade/Job/RefreshOutboundAuthToken.php new file mode 100644 index 0000000..588617f --- /dev/null +++ b/Digitigrade/Job/RefreshOutboundAuthToken.php @@ -0,0 +1,29 @@ +<?php +namespace Digitigrade\Job; + +use Digitigrade\Job; +use Digitigrade\JobQueue; +use Digitigrade\Logger; +use Digitigrade\Model\Instance; + +class RefreshOutboundAuthToken extends Job { + public string $domain; + public string $token; + public \DateTimeInterface $when; + + public function __construct(string $domain, \DateTimeInterface $when) { + $this->domain = $domain; + $this->when = $when; + $this->remainingTries = 5; + } + + public function run() { + Logger::getInstance()->info("refreshing outbound token for $this->domain"); + $instance = Instance::findByDomain($this->domain); + $instance->refreshOutboundAuthToken(); + } + + public function submit() { + JobQueue::getInstance()->submitDelayedUntil($this, $this->when); + } +}
\ No newline at end of file diff --git a/Digitigrade/Job/RequestOutboundAuthToken.php b/Digitigrade/Job/RequestOutboundAuthToken.php new file mode 100644 index 0000000..69430c8 --- /dev/null +++ b/Digitigrade/Job/RequestOutboundAuthToken.php @@ -0,0 +1,28 @@ +<?php +namespace Digitigrade\Job; + +use Digitigrade\Job; +use Digitigrade\JobQueue; +use Digitigrade\Logger; +use Digitigrade\Model\Instance; + +class RequestOutboundAuthToken extends Job { + public string $domain; + public string $secret; + + public function __construct(string $domain, string $secret) { + $this->domain = $domain; + $this->secret = $secret; + $this->remainingTries = 1; + } + + public function run() { + Logger::getInstance()->info("responding to $this->domain's dialback challenge"); + $instance = Instance::findByDomain($this->domain); + $instance->requestOutboundAuthToken($this->secret); + } + + public function submit() { + JobQueue::getInstance()->submitUrgent($this); + } +}
\ No newline at end of file diff --git a/Digitigrade/Job/UpdateInstanceInfo.php b/Digitigrade/Job/UpdateInstanceInfo.php index 85c11f0..abcd182 100644 --- a/Digitigrade/Job/UpdateInstanceInfo.php +++ b/Digitigrade/Job/UpdateInstanceInfo.php @@ -2,6 +2,7 @@ namespace Digitigrade\Job; use Digitigrade\Job; +use Digitigrade\JobQueue; use Digitigrade\Logger; use Digitigrade\Model\Instance; @@ -19,4 +20,8 @@ class UpdateInstanceInfo extends Job { throw new \RuntimeException("remote object doesn't seem to exist"); } } + + public function submit() { + JobQueue::getInstance()->submitLazy($this); + } }
\ No newline at end of file diff --git a/Digitigrade/JobQueue.php b/Digitigrade/JobQueue.php index 9f95584..cce6be5 100644 --- a/Digitigrade/JobQueue.php +++ b/Digitigrade/JobQueue.php @@ -65,7 +65,7 @@ class JobQueue extends Singleton { $job->run(); $this->conn->delete($beanJob); $log->info('Job ' . $beanJob->getId() . ' completed successfully'); - } catch (\Exception $e) { + } catch (\Throwable $e) { if ($job->remainingTries <= 0) { $this->conn->delete($beanJob); $log->error( diff --git a/Digitigrade/Model.php b/Digitigrade/Model.php index 84e6439..b5c9e99 100644 --- a/Digitigrade/Model.php +++ b/Digitigrade/Model.php @@ -55,7 +55,7 @@ abstract class Model { foreach ($props as $p) { $pName = $p->getName(); if (!isset($this->{$pName})) - continue; + $this->{$pName} = null; // we need to determine the plain value to insert ... $value = $this->{$pName}; $discarded = false; @@ -213,9 +213,10 @@ abstract class Model { continue; $type = $reflType->getName(); if (is_subclass_of($type, Model::class) && !is_subclass_of($type, FetchableModel::class)) { - if (property_exists($this, 'id')) + if (isset($this->{$propName}) && property_exists($this, 'id')) { $this->{$propName}->setOwnerId($this->id); - $this->{$propName}->save(); + $this->{$propName}->save(); + } } } diff --git a/Digitigrade/Model/FetchableModel.php b/Digitigrade/Model/FetchableModel.php index 0b67599..e091a87 100644 --- a/Digitigrade/Model/FetchableModel.php +++ b/Digitigrade/Model/FetchableModel.php @@ -58,16 +58,20 @@ abstract class FetchableModel extends Model implements RemoteFetchable { } } - $obj->beforeSave($uri); + $obj->finaliseAfterFetch($uri); if ($autoSave) { $obj->save(); + $obj->finaliseAfterSave(); } return $obj; } - protected function beforeSave(string $uri) { + protected function finaliseAfterFetch(string $uri) { + } + + protected function finaliseAfterSave() { } public static function findByUri(string $uri, bool $autoSave = true, bool $forceRefetch = false): ?static { diff --git a/Digitigrade/Model/Instance.php b/Digitigrade/Model/Instance.php index de3ff9d..5f33e6d 100644 --- a/Digitigrade/Model/Instance.php +++ b/Digitigrade/Model/Instance.php @@ -1,7 +1,7 @@ <?php namespace Digitigrade\Model; -use Digitigrade\Model; +use Digitigrade\Job\RefreshOutboundAuthToken; class Instance extends FetchableModel { public ?int $id; @@ -15,6 +15,7 @@ class Instance extends FetchableModel { public ?string $softwareHomepage; public InstanceEndpoints $endpoints; + public InstanceAuth $auth; protected function getUpdateWhereClause(\PDO $db): ?string { if (self::findWhere('domain = ?', [$this->domain]) != null) @@ -28,14 +29,25 @@ class Instance extends FetchableModel { return self::findByUri("https://$domain/.well-known/pawpub-instance", $autoSave, $forceRefetch); } - public function hydrate() { + protected function hydrate() { $this->endpoints = InstanceEndpoints::findWhere('instance_id = ?', [$this->id]); + $auth = InstanceAuth::findWhere('instance_id = ?', [$this->id]); + if ($auth == null) { + $auth = new InstanceAuth(); + $auth->setOwnerId($this->id); + } + $this->auth = $auth; } - protected function beforeSave(string $uri) { + protected function finaliseAfterFetch(string $uri) { $this->domain = explode('/', $uri)[2]; } + protected function finaliseAfterSave() { + // this has to be done after saving because we didn't have an id before + $this->auth = InstanceAuth::findWhere('instance_id = ?', [$this->id]) ?? new InstanceAuth(); + } + public static function findByUri(string $uri, bool $autoSave = true, bool $forceRefetch = false): ?static { if (!$forceRefetch) { $domain = explode('/', $uri)[2]; @@ -45,4 +57,55 @@ class Instance extends FetchableModel { } return self::fetchFromRemote($uri, $autoSave); } + + public function beginOutboundAuth() { + $authEndpoint = $this->endpoints->auth; + if (!isset($authEndpoint)) { + throw new \RuntimeException("can't authenticate with $this->domain because it doesn't have an auth endpoint"); + } + file_get_contents($authEndpoint . '?phase=dialback&target=' . urlencode(path_to_uri('/auth/dialback'))); + } + + public function requestOutboundAuthToken(string $secret) { + $authEndpoint = $this->endpoints->auth; + if (!isset($authEndpoint)) { + throw new \RuntimeException("can't authenticate with $this->domain because it doesn't have an auth endpoint"); + } + $response = file_get_contents($authEndpoint . '?phase=token&secret=' . urlencode($secret)); + if ($response === false) { + throw new \RuntimeException("requesting a token from $this->domain failed"); + } + $data = json_decode($response); + if ($data === false || !isset($data->token, $data->expires)) { + throw new \RuntimeException("$this->domain sent bogus json in response to token request"); + } + + $this->auth->outboundToken = $data->token; + $this->save(); + + (new RefreshOutboundAuthToken($this->domain, new \DateTimeImmutable($data->expires)))->submit(); + } + + public function refreshOutboundAuthToken() { + $authEndpoint = $this->endpoints->auth; + if (!isset($authEndpoint)) { + throw new \RuntimeException("can't authenticate with $this->domain because it doesn't have an auth endpoint"); + } + if (!isset($this->auth->outboundToken)) { + throw new \RuntimeException("can't refresh outbound token for $this->domain because i don't already have one"); + } + $response = file_get_contents($authEndpoint . '?phase=refresh&token=' . urlencode($this->auth->outboundToken)); + if ($response === false) { + throw new \RuntimeException("requesting a new token from $this->domain failed"); + } + $data = json_decode($response); + if ($data === false || !isset($data->token, $data->expires)) { + throw new \RuntimeException("$this->domain sent bogus json in response to token refresh request"); + } + + $this->auth->outboundToken = $data->token; + $this->save(); + + (new RefreshOutboundAuthToken($this->domain, new \DateTimeImmutable($data->expires)))->submit(); + } }
\ No newline at end of file diff --git a/Digitigrade/Model/InstanceAuth.php b/Digitigrade/Model/InstanceAuth.php new file mode 100644 index 0000000..616d4c5 --- /dev/null +++ b/Digitigrade/Model/InstanceAuth.php @@ -0,0 +1,21 @@ +<?php +namespace Digitigrade\Model; + +use Digitigrade\Model; + +class InstanceAuth extends Model { + public ?int $instanceId; + public ?string $outboundToken; + public ?string $inboundToken; + + protected function setOwnerId(int $id) { + $this->instanceId = $id; + } + + protected function getUpdateWhereClause($db): ?string { + if (self::findWhere('instance_id = ?', [$this->instanceId]) != null) { + return "instance_id = $this->instanceId"; + } + return null; + } +}
\ No newline at end of file |
