cloudstack-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From bhais...@apache.org
Subject [2/4] git commit: updated refs/heads/saml-pp-squashed to 7310c2d
Date Fri, 26 Jun 2015 16:11:29 GMT
http://git-wip-us.apache.org/repos/asf/cloudstack/blob/7310c2d4/plugins/user-authenticators/saml2/test/org/apache/cloudstack/api/command/GetServiceProviderMetaDataCmdTest.java
----------------------------------------------------------------------
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
deleted file mode 100644
index cb16f0c..0000000
--- a/plugins/user-authenticators/saml2/test/org/apache/cloudstack/api/command/GetServiceProviderMetaDataCmdTest.java
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you 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 org.apache.cloudstack.api.command;
-
-import com.cloud.utils.HttpUtils;
-import org.apache.cloudstack.api.ApiServerService;
-import org.apache.cloudstack.api.auth.APIAuthenticationType;
-import org.apache.cloudstack.saml.SAML2AuthManager;
-import org.apache.cloudstack.utils.auth.SAMLUtils;
-import org.junit.Assert;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-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;
-import java.security.InvalidKeyException;
-import java.security.NoSuchAlgorithmException;
-import java.security.NoSuchProviderException;
-import java.security.SignatureException;
-import java.security.cert.CertificateEncodingException;
-import java.security.cert.CertificateParsingException;
-import java.security.cert.X509Certificate;
-
-@RunWith(MockitoJUnitRunner.class)
-public class GetServiceProviderMetaDataCmdTest {
-
-    @Mock
-    ApiServerService apiServer;
-
-    @Mock
-    SAML2AuthManager samlAuthManager;
-
-    @Mock
-    HttpSession session;
-
-    @Mock
-    HttpServletResponse resp;
-
-    @Mock
-    HttpServletRequest req;
-
-    @Test
-    public void testAuthenticate() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException, CertificateParsingException, CertificateEncodingException, NoSuchAlgorithmException, InvalidKeyException, NoSuchProviderException, SignatureException {
-        GetServiceProviderMetaDataCmd cmd = new GetServiceProviderMetaDataCmd();
-
-        Field apiServerField = GetServiceProviderMetaDataCmd.class.getDeclaredField("_apiServer");
-        apiServerField.setAccessible(true);
-        apiServerField.set(cmd, apiServer);
-
-        Field managerField = GetServiceProviderMetaDataCmd.class.getDeclaredField("_samlAuthManager");
-        managerField.setAccessible(true);
-        managerField.set(cmd, samlAuthManager);
-
-        String spId = "someSPID";
-        String url = "someUrl";
-        X509Certificate cert = SAMLUtils.generateRandomX509Certificate(SAMLUtils.generateRandomKeyPair());
-        Mockito.when(samlAuthManager.getServiceProviderId()).thenReturn(spId);
-        Mockito.when(samlAuthManager.getIdpSigningKey()).thenReturn(cert);
-        Mockito.when(samlAuthManager.getIdpSingleLogOutUrl()).thenReturn(url);
-        Mockito.when(samlAuthManager.getSpSingleLogOutUrl()).thenReturn(url);
-
-        String result = cmd.authenticate("command", null, session, "random", HttpUtils.RESPONSE_TYPE_JSON, new StringBuilder(), req, resp);
-        Assert.assertTrue(result.contains("md:EntityDescriptor"));
-
-        Mockito.verify(samlAuthManager, Mockito.atLeast(1)).getServiceProviderId();
-        Mockito.verify(samlAuthManager, Mockito.atLeast(1)).getSpSingleSignOnUrl();
-        Mockito.verify(samlAuthManager, Mockito.atLeast(1)).getSpSingleLogOutUrl();
-        Mockito.verify(samlAuthManager, Mockito.never()).getIdpSingleSignOnUrl();
-        Mockito.verify(samlAuthManager, Mockito.never()).getIdpSingleLogOutUrl();
-    }
-
-    @Test
-    public void testGetAPIType() {
-        Assert.assertTrue(new GetServiceProviderMetaDataCmd().getAPIType() == APIAuthenticationType.LOGIN_API);
-    }
-}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/7310c2d4/plugins/user-authenticators/saml2/test/org/apache/cloudstack/api/command/SAML2LoginAPIAuthenticatorCmdTest.java
----------------------------------------------------------------------
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 30ecc93..00455b9 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
@@ -29,9 +29,10 @@ import org.apache.cloudstack.api.ApiServerService;
 import org.apache.cloudstack.api.BaseCmd;
 import org.apache.cloudstack.api.ServerApiException;
 import org.apache.cloudstack.api.auth.APIAuthenticationType;
-import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
 import org.apache.cloudstack.saml.SAML2AuthManager;
-import org.apache.cloudstack.utils.auth.SAMLUtils;
+import org.apache.cloudstack.saml.SAMLPluginConstants;
+import org.apache.cloudstack.saml.SAMLProviderMetadata;
+import org.apache.cloudstack.saml.SAMLUtils;
 import org.joda.time.DateTime;
 import org.junit.Assert;
 import org.junit.Test;
@@ -43,6 +44,7 @@ import org.opensaml.common.SAMLVersion;
 import org.opensaml.saml2.core.Assertion;
 import org.opensaml.saml2.core.AttributeStatement;
 import org.opensaml.saml2.core.AuthnStatement;
+import org.opensaml.saml2.core.Issuer;
 import org.opensaml.saml2.core.NameID;
 import org.opensaml.saml2.core.NameIDType;
 import org.opensaml.saml2.core.Response;
@@ -52,6 +54,7 @@ import org.opensaml.saml2.core.Subject;
 import org.opensaml.saml2.core.impl.AssertionBuilder;
 import org.opensaml.saml2.core.impl.AttributeStatementBuilder;
 import org.opensaml.saml2.core.impl.AuthnStatementBuilder;
+import org.opensaml.saml2.core.impl.IssuerBuilder;
 import org.opensaml.saml2.core.impl.NameIDBuilder;
 import org.opensaml.saml2.core.impl.ResponseBuilder;
 import org.opensaml.saml2.core.impl.StatusBuilder;
@@ -62,6 +65,7 @@ import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 import javax.servlet.http.HttpSession;
 import java.lang.reflect.Field;
+import java.security.KeyPair;
 import java.security.cert.X509Certificate;
 import java.util.HashMap;
 import java.util.Map;
@@ -76,9 +80,6 @@ public class SAML2LoginAPIAuthenticatorCmdTest {
     SAML2AuthManager samlAuthManager;
 
     @Mock
-    ConfigurationDao configDao;
-
-    @Mock
     DomainManager domainMgr;
 
     @Mock
@@ -104,6 +105,9 @@ public class SAML2LoginAPIAuthenticatorCmdTest {
         samlMessage.setID("foo");
         samlMessage.setVersion(SAMLVersion.VERSION_20);
         samlMessage.setIssueInstant(new DateTime(0));
+        Issuer issuer = new IssuerBuilder().buildObject();
+        issuer.setValue("MockedIssuer");
+        samlMessage.setIssuer(issuer);
         Status status = new StatusBuilder().buildObject();
         StatusCode statusCode = new StatusCodeBuilder().buildObject();
         statusCode.setValue(StatusCode.SUCCESS_URI);
@@ -145,32 +149,33 @@ public class SAML2LoginAPIAuthenticatorCmdTest {
         domainMgrField.setAccessible(true);
         domainMgrField.set(cmd, domainMgr);
 
-        Field configDaoField = SAML2LoginAPIAuthenticatorCmd.class.getDeclaredField("_configDao");
-        configDaoField.setAccessible(true);
-        configDaoField.set(cmd, configDao);
-
         Field userAccountDaoField = SAML2LoginAPIAuthenticatorCmd.class.getDeclaredField("_userAccountDao");
         userAccountDaoField.setAccessible(true);
         userAccountDaoField.set(cmd, userAccountDao);
 
         String spId = "someSPID";
         String url = "someUrl";
-        X509Certificate cert = SAMLUtils.generateRandomX509Certificate(SAMLUtils.generateRandomKeyPair());
-        Mockito.when(samlAuthManager.getServiceProviderId()).thenReturn(spId);
-        Mockito.when(samlAuthManager.getIdpSigningKey()).thenReturn(null);
-        Mockito.when(samlAuthManager.getIdpSingleSignOnUrl()).thenReturn(url);
-        Mockito.when(samlAuthManager.getSpSingleSignOnUrl()).thenReturn(url);
+        KeyPair kp = SAMLUtils.generateRandomKeyPair();
+        X509Certificate cert = SAMLUtils.generateRandomX509Certificate(kp);
+
+        SAMLProviderMetadata providerMetadata = new SAMLProviderMetadata();
+        providerMetadata.setEntityId("random");
+        providerMetadata.setSigningCertificate(cert);
+        providerMetadata.setEncryptionCertificate(cert);
+        providerMetadata.setKeyPair(kp);
+        providerMetadata.setSsoUrl("http://test.local");
+        providerMetadata.setSloUrl("http://test.local");
 
         Mockito.when(session.getAttribute(Mockito.anyString())).thenReturn(null);
-        Mockito.when(configDao.getValue(Mockito.anyString())).thenReturn("someString");
 
         Mockito.when(domain.getId()).thenReturn(1L);
         Mockito.when(domainMgr.getDomain(Mockito.anyString())).thenReturn(domain);
         UserAccountVO user = new UserAccountVO();
-        user.setUsername(SAMLUtils.createSAMLId("someUID"));
         user.setId(1000L);
         Mockito.when(userAccountDao.getUserAccount(Mockito.anyString(), Mockito.anyLong())).thenReturn(user);
         Mockito.when(apiServer.verifyUser(Mockito.anyLong())).thenReturn(false);
+        Mockito.when(samlAuthManager.getSPMetadata()).thenReturn(providerMetadata);
+        Mockito.when(samlAuthManager.getIdPMetadata(Mockito.anyString())).thenReturn(providerMetadata);
 
         Map<String, Object[]> params = new HashMap<String, Object[]>();
 
@@ -179,16 +184,14 @@ public class SAML2LoginAPIAuthenticatorCmdTest {
         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"});
+        params.put(SAMLPluginConstants.SAML_RESPONSE, new String[]{"Some String"});
         Mockito.stub(cmd.processSAMLResponse(Mockito.anyString())).toReturn(buildMockResponse());
         try {
             cmd.authenticate("command", params, session, "random", HttpUtils.RESPONSE_TYPE_JSON, new StringBuilder(), req, resp);
         } catch (ServerApiException ignored) {
         }
-        Mockito.verify(configDao, Mockito.atLeastOnce()).getValue(Mockito.anyString());
-        Mockito.verify(domainMgr, Mockito.times(1)).getDomain(Mockito.anyString());
-        Mockito.verify(userAccountDao, Mockito.times(1)).getUserAccount(Mockito.anyString(), Mockito.anyLong());
-        Mockito.verify(apiServer, Mockito.times(1)).verifyUser(Mockito.anyLong());
+        Mockito.verify(userAccountDao, Mockito.times(0)).getUserAccount(Mockito.anyString(), Mockito.anyLong());
+        Mockito.verify(apiServer, Mockito.times(0)).verifyUser(Mockito.anyLong());
     }
 
     @Test

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/7310c2d4/plugins/user-authenticators/saml2/test/org/apache/cloudstack/api/command/SAML2LogoutAPIAuthenticatorCmdTest.java
----------------------------------------------------------------------
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 e9834c9..eff4b29 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
@@ -22,9 +22,8 @@ package org.apache.cloudstack.api.command;
 import com.cloud.utils.HttpUtils;
 import org.apache.cloudstack.api.ApiServerService;
 import org.apache.cloudstack.api.auth.APIAuthenticationType;
-import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
 import org.apache.cloudstack.saml.SAML2AuthManager;
-import org.apache.cloudstack.utils.auth.SAMLUtils;
+import org.apache.cloudstack.saml.SAMLUtils;
 import org.junit.Assert;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -48,9 +47,6 @@ public class SAML2LogoutAPIAuthenticatorCmdTest {
     SAML2AuthManager samlAuthManager;
 
     @Mock
-    ConfigurationDao configDao;
-
-    @Mock
     HttpSession session;
 
     @Mock
@@ -71,19 +67,10 @@ public class SAML2LogoutAPIAuthenticatorCmdTest {
         managerField.setAccessible(true);
         managerField.set(cmd, samlAuthManager);
 
-        Field configDaoField = SAML2LogoutAPIAuthenticatorCmd.class.getDeclaredField("_configDao");
-        configDaoField.setAccessible(true);
-        configDaoField.set(cmd, configDao);
-
         String spId = "someSPID";
         String url = "someUrl";
         X509Certificate cert = SAMLUtils.generateRandomX509Certificate(SAMLUtils.generateRandomKeyPair());
-        Mockito.when(samlAuthManager.getServiceProviderId()).thenReturn(spId);
-        Mockito.when(samlAuthManager.getIdpSigningKey()).thenReturn(cert);
-        Mockito.when(samlAuthManager.getIdpSingleLogOutUrl()).thenReturn(url);
-        Mockito.when(samlAuthManager.getSpSingleLogOutUrl()).thenReturn(url);
         Mockito.when(session.getAttribute(Mockito.anyString())).thenReturn(null);
-        Mockito.when(configDao.getValue(Mockito.anyString())).thenReturn("someString");
 
         cmd.authenticate("command", null, session, "random", HttpUtils.RESPONSE_TYPE_JSON, new StringBuilder(), req, resp);
         Mockito.verify(resp, Mockito.times(1)).sendRedirect(Mockito.anyString());

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/7310c2d4/server/src/com/cloud/api/ApiServer.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/api/ApiServer.java b/server/src/com/cloud/api/ApiServer.java
index 6dcf48a..2ab1f7c 100755
--- a/server/src/com/cloud/api/ApiServer.java
+++ b/server/src/com/cloud/api/ApiServer.java
@@ -1062,8 +1062,8 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer
             final SecureRandom sesssionKeyRandom = new SecureRandom();
             final byte sessionKeyBytes[] = new byte[20];
             sesssionKeyRandom.nextBytes(sessionKeyBytes);
-            final String sessionKey = Base64.encodeBase64String(sessionKeyBytes);
-            session.setAttribute("sessionkey", sessionKey);
+            final String sessionKey = Base64.encodeBase64URLSafeString(sessionKeyBytes);
+            session.setAttribute(ApiConstants.SESSIONKEY, sessionKey);
 
             return createLoginResponse(session);
         }

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/7310c2d4/server/src/com/cloud/api/ApiServlet.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/api/ApiServlet.java b/server/src/com/cloud/api/ApiServlet.java
index 8d34dfe..2bffc77 100644
--- a/server/src/com/cloud/api/ApiServlet.java
+++ b/server/src/com/cloud/api/ApiServlet.java
@@ -231,7 +231,7 @@ public class ApiServlet extends HttpServlet {
                 userId = (Long)session.getAttribute("userid");
                 final String account = (String)session.getAttribute("account");
                 final Object accountObj = session.getAttribute("accountobj");
-                final String sessionKey = (String)session.getAttribute("sessionkey");
+                final String sessionKey = (String)session.getAttribute(ApiConstants.SESSIONKEY);
                 final String[] sessionKeyParam = (String[])params.get(ApiConstants.SESSIONKEY);
                 if ((sessionKeyParam == null) || (sessionKey == null) || !sessionKey.equals(sessionKeyParam[0])) {
                     try {

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/7310c2d4/server/src/com/cloud/configuration/Config.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/configuration/Config.java b/server/src/com/cloud/configuration/Config.java
index fc529b5..0d6a4e6 100755
--- a/server/src/com/cloud/configuration/Config.java
+++ b/server/src/com/cloud/configuration/Config.java
@@ -1362,78 +1362,6 @@ public enum Config {
             "300000",
             "The allowable clock difference in milliseconds between when an SSO login request is made and when it is received.",
             null),
-    SAMLIsPluginEnabled(
-            "Advanced",
-            ManagementServer.class,
-            Boolean.class,
-            "saml2.enabled",
-            "false",
-            "Set it to true to enable SAML SSO plugin",
-            null),
-    SAMLUserDomain(
-            "Advanced",
-            ManagementServer.class,
-            String.class,
-            "saml2.default.domainid",
-            "1",
-            "The default domain UUID to use when creating users from SAML SSO",
-            null),
-    SAMLCloudStackRedirectionUrl(
-            "Advanced",
-            ManagementServer.class,
-            String.class,
-            "saml2.redirect.url",
-            "http://localhost:8080/client",
-            "The CloudStack UI url the SSO should redirected to when successful",
-            null),
-    SAMLServiceProviderID(
-            "Advanced",
-            ManagementServer.class,
-            String.class,
-            "saml2.sp.id",
-            "org.apache.cloudstack",
-            "SAML2 Service Provider Identifier String",
-            null),
-    SAMLServiceProviderSingleSignOnURL(
-            "Advanced",
-            ManagementServer.class,
-            String.class,
-            "saml2.sp.sso.url",
-            "http://localhost:8080/client/api?command=samlSso",
-            "SAML2 CloudStack Service Provider Single Sign On URL",
-            null),
-    SAMLServiceProviderSingleLogOutURL(
-            "Advanced",
-            ManagementServer.class,
-            String.class,
-            "saml2.sp.slo.url",
-            "http://localhost:8080/client/api?command=samlSlo",
-            "SAML2 CloudStack Service Provider Single Log Out URL",
-            null),
-    SAMLIdentityProviderID(
-            "Advanced",
-            ManagementServer.class,
-            String.class,
-            "saml2.idp.id",
-            "https://openidp.feide.no",
-            "SAML2 Identity Provider Identifier String",
-            null),
-    SAMLIdentityProviderMetadataURL(
-            "Advanced",
-            ManagementServer.class,
-            String.class,
-            "saml2.idp.metadata.url",
-            "https://openidp.feide.no/simplesaml/saml2/idp/metadata.php",
-            "SAML2 Identity Provider Metadata XML Url",
-            null),
-    SAMLTimeout(
-            "Advanced",
-            ManagementServer.class,
-            Long.class,
-            "saml2.timeout",
-            "30000",
-            "SAML2 IDP Metadata Downloading and parsing etc. activity timeout in milliseconds",
-            null),
     //NetworkType("Hidden", ManagementServer.class, String.class, "network.type", "vlan", "The type of network that this deployment will use.", "vlan,direct"),
     RouterRamSize("Hidden", NetworkOrchestrationService.class, Integer.class, "router.ram.size", "256", "Default RAM for router VM (in MB).", null),
 

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/7310c2d4/setup/db/db/schema-451to452-cleanup.sql
----------------------------------------------------------------------
diff --git a/setup/db/db/schema-451to452-cleanup.sql b/setup/db/db/schema-451to452-cleanup.sql
new file mode 100644
index 0000000..9f5e62a
--- /dev/null
+++ b/setup/db/db/schema-451to452-cleanup.sql
@@ -0,0 +1,20 @@
+-- Licensed to the Apache Software Foundation (ASF) under one
+-- or more contributor license agreements.  See the NOTICE file
+-- distributed with this work for additional information
+-- regarding copyright ownership.  The ASF licenses this file
+-- to you 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.
+
+--;
+-- Schema cleanup from 4.5.1 to 4.5.2;
+--;

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/7310c2d4/setup/db/db/schema-451to452.sql
----------------------------------------------------------------------
diff --git a/setup/db/db/schema-451to452.sql b/setup/db/db/schema-451to452.sql
new file mode 100644
index 0000000..5c89008
--- /dev/null
+++ b/setup/db/db/schema-451to452.sql
@@ -0,0 +1,35 @@
+-- Licensed to the Apache Software Foundation (ASF) under one
+-- or more contributor license agreements.  See the NOTICE file
+-- distributed with this work for additional information
+-- regarding copyright ownership.  The ASF licenses this file
+-- to you 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.
+
+--;
+-- Schema upgrade from 4.5.1 to 4.5.2;
+--;
+
+DELETE FROM `cloud`.`configuration` WHERE name like 'saml%';
+
+ALTER TABLE `cloud`.`user` ADD COLUMN `external_entity` text DEFAULT NULL COMMENT "reference to external federation entity";
+
+DROP TABLE IF EXISTS `cloud`.`saml_token`;
+CREATE TABLE `cloud`.`saml_token` (
+  `id` bigint unsigned NOT NULL AUTO_INCREMENT,
+  `uuid` varchar(255) UNIQUE NOT NULL COMMENT 'The Authn Unique Id',
+  `domain_id` bigint unsigned DEFAULT NULL,
+  `entity` text NOT NULL COMMENT 'Identity Provider Entity Id',
+  `created` DATETIME NOT NULL,
+  PRIMARY KEY (`id`),
+  CONSTRAINT `fk_saml_token__domain_id` FOREIGN KEY(`domain_id`) REFERENCES `domain`(`id`) ON DELETE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/7310c2d4/tools/apidoc/gen_toc.py
----------------------------------------------------------------------
diff --git a/tools/apidoc/gen_toc.py b/tools/apidoc/gen_toc.py
index 95f06c8..4fac7ee 100644
--- a/tools/apidoc/gen_toc.py
+++ b/tools/apidoc/gen_toc.py
@@ -115,6 +115,9 @@ known_categories = {
     'logout': 'Authentication',
     'saml': 'Authentication',
     'getSPMetadata': 'Authentication',
+    'listIdps': 'Authentication',
+    'authorizeSamlSso': 'Authentication',
+    'listSamlAuthorization': 'Authentication',
     'Capacity': 'System Capacity',
     'NetworkDevice': 'Network Device',
     'ExternalLoadBalancer': 'Ext Load Balancer',

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/7310c2d4/ui/css/cloudstack3.css
----------------------------------------------------------------------
diff --git a/ui/css/cloudstack3.css b/ui/css/cloudstack3.css
index 0a2c57f..3539329 100644
--- a/ui/css/cloudstack3.css
+++ b/ui/css/cloudstack3.css
@@ -369,7 +369,7 @@ body.login {
 .login .select-language select {
   width: 260px;
   border: 1px solid #808080;
-  margin-top: 30px;
+  margin-top: 20px;
   /*+border-radius:4px;*/
   -moz-border-radius: 4px;
   -webkit-border-radius: 4px;
@@ -460,14 +460,12 @@ body.login {
   background: transparent url(../images/sprites.png) -563px -747px;
   cursor: pointer;
   border: none;
-  margin: 7px 120px 0 -1px;
   text-align: center;
   width: 60px;
   height: 15px;
   display: block;
   color: #FFFFFF;
   font-weight: bold;
-  float: left;
   text-indent: -1px;
   /*+text-shadow:0px 1px 2px #000000;*/
   -moz-text-shadow: 0px 1px 2px #000000;
@@ -12730,6 +12728,15 @@ div.ui-dialog div.autoscaler div.field-group div.form-container form div.form-it
   background-position: -196px -704px;
 }
 
+.configureSamlAuthorization .icon {
+  background-position: -165px -122px;
+}
+
+.configureSamlAuthorization:hover .icon {
+  background-position: -165px -704px;
+}
+
+
 .viewConsole .icon {
   background-position: -231px -2px;
 }

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/7310c2d4/ui/dictionary.jsp
----------------------------------------------------------------------
diff --git a/ui/dictionary.jsp b/ui/dictionary.jsp
index 317acfa..9d4baa2 100644
--- a/ui/dictionary.jsp
+++ b/ui/dictionary.jsp
@@ -141,6 +141,7 @@ dictionary = {
 'label.action.cancel.maintenance.mode': '<fmt:message key="label.action.cancel.maintenance.mode" />',
 'label.action.cancel.maintenance.mode.processing': '<fmt:message key="label.action.cancel.maintenance.mode.processing" />',
 'label.action.change.password': '<fmt:message key="label.action.change.password" />',
+'label.action.configure.samlauthorization': '<fmt:message key="label.action.configure.samlauthorization" />',
 'label.action.change.service': '<fmt:message key="label.action.change.service" />',
 'label.action.change.service.processing': '<fmt:message key="label.action.change.service.processing" />',
 'label.action.copy.ISO': '<fmt:message key="label.action.copy.ISO" />',
@@ -762,6 +763,9 @@ dictionary = {
 'label.login': '<fmt:message key="label.login" />',
 'label.logout': '<fmt:message key="label.logout" />',
 'label.saml.login': '<fmt:message key="label.saml.login" />',
+'label.saml.enable': '<fmt:message key="label.saml.enable" />',
+'label.saml.entity': '<fmt:message key="label.saml.entity" />',
+'label.add.LDAP.account': '<fmt:message key="label.add.LDAP.account" />',
 'label.lun': '<fmt:message key="label.lun" />',
 'label.LUN.number': '<fmt:message key="label.LUN.number" />',
 'label.make.project.owner': '<fmt:message key="label.make.project.owner" />',

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/7310c2d4/ui/index.jsp
----------------------------------------------------------------------
diff --git a/ui/index.jsp b/ui/index.jsp
index 4b601a5..0f8ef94 100644
--- a/ui/index.jsp
+++ b/ui/index.jsp
@@ -51,28 +51,44 @@
                 <form>
                     <div class="logo"></div>
                     <div class="fields">
-                        <!-- User name -->
-                        <div class="field username">
-                            <label for="username"><fmt:message key="label.username"/></label>
-                            <input type="text" name="username" class="required" />
+                        <div id="login-dropdown">
+                            <select id="login-options" style="width: 260px">
+                                <option value="cloudstack-login">Local <fmt:message key="label.login"/></option>
+                            </select>
                         </div>
-                        <!-- Password -->
-                        <div class="field password">
-                            <label for="password"><fmt:message key="label.password"/></label>
-                            <input type="password" name="password" class="required" autocomplete="off" />
+
+                        <div id="saml-login">
+                            <div class="field domain">
+                                <label for="saml-domain"><fmt:message key="label.domain"/></label>
+                                <input id="saml-domain" type="text" name="saml-domain" />
+                                <input id="saml-login-submit" type="submit" value="<fmt:message key="label.login"/>" />
+                            </div>
                         </div>
-                        <!-- Domain -->
-                        <div class="field domain">
-                            <label for="domain"><fmt:message key="label.domain"/></label>
-                            <input type="text" name="domain" />
+
+                        <div id="cloudstack-login">
+                            <!-- User name -->
+                            <div class="field username">
+                                <label for="username"><fmt:message key="label.username"/></label>
+                                <input type="text" name="username" class="required" />
+                            </div>
+                            <!-- Password -->
+                            <div class="field password">
+                                <label for="password"><fmt:message key="label.password"/></label>
+                                <input type="password" name="password" class="required" autocomplete="off" />
+                            </div>
+                            <!-- Domain -->
+                            <div class="field domain">
+                                <label for="domain"><fmt:message key="label.domain"/></label>
+                                <input type="text" name="domain" />
+                            </div>
+                            <!-- Submit (login) -->
+                            <input id="cloudstack-login-submit" type="submit" value="<fmt:message key="label.login"/>" />
                         </div>
-                        <!-- Submit (login) -->
-                        <input type="submit" value="<fmt:message key="label.login"/>" />
-                        <div id="saml-login"><input type="samlsubmit" value="<fmt:message key="label.saml.login"/>"/></div>
+
                         <!-- Select language -->
                         <div class="select-language">
                             <select name="language">
-                                <option value=""></option> <!-- when this blank option is selected, browser's default language will be used -->
+                                <option value=""></option> <!-- when this blank option is selected, default language of the browser will be used -->
                                 <option value="en"><fmt:message key="label.lang.english"/></option>
                                 <option value="ja_JP"><fmt:message key="label.lang.japanese"/></option>
                                 <option value="zh_CN"><fmt:message key="label.lang.chinese"/></option>

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/7310c2d4/ui/scripts/accounts.js
----------------------------------------------------------------------
diff --git a/ui/scripts/accounts.js b/ui/scripts/accounts.js
index bb85ebf..ad8ee61 100644
--- a/ui/scripts/accounts.js
+++ b/ui/scripts/accounts.js
@@ -1223,6 +1223,102 @@
                                 }
                             },
 
+                            configureSamlAuthorization: {
+                                label: 'label.action.configure.samlauthorization',
+                                messages: {
+                                    notification: function(args) {
+                                        return 'label.action.configure.samlauthorization';
+                                    }
+                                },
+                                action: {
+                                    custom: function(args) {
+                                        var start = args.start;
+                                        var complete = args.complete;
+                                        var context = args.context;
+
+                                        if (g_idpList) {
+                                            $.ajax({
+                                                url: createURL('listSamlAuthorization'),
+                                                data: {
+                                                    userid: context.users[0].id,
+                                                },
+                                                success: function(json) {
+                                                    var authorization = json.listsamlauthorizationsresponse.samlauthorization[0];
+                                                    cloudStack.dialog.createForm({
+                                                        form: {
+                                                            title: 'label.action.configure.samlauthorization',
+                                                            fields: {
+                                                                samlEnable: {
+                                                                    label: 'label.saml.enable',
+                                                                    docID: 'helpSamlEnable',
+                                                                    isBoolean: true,
+                                                                    isChecked: authorization.status,
+                                                                    validation: {
+                                                                        required: false
+                                                                    }
+                                                                },
+                                                                samlEntity: {
+                                                                    label: 'label.saml.entity',
+                                                                    docID: 'helpSamlEntity',
+                                                                    validation: {
+                                                                        required: false
+                                                                    },
+                                                                    select: function(args) {
+                                                                        var items = [];
+                                                                        $(g_idpList).each(function() {
+                                                                            items.push({
+                                                                                id: this.id,
+                                                                                description: this.orgName
+                                                                            });
+                                                                        });
+                                                                        args.response.success({
+                                                                            data: items
+                                                                        });
+                                                                        args.$select.change(function() {
+                                                                            $('select[name="samlEntity"] option[value="' + authorization.idpid  + '"]').attr("selected", "selected");
+                                                                        });
+                                                                    }
+                                                                }
+                                                            }
+                                                        },
+                                                        after: function(args) {
+                                                            start();
+                                                            var enableSaml = false;
+                                                            var idpId = '';
+                                                            if (args.data.hasOwnProperty('samlEnable')) {
+                                                                enableSaml = (args.data.samlEnable === 'on');
+                                                            }
+                                                            if (args.data.hasOwnProperty('samlEntity')) {
+                                                                idpId = args.data.samlEntity;
+                                                            }
+                                                            $.ajax({
+                                                                url: createURL('authorizeSamlSso'),
+                                                                data: {
+                                                                    userid: context.users[0].id,
+                                                                    enable: enableSaml,
+                                                                    entityid: idpId
+                                                                },
+                                                                type: "POST",
+                                                                success: function(json) {
+                                                                    complete();
+                                                                },
+                                                                error: function(json) {
+                                                                    complete({ error: parseXMLHttpResponse(json) });
+                                                                }
+                                                            });
+                                                        }
+                                                    });
+                                                },
+                                                error: function(json) {
+                                                    complete({ error: parseXMLHttpResponse(json) });
+                                                }
+                                            });
+
+                                        }
+                                    }
+                                }
+                            },
+
                             generateKeys: {
                                 label: 'label.action.generate.keys',
                                 messages: {
@@ -1520,6 +1616,9 @@
             allowedActions.push("edit");
             allowedActions.push("changePassword");
             allowedActions.push("generateKeys");
+            if (g_idpList) {
+                allowedActions.push("configureSamlAuthorization");
+            }
             if (!(jsonObj.domain == "ROOT" && jsonObj.account == "admin" && jsonObj.accounttype == 1)) { //if not system-generated default admin account user
                 if (jsonObj.state == "enabled")
                     allowedActions.push("disable");
@@ -1541,6 +1640,9 @@
                 
                 allowedActions.push("changePassword");
                 allowedActions.push("generateKeys");
+                if (g_idpList) {
+                    allowedActions.push("configureSamlAuthorization");
+                }
         	}        	
         }
         return allowedActions;

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/7310c2d4/ui/scripts/accountsWizard.js
----------------------------------------------------------------------
diff --git a/ui/scripts/accountsWizard.js b/ui/scripts/accountsWizard.js
index 82e7eab..7ea5eaa 100644
--- a/ui/scripts/accountsWizard.js
+++ b/ui/scripts/accountsWizard.js
@@ -162,8 +162,34 @@
                 validation: {
                     required: false
                 }
+            },
+            samlEnable: {
+                label: 'label.saml.enable',
+                docID: 'helpSamlEnable',
+                isBoolean: true,
+                validation: {
+                    required: false
+                }
+            },
+            samlEntity: {
+                label: 'label.saml.entity',
+                docID: 'helpSamlEntity',
+                validation: {
+                    required: false
+                },
+                select: function(args) {
+                    var items = [];
+                    $(g_idpList).each(function() {
+                        items.push({
+                            id: this.id,
+                            description: this.orgName
+                        });
+                    });
+                    args.response.success({
+                        data: items
+                    });
+                }
             }
-
         },
 
         action: function(args) {
@@ -218,6 +244,18 @@
                 array1.push("&group=" + args.groupname);
             }
 
+            var authorizeUsersForSamlSSO = function (users, entity) {
+                for (var i = 0; i < users.length; i++) {
+                    $.ajax({
+                        url: createURL('authorizeSamlSso&enable=true&userid=' + users[i].id + "&entityid=" + entity),
+                        error: function(XMLHttpResponse) {
+                            args.response.error(parseXMLHttpResponse(XMLHttpResponse));
+                        }
+                    });
+                }
+                return;
+            };
+
             if (ldapStatus) {
                 if (args.groupname) {
                     $.ajax({
@@ -225,6 +263,13 @@
                         dataType: "json",
                         type: "POST",
                         async: false,
+                        success: function (json) {
+                            if (json.ldapuserresponse && args.data.samlEnable && args.data.samlEnable === 'on') {
+                                cloudStack.dialog.notice({
+                                    message: "Unable to find users IDs to enable SAML Single Sign On, kindly enable it manually."
+                                });
+                            }
+                        },
                         error: function(XMLHttpResponse) {
                             args.response.error(parseXMLHttpResponse(XMLHttpResponse));
                         }
@@ -235,6 +280,14 @@
                         dataType: "json",
                         type: "POST",
                         async: false,
+                        success: function(json) {
+                            if (args.data.samlEnable && args.data.samlEnable === 'on') {
+                                var users = json.createaccountresponse.account.user;
+                                var entity = args.data.samlEntity;
+                                if (users && entity)
+                                    authorizeUsersForSamlSSO(users, entity);
+                            }
+                        },
                         error: function(XMLHttpResponse) {
                             args.response.error(parseXMLHttpResponse(XMLHttpResponse));
                         }
@@ -246,6 +299,14 @@
                     dataType: "json",
                     type: "POST",
                     async: false,
+                    success: function(json) {
+                        if (args.data.samlEnable && args.data.samlEnable === 'on') {
+                            var users = json.createaccountresponse.account.user;
+                            var entity = args.data.samlEntity;
+                            if (users && entity)
+                                authorizeUsersForSamlSSO(users, entity);
+                        }
+                    },
                     error: function(XMLHttpResponse) {
                         args.response.error(parseXMLHttpResponse(XMLHttpResponse));
                     }

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/7310c2d4/ui/scripts/cloudStack.js
----------------------------------------------------------------------
diff --git a/ui/scripts/cloudStack.js b/ui/scripts/cloudStack.js
index 479b162..a701c41 100644
--- a/ui/scripts/cloudStack.js
+++ b/ui/scripts/cloudStack.js
@@ -115,7 +115,7 @@
                             cookieValue = cookieValue.slice(1, cookieValue.length-1);
                             $.cookie(cookieName, cookieValue, { expires: 1 });
                         }
-                        return cookieValue;
+                        return decodeURIComponent(cookieValue);
                     };
                     unBoxCookieValue('sessionkey');
                     // if sessionkey cookie exists use this to set g_sessionKey
@@ -353,6 +353,17 @@
             },
 
             samlLoginAction: function(args) {
+                g_sessionKey = null;
+                g_username = null;
+                g_account = null;
+                g_domainid = null;
+                g_timezoneoffset = null;
+                g_timezone = null;
+                g_supportELB = null;
+                g_kvmsnapshotenabled = null;
+                g_regionsecondaryenabled = null;
+                g_loginCmdText = null;
+
                 $.cookie('JSESSIONID', null);
                 $.cookie('sessionkey', null);
                 $.cookie('username', null);
@@ -360,7 +371,14 @@
                 $.cookie('domainid', null);
                 $.cookie('role', null);
                 $.cookie('timezone', null);
-                window.location.href = createURL('samlSso');
+                var url = 'samlSso';
+                if (args.data.idpid) {
+                    url = url + '&idpid=' + args.data.idpid;
+                }
+                if (args.data.domain) {
+                    url = url + '&domain=' + args.data.domain;
+                }
+                window.location.href = createURL(url);
             },
 
             // Show cloudStack main UI widget

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/7310c2d4/ui/scripts/docs.js
----------------------------------------------------------------------
diff --git a/ui/scripts/docs.js b/ui/scripts/docs.js
index d38bcf7..ed94ccc 100755
--- a/ui/scripts/docs.js
+++ b/ui/scripts/docs.js
@@ -1247,6 +1247,14 @@ cloudStack.docs = {
         desc: 'The group name from which you want to import LDAP users',
         externalLink: ''
     },
+    helpSamlEnable: {
+        desc: 'Enable SAML Single Sign On for the user(s)',
+        externalLink: ''
+    },
+    helpSamlEntity: {
+        desc: 'Choose the SAML Identity Provider Entity ID with which you want to enable the Single Sign On for the user(s)',
+        externalLink: ''
+    },
     helpVpcOfferingName: {
         desc: 'Any desired name for the VPC offering',
         externalLink: ''

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/7310c2d4/ui/scripts/sharedFunctions.js
----------------------------------------------------------------------
diff --git a/ui/scripts/sharedFunctions.js b/ui/scripts/sharedFunctions.js
index 1e1514b..75860dc 100644
--- a/ui/scripts/sharedFunctions.js
+++ b/ui/scripts/sharedFunctions.js
@@ -32,6 +32,7 @@ var g_regionsecondaryenabled = null;
 var g_userPublicTemplateEnabled = "true";
 var g_cloudstackversion = null;
 var g_queryAsyncJobResultInterval = 3000;
+var g_idpList = null;
 
 //keyboard keycode
 var keycode_Enter = 13;

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/7310c2d4/ui/scripts/ui-custom/accountsWizard.js
----------------------------------------------------------------------
diff --git a/ui/scripts/ui-custom/accountsWizard.js b/ui/scripts/ui-custom/accountsWizard.js
index 3259227..cfbe930 100644
--- a/ui/scripts/ui-custom/accountsWizard.js
+++ b/ui/scripts/ui-custom/accountsWizard.js
@@ -271,6 +271,11 @@
                     delete args.informationNotInLdap.ldapGroupName;
                 }
 
+                if (g_idpList == null) {
+                    delete args.informationNotInLdap.samlEnable;
+                    delete args.informationNotInLdap.samlEntity;
+                }
+
                 var informationNotInLdap = cloudStack.dialog.createForm({
                     context: context,
                     noDialog: true,

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/7310c2d4/ui/scripts/ui-custom/login.js
----------------------------------------------------------------------
diff --git a/ui/scripts/ui-custom/login.js b/ui/scripts/ui-custom/login.js
index 1f82c82..6d7d3fe 100644
--- a/ui/scripts/ui-custom/login.js
+++ b/ui/scripts/ui-custom/login.js
@@ -94,7 +94,7 @@
         $inputs.filter(':first').addClass('first-input').focus();
 
         // Login action
-        $login.find('input[type=submit]').click(function() {
+        $login.find('#cloudstack-login-submit').click(function() {
             if (!$form.valid()) return false;
 
             var data = cloudStack.serializeForm($form);
@@ -121,27 +121,85 @@
         });
 
         // SAML Login action
-        $login.find('input[type=samlsubmit]').click(function() {
+        $login.find('#saml-login-submit').click(function() {
             args.samlLoginAction({
+                data: {'idpid': $login.find('#login-options').find(':selected').val(),
+                       'domain': $login.find('#saml-domain').val()}
             });
+            return false;
         });
 
         // Show SAML button if only SP is configured
-        $login.find("#saml-login").hide();
+        $login.find('#login-dropdown').hide();
+        $login.find('#saml-login').hide();
+        $login.find('#cloudstack-login').hide();
+
+        var toggleLoginView = function (selectedOption) {
+            if (selectedOption === '') {
+                    $login.find('#saml-login').hide();
+                    $login.find('#cloudstack-login').hide();
+            } else if (selectedOption === 'cloudstack-login') {
+                    $login.find('#saml-login').hide();
+                    $login.find('#cloudstack-login').show();
+            } else {
+                    $login.find('#saml-login').show();
+                    $login.find('#cloudstack-login').hide();
+            }
+        };
+
+        $login.find('#login-options').change(function() {
+            var selectedOption = $login.find('#login-options').find(':selected').val();
+            toggleLoginView(selectedOption);
+            if (selectedOption && selectedOption !== '') {
+                $.cookie('login-option', selectedOption);
+            }
+        });
+
         $.ajax({
-            type: "GET",
-            url: createURL("getSPMetadata"),
-            dataType: "json",
+            type: 'GET',
+            url: createURL('listIdps'),
+            dataType: 'json',
             async: false,
             success: function(data, textStatus, xhr) {
                 if (xhr.status === 200) {
-                    $login.find('#saml-login').show();
+                    $login.find('#login-dropdown').show();
                 } else {
-                    $login.find('#saml-login').hide();
+                    $login.find('#cloudstack-login').show();
+                    return;
+                }
+
+                $login.find('#login-options')
+                    .append($('<option>', {
+                        value: '',
+                        text: '--- Select Identity Provider -- ',
+                        selected: true
+                    }));
+
+                if (data.listidpsresponse && data.listidpsresponse.idp) {
+                    var idpList = data.listidpsresponse.idp.sort(function (a, b) {
+                        return a.orgName.localeCompare(b.orgName);
+                    });
+                    g_idpList = idpList;
+                    $.each(idpList, function(index, idp) {
+                        $login.find('#login-options')
+                            .append($('<option>', {
+                                value: idp.id,
+                                text: idp.orgName
+                            }));
+                    });
+                }
+
+                var loginOption = $.cookie('login-option');
+                if (loginOption) {
+                    var option = $login.find('#login-options option[value="' + loginOption + '"]');
+                    if (option.length > 0) {
+                        option.prop('selected', true);
+                        toggleLoginView(loginOption);
+                    }
                 }
             },
             error: function(xhr) {
-                $login.find('#saml-login').hide();
+                $login.find('#cloudstack-login').show();
             },
         });
 

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/7310c2d4/utils/src/org/apache/cloudstack/utils/auth/SAMLUtils.java
----------------------------------------------------------------------
diff --git a/utils/src/org/apache/cloudstack/utils/auth/SAMLUtils.java b/utils/src/org/apache/cloudstack/utils/auth/SAMLUtils.java
deleted file mode 100644
index a6d2d34..0000000
--- a/utils/src/org/apache/cloudstack/utils/auth/SAMLUtils.java
+++ /dev/null
@@ -1,330 +0,0 @@
-//
-// Licensed to the Apache Software Foundation (ASF) under one
-// or more contributor license agreements.  See the NOTICE file
-// distributed with this work for additional information
-// regarding copyright ownership.  The ASF licenses this file
-// to you 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 org.apache.cloudstack.utils.auth;
-
-import com.cloud.utils.HttpUtils;
-import org.apache.commons.codec.digest.DigestUtils;
-import org.apache.log4j.Logger;
-import org.bouncycastle.jce.provider.BouncyCastleProvider;
-import org.bouncycastle.x509.X509V1CertificateGenerator;
-import org.joda.time.DateTime;
-import org.joda.time.DateTimeZone;
-import org.opensaml.Configuration;
-import org.opensaml.common.SAMLVersion;
-import org.opensaml.common.xml.SAMLConstants;
-import org.opensaml.saml2.core.AuthnContextClassRef;
-import org.opensaml.saml2.core.AuthnContextComparisonTypeEnumeration;
-import org.opensaml.saml2.core.AuthnRequest;
-import org.opensaml.saml2.core.Issuer;
-import org.opensaml.saml2.core.LogoutRequest;
-import org.opensaml.saml2.core.NameID;
-import org.opensaml.saml2.core.NameIDPolicy;
-import org.opensaml.saml2.core.NameIDType;
-import org.opensaml.saml2.core.RequestedAuthnContext;
-import org.opensaml.saml2.core.Response;
-import org.opensaml.saml2.core.SessionIndex;
-import org.opensaml.saml2.core.impl.AuthnContextClassRefBuilder;
-import org.opensaml.saml2.core.impl.AuthnRequestBuilder;
-import org.opensaml.saml2.core.impl.IssuerBuilder;
-import org.opensaml.saml2.core.impl.LogoutRequestBuilder;
-import org.opensaml.saml2.core.impl.NameIDBuilder;
-import org.opensaml.saml2.core.impl.NameIDPolicyBuilder;
-import org.opensaml.saml2.core.impl.RequestedAuthnContextBuilder;
-import org.opensaml.saml2.core.impl.SessionIndexBuilder;
-import org.opensaml.xml.ConfigurationException;
-import org.opensaml.xml.XMLObject;
-import org.opensaml.xml.io.Marshaller;
-import org.opensaml.xml.io.MarshallingException;
-import org.opensaml.xml.io.Unmarshaller;
-import org.opensaml.xml.io.UnmarshallerFactory;
-import org.opensaml.xml.io.UnmarshallingException;
-import org.opensaml.xml.signature.SignatureConstants;
-import org.opensaml.xml.util.Base64;
-import org.opensaml.xml.util.XMLHelper;
-import org.w3c.dom.Document;
-import org.w3c.dom.Element;
-import org.xml.sax.SAXException;
-
-import javax.security.auth.x500.X500Principal;
-import javax.xml.parsers.DocumentBuilder;
-import javax.xml.parsers.DocumentBuilderFactory;
-import javax.xml.parsers.ParserConfigurationException;
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.StringWriter;
-import java.io.UnsupportedEncodingException;
-import java.math.BigInteger;
-import java.net.URLEncoder;
-import java.security.InvalidKeyException;
-import java.security.KeyFactory;
-import java.security.KeyPair;
-import java.security.KeyPairGenerator;
-import java.security.NoSuchAlgorithmException;
-import java.security.NoSuchProviderException;
-import java.security.PrivateKey;
-import java.security.PublicKey;
-import java.security.SecureRandom;
-import java.security.Security;
-import java.security.Signature;
-import java.security.SignatureException;
-import java.security.cert.CertificateEncodingException;
-import java.security.cert.X509Certificate;
-import java.security.spec.InvalidKeySpecException;
-import java.security.spec.PKCS8EncodedKeySpec;
-import java.security.spec.X509EncodedKeySpec;
-import java.util.zip.Deflater;
-import java.util.zip.DeflaterOutputStream;
-
-public class SAMLUtils {
-    public static final Logger s_logger = Logger.getLogger(SAMLUtils.class);
-
-    public static final String SAML_RESPONSE = "SAMLResponse";
-    public static final String SAML_NS = "SAML-";
-    public static final String SAML_NAMEID = "SAML_NAMEID";
-    public static final String SAML_SESSION = "SAML_SESSION";
-    public static final String SAMLSP_KEYPAIR = "SAMLSP_KEYPAIR";
-    public static final String SAMLSP_X509CERT = "SAMLSP_X509CERT";
-
-    public static String createSAMLId(String uid) {
-        if (uid == null)  {
-            return null;
-        }
-        String hash = DigestUtils.sha256Hex(uid);
-        String samlUuid = SAML_NS + hash;
-        return samlUuid.substring(0, 40);
-    }
-
-    public static boolean checkSAMLUser(String uuid, String username) {
-        if (uuid == null || uuid.isEmpty() || username == null || username.isEmpty()) {
-            return false;
-        }
-        return uuid.startsWith(SAML_NS) && createSAMLId(username).equals(uuid);
-    }
-
-    public static String generateSecureRandomId() {
-        return new BigInteger(160, new SecureRandom()).toString(32);
-    }
-
-    public static AuthnRequest buildAuthnRequestObject(String spId, String idpUrl, String consumerUrl) {
-        String authnId = generateSecureRandomId();
-        // Issuer object
-        IssuerBuilder issuerBuilder = new IssuerBuilder();
-        Issuer issuer = issuerBuilder.buildObject();
-        issuer.setValue(spId);
-
-        // NameIDPolicy
-        NameIDPolicyBuilder nameIdPolicyBuilder = new NameIDPolicyBuilder();
-        NameIDPolicy nameIdPolicy = nameIdPolicyBuilder.buildObject();
-        nameIdPolicy.setFormat(NameIDType.PERSISTENT);
-        nameIdPolicy.setSPNameQualifier(spId);
-        nameIdPolicy.setAllowCreate(true);
-
-        // AuthnContextClass
-        AuthnContextClassRefBuilder authnContextClassRefBuilder = new AuthnContextClassRefBuilder();
-        AuthnContextClassRef authnContextClassRef = authnContextClassRefBuilder.buildObject(
-                SAMLConstants.SAML20_NS,
-                "AuthnContextClassRef", "saml");
-        authnContextClassRef.setAuthnContextClassRef("urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport");
-
-        // AuthnContex
-        RequestedAuthnContextBuilder requestedAuthnContextBuilder = new RequestedAuthnContextBuilder();
-        RequestedAuthnContext requestedAuthnContext = requestedAuthnContextBuilder.buildObject();
-        requestedAuthnContext
-                .setComparison(AuthnContextComparisonTypeEnumeration.EXACT);
-        requestedAuthnContext.getAuthnContextClassRefs().add(
-                authnContextClassRef);
-
-        // Creation of AuthRequestObject
-        AuthnRequestBuilder authRequestBuilder = new AuthnRequestBuilder();
-        AuthnRequest authnRequest = authRequestBuilder.buildObject();
-        authnRequest.setID(authnId);
-        authnRequest.setDestination(idpUrl);
-        authnRequest.setVersion(SAMLVersion.VERSION_20);
-        authnRequest.setForceAuthn(false);
-        authnRequest.setIsPassive(false);
-        authnRequest.setIssuer(issuer);
-        authnRequest.setIssueInstant(new DateTime());
-        authnRequest.setProtocolBinding(SAMLConstants.SAML2_REDIRECT_BINDING_URI);
-        authnRequest.setAssertionConsumerServiceURL(consumerUrl);
-        authnRequest.setProviderName(spId);
-        authnRequest.setNameIDPolicy(nameIdPolicy);
-        authnRequest.setRequestedAuthnContext(requestedAuthnContext);
-
-        return authnRequest;
-    }
-
-    public static LogoutRequest buildLogoutRequest(String logoutUrl, String spId, NameID sessionNameId, String sessionIndex) {
-        IssuerBuilder issuerBuilder = new IssuerBuilder();
-        Issuer issuer = issuerBuilder.buildObject();
-        issuer.setValue(spId);
-
-        SessionIndex sessionIndexElement = new SessionIndexBuilder().buildObject();
-        sessionIndexElement.setSessionIndex(sessionIndex);
-
-        NameID nameID = new NameIDBuilder().buildObject();
-        nameID.setValue(sessionNameId.getValue());
-        nameID.setFormat(sessionNameId.getFormat());
-
-        LogoutRequest logoutRequest = new LogoutRequestBuilder().buildObject();
-        logoutRequest.setID(generateSecureRandomId());
-        logoutRequest.setDestination(logoutUrl);
-        logoutRequest.setVersion(SAMLVersion.VERSION_20);
-        logoutRequest.setIssueInstant(new DateTime());
-        logoutRequest.setIssuer(issuer);
-        logoutRequest.getSessionIndexes().add(sessionIndexElement);
-        logoutRequest.setNameID(nameID);
-        return logoutRequest;
-    }
-
-    public static String encodeSAMLRequest(XMLObject authnRequest)
-            throws MarshallingException, IOException {
-        Marshaller marshaller = Configuration.getMarshallerFactory()
-                .getMarshaller(authnRequest);
-        Element authDOM = marshaller.marshall(authnRequest);
-        StringWriter requestWriter = new StringWriter();
-        XMLHelper.writeNode(authDOM, requestWriter);
-        String requestMessage = requestWriter.toString();
-        Deflater deflater = new Deflater(Deflater.DEFLATED, true);
-        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
-        DeflaterOutputStream deflaterOutputStream = new DeflaterOutputStream(byteArrayOutputStream, deflater);
-        deflaterOutputStream.write(requestMessage.getBytes());
-        deflaterOutputStream.close();
-        String encodedRequestMessage = Base64.encodeBytes(byteArrayOutputStream.toByteArray(), Base64.DONT_BREAK_LINES);
-        encodedRequestMessage = URLEncoder.encode(encodedRequestMessage, HttpUtils.UTF_8).trim();
-        return encodedRequestMessage;
-    }
-
-    public static Response decodeSAMLResponse(String responseMessage)
-            throws ConfigurationException, ParserConfigurationException,
-            SAXException, IOException, UnmarshallingException {
-        DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
-        documentBuilderFactory.setNamespaceAware(true);
-        DocumentBuilder docBuilder = documentBuilderFactory.newDocumentBuilder();
-        byte[] base64DecodedResponse = Base64.decode(responseMessage);
-        Document document = docBuilder.parse(new ByteArrayInputStream(base64DecodedResponse));
-        Element element = document.getDocumentElement();
-        UnmarshallerFactory unmarshallerFactory = Configuration.getUnmarshallerFactory();
-        Unmarshaller unmarshaller = unmarshallerFactory.getUnmarshaller(element);
-        return (Response) unmarshaller.unmarshall(element);
-    }
-
-    public static String generateSAMLRequestSignature(String urlEncodedString, PrivateKey signingKey)
-            throws NoSuchAlgorithmException, SignatureException, InvalidKeyException, UnsupportedEncodingException {
-        if (signingKey == null) {
-            return urlEncodedString;
-        }
-        String url = urlEncodedString + "&SigAlg=" + URLEncoder.encode(SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA1, HttpUtils.UTF_8);
-        Signature signature = Signature.getInstance("SHA1withRSA");
-        signature.initSign(signingKey);
-        signature.update(url.getBytes());
-        String signatureString = Base64.encodeBytes(signature.sign(), Base64.DONT_BREAK_LINES);
-        if (signatureString != null) {
-            return url + "&Signature=" + URLEncoder.encode(signatureString, HttpUtils.UTF_8);
-        }
-        return url;
-    }
-
-    public static KeyFactory getKeyFactory() {
-        KeyFactory keyFactory = null;
-        try {
-            Security.addProvider(new BouncyCastleProvider());
-            keyFactory = KeyFactory.getInstance("RSA", "BC");
-        } catch (NoSuchAlgorithmException | NoSuchProviderException e) {
-            s_logger.error("Unable to create KeyFactory:" + e.getMessage());
-        }
-        return keyFactory;
-    }
-
-    public static String savePublicKey(PublicKey key) {
-        try {
-            KeyFactory keyFactory = SAMLUtils.getKeyFactory();
-            if (keyFactory == null) return null;
-            X509EncodedKeySpec spec = keyFactory.getKeySpec(key, X509EncodedKeySpec.class);
-            return new String(org.bouncycastle.util.encoders.Base64.encode(spec.getEncoded()));
-        } catch (InvalidKeySpecException e) {
-            s_logger.error("Unable to create KeyFactory:" + e.getMessage());
-        }
-        return null;
-    }
-
-    public static String savePrivateKey(PrivateKey key) {
-        try {
-            KeyFactory keyFactory = SAMLUtils.getKeyFactory();
-            if (keyFactory == null) return null;
-            PKCS8EncodedKeySpec spec = keyFactory.getKeySpec(key,
-                    PKCS8EncodedKeySpec.class);
-            return new String(org.bouncycastle.util.encoders.Base64.encode(spec.getEncoded()));
-        } catch (InvalidKeySpecException e) {
-            s_logger.error("Unable to create KeyFactory:" + e.getMessage());
-        }
-        return null;
-    }
-
-    public static PublicKey loadPublicKey(String publicKey) {
-        byte[] sigBytes = org.bouncycastle.util.encoders.Base64.decode(publicKey);
-        X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(sigBytes);
-        KeyFactory keyFact = SAMLUtils.getKeyFactory();
-        if (keyFact == null)
-            return null;
-        try {
-            return keyFact.generatePublic(x509KeySpec);
-        } catch (InvalidKeySpecException e) {
-            s_logger.error("Unable to create PrivateKey from privateKey string:" + e.getMessage());
-        }
-        return null;
-    }
-
-    public static PrivateKey loadPrivateKey(String privateKey) {
-        byte[] sigBytes = org.bouncycastle.util.encoders.Base64.decode(privateKey);
-        PKCS8EncodedKeySpec pkscs8KeySpec = new PKCS8EncodedKeySpec(sigBytes);
-        KeyFactory keyFact = SAMLUtils.getKeyFactory();
-        if (keyFact == null)
-            return null;
-        try {
-            return keyFact.generatePrivate(pkscs8KeySpec);
-        } catch (InvalidKeySpecException e) {
-            s_logger.error("Unable to create PrivateKey from privateKey string:" + e.getMessage());
-        }
-        return null;
-    }
-
-    public static KeyPair generateRandomKeyPair() throws NoSuchProviderException, NoSuchAlgorithmException {
-        Security.addProvider(new BouncyCastleProvider());
-        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA", "BC");
-        keyPairGenerator.initialize(4096, new SecureRandom());
-        return keyPairGenerator.generateKeyPair();
-    }
-
-    public static X509Certificate generateRandomX509Certificate(KeyPair keyPair) throws NoSuchAlgorithmException, NoSuchProviderException, CertificateEncodingException, SignatureException, InvalidKeyException {
-        DateTime now = DateTime.now(DateTimeZone.UTC);
-        X500Principal dnName = new X500Principal("CN=ApacheCloudStack");
-        X509V1CertificateGenerator certGen = new X509V1CertificateGenerator();
-        certGen.setSerialNumber(BigInteger.valueOf(System.currentTimeMillis()));
-        certGen.setSubjectDN(dnName);
-        certGen.setIssuerDN(dnName);
-        certGen.setNotBefore(now.minusDays(1).toDate());
-        certGen.setNotAfter(now.plusYears(3).toDate());
-        certGen.setPublicKey(keyPair.getPublic());
-        certGen.setSignatureAlgorithm("SHA256WithRSAEncryption");
-        return certGen.generate(keyPair.getPrivate(), "BC");
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/7310c2d4/utils/test/org/apache/cloudstack/utils/auth/SAMLUtilsTest.java
----------------------------------------------------------------------
diff --git a/utils/test/org/apache/cloudstack/utils/auth/SAMLUtilsTest.java b/utils/test/org/apache/cloudstack/utils/auth/SAMLUtilsTest.java
deleted file mode 100644
index bebfd13..0000000
--- a/utils/test/org/apache/cloudstack/utils/auth/SAMLUtilsTest.java
+++ /dev/null
@@ -1,91 +0,0 @@
-//
-// Licensed to the Apache Software Foundation (ASF) under one
-// or more contributor license agreements.  See the NOTICE file
-// distributed with this work for additional information
-// regarding copyright ownership.  The ASF licenses this file
-// to you 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 org.apache.cloudstack.utils.auth;
-
-import junit.framework.TestCase;
-import org.junit.Test;
-import org.opensaml.saml2.core.AuthnRequest;
-import org.opensaml.saml2.core.LogoutRequest;
-import org.opensaml.saml2.core.NameID;
-import org.opensaml.saml2.core.impl.NameIDBuilder;
-
-import java.security.KeyPair;
-import java.security.PrivateKey;
-import java.security.PublicKey;
-
-public class SAMLUtilsTest extends TestCase {
-
-    @Test
-    public void testSAMLId() throws Exception {
-        assertEquals(SAMLUtils.createSAMLId(null), null);
-        assertEquals(SAMLUtils.createSAMLId("someUserName"), "SAML-305e19dd2581f33fd90b3949298ec8b17de");
-
-        assertTrue(SAMLUtils.checkSAMLUser(SAMLUtils.createSAMLId("someUserName"), "someUserName"));
-        assertFalse(SAMLUtils.checkSAMLUser(SAMLUtils.createSAMLId("someUserName"), "someOtherUserName"));
-        assertFalse(SAMLUtils.checkSAMLUser(SAMLUtils.createSAMLId(null), "someOtherUserName"));
-        assertFalse(SAMLUtils.checkSAMLUser("randomUID", "randomUID"));
-        assertFalse(SAMLUtils.checkSAMLUser(null, null));
-    }
-
-    @Test
-    public void testGenerateSecureRandomId() throws Exception {
-        assertTrue(SAMLUtils.generateSecureRandomId().length() > 0);
-    }
-
-    @Test
-    public void testBuildAuthnRequestObject() throws Exception {
-        String consumerUrl = "http://someurl.com";
-        String idpUrl = "http://idp.domain.example";
-        String spId = "cloudstack";
-        AuthnRequest req = SAMLUtils.buildAuthnRequestObject(spId, idpUrl, consumerUrl);
-        assertEquals(req.getAssertionConsumerServiceURL(), consumerUrl);
-        assertEquals(req.getDestination(), idpUrl);
-        assertEquals(req.getIssuer().getValue(), spId);
-    }
-
-    @Test
-    public void testBuildLogoutRequest() throws Exception {
-        String logoutUrl = "http://logoutUrl";
-        String spId = "cloudstack";
-        String sessionIndex = "12345";
-        String nameIdString = "someNameID";
-        NameID sessionNameId = new NameIDBuilder().buildObject();
-        sessionNameId.setValue(nameIdString);
-        LogoutRequest req = SAMLUtils.buildLogoutRequest(logoutUrl, spId, sessionNameId,  sessionIndex);
-        assertEquals(req.getDestination(), logoutUrl);
-        assertEquals(req.getIssuer().getValue(), spId);
-        assertEquals(req.getNameID().getValue(), nameIdString);
-        assertEquals(req.getSessionIndexes().get(0).getSessionIndex(), sessionIndex);
-    }
-
-    @Test
-    public void testX509Helpers() throws Exception {
-        KeyPair keyPair = SAMLUtils.generateRandomKeyPair();
-
-        String privateKeyString = SAMLUtils.savePrivateKey(keyPair.getPrivate());
-        String publicKeyString = SAMLUtils.savePublicKey(keyPair.getPublic());
-
-        PrivateKey privateKey = SAMLUtils.loadPrivateKey(privateKeyString);
-        PublicKey publicKey = SAMLUtils.loadPublicKey(publicKeyString);
-
-        assertTrue(privateKey.equals(keyPair.getPrivate()));
-        assertTrue(publicKey.equals(keyPair.getPublic()));
-    }
-}
\ No newline at end of file


Mime
View raw message