edu.cornell.mannlib.oce.servlets.MainController.java Source code

Java tutorial

Introduction

Here is the source code for edu.cornell.mannlib.oce.servlets.MainController.java

Source

/* $This file is distributed under the terms of the license in /doc/license.txt$ */

package edu.cornell.mannlib.oce.servlets;

import static edu.cornell.mannlib.orcidclient.actions.ApiAction.AUTHENTICATE;
import static javax.servlet.http.HttpServletResponse.SC_FORBIDDEN;
import static javax.servlet.http.HttpServletResponse.SC_INTERNAL_SERVER_ERROR;

import java.io.IOException;
import java.net.URISyntaxException;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import edu.cornell.mannlib.oce.freemarker.FreemarkerProcessor;
import edu.cornell.mannlib.orcidclient.OrcidClientException;
import edu.cornell.mannlib.orcidclient.auth.AuthorizationManager;
import edu.cornell.mannlib.orcidclient.auth.AuthorizationStatus;
import edu.cornell.mannlib.orcidclient.context.OrcidClientContext;
import edu.cornell.mannlib.orcidclient.context.OrcidClientContext.Setting;

/**
 * Use a fresh instance of MainControllerCore in each request. so we can use
 * instance variables without worrying about thread safety.
 */
public class MainController extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        new MainControllerCore(req, resp).execute();
    }

}

class MainControllerCore {
    private static final Log log = LogFactory.getLog(MainControllerCore.class);
    private static final Log successLog = LogFactory.getLog("SUCCESS");
    private static final Log problemLog = LogFactory.getLog("PROBLEM");

    private static final String PATHINFO_PROCESS = "/process";
    private static final String PATH_PROCESS = "main" + PATHINFO_PROCESS;
    private static final String PATHINFO_CALLBACK = "/callback";
    private static final String HEADER_NAME = "REMOTE_USER";

    private enum Result {
        SUCCESS, FAILED, DENIED
    }

    private final HttpServletRequest req;
    private final HttpServletResponse resp;
    private final String loginId;
    private final String pathInfo;

    private final OrcidClientContext occ;
    private final AuthorizationManager authManager;
    private final AuthorizationStatus authStatus;

    private Result result;
    private String orcidId;

    public MainControllerCore(HttpServletRequest req, HttpServletResponse resp) {
        this.req = req;
        this.resp = resp;
        this.loginId = req.getHeader(HEADER_NAME);
        this.pathInfo = req.getPathInfo();
        log.debug("NetID=" + loginId + ", pathInfo=" + pathInfo);

        this.occ = OrcidClientContext.getInstance();
        this.authManager = this.occ.getAuthorizationManager(req);
        this.authStatus = authManager.getAuthorizationStatus(AUTHENTICATE);
        log.debug("AuthStatus: " + authStatus);
    }

    public void execute() throws IOException {
        if (!isOrcidConfigured()) {
            showOrcidNotConfigured();
            return;
        }

        if (!isLoggedIn()) {
            showNotLoggedIn();
            return;
        }

        if (PATHINFO_PROCESS.equals(pathInfo)) {
            processAuthorization();
            return;
        }

        if (PATHINFO_CALLBACK.equals(pathInfo)) {
            handleCallback();
            return;
        }

        startFromScratch();
        showWelcomeScreen();
    }

    private boolean isOrcidConfigured() {
        try {
            OrcidClientContext.getInstance().getSetting(Setting.CLIENT_ID);
            return true;
        } catch (Exception e) {
            return false;
        }
    }

    private boolean isLoggedIn() {
        return (loginId != null) && (!loginId.isEmpty());
    }

    private void processAuthorization() throws IOException {
        if (authStatus.isNone()) {
            // we haven't started the process yet.
            seekAuthorizationForAuthenticate();
        } else if (authStatus.isSuccess()) {
            // the user authorized us -- we win.
            showSuccess();
        } else if (authStatus.isDenied()) {
            // the user denied us -- we lose.
            showAuthorizationDenied();
        } else {
            // something funky happened -- give up.
            showAuthorizationFailed();
        }
    }

    private void handleCallback() throws IOException {
        try {
            AuthorizationStatus auth = authManager.processAuthorizationResponse();
            if (auth.isSuccess()) {
                resp.sendRedirect(auth.getSuccessUrl());
            } else {
                resp.sendRedirect(auth.getFailureUrl());
            }
        } catch (OrcidClientException e) {
            log.error("Invalid authorization response", e);
            resp.sendError(SC_INTERNAL_SERVER_ERROR);
        }
    }

    private void seekAuthorizationForAuthenticate() throws IOException {
        try {
            log.debug("Seeking authorization to authenticate.");
            String returnUrl = occ.resolvePathWithWebapp(PATH_PROCESS);
            String seekUrl = authManager.seekAuthorization(AUTHENTICATE, returnUrl);
            resp.sendRedirect(seekUrl);
        } catch (URISyntaxException | OrcidClientException e) {
            authStatus.setFailure("Failed to seek authorization from Orcid", e);
            resp.sendRedirect(PATH_PROCESS);
        }
    }

    private void showNotLoggedIn() throws IOException {
        log.error("Not logged in! How did CUWebAuth let us through?");
        log.error("Headers are: " + buildHeaderMap());
        resp.sendError(SC_FORBIDDEN);
    }

    @SuppressWarnings("unchecked")
    private Map<String, Set<String>> buildHeaderMap() {
        Map<String, Set<String>> headerMap = new HashMap<>();
        for (Enumeration<String> names = req.getHeaderNames(); names.hasMoreElements();) {
            String name = names.nextElement();
            Set<String> valueSet = new HashSet<>();
            for (Enumeration<String> values = req.getHeaders(name); values.hasMoreElements();) {
                valueSet.add(values.nextElement());
            }
            headerMap.put(name, valueSet);
        }
        return headerMap;
    }

    private void showOrcidNotConfigured() throws IOException {
        log.error("Orcid not configured! " + "How did we get through the StartupErrorFilter?");
        resp.sendError(SC_INTERNAL_SERVER_ERROR);
    }

    private void startFromScratch() {
        authManager.clearStatus(AUTHENTICATE);
    }

    private void showWelcomeScreen() throws IOException {
        renderTemplate("welcomeScreen.ftl");
    }

    private void showAuthorizationDenied() throws IOException {
        result = Result.DENIED;
        problemLogMessage();
        renderTemplate("resultScreen.ftl");
    }

    private void showAuthorizationFailed() throws IOException {
        result = Result.FAILED;
        log.error("Authorization failed: authStatus=" + authStatus);
        problemLogMessage();
        renderTemplate("resultScreen.ftl");
    }

    private void showSuccess() throws IOException {
        result = Result.SUCCESS;
        orcidId = authStatus.getAccessToken().getOrcid();
        successLogMessage();
        renderTemplate("resultScreen.ftl");
    }

    private void renderTemplate(String templateName) throws IOException {
        Map<String, Object> map = new HashMap<>();
        map.put("loginId", loginId);
        map.put("result", String.valueOf(result));
        map.put("orcidId", String.valueOf(orcidId));
        map.put("baseUrl", figureBaseUrl());
        new FreemarkerProcessor(req, resp).renderTemplate(templateName, map);
    }

    private String figureBaseUrl() {
        String rawBaseUrl = occ.getSetting(Setting.WEBAPP_BASE_URL);
        int colonHere = rawBaseUrl.indexOf(':');
        return rawBaseUrl.substring(colonHere + 1);
    }

    private void successLogMessage() {
        successLog.info("result=" + result + ", loginId=" + loginId + ", orcidId=" + orcidId);
    }

    private void problemLogMessage() {
        problemLog.info("result=" + result + ", loginId=" + loginId + ", orcidId=" + orcidId + ", authStatus="
                + authStatus);
    }

}