diff --git a/server/src/com/cloud/api/ApiServer.java b/server/src/com/cloud/api/ApiServer.java old mode 100644 new mode 100755 index 5c29f8dada8..02902039a82 --- a/server/src/com/cloud/api/ApiServer.java +++ b/server/src/com/cloud/api/ApiServer.java @@ -46,6 +46,7 @@ import java.util.concurrent.TimeUnit; import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; +import javax.servlet.http.HttpServletResponse; import org.apache.http.ConnectionClosedException; import org.apache.http.HttpException; @@ -73,6 +74,7 @@ import org.apache.http.protocol.ResponseContent; import org.apache.http.protocol.ResponseDate; import org.apache.http.protocol.ResponseServer; import org.apache.log4j.Logger; +import org.apache.log4j.NDC; import com.cloud.configuration.ConfigurationVO; import com.cloud.configuration.dao.ConfigurationDao; @@ -235,9 +237,8 @@ public class ApiServer implements HttpRequestHandler { try { // always trust commands from API port, user context will always be UID_SYSTEM/ACCOUNT_ID_SYSTEM UserContext.registerContext(User.UID_SYSTEM, Account.ACCOUNT_ID_SYSTEM, null, true); - - String responseText = handleRequest(parameterMap, true, responseType); - sb.append(" 200 " + ((responseText == null) ? 0 : responseText.length())); + NDC.push("userId="+User.UID_SYSTEM+ " accountId="+Account.ACCOUNT_ID_SYSTEM+ " sessionId="+null ); + String responseText = handleRequest(parameterMap, true, responseType, sb); writeResponse(response, responseText, false, responseType); } catch (ServerApiException se) { try { @@ -260,12 +261,12 @@ public class ApiServer implements HttpRequestHandler { } } finally { s_accessLogger.info(sb.toString()); - + NDC.remove(); UserContext.unregisterContext(); } } - public String handleRequest(Map params, boolean decode, String responseType) throws ServerApiException { + public String handleRequest(Map params, boolean decode, String responseType, StringBuffer auditTrailSb) throws ServerApiException { String response = null; try { String[] command = (String[])params.get("command"); @@ -300,10 +301,13 @@ public class ApiServer implements HttpRequestHandler { Map validatedParams = cmdObj.validateParams(paramMap, decode); List> resultValues = cmdObj.execute(validatedParams); + buildAuditTrail(auditTrailSb, command[0], resultValues); response = cmdObj.buildResponse(resultValues, responseType); } else { - s_logger.warn("unknown API command: " + ((command == null) ? "null" : command[0])); - response = buildErrorResponse("unknown API command: " + ((command == null) ? "null" : command[0]), responseType); + String errorString = " unknown API command: " + ((command == null) ? "null" : command[0]); + s_logger.warn(errorString); + auditTrailSb.append(" " +errorString); + response = buildErrorResponse(errorString, responseType); } } } catch (Exception ex) { @@ -316,7 +320,39 @@ public class ApiServer implements HttpRequestHandler { } return response; } - + + private void buildAuditTrail(StringBuffer auditTrailSb, String command, List> resultValues){ + + if (resultValues == null) return; + auditTrailSb.append(" " + HttpServletResponse.SC_OK); + if (command.equals("queryAsyncJobResult")){ //For this command we need to also log job status and job resultcode + for (Pair pair : resultValues){ + String key = pair.first(); + if (key.equals(BaseCmd.Properties.JOB_STATUS.getName())){ + auditTrailSb.append(" "); + auditTrailSb.append(key); + auditTrailSb.append("="); + auditTrailSb.append(pair.second()); + }else if (key.equals(BaseCmd.Properties.JOB_RESULT_CODE.getName())){ + auditTrailSb.append(" "); + auditTrailSb.append(key); + auditTrailSb.append("="); + auditTrailSb.append(pair.second()); + } + } + }else { + for (Pair pair : resultValues){ + if (pair.first().equals(BaseCmd.Properties.JOB_ID.getName())){ // Its an async job so report the jobid + auditTrailSb.append(" "); + auditTrailSb.append(pair.first()); + auditTrailSb.append("="); + auditTrailSb.append(pair.second()); + } + } + } + + } + public boolean verifyRequest(Map requestParameters, String userId) { try { String apiKey = null; diff --git a/server/src/com/cloud/api/ApiServlet.java b/server/src/com/cloud/api/ApiServlet.java old mode 100644 new mode 100755 index d761286096b..7c6b3de73fe --- a/server/src/com/cloud/api/ApiServlet.java +++ b/server/src/com/cloud/api/ApiServlet.java @@ -31,6 +31,7 @@ import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import org.apache.log4j.Logger; +import org.apache.log4j.NDC; import com.cloud.maid.StackMaid; import com.cloud.user.Account; @@ -40,10 +41,11 @@ import com.cloud.utils.exception.CloudRuntimeException; @SuppressWarnings("serial") public class ApiServlet extends HttpServlet { - public static final Logger s_logger = Logger.getLogger(ApiServlet.class.getName()); - - private ApiServer _apiServer = null; + public static final Logger s_logger = Logger.getLogger(ApiServlet.class.getName()); + private static final Logger s_accessLogger = Logger.getLogger("apiserver." + ApiServer.class.getName()); + private ApiServer _apiServer = null; + public ApiServlet() { super(); _apiServer = ApiServer.getInstance(); @@ -53,7 +55,7 @@ public class ApiServlet extends HttpServlet { } protected void doGet(HttpServletRequest req, HttpServletResponse resp) { - try { + try { processRequest(req, resp); } finally { StackMaid.current().exitCleanup(); @@ -69,11 +71,14 @@ public class ApiServlet extends HttpServlet { } @SuppressWarnings("unchecked") - private void processRequest(HttpServletRequest req, HttpServletResponse resp) { + private void processRequest(HttpServletRequest req, HttpServletResponse resp) { + StringBuffer auditTrailSb = new StringBuffer(); + auditTrailSb.append(req.getRemoteAddr()); + auditTrailSb.append(" -- " + req.getMethod() + " " ); try { Map params = new HashMap(); params.putAll(req.getParameterMap()); - HttpSession session = req.getSession(false); + HttpSession session = req.getSession(false); // get the response format since we'll need it in a couple of places String responseType = BaseCmd.RESPONSE_TYPE_XML; @@ -87,22 +92,30 @@ public class ApiServlet extends HttpServlet { String command = (String)commandObj[0]; if ("logout".equalsIgnoreCase(command)) { // if this is just a logout, invalidate the session and return - if (session != null) { - String userIdStr = (String)session.getAttribute("userId"); + if (session != null) { + String userIdStr = (String)session.getAttribute(BaseCmd.Properties.USER_ID.getName()); + Account account = (Account)session.getAttribute(BaseCmd.Properties.ACCOUNT_OBJ.getName()); + NDC.push("userId="+userIdStr+ + " accountId="+ account==null ? null:account.getId()+ + " sessionId="+session.getId() ); if (userIdStr != null) { _apiServer.logoutUser(Long.parseLong(userIdStr)); } session.invalidate(); - } + } + auditTrailSb.append("command=logout"); + auditTrailSb.append(" " +HttpServletResponse.SC_OK); writeResponse(resp, getLogoutSuccessResponse(responseType), false, responseType); return; - } else if ("login".equalsIgnoreCase(command)) { + } else if ("login".equalsIgnoreCase(command)) { + auditTrailSb.append("command=login"); // if this is a login, authenticate the user and return if (session != null) session.invalidate(); session = req.getSession(true); String[] username = (String[])params.get("username"); String[] password = (String[])params.get("password"); - String[] domainIdArr = (String[])params.get("domainid"); + String[] domainIdArr = (String[])params.get("domainid"); + if (domainIdArr == null) { domainIdArr = (String[])params.get("domainId"); } @@ -111,16 +124,19 @@ public class ApiServlet extends HttpServlet { if ((domainIdArr != null) && (domainIdArr.length > 0)) { try{ domainId = new Long(Long.parseLong(domainIdArr[0])); + auditTrailSb.append(" domainid=" +domainId);// building the params for POST call } catch(NumberFormatException e) { s_logger.warn("Invalid domain id entered by user"); + auditTrailSb.append(" " + HttpServletResponse.SC_UNAUTHORIZED + " " + "Invalid domain id entered, please enter a valid one"); resp.sendError(HttpServletResponse.SC_UNAUTHORIZED,"Invalid domain id entered, please enter a valid one"); } } String domain = null; if (domainName != null) { domain = domainName[0]; + auditTrailSb.append(" domain=" +domain); if (domain != null) { // ensure domain starts with '/' and ends with '/' if (!domain.endsWith("/")) { @@ -133,25 +149,31 @@ public class ApiServlet extends HttpServlet { } if (username != null) { + auditTrailSb.append(" username=" +username[0]); String pwd = ((password == null) ? null : password[0]); List> sessionParams = _apiServer.loginUser(username[0], pwd, domainId, domain, params); if (sessionParams != null) { for (Pair sessionParam : sessionParams) { session.setAttribute(sessionParam.first(), sessionParam.second()); - } - String loginResponse = getLoginSuccessResponse(session, responseType); + } + NDC.push("userId="+session.getAttribute(BaseCmd.Properties.USER_ID.getName())+ + " accountId="+ ((Account)session.getAttribute(BaseCmd.Properties.ACCOUNT_OBJ.getName())).getId()+ + " sessionId="+session.getId() ); + String loginResponse = getLoginSuccessResponse(session, responseType); + auditTrailSb.append(" " +HttpServletResponse.SC_OK); writeResponse(resp, loginResponse, false, responseType); return; } else { // TODO: fall through to API key, or just fail here w/ auth error? (HTTP 401) - session.invalidate(); + session.invalidate(); + auditTrailSb.append(" " + HttpServletResponse.SC_UNAUTHORIZED + " " + "failed to authenticated user, check username/password are correct"); resp.sendError(HttpServletResponse.SC_UNAUTHORIZED, "failed to authenticated user, check username/password are correct"); return; } } } } - + auditTrailSb.append(req.getQueryString()); boolean isNew = ((session == null) ? true : session.isNew()); Object accountObj = null; @@ -168,6 +190,7 @@ public class ApiServlet extends HttpServlet { String[] sessionKeyParam = (String[])params.get(BaseCmd.Properties.SESSION_KEY.getName()); if ((sessionKeyParam == null) || (sessionKey == null) || !sessionKey.equals(sessionKeyParam[0])) { session.invalidate(); + auditTrailSb.append(" " + HttpServletResponse.SC_UNAUTHORIZED + " " + "unable to verify user credentials"); resp.sendError(HttpServletResponse.SC_UNAUTHORIZED, "unable to verify user credentials"); } @@ -175,7 +198,8 @@ public class ApiServlet extends HttpServlet { if ((userId != null) && (account != null) && (accountObj != null) && _apiServer.verifyUser(Long.valueOf(userId))) { String[] command = (String[])params.get("command"); if (command == null) { - s_logger.info("missing command, ignoring request..."); + s_logger.info("missing command, ignoring request..."); + auditTrailSb.append(" " + HttpServletResponse.SC_BAD_REQUEST + " " + "no command specified"); resp.sendError(HttpServletResponse.SC_BAD_REQUEST, "no command specified"); return; } @@ -187,6 +211,7 @@ public class ApiServlet extends HttpServlet { account = null; accountObj = null; session.invalidate(); + auditTrailSb.append(" " + HttpServletResponse.SC_UNAUTHORIZED + " " + "unable to verify user credentials"); resp.sendError(HttpServletResponse.SC_UNAUTHORIZED, "unable to verify user credentials"); return; } @@ -212,29 +237,35 @@ public class ApiServlet extends HttpServlet { } // update user context info here so that we can take information if the request is authenticated - // via api key mechenism + // via api key mechanism updateUserContext(params, session != null ? session.getId() : null); try { - String response = _apiServer.handleRequest(params, false, responseType); + String response = _apiServer.handleRequest(params, false, responseType, auditTrailSb); writeResponse(resp, response != null ? response : "", false, responseType); - } catch (ServerApiException se) { + } catch (ServerApiException se) { + auditTrailSb.append(" " +se.getErrorCode() + " " + se.getDescription()); resp.sendError(se.getErrorCode(), se.getDescription()); } } else { if (session != null) { session.invalidate(); } + auditTrailSb.append(" " + HttpServletResponse.SC_UNAUTHORIZED + " " + "unable to verify user credentials and/or request signature"); resp.sendError(HttpServletResponse.SC_UNAUTHORIZED, "unable to verify user credentials and/or request signature"); } } catch (IOException ioex) { if (s_logger.isTraceEnabled()) { s_logger.trace("exception processing request: " + ioex); - } + } + auditTrailSb.append(" exception processing request" ); } catch (Exception ex) { - s_logger.error("unknown exception writing api response", ex); + s_logger.error("unknown exception writing api response", ex); + auditTrailSb.append(" unknown exception writing api response"); } finally { + s_accessLogger.info(auditTrailSb.toString()); // cleanup user context to prevent from being peeked in other request context UserContext.unregisterContext(); + NDC.remove(); } } @@ -249,7 +280,7 @@ public class ApiServlet extends HttpServlet { if(accountObj != null) accountId = accountObj.getId(); - + NDC.push("userId="+userId+ " accountId="+accountId+ " sessionId="+sessionId ); UserContext.updateContext(userId, accountId, sessionId); }