/*
 * nassh-relay - Relay Server for tunneling ssh through a http endpoint
 *
 * Website: https://github.com/zyclonite/nassh-relay
 *
 * Copyright 2014-2020   zyclonite    networx
 *                       http://zyclonite.net
 * Developer: Lukas Prettenthaler
 */
package net.zyclonite.nassh.handler;

import io.vertx.core.Handler;
import io.vertx.core.Vertx;
import io.vertx.core.http.Cookie;
import io.vertx.core.http.HttpServerRequest;
import io.vertx.core.http.HttpServerResponse;
import io.vertx.core.json.JsonObject;
import io.vertx.core.logging.Logger;
import io.vertx.core.logging.LoggerFactory;
import io.vertx.ext.auth.oauth2.AccessToken;
import io.vertx.ext.auth.oauth2.OAuth2Auth;
import io.vertx.ext.auth.oauth2.providers.GoogleAuth;
import io.vertx.ext.web.RoutingContext;
import net.zyclonite.nassh.model.AuthSession;
import net.zyclonite.nassh.util.AuthSessionManager;
import net.zyclonite.nassh.util.Constants;

import java.util.UUID;

/**
 * @author zyclonite
 */
public class CookiePostHandler implements Handler<RoutingContext> {

    private static Logger logger = LoggerFactory.getLogger(CookiePostHandler.class);
    private final OAuth2Auth oauth2;

    public CookiePostHandler(final Vertx vertx, final JsonObject config) {
        final JsonObject auth = config.getJsonObject("auth");
        this.oauth2 = GoogleAuth.create(vertx, auth.getString("client-id"), auth.getString("client-secret"));
    }

    @Override
    public void handle(final RoutingContext context) {
        logger.debug("got request");
        final HttpServerRequest request = context.request();
        final HttpServerResponse response = context.response();
        response.putHeader("Cache-Control", "no-store, no-cache, must-revalidate, max-age=0");
        response.putHeader("Pragma", "no-cache");
        response.putHeader("Content-Type", "no-cache");
        response.putHeader("Content-Type", "application/json");
        final Cookie cookie = context.getCookie(Constants.SESSIONCOOKIE);
        UUID sessioncookie;
        if (cookie == null) {
            sessioncookie = null;
        } else {
            sessioncookie = UUID.fromString(cookie.getValue());
        }
        final AuthSession session = AuthSessionManager.getSession(sessioncookie);
        if (session == null) {
            response.setStatusCode(403);
            response.end("\"Invalid session cookie.\"");
            return;
        }
        final String token = session.get("token");
        final String state = session.get("state");
        if (token != null) {
            response.setStatusCode(200);
            response.end("\"Current user is already connected.\"");
            return;
        }
        if (!request.params().contains("state") || !request.params().get("state").equals(state)) {
            response.setStatusCode(403);
            response.end("\"Invalid state parameter.\"");
            return;
        }
        request.bodyHandler(body -> {
            final JsonObject tokenConfig = new JsonObject()
                .put("code", body.toString())
                .put("redirect_uri", "postmessage");
            oauth2.authenticate(tokenConfig, ar -> {
                if (ar.succeeded() && ar.result() instanceof AccessToken) {
                    final AccessToken accessToken = (AccessToken) ar.result();
                    accessToken.setTrustJWT(true);
                    final JsonObject user = accessToken.idToken();
                    final String id = user.getString("sub");
                    final String email = user.getString("email");
                    final String hostedDomain = user.getString("hd");

                    logger.info("Google User: id: " + id + " email: " + email + " domain: " + hostedDomain + " logged in");
                    session.put("token", accessToken.opaqueAccessToken());
                    session.put("id", id);
                    session.put("email", email);
                    session.put("domain", hostedDomain);
                    response.setStatusCode(200);
                    response.end("\"Successfully connected user.\"");
                } else {
                    response.setStatusCode(500);
                    response.end("\"Failed to read token data from Google. "
                        + ar.cause().getMessage() + "\"");
                }
            });
        });
    }
}