diff --git a/api/src/org/apache/cloudstack/api/auth/APIAuthenticator.java b/api/src/org/apache/cloudstack/api/auth/APIAuthenticator.java index 00875614f7c..7e33b1347db 100644 --- a/api/src/org/apache/cloudstack/api/auth/APIAuthenticator.java +++ b/api/src/org/apache/cloudstack/api/auth/APIAuthenticator.java @@ -18,6 +18,7 @@ package org.apache.cloudstack.api.auth; import org.apache.cloudstack.api.ServerApiException; +import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import java.util.List; @@ -37,7 +38,7 @@ import java.net.InetAddress; public interface APIAuthenticator { public String authenticate(String command, Map params, HttpSession session, InetAddress remoteAddress, String responseType, - StringBuilder auditTrailSb, final HttpServletResponse resp) throws ServerApiException; + StringBuilder auditTrailSb, final HttpServletRequest req, final HttpServletResponse resp) throws ServerApiException; public APIAuthenticationType getAPIType(); diff --git a/plugins/user-authenticators/saml2/src/org/apache/cloudstack/api/command/GetServiceProviderMetaDataCmd.java b/plugins/user-authenticators/saml2/src/org/apache/cloudstack/api/command/GetServiceProviderMetaDataCmd.java index 4697438a10c..721b18e1d9e 100644 --- a/plugins/user-authenticators/saml2/src/org/apache/cloudstack/api/command/GetServiceProviderMetaDataCmd.java +++ b/plugins/user-authenticators/saml2/src/org/apache/cloudstack/api/command/GetServiceProviderMetaDataCmd.java @@ -58,6 +58,7 @@ import org.opensaml.xml.security.x509.X509KeyInfoGeneratorFactory; import org.w3c.dom.Document; import javax.inject.Inject; +import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import javax.xml.parsers.DocumentBuilder; @@ -105,7 +106,7 @@ public class GetServiceProviderMetaDataCmd extends BaseCmd implements APIAuthent } @Override - public String authenticate(String command, Map params, HttpSession session, InetAddress remoteAddress, String responseType, StringBuilder auditTrailSb, HttpServletResponse resp) throws ServerApiException { + public String authenticate(String command, Map params, HttpSession session, InetAddress remoteAddress, String responseType, StringBuilder auditTrailSb, final HttpServletRequest req, final HttpServletResponse resp) throws ServerApiException { SAMLMetaDataResponse response = new SAMLMetaDataResponse(); response.setResponseName(getCommandName()); diff --git a/plugins/user-authenticators/saml2/src/org/apache/cloudstack/api/command/SAML2LoginAPIAuthenticatorCmd.java b/plugins/user-authenticators/saml2/src/org/apache/cloudstack/api/command/SAML2LoginAPIAuthenticatorCmd.java index de6031c1cff..dda5876213e 100644 --- a/plugins/user-authenticators/saml2/src/org/apache/cloudstack/api/command/SAML2LoginAPIAuthenticatorCmd.java +++ b/plugins/user-authenticators/saml2/src/org/apache/cloudstack/api/command/SAML2LoginAPIAuthenticatorCmd.java @@ -62,6 +62,7 @@ import org.xml.sax.SAXException; import javax.inject.Inject; import javax.servlet.http.Cookie; +import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import javax.xml.parsers.ParserConfigurationException; @@ -165,7 +166,7 @@ public class SAML2LoginAPIAuthenticatorCmd extends BaseCmd implements APIAuthent } @Override - public String authenticate(final String command, final Map params, final HttpSession session, final InetAddress remoteAddress, final String responseType, final StringBuilder auditTrailSb, final HttpServletResponse resp) throws ServerApiException { + public String authenticate(final String command, final Map params, final HttpSession session, final InetAddress remoteAddress, final String responseType, final StringBuilder auditTrailSb, final HttpServletRequest req, final HttpServletResponse resp) throws ServerApiException { try { if (!params.containsKey("SAMLResponse") && !params.containsKey("SAMLart")) { String idpUrl = null; diff --git a/plugins/user-authenticators/saml2/src/org/apache/cloudstack/api/command/SAML2LogoutAPIAuthenticatorCmd.java b/plugins/user-authenticators/saml2/src/org/apache/cloudstack/api/command/SAML2LogoutAPIAuthenticatorCmd.java index 3608fed8138..17a0a30ca53 100644 --- a/plugins/user-authenticators/saml2/src/org/apache/cloudstack/api/command/SAML2LogoutAPIAuthenticatorCmd.java +++ b/plugins/user-authenticators/saml2/src/org/apache/cloudstack/api/command/SAML2LogoutAPIAuthenticatorCmd.java @@ -43,6 +43,7 @@ import org.opensaml.xml.io.UnmarshallingException; import org.xml.sax.SAXException; import javax.inject.Inject; +import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import javax.xml.parsers.ParserConfigurationException; @@ -84,7 +85,7 @@ public class SAML2LogoutAPIAuthenticatorCmd extends BaseCmd implements APIAuthen } @Override - public String authenticate(String command, Map params, HttpSession session, InetAddress remoteAddress, String responseType, StringBuilder auditTrailSb, final HttpServletResponse resp) throws ServerApiException { + public String authenticate(String command, Map params, HttpSession session, InetAddress remoteAddress, String responseType, StringBuilder auditTrailSb, final HttpServletRequest req, final HttpServletResponse resp) throws ServerApiException { auditTrailSb.append("=== SAML SLO Logging out ==="); LogoutCmdResponse response = new LogoutCmdResponse(); response.setDescription("success"); diff --git a/plugins/user-authenticators/saml2/test/org/apache/cloudstack/api/command/GetServiceProviderMetaDataCmdTest.java b/plugins/user-authenticators/saml2/test/org/apache/cloudstack/api/command/GetServiceProviderMetaDataCmdTest.java index c727d84ca4e..e53e70193c0 100644 --- a/plugins/user-authenticators/saml2/test/org/apache/cloudstack/api/command/GetServiceProviderMetaDataCmdTest.java +++ b/plugins/user-authenticators/saml2/test/org/apache/cloudstack/api/command/GetServiceProviderMetaDataCmdTest.java @@ -31,6 +31,7 @@ import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.runners.MockitoJUnitRunner; +import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import java.lang.reflect.Field; @@ -59,6 +60,9 @@ public class GetServiceProviderMetaDataCmdTest { @Mock HttpServletResponse resp; + @Mock + HttpServletRequest req; + @Test public void testAuthenticate() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException, CertificateParsingException, CertificateEncodingException, NoSuchAlgorithmException, InvalidKeyException, NoSuchProviderException, SignatureException, UnknownHostException { GetServiceProviderMetaDataCmd cmd = new GetServiceProviderMetaDataCmd(); @@ -79,7 +83,7 @@ public class GetServiceProviderMetaDataCmdTest { Mockito.when(samlAuthManager.getIdpSingleLogOutUrl()).thenReturn(url); Mockito.when(samlAuthManager.getSpSingleLogOutUrl()).thenReturn(url); - String result = cmd.authenticate("command", null, session, InetAddress.getByName("127.0.0.1"), HttpUtils.RESPONSE_TYPE_JSON, new StringBuilder(), resp); + String result = cmd.authenticate("command", null, session, InetAddress.getByName("127.0.0.1"), HttpUtils.RESPONSE_TYPE_JSON, new StringBuilder(), req, resp); Assert.assertTrue(result.contains("md:EntityDescriptor")); Mockito.verify(samlAuthManager, Mockito.atLeast(1)).getServiceProviderId(); @@ -93,4 +97,4 @@ public class GetServiceProviderMetaDataCmdTest { public void testGetAPIType() { Assert.assertTrue(new GetServiceProviderMetaDataCmd().getAPIType() == APIAuthenticationType.LOGIN_API); } -} \ No newline at end of file +} diff --git a/plugins/user-authenticators/saml2/test/org/apache/cloudstack/api/command/SAML2LoginAPIAuthenticatorCmdTest.java b/plugins/user-authenticators/saml2/test/org/apache/cloudstack/api/command/SAML2LoginAPIAuthenticatorCmdTest.java index 321b6fa90df..8fbed414363 100644 --- a/plugins/user-authenticators/saml2/test/org/apache/cloudstack/api/command/SAML2LoginAPIAuthenticatorCmdTest.java +++ b/plugins/user-authenticators/saml2/test/org/apache/cloudstack/api/command/SAML2LoginAPIAuthenticatorCmdTest.java @@ -58,6 +58,7 @@ import org.opensaml.saml2.core.impl.StatusBuilder; import org.opensaml.saml2.core.impl.StatusCodeBuilder; import org.opensaml.saml2.core.impl.SubjectBuilder; +import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import java.lang.reflect.Field; @@ -96,6 +97,9 @@ public class SAML2LoginAPIAuthenticatorCmdTest { @Mock HttpServletResponse resp; + @Mock + HttpServletRequest req; + private Response buildMockResponse() throws Exception { Response samlMessage = new ResponseBuilder().buildObject(); samlMessage.setID("foo"); @@ -172,14 +176,14 @@ public class SAML2LoginAPIAuthenticatorCmdTest { Map params = new HashMap(); // SSO redirection test - cmd.authenticate("command", params, session, InetAddress.getByName("127.0.0.1"), HttpUtils.RESPONSE_TYPE_JSON, new StringBuilder(), resp); + cmd.authenticate("command", params, session, InetAddress.getByName("127.0.0.1"), HttpUtils.RESPONSE_TYPE_JSON, new StringBuilder(), req, resp); Mockito.verify(resp, Mockito.times(1)).sendRedirect(Mockito.anyString()); // SSO SAMLResponse verification test, this should throw ServerApiException for auth failure params.put(SAMLUtils.SAML_RESPONSE, new String[]{"Some String"}); Mockito.stub(cmd.processSAMLResponse(Mockito.anyString())).toReturn(buildMockResponse()); try { - cmd.authenticate("command", params, session, InetAddress.getByName("127.0.0.1"), HttpUtils.RESPONSE_TYPE_JSON, new StringBuilder(), resp); + cmd.authenticate("command", params, session, InetAddress.getByName("127.0.0.1"), HttpUtils.RESPONSE_TYPE_JSON, new StringBuilder(), req, resp); } catch (ServerApiException ignored) { } Mockito.verify(configDao, Mockito.atLeastOnce()).getValue(Mockito.anyString()); @@ -192,4 +196,4 @@ public class SAML2LoginAPIAuthenticatorCmdTest { public void testGetAPIType() { Assert.assertTrue(new GetServiceProviderMetaDataCmd().getAPIType() == APIAuthenticationType.LOGIN_API); } -} \ No newline at end of file +} diff --git a/plugins/user-authenticators/saml2/test/org/apache/cloudstack/api/command/SAML2LogoutAPIAuthenticatorCmdTest.java b/plugins/user-authenticators/saml2/test/org/apache/cloudstack/api/command/SAML2LogoutAPIAuthenticatorCmdTest.java index 9ef00b4184f..4388b886c22 100644 --- a/plugins/user-authenticators/saml2/test/org/apache/cloudstack/api/command/SAML2LogoutAPIAuthenticatorCmdTest.java +++ b/plugins/user-authenticators/saml2/test/org/apache/cloudstack/api/command/SAML2LogoutAPIAuthenticatorCmdTest.java @@ -32,6 +32,7 @@ import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.runners.MockitoJUnitRunner; +import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import java.lang.reflect.Field; @@ -56,6 +57,9 @@ public class SAML2LogoutAPIAuthenticatorCmdTest { @Mock HttpServletResponse resp; + @Mock + HttpServletRequest req; + @Test public void testAuthenticate() throws Exception { SAML2LogoutAPIAuthenticatorCmd cmd = new SAML2LogoutAPIAuthenticatorCmd(); @@ -82,7 +86,7 @@ public class SAML2LogoutAPIAuthenticatorCmdTest { Mockito.when(session.getAttribute(Mockito.anyString())).thenReturn(null); Mockito.when(configDao.getValue(Mockito.anyString())).thenReturn("someString"); - cmd.authenticate("command", null, session, InetAddress.getByName("127.0.0.1"), HttpUtils.RESPONSE_TYPE_JSON, new StringBuilder(), resp); + cmd.authenticate("command", null, session, InetAddress.getByName("127.0.0.1"), HttpUtils.RESPONSE_TYPE_JSON, new StringBuilder(), req, resp); Mockito.verify(resp, Mockito.times(1)).sendRedirect(Mockito.anyString()); Mockito.verify(session, Mockito.atLeastOnce()).getAttribute(Mockito.anyString()); } @@ -91,4 +95,4 @@ public class SAML2LogoutAPIAuthenticatorCmdTest { public void testGetAPIType() throws Exception { Assert.assertTrue(new SAML2LogoutAPIAuthenticatorCmd().getAPIType() == APIAuthenticationType.LOGOUT_API); } -} \ No newline at end of file +} diff --git a/server/src/com/cloud/api/ApiServlet.java b/server/src/com/cloud/api/ApiServlet.java index dccc9e7edd4..3b3a0be7c46 100644 --- a/server/src/com/cloud/api/ApiServlet.java +++ b/server/src/com/cloud/api/ApiServlet.java @@ -197,7 +197,7 @@ public class ApiServlet extends HttpServlet { } try { - responseString = apiAuthenticator.authenticate(command, params, session, InetAddress.getByName(remoteAddress), responseType, auditTrailSb, resp); + responseString = apiAuthenticator.authenticate(command, params, session, InetAddress.getByName(remoteAddress), responseType, auditTrailSb, req, resp); } catch (ServerApiException e) { httpResponseCode = e.getErrorCode().getHttpCode(); responseString = e.getMessage(); diff --git a/server/src/com/cloud/api/auth/DefaultLoginAPIAuthenticatorCmd.java b/server/src/com/cloud/api/auth/DefaultLoginAPIAuthenticatorCmd.java index 39c95ea7877..d6eac7864d4 100644 --- a/server/src/com/cloud/api/auth/DefaultLoginAPIAuthenticatorCmd.java +++ b/server/src/com/cloud/api/auth/DefaultLoginAPIAuthenticatorCmd.java @@ -33,6 +33,7 @@ import org.apache.cloudstack.api.response.LoginCmdResponse; import org.apache.log4j.Logger; import javax.inject.Inject; +import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import java.util.List; @@ -104,8 +105,11 @@ public class DefaultLoginAPIAuthenticatorCmd extends BaseCmd implements APIAuthe } @Override - public String authenticate(String command, Map params, HttpSession session, InetAddress remoteAddress, String responseType, StringBuilder auditTrailSb, final HttpServletResponse resp) throws ServerApiException { - + public String authenticate(String command, Map params, HttpSession session, InetAddress remoteAddress, String responseType, StringBuilder auditTrailSb, final HttpServletRequest req, final HttpServletResponse resp) throws ServerApiException { + // Disallow non POST requests + if (HTTPMethod.valueOf(req.getMethod()) != HTTPMethod.POST) { + throw new ServerApiException(ApiErrorCode.METHOD_NOT_ALLOWED, "Please use HTTP POST to authenticate using this API"); + } // FIXME: ported from ApiServlet, refactor and cleanup final String[] username = (String[])params.get(ApiConstants.USERNAME); final String[] password = (String[])params.get(ApiConstants.PASSWORD); diff --git a/server/src/com/cloud/api/auth/DefaultLogoutAPIAuthenticatorCmd.java b/server/src/com/cloud/api/auth/DefaultLogoutAPIAuthenticatorCmd.java index 748c8e54932..eb3d448a535 100644 --- a/server/src/com/cloud/api/auth/DefaultLogoutAPIAuthenticatorCmd.java +++ b/server/src/com/cloud/api/auth/DefaultLogoutAPIAuthenticatorCmd.java @@ -28,6 +28,7 @@ import org.apache.cloudstack.api.auth.PluggableAPIAuthenticator; import org.apache.cloudstack.api.response.LogoutCmdResponse; import org.apache.log4j.Logger; +import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import java.util.List; @@ -61,7 +62,7 @@ public class DefaultLogoutAPIAuthenticatorCmd extends BaseCmd implements APIAuth } @Override - public String authenticate(String command, Map params, HttpSession session, InetAddress remoteAddress, String responseType, StringBuilder auditTrailSb, final HttpServletResponse resp) throws ServerApiException { + public String authenticate(String command, Map params, HttpSession session, InetAddress remoteAddress, String responseType, StringBuilder auditTrailSb, final HttpServletRequest req, final HttpServletResponse resp) throws ServerApiException { auditTrailSb.append("=== Logging out ==="); LogoutCmdResponse response = new LogoutCmdResponse(); response.setDescription("success"); diff --git a/server/test/com/cloud/api/ApiServletTest.java b/server/test/com/cloud/api/ApiServletTest.java index 57caa44bf90..a0a600409c6 100644 --- a/server/test/com/cloud/api/ApiServletTest.java +++ b/server/test/com/cloud/api/ApiServletTest.java @@ -105,7 +105,7 @@ public class ApiServletTest { Mockito.when(authManager.getAPIAuthenticator(Mockito.anyString())).thenReturn(authenticator); Mockito.when(authenticator.authenticate(Mockito.anyString(), Mockito.anyMap(), Mockito.isA(HttpSession.class), - Mockito.same(InetAddress.getByName("127.0.0.1")), Mockito.anyString(), Mockito.isA(StringBuilder.class), Mockito.isA(HttpServletResponse.class))).thenReturn("{\"loginresponse\":{}"); + Mockito.same(InetAddress.getByName("127.0.0.1")), Mockito.anyString(), Mockito.isA(StringBuilder.class), Mockito.isA(HttpServletRequest.class), Mockito.isA(HttpServletResponse.class))).thenReturn("{\"loginresponse\":{}"); Field authManagerField = ApiServlet.class.getDeclaredField("_authManager"); authManagerField.setAccessible(true); @@ -217,7 +217,7 @@ public class ApiServletTest { Mockito.verify(authManager).getAPIAuthenticator("logout"); Mockito.verify(authenticator).authenticate(Mockito.anyString(), Mockito.anyMap(), Mockito.isA(HttpSession.class), - Mockito.eq(InetAddress.getByName("127.0.0.1")), Mockito.anyString(), Mockito.isA(StringBuilder.class), Mockito.isA(HttpServletResponse.class)); + Mockito.eq(InetAddress.getByName("127.0.0.1")), Mockito.anyString(), Mockito.isA(StringBuilder.class), Mockito.isA(HttpServletRequest.class), Mockito.isA(HttpServletResponse.class)); Mockito.verify(session).invalidate(); } @@ -239,7 +239,7 @@ public class ApiServletTest { Mockito.verify(authManager).getAPIAuthenticator("login"); Mockito.verify(authenticator).authenticate(Mockito.anyString(), Mockito.anyMap(), Mockito.isA(HttpSession.class), - Mockito.eq(InetAddress.getByName("127.0.0.1")), Mockito.anyString(), Mockito.isA(StringBuilder.class), Mockito.isA(HttpServletResponse.class)); + Mockito.eq(InetAddress.getByName("127.0.0.1")), Mockito.anyString(), Mockito.isA(StringBuilder.class), Mockito.isA(HttpServletRequest.class), Mockito.isA(HttpServletResponse.class)); } @Test