<?php // vim:set et ts=4 sw=4 sts=4:
# Copyright (C) 2003-2014 hipplay Inc.
#
# This is UNPUBLISHED PROPRIETARY SOURCE CODE OF hipplay Inc.
# The copyright notice above does not evidence any actual or intended
# publication of such source code. The contents of this file may not
# be disclosed to third parties, copied or duplicated in any form, in
# whole or in part, without the prior written permission of
# hipplay Inc.
#
namespace HipPlayConnect\Identity;


use DomainException;
use HipPlayConnect\Configuration\AxBranding;
use HipPlayConnect\Configuration\AxOERelease;
use HipPlayConnect\ThirdParty\Firebase\JWT;
use UnexpectedValueException;

class AxAuthenticationToken {

    const DEBUG_TOKEN = FALSE;

    /**
     * Verify the contents of the Authentication Token
     *
     * @param string $token The JWT standard token.
     *
     * @throws AxAuthenticationCodeErrorException
     * @throws AxInvalidAuthenticationCodeException
     */
    public static function verify($token) {
        $publicKeyPem = file_get_contents('/etc/ssl/public/cloud-public.pem');
        $publicKey = openssl_pkey_get_public($publicKeyPem);

        $subDomain = AxOERelease::get('Subdomain');

        try {
            $claims = JWT::decode($token, $publicKey);
        } catch (UnexpectedValueException $e) {
            throw new AxInvalidAuthenticationCodeException();
        } catch (DomainException $e) {
            throw new AxAuthenticationCodeErrorException();
        }

        if (!self::verifyClaims($claims, $subDomain))
            throw new AxInvalidAuthenticationCodeException();
    }

    protected static function getUpTime() {
        $strUpTime = @file_get_contents('/proc/uptime');
        if (FALSE === $strUpTime) {
            error_log("Could not read /proc/uptime");
            return 0;
        }

        return intval($strUpTime);
    }

    protected static function getServerClockDelta() {
        if (!file_exists('/tmp/server-clock-delta'))
            self::generateServerClockDelta();

        $delta = @file_get_contents('/tmp/server-clock-delta');
        if (FALSE === $delta) {
            error_log("Could not read /tmp/server-clock-delta");
            return 0;
        }

        return trim($delta);
    }

    protected static function generateServerClockDelta() {
        $headers = get_headers("https://" . AxBranding::get('PortalServer'), 1);
        if (!is_array($headers) || !isset($headers['Date']))
            return;
        date_default_timezone_set('UTC');
        $utc = strtotime($headers['Date']);
        $uptime = self::getUpTime();
        $delta = $utc - $uptime;

        if (!$utc || !$uptime)
            return;

        @file_put_contents('/tmp/server-clock-delta', $delta);
    }

    protected static function verifyClaims($claims, $audience) {
        if (self::DEBUG_TOKEN) error_log("Audience: $audience");
        $upTime = self::getUpTime();
        if (self::DEBUG_TOKEN) error_log("Uptime: $upTime");
        $delta = self::getServerClockDelta();
        if (self::DEBUG_TOKEN) error_log("Delta: $delta");
        $now = $upTime + $delta;

        // Not our website
        if ($claims->iss != "kwiltapp.com") {
            error_log("Invalid issuer: " . $claims->iss);
            return FALSE;
        }

        // Not this CPE
        if ($claims->aud != $audience) {
            error_log("Invalid audience: " . $claims->aud);
            return FALSE;
        }

        // Access tokens are not currently account specific on the CPE side.
//        if ($claims->sub != $accountId)
//            return FALSE;

        // Access token not yet valid
        if ($now < $claims->nbf) {
            error_log("Not yet valid: nbf=" . $claims->nbf . " now=$now");
            return FALSE;
        } else {
            if (self::DEBUG_TOKEN) error_log("Already valid: nbf=" . $claims->nbf . " now=$now");
        }

        // Access token already expired
        if ($now > $claims->exp) {
            error_log("Already expired: exp=" . $claims->exp . " now=$now");
            return FALSE;
        } else {
            if (self::DEBUG_TOKEN) error_log("Not yet expired: exp=" . $claims->exp . " now=$now");
        }

        return TRUE;
    }

} 