diff --git a/plugins/user-authenticators/saml2/pom.xml b/plugins/user-authenticators/saml2/pom.xml
index e0e6688d637..7de21c05caf 100644
--- a/plugins/user-authenticators/saml2/pom.xml
+++ b/plugins/user-authenticators/saml2/pom.xml
@@ -53,5 +53,11 @@
${cs.xercesImpl.version}
test
+
+ org.assertj
+ assertj-core
+ ${cs.assertj.version}
+ test
+
diff --git a/plugins/user-authenticators/saml2/src/main/java/org/apache/cloudstack/saml/SAMLUtils.java b/plugins/user-authenticators/saml2/src/main/java/org/apache/cloudstack/saml/SAMLUtils.java
index cbbdbd28bf8..c65e4c09be6 100644
--- a/plugins/user-authenticators/saml2/src/main/java/org/apache/cloudstack/saml/SAMLUtils.java
+++ b/plugins/user-authenticators/saml2/src/main/java/org/apache/cloudstack/saml/SAMLUtils.java
@@ -150,7 +150,8 @@ public class SAMLUtils {
if (spMetadata.getKeyPair() != null) {
privateKey = spMetadata.getKeyPair().getPrivate();
}
- redirectUrl = idpMetadata.getSsoUrl() + "?" + SAMLUtils.generateSAMLRequestSignature("SAMLRequest=" + SAMLUtils.encodeSAMLRequest(authnRequest), privateKey, signatureAlgorithm);
+ String appendOperator = idpMetadata.getSsoUrl().contains("?") ? "&" : "?";
+ redirectUrl = idpMetadata.getSsoUrl() + appendOperator + 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());
}
diff --git a/plugins/user-authenticators/saml2/src/test/java/org/apache/cloudstack/SAMLUtilsTest.java b/plugins/user-authenticators/saml2/src/test/java/org/apache/cloudstack/SAMLUtilsTest.java
index 433fdf3224a..cf265199b42 100644
--- a/plugins/user-authenticators/saml2/src/test/java/org/apache/cloudstack/SAMLUtilsTest.java
+++ b/plugins/user-authenticators/saml2/src/test/java/org/apache/cloudstack/SAMLUtilsTest.java
@@ -19,18 +19,22 @@
package org.apache.cloudstack;
-import java.security.KeyPair;
-import java.security.PrivateKey;
-import java.security.PublicKey;
-import java.util.regex.Pattern;
-
+import junit.framework.TestCase;
+import org.apache.cloudstack.saml.SAML2AuthManager;
+import org.apache.cloudstack.saml.SAMLProviderMetadata;
import org.apache.cloudstack.saml.SAMLUtils;
import org.apache.cloudstack.utils.security.CertUtils;
import org.junit.Test;
import org.opensaml.saml2.core.AuthnRequest;
import org.opensaml.saml2.core.LogoutRequest;
-import junit.framework.TestCase;
+import java.net.URI;
+import java.security.KeyPair;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.util.regex.Pattern;
+
+import static org.assertj.core.api.Assertions.assertThat;
public class SAMLUtilsTest extends TestCase {
@@ -60,6 +64,63 @@ public class SAMLUtilsTest extends TestCase {
assertEquals(req.getIssuer().getValue(), spId);
}
+ @Test
+ public void testBuildAuthnRequestUrlWithoutQueryParam() throws Exception {
+ String urlScheme = "http";
+
+ String spDomain = "sp.domain.example";
+ String spUrl = urlScheme + "://" + spDomain;
+ String spId = "serviceProviderId";
+
+ String idpDomain = "idp.domain.example";
+ String idpUrl = urlScheme + "://" + idpDomain;
+ String idpId = "identityProviderId";
+
+ String authnId = SAMLUtils.generateSecureRandomId();
+
+ SAMLProviderMetadata spMetadata = new SAMLProviderMetadata();
+ spMetadata.setEntityId(spId);
+ spMetadata.setSsoUrl(spUrl);
+
+ SAMLProviderMetadata idpMetadata = new SAMLProviderMetadata();
+ idpMetadata.setSsoUrl(idpUrl);
+ idpMetadata.setEntityId(idpId);
+
+ URI redirectUrl = new URI(SAMLUtils.buildAuthnRequestUrl(authnId, spMetadata, idpMetadata, SAML2AuthManager.SAMLSignatureAlgorithm.value()));
+ assertThat(redirectUrl).hasScheme(urlScheme).hasHost(idpDomain).hasParameter("SAMLRequest");
+ assertEquals(urlScheme, redirectUrl.getScheme());
+ assertEquals(idpDomain, redirectUrl.getHost());
+ }
+
+ @Test
+ public void testBuildAuthnRequestUrlWithQueryParam() throws Exception {
+ String urlScheme = "http";
+
+ String spDomain = "sp.domain.example";
+ String spUrl = urlScheme + "://" + spDomain;
+ String spId = "cloudstack";
+
+ String idpDomain = "idp.domain.example";
+ String idpQueryParam = "idpid=CX1298373";
+ String idpUrl = urlScheme + "://" + idpDomain + "?" + idpQueryParam;
+ String idpId = "identityProviderId";
+
+ String authnId = SAMLUtils.generateSecureRandomId();
+
+ SAMLProviderMetadata spMetadata = new SAMLProviderMetadata();
+ spMetadata.setEntityId(spId);
+ spMetadata.setSsoUrl(spUrl);
+
+ SAMLProviderMetadata idpMetadata = new SAMLProviderMetadata();
+ idpMetadata.setSsoUrl(idpUrl);
+ idpMetadata.setEntityId(idpId);
+
+ URI redirectUrl = new URI(SAMLUtils.buildAuthnRequestUrl(authnId, spMetadata, idpMetadata, SAML2AuthManager.SAMLSignatureAlgorithm.value()));
+ assertThat(redirectUrl).hasScheme(urlScheme).hasHost(idpDomain).hasParameter("idpid").hasParameter("SAMLRequest");
+ assertEquals(urlScheme, redirectUrl.getScheme());
+ assertEquals(idpDomain, redirectUrl.getHost());
+ }
+
@Test
public void testBuildLogoutRequest() throws Exception {
String logoutUrl = "http://logoutUrl";
diff --git a/pom.xml b/pom.xml
index f2dfbca68dc..6fafb5fd017 100644
--- a/pom.xml
+++ b/pom.xml
@@ -118,6 +118,7 @@
7.1.0
2.27.2
2.12.2
+ 3.23.1
5.10.0
diff --git a/scripts/util/keystore-setup b/scripts/util/keystore-setup
index 65f04c48d57..8ca6cc77baa 100755
--- a/scripts/util/keystore-setup
+++ b/scripts/util/keystore-setup
@@ -44,7 +44,7 @@ keytool -genkey -storepass "$KS_PASS" -keypass "$KS_PASS" -alias "$ALIAS" -keyal
# Generate CSR
rm -f "$CSR_FILE"
-addresses=$(ip address | grep inet | awk '{print $2}' | sed 's/\/.*//g' | grep -v '^169.254.' | grep -v '^127.0.0.1' | grep -v '^::1' | sed 's/^/ip:/g' | tr '\r\n' ',')
+addresses=$(ip address | grep inet | awk '{print $2}' | sed 's/\/.*//g' | grep -v '^169.254.' | grep -v '^127.0.0.1' | egrep -v '^::1|^fe80' | grep -v '^::1' | sed 's/^/ip:/g' | tr '\r\n' ',')
keytool -certreq -storepass "$KS_PASS" -alias "$ALIAS" -file $CSR_FILE -keystore "$KS_FILE" -ext san="$addresses" > /dev/null 2>&1
cat "$CSR_FILE"
diff --git a/test/integration/smoke/test_events_resource.py b/test/integration/smoke/test_events_resource.py
index 352771443b2..4d6872af57e 100644
--- a/test/integration/smoke/test_events_resource.py
+++ b/test/integration/smoke/test_events_resource.py
@@ -194,4 +194,4 @@ class TestEventsResource(cloudstackTestCase):
@classmethod
def tearDownClass(cls):
- super(TestEventsResource, cls).tearDownClass()
\ No newline at end of file
+ super(TestEventsResource, cls).tearDownClass()
diff --git a/ui/src/components/view/SearchView.vue b/ui/src/components/view/SearchView.vue
index 48345218693..2c350de816d 100644
--- a/ui/src/components/view/SearchView.vue
+++ b/ui/src/components/view/SearchView.vue
@@ -54,8 +54,7 @@
:ref="field.name"
:name="field.name"
:key="index"
- :label="field.name==='keyword' ?
- ('listAnnotations' in $store.getters.apis ? $t('label.annotation') : $t('label.name')) : $t('label.' + field.name)">
+ :label="retrieveFieldLabel(field.name)">
item.name === 'groupid')
+ this.fields[groupIndex].loading = true
+ promises.push(await this.fetchInstanceGroups())
+ }
+
if (arrayField.includes('entitytype')) {
const entityTypeIndex = this.fields.findIndex(item => item.name === 'entitytype')
this.fields[entityTypeIndex].loading = true
@@ -378,6 +400,12 @@ export default {
this.fields[clusterIndex].opts = this.sortArray(cluster[0].data)
}
}
+ if (groupIndex > -1) {
+ const groups = response.filter(item => item.type === 'groupid')
+ if (groups && groups.length > 0) {
+ this.fields[groupIndex].opts = this.sortArray(groups[0].data)
+ }
+ }
}).finally(() => {
if (zoneIndex > -1) {
this.fields[zoneIndex].loading = false
@@ -391,6 +419,9 @@ export default {
if (clusterIndex > -1) {
this.fields[clusterIndex].loading = false
}
+ if (groupIndex > -1) {
+ this.fields[groupIndex].loading = false
+ }
this.fillFormFieldValues()
})
},
@@ -468,6 +499,19 @@ export default {
})
})
},
+ fetchInstanceGroups () {
+ return new Promise((resolve, reject) => {
+ api('listInstanceGroups', { listAll: true }).then(json => {
+ const instancegroups = json.listinstancegroupsresponse.instancegroup
+ resolve({
+ type: 'groupid',
+ data: instancegroups
+ })
+ }).catch(error => {
+ reject(error.response.headers['x-description'])
+ })
+ })
+ },
fetchGuestNetworkTypes () {
const types = []
if (this.apiName.indexOf('listNetworks') > -1) {
diff --git a/ui/src/config/section/compute.js b/ui/src/config/section/compute.js
index 447bcf3243b..5249185651d 100644
--- a/ui/src/config/section/compute.js
+++ b/ui/src/config/section/compute.js
@@ -75,7 +75,7 @@ export default {
}
return fields
},
- searchFilters: ['name', 'zoneid', 'domainid', 'account', 'tags'],
+ searchFilters: ['name', 'zoneid', 'domainid', 'account', 'groupid', 'tags'],
details: () => {
var fields = ['displayname', 'name', 'id', 'state', 'ipaddress', 'ip6address', 'templatename', 'ostypename', 'serviceofferingname', 'isdynamicallyscalable', 'haenable', 'hypervisor', 'boottype', 'bootmode', 'account', 'domain', 'zonename']
const listZoneHaveSGEnabled = store.getters.zones.filter(zone => zone.securitygroupsenabled === true)