diff --git a/api/src/org/apache/cloudstack/api/auth/APIAuthenticator.java b/api/src/org/apache/cloudstack/api/auth/APIAuthenticator.java index b008f00aed1..67fa1d8816e 100644 --- a/api/src/org/apache/cloudstack/api/auth/APIAuthenticator.java +++ b/api/src/org/apache/cloudstack/api/auth/APIAuthenticator.java @@ -20,6 +20,7 @@ import org.apache.cloudstack.api.ServerApiException; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; +import java.util.List; import java.util.Map; /* @@ -36,5 +37,8 @@ public interface APIAuthenticator { public String authenticate(String command, Map params, HttpSession session, String remoteAddress, String responseType, StringBuilder auditTrailSb, final HttpServletResponse resp) throws ServerApiException; + public APIAuthenticationType getAPIType(); + + public void setAuthenticators(List authenticators); } 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 ec3a4d242bb..88acfe1e916 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 @@ -32,6 +32,7 @@ import org.apache.cloudstack.api.Parameter; import org.apache.cloudstack.api.ServerApiException; import org.apache.cloudstack.api.auth.APIAuthenticationType; import org.apache.cloudstack.api.auth.APIAuthenticator; +import org.apache.cloudstack.api.auth.PluggableAPIAuthenticator; import org.apache.cloudstack.api.response.LoginCmdResponse; import org.apache.cloudstack.context.CallContext; import org.apache.cloudstack.saml.SAML2AuthManager; @@ -49,7 +50,10 @@ import org.opensaml.saml2.core.StatusCode; import org.opensaml.xml.ConfigurationException; import org.opensaml.xml.io.MarshallingException; import org.opensaml.xml.io.UnmarshallingException; +import org.opensaml.xml.security.x509.BasicX509Credential; import org.opensaml.xml.signature.Signature; +import org.opensaml.xml.signature.SignatureValidator; +import org.opensaml.xml.validation.ValidationException; import org.xml.sax.SAXException; import javax.inject.Inject; @@ -80,7 +84,7 @@ public class SAML2LoginAPIAuthenticatorCmd extends BaseCmd implements APIAuthent ApiServerService _apiServer; @Inject EntityManager _entityMgr; - @Inject + SAML2AuthManager _samlAuthManager; ///////////////////////////////////////////////////// @@ -135,9 +139,10 @@ public class SAML2LoginAPIAuthenticatorCmd extends BaseCmd implements APIAuthent public Response processSAMLResponse(String responseMessage) { Response responseObject = null; try { + DefaultBootstrap.bootstrap(); responseObject = SAMLUtils.decodeSAMLResponse(responseMessage); - } catch (ConfigurationException | ParserConfigurationException | SAXException | IOException | UnmarshallingException e) { + } catch (ConfigurationException | FactoryConfigurationError | ParserConfigurationException | SAXException | IOException | UnmarshallingException e) { s_logger.error("SAMLResponse processing error: " + e.getMessage()); } return responseObject; @@ -165,9 +170,20 @@ public class SAML2LoginAPIAuthenticatorCmd extends BaseCmd implements APIAuthent params, responseType)); } - Signature sig = processedSAMLResponse.getSignature(); - //SignatureValidator validator = new SignatureValidator(credential); - //validator.validate(sig); + if (_samlAuthManager.getIdpSigningKey() != null) { + Signature sig = processedSAMLResponse.getSignature(); + BasicX509Credential credential = new BasicX509Credential(); + credential.setEntityCertificate(_samlAuthManager.getIdpSigningKey()); + SignatureValidator validator = new SignatureValidator(credential); + try { + validator.validate(sig); + } catch (ValidationException e) { + s_logger.error("SAML Response's signature failed to be validated by IDP signing key:" + e.getMessage()); + throw new ServerApiException(ApiErrorCode.ACCOUNT_ERROR, _apiServer.getSerializedApiError(ApiErrorCode.ACCOUNT_ERROR.getHttpCode(), + "SAML Response's signature failed to be validated by IDP signing key", + params, responseType)); + } + } String uniqueUserId = null; String accountName = "admin"; //GET from config, try, fail @@ -251,4 +267,16 @@ public class SAML2LoginAPIAuthenticatorCmd extends BaseCmd implements APIAuthent public APIAuthenticationType getAPIType() { return APIAuthenticationType.LOGIN_API; } + + @Override + public void setAuthenticators(List authenticators) { + for (PluggableAPIAuthenticator authManager: authenticators) { + if (authManager instanceof SAML2AuthManager) { + _samlAuthManager = (SAML2AuthManager) authManager; + } + } + if (_samlAuthManager == null) { + s_logger.error("No suitable Pluggable Authentication Manager found for SAML2 Login Cmd"); + } + } } diff --git a/server/src/com/cloud/api/auth/APIAuthenticationManagerImpl.java b/server/src/com/cloud/api/auth/APIAuthenticationManagerImpl.java index 790b6d90e67..24ccbeb6032 100644 --- a/server/src/com/cloud/api/auth/APIAuthenticationManagerImpl.java +++ b/server/src/com/cloud/api/auth/APIAuthenticationManagerImpl.java @@ -81,6 +81,7 @@ public class APIAuthenticationManagerImpl extends ManagerBase implements APIAuth try { apiAuthenticator = (APIAuthenticator) s_authenticators.get(name).newInstance(); apiAuthenticator = ComponentContext.inject(apiAuthenticator); + apiAuthenticator.setAuthenticators(_apiAuthenticators); } catch (InstantiationException | IllegalAccessException e) { if (s_logger.isDebugEnabled()) { s_logger.debug("APIAuthenticationManagerImpl::getAPIAuthenticator failed: " + e.getMessage()); diff --git a/server/src/com/cloud/api/auth/DefaultLoginAPIAuthenticatorCmd.java b/server/src/com/cloud/api/auth/DefaultLoginAPIAuthenticatorCmd.java index 2fb3f560175..fa23abd43e4 100644 --- a/server/src/com/cloud/api/auth/DefaultLoginAPIAuthenticatorCmd.java +++ b/server/src/com/cloud/api/auth/DefaultLoginAPIAuthenticatorCmd.java @@ -28,12 +28,14 @@ import org.apache.cloudstack.api.Parameter; import org.apache.cloudstack.api.ServerApiException; import org.apache.cloudstack.api.auth.APIAuthenticationType; import org.apache.cloudstack.api.auth.APIAuthenticator; +import org.apache.cloudstack.api.auth.PluggableAPIAuthenticator; import org.apache.cloudstack.api.response.LoginCmdResponse; import org.apache.log4j.Logger; import javax.inject.Inject; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; +import java.util.List; import java.util.Map; @APICommand(name = "login", description = "Logs a user into the CloudStack. A successful login attempt will generate a JSESSIONID cookie value that can be passed in subsequent Query command calls until the \"logout\" command has been issued or the session has expired.", requestHasSensitiveInfo = true, responseObject = LoginCmdResponse.class, entityType = {}) @@ -172,4 +174,8 @@ public class DefaultLoginAPIAuthenticatorCmd extends BaseCmd implements APIAuthe public APIAuthenticationType getAPIType() { return APIAuthenticationType.LOGIN_API; } + + @Override + public void setAuthenticators(List authenticators) { + } } diff --git a/server/src/com/cloud/api/auth/DefaultLogoutAPIAuthenticatorCmd.java b/server/src/com/cloud/api/auth/DefaultLogoutAPIAuthenticatorCmd.java index 999cefd8e3f..ee7936ad734 100644 --- a/server/src/com/cloud/api/auth/DefaultLogoutAPIAuthenticatorCmd.java +++ b/server/src/com/cloud/api/auth/DefaultLogoutAPIAuthenticatorCmd.java @@ -24,11 +24,13 @@ import org.apache.cloudstack.api.BaseCmd; import org.apache.cloudstack.api.ServerApiException; import org.apache.cloudstack.api.auth.APIAuthenticationType; import org.apache.cloudstack.api.auth.APIAuthenticator; +import org.apache.cloudstack.api.auth.PluggableAPIAuthenticator; import org.apache.cloudstack.api.response.LogoutCmdResponse; import org.apache.log4j.Logger; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; +import java.util.List; import java.util.Map; @APICommand(name = "logout", description = "Logs out the user", responseObject = LogoutCmdResponse.class, entityType = {}) @@ -70,4 +72,8 @@ public class DefaultLogoutAPIAuthenticatorCmd extends BaseCmd implements APIAuth public APIAuthenticationType getAPIType() { return APIAuthenticationType.LOGOUT_API; } + + @Override + public void setAuthenticators(List authenticators) { + } } diff --git a/server/src/com/cloud/configuration/Config.java b/server/src/com/cloud/configuration/Config.java index b2fb85f4b28..de4aaed56a4 100755 --- a/server/src/com/cloud/configuration/Config.java +++ b/server/src/com/cloud/configuration/Config.java @@ -1384,7 +1384,7 @@ public enum Config { ManagementServer.class, String.class, "saml2.sp.id", - "Apache CloudStack", + "org.apache.cloudstack", "SAML2 Service Provider Identifier String", null), SAMLServiceProviderSingleSignOnURL(