You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cloudstack.apache.org by bh...@apache.org on 2015/06/29 12:59:35 UTC

[1/5] git commit: updated refs/heads/master to 3ff92e8

Repository: cloudstack
Updated Branches:
  refs/heads/master 527d6ee77 -> 3ff92e883


http://git-wip-us.apache.org/repos/asf/cloudstack/blob/107595a6/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 e53e701..0000000
--- a/plugins/user-authenticators/saml2/test/org/apache/cloudstack/api/command/GetServiceProviderMetaDataCmdTest.java
+++ /dev/null
@@ -1,100 +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;
-import java.net.InetAddress;
-import java.net.UnknownHostException;
-
-@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, UnknownHostException {
-        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, 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();
-        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);
-    }
-}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/107595a6/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 8fbed41..6960b3b 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;
@@ -77,9 +81,6 @@ public class SAML2LoginAPIAuthenticatorCmdTest {
     SAML2AuthManager samlAuthManager;
 
     @Mock
-    ConfigurationDao configDao;
-
-    @Mock
     DomainManager domainMgr;
 
     @Mock
@@ -105,6 +106,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);
@@ -146,32 +150,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[]>();
 
@@ -180,16 +185,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, 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());
-        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/107595a6/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 4388b88..cbfcc55 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;
@@ -49,9 +48,6 @@ public class SAML2LogoutAPIAuthenticatorCmdTest {
     SAML2AuthManager samlAuthManager;
 
     @Mock
-    ConfigurationDao configDao;
-
-    @Mock
     HttpSession session;
 
     @Mock
@@ -72,19 +68,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, InetAddress.getByName("127.0.0.1"), 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/107595a6/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 cf719c0..4da8b1e 100644
--- 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/107595a6/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 3b3a0be..2a2844e 100644
--- a/server/src/com/cloud/api/ApiServlet.java
+++ b/server/src/com/cloud/api/ApiServlet.java
@@ -238,7 +238,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/107595a6/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 2352313..ca89881 100644
--- a/server/src/com/cloud/configuration/Config.java
+++ b/server/src/com/cloud/configuration/Config.java
@@ -1385,78 +1385,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/107595a6/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/107595a6/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/107595a6/tools/apidoc/gen_toc.py
----------------------------------------------------------------------
diff --git a/tools/apidoc/gen_toc.py b/tools/apidoc/gen_toc.py
index e53d69d..cb26e2b 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/107595a6/ui/css/cloudstack3.css
----------------------------------------------------------------------
diff --git a/ui/css/cloudstack3.css b/ui/css/cloudstack3.css
index a4e2a2a..2bcd5e5 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;
@@ -12749,6 +12747,14 @@ 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;
 }
@@ -12972,13 +12978,6 @@ div.ui-dialog div.autoscaler div.field-group div.form-container form div.form-it
   border-radius: 4px;
   border-radius: 4px 4px 4px 4px;
   border: 1px solid #AFAFAF;
