Completed
Push — develop ( 62e795...4dd78e )
by Abdelrahman
01:51
created

AuthorizedController::isClassName()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 1
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Cortex\Foundation\Http\Controllers;
6
7
use ReflectionClass;
8
use ReflectionMethod;
9
use Illuminate\Support\Str;
10
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
11
12
class AuthorizedController extends AuthenticatedController
13
{
14
    use AuthorizesRequests;
15
16
    /**
17
     * The resource Ability Map.
18
     *
19
     * @var array
20
     */
21
    protected $resourceAbilityMap = [
22
        'activities' => 'audit',
23
        'index' => 'list',
24
        'logs' => 'audit',
25
    ];
26
27
    /**
28
     * The resource methods without models.
29
     *
30
     * @var array
31
     */
32
    protected $resourceMethodsWithoutModels = [];
33
34
    /**
35
     * Resource action whitelist.
36
     * Array of resource actions to skip mapping to abilities automatically.
37
     *
38
     * @var array
39
     */
40
    protected $resourceActionWhitelist = [];
41
42
    /**
43
     * Create a new authorized controller instance.
44
     *
45
     * @throws \Illuminate\Auth\Access\AuthorizationException
46
     */
47
    public function __construct()
48
    {
49
        parent::__construct();
50
51
        if (property_exists(static::class, 'resource')) {
52
            $this->isClassName($this->resource) ? $this->authorizeResource($this->resource) : $this->authorizeGeneric($this->resource);
0 ignored issues
show
Bug introduced by
The property resource does not seem to exist. Did you mean resourceAbilityMap?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 135 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
53
        } else {
54
            // At this stage, sessions still not loaded yet, and `AuthorizationException`
55
            // depends on seesions to flash redirection error msg, so delegate to a middleware
56
            // Since Laravel 5.3 controller constructors executed before middleware to be able to append
57
            // new middleware to the pipeline then all middleware executed together, and sessions started in `StartSession` middleware
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 134 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
58
            $this->middleware('can:null');
59
        }
60
    }
61
62
    /**
63
     * {@inheritdoc}
64
     */
65
    public function authorizeResource($model, $parameter = null, array $options = [], $request = null): void
66
    {
67
        $middleware = [];
68
        $parameter = $parameter ?: Str::snake(class_basename($model));
69
70
        foreach ($this->mapResourceAbilities() as $method => $ability) {
71
            $modelName = in_array($method, $this->resourceMethodsWithoutModels()) ? $model : $parameter;
72
73
            $middleware["can:{$ability},{$modelName}"][] = $method;
74
        }
75
76
        foreach ($middleware as $middlewareName => $methods) {
77
            $this->middleware($middlewareName, $options)->only($methods);
78
        }
79
    }
80
81
    /**
82
     * {@inheritdoc}
83
     */
84
    public function authorizeGeneric($resource): void
85
    {
86
        $middleware = [];
87
88
        foreach ($this->mapResourceAbilities() as $method => $ability) {
89
            $middleware["can:{$resource}"][] = $method;
90
        }
91
92
        foreach ($middleware as $middlewareName => $methods) {
93
            $this->middleware($middlewareName)->only($methods);
94
        }
95
    }
96
97
    /**
98
     * Map resource actions to resource abilities.
99
     *
100
     * @return array
101
     */
102
    protected function mapResourceAbilities(): array
103
    {
104
        // Reflect calling controller
105
        $controller = new ReflectionClass(static::class);
106
107
        // Get public methods and filter magic methods
108
        $methods = array_filter($controller->getMethods(ReflectionMethod::IS_PUBLIC), function ($item) use ($controller) {
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 122 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
109
            return $item->class === $controller->name && mb_substr($item->name, 0, 2) !== '__' && ! in_array($item->name, $this->resourceActionWhitelist);
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 154 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
110
        });
111
112
        // Get controller actions
113
        $actions = array_combine($items = array_map(function ($action) {
114
            return $action->name;
115
        }, $methods), $items);
116
117
        // Map resource actions to resourse abilities
118
        array_walk($actions, function ($value, $key) use (&$actions) {
119
            $actions[$key] = array_get($this->resourceAbilityMap(), $key, $value);
120
        });
121
122
        return $actions;
123
    }
124
125
    /**
126
     * {@inheritdoc}
127
     */
128
    protected function resourceAbilityMap(): array
129
    {
130
        return array_merge(parent::resourceAbilityMap(), $this->resourceAbilityMap);
131
    }
132
133
    /**
134
     * {@inheritdoc}
135
     */
136
    protected function resourceMethodsWithoutModels()
137
    {
138
        return array_merge(parent::resourceMethodsWithoutModels(), $this->resourceMethodsWithoutModels);
139
    }
140
141
    /**
142
     * Checks if the given string looks like a fully qualified class name.
143
     *
144
     * @param  string  $value
145
     * @return bool
146
     */
147
    protected function isClassName($value)
148
    {
149
        return strpos($value, '\\') !== false;
150
    }
151
}
152