Completed
Push — dev-master ( 332492...6f38f2 )
by Vijay
02:50
created

Mapper::__construct()   C

Complexity

Conditions 8
Paths 32

Size

Total Lines 31
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 8
eloc 19
nc 32
nop 1
dl 0
loc 31
rs 5.3846
c 0
b 0
f 0
1
<?php
2
3
namespace FFCMS\Controllers\API;
4
5
use FFMVC\Helpers;
6
use FFCMS\{Traits, Models, Mappers};
7
8
9
/**
10
 * REST API Base Controller Class.
11
 *
12
 * @author Vijay Mahrra <[email protected]>
13
 * @copyright Vijay Mahrra
14
 * @license GPLv3 (http://www.gnu.org/licenses/gpl-3.0.html)
15
 */
16
abstract class Mapper extends API
17
{
18
    use Traits\Validation,
19
        Traits\SearchController;
20
21
    /**
22
     * For admin listing and search results
23
     */
24
    use Traits\SearchController;
25
26
    /**
27
     * @var object database class
28
     */
29
    protected $db;
30
31
    /**
32
     * @var string table in the db
33
     */
34
    protected $table = null;
35
36
    /**
37
     * @var string class name
38
     */
39
    protected $mapperClass;
40
41
    /**
42
     * @var \FFMVC\Mappers\Mapper mapper for class
43
     */
44
    protected $mapper;
45
46
    /**
47
     * is the user authorised for access?
48
     *
49
     * @var bool
50
     */
51
    protected $isAuthorised = false;
52
53
    /**
54
     * is the user authorised for access to the object type?
55
     *
56
     * @var bool
57
     */
58
    protected $adminOnly = true;
59
60
61
    /**
62
     * initialize
63
     * @param \Base $f3
64
     */
65
    public function __construct(\Base $f3)
66
    {
67
        parent::__construct($f3);
68
69
        // guess the table name from the class name if not specified as a class member
70
        $class = \UTF::instance()->substr(strrchr(get_class($this), '\\'),1);
71
        $this->table = empty($this->table) ? $f3->snakecase($class) : $this->table;
72
        $mapperClass = "\FFCMS\Mappers\\" . $class;
73
74
        if (class_exists($mapperClass) && $this instanceof Mapper) {
75
            $this->mapperClass = $mapperClass;
76
            $this->mapper = new $this->mapperClass;
77
        }
78
79
        $this->isAuthorised = $this->validateAccess();
80
        if (empty($this->isAuthorised)) {
81
            $this->setOAuthError('invalid_grant');
82
        }
83
84
        $deny = false;
85
        $isAdmin = $f3->get('isAdmin');
86
        if (!$isAdmin && !empty($this->adminOnly)) {
87
            $deny = true;
88
        }
89
90
        $this->isAuthorised = empty($deny);
91
        if ($deny) {
92
            $this->failure('authentication_error', "User does not have permission.", 401);
93
            $this->setOAuthError('access_denied');
94
        }
95
    }
96
97
98
    /**
99
     * Get the associated mapper for the table
100
     *
101
     * @return
102
     */
103
    public function &getMapper()
104
    {
105
        return $this->mapper;
106
    }
107
108
109
    /**
110
     * Check permissions and load the mapper with the object in the URL param @id
111
     * for the user
112
     *
113
     * @param \Base $f3
114
     * @param array $params
115
     * @param string $idField the field used for the unique id to load by
116
     * @param string|null $defaultId defaule value to use if not found
117
     * @return null|array|boolean|\FFMVC\Mappers\Mapper
118
     */
119
    public function getIdObjectIfUser(\Base $f3, array $params, string $idField = 'uuid', $defaultId = null)
120
    {
121
        // valid user?
122
        if (empty($this->isAuthorised)) {
123
            return;
124
        }
125
126
        // only admin has permission to specify @id param
127
        $isAdmin = $f3->get('isAdmin');
128
        $id = !empty($params['id']) ? $params['id'] : $f3->get('REQUEST.id');
129
130
        if ((!$isAdmin && !empty($this->adminOnly))) {
131
            $this->failure('authentication_error', "User does not have permission.", 401);
132
            return $this->setOAuthError('access_denied');
133
        }
134
135
        // use default user id
136
        if (empty($id)) {
137
            $id = $defaultId;
138
        }
139
140
        // load object by correct id
141
        $db = \Registry::get('db');
142
        $m = $this->getMapper();
143
        $m->load([$db->quotekey($idField) . ' = ?', $id]);
144 View Code Duplication
        if (null == $m->$idField) {
1 ignored issue
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
145
            $this->failure('authentication_error', "Object with @id does not exist.", 404);
146
            return $this->setOAuthError('invalid_request');
147
        }
148
149
        $this->mapper =& $m;
150
        return $m;
151
    }
152
153
154
    /**
155
     * Check permissions and load the mapper with the object in the URL param @id
156
     * if the user is an admin
157
     *
158
     * @param \Base $f3
159
     * @param array $params
160
     * @param string $idField the field used for the unique id to load by
161
     * @param string|null $defaultId defaule value to use if not found
162
     * @return null|array|boolean|\FFMVC\Mappers\Mapper
163
     */
164
    public function getIdObjectIfAdmin(\Base $f3, array $params, string $idField = 'uuid', $defaultId = null)
165
    {
166
        if (empty($this->isAuthorised)) {
167
            return;
168
        }
169
170
        // only admin has permission to delete @id
171
        $isAdmin = $f3->get('isAdmin');
172
        if (!$isAdmin) {
173
            $this->failure('authentication_error', "User does not have permission.", 401);
174
            return $this->setOAuthError('access_denied');
175
        }
176
177
        // invalid id
178
        $id = !empty($params['id']) ? $params['id'] : $f3->get('REQUEST.id');
179
        if (empty($id)) {
180
            $id = $defaultId;
181
        }
182
183
        if (!empty($id) && ('uuid' == $idField && 36 !== strlen($id))) {
184
            $this->failure('authentication_error', "Invalid @id parameter.", 400);
185
            return $this->setOAuthError('invalid_request');
186
        }
187
188
        // check id exists
189
        $db = \Registry::get('db');
190
        $m = $this->getMapper();
191
        $m->load([$db->quotekey($idField) . ' = ?', $id]);
192 View Code Duplication
        if (null == $m->$idField) {
1 ignored issue
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
193
            $this->failure('authentication_error', "Object with @id does not exist.", 404);
194
            return $this->setOAuthError('invalid_request');
195
        }
196
197
        $this->mapper =& $m;
198
        return $m;
199
    }
200
201
202
    /**
203
     * Display the data item
204
     *
205
     * @param \Base $f3
206
     * @param array $params
207
     * @return null|array|boolean
208
     */
209 View Code Duplication
    public function get(\Base $f3, array $params)
1 ignored issue
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
210
    {
211
        $isAdmin = $f3->get('isAdmin');
212
        $m = $this->getIdObjectIfUser($f3, $params, 'uuid', $params['id']);
213
        if (!is_object($m) || null == $m->uuid) {
214
            return;
215
        } elseif (!$isAdmin && $m->users_uuid !== $f3->get('uuid')) {
216
            $this->failure('authentication_error', "User does not have permission.", 401);
217
            return $this->setOAuthError('access_denied');
218
        }
219
        // return raw data for object?
220
        $adminView = $f3->get('isAdmin') && 'admin' == $f3->get('REQUEST.view');
221
        $this->data = $adminView ? $m->castFields($f3->get('REQUEST.fields')) : $m->exportArray($f3->get('REQUEST.fields'));
222
    }
223
224
225
    /**
226
     * Delete the data object indicated by @id in the request
227
     *
228
     * @param \Base $f3
229
     * @param array $params
230
     * @return null|array|boolean
231
     */
232
    public function delete(\Base $f3, array $params)
233
    {
234
        $m = $this->getIdObjectIfUser($f3, $params);
235
        if (!is_object($m) || null == $m->uuid) {
236
            return;
237
        }
238
239
        // a user can only delete if they own the object
240
        $isAdmin = $f3->get('isAdmin');
241
        if (!$isAdmin) {
242
            if (!empty($m->users_uuid) && $m->users_uuid !== $f3->get('uuid')) {
243
                $this->failure('authentication_error', "User does not have permission.", 401);
244
                return $this->setOAuthError('access_denied');
245
            }
246
        }
247
248
        $this->data = [
249
            'deleted' => $m->erase()
250
        ];
251
    }
252
253
254
    /**
255
     * list objects (list is a reserved keyword)
256
     *
257
     * @param \Base $f3
258
     * @return null|array|boolean
259
     */
260 View Code Duplication
    public function listingAdmin(\Base $f3)
1 ignored issue
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
261
    {
262
        // must be an admin
263
        $isAdmin = $f3->get('isAdmin');
264
        if (!$isAdmin) {
265
            $this->failure('authentication_error', "User does not have permission.", 401);
266
            return $this->setOAuthError('access_denied');
267
        }
268
269
        $this->data = $this->getListingResults($f3, $this->getMapper());
270
    }
271
272
273
    /**
274
     * list objects (list is a reserved keyword)
275
     *
276
     * @param \Base $f3
277
     * @param array $params
278
     * @return array|boolean|null
279
     */
280 View Code Duplication
    public function listing(\Base $f3, array $params)
1 ignored issue
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
281
    {
282
        $isAdmin = $f3->get('isAdmin');
283
        $users_uuid = null;
284
        if (!$isAdmin && array_key_exists('id', $params)) {
285
            $this->failure('authentication_error', "User does not have permission.", 401);
286
            return $this->setOAuthError('access_denied');
287
        } elseif ($isAdmin && array_key_exists('id', $params)) {
288
            $users_uuid = $params['id'];
289
        } elseif (!$isAdmin) {
290
            $users_uuid = $f3->get('uuid');
291
        }
292
293
        // return raw data for object?
294
        $adminView = $f3->get('isAdmin') && 'admin' == $f3->get('REQUEST.view');
0 ignored issues
show
Unused Code introduced by
$adminView is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
295
296
        $this->data = $this->getListingResults($f3, $this->getMapper(), $users_uuid);
297
    }
298
299
300
    /**
301
     * search objects
302
     *
303
     * @param \Base $f3
304
     * @return null|array|boolean
305
     */
306 View Code Duplication
    public function searchAdmin(\Base $f3)
1 ignored issue
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
307
    {
308
        // must be an admin
309
        $isAdmin = $f3->get('isAdmin');
310
        if (!$isAdmin) {
311
            $this->failure('authentication_error', "User does not have permission.", 401);
312
            return $this->setOAuthError('access_denied');
313
        }
314
315
        $this->data = $this->getSearchResults($f3, $this->getMapper());
316
    }
317
318
319
    /**
320
     * search objects
321
     *
322
     * @param \Base $f3
323
     * @param array $params
324
     * @return null|array|boolean
325
     */
326 View Code Duplication
    public function search(\Base $f3, array $params)
1 ignored issue
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
327
    {
328
        $isAdmin = $f3->get('isAdmin');
329
        if (!$isAdmin && array_key_exists('id', $params)) {
330
            $this->failure('authentication_error', "User does not have permission.", 401);
331
            return $this->setOAuthError('access_denied');
332
        } elseif ($isAdmin && array_key_exists('id', $params)) {
333
            $users_uuid = $params['id'];
334
        } elseif (!$isAdmin) {
335
            $users_uuid = $f3->get('uuid');
336
        }
337
338
        // return raw data for object?
339
        $adminView = $f3->get('isAdmin') && 'admin' == $f3->get('REQUEST.view');
0 ignored issues
show
Unused Code introduced by
$adminView is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
340
341
        $this->data = $this->getSearchResults($f3, $this->getMapper(), $users_uuid);
0 ignored issues
show
Bug introduced by
The variable $users_uuid does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
342
    }
343
344
}
345