void processRequestInContext()

in server/src/main/java/com/cloud/api/ApiServlet.java [170:381]


    void processRequestInContext(final HttpServletRequest req, final HttpServletResponse resp) {
        InetAddress remoteAddress = null;
        try {
            remoteAddress = getClientAddress(req);
        } catch (UnknownHostException e) {
            LOGGER.warn("UnknownHostException when trying to lookup remote IP-Address. This should never happen. Blocking request.", e);
            final String response = apiServer.getSerializedApiError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
                    "UnknownHostException when trying to lookup remote IP-Address", null,
                    HttpUtils.RESPONSE_TYPE_XML);
            HttpUtils.writeHttpResponse(resp, response, HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
                    HttpUtils.RESPONSE_TYPE_XML, ApiServer.JSONcontentType.value());
            return;
        }

        final StringBuilder auditTrailSb = new StringBuilder(128);
        auditTrailSb.append(" ").append(remoteAddress.getHostAddress());
        auditTrailSb.append(" -- ").append(req.getMethod()).append(' ');
        // get the response format since we'll need it in a couple of places
        String responseType = HttpUtils.RESPONSE_TYPE_XML;
        final Map<String, Object[]> params = new HashMap<String, Object[]>();
        Map<String, String[]> reqParams = req.getParameterMap();
        checkSingleQueryParameterValue(reqParams);
        params.putAll(reqParams);

        utf8Fixup(req, params);

        // logging the request start and end in management log for easy debugging
        String reqStr = "";
        String cleanQueryString = StringUtils.cleanString(req.getQueryString());
        if (LOGGER.isDebugEnabled()) {
            reqStr = auditTrailSb.toString() + " " + cleanQueryString;
            LOGGER.debug("===START=== " + reqStr);
        }

        try {
            resp.setContentType(HttpUtils.XML_CONTENT_TYPE);

            HttpSession session = req.getSession(false);
            if (LOGGER.isTraceEnabled()) {
                LOGGER.trace(String.format("session found: %s", session));
            }
            final Object[] responseTypeParam = params.get(ApiConstants.RESPONSE);
            if (responseTypeParam != null) {
                responseType = (String)responseTypeParam[0];
            }

            final Object[] commandObj = params.get(ApiConstants.COMMAND);
            final String command = commandObj == null ? null : (String) commandObj[0];
            final Object[] userObj = params.get(ApiConstants.USERNAME);
            String username = userObj == null ? null : (String)userObj[0];
            if (LOGGER.isTraceEnabled()) {
                String logCommand = saveLogString(command);
                String logName = saveLogString(username);
                LOGGER.trace(String.format("command %s processing for user \"%s\"",
                        logCommand,
                        logName));
            }

            if (command != null && !command.equals(ValidateUserTwoFactorAuthenticationCodeCmd.APINAME)) {

                APIAuthenticator apiAuthenticator = authManager.getAPIAuthenticator(command);
                if (apiAuthenticator != null) {
                    auditTrailSb.append("command=");
                    auditTrailSb.append(command);

                    int httpResponseCode = HttpServletResponse.SC_OK;
                    String responseString = null;

                    if (apiAuthenticator.getAPIType() == APIAuthenticationType.LOGIN_API) {
                        if (session != null) {
                            invalidateHttpSession(session, "invalidating session for login call");
                        }
                        session = req.getSession(true);

                        if (ApiServer.EnableSecureSessionCookie.value()) {
                            resp.setHeader("SET-COOKIE", String.format("JSESSIONID=%s;Secure;HttpOnly;Path=/client", session.getId()));
                            if (LOGGER.isDebugEnabled()) {
                                LOGGER.debug("Session cookie is marked secure!");
                            }
                        }
                    }

                    try {
                        if (LOGGER.isTraceEnabled()) {
                            LOGGER.trace(String.format("apiAuthenticator.authenticate(%s, params[%d], %s, %s, %s, %s, %s,%s)",
                                    saveLogString(command), params.size(), session.getId(), remoteAddress.getHostAddress(), saveLogString(responseType), "auditTrailSb", "req", "resp"));
                        }
                        responseString = apiAuthenticator.authenticate(command, params, session, remoteAddress, responseType, auditTrailSb, req, resp);
                        if (session != null && session.getAttribute(ApiConstants.SESSIONKEY) != null) {
                            String sameSite = getApiSessionKeySameSite();
                            resp.addHeader("SET-COOKIE", String.format("%s=%s;HttpOnly;%s", ApiConstants.SESSIONKEY, session.getAttribute(ApiConstants.SESSIONKEY), sameSite));
                        }
                    } catch (ServerApiException e) {
                        httpResponseCode = e.getErrorCode().getHttpCode();
                        responseString = e.getMessage();
                        LOGGER.debug("Authentication failure: " + e.getMessage());
                    }

                    if (apiAuthenticator.getAPIType() == APIAuthenticationType.LOGOUT_API) {
                        if (session == null) {
                            throw new ServerApiException(ApiErrorCode.PARAM_ERROR, "Session not found for the logout process.");
                        }

                        final Long userId = (Long) session.getAttribute("userid");
                        final Account account = (Account) session.getAttribute("accountobj");
                        Long accountId = null;
                        if (account != null) {
                            accountId = account.getId();
                        }
                        auditTrailSb.insert(0, "(userId=" + userId + " accountId=" + accountId + " sessionId=" + session.getId() + ")");
                        if (userId != null) {
                            apiServer.logoutUser(userId);
                        }
                        invalidateHttpSession(session, "invalidating session after logout call");

                        final Cookie[] cookies = req.getCookies();
                        if (cookies != null) {
                            for (final Cookie cookie : cookies) {
                                cookie.setValue("");
                                cookie.setMaxAge(0);
                                resp.addCookie(cookie);
                            }
                        }
                    }
                    HttpUtils.writeHttpResponse(resp, responseString, httpResponseCode, responseType, ApiServer.JSONcontentType.value());
                    return;
                }
            } else {
                LOGGER.trace("no command available");
            }
            auditTrailSb.append(cleanQueryString);
            final boolean isNew = ((session == null) ? true : session.isNew());

            // Initialize an empty context and we will update it after we have verified the request below,
            // we no longer rely on web-session here, verifyRequest will populate user/account information
            // if a API key exists

            if (isNew && LOGGER.isTraceEnabled()) {
                LOGGER.trace(String.format("new session: %s", session));
            }

            if (!isNew && (command.equalsIgnoreCase(ValidateUserTwoFactorAuthenticationCodeCmd.APINAME) || (!skip2FAcheckForAPIs(command) && !skip2FAcheckForUser(session)))) {
                LOGGER.debug("Verifying two factor authentication");
                boolean success = verify2FA(session, command, auditTrailSb, params, remoteAddress, responseType, req, resp);
                if (!success) {
                    LOGGER.debug("Verification of two factor authentication failed");
                    return;
                }
            }

            Long userId = null;
            if (!isNew) {
                userId = (Long)session.getAttribute("userid");
                final String account = (String) session.getAttribute("account");
                final Object accountObj = session.getAttribute("accountobj");
                if (account != null) {
                    if (invalidateHttpSessionIfNeeded(req, resp, auditTrailSb, responseType, params, session, account)) return;
                } else {
                    if (LOGGER.isDebugEnabled()) {
                        LOGGER.debug("no account, this request will be validated through apikey(%s)/signature");
                    }
                }

                if (! requestChecksoutAsSane(resp, auditTrailSb, responseType, params, session, command, userId, account, accountObj))
                    return;
            } else {
                CallContext.register(accountMgr.getSystemUser(), accountMgr.getSystemAccount());
            }
            setProjectContext(params);
            if (LOGGER.isTraceEnabled()) {
                LOGGER.trace(String.format("verifying request for user %s from %s with %d parameters",
                        userId, remoteAddress.getHostAddress(), params.size()));
            }
            if (apiServer.verifyRequest(params, userId, remoteAddress)) {
                auditTrailSb.insert(0, "(userId=" + CallContext.current().getCallingUserId() + " accountId=" + CallContext.current().getCallingAccount().getId() +
                        " sessionId=" + (session != null ? session.getId() : null) + ")");

                // Add the HTTP method (GET/POST/PUT/DELETE) as well into the params map.
                params.put("httpmethod", new String[]{req.getMethod()});
                setProjectContext(params);
                setClientAddressForConsoleEndpointAccess(command, params, req);
                final String response = apiServer.handleRequest(params, responseType, auditTrailSb);
                HttpUtils.writeHttpResponse(resp, response != null ? response : "", HttpServletResponse.SC_OK, responseType, ApiServer.JSONcontentType.value());
            } else {
                if (session != null) {
                    invalidateHttpSession(session, String.format("request verification failed for %s from %s", userId, remoteAddress.getHostAddress()));
                }

                auditTrailSb.append(" " + HttpServletResponse.SC_UNAUTHORIZED + " " + "unable to verify user credentials and/or request signature");
                final String serializedResponse =
                        apiServer.getSerializedApiError(HttpServletResponse.SC_UNAUTHORIZED, "unable to verify user credentials and/or request signature", params,
                                responseType);
                HttpUtils.writeHttpResponse(resp, serializedResponse, HttpServletResponse.SC_UNAUTHORIZED, responseType, ApiServer.JSONcontentType.value());

            }
        } catch (final ServerApiException se) {
            final String serializedResponseText = apiServer.getSerializedApiError(se, params, responseType);
            resp.setHeader("X-Description", se.getDescription());
            HttpUtils.writeHttpResponse(resp, serializedResponseText, se.getErrorCode().getHttpCode(), responseType, ApiServer.JSONcontentType.value());
            auditTrailSb.append(" " + se.getErrorCode() + " " + se.getDescription());
        } catch (final Exception ex) {
            LOGGER.error("unknown exception writing api response", ex);
            auditTrailSb.append(" unknown exception writing api response");
        } finally {
            ACCESSLOGGER.info(auditTrailSb.toString());
            if (LOGGER.isDebugEnabled()) {
                LOGGER.debug("===END=== " + reqStr);
            }
            // cleanup user context to prevent from being peeked in other request context
            CallContext.unregister();
        }
    }