aboutsummaryrefslogtreecommitdiffhomepage
path: root/Digitigrade/PolicyManager.php
blob: 69e2ab07f6e2933d289ee7dd94cbce0cae2d937c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
<?php
namespace Digitigrade;

use Digitigrade\HttpResponseStatus\PolicyRejected;
use Digitigrade\Model\Actor;
use Digitigrade\Model\FetchableModel;
use Digitigrade\Model\Instance;
use Digitigrade\Model\Interaction;
use Digitigrade\Model\Note;

class PolicyManager extends Singleton {
    /** @var Policy[] */
    private array $policies = [];

    public function register(Policy $policy) {
        $this->policies[] = $policy;
    }

    public function registerAll(Policy ...$policies) {
        foreach ($policies as $p) {
            $this->register($p);
        }
    }

    /**
     * Checks if an object is allowed by all policies and possibly alters it.
     * @param FetchableModel $object the object to check (may be modified in-place)
     * @return bool true if allowed, false if it should be dropped
     */
    public function check(FetchableModel $object): bool {
        return match ($object::class) {
            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)),
            default => true
        };
    }

    public function checkOrThrow(FetchableModel $object) {
        if (!$this->check($object)) {
            throw new PolicyRejected();
        }
    }

    /**
     * Checks if an object is allowed to be sent to a certain Instance by all
     * policies, and possibly alters it.
     * @param FetchableModel $object the object to check (may be modified in-place)
     * @param ?Instance $instance the instance it's being sent to (may be null if unknown)
     * @return bool true if allowed, false if the request should be cancelled
     */
    public function checkFederation(FetchableModel $object, ?Instance $instance): bool {
        return match ($object::class) {
            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)),
            default => true
        };
    }

    public function checkFederationOrThrow(FetchableModel $object, ?Instance $instance) {
        if (!$this->checkFederation($object, $instance)) {
            throw new PolicyRejected();
        }
    }

    private static function arrayAll(array $arr, callable $test): bool {
        // i would use array_all but that's php 8.4+ only which i don't have lol
        foreach ($arr as $item) {
            if (!$test($item)) {
                return false;
            }
        }
        return true;
    }
}