1 | <?php |
||||
2 | /** |
||||
3 | * Elgg web services API plugin |
||||
4 | */ |
||||
5 | |||||
6 | /** |
||||
7 | * Web services init |
||||
8 | * |
||||
9 | * @return void |
||||
10 | */ |
||||
11 | function ws_init() { |
||||
12 | 31 | $lib_dir = __DIR__ . "/lib"; |
|||
13 | 31 | elgg_register_library('elgg:ws', "$lib_dir/web_services.php"); |
|||
14 | 31 | elgg_register_library('elgg:ws:api_user', "$lib_dir/api_user.php"); |
|||
15 | 31 | elgg_register_library('elgg:ws:client', "$lib_dir/client.php"); |
|||
16 | 31 | elgg_register_library('elgg:ws:tokens', "$lib_dir/tokens.php"); |
|||
17 | |||||
18 | 31 | elgg_load_library('elgg:ws:api_user'); |
|||
19 | 31 | elgg_load_library('elgg:ws:tokens'); |
|||
20 | |||||
21 | 31 | elgg_register_page_handler('services', 'ws_page_handler'); |
|||
0 ignored issues
–
show
|
|||||
22 | |||||
23 | // Register a service handler for the default web services |
||||
24 | // The name rest is a misnomer as they are not RESTful |
||||
25 | 31 | elgg_ws_register_service_handler('rest', 'ws_rest_handler'); |
|||
26 | |||||
27 | // expose the list of api methods |
||||
28 | 31 | elgg_ws_expose_function("system.api.list", "list_all_apis", null, |
|||
29 | 31 | elgg_echo("system.api.list"), "GET", false, false); |
|||
30 | |||||
31 | // The authentication token api |
||||
32 | 31 | elgg_ws_expose_function( |
|||
33 | 31 | "auth.gettoken", |
|||
34 | 31 | "auth_gettoken", |
|||
35 | [ |
||||
36 | 31 | 'username' => ['type' => 'string'], |
|||
37 | 'password' => ['type' => 'string'], |
||||
38 | ], |
||||
39 | 31 | elgg_echo('auth.gettoken'), |
|||
40 | 31 | 'POST', |
|||
41 | 31 | false, |
|||
42 | 31 | false |
|||
43 | ); |
||||
44 | |||||
45 | 31 | elgg_register_plugin_hook_handler('rest:output', 'system.api.list', 'ws_system_api_list_hook'); |
|||
46 | 31 | } |
|||
47 | |||||
48 | /** |
||||
49 | * Handle a web service request |
||||
50 | * |
||||
51 | * Handles requests of format: http://site/services/api/handler/response_format/request |
||||
52 | * The first element after 'services/api/' is the service handler name as |
||||
53 | * registered by {@link register_service_handler()}. |
||||
54 | * |
||||
55 | * The remaining string is then passed to the {@link service_handler()} |
||||
56 | * which explodes by /, extracts the first element as the response format |
||||
57 | * (viewtype), and then passes the remaining array to the service handler |
||||
58 | * function registered by {@link register_service_handler()}. |
||||
59 | * |
||||
60 | * If a service handler isn't found, a 404 header is sent. |
||||
61 | * |
||||
62 | * @param array $segments URL segments |
||||
63 | * |
||||
64 | * @return bool |
||||
65 | */ |
||||
66 | function ws_page_handler($segments) { |
||||
67 | elgg_load_library('elgg:ws'); |
||||
68 | |||||
69 | if (!isset($segments[0]) || $segments[0] != 'api') { |
||||
70 | return false; |
||||
71 | } |
||||
72 | array_shift($segments); |
||||
73 | |||||
74 | $handler = array_shift($segments); |
||||
75 | $request = implode('/', $segments); |
||||
76 | |||||
77 | service_handler($handler, $request); |
||||
0 ignored issues
–
show
$request of type string is incompatible with the type array expected by parameter $request of service_handler() .
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
Loading history...
|
|||||
78 | |||||
79 | return true; |
||||
80 | } |
||||
81 | |||||
82 | /** |
||||
83 | * A global array holding API methods. |
||||
84 | * The structure of this is |
||||
85 | * $API_METHODS = array ( |
||||
86 | * $method => array ( |
||||
87 | * "description" => "Some human readable description" |
||||
88 | * "function" = 'my_function_callback' |
||||
89 | * "parameters" = array ( |
||||
90 | * "variable" = array ( // the order should be the same as the function callback |
||||
91 | * type => 'int' | 'bool' | 'float' | 'string' |
||||
92 | * required => true (default) | false |
||||
93 | * default => value // optional |
||||
94 | * ) |
||||
95 | * ) |
||||
96 | * "call_method" = 'GET' | 'POST' |
||||
97 | * "require_api_auth" => true | false (default) |
||||
98 | * "require_user_auth" => true | false (default) |
||||
99 | * ) |
||||
100 | * ) |
||||
101 | */ |
||||
102 | global $API_METHODS; |
||||
103 | $API_METHODS = []; |
||||
104 | |||||
105 | /** Define a global array of errors */ |
||||
106 | global $ERRORS; |
||||
107 | $ERRORS = []; |
||||
108 | |||||
109 | /** |
||||
110 | * Expose a function as a web service. |
||||
111 | * |
||||
112 | * Limitations: Currently cannot expose functions which expect objects. |
||||
113 | * It also cannot handle arrays of bools or arrays of arrays. |
||||
114 | * Also, input will be filtered to protect against XSS attacks through the web services. |
||||
115 | * |
||||
116 | * @param string $method The api name to expose - for example "myapi.dosomething" |
||||
117 | * @param callable $function Callable to handle API call |
||||
118 | * @param array $parameters (optional) List of parameters in the same order as in |
||||
119 | * your function. Default values may be set for parameters which |
||||
120 | * allow REST api users flexibility in what parameters are passed. |
||||
121 | * Generally, optional parameters should be after required |
||||
122 | * parameters. If an optional parameter is not set and has no default, |
||||
123 | * the API callable will receive null. |
||||
124 | * |
||||
125 | * This array should be in the format |
||||
126 | * "variable" = array ( |
||||
127 | * type => 'int' | 'bool' | 'float' | 'string' | 'array' |
||||
128 | * required => true (default) | false |
||||
129 | * default => value (optional) |
||||
130 | * ) |
||||
131 | * @param string $description (optional) human readable description of the function. |
||||
132 | * @param string $call_method (optional) Define what http method must be used for |
||||
133 | * this function. Default: GET |
||||
134 | * @param bool $require_api_auth (optional) (default is false) Does this method |
||||
135 | * require API authorization? (example: API key) |
||||
136 | * @param bool $require_user_auth (optional) (default is false) Does this method |
||||
137 | * require user authorization? |
||||
138 | * @param bool $assoc (optional) If set to true, the callback function will receive a single argument |
||||
139 | * that contains an associative array of parameter => input pairs for the method. |
||||
140 | * |
||||
141 | * @return bool |
||||
142 | * @throws InvalidParameterException |
||||
143 | */ |
||||
144 | function elgg_ws_expose_function( |
||||
145 | $method, |
||||
146 | $function, |
||||
147 | $parameters = null, |
||||
148 | $description = "", |
||||
149 | $call_method = "GET", |
||||
150 | $require_api_auth = false, |
||||
151 | $require_user_auth = false, |
||||
152 | $assoc = false |
||||
153 | ) { |
||||
154 | |||||
155 | 50 | global $API_METHODS; |
|||
156 | |||||
157 | 50 | if (empty($method) || empty($function)) { |
|||
158 | 2 | $msg = elgg_echo('InvalidParameterException:APIMethodOrFunctionNotSet'); |
|||
159 | 2 | throw new InvalidParameterException($msg); |
|||
160 | } |
||||
161 | |||||
162 | // does not check whether this method has already been exposed - good idea? |
||||
163 | 48 | $API_METHODS[$method] = []; |
|||
164 | |||||
165 | 48 | $API_METHODS[$method]["description"] = $description; |
|||
166 | |||||
167 | // does not check whether callable - done in execute_method() |
||||
168 | 48 | $API_METHODS[$method]["function"] = $function; |
|||
169 | |||||
170 | 48 | if ($parameters != null) { |
|||
171 | 46 | if (!is_array($parameters)) { |
|||
172 | 1 | $msg = elgg_echo('InvalidParameterException:APIParametersArrayStructure', [$method]); |
|||
173 | 1 | throw new InvalidParameterException($msg); |
|||
174 | } |
||||
175 | |||||
176 | // catch common mistake of not setting up param array correctly |
||||
177 | 45 | $first = current($parameters); |
|||
178 | 45 | if (!is_array($first)) { |
|||
179 | 1 | $msg = elgg_echo('InvalidParameterException:APIParametersArrayStructure', [$method]); |
|||
180 | 1 | throw new InvalidParameterException($msg); |
|||
181 | } |
||||
182 | } |
||||
183 | |||||
184 | 46 | if ($parameters != null) { |
|||
185 | // ensure the required flag is set correctly in default case for each parameter |
||||
186 | 44 | foreach ($parameters as $key => $value) { |
|||
187 | // check if 'required' was specified - if not, make it true |
||||
188 | 44 | if (!array_key_exists('required', $value)) { |
|||
189 | 44 | $parameters[$key]['required'] = true; |
|||
190 | } |
||||
191 | } |
||||
192 | |||||
193 | 44 | $API_METHODS[$method]["parameters"] = $parameters; |
|||
194 | } |
||||
195 | |||||
196 | 46 | $call_method = strtoupper($call_method); |
|||
197 | 46 | switch ($call_method) { |
|||
198 | case 'POST' : |
||||
199 | 39 | $API_METHODS[$method]["call_method"] = 'POST'; |
|||
200 | 39 | break; |
|||
201 | case 'GET' : |
||||
202 | 37 | $API_METHODS[$method]["call_method"] = 'GET'; |
|||
203 | 37 | break; |
|||
204 | default : |
||||
205 | 1 | $msg = elgg_echo('InvalidParameterException:UnrecognisedHttpMethod', |
|||
206 | 1 | [$call_method, $method]); |
|||
207 | |||||
208 | 1 | throw new InvalidParameterException($msg); |
|||
209 | } |
||||
210 | |||||
211 | 45 | $API_METHODS[$method]["require_api_auth"] = $require_api_auth; |
|||
212 | |||||
213 | 45 | $API_METHODS[$method]["require_user_auth"] = $require_user_auth; |
|||
214 | |||||
215 | 45 | $API_METHODS[$method]["assoc"] = (bool) $assoc; |
|||
216 | |||||
217 | 45 | return true; |
|||
218 | } |
||||
219 | |||||
220 | /** |
||||
221 | * Unregister a web services method |
||||
222 | * |
||||
223 | * @param string $method The api name that was exposed |
||||
224 | * @return void |
||||
225 | */ |
||||
226 | function elgg_ws_unexpose_function($method) { |
||||
227 | 1 | global $API_METHODS; |
|||
228 | |||||
229 | 1 | if (isset($API_METHODS[$method])) { |
|||
230 | 1 | unset($API_METHODS[$method]); |
|||
231 | } |
||||
232 | 1 | } |
|||
233 | |||||
234 | /** |
||||
235 | * Simple api to return a list of all api's installed on the system. |
||||
236 | * |
||||
237 | * @return array |
||||
238 | * @access private |
||||
239 | */ |
||||
240 | function list_all_apis() { |
||||
241 | global $API_METHODS; |
||||
242 | |||||
243 | // sort first |
||||
244 | ksort($API_METHODS); |
||||
245 | |||||
246 | return $API_METHODS; |
||||
247 | } |
||||
248 | |||||
249 | /** |
||||
250 | * Registers a web services handler |
||||
251 | * |
||||
252 | * @param string $handler Web services type |
||||
253 | * @param string $function Your function name |
||||
254 | * |
||||
255 | * @return bool Depending on success |
||||
256 | */ |
||||
257 | function elgg_ws_register_service_handler($handler, $function) { |
||||
258 | 31 | $servicehandler = _elgg_config()->servicehandler; |
|||
259 | 31 | if (!$servicehandler) { |
|||
260 | 18 | $servicehandler = []; |
|||
261 | } |
||||
262 | 31 | if (is_callable($function, true)) { |
|||
263 | 31 | $servicehandler[$handler] = $function; |
|||
264 | 31 | _elgg_config()->servicehandler = $servicehandler; |
|||
265 | 31 | return true; |
|||
266 | } |
||||
267 | |||||
268 | return false; |
||||
269 | } |
||||
270 | |||||
271 | /** |
||||
272 | * Remove a web service |
||||
273 | * To replace a web service handler, register the desired handler over the old on |
||||
274 | * with register_service_handler(). |
||||
275 | * |
||||
276 | * @param string $handler web services type |
||||
277 | * @return void |
||||
278 | */ |
||||
279 | function elgg_ws_unregister_service_handler($handler) { |
||||
280 | $servicehandler = _elgg_config()->servicehandler; |
||||
281 | |||||
282 | if (isset($servicehandler, $servicehandler[$handler])) { |
||||
283 | unset($servicehandler[$handler]); |
||||
284 | _elgg_config()->servicehandler = $servicehandler; |
||||
285 | } |
||||
286 | } |
||||
287 | |||||
288 | /** |
||||
289 | * REST API handler |
||||
290 | * |
||||
291 | * @return void |
||||
292 | * @access private |
||||
293 | * |
||||
294 | * @throws SecurityException|APIException |
||||
295 | */ |
||||
296 | function ws_rest_handler() { |
||||
297 | |||||
298 | $viewtype = elgg_get_viewtype(); |
||||
299 | |||||
300 | if (!elgg_view_exists('api/output', $viewtype)) { |
||||
301 | header("HTTP/1.0 400 Bad Request"); |
||||
302 | header("Content-type: text/plain"); |
||||
303 | echo "Missing view 'api/output' in viewtype '$viewtype'."; |
||||
304 | if (in_array($viewtype, ['xml', 'php'])) { |
||||
305 | echo "\nEnable the 'data_views' plugin to add this view."; |
||||
306 | } |
||||
307 | exit; |
||||
308 | } |
||||
309 | |||||
310 | elgg_load_library('elgg:ws'); |
||||
311 | |||||
312 | // Register the error handler |
||||
313 | error_reporting(E_ALL); |
||||
314 | set_error_handler('_php_api_error_handler'); |
||||
315 | |||||
316 | // Register a default exception handler |
||||
317 | set_exception_handler('_php_api_exception_handler'); |
||||
318 | |||||
319 | // plugins should return true to control what API and user authentication handlers are registered |
||||
320 | if (elgg_trigger_plugin_hook('rest', 'init', null, false) == false) { |
||||
321 | // for testing from a web browser, you can use the session PAM |
||||
322 | // do not use for production sites!! |
||||
323 | //register_pam_handler('pam_auth_session'); |
||||
324 | |||||
325 | // user token can also be used for user authentication |
||||
326 | register_pam_handler('pam_auth_usertoken'); |
||||
327 | |||||
328 | // simple API key check |
||||
329 | register_pam_handler('api_auth_key', "sufficient", "api"); |
||||
330 | // hmac |
||||
331 | register_pam_handler('api_auth_hmac', "sufficient", "api"); |
||||
332 | } |
||||
333 | |||||
334 | // Get parameter variables |
||||
335 | $method = get_input('method'); |
||||
336 | $result = null; |
||||
0 ignored issues
–
show
|
|||||
337 | |||||
338 | // this will throw an exception if authentication fails |
||||
339 | authenticate_method($method); |
||||
340 | |||||
341 | $result = execute_method($method); |
||||
342 | |||||
343 | |||||
344 | if (!($result instanceof GenericResult)) { |
||||
345 | throw new APIException(elgg_echo('APIException:ApiResultUnknown')); |
||||
346 | } |
||||
347 | |||||
348 | // Output the result |
||||
349 | echo elgg_view_page($method, elgg_view("api/output", ["result" => $result])); |
||||
350 | } |
||||
351 | |||||
352 | /** |
||||
353 | * Filters system API list to remove PHP internal function names |
||||
354 | * |
||||
355 | * @param string $hook "rest:output" |
||||
356 | * @param string $type "system.api.list" |
||||
357 | * @param array $return API list |
||||
358 | * @param array $params Method params |
||||
359 | * @return array |
||||
360 | */ |
||||
361 | function ws_system_api_list_hook($hook, $type, $return, $params) { |
||||
362 | |||||
363 | if (!empty($return) && is_array($return)) { |
||||
364 | foreach ($return as $method => $settings) { |
||||
365 | unset($return[$method]['function']); |
||||
366 | } |
||||
367 | } |
||||
368 | |||||
369 | return $return; |
||||
370 | } |
||||
371 | |||||
372 | return function() { |
||||
373 | 18 | elgg_register_event_handler('init', 'system', 'ws_init'); |
|||
374 | }; |
||||
375 |
This function has been deprecated. The supplier of the function has supplied an explanatory message.
The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.