Skip to content
Snippets Groups Projects
index.php 8.84 KiB
<?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.');
    }