<?php /* ====================================================================== * * A P I F O R C I S E R V E R * * GET /api/v1/projects/ * GET /api/v1/project/[ID]/build/[name-of-branch] * POST /api/v1/project/[ID]/build/[name-of-branch] * GET /api/v1/project/[ID]/phases * * ---------------------------------------------------------------------- * 2020-06-16 v0.9 <axel.hahn@iml.unibe.ch> * ====================================================================== */ $bDebug=false; ini_set('display_errors', 1); ini_set('display_startup_errors', 1); error_reporting(E_ALL); require_once("../../config/inc_projects_config.php"); $sDirClasses=__DIR__.'/../deployment/classes/'; require_once($sDirClasses.'/project.class.php'); require_once($sDirClasses.'logger.class.php'); $iMaxAge=60; // ---------------------------------------------------------------------- // FUNCTIONS // ---------------------------------------------------------------------- /** * write debug text (if enabled) * @global boolean $bDebug * @param string $s message * @param string $sLevel level; one of info| * @return boolean */ function _wd($s, $sLevel='info'){ global $bDebug; if ($bDebug){ echo '<div class="debug debug-'.$sLevel.'">DEBUG: '.$s.'</div>'; } return true; } /** * abort execution with error * @param string $s message * @param integer $iStatus http status code to send */ function _quit($s, $iStatus=400){ $aStatus=array( 400=>'HTTP/1.0 400 Bad Request', 403=>'HTTP/1.0 403 Access denied', 404=>'HTTP/1.0 404 Not found', ); header($aStatus[$iStatus]); _done(array('status'=>$iStatus, 'info'=>$aStatus[$iStatus], 'message'=>$s)); } /** * end with OK output * @param type $Data */ function _done($Data){ echo is_array($Data) ? json_encode($Data, JSON_PRETTY_PRINT) : $Data ; die(); } /** * Check authorization in the http request header and age of timestamp * On a failed check the request will be terminated * @global int $iMaxAge max allowed age * @param type $sProjectSecret * @return boolean */ function _checkAuth($sProjectSecret){ global $iMaxAge; $aReqHeaders=apache_request_headers(); _wd('<pre>'.print_r($aReqHeaders, 1).'</pre>'); if(!isset($aReqHeaders['Authorization'])){ _quit('Access denied. Missing authorization.', 403); } $sGotHash= preg_replace('/^.*\:/', '', $aReqHeaders['Authorization']); $sGotDate= $aReqHeaders['Date']; $sGotMethod=$_SERVER['REQUEST_METHOD']; $sGotReq=$_SERVER['REQUEST_URI']; $sMyData="${sGotMethod}\n${sGotReq}\n${sGotDate}\n"; $sMyHash= base64_encode(hash_hmac("sha1", $sMyData, $sProjectSecret)); _wd('Hash: '.$sGotHash.' -- from header'); _wd('Hash: '.$sMyHash.' -- rebuilt'); if($sGotHash!==$sMyHash){ _quit('Access denied. Invalid hash.', 403); } $iAge=date('U')-date('U', strtotime($sGotDate)); _wd('Date: '.$sGotDate.' - age: '.$iAge.' sec'); if($iAge>$iMaxAge){ _quit('Access denied. Hash is out of date: '.$iAge. ' sec is older '.$iMaxAge.' sec', 403); } return true; } // ---------------------------------------------------------------------- // MAIN // ---------------------------------------------------------------------- if (!$bDebug){ header('Content-Type: application/json'); } _wd('Start: '.date('Y-m-d H:i:s').'<style>body{background:#eee; color:#456;} .debug{background:#ddd; margin-bottom: 2px;} </style>'); _wd('request uri is '.$_SERVER["REQUEST_URI"]); _wd('<pre>GET: '.print_r($_GET, 1).'</pre>'); // ---------- SPLIT URL $aUriSplit= explode('/', preg_replace('/\?.*$/', '', $_SERVER["REQUEST_URI"])); array_shift($aUriSplit); array_shift($aUriSplit); _wd('<pre>$aUriSplit: '.print_r($aUriSplit, 1).'</pre>'); /* /api/v1/projects/ci/build?auth=123 $aUriSplit: Array ( [0] => v1 [1] => projects [2] => ci [3] => build ) */ $sApiVersion = isset($aUriSplit[0]) ? $aUriSplit[0] : false; $sApiItem = isset($aUriSplit[1]) ? $aUriSplit[1] : false; if(!$sApiVersion){ _quit('ERROR: no param for api version was found.'); } if(!$sApiItem){ _quit('ERROR: no param for item was found.'); } switch ($sApiVersion){ case 'v1': switch($sApiItem){ case 'projects': $oProject=new project(); $aList=$oProject->getProjects(); _wd('<pre>'.print_r($aList,1).'</pre>'); _done($aList); break;; case 'project': // path /api/v1/project $sPrjId = isset($aUriSplit[2]) ? $aUriSplit[2] : false; $sPrjAction = isset($aUriSplit[3]) ? $aUriSplit[3] : false; $sParam4 = isset($aUriSplit[4]) ? $aUriSplit[4] : false; $sParam5 = isset($aUriSplit[5]) ? $aUriSplit[5] : false; $sMethod = $_SERVER['REQUEST_METHOD']; _wd('$sPrjId = '.$sPrjId); _wd('$sPrjAction = '.$sPrjAction); $oCLog = new logger(); // try to init the given project try{ ob_start(); $oProject=new project($sPrjId); // $oProject->setProjectById($sPrjId); ob_end_clean(); } catch (Exception $exc) { _quit('ERROR: project with id ['.$sPrjId.'] does not exist.', 404); } // get secret $aPrjCfg=$oProject->getConfig(); $sProjectSecret=isset($aPrjCfg['api']['secret']) ? $aPrjCfg['api']['secret'] : false; if(!$sProjectSecret){ _quit('Access denied. API access is disabled.'); } // check authorization _checkAuth($sProjectSecret); // echo "OK: request was authorized successfully.\n"; $oProject->oUser->setUser('api'); switch($sPrjAction){ case "build": if ($sParam4){ $aResult=$oProject->setBranchname($sParam4 . ($sParam5 ? '/'.$sParam5 : '')); } $sBranchname=$oProject->getBranchname(); $aRepodata = $oProject->getRemoteBranches(true); if(!isset($aRepodata[$sBranchname])){ _quit('ERROR: branch not found: '.$sBranchname, 404); } // echo "branch is set to ".$oProject->getBranchname()."\n"; if ($sMethod==='GET'){ $sNext=$oProject->getNextPhase(); _done(array( 'branch'=>$sBranchname, 'phase'=>$sNext, 'repo'=>$aRepodata[$sBranchname] )); } if ($sMethod==='POST'){ echo "starting build() ..."; flush(); echo $oProject->build(); } break;; case "phases": _done($oProject->getAllPhaseInfos()); break;; default: _quit('ERROR: Wrong action ['.$sApiItem.'].'); } break;; default: _quit('ERROR: item ['.$sApiItem.'] is invalid.'); } break; default: _quit('ERROR: Wrong (unsupported) api version.'); }