Skip to content
Snippets Groups Projects
Select Git revision
  • 6de788a29e6be78f91eaa368b79540da7a56bcd7
  • master default protected
  • simple-task/7248-eol-check-add-node-22
  • 6877_check_iml_deployment
4 results

check_http

Blame
  • index.php 9.19 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>  
     * 2021-03-29  v1.2  <axel.hahn@iml.unibe.ch>  support slashes in branch names
     * 2024-09-02  v1.3  <axel.hahn@unibe.ch>      php8 only; added variable types; short array syntax
     * ======================================================================
     */
    
    $bDebug = false;
    ini_set('display_errors', 1);
    ini_set('display_startup_errors', 1);
    error_reporting(E_ALL);
    
    /**
     * Path to deployment classes
     * @var string
     */
     $sDirClasses = __DIR__ . '/../deployment/classes/';
    
     /**
     * Allowed time delta for client or server
     * @var integer
     */
    $iMaxAge = 60;
    
    require_once("../../config/inc_projects_config.php");
    
    require_once($sDirClasses . '/project.class.php');
    require_once($sDirClasses . 'logger.class.php');
    
    // ----------------------------------------------------------------------
    // FUNCTIONS
    // ----------------------------------------------------------------------
    /**
     * Write debug text (if enabled)
     * 
     * @global boolean $bDebug
     * 
     * @param string  $s       message
     * @param string  $sLevel  level; one of info|
     * @return boolean
     */
    function _wd(string $s, string $sLevel = 'info'): bool
    {
        global $bDebug;
        if ($bDebug) {
            echo "<div class=\"debug debug-$sLevel\">DEBUG: $s</div>";
        }
        return true;
    }
    
    /**
     * Abort execution of API request with error
     * 
     * @param string   $s        message
     * @param integer  $iStatus  http status code to send
     */
    function _quit(string $s, int $iStatus = 400): void
    {
        $aStatus = [
            400 => 'HTTP/1.0 400 Bad Request',
            401 => 'HTTP/1.0 401 Unauthorized',
            403 => 'HTTP/1.0 403 Access denied',
            404 => 'HTTP/1.0 404 Not found',
        ];
        header($aStatus[$iStatus]);
        _done(['status' => $iStatus, 'info' => $aStatus[$iStatus], 'message' => $s]);
    }
    
    /**
     * End with OK output
     * 
     * @param array $Data  array data to show as JSON
     * @return void
     */
    function _done(array $Data): void
    {
        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 string $sProjectSecret
     * @return boolean
     */
    function _checkAuth(string $sProjectSecret): bool
    {
        global $iMaxAge;
        $aReqHeaders = apache_request_headers();
        _wd('<pre>' . print_r($aReqHeaders, 1) . '</pre>');
        if (!isset($aReqHeaders['Authorization'])) {
            _quit('Access denied. Missing authorization.', 401);
        }
        if (!isset($aReqHeaders['Date'])) {
            _quit('Access denied. Missing field "Date:" in the request header.', 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.', 401);
        }
    
        $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. Maybe client or server is out of sync.', 403);
        }
        if ($iAge < -$iMaxAge) {
            _quit('Access denied. Hash is ' . $iAge . ' sec in future but only ' . $iMaxAge . ' sec are allowed. Maybe client or server is out of sync.', 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/...
    $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;
                    $sBranch = implode('/', array_slice($aUriSplit, 4));
    
                    // $sParam4    = isset($aUriSplit[4]) ? $aUriSplit[4] : false;
                    // $sParam5    = isset($aUriSplit[5]) ? $aUriSplit[5] : false;
                    $sMethod = $_SERVER['REQUEST_METHOD'];
                    _wd('$sPrjId = ' . $sPrjId);
                    _wd('$sPrjAction = ' . $sPrjAction);
                    _wd('$sBranch = ' . $sBranch);
    
                    $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 . '] cannot be initialized.', 400); // never reached
                    }
    
                    // get secret
                    $aPrjCfg = $oProject->getConfig();
                    if(!count($aPrjCfg)){
                        _quit('ERROR: project with id [' . $sPrjId . '] does not exist.', 404);
                    }
    
                    $sProjectSecret = $aPrjCfg['api']['secret'] ?? false;
                    if (!$sProjectSecret) {
                        _quit('Access denied. API access is disabled.', 403);
                    }
    
                    // check authorization 
                    _checkAuth($sProjectSecret);
    
                    // echo "OK: request was authorized successfully.\n";
    
                    $oProject->oUser->setUser('api');
    
                    switch ($sPrjAction) {
                        case "build":
                            if ($sBranch) {
                                $aResult = $oProject->setBranchname($sBranch);
                            }
                            $sBranchname = $oProject->getBranchname();
                            $aRepodata = $oProject->getRemoteBranches(true); // ignore cache = 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([
                                    '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.');
    }