Completed
Branch dev (bc6e47)
by Raffael
02:23
created

Group::_getGroup()   C

Complexity

Conditions 8
Paths 5

Size

Total Lines 23
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 23
rs 6.1403
c 0
b 0
f 0
cc 8
eloc 12
nc 5
nop 3
1
<?php
2
3
declare(strict_types=1);
4
5
/**
6
 * Balloon
7
 *
8
 * @author      Raffael Sahli <[email protected]>
9
 * @copyright   Copryright (c) 2012-2017 gyselroth GmbH (https://gyselroth.com)
10
 * @license     GPL-3.0 https://opensource.org/licenses/GPL-3.0
11
 */
12
13
namespace Balloon\Api\v1;
14
15
use Balloon\Exception;
0 ignored issues
show
Bug introduced by
This use statement conflicts with another class in this namespace, Balloon\Api\v1\Exception.

Let’s assume that you have a directory layout like this:

.
|-- OtherDir
|   |-- Bar.php
|   `-- Foo.php
`-- SomeDir
    `-- Foo.php

and let’s assume the following content of Bar.php:

// Bar.php
namespace OtherDir;

use SomeDir\Foo; // This now conflicts the class OtherDir\Foo

If both files OtherDir/Foo.php and SomeDir/Foo.php are loaded in the same runtime, you will see a PHP error such as the following:

PHP Fatal error:  Cannot use SomeDir\Foo as Foo because the name is already in use in OtherDir/Foo.php

However, as OtherDir/Foo.php does not necessarily have to be loaded and the error is only triggered if it is loaded before OtherDir/Bar.php, this problem might go unnoticed for a while. In order to prevent this error from surfacing, you must import the namespace with a different alias:

// Bar.php
namespace OtherDir;

use SomeDir\Foo as SomeDirFoo; // There is no conflict anymore.
Loading history...
16
use Balloon\Filesystem\Acl\Exception\Forbidden as ForbiddenException;
17
use Balloon\Server;
18
use Micro\Http\Response;
19
use MongoDB\BSON\ObjectId;
20
21
class Group
22
{
23
    /**
24
     * Group.
25
     *
26
     * @var Group
27
     */
28
    protected $group;
29
30
    /**
31
     * Server.
32
     *
33
     * @var Server
34
     */
35
    protected $server;
36
37
    /**
38
     * Initialize.
39
     *
40
     * @param Server $server
41
     */
42
    public function __construct(Server $server)
43
    {
44
        $this->group = $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\Api\v1\Group> of property $group.

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

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...
108
                if (null !== $id && null !== $name) {
109
                    throw new Exception\InvalidArgument('provide either id (group id) or name (groupname)');
110
                }
111
112
                if (null !== $id) {
113
                    return $this->server->getGroupById(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\Group) is incompatible with the return type documented by Balloon\Api\v1\Group::_getGroup of type Balloon\Api\v1\Group.

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...
114
                }
115
116
                return $this->server->getGroupByName($name);
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this->server->getGroupByName($name); (Balloon\Server\Group) is incompatible with the return type documented by Balloon\Api\v1\Group::_getGroup of type Balloon\Api\v1\Group.

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...
117
            }
118
119
            throw new ForbiddenException(
120
                    'submitted parameters require to have admin privileges',
121
                    ForbiddenException::ADMIN_PRIV_REQUIRED
122
                );
123
        }
124
125
        return $this->group;
126
    }
127
128
    /**
129
     * @api {get} /api/v1/group/member Group member
130
     * @apiVersion 1.0.0
131
     * @apiName getGroups
132
     * @apiUse _getGroup
133
     * @apiGroup Group
134
     * @apiPermission none
135
     * @apiDescription Get all group groups
136
     * If you want to receive your own groups you have to leave the parameters id and name empty.
137
     * Requesting this api with parameter id or name requires admin privileges.
138
     *
139
     * @apiExample Example usage:
140
     * curl -XGET "https://SERVER/api/v1/group/groups?pretty"
141
     * curl -XGET "https://SERVER/api/v1/group/544627ed3c58891f058b4611/groups?pretty"
142
     * curl -XGET "https://SERVER/api/v1/group/groups?name=logingroup&pretty"
143
     *
144
     * @apiSuccess {number} status Status Code
145
     * @apiSuccess {string[]} data  All groups with membership
146
     * @apiSuccessExample {json} Success-Response:
147
     * HTTP/1.1 200 OK
148
     * {
149
     *     "status": 200,
150
     *     "data": [
151
     *          "group1",
152
     *          "group2",
153
     *     ]
154
     * }
155
     *
156
     * @param string $id
157
     * @param string $name
158
     */
159
    public function getMember(?string $id = null, ?string $name = null): Response
160
    {
161
        $result = $this->_getGroup($id, $name)->getGroups();
0 ignored issues
show
Bug introduced by
The method getGroups() does not seem to exist on object<Balloon\Api\v1\Group>.

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...
162
163
        return (new Response())->setCode(200)->setBody($result);
164
    }
165
166
    /**
167
     * @api {get} /api/v1/group/attributes Group attributes
168
     * @apiVersion 1.0.0
169
     * @apiName getAttributes
170
     * @apiUse _getGroup
171
     * @apiGroup Group
172
     * @apiPermission none
173
     * @apiDescription Get all group attributes including groupname, mail, id,....
174
     * If you want to receive your own attributes you have to leave the parameters id and name empty.
175
     * Requesting this api with parameter id or name requires admin privileges.
176
     *
177
     * @apiExample Example usage:
178
     * curl -XGET "https://SERVER/api/v1/group/attributes?pretty"
179
     * curl -XGET "https://SERVER/api/v1/group/544627ed3c58891f058b4611/attributes?pretty"
180
     * curl -XGET "https://SERVER/api/v1/group/attributes?name=loginser&pretty"
181
     *
182
     * @apiSuccess (200 OK) {number} status Status Code
183
     * @apiSuccess (200 OK) {object[]} group attributes
184
     *
185
     * @apiSuccessExample {json} Success-Response:
186
     * HTTP/1.1 200 OK
187
     * {
188
     *      "status": 200,
189
     *      "data": [] //shortened
190
     * }
191
     *
192
     * @param string $id
193
     * @param string $name
194
     * @param string $attributes
195
     *
196
     * @return Response
197
     */
198
    public function getAttributes(?string $id = null, ?string $name = null, array $attributes = []): Response
199
    {
200
        $result = $this->_getGroup($id, $name)->getAttribute($attributes);
0 ignored issues
show
Bug introduced by
The method getAttribute() does not exist on Balloon\Api\v1\Group. Did you maybe mean getAttributes()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
201
202
        return (new Response())->setCode(200)->setBody($result);
203
    }
204
205
    /**
206
     * @api {head} /api/v1/group?id=:id Group exists?
207
     * @apiVersion 1.0.0
208
     * @apiName postQuota
209
     * @apiUse _getGroup
210
     * @apiGroup Group
211
     * @apiPermission admin
212
     * @apiDescription Check if group account exists
213
     *
214
     * @apiExample Example usage:
215
     * curl -XHEAD "https://SERVER/api/v1/group"
216
     * curl -XHEAD "https://SERVER/api/v1/group/544627ed3c58891f058b4611"
217
     * curl -XHEAD "https://SERVER/api/v1/group?name=logingroup"
218
     *
219
     * @apiSuccessExample {json} Success-Response:
220
     * HTTP/1.1 204 No Content
221
     *
222
     * @param string $name
223
     * @param string $id
224
     *
225
     * @return Response
226
     */
227
    public function head(?string $id = null, ?string $name = null): Response
228
    {
229
        $result = $this->_getGroup($id, $name, true);
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...
230
231
        return (new Response())->setCode(204);
232
    }
233
234
    /**
235
     * @api {post} /api/v1/group
236
     * @apiVersion 1.0.0
237
     * @apiName postGroup
238
     * @apiGroup Group
239
     * @apiPermission admin
240
     * @apiDescription Create group
241
     *
242
     * @apiExample Example usage:
243
     * curl -XPOST "https://SERVER/api/v1/group"
244
     *
245
     * @apiParam (POST Parameter) {string} name of the new group
246
     * @apiParam (POST Parameter) {string[]} ID of group member
247
     * @apiParam (POST Parameter) {string[]} Attributes
248
     *
249
     * @apiSuccess (200 OK) {number} status Status Code
250
     * @apiSuccess (200 OK) {object[]} group attributes
251
     *
252
     * @apiSuccessExample {json} Success-Response:
253
     * HTTP/1.1 201 Created
254
     * {
255
     *      "status": 201,
256
     *      "data": "544627ed3c58891f058b4633"
257
     * }
258
     *
259
     * @param string $name
260
     * @param array  $member
261
     * @param array  $attributes
262
     *
263
     * @return Response
264
     */
265
    public function post(string $name, array $member, array $attributes = []): Response
266
    {
267
        $id = $this->server->addGroup($name, $member, $attributes);
268
269
        return (new Response())->setBody((string) $id)->setCode(201);
270
    }
271
272
    /**
273
     * @api {post} /api/v1/group/attributes?id=:id Set attributes
274
     * @apiVersion 1.0.0
275
     * @apiName postAttributes
276
     * @apiUse _getGroup
277
     * @apiGroup Group
278
     * @apiPermission admin
279
     * @apiDescription Set attributes for group
280
     *
281
     * @apiExample Example usage:
282
     * curl -XPOST "https://SERVER/api/v1/group/attributes" -d '{"attributes": ["mail": "[email protected]"]}'
283
     * curl -XPOST "https://SERVER/api/v1/group/attributes?{%22attributes%22:[%22mail%22:%[email protected]%22]}""
284
     * curl -XPOST "https://SERVER/api/v1/group/544627ed3c58891f058b4611/attributes" -d '{"attributes": ["admin": "false"]}'
285
     * curl -XPOST "https://SERVER/api/v1/group/quota?name=logingroup"  -d '{"attributes": ["admin": "false"]}'
286
     *
287
     * @apiParam (POST Parameter) {number} hard The new hard quota in bytes
288
     * @apiParam (POST Parameter) {number} soft The new soft quota in bytes
289
     *
290
     * @apiSuccessExample {json} Success-Response:
291
     * HTTP/1.1 204 No Content
292
     *
293
     * @param string $name
294
     * @param string $id
295
     * @param array  $attributes
296
     *
297
     * @return Response
298
     */
299
    public function postAttributes(array $attributes = [], ?string $id = null, ?string $name = null): Response
300
    {
301
        $this->_getGroup($id, $name, true)->setAttribute($attributes)->save(array_keys($attributes));
0 ignored issues
show
Bug introduced by
The method setAttribute() does not seem to exist on object<Balloon\Api\v1\Group>.

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...
302
303
        return (new Response())->setCode(204);
304
    }
305
306
    /**
307
     * @api {delete} /api/v1/group?id=:id Delete group
308
     * @apiVersion 1.0.0
309
     * @apiName delete
310
     * @apiUse _getGroup
311
     * @apiGroup Group
312
     * @apiPermission admin
313
     * @apiDescription Delete group account, this will also remove any data owned by the group. If force is false, all data gets moved to the trash. If force
314
     * is true all data owned by the group gets ereased.
315
     *
316
     * @apiExample Example usage:
317
     * curl -XDELETE "https://SERVER/api/v1/group/544627ed3c58891f058b4611?force=1"
318
     * curl -XDELETE "https://SERVER/api/v1/group?name=logingroup"
319
     *
320
     * @apiParam (GET Parameter) {bool} [force=false] Per default the group account will be disabled, if force is set
321
     * the group account gets removed completely.
322
     *
323
     * @apiErrorExample {json} Error-Response (Can not delete yourself):
324
     * HTTP/1.1 400 Bad Request
325
     * {
326
     *      "status": 400,
327
     *      "data": {
328
     *          "error": "Balloon\\Exception\\Conflict",
329
     *          "message": "requested group was not found"
330
     *      }
331
     * }
332
     *
333
     * @apiSuccessExample {json} Success-Response:
334
     * HTTP/1.1 204 No Content
335
     *
336
     * @param string $name
337
     * @param string $id
338
     *
339
     * @return Response
340
     */
341
    public function delete(?string $id = null, ?string $name = null): Response
342
    {
343
        $group = $this->_getGroup($id, $name, true);
344
        $group->delete();
345
346
        return (new Response())->setCode(204);
347
    }
348
349
    /**
350
     * @api {post} /api/v1/group/undelete?id=:id Reactivate group account
351
     * @apiVersion 1.0.0
352
     * @apiName postUndelete
353
     * @apiUse _getGroup
354
     * @apiGroup Group
355
     * @apiPermission admin
356
     * @apiDescription Apiore group account. This endpoint does not restore any data, it only does reactivate an existing group account.
357
     *
358
     * @apiExample Example usage:
359
     * curl -XPOST "https://SERVER/api/v1/group/544627ed3c58891f058b4611/undelete"
360
     * curl -XPOST "https://SERVER/api/v1/group/undelete?group=logingroup"
361
     *
362
     * @apiSuccessExample {json} Success-Response:
363
     * HTTP/1.1 204 No Content
364
     *
365
     * @param string $name
366
     * @param string $id
367
     *
368
     * @return Response
369
     */
370
    public function postUndelete(?string $id = null, ?string $name = null): Response
371
    {
372
        $this->_getGroup($id, $name)->undelete();
0 ignored issues
show
Bug introduced by
The method undelete() does not exist on Balloon\Api\v1\Group. Did you maybe mean delete()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
373
374
        return (new Response())->setCode(204);
375
    }
376
}
377