Passed
Push — master ( 5a91bc...be866d )
by Łukasz
02:23
created

Manager::hasRole()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 2
dl 0
loc 3
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace Mrluke\Privileges;
4
5
use InvalidArgumentException;
6
use Mrluke\Configuration\Contracts\ArrayHost as Host;
1 ignored issue
show
Bug introduced by
The type Mrluke\Configuration\Contracts\ArrayHost was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
7
use Mrluke\Configuration\Exceptions\ConfigurationException;
1 ignored issue
show
Bug introduced by
The type Mrluke\Configuration\Exc...\ConfigurationException was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
8
use Mrluke\Privileges\Contracts\Authorizable;
9
use Mrluke\Privileges\Contracts\Permission;
10
use Mrluke\Privileges\Contracts\Permitable;
11
use Mrluke\Privileges\Contracts\Role;
12
13
/**
14
 * Manager is a class that provides complex methods
15
 * to assign Authorizable to permissions and role.
16
 *
17
 * @author    Łukasz Sitnicki (mr-luke)
18
 * @link      http://github.com/mr-luke/privileges
19
 *
20
 * @category  Laravel
21
 * @package   mr-luke/privileges
22
 * @license   MIT
23
 * @version   1.0.0
24
 */
25
class Manager
26
{
27
    /**
28
     * Authorizable primary key name.
29
     *
30
     * @var string
31
     */
32
    protected $authKeyName;
33
34
    /**
35
     * Authorizable primary key type.
36
     *
37
     * @var string
38
     */
39
    protected $authKeyType;
40
41
    /**
42
     * Authorizable table name.
43
     *
44
     * @var string
45
     */
46
    protected $authTable;
47
48
    /**
49
     * Configuration instance.
50
     *
51
     * @var \Mrluke\Configuration\Contracts\ArrayHost
52
     */
53
    protected $config;
54
55
    public function __construct(Host $config)
56
    {
57
        $this->config = $config;
58
    }
59
60
    /**
61
     * Assign Authorizable to given role.
62
     *
63
     * @param  \Mrluke\Privileges\Contracts\Authorizable $auth
64
     * @param  mixed                                     $role
65
     * @return void
66
     */
67
    public function assignRole(Authorizable $auth, $role): void
68
    {
69
        if ($role instanceof Role) {
70
            $auth->roles()->syncWithoutDetaching([$role->id]);
1 ignored issue
show
Bug introduced by
Accessing id on the interface Mrluke\Privileges\Contracts\Role suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
71
        } elseif (is_integer($role)) {
72
            $auth->roles()->syncWithoutDetaching([$role]);
73
        } elseif (is_array($role)) {
74
            $auth->roles()->syncWithoutDetaching($role);
75
        } else {
76
            throw new InvalidArgumentException(
77
                sprintf('[role] parameter must be type of integer or instance of \Mrluke\Privileges\Contracts\Role. %s type given.', gettype($role))
78
            );
79
        }
80
    }
81
82
    /**
83
     * Return permission level based on personal's & role's permission.
84
     *
85
     * @param  \Mrluke\Privileges\Contracts\Authorizable $auth
86
     * @param  string                                    $scope
87
     * @return int
88
     *
89
     * @throws \InvalidArgumentException
90
     */
91
    public function considerPermission(Authorizable $auth, string $scope): int
92
    {
93
        if ($personal = $this->getPermission($auth, $scope)) {
94
            // Personal permissions has priority
95
            // over role's ones.
96
            //
97
            return $personal->level;
1 ignored issue
show
Bug introduced by
Accessing level on the interface Mrluke\Privileges\Contracts\Permission suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
98
        }
99
100
        $general = 0;
101
        $auth->load('roles.permissions');
1 ignored issue
show
Bug introduced by
The method load() does not exist on Mrluke\Privileges\Contracts\Authorizable. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

101
        $auth->/** @scrutinizer ignore-call */ 
102
               load('roles.permissions');

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
102
103
        foreach ($auth->roles as $r) {
1 ignored issue
show
Bug introduced by
Accessing roles on the interface Mrluke\Privileges\Contracts\Authorizable suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
104
            // Let's check if there's a given scope defined
105
            // as a permission in any of Authorizable roles.
106
            //
107
            if ($p = $this->getPermission($r, $scope)) {
108
                ($p->level < $general) ?: $general = $p->level;
109
            }
110
        }
111
112
        return $general;
113
    }
114
115
    /**
116
     * Return restrictions based on roles.
117
     *
118
     * @param  \Mrluke\Privileges\Contracts\Authorizable $auth
119
     * @return int
120
     *
121
     * @throws \InvalidArgumentException
122
     */
123
    public function considerRestriction(Authorizable $auth): array
124
    {
125
        $restrictions = [];
126
        $level        = 0;
127
128
        $auth->load('roles.permissions');
129
130
        foreach ($auth->roles as $r) {
1 ignored issue
show
Bug introduced by
Accessing roles on the interface Mrluke\Privileges\Contracts\Authorizable suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
131
            // Let's check if there're restrictions for a roles.
132
            ($level < $r->level) ?: $restrictions = $r->restrictions;
133
        }
134
135
        return $restrictions;
136
    }
137
138
    /**
139
     * Detect which scope should be applied for given model.
140
     *
141
     * @param  string $model
142
     * @return string|null
143
     */
144
    public function detectScope(string $model)
145
    {
146
        return $this->config->get(
147
            'mapping.'.$model,
148
            $this->config->get('mapping_default')
149
        );
150
    }
151
152
    /**
153
     * Return Authorizable model reference.
154
     *
155
     * @return string
156
     */
157
    public function getAuthorizableModel(): string
158
    {
159
        return $this->config->get('authorizable');
160
    }
161
162
    /**
163
     * Return Authorizable migration config.
164
     *
165
     * @return array
166
     */
167
    public function getAuthorizableMigration(): array
168
    {
169
        if (is_null($this->authKeyName)) {
1 ignored issue
show
introduced by
The condition is_null($this->authKeyName) is always false.
Loading history...
170
            $authorizableClass = $this->config->get('authorizable');
171
            $instance = new $authorizableClass;
172
173
            if (!$instance instanceof Model) {
174
                throw new ConfigurationException(
175
                    sprintf('An instance of [authorizable] should be \Illuminate\Databe\Eloquent\Model. %s given.', get_class($instance))
176
                );
177
            }
178
179
            $this->authKeyName = $instance->getKeyName();
180
            $this->authKeyType = $instance->getKeyType();
181
            $this->authTable   = $instance->getTable();
182
        }
183
184
        return [
185
            'key'   => $this->authKeyName,
186
            'type'  => $this->authKeyType,
187
            'table' => $this->authTable
188
        ];
189
    }
190
191
    /**
192
     * Return Permission for given scope.
193
     *
194
     * @param  \Mrluke\Privileges\Contracts\Permitable $subject
195
     * @param  string                                  $scope
196
     * @return \Mrluke\Privileges\Contracts\Permission|null
197
     *
198
     * @throws \InvalidArgumentException
199
     */
200
    public function getPermission(Permitable $subject, string $scope)
201
    {
202
        $this->checkScope($scope);
203
204
        return $subject->permissions->where('scope', $scope)->first();
1 ignored issue
show
Bug introduced by
Accessing permissions on the interface Mrluke\Privileges\Contracts\Permitable suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
205
    }
206
207
    /**
208
     * Grant or update premission for a Permitable.
209
     *
210
     * @param  \Mrluke\Privileges\Contracts\Permitable $subject
211
     * @param  string                                  $scope
212
     * @param  int                                     $level
213
     * @return void
214
     *
215
     * @throws \InvalidArgumentException
216
     */
217
    public function grantPermission(Permitable $subject, string $scope, int $level): void
218
    {
219
        $this->checkScopeAndLevel($scope, $level);
220
221
        $permission = $subject->permissions()->ofScope($scope)->first();
222
223
        $permission ? $permission->update(['level' => $level]) : $subject->permissions()->create([
224
            'scope' => $scope,
225
            'level' => $level
226
        ]);
227
    }
228
229
    /**
230
     * Determine if there's a given scope Permission for a Permitable.
231
     *
232
     * @param  \Mrluke\Privileges\Contracts\Permitable $subject
233
     * @param  string                                  $scope
234
     * @return bool
235
     *
236
     * @throws \InvalidArgumentException
237
     */
238
    public function hasPermission(Permitable $subject, string $scope): bool
239
    {
240
        $this->checkScope($scope);
241
242
        return $subject->permissions->where('scope', $scope)->exists();
1 ignored issue
show
Bug introduced by
Accessing permissions on the interface Mrluke\Privileges\Contracts\Permitable suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
243
    }
244
245
    /**
246
     * Determine if Permitable has given Role assigned.
247
     *
248
     * @param  \Mrluke\Privileges\Contracts\Permitable $subject
249
     * @param  mixed                                   $role
250
     * @return bool
251
     */
252
    public function hasRole(Permitable $subject, $role): bool
253
    {
254
        return $subject->roles->where('id', $role->id)->exists();
1 ignored issue
show
Bug introduced by
Accessing roles on the interface Mrluke\Privileges\Contracts\Permitable suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
255
    }
256
257
    /**
258
     * Regain permission for a Permitable.
259
     *
260
     * @param  \Mrluke\Privileges\Contracts\Permitable $subject
261
     * @param  string                                  $scope
262
     * @return void
263
     *
264
     * @throws \InvalidArgumentException
265
     */
266
    public function regainPermission(Permitable $subject, string $scope): void
267
    {
268
        $this->checkScope($scope);
269
270
        $subject->permissions()->ofScope($scope)->delete();
271
    }
272
273
    /**
274
     * Remove Authorizable's role.
275
     *
276
     * @param  \Mrluke\Privileges\Contracts\Authorizable $auth
277
     * @param  mixed                                     $role
278
     * @return void
279
     */
280
    public function removeRole(Authorizable $auth, $role): void
281
    {
282
        if ($role instanceof Role) {
283
            $auth->roles()->detach($role->id);
1 ignored issue
show
Bug introduced by
Accessing id on the interface Mrluke\Privileges\Contracts\Role suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
284
        } elseif (is_integer($role)) {
285
            $auth->roles()->detach($role);
286
        } else {
287
            throw new InvalidArgumentException(
288
                sprintf('[role] parameter must be type of integer or instance of \Mrluke\Privileges\Contracts\Role. %s type given.', gettype($role))
289
            );
290
        }
291
    }
292
293
    /**
294
     * Return class attributes or setting.
295
     *
296
     * @param  string $name
297
     * @return mixed
298
     */
299
    public function __get(string $name)
300
    {
301
        if (in_array($name, ['authKeyName', 'authKeyType', 'authTable'])) {
302
            return $this->{$name};
303
        }
304
305
        return $this->config->get($name, null);
306
    }
307
308
    /**
309
     * Check if the scope value.
310
     *
311
     * @param  string $scope
312
     * @return void
313
     *
314
     * @throws \InvalidArgumentException
315
     */
316
    private function checkScope(string $scope): void
317
    {
318
        if (!in_array($scope, $this->config->get('scopes'))) {
319
            throw new InvalidArgumentException('Given [scope] is not allowed.');
320
        }
321
    }
322
323
    /**
324
     * Check if the scope & level values.
325
     *
326
     * @param  string $scope
327
     * @param  int    $level
328
     * @return void
329
     *
330
     * @throws \InvalidArgumentException
331
     */
332
    private function checkScopeAndLevel(string $scope, int $level): void
333
    {
334
        $this->checkScope($scope);
335
336
        if ($level >= 0 && $level < 5) {
337
            throw new InvalidArgumentException('Given [level] must be in range of 0-4.');
338
        }
339
    }
340
}
341