EGOCMS  24.0
EGOTEC Content-Managament-System
Ego_REST_Server.php
gehe zur Dokumentation dieser Datei
1 <?php
9 require_once 'base/Site.php';
10 require_once 'base/Page.php';
11 require_once 'rights/User_SQL.php';
12 require_once 'rights/Group_SQL.php';
13 require_once 'rights/Role_SQL.php';
14 
18 class Ego_REST_Server_Exception extends Exception {
19  // Unbekannter Fehler
20  const UNKNOWN = 0;
21  const UNKNOWN_TEXT = 'Unknown error';
22 
23  // Nicht authorisiert
24  const NOT_AUTHORIZED = 1;
25  const NOT_AUTHORIZED_TEXT = 'Not authorized';
26 
27  // Ungültiger Benutzer
28  const INVALID_USER = 2;
29  const INVALID_USER_TEXT = 'Invalid user';
30 
31  // Ungültige Methode
32  const INVALID_METHOD = 4;
33  const INVALID_METHOD_TEXT = 'Invalid method';
34 
35  // Ungültige Parameter
36  const INVALID_PARAMS = 8;
37  const INVALID_PARAMS_TEXT = 'Invalid parameters';
38 }
39 
49  const BASE_URI = '/rest/';
50 
56  private $requestMethod;
57 
63  private $requestType = '';
64 
70  private $permissions = array();
71 
77  private $params = array();
78 
84  private $site = null;
85 
89  public function __construct() {
90  $this->requestMethod = strtoupper($_SERVER['REQUEST_METHOD']);
91 
92  // Gesendete Parameter ermitteln
93  $request = file_get_contents('php://input');
94  $_REQUEST = array_merge(array_merge($_GET, $_POST), (array) json_decode($request, true));
95 
96  if ($_REQUEST['@data']) {
97  $_REQUEST = json_decode($_REQUEST['@data'], true);
98  }
99 
100  if (!is_array($_REQUEST)) {
101  $_REQUEST = array();
102  }
103 
104  // Upload erkennen
105  foreach ($_REQUEST as $key => $value) {
106  if ($value == '@file' && !empty($source = array_shift($_FILES))) {
107  $_REQUEST[$key] = $source['tmp_name'];
108  break;
109  }
110  }
111 
118  $convert_pages = function ($values) use (&$convert_pages) {
119  foreach ($values as $key => $value) {
120  if (is_array($value)) {
121  $values[$key] = $convert_pages($value);
122  } elseif (strpos($value, '@identity:') === 0) {
123  $values[$key] = Page::byIdentity(substr($value, 10), [
124  'auth_or' => '1=1',
125  'deleted_or' => '1=1',
126  'inactive' => true,
127  'only_active' => false
128  ]);
129  }
130  }
131  return $values;
132  };
133  $_REQUEST = $convert_pages($_REQUEST);
134 
135  // Parameter aus der URL ermitteln
136  $uri = $_SERVER['REQUEST_URI'];
137  if (strpos($uri, self::BASE_URI) === 0) {
138  // Auszuwertenden URL Bestandteil ermitteln
139  $uri = explode('/', substr(parse_url($uri, PHP_URL_PATH), strlen(self::BASE_URI)));
140 
141  if (sizeof($uri) > 1) {
142  // Es gibt mehr als einen Bestandteil, dann herausfinden welches Objekt angesprochen wird
143  switch ($uri[0]) {
144  // User_SQL
145  case '~user':
146  $this->requestType = 'user';
147  $this->params['user_id'] = (string) $uri[1]; // Benutzer ID
148  if (isset($uri[2])) {
149  $this->params['method'] = (string) $uri[2]; // User_SQL Methode
150  }
151  break;
152  // Group_SQL
153  case '~group':
154  $this->requestType = 'group';
155  $this->params['group_id'] = (string) $uri[1]; // Gruppen ID
156  if (isset($uri[2])) {
157  $this->params['method'] = (string) $uri[2]; // Group_SQL Methode
158  }
159  break;
160  // Role_SQL
161  case '~role':
162  $this->requestType = 'role';
163  $this->params['role_id'] = (string) $uri[1]; // Rollen ID
164  if (isset($uri[2])) {
165  $this->params['method'] = (string) $uri[2]; // Role_SQL Methode
166  }
167  break;
168  case '~system':
169  $this->requestType = 'system';
170  if (isset($uri[1])) {
171  $this->params['method'] = (string) $uri[1]; // Ego_System Methode
172  }
173  break;
174  // Site und Page
175  default:
176  $this->requestType = 'site';
177 
178  $this->params['site'] = $uri[0]; // Mandant
179  $this->params['lang'] = $uri[1]; // Sprache
180  if (is_numeric($uri[2])) {
181  $this->requestType = 'page';
182 
183  $this->params['id'] = $uri[2]; // ID
184  if (isset($uri[3])) {
185  if ($uri[3] == 'pool' && isset($uri[4])) {
186  $this->requestType = 'pool';
187  $this->params['method'] = (string) $uri[4]; // Mediapool Methode
188  } else {
189  $this->params['method'] = (string) $uri[3]; // Page Methode
190  }
191  }
192  } else {
193  $this->params['method'] = (string) $uri[2]; // Site Methode
194  }
195  }
196  } else {
197  // Gibt es nur einen Bestandteil, dann ist das eine Methode des Ego_REST Servers
198  $this->params['method'] = $uri[0];
199  }
200 
201  // Rechte Mapping ermitteln
202  if (!$GLOBALS['auth']->isNobody()) {
203  $cache = $GLOBALS['egotec_conf']['cache_dir'].'api-' . md5(serialize([
204  $GLOBALS['auth']->user->field['user_id'],
205  $GLOBALS['auth']->user->extra['api_token'],
206  (string) $this->params['site']
207  ]));
208  if (Ego_System::file_exists($cache)) {
209  // Rechte Mapping aus der Cache verwenden
210  $this->permissions = (array) @json_decode(Ego_System::file_get_contents($cache), true);
211  } else {
212  $permissions = array(
213  $GLOBALS['egotec_conf']['lib_dir'].'base/rest.ini',
214  $GLOBALS['egotec_conf']['site_dir'].'_global/rest.ini'
215  );
216  if (!empty($this->params['site'])) {
217  $permissions[] = $GLOBALS['egotec_conf']['site_dir'].$this->params['site'].'/rest.ini';
218 
219  // Rechte Mapping im Theme suchen
220  require_once('base/Site.php');
221  try {
222  $this->site = new Site($this->params['site'], (string) $this->params['lang']);
223  if ($this->site->theme) {
224  $permissions[] = $GLOBALS['egotec_conf']['pub_dir'].'theme/'.$this->site->theme.'/site/rest.ini';
225  }
226  } catch (Site_Exception $e) {
227  $this->sendError();
228  egotec_error_log($e->getMessage());
229  return array(
230  'error' => 'Unexpected error' . (Ego_System::isDevMode() ? ': ' . $e->getMessage() : ''),
231  'code' => -1
232  );
233  }
234  }
235  foreach ($permissions as $file) {
236  if (Ego_System::file_exists($file)) {
237  $data = (array) @parse_ini_file($file, true);
238  if (!empty($data)) {
239  $this->permissions = array_merge_recursive($this->permissions, $data);
240  }
241  }
242  }
243 
244  // Rechte Mapping in die Cache schreiben
245  Ego_System::file_put_contents($cache, json_encode($this->permissions));
246  }
247  }
248  }
249  }
250 
256  public function getResponse() {
257  $result = null;
258  try {
259  if ($this->requestType && $GLOBALS['auth']->isNobody()) {
260  // Wenn ein Objekt angesprochen wird, muss man angemeldet sein
262  }
263  switch ($this->requestType) {
264  // Handling für Site und Page Anfragen
265  case 'site':
266  case 'page':
267  case 'pool':
268  if ($this->site) {
269  // Das Site Objekt ist bereits bekannt
270  $site = $this->site;
271  } else {
272  require_once('base/Site.php');
273  $site = new Site($this->params['site'], (string) $this->params['lang']);
274  }
275  require_once('base/Page.php');
276 
277  // Standardmäßig werden keine Seiten gefunden, die nicht in der Sitemap angezeigt werden
278  if (!isset($this->params['sitemap'])) {
279  $site->addParam(['sitemap' => true]);
280  }
281 
282  if (!$site->hasRight('view')) {
283  // Das Ansichtsrecht der Site wird nicht erfüllt
285  }
286  $page = null;
287  if (!empty($this->params['id'])) {
288  $page_param = [];
289  if (empty($this->params['method'])) {
290  // beim Aufruf von "/demo/de/1" sind alle übergebenen Parameter "param"
291  $page_param = $_REQUEST;
292  } elseif (isset($_REQUEST['page_param'])) {
293  // beim Aufruf von z.B. "/demo/de/1/getChildren" wird "page_param" zu "param"
294  $page_param = $_REQUEST['page_param'];
295  unset($_REQUEST['page_param']);
296  }
297 
298  // Bei einer Live Replikation müssen alle Seiten ermitteln werden können
299  if ($_SERVER['HTTP_X_REPLICATION']) {
300  $page_param = array_merge($page_param, [
301  'inactive' => true,
302  'only_active' => false,
303  'auth_or' => '1=1',
304  'deleted_or' => '1=1'
305  ]);
306  }
307 
308  $page = $GLOBALS['page'] = $site->getPage($this->params['id'], $page_param);
309  if (!$page) {
311  }
312  }
313 
314  switch ($this->requestMethod) {
315  case 'POST':
316  case 'GET':
317  if (!empty($this->params['method'])) {
318  if ($page) {
319  if ($this->requestType == 'pool') {
320  // Eine Mediapool Methode
321  $result = $this->call($page->getMediapool(), $this->params['method'], $_REQUEST);
322  } else {
323  // Eine Page Methode
324  $result = $this->call($page, $this->params['method'], $_REQUEST);
325  }
326  } else {
327  // Eine Site Methode
328  $result = $this->call($site, $this->params['method'], $_REQUEST);
329  }
330  } else {
331  switch ($this->requestMethod) {
332  case 'POST':
333  if (!empty($this->params['id'])) {
334  if ($this->requestType == 'pool') {
335  // In Mediapool hochladen
336  $result = $this->call($page->getMediapool(), 'put', $_REQUEST);
337  } else {
338  // Page anlegen
339  $result = $this->call($page, 'newChild', $_REQUEST);
340  }
341  } else {
342  // Site anlegen
343  if (!$GLOBALS['auth']->hasSuperuserPermission()) {
344  // Mandanten dürfen nur Superuser anlegen
346  }
347  $result = Site::createSite($_REQUEST);
348  }
349  break;
350  case 'GET':
351  if ($page) {
352  if (!$page->hasRights('view')) {
353  // Page ermitteln ist nicht erlaubt
355  }
356  if ($this->requestType == 'pool') {
357  // Mediapool zurückliefern
358  $result = $this->call($page->getMediapool(), 'list', $_REQUEST);
359  } else {
360  // Page zurückliefern
361  $result = $page;
362  }
363  } else {
364  // Site zurückliefern
365  $result = $site;
366  }
367  }
368  }
369  break;
370  case 'PUT':
371  if ($page) {
372  if (empty($this->params['method'])) {
373  if ($this->requestType == 'pool') {
374  // Mediapool aktualisieren
375  $result = $this->call($page->getMediapool(), 'edit', $_REQUEST);
376  } else {
377  // Page aktualisieren
378  $result = $this->call($page, 'update', $_REQUEST);
379  }
380  } else {
381  if ($this->requestType == 'pool') {
382  // Eine Pool Methode
383  $result = $this->call($page->getMediapool(), $this->params['method'], $_REQUEST);
384  } else {
385  // Eine Page Methode
386  $result = $this->call($page, $this->params['method'], $_REQUEST);
387  }
388  }
389  }
390  break;
391  case 'DELETE':
392  if ($page) {
393  if ($this->requestType == 'pool') {
394  // Eine Mediapool Datei löschen
395  $result = $this->call($page->getMediapool(), 'delete', $_REQUEST);
396  } else {
397  // Page löschen
398  $result = $this->call($page, 'delete', $_REQUEST);
399  }
400  }
401  }
402  break;
403 
404  // Handling für Benutzer Anfragen
405  case 'user':
406  $user = new User_SQL($this->params['user_id']);
407 
408  switch ($this->requestMethod) {
409  case 'POST':
410  case 'GET':
411  if (!empty($this->params['method'])) {
412  // Eine User_SQL Methode
413  $result = $this->call($user, $this->params['method'], $_REQUEST);
414  } else {
415  if ($this->requestMethod == 'POST') {
416  // Benutzer anlegen
417  if (!empty($user->field['user_id'])) {
418  $user = new User_SQL();
419  }
420  $this->call($user, 'update', $_REQUEST);
421  }
422 
423  // Benutzer zurückliefern
424  $result = $user;
425  }
426  break;
427  case 'PUT':
428  // Benutzer aktualisieren
429  $result = $this->call($user, 'update', $_REQUEST);
430  break;
431  case 'DELETE':
432  // Benutzer löschen
433  $result = $this->call($user, 'delete', $_REQUEST);
434  }
435  break;
436 
437  // Handling für Gruppen Anfragen
438  case 'group':
439  $group = new Group_SQL($this->params['group_id']);
440 
441  switch ($this->requestMethod) {
442  case 'POST':
443  case 'GET':
444  if (!empty($this->params['method'])) {
445  // Eine Group_SQL Methode
446  $result = $this->call($group, $this->params['method'], $_REQUEST);
447  } else {
448  if ($this->requestMethod == 'POST') {
449  // Gruppe anlegen
450  $new_group = new Group_SQL();
451  if ($this->call($group, 'addChild', array($new_group))) {
452  $new_group = new Group_SQL($new_group->field['group_id']);
453  $this->call($new_group, 'update', $_REQUEST);
454  $group = $new_group;
455  } else {
457  }
458  }
459 
460  // Gruppe zurückliefern
461  $result = $group;
462  }
463  break;
464  case 'PUT':
465  // Gruppe aktualisieren
466  $result = $this->call($group, 'update', $_REQUEST);
467  break;
468  case 'DELETE':
469  // Gruppe löschen
470  $result = $this->call($group, 'delete', $_REQUEST);
471  }
472  break;
473 
474  // Handling für Rollen Anfragen
475  case 'role':
476  $role = new Role_SQL($this->params['role_id']);
477 
478  switch ($this->requestMethod) {
479  case 'POST':
480  case 'GET':
481  if (!empty($this->params['method'])) {
482  // Eine Role_SQL Methode
483  $result = $this->call($role, $this->params['method'], $_REQUEST);
484  } else {
485  if ($this->requestMethod == 'POST') {
486  // Rolle anlegen
487  $new_role = new Role_SQL();
488  if ($this->call($role, 'addChild', array($new_role))) {
489  $new_role = new Role_SQL($new_role->field['role_id']);
490  $this->call($new_role, 'update', $_REQUEST);
491  $role = $new_role;
492  } else {
494  }
495  }
496 
497  // Rolle zurückliefern
498  $result = $role;
499  }
500  break;
501  case 'PUT':
502  // Rolle aktualisieren
503  $result = $this->call($role, 'update', $_REQUEST);
504  break;
505  case 'DELETE':
506  // Rolle löschen
507  $result = $this->call($role, 'delete', $_REQUEST);
508  }
509  break;
510 
511  // Handling für Ego_System Anfragen
512  case 'system':
513  $result = $this->call('Ego_System', $this->params['method'], $_REQUEST);
514  break;
515 
516  // Handling für Ego_REST Server Anfragen
517  default:
518  // Ego_REST Server Methode
519  $result = $this->call($this, $this->params['method'], $_REQUEST);
520  }
521  $this->sendSuccess();
522  } catch (Ego_REST_Server_Exception $e) {
523  // Ein Ego_REST Server Fehler wird ausgegeben
524  switch ($e->getCode()) {
526  Ego_System::header(401);
527  exit;
528  default:
529  $this->sendError();
530  }
531  return array(
532  'error' => $e->getMessage(),
533  'code' => $e->getCode()
534  );
535  } catch (Exception $e) {
536  // Jeder andere Fehler wird in die Errorlog geschrieben
537  $this->sendError();
538  egotec_error_log($e->getMessage());
539  return array(
540  'error' => 'Unexpected error' . (Ego_System::isDevMode() ? ': ' . $e->getMessage() : ''),
541  'code' => -1
542  );
543  }
544 
545  return $this->toJSON($result);
546  }
547 
556  private function call($object, $method, $params = array()) {
557  if (empty($method) || !method_exists($object, $method)) {
558  // Ungültiger Aufruf
560  }
561 
562  // Berechtigung prüfen
563  $authorized = true;
564  if (!empty($this->requestType)) {
565  if (isset($this->permissions[$this->requestType]) && isset($this->permissions[$this->requestType][$method])) {
566  $values = explode(':', $this->permissions[$this->requestType][$method]);
567  $methods = explode(',', $values[0]);
568  $perms = array_filter(explode(',', $values[1]), function($value) {
569  return trim($value) != '';
570  });
571 
572  if (!empty($methods) && !in_array($this->requestMethod, $methods)) {
573  // Request Methode ist für diesen Aufruf nicht erlaubt
574  $authorized = false;
575  }
576 
577  // Prüfen, ob der aktive Benutzer das notwendige Recht für diesen Aufruf besitzt
578  if ($authorized && !empty($perms)) {
579  // Die Site Methode mit diesen Rechten aufrufen
580  if (is_a($object, 'Site')) {
581  $object->setRights($perms);
582  }
583 
584  // Rechte für diese Page prüfen
585  if (is_a($object, 'Page') && !$object->hasRights($perms)) {
586  $authorized = false;
587  }
588 
589  // Rechte für diesen Mediapool prüfen
590  if (is_a($object, 'Mediapool') && ($page = $object->getPage()) && !$page->hasRights($perms)) {
591  $authorized = false;
592  }
593  }
594 
595  // Rechte für diesen Benutzer prüfen
596  if (
597  $authorized
598  && (is_a($object, 'User_SQL')
599  || is_a($object, 'Group_SQL')
600  || is_a($object, 'Role_SQL'))
601  && !$GLOBALS['auth']->hasPermissionOn($object)
602  ) {
603  $authorized = false;
604  }
605 
606  // Rechte für "Ego_System" prüfen
607  if ($this->requestType == 'system') {
608  if (!empty($perms)) {
609  $authorized = false;
610  foreach ($perms as $perm) {
611  list($group, $role) = explode(';', $perm);
612  if ($GLOBALS['auth']->hasPermission($group, $role)) {
613  $authorized = true;
614  break;
615  }
616  }
617  } elseif (!$GLOBALS['auth']->hasSuperuserPermission()) {
618  // Standardmäßig nur für Superuser zulassen
619  $authorized = false;
620  }
621  }
622  } else {
623  // Ist ein Request Typ gesetzt, dann müssen die erlaubten Methoden definiert sein
624  $authorized = false;
625  }
626  if (!$authorized) {
628  }
629  }
630 
631  if (!is_array($params)) {
632  $params = array();
633  }
634  return @call_user_func_array(array($object, $method), $params);
635  }
636 
643  private function toJSON($object) {
644  // Page, User_SQL, Group_SQL und Role_SQL umwandeln
645  if (
646  is_a($object, 'Page')
647  || is_a($object, 'User_SQL')
648  || is_a($object, 'Group_SQL')
649  || is_a($object, 'Role_SQL')
650  ) {
651  return array(
652  'field' => $object->field,
653  'extra' => $object->extra
654  );
655  }
656 
657  // Page_Iterator, User_Iterator, Group_Iterator und Role_Iterator umwandeln
658  if (
659  is_a($object, 'Page_Iterator')
660  || is_a($object, 'User_Iterator')
661  || is_a($object, 'Group_Iterator')
662  || is_a($object, 'Role_Iterator')
663  ) {
664  $results = array();
665  foreach ($object as $item) {
666  $results[] = $this->toJSON($item);
667  }
668  return $results;
669  }
670 
671  return $object;
672  }
673 
679  private function sendSuccess() {
680  switch ($this->requestMethod) {
681  case 'POST':
682  Ego_System::header(201);
683  break;
684  case 'GET':
685  Ego_System::header(200);
686  break;
687  case 'PUT':
688  Ego_System::header(202);
689  break;
690  case 'DELETE':
691  Ego_System::header(204);
692  break;
693  default:
694  Ego_System::header(405);
695  exit;
696  }
697  }
698 
704  private function sendError() {
705  switch ($this->requestMethod) {
706  case 'GET':
707  Ego_System::header(204);
708  break;
709  case 'DELETE':
710  Ego_System::header(400);
711  break;
712  case 'PUT':
713  case 'POST':
714  Ego_System::header(409);
715  break;
716  default:
717  Ego_System::header(405);
718  exit;
719  }
720  }
721 
729  private function startSession($user_id, $token) {
730  $user = new User_SQL($user_id);
731  if (!empty($token) && !empty($user->extra['api_token']) && $user->extra['api_token'] == $token) {
732  // Der Benutzer darf sich über die REST API anmelden
733  Auth_Factory::login($user_id);
734  return !$GLOBALS['auth']->isNobody() ? session_id() : null;
735  } else {
737  }
738  }
739 
745  private function closeSession() {
746  if (!empty($_SESSION['auth_id'])) {
747  Auth_Factory::start($GLOBALS['egotec_conf']['auth']['type'], $_SESSION['auth_id'], false, 'logout');
748  return true;
749  } else {
751  }
752  }
753 }
754 ?>
static file_put_contents($filename, $data, $flags=0, $context=null)
static header($header, $replace=true)
static isDevMode($ignore=true)
static file_exists($file)
static file_get_contents($filename, $utf8=true, $context=null)
static byIdentity($identity, $param=array(), $site=null)
Definition: Page.php:11960
Definition: Site.php:30
static createSite($new_site)
Definition: Site.php:234