| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222 | 
							- <?php
 
- /**
 
-  * Classe de gestion des API (REST).
 
-  * @author Valentin CARRUESCO
 
-  * @category Plugin
 
-  * @license MIT
 
-  */
 
- class Api{
 
- 	public $slug,$description;
 
- 	function __construct($slug=null,$description=null){
 
- 		if(isset($slug)) $this->slug = $slug;
 
- 		if(isset($description)) $this->description = $description;
 
- 		$this->route = array();
 
- 		return $this;
 
- 	}
 
- 	function route($slug = null,$description = null,$method = null,$callback){
 
- 		$route =  new ApiRoute($slug,$description,$method,$callback);
 
- 		$this->route[$route->slug][$method] = $route;
 
- 	}
 
- 	function register(){
 
- 		Plugin::addHook('api',function(&$apis){
 
- 			$apis[$this->slug] = $this;
 
- 			
 
- 		});
 
- 	}
 
- 	//retourne la liste des headers de la requete courante (clé normalisées)
 
- 	public static function headers(){
 
- 		$headers = array();
 
- 		foreach(getallheaders() as $key=>$value) $headers[mb_strtolower($key)] = $value;
 
- 		return $headers;
 
- 	}
 
- 	//définis si la requete courante est via api ou en http classique (ou autre)
 
- 	public static function requested(){
 
- 		return preg_match('/\/api\/v[0-9]*\//i', $_SERVER['REQUEST_URI']);
 
- 	}
 
- 	//SI un JWToken est fournis en header Authorization: Bearer <token>, on récupere la session associée
 
- 	//si la session a expirée on la relance
 
- 	public static function token_session(){
 
- 		if(php_sapi_name() == 'cli') return;
 
- 		if(!self::requested()) return;
 
- 		
 
- 		$headers = self::headers();
 
- 		if(!isset($headers['authorization'])) return;
 
- 		
 
- 		try{
 
- 			$authorization = explode(' ',$headers['authorization']);
 
- 			if(count($authorization)!=2 || strtolower($authorization[0])!='bearer') throw new Exception("Bad Authorization header (required Authorization: Bearer <JWToken>)",403);
 
- 			global $conf,$myUser;
 
- 		
 
- 			$token = JWToken::parse(trim($authorization[1]),$conf->get('jwtauth_secret'));
 
- 			//si une session est en cours 
 
- 			if(session_status() == PHP_SESSION_ACTIVE){ 
 
- 				//si la session courant a le mauvais id (ex une session anonyme ou expirée) on la détruit et on la recréé avec le bon id
 
- 				if( session_id() !=$token['attributes']['session_id']){
 
- 					session_destroy();
 
- 					session_id($token['attributes']['session_id']);
 
- 					session_start();
 
- 				}
 
- 			}else{
 
- 				//si aucun session en cours, on la créé avec le bon id
 
- 				session_id($token['attributes']['session_id']);
 
- 				session_start();
 
- 			}
 
- 			
 
- 			if(!isset($_SESSION['currentUser'])) throw new Exception('Token session has expired',498);
 
- 		}catch(Exception $e){
 
- 			$response['error'] = $e->getMessage(); 
 
- 			$response['errorCode'] = $e->getCode(); 
 
- 			echo json_encode($response);
 
- 			exit;
 
- 		}
 
- 	}
 
- 	public static function run(){
 
- 		global $_,$myUser;
 
- 		$response = array();
 
- 		
 
- 		try{
 
- 			set_error_handler(function( $errno ,  $errstr , $errfile ,  $errline){
 
- 				throw new Exception("Error ".$errno." Processing Request : ".$errfile." => ".$errstr.' L'.$errline, 500);
 
- 			});
 
- 			// register_shutdown_function(function(){
 
- 			// 	$error = error_get_last();
 
- 			// 	throw new Exception("Error 0 Processing Request ".$error['file'], 500);
 
- 			// });
 
- 			$command = explode('/',$_['command']);
 
- 			if(count($command)<1) throw new Exception("Unspecified API");
 
- 			$headers = self::headers();
 
- 			
 
- 			//basic auth (deprecated, utiliser plutot JWToken et Authorization Bearer)
 
- 			if(isset($_SERVER['PHP_AUTH_USER']) && isset($_SERVER['PHP_AUTH_PW'])){
 
- 				$user = User::check($_SERVER['PHP_AUTH_USER'],$_SERVER['PHP_AUTH_PW']);
 
- 				if(!$user) throw new Exception('Le compte spécifié est inexistant');
 
- 				$myUser = $user;
 
- 			}
 
- 			
 
- 			$request = array(
 
- 				'version' =>array_shift($command),
 
- 				'module' =>array_shift($command),
 
- 				'route' =>array_shift($command),
 
- 				'body' =>file_get_contents("php://input"),
 
- 				'headers' =>$headers,
 
- 				'method' => strtoupper($_SERVER['REQUEST_METHOD']),
 
- 				'parameters' => $_,
 
- 				'pathes' => array_filter($command)
 
- 			);
 
- 			$code = 200;
 
- 			$headers = array(
 
- 				'Content-Type: application/json'
 
- 			);
 
- 			$apis = array();
 
- 			Plugin::callHook('api',array(&$apis));
 
- 			//format de l'url : /api/v{version}/
 
- 			if(!isset($request['version']) || !is_numeric(substr($request['version'],1)) || substr($request['version'], 0,1)!='v') throw new Exception("Api version is missing or invalid, specify ".ROOT_URL."/api/{version} for valid requests", 404);
 
- 			if($request['module'] == 'schema'){
 
- 				if(!$myUser->connected()) throw new Exception("Credentials are missing",401);
 
- 				foreach ($apis as $api) {
 
- 					$routes= array();
 
- 					foreach($api->route as $route){
 
- 						
 
- 						foreach($route as $method=>$infos){
 
- 							if(!isset($routes[$infos->slug])) $routes[$infos->slug] = array() ;
 
- 							$routes[$infos->slug][] = $infos->method.' '.$api->slug .'/'.$infos->pattern .': '. $infos->description ;
 
- 						}
 
- 					}
 
- 					$response[] =  array( $api->slug => $api->description,'routes'=> $routes);
 
- 				}
 
- 			}else{
 
- 				if(!isset($apis[$request['module']])) throw new Exception("Api '".$request['module']."' is missing, see ".ROOT_URL."/api/{version}/schema for available calls", 404);
 
- 				$api = $apis[$request['module']];
 
- 				
 
- 				if(!isset($api->route[$request['route']]))  throw new Exception("Route '".$api->slug.'/'.$request['route']."' is missing, see ".ROOT_URL."/api/schema?pretty for available calls", 404);
 
- 				$route = $api->route[$request['route']];
 
- 				
 
- 				if(!isset($route[$request['method']]))  throw new Exception("Method ".$request['method']." '".$api->slug.'/'.$route->slug."' is not allowed", 405);
 
- 				$method = $route[$request['method']];
 
- 				$callback = $method->callback;
 
- 				$callback($request,$response);
 
- 				if(isset($response['code'])) $code = $response['code'];
 
- 				if(isset($response['headers'])) $headers = array_merge($headers,$response['headers']);
 
- 				unset($response['code']);
 
- 				unset($response['headers']);
 
- 			}
 
- 			
 
- 		}catch(Exception $e){
 
- 			$response['error'] = $e->getMessage();
 
- 			$code = $e->getCode();					
 
- 			if(empty($code)) $code = 0;
 
- 			$response['errorCode'] = $code;	
 
- 		}
 
- 		$codes = array(
 
- 			200 => 'HTTP/1.1 200 Ok',
 
- 			201 => 'HTTP/1.1 201 Created',
 
- 			204 => 'HTTP/1.1 204 No Content',
 
- 			207 => 'HTTP/1.1 207 Multi-Status',
 
- 			400 => 'HTTP/1.0 400 Bad Request',
 
- 			401 => 'HTTP/1.0 401 Unauthorized',
 
- 			403 => 'HTTP/1.1 403 Forbidden',
 
- 			404 => 'HTTP/1.1 404 Not Found',
 
- 			404 => 'HTTP/1.1 405 Method Not Allowed',
 
- 			406 => 'HTTP/1.1 406 Not acceptable',
 
- 			409 => 'HTTP/1.1 409 Conflict',
 
- 			415 => 'HTTP/1.1 415 Unsupported Media Type',
 
- 			423 => 'HTTP/1.1 423 Locked',
 
- 			500 => 'HTTP/1.1 500 Internal Server Error',
 
- 			501 => 'HTTP/1.1 501 Not implemented',
 
- 			0 => 'HTTP/1.1 0 Unknown Error',
 
- 		);
 
- 		if (ob_get_length()) ob_end_clean();
 
- 		header(isset($codes[$code]) ? $codes[$code] : $codes[0]);
 
- 		foreach ($headers as $header) {
 
- 			header($header);
 
- 		}
 
- 		
 
- 		if(isset($_['pretty'])){
 
- 			echo json_encode($response,JSON_PRETTY_PRINT);
 
- 		}else{
 
- 			echo json_encode($response);
 
- 		}
 
- 		exit();
 
- 	}
 
- }
 
- class ApiRoute{
 
- 	public $slug,$description,$callback,$method,$pathes,$parameters,$pattern;
 
- 	function __construct($slug='default',$description='No description',$method='GET',$callback){
 
- 		$this->pattern = $slug;
 
- 		$infos = explode('?',$slug);
 
- 		if(isset($infos[1])) $this->parameters = $infos[1];
 
- 		$infos = explode('/',$infos[0]);
 
- 		$this->slug = array_shift($infos);
 
- 		$this->pathes = $infos;
 
- 		$this->description = $description;
 
- 		$this->method = $method;
 
- 		$this->callback = $callback;
 
- 	}
 
- }
 
- ?>
 
 
  |