Completed
Push — 4-cactus ( 35f1a2...d71008 )
by Stefano
16s queued 11s
created

BEdita/API/src/Controller/HomeController.php (2 issues)

1
<?php
2
/**
3
 * BEdita, API-first content management framework
4
 * Copyright 2017 ChannelWeb Srl, Chialab Srl
5
 *
6
 * This file is part of BEdita: you can redistribute it and/or modify
7
 * it under the terms of the GNU Lesser General Public License as published
8
 * by the Free Software Foundation, either version 3 of the License, or
9
 * (at your option) any later version.
10
 *
11
 * See LICENSE.LGPL or <http://gnu.org/licenses/lgpl-3.0.html> for more details.
12
 */
13
namespace BEdita\API\Controller;
14
15
use BEdita\Core\Utility\LoggedUser;
16
use Cake\Core\Configure;
17
use Cake\Http\ServerRequest;
18
use Cake\ORM\TableRegistry;
19
use Cake\Routing\Router;
20
use Cake\Utility\Hash;
21
use Cake\Utility\Inflector;
22
use Zend\Diactoros\Uri;
23
24
/**
25
 * Controller for `/home` endpoint.
26
 *
27
 * @since 4.0.0
28
 */
29
class HomeController extends AppController
30
{
31
32
    /**
33
     * Default endpoints with:
34
     *  - supported methods, where 'ALL' means 'GET', 'POST', 'PATCH' and 'DELETE' are supported
35
     *  - multiple types flag, if true multiple types are handled (like abstract object types or `/trash`)
36
     *
37
     * @var array
38
     */
39
    protected $defaultEndpoints = [
40
        '/auth' => [
41
           'methods' => ['GET', 'POST'],
42
           'multiple_types' => false,
43
        ],
44
        '/admin' => [
45
            'methods' => 'ALL',
46
            'multiple_types' => true,
47
         ],
48
         '/model' => [
49
            'methods' => 'ALL',
50
            'multiple_types' => true,
51
         ],
52
         '/roles' => [
53
            'methods' => 'ALL',
54
            'multiple_types' => false,
55
         ],
56
         '/signup' => [
57
            'methods' => ['POST'],
58
            'multiple_types' => false,
59
         ],
60
         '/status' => [
61
            'methods' => ['GET'],
62
            'multiple_types' => false,
63
         ],
64
         '/trash' => [
65
            'methods' => 'ALL',
66
            'multiple_types' => true,
67
         ],
68
    ];
69
70
    /**
71
     * Default allowed methods for unlogged users
72
     * '/*' means: all other endpoints
73
     *
74
     * @var array
75
     */
76
    protected $defaultAllowUnlogged = [
77
        '/auth' => ['POST'],
78
        '/signup' => ['POST'],
79
        '/*' => ['GET'],
80
    ];
81
82
    /**
83
     * List API available endpoints
84
     *
85
     * @return void
86
     */
87
    public function index()
88
    {
89
        $this->request->allowMethod(['get', 'head']);
0 ignored issues
show
The method allowMethod() does not exist on null. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

89
        $this->request->/** @scrutinizer ignore-call */ 
90
                        allowMethod(['get', 'head']);

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...
90
91
        $default = Hash::insert($this->defaultEndpoints, '{*}.object_type', false);
0 ignored issues
show
false of type false is incompatible with the type array|null expected by parameter $values of Cake\Utility\Hash::insert(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

91
        $default = Hash::insert($this->defaultEndpoints, '{*}.object_type', /** @scrutinizer ignore-type */ false);
Loading history...
92
        $endPoints = array_merge($this->objectTypesEndpoints(), $default);
93
        foreach ($endPoints as $e => $data) {
94
            $resources[$e] = $this->endpointFeatures($e, $data);
95
        }
96
        $project = Configure::read('Project');
97
        $version = Configure::read('BEdita.version');
98
99
        $this->set('_meta', compact('resources', 'project', 'version'));
100
        $this->set('_serialize', []);
101
    }
102
103
    /**
104
     * Return endpoint features to display in `/home` response
105
     *
106
     * @param string $endpoint Endpoint name
107
     * @param array $options Endpoint options - methods and multiple types flag
108
     * @return array Array of features
109
     */
110
    protected function endpointFeatures($endpoint, $options)
111
    {
112
        $methods = $options['methods'];
113
        if ($methods === 'ALL') {
114
            $methods = ['GET', 'POST', 'PATCH', 'DELETE'];
115
        }
116
        $allow = [];
117
        foreach ($methods as $method) {
118
            if ($this->checkAuthorization($endpoint, $method)) {
119
                $allow[] = $method;
120
            }
121
        }
122
123
        return [
124
            'href' => Router::url($endpoint, true),
125
            'hints' => [
126
                'allow' => $allow,
127
                'formats' => [
128
                    'application/json',
129
                    'application/vnd.api+json',
130
                ],
131
                'display' => [
132
                    'label' => Inflector::camelize(substr($endpoint, 1)),
133
                ],
134
                'object_type' => $options['object_type'],
135
                'multiple_types' => $options['multiple_types'],
136
            ],
137
        ];
138
    }
139
140
    /**
141
     * Returns available object types to list as endpoints
142
     *
143
     * @return array Array of object type names
144
     */
145
    protected function objectTypesEndpoints()
146
    {
147
        $allTypes = TableRegistry::get('ObjectTypes')
148
                        ->find('list', ['keyField' => 'name', 'valueField' => 'is_abstract'])
149
                        ->where(['enabled' => true])
150
                        ->toArray();
151
        $endPoints = [];
152
        foreach ($allTypes as $t => $abstract) {
153
            $endPoints['/' . $t] = [
154
                'methods' => $abstract ? ['GET', 'DELETE'] : 'ALL',
155
                'object_type' => true,
156
                'multiple_types' => $abstract,
157
            ];
158
        }
159
160
        return $endPoints;
161
    }
162
163
    /**
164
     * Check Authorization on endpoint
165
     *
166
     * @param string $endpoint Endpoint URI
167
     * @param string $method HTTP method
168
     * @return bool True on granted authorization, false otherwise
169
     */
170
    protected function checkAuthorization($endpoint, $method)
171
    {
172
        if (empty(LoggedUser::getUser()) && !$this->unloggedAuthorized($endpoint, $method)) {
173
            return false;
174
        }
175
176
        $environment = ['REQUEST_METHOD' => $method];
177
        $uri = new Uri($endpoint);
178
        $request = new ServerRequest(compact('environment', 'uri'));
179
        $authorize = $this->Auth->getAuthorize('BEdita/API.Endpoint');
180
181
        return $authorize->authorize(LoggedUser::getUser(), $request);
182
    }
183
184
    /**
185
     * Default unlogged authorization on endpoint method, without permissions check
186
     *
187
     * @param string $endpoint Endpoint URI
188
     * @param string $method HTTP method
189
     * @return bool True on granted authorization, false otherwise
190
     */
191
    protected function unloggedAuthorized($endpoint, $method)
192
    {
193
        $defaultAllow = Hash::get($this->defaultAllowUnlogged, $endpoint, $this->defaultAllowUnlogged['/*']);
194
195
        return in_array($method, $defaultAllow);
196
    }
197
}
198