ApiAuthorizer.php 5.23 KB
Newer Older
1
2
3
4
5
<?php


namespace Source\Authorization;

6
use ReflectionClass;
7
8
use Source\Gateways\Users\UsersRepository;
use Source\Gateways\Groups\GroupsRepository;
9
use Source\Exceptions\AuthorizationException;
Jacob Priddy's avatar
Jacob Priddy committed
10
use Source\Exceptions\EntityNotFoundException;
11
use Source\Gateways\GroupUser\GroupUserRepository;
12
13
14

class ApiAuthorizer implements Authorizer
{
15
16
17
18
19
    /**
     * @var \Source\Gateways\GroupUser\GroupUserRepository
     */
    protected GroupUserRepository $groupUserRepository;

20
21
22
23
24
25
26
27
28
29
    /**
     * @var \Source\Gateways\Groups\GroupsRepository
     */
    protected GroupsRepository $groups;

    /**
     * @var \Source\Gateways\Users\UsersRepository
     */
    protected UsersRepository $users;

30
31
32
33
34
    /**
     * @var string|null
     */
    protected ?string $currentUserId;

Jacob Priddy's avatar
Jacob Priddy committed
35
36
37
38
39
    /**
     * @var string[]
     */
    protected array $userGroups;

40
    public function __construct(
41
        ?string $currentUserId,
42
43
44
45
46
47
        UsersRepository $users,
        GroupsRepository $groups,
        GroupUserRepository $groupUserRepository
    ) {
        $this->users = $users;
        $this->groups = $groups;
48
        $this->currentUserId = $currentUserId;
49
        $this->groupUserRepository = $groupUserRepository;
50
51
    }

52
53
54
    /**
     * @param string|null $id
     */
55
56
    public function setCurrentUserId(?string $id): void
    {
Jacob Priddy's avatar
Jacob Priddy committed
57
58
59
60
        if ($this->currentUserId !== $id) {
            $this->userGroups = [];
            $this->currentUserId = $id;
        }
61
62
    }

63
64
65
66
67
68
69
70
71
72
73
74
    /**
     * @inheritDoc
     */
    public function getPermissions(): array
    {
        $reflection = new ReflectionClass(Permissions::class);

        return array_values(array_filter($reflection->getConstants(), function (string $permission) {
            return $this->allows($permission);
        }));
    }

75
    /**
76
     * @inheritDoc
77
     */
78
    public function protectAll(array $permissions): void
79
    {
80
81
        if (!$this->allowsAll($permissions)) {
            throw new AuthorizationException();
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
        }
    }

    /**
     * @inheritDoc
     */
    public function allowsAll(array $permissions): bool
    {
        $groups = $this->getGroupsForCurrentUser();

        if (in_array(Permissions::ADMIN, $groups, true)) {
            return true;
        }

        foreach ($permissions as $permission) {
            if (!in_array($permission, $groups, true)) {
                return false;
            }
100
        }
101
102

        return true;
103
    }
104
105

    /**
106
107
     * @return string[]
     * @throws \Source\Exceptions\EntityNotFoundException
108
     */
109
    protected function getGroupsForCurrentUser(): array
110
    {
111
        if (!$this->currentUserId) {
112
            return [];
113
114
        }

115
        return $this->getGroupsForUser($this->currentUserId);
Jacob Priddy's avatar
Jacob Priddy committed
116
117
118
119
120
121
122
123
124
    }

    /**
     * @param string $userId
     * @return string[]
     * @throws \Source\Exceptions\EntityNotFoundException
     */
    protected function getGroupsForUser(string $userId): array
    {
Jacob Priddy's avatar
Jacob Priddy committed
125
126
127
128
129
        if (!$this->userGroups) {
            $this->userGroups = static::groupNames($this->groupUserRepository->getGroupsForUser($userId));
        }

        return $this->userGroups;
130
131
132
133
134
135
136
137
138
139
    }

    /**
     * @param \Source\Entities\Group[] $groups
     * @return string[]
     */
    protected static function groupNames(array $groups): array
    {
        $groupNames = [];

140
        foreach ($groups as $group) {
141
            $groupNames[] = $group->getTitle();
142
143
        }

144
        return $groupNames;
145
146
147
148
149
    }

    /**
     * @inheritDoc
     */
150
    public function protectOne(array $permissions): void
151
    {
152
        if (!$this->allowsOne($permissions)) {
153
154
155
156
157
158
159
            throw new AuthorizationException();
        }
    }

    /**
     * @inheritDoc
     */
160
    public function allowsOne(array $permissions): bool
161
    {
162
163
164
165
        $groups = $this->getGroupsForCurrentUser();

        if (in_array(Permissions::ADMIN, $groups, true)) {
            return true;
166
        }
167
168
169
170
171
172
173
174

        foreach ($groups as $group) {
            if (in_array($group, $permissions, true)) {
                return true;
            }
        }

        return false;
175
    }
Jacob Priddy's avatar
Jacob Priddy committed
176
177
178
179
180
181
182
183
184

    /**
     * @inheritDoc
     */
    public function protect(string $permission): void
    {
        $this->protectAll([$permission]);
    }

185
186
187
188
189
190
191
192
    /**
     * @inheritDoc
     */
    public function allows(string $permission): bool
    {
        return $this->allowsAll([$permission]);
    }

Jacob Priddy's avatar
Jacob Priddy committed
193
194
195
196
197
    /**
     * @inheritDoc
     */
    public function protectAdminRights(string $userId, ?string $groupId = null): void
    {
198
        $user = $this->users->get($userId);
Jacob Priddy's avatar
Jacob Priddy committed
199
200
201
202
203
204
205
206
207
208
209
210
211

        if (!$user) {
            throw new EntityNotFoundException('User not found.');
        }

        $groups = $this->getGroupsForUser($userId);

        // Only admins have access to other admins
        if (in_array(Permissions::ADMIN, $groups, true)) {
            $this->protect(Permissions::ADMIN);
        }

        if ($groupId) {
212
            $group = $this->groups->get($groupId);
Jacob Priddy's avatar
Jacob Priddy committed
213
214
215
216
217
218
219
220
221
222
223
224
225
            if (!$group) {
                throw new EntityNotFoundException('Group not found.');
            }
            // Only admins can create or remove other admins
            if ($group->hasTitleOf(Permissions::ADMIN)) {
                $this->protect(Permissions::ADMIN);
            }
        }

        if ($user->hasFirstNameOf('admin')) {
            throw new AuthorizationException('You cannot modify the admin user.');
        }
    }
226
}