<?php

/**
 * Front Controller using http://flightphp.com/
 * In all these calls user session must exist, so we have to start it at the 
 * beginning using the session_start() function.
 */
//

function setCallback($callback) {
    global $session, $callbackHandler;

    if (!isset($callback) || $callback === '') {
        $callback = null;
    }
    $session->setCallbackURL($callbackHandler, $callback);
    return $session->getCallbackURL();
}

/**
 * Display the main page (authentication method selection) or the available
 * services list if a valid callback is not found
 */
Flight::route('/', function() {

    session_start();
    global $locator;

    $action = Flight::request()->query['action'];
    $locator->getSession()->setAction($action);

    switch ($action) {
        case "oauth2client":
            $clientId = $locator->getSession()->getOAuth2RequestData()->clientId;
            $client = $locator->getBrowserBasedOAuth2ClientById($clientId);
            $authPageModel = new \RAP\AuthPageModel($locator, $client);
            renderMainPage($authPageModel);
            break;
        case "account":
            $client = new \RAP\InternalClient('account');
            $client->icon = 'account-manager.png';
            $client->title = 'Account Management';
            $authPageModel = new \RAP\AuthPageModel($locator, $client);
            renderMainPage($authPageModel);
            break;
        case "join":
            $client = new \RAP\InternalClient('account');
            $client->title = 'Join identities';
            $authPageModel = new \RAP\AuthPageModel($locator, $client);
            renderMainPage($authPageModel);
            break;
        default:
            session_destroy();
            $clients = $locator->getBrowserBasedOAuth2Clients();
            Flight::render('services-list.php', array('title' => 'RAP',
                'version' => $locator->getVersion(),
                'contextRoot' => $locator->config->contextRoot,
                'clients' => $clients,
                'action' => $locator->getBasePath() . '/'));
            break;
    }
});

function renderMainPage(RAP\AuthPageModel $authPageModel) {
    global $locator;
    Flight::render('main-page.php', array(
        'title' => 'RAP',
        'version' => $locator->getVersion(),
        'contextRoot' => $locator->config->contextRoot,
        'model' => $authPageModel));
}

Flight::route('GET /auth/oauth2/authorize', function() {

    session_start();
    global $locator;

    $params = [
        "client_id" => filter_input(INPUT_GET, 'client_id', FILTER_SANITIZE_STRING),
        "redirect_uri" => filter_input(INPUT_GET, 'redirect_uri', FILTER_SANITIZE_STRING),
        "alg" => filter_input(INPUT_GET, 'alg', FILTER_SANITIZE_STRING),
        "state" => filter_input(INPUT_GET, 'state', FILTER_SANITIZE_STRING),
        "scope" => filter_input(INPUT_GET, 'scope', FILTER_SANITIZE_STRING),
        "nonce" => filter_input(INPUT_GET, 'nonce', FILTER_SANITIZE_STRING)
    ];

    $requestHandler = new \RAP\OAuth2RequestHandler($locator);
    $requestHandler->handleAuthorizeRequest($params);

    Flight::redirect('/?action=oauth2client');
});

Flight::route('POST /auth/oauth2/token', function() {

    global $locator;

    $params = [
        "grant_type" => filter_input(INPUT_POST, "grant_type", FILTER_SANITIZE_STRING),
        "code" => filter_input(INPUT_POST, "code", FILTER_SANITIZE_STRING),
        "redirect_uri" => filter_input(INPUT_POST, "redirect_uri", FILTER_SANITIZE_STRING),
        "refresh_token" => filter_input(INPUT_POST, "refresh_token", FILTER_SANITIZE_STRING),
        "scope" => filter_input(INPUT_POST, "scope", FILTER_SANITIZE_STRING),
        // For token exchange
        "resource" => filter_input(INPUT_POST, "resource", FILTER_SANITIZE_STRING),
        "audience" => filter_input(INPUT_POST, "audience", FILTER_SANITIZE_STRING),
        "expires_in" => filter_input(INPUT_POST, "expires_in", FILTER_SANITIZE_NUMBER_INT),
        "subject_token" => filter_input(INPUT_POST, "subject_token", FILTER_SANITIZE_STRING),
        "subject_token_type" => filter_input(INPUT_POST, "subject_token_type", FILTER_SANITIZE_STRING)
    ];

    $headers = apache_request_headers();

    $requestHandler = new \RAP\OAuth2RequestHandler($locator);
    $token = $requestHandler->handleAccessTokenRequest($params, $headers);

    Flight::json($token);
});

Flight::route('POST /auth/oauth2/check_token', function() {

    global $locator;

    $requestHandler = new \RAP\OAuth2RequestHandler($locator);
    $headers = apache_request_headers();
    $result = $requestHandler->handleCheckTokenRequest($headers);

    Flight::json($result);
});

Flight::route('GET /auth/oidc/jwks', function() {

    global $locator;

    $jwksHandler = new \RAP\JWKSHandler($locator);
    $jwks = $jwksHandler->getJWKS();

    Flight::json($jwks);
});

Flight::route('GET /logout', function() {
    session_start();
    session_destroy();
    Flight::redirect('/');
});

function sendAuthRedirect($url) {
    session_start();
    // reload callback from query to avoid problem with session shared between 
    // multiple browser tabs
    setCallback(Flight::request()->query['callback']);
    Flight::redirect($url);
}

Flight::route('/auth/social/google', function() {
    session_start();
    global $locator;
    $googleLogin = new \RAP\GoogleLogin($locator);
    $redirect = $googleLogin->login();
    if ($redirect !== null) {
        Flight::redirect($redirect);
    }
});

Flight::route('/auth/social/facebook', function() {
    session_start();
    global $locator;
    $facebookLogin = new \RAP\FacebookLogin($locator);
    Flight::redirect($facebookLogin->login());
});

Flight::route('/auth/social/facebook/token', function() {
    session_start();
    global $locator;
    $facebookLogin = new \RAP\FacebookLogin($locator);
    Flight::redirect($facebookLogin->retrieveToken());
});

Flight::route('/auth/social/linkedIn', function() {
    session_start();
    global $locator;
    $linkedInLogin = new \RAP\LinkedInLogin($locator);
    Flight::redirect($linkedInLogin->login());
});

Flight::route('/auth/social/linkedIn/token', function() {
    session_start();
    global $locator;
    $linkedInLogin = new \RAP\LinkedInLogin($locator);
    Flight::redirect($linkedInLogin->retrieveToken());
});

Flight::route('/auth/orcid', function() {
    session_start();
    global $locator;
    $orcidLogin = new \RAP\OrcidLogin($locator);
    Flight::redirect($orcidLogin->login());
});

Flight::route('/auth/orcid/token', function() {
    session_start();
    global $locator;
    $code = filter_input(INPUT_GET, 'code', FILTER_SANITIZE_STRING);
    $orcidLogin = new \RAP\OrcidLogin($locator);
    Flight::redirect($orcidLogin->retrieveToken($code));
});

Flight::route('/auth/eduGAIN', function() {
    session_start();
    global $locator;
    $shibbolethLogin = new \RAP\ShibbolethLogin($locator);
    Flight::redirect($shibbolethLogin->login());
});

Flight::route('/auth/x509', function() {
    session_start();
    global $locator;
    $x509Login = new \RAP\X509Login($locator);
    $x509Login->login();
});

Flight::route('/local', function() {
    global $locator;
    Flight::redirect($locator->config->authenticationMethods->LocalIdP->url);
});

/**
 * Render the page for selecting the correct name and username from candidates
 * list during a X.509 registration.
 */
Flight::route('GET /x509-name-surname', function() {

    session_start();
    global $locator;
    $session = $locator->getSession();

    if ($session->getX509DataToRegister() !== null && $session->getX509DataToRegister()->name === null) {
        Flight::render('x509-name-surname.php', array('title' => 'Select name and surname',
            'version' => $locator->getVersion(),
            'contextRoot' => $locator->config->contextRoot,
            'fullName' => $session->getX509DataToRegister()->fullName,
            'candidateNames' => $session->getX509DataToRegister()->candidateNames));
    } else {
        // Redirect to index
        header("Location: " . $locator->getBasePath());
        die();
    }
});

/**
 * Complete the X.509 registration selecting the correct name and surname specified
 * by the user (special case of composite names).
 */
Flight::route('POST /submit-x509-name', function() {

    session_start();

    $selectedNameIndex = Flight::request()->data['selected-name'];

    global $locator;
    $session = $locator->getSession();

    if ($session->getX509DataToRegister() !== null) {
        $session->getX509DataToRegister()->selectCandidateName($selectedNameIndex);
        $loginHandler = new \RAP\X509Login($locator);
        $redirect = $loginHandler->afterNameSurnameSelection($session->getX509DataToRegister());
        Flight::redirect($redirect);
    } else {
        die('X.509 data not returned');
    }
});

/**
 * Display Term of Use acceptance page.
 */
Flight::route('GET /tou-check', function() {

    session_start();
    global $locator;

    if ($locator->getSession()->getUser() === null) {
        die("User data not retrieved.");
    } else {
        Flight::render('tou-check.php', array('title' => 'Terms of Use acceptance',
            'user' => $locator->getSession()->getUser(),
            'version' => $locator->getVersion(),
            'contextRoot' => $locator->config->contextRoot,
            'registration_url' => $locator->getBasePath() . '/register'));
    }
});

Flight::route('GET /confirm-join', function() {

    session_start();
    global $locator;

    if ($locator->getSession()->getUser() === null) {
        die("User data not retrieved.");
    } else {
        Flight::render('confirm-join.php', array('title' => 'Confirm join',
            'autojoin' => $locator->getSession()->isAutojoin(),
            'user' => $locator->getSession()->getUser(),
            'user_to_join' => $locator->getSession()->getUserToJoin(),
            'version' => $locator->getVersion(),
            'contextRoot' => $locator->config->contextRoot));
    }
});

Flight::route('POST /confirm-join', function() {

    session_start();
    global $locator;

    $user = $locator->getSession()->getUserToJoin();
    if ($user === null) {
        die("Unable to find user to join");
    } else {
        $loginHandler = new \RAP\LoginHandler($locator, $locator->getSession()->getLoginIdentityType());
        Flight::redirect($loginHandler->getAfterLoginRedirect($user));
    }
});

/**
 * Stores the user data into the database after he/she accepted the Terms of Use.
 */
Flight::route('GET /register', function() {

    session_start();
    global $locator;

    $user = $locator->getSession()->getUser();

    if ($user === null) {
        die("User data not retrieved.");
    } else {
        $locator->getUserHandler()->saveUser($user);

        $loginHandler = new \RAP\LoginHandler($locator, $locator->getSession()->getLoginIdentityType());
        Flight::redirect($loginHandler->getAfterLoginRedirect($user));
    }
});

/**
 * Shows Account Management page.
 */
Flight::route('GET /account', function () {

    session_start();
    global $locator;

    $user = $locator->getSession()->getUser();
    if ($user === null) {
        Flight::redirect('/');
    } else {
        $admin = $locator->getUserDAO()->isAdmin($user->id);
        Flight::render('account-management.php', array('title' => 'RAP Account Management',
            'version' => $locator->getVersion(),
            'contextRoot' => $locator->config->contextRoot,
            'session' => $locator->getSession(),
            'admin' => $admin));
    }
});

Flight::route('GET /token-issuer', function () {

    session_start();

    if (empty($_SESSION['csrf_token'])) {
        $_SESSION['csrf_token'] = bin2hex(random_bytes(32));
    }
    $csrfToken = $_SESSION['csrf_token'];

    global $locator;

    $user = $locator->getSession()->getUser();
    $config = $locator->config->tokenIssuer;

    if ($user === null) {
        Flight::redirect('/');
    } else {
        Flight::render('token-issuer.php', array('title' => 'RAP Token Issuer',
            'version' => $locator->getVersion(),
            'contextRoot' => $locator->config->contextRoot,
            'session' => $locator->getSession(),
            'config' => $config,
            'csrfToken' => $csrfToken));
    }
});

Flight::route('POST /token-issuer', function () {

    session_start();
    global $locator;

    if (empty($_POST['csrf_token']) || !(hash_equals($_SESSION['csrf_token'], $_POST['csrf_token']))) {
        throw new \RAP\UnauthorizedException("Invalid CSRF token");
    }
    if ($locator->getSession()->getUser() === null) {
        throw new \RAP\UnauthorizedException("You must be registered to perform this action");
    }

    $postData = Flight::request()->data;
    if (!isset($postData['lifespan']) || !isset($postData['audit'])) {
        throw new \RAP\BadRequestException("Missing form parameter");
    }

    $tokenBuilder = $locator->getTokenBuilder();
    $userId = $locator->getSession()->getUser()->id;
    $token = $tokenBuilder->generateNewToken($userId, $postData['lifespan'], $postData['audit']);

    header('Content-Type: text/plain');
    header("Content-disposition: attachment; filename=\"token.txt\"");
    echo $token;
});

include 'admin.php';