Skip to content

Commit e57a0f9

Browse files
Merge remote-tracking branch 'origin/4.17'
2 parents aa7dd30 + c6b6114 commit e57a0f9

File tree

8 files changed

+126
-13
lines changed

8 files changed

+126
-13
lines changed

plugins/user-authenticators/saml2/pom.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,5 +53,11 @@
5353
<version>${cs.xercesImpl.version}</version>
5454
<scope>test</scope>
5555
</dependency>
56+
<dependency>
57+
<groupId>org.assertj</groupId>
58+
<artifactId>assertj-core</artifactId>
59+
<version>${cs.assertj.version}</version>
60+
<scope>test</scope>
61+
</dependency>
5662
</dependencies>
5763
</project>

plugins/user-authenticators/saml2/src/main/java/org/apache/cloudstack/saml/SAMLUtils.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,8 @@ public static String buildAuthnRequestUrl(final String authnId, final SAMLProvid
150150
if (spMetadata.getKeyPair() != null) {
151151
privateKey = spMetadata.getKeyPair().getPrivate();
152152
}
153-
redirectUrl = idpMetadata.getSsoUrl() + "?" + SAMLUtils.generateSAMLRequestSignature("SAMLRequest=" + SAMLUtils.encodeSAMLRequest(authnRequest), privateKey, signatureAlgorithm);
153+
String appendOperator = idpMetadata.getSsoUrl().contains("?") ? "&" : "?";
154+
redirectUrl = idpMetadata.getSsoUrl() + appendOperator + SAMLUtils.generateSAMLRequestSignature("SAMLRequest=" + SAMLUtils.encodeSAMLRequest(authnRequest), privateKey, signatureAlgorithm);
154155
} catch (ConfigurationException | FactoryConfigurationError | MarshallingException | IOException | NoSuchAlgorithmException | InvalidKeyException | java.security.SignatureException e) {
155156
s_logger.error("SAML AuthnRequest message building error: " + e.getMessage());
156157
}

plugins/user-authenticators/saml2/src/test/java/org/apache/cloudstack/SAMLUtilsTest.java

Lines changed: 67 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,18 +19,22 @@
1919

2020
package org.apache.cloudstack;
2121

22-
import java.security.KeyPair;
23-
import java.security.PrivateKey;
24-
import java.security.PublicKey;
25-
import java.util.regex.Pattern;
26-
22+
import junit.framework.TestCase;
23+
import org.apache.cloudstack.saml.SAML2AuthManager;
24+
import org.apache.cloudstack.saml.SAMLProviderMetadata;
2725
import org.apache.cloudstack.saml.SAMLUtils;
2826
import org.apache.cloudstack.utils.security.CertUtils;
2927
import org.junit.Test;
3028
import org.opensaml.saml2.core.AuthnRequest;
3129
import org.opensaml.saml2.core.LogoutRequest;
3230

33-
import junit.framework.TestCase;
31+
import java.net.URI;
32+
import java.security.KeyPair;
33+
import java.security.PrivateKey;
34+
import java.security.PublicKey;
35+
import java.util.regex.Pattern;
36+
37+
import static org.assertj.core.api.Assertions.assertThat;
3438

3539
public class SAMLUtilsTest extends TestCase {
3640

@@ -60,6 +64,63 @@ public void testBuildAuthnRequestObject() throws Exception {
6064
assertEquals(req.getIssuer().getValue(), spId);
6165
}
6266

67+
@Test
68+
public void testBuildAuthnRequestUrlWithoutQueryParam() throws Exception {
69+
String urlScheme = "http";
70+
71+
String spDomain = "sp.domain.example";
72+
String spUrl = urlScheme + "://" + spDomain;
73+
String spId = "serviceProviderId";
74+
75+
String idpDomain = "idp.domain.example";
76+
String idpUrl = urlScheme + "://" + idpDomain;
77+
String idpId = "identityProviderId";
78+
79+
String authnId = SAMLUtils.generateSecureRandomId();
80+
81+
SAMLProviderMetadata spMetadata = new SAMLProviderMetadata();
82+
spMetadata.setEntityId(spId);
83+
spMetadata.setSsoUrl(spUrl);
84+
85+
SAMLProviderMetadata idpMetadata = new SAMLProviderMetadata();
86+
idpMetadata.setSsoUrl(idpUrl);
87+
idpMetadata.setEntityId(idpId);
88+
89+
URI redirectUrl = new URI(SAMLUtils.buildAuthnRequestUrl(authnId, spMetadata, idpMetadata, SAML2AuthManager.SAMLSignatureAlgorithm.value()));
90+
assertThat(redirectUrl).hasScheme(urlScheme).hasHost(idpDomain).hasParameter("SAMLRequest");
91+
assertEquals(urlScheme, redirectUrl.getScheme());
92+
assertEquals(idpDomain, redirectUrl.getHost());
93+
}
94+
95+
@Test
96+
public void testBuildAuthnRequestUrlWithQueryParam() throws Exception {
97+
String urlScheme = "http";
98+
99+
String spDomain = "sp.domain.example";
100+
String spUrl = urlScheme + "://" + spDomain;
101+
String spId = "cloudstack";
102+
103+
String idpDomain = "idp.domain.example";
104+
String idpQueryParam = "idpid=CX1298373";
105+
String idpUrl = urlScheme + "://" + idpDomain + "?" + idpQueryParam;
106+
String idpId = "identityProviderId";
107+
108+
String authnId = SAMLUtils.generateSecureRandomId();
109+
110+
SAMLProviderMetadata spMetadata = new SAMLProviderMetadata();
111+
spMetadata.setEntityId(spId);
112+
spMetadata.setSsoUrl(spUrl);
113+
114+
SAMLProviderMetadata idpMetadata = new SAMLProviderMetadata();
115+
idpMetadata.setSsoUrl(idpUrl);
116+
idpMetadata.setEntityId(idpId);
117+
118+
URI redirectUrl = new URI(SAMLUtils.buildAuthnRequestUrl(authnId, spMetadata, idpMetadata, SAML2AuthManager.SAMLSignatureAlgorithm.value()));
119+
assertThat(redirectUrl).hasScheme(urlScheme).hasHost(idpDomain).hasParameter("idpid").hasParameter("SAMLRequest");
120+
assertEquals(urlScheme, redirectUrl.getScheme());
121+
assertEquals(idpDomain, redirectUrl.getHost());
122+
}
123+
63124
@Test
64125
public void testBuildLogoutRequest() throws Exception {
65126
String logoutUrl = "http://logoutUrl";

pom.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,7 @@
118118
<cs.testng.version>7.1.0</cs.testng.version>
119119
<cs.wiremock.version>2.27.2</cs.wiremock.version>
120120
<cs.xercesImpl.version>2.12.2</cs.xercesImpl.version>
121+
<cs.assertj.version>3.23.1</cs.assertj.version>
121122

122123
<!-- Dependencies versions -->
123124
<cs.amqp-client.version>5.10.0</cs.amqp-client.version>

scripts/util/keystore-setup

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ keytool -genkey -storepass "$KS_PASS" -keypass "$KS_PASS" -alias "$ALIAS" -keyal
4444

4545
# Generate CSR
4646
rm -f "$CSR_FILE"
47-
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' ',')
47+
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' ',')
4848
keytool -certreq -storepass "$KS_PASS" -alias "$ALIAS" -file $CSR_FILE -keystore "$KS_FILE" -ext san="$addresses" > /dev/null 2>&1
4949
cat "$CSR_FILE"
5050

test/integration/smoke/test_events_resource.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -194,4 +194,4 @@ def tearDown(self):
194194

195195
@classmethod
196196
def tearDownClass(cls):
197-
super(TestEventsResource, cls).tearDownClass()
197+
super(TestEventsResource, cls).tearDownClass()

ui/src/components/view/SearchView.vue

Lines changed: 47 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,7 @@
5454
:ref="field.name"
5555
:name="field.name"
5656
:key="index"
57-
:label="field.name==='keyword' ?
58-
('listAnnotations' in $store.getters.apis ? $t('label.annotation') : $t('label.name')) : $t('label.' + field.name)">
57+
:label="retrieveFieldLabel(field.name)">
5958
<a-select
6059
allowClear
6160
v-if="field.type==='list'"
@@ -242,6 +241,19 @@ export default {
242241
if (!this.visibleFilter) return
243242
this.initFormFieldData()
244243
},
244+
retrieveFieldLabel (fieldName) {
245+
if (fieldName === 'groupid') {
246+
fieldName = 'group'
247+
}
248+
if (fieldName === 'keyword') {
249+
if ('listAnnotations' in this.$store.getters.apis) {
250+
return this.$t('label.annotation')
251+
} else {
252+
return this.$t('label.name')
253+
}
254+
}
255+
return this.$t('label.' + fieldName)
256+
},
245257
async initFormFieldData () {
246258
const arrayField = []
247259
this.fields = []
@@ -260,7 +272,10 @@ export default {
260272
if (item === 'clusterid' && !('listClusters' in this.$store.getters.apis)) {
261273
return true
262274
}
263-
if (['zoneid', 'domainid', 'state', 'level', 'clusterid', 'podid', 'entitytype', 'type'].includes(item)) {
275+
if (item === 'groupid' && !('listInstanceGroups' in this.$store.getters.apis)) {
276+
return true
277+
}
278+
if (['zoneid', 'domainid', 'state', 'level', 'clusterid', 'podid', 'groupid', 'entitytype', 'type'].includes(item)) {
264279
type = 'list'
265280
} else if (item === 'tags') {
266281
type = 'tag'
@@ -282,6 +297,7 @@ export default {
282297
let domainIndex = -1
283298
let podIndex = -1
284299
let clusterIndex = -1
300+
let groupIndex = -1
285301
286302
if (arrayField.includes('type')) {
287303
if (this.$route.path === '/guestnetwork' || this.$route.path.includes('/guestnetwork/')) {
@@ -330,6 +346,12 @@ export default {
330346
promises.push(await this.fetchClusters())
331347
}
332348
349+
if (arrayField.includes('groupid')) {
350+
groupIndex = this.fields.findIndex(item => item.name === 'groupid')
351+
this.fields[groupIndex].loading = true
352+
promises.push(await this.fetchInstanceGroups())
353+
}
354+
333355
if (arrayField.includes('entitytype')) {
334356
const entityTypeIndex = this.fields.findIndex(item => item.name === 'entitytype')
335357
this.fields[entityTypeIndex].loading = true
@@ -378,6 +400,12 @@ export default {
378400
this.fields[clusterIndex].opts = this.sortArray(cluster[0].data)
379401
}
380402
}
403+
if (groupIndex > -1) {
404+
const groups = response.filter(item => item.type === 'groupid')
405+
if (groups && groups.length > 0) {
406+
this.fields[groupIndex].opts = this.sortArray(groups[0].data)
407+
}
408+
}
381409
}).finally(() => {
382410
if (zoneIndex > -1) {
383411
this.fields[zoneIndex].loading = false
@@ -391,6 +419,9 @@ export default {
391419
if (clusterIndex > -1) {
392420
this.fields[clusterIndex].loading = false
393421
}
422+
if (groupIndex > -1) {
423+
this.fields[groupIndex].loading = false
424+
}
394425
this.fillFormFieldValues()
395426
})
396427
},
@@ -468,6 +499,19 @@ export default {
468499
})
469500
})
470501
},
502+
fetchInstanceGroups () {
503+
return new Promise((resolve, reject) => {
504+
api('listInstanceGroups', { listAll: true }).then(json => {
505+
const instancegroups = json.listinstancegroupsresponse.instancegroup
506+
resolve({
507+
type: 'groupid',
508+
data: instancegroups
509+
})
510+
}).catch(error => {
511+
reject(error.response.headers['x-description'])
512+
})
513+
})
514+
},
471515
fetchGuestNetworkTypes () {
472516
const types = []
473517
if (this.apiName.indexOf('listNetworks') > -1) {

ui/src/config/section/compute.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ export default {
7575
}
7676
return fields
7777
},
78-
searchFilters: ['name', 'zoneid', 'domainid', 'account', 'tags'],
78+
searchFilters: ['name', 'zoneid', 'domainid', 'account', 'groupid', 'tags'],
7979
details: () => {
8080
var fields = ['displayname', 'name', 'id', 'state', 'ipaddress', 'ip6address', 'templatename', 'ostypename', 'serviceofferingname', 'isdynamicallyscalable', 'haenable', 'hypervisor', 'boottype', 'bootmode', 'account', 'domain', 'zonename']
8181
const listZoneHaveSGEnabled = store.getters.zones.filter(zone => zone.securitygroupsenabled === true)

0 commit comments

Comments
 (0)