Details
diff --git a/azkaban-common/src/main/java/azkaban/utils/WebUtils.java b/azkaban-common/src/main/java/azkaban/utils/WebUtils.java
index 55891b2..916e441 100644
--- a/azkaban-common/src/main/java/azkaban/utils/WebUtils.java
+++ b/azkaban-common/src/main/java/azkaban/utils/WebUtils.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012 LinkedIn Corp.
+ * Copyright 2016 LinkedIn Corp.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
@@ -17,6 +17,7 @@
package azkaban.utils;
import java.text.NumberFormat;
+import java.util.Map;
import org.joda.time.DateTime;
import org.joda.time.DurationFieldType;
@@ -33,6 +34,8 @@ public class WebUtils {
private static final long ONE_GB = 1024 * ONE_MB;
private static final long ONE_TB = 1024 * ONE_GB;
+ public static final String X_FORWARDED_FOR_HEADER = "X-Forwarded-For";
+
public String formatDate(long timeMS) {
if (timeMS == -1) {
return "-";
@@ -164,4 +167,41 @@ public class WebUtils {
else
return sizeBytes + " B";
}
+
+ /**
+ * Gets the actual client IP address inspecting the X-Forwarded-For
+ * HTTP header or using the provided 'remote IP address' from the
+ * low level TCP connection from the client.
+ *
+ * If multiple IP addresses are provided in the X-Forwarded-For header
+ * then the first one (first hop) is used
+ *
+ * @param httpHeaders List of HTTP headers for the current request
+ * @param remoteAddr The client IP address and port from the current request's TCP connection
+ * @return The actual client IP address
+ */
+ public String getRealClientIpAddr(Map<String, String> httpHeaders, String remoteAddr){
+
+ // If some upstream device added an X-Forwarded-For header
+ // use it for the client ip
+ // This will support scenarios where load balancers or gateways
+ // front the Azkaban web server and a changing Ip address invalidates
+ // the session
+
+ String clientIp = httpHeaders.getOrDefault(X_FORWARDED_FOR_HEADER, null);
+ if(clientIp == null){
+ clientIp = remoteAddr;
+ }
+ else{
+ // header can contain comma separated list of upstream servers - get the first one
+ String ips[] = clientIp.split(",");
+ clientIp = ips[0];
+ }
+
+ // Strip off port and only get IP address
+ String parts[] = clientIp.split(":");
+ clientIp = parts[0];
+
+ return clientIp;
+ }
}
diff --git a/azkaban-common/src/test/java/azkaban/utils/WebUtilsTest.java b/azkaban-common/src/test/java/azkaban/utils/WebUtilsTest.java
new file mode 100644
index 0000000..39cb66e
--- /dev/null
+++ b/azkaban-common/src/test/java/azkaban/utils/WebUtilsTest.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright 2014 LinkedIn Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package azkaban.utils;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+/**
+ * Test class for azkaban.utils.WebUtils
+ */
+public class WebUtilsTest {
+
+ @Test
+ public void testWhenNoXForwardedForHeaderUseClientIp(){
+
+ String clientIp = "127.0.0.1:10000";
+ Map<String, String> headers = new HashMap<>();
+
+ WebUtils utils = new WebUtils();
+
+ String ip = utils.getRealClientIpAddr(headers, clientIp);
+
+ assertEquals(ip, "127.0.0.1");
+ }
+
+ @Test
+ public void testWhenClientIpNoPort(){
+
+ String clientIp = "192.168.1.1";
+ Map<String, String> headers = new HashMap<>();
+
+ WebUtils utils = new WebUtils();
+
+ String ip = utils.getRealClientIpAddr(headers, clientIp);
+
+ assertEquals(ip, "192.168.1.1");
+ }
+
+ @Test
+ public void testWhenXForwardedForHeaderUseHeader(){
+
+ String clientIp = "127.0.0.1:10000";
+ String upstreamIp = "192.168.1.1:10000";
+ Map<String, String> headers = new HashMap<>();
+
+ headers.put("X-Forwarded-For", upstreamIp);
+
+ WebUtils utils = new WebUtils();
+
+ String ip = utils.getRealClientIpAddr(headers, clientIp);
+
+ assertEquals(ip, "192.168.1.1");
+ }
+
+ @Test
+ public void testWhenXForwardedForHeaderMultipleUpstreamsUseHeader(){
+
+ String clientIp = "127.0.0.1:10000";
+ String upstreamIp = "192.168.1.1:10000";
+ Map<String, String> headers = new HashMap<>();
+
+ headers.put("X-Forwarded-For", upstreamIp + ",127.0.0.1,55.55.55.55");
+
+ WebUtils utils = new WebUtils();
+
+ String ip = utils.getRealClientIpAddr(headers, clientIp);
+
+ assertEquals(ip, "192.168.1.1");
+ }
+
+}
diff --git a/azkaban-web-server/src/main/java/azkaban/webapp/servlet/AbstractAzkabanServlet.java b/azkaban-web-server/src/main/java/azkaban/webapp/servlet/AbstractAzkabanServlet.java
index b19021d..39cc07d 100644
--- a/azkaban-web-server/src/main/java/azkaban/webapp/servlet/AbstractAzkabanServlet.java
+++ b/azkaban-web-server/src/main/java/azkaban/webapp/servlet/AbstractAzkabanServlet.java
@@ -330,7 +330,7 @@ public abstract class AbstractAzkabanServlet extends HttpServlet {
*/
protected Page newPage(HttpServletRequest req, HttpServletResponse resp,
Session session, String template) {
- Page page = new Page(req, resp, application.getVelocityEngine(), template);
+ Page page = new Page(req, resp, getApplication().getVelocityEngine(), template);
page.add("azkaban_name", name);
page.add("azkaban_label", label);
page.add("azkaban_color", color);
@@ -381,7 +381,7 @@ public abstract class AbstractAzkabanServlet extends HttpServlet {
*/
protected Page newPage(HttpServletRequest req, HttpServletResponse resp,
String template) {
- Page page = new Page(req, resp, application.getVelocityEngine(), template);
+ Page page = new Page(req, resp, getApplication().getVelocityEngine(), template);
page.add("azkaban_name", name);
page.add("azkaban_label", label);
page.add("azkaban_color", color);
diff --git a/azkaban-web-server/src/main/java/azkaban/webapp/servlet/LoginAbstractAzkabanServlet.java b/azkaban-web-server/src/main/java/azkaban/webapp/servlet/LoginAbstractAzkabanServlet.java
index 1d9f315..f2a4603 100644
--- a/azkaban-web-server/src/main/java/azkaban/webapp/servlet/LoginAbstractAzkabanServlet.java
+++ b/azkaban-web-server/src/main/java/azkaban/webapp/servlet/LoginAbstractAzkabanServlet.java
@@ -32,6 +32,7 @@ import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
+import azkaban.utils.WebUtils;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.apache.commons.io.IOUtils;
import org.apache.log4j.Logger;
@@ -138,7 +139,7 @@ public abstract class LoginAbstractAzkabanServlet extends
*/
private void logRequest(HttpServletRequest req, Session session) {
StringBuilder buf = new StringBuilder();
- buf.append(req.getRemoteAddr()).append(" ");
+ buf.append(getRealClientIpAddr(req)).append(" ");
if (session != null && session.getUser() != null) {
buf.append(session.getUser().getUserId()).append(" ");
} else {
@@ -166,7 +167,7 @@ public abstract class LoginAbstractAzkabanServlet extends
buf.append("not-browser");
}
}
-
+
logger.info(buf.toString());
}
@@ -210,9 +211,25 @@ public abstract class LoginAbstractAzkabanServlet extends
return false;
}
+ private String getRealClientIpAddr(HttpServletRequest req){
+
+ // If some upstream device added an X-Forwarded-For header
+ // use it for the client ip
+ // This will support scenarios where load balancers or gateways
+ // front the Azkaban web server and a changing Ip address invalidates
+ // the session
+ HashMap<String, String> headers = new HashMap<>();
+ headers.put(WebUtils.X_FORWARDED_FOR_HEADER,
+ req.getHeader(WebUtils.X_FORWARDED_FOR_HEADER.toLowerCase()));
+
+ WebUtils utils = new WebUtils();
+
+ return utils.getRealClientIpAddr(headers, req.getRemoteAddr());
+ }
+
private Session getSessionFromRequest(HttpServletRequest req)
throws ServletException {
- String remoteIp = req.getRemoteAddr();
+ String remoteIp = getRealClientIpAddr(req);
Cookie cookie = getCookieByName(req, SESSION_ID_NAME);
String sessionId = null;
@@ -269,7 +286,7 @@ public abstract class LoginAbstractAzkabanServlet extends
// See if the session id is properly set.
if (params.containsKey("session.id")) {
String sessionId = (String) params.get("session.id");
- String ip = req.getRemoteAddr();
+ String ip = getRealClientIpAddr(req);
session = getSessionFromSessionId(sessionId, ip);
if (session != null) {
@@ -286,7 +303,7 @@ public abstract class LoginAbstractAzkabanServlet extends
String username = (String) params.get("username");
String password = (String) params.get("password");
- String ip = req.getRemoteAddr();
+ String ip = getRealClientIpAddr(req);
try {
session = createSession(username, password, ip);
@@ -333,7 +350,7 @@ public abstract class LoginAbstractAzkabanServlet extends
throws UserManagerException, ServletException {
String username = getParam(req, "username");
String password = getParam(req, "password");
- String ip = req.getRemoteAddr();
+ String ip = getRealClientIpAddr(req);
return createSession(username, password, ip);
}
diff --git a/azkaban-web-server/src/restli/java/azkaban/restli/ProjectManagerResource.java b/azkaban-web-server/src/restli/java/azkaban/restli/ProjectManagerResource.java
index 4633496..8447ac3 100644
--- a/azkaban-web-server/src/restli/java/azkaban/restli/ProjectManagerResource.java
+++ b/azkaban-web-server/src/restli/java/azkaban/restli/ProjectManagerResource.java
@@ -62,9 +62,7 @@ public class ProjectManagerResource extends ResourceContextHolder {
logger.info("Deploy called. {sessionId: " + sessionId + ", projectName: "
+ projectName + ", packageUrl:" + packageUrl + "}");
- String ip =
- (String) this.getContext().getRawRequestContext()
- .getLocalAttr("REMOTE_ADDR");
+ String ip = ResourceUtils.getRealClientIpAddr(this.getContext());
User user = ResourceUtils.getUserFromSessionId(sessionId, ip);
ProjectManager projectManager = getAzkaban().getProjectManager();
Project project = projectManager.getProject(projectName);
diff --git a/azkaban-web-server/src/restli/java/azkaban/restli/ResourceUtils.java b/azkaban-web-server/src/restli/java/azkaban/restli/ResourceUtils.java
index 36be301..ccb0a18 100644
--- a/azkaban-web-server/src/restli/java/azkaban/restli/ResourceUtils.java
+++ b/azkaban-web-server/src/restli/java/azkaban/restli/ResourceUtils.java
@@ -21,8 +21,12 @@ import azkaban.user.Role;
import azkaban.user.User;
import azkaban.user.UserManager;
import azkaban.user.UserManagerException;
+import azkaban.utils.WebUtils;
import azkaban.webapp.AzkabanWebServer;
import azkaban.server.session.Session;
+import com.linkedin.restli.server.ResourceContext;
+
+import java.util.Map;
public class ResourceUtils {
@@ -56,4 +60,19 @@ public class ResourceUtils {
return session.getUser();
}
+
+ public static String getRealClientIpAddr(ResourceContext context){
+
+ // If some upstream device added an X-Forwarded-For header
+ // use it for the client ip
+ // This will support scenarios where load balancers or gateways
+ // front the Azkaban web server and a changing Ip address invalidates
+ // the session
+ Map<String, String> headers = context.getRequestHeaders();
+
+ WebUtils utils = new WebUtils();
+
+ return utils.getRealClientIpAddr(headers,
+ (String) context.getRawRequestContext().getLocalAttr("REMOTE_ADDR"));
+ }
}
diff --git a/azkaban-web-server/src/restli/java/azkaban/restli/UserManagerResource.java b/azkaban-web-server/src/restli/java/azkaban/restli/UserManagerResource.java
index d3c0c33..ed995c3 100644
--- a/azkaban-web-server/src/restli/java/azkaban/restli/UserManagerResource.java
+++ b/azkaban-web-server/src/restli/java/azkaban/restli/UserManagerResource.java
@@ -44,9 +44,7 @@ public class UserManagerResource extends ResourceContextHolder {
public String login(@ActionParam("username") String username,
@ActionParam("password") String password) throws UserManagerException,
ServletException {
- String ip =
- (String) this.getContext().getRawRequestContext()
- .getLocalAttr("REMOTE_ADDR");
+ String ip = ResourceUtils.getRealClientIpAddr(this.getContext());
logger
.info("Attempting to login for " + username + " from ip '" + ip + "'");
@@ -59,9 +57,7 @@ public class UserManagerResource extends ResourceContextHolder {
@Action(name = "getUserFromSessionId")
public User getUserFromSessionId(@ActionParam("sessionId") String sessionId) {
- String ip =
- (String) this.getContext().getRawRequestContext()
- .getLocalAttr("REMOTE_ADDR");
+ String ip = ResourceUtils.getRealClientIpAddr(this.getContext());
Session session = getSessionFromSessionId(sessionId, ip);
azkaban.user.User azUser = session.getUser();
diff --git a/azkaban-web-server/src/test/java/azkaban/fixture/MockLoginAzkabanServlet.java b/azkaban-web-server/src/test/java/azkaban/fixture/MockLoginAzkabanServlet.java
new file mode 100644
index 0000000..e266711
--- /dev/null
+++ b/azkaban-web-server/src/test/java/azkaban/fixture/MockLoginAzkabanServlet.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright 2016 LinkedIn Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package azkaban.fixture;
+
+
+import azkaban.server.AzkabanServer;
+import azkaban.server.session.Session;
+import azkaban.server.session.SessionCache;
+import azkaban.user.UserManager;
+import azkaban.utils.Props;
+import azkaban.webapp.AzkabanWebServer;
+import azkaban.webapp.servlet.LoginAbstractAzkabanServlet;
+import azkaban.user.User;
+import org.apache.velocity.app.VelocityEngine;
+import org.mockito.Spy;
+import org.mortbay.jetty.Server;
+
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletException;
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import java.io.IOException;
+import java.util.UUID;
+
+import static org.mockito.Mockito.*;
+
+public class MockLoginAzkabanServlet extends LoginAbstractAzkabanServlet {
+
+ private static final String SESSION_ID_NAME = "azkaban.browser.session.id";
+
+ public static HttpServletRequest getRequestWithNoUpstream(String clientIp, String sessionId, String requestMethod){
+
+ HttpServletRequest req = mock(HttpServletRequest.class);
+
+ when(req.getRemoteAddr()).thenReturn(clientIp);
+ when(req.getHeader("x-forwarded-for")).thenReturn(null);
+ when(req.getMethod()).thenReturn(requestMethod);
+ when(req.getContentType()).thenReturn("application/x-www-form-urlencoded");
+
+ // Requires sessionId to be passed that is in the application's session cache
+ when(req.getParameter("session.id")).thenReturn(sessionId);
+
+ return req;
+ }
+
+ public static HttpServletRequest getRequestWithUpstream(String clientIp, String upstreamIp, String sessionId, String requestMethod){
+
+ HttpServletRequest req = mock(HttpServletRequest.class);
+
+ when(req.getRemoteAddr()).thenReturn("2.2.2.2:9999");
+ when(req.getHeader("x-forwarded-for")).thenReturn(upstreamIp);
+ when(req.getMethod()).thenReturn(requestMethod);
+ when(req.getContentType()).thenReturn("application/x-www-form-urlencoded");
+
+ // Requires sessionId to be passed that is in the application's session cache
+ when(req.getParameter("session.id")).thenReturn(sessionId);
+
+ return req;
+ }
+
+ public static HttpServletRequest getRequestWithMultipleUpstreams(String clientIp, String upstreamIp, String sessionId, String requestMethod){
+
+ HttpServletRequest req = mock(HttpServletRequest.class);
+
+ when(req.getRemoteAddr()).thenReturn("2.2.2.2:9999");
+ when(req.getHeader("x-forwarded-for")).thenReturn(upstreamIp + ",1.1.1.1,3.3.3.3:33333");
+ when(req.getMethod()).thenReturn(requestMethod);
+ when(req.getContentType()).thenReturn("application/x-www-form-urlencoded");
+
+ // Requires sessionId to be passed that is in the application's session cache
+ when(req.getParameter("session.id")).thenReturn(sessionId);
+
+ return req;
+ }
+
+ public static MockLoginAzkabanServlet getServletWithSession(String sessionId,
+ String username, String clientIp)
+ throws Exception{
+
+ MockLoginAzkabanServlet servlet = new MockLoginAzkabanServlet();
+
+ Server server = mock(Server.class);
+ Props props = new Props();
+ UserManager userManager = mock(UserManager.class);
+
+ // Need to mock and inject an application instance into the servlet
+ AzkabanWebServer app = mock(AzkabanWebServer.class);
+
+ MockLoginAzkabanServlet servletSpy = spy(servlet);
+
+ when(servletSpy.getApplication()).thenReturn(app);
+
+ // Create a concrete SessionCache so a session will get persisted
+ // and can get looked up
+ SessionCache cache = new SessionCache(props);
+ when(app.getSessionCache()).thenReturn(cache);
+
+ // Need a valid object here when processing a request
+ when(app.getVelocityEngine()).thenReturn(mock(VelocityEngine.class));
+
+ // Construct and store a session in the servlet
+ azkaban.user.User user = mock(azkaban.user.User.class);
+ when(user.getEmail()).thenReturn(username + "@mail.com");
+ when(user.getUserId()).thenReturn(username);
+
+ Session session = new Session(sessionId, user, clientIp);
+ servletSpy.getApplication().getSessionCache().addSession(session);
+
+
+ // Return the servletSpy since we replaced implementation for 'getApplication'
+ return servletSpy;
+ }
+
+ @Override
+ protected void handleGet(HttpServletRequest req, HttpServletResponse resp, Session session)
+ throws ServletException, IOException {
+
+ resp.getWriter().write("SUCCESS_MOCK_LOGIN_SERVLET");
+ }
+
+ @Override
+ protected void handlePost(HttpServletRequest req, HttpServletResponse resp, Session session)
+ throws ServletException, IOException {
+
+ resp.getWriter().write("SUCCESS_MOCK_LOGIN_SERVLET");
+ }
+}
diff --git a/azkaban-web-server/src/test/java/azkaban/fixture/MockResourceContext.java b/azkaban-web-server/src/test/java/azkaban/fixture/MockResourceContext.java
new file mode 100644
index 0000000..bce0e90
--- /dev/null
+++ b/azkaban-web-server/src/test/java/azkaban/fixture/MockResourceContext.java
@@ -0,0 +1,142 @@
+/*
+ * Copyright 2016 LinkedIn Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package azkaban.fixture;
+
+import com.linkedin.data.transform.filter.request.MaskTree;
+import com.linkedin.r2.message.RequestContext;
+import com.linkedin.r2.message.rest.RestRequest;
+import com.linkedin.restli.server.PathKeys;
+import com.linkedin.restli.server.ProjectionMode;
+import com.linkedin.restli.server.ResourceContext;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+
+public class MockResourceContext implements ResourceContext {
+
+ Map<String, String> requestHeaders;
+ RequestContext requestContext;
+
+ @Override
+ public RestRequest getRawRequest() {
+ return null;
+ }
+
+ @Override
+ public String getRequestMethod() {
+ return null;
+ }
+
+ @Override
+ public PathKeys getPathKeys() {
+ return null;
+ }
+
+ @Override
+ public MaskTree getProjectionMask() {
+ return null;
+ }
+
+ @Override
+ public boolean hasParameter(String key) {
+ return false;
+ }
+
+ @Override
+ public String getParameter(String key) {
+ return null;
+ }
+
+ @Override
+ public Object getStructuredParameter(String key) {
+ return null;
+ }
+
+ @Override
+ public List<String> getParameterValues(String key) {
+ return null;
+ }
+
+ public void setRequestHeader(String name, String value){
+
+ this.requestHeaders.put(name, value);
+ }
+
+ @Override
+ public Map<String, String> getRequestHeaders() {
+ return this.requestHeaders;
+ }
+
+ @Override
+ public void setResponseHeader(String name, String value) {
+
+ }
+
+ @Override
+ public RequestContext getRawRequestContext() {
+ return this.requestContext;
+ }
+
+ @Override
+ public ProjectionMode getProjectionMode() {
+ return null;
+ }
+
+ @Override
+ public void setProjectionMode(ProjectionMode mode) {
+
+ }
+
+ public void setLocalAttr(String name, String value){
+ requestContext.putLocalAttr(name, value);
+ }
+
+ public MockResourceContext(){
+ requestHeaders = new HashMap<>();
+ requestContext = new RequestContext();
+ }
+
+ public static MockResourceContext getResourceContextWithUpstream(String clientIp, String upstream){
+ MockResourceContext ctx = new MockResourceContext();
+
+ ctx.setLocalAttr("REMOTE_ADDR", clientIp);
+ ctx.setRequestHeader("X-Forwarded-For", upstream);
+
+ return ctx;
+ }
+
+ public static MockResourceContext getResourceContextWithMultipleUpstreams(String clientIp,
+ String firstUpstream){
+ MockResourceContext ctx = new MockResourceContext();
+
+ ctx.setLocalAttr("REMOTE_ADDR", clientIp);
+ ctx.setRequestHeader("X-Forwarded-For", firstUpstream + ",55.55.55.55:55555,1.1.1.1:9999");
+
+ return ctx;
+ }
+
+ public static MockResourceContext getResourceContext(String clientIp){
+ MockResourceContext ctx = new MockResourceContext();
+
+ ctx.setLocalAttr("REMOTE_ADDR", clientIp);
+
+ return ctx;
+ }
+
+}
diff --git a/azkaban-web-server/src/test/java/azkaban/restli/ResourceUtilsTest.java b/azkaban-web-server/src/test/java/azkaban/restli/ResourceUtilsTest.java
new file mode 100644
index 0000000..c6203b5
--- /dev/null
+++ b/azkaban-web-server/src/test/java/azkaban/restli/ResourceUtilsTest.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2016 LinkedIn Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package azkaban.restli;
+
+import azkaban.fixture.MockResourceContext;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+public class ResourceUtilsTest {
+
+ @Test
+ public void testWhenNoXForwardedForHeaderUseClientIp(){
+
+ String clientIp = "127.0.0.1:10000";
+ MockResourceContext ctx = MockResourceContext.getResourceContext(clientIp);
+ assertNotNull(ctx);
+
+ String ip = ResourceUtils.getRealClientIpAddr(ctx);
+
+ assertEquals(ip, "127.0.0.1");
+ }
+
+ @Test
+ public void testWhenClientIpNoPort(){
+
+ String clientIp = "192.168.1.1";
+ MockResourceContext ctx = MockResourceContext.getResourceContext(clientIp);
+ assertNotNull(ctx);
+
+ String ip = ResourceUtils.getRealClientIpAddr(ctx);
+
+ assertEquals(ip, "192.168.1.1");
+ }
+
+ @Test
+ public void testWhenXForwardedForHeaderUseHeader(){
+
+ String clientIp = "127.0.0.1:10000";
+ String upstreamIp = "192.168.1.1:10000";
+ MockResourceContext ctx = MockResourceContext.getResourceContextWithUpstream(clientIp, upstreamIp);
+ assertNotNull(ctx);
+
+ String ip = ResourceUtils.getRealClientIpAddr(ctx);
+
+ assertEquals(ip, "192.168.1.1");
+ }
+
+ @Test
+ public void testWhenXForwardedForHeaderMultipleUpstreamsUseHeader(){
+
+ String clientIp = "127.0.0.1:10000";
+ String upstreamIp = "192.168.1.1:10000";
+ MockResourceContext ctx = MockResourceContext.getResourceContextWithMultipleUpstreams(clientIp, upstreamIp);
+ assertNotNull(ctx);
+
+ String ip = ResourceUtils.getRealClientIpAddr(ctx);
+
+ assertEquals(ip, "192.168.1.1");
+ }
+}
diff --git a/azkaban-web-server/src/test/java/azkaban/webapp/servlet/LoginAbstractAzkabanServletTest.java b/azkaban-web-server/src/test/java/azkaban/webapp/servlet/LoginAbstractAzkabanServletTest.java
new file mode 100644
index 0000000..564ba8b
--- /dev/null
+++ b/azkaban-web-server/src/test/java/azkaban/webapp/servlet/LoginAbstractAzkabanServletTest.java
@@ -0,0 +1,193 @@
+/*
+ * Copyright 2016 LinkedIn Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package azkaban.webapp.servlet;
+
+
+import azkaban.fixture.MockLoginAzkabanServlet;
+import azkaban.server.session.Session;
+import azkaban.server.session.SessionCache;
+import org.junit.Test;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.io.Writer;
+
+import static junit.framework.Assert.assertNotNull;
+import static junit.framework.TestCase.assertEquals;
+import static junit.framework.TestCase.assertNotSame;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class LoginAbstractAzkabanServletTest {
+
+ private HttpServletResponse getResponse(StringWriter stringWriter){
+ HttpServletResponse resp = mock(HttpServletResponse.class);
+ PrintWriter writer = new PrintWriter(stringWriter);
+
+ try{
+ when(resp.getWriter()).thenReturn(writer);
+ }
+ catch(IOException ex){
+ System.out.println(ex);
+ }
+
+ return resp;
+ }
+
+ @Test
+ public void testWhenGetRequestSessionIsValid() throws Exception, IOException, ServletException {
+
+ String clientIp = "127.0.0.1:10000";
+ String sessionId = "111";
+
+
+ HttpServletRequest req = MockLoginAzkabanServlet.getRequestWithNoUpstream(clientIp, sessionId, "GET");
+
+ StringWriter writer = new StringWriter();
+ HttpServletResponse resp = getResponse(writer);
+
+ MockLoginAzkabanServlet servlet = MockLoginAzkabanServlet.getServletWithSession(sessionId,
+ "user", "127.0.0.1");
+
+ servlet.doGet(req, resp);
+
+ // Assert that our response was written (we have a valid session)
+ assertEquals("SUCCESS_MOCK_LOGIN_SERVLET", writer.toString());
+ }
+
+ @Test
+ public void testWhenPostRequestSessionIsValid() throws Exception{
+
+ String clientIp = "127.0.0.1:10000";
+ String sessionId = "111";
+
+
+ HttpServletRequest req = MockLoginAzkabanServlet.getRequestWithNoUpstream(clientIp, sessionId, "POST");
+ StringWriter writer = new StringWriter();
+ HttpServletResponse resp = getResponse(writer);
+
+ MockLoginAzkabanServlet servlet = MockLoginAzkabanServlet.getServletWithSession(sessionId,
+ "user", "127.0.0.1");
+
+
+ servlet.doPost(req, resp);
+
+ // Assert that our response was written (we have a valid session)
+ assertEquals("SUCCESS_MOCK_LOGIN_SERVLET", writer.toString());
+ }
+
+ @Test
+ public void testWhenPostRequestChangedClientIpSessionIsInvalid() throws Exception{
+
+ String clientIp = "127.0.0.2:10000";
+ String sessionId = "111";
+
+
+ HttpServletRequest req = MockLoginAzkabanServlet.getRequestWithNoUpstream(clientIp, sessionId, "POST");
+
+ StringWriter writer = new StringWriter();
+ HttpServletResponse resp = getResponse(writer);
+
+
+ MockLoginAzkabanServlet servlet = MockLoginAzkabanServlet.getServletWithSession(sessionId,
+ "user", "127.0.0.1");
+
+
+ servlet.doPost(req, resp);
+
+ // Assert that our response was written (we have a valid session)
+ assertNotSame("SUCCESS_MOCK_LOGIN_SERVLET", writer.toString());
+ }
+
+ @Test
+ public void testWhenPostRequestChangedClientPortSessionIsValid() throws Exception{
+
+ String clientIp = "127.0.0.1:10000";
+ String sessionId = "111";
+
+
+ HttpServletRequest req = MockLoginAzkabanServlet.getRequestWithNoUpstream(clientIp, sessionId, "POST");
+
+ StringWriter writer = new StringWriter();
+ HttpServletResponse resp = getResponse(writer);
+
+
+ MockLoginAzkabanServlet servlet = MockLoginAzkabanServlet.getServletWithSession(sessionId,
+ "user", "127.0.0.1");
+
+
+ servlet.doPost(req, resp);
+
+ // Assert that our response was written (we have a valid session)
+ assertEquals("SUCCESS_MOCK_LOGIN_SERVLET", writer.toString());
+ }
+
+ @Test
+ public void testWhenPostRequestWithUpstreamSessionIsValid() throws Exception{
+
+ String clientIp = "127.0.0.1:10000";
+ String upstreamIp = "192.168.1.1:11111";
+ String sessionId = "111";
+
+
+ HttpServletRequest req = MockLoginAzkabanServlet.getRequestWithUpstream(clientIp, upstreamIp,
+ sessionId, "POST");
+
+ StringWriter writer = new StringWriter();
+ HttpServletResponse resp = getResponse(writer);
+
+
+ MockLoginAzkabanServlet servlet = MockLoginAzkabanServlet.getServletWithSession(sessionId,
+ "user", "192.168.1.1");
+
+
+ servlet.doPost(req, resp);
+
+ // Assert that our response was written (we have a valid session)
+ assertEquals("SUCCESS_MOCK_LOGIN_SERVLET", writer.toString());
+ }
+
+ @Test
+ public void testWhenPostRequestWithMultipleUpstreamsSessionIsValid() throws Exception{
+
+ String clientIp = "127.0.0.1:10000";
+ String upstreamIp = "192.168.1.1:11111,888.8.8.8:2222,5.5.5.5:5555";
+ String sessionId = "111";
+
+
+ HttpServletRequest req = MockLoginAzkabanServlet.getRequestWithUpstream(clientIp, upstreamIp,
+ sessionId, "POST");
+
+ StringWriter writer = new StringWriter();
+ HttpServletResponse resp = getResponse(writer);
+
+
+ MockLoginAzkabanServlet servlet = MockLoginAzkabanServlet.getServletWithSession(sessionId,
+ "user", "192.168.1.1");
+
+
+ servlet.doPost(req, resp);
+
+ // Assert that our response was written (we have a valid session)
+ assertEquals("SUCCESS_MOCK_LOGIN_SERVLET", writer.toString());
+ }
+}