Test Setup Failed
Push — master ( 6b4d13...3933f9 )
by Raffael
07:23
created

User::head()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 6
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 3
nc 1
nop 2
1
<?php
2
3
declare(strict_types=1);
4
5
/**
6
 * balloon
7
 *
8
 * @copyright   Copryright (c) 2012-2018 gyselroth GmbH (https://gyselroth.com)
9
 * @license     GPL-3.0 https://opensource.org/licenses/GPL-3.0
10
 */
11
12
namespace Balloon\App\Api\v1;
13
14
use Balloon\Server;
15
use Balloon\Server\AttributeDecorator;
16
use Balloon\Server\User\Exception;
17
use Micro\Http\Response;
18
use MongoDB\BSON\ObjectId;
19
20
class User
21
{
22
    /**
23
     * User.
24
     *
25
     * @var User
26
     */
27
    protected $user;
28
29
    /**
30
     * Server.
31
     *
32
     * @var Server
33
     */
34
    protected $server;
35
36
    /**
37
     * Decorator.
38
     *
39
     * @var AttributeDecorator
40
     */
41
    protected $decorator;
42
43
    /**
44
     * Initialize.
45
     *
46
     * @param Server $server
47
     */
48
    public function __construct(Server $server, AttributeDecorator $decorator)
49
    {
50
        $this->user = $server->getIdentity();
0 ignored issues
show
Documentation Bug introduced by
It seems like $server->getIdentity() of type object<Balloon\Server\User> is incompatible with the declared type object<Balloon\App\Api\v1\User> of property $user.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
51
        $this->server = $server;
52
        $this->decorator = $decorator;
53
    }
54
55
    /**
56
     * @apiDefine _getUser
57
     *
58
     * @apiParam (GET Parameter) {string[]} uid Either a single uid (user id) or a uname (username) must be given (admin privilege required).
59
     * @apiParam (GET Parameter) {string[]} uname Either a single uid (user id) or a uname (username) must be given (admin privilege required).
60
     *
61
     * @apiErrorExample {json} Error-Response (No admin privileges):
62
     * HTTP/1.1 403 Forbidden
63
     * {
64
     *      "status": 403,
65
     *      "data": {
66
     *          "error": "Balloon\\Filesystem\\Acl\\Exception\\Forbidden",
67
     *          "message": "submitted parameters require to have admin privileges",
68
     *          "code": 41
69
     *      }
70
     * }
71
     *
72
     * @apiErrorExample {json} Error-Response (User not found):
73
     * HTTP/1.1 404 Not Found
74
     * {
75
     *      "status": 404,
76
     *      "data": {
77
     *          "error": "Balloon\\Server\\User\\Exception",
78
     *          "message": "requested user was not found",
79
     *          "code": 53
80
     *      }
81
     * }
82
     *
83
     * @apiErrorExample {json} Error-Response (Invalid argument):
84
     * HTTP/1.1 400 Bad Request
85
     * {
86
     *      "status": 400,
87
     *      "data": {
88
     *          "error": "Balloon\\Exception\\InvalidArgument",
89
     *          "message": "provide either uid (user id) or uname (username)",
90
     *          "Code": 0
91
     *      }
92
     * }
93
     */
94
95
    /**
96
     * @apiDefine _getUsers
97
     *
98
     * @apiParam (GET Parameter) {string[]} uid Either a single uid (user id) as string or multiple as an array or a single uname (username) as string or multiple usernames as array must be given.
99
     * @apiParam (GET Parameter) {string[]} uname Either a single uid (userid) as string or multiple as an array or a single uname (username) as string or multiple usernames as array must be given.
100
     */
101
102
    /**
103
     * Get user instance.
104
     *
105
     * @param string $id
106
     * @param string $uname
107
     * @param bool   $require_admin
108
     *
109
     * @return User
110
     */
111
    public function _getUser(?string $id = null, ?string $uname = null, bool $require_admin = false)
112
    {
113
        if (null !== $id || null !== $uname || true === $require_admin) {
114
            if ($this->user->isAdmin()) {
0 ignored issues
show
Bug introduced by
The method isAdmin() does not seem to exist on object<Balloon\App\Api\v1\User>.

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...
115
                if (null !== $id && null !== $uname) {
116
                    throw new Exception\InvalidArgument(
117
                        'provide either id (user id) or uname (username)',
118
                        Exception\InvalidArgument::IDENTIFIER_NOT_UNIQUE
119
                    );
120
                }
121
122
                if (null !== $id) {
123
                    return $this->server->getUserById(new ObjectId($id));
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this->server->ge...DB\BSON\ObjectId($id)); (Balloon\Server\User) is incompatible with the return type documented by Balloon\App\Api\v1\User::_getUser of type Balloon\App\Api\v1\User.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
124
                }
125
126
                return $this->server->getUserByName($uname);
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this->server->getUserByName($uname); (Balloon\Server\User) is incompatible with the return type documented by Balloon\App\Api\v1\User::_getUser of type Balloon\App\Api\v1\User.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
127
            }
128
129
            throw new Exception\NotAdmin('submitted parameters require admin privileges');
130
        }
131
132
        return $this->user;
133
    }
134
135
    /**
136
     * @api {get} /api/v1/user/groups Group membership
137
     * @apiVersion 1.0.0
138
     * @apiName getGroups
139
     * @apiUse _getUser
140
     * @apiGroup User
141
     * @apiPermission none
142
     * @apiDescription Get all user groups
143
     * If you want to receive your own groups you have to leave the parameters uid and uname empty.
144
     * Requesting this api with parameter uid or uname requires admin privileges.
145
     *
146
     * @apiExample Example usage:
147
     * curl -XGET "https://SERVER/api/v1/user/groups?pretty"
148
     * curl -XGET "https://SERVER/api/v1/user/544627ed3c58891f058b4611/groups?pretty"
149
     * curl -XGET "https://SERVER/api/v1/user/groups?uname=loginuser&pretty"
150
     *
151
     * @apiSuccess {number} status Status Code
152
     * @apiSuccess {string[]} data  All groups with membership
153
     * @apiSuccessExample {json} Success-Response:
154
     * HTTP/1.1 200 OK
155
     * {
156
     *     "status": 200,
157
     *     "data": [
158
     *          "group1",
159
     *          "group2",
160
     *     ]
161
     * }
162
     *
163
     * @param string $uid
164
     * @param string $uname
165
     */
166
    public function getGroups(?string $uid = null, ?string $uname = null): Response
167
    {
168
        $result = $this->_getUser($uid, $uname)->getGroups();
169
170
        return (new Response())->setCode(200)->setBody([
171
            'code' => 200,
172
            'data' => $result,
173
        ]);
174
    }
175
176
    /**
177
     * @api {get} /api/v1/user/shares Share membership
178
     * @apiVersion 1.0.0
179
     * @apiName getShares
180
     * @apiUse _getUser
181
     * @apiGroup User
182
     * @apiPermission none
183
     * @apiDescription Get all shares
184
     * If you want to receive your own shares (member or owner) you have to leave the parameters uid and uname empty.
185
     * Requesting this api with parameter uid or uname requires admin privileges.
186
     *
187
     * @apiExample Example usage:
188
     * curl -XGET "https://SERVER/api/v1/user/shares?pretty"
189
     * curl -XGET "https://SERVER/api/v1/user/544627ed3c58891f058b4611/shares?pretty"
190
     * curl -XGET "https://SERVER/api/v1/user/shares?uname=loginuser&pretty"
191
     *
192
     * @apiSuccess {number} status Status Code
193
     * @apiSuccess {string[]} data  All shares with membership
194
     * @apiSuccessExample {json} Success-Response:
195
     * HTTP/1.1 200 OK
196
     * {
197
     *     "status": 200,
198
     *     "data": [
199
     *          "shareid1",
200
     *          "shareid2",
201
     *     ]
202
     * }
203
     *
204
     * @param string $uid
205
     * @param string $uname
206
     *
207
     * @return Response
208
     */
209
    public function getShares(?string $uid = null, ?string $uname = null): Response
210
    {
211
        $result = $this->_getUser($uid, $uname)->getShares();
212
213
        return (new Response())->setCode(200)->setBody([
214
            'code' => 200,
215
            'data' => $result,
216
        ]);
217
    }
218
219
    /**
220
     * @api {get} /api/v1/user/quota-usage Quota usage
221
     * @apiVersion 1.0.0
222
     * @apiName getQuotaUsage
223
     * @apiUse _getUser
224
     * @apiGroup User
225
     * @apiPermission none
226
     * @apiDescription Get user quota usage (including hard,soft,used and available space).
227
     * If you want to receive your own quota you have to leave the parameters uid and uname empty.
228
     * Requesting this api with parameter uid or uname requires admin privileges.
229
     *
230
     * @apiExample Example usage:
231
     * curl -XGET "https://SERVER/api/v1/user/quota-usage?pretty"
232
     * curl -XGET "https://SERVER/api/v1/user/544627ed3c58891f058b4611/quota-usage?pretty"
233
     * curl -XGET "https://SERVER/api/v1/user/quota-usage?uname=loginuser&pretty"
234
     *
235
     * @apiSuccess {number} status Status Code
236
     * @apiSuccess {object} data Quota stats
237
     * @apiSuccess {number} data.used Used quota in bytes
238
     * @apiSuccess {number} data.available Quota left in bytes
239
     * @apiSuccess {number} data.hard_quota Hard quota in bytes
240
     * @apiSuccess {number} data.soft_quota Soft quota (Warning) in bytes
241
     * @apiSuccessExample {json} Success-Response:
242
     * HTTP/1.1 200 OK
243
     * {
244
     *     "status": 200,
245
     *     "data": {
246
     *         "used": 15543092,
247
     *         "available": 5353166028,
248
     *         "hard_quota": 5368709120,
249
     *         "soft_quota": 5368709120
250
     *     }
251
     * }
252
     *
253
     * @param string $uid
254
     * @param string $uname
255
     *
256
     * @return Response
257
     */
258
    public function getQuotaUsage(?string $uid = null, ?string $uname = null): Response
259
    {
260
        $result = $this->_getUser($uid, $uname)->getQuotaUsage();
261
262
        return (new Response())->setCode(200)->setBody([
263
            'code' => 200,
264
            'data' => $result,
265
        ]);
266
    }
267
268
    /**
269
     * @api {get} /api/v1/user/attributes User attributes
270
     * @apiVersion 1.0.0
271
     * @apiName getAttributes
272
     * @apiUse _getUser
273
     * @apiGroup User
274
     * @apiPermission none
275
     * @apiDescription Get all user attributes including username, mail, id,....
276
     * If you want to receive your own attributes you have to leave the parameters uid and uname empty.
277
     * Requesting this api with parameter uid or uname requires admin privileges.
278
     *
279
     * @apiExample Example usage:
280
     * curl -XGET "https://SERVER/api/v1/user/attributes?pretty"
281
     * curl -XGET "https://SERVER/api/v1/user/544627ed3c58891f058b4611/attributes?pretty"
282
     * curl -XGET "https://SERVER/api/v1/user/attributes?uname=loginser&pretty"
283
     *
284
     * @apiSuccess (200 OK) {number} status Status Code
285
     * @apiSuccess (200 OK) {object[]} user attributes
286
     *
287
     * @apiSuccessExample {json} Success-Response:
288
     * HTTP/1.1 200 OK
289
     * {
290
     *      "status": 200,
291
     *      "data": [] //shortened
292
     * }
293
     *
294
     * @param string $uid
295
     * @param string $uname
296
     * @param string $attributes
297
     *
298
     * @return Response
299
     */
300
    public function getAttributes(?string $uid = null, ?string $uname = null, array $attributes = []): Response
301
    {
302
        $result = $this->decorator->decorate($this->_getUser($uid, $uname), $attributes);
0 ignored issues
show
Documentation introduced by
$this->_getUser($uid, $uname) is of type object<Balloon\App\Api\v1\User>, but the function expects a object<Balloon\Server\RoleInterface>.

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...
303
304
        return (new Response())->setCode(200)->setBody([
305
            'code' => 200,
306
            'data' => $result,
307
        ]);
308
    }
309
310
    /**
311
     * @api {head} /api/v1/user?uid=:uid User exists?
312
     * @apiVersion 1.0.0
313
     * @apiName postQuota
314
     * @apiUse _getUser
315
     * @apiGroup User
316
     * @apiPermission admin
317
     * @apiDescription Check if user account exists
318
     *
319
     * @apiExample Example usage:
320
     * curl -XHEAD "https://SERVER/api/v1/user"
321
     * curl -XHEAD "https://SERVER/api/v1/user/544627ed3c58891f058b4611"
322
     * curl -XHEAD "https://SERVER/api/v1/user?uname=loginuser"
323
     *
324
     * @apiSuccessExample {json} Success-Response:
325
     * HTTP/1.1 204 No Content
326
     *
327
     * @param string $uname
328
     * @param string $uid
329
     *
330
     * @return Response
331
     */
332
    public function head(?string $uid = null, ?string $uname = null): Response
333
    {
334
        $result = $this->_getUser($uid, $uname);
0 ignored issues
show
Unused Code introduced by
$result 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...
335
336
        return (new Response())->setCode(204);
337
    }
338
339
    /**
340
     * @api {get} /api/v1/user/whoami Who am I?
341
     * @apiVersion 1.0.0
342
     * @apiName getWhoami
343
     * @apiUse _getUser
344
     * @apiGroup User
345
     * @apiPermission none
346
     * @apiDescription Get the username of the authenticated user
347
     * If you want to receive your own username you have to leave the parameters uid and uname empty.
348
     * Requesting this api with parameter uid or uname requires admin privileges.
349
     *
350
     * @apiExample Example usage:
351
     * curl -XGET "https://SERVER/api/v2/user/whoami?pretty"
352
     *
353
     * @apiSuccess {number} code HTTP status code
354
     * @apiSuccess {string} data Username
355
     * @apiSuccessExample {json} Success-Response:
356
     * HTTP/1.1 200 OK
357
     * {
358
     *     "code": 200,
359
     *     "data": "user"
360
     * }
361
     *
362
     * @param string $uid
0 ignored issues
show
Bug introduced by
There is no parameter named $uid. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
363
     * @param string $uname
0 ignored issues
show
Bug introduced by
There is no parameter named $uname. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
364
     *
365
     * @return Response
366
     */
367
    public function getWhoami(array $attributes = []): Response
0 ignored issues
show
Unused Code introduced by
The parameter $attributes is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
368
    {
369
        return (new Response())->setCode(200)->setBody([
370
            'code' => 200,
371
            'data' => $this->_getUser()->getUsername(),
0 ignored issues
show
Bug introduced by
The method getUsername() does not seem to exist on object<Balloon\App\Api\v1\User>.

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...
372
        ]);
373
    }
374
375
    /**
376
     * @api {get} /api/v1/user/node-attribute-summary Node attribute summary
377
     * @apiVersion 1.0.0
378
     * @apiName getNodeAttributeSummary
379
     * @apiUse _getUser
380
     * @apiGroup User
381
     * @apiPermission none
382
     * @apiDescription Get summary and usage of specific node attributes
383
     * If you want to receive your own node summary you have to leave the parameters uid and uname empty.
384
     * Requesting this api with parameter uid or uname requires admin privileges.
385
     *
386
     * @apiExample Example usage:
387
     * curl -XGET "https://SERVER/api/v1/user/node-attribute-summary?pretty"
388
     * curl -XGET "https://SERVER/api/v1/user/544627ed3c58891f058b4611/node-attribute-summary?pretty"
389
     * curl -XGET "https://SERVER/api/v1/user/node-attribute-summary?uname=loginuser&pretty"
390
     *
391
     * @param string $id
392
     * @param string $uname
393
     * @param string $attributes
394
     * @param int    $limit
395
     *
396
     * @return Response
397
     */
398
    public function getNodeAttributeSummary(?string $id = null, ?string $uname = null, array $attributes = [], int $limit = 25): Response
399
    {
400
        $result = $this->_getUser($id, $uname)->getNodeAttributeSummary($attributes, $limit);
0 ignored issues
show
Documentation introduced by
$attributes is of type array, but the function expects a string|null.

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...
401
402
        return (new Response())->setCode(200)->setBody([
403
            'code' => 200,
404
            'data' => $result,
405
        ]);
406
    }
407
408
    /**
409
     * @api {post} /api/v1/user/quota?uid=:uid Set quota
410
     * @apiVersion 1.0.0
411
     * @apiName postQuota
412
     * @apiUse _getUser
413
     * @apiGroup User
414
     * @apiPermission admin
415
     * @apiDescription Set quota for user
416
     *
417
     * @apiExample Example usage:
418
     * curl -XPOST -d hard=10000000 -d soft=999999 "https://SERVER/api/v1/user/quota"
419
     * curl -XPOST -d hard=10000000 -d soft=999999 "https://SERVER/api/v1/user/544627ed3c58891f058b4611/quota"
420
     * curl -XPOST -d hard=10000000 -d soft=999999 "https://SERVER/api/v1/user/quota?uname=loginuser"
421
     *
422
     * @apiParam (GET Parameter) {number} hard The new hard quota in bytes
423
     * @apiParam (GET Parameter) {number} soft The new soft quota in bytes
424
     *
425
     * @apiSuccessExample {json} Success-Response:
426
     * HTTP/1.1 204 No Content
427
     *
428
     * @param string $uname
429
     * @param string $uid
430
     * @param int    $hard
431
     * @param int    $soft
432
     *
433
     * @return Response
434
     */
435
    public function postQuota(int $hard, int $soft, ?string $uid = null, ?string $uname = null): Response
436
    {
437
        $result = $this->_getUser($uid, $uname, true)
0 ignored issues
show
Bug introduced by
The method setHardQuota() does not seem to exist on object<Balloon\App\Api\v1\User>.

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...
Unused Code introduced by
$result 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...
438
            ->setHardQuota($hard)
439
            ->setSoftQuota($soft);
440
441
        return (new Response())->setCode(204);
442
    }
443
}
444