-  -moz-box-shadow: inset 0px 1px #727272;
-  -webkit-box-shadow: inset 0px 1px #727272;
-  -o-box-shadow: inset 0px 1px #727272;
-  box-shadow: inset 0px 1px #727272;
-  -moz-box-shadow: inset 0px 1px 0px #727272;
-  -webkit-box-shadow: inset 0px 1px 0px #727272;
-  -o-box-shadow: inset 0px 1px 0px #727272;
 }
 
 .manual-account-details > *:nth-child(even) {

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/107595a6/ui/dictionary.jsp
----------------------------------------------------------------------
diff --git a/ui/dictionary.jsp b/ui/dictionary.jsp
index 63d22bd..7d17267 100644
--- a/ui/dictionary.jsp
+++ b/ui/dictionary.jsp
@@ -143,6 +143,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" />',
@@ -764,7 +765,9 @@ dictionary = {
 'label.local.storage': '<fmt:message key="label.local.storage" />',
 '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/107595a6/ui/index.jsp
----------------------------------------------------------------------
diff --git a/ui/index.jsp b/ui/index.jsp
index 19c2dbf..e062799 100644
--- a/ui/index.jsp
+++ b/ui/index.jsp
@@ -51,28 +51,45 @@
                 <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="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>
                         </div>
-                        <!-- Domain -->
-                        <div class="field domain">
-                            <label for="domain"><fmt:message key="label.domain"/></label>
-                            <input type="text" name="domain" />
+
+                        <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" />
+                            </div>
+                        </div>
+
+                        <div id="login-submit">
+                            <!-- Submit (login) -->
+                            <input id="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/107595a6/ui/scripts/accounts.js
----------------------------------------------------------------------
diff --git a/ui/scripts/accounts.js b/ui/scripts/accounts.js
index 9e47d11..b63d184 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: {
@@ -1797,6 +1893,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");
@@ -1818,6 +1917,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/107595a6/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/107595a6/ui/scripts/cloudStack.js
----------------------------------------------------------------------
diff --git a/ui/scripts/cloudStack.js b/ui/scripts/cloudStack.js
index 55200b6..0e0a71a 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/107595a6/ui/scripts/docs.js
----------------------------------------------------------------------
diff --git a/ui/scripts/docs.js b/ui/scripts/docs.js
index 53c31c2..809c398 100755
--- a/ui/scripts/docs.js
+++ b/ui/scripts/docs.js
@@ -1261,6 +1261,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/107595a6/ui/scripts/sharedFunctions.js
----------------------------------------------------------------------
diff --git a/ui/scripts/sharedFunctions.js b/ui/scripts/sharedFunctions.js
index e8f7cc9..c29956a 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/107595a6/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/107595a6/ui/scripts/ui-custom/login.js
----------------------------------------------------------------------
diff --git a/ui/scripts/ui-custom/login.js b/ui/scripts/ui-custom/login.js
index 194c881..7c7e923 100644
--- a/ui/scripts/ui-custom/login.js
+++ b/ui/scripts/ui-custom/login.js
@@ -94,54 +94,120 @@
         $inputs.filter(':first').addClass('first-input').focus();
 
         // Login action
-        $login.find('input[type=submit]').click(function() {
-            if (!$form.valid()) return false;
-
-            var data = cloudStack.serializeForm($form);
-
-            args.loginAction({
-                data: data,
-                response: {
-                    success: function(args) {
-                        $login.remove();
-                        $('html body').removeClass('login');
-                        complete({
-                            user: args.data.user
-                        });
-                    },
-                    error: function(args) {
-                        cloudStack.dialog.notice({
-                            message: args
-                        });
+        var selectedLogin = 'cloudstack';
+        $login.find('#login-submit').click(function() {
+            if (selectedLogin === 'cloudstack') {
+                // CloudStack Local Login
+                if (!$form.valid()) return false;
+
+                var data = cloudStack.serializeForm($form);
+
+                args.loginAction({
+                    data: data,
+                    response: {
+                        success: function(args) {
+                            $login.remove();
+                            $('html body').removeClass('login');
+                            complete({
+                                user: args.data.user
+                            });
+                        },
+                        error: function(args) {
+                            cloudStack.dialog.notice({
+                                message: args
+                            });
+                        }
                     }
-                }
-            });
-
+                });
+            } else if (selectedLogin === 'saml') {
+                // SAML
+                args.samlLoginAction({
+                    data: {'idpid': $login.find('#login-options').find(':selected').val(),
+                          'domain': $login.find('#saml-domain').val()}
+                });
+            }
             return false;
         });
 
-        // SAML Login action
-        $login.find('input[type=samlsubmit]').click(function() {
-            args.samlLoginAction({
-            });
+        // Show SAML button if only SP is configured
+        $login.find('#login-dropdown').hide();
+        $login.find('#saml-login').hide();
+        $login.find('#cloudstack-login').hide();
+
+        var toggleLoginView = function (selectedOption) {
+            $login.find('#login-submit').show();
+            if (selectedOption === '') {
+                    $login.find('#saml-login').hide();
+                    $login.find('#cloudstack-login').hide();
+                    $login.find('#login-submit').hide();
+                    selectedLogin = 'none';
+            } else if (selectedOption === 'cloudstack-login') {
+                    $login.find('#saml-login').hide();
+                    $login.find('#cloudstack-login').show();
+                    selectedLogin = 'cloudstack';
+            } else {
+                    $login.find('#saml-login').show();
+                    $login.find('#cloudstack-login').hide();
+                    selectedLogin = 'saml';
+            }
+        };
+
+        $login.find('#login-options').change(function() {
+            var selectedOption = $login.find('#login-options').find(':selected').val();
+            toggleLoginView(selectedOption);
+            if (selectedOption && selectedOption !== '') {
+                $.cookie('login-option', selectedOption);
+            }
         });
 
-        // Show SAML button if only SP is configured
-        $login.find("#saml-login").hide();
         $.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();
+                    $login.find('#login-submit').hide();
                 } else {
-                    $login.find('#saml-login').hide();
+                    $login.find('#cloudstack-login').show();
+                    $login.find('#login-submit').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/107595a6/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/107595a6/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


[3/5] git commit: updated refs/heads/master to 3ff92e8

Posted by bh...@apache.org.
CLOUDSTACK-8457: SAML auth plugin improvements for production usage

* Move config options to SAML plugin
  This moves all configuration options from Config.java to SAML auth manager. This
  allows us to use the config framework.
* Make SAML2UserAuthenticator validate SAML token in httprequest
* Make logout API use ConfigKeys defined in saml auth manager
* Before doing SAML auth, cleanup local states and cookies
* Fix configurations in 4.5.1 to 4.5.2 upgrade path
* Fail if idp has no sso URL defined
* Add a default set of SAML SP cert for testing purposes
  Now to enable and use saml, one needs to do a deploydb-saml after doing a deploydb
* UI remembers login selections, IDP server

- CLOUDSTACK-8458:
    * On UI show dropdown list of discovered IdPs
    * Support SAML Federation, where there may be more than one IdP
        - New datastructure to hold metadata of SP or IdP
        - Recursive processing of IdP metadata
        - Fix login/logout APIs to get new interface and metadata data structure
        - Add org/contact information to metadata
        - Add new API: listIdps that returns list of all discovered IdPs
        - Refactor and cleanup code and tests

- CLOUDSTACK-8459:
    * Add HTTP-POST binding to SP metadata
    * Authn requests must use either HTTP POST/Artifact binding

- CLOUDSTACK-8461:
    * Use unspecified x509 cert as a fallback encryption/signing key
      In case a IDP's metadata does not clearly say if their certificates need to be
      used as signing or encryption and we don't find that, fallback to use the
      unspecified key itself.

- CLOUDSTACK-8462:
    * SAML Auth plugin should not do authorization
      This removes logic to create user if they don't exist. This strictly now
      assumes that users have been already created/imported/authorized by admins.
      As per SAML v2.0 spec section 4.1.2, the SP provider should create authn requests using
      either HTTP POST or HTTP Artifact binding to transfer the message through a
      user agent (browser in our case). The use of HTTP Redirect was one of the reasons
      why this plugin failed to work for some IdP servers that enforce this.
    * Add new User Source
      By reusing the source field, we can find if a user has been SAML enabled or not.
      The limitation is that, once say a user is imported by LDAP and then SAML
      enabled - they won't be able to use LDAP for authentication
    * UI should allow users to pass in domain they want to log into, though it is
      optional and needed only when a user has accounts across domains with same
      username and authorized IDP server
    * SAML users need to be authorized before they can authenticate
        - New column entity to track saml entity id for a user
        - Reusing source column to check if user is saml enabled or not
        - Add new source types, saml2 and saml2disabled
        - New table saml_token to solve the issue of multiple users across domains and
          to enforce security by tracking authn token and checking the samlresponse for
          the tokens
        - Implement API: authorizeSamlSso to enable/disable saml authentication for a
          user
        - Stubs to implement saml token flushing/expiry

- CLOUDSTACK-8463:
    * Use username attribute specified in global setting
      Use username attribute defined by admin from a global setting
      In case of encrypted assertion/attributes:
      - Decrypt them
      - Check signature if provided to check authenticity of message using IdP's
        public key and SP's private key
      - Loop through attributes to find the username

- CLOUDSTACK-8538:
    * Add new global config for SAML request sig algorithm

- CLOUDSTACK-8539:
    * Add metadata refresh timer task and token expiring
        - Fix domain path and save it to saml_tokens
        - Expire hour old saml tokens
        - Refresh metadata based on timer task
        - Fix unit tests

This closes #489

(cherry picked from commit 20ce346f3acb794b08a51841bab2188d426bf7dc)
Signed-off-by: Rohit Yadav <ro...@shapeblue.com>

Conflicts:
	client/WEB-INF/classes/resources/messages_hu.properties
	plugins/hypervisors/xenserver/src/com/cloud/hypervisor/xenserver/resource/wrapper/xenbase/CitrixCheckHealthCommandWrapper.java
	plugins/user-authenticators/saml2/src/org/apache/cloudstack/api/command/SAML2LoginAPIAuthenticatorCmd.java
	ui/scripts/ui-custom/login.js


Project: http://git-wip-us.apache.org/repos/asf/cloudstack/repo
Commit: http://git-wip-us.apache.org/repos/asf/cloudstack/commit/107595a6
Tree: http://git-wip-us.apache.org/repos/asf/cloudstack/tree/107595a6
Diff: http://git-wip-us.apache.org/repos/asf/cloudstack/diff/107595a6

Branch: refs/heads/master
Commit: 107595a6a5c669c63838bb6c52a93853cbd3c855
Parents: 527d6ee
Author: Rohit Yadav <ro...@shapeblue.com>
Authored: Thu May 28 14:50:12 2015 +0200
Committer: Rohit Yadav <ro...@shapeblue.com>
Committed: Mon Jun 29 12:31:51 2015 +0200

----------------------------------------------------------------------
 api/src/com/cloud/user/User.java                |   7 +-
 api/src/com/cloud/user/UserAccount.java         |   4 +
 .../org/apache/cloudstack/api/ApiConstants.java |   3 +-
 .../classes/resources/messages.properties       |   5 +-
 .../classes/resources/messages_fr_FR.properties |   1 -
 .../classes/resources/messages_hu.properties    |   1 -
 client/tomcatconf/commands.properties.in        |   3 +
 developer/developer-prefill.sql                 |   5 -
 developer/developer-saml.sql                    |  63 +++
 developer/pom.xml                               |  58 +++
 .../src/com/cloud/user/UserAccountVO.java       |  11 +
 engine/schema/src/com/cloud/user/UserVO.java    |  10 +
 .../src/com/cloud/user/dao/UserAccountDao.java  |   4 +
 .../com/cloud/user/dao/UserAccountDaoImpl.java  |  19 +-
 plugins/user-authenticators/saml2/pom.xml       |   5 +
 .../cloudstack/saml2/spring-saml2-context.xml   |   3 +
 .../api/command/AuthorizeSAMLSSOCmd.java        | 105 +++++
 .../command/GetServiceProviderMetaDataCmd.java  |  82 +++-
 .../cloudstack/api/command/ListIdpsCmd.java     | 114 +++++
 .../api/command/ListSamlAuthorizationCmd.java   |  95 ++++
 .../command/SAML2LoginAPIAuthenticatorCmd.java  | 288 +++++++------
 .../command/SAML2LogoutAPIAuthenticatorCmd.java |  29 +-
 .../cloudstack/api/response/IdpResponse.java    |  62 +++
 .../api/response/SamlAuthorizationResponse.java |  68 +++
 .../cloudstack/saml/SAML2AuthManager.java       |  67 ++-
 .../cloudstack/saml/SAML2AuthManagerImpl.java   | 430 +++++++++++++++----
 .../cloudstack/saml/SAML2UserAuthenticator.java |  28 +-
 .../cloudstack/saml/SAMLPluginConstants.java    |  30 ++
 .../cloudstack/saml/SAMLProviderMetadata.java   | 122 ++++++
 .../apache/cloudstack/saml/SAMLTokenDao.java    |  23 +
 .../cloudstack/saml/SAMLTokenDaoImpl.java       |  51 +++
 .../org/apache/cloudstack/saml/SAMLTokenVO.java |  97 +++++
 .../org/apache/cloudstack/saml/SAMLUtils.java   | 354 +++++++++++++++
 .../GetServiceProviderMetaDataCmdTest.java      | 104 +++++
 .../cloudstack/SAML2UserAuthenticatorTest.java  |   8 +-
 .../org/apache/cloudstack/SAMLUtilsTest.java    |  74 ++++
 .../GetServiceProviderMetaDataCmdTest.java      | 100 -----
 .../SAML2LoginAPIAuthenticatorCmdTest.java      |  45 +-
 .../SAML2LogoutAPIAuthenticatorCmdTest.java     |  15 +-
 server/src/com/cloud/api/ApiServer.java         |   4 +-
 server/src/com/cloud/api/ApiServlet.java        |   2 +-
 server/src/com/cloud/configuration/Config.java  |  72 ----
 setup/db/db/schema-451to452-cleanup.sql         |  20 +
 setup/db/db/schema-451to452.sql                 |  35 ++
 tools/apidoc/gen_toc.py                         |   3 +
 ui/css/cloudstack3.css                          |  19 +-
 ui/dictionary.jsp                               |   5 +-
 ui/index.jsp                                    |  49 ++-
 ui/scripts/accounts.js                          | 102 +++++
 ui/scripts/accountsWizard.js                    |  63 ++-
 ui/scripts/cloudStack.js                        |  22 +-
 ui/scripts/docs.js                              |   8 +
 ui/scripts/sharedFunctions.js                   |   1 +
 ui/scripts/ui-custom/accountsWizard.js          |   5 +
 ui/scripts/ui-custom/login.js                   | 132 ++++--
 .../apache/cloudstack/utils/auth/SAMLUtils.java | 330 --------------
 .../cloudstack/utils/auth/SAMLUtilsTest.java    |  91 ----
 57 files changed, 2581 insertions(+), 975 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cloudstack/blob/107595a6/api/src/com/cloud/user/User.java
----------------------------------------------------------------------
diff --git a/api/src/com/cloud/user/User.java b/api/src/com/cloud/user/User.java
index 8095e23..0ecdcfa 100644
--- a/api/src/com/cloud/user/User.java
+++ b/api/src/com/cloud/user/User.java
@@ -23,7 +23,7 @@ import org.apache.cloudstack.api.InternalIdentity;
 public interface User extends OwnedBy, InternalIdentity {
 
     public enum Source {
-        LDAP, UNKNOWN
+        LDAP, SAML2, SAML2DISABLED, UNKNOWN
     }
 
     public static final long UID_SYSTEM = 1;
@@ -84,4 +84,9 @@ public interface User extends OwnedBy, InternalIdentity {
 
     public Source getSource();
 
+    void setSource(Source source);
+
+    public String getExternalEntity();
+
+    public void setExternalEntity(String entity);
 }

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/107595a6/api/src/com/cloud/user/UserAccount.java
----------------------------------------------------------------------
diff --git a/api/src/com/cloud/user/UserAccount.java b/api/src/com/cloud/user/UserAccount.java
index d44fcf7..0449514 100644
--- a/api/src/com/cloud/user/UserAccount.java
+++ b/api/src/com/cloud/user/UserAccount.java
@@ -63,4 +63,8 @@ public interface UserAccount extends InternalIdentity {
     int getLoginAttempts();
 
     public User.Source getSource();
+
+    public String getExternalEntity();
+
+    public void setExternalEntity(String entity);
 }

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/107595a6/api/src/org/apache/cloudstack/api/ApiConstants.java
----------------------------------------------------------------------
diff --git a/api/src/org/apache/cloudstack/api/ApiConstants.java b/api/src/org/apache/cloudstack/api/ApiConstants.java
index 2b64258..204f33b 100644
--- a/api/src/org/apache/cloudstack/api/ApiConstants.java
+++ b/api/src/org/apache/cloudstack/api/ApiConstants.java
@@ -375,6 +375,7 @@ public class ApiConstants {
     public static final String ISOLATION_METHODS = "isolationmethods";
     public static final String PHYSICAL_NETWORK_ID = "physicalnetworkid";
     public static final String DEST_PHYSICAL_NETWORK_ID = "destinationphysicalnetworkid";
+    public static final String ENABLE = "enable";
     public static final String ENABLED = "enabled";
     public static final String SERVICE_NAME = "servicename";
     public static final String DHCP_RANGE = "dhcprange";
@@ -518,7 +519,7 @@ public class ApiConstants {
     public static final String VMPROFILE_ID = "vmprofileid";
     public static final String VMGROUP_ID = "vmgroupid";
     public static final String CS_URL = "csurl";
-    public static final String IDP_URL = "idpurl";
+    public static final String IDP_ID = "idpid";
     public static final String SCALEUP_POLICY_IDS = "scaleuppolicyids";
     public static final String SCALEDOWN_POLICY_IDS = "scaledownpolicyids";
     public static final String SCALEUP_POLICIES = "scaleuppolicies";

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/107595a6/client/WEB-INF/classes/resources/messages.properties
----------------------------------------------------------------------
diff --git a/client/WEB-INF/classes/resources/messages.properties b/client/WEB-INF/classes/resources/messages.properties
index 20970fe..701582c 100644
--- a/client/WEB-INF/classes/resources/messages.properties
+++ b/client/WEB-INF/classes/resources/messages.properties
@@ -115,6 +115,7 @@ label.action.attach.iso=Attach ISO
 label.action.cancel.maintenance.mode.processing=Cancelling Maintenance Mode....
 label.action.cancel.maintenance.mode=Cancel Maintenance Mode
 label.action.change.password=Change Password
+label.action.configure.samlauthorization=Configure SAML SSO Authorization
 label.action.change.service.processing=Changing Service....
 label.action.change.service=Change Service
 label.action.copy.ISO.processing=Copying ISO....
@@ -763,7 +764,9 @@ label.local.storage=Local Storage
 label.local=Local
 label.login=Login
 label.logout=Logout
-label.saml.login=SAML Login
+label.saml.enable=Authorize SAML SSO
+label.saml.entity=Identity Provider
+label.add.LDAP.account=Add LDAP Account
 label.LUN.number=LUN \#
 label.lun=LUN
 label.make.project.owner=Make account project owner

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/107595a6/client/WEB-INF/classes/resources/messages_fr_FR.properties
----------------------------------------------------------------------
diff --git a/client/WEB-INF/classes/resources/messages_fr_FR.properties b/client/WEB-INF/classes/resources/messages_fr_FR.properties
index 118f7f9..8a8efb7 100644
--- a/client/WEB-INF/classes/resources/messages_fr_FR.properties
+++ b/client/WEB-INF/classes/resources/messages_fr_FR.properties
@@ -1289,7 +1289,6 @@ label.s3.nfs.server=Serveur NFS S3
 label.s3.secret_key=Cl\u00e9 Priv\u00e9e
 label.s3.socket_timeout=D\u00e9lai d\\'expiration de la socket
 label.s3.use_https=Utiliser HTTPS
-label.saml.login=Identifiant SAML
 label.saturday=Samedi
 label.save.and.continue=Enregistrer et continuer
 label.save=Sauvegarder

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/107595a6/client/WEB-INF/classes/resources/messages_hu.properties
----------------------------------------------------------------------
diff --git a/client/WEB-INF/classes/resources/messages_hu.properties b/client/WEB-INF/classes/resources/messages_hu.properties
index 397c43f..6482c9c 100644
--- a/client/WEB-INF/classes/resources/messages_hu.properties
+++ b/client/WEB-INF/classes/resources/messages_hu.properties
@@ -1282,7 +1282,6 @@ label.s3.nfs.server=S3 NFS kiszolg\u00e1l\u00f3
 label.s3.secret_key=Titkos kulcs
 label.s3.socket_timeout=Kapcsolat id\u0151t\u00fall\u00e9p\u00e9s
 label.s3.use_https=HTTPS haszn\u00e1lata
-label.saml.login=SAML bejelentkez\u00e9s
 label.saturday=Szombat
 label.save.and.continue=Ment\u00e9s \u00e9s folytat\u00e1s
 label.save=Ment\u00e9s

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/107595a6/client/tomcatconf/commands.properties.in
----------------------------------------------------------------------
diff --git a/client/tomcatconf/commands.properties.in b/client/tomcatconf/commands.properties.in
index f4ff1c9..29752c6 100644
--- a/client/tomcatconf/commands.properties.in
+++ b/client/tomcatconf/commands.properties.in
@@ -26,6 +26,9 @@ logout=15
 samlSso=15
 samlSlo=15
 getSPMetadata=15
+listIdps=15
+authorizeSamlSso=7
+listSamlAuthorization=7
 
 ### Account commands
 createAccount=7

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/107595a6/developer/developer-prefill.sql
----------------------------------------------------------------------
diff --git a/developer/developer-prefill.sql b/developer/developer-prefill.sql
index 27b36e7..3097203 100644
--- a/developer/developer-prefill.sql
+++ b/developer/developer-prefill.sql
@@ -83,9 +83,4 @@ INSERT INTO `cloud`.`configuration` (category, instance, component, name, value)
             VALUES ('Advanced', 'DEFAULT', 'management-server',
             'developer', 'true');
 
--- Enable SAML plugin for developers by default
-INSERT INTO `cloud`.`configuration` (category, instance, component, name, value)
-            VALUES ('Advanced', 'DEFAULT', 'management-server',
-            'saml2.enabled', 'true');
-
 commit;

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/107595a6/developer/developer-saml.sql
----------------------------------------------------------------------
diff --git a/developer/developer-saml.sql b/developer/developer-saml.sql
new file mode 100644
index 0000000..18afb288
--- /dev/null
+++ b/developer/developer-saml.sql
@@ -0,0 +1,63 @@
+-- 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.
+
+-- SAML keystore for testing, allows testing on ssocirlce and other public IdPs
+-- with pre-seeded SP metadata
+USE cloud;
+
+-- Enable SAML plugin for developers by default
+INSERT INTO `cloud`.`configuration` (category, instance, component, name, value)
+            VALUES ('Advanced', 'DEFAULT', 'SAML2-PLUGIN',
+            'saml2.enabled', 'true')
+            ON DUPLICATE KEY UPDATE value=VALUES(value);
+
+INSERT INTO `cloud`.`configuration` (category, instance, component, name, value)
+            VALUES ('Advanced', 'DEFAULT', 'SAML2-PLUGIN',
+            'saml2.default.idpid', 'https://idp.bhaisaab.org/idp/shibboleth')
+            ON DUPLICATE KEY UPDATE value=VALUES(value);
+
+INSERT INTO `cloud`.`configuration` (category, instance, component, name, value)
+            VALUES ('Advanced', 'DEFAULT', 'SAML2-PLUGIN',
+            'saml2.idp.metadata.url', 'http://idp.bhaisaab.org/idp/shibboleth')
+            ON DUPLICATE KEY UPDATE value=VALUES(value);
+
+-- Enable LDAP source
+INSERT INTO `cloud`.`ldap_configuration` (hostname, port)
+            VALUES ('idp.bhaisaab.org', 389);
+
+-- Fix ldap configs
+INSERT INTO `cloud`.`configuration` (category, instance, component, name, value)
+            VALUES ('Advanced', 'DEFAULT', 'management-server',
+            'ldap.basedn', 'ou=people,dc=idp,dc=bhaisaab,dc=org')
+            ON DUPLICATE KEY UPDATE value=VALUES(value);
+
+INSERT INTO `cloud`.`configuration` (category, instance, component, name, value)
+            VALUES ('Advanced', 'DEFAULT', 'management-server',
+            'ldap.bind.principal', 'cn=admin,dc=idp,dc=bhaisaab,dc=org')
+            ON DUPLICATE KEY UPDATE value=VALUES(value);
+
+INSERT INTO `cloud`.`configuration` (category, instance, component, name, value)
+            VALUES ('Advanced', 'DEFAULT', 'management-server',
+            'ldap.bind.password', 'password')
+            ON DUPLICATE KEY UPDATE value=VALUES(value);
+
+-- Add default set of certificates for testing
+LOCK TABLES `keystore` WRITE;
+/*!40000 ALTER TABLE `keystore` DISABLE KEYS */;
+INSERT INTO `keystore` VALUES (1,'SAMLSP_KEYPAIR','MIIJRAIBADANBgkqhkiG9w0BAQEFAASCCS4wggkqAgEAAoICAQDQDirtAajTrScXCUgXrsOBgQ++y+IUcyzXwUGEYBlM9kBCmcdlbt5zuYQ8pOvoOQz6CAVqSjYNbnbg1ph37Zfv/tzGUg2V5cpB5BfEyt2KY0mBFNbwa0LKnCAYlBlarm4XZF+oZ0maH6cdboHdHiEqNRscylnXYA2zHKU3YoEfOR+9acl4z54PAyuPjr9SWUPTAyYf326i0q+h3J4nT6FwBFK+yKSC1PeVG/viYQ0otU1UUDkQ3pX81qfJfN/6Ih7W4v73LgUlZsTBMzlu/kJzMuP5Wc5IkU6Mt+8EHMZeLnN0ZErkMk0DCQE14hG8W7S8/inUJpwJlmb5E634Zq8u0I1zVUmSmAGqvvqKJBGnqY5X/j2bsA8B2qFsrcxasIlkKaWLvY+AvXD5X0OHwIZzRbuuCtguSz671C7Cwok8R3N+e9ATDHmG9gC10NJaB6dUBA9p2UdA82TR73x6nGe8pLJnGyecEQfxz1+ptPVAENj1Rl3Wrwu/dbPd/X6inlYpuwlsnWi3LYrkguT/9W3Z2uuq5PTVT04zcyev+50gVnDPzTrNCZfHpmMNQIqZGFmFKz4m+VUZmzZOdg1Vx51t9+t7iHGHqFk6/vnqqWiyEuTEFAaC9krm1VNzGvno5LyNm5Dk9JGAHFfjgNV++viGTpSPLeEeMnlvPuQJy5OJMwIDAQABAoICABME6Imn+C35izRA5fU8RaUGDlFrw+wIp1XF1d5rBoURkchE1ISCQRWlJOCCVwpwhK4qo4wW4qARtA5Tr7Zu4s/OpZH/mDxWuEmTt1SHEv9+mg6RwCBUPdPVt91nVHYEsg2zYEc9we2z7Qv0uSxkf7WjCypzmQjmP/paqQPKHnGjQDKJhCBmIlXO/WFvNDAr9tZIWGjbfP
 qndeS/DTocvm5GBuZn4xoOq99Woo0MQC6zfDEz8DOJlX56hPYXU0ZDbjxInfQsoc3MejoLG7n4xkxPn6WAvynFFsAoZFIk60Faz7UZIfuAWafoX9L0KpjkbT5Fob9CFEuQEzO7x9CIWoUr2PYn8HbThHDUOFAuVVpOLqleLPCrxkX/P01WTrLFuT6vSJKW2kxVwiHvZH6pNT01X/nlHDD6Jd9oWse2jIDBVor6fMnNDtgKl9azKgyakxoOGB7BMcb5u0Im8vFBCCRIyN3lrYjjR1F3H1tvY6Q0fEGLkilO334IyjC63he6lZ6NqslE/3QWEyyIiCL52rMzadN2SwVNawCa8YIR6+TpBjKyqY17LCP57v3UyM6J/kcUqXxDRcg1XnsjiWU+u0j9ZdlBgcbNuQeb1jD2QgICcyr/tWyJ2asyVfvARcD/xt5a9AnGjO0LnwMfw/DdBz1XCxz5uf3gOM69+nXk2gWhAoIBAQDr7NhlmVrASpOJHXXvqkpC2P4+hx7bmwKUZPbqm32pqCBypn6Qd2h4wdFzcP41wN6kpYqmmsPBoezctSgromfHeVjTyjhGku8b1HqtyRoX5sqIIPtv5pKKGS/tVWfyqQ8BspcdhZaR7+ZiRsLRwOXCscRq82+vbyq5Jd1bjsDGeLtcYyASv3II1xTBzSgNlvB+WiFXIlGWT9IPXwhv6IxZn7ME/y992d7bhgPxCcdTfKQNPBpcKerjcNxeDMto8vVijBDqujVpTf+3YvOOzWrcLn5UORFzpVho7oml5+5qnkFI/RZoiagUixFeQMv5+72qKJrxJu3VfI3mxlzZm5YjAoIBAQDhwjvzNWCQ2y7wwwnr88snVxAhd7kByvbKmnFVzHFTy2lheyWkiAqXj9juqsCtbkOK8a1ogmFAHz3i0/n+QhcJ20gudryniMt+u+wKxpmEKyqHKX3d4biPOvkKx7tdfwnlRKpSWXuynlDNVaQnJKUqBqDygLaE2s0
 LK3Fwdl+HN5ZPjRcuHkNpXA8t5lbm3tttMIs3JMneKAq77rodgRg+JcYhUNiybek3cZQcEiIGoh8UU6AhgQIOyMy5lkdG7XwZ2FEMQlqZo+T4HnkdTMU1CbTav1/ZzwDQP4/BJvKXhdRBuYHHAwhV6NIEMk5fzXcOoYmhfOMjvftrSxqUOImxAoIBAQDrhaEuJB8d0hVhD6EZ5kWGYHvHzjp2/1Ne80AwS5Pyl5309tNow1vvGYZQGaAd53Icqgo1clE0b8M3Pj5g+RtjXnfXzovJoIvFm6Pw887xx3uu1EZOmr710FkxNE62SCFsD26ekSsUe4rh10RMA6cbaz3riySW3YKoHO3Tpjo6qHJas7ZkIOzleFoHcximIGXrrWyVQPRz+zF4GOYiWeQq4KvltB8kIylAu5QZwCpV5Rsc/0BNe6c68QN9fIZgOhPQEoYc3lHN04kR+V2t1NH2BxAkYmhSq+ELt/6AOn6fv2brR4VkTPAXuhFXp5Y59B+OzESJs9RAiLxcgvBUaOdDAoIBAQCzlPJjUL5z/Cam1j76NoAP1y25saa1SmJuX9Rvz6UGZvR42qDi9GSYk5CYqbODQgbwa7bpP21kuHVeDgj6vE/fQ1NzwnfnPOXC9nGZUMmlXUEDK3o4GenZ5atda+wbP4b7nVdvEkdXmp/j9pARoxDPEV7OCJ0nqXUZwYEHWOI8iXdD6JPb168ADH72oBfYpsYdYVQclWMPGQMQ46Gg/qPuK9YjglAd/1hZBjwu6C2w4R2f6bWjcR/V6t0Pc/9W6GqjlHNEMTQoqzrkNDlbmUn2GraGm1z/wa5/+U+88eJfrdFeRtZ5HGxxCjalp+64PpTKSq1UjCeSsvlgK+oEpcTBAoIBAQCDDcS69BnjFWNa68FBrA2Uw6acQ6lnpXALohXCRL5qOTMe/FFDEBo0ADGrGpSo+cPaE2buNsYO79CafqTxPoZ38OAtTVmX3uL3z9+2ne2dc486gmAn
 KdJA8w9uawqMEkVpTA9f4WiBJJVzPwAv19AJCPKfUaB8IdNPV+HL8CdK+Dm+lZBADlB9RyvkJRLVJUAuK8/h9kbS3myKI6FIBeFFJpXRONkBSEkANknMqelvdf0GQsHliRslqIK2QVTIOmkJKecG35OhZ5WtU54oSxljlvmtvEKkEJAhEUyfFQRwQTTsDxkFFsfIVr9gv8K1RVEb4D00GUY7GSyAgPKPNsib','MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA0A4q7QGo060nFwlIF67DgYEPvsviFHMs18FBhGAZTPZAQpnHZW7ec7mEPKTr6DkM+ggFako2DW524NaYd+2X7/7cxlINleXKQeQXxMrdimNJgRTW8GtCypwgGJQZWq5uF2RfqGdJmh+nHW6B3R4hKjUbHMpZ12ANsxylN2KBHzkfvWnJeM+eDwMrj46/UllD0wMmH99uotKvodyeJ0+hcARSvsikgtT3lRv74mENKLVNVFA5EN6V/NanyXzf+iIe1uL+9y4FJWbEwTM5bv5CczLj+VnOSJFOjLfvBBzGXi5zdGRK5DJNAwkBNeIRvFu0vP4p1CacCZZm+ROt+GavLtCNc1VJkpgBqr76iiQRp6mOV/49m7APAdqhbK3MWrCJZCmli72PgL1w+V9Dh8CGc0W7rgrYLks+u9QuwsKJPEdzfnvQEwx5hvYAtdDSWgenVAQPadlHQPNk0e98epxnvKSyZxsnnBEH8c9fqbT1QBDY9UZd1q8Lv3Wz3f1+op5WKbsJbJ1oty2K5ILk//Vt2drrquT01U9OM3Mnr/udIFZwz806zQmXx6ZjDUCKmRhZhSs+JvlVGZs2TnYNVcedbffre4hxh6hZOv756qloshLkxBQGgvZK5tVTcxr56OS8jZuQ5PSRgBxX44DVfvr4hk6Ujy3hHjJ5bz7kCcuTiTMCAwEAAQ==','samlsp-keypair',NULL),(2,'S
 AMLSP_X509CERT','rO0ABXNyAC1qYXZhLnNlY3VyaXR5LmNlcnQuQ2VydGlmaWNhdGUkQ2VydGlmaWNhdGVSZXCJJ2qdya48DAIAAlsABGRhdGF0AAJbQkwABHR5cGV0ABJMamF2YS9sYW5nL1N0cmluZzt4cHVyAAJbQqzzF/gGCFTgAgAAeHAAAASzMIIErzCCApcCBgFNmkdlAzANBgkqhkiG9w0BAQsFADAbMRkwFwYDVQQDExBBcGFjaGVDbG91ZFN0YWNrMB4XDTE1MDUyNzExMjc1OVoXDTE4MDUyODExMjc1OVowGzEZMBcGA1UEAxMQQXBhY2hlQ2xvdWRTdGFjazCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANAOKu0BqNOtJxcJSBeuw4GBD77L4hRzLNfBQYRgGUz2QEKZx2Vu3nO5hDyk6+g5DPoIBWpKNg1uduDWmHftl+/+3MZSDZXlykHkF8TK3YpjSYEU1vBrQsqcIBiUGVqubhdkX6hnSZofpx1ugd0eISo1GxzKWddgDbMcpTdigR85H71pyXjPng8DK4+Ov1JZQ9MDJh/fbqLSr6HcnidPoXAEUr7IpILU95Ub++JhDSi1TVRQORDelfzWp8l83/oiHtbi/vcuBSVmxMEzOW7+QnMy4/lZzkiRToy37wQcxl4uc3RkSuQyTQMJATXiEbxbtLz+KdQmnAmWZvkTrfhmry7QjXNVSZKYAaq++ookEaepjlf+PZuwDwHaoWytzFqwiWQppYu9j4C9cPlfQ4fAhnNFu64K2C5LPrvULsLCiTxHc3570BMMeYb2ALXQ0loHp1QED2nZR0DzZNHvfHqcZ7yksmcbJ5wRB/HPX6m09UAQ2PVGXdavC791s939fqKeVim7CWydaLctiuSC5P/1bdna66rk9NVPTjNzJ6/7nSBWcM/NOs0Jl8emYw1AipkYWYUrPib5VRmbNk52DVXHnW3363uI
 cYeoWTr++eqpaLIS5MQUBoL2SubVU3Ma+ejkvI2bkOT0kYAcV+OA1X76+IZOlI8t4R4yeW8+5AnLk4kzAgMBAAEwDQYJKoZIhvcNAQELBQADggIBAHZWSGpypDmQLQWr2FCVQUnulbPuMMJ0sCH0rNLGLe8qNbZ0YeAuWFsg7+0kVGZ4OuDgioIhD0h3Q3huZtF/WF81eyZqPyVfkXG8egjK58AzMDPHZECeoSVGUCZuq3wjmbnT2sLLDvr8RrzMbbCEvkrYHWivQ18Lbd3eWYYnDbXZRy9GuSWrA9cMqXVYjSTxam9Kel33BIF6CAlMQN5o11oiAv+ciNoxHqGh+8xX3kFKP+x+SRt40NOEs537lEpj/6KdLvd/bP6J4K94jAX3lsdg6zDaBiQWl7P3t50AKtP384Qsb/33uXcbTyw/TkzvPcbmsgTbEUTZIOv44CxMstFrUCyT7ptrzLvDk7Iy2cMgWghULgDvKT3esPE9pleyHG8bkjGt9ypDF/Lmp7j/kILYbF7eq1wIbHOSam4p8WyddVsW4nesu6fqLiCGXum9paChIfvL3To/VHFFKduhJd0Y7LMgWO7pXxWh7XfgRmzQaEN1eJmj5315HEYTS2wXWjptwYDrhiobKuCbpADfOQks8xNKJFLMnXp+IvAqz+ZjkNOz60MLuQ3hvKLTo6nQcTYTfZZxo3Aap30/hA2GtxxSXK/xpBDm58jcVoudgCdxML/OqERBfcADBLvIw5h9+DlXjPUg25IefU0oA336YtnzftJ6cfQfatrc0tBqNEeXdAAFWC41MDk=','','samlsp-x509cert',NULL);
+/*!40000 ALTER TABLE `keystore` ENABLE KEYS */;
+UNLOCK TABLES;

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/107595a6/developer/pom.xml
----------------------------------------------------------------------
diff --git a/developer/pom.xml b/developer/pom.xml
index d6dc6a1..ef38e8a 100644
--- a/developer/pom.xml
+++ b/developer/pom.xml
@@ -158,6 +158,64 @@
       </build>
     </profile>
     <profile>
+      <!-- saml deploydb property -->
+      <id>deploydb-saml</id>
+      <activation>
+        <property>
+          <name>deploydb-saml</name>
+        </property>
+      </activation>
+      <build>
+        <plugins>
+          <plugin>
+            <groupId>org.codehaus.mojo</groupId>
+            <artifactId>exec-maven-plugin</artifactId>
+            <dependencies>
+              <dependency>
+                <groupId>mysql</groupId>
+                <artifactId>mysql-connector-java</artifactId>
+                <version>${cs.mysql.version}</version>
+              </dependency>
+            </dependencies>
+            <version>1.2.1</version>
+            <executions>
+              <execution>
+                <phase>process-resources</phase>
+                <id>create-schema-simulator</id>
+                <goals>
+                  <goal>java</goal>
+                </goals>
+              </execution>
+            </executions>
+            <configuration>
+              <mainClass>com.cloud.upgrade.DatabaseCreator</mainClass>
+              <includePluginDependencies>true</includePluginDependencies>
+              <arguments>
+                <!-- db properties file -->
+                <argument>${basedir}/../utils/conf/db.properties</argument>
+                <argument>${basedir}/../utils/conf/db.properties.override</argument>
+                <!-- simulator sql files -->
+                <argument>${basedir}/developer-saml.sql</argument>
+                <!-- upgrade -->
+                <argument>com.cloud.upgrade.DatabaseUpgradeChecker</argument>
+                <argument>--rootpassword=${db.root.password}</argument>
+              </arguments>
+              <systemProperties>
+                <systemProperty>
+                  <key>catalina.home</key>
+                  <value>${basedir}/../utils</value>
+                </systemProperty>
+                <systemProperty>
+                  <key>paths.script</key>
+                  <value>${basedir}/target/db</value>
+                </systemProperty>
+              </systemProperties>
+            </configuration>
+          </plugin>
+        </plugins>
+      </build>
+    </profile>
+    <profile>
       <!-- simulator deploydb property -->
       <id>deploydb-simulator</id>
       <activation>

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/107595a6/engine/schema/src/com/cloud/user/UserAccountVO.java
----------------------------------------------------------------------
diff --git a/engine/schema/src/com/cloud/user/UserAccountVO.java b/engine/schema/src/com/cloud/user/UserAccountVO.java
index 5f33c47..80ee873 100644
--- a/engine/schema/src/com/cloud/user/UserAccountVO.java
+++ b/engine/schema/src/com/cloud/user/UserAccountVO.java
@@ -105,6 +105,9 @@ public class UserAccountVO implements UserAccount, InternalIdentity {
     @Enumerated(value = EnumType.STRING)
     private User.Source source;
 
+    @Column(name = "external_entity", length = 65535)
+    private String externalEntity = null;
+
     public UserAccountVO() {
     }
 
@@ -296,4 +299,12 @@ public class UserAccountVO implements UserAccount, InternalIdentity {
     public void setSource(User.Source source) {
         this.source = source;
     }
+
+    public String getExternalEntity() {
+        return externalEntity;
+    }
+
+    public void setExternalEntity(String externalEntity) {
+        this.externalEntity = externalEntity;
+    }
 }

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/107595a6/engine/schema/src/com/cloud/user/UserVO.java
----------------------------------------------------------------------
diff --git a/engine/schema/src/com/cloud/user/UserVO.java b/engine/schema/src/com/cloud/user/UserVO.java
index eb2813b..da7811e 100644
--- a/engine/schema/src/com/cloud/user/UserVO.java
+++ b/engine/schema/src/com/cloud/user/UserVO.java
@@ -101,6 +101,9 @@ public class UserVO implements User, Identity, InternalIdentity {
     @Enumerated(value = EnumType.STRING)
     private Source source;
 
+    @Column(name = "external_entity", length = 65535)
+    private String externalEntity;
+
     public UserVO() {
         this.uuid = UUID.randomUUID().toString();
     }
@@ -283,4 +286,11 @@ public class UserVO implements User, Identity, InternalIdentity {
         this.source = source;
     }
 
+    public String getExternalEntity() {
+        return externalEntity;
+    }
+
+    public void setExternalEntity(String externalEntity) {
+        this.externalEntity = externalEntity;
+    }
 }

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/107595a6/engine/schema/src/com/cloud/user/dao/UserAccountDao.java
----------------------------------------------------------------------
diff --git a/engine/schema/src/com/cloud/user/dao/UserAccountDao.java b/engine/schema/src/com/cloud/user/dao/UserAccountDao.java
index a26ff7f..1d005b2 100644
--- a/engine/schema/src/com/cloud/user/dao/UserAccountDao.java
+++ b/engine/schema/src/com/cloud/user/dao/UserAccountDao.java
@@ -20,7 +20,11 @@ import com.cloud.user.UserAccount;
 import com.cloud.user.UserAccountVO;
 import com.cloud.utils.db.GenericDao;
 
+import java.util.List;
+
 public interface UserAccountDao extends GenericDao<UserAccountVO, Long> {
+    List<UserAccountVO> getAllUsersByNameAndEntity(String username, String entity);
+
     UserAccount getUserAccount(String username, Long domainId);
 
     boolean validateUsernameInDomain(String username, Long domainId);

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/107595a6/engine/schema/src/com/cloud/user/dao/UserAccountDaoImpl.java
----------------------------------------------------------------------
diff --git a/engine/schema/src/com/cloud/user/dao/UserAccountDaoImpl.java b/engine/schema/src/com/cloud/user/dao/UserAccountDaoImpl.java
index 1449e6b..a8d9e39 100644
--- a/engine/schema/src/com/cloud/user/dao/UserAccountDaoImpl.java
+++ b/engine/schema/src/com/cloud/user/dao/UserAccountDaoImpl.java
@@ -16,15 +16,15 @@
 // under the License.
 package com.cloud.user.dao;
 
-import javax.ejb.Local;
-
-import org.springframework.stereotype.Component;
-
 import com.cloud.user.UserAccount;
 import com.cloud.user.UserAccountVO;
 import com.cloud.utils.db.GenericDaoBase;
 import com.cloud.utils.db.SearchBuilder;
 import com.cloud.utils.db.SearchCriteria;
+import org.springframework.stereotype.Component;
+
+import javax.ejb.Local;
+import java.util.List;
 
 @Component
 @Local(value = {UserAccountDao.class})
@@ -39,6 +39,17 @@ public class UserAccountDaoImpl extends GenericDaoBase<UserAccountVO, Long> impl
     }
 
     @Override
+    public List<UserAccountVO> getAllUsersByNameAndEntity(String username, String entity) {
+        if (username == null) {
+            return null;
+        }
+        SearchCriteria<UserAccountVO> sc = createSearchCriteria();
+        sc.addAnd("username", SearchCriteria.Op.EQ, username);
+        sc.addAnd("externalEntity", SearchCriteria.Op.EQ, entity);
+        return listBy(sc);
+    }
+
+    @Override
     public UserAccount getUserAccount(String username, Long domainId) {
         if ((username == null) || (domainId == null)) {
             return null;

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/107595a6/plugins/user-authenticators/saml2/pom.xml
----------------------------------------------------------------------
diff --git a/plugins/user-authenticators/saml2/pom.xml b/plugins/user-authenticators/saml2/pom.xml
index 834d5f0..6c6f5ca 100644
--- a/plugins/user-authenticators/saml2/pom.xml
+++ b/plugins/user-authenticators/saml2/pom.xml
@@ -47,5 +47,10 @@
       <artifactId>cloud-api</artifactId>
       <version>${project.version}</version>
     </dependency>
+    <dependency>
+      <groupId>org.apache.cloudstack</groupId>
+      <artifactId>cloud-framework-config</artifactId>
+      <version>${project.version}</version>
+    </dependency>
   </dependencies>
 </project>

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/107595a6/plugins/user-authenticators/saml2/resources/META-INF/cloudstack/saml2/spring-saml2-context.xml
----------------------------------------------------------------------
diff --git a/plugins/user-authenticators/saml2/resources/META-INF/cloudstack/saml2/spring-saml2-context.xml b/plugins/user-authenticators/saml2/resources/META-INF/cloudstack/saml2/spring-saml2-context.xml
index 92f89b8..d3a2194 100644
--- a/plugins/user-authenticators/saml2/resources/META-INF/cloudstack/saml2/spring-saml2-context.xml
+++ b/plugins/user-authenticators/saml2/resources/META-INF/cloudstack/saml2/spring-saml2-context.xml
@@ -33,4 +33,7 @@
         <property name="name" value="SAML2Auth"/>
     </bean>
 
+    <bean id="samlTokenDao" class="org.apache.cloudstack.saml.SAMLTokenDaoImpl">
+    </bean>
+
 </beans>

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/107595a6/plugins/user-authenticators/saml2/src/org/apache/cloudstack/api/command/AuthorizeSAMLSSOCmd.java
----------------------------------------------------------------------
diff --git a/plugins/user-authenticators/saml2/src/org/apache/cloudstack/api/command/AuthorizeSAMLSSOCmd.java b/plugins/user-authenticators/saml2/src/org/apache/cloudstack/api/command/AuthorizeSAMLSSOCmd.java
new file mode 100644
index 0000000..54ce418
--- /dev/null
+++ b/plugins/user-authenticators/saml2/src/org/apache/cloudstack/api/command/AuthorizeSAMLSSOCmd.java
@@ -0,0 +1,105 @@
+// 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.domain.Domain;
+import com.cloud.user.Account;
+import com.cloud.user.UserAccount;
+import org.apache.cloudstack.acl.SecurityChecker;
+import org.apache.cloudstack.api.APICommand;
+import org.apache.cloudstack.api.ApiConstants;
+import org.apache.cloudstack.api.ApiErrorCode;
+import org.apache.cloudstack.api.BaseCmd;
+import org.apache.cloudstack.api.Parameter;
+import org.apache.cloudstack.api.ServerApiException;
+import org.apache.cloudstack.api.response.IdpResponse;
+import org.apache.cloudstack.api.response.SuccessResponse;
+import org.apache.cloudstack.api.response.UserResponse;
+import org.apache.cloudstack.context.CallContext;
+import org.apache.cloudstack.saml.SAML2AuthManager;
+import org.apache.log4j.Logger;
+
+import javax.inject.Inject;
+
+@APICommand(name = "authorizeSamlSso", description = "Allow or disallow a user to use SAML SSO", responseObject = SuccessResponse.class, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false)
+public class AuthorizeSAMLSSOCmd extends BaseCmd {
+    public static final Logger s_logger = Logger.getLogger(AuthorizeSAMLSSOCmd.class.getName());
+
+    private static final String s_name = "authorizesamlssoresponse";
+
+    @Inject
+    SAML2AuthManager _samlAuthManager;
+
+    /////////////////////////////////////////////////////
+    //////////////// API parameters /////////////////////
+    /////////////////////////////////////////////////////
+
+    @Parameter(name = ApiConstants.USER_ID, type = CommandType.UUID, entityType = UserResponse.class, required = true, description = "User uuid")
+    private Long id;
+
+    @Parameter(name = ApiConstants.ENABLE, type = CommandType.BOOLEAN, required = true, description = "If true, authorizes user to be able to use SAML for Single Sign. If False, disable user to user SAML SSO.")
+    private Boolean enable;
+
+    public Boolean getEnable() {
+        return enable;
+    }
+
+    public String getEntityId() {
+        return entityId;
+    }
+
+    @Parameter(name = ApiConstants.ENTITY_ID, type = CommandType.STRING, entityType = IdpResponse.class, description = "The Identity Provider ID the user is allowed to get single signed on from")
+    private String entityId;
+
+    public Long getId() {
+        return id;
+    }
+
+    @Override
+    public String getCommandName() {
+        return s_name;
+    }
+
+    @Override
+    public long getEntityOwnerId() {
+        return Account.ACCOUNT_ID_SYSTEM;
+    }
+
+    @Override
+    public void execute() {
+        // Check permissions
+        UserAccount userAccount = _accountService.getUserAccountById(getId());
+        if (userAccount == null) {
+            throw new ServerApiException(ApiErrorCode.ACCOUNT_ERROR , "Unable to find a user account with the given ID");
+        }
+        Domain domain = _domainService.getDomain(userAccount.getDomainId());
+        Account account = _accountService.getAccount(userAccount.getAccountId());
+        _accountService.checkAccess(CallContext.current().getCallingAccount(), domain);
+        _accountService.checkAccess(CallContext.current().getCallingAccount(), SecurityChecker.AccessType.OperateEntry, true, account);
+
+        CallContext.current().setEventDetails("UserId: " + getId());
+        SuccessResponse response = new SuccessResponse();
+        Boolean status = false;
+
+        if (_samlAuthManager.authorizeUser(getId(), getEntityId(), getEnable())) {
+            status = true;
+        }
+        response.setResponseName(getCommandName());
+        response.setSuccess(status);
+        setResponseObject(response);
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/107595a6/plugins/user-authenticators/saml2/src/org/apache/cloudstack/api/command/GetServiceProviderMetaDataCmd.java
----------------------------------------------------------------------
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 721b18e..466a3dd 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
@@ -14,7 +14,6 @@
 // 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.api.response.ApiResponseSerializer;
@@ -30,21 +29,36 @@ import org.apache.cloudstack.api.auth.APIAuthenticator;
 import org.apache.cloudstack.api.auth.PluggableAPIAuthenticator;
 import org.apache.cloudstack.api.response.SAMLMetaDataResponse;
 import org.apache.cloudstack.saml.SAML2AuthManager;
+import org.apache.cloudstack.saml.SAMLProviderMetadata;
 import org.apache.log4j.Logger;
 import org.opensaml.Configuration;
 import org.opensaml.DefaultBootstrap;
 import org.opensaml.common.xml.SAMLConstants;
 import org.opensaml.saml2.core.NameIDType;
 import org.opensaml.saml2.metadata.AssertionConsumerService;
+import org.opensaml.saml2.metadata.ContactPerson;
+import org.opensaml.saml2.metadata.ContactPersonTypeEnumeration;
+import org.opensaml.saml2.metadata.EmailAddress;
 import org.opensaml.saml2.metadata.EntityDescriptor;
+import org.opensaml.saml2.metadata.GivenName;
 import org.opensaml.saml2.metadata.KeyDescriptor;
+import org.opensaml.saml2.metadata.LocalizedString;
 import org.opensaml.saml2.metadata.NameIDFormat;
+import org.opensaml.saml2.metadata.Organization;
+import org.opensaml.saml2.metadata.OrganizationName;
+import org.opensaml.saml2.metadata.OrganizationURL;
 import org.opensaml.saml2.metadata.SPSSODescriptor;
 import org.opensaml.saml2.metadata.SingleLogoutService;
 import org.opensaml.saml2.metadata.impl.AssertionConsumerServiceBuilder;
+import org.opensaml.saml2.metadata.impl.ContactPersonBuilder;
+import org.opensaml.saml2.metadata.impl.EmailAddressBuilder;
 import org.opensaml.saml2.metadata.impl.EntityDescriptorBuilder;
+import org.opensaml.saml2.metadata.impl.GivenNameBuilder;
 import org.opensaml.saml2.metadata.impl.KeyDescriptorBuilder;
 import org.opensaml.saml2.metadata.impl.NameIDFormatBuilder;
+import org.opensaml.saml2.metadata.impl.OrganizationBuilder;
+import org.opensaml.saml2.metadata.impl.OrganizationNameBuilder;
+import org.opensaml.saml2.metadata.impl.OrganizationURLBuilder;
 import org.opensaml.saml2.metadata.impl.SPSSODescriptorBuilder;
 import org.opensaml.saml2.metadata.impl.SingleLogoutServiceBuilder;
 import org.opensaml.xml.ConfigurationException;
@@ -73,6 +87,7 @@ import javax.xml.transform.stream.StreamResult;
 import java.io.IOException;
 import java.io.StringWriter;
 import java.util.List;
+import java.util.Locale;
 import java.util.Map;
 import java.net.InetAddress;
 
@@ -119,8 +134,10 @@ public class GetServiceProviderMetaDataCmd extends BaseCmd implements APIAuthent
                     params, responseType));
         }
 
+        final SAMLProviderMetadata spMetadata = _samlAuthManager.getSPMetadata();
+
         EntityDescriptor spEntityDescriptor = new EntityDescriptorBuilder().buildObject();
-        spEntityDescriptor.setEntityID(_samlAuthManager.getServiceProviderId());
+        spEntityDescriptor.setEntityID(spMetadata.getEntityId());
 
         SPSSODescriptor spSSODescriptor = new SPSSODescriptorBuilder().buildObject();
         spSSODescriptor.setWantAssertionsSigned(true);
@@ -130,19 +147,23 @@ public class GetServiceProviderMetaDataCmd extends BaseCmd implements APIAuthent
         keyInfoGeneratorFactory.setEmitEntityCertificate(true);
         KeyInfoGenerator keyInfoGenerator = keyInfoGeneratorFactory.newInstance();
 
+        KeyDescriptor signKeyDescriptor = new KeyDescriptorBuilder().buildObject();
+        signKeyDescriptor.setUse(UsageType.SIGNING);
+
         KeyDescriptor encKeyDescriptor = new KeyDescriptorBuilder().buildObject();
         encKeyDescriptor.setUse(UsageType.ENCRYPTION);
 
-        KeyDescriptor signKeyDescriptor = new KeyDescriptorBuilder().buildObject();
-        signKeyDescriptor.setUse(UsageType.SIGNING);
+        BasicX509Credential signingCredential = new BasicX509Credential();
+        signingCredential.setEntityCertificate(spMetadata.getSigningCertificate());
+
+        BasicX509Credential encryptionCredential = new BasicX509Credential();
+        encryptionCredential.setEntityCertificate(spMetadata.getEncryptionCertificate());
 
-        BasicX509Credential credential = new BasicX509Credential();
-        credential.setEntityCertificate(_samlAuthManager.getSpX509Certificate());
         try {
-            encKeyDescriptor.setKeyInfo(keyInfoGenerator.generate(credential));
-            signKeyDescriptor.setKeyInfo(keyInfoGenerator.generate(credential));
-            spSSODescriptor.getKeyDescriptors().add(encKeyDescriptor);
+            signKeyDescriptor.setKeyInfo(keyInfoGenerator.generate(signingCredential));
+            encKeyDescriptor.setKeyInfo(keyInfoGenerator.generate(encryptionCredential));
             spSSODescriptor.getKeyDescriptors().add(signKeyDescriptor);
+            spSSODescriptor.getKeyDescriptors().add(encKeyDescriptor);
         } catch (SecurityException e) {
             s_logger.warn("Unable to add SP X509 descriptors:" + e.getMessage());
         }
@@ -160,19 +181,50 @@ public class GetServiceProviderMetaDataCmd extends BaseCmd implements APIAuthent
         spSSODescriptor.getNameIDFormats().add(transientNameIDFormat);
 
         AssertionConsumerService assertionConsumerService = new AssertionConsumerServiceBuilder().buildObject();
-        assertionConsumerService.setIndex(0);
-        assertionConsumerService.setBinding(SAMLConstants.SAML2_REDIRECT_BINDING_URI);
-        assertionConsumerService.setLocation(_samlAuthManager.getSpSingleSignOnUrl());
+        assertionConsumerService.setIndex(1);
+        assertionConsumerService.setIsDefault(true);
+        assertionConsumerService.setBinding(SAMLConstants.SAML2_POST_BINDING_URI);
+        assertionConsumerService.setLocation(spMetadata.getSsoUrl());
+        spSSODescriptor.getAssertionConsumerServices().add(assertionConsumerService);
+
+        AssertionConsumerService assertionConsumerService2 = new AssertionConsumerServiceBuilder().buildObject();
+        assertionConsumerService2.setIndex(2);
+        assertionConsumerService2.setBinding(SAMLConstants.SAML2_REDIRECT_BINDING_URI);
+        assertionConsumerService2.setLocation(spMetadata.getSsoUrl());
+        spSSODescriptor.getAssertionConsumerServices().add(assertionConsumerService2);
 
         SingleLogoutService ssoService = new SingleLogoutServiceBuilder().buildObject();
         ssoService.setBinding(SAMLConstants.SAML2_REDIRECT_BINDING_URI);
-        ssoService.setLocation(_samlAuthManager.getSpSingleLogOutUrl());
-
+        ssoService.setLocation(spMetadata.getSloUrl());
         spSSODescriptor.getSingleLogoutServices().add(ssoService);
-        spSSODescriptor.getAssertionConsumerServices().add(assertionConsumerService);
+
+        SingleLogoutService ssoService2 = new SingleLogoutServiceBuilder().buildObject();
+        ssoService2.setBinding(SAMLConstants.SAML2_POST_BINDING_URI);
+        ssoService2.setLocation(spMetadata.getSloUrl());
+        spSSODescriptor.getSingleLogoutServices().add(ssoService2);
+
         spSSODescriptor.addSupportedProtocol(SAMLConstants.SAML20P_NS);
         spEntityDescriptor.getRoleDescriptors().add(spSSODescriptor);
 
+        ContactPerson contactPerson = new ContactPersonBuilder().buildObject();
+        GivenName givenName = new GivenNameBuilder().buildObject();
+        givenName.setName(spMetadata.getContactPersonName());
+        EmailAddress emailAddress = new EmailAddressBuilder().buildObject();
+        emailAddress.setAddress(spMetadata.getContactPersonEmail());
+        contactPerson.setType(ContactPersonTypeEnumeration.TECHNICAL);
+        contactPerson.setGivenName(givenName);
+        contactPerson.getEmailAddresses().add(emailAddress);
+        spEntityDescriptor.getContactPersons().add(contactPerson);
+
+        Organization organization = new OrganizationBuilder().buildObject();
+        OrganizationName organizationName = new OrganizationNameBuilder().buildObject();
+        organizationName.setName(new LocalizedString(spMetadata.getOrganizationName(), Locale.getDefault().getLanguage()));
+        OrganizationURL organizationURL = new OrganizationURLBuilder().buildObject();
+        organizationURL.setURL(new LocalizedString(spMetadata.getOrganizationUrl(), Locale.getDefault().getLanguage()));
+        organization.getOrganizationNames().add(organizationName);
+        organization.getURLs().add(organizationURL);
+        spEntityDescriptor.setOrganization(organization);
+
         StringWriter stringWriter = new StringWriter();
         try {
             DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/107595a6/plugins/user-authenticators/saml2/src/org/apache/cloudstack/api/command/ListIdpsCmd.java
----------------------------------------------------------------------
diff --git a/plugins/user-authenticators/saml2/src/org/apache/cloudstack/api/command/ListIdpsCmd.java b/plugins/user-authenticators/saml2/src/org/apache/cloudstack/api/command/ListIdpsCmd.java
new file mode 100644
index 0000000..7d7c95e
--- /dev/null
+++ b/plugins/user-authenticators/saml2/src/org/apache/cloudstack/api/command/ListIdpsCmd.java
@@ -0,0 +1,114 @@
+// 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.api.response.ApiResponseSerializer;
+import com.cloud.user.Account;
+import org.apache.cloudstack.api.APICommand;
+import org.apache.cloudstack.api.ApiErrorCode;
+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.api.auth.APIAuthenticator;
+import org.apache.cloudstack.api.auth.PluggableAPIAuthenticator;
+import org.apache.cloudstack.api.response.IdpResponse;
+import org.apache.cloudstack.api.response.ListResponse;
+import org.apache.cloudstack.saml.SAML2AuthManager;
+import org.apache.cloudstack.saml.SAMLProviderMetadata;
+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.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+@APICommand(name = "listIdps", description = "Returns list of discovered SAML Identity Providers", responseObject = IdpResponse.class, entityType = {})
+public class ListIdpsCmd extends BaseCmd implements APIAuthenticator {
+    public static final Logger s_logger = Logger.getLogger(ListIdpsCmd.class.getName());
+    private static final String s_name = "listidpsresponse";
+
+    @Inject
+    ApiServerService _apiServer;
+
+    SAML2AuthManager _samlAuthManager;
+
+    /////////////////////////////////////////////////////
+    /////////////// API Implementation///////////////////
+    /////////////////////////////////////////////////////
+
+    @Override
+    public String getCommandName() {
+        return s_name;
+    }
+
+    @Override
+    public long getEntityOwnerId() {
+        return Account.ACCOUNT_TYPE_NORMAL;
+    }
+
+    @Override
+    public void execute() throws ServerApiException {
+        // We should never reach here
+        throw new ServerApiException(ApiErrorCode.METHOD_NOT_ALLOWED, "This is an authentication api, cannot be used directly");
+    }
+
+    @Override
+    public String authenticate(String command, Map<String, Object[]> params, HttpSession session, String remoteAddress, String responseType, StringBuilder auditTrailSb, final HttpServletRequest req, final HttpServletResponse resp) throws ServerApiException {
+        auditTrailSb.append("=== SAML List IdPs ===");
+        ListResponse<IdpResponse> response = new ListResponse<IdpResponse>();
+        List<IdpResponse> idpResponseList = new ArrayList<IdpResponse>();
+        for (SAMLProviderMetadata metadata: _samlAuthManager.getAllIdPMetadata()) {
+            if (metadata == null) {
+                continue;
+            }
+            IdpResponse idpResponse = new IdpResponse();
+            idpResponse.setId(metadata.getEntityId());
+            if (metadata.getOrganizationName() == null || metadata.getOrganizationName().isEmpty()) {
+                idpResponse.setOrgName(metadata.getEntityId());
+            } else {
+                idpResponse.setOrgName(metadata.getOrganizationName());
+            }
+            idpResponse.setOrgUrl(metadata.getOrganizationUrl());
+            idpResponse.setObjectName("idp");
+            idpResponseList.add(idpResponse);
+        }
+        response.setResponses(idpResponseList, idpResponseList.size());
+        response.setResponseName(getCommandName());
+        return ApiResponseSerializer.toSerializedString(response, responseType);
+    }
+
+    @Override
+    public APIAuthenticationType getAPIType() {
+        return APIAuthenticationType.LOGIN_API;
+    }
+
+    @Override
+    public void setAuthenticators(List<PluggableAPIAuthenticator> authenticators) {
+        for (PluggableAPIAuthenticator authManager: authenticators) {
+            if (authManager != null && authManager instanceof SAML2AuthManager) {
+                _samlAuthManager = (SAML2AuthManager) authManager;
+            }
+        }
+        if (_samlAuthManager == null) {
+            s_logger.error("No suitable Pluggable Authentication Manager found for SAML2 Login Cmd");
+        }
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/107595a6/plugins/user-authenticators/saml2/src/org/apache/cloudstack/api/command/ListSamlAuthorizationCmd.java
----------------------------------------------------------------------
diff --git a/plugins/user-authenticators/saml2/src/org/apache/cloudstack/api/command/ListSamlAuthorizationCmd.java b/plugins/user-authenticators/saml2/src/org/apache/cloudstack/api/command/ListSamlAuthorizationCmd.java
new file mode 100644
index 0000000..be958a1
--- /dev/null
+++ b/plugins/user-authenticators/saml2/src/org/apache/cloudstack/api/command/ListSamlAuthorizationCmd.java
@@ -0,0 +1,95 @@
+// 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.user.Account;
+import com.cloud.user.User;
+import com.cloud.user.UserVO;
+import com.cloud.user.dao.UserDao;
+import org.apache.cloudstack.acl.SecurityChecker;
+import org.apache.cloudstack.api.APICommand;
+import org.apache.cloudstack.api.ApiConstants;
+import org.apache.cloudstack.api.BaseListCmd;
+import org.apache.cloudstack.api.Parameter;
+import org.apache.cloudstack.api.response.ListResponse;
+import org.apache.cloudstack.api.response.SamlAuthorizationResponse;
+import org.apache.cloudstack.api.response.UserResponse;
+import org.apache.cloudstack.context.CallContext;
+import org.apache.log4j.Logger;
+
+import javax.inject.Inject;
+import java.util.ArrayList;
+import java.util.List;
+
+@APICommand(name = "listSamlAuthorization", description = "Lists authorized users who can used SAML SSO", responseObject = SamlAuthorizationResponse.class, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false)
+public class ListSamlAuthorizationCmd extends BaseListCmd {
+    public static final Logger s_logger = Logger.getLogger(ListSamlAuthorizationCmd.class.getName());
+    private static final String s_name = "listsamlauthorizationsresponse";
+
+    @Inject
+    private UserDao _userDao;
+
+    /////////////////////////////////////////////////////
+    //////////////// API parameters /////////////////////
+    /////////////////////////////////////////////////////
+    @Parameter(name = ApiConstants.USER_ID, type = CommandType.UUID, entityType = UserResponse.class, required = false, description = "User uuid")
+    private Long userId;
+
+    /////////////////////////////////////////////////////
+    /////////////// API Implementation///////////////////
+    /////////////////////////////////////////////////////
+
+    public Long getUserId() {
+        return userId;
+    }
+
+    @Override
+    public String getCommandName() {
+        return s_name;
+    }
+
+    @Override
+    public long getEntityOwnerId() {
+        return Account.ACCOUNT_ID_SYSTEM;
+    }
+
+    @Override
+    public void execute() {
+        List<UserVO> users = new ArrayList<UserVO>();
+        if (getUserId() != null) {
+            UserVO user = _userDao.getUser(getUserId());
+            if (user != null) {
+                Account account = _accountService.getAccount(user.getAccountId());
+                _accountService.checkAccess(CallContext.current().getCallingAccount(), SecurityChecker.AccessType.ListEntry, true, account);
+                users.add(user);
+            }
+        } else if (CallContext.current().getCallingAccount().getType() == Account.ACCOUNT_TYPE_ADMIN) {
+            users = _userDao.listAll();
+        }
+
+        ListResponse<SamlAuthorizationResponse> response = new ListResponse<SamlAuthorizationResponse>();
+        List<SamlAuthorizationResponse> authorizationResponses = new ArrayList<SamlAuthorizationResponse>();
+        for (User user: users) {
+            SamlAuthorizationResponse authorizationResponse = new SamlAuthorizationResponse(user.getUuid(), user.getSource().equals(User.Source.SAML2), user.getExternalEntity());
+            authorizationResponse.setObjectName("samlauthorization");
+            authorizationResponses.add(authorizationResponse);
+        }
+        response.setResponses(authorizationResponses);
+        response.setResponseName(getCommandName());
+        setResponseObject(response);
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/107595a6/plugins/user-authenticators/saml2/src/org/apache/cloudstack/api/command/SAML2LoginAPIAuthenticatorCmd.java
----------------------------------------------------------------------
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 dda5876..26a4c9b 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
@@ -14,16 +14,14 @@
 // 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.api.response.ApiResponseSerializer;
-import com.cloud.configuration.Config;
-import com.cloud.domain.Domain;
 import com.cloud.exception.CloudAuthenticationException;
 import com.cloud.user.Account;
 import com.cloud.user.DomainManager;
 import com.cloud.user.UserAccount;
+import com.cloud.user.UserAccountVO;
 import com.cloud.user.dao.UserAccountDao;
 import com.cloud.utils.HttpUtils;
 import com.cloud.utils.db.EntityManager;
@@ -38,24 +36,29 @@ 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.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.SAMLTokenVO;
+import org.apache.cloudstack.saml.SAMLUtils;
 import org.apache.log4j.Logger;
 import org.opensaml.DefaultBootstrap;
 import org.opensaml.saml2.core.Assertion;
-import org.opensaml.saml2.core.Attribute;
-import org.opensaml.saml2.core.AttributeStatement;
-import org.opensaml.saml2.core.AuthnRequest;
-import org.opensaml.saml2.core.NameID;
-import org.opensaml.saml2.core.NameIDType;
+import org.opensaml.saml2.core.EncryptedAssertion;
+import org.opensaml.saml2.core.Issuer;
 import org.opensaml.saml2.core.Response;
 import org.opensaml.saml2.core.StatusCode;
+import org.opensaml.saml2.encryption.Decrypter;
 import org.opensaml.xml.ConfigurationException;
-import org.opensaml.xml.io.MarshallingException;
+import org.opensaml.xml.encryption.DecryptionException;
+import org.opensaml.xml.encryption.EncryptedKeyResolver;
+import org.opensaml.xml.encryption.InlineEncryptedKeyResolver;
 import org.opensaml.xml.io.UnmarshallingException;
+import org.opensaml.xml.security.SecurityHelper;
+import org.opensaml.xml.security.credential.Credential;
+import org.opensaml.xml.security.keyinfo.StaticKeyInfoCredentialResolver;
 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;
@@ -68,14 +71,11 @@ import javax.servlet.http.HttpSession;
 import javax.xml.parsers.ParserConfigurationException;
 import javax.xml.stream.FactoryConfigurationError;
 import java.io.IOException;
-import java.net.URLEncoder;
 import java.net.InetAddress;
-import java.security.InvalidKeyException;
-import java.security.NoSuchAlgorithmException;
-import java.security.PrivateKey;
+import java.net.URLEncoder;
+
 import java.util.List;
 import java.util.Map;
-import java.util.UUID;
 
 @APICommand(name = "samlSso", description = "SP initiated SAML Single Sign On", requestHasSensitiveInfo = true, responseObject = LoginCmdResponse.class, entityType = {})
 public class SAML2LoginAPIAuthenticatorCmd extends BaseCmd implements APIAuthenticator {
@@ -85,16 +85,14 @@ public class SAML2LoginAPIAuthenticatorCmd extends BaseCmd implements APIAuthent
     /////////////////////////////////////////////////////
     //////////////// API parameters /////////////////////
     /////////////////////////////////////////////////////
-    @Parameter(name = ApiConstants.IDP_URL, type = CommandType.STRING, description = "Identity Provider SSO HTTP-Redirect binding URL", required = true)
-    private String idpUrl;
+    @Parameter(name = ApiConstants.IDP_ID, type = CommandType.STRING, description = "Identity Provider Entity ID", required = true)
+    private String idpId;
 
     @Inject
     ApiServerService _apiServer;
     @Inject
     EntityManager _entityMgr;
     @Inject
-    ConfigurationDao _configDao;
-    @Inject
     DomainManager _domainMgr;
     @Inject
     private UserAccountDao _userAccountDao;
@@ -105,8 +103,8 @@ public class SAML2LoginAPIAuthenticatorCmd extends BaseCmd implements APIAuthent
     /////////////////// Accessors ///////////////////////
     /////////////////////////////////////////////////////
 
-    public String getIdpUrl() {
-        return idpUrl;
+    public String getIdpId() {
+        return idpId;
     }
 
     /////////////////////////////////////////////////////
@@ -129,30 +127,6 @@ public class SAML2LoginAPIAuthenticatorCmd extends BaseCmd implements APIAuthent
         throw new ServerApiException(ApiErrorCode.METHOD_NOT_ALLOWED, "This is an authentication api, cannot be used directly");
     }
 
-    private String buildAuthnRequestUrl(String idpUrl) {
-        String spId = _samlAuthManager.getServiceProviderId();
-        String consumerUrl = _samlAuthManager.getSpSingleSignOnUrl();
-        String identityProviderUrl = _samlAuthManager.getIdpSingleSignOnUrl();
-
-        if (idpUrl != null) {
-            identityProviderUrl = idpUrl;
-        }
-
-        String redirectUrl = "";
-        try {
-            DefaultBootstrap.bootstrap();
-            AuthnRequest authnRequest = SAMLUtils.buildAuthnRequestObject(spId, identityProviderUrl, consumerUrl);
-            PrivateKey privateKey = null;
-            if (_samlAuthManager.getSpKeyPair() != null) {
-                privateKey = _samlAuthManager.getSpKeyPair().getPrivate();
-            }
-            redirectUrl = identityProviderUrl + "?" + SAMLUtils.generateSAMLRequestSignature("SAMLRequest=" + SAMLUtils.encodeSAMLRequest(authnRequest), privateKey);
-        } catch (ConfigurationException | FactoryConfigurationError | MarshallingException | IOException | NoSuchAlgorithmException | InvalidKeyException | java.security.SignatureException e) {
-            s_logger.error("SAML AuthnRequest message building error: " + e.getMessage());
-        }
-        return redirectUrl;
-    }
-
     public Response processSAMLResponse(String responseMessage) {
         Response responseObject = null;
         try {
@@ -168,13 +142,44 @@ public class SAML2LoginAPIAuthenticatorCmd extends BaseCmd implements APIAuthent
     @Override
     public String authenticate(final String command, final Map<String, Object[]> 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;
-                final String[] idps = (String[])params.get(ApiConstants.IDP_URL);
-                if (idps != null && idps.length > 0) {
-                    idpUrl = idps[0];
+            if (!params.containsKey(SAMLPluginConstants.SAML_RESPONSE) && !params.containsKey("SAMLart")) {
+                String idpId = null;
+                String domainPath = null;
+
+                if (params.containsKey(ApiConstants.IDP_ID)) {
+                    idpId = ((String[])params.get(ApiConstants.IDP_ID))[0];
                 }
-                String redirectUrl = this.buildAuthnRequestUrl(idpUrl);
+
+                if (params.containsKey(ApiConstants.DOMAIN)) {
+                    domainPath = ((String[])params.get(ApiConstants.DOMAIN))[0];
+                }
+
+                if (domainPath != null && !domainPath.isEmpty()) {
+                    if (!domainPath.startsWith("/")) {
+                        domainPath = "/" + domainPath;
+                    }
+                    if (!domainPath.endsWith("/")) {
+                        domainPath = domainPath + "/";
+                    }
+                }
+
+                SAMLProviderMetadata spMetadata = _samlAuthManager.getSPMetadata();
+                SAMLProviderMetadata idpMetadata = _samlAuthManager.getIdPMetadata(idpId);
+                if (idpMetadata == null) {
+                    throw new ServerApiException(ApiErrorCode.PARAM_ERROR, _apiServer.getSerializedApiError(ApiErrorCode.PARAM_ERROR.getHttpCode(),
+                            "IdP ID (" + idpId + ") is not found in our list of supported IdPs, cannot proceed.",
+                            params, responseType));
+                }
+                if (idpMetadata.getSsoUrl() == null || idpMetadata.getSsoUrl().isEmpty()) {
+                    throw new ServerApiException(ApiErrorCode.PARAM_ERROR, _apiServer.getSerializedApiError(ApiErrorCode.PARAM_ERROR.getHttpCode(),
+                            "IdP ID (" + idpId + ") has no Single Sign On URL defined please contact "
+                                    + idpMetadata.getContactPersonName() + " <" + idpMetadata.getContactPersonEmail() + ">, cannot proceed.",
+                            params, responseType));
+                }
+                String authnId = SAMLUtils.generateSecureRandomId();
+                _samlAuthManager.saveToken(authnId, domainPath, idpMetadata.getEntityId());
+                s_logger.debug("Sending SAMLRequest id=" + authnId);
+                String redirectUrl = SAMLUtils.buildAuthnRequestUrl(authnId, spMetadata, idpMetadata, SAML2AuthManager.SAMLSignatureAlgorithm.value());
                 resp.sendRedirect(redirectUrl);
                 return "";
             } if (params.containsKey("SAMLart")) {
@@ -182,7 +187,7 @@ public class SAML2LoginAPIAuthenticatorCmd extends BaseCmd implements APIAuthent
                         "SAML2 HTTP Artifact Binding is not supported",
                         params, responseType));
             } else {
-                final String samlResponse = ((String[])params.get(SAMLUtils.SAML_RESPONSE))[0];
+                final String samlResponse = ((String[])params.get(SAMLPluginConstants.SAML_RESPONSE))[0];
                 Response processedSAMLResponse = this.processSAMLResponse(samlResponse);
                 String statusCode = processedSAMLResponse.getStatus().getStatusCode().getValue();
                 if (!statusCode.equals(StatusCode.SUCCESS_URI)) {
@@ -191,10 +196,37 @@ public class SAML2LoginAPIAuthenticatorCmd extends BaseCmd implements APIAuthent
                             params, responseType));
                 }
 
-                if (_samlAuthManager.getIdpSigningKey() != null) {
-                    org.opensaml.xml.signature.Signature sig = processedSAMLResponse.getSignature();
+                String username = null;
+                Long domainId = null;
+                Issuer issuer = processedSAMLResponse.getIssuer();
+                SAMLProviderMetadata spMetadata = _samlAuthManager.getSPMetadata();
+                SAMLProviderMetadata idpMetadata = _samlAuthManager.getIdPMetadata(issuer.getValue());
+
+                String responseToId = processedSAMLResponse.getInResponseTo();
+                s_logger.debug("Received SAMLResponse in response to id=" + responseToId);
+                SAMLTokenVO token = _samlAuthManager.getToken(responseToId);
+                if (token != null) {
+                    if (token.getDomainId() != null) {
+                        domainId = token.getDomainId();
+                    }
+                    if (!(token.getEntity().equalsIgnoreCase(issuer.getValue()))) {
+                        throw new ServerApiException(ApiErrorCode.ACCOUNT_ERROR, _apiServer.getSerializedApiError(ApiErrorCode.ACCOUNT_ERROR.getHttpCode(),
+                                "The SAML response contains Issuer Entity ID that is different from the original SAML request",
+                                params, responseType));
+                    }
+                } else {
+                    throw new ServerApiException(ApiErrorCode.ACCOUNT_ERROR, _apiServer.getSerializedApiError(ApiErrorCode.ACCOUNT_ERROR.getHttpCode(),
+                            "Received SAML response for a SSO request that we may not have made or has expired, please try logging in again",
+                            params, responseType));
+                }
+
+                // Set IdpId for this session
+                session.setAttribute(SAMLPluginConstants.SAML_IDPID, issuer.getValue());
+
+                Signature sig = processedSAMLResponse.getSignature();
+                if (idpMetadata.getSigningCertificate() != null && sig != null) {
                     BasicX509Credential credential = new BasicX509Credential();
-                    credential.setEntityCertificate(_samlAuthManager.getIdpSigningKey());
+                    credential.setEntityCertificate(idpMetadata.getSigningCertificate());
                     SignatureValidator validator = new SignatureValidator(credential);
                     try {
                         validator.validate(sig);
@@ -205,94 +237,106 @@ public class SAML2LoginAPIAuthenticatorCmd extends BaseCmd implements APIAuthent
                                 params, responseType));
                     }
                 }
-
-                String domainString = _configDao.getValue(Config.SAMLUserDomain.key());
-
-                Long domainId = null;
-                Domain domain = _domainMgr.getDomain(domainString);
-                if (domain != null) {
-                    domainId = domain.getId();
-                } else {
-                    try {
-                        domainId = Long.parseLong(domainString);
-                    } catch (NumberFormatException ignore) {
-                    }
-                }
-                if (domainId == null) {
-                    s_logger.error("The default domain ID for SAML users is not set correct, it should be a UUID. ROOT domain will be used.");
+                if (username == null) {
+                    username = SAMLUtils.getValueFromAssertions(processedSAMLResponse.getAssertions(), SAML2AuthManager.SAMLUserAttributeName.value());
                 }
 
-                String username = null;
-                String password = SAMLUtils.generateSecureRandomId(); // Random password
-                String firstName = "";
-                String lastName = "";
-                String timeZone = "GMT";
-                String email = "";
-                short accountType = 0; // User account
-
-                Assertion assertion = processedSAMLResponse.getAssertions().get(0);
-                NameID nameId = assertion.getSubject().getNameID();
-                String sessionIndex = assertion.getAuthnStatements().get(0).getSessionIndex();
-                session.setAttribute(SAMLUtils.SAML_NAMEID, nameId);
-                session.setAttribute(SAMLUtils.SAML_SESSION, sessionIndex);
-
-                if (nameId.getFormat().equals(NameIDType.PERSISTENT) || nameId.getFormat().equals(NameIDType.EMAIL)) {
-                    username = nameId.getValue();
-                    if (nameId.getFormat().equals(NameIDType.EMAIL)) {
-                        email = username;
+                for (Assertion assertion: processedSAMLResponse.getAssertions()) {
+                    if (assertion!= null && assertion.getSubject() != null && assertion.getSubject().getNameID() != null) {
+                        session.setAttribute(SAMLPluginConstants.SAML_NAMEID, assertion.getSubject().getNameID().getValue());
+                        break;
                     }
                 }
 
-                List<AttributeStatement> attributeStatements = assertion.getAttributeStatements();
-                if (attributeStatements != null && attributeStatements.size() > 0) {
-                    for (AttributeStatement attributeStatement: attributeStatements) {
-                        if (attributeStatement == null) {
-                            continue;
-                        }
-                        // Try capturing standard LDAP attributes
-                        for (Attribute attribute: attributeStatement.getAttributes()) {
-                            String attributeName = attribute.getName();
-                            String attributeValue = attribute.getAttributeValues().get(0).getDOM().getTextContent();
-                            if (attributeName.equalsIgnoreCase("uid") && username == null) {
-                                username = attributeValue;
-                            } else if (attributeName.equalsIgnoreCase("givenName")) {
-                                firstName = attributeValue;
-                            } else if (attributeName.equalsIgnoreCase(("sn"))) {
-                                lastName = attributeValue;
-                            } else if (attributeName.equalsIgnoreCase("mail")) {
-                                email = attributeValue;
+                if (idpMetadata.getEncryptionCertificate() != null && spMetadata != null
+                        && spMetadata.getKeyPair() != null && spMetadata.getKeyPair().getPrivate() != null) {
+                    Credential credential = SecurityHelper.getSimpleCredential(idpMetadata.getEncryptionCertificate().getPublicKey(),
+                            spMetadata.getKeyPair().getPrivate());
+                    StaticKeyInfoCredentialResolver keyInfoResolver = new StaticKeyInfoCredentialResolver(credential);
+                    EncryptedKeyResolver keyResolver = new InlineEncryptedKeyResolver();
+                    Decrypter decrypter = new Decrypter(null, keyInfoResolver, keyResolver);
+                    decrypter.setRootInNewDocument(true);
+                    List<EncryptedAssertion> encryptedAssertions = processedSAMLResponse.getEncryptedAssertions();
+                    if (encryptedAssertions != null) {
+                        for (EncryptedAssertion encryptedAssertion : encryptedAssertions) {
+                            Assertion assertion = null;
+                            try {
+                                assertion = decrypter.decrypt(encryptedAssertion);
+                            } catch (DecryptionException e) {
+                                s_logger.warn("SAML EncryptedAssertion error: " + e.toString());
+                            }
+                            if (assertion == null) {
+                                continue;
+                            }
+                            Signature encSig = assertion.getSignature();
+                            if (idpMetadata.getSigningCertificate() != null && encSig != null) {
+                                BasicX509Credential sigCredential = new BasicX509Credential();
+                                sigCredential.setEntityCertificate(idpMetadata.getSigningCertificate());
+                                SignatureValidator validator = new SignatureValidator(sigCredential);
+                                try {
+                                    validator.validate(encSig);
+                                } 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));
+                                }
+                            }
+                            if (assertion.getSubject() != null && assertion.getSubject().getNameID() != null) {
+                                session.setAttribute(SAMLPluginConstants.SAML_NAMEID, assertion.getSubject().getNameID().getValue());
+                            }
+                            if (username == null) {
+                                username = SAMLUtils.getValueFromAttributeStatements(assertion.getAttributeStatements(), SAML2AuthManager.SAMLUserAttributeName.value());
                             }
                         }
                     }
                 }
 
-                if (username == null && email != null) {
-                    username = email;
+                if (username == null) {
+                    throw new ServerApiException(ApiErrorCode.ACCOUNT_ERROR, _apiServer.getSerializedApiError(ApiErrorCode.ACCOUNT_ERROR.getHttpCode(),
+                            "Failed to find admin configured username attribute in the SAML Response. Please ask your administrator to check SAML user attribute name.", params, responseType));
                 }
-                final String uniqueUserId = SAMLUtils.createSAMLId(username);
 
-                UserAccount userAccount = _userAccountDao.getUserAccount(username, domainId);
-                if (userAccount == null && uniqueUserId != null && username != null) {
-                    CallContext.current().setEventDetails("SAML Account/User with UserName: " + username + ", FirstName :" + password + ", LastName: " + lastName);
-                    userAccount = _accountService.createUserAccount(username, password, firstName, lastName, email, timeZone,
-                            username, (short) accountType, domainId, null, null, UUID.randomUUID().toString(), uniqueUserId);
+                UserAccount userAccount = null;
+                List<UserAccountVO> possibleUserAccounts = _userAccountDao.getAllUsersByNameAndEntity(username, issuer.getValue());
+                if (possibleUserAccounts != null && possibleUserAccounts.size() > 0) {
+                    if (possibleUserAccounts.size() == 1) {
+                        userAccount = possibleUserAccounts.get(0);
+                    } else if (possibleUserAccounts.size() > 1) {
+                        if (domainId != null) {
+                            userAccount = _userAccountDao.getUserAccount(username, domainId);
+                        } else {
+                            throw new ServerApiException(ApiErrorCode.ACCOUNT_ERROR, _apiServer.getSerializedApiError(ApiErrorCode.ACCOUNT_ERROR.getHttpCode(),
+                                    "You have accounts in multiple domains, please re-login by specifying the domain you want to log into.",
+                                    params, responseType));
+                        }
+                    }
+                }
+
+                if (userAccount == null || userAccount.getExternalEntity() == null || !_samlAuthManager.isUserAuthorized(userAccount.getId(), issuer.getValue())) {
+                    throw new ServerApiException(ApiErrorCode.ACCOUNT_ERROR, _apiServer.getSerializedApiError(ApiErrorCode.ACCOUNT_ERROR.getHttpCode(),
+                            "Your authenticated user is not authorized, please contact your administrator",
+                            params, responseType));
                 }
 
                 if (userAccount != null) {
                     try {
                         if (_apiServer.verifyUser(userAccount.getId())) {
-                            LoginCmdResponse loginResponse = (LoginCmdResponse) _apiServer.loginUser(session, username, userAccount.getPassword(), domainId, null, remoteAddress, params);
+                            LoginCmdResponse loginResponse = (LoginCmdResponse) _apiServer.loginUser(session, userAccount.getUsername(), userAccount.getUsername() + userAccount.getSource().toString(),
+                                    userAccount.getDomainId(), null, remoteAddress, params);
                             resp.addCookie(new Cookie("userid", URLEncoder.encode(loginResponse.getUserId(), HttpUtils.UTF_8)));
                             resp.addCookie(new Cookie("domainid", URLEncoder.encode(loginResponse.getDomainId(), HttpUtils.UTF_8)));
                             resp.addCookie(new Cookie("role", URLEncoder.encode(loginResponse.getType(), HttpUtils.UTF_8)));
                             resp.addCookie(new Cookie("username", URLEncoder.encode(loginResponse.getUsername(), HttpUtils.UTF_8)));
-                            resp.addCookie(new Cookie("sessionkey", URLEncoder.encode(loginResponse.getSessionKey(), HttpUtils.UTF_8)));
+                            resp.addCookie(new Cookie(ApiConstants.SESSIONKEY, URLEncoder.encode(loginResponse.getSessionKey(), HttpUtils.UTF_8)));
                             resp.addCookie(new Cookie("account", URLEncoder.encode(loginResponse.getAccount(), HttpUtils.UTF_8)));
-                            resp.addCookie(new Cookie("timezone", URLEncoder.encode(loginResponse.getTimeZone(), HttpUtils.UTF_8)));
+                            String timezone = loginResponse.getTimeZone();
+                            if (timezone != null) {
+                                resp.addCookie(new Cookie("timezone", URLEncoder.encode(timezone, HttpUtils.UTF_8)));
+                            }
                             resp.addCookie(new Cookie("userfullname", URLEncoder.encode(loginResponse.getFirstName() + " " + loginResponse.getLastName(), HttpUtils.UTF_8).replace("+", "%20")));
-                            resp.sendRedirect(_configDao.getValue(Config.SAMLCloudStackRedirectionUrl.key()));
+                            resp.sendRedirect(SAML2AuthManager.SAMLCloudStackRedirectionUrl.value());
                             return ApiResponseSerializer.toSerializedString(loginResponse, responseType);
-
                         }
                     } catch (final CloudAuthenticationException ignored) {
                     }
@@ -303,7 +347,7 @@ public class SAML2LoginAPIAuthenticatorCmd extends BaseCmd implements APIAuthent
             auditTrailSb.append(e.getMessage());
         }
         throw new ServerApiException(ApiErrorCode.ACCOUNT_ERROR, _apiServer.getSerializedApiError(ApiErrorCode.ACCOUNT_ERROR.getHttpCode(),
-                "Unable to authenticate or retrieve user while performing SAML based SSO",
+                "Unable to authenticate user while performing SAML based SSO. Please make sure your user/account has been added, enable and authorized by the admin before you can authenticate. Please contact your administrator.",
                 params, responseType));
     }
 


[2/5] git commit: updated refs/heads/master to 3ff92e8

Posted by bh...@apache.org.
http://git-wip-us.apache.org/repos/asf/cloudstack/blob/107595a6/plugins/user-authenticators/saml2/src/org/apache/cloudstack/api/command/SAML2LogoutAPIAuthenticatorCmd.java
----------------------------------------------------------------------
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 17a0a30..817e62c 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
@@ -17,7 +17,6 @@
 package org.apache.cloudstack.api.command;
 
 import com.cloud.api.response.ApiResponseSerializer;
-import com.cloud.configuration.Config;
 import com.cloud.user.Account;
 import org.apache.cloudstack.api.APICommand;
 import org.apache.cloudstack.api.ApiErrorCode;
@@ -28,13 +27,13 @@ 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.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.apache.log4j.Logger;
 import org.opensaml.DefaultBootstrap;
 import org.opensaml.saml2.core.LogoutRequest;
-import org.opensaml.saml2.core.NameID;
 import org.opensaml.saml2.core.Response;
 import org.opensaml.saml2.core.StatusCode;
 import org.opensaml.xml.ConfigurationException;
@@ -60,8 +59,7 @@ public class SAML2LogoutAPIAuthenticatorCmd extends BaseCmd implements APIAuthen
 
     @Inject
     ApiServerService _apiServer;
-    @Inject
-    ConfigurationDao _configDao;
+
     SAML2AuthManager _samlAuthManager;
 
     /////////////////////////////////////////////////////
@@ -94,7 +92,7 @@ public class SAML2LogoutAPIAuthenticatorCmd extends BaseCmd implements APIAuthen
 
         if (session == null) {
             try {
-                resp.sendRedirect(_configDao.getValue(Config.SAMLCloudStackRedirectionUrl.key()));
+                resp.sendRedirect(SAML2AuthManager.SAMLCloudStackRedirectionUrl.value());
             } catch (IOException ignored) {
             }
             return responseString;
@@ -111,7 +109,7 @@ public class SAML2LogoutAPIAuthenticatorCmd extends BaseCmd implements APIAuthen
 
         if (params != null && params.containsKey("SAMLResponse")) {
             try {
-                final String samlResponse = ((String[])params.get(SAMLUtils.SAML_RESPONSE))[0];
+                final String samlResponse = ((String[])params.get(SAMLPluginConstants.SAML_RESPONSE))[0];
                 Response processedSAMLResponse = SAMLUtils.decodeSAMLResponse(samlResponse);
                 String statusCode = processedSAMLResponse.getStatus().getStatusCode().getValue();
                 if (!statusCode.equals(StatusCode.SUCCESS_URI)) {
@@ -123,25 +121,26 @@ public class SAML2LogoutAPIAuthenticatorCmd extends BaseCmd implements APIAuthen
                 s_logger.error("SAMLResponse processing error: " + e.getMessage());
             }
             try {
-                resp.sendRedirect(_configDao.getValue(Config.SAMLCloudStackRedirectionUrl.key()));
+                resp.sendRedirect(SAML2AuthManager.SAMLCloudStackRedirectionUrl.value());
             } catch (IOException ignored) {
             }
             return responseString;
         }
 
-        NameID nameId = (NameID) session.getAttribute(SAMLUtils.SAML_NAMEID);
-        String sessionIndex = (String) session.getAttribute(SAMLUtils.SAML_SESSION);
-        if (nameId == null || sessionIndex == null) {
+        String idpId = (String) session.getAttribute(SAMLPluginConstants.SAML_IDPID);
+        SAMLProviderMetadata idpMetadata = _samlAuthManager.getIdPMetadata(idpId);
+        String nameId = (String) session.getAttribute(SAMLPluginConstants.SAML_NAMEID);
+        if (idpMetadata == null || nameId == null || nameId.isEmpty()) {
             try {
-                resp.sendRedirect(_configDao.getValue(Config.SAMLCloudStackRedirectionUrl.key()));
+                resp.sendRedirect(SAML2AuthManager.SAMLCloudStackRedirectionUrl.value());
             } catch (IOException ignored) {
             }
             return responseString;
         }
-        LogoutRequest logoutRequest = SAMLUtils.buildLogoutRequest(_samlAuthManager.getIdpSingleLogOutUrl(), _samlAuthManager.getServiceProviderId(), nameId, sessionIndex);
+        LogoutRequest logoutRequest = SAMLUtils.buildLogoutRequest(idpMetadata.getSloUrl(), _samlAuthManager.getSPMetadata().getEntityId(), nameId);
 
         try {
-            String redirectUrl = _samlAuthManager.getIdpSingleLogOutUrl() + "?SAMLRequest=" + SAMLUtils.encodeSAMLRequest(logoutRequest);
+            String redirectUrl = idpMetadata.getSloUrl() + "?SAMLRequest=" + SAMLUtils.encodeSAMLRequest(logoutRequest);
             resp.sendRedirect(redirectUrl);
         } catch (MarshallingException | IOException e) {
             s_logger.error("SAML SLO error: " + e.getMessage());

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/107595a6/plugins/user-authenticators/saml2/src/org/apache/cloudstack/api/response/IdpResponse.java
----------------------------------------------------------------------
diff --git a/plugins/user-authenticators/saml2/src/org/apache/cloudstack/api/response/IdpResponse.java b/plugins/user-authenticators/saml2/src/org/apache/cloudstack/api/response/IdpResponse.java
new file mode 100644
index 0000000..d95cc33
--- /dev/null
+++ b/plugins/user-authenticators/saml2/src/org/apache/cloudstack/api/response/IdpResponse.java
@@ -0,0 +1,62 @@
+// 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.response;
+
+import com.cloud.serializer.Param;
+import com.google.gson.annotations.SerializedName;
+
+public class IdpResponse extends AuthenticationCmdResponse {
+    @SerializedName("id")
+    @Param(description = "The IdP Entity ID")
+    private String id;
+
+    @SerializedName("orgName")
+    @Param(description = "The IdP Organization Name")
+    private String orgName;
+
+    @SerializedName("orgUrl")
+    @Param(description = "The IdP Organization URL")
+    private String orgUrl;
+
+    public IdpResponse() {
+        super();
+    }
+
+    public String getId() {
+        return id;
+    }
+
+    public void setId(String id) {
+        this.id = id;
+    }
+
+    public String getOrgName() {
+        return orgName;
+    }
+
+    public void setOrgName(String orgName) {
+        this.orgName = orgName;
+    }
+
+    public String getOrgUrl() {
+        return orgUrl;
+    }
+
+    public void setOrgUrl(String orgUrl) {
+        this.orgUrl = orgUrl;
+    }
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/107595a6/plugins/user-authenticators/saml2/src/org/apache/cloudstack/api/response/SamlAuthorizationResponse.java
----------------------------------------------------------------------
diff --git a/plugins/user-authenticators/saml2/src/org/apache/cloudstack/api/response/SamlAuthorizationResponse.java b/plugins/user-authenticators/saml2/src/org/apache/cloudstack/api/response/SamlAuthorizationResponse.java
new file mode 100644
index 0000000..445ee88
--- /dev/null
+++ b/plugins/user-authenticators/saml2/src/org/apache/cloudstack/api/response/SamlAuthorizationResponse.java
@@ -0,0 +1,68 @@
+// 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.response;
+
+import com.cloud.serializer.Param;
+import com.cloud.user.User;
+import com.google.gson.annotations.SerializedName;
+import org.apache.cloudstack.api.BaseResponse;
+import org.apache.cloudstack.api.EntityReference;
+
+@EntityReference(value = User.class)
+public class SamlAuthorizationResponse extends BaseResponse {
+    @SerializedName("userid")
+    @Param(description = "the user ID")
+    private String userId;
+
+    @SerializedName("status")
+    @Param(description = "the SAML authorization status")
+    private Boolean status;
+
+    @SerializedName("idpid")
+    @Param(description = "the authorized Identity Provider ID")
+    private String idpId;
+
+    public SamlAuthorizationResponse(String userId, Boolean status, String idpId) {
+        this.userId = userId;
+        this.status = status;
+        this.idpId = idpId;
+    }
+
+    public String getUserId() {
+        return userId;
+    }
+
+    public void setUserId(String userId) {
+        this.userId = userId;
+    }
+
+    public Boolean getStatus() {
+        return status;
+    }
+
+    public void setStatus(Boolean status) {
+        this.status = status;
+    }
+
+    public String getIdpId() {
+        return idpId;
+    }
+
+    public void setIdpId(String idpId) {
+        this.idpId = idpId;
+    }
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/107595a6/plugins/user-authenticators/saml2/src/org/apache/cloudstack/saml/SAML2AuthManager.java
----------------------------------------------------------------------
diff --git a/plugins/user-authenticators/saml2/src/org/apache/cloudstack/saml/SAML2AuthManager.java b/plugins/user-authenticators/saml2/src/org/apache/cloudstack/saml/SAML2AuthManager.java
index 9c0d4b4..fc9a6db 100644
--- a/plugins/user-authenticators/saml2/src/org/apache/cloudstack/saml/SAML2AuthManager.java
+++ b/plugins/user-authenticators/saml2/src/org/apache/cloudstack/saml/SAML2AuthManager.java
@@ -17,23 +17,64 @@
 
 package org.apache.cloudstack.saml;
 
+import com.cloud.utils.component.PluggableService;
 import org.apache.cloudstack.api.auth.PluggableAPIAuthenticator;
+import org.apache.cloudstack.framework.config.ConfigKey;
 
-import java.security.KeyPair;
-import java.security.cert.X509Certificate;
+import java.util.Collection;
 
-public interface SAML2AuthManager extends PluggableAPIAuthenticator {
-    public String getServiceProviderId();
-    public String getIdentityProviderId();
+public interface SAML2AuthManager extends PluggableAPIAuthenticator, PluggableService {
 
-    public X509Certificate getIdpSigningKey();
-    public X509Certificate getIdpEncryptionKey();
-    public X509Certificate getSpX509Certificate();
-    public KeyPair getSpKeyPair();
+    public static final ConfigKey<Boolean> SAMLIsPluginEnabled = new ConfigKey<Boolean>("Advanced", Boolean.class, "saml2.enabled", "false",
+            "Indicates whether SAML SSO plugin is enabled or not", true);
 
-    public String getSpSingleSignOnUrl();
-    public String getIdpSingleSignOnUrl();
+    public static final ConfigKey<String> SAMLServiceProviderID = new ConfigKey<String>("Advanced", String.class, "saml2.sp.id", "org.apache.cloudstack",
+            "SAML2 Service Provider Identifier String", true);
 
-    public String getSpSingleLogOutUrl();
-    public String getIdpSingleLogOutUrl();
+    public static final ConfigKey<String> SAMLServiceProviderContactPersonName = new ConfigKey<String>("Advanced", String.class, "saml2.sp.contact.person", "CloudStack Developers",
+            "SAML2 Service Provider Contact Person Name", true);
+
+    public static final ConfigKey<String> SAMLServiceProviderContactEmail = new ConfigKey<String>("Advanced", String.class, "saml2.sp.contact.email", "dev@cloudstack.apache.org",
+            "SAML2 Service Provider Contact Email Address", true);
+
+    public static final ConfigKey<String> SAMLServiceProviderOrgName = new ConfigKey<String>("Advanced", String.class, "saml2.sp.org.name", "Apache CloudStack",
+            "SAML2 Service Provider Organization Name", true);
+
+    public static final ConfigKey<String> SAMLServiceProviderOrgUrl = new ConfigKey<String>("Advanced", String.class, "saml2.sp.org.url", "http://cloudstack.apache.org",
+            "SAML2 Service Provider Organization URL", true);
+
+    public static final ConfigKey<String> SAMLServiceProviderSingleSignOnURL = new ConfigKey<String>("Advanced", String.class, "saml2.sp.sso.url", "http://localhost:8080/client/api?command=samlSso",
+            "SAML2 CloudStack Service Provider Single Sign On URL", true);
+
+    public static final ConfigKey<String> SAMLServiceProviderSingleLogOutURL = new ConfigKey<String>("Advanced", String.class, "saml2.sp.slo.url", "http://localhost:8080/client/",
+            "SAML2 CloudStack Service Provider Single Log Out URL", true);
+
+    public static final ConfigKey<String> SAMLCloudStackRedirectionUrl = new ConfigKey<String>("Advanced", String.class, "saml2.redirect.url", "http://localhost:8080/client",
+            "The CloudStack UI url the SSO should redirected to when successful", true);
+
+    public static final ConfigKey<String> SAMLUserAttributeName = new ConfigKey<String>("Advanced", String.class, "saml2.user.attribute", "uid",
+            "Attribute name to be looked for in SAML response that will contain the username", true);
+
+    public static final ConfigKey<String> SAMLIdentityProviderMetadataURL = new ConfigKey<String>("Advanced", String.class, "saml2.idp.metadata.url", "https://openidp.feide.no/simplesaml/saml2/idp/metadata.php",
+            "SAML2 Identity Provider Metadata XML Url", true);
+
+    public static final ConfigKey<String> SAMLDefaultIdentityProviderId = new ConfigKey<String>("Advanced", String.class, "saml2.default.idpid", "https://openidp.feide.no",
+            "The default IdP entity ID to use only in case of multiple IdPs", true);
+
+    public static final ConfigKey<String> SAMLSignatureAlgorithm = new ConfigKey<String>("Advanced", String.class, "saml2.sigalg", "SHA1",
+            "The algorithm to use to when signing a SAML request. Default is SHA1, allowed algorithms: SHA1, SHA256, SHA384, SHA512", true);
+
+    public static final ConfigKey<Integer> SAMLTimeout = new ConfigKey<Integer>("Advanced", Integer.class, "saml2.timeout", "1800",
+            "SAML2 IDP Metadata refresh interval in seconds, minimum value is set to 300", true);
+
+    public SAMLProviderMetadata getSPMetadata();
+    public SAMLProviderMetadata getIdPMetadata(String entityId);
+    public Collection<SAMLProviderMetadata> getAllIdPMetadata();
+
+    public boolean isUserAuthorized(Long userId, String entityId);
+    public boolean authorizeUser(Long userId, String entityId, boolean enable);
+
+    public void saveToken(String authnId, String domain, String entity);
+    public SAMLTokenVO getToken(String authnId);
+    public void expireTokens();
 }

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/107595a6/plugins/user-authenticators/saml2/src/org/apache/cloudstack/saml/SAML2AuthManagerImpl.java
----------------------------------------------------------------------
diff --git a/plugins/user-authenticators/saml2/src/org/apache/cloudstack/saml/SAML2AuthManagerImpl.java b/plugins/user-authenticators/saml2/src/org/apache/cloudstack/saml/SAML2AuthManagerImpl.java
index 36c9da5..185955c 100644
--- a/plugins/user-authenticators/saml2/src/org/apache/cloudstack/saml/SAML2AuthManagerImpl.java
+++ b/plugins/user-authenticators/saml2/src/org/apache/cloudstack/saml/SAML2AuthManagerImpl.java
@@ -16,28 +16,46 @@
 // under the License.
 package org.apache.cloudstack.saml;
 
-import com.cloud.configuration.Config;
+import com.cloud.domain.Domain;
+import com.cloud.user.DomainManager;
+import com.cloud.user.User;
+import com.cloud.user.UserVO;
+import com.cloud.user.dao.UserDao;
+import com.cloud.utils.PropertiesUtil;
 import com.cloud.utils.component.AdapterBase;
 import org.apache.cloudstack.api.auth.PluggableAPIAuthenticator;
+import org.apache.cloudstack.api.command.AuthorizeSAMLSSOCmd;
 import org.apache.cloudstack.api.command.GetServiceProviderMetaDataCmd;
+import org.apache.cloudstack.api.command.ListIdpsCmd;
+import org.apache.cloudstack.api.command.ListSamlAuthorizationCmd;
 import org.apache.cloudstack.api.command.SAML2LoginAPIAuthenticatorCmd;
 import org.apache.cloudstack.api.command.SAML2LogoutAPIAuthenticatorCmd;
-import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
+import org.apache.cloudstack.framework.config.ConfigKey;
+import org.apache.cloudstack.framework.config.Configurable;
 import org.apache.cloudstack.framework.security.keystore.KeystoreDao;
 import org.apache.cloudstack.framework.security.keystore.KeystoreVO;
-import org.apache.cloudstack.utils.auth.SAMLUtils;
-import org.apache.log4j.Logger;
 import org.apache.commons.codec.binary.Base64;
+import org.apache.commons.httpclient.HttpClient;
+import org.apache.log4j.Logger;
 import org.opensaml.DefaultBootstrap;
 import org.opensaml.common.xml.SAMLConstants;
+import org.opensaml.saml2.metadata.ContactPerson;
+import org.opensaml.saml2.metadata.EmailAddress;
+import org.opensaml.saml2.metadata.EntitiesDescriptor;
 import org.opensaml.saml2.metadata.EntityDescriptor;
 import org.opensaml.saml2.metadata.IDPSSODescriptor;
 import org.opensaml.saml2.metadata.KeyDescriptor;
+import org.opensaml.saml2.metadata.OrganizationDisplayName;
+import org.opensaml.saml2.metadata.OrganizationName;
+import org.opensaml.saml2.metadata.OrganizationURL;
 import org.opensaml.saml2.metadata.SingleLogoutService;
 import org.opensaml.saml2.metadata.SingleSignOnService;
+import org.opensaml.saml2.metadata.provider.AbstractReloadingMetadataProvider;
+import org.opensaml.saml2.metadata.provider.FilesystemMetadataProvider;
 import org.opensaml.saml2.metadata.provider.HTTPMetadataProvider;
 import org.opensaml.saml2.metadata.provider.MetadataProviderException;
 import org.opensaml.xml.ConfigurationException;
+import org.opensaml.xml.XMLObject;
 import org.opensaml.xml.parse.BasicParserPool;
 import org.opensaml.xml.security.credential.UsageType;
 import org.opensaml.xml.security.keyinfo.KeyInfoHelper;
@@ -48,6 +66,7 @@ import javax.inject.Inject;
 import javax.xml.stream.FactoryConfigurationError;
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
+import java.io.File;
 import java.io.IOException;
 import java.io.ObjectInputStream;
 import java.io.ObjectOutput;
@@ -63,61 +82,87 @@ import java.security.cert.CertificateEncodingException;
 import java.security.cert.CertificateException;
 import java.security.cert.X509Certificate;
 import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
+import java.util.Timer;
+import java.util.TimerTask;
 
 @Component
 @Local(value = {SAML2AuthManager.class, PluggableAPIAuthenticator.class})
-public class SAML2AuthManagerImpl extends AdapterBase implements SAML2AuthManager {
+public class SAML2AuthManagerImpl extends AdapterBase implements SAML2AuthManager, Configurable {
     private static final Logger s_logger = Logger.getLogger(SAML2AuthManagerImpl.class);
 
-    private String serviceProviderId;
-    private String identityProviderId;
+    private SAMLProviderMetadata _spMetadata = new SAMLProviderMetadata();
+    private Map<String, SAMLProviderMetadata> _idpMetadataMap = new HashMap<String, SAMLProviderMetadata>();
 
-    private X509Certificate idpSigningKey;
-    private X509Certificate idpEncryptionKey;
-    private X509Certificate spX509Key;
-    private KeyPair spKeyPair;
-
-    private String spSingleSignOnUrl;
     private String idpSingleSignOnUrl;
-
-    private String spSingleLogOutUrl;
     private String idpSingleLogOutUrl;
 
-    private HTTPMetadataProvider idpMetaDataProvider;
+    private Timer _timer;
+    private int _refreshInterval = SAMLPluginConstants.SAML_REFRESH_INTERVAL;
+    private AbstractReloadingMetadataProvider _idpMetaDataProvider;
 
     @Inject
-    ConfigurationDao _configDao;
+    private KeystoreDao _ksDao;
 
     @Inject
-    private KeystoreDao _ksDao;
+    private SAMLTokenDao _samlTokenDao;
+
+    @Inject
+    private UserDao _userDao;
+
+    @Inject
+    DomainManager _domainMgr;
 
     @Override
     public boolean start() {
         if (isSAMLPluginEnabled()) {
             setup();
+            s_logger.info("SAML auth plugin loaded");
+        } else {
+            s_logger.info("SAML auth plugin not enabled so not loading");
         }
         return super.start();
     }
 
-    private boolean setup() {
-        KeystoreVO keyStoreVO = _ksDao.findByName(SAMLUtils.SAMLSP_KEYPAIR);
+    @Override
+    public boolean stop() {
+        if (_timer != null) {
+            _timer.cancel();
+        }
+        return super.stop();
+    }
+
+    private boolean initSP() {
+        KeystoreVO keyStoreVO = _ksDao.findByName(SAMLPluginConstants.SAMLSP_KEYPAIR);
         if (keyStoreVO == null) {
             try {
                 KeyPair keyPair = SAMLUtils.generateRandomKeyPair();
-                _ksDao.save(SAMLUtils.SAMLSP_KEYPAIR, SAMLUtils.savePrivateKey(keyPair.getPrivate()), SAMLUtils.savePublicKey(keyPair.getPublic()), "samlsp-keypair");
-                keyStoreVO = _ksDao.findByName(SAMLUtils.SAMLSP_KEYPAIR);
+                _ksDao.save(SAMLPluginConstants.SAMLSP_KEYPAIR, SAMLUtils.savePrivateKey(keyPair.getPrivate()), SAMLUtils.savePublicKey(keyPair.getPublic()), "samlsp-keypair");
+                keyStoreVO = _ksDao.findByName(SAMLPluginConstants.SAMLSP_KEYPAIR);
+                s_logger.info("No SAML keystore found, created and saved a new Service Provider keypair");
             } catch (NoSuchProviderException | NoSuchAlgorithmException e) {
-                s_logger.error("Unable to create and save SAML keypair");
+                s_logger.error("Unable to create and save SAML keypair: " + e.toString());
             }
         }
 
+        String spId = SAMLServiceProviderID.value();
+        String spSsoUrl = SAMLServiceProviderSingleSignOnURL.value();
+        String spSloUrl = SAMLServiceProviderSingleLogOutURL.value();
+        String spOrgName = SAMLServiceProviderOrgName.value();
+        String spOrgUrl = SAMLServiceProviderOrgUrl.value();
+        String spContactPersonName = SAMLServiceProviderContactPersonName.value();
+        String spContactPersonEmail = SAMLServiceProviderContactEmail.value();
+        KeyPair spKeyPair = null;
+        X509Certificate spX509Key = null;
         if (keyStoreVO != null) {
             PrivateKey privateKey = SAMLUtils.loadPrivateKey(keyStoreVO.getCertificate());
             PublicKey publicKey = SAMLUtils.loadPublicKey(keyStoreVO.getKey());
             if (privateKey != null && publicKey != null) {
                 spKeyPair = new KeyPair(publicKey, privateKey);
-                KeystoreVO x509VO = _ksDao.findByName(SAMLUtils.SAMLSP_X509CERT);
+                KeystoreVO x509VO = _ksDao.findByName(SAMLPluginConstants.SAMLSP_X509CERT);
                 if (x509VO == null) {
                     try {
                         spX509Key = SAMLUtils.generateRandomX509Certificate(spKeyPair);
@@ -125,7 +170,7 @@ public class SAML2AuthManagerImpl extends AdapterBase implements SAML2AuthManage
                         ObjectOutput out = new ObjectOutputStream(bos);
                         out.writeObject(spX509Key);
                         out.flush();
-                        _ksDao.save(SAMLUtils.SAMLSP_X509CERT, Base64.encodeBase64String(bos.toByteArray()), "", "samlsp-x509cert");
+                        _ksDao.save(SAMLPluginConstants.SAMLSP_X509CERT, Base64.encodeBase64String(bos.toByteArray()), "", "samlsp-x509cert");
                         bos.close();
                     } catch (NoSuchAlgorithmException | NoSuchProviderException | CertificateEncodingException | SignatureException | InvalidKeyException | IOException e) {
                         s_logger.error("SAML Plugin won't be able to use X509 signed authentication");
@@ -142,61 +187,194 @@ public class SAML2AuthManagerImpl extends AdapterBase implements SAML2AuthManage
                 }
             }
         }
-
-        this.serviceProviderId = _configDao.getValue(Config.SAMLServiceProviderID.key());
-        this.identityProviderId = _configDao.getValue(Config.SAMLIdentityProviderID.key());
-
-        this.spSingleSignOnUrl = _configDao.getValue(Config.SAMLServiceProviderSingleSignOnURL.key());
-        this.spSingleLogOutUrl = _configDao.getValue(Config.SAMLServiceProviderSingleLogOutURL.key());
-
-        String idpMetaDataUrl = _configDao.getValue(Config.SAMLIdentityProviderMetadataURL.key());
-
-        int tolerance = 30000;
-        String timeout = _configDao.getValue(Config.SAMLTimeout.key());
-        if (timeout != null) {
-            tolerance = Integer.parseInt(timeout);
+        if (spKeyPair != null && spX509Key != null
+                && spId != null && spSsoUrl != null && spSloUrl != null
+                && spOrgName != null && spOrgUrl != null
+                && spContactPersonName != null && spContactPersonEmail != null) {
+            _spMetadata.setEntityId(spId);
+            _spMetadata.setOrganizationName(spOrgName);
+            _spMetadata.setOrganizationUrl(spOrgUrl);
+            _spMetadata.setContactPersonName(spContactPersonName);
+            _spMetadata.setContactPersonEmail(spContactPersonEmail);
+            _spMetadata.setSsoUrl(spSsoUrl);
+            _spMetadata.setSloUrl(spSloUrl);
+            _spMetadata.setKeyPair(spKeyPair);
+            _spMetadata.setSigningCertificate(spX509Key);
+            _spMetadata.setEncryptionCertificate(spX509Key);
+            return true;
         }
+        return false;
+    }
 
-        try {
-            DefaultBootstrap.bootstrap();
-            idpMetaDataProvider = new HTTPMetadataProvider(idpMetaDataUrl, tolerance);
-            idpMetaDataProvider.setRequireValidMetadata(true);
-            idpMetaDataProvider.setParserPool(new BasicParserPool());
-            idpMetaDataProvider.initialize();
+    private void addIdpToMap(EntityDescriptor descriptor, Map<String, SAMLProviderMetadata> idpMap) {
+        SAMLProviderMetadata idpMetadata = new SAMLProviderMetadata();
+        idpMetadata.setEntityId(descriptor.getEntityID());
+        s_logger.debug("Adding IdP to the list of discovered IdPs: " + descriptor.getEntityID());
+        if (descriptor.getOrganization() != null) {
+            if (descriptor.getOrganization().getDisplayNames() != null) {
+                for (OrganizationDisplayName orgName : descriptor.getOrganization().getDisplayNames()) {
+                    if (orgName != null && orgName.getName() != null) {
+                        idpMetadata.setOrganizationName(orgName.getName().getLocalString());
+                        break;
+                    }
+                }
+            }
+            if (idpMetadata.getOrganizationName() == null && descriptor.getOrganization().getOrganizationNames() != null) {
+                for (OrganizationName orgName : descriptor.getOrganization().getOrganizationNames()) {
+                    if (orgName != null && orgName.getName() != null) {
+                        idpMetadata.setOrganizationName(orgName.getName().getLocalString());
+                        break;
+                    }
+                }
+            }
+            if (descriptor.getOrganization().getURLs() != null) {
+                for (OrganizationURL organizationURL : descriptor.getOrganization().getURLs()) {
+                    if (organizationURL != null && organizationURL.getURL() != null) {
+                        idpMetadata.setOrganizationUrl(organizationURL.getURL().getLocalString());
+                        break;
+                    }
+                }
+            }
+        }
+        if (descriptor.getContactPersons() != null) {
+            for (ContactPerson person : descriptor.getContactPersons()) {
+                if (person == null || (person.getGivenName() == null && person.getSurName() == null)
+                        || person.getEmailAddresses() == null) {
+                    continue;
+                }
+                if (person.getGivenName() != null) {
+                    idpMetadata.setContactPersonName(person.getGivenName().getName());
 
-            EntityDescriptor idpEntityDescriptor = idpMetaDataProvider.getEntityDescriptor(this.identityProviderId);
+                } else if (person.getSurName() != null) {
+                    idpMetadata.setContactPersonName(person.getSurName().getName());
+                }
+                for (EmailAddress emailAddress : person.getEmailAddresses()) {
+                    if (emailAddress != null && emailAddress.getAddress() != null) {
+                        idpMetadata.setContactPersonEmail(emailAddress.getAddress());
+                    }
+                }
+                if (idpMetadata.getContactPersonName() != null && idpMetadata.getContactPersonEmail() != null) {
+                    break;
+                }
+            }
+        }
 
-            IDPSSODescriptor idpssoDescriptor = idpEntityDescriptor.getIDPSSODescriptor(SAMLConstants.SAML20P_NS);
-            if (idpssoDescriptor != null) {
-                for (SingleSignOnService ssos: idpssoDescriptor.getSingleSignOnServices()) {
+        IDPSSODescriptor idpDescriptor = descriptor.getIDPSSODescriptor(SAMLConstants.SAML20P_NS);
+        if (idpDescriptor != null) {
+            if (idpDescriptor.getSingleSignOnServices() != null) {
+                for (SingleSignOnService ssos : idpDescriptor.getSingleSignOnServices()) {
                     if (ssos.getBinding().equals(SAMLConstants.SAML2_REDIRECT_BINDING_URI)) {
-                        this.idpSingleSignOnUrl = ssos.getLocation();
+                        idpMetadata.setSsoUrl(ssos.getLocation());
                     }
                 }
-
-                for (SingleLogoutService slos: idpssoDescriptor.getSingleLogoutServices()) {
+            }
+            if (idpDescriptor.getSingleLogoutServices() != null) {
+                for (SingleLogoutService slos : idpDescriptor.getSingleLogoutServices()) {
                     if (slos.getBinding().equals(SAMLConstants.SAML2_REDIRECT_BINDING_URI)) {
-                        this.idpSingleLogOutUrl = slos.getLocation();
+                        idpMetadata.setSloUrl(slos.getLocation());
                     }
                 }
+            }
 
-                for (KeyDescriptor kd: idpssoDescriptor.getKeyDescriptors()) {
+            X509Certificate unspecifiedKey = null;
+            if (idpDescriptor.getKeyDescriptors() != null) {
+                for (KeyDescriptor kd : idpDescriptor.getKeyDescriptors()) {
                     if (kd.getUse() == UsageType.SIGNING) {
                         try {
-                            this.idpSigningKey = KeyInfoHelper.getCertificates(kd.getKeyInfo()).get(0);
+                            idpMetadata.setSigningCertificate(KeyInfoHelper.getCertificates(kd.getKeyInfo()).get(0));
                         } catch (CertificateException ignored) {
                         }
                     }
                     if (kd.getUse() == UsageType.ENCRYPTION) {
                         try {
-                            this.idpEncryptionKey = KeyInfoHelper.getCertificates(kd.getKeyInfo()).get(0);
+                            idpMetadata.setEncryptionCertificate(KeyInfoHelper.getCertificates(kd.getKeyInfo()).get(0));
+                        } catch (CertificateException ignored) {
+                        }
+                    }
+                    if (kd.getUse() == UsageType.UNSPECIFIED) {
+                        try {
+                            unspecifiedKey = KeyInfoHelper.getCertificates(kd.getKeyInfo()).get(0);
                         } catch (CertificateException ignored) {
                         }
                     }
                 }
+            }
+            if (idpMetadata.getSigningCertificate() == null && unspecifiedKey != null) {
+                idpMetadata.setSigningCertificate(unspecifiedKey);
+            }
+            if (idpMetadata.getEncryptionCertificate() == null && unspecifiedKey != null) {
+                idpMetadata.setEncryptionCertificate(unspecifiedKey);
+            }
+            if (idpMap.containsKey(idpMetadata.getEntityId())) {
+                s_logger.warn("Duplicate IdP metadata found with entity Id: " + idpMetadata.getEntityId());
+            }
+            idpMap.put(idpMetadata.getEntityId(), idpMetadata);
+        }
+    }
+
+    private void discoverAndAddIdp(XMLObject metadata, Map<String, SAMLProviderMetadata> idpMap) {
+        if (metadata instanceof EntityDescriptor) {
+            EntityDescriptor entityDescriptor = (EntityDescriptor) metadata;
+            addIdpToMap(entityDescriptor, idpMap);
+        } else if (metadata instanceof EntitiesDescriptor) {
+            EntitiesDescriptor entitiesDescriptor = (EntitiesDescriptor) metadata;
+            if (entitiesDescriptor.getEntityDescriptors() != null) {
+                for (EntityDescriptor entityDescriptor: entitiesDescriptor.getEntityDescriptors()) {
+                    addIdpToMap(entityDescriptor, idpMap);
+                }
+            }
+            if (entitiesDescriptor.getEntitiesDescriptors() != null) {
+                for (EntitiesDescriptor entitiesDescriptorInner: entitiesDescriptor.getEntitiesDescriptors()) {
+                    discoverAndAddIdp(entitiesDescriptorInner, idpMap);
+                }
+            }
+        }
+    }
+
+    class MetadataRefreshTask extends TimerTask {
+        @Override
+        public void run() {
+            if (_idpMetaDataProvider == null) {
+                return;
+            }
+            s_logger.debug("Starting SAML IDP Metadata Refresh Task");
+            Map <String, SAMLProviderMetadata> metadataMap = new HashMap<String, SAMLProviderMetadata>();
+            try {
+                discoverAndAddIdp(_idpMetaDataProvider.getMetadata(), metadataMap);
+                _idpMetadataMap = metadataMap;
+                expireTokens();
+                s_logger.debug("Finished refreshing SAML Metadata and expiring old auth tokens");
+            } catch (MetadataProviderException e) {
+                s_logger.warn("SAML Metadata Refresh task failed with exception: " + e.getMessage());
+            }
+
+        }
+    }
+
+    private boolean setup() {
+        if (!initSP()) {
+            s_logger.error("SAML Plugin failed to initialize, please fix the configuration and restart management server");
+            return false;
+        }
+        _timer = new Timer();
+        final HttpClient client = new HttpClient();
+        final String idpMetaDataUrl = SAMLIdentityProviderMetadataURL.value();
+        if (SAMLTimeout.value() != null && SAMLTimeout.value() > SAMLPluginConstants.SAML_REFRESH_INTERVAL) {
+            _refreshInterval = SAMLTimeout.value();
+        }
+        try {
+            DefaultBootstrap.bootstrap();
+            if (idpMetaDataUrl.startsWith("http")) {
+                _idpMetaDataProvider = new HTTPMetadataProvider(_timer, client, idpMetaDataUrl);
             } else {
-                s_logger.warn("Provided IDP XML Metadata does not contain IDPSSODescriptor, SAML authentication may not work");
+                File metadataFile = PropertiesUtil.findConfigFile(idpMetaDataUrl);
+                s_logger.debug("Provided Metadata is not a URL, trying to read metadata file from local path: " + metadataFile.getAbsolutePath());
+                _idpMetaDataProvider = new FilesystemMetadataProvider(_timer, metadataFile);
             }
+            _idpMetaDataProvider.setRequireValidMetadata(true);
+            _idpMetaDataProvider.setParserPool(new BasicParserPool());
+            _idpMetaDataProvider.initialize();
+            _timer.scheduleAtFixedRate(new MetadataRefreshTask(), 0, _refreshInterval * 1000);
         } catch (MetadataProviderException e) {
             s_logger.error("Unable to read SAML2 IDP MetaData URL, error:" + e.getMessage());
             s_logger.error("SAML2 Authentication may be unavailable");
@@ -204,70 +382,138 @@ public class SAML2AuthManagerImpl extends AdapterBase implements SAML2AuthManage
             s_logger.error("OpenSAML bootstrapping failed: error: " + e.getMessage());
         } catch (NullPointerException e) {
             s_logger.error("Unable to setup SAML Auth Plugin due to NullPointerException" +
-                    " please check the SAML IDP metadata URL and entity ID in global settings: " + e.getMessage());
-        }
-
-        if (this.idpSingleLogOutUrl == null || this.idpSingleSignOnUrl == null) {
-            s_logger.error("SAML based authentication won't work");
+                    " please check the SAML global settings: " + e.getMessage());
         }
-
         return true;
     }
 
     @Override
-    public List<Class<?>> getAuthCommands() {
-        List<Class<?>> cmdList = new ArrayList<Class<?>>();
-        if (!isSAMLPluginEnabled()) {
-            return cmdList;
+    public SAMLProviderMetadata getSPMetadata() {
+        return _spMetadata;
+    }
+
+    @Override
+    public SAMLProviderMetadata getIdPMetadata(String entityId) {
+        if (entityId != null && _idpMetadataMap.containsKey(entityId)) {
+            return _idpMetadataMap.get(entityId);
         }
-        cmdList.add(SAML2LoginAPIAuthenticatorCmd.class);
-        cmdList.add(SAML2LogoutAPIAuthenticatorCmd.class);
-        cmdList.add(GetServiceProviderMetaDataCmd.class);
-        return cmdList;
+        String defaultIdpId = SAMLDefaultIdentityProviderId.value();
+        if (defaultIdpId != null && _idpMetadataMap.containsKey(defaultIdpId)) {
+            return _idpMetadataMap.get(defaultIdpId);
+        }
+        // In case of a single IdP, return that as default
+        if (_idpMetadataMap.size() == 1) {
+            return _idpMetadataMap.values().iterator().next();
+        }
+        return null;
     }
 
-    public String getServiceProviderId() {
-        return serviceProviderId;
+    @Override
+    public Collection<SAMLProviderMetadata> getAllIdPMetadata() {
+        return _idpMetadataMap.values();
     }
 
-    public String getIdpSingleSignOnUrl() {
-        return this.idpSingleSignOnUrl;
+    @Override
+    public boolean isUserAuthorized(Long userId, String entityId) {
+        UserVO user = _userDao.getUser(userId);
+        if (user != null) {
+            if (user.getSource().equals(User.Source.SAML2) &&
+                    user.getExternalEntity().equalsIgnoreCase(entityId)) {
+                return true;
+            }
+        }
+        return false;
     }
 
-    public String getIdpSingleLogOutUrl() {
-        return this.idpSingleLogOutUrl;
+    @Override
+    public boolean authorizeUser(Long userId, String entityId, boolean enable) {
+        UserVO user = _userDao.getUser(userId);
+        if (user != null) {
+            if (enable) {
+                user.setExternalEntity(entityId);
+                user.setSource(User.Source.SAML2);
+            } else {
+                if (user.getSource().equals(User.Source.SAML2)) {
+                    user.setSource(User.Source.SAML2DISABLED);
+                } else {
+                    return false;
+                }
+            }
+            _userDao.update(user.getId(), user);
+            return true;
+        }
+        return false;
     }
 
-    public String getSpSingleSignOnUrl() {
-        return spSingleSignOnUrl;
+    @Override
+    public void saveToken(String authnId, String domainPath, String entity) {
+        Long domainId = null;
+        if (domainPath != null) {
+            Domain domain = _domainMgr.findDomainByPath(domainPath);
+            if (domain != null) {
+                domainId = domain.getId();
+            }
+        }
+        SAMLTokenVO token = new SAMLTokenVO(authnId, domainId, entity);
+        if (_samlTokenDao.findByUuid(authnId) == null) {
+            _samlTokenDao.persist(token);
+        } else {
+            s_logger.warn("Duplicate SAML token for entity=" + entity + " token id=" + authnId + " domain=" + domainPath);
+        }
     }
 
-    public String getSpSingleLogOutUrl() {
-        return spSingleLogOutUrl;
+    @Override
+    public SAMLTokenVO getToken(String authnId) {
+        return _samlTokenDao.findByUuid(authnId);
     }
 
-    public String getIdentityProviderId() {
-        return identityProviderId;
+    @Override
+    public void expireTokens() {
+        _samlTokenDao.expireTokens();
     }
 
-    public X509Certificate getIdpSigningKey() {
-        return idpSigningKey;
+    public Boolean isSAMLPluginEnabled() {
+        return SAMLIsPluginEnabled.value();
     }
 
-    public X509Certificate getIdpEncryptionKey() {
-        return idpEncryptionKey;
+    @Override
+    public String getConfigComponentName() {
+        return "SAML2-PLUGIN";
     }
 
-    public Boolean isSAMLPluginEnabled() {
-        return Boolean.valueOf(_configDao.getValue(Config.SAMLIsPluginEnabled.key()));
+    @Override
+    public List<Class<?>> getAuthCommands() {
+        List<Class<?>> cmdList = new ArrayList<Class<?>>();
+        if (!isSAMLPluginEnabled()) {
+            return cmdList;
+        }
+        cmdList.add(SAML2LoginAPIAuthenticatorCmd.class);
+        cmdList.add(SAML2LogoutAPIAuthenticatorCmd.class);
+        cmdList.add(GetServiceProviderMetaDataCmd.class);
+        cmdList.add(ListIdpsCmd.class);
+        return cmdList;
     }
 
-    public X509Certificate getSpX509Certificate() {
-        return spX509Key;
+    @Override
+    public List<Class<?>> getCommands() {
+        List<Class<?>> cmdList = new ArrayList<Class<?>>();
+        if (!isSAMLPluginEnabled()) {
+            return cmdList;
+        }
+        cmdList.add(AuthorizeSAMLSSOCmd.class);
+        cmdList.add(ListSamlAuthorizationCmd.class);
+        return cmdList;
     }
 
     @Override
-    public KeyPair getSpKeyPair() {
-        return spKeyPair;
+    public ConfigKey<?>[] getConfigKeys() {
+        return new ConfigKey<?>[] {
+                SAMLIsPluginEnabled, SAMLServiceProviderID,
+                SAMLServiceProviderContactPersonName, SAMLServiceProviderContactEmail,
+                SAMLServiceProviderOrgName, SAMLServiceProviderOrgUrl,
+                SAMLServiceProviderSingleSignOnURL, SAMLServiceProviderSingleLogOutURL,
+                SAMLCloudStackRedirectionUrl, SAMLUserAttributeName,
+                SAMLIdentityProviderMetadataURL, SAMLDefaultIdentityProviderId,
+                SAMLSignatureAlgorithm, SAMLTimeout};
     }
 }

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/107595a6/plugins/user-authenticators/saml2/src/org/apache/cloudstack/saml/SAML2UserAuthenticator.java
----------------------------------------------------------------------
diff --git a/plugins/user-authenticators/saml2/src/org/apache/cloudstack/saml/SAML2UserAuthenticator.java b/plugins/user-authenticators/saml2/src/org/apache/cloudstack/saml/SAML2UserAuthenticator.java
index 68bd81c..5c8a390 100644
--- a/plugins/user-authenticators/saml2/src/org/apache/cloudstack/saml/SAML2UserAuthenticator.java
+++ b/plugins/user-authenticators/saml2/src/org/apache/cloudstack/saml/SAML2UserAuthenticator.java
@@ -21,12 +21,20 @@ import com.cloud.user.UserAccount;
 import com.cloud.user.dao.UserAccountDao;
 import com.cloud.user.dao.UserDao;
 import com.cloud.utils.Pair;
-import org.apache.cloudstack.utils.auth.SAMLUtils;
 import org.apache.cxf.common.util.StringUtils;
 import org.apache.log4j.Logger;
+import org.opensaml.DefaultBootstrap;
+import org.opensaml.saml2.core.Response;
+import org.opensaml.saml2.core.StatusCode;
+import org.opensaml.xml.ConfigurationException;
+import org.opensaml.xml.io.UnmarshallingException;
+import org.xml.sax.SAXException;
 
 import javax.ejb.Local;
 import javax.inject.Inject;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.stream.FactoryConfigurationError;
+import java.io.IOException;
 import java.util.Map;
 
 @Local(value = {UserAuthenticator.class})
@@ -50,13 +58,23 @@ public class SAML2UserAuthenticator extends DefaultUserAuthenticator {
         }
 
         final UserAccount userAccount = _userAccountDao.getUserAccount(username, domainId);
-        if (userAccount == null) {
-            s_logger.debug("Unable to find user with " + username + " in domain " + domainId);
+        if (userAccount == null || userAccount.getSource() != User.Source.SAML2) {
+            s_logger.debug("Unable to find user with " + username + " in domain " + domainId + ", or user source is not SAML2");
             return new Pair<Boolean, ActionOnFailedAuthentication>(false, null);
         } else {
             User user = _userDao.getUser(userAccount.getId());
-            if (user != null && SAMLUtils.checkSAMLUser(user.getUuid(), username) &&
-                    requestParameters != null && requestParameters.containsKey(SAMLUtils.SAML_RESPONSE)) {
+            if (user != null && requestParameters != null && requestParameters.containsKey(SAMLPluginConstants.SAML_RESPONSE)) {
+                final String samlResponse = ((String[])requestParameters.get(SAMLPluginConstants.SAML_RESPONSE))[0];
+                Response responseObject = null;
+                try {
+                    DefaultBootstrap.bootstrap();
+                    responseObject = SAMLUtils.decodeSAMLResponse(samlResponse);
+                } catch (ConfigurationException | FactoryConfigurationError | ParserConfigurationException | SAXException | IOException | UnmarshallingException e) {
+                    return new Pair<Boolean, ActionOnFailedAuthentication>(false, null);
+                }
+                if (!responseObject.getStatus().getStatusCode().getValue().equals(StatusCode.SUCCESS_URI)) {
+                    return new Pair<Boolean, ActionOnFailedAuthentication>(false, null);
+                }
                 return new Pair<Boolean, ActionOnFailedAuthentication>(true, null);
             }
         }

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/107595a6/plugins/user-authenticators/saml2/src/org/apache/cloudstack/saml/SAMLPluginConstants.java
----------------------------------------------------------------------
diff --git a/plugins/user-authenticators/saml2/src/org/apache/cloudstack/saml/SAMLPluginConstants.java b/plugins/user-authenticators/saml2/src/org/apache/cloudstack/saml/SAMLPluginConstants.java
new file mode 100644
index 0000000..5f806e2
--- /dev/null
+++ b/plugins/user-authenticators/saml2/src/org/apache/cloudstack/saml/SAMLPluginConstants.java
@@ -0,0 +1,30 @@
+//
+// 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.saml;
+
+public class SAMLPluginConstants {
+    public static final int SAML_REFRESH_INTERVAL = 300;
+
+    public static final String SAML_RESPONSE = "SAMLResponse";
+    public static final String SAML_IDPID = "SAML_IDPID";
+    public static final String SAML_SESSIONID = "SAML_SESSIONID";
+    public static final String SAML_NAMEID = "SAML_NAMEID";
+    public static final String SAMLSP_KEYPAIR = "SAMLSP_KEYPAIR";
+    public static final String SAMLSP_X509CERT = "SAMLSP_X509CERT";
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/107595a6/plugins/user-authenticators/saml2/src/org/apache/cloudstack/saml/SAMLProviderMetadata.java
----------------------------------------------------------------------
diff --git a/plugins/user-authenticators/saml2/src/org/apache/cloudstack/saml/SAMLProviderMetadata.java b/plugins/user-authenticators/saml2/src/org/apache/cloudstack/saml/SAMLProviderMetadata.java
new file mode 100644
index 0000000..c7138a1
--- /dev/null
+++ b/plugins/user-authenticators/saml2/src/org/apache/cloudstack/saml/SAMLProviderMetadata.java
@@ -0,0 +1,122 @@
+// 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.saml;
+
+import java.security.KeyPair;
+import java.security.cert.X509Certificate;
+
+public class SAMLProviderMetadata {
+    private String entityId;
+    private String organizationName;
+    private String organizationUrl;
+    private String contactPersonName;
+    private String contactPersonEmail;
+    private String ssoUrl;
+    private String sloUrl;
+    private KeyPair keyPair;
+    private X509Certificate signingCertificate;
+    private X509Certificate encryptionCertificate;
+
+    public SAMLProviderMetadata() {
+    }
+
+    public void setCommonCertificate(X509Certificate certificate) {
+        this.signingCertificate = certificate;
+        this.encryptionCertificate = certificate;
+    }
+
+    public String getEntityId() {
+        return entityId;
+    }
+
+    public void setEntityId(String entityId) {
+        this.entityId = entityId;
+    }
+
+    public String getContactPersonName() {
+        return contactPersonName;
+    }
+
+    public void setContactPersonName(String contactPersonName) {
+        this.contactPersonName = contactPersonName;
+    }
+
+    public String getContactPersonEmail() {
+        return contactPersonEmail;
+    }
+
+    public void setContactPersonEmail(String contactPersonEmail) {
+        this.contactPersonEmail = contactPersonEmail;
+    }
+
+    public String getOrganizationName() {
+        return organizationName;
+    }
+
+    public void setOrganizationName(String organizationName) {
+        this.organizationName = organizationName;
+    }
+
+    public String getOrganizationUrl() {
+        return organizationUrl;
+    }
+
+    public void setOrganizationUrl(String organizationUrl) {
+        this.organizationUrl = organizationUrl;
+    }
+
+    public KeyPair getKeyPair() {
+        return keyPair;
+    }
+
+    public void setKeyPair(KeyPair keyPair) {
+        this.keyPair = keyPair;
+    }
+
+    public X509Certificate getSigningCertificate() {
+        return signingCertificate;
+    }
+
+    public void setSigningCertificate(X509Certificate signingCertificate) {
+        this.signingCertificate = signingCertificate;
+    }
+
+    public X509Certificate getEncryptionCertificate() {
+        return encryptionCertificate;
+    }
+
+    public void setEncryptionCertificate(X509Certificate encryptionCertificate) {
+        this.encryptionCertificate = encryptionCertificate;
+    }
+
+    public String getSsoUrl() {
+        return ssoUrl;
+    }
+
+    public void setSsoUrl(String ssoUrl) {
+        this.ssoUrl = ssoUrl;
+    }
+
+    public String getSloUrl() {
+        return sloUrl;
+    }
+
+    public void setSloUrl(String sloUrl) {
+        this.sloUrl = sloUrl;
+    }
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/107595a6/plugins/user-authenticators/saml2/src/org/apache/cloudstack/saml/SAMLTokenDao.java
----------------------------------------------------------------------
diff --git a/plugins/user-authenticators/saml2/src/org/apache/cloudstack/saml/SAMLTokenDao.java b/plugins/user-authenticators/saml2/src/org/apache/cloudstack/saml/SAMLTokenDao.java
new file mode 100644
index 0000000..b045562
--- /dev/null
+++ b/plugins/user-authenticators/saml2/src/org/apache/cloudstack/saml/SAMLTokenDao.java
@@ -0,0 +1,23 @@
+// 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.saml;
+
+import com.cloud.utils.db.GenericDao;
+
+public interface SAMLTokenDao extends GenericDao<SAMLTokenVO, Long> {
+    public void expireTokens();
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/107595a6/plugins/user-authenticators/saml2/src/org/apache/cloudstack/saml/SAMLTokenDaoImpl.java
----------------------------------------------------------------------
diff --git a/plugins/user-authenticators/saml2/src/org/apache/cloudstack/saml/SAMLTokenDaoImpl.java b/plugins/user-authenticators/saml2/src/org/apache/cloudstack/saml/SAMLTokenDaoImpl.java
new file mode 100644
index 0000000..eb106d9
--- /dev/null
+++ b/plugins/user-authenticators/saml2/src/org/apache/cloudstack/saml/SAMLTokenDaoImpl.java
@@ -0,0 +1,51 @@
+// 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.saml;
+
+import com.cloud.utils.db.DB;
+import com.cloud.utils.db.GenericDaoBase;
+import com.cloud.utils.db.TransactionLegacy;
+import com.cloud.utils.exception.CloudRuntimeException;
+import org.springframework.stereotype.Component;
+
+import javax.ejb.Local;
+import java.sql.PreparedStatement;
+
+@DB
+@Component
+@Local(value = {SAMLTokenDao.class})
+public class SAMLTokenDaoImpl extends GenericDaoBase<SAMLTokenVO, Long> implements SAMLTokenDao {
+
+    public SAMLTokenDaoImpl() {
+        super();
+    }
+
+    @Override
+    public void expireTokens() {
+        TransactionLegacy txn = TransactionLegacy.currentTxn();
+        try {
+            txn.start();
+            String sql = "DELETE FROM `saml_token` WHERE `created` < (NOW() - INTERVAL 1 HOUR)";
+            PreparedStatement pstmt = txn.prepareAutoCloseStatement(sql);
+            pstmt.executeUpdate();
+            txn.commit();
+        } catch (Exception e) {
+            txn.rollback();
+            throw new CloudRuntimeException("Unable to flush old SAML tokens due to exception", e);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/107595a6/plugins/user-authenticators/saml2/src/org/apache/cloudstack/saml/SAMLTokenVO.java
----------------------------------------------------------------------
diff --git a/plugins/user-authenticators/saml2/src/org/apache/cloudstack/saml/SAMLTokenVO.java b/plugins/user-authenticators/saml2/src/org/apache/cloudstack/saml/SAMLTokenVO.java
new file mode 100644
index 0000000..c8ac2f1
--- /dev/null
+++ b/plugins/user-authenticators/saml2/src/org/apache/cloudstack/saml/SAMLTokenVO.java
@@ -0,0 +1,97 @@
+// 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.saml;
+
+import com.cloud.utils.db.GenericDao;
+import org.apache.cloudstack.api.Identity;
+import org.apache.cloudstack.api.InternalIdentity;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+import javax.persistence.Table;
+import java.util.Date;
+
+@Entity
+@Table(name = "saml_token")
+public class SAMLTokenVO implements Identity, InternalIdentity {
+    @Id
+    @GeneratedValue(strategy = GenerationType.IDENTITY)
+    @Column(name = "id")
+    private long id;
+
+    @Column(name = "uuid")
+    private String uuid;
+
+    @Column(name = "domain_id")
+    private Long domainId = null;
+
+    @Column(name = "entity")
+    private String entity = null;
+
+    @Column(name = GenericDao.CREATED_COLUMN)
+    private Date created;
+
+    public SAMLTokenVO() {
+    }
+
+    public SAMLTokenVO(String uuid, Long domainId, String entity) {
+        this.uuid = uuid;
+        this.domainId = domainId;
+        this.entity = entity;
+    }
+
+    @Override
+    public long getId() {
+        return id;
+    }
+
+    @Override
+    public String getUuid() {
+        return uuid;
+    }
+
+    public void setUuid(String uuid) {
+        this.uuid = uuid;
+    }
+
+    public Long getDomainId() {
+        return domainId;
+    }
+
+    public void setDomainId(long domainId) {
+        this.domainId = domainId;
+    }
+
+    public String getEntity() {
+        return entity;
+    }
+
+    public void setEntity(String entity) {
+        this.entity = entity;
+    }
+
+    public Date getCreated() {
+        return created;
+    }
+
+    public void setCreated(Date created) {
+        this.created = created;
+    }
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/107595a6/plugins/user-authenticators/saml2/src/org/apache/cloudstack/saml/SAMLUtils.java
----------------------------------------------------------------------
diff --git a/plugins/user-authenticators/saml2/src/org/apache/cloudstack/saml/SAMLUtils.java b/plugins/user-authenticators/saml2/src/org/apache/cloudstack/saml/SAMLUtils.java
new file mode 100644
index 0000000..0216ad7
--- /dev/null
+++ b/plugins/user-authenticators/saml2/src/org/apache/cloudstack/saml/SAMLUtils.java
@@ -0,0 +1,354 @@
+//
+// 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.saml;
+
+import com.cloud.utils.HttpUtils;
+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.DefaultBootstrap;
+import org.opensaml.common.SAMLVersion;
+import org.opensaml.common.xml.SAMLConstants;
+import org.opensaml.saml2.core.Assertion;
+import org.opensaml.saml2.core.Attribute;
+import org.opensaml.saml2.core.AttributeStatement;
+import org.opensaml.saml2.core.AuthnContext;
+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.RequestedAuthnContext;
+import org.opensaml.saml2.core.Response;
+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.RequestedAuthnContextBuilder;
+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 javax.xml.stream.FactoryConfigurationError;
+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.List;
+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 String generateSecureRandomId() {
+        return new BigInteger(160, new SecureRandom()).toString(32);
+    }
+
+    public static String getValueFromAttributeStatements(final List<AttributeStatement> attributeStatements, final String attributeKey) {
+        if (attributeStatements == null || attributeStatements.size() < 1 || attributeKey == null) {
+            return null;
+        }
+        for (AttributeStatement attributeStatement : attributeStatements) {
+            if (attributeStatement == null || attributeStatements.size() < 1) {
+                continue;
+            }
+            for (Attribute attribute : attributeStatement.getAttributes()) {
+                if (attribute.getAttributeValues() != null && attribute.getAttributeValues().size() > 0) {
+                    String value = attribute.getAttributeValues().get(0).getDOM().getTextContent();
+                    s_logger.debug("SAML attribute name: " + attribute.getName() + " friendly-name:" + attribute.getFriendlyName() + " value:" + value);
+                    if (attributeKey.equals(attribute.getName()) || attributeKey.equals(attribute.getFriendlyName())) {
+                        return value;
+                    }
+                }
+            }
+        }
+        return null;
+    }
+
+    public static String getValueFromAssertions(final List<Assertion> assertions, final String attributeKey) {
+        if (assertions == null || attributeKey == null) {
+            return null;
+        }
+        for (Assertion assertion : assertions) {
+            String value = getValueFromAttributeStatements(assertion.getAttributeStatements(), attributeKey);
+            if (value != null) {
+                return value;
+            }
+        }
+        return null;
+    }
+
+    public static String buildAuthnRequestUrl(final String authnId, final SAMLProviderMetadata spMetadata, final SAMLProviderMetadata idpMetadata, final String signatureAlgorithm) {
+        String redirectUrl = "";
+        try {
+            DefaultBootstrap.bootstrap();
+            AuthnRequest authnRequest = SAMLUtils.buildAuthnRequestObject(authnId, spMetadata.getEntityId(), idpMetadata.getSsoUrl(), spMetadata.getSsoUrl());
+            PrivateKey privateKey = null;
+            if (spMetadata.getKeyPair() != null) {
+                privateKey = spMetadata.getKeyPair().getPrivate();
+            }
+            redirectUrl = idpMetadata.getSsoUrl() + "?" + SAMLUtils.generateSAMLRequestSignature("SAMLRequest=" + SAMLUtils.encodeSAMLRequest(authnRequest), privateKey, signatureAlgorithm);
+        } catch (ConfigurationException | FactoryConfigurationError | MarshallingException | IOException | NoSuchAlgorithmException | InvalidKeyException | java.security.SignatureException e) {
+            s_logger.error("SAML AuthnRequest message building error: " + e.getMessage());
+        }
+        return redirectUrl;
+    }
+
+    public static AuthnRequest buildAuthnRequestObject(final String authnId, final String spId, final String idpUrl, final String consumerUrl) {
+        // Issuer object
+        IssuerBuilder issuerBuilder = new IssuerBuilder();
+        Issuer issuer = issuerBuilder.buildObject();
+        issuer.setValue(spId);
+
+        // AuthnContextClass
+        AuthnContextClassRefBuilder authnContextClassRefBuilder = new AuthnContextClassRefBuilder();
+        AuthnContextClassRef authnContextClassRef = authnContextClassRefBuilder.buildObject(
+                SAMLConstants.SAML20_NS,
+                "AuthnContextClassRef", "saml");
+        authnContextClassRef.setAuthnContextClassRef(AuthnContext.PPT_AUTHN_CTX);
+
+        // AuthnContext
+        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.setIssueInstant(new DateTime());
+        authnRequest.setProtocolBinding(SAMLConstants.SAML2_POST_BINDING_URI);
+        authnRequest.setAssertionConsumerServiceURL(consumerUrl);
+        authnRequest.setProviderName(spId);
+        authnRequest.setIssuer(issuer);
+        authnRequest.setRequestedAuthnContext(requestedAuthnContext);
+
+        return authnRequest;
+    }
+
+    public static LogoutRequest buildLogoutRequest(String logoutUrl, String spId, String nameIdString) {
+        Issuer issuer = new IssuerBuilder().buildObject();
+        issuer.setValue(spId);
+        NameID nameID = new NameIDBuilder().buildObject();
+        nameID.setValue(nameIdString);
+        LogoutRequest logoutRequest = new LogoutRequestBuilder().buildObject();
+        logoutRequest.setID(generateSecureRandomId());
+        logoutRequest.setDestination(logoutUrl);
+        logoutRequest.setVersion(SAMLVersion.VERSION_20);
+        logoutRequest.setIssueInstant(new DateTime());
+        logoutRequest.setIssuer(issuer);
+        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(final String urlEncodedString, final PrivateKey signingKey, final String sigAlgorithmName)
+            throws NoSuchAlgorithmException, SignatureException, InvalidKeyException, UnsupportedEncodingException {
+        if (signingKey == null) {
+            return urlEncodedString;
+        }
+
+        String opensamlAlgoIdSignature = SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA1;
+        String javaSignatureAlgorithmName = "SHA1withRSA";
+
+        if (sigAlgorithmName.equalsIgnoreCase("SHA256")) {
+            opensamlAlgoIdSignature = SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA256;
+            javaSignatureAlgorithmName = "SHA256withRSA";
+        } else if (sigAlgorithmName.equalsIgnoreCase("SHA384")) {
+            opensamlAlgoIdSignature = SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA384;
+            javaSignatureAlgorithmName = "SHA384withRSA";
+        } else if (sigAlgorithmName.equalsIgnoreCase("SHA512")) {
+            opensamlAlgoIdSignature = SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA512;
+            javaSignatureAlgorithmName = "SHA512withRSA";
+        }
+
+        String url = urlEncodedString + "&SigAlg=" + URLEncoder.encode(opensamlAlgoIdSignature, HttpUtils.UTF_8);
+        Signature signature = Signature.getInstance(javaSignatureAlgorithmName);
+        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/107595a6/plugins/user-authenticators/saml2/test/org/apache/cloudstack/GetServiceProviderMetaDataCmdTest.java
----------------------------------------------------------------------
diff --git a/plugins/user-authenticators/saml2/test/org/apache/cloudstack/GetServiceProviderMetaDataCmdTest.java b/plugins/user-authenticators/saml2/test/org/apache/cloudstack/GetServiceProviderMetaDataCmdTest.java
new file mode 100644
index 0000000..db1323f
--- /dev/null
+++ b/plugins/user-authenticators/saml2/test/org/apache/cloudstack/GetServiceProviderMetaDataCmdTest.java
@@ -0,0 +1,104 @@
+/*
+ * 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;
+
+import com.cloud.utils.HttpUtils;
+import org.apache.cloudstack.api.ApiServerService;
+import org.apache.cloudstack.api.auth.APIAuthenticationType;
+import org.apache.cloudstack.api.command.GetServiceProviderMetaDataCmd;
+import org.apache.cloudstack.saml.SAML2AuthManager;
+import org.apache.cloudstack.saml.SAMLProviderMetadata;
+import org.apache.cloudstack.saml.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.KeyPair;
+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;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+
+@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, UnknownHostException {
+        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";
+        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(samlAuthManager.getSPMetadata()).thenReturn(providerMetadata);
+
+        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"));
+    }
+
+    @Test
+    public void testGetAPIType() {
+        Assert.assertTrue(new GetServiceProviderMetaDataCmd().getAPIType() == APIAuthenticationType.LOGIN_API);
+    }
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/107595a6/plugins/user-authenticators/saml2/test/org/apache/cloudstack/SAML2UserAuthenticatorTest.java
----------------------------------------------------------------------
diff --git a/plugins/user-authenticators/saml2/test/org/apache/cloudstack/SAML2UserAuthenticatorTest.java b/plugins/user-authenticators/saml2/test/org/apache/cloudstack/SAML2UserAuthenticatorTest.java
index 83792c6..5b37388 100644
--- a/plugins/user-authenticators/saml2/test/org/apache/cloudstack/SAML2UserAuthenticatorTest.java
+++ b/plugins/user-authenticators/saml2/test/org/apache/cloudstack/SAML2UserAuthenticatorTest.java
@@ -25,8 +25,8 @@ import com.cloud.user.UserVO;
 import com.cloud.user.dao.UserAccountDao;
 import com.cloud.user.dao.UserDao;
 import com.cloud.utils.Pair;
+import org.apache.cloudstack.saml.SAMLPluginConstants;
 import org.apache.cloudstack.saml.SAML2UserAuthenticator;
-import org.apache.cloudstack.utils.auth.SAMLUtils;
 import org.junit.Assert;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -68,8 +68,6 @@ public class SAML2UserAuthenticatorTest {
         account.setId(1L);
 
         UserVO user = new UserVO();
-        user.setUuid(SAMLUtils.createSAMLId("someUID"));
-
         Mockito.when(userAccountDao.getUserAccount(Mockito.anyString(), Mockito.anyLong())).thenReturn(account);
         Mockito.when(userDao.getUser(Mockito.anyLong())).thenReturn(user);
 
@@ -81,9 +79,9 @@ public class SAML2UserAuthenticatorTest {
         Assert.assertFalse(pair.first());
 
         // When there is SAMLRequest in params and user is same as the mocked one
-        params.put(SAMLUtils.SAML_RESPONSE, new Object[]{});
+        params.put(SAMLPluginConstants.SAML_RESPONSE, new String[]{"RandomString"});
         pair = authenticator.authenticate("someUID", "random", 1l, params);
-        Assert.assertTrue(pair.first());
+        Assert.assertFalse(pair.first());
 
         // When there is SAMLRequest in params but username is null
         pair = authenticator.authenticate(null, "random", 1l, params);

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/107595a6/plugins/user-authenticators/saml2/test/org/apache/cloudstack/SAMLUtilsTest.java
----------------------------------------------------------------------
diff --git a/plugins/user-authenticators/saml2/test/org/apache/cloudstack/SAMLUtilsTest.java b/plugins/user-authenticators/saml2/test/org/apache/cloudstack/SAMLUtilsTest.java
new file mode 100644
index 0000000..bd87831
--- /dev/null
+++ b/plugins/user-authenticators/saml2/test/org/apache/cloudstack/SAMLUtilsTest.java
@@ -0,0 +1,74 @@
+//
+// 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;
+
+import junit.framework.TestCase;
+import org.apache.cloudstack.saml.SAMLUtils;
+import org.junit.Test;
+import org.opensaml.saml2.core.AuthnRequest;
+import org.opensaml.saml2.core.LogoutRequest;
+
+import java.security.KeyPair;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+
+public class SAMLUtilsTest extends TestCase {
+
+    @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";
+        String authnId = SAMLUtils.generateSecureRandomId();
+        AuthnRequest req = SAMLUtils.buildAuthnRequestObject(authnId, 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 nameId = "_12345";
+        LogoutRequest req = SAMLUtils.buildLogoutRequest(logoutUrl, spId, nameId);
+        assertEquals(req.getDestination(), logoutUrl);
+        assertEquals(req.getIssuer().getValue(), spId);
+    }
+
+    @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


[4/5] git commit: updated refs/heads/master to 3ff92e8

Posted by bh...@apache.org.
http://git-wip-us.apache.org/repos/asf/cloudstack/blob/3ff92e88/setup/db/db/schema-452to460.sql
----------------------------------------------------------------------
diff --git a/setup/db/db/schema-452to460.sql b/setup/db/db/schema-452to460.sql
new file mode 100644
index 0000000..0abd4f8
--- /dev/null
+++ b/setup/db/db/schema-452to460.sql
@@ -0,0 +1,400 @@
+-- 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.6.0
+--
+
+ALTER TABLE `cloud`.`snapshots` ADD COLUMN `min_iops` bigint(20) unsigned COMMENT 'Minimum IOPS';
+ALTER TABLE `cloud`.`snapshots` ADD COLUMN `max_iops` bigint(20) unsigned COMMENT 'Maximum IOPS';
+
+INSERT IGNORE INTO `cloud`.`configuration` VALUES ("Advanced", 'DEFAULT', 'management-server', "stats.output.uri", "", "URI to additionally send StatsCollector statistics to", "", NULL, NULL, 0);
+
+DROP VIEW IF EXISTS `cloud`.`domain_view`;
+CREATE VIEW `cloud`.`domain_view` AS
+    select
+        domain.id id,
+        domain.parent parent,
+        domain.name name,
+        domain.uuid uuid,
+        domain.owner owner,
+        domain.path path,
+        domain.level level,
+        domain.child_count child_count,
+        domain.next_child_seq next_child_seq,
+        domain.removed removed,
+        domain.state state,
+        domain.network_domain network_domain,
+        domain.type type,
+        vmlimit.max vmLimit,
+        vmcount.count vmTotal,
+        iplimit.max ipLimit,
+        ipcount.count ipTotal,
+        volumelimit.max volumeLimit,
+        volumecount.count volumeTotal,
+        snapshotlimit.max snapshotLimit,
+        snapshotcount.count snapshotTotal,
+        templatelimit.max templateLimit,
+        templatecount.count templateTotal,
+        vpclimit.max vpcLimit,
+        vpccount.count vpcTotal,
+        projectlimit.max projectLimit,
+        projectcount.count projectTotal,
+        networklimit.max networkLimit,
+        networkcount.count networkTotal,
+        cpulimit.max cpuLimit,
+        cpucount.count cpuTotal,
+        memorylimit.max memoryLimit,
+        memorycount.count memoryTotal,
+        primary_storage_limit.max primaryStorageLimit,
+        primary_storage_count.count primaryStorageTotal,
+        secondary_storage_limit.max secondaryStorageLimit,
+        secondary_storage_count.count secondaryStorageTotal
+    from
+        `cloud`.`domain`
+            left join
+        `cloud`.`resource_limit` vmlimit ON domain.id = vmlimit.domain_id
+            and vmlimit.type = 'user_vm'
+            left join
+        `cloud`.`resource_count` vmcount ON domain.id = vmcount.domain_id
+            and vmcount.type = 'user_vm'
+            left join
+        `cloud`.`resource_limit` iplimit ON domain.id = iplimit.domain_id
+            and iplimit.type = 'public_ip'
+            left join
+        `cloud`.`resource_count` ipcount ON domain.id = ipcount.domain_id
+            and ipcount.type = 'public_ip'
+            left join
+        `cloud`.`resource_limit` volumelimit ON domain.id = volumelimit.domain_id
+            and volumelimit.type = 'volume'
+            left join
+        `cloud`.`resource_count` volumecount ON domain.id = volumecount.domain_id
+            and volumecount.type = 'volume'
+            left join
+        `cloud`.`resource_limit` snapshotlimit ON domain.id = snapshotlimit.domain_id
+            and snapshotlimit.type = 'snapshot'
+            left join
+        `cloud`.`resource_count` snapshotcount ON domain.id = snapshotcount.domain_id
+            and snapshotcount.type = 'snapshot'
+            left join
+        `cloud`.`resource_limit` templatelimit ON domain.id = templatelimit.domain_id
+            and templatelimit.type = 'template'
+            left join
+        `cloud`.`resource_count` templatecount ON domain.id = templatecount.domain_id
+            and templatecount.type = 'template'
+            left join
+        `cloud`.`resource_limit` vpclimit ON domain.id = vpclimit.domain_id
+            and vpclimit.type = 'vpc'
+            left join
+        `cloud`.`resource_count` vpccount ON domain.id = vpccount.domain_id
+            and vpccount.type = 'vpc'
+            left join
+        `cloud`.`resource_limit` projectlimit ON domain.id = projectlimit.domain_id
+            and projectlimit.type = 'project'
+            left join
+        `cloud`.`resource_count` projectcount ON domain.id = projectcount.domain_id
+            and projectcount.type = 'project'
+            left join
+        `cloud`.`resource_limit` networklimit ON domain.id = networklimit.domain_id
+            and networklimit.type = 'network'
+            left join
+        `cloud`.`resource_count` networkcount ON domain.id = networkcount.domain_id
+            and networkcount.type = 'network'
+            left join
+        `cloud`.`resource_limit` cpulimit ON domain.id = cpulimit.domain_id
+            and cpulimit.type = 'cpu'
+            left join
+        `cloud`.`resource_count` cpucount ON domain.id = cpucount.domain_id
+            and cpucount.type = 'cpu'
+            left join
+        `cloud`.`resource_limit` memorylimit ON domain.id = memorylimit.domain_id
+            and memorylimit.type = 'memory'
+            left join
+        `cloud`.`resource_count` memorycount ON domain.id = memorycount.domain_id
+            and memorycount.type = 'memory'
+            left join
+        `cloud`.`resource_limit` primary_storage_limit ON domain.id = primary_storage_limit.domain_id
+            and primary_storage_limit.type = 'primary_storage'
+            left join
+        `cloud`.`resource_count` primary_storage_count ON domain.id = primary_storage_count.domain_id
+            and primary_storage_count.type = 'primary_storage'
+            left join
+        `cloud`.`resource_limit` secondary_storage_limit ON domain.id = secondary_storage_limit.domain_id
+            and secondary_storage_limit.type = 'secondary_storage'
+            left join
+        `cloud`.`resource_count` secondary_storage_count ON domain.id = secondary_storage_count.domain_id
+            and secondary_storage_count.type = 'secondary_storage';
+
+INSERT IGNORE INTO `cloud`.`configuration` VALUES ('Domain Defaults', 'DEFAULT', 'management-server', 'max.domain.user.vms','-1','The default maximum number of user VMs that can be deployed for a domain', '-1', NULL, NULL, 0);
+INSERT IGNORE INTO `cloud`.`configuration` VALUES ('Domain Defaults', 'DEFAULT', 'management-server', 'max.domain.public.ips','-1','The default maximum number of public IPs that can be consumed by a domain', '-1', NULL, NULL, 0);
+INSERT IGNORE INTO `cloud`.`configuration` VALUES ('Domain Defaults', 'DEFAULT', 'management-server', 'max.domain.templates','-1','The default maximum number of templates that can be deployed for a domain', '-1', NULL, NULL, 0);
+INSERT IGNORE INTO `cloud`.`configuration` VALUES ('Domain Defaults', 'DEFAULT', 'management-server', 'max.domain.snapshots','-1','The default maximum number of snapshots that can be created for a domain', '-1', NULL, NULL, 0);
+INSERT IGNORE INTO `cloud`.`configuration` VALUES ('Domain Defaults', 'DEFAULT', 'management-server', 'max.domain.volumes','-1','The default maximum number of volumes that can be created for a domain', '-1', NULL, NULL, 0);
+INSERT IGNORE INTO `cloud`.`configuration` VALUES ('Domain Defaults', 'DEFAULT', 'management-server', 'max.domain.networks', '-1', 'The default maximum number of networks that can be created for a domain', '-1', NULL, NULL, 0);
+INSERT IGNORE INTO `cloud`.`configuration` VALUES ('Domain Defaults', 'DEFAULT', 'management-server', 'max.domain.vpcs', '-1', 'The default maximum number of vpcs that can be created for a domain', '-1', NULL, NULL, 0);
+INSERT IGNORE INTO `cloud`.`configuration` VALUES ('Domain Defaults', 'DEFAULT', 'management-server', 'max.domain.cpus', '-1', 'The default maximum number of cpu cores that can be used for a domain', '-1', NULL, NULL, 0);
+INSERT IGNORE INTO `cloud`.`configuration` VALUES ('Domain Defaults', 'DEFAULT', 'management-server', 'max.domain.memory', '-1', 'The default maximum memory (in MiB) that can be used for a domain', '-1', NULL, NULL, 0);
+INSERT IGNORE INTO `cloud`.`configuration` VALUES ('Domain Defaults', 'DEFAULT', 'management-server', 'max.domain.primary.storage', '-1', 'The default maximum primary storage space (in GiB) that can be used for a domain', '-1', NULL, NULL, 0);
+INSERT IGNORE INTO `cloud`.`configuration` VALUES ('Domain Defaults', 'DEFAULT', 'management-server', 'max.domain.secondary.storage', '-1', 'The default maximum secondary storage space (in GiB) that can be used for a domain', '-1', NULL, NULL, 0);
+
+ALTER TABLE `cloud`.`vm_instance` ADD COLUMN `user_id` bigint unsigned NOT NULL DEFAULT 1 COMMENT 'user id of VM deployer';
+
+DROP VIEW IF EXISTS `cloud`.`user_vm_view`;
+CREATE VIEW `cloud`.`user_vm_view` AS
+    select
+        vm_instance.id id,
+        vm_instance.name name,
+        user_vm.display_name display_name,
+        user_vm.user_data user_data,
+        account.id account_id,
+        account.uuid account_uuid,
+        account.account_name account_name,
+        account.type account_type,
+        domain.id domain_id,
+        domain.uuid domain_uuid,
+        domain.name domain_name,
+        domain.path domain_path,
+        projects.id project_id,
+        projects.uuid project_uuid,
+        projects.name project_name,
+        instance_group.id instance_group_id,
+        instance_group.uuid instance_group_uuid,
+        instance_group.name instance_group_name,
+        vm_instance.uuid uuid,
+        vm_instance.user_id user_id,
+        vm_instance.last_host_id last_host_id,
+        vm_instance.vm_type type,
+        vm_instance.limit_cpu_use limit_cpu_use,
+        vm_instance.created created,
+        vm_instance.state state,
+        vm_instance.removed removed,
+        vm_instance.ha_enabled ha_enabled,
+        vm_instance.hypervisor_type hypervisor_type,
+        vm_instance.instance_name instance_name,
+        vm_instance.guest_os_id guest_os_id,
+        vm_instance.display_vm display_vm,
+        guest_os.uuid guest_os_uuid,
+        vm_instance.pod_id pod_id,
+        host_pod_ref.uuid pod_uuid,
+        vm_instance.private_ip_address private_ip_address,
+        vm_instance.private_mac_address private_mac_address,
+        vm_instance.vm_type vm_type,
+        data_center.id data_center_id,
+        data_center.uuid data_center_uuid,
+        data_center.name data_center_name,
+        data_center.is_security_group_enabled security_group_enabled,
+        data_center.networktype data_center_type,
+        host.id host_id,
+        host.uuid host_uuid,
+        host.name host_name,
+        vm_template.id template_id,
+        vm_template.uuid template_uuid,
+        vm_template.name template_name,
+        vm_template.display_text template_display_text,
+        vm_template.enable_password password_enabled,
+        iso.id iso_id,
+        iso.uuid iso_uuid,
+        iso.name iso_name,
+        iso.display_text iso_display_text,
+        service_offering.id service_offering_id,
+        svc_disk_offering.uuid service_offering_uuid,
+        disk_offering.uuid disk_offering_uuid,
+        disk_offering.id disk_offering_id,
+        Case
+             When (`cloud`.`service_offering`.`cpu` is null) then (`custom_cpu`.`value`)
+             Else ( `cloud`.`service_offering`.`cpu`)
+        End as `cpu`,
+        Case
+            When (`cloud`.`service_offering`.`speed` is null) then (`custom_speed`.`value`)
+            Else ( `cloud`.`service_offering`.`speed`)
+        End as `speed`,
+        Case
+            When (`cloud`.`service_offering`.`ram_size` is null) then (`custom_ram_size`.`value`)
+            Else ( `cloud`.`service_offering`.`ram_size`)
+        END as `ram_size`,
+        svc_disk_offering.name service_offering_name,
+        disk_offering.name disk_offering_name,
+        storage_pool.id pool_id,
+        storage_pool.uuid pool_uuid,
+        storage_pool.pool_type pool_type,
+        volumes.id volume_id,
+        volumes.uuid volume_uuid,
+        volumes.device_id volume_device_id,
+        volumes.volume_type volume_type,
+        security_group.id security_group_id,
+        security_group.uuid security_group_uuid,
+        security_group.name security_group_name,
+        security_group.description security_group_description,
+        nics.id nic_id,
+        nics.uuid nic_uuid,
+        nics.network_id network_id,
+        nics.ip4_address ip_address,
+        nics.ip6_address ip6_address,
+        nics.ip6_gateway ip6_gateway,
+        nics.ip6_cidr ip6_cidr,
+        nics.default_nic is_default_nic,
+        nics.gateway gateway,
+        nics.netmask netmask,
+        nics.mac_address mac_address,
+        nics.broadcast_uri broadcast_uri,
+        nics.isolation_uri isolation_uri,
+        vpc.id vpc_id,
+        vpc.uuid vpc_uuid,
+        networks.uuid network_uuid,
+        networks.name network_name,
+        networks.traffic_type traffic_type,
+        networks.guest_type guest_type,
+        user_ip_address.id public_ip_id,
+        user_ip_address.uuid public_ip_uuid,
+        user_ip_address.public_ip_address public_ip_address,
+        ssh_keypairs.keypair_name keypair_name,
+        resource_tags.id tag_id,
+        resource_tags.uuid tag_uuid,
+        resource_tags.key tag_key,
+        resource_tags.value tag_value,
+        resource_tags.domain_id tag_domain_id,
+        resource_tags.account_id tag_account_id,
+        resource_tags.resource_id tag_resource_id,
+        resource_tags.resource_uuid tag_resource_uuid,
+        resource_tags.resource_type tag_resource_type,
+        resource_tags.customer tag_customer,
+        async_job.id job_id,
+        async_job.uuid job_uuid,
+        async_job.job_status job_status,
+        async_job.account_id job_account_id,
+        affinity_group.id affinity_group_id,
+        affinity_group.uuid affinity_group_uuid,
+        affinity_group.name affinity_group_name,
+        affinity_group.description affinity_group_description,
+        vm_instance.dynamically_scalable dynamically_scalable
+
+    from
+        `cloud`.`user_vm`
+            inner join
+        `cloud`.`vm_instance` ON vm_instance.id = user_vm.id
+            and vm_instance.removed is NULL
+            inner join
+        `cloud`.`account` ON vm_instance.account_id = account.id
+            inner join
+        `cloud`.`domain` ON vm_instance.domain_id = domain.id
+            left join
+        `cloud`.`guest_os` ON vm_instance.guest_os_id = guest_os.id
+            left join
+        `cloud`.`host_pod_ref` ON vm_instance.pod_id = host_pod_ref.id
+            left join
+        `cloud`.`projects` ON projects.project_account_id = account.id
+            left join
+        `cloud`.`instance_group_vm_map` ON vm_instance.id = instance_group_vm_map.instance_id
+            left join
+        `cloud`.`instance_group` ON instance_group_vm_map.group_id = instance_group.id
+            left join
+        `cloud`.`data_center` ON vm_instance.data_center_id = data_center.id
+            left join
+        `cloud`.`host` ON vm_instance.host_id = host.id
+            left join
+        `cloud`.`vm_template` ON vm_instance.vm_template_id = vm_template.id
+            left join
+        `cloud`.`vm_template` iso ON iso.id = user_vm.iso_id
+            left join
+        `cloud`.`service_offering` ON vm_instance.service_offering_id = service_offering.id
+            left join
+        `cloud`.`disk_offering` svc_disk_offering ON vm_instance.service_offering_id = svc_disk_offering.id
+            left join
+        `cloud`.`disk_offering` ON vm_instance.disk_offering_id = disk_offering.id
+            left join
+        `cloud`.`volumes` ON vm_instance.id = volumes.instance_id
+            left join
+        `cloud`.`storage_pool` ON volumes.pool_id = storage_pool.id
+            left join
+        `cloud`.`security_group_vm_map` ON vm_instance.id = security_group_vm_map.instance_id
+            left join
+        `cloud`.`security_group` ON security_group_vm_map.security_group_id = security_group.id
+            left join
+        `cloud`.`nics` ON vm_instance.id = nics.instance_id and nics.removed is null
+            left join
+        `cloud`.`networks` ON nics.network_id = networks.id
+            left join
+        `cloud`.`vpc` ON networks.vpc_id = vpc.id and vpc.removed is null
+            left join
+        `cloud`.`user_ip_address` ON user_ip_address.vm_id = vm_instance.id
+            left join
+        `cloud`.`user_vm_details` as ssh_details ON ssh_details.vm_id = vm_instance.id
+            and ssh_details.name = 'SSH.PublicKey'
+            left join
+        `cloud`.`ssh_keypairs` ON ssh_keypairs.public_key = ssh_details.value
+            left join
+        `cloud`.`resource_tags` ON resource_tags.resource_id = vm_instance.id
+            and resource_tags.resource_type = 'UserVm'
+            left join
+        `cloud`.`async_job` ON async_job.instance_id = vm_instance.id
+            and async_job.instance_type = 'VirtualMachine'
+            and async_job.job_status = 0
+            left join
+        `cloud`.`affinity_group_vm_map` ON vm_instance.id = affinity_group_vm_map.instance_id
+            left join
+        `cloud`.`affinity_group` ON affinity_group_vm_map.affinity_group_id = affinity_group.id
+            left join
+        `cloud`.`user_vm_details` `custom_cpu`  ON (((`custom_cpu`.`vm_id` = `cloud`.`vm_instance`.`id`) and (`custom_cpu`.`name` = 'CpuNumber')))
+            left join
+        `cloud`.`user_vm_details` `custom_speed`  ON (((`custom_speed`.`vm_id` = `cloud`.`vm_instance`.`id`) and (`custom_speed`.`name` = 'CpuSpeed')))
+           left join
+        `cloud`.`user_vm_details` `custom_ram_size`  ON (((`custom_ram_size`.`vm_id` = `cloud`.`vm_instance`.`id`) and (`custom_ram_size`.`name` = 'memory')));
+
+-- ovm3 stuff
+INSERT INTO `cloud`.`guest_os_hypervisor` (hypervisor_type, guest_os_name, guest_os_id) VALUES  ("Ovm3", 'Sun Solaris 10(32-bit)', 79);
+INSERT INTO `cloud`.`guest_os_hypervisor` (hypervisor_type, guest_os_name, guest_os_id) VALUES  ("Ovm3", 'Sun Solaris 10(64-bit)', 80);
+INSERT INTO `cloud`.`guest_os_hypervisor` (hypervisor_type, guest_os_name, guest_os_id) VALUES  ("Ovm3", 'Sun Solaris 11(32-bit)', 158);
+INSERT INTO `cloud`.`guest_os_hypervisor` (hypervisor_type, guest_os_name, guest_os_id) VALUES  ("Ovm3", 'Sun Solaris 11(64-bit)', 159);
+INSERT INTO `cloud`.`guest_os_hypervisor` (hypervisor_type, guest_os_name, guest_os_id) VALUES  ("Ovm3", 'Other Linux (32-bit)', 98);
+INSERT INTO `cloud`.`guest_os_hypervisor` (hypervisor_type, guest_os_name, guest_os_id) VALUES  ("Ovm3", 'Other Linux (64-bit)', 99);
+INSERT INTO `cloud`.`guest_os_hypervisor` (hypervisor_type, guest_os_name, guest_os_id) VALUES  ('Ovm3', 'Other PV (32-bit)', 139);
+INSERT INTO `cloud`.`guest_os_hypervisor` (hypervisor_type, guest_os_name, guest_os_id) VALUES  ('Ovm3', 'Other PV (64-bit)', 140);
+INSERT INTO `cloud`.`guest_os_hypervisor` (hypervisor_type, guest_os_name, guest_os_id) VALUES  ('Ovm3', 'DOS', 102);
+INSERT INTO `cloud`.`guest_os_hypervisor` (hypervisor_type, guest_os_name, guest_os_id) VALUES  ("Ovm3", 'Windows 8 (32-bit)', 165);
+INSERT INTO `cloud`.`guest_os_hypervisor` (hypervisor_type, guest_os_name, guest_os_id) VALUES  ("Ovm3", 'Windows 8 (64-bit)', 166);
+INSERT INTO `cloud`.`guest_os_hypervisor` (hypervisor_type, guest_os_name, guest_os_id) VALUES  ("Ovm3", 'Windows Server 2012 (64-bit)', 167);
+
+INSERT IGNORE INTO `cloud`.`hypervisor_capabilities`(hypervisor_type, hypervisor_version, max_guests_limit, security_group_enabled) VALUES ('Ovm3', '3.2', 25, 0);
+INSERT IGNORE INTO `cloud`.`hypervisor_capabilities`(hypervisor_type, hypervisor_version, max_guests_limit, security_group_enabled) VALUES ('Ovm3', '3.3', 50, 0);
+UPDATE  `cloud`.`volumes` v,  `cloud`.`storage_pool` s,  `cloud`.`cluster` c  set v.format='RAW' where v.pool_id=s.id and s.cluster_id=c.id and c.hypervisor_type='Ovm3';
+UPDATE configuration SET value='KVM,XenServer,VMware,BareMetal,Ovm,Ovm3,LXC' WHERE name='hypervisor.list';
+INSERT INTO `cloud`.`vm_template` (id, uuid, unique_name, name, public, created, type, hvm, bits, account_id, url, checksum, enable_password, display_text, format, guest_os_id,featured, cross_zones, hypervisor_type, state)
+VALUES (12, UUID(), 'routing-12', 'SystemVM Template (Ovm3)', 0, now(), 'SYSTEM', 0, 64, 1, 'http://download.cloud.com/templates/4.6/systemvm64template.ovm.raw.bz2', '4425688804dbcf0abc9e9e56c53070d7', 0, 'SystemVM Template (Ovm3)', 'RAW', 183, 0, 1, 'Ovm3', 'Active' );
+
+INSERT IGNORE INTO `cloud`.`configuration` (`category`, `instance`, `component`, `name`, `value`, `default_value`, `description`) VALUES ('Advanced', 'DEFAULT', 'ManagementServer', 'ovm3.heartbeat.timeout' , '180', '120', 'Timeout value to send to the checkheartbeat script for guarding the self fencing functionality on ovm3');
+INSERT IGNORE INTO `cloud`.`configuration` (`category`, `instance`, `component`, `name`, `value`, `default_value`, `description`) VALUES ('Advanced', 'DEFAULT', 'ManagementServer', 'ovm3.heartbeat.interval' , '10', '1', 'Interval value the checkheartbeat script uses before triggering the timeout for ovm3');
+INSERT IGNORE INTO `cloud`.`configuration` VALUES ('Advanced', 'DEFAULT', 'NetworkManager', 'router.template.ovm3', 'SystemVM Template (Ovm3)', 'Name of the default router template on Ovm3.','SystemVM Template (Ovm3)', NULL, NULL, 0);
+
+UPDATE IGNORE `cloud`.`configuration` SET `value`="PLAINTEXT" WHERE `name`="user.authenticators.exclude";
+
+DROP TABLE IF EXISTS `cloud`.`external_bigswitch_vns_devices`;
+CREATE TABLE `cloud`.`external_bigswitch_bcf_devices` (
+  `id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT 'id',
+  `uuid` varchar(255) UNIQUE,
+  `physical_network_id` bigint unsigned NOT NULL COMMENT 'id of the physical network in to which bigswitch bcf device is added',
+  `provider_name` varchar(255) NOT NULL COMMENT 'Service Provider name corresponding to this bigswitch bcf device',
+  `device_name` varchar(255) NOT NULL COMMENT 'name of the bigswitch bcf device',
+  `host_id` bigint unsigned NOT NULL COMMENT 'host id coresponding to the external bigswitch bcf device',
+  `hostname` varchar(255) NOT NULL COMMENT 'host name or IP address for the bigswitch bcf device',
+  `username` varchar(255) NOT NULL COMMENT 'username for the bigswitch bcf device',
+  `password` varchar(255) NOT NULL COMMENT 'password for the bigswitch bcf device',
+  `nat` boolean NOT NULL COMMENT 'NAT support for the bigswitch bcf device',
+  `hash` varchar(255) NOT NULL COMMENT 'topology hash for the bigswitch bcf networks',
+  PRIMARY KEY  (`id`),
+  CONSTRAINT `fk_external_bigswitch_bcf_devices__host_id` FOREIGN KEY (`host_id`) REFERENCES `host`(`id`) ON DELETE CASCADE,
+  CONSTRAINT `fk_external_bigswitch_bcf_devices__physical_network_id` FOREIGN KEY (`physical_network_id`) REFERENCES `physical_network`(`id`) ON DELETE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;


[5/5] git commit: updated refs/heads/master to 3ff92e8

Posted by bh...@apache.org.
database: fix upgrade paths from 4.5.2 to 4.6.0

Signed-off-by: Rohit Yadav <ro...@shapeblue.com>


Project: http://git-wip-us.apache.org/repos/asf/cloudstack/repo
Commit: http://git-wip-us.apache.org/repos/asf/cloudstack/commit/3ff92e88
Tree: http://git-wip-us.apache.org/repos/asf/cloudstack/tree/3ff92e88
Diff: http://git-wip-us.apache.org/repos/asf/cloudstack/diff/3ff92e88

Branch: refs/heads/master
Commit: 3ff92e883ef982eadb86840dff5d6792b54586a2
Parents: 107595a
Author: Rohit Yadav <ro...@shapeblue.com>
Authored: Mon Jun 29 12:35:18 2015 +0200
Committer: Rohit Yadav <ro...@shapeblue.com>
Committed: Mon Jun 29 12:58:47 2015 +0200

----------------------------------------------------------------------
 .../cloud/upgrade/DatabaseUpgradeChecker.java   | 127 +++---
 .../com/cloud/upgrade/dao/Upgrade451to452.java  |  67 ++++
 .../com/cloud/upgrade/dao/Upgrade451to460.java  | 149 -------
 .../com/cloud/upgrade/dao/Upgrade452to460.java  | 149 +++++++
 .../cloudstack/api/command/ListIdpsCmd.java     |   4 +-
 setup/db/db/schema-451to460-cleanup.sql         |  20 -
 setup/db/db/schema-451to460.sql                 | 400 -------------------
 setup/db/db/schema-452to460-cleanup.sql         |  20 +
 setup/db/db/schema-452to460.sql                 | 400 +++++++++++++++++++
 9 files changed, 702 insertions(+), 634 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cloudstack/blob/3ff92e88/engine/schema/src/com/cloud/upgrade/DatabaseUpgradeChecker.java
----------------------------------------------------------------------
diff --git a/engine/schema/src/com/cloud/upgrade/DatabaseUpgradeChecker.java b/engine/schema/src/com/cloud/upgrade/DatabaseUpgradeChecker.java
index 4bc54b1..f3cbdea 100644
--- a/engine/schema/src/com/cloud/upgrade/DatabaseUpgradeChecker.java
+++ b/engine/schema/src/com/cloud/upgrade/DatabaseUpgradeChecker.java
@@ -16,25 +16,6 @@
 // under the License.
 package com.cloud.upgrade;
 
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.FileReader;
-import java.io.IOException;
-import java.sql.Connection;
-import java.sql.PreparedStatement;
-import java.sql.ResultSet;
-import java.sql.SQLException;
-import java.util.ArrayList;
-import java.util.Date;
-import java.util.HashMap;
-import java.util.List;
-import java.util.TreeMap;
-
-import javax.ejb.Local;
-import javax.inject.Inject;
-
-import org.apache.log4j.Logger;
-
 import com.cloud.maint.Version;
 import com.cloud.upgrade.dao.DbUpgrade;
 import com.cloud.upgrade.dao.Upgrade217to218;
@@ -74,7 +55,8 @@ import com.cloud.upgrade.dao.Upgrade442to450;
 import com.cloud.upgrade.dao.Upgrade443to450;
 import com.cloud.upgrade.dao.Upgrade444to450;
 import com.cloud.upgrade.dao.Upgrade450to451;
-import com.cloud.upgrade.dao.Upgrade451to460;
+import com.cloud.upgrade.dao.Upgrade451to452;
+import com.cloud.upgrade.dao.Upgrade452to460;
 import com.cloud.upgrade.dao.UpgradeSnapshot217to224;
 import com.cloud.upgrade.dao.UpgradeSnapshot223to224;
 import com.cloud.upgrade.dao.VersionDao;
@@ -86,6 +68,23 @@ import com.cloud.utils.db.GlobalLock;
 import com.cloud.utils.db.ScriptRunner;
 import com.cloud.utils.db.TransactionLegacy;
 import com.cloud.utils.exception.CloudRuntimeException;
+import org.apache.log4j.Logger;
+
+import javax.ejb.Local;
+import javax.inject.Inject;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.IOException;
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.TreeMap;
 
 @Local(value = {SystemIntegrityChecker.class})
 public class DatabaseUpgradeChecker implements SystemIntegrityChecker {
@@ -105,7 +104,7 @@ public class DatabaseUpgradeChecker implements SystemIntegrityChecker {
             new Upgrade2211to2212(), new Upgrade2212to2213(), new Upgrade2213to2214(), new Upgrade2214to30(),
             new Upgrade30to301(), new Upgrade301to302(), new Upgrade302to40(), new Upgrade40to41(), new Upgrade410to420(),
             new Upgrade420to421(), new Upgrade421to430(), new Upgrade430to440(), new Upgrade440to441(), new Upgrade441to442(),
-            new Upgrade442to450(), new Upgrade450to451(), new Upgrade451to460()});
+            new Upgrade442to450(), new Upgrade450to451(), new Upgrade451to452(), new Upgrade452to460()});
 
         _upgradeMap.put("2.1.8", new DbUpgrade[] {new Upgrade218to22(), new Upgrade221to222(), new UpgradeSnapshot217to224(),
             new Upgrade222to224(), new Upgrade218to224DomainVlans(), new Upgrade224to225(), new Upgrade225to226(),
@@ -113,7 +112,7 @@ public class DatabaseUpgradeChecker implements SystemIntegrityChecker {
             new Upgrade2211to2212(), new Upgrade2212to2213(), new Upgrade2213to2214(),
             new Upgrade2214to30(), new Upgrade30to301(), new Upgrade301to302(), new Upgrade302to40(), new Upgrade40to41(),
             new Upgrade410to420(), new Upgrade420to421(),
-            new Upgrade421to430(), new Upgrade430to440(), new Upgrade440to441(), new Upgrade441to442(), new Upgrade442to450(), new Upgrade450to451(), new Upgrade451to460()});
+            new Upgrade421to430(), new Upgrade430to440(), new Upgrade440to441(), new Upgrade441to442(), new Upgrade442to450(), new Upgrade450to451(), new Upgrade451to452(), new Upgrade452to460()});
 
         _upgradeMap.put("2.1.9", new DbUpgrade[] {new Upgrade218to22(), new Upgrade221to222(), new UpgradeSnapshot217to224(),
             new Upgrade222to224(), new Upgrade218to224DomainVlans(), new Upgrade224to225(), new Upgrade225to226(),
@@ -121,147 +120,149 @@ public class DatabaseUpgradeChecker implements SystemIntegrityChecker {
             new Upgrade2211to2212(), new Upgrade2212to2213(), new Upgrade2213to2214(), new Upgrade2214to30(),
             new Upgrade30to301(), new Upgrade301to302(), new Upgrade302to40(), new Upgrade40to41(), new Upgrade410to420(),
             new Upgrade420to421(), new Upgrade421to430(), new Upgrade430to440(), new Upgrade440to441(), new Upgrade441to442(),
-            new Upgrade442to450(), new Upgrade450to451(), new Upgrade451to460()});
+            new Upgrade442to450(), new Upgrade450to451(), new Upgrade451to452(), new Upgrade452to460()});
 
         _upgradeMap.put("2.2.1", new DbUpgrade[] {new Upgrade221to222(), new UpgradeSnapshot223to224(), new Upgrade222to224(),
             new Upgrade224to225(), new Upgrade225to226(), new Upgrade227to228(), new Upgrade228to229(),
             new Upgrade229to2210(), new Upgrade2210to2211(), new Upgrade2211to2212(), new Upgrade2212to2213(),
             new Upgrade2213to2214(), new Upgrade2214to30(), new Upgrade30to301(), new Upgrade301to302(), new Upgrade302to40(),
             new Upgrade40to41(), new Upgrade410to420(), new Upgrade420to421(), new Upgrade421to430(), new Upgrade430to440(),
-            new Upgrade440to441(), new Upgrade441to442(), new Upgrade442to450(), new Upgrade450to451(), new Upgrade451to460()});
+            new Upgrade440to441(), new Upgrade441to442(), new Upgrade442to450(), new Upgrade450to451(), new Upgrade451to452(), new Upgrade452to460()});
 
         _upgradeMap.put("2.2.2", new DbUpgrade[] {new Upgrade222to224(), new UpgradeSnapshot223to224(), new Upgrade224to225(),
             new Upgrade225to226(), new Upgrade227to228(), new Upgrade228to229(), new Upgrade229to2210(),
             new Upgrade2210to2211(), new Upgrade2211to2212(), new Upgrade2212to2213(), new Upgrade2213to2214(),
             new Upgrade2214to30(), new Upgrade30to301(), new Upgrade301to302(), new Upgrade302to40(), new Upgrade40to41(), new Upgrade410to420(), new Upgrade420to421(),
-            new Upgrade421to430(), new Upgrade430to440(), new Upgrade440to441(), new Upgrade441to442(), new Upgrade442to450(), new Upgrade450to451(), new Upgrade451to460()});
+            new Upgrade421to430(), new Upgrade430to440(), new Upgrade440to441(), new Upgrade441to442(), new Upgrade442to450(), new Upgrade450to451(), new Upgrade451to452(), new Upgrade452to460()});
 
         _upgradeMap.put("2.2.3", new DbUpgrade[] {new Upgrade222to224(), new UpgradeSnapshot223to224(), new Upgrade224to225(),
             new Upgrade225to226(), new Upgrade227to228(), new Upgrade228to229(), new Upgrade229to2210(),
             new Upgrade2210to2211(), new Upgrade2211to2212(), new Upgrade2212to2213(), new Upgrade2213to2214(),
             new Upgrade2214to30(), new Upgrade30to301(), new Upgrade301to302(), new Upgrade302to40(), new Upgrade40to41(), new Upgrade410to420(), new Upgrade420to421(),
-            new Upgrade421to430(), new Upgrade430to440(), new Upgrade440to441(), new Upgrade441to442(), new Upgrade442to450(), new Upgrade450to451(), new Upgrade451to460()});
+            new Upgrade421to430(), new Upgrade430to440(), new Upgrade440to441(), new Upgrade441to442(), new Upgrade442to450(), new Upgrade450to451(), new Upgrade451to452(), new Upgrade452to460()});
 
         _upgradeMap.put("2.2.4", new DbUpgrade[] {new Upgrade224to225(), new Upgrade225to226(), new Upgrade227to228(),
             new Upgrade228to229(), new Upgrade229to2210(), new Upgrade2210to2211(), new Upgrade2211to2212(),
             new Upgrade2212to2213(), new Upgrade2213to2214(), new Upgrade2214to30(), new Upgrade30to301(),
             new Upgrade301to302(), new Upgrade302to40(), new Upgrade40to41(), new Upgrade410to420(), new Upgrade420to421(),
-            new Upgrade421to430(), new Upgrade430to440(), new Upgrade440to441(), new Upgrade441to442(), new Upgrade442to450(), new Upgrade450to451(), new Upgrade451to460()});
+            new Upgrade421to430(), new Upgrade430to440(), new Upgrade440to441(), new Upgrade441to442(), new Upgrade442to450(), new Upgrade450to451(), new Upgrade451to452(), new Upgrade452to460()});
 
         _upgradeMap.put("2.2.5", new DbUpgrade[] {new Upgrade225to226(), new Upgrade227to228(), new Upgrade228to229(),
             new Upgrade229to2210(), new Upgrade2210to2211(), new Upgrade2211to2212(), new Upgrade2212to2213(),
             new Upgrade2213to2214(), new Upgrade2214to30(), new Upgrade30to301(), new Upgrade301to302(),
             new Upgrade302to40(), new Upgrade40to41(), new Upgrade410to420(), new Upgrade420to421(), new Upgrade421to430(),
-            new Upgrade430to440(), new Upgrade440to441(), new Upgrade441to442(), new Upgrade442to450(), new Upgrade450to451(), new Upgrade451to460()});
+            new Upgrade430to440(), new Upgrade440to441(), new Upgrade441to442(), new Upgrade442to450(), new Upgrade450to451(), new Upgrade451to452(), new Upgrade452to460()});
 
         _upgradeMap.put("2.2.6", new DbUpgrade[] {new Upgrade227to228(), new Upgrade228to229(), new Upgrade229to2210(),
             new Upgrade2210to2211(), new Upgrade2211to2212(), new Upgrade2212to2213(), new Upgrade2213to2214(),
             new Upgrade2214to30(), new Upgrade30to301(), new Upgrade301to302(), new Upgrade302to40(), new Upgrade40to41(),
             new Upgrade410to420(), new Upgrade420to421(),
-            new Upgrade421to430(), new Upgrade430to440(), new Upgrade440to441(), new Upgrade441to442(), new Upgrade442to450(), new Upgrade450to451(), new Upgrade451to460()});
+            new Upgrade421to430(), new Upgrade430to440(), new Upgrade440to441(), new Upgrade441to442(), new Upgrade442to450(), new Upgrade450to451(), new Upgrade451to452(), new Upgrade452to460()});
 
         _upgradeMap.put("2.2.7", new DbUpgrade[] {new Upgrade227to228(), new Upgrade228to229(), new Upgrade229to2210(),
             new Upgrade2210to2211(), new Upgrade2211to2212(), new Upgrade2212to2213(),
             new Upgrade2213to2214(), new Upgrade2214to30(), new Upgrade30to301(), new Upgrade301to302(), new Upgrade302to40(), new Upgrade40to41(),
             new Upgrade410to420(),
             new Upgrade420to421(), new Upgrade421to430(), new Upgrade430to440(), new Upgrade440to441(), new Upgrade441to442(),
-            new Upgrade442to450(), new Upgrade450to451(), new Upgrade451to460()});
+            new Upgrade442to450(), new Upgrade450to451(), new Upgrade451to452(), new Upgrade452to460()});
 
         _upgradeMap.put("2.2.8", new DbUpgrade[] {new Upgrade228to229(), new Upgrade229to2210(), new Upgrade2210to2211(),
             new Upgrade2211to2212(), new Upgrade2212to2213(), new Upgrade2213to2214(), new Upgrade2214to30()
             , new Upgrade30to301(), new Upgrade301to302(), new Upgrade302to40(), new Upgrade40to41(), new Upgrade410to420(), new Upgrade420to421(),
-            new Upgrade421to430(), new Upgrade430to440(), new Upgrade440to441(), new Upgrade441to442(), new Upgrade442to450(), new Upgrade450to451(), new Upgrade451to460()});
+            new Upgrade421to430(), new Upgrade430to440(), new Upgrade440to441(), new Upgrade441to442(), new Upgrade442to450(), new Upgrade450to451(), new Upgrade451to452(), new Upgrade452to460()});
 
         _upgradeMap.put("2.2.9", new DbUpgrade[] {new Upgrade229to2210(), new Upgrade2210to2211(), new Upgrade2211to2212(),
             new Upgrade2212to2213(), new Upgrade2213to2214(), new Upgrade2214to30(), new Upgrade30to301(),
             new Upgrade301to302(), new Upgrade302to40(), new Upgrade40to41(), new Upgrade410to420(), new Upgrade420to421(),
-            new Upgrade421to430(), new Upgrade430to440(), new Upgrade440to441(), new Upgrade441to442(), new Upgrade442to450(), new Upgrade450to451(), new Upgrade451to460()});
+            new Upgrade421to430(), new Upgrade430to440(), new Upgrade440to441(), new Upgrade441to442(), new Upgrade442to450(), new Upgrade450to451(), new Upgrade451to452(), new Upgrade452to460()});
 
         _upgradeMap.put("2.2.10", new DbUpgrade[] {new Upgrade2210to2211(), new Upgrade2211to2212(), new Upgrade2212to2213(),
             new Upgrade2213to2214(), new Upgrade2214to30(), new Upgrade30to301(), new Upgrade301to302(), new Upgrade302to40(),
             new Upgrade40to41(), new Upgrade410to420(), new Upgrade420to421(), new Upgrade421to430(), new Upgrade430to440(),
-            new Upgrade440to441(), new Upgrade441to442(), new Upgrade442to450(), new Upgrade450to451(), new Upgrade451to460()});
+            new Upgrade440to441(), new Upgrade441to442(), new Upgrade442to450(), new Upgrade450to451(), new Upgrade451to452(), new Upgrade452to460()});
 
         _upgradeMap.put("2.2.12", new DbUpgrade[] {new Upgrade2212to2213(), new Upgrade2213to2214(), new Upgrade2214to30(),
             new Upgrade30to301(), new Upgrade301to302(), new Upgrade302to40(), new Upgrade40to41(), new Upgrade410to420(), new Upgrade420to421(), new Upgrade421to430(),
-            new Upgrade430to440(), new Upgrade440to441(), new Upgrade441to442(), new Upgrade442to450(), new Upgrade450to451(), new Upgrade451to460()});
+            new Upgrade430to440(), new Upgrade440to441(), new Upgrade441to442(), new Upgrade442to450(), new Upgrade450to451(), new Upgrade451to452(), new Upgrade452to460()});
 
         _upgradeMap.put("2.2.13", new DbUpgrade[] {new Upgrade2213to2214(), new Upgrade2214to30(), new Upgrade30to301(),
             new Upgrade301to302(), new Upgrade302to40(), new Upgrade40to41(), new Upgrade410to420(), new Upgrade420to421(),
-            new Upgrade421to430(), new Upgrade430to440(), new Upgrade440to441(), new Upgrade441to442(), new Upgrade442to450(), new Upgrade450to451(), new Upgrade451to460()});
+            new Upgrade421to430(), new Upgrade430to440(), new Upgrade440to441(), new Upgrade441to442(), new Upgrade442to450(), new Upgrade450to451(), new Upgrade451to452(), new Upgrade452to460()});
 
         _upgradeMap.put("2.2.14", new DbUpgrade[] {new Upgrade2214to30(), new Upgrade30to301(), new Upgrade301to302(),
             new Upgrade302to40(), new Upgrade40to41(), new Upgrade410to420(), new Upgrade420to421(), new Upgrade421to430(),
-            new Upgrade430to440(), new Upgrade440to441(), new Upgrade441to442(), new Upgrade442to450(), new Upgrade450to451(), new Upgrade451to460()});
+            new Upgrade430to440(), new Upgrade440to441(), new Upgrade441to442(), new Upgrade442to450(), new Upgrade450to451(), new Upgrade451to452(), new Upgrade452to460()});
 
         _upgradeMap.put("3.0.0", new DbUpgrade[] {new Upgrade30to301(), new Upgrade301to302(), new Upgrade302to40(),
             new Upgrade40to41(), new Upgrade410to420(),
-            new Upgrade420to421(), new Upgrade421to430(), new Upgrade430to440(), new Upgrade440to441(), new Upgrade441to442(), new Upgrade442to450(), new Upgrade450to451(), new Upgrade451to460()});
+            new Upgrade420to421(), new Upgrade421to430(), new Upgrade430to440(), new Upgrade440to441(), new Upgrade441to442(), new Upgrade442to450(), new Upgrade450to451(), new Upgrade451to452(), new Upgrade452to460()});
 
         _upgradeMap.put("3.0.1", new DbUpgrade[] {new Upgrade301to302(), new Upgrade302to40(), new Upgrade40to41(), new Upgrade410to420(), new Upgrade420to421(),
-            new Upgrade421to430(), new Upgrade430to440(), new Upgrade440to441(), new Upgrade441to442(), new Upgrade442to450(), new Upgrade450to451(), new Upgrade451to460()});
+            new Upgrade421to430(), new Upgrade430to440(), new Upgrade440to441(), new Upgrade441to442(), new Upgrade442to450(), new Upgrade450to451(), new Upgrade451to452(), new Upgrade452to460()});
 
         _upgradeMap.put("3.0.2", new DbUpgrade[] {new Upgrade302to40(), new Upgrade40to41(), new Upgrade410to420(), new Upgrade420to421(), new Upgrade421to430(),
-            new Upgrade430to440(), new Upgrade440to441(), new Upgrade441to442(), new Upgrade442to450(), new Upgrade450to451(), new Upgrade451to460()});
+            new Upgrade430to440(), new Upgrade440to441(), new Upgrade441to442(), new Upgrade442to450(), new Upgrade450to451(), new Upgrade451to452(), new Upgrade452to460()});
+
+        _upgradeMap.put("4.0.0", new DbUpgrade[] {new Upgrade40to41(), new Upgrade410to420(), new Upgrade420to421(), new Upgrade421to430(), new Upgrade430to440(), new Upgrade440to441(), new Upgrade441to442(), new Upgrade442to450(), new Upgrade450to451(), new Upgrade451to452(), new Upgrade452to460()});
 
-        _upgradeMap.put("4.0.0", new DbUpgrade[] {new Upgrade40to41(), new Upgrade410to420(), new Upgrade420to421(), new Upgrade421to430(), new Upgrade430to440(), new Upgrade440to441(), new Upgrade441to442(), new Upgrade442to450(), new Upgrade450to451(), new Upgrade451to460()});
+        _upgradeMap.put("4.0.1", new DbUpgrade[] {new Upgrade40to41(), new Upgrade410to420(), new Upgrade420to421(), new Upgrade421to430(), new Upgrade430to440(), new Upgrade440to441(), new Upgrade441to442(), new Upgrade442to450(), new Upgrade450to451(), new Upgrade451to452(), new Upgrade452to460()});
 
-        _upgradeMap.put("4.0.1", new DbUpgrade[] {new Upgrade40to41(), new Upgrade410to420(), new Upgrade420to421(), new Upgrade421to430(), new Upgrade430to440(), new Upgrade440to441(), new Upgrade441to442(), new Upgrade442to450(), new Upgrade450to451(), new Upgrade451to460()});
+        _upgradeMap.put("4.0.2", new DbUpgrade[] {new Upgrade40to41(), new Upgrade410to420(), new Upgrade420to421(), new Upgrade421to430(), new Upgrade430to440(), new Upgrade440to441(), new Upgrade441to442(), new Upgrade442to450(), new Upgrade450to451(), new Upgrade451to452(), new Upgrade452to460()});
 
-        _upgradeMap.put("4.0.2", new DbUpgrade[] {new Upgrade40to41(), new Upgrade410to420(), new Upgrade420to421(), new Upgrade421to430(), new Upgrade430to440(), new Upgrade440to441(), new Upgrade441to442(), new Upgrade442to450(), new Upgrade450to451(), new Upgrade451to460()});
+        _upgradeMap.put("4.1.0", new DbUpgrade[] {new Upgrade410to420(), new Upgrade420to421(), new Upgrade421to430(), new Upgrade430to440(), new Upgrade440to441(), new Upgrade441to442(), new Upgrade442to450(), new Upgrade450to451(), new Upgrade451to452(), new Upgrade452to460()});
 
-        _upgradeMap.put("4.1.0", new DbUpgrade[] {new Upgrade410to420(), new Upgrade420to421(), new Upgrade421to430(), new Upgrade430to440(), new Upgrade440to441(), new Upgrade441to442(), new Upgrade442to450(), new Upgrade450to451(), new Upgrade451to460()});
+        _upgradeMap.put("4.1.1", new DbUpgrade[] {new Upgrade410to420(), new Upgrade420to421(), new Upgrade421to430(), new Upgrade430to440(), new Upgrade440to441(), new Upgrade441to442(), new Upgrade442to450(), new Upgrade450to451(), new Upgrade451to452(), new Upgrade452to460()});
 
-        _upgradeMap.put("4.1.1", new DbUpgrade[] {new Upgrade410to420(), new Upgrade420to421(), new Upgrade421to430(), new Upgrade430to440(), new Upgrade440to441(), new Upgrade441to442(), new Upgrade442to450(), new Upgrade450to451(), new Upgrade451to460()});
+        _upgradeMap.put("4.2.0", new DbUpgrade[] {new Upgrade420to421(), new Upgrade421to430(), new Upgrade430to440(), new Upgrade440to441(), new Upgrade441to442(), new Upgrade442to450(), new Upgrade450to451(), new Upgrade451to452(), new Upgrade452to460()});
 
-        _upgradeMap.put("4.2.0", new DbUpgrade[] {new Upgrade420to421(), new Upgrade421to430(), new Upgrade430to440(), new Upgrade440to441(), new Upgrade441to442(), new Upgrade442to450(), new Upgrade450to451(), new Upgrade451to460()});
+        _upgradeMap.put("4.2.1", new DbUpgrade[] {new Upgrade421to430(), new Upgrade430to440(), new Upgrade440to441(), new Upgrade441to442(), new Upgrade442to450(), new Upgrade450to451(), new Upgrade451to452(), new Upgrade452to460()});
 
-        _upgradeMap.put("4.2.1", new DbUpgrade[] {new Upgrade421to430(), new Upgrade430to440(), new Upgrade440to441(), new Upgrade441to442(), new Upgrade442to450(), new Upgrade450to451(), new Upgrade451to460()});
+        _upgradeMap.put("4.3.0", new DbUpgrade[] {new Upgrade430to440(), new Upgrade440to441(), new Upgrade441to442(), new Upgrade442to450(), new Upgrade450to451(), new Upgrade451to452(), new Upgrade452to460()});
 
-        _upgradeMap.put("4.3.0", new DbUpgrade[] {new Upgrade430to440(), new Upgrade440to441(), new Upgrade441to442(), new Upgrade442to450(), new Upgrade450to451(), new Upgrade451to460()});
+        _upgradeMap.put("4.3.1", new DbUpgrade[] {new Upgrade431to440(), new Upgrade440to441(), new Upgrade441to442(), new Upgrade442to450(), new Upgrade450to451(), new Upgrade451to452(), new Upgrade452to460()});
 
-        _upgradeMap.put("4.3.1", new DbUpgrade[] {new Upgrade431to440(), new Upgrade440to441(), new Upgrade441to442(), new Upgrade442to450(), new Upgrade450to451(), new Upgrade451to460()});
+        _upgradeMap.put("4.3.2", new DbUpgrade[] {new Upgrade432to440(), new Upgrade440to441(), new Upgrade441to442(), new Upgrade442to450(), new Upgrade450to451(), new Upgrade451to452(), new Upgrade452to460()});
 
-        _upgradeMap.put("4.3.2", new DbUpgrade[] {new Upgrade432to440(), new Upgrade440to441(), new Upgrade441to442(), new Upgrade442to450(), new Upgrade450to451(), new Upgrade451to460()});
+        _upgradeMap.put("4.4.0", new DbUpgrade[] {new Upgrade440to441(), new Upgrade441to442(), new Upgrade442to450(), new Upgrade450to451(), new Upgrade451to452(), new Upgrade452to460()});
 
-        _upgradeMap.put("4.4.0", new DbUpgrade[] {new Upgrade440to441(), new Upgrade441to442(), new Upgrade442to450(), new Upgrade450to451(), new Upgrade451to460()});
+        _upgradeMap.put("4.4.1", new DbUpgrade[] {new Upgrade441to442(), new Upgrade442to450(), new Upgrade450to451(), new Upgrade451to452(), new Upgrade452to460() });
 
-        _upgradeMap.put("4.4.1", new DbUpgrade[] {new Upgrade441to442(), new Upgrade442to450(), new Upgrade450to451(), new Upgrade451to460() });
+        _upgradeMap.put("4.4.2", new DbUpgrade[] {new Upgrade442to450(), new Upgrade450to451(), new Upgrade451to452(), new Upgrade452to460()});
 
-        _upgradeMap.put("4.4.2", new DbUpgrade[] {new Upgrade442to450(), new Upgrade450to451(), new Upgrade451to460()});
+        _upgradeMap.put("4.4.3", new DbUpgrade[] {new Upgrade443to450(), new Upgrade450to451(), new Upgrade451to452(), new Upgrade452to460()});
 
-        _upgradeMap.put("4.4.3", new DbUpgrade[] {new Upgrade443to450(), new Upgrade450to451(), new Upgrade451to460()});
+        _upgradeMap.put("4.4.4", new DbUpgrade[] {new Upgrade444to450(), new Upgrade450to451(), new Upgrade451to452(), new Upgrade452to460()});
 
-        _upgradeMap.put("4.4.4", new DbUpgrade[] {new Upgrade444to450(), new Upgrade450to451(), new Upgrade451to460()});
+        _upgradeMap.put("4.5.0", new DbUpgrade[] {new Upgrade450to451(), new Upgrade451to452(), new Upgrade452to460()});
 
-        _upgradeMap.put("4.5.0", new DbUpgrade[] {new Upgrade450to451(), new Upgrade451to460()});
+        _upgradeMap.put("4.5.1", new DbUpgrade[] {new Upgrade451to452(), new Upgrade452to460()});
 
-        _upgradeMap.put("4.5.1", new DbUpgrade[] {new Upgrade451to460()});
+        _upgradeMap.put("4.5.2", new DbUpgrade[] {new Upgrade452to460()});
 
         //CP Upgrades
         _upgradeMap.put("3.0.3", new DbUpgrade[] {new Upgrade303to304(), new Upgrade304to305(), new Upgrade305to306(), new Upgrade306to307(), new Upgrade307to410(),
-            new Upgrade410to420(), new Upgrade420to421(), new Upgrade421to430(), new Upgrade430to440(), new Upgrade440to441(), new Upgrade441to442(), new Upgrade442to450(), new Upgrade450to451(), new Upgrade451to460()});
+            new Upgrade410to420(), new Upgrade420to421(), new Upgrade421to430(), new Upgrade430to440(), new Upgrade440to441(), new Upgrade441to442(), new Upgrade442to450(), new Upgrade450to451(), new Upgrade451to452(), new Upgrade452to460()});
 
         _upgradeMap.put("3.0.4", new DbUpgrade[] {new Upgrade304to305(), new Upgrade305to306(), new Upgrade306to307(), new Upgrade307to410(), new Upgrade410to420(),
-            new Upgrade420to421(), new Upgrade421to430(), new Upgrade430to440(), new Upgrade440to441(), new Upgrade441to442(), new Upgrade442to450(), new Upgrade450to451(), new Upgrade451to460()});
+            new Upgrade420to421(), new Upgrade421to430(), new Upgrade430to440(), new Upgrade440to441(), new Upgrade441to442(), new Upgrade442to450(), new Upgrade450to451(), new Upgrade451to452(), new Upgrade452to460()});
 
         _upgradeMap.put("3.0.5", new DbUpgrade[] {new Upgrade305to306(), new Upgrade306to307(), new Upgrade307to410(), new Upgrade410to420(), new Upgrade420to421(),
-            new Upgrade421to430(), new Upgrade430to440(), new Upgrade440to441(), new Upgrade441to442(), new Upgrade442to450(), new Upgrade450to451(), new Upgrade451to460()});
+            new Upgrade421to430(), new Upgrade430to440(), new Upgrade440to441(), new Upgrade441to442(), new Upgrade442to450(), new Upgrade450to451(), new Upgrade451to452(), new Upgrade452to460()});
 
         _upgradeMap.put("3.0.6", new DbUpgrade[] {new Upgrade306to307(), new Upgrade307to410(), new Upgrade410to420(), new Upgrade420to421(), new Upgrade421to430(),
-            new Upgrade430to440(), new Upgrade440to441(), new Upgrade441to442(), new Upgrade442to450(), new Upgrade450to451(), new Upgrade451to460()});
+            new Upgrade430to440(), new Upgrade440to441(), new Upgrade441to442(), new Upgrade442to450(), new Upgrade450to451(), new Upgrade451to452(), new Upgrade452to460()});
 
-        _upgradeMap.put("3.0.7", new DbUpgrade[] {new Upgrade307to410(), new Upgrade410to420(), new Upgrade420to421(), new Upgrade421to430(), new Upgrade430to440(), new Upgrade440to441(), new Upgrade441to442(), new Upgrade442to450(), new Upgrade450to451(), new Upgrade451to460()});
+        _upgradeMap.put("3.0.7", new DbUpgrade[] {new Upgrade307to410(), new Upgrade410to420(), new Upgrade420to421(), new Upgrade421to430(), new Upgrade430to440(), new Upgrade440to441(), new Upgrade441to442(), new Upgrade442to450(), new Upgrade450to451(), new Upgrade451to452(), new Upgrade452to460()});
 
         _upgradeMap.put("2.2.15", new DbUpgrade[] {new Upgrade2214to30(), new Upgrade30to301(), new Upgrade301to302(),
             new Upgrade302to303(), new Upgrade303to304(), new Upgrade304to305(), new Upgrade305to306(), new Upgrade306to307(), new Upgrade307to410(),
             new Upgrade410to420(),
-            new Upgrade420to421(), new Upgrade421to430(), new Upgrade430to440(), new Upgrade440to441(), new Upgrade441to442(), new Upgrade442to450(), new Upgrade450to451(), new Upgrade451to460()});
+            new Upgrade420to421(), new Upgrade421to430(), new Upgrade430to440(), new Upgrade440to441(), new Upgrade441to442(), new Upgrade442to450(), new Upgrade450to451(), new Upgrade451to452(), new Upgrade452to460()});
 
         _upgradeMap.put("2.2.16", new DbUpgrade[] {new Upgrade2214to30(), new Upgrade30to301(), new Upgrade301to302(),
             new Upgrade302to303(), new Upgrade303to304(), new Upgrade304to305(), new Upgrade305to306(), new Upgrade306to307(), new Upgrade307to410(),
             new Upgrade410to420(),
-            new Upgrade420to421(), new Upgrade421to430(), new Upgrade430to440(), new Upgrade440to441(), new Upgrade441to442(), new Upgrade442to450(), new Upgrade450to451(), new Upgrade451to460()});
+            new Upgrade420to421(), new Upgrade421to430(), new Upgrade430to440(), new Upgrade440to441(), new Upgrade441to442(), new Upgrade442to450(), new Upgrade450to451(), new Upgrade451to452(), new Upgrade452to460()});
     }
 
     protected void runScript(Connection conn, File file) {

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/3ff92e88/engine/schema/src/com/cloud/upgrade/dao/Upgrade451to452.java
----------------------------------------------------------------------
diff --git a/engine/schema/src/com/cloud/upgrade/dao/Upgrade451to452.java b/engine/schema/src/com/cloud/upgrade/dao/Upgrade451to452.java
new file mode 100644
index 0000000..870e534
--- /dev/null
+++ b/engine/schema/src/com/cloud/upgrade/dao/Upgrade451to452.java
@@ -0,0 +1,67 @@
+// 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 com.cloud.upgrade.dao;
+
+import com.cloud.utils.exception.CloudRuntimeException;
+import com.cloud.utils.script.Script;
+import org.apache.log4j.Logger;
+
+import java.io.File;
+import java.sql.Connection;
+
+public class Upgrade451to452 implements DbUpgrade {
+    final static Logger s_logger = Logger.getLogger(Upgrade451to452.class);
+
+    @Override
+    public String[] getUpgradableVersionRange() {
+        return new String[] {"4.5.1", "4.5.2"};
+    }
+
+    @Override
+    public String getUpgradedVersion() {
+        return "4.5.2";
+    }
+
+    @Override
+    public boolean supportsRollingUpgrade() {
+        return false;
+    }
+
+    @Override
+    public File[] getPrepareScripts() {
+        String script = Script.findScript("", "db/schema-451to452.sql");
+        if (script == null) {
+            throw new CloudRuntimeException("Unable to find db/schema-451to452.sql");
+        }
+        return new File[] {new File(script)};
+    }
+
+    @Override
+    public void performDataMigration(Connection conn) {
+    }
+
+    @Override
+    public File[] getCleanupScripts() {
+        String script = Script.findScript("", "db/schema-451to452-cleanup.sql");
+        if (script == null) {
+            throw new CloudRuntimeException("Unable to find db/schema-451to452-cleanup.sql");
+        }
+
+        return new File[] {new File(script)};
+    }
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/3ff92e88/engine/schema/src/com/cloud/upgrade/dao/Upgrade451to460.java
----------------------------------------------------------------------
diff --git a/engine/schema/src/com/cloud/upgrade/dao/Upgrade451to460.java b/engine/schema/src/com/cloud/upgrade/dao/Upgrade451to460.java
deleted file mode 100644
index 383654f..0000000
--- a/engine/schema/src/com/cloud/upgrade/dao/Upgrade451to460.java
+++ /dev/null
@@ -1,149 +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 com.cloud.upgrade.dao;
-
-import java.io.File;
-import java.sql.Connection;
-import java.sql.PreparedStatement;
-import java.sql.ResultSet;
-import java.sql.SQLException;
-
-import org.apache.log4j.Logger;
-
-import com.cloud.utils.exception.CloudRuntimeException;
-import com.cloud.utils.script.Script;
-
-public class Upgrade451to460 implements DbUpgrade {
-    final static Logger s_logger = Logger.getLogger(Upgrade451to460.class);
-
-    @Override
-    public String[] getUpgradableVersionRange() {
-        return new String[] { "4.5.1", "4.6.0" };
-    }
-
-    @Override
-    public String getUpgradedVersion() {
-        return "4.6.0";
-    }
-
-    @Override
-    public boolean supportsRollingUpgrade() {
-        return false;
-    }
-
-    @Override
-    public File[] getPrepareScripts() {
-        final String script = Script.findScript("", "db/schema-451to460.sql");
-        if (script == null) {
-            throw new CloudRuntimeException("Unable to find db/schema-451to460.sql");
-        }
-
-        return new File[] { new File(script) };
-    }
-
-    @Override
-    public void performDataMigration(final Connection conn) {
-        updateVMInstanceUserId(conn);
-    }
-
-    public void updateVMInstanceUserId(final Connection conn) {
-        // For schemas before this, copy first user from an account_id which
-        // deployed already running VMs
-        s_logger.debug("Updating vm_instance column user_id using first user in vm_instance's account_id");
-        final String vmInstanceSql = "SELECT id, account_id FROM `cloud`.`vm_instance`";
-        final String userSql = "SELECT id FROM `cloud`.`user` where account_id=?";
-        final String userIdUpdateSql = "update `cloud`.`vm_instance` set user_id=? where id=?";
-        try (PreparedStatement selectStatement = conn.prepareStatement(vmInstanceSql)) {
-            final ResultSet results = selectStatement.executeQuery();
-            while (results.next()) {
-                final long vmId = results.getLong(1);
-                final long accountId = results.getLong(2);
-                try (PreparedStatement selectUserStatement = conn.prepareStatement(userSql)) {
-                    selectUserStatement.setLong(1, accountId);
-                    final ResultSet userResults = selectUserStatement.executeQuery();
-                    if (userResults.next()) {
-                        final long userId = userResults.getLong(1);
-                        try (PreparedStatement updateStatement = conn.prepareStatement(userIdUpdateSql)) {
-                            updateStatement.setLong(1, userId);
-                            updateStatement.setLong(2, vmId);
-                            updateStatement.executeUpdate();
-                        } catch (final SQLException e) {
-                            throw new CloudRuntimeException("Unable to update user ID " + userId + " on vm_instance id=" + vmId, e);
-                        }
-                    }
-
-                } catch (final SQLException e) {
-                    throw new CloudRuntimeException("Unable to update user ID using accountId " + accountId + " on vm_instance id=" + vmId, e);
-                }
-            }
-        } catch (final SQLException e) {
-            throw new CloudRuntimeException("Unable to update user Ids for previously deployed VMs", e);
-        }
-        s_logger.debug("Done updating user Ids for previously deployed VMs");
-        addRedundancyForNwAndVpc(conn);
-        removeBumPriorityColumn(conn);
-    }
-
-    private void addRedundancyForNwAndVpc(final Connection conn) {
-        ResultSet rs = null;
-        try (PreparedStatement addRedundantColToVpcOfferingPstmt = conn
-                .prepareStatement("ALTER TABLE `cloud`.`vpc_offerings` ADD COLUMN `redundant_router_service` tinyint(1) DEFAULT 0");
-                PreparedStatement addRedundantColToVpcPstmt = conn.prepareStatement("ALTER TABLE `cloud`.`vpc` ADD COLUMN `redundant` tinyint(1) DEFAULT 0");
-                PreparedStatement addRedundantColToNwPstmt = conn.prepareStatement("ALTER TABLE `cloud`.`networks` ADD COLUMN `redundant` tinyint(1) DEFAULT 0");
-
-                // The redundancy of the networks must be based on the
-                // redundancy of their network offerings
-                PreparedStatement redundancyPerNwPstmt = conn.prepareStatement("select distinct nw.network_offering_id from networks nw join network_offerings off "
-                        + "on nw.network_offering_id = off.id where off.redundant_router_service = 1");
-                PreparedStatement updateNwRedundancyPstmt = conn.prepareStatement("update networks set redundant = 1 where network_offering_id = ?");) {
-            addRedundantColToVpcPstmt.executeUpdate();
-            addRedundantColToVpcOfferingPstmt.executeUpdate();
-            addRedundantColToNwPstmt.executeUpdate();
-
-            rs = redundancyPerNwPstmt.executeQuery();
-            while (rs.next()) {
-                final long nwOfferingId = rs.getLong("nw.network_offering_id");
-                updateNwRedundancyPstmt.setLong(1, nwOfferingId);
-                updateNwRedundancyPstmt.executeUpdate();
-            }
-        } catch (final SQLException e) {
-            e.printStackTrace();
-            throw new CloudRuntimeException("Adding redundancy to vpc, networks and vpc_offerings failed", e);
-        }
-    }
-
-    private void removeBumPriorityColumn(final Connection conn) {
-        try (PreparedStatement removeBumPriorityColumnPstmt = conn.prepareStatement("ALTER TABLE `cloud`.`domain_router` DROP COLUMN `is_priority_bumpup`");) {
-            removeBumPriorityColumnPstmt.executeUpdate();
-        } catch (final SQLException e) {
-            e.printStackTrace();
-            throw new CloudRuntimeException("Adding redundancy to vpc, networks and vpc_offerings failed", e);
-        }
-    }
-
-    @Override
-    public File[] getCleanupScripts() {
-        final String script = Script.findScript("", "db/schema-451to460-cleanup.sql");
-        if (script == null) {
-            throw new CloudRuntimeException("Unable to find db/schema-451to460-cleanup.sql");
-        }
-
-        return new File[] { new File(script) };
-    }
-
-}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/3ff92e88/engine/schema/src/com/cloud/upgrade/dao/Upgrade452to460.java
----------------------------------------------------------------------
diff --git a/engine/schema/src/com/cloud/upgrade/dao/Upgrade452to460.java b/engine/schema/src/com/cloud/upgrade/dao/Upgrade452to460.java
new file mode 100644
index 0000000..9c2b1e3
--- /dev/null
+++ b/engine/schema/src/com/cloud/upgrade/dao/Upgrade452to460.java
@@ -0,0 +1,149 @@
+// 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 com.cloud.upgrade.dao;
+
+import java.io.File;
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+
+import org.apache.log4j.Logger;
+
+import com.cloud.utils.exception.CloudRuntimeException;
+import com.cloud.utils.script.Script;
+
+public class Upgrade452to460 implements DbUpgrade {
+    final static Logger s_logger = Logger.getLogger(Upgrade452to460.class);
+
+    @Override
+    public String[] getUpgradableVersionRange() {
+        return new String[] { "4.5.2", "4.6.0" };
+    }
+
+    @Override
+    public String getUpgradedVersion() {
+        return "4.6.0";
+    }
+
+    @Override
+    public boolean supportsRollingUpgrade() {
+        return false;
+    }
+
+    @Override
+    public File[] getPrepareScripts() {
+        final String script = Script.findScript("", "db/schema-452to460.sql");
+        if (script == null) {
+            throw new CloudRuntimeException("Unable to find db/schema-452to460.sql");
+        }
+
+        return new File[] { new File(script) };
+    }
+
+    @Override
+    public void performDataMigration(final Connection conn) {
+        updateVMInstanceUserId(conn);
+    }
+
+    public void updateVMInstanceUserId(final Connection conn) {
+        // For schemas before this, copy first user from an account_id which
+        // deployed already running VMs
+        s_logger.debug("Updating vm_instance column user_id using first user in vm_instance's account_id");
+        final String vmInstanceSql = "SELECT id, account_id FROM `cloud`.`vm_instance`";
+        final String userSql = "SELECT id FROM `cloud`.`user` where account_id=?";
+        final String userIdUpdateSql = "update `cloud`.`vm_instance` set user_id=? where id=?";
+        try (PreparedStatement selectStatement = conn.prepareStatement(vmInstanceSql)) {
+            final ResultSet results = selectStatement.executeQuery();
+            while (results.next()) {
+                final long vmId = results.getLong(1);
+                final long accountId = results.getLong(2);
+                try (PreparedStatement selectUserStatement = conn.prepareStatement(userSql)) {
+                    selectUserStatement.setLong(1, accountId);
+                    final ResultSet userResults = selectUserStatement.executeQuery();
+                    if (userResults.next()) {
+                        final long userId = userResults.getLong(1);
+                        try (PreparedStatement updateStatement = conn.prepareStatement(userIdUpdateSql)) {
+                            updateStatement.setLong(1, userId);
+                            updateStatement.setLong(2, vmId);
+                            updateStatement.executeUpdate();
+                        } catch (final SQLException e) {
+                            throw new CloudRuntimeException("Unable to update user ID " + userId + " on vm_instance id=" + vmId, e);
+                        }
+                    }
+
+                } catch (final SQLException e) {
+                    throw new CloudRuntimeException("Unable to update user ID using accountId " + accountId + " on vm_instance id=" + vmId, e);
+                }
+            }
+        } catch (final SQLException e) {
+            throw new CloudRuntimeException("Unable to update user Ids for previously deployed VMs", e);
+        }
+        s_logger.debug("Done updating user Ids for previously deployed VMs");
+        addRedundancyForNwAndVpc(conn);
+        removeBumPriorityColumn(conn);
+    }
+
+    private void addRedundancyForNwAndVpc(final Connection conn) {
+        ResultSet rs = null;
+        try (PreparedStatement addRedundantColToVpcOfferingPstmt = conn
+                .prepareStatement("ALTER TABLE `cloud`.`vpc_offerings` ADD COLUMN `redundant_router_service` tinyint(1) DEFAULT 0");
+                PreparedStatement addRedundantColToVpcPstmt = conn.prepareStatement("ALTER TABLE `cloud`.`vpc` ADD COLUMN `redundant` tinyint(1) DEFAULT 0");
+                PreparedStatement addRedundantColToNwPstmt = conn.prepareStatement("ALTER TABLE `cloud`.`networks` ADD COLUMN `redundant` tinyint(1) DEFAULT 0");
+
+                // The redundancy of the networks must be based on the
+                // redundancy of their network offerings
+                PreparedStatement redundancyPerNwPstmt = conn.prepareStatement("select distinct nw.network_offering_id from networks nw join network_offerings off "
+                        + "on nw.network_offering_id = off.id where off.redundant_router_service = 1");
+                PreparedStatement updateNwRedundancyPstmt = conn.prepareStatement("update networks set redundant = 1 where network_offering_id = ?");) {
+            addRedundantColToVpcPstmt.executeUpdate();
+            addRedundantColToVpcOfferingPstmt.executeUpdate();
+            addRedundantColToNwPstmt.executeUpdate();
+
+            rs = redundancyPerNwPstmt.executeQuery();
+            while (rs.next()) {
+                final long nwOfferingId = rs.getLong("nw.network_offering_id");
+                updateNwRedundancyPstmt.setLong(1, nwOfferingId);
+                updateNwRedundancyPstmt.executeUpdate();
+            }
+        } catch (final SQLException e) {
+            e.printStackTrace();
+            throw new CloudRuntimeException("Adding redundancy to vpc, networks and vpc_offerings failed", e);
+        }
+    }
+
+    private void removeBumPriorityColumn(final Connection conn) {
+        try (PreparedStatement removeBumPriorityColumnPstmt = conn.prepareStatement("ALTER TABLE `cloud`.`domain_router` DROP COLUMN `is_priority_bumpup`");) {
+            removeBumPriorityColumnPstmt.executeUpdate();
+        } catch (final SQLException e) {
+            e.printStackTrace();
+            throw new CloudRuntimeException("Adding redundancy to vpc, networks and vpc_offerings failed", e);
+        }
+    }
+
+    @Override
+    public File[] getCleanupScripts() {
+        final String script = Script.findScript("", "db/schema-452to460-cleanup.sql");
+        if (script == null) {
+            throw new CloudRuntimeException("Unable to find db/schema-452to460-cleanup.sql");
+        }
+
+        return new File[] { new File(script) };
+    }
+
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/3ff92e88/plugins/user-authenticators/saml2/src/org/apache/cloudstack/api/command/ListIdpsCmd.java
----------------------------------------------------------------------
diff --git a/plugins/user-authenticators/saml2/src/org/apache/cloudstack/api/command/ListIdpsCmd.java b/plugins/user-authenticators/saml2/src/org/apache/cloudstack/api/command/ListIdpsCmd.java
index 7d7c95e..0592425 100644
--- a/plugins/user-authenticators/saml2/src/org/apache/cloudstack/api/command/ListIdpsCmd.java
+++ b/plugins/user-authenticators/saml2/src/org/apache/cloudstack/api/command/ListIdpsCmd.java
@@ -36,6 +36,7 @@ import javax.inject.Inject;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 import javax.servlet.http.HttpSession;
+import java.net.InetAddress;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
@@ -71,8 +72,7 @@ public class ListIdpsCmd extends BaseCmd implements APIAuthenticator {
     }
 
     @Override
-    public String authenticate(String command, Map<String, Object[]> params, HttpSession session, String remoteAddress, String responseType, StringBuilder auditTrailSb, final HttpServletRequest req, final HttpServletResponse resp) throws ServerApiException {
-        auditTrailSb.append("=== SAML List IdPs ===");
+    public String authenticate(String command, Map<String, Object[]> params, HttpSession session, InetAddress remoteAddress, String responseType, StringBuilder auditTrailSb, HttpServletRequest req, HttpServletResponse resp) throws ServerApiException {
         ListResponse<IdpResponse> response = new ListResponse<IdpResponse>();
         List<IdpResponse> idpResponseList = new ArrayList<IdpResponse>();
         for (SAMLProviderMetadata metadata: _samlAuthManager.getAllIdPMetadata()) {

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/3ff92e88/setup/db/db/schema-451to460-cleanup.sql
----------------------------------------------------------------------
diff --git a/setup/db/db/schema-451to460-cleanup.sql b/setup/db/db/schema-451to460-cleanup.sql
deleted file mode 100644
index 4bbaa95..0000000
--- a/setup/db/db/schema-451to460-cleanup.sql
+++ /dev/null
@@ -1,20 +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.
-
---
--- Schema cleanup from 4.5.1 to 4.6.0
---

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/3ff92e88/setup/db/db/schema-451to460.sql
----------------------------------------------------------------------
diff --git a/setup/db/db/schema-451to460.sql b/setup/db/db/schema-451to460.sql
deleted file mode 100644
index 0abd4f8..0000000
--- a/setup/db/db/schema-451to460.sql
+++ /dev/null
@@ -1,400 +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.
-
---
--- Schema upgrade from 4.5.1 to 4.6.0
---
-
-ALTER TABLE `cloud`.`snapshots` ADD COLUMN `min_iops` bigint(20) unsigned COMMENT 'Minimum IOPS';
-ALTER TABLE `cloud`.`snapshots` ADD COLUMN `max_iops` bigint(20) unsigned COMMENT 'Maximum IOPS';
-
-INSERT IGNORE INTO `cloud`.`configuration` VALUES ("Advanced", 'DEFAULT', 'management-server', "stats.output.uri", "", "URI to additionally send StatsCollector statistics to", "", NULL, NULL, 0);
-
-DROP VIEW IF EXISTS `cloud`.`domain_view`;
-CREATE VIEW `cloud`.`domain_view` AS
-    select
-        domain.id id,
-        domain.parent parent,
-        domain.name name,
-        domain.uuid uuid,
-        domain.owner owner,
-        domain.path path,
-        domain.level level,
-        domain.child_count child_count,
-        domain.next_child_seq next_child_seq,
-        domain.removed removed,
-        domain.state state,
-        domain.network_domain network_domain,
-        domain.type type,
-        vmlimit.max vmLimit,
-        vmcount.count vmTotal,
-        iplimit.max ipLimit,
-        ipcount.count ipTotal,
-        volumelimit.max volumeLimit,
-        volumecount.count volumeTotal,
-        snapshotlimit.max snapshotLimit,
-        snapshotcount.count snapshotTotal,
-        templatelimit.max templateLimit,
-        templatecount.count templateTotal,
-        vpclimit.max vpcLimit,
-        vpccount.count vpcTotal,
-        projectlimit.max projectLimit,
-        projectcount.count projectTotal,
-        networklimit.max networkLimit,
-        networkcount.count networkTotal,
-        cpulimit.max cpuLimit,
-        cpucount.count cpuTotal,
-        memorylimit.max memoryLimit,
-        memorycount.count memoryTotal,
-        primary_storage_limit.max primaryStorageLimit,
-        primary_storage_count.count primaryStorageTotal,
-        secondary_storage_limit.max secondaryStorageLimit,
-        secondary_storage_count.count secondaryStorageTotal
-    from
-        `cloud`.`domain`
-            left join
-        `cloud`.`resource_limit` vmlimit ON domain.id = vmlimit.domain_id
-            and vmlimit.type = 'user_vm'
-            left join
-        `cloud`.`resource_count` vmcount ON domain.id = vmcount.domain_id
-            and vmcount.type = 'user_vm'
-            left join
-        `cloud`.`resource_limit` iplimit ON domain.id = iplimit.domain_id
-            and iplimit.type = 'public_ip'
-            left join
-        `cloud`.`resource_count` ipcount ON domain.id = ipcount.domain_id
-            and ipcount.type = 'public_ip'
-            left join
-        `cloud`.`resource_limit` volumelimit ON domain.id = volumelimit.domain_id
-            and volumelimit.type = 'volume'
-            left join
-        `cloud`.`resource_count` volumecount ON domain.id = volumecount.domain_id
-            and volumecount.type = 'volume'
-            left join
-        `cloud`.`resource_limit` snapshotlimit ON domain.id = snapshotlimit.domain_id
-            and snapshotlimit.type = 'snapshot'
-            left join
-        `cloud`.`resource_count` snapshotcount ON domain.id = snapshotcount.domain_id
-            and snapshotcount.type = 'snapshot'
-            left join
-        `cloud`.`resource_limit` templatelimit ON domain.id = templatelimit.domain_id
-            and templatelimit.type = 'template'
-            left join
-        `cloud`.`resource_count` templatecount ON domain.id = templatecount.domain_id
-            and templatecount.type = 'template'
-            left join
-        `cloud`.`resource_limit` vpclimit ON domain.id = vpclimit.domain_id
-            and vpclimit.type = 'vpc'
-            left join
-        `cloud`.`resource_count` vpccount ON domain.id = vpccount.domain_id
-            and vpccount.type = 'vpc'
-            left join
-        `cloud`.`resource_limit` projectlimit ON domain.id = projectlimit.domain_id
-            and projectlimit.type = 'project'
-            left join
-        `cloud`.`resource_count` projectcount ON domain.id = projectcount.domain_id
-            and projectcount.type = 'project'
-            left join
-        `cloud`.`resource_limit` networklimit ON domain.id = networklimit.domain_id
-            and networklimit.type = 'network'
-            left join
-        `cloud`.`resource_count` networkcount ON domain.id = networkcount.domain_id
-            and networkcount.type = 'network'
-            left join
-        `cloud`.`resource_limit` cpulimit ON domain.id = cpulimit.domain_id
-            and cpulimit.type = 'cpu'
-            left join
-        `cloud`.`resource_count` cpucount ON domain.id = cpucount.domain_id
-            and cpucount.type = 'cpu'
-            left join
-        `cloud`.`resource_limit` memorylimit ON domain.id = memorylimit.domain_id
-            and memorylimit.type = 'memory'
-            left join
-        `cloud`.`resource_count` memorycount ON domain.id = memorycount.domain_id
-            and memorycount.type = 'memory'
-            left join
-        `cloud`.`resource_limit` primary_storage_limit ON domain.id = primary_storage_limit.domain_id
-            and primary_storage_limit.type = 'primary_storage'
-            left join
-        `cloud`.`resource_count` primary_storage_count ON domain.id = primary_storage_count.domain_id
-            and primary_storage_count.type = 'primary_storage'
-            left join
-        `cloud`.`resource_limit` secondary_storage_limit ON domain.id = secondary_storage_limit.domain_id
-            and secondary_storage_limit.type = 'secondary_storage'
-            left join
-        `cloud`.`resource_count` secondary_storage_count ON domain.id = secondary_storage_count.domain_id
-            and secondary_storage_count.type = 'secondary_storage';
-
-INSERT IGNORE INTO `cloud`.`configuration` VALUES ('Domain Defaults', 'DEFAULT', 'management-server', 'max.domain.user.vms','-1','The default maximum number of user VMs that can be deployed for a domain', '-1', NULL, NULL, 0);
-INSERT IGNORE INTO `cloud`.`configuration` VALUES ('Domain Defaults', 'DEFAULT', 'management-server', 'max.domain.public.ips','-1','The default maximum number of public IPs that can be consumed by a domain', '-1', NULL, NULL, 0);
-INSERT IGNORE INTO `cloud`.`configuration` VALUES ('Domain Defaults', 'DEFAULT', 'management-server', 'max.domain.templates','-1','The default maximum number of templates that can be deployed for a domain', '-1', NULL, NULL, 0);
-INSERT IGNORE INTO `cloud`.`configuration` VALUES ('Domain Defaults', 'DEFAULT', 'management-server', 'max.domain.snapshots','-1','The default maximum number of snapshots that can be created for a domain', '-1', NULL, NULL, 0);
-INSERT IGNORE INTO `cloud`.`configuration` VALUES ('Domain Defaults', 'DEFAULT', 'management-server', 'max.domain.volumes','-1','The default maximum number of volumes that can be created for a domain', '-1', NULL, NULL, 0);
-INSERT IGNORE INTO `cloud`.`configuration` VALUES ('Domain Defaults', 'DEFAULT', 'management-server', 'max.domain.networks', '-1', 'The default maximum number of networks that can be created for a domain', '-1', NULL, NULL, 0);
-INSERT IGNORE INTO `cloud`.`configuration` VALUES ('Domain Defaults', 'DEFAULT', 'management-server', 'max.domain.vpcs', '-1', 'The default maximum number of vpcs that can be created for a domain', '-1', NULL, NULL, 0);
-INSERT IGNORE INTO `cloud`.`configuration` VALUES ('Domain Defaults', 'DEFAULT', 'management-server', 'max.domain.cpus', '-1', 'The default maximum number of cpu cores that can be used for a domain', '-1', NULL, NULL, 0);
-INSERT IGNORE INTO `cloud`.`configuration` VALUES ('Domain Defaults', 'DEFAULT', 'management-server', 'max.domain.memory', '-1', 'The default maximum memory (in MiB) that can be used for a domain', '-1', NULL, NULL, 0);
-INSERT IGNORE INTO `cloud`.`configuration` VALUES ('Domain Defaults', 'DEFAULT', 'management-server', 'max.domain.primary.storage', '-1', 'The default maximum primary storage space (in GiB) that can be used for a domain', '-1', NULL, NULL, 0);
-INSERT IGNORE INTO `cloud`.`configuration` VALUES ('Domain Defaults', 'DEFAULT', 'management-server', 'max.domain.secondary.storage', '-1', 'The default maximum secondary storage space (in GiB) that can be used for a domain', '-1', NULL, NULL, 0);
-
-ALTER TABLE `cloud`.`vm_instance` ADD COLUMN `user_id` bigint unsigned NOT NULL DEFAULT 1 COMMENT 'user id of VM deployer';
-
-DROP VIEW IF EXISTS `cloud`.`user_vm_view`;
-CREATE VIEW `cloud`.`user_vm_view` AS
-    select
-        vm_instance.id id,
-        vm_instance.name name,
-        user_vm.display_name display_name,
-        user_vm.user_data user_data,
-        account.id account_id,
-        account.uuid account_uuid,
-        account.account_name account_name,
-        account.type account_type,
-        domain.id domain_id,
-        domain.uuid domain_uuid,
-        domain.name domain_name,
-        domain.path domain_path,
-        projects.id project_id,
-        projects.uuid project_uuid,
-        projects.name project_name,
-        instance_group.id instance_group_id,
-        instance_group.uuid instance_group_uuid,
-        instance_group.name instance_group_name,
-        vm_instance.uuid uuid,
-        vm_instance.user_id user_id,
-        vm_instance.last_host_id last_host_id,
-        vm_instance.vm_type type,
-        vm_instance.limit_cpu_use limit_cpu_use,
-        vm_instance.created created,
-        vm_instance.state state,
-        vm_instance.removed removed,
-        vm_instance.ha_enabled ha_enabled,
-        vm_instance.hypervisor_type hypervisor_type,
-        vm_instance.instance_name instance_name,
-        vm_instance.guest_os_id guest_os_id,
-        vm_instance.display_vm display_vm,
-        guest_os.uuid guest_os_uuid,
-        vm_instance.pod_id pod_id,
-        host_pod_ref.uuid pod_uuid,
-        vm_instance.private_ip_address private_ip_address,
-        vm_instance.private_mac_address private_mac_address,
-        vm_instance.vm_type vm_type,
-        data_center.id data_center_id,
-        data_center.uuid data_center_uuid,
-        data_center.name data_center_name,
-        data_center.is_security_group_enabled security_group_enabled,
-        data_center.networktype data_center_type,
-        host.id host_id,
-        host.uuid host_uuid,
-        host.name host_name,
-        vm_template.id template_id,
-        vm_template.uuid template_uuid,
-        vm_template.name template_name,
-        vm_template.display_text template_display_text,
-        vm_template.enable_password password_enabled,
-        iso.id iso_id,
-        iso.uuid iso_uuid,
-        iso.name iso_name,
-        iso.display_text iso_display_text,
-        service_offering.id service_offering_id,
-        svc_disk_offering.uuid service_offering_uuid,
-        disk_offering.uuid disk_offering_uuid,
-        disk_offering.id disk_offering_id,
-        Case
-             When (`cloud`.`service_offering`.`cpu` is null) then (`custom_cpu`.`value`)
-             Else ( `cloud`.`service_offering`.`cpu`)
-        End as `cpu`,
-        Case
-            When (`cloud`.`service_offering`.`speed` is null) then (`custom_speed`.`value`)
-            Else ( `cloud`.`service_offering`.`speed`)
-        End as `speed`,
-        Case
-            When (`cloud`.`service_offering`.`ram_size` is null) then (`custom_ram_size`.`value`)
-            Else ( `cloud`.`service_offering`.`ram_size`)
-        END as `ram_size`,
-        svc_disk_offering.name service_offering_name,
-        disk_offering.name disk_offering_name,
-        storage_pool.id pool_id,
-        storage_pool.uuid pool_uuid,
-        storage_pool.pool_type pool_type,
-        volumes.id volume_id,
-        volumes.uuid volume_uuid,
-        volumes.device_id volume_device_id,
-        volumes.volume_type volume_type,
-        security_group.id security_group_id,
-        security_group.uuid security_group_uuid,
-        security_group.name security_group_name,
-        security_group.description security_group_description,
-        nics.id nic_id,
-        nics.uuid nic_uuid,
-        nics.network_id network_id,
-        nics.ip4_address ip_address,
-        nics.ip6_address ip6_address,
-        nics.ip6_gateway ip6_gateway,
-        nics.ip6_cidr ip6_cidr,
-        nics.default_nic is_default_nic,
-        nics.gateway gateway,
-        nics.netmask netmask,
-        nics.mac_address mac_address,
-        nics.broadcast_uri broadcast_uri,
-        nics.isolation_uri isolation_uri,
-        vpc.id vpc_id,
-        vpc.uuid vpc_uuid,
-        networks.uuid network_uuid,
-        networks.name network_name,
-        networks.traffic_type traffic_type,
-        networks.guest_type guest_type,
-        user_ip_address.id public_ip_id,
-        user_ip_address.uuid public_ip_uuid,
-        user_ip_address.public_ip_address public_ip_address,
-        ssh_keypairs.keypair_name keypair_name,
-        resource_tags.id tag_id,
-        resource_tags.uuid tag_uuid,
-        resource_tags.key tag_key,
-        resource_tags.value tag_value,
-        resource_tags.domain_id tag_domain_id,
-        resource_tags.account_id tag_account_id,
-        resource_tags.resource_id tag_resource_id,
-        resource_tags.resource_uuid tag_resource_uuid,
-        resource_tags.resource_type tag_resource_type,
-        resource_tags.customer tag_customer,
-        async_job.id job_id,
-        async_job.uuid job_uuid,
-        async_job.job_status job_status,
-        async_job.account_id job_account_id,
-        affinity_group.id affinity_group_id,
-        affinity_group.uuid affinity_group_uuid,
-        affinity_group.name affinity_group_name,
-        affinity_group.description affinity_group_description,
-        vm_instance.dynamically_scalable dynamically_scalable
-
-    from
-        `cloud`.`user_vm`
-            inner join
-        `cloud`.`vm_instance` ON vm_instance.id = user_vm.id
-            and vm_instance.removed is NULL
-            inner join
-        `cloud`.`account` ON vm_instance.account_id = account.id
-            inner join
-        `cloud`.`domain` ON vm_instance.domain_id = domain.id
-            left join
-        `cloud`.`guest_os` ON vm_instance.guest_os_id = guest_os.id
-            left join
-        `cloud`.`host_pod_ref` ON vm_instance.pod_id = host_pod_ref.id
-            left join
-        `cloud`.`projects` ON projects.project_account_id = account.id
-            left join
-        `cloud`.`instance_group_vm_map` ON vm_instance.id = instance_group_vm_map.instance_id
-            left join
-        `cloud`.`instance_group` ON instance_group_vm_map.group_id = instance_group.id
-            left join
-        `cloud`.`data_center` ON vm_instance.data_center_id = data_center.id
-            left join
-        `cloud`.`host` ON vm_instance.host_id = host.id
-            left join
-        `cloud`.`vm_template` ON vm_instance.vm_template_id = vm_template.id
-            left join
-        `cloud`.`vm_template` iso ON iso.id = user_vm.iso_id
-            left join
-        `cloud`.`service_offering` ON vm_instance.service_offering_id = service_offering.id
-            left join
-        `cloud`.`disk_offering` svc_disk_offering ON vm_instance.service_offering_id = svc_disk_offering.id
-            left join
-        `cloud`.`disk_offering` ON vm_instance.disk_offering_id = disk_offering.id
-            left join
-        `cloud`.`volumes` ON vm_instance.id = volumes.instance_id
-            left join
-        `cloud`.`storage_pool` ON volumes.pool_id = storage_pool.id
-            left join
-        `cloud`.`security_group_vm_map` ON vm_instance.id = security_group_vm_map.instance_id
-            left join
-        `cloud`.`security_group` ON security_group_vm_map.security_group_id = security_group.id
-            left join
-        `cloud`.`nics` ON vm_instance.id = nics.instance_id and nics.removed is null
-            left join
-        `cloud`.`networks` ON nics.network_id = networks.id
-            left join
-        `cloud`.`vpc` ON networks.vpc_id = vpc.id and vpc.removed is null
-            left join
-        `cloud`.`user_ip_address` ON user_ip_address.vm_id = vm_instance.id
-            left join
-        `cloud`.`user_vm_details` as ssh_details ON ssh_details.vm_id = vm_instance.id
-            and ssh_details.name = 'SSH.PublicKey'
-            left join
-        `cloud`.`ssh_keypairs` ON ssh_keypairs.public_key = ssh_details.value
-            left join
-        `cloud`.`resource_tags` ON resource_tags.resource_id = vm_instance.id
-            and resource_tags.resource_type = 'UserVm'
-            left join
-        `cloud`.`async_job` ON async_job.instance_id = vm_instance.id
-            and async_job.instance_type = 'VirtualMachine'
-            and async_job.job_status = 0
-            left join
-        `cloud`.`affinity_group_vm_map` ON vm_instance.id = affinity_group_vm_map.instance_id
-            left join
-        `cloud`.`affinity_group` ON affinity_group_vm_map.affinity_group_id = affinity_group.id
-            left join
-        `cloud`.`user_vm_details` `custom_cpu`  ON (((`custom_cpu`.`vm_id` = `cloud`.`vm_instance`.`id`) and (`custom_cpu`.`name` = 'CpuNumber')))
-            left join
-        `cloud`.`user_vm_details` `custom_speed`  ON (((`custom_speed`.`vm_id` = `cloud`.`vm_instance`.`id`) and (`custom_speed`.`name` = 'CpuSpeed')))
-           left join
-        `cloud`.`user_vm_details` `custom_ram_size`  ON (((`custom_ram_size`.`vm_id` = `cloud`.`vm_instance`.`id`) and (`custom_ram_size`.`name` = 'memory')));
-
--- ovm3 stuff
-INSERT INTO `cloud`.`guest_os_hypervisor` (hypervisor_type, guest_os_name, guest_os_id) VALUES  ("Ovm3", 'Sun Solaris 10(32-bit)', 79);
-INSERT INTO `cloud`.`guest_os_hypervisor` (hypervisor_type, guest_os_name, guest_os_id) VALUES  ("Ovm3", 'Sun Solaris 10(64-bit)', 80);
-INSERT INTO `cloud`.`guest_os_hypervisor` (hypervisor_type, guest_os_name, guest_os_id) VALUES  ("Ovm3", 'Sun Solaris 11(32-bit)', 158);
-INSERT INTO `cloud`.`guest_os_hypervisor` (hypervisor_type, guest_os_name, guest_os_id) VALUES  ("Ovm3", 'Sun Solaris 11(64-bit)', 159);
-INSERT INTO `cloud`.`guest_os_hypervisor` (hypervisor_type, guest_os_name, guest_os_id) VALUES  ("Ovm3", 'Other Linux (32-bit)', 98);
-INSERT INTO `cloud`.`guest_os_hypervisor` (hypervisor_type, guest_os_name, guest_os_id) VALUES  ("Ovm3", 'Other Linux (64-bit)', 99);
-INSERT INTO `cloud`.`guest_os_hypervisor` (hypervisor_type, guest_os_name, guest_os_id) VALUES  ('Ovm3', 'Other PV (32-bit)', 139);
-INSERT INTO `cloud`.`guest_os_hypervisor` (hypervisor_type, guest_os_name, guest_os_id) VALUES  ('Ovm3', 'Other PV (64-bit)', 140);
-INSERT INTO `cloud`.`guest_os_hypervisor` (hypervisor_type, guest_os_name, guest_os_id) VALUES  ('Ovm3', 'DOS', 102);
-INSERT INTO `cloud`.`guest_os_hypervisor` (hypervisor_type, guest_os_name, guest_os_id) VALUES  ("Ovm3", 'Windows 8 (32-bit)', 165);
-INSERT INTO `cloud`.`guest_os_hypervisor` (hypervisor_type, guest_os_name, guest_os_id) VALUES  ("Ovm3", 'Windows 8 (64-bit)', 166);
-INSERT INTO `cloud`.`guest_os_hypervisor` (hypervisor_type, guest_os_name, guest_os_id) VALUES  ("Ovm3", 'Windows Server 2012 (64-bit)', 167);
-
-INSERT IGNORE INTO `cloud`.`hypervisor_capabilities`(hypervisor_type, hypervisor_version, max_guests_limit, security_group_enabled) VALUES ('Ovm3', '3.2', 25, 0);
-INSERT IGNORE INTO `cloud`.`hypervisor_capabilities`(hypervisor_type, hypervisor_version, max_guests_limit, security_group_enabled) VALUES ('Ovm3', '3.3', 50, 0);
-UPDATE  `cloud`.`volumes` v,  `cloud`.`storage_pool` s,  `cloud`.`cluster` c  set v.format='RAW' where v.pool_id=s.id and s.cluster_id=c.id and c.hypervisor_type='Ovm3';
-UPDATE configuration SET value='KVM,XenServer,VMware,BareMetal,Ovm,Ovm3,LXC' WHERE name='hypervisor.list';
-INSERT INTO `cloud`.`vm_template` (id, uuid, unique_name, name, public, created, type, hvm, bits, account_id, url, checksum, enable_password, display_text, format, guest_os_id,featured, cross_zones, hypervisor_type, state)
-VALUES (12, UUID(), 'routing-12', 'SystemVM Template (Ovm3)', 0, now(), 'SYSTEM', 0, 64, 1, 'http://download.cloud.com/templates/4.6/systemvm64template.ovm.raw.bz2', '4425688804dbcf0abc9e9e56c53070d7', 0, 'SystemVM Template (Ovm3)', 'RAW', 183, 0, 1, 'Ovm3', 'Active' );
-
-INSERT IGNORE INTO `cloud`.`configuration` (`category`, `instance`, `component`, `name`, `value`, `default_value`, `description`) VALUES ('Advanced', 'DEFAULT', 'ManagementServer', 'ovm3.heartbeat.timeout' , '180', '120', 'Timeout value to send to the checkheartbeat script for guarding the self fencing functionality on ovm3');
-INSERT IGNORE INTO `cloud`.`configuration` (`category`, `instance`, `component`, `name`, `value`, `default_value`, `description`) VALUES ('Advanced', 'DEFAULT', 'ManagementServer', 'ovm3.heartbeat.interval' , '10', '1', 'Interval value the checkheartbeat script uses before triggering the timeout for ovm3');
-INSERT IGNORE INTO `cloud`.`configuration` VALUES ('Advanced', 'DEFAULT', 'NetworkManager', 'router.template.ovm3', 'SystemVM Template (Ovm3)', 'Name of the default router template on Ovm3.','SystemVM Template (Ovm3)', NULL, NULL, 0);
-
-UPDATE IGNORE `cloud`.`configuration` SET `value`="PLAINTEXT" WHERE `name`="user.authenticators.exclude";
-
-DROP TABLE IF EXISTS `cloud`.`external_bigswitch_vns_devices`;
-CREATE TABLE `cloud`.`external_bigswitch_bcf_devices` (
-  `id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT 'id',
-  `uuid` varchar(255) UNIQUE,
-  `physical_network_id` bigint unsigned NOT NULL COMMENT 'id of the physical network in to which bigswitch bcf device is added',
-  `provider_name` varchar(255) NOT NULL COMMENT 'Service Provider name corresponding to this bigswitch bcf device',
-  `device_name` varchar(255) NOT NULL COMMENT 'name of the bigswitch bcf device',
-  `host_id` bigint unsigned NOT NULL COMMENT 'host id coresponding to the external bigswitch bcf device',
-  `hostname` varchar(255) NOT NULL COMMENT 'host name or IP address for the bigswitch bcf device',
-  `username` varchar(255) NOT NULL COMMENT 'username for the bigswitch bcf device',
-  `password` varchar(255) NOT NULL COMMENT 'password for the bigswitch bcf device',
-  `nat` boolean NOT NULL COMMENT 'NAT support for the bigswitch bcf device',
-  `hash` varchar(255) NOT NULL COMMENT 'topology hash for the bigswitch bcf networks',
-  PRIMARY KEY  (`id`),
-  CONSTRAINT `fk_external_bigswitch_bcf_devices__host_id` FOREIGN KEY (`host_id`) REFERENCES `host`(`id`) ON DELETE CASCADE,
-  CONSTRAINT `fk_external_bigswitch_bcf_devices__physical_network_id` FOREIGN KEY (`physical_network_id`) REFERENCES `physical_network`(`id`) ON DELETE CASCADE
-) ENGINE=InnoDB DEFAULT CHARSET=utf8;

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/3ff92e88/setup/db/db/schema-452to460-cleanup.sql
----------------------------------------------------------------------
diff --git a/setup/db/db/schema-452to460-cleanup.sql b/setup/db/db/schema-452to460-cleanup.sql
new file mode 100644
index 0000000..4bbaa95
--- /dev/null
+++ b/setup/db/db/schema-452to460-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.6.0
+--