This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include
, or for example
via PHP's auto-loading mechanism.
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | |||
3 | namespace Ntb\RestAPI; |
||
4 | |||
5 | use Director; |
||
6 | use Exception; |
||
7 | use HTMLText; |
||
8 | use Member; |
||
9 | use SS_HTTPRequest; |
||
10 | use SS_HTTPResponse; |
||
11 | use SS_Log; |
||
12 | |||
13 | /** |
||
14 | * Base class for the rest resource controllers. |
||
15 | * @author Christian Blank <[email protected]> |
||
16 | */ |
||
17 | abstract class BaseRestController extends \Controller { |
||
18 | |||
19 | private static $allowed_actions = array ( |
||
0 ignored issues
–
show
Comprehensibility
introduced
by
![]() |
|||
20 | 'options' => true, |
||
21 | 'head' => true |
||
22 | ); |
||
23 | |||
24 | /** |
||
25 | * Configuration option. |
||
26 | * If set to true, only https connections will be processed. |
||
27 | * @var bool |
||
28 | */ |
||
29 | private static $https_only = true; |
||
30 | |||
31 | /** |
||
32 | * |
||
33 | */ |
||
34 | public function init() { |
||
35 | parent::init(); |
||
36 | // check for https |
||
37 | if($this->config()->https_only && !Director::is_https()) { |
||
38 | $response = $this->getResponse(); |
||
39 | $response->setStatusCode('403', 'http request not allowed'); |
||
40 | $response->setBody("Request over HTTP is not allowed. Please switch to https."); |
||
41 | $response->output(); |
||
42 | exit; |
||
43 | } |
||
44 | // check for CORS options request |
||
45 | if ($this->request->httpMethod() === 'OPTIONS' ) { |
||
46 | // create direct response without requesting any controller |
||
47 | $response = $this->getResponse(); |
||
48 | // set CORS header from config |
||
49 | $response = $this->addCORSHeaders($response); |
||
50 | $response->output(); |
||
51 | exit; |
||
52 | } |
||
53 | } |
||
54 | |||
55 | /** |
||
56 | * @param SS_HTTPRequest $request |
||
57 | * @return null |
||
0 ignored issues
–
show
|
|||
58 | * @throws RestUserException |
||
59 | */ |
||
60 | public function head(SS_HTTPRequest $request) { |
||
61 | if(method_exists($this, 'get')) { |
||
62 | $result = $this->get($request); |
||
0 ignored issues
–
show
The method
get does not exist on object<Ntb\RestAPI\BaseRestController> ? Since you implemented __call , maybe consider adding a @method annotation.
If you implement This is often the case, when class ParentClass {
private $data = array();
public function __call($method, array $args) {
if (0 === strpos($method, 'get')) {
return $this->data[strtolower(substr($method, 3))];
}
throw new \LogicException(sprintf('Unsupported method: %s', $method));
}
}
/**
* If this class knows which fields exist, you can specify the methods here:
*
* @method string getName()
*/
class SomeClass extends ParentClass { }
![]() |
|||
63 | if($result instanceof SS_HTTPResponse) { |
||
64 | $result->setBody(null); |
||
65 | return $result; |
||
66 | } |
||
67 | return null; |
||
68 | } |
||
69 | throw new RestUserException("Endpoint doesn't have a GET implementation", 404); |
||
70 | } |
||
71 | |||
72 | /** |
||
73 | * handleAction implementation for rest controllers. This handles the requested action differently then the standard |
||
74 | * implementation. |
||
75 | * |
||
76 | * @param SS_HTTPRequest $request |
||
77 | * @param string $action |
||
78 | * @return HTMLText|SS_HTTPResponse |
||
79 | */ |
||
80 | protected function handleAction($request, $action) { |
||
81 | foreach($request->latestParams() as $k => $v) { |
||
82 | if($v || !isset($this->urlParams[$k])) $this->urlParams[$k] = $v; |
||
83 | } |
||
84 | // set the action to the request method / for developing we could use an additional parameter to choose another method |
||
85 | $action = $this->getMethodName($request); |
||
86 | $this->action = $action; |
||
87 | $this->requestParams = $request->requestVars(); |
||
0 ignored issues
–
show
It seems like
$request->requestVars() can be null . However, the property $requestParams is declared as array . Maybe change the type of the property to array|null or add a type check?
Our type inference engine has found an assignment of a scalar value (like a string, an integer or null) to a property which is an array. Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property. To type hint that a parameter can be either an array or null, you can set a type hint of array and a default value of null. The PHP interpreter will then accept both an array or null for that parameter. function aContainsB(array $needle = null, array $haystack) {
if (!$needle) {
return false;
}
return array_intersect($haystack, $needle) == $haystack;
}
The function can be called with either null or an array for the parameter ![]() |
|||
88 | $className = $this->class; |
||
89 | // create serializer |
||
90 | $serializer = SerializerFactory::create_from_request($request); |
||
91 | $response = $this->getResponse(); |
||
92 | // perform action |
||
93 | try { |
||
94 | if(!$this->hasAction($action)) { |
||
95 | // method couldn't found on controller |
||
96 | throw new RestUserException("Action '$action' isn't available on class $className.", 404); |
||
97 | } |
||
98 | if(!$this->checkAccessAction($action)) { |
||
99 | throw new RestUserException("Action '$action' isn't allowed on class $className.", 404, 401); |
||
100 | } |
||
101 | $actionResult = null; |
||
102 | if(method_exists($this, 'beforeCallActionHandler')) { |
||
103 | // call before action hook |
||
104 | $actionResult = $this->beforeCallActionHandler($request, $action); |
||
0 ignored issues
–
show
The method
beforeCallActionHandler does not exist on object<Ntb\RestAPI\BaseRestController> ? Since you implemented __call , maybe consider adding a @method annotation.
If you implement This is often the case, when class ParentClass {
private $data = array();
public function __call($method, array $args) {
if (0 === strpos($method, 'get')) {
return $this->data[strtolower(substr($method, 3))];
}
throw new \LogicException(sprintf('Unsupported method: %s', $method));
}
}
/**
* If this class knows which fields exist, you can specify the methods here:
*
* @method string getName()
*/
class SomeClass extends ParentClass { }
![]() |
|||
105 | } |
||
106 | // if action hook contains data it will be used as result, otherwise the action handler will be called |
||
107 | if(!$actionResult) { |
||
108 | // perform action |
||
109 | $actionResult = $this->$action($request); |
||
110 | } |
||
111 | $body = $actionResult; |
||
112 | } catch(RestUserException $ex) { |
||
113 | // a user exception was caught |
||
114 | $response->setStatusCode($ex->getHttpStatusCode()); |
||
115 | $body = [ |
||
116 | 'message' => $ex->getMessage(), |
||
117 | 'code' => $ex->getCode() |
||
118 | ]; |
||
119 | // log all data |
||
120 | SS_Log::log( |
||
121 | json_encode(array_merge($body, ['file' => $ex->getFile(), 'line' => $ex->getLine()])), |
||
122 | SS_Log::INFO); |
||
123 | } catch(RestSystemException $ex) { |
||
124 | // a system exception was caught |
||
125 | $response->addHeader('Content-Type', $serializer->contentType()); |
||
126 | $response->setStatusCode($ex->getHttpStatusCode()); |
||
127 | $body = [ |
||
128 | 'message' => $ex->getMessage(), |
||
129 | 'code' => $ex->getCode() |
||
130 | ]; |
||
131 | View Code Duplication | if(Director::isDev()) { |
|
0 ignored issues
–
show
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. ![]() |
|||
132 | $body = array_merge($body, [ |
||
133 | 'file' => $ex->getFile(), |
||
134 | 'line' => $ex->getLine(), |
||
135 | 'trace' => $ex->getTrace() |
||
136 | ]); |
||
137 | } |
||
138 | // log all data |
||
139 | SS_Log::log( |
||
140 | json_encode(array_merge($body, ['file' => $ex->getFile(), 'line' => $ex->getLine()])), |
||
141 | SS_Log::WARN); |
||
142 | } catch(Exception $ex) { |
||
143 | // an unexpected exception was caught |
||
144 | $response->addHeader('Content-Type', $serializer->contentType()); |
||
145 | $response->setStatusCode("500"); |
||
146 | $body = [ |
||
147 | 'message' => $ex->getMessage(), |
||
148 | 'code' => $ex->getCode() |
||
149 | ]; |
||
150 | View Code Duplication | if(Director::isDev()) { |
|
0 ignored issues
–
show
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. ![]() |
|||
151 | $body = array_merge($body, [ |
||
152 | 'file' => $ex->getFile(), |
||
153 | 'line' => $ex->getLine(), |
||
154 | 'trace' => $ex->getTrace() |
||
155 | ]); |
||
156 | } |
||
157 | // log all data and the trace to get a better understanding of the exception |
||
158 | SS_Log::log( |
||
159 | json_encode(array_merge( |
||
160 | $body, ['file' => $ex->getFile(), 'line' => $ex->getLine(),'trace' => $ex->getTrace()])), |
||
161 | SS_Log::ERR); |
||
162 | } |
||
163 | // serialize content and set body of response |
||
164 | $response->addHeader('Content-Type', $serializer->contentType()); |
||
165 | // TODO: body could be an exception; check it before the response is generated |
||
166 | $response->setBody($serializer->serialize($body)); |
||
167 | // set CORS header from config |
||
168 | $response = $this->addCORSHeaders($response); |
||
169 | return $response; |
||
170 | } |
||
171 | |||
172 | /** |
||
173 | * Returns the http method for this request. If the current environment is a development env, the method can be |
||
174 | * changed with a `method` variable. |
||
175 | * |
||
176 | * @param \SS_HTTPRequest $request the current request |
||
177 | * @return string the used http method as string |
||
178 | */ |
||
179 | private function getMethodName($request) { |
||
180 | $method = ''; |
||
181 | if(Director::isDev() && ($varMethod = $request->getVar('method'))) { |
||
182 | if(in_array(strtoupper($varMethod), ['GET','POST','PUT','DELETE','HEAD', 'PATCH'])) { |
||
183 | $method = $varMethod; |
||
184 | } |
||
185 | } else { |
||
186 | $method = $request->httpMethod(); |
||
187 | } |
||
188 | return strtolower($method); |
||
189 | } |
||
190 | |||
191 | /** |
||
192 | * Check, if the request is authenticated. |
||
193 | * @return bool |
||
194 | * @throws RestSystemException |
||
195 | */ |
||
196 | protected function isAuthenticated() { |
||
197 | return $this->currentUser() ? true : false; |
||
198 | } |
||
199 | |||
200 | /** |
||
201 | * Check if the user has admin privileges. |
||
202 | * |
||
203 | * @return bool |
||
204 | * @throws RestSystemException |
||
205 | */ |
||
206 | protected function isAdmin() { |
||
207 | $member = $this->currentUser(); |
||
208 | return $member && \Injector::inst()->get('PermissionChecks')->isAdmin($member); |
||
209 | } |
||
210 | |||
211 | /** |
||
212 | * @param \SS_HTTPResponse $response the current response object |
||
213 | * @return \SS_HTTPResponse the response with CORS headers |
||
214 | */ |
||
215 | protected function addCORSHeaders($response) { |
||
216 | $response->addHeader('Access-Control-Allow-Origin', \Config::inst()->get('BaseRestController', 'CORSOrigin')); |
||
217 | $response->addHeader('Access-Control-Allow-Methods', \Config::inst()->get('BaseRestController', 'CORSMethods')); |
||
218 | $response->addHeader('Access-Control-Max-Age', \Config::inst()->get('BaseRestController', 'CORSMaxAge')); |
||
219 | $response->addHeader('Access-Control-Allow-Headers', \Config::inst()->get('BaseRestController', 'CORSAllowHeaders')); |
||
220 | return $response; |
||
221 | } |
||
222 | |||
223 | /** |
||
224 | * Return the current user from the request. |
||
225 | * @return \Member the current user |
||
226 | */ |
||
227 | protected function currentUser() { |
||
228 | return AuthFactory::createAuth()->current($this->request); |
||
229 | } |
||
230 | } |
||
231 |