aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--Digitigrade/Job/ProcessIncomingPush.php10
-rw-r--r--Digitigrade/Model.php6
-rw-r--r--Digitigrade/Model/ExtensionObject.php43
-rw-r--r--Digitigrade/Policy.php23
-rw-r--r--Digitigrade/PolicyManager.php3
-rw-r--r--migrations/20250507_175321_create_extension_object.php15
-rw-r--r--routes/extension.php21
7 files changed, 117 insertions, 4 deletions
diff --git a/Digitigrade/Job/ProcessIncomingPush.php b/Digitigrade/Job/ProcessIncomingPush.php
index 16dd1ce..384949b 100644
--- a/Digitigrade/Job/ProcessIncomingPush.php
+++ b/Digitigrade/Job/ProcessIncomingPush.php
@@ -5,6 +5,7 @@ use Digitigrade\Job;
use Digitigrade\JobQueue;
use Digitigrade\Logger;
use Digitigrade\Model\Actor;
+use Digitigrade\Model\ExtensionObject;
use Digitigrade\Model\Interaction;
use Digitigrade\Model\Note;
@@ -51,7 +52,14 @@ class ProcessIncomingPush extends Job {
}
break;
case 'extension':
- throw new \RuntimeException('object type ' . $this->object->type . ' not yet implemented :(');
+ $log->info('importing extension with uri ' . $this->object->self);
+ $alreadyExisted = ExtensionObject::countWhere('uri = ?', [$this->object->self]) > 0;
+ $extension = ExtensionObject::importFromReceivedObject($this->object);
+ break;
+ /*if (!$alreadyExisted) {
+ $extension?->processTimelineAdditions();
+ $extension?->processNotifications();
+ }*/
default:
throw new \RuntimeException('invalid object type: ' . $this->object->type);
}
diff --git a/Digitigrade/Model.php b/Digitigrade/Model.php
index 14d025d..6e87860 100644
--- a/Digitigrade/Model.php
+++ b/Digitigrade/Model.php
@@ -81,6 +81,9 @@ abstract class Model {
} elseif (is_array($value)) {
// arrays ought to be handled separately when saving (different db table)
$discarded = true;
+ } elseif ($value instanceof \stdClass) {
+ // probably json
+ $value = json_encode($value);
} elseif ($value == null && $pName == 'id') {
// don't overwrite an existing primary key with null ..
// this is kinda janky but it hopefully will work
@@ -118,6 +121,9 @@ abstract class Model {
} elseif (is_subclass_of($typeClass, Model::class) && is_int($value)) {
// if it's another model we can try to look it up by id automatically
$obj->{$name} = $typeClass::find($value);
+ } elseif (is_a($typeClass, \stdClass::class, true)) {
+ // if it's a stdclass it's usually json
+ $obj->{$name} = json_decode($value);
} else {
// otherwise try to instantiate the correct class and pass it the simple value
$obj->{$name} = new $typeClass($value);
diff --git a/Digitigrade/Model/ExtensionObject.php b/Digitigrade/Model/ExtensionObject.php
new file mode 100644
index 0000000..8df8148
--- /dev/null
+++ b/Digitigrade/Model/ExtensionObject.php
@@ -0,0 +1,43 @@
+<?php
+namespace Digitigrade\Model;
+
+class ExtensionObject extends PushableModel {
+ public ?int $id;
+ public string $uri;
+ public string $extension;
+ public \stdClass $data;
+
+ protected function getUpdateWhereClause(\PDO $db): ?string {
+ if (self::findWhere('uri = ?', [$this->uri]) != null)
+ return 'uri = ' . $db->quote($this->uri);
+ if (self::findWhere('id = ?', [$this->id]) != null)
+ return "id = $this->id";
+ return null;
+ }
+
+ protected static function createFromJson(\stdClass $data, bool $autoSave, ?string $uri = null): static {
+ $obj = new static();
+ $obj->uri = $data->self;
+ $obj->extension = $data->extension;
+ unset($data->self, $data->extension, $data->type);
+ $obj->data = clone $data;
+ $data->self = $obj->uri;
+ $data->extension = $obj->extension;
+ $data->type = 'extension';
+ return $obj;
+ }
+
+ public function getRelevantServers(): array {
+ // TODO
+ return [];
+ }
+
+ public function jsonSerialize(): mixed {
+ return [
+ 'type' => 'extension',
+ 'self' => path_to_uri("/extension/$this->id"),
+ 'extension' => $this->extension,
+ ...get_object_vars($this->data)
+ ];
+ }
+} \ No newline at end of file
diff --git a/Digitigrade/Policy.php b/Digitigrade/Policy.php
index 973c229..f5ecbfb 100644
--- a/Digitigrade/Policy.php
+++ b/Digitigrade/Policy.php
@@ -2,6 +2,7 @@
namespace Digitigrade;
use Digitigrade\Model\Actor;
+use Digitigrade\Model\ExtensionObject;
use Digitigrade\Model\Instance;
use Digitigrade\Model\Interaction;
use Digitigrade\Model\Note;
@@ -34,7 +35,14 @@ abstract class Policy {
return true;
}
- // TODO: public function checkExtension()
+ /**
+ * Checks if an ExtensionObject is allowed and possibly alters it.
+ * @param ExtensionObject $extension the extension to check (may be modified in-place)
+ * @return bool true if allowed, false if the extension should be dropped
+ */
+ public function checkExtension(ExtensionObject $extension): bool {
+ return true;
+ }
/**
* Checks if an Actor is allowed to be federated to a certain Instance,
@@ -59,7 +67,7 @@ abstract class Policy {
}
/**
- * Checks if an Note is allowed to be federated to a certain Instance,
+ * Checks if a Note is allowed to be federated to a certain Instance,
* and possibly alters it.
* @param Note $note the note to check (may be modified in-place)
* @param ?Instance $instance the instance that is receiving this (may be null if unknown)
@@ -69,5 +77,14 @@ abstract class Policy {
return true;
}
- // TODO: public function checkExtensionFederation()
+ /**
+ * Checks if an ExtensionObject is allowed to be federated to a certain Instance,
+ * and possibly alters it.
+ * @param ExtensionObject $extension the extension to check (may be modified in-place)
+ * @param ?Instance $instance the instance that is receiving this (may be null if unknown)
+ * @return bool true if allowed, false if the request should be cancelled
+ */
+ public function checkExtensionFederation(ExtensionObject $extension, ?Instance $instance): bool {
+ return true;
+ }
} \ No newline at end of file
diff --git a/Digitigrade/PolicyManager.php b/Digitigrade/PolicyManager.php
index 69e2ab0..1f4d8fa 100644
--- a/Digitigrade/PolicyManager.php
+++ b/Digitigrade/PolicyManager.php
@@ -3,6 +3,7 @@ namespace Digitigrade;
use Digitigrade\HttpResponseStatus\PolicyRejected;
use Digitigrade\Model\Actor;
+use Digitigrade\Model\ExtensionObject;
use Digitigrade\Model\FetchableModel;
use Digitigrade\Model\Instance;
use Digitigrade\Model\Interaction;
@@ -32,6 +33,7 @@ class PolicyManager extends Singleton {
Actor::class => self::arrayAll($this->policies, fn(Policy $p) => $p->checkActor($object)),
Interaction::class => self::arrayAll($this->policies, fn(Policy $p) => $p->checkInteraction($object)),
Note::class => self::arrayAll($this->policies, fn(Policy $p) => $p->checkNote($object)),
+ ExtensionObject::class => self::arrayAll($this->policies, fn(Policy $p) => $p->checkExtension($object)),
default => true
};
}
@@ -54,6 +56,7 @@ class PolicyManager extends Singleton {
Actor::class => self::arrayAll($this->policies, fn(Policy $p) => $p->checkActorFederation($object, $instance)),
Interaction::class => self::arrayAll($this->policies, fn(Policy $p) => $p->checkInteractionFederation($object, $instance)),
Note::class => self::arrayAll($this->policies, fn(Policy $p) => $p->checkNoteFederation($object, $instance)),
+ ExtensionObject::class => self::arrayAll($this->policies, fn(Policy $p) => $p->checkExtensionFederation($object, $instance)),
default => true
};
}
diff --git a/migrations/20250507_175321_create_extension_object.php b/migrations/20250507_175321_create_extension_object.php
new file mode 100644
index 0000000..94b66dc
--- /dev/null
+++ b/migrations/20250507_175321_create_extension_object.php
@@ -0,0 +1,15 @@
+<?php
+
+use \Digitigrade\Db\Migrator;
+
+Migrator::getInstance()->register(20250507_175321, function (PDO $db) {
+ $db->exec(<<<END
+ CREATE TABLE extension_object (
+ id bigserial primary key not null,
+ uri text unique not null,
+ deleted boolean,
+ extension text not null,
+ data jsonb not null
+ );
+ END);
+});
diff --git a/routes/extension.php b/routes/extension.php
new file mode 100644
index 0000000..50413e8
--- /dev/null
+++ b/routes/extension.php
@@ -0,0 +1,21 @@
+<?php
+
+use Digitigrade\GlobalConfig;
+use Digitigrade\HttpResponseStatus\NotFound;
+use Digitigrade\Model\ExtensionObject;
+use Digitigrade\Model\Instance;
+use Digitigrade\PolicyManager;
+use Digitigrade\Router;
+
+Router::getInstance()->mount('/extension/:id', function (array $args) {
+ $obj = ExtensionObject::find($args['id']);
+ $instance = Instance::findByRequestHeaders();
+ if ($obj == null || $obj->deleted) {
+ throw new NotFound("i don't know that extension object");
+ }
+ if (hostname_from_uri($obj->uri) != GlobalConfig::getInstance()->getCanonicalHost()) {
+ throw new NotFound("i don't want to tell you about non local objects sorry");
+ }
+ PolicyManager::getInstance()->checkFederationOrThrow($obj, $instance);
+ json_response($obj);
+}); \ No newline at end of file