Completed
Push — develop ( c3857f...9623b5 )
by Mohamed
08:41
created

Permission::isInPublicProjectContext()   A

Complexity

Conditions 4
Paths 6

Size

Total Lines 10
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 4

Importance

Changes 1
Bugs 0 Features 1
Metric Value
c 1
b 0
f 1
dl 0
loc 10
ccs 6
cts 6
cp 1
rs 9.2
cc 4
eloc 6
nc 6
nop 2
crap 4
1
<?php
2
3
/*
4
 * This file is part of the Tinyissue package.
5
 *
6
 * (c) Mohamed Alsharaf <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
namespace Tinyissue\Http\Middleware;
13
14
use Closure;
15
use Tinyissue\Model\User;
16
use Illuminate\Http\Request;
17
use Illuminate\Routing\Route;
18
use Illuminate\Contracts\Auth\Guard;
19
use Tinyissue\Model\Project as ProjectModel;
20
use Tinyissue\Model\Permission as PermissionModel;
21
use Illuminate\Database\Eloquent\Model as ModelAbstract;
22
23
/**
24
 * Permission is a Middleware class to for checking if current user has the permission to access the request.
25
 *
26
 * @author Mohamed Alsharaf <[email protected]>
27
 */
28
class Permission
29
{
30
    /**
31
     * The Guard implementation.
32
     *
33
     * @var Guard
34
     */
35
    protected $auth;
36
37
    /**
38
     * List of permissions that can be accessed by public users.
39
     *
40
     * @var array
41
     */
42
    protected $publicAccess = [
43
        'issue-view',
44
    ];
45
46
    /**
47
     * Ordered list of contexts.
48
     *
49
     * @var array
50
     */
51
    protected $contexts = [
52
        'comment',
53
        'attachment',
54
        'issue',
55
        'project',
56
    ];
57
58
    /**
59
     * Create a new filter instance.
60
     *
61
     * @param Guard $auth
62
     */
63 45
    public function __construct(Guard $auth)
64
    {
65 45
        $this->auth = $auth;
66 45
    }
67
68
    /**
69
     * Handle an incoming request.
70
     *
71
     * @param Request  $request
72
     * @param \Closure $next
73
     *
74
     * @return mixed
75
     */
76 44
    public function handle(Request $request, Closure $next)
77
    {
78 44
        $permission = $this->getPermission($request);
79
80
        // Can't access if public project disabled or user does not have access
81 44
        if (!$this->isInPublicProjectContext($request, $permission) && !$this->canAccess($request, $permission)) {
82 8
            abort(401);
83
        }
84
85 40
        return $next($request);
86
    }
87
88
    /**
89
     * Whether or not the current context is in public project.
90
     *
91
     * @param Request $request
92
     * @param string  $permission
93
     *
94
     * @return bool
95
     */
96 44
    protected function isInPublicProjectContext(Request $request, $permission)
97
    {
98
        /** @var ProjectModel|null $project */
99 44
        $project         = $request->route()->getParameter('project');
100 44
        $isPublicEnabled = app('tinyissue.settings')->isPublicProjectsEnabled();
101 44
        $isPublicAccess  = in_array($permission, $this->publicAccess);
102 44
        $isPublicProject = $project instanceof ProjectModel && $project->isPublic();
103
104 44
        return $isPublicEnabled && $isPublicAccess && $isPublicProject;
105
    }
106
107
    /**
108
     * Whether or not the user can access the current context.
109
     *
110
     * @param Request $request
111
     * @param string  $permission
112
     *
113
     * @return bool
114
     */
115 44
    protected function canAccess(Request $request, $permission)
116
    {
117 44
        $user = $this->auth->user();
118
119 44
        return !(!is_null($user) && (!$user->permission($permission) || !$this->canAccessContext($user, $request->route(), $permission)));
0 ignored issues
show
Compatibility introduced by
$user of type object<Illuminate\Contracts\Auth\Authenticatable> is not a sub-type of object<Tinyissue\Model\User>. It seems like you assume a concrete implementation of the interface Illuminate\Contracts\Auth\Authenticatable to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
Documentation introduced by
$request->route() is of type object|string, but the function expects a object<Illuminate\Routing\Route>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
120
    }
121
122
    /**
123
     * Whether or not the user has a valid permission in current context
124
     * e.g. can access the issue or the project.
125
     *
126
     * @param User   $user
127
     * @param Route  $route
128
     * @param string $permission
129
     *
130
     * @return bool
131
     */
132 41
    public function canAccessContext(User $user, Route $route, $permission)
133
    {
134
        // Can access all projects
135 41
        if ($user->permission(PermissionModel::PERM_PROJECT_ALL)) {
136 36
            return true;
137
        }
138
139
        // Can access the current context
140 10
        $context = $this->getCurrentContext($route);
141 10
        $action  = $permission == PermissionModel::PERM_ISSUE_MODIFY ? 'canEdit' : 'canView';
142
143 10
        return $context->$action($user);
144
    }
145
146
    /**
147
     * Return the model object of the current context.
148
     * We check the lowest ( Comment ) first, to the highest ( Project ).
149
     *
150
     * @param Route $route
151
     *
152
     * @return ModelAbstract
153
     */
154 10
    protected function getCurrentContext(Route $route)
155
    {
156 10
        foreach ($this->contexts as $context) {
157 10
            $parameter = $route->getParameter($context);
158 10
            if ($parameter instanceof ModelAbstract) {
159 10
                return $parameter;
160
            }
161
        }
162
163
        return $route->getParameter('project');
0 ignored issues
show
Bug Compatibility introduced by
The expression $route->getParameter('project'); of type string|object adds the type string to the return on line 163 which is incompatible with the return type documented by Tinyissue\Http\Middlewar...sion::getCurrentContext of type Illuminate\Database\Eloquent\Model.
Loading history...
164
    }
165
166
    /**
167
     * Returns the permission defined in route action.
168
     *
169
     * @param Request $request
170
     *
171
     * @return mixed
172
     */
173 44
    protected function getPermission(Request $request)
174
    {
175 44
        $actions = $request->route()->getAction();
176
177 44
        return $actions['permission'];
178
    }
179
}
180