Skip to content

Commit de4e74b

Browse files
committed
saml: Add unit tests for saml plugin
- Fixes signatures on plugin manager for ease of testing - Fixes authenticator - Adds unit testing for getType and authenticate methods for all cmd classes - Adds SAMLAuthenticator test Signed-off-by: Rohit Yadav <[email protected]>
1 parent 1ed532f commit de4e74b

7 files changed

Lines changed: 419 additions & 14 deletions

File tree

plugins/user-authenticators/saml2/src/org/apache/cloudstack/api/command/SAML2LoginAPIAuthenticatorCmd.java

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ public class SAML2LoginAPIAuthenticatorCmd extends BaseCmd implements APIAuthent
8989
@Inject
9090
ConfigurationDao _configDao;
9191
@Inject
92-
private DomainManager _domainMgr;
92+
DomainManager _domainMgr;
9393

9494
SAML2AuthManager _samlAuthManager;
9595

@@ -141,7 +141,7 @@ private String buildAuthnRequestUrl(String idpUrl) {
141141
return redirectUrl;
142142
}
143143

144-
private Response processSAMLResponse(String responseMessage) {
144+
public Response processSAMLResponse(String responseMessage) {
145145
Response responseObject = null;
146146
try {
147147
DefaultBootstrap.bootstrap();
@@ -162,12 +162,12 @@ public String authenticate(final String command, final Map<String, Object[]> par
162162
if (idps != null && idps.length > 0) {
163163
idpUrl = idps[0];
164164
}
165-
String redirectUrl = buildAuthnRequestUrl(idpUrl);
165+
String redirectUrl = this.buildAuthnRequestUrl(idpUrl);
166166
resp.sendRedirect(redirectUrl);
167167
return "";
168168
} else {
169169
final String samlResponse = ((String[])params.get(SAMLUtils.SAML_RESPONSE))[0];
170-
Response processedSAMLResponse = processSAMLResponse(samlResponse);
170+
Response processedSAMLResponse = this.processSAMLResponse(samlResponse);
171171
String statusCode = processedSAMLResponse.getStatus().getStatusCode().getValue();
172172
if (!statusCode.equals(StatusCode.SUCCESS_URI)) {
173173
throw new ServerApiException(ApiErrorCode.ACCOUNT_ERROR, _apiServer.getSerializedApiError(ApiErrorCode.ACCOUNT_ERROR.getHttpCode(),
@@ -209,7 +209,7 @@ public String authenticate(final String command, final Map<String, Object[]> par
209209
}
210210

211211
String username = null;
212-
String password = "";
212+
String password = SAMLUtils.generateSecureRandomId(); // Random password
213213
String firstName = "";
214214
String lastName = "";
215215
String timeZone = "";
@@ -229,8 +229,6 @@ public String authenticate(final String command, final Map<String, Object[]> par
229229
}
230230
}
231231

232-
String issuer = assertion.getIssuer().getValue();
233-
String audience = assertion.getConditions().getAudienceRestrictions().get(0).getAudiences().get(0).getAudienceURI();
234232
AttributeStatement attributeStatement = assertion.getAttributeStatements().get(0);
235233
List<Attribute> attributes = attributeStatement.getAttributes();
236234

plugins/user-authenticators/saml2/src/org/apache/cloudstack/api/command/SAML2LogoutAPIAuthenticatorCmd.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ public String authenticate(String command, Map<String, Object[]> params, HttpSes
9999
params, responseType));
100100
}
101101

102-
if (params.containsKey("SAMLResponse")) {
102+
if (params != null && params.containsKey("SAMLResponse")) {
103103
try {
104104
final String samlResponse = ((String[])params.get(SAMLUtils.SAML_RESPONSE))[0];
105105
Response processedSAMLResponse = SAMLUtils.decodeSAMLResponse(samlResponse);

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

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ public Pair<Boolean, ActionOnFailedAuthentication> authenticate(String username,
4949
} else {
5050
User user = _userDao.getUser(userAccount.getId());
5151
if (user != null && SAMLUtils.checkSAMLUserId(user.getUuid()) &&
52-
requestParameters.containsKey(SAMLUtils.SAML_RESPONSE)) {
52+
requestParameters != null && requestParameters.containsKey(SAMLUtils.SAML_RESPONSE)) {
5353
return new Pair<Boolean, ActionOnFailedAuthentication>(true, null);
5454
}
5555
}
@@ -59,8 +59,6 @@ public Pair<Boolean, ActionOnFailedAuthentication> authenticate(String username,
5959

6060
@Override
6161
public String encode(final String password) {
62-
// TODO: Complete method
63-
StringBuilder sb = new StringBuilder(32);
64-
return sb.toString();
62+
return SAMLUtils.generateSecureRandomId();
6563
}
6664
}

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

Lines changed: 49 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,21 +19,68 @@
1919

2020
package org.apache.cloudstack;
2121

22+
import com.cloud.server.auth.UserAuthenticator.ActionOnFailedAuthentication;
23+
import com.cloud.user.UserAccountVO;
24+
import com.cloud.user.UserVO;
25+
import com.cloud.user.dao.UserAccountDao;
26+
import com.cloud.user.dao.UserDao;
27+
import com.cloud.utils.Pair;
28+
import org.apache.cloudstack.saml.SAML2UserAuthenticator;
29+
import org.apache.cloudstack.utils.auth.SAMLUtils;
30+
import org.junit.Assert;
2231
import org.junit.Test;
2332
import org.junit.runner.RunWith;
24-
33+
import org.mockito.Mock;
34+
import org.mockito.Mockito;
2535
import org.mockito.runners.MockitoJUnitRunner;
2636

37+
import java.lang.reflect.Field;
38+
import java.util.HashMap;
39+
import java.util.Map;
40+
2741
@RunWith(MockitoJUnitRunner.class)
2842
public class SAML2UserAuthenticatorTest {
2943

44+
@Mock
45+
UserAccountDao userAccountDao;
46+
@Mock
47+
UserDao userDao;
48+
3049
@Test
3150
public void encode() {
32-
51+
Assert.assertTrue(new SAML2UserAuthenticator().encode("random String").length() == 32);
3352
}
3453

3554
@Test
3655
public void authenticate() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
56+
SAML2UserAuthenticator authenticator = new SAML2UserAuthenticator();
57+
58+
Field daoField = SAML2UserAuthenticator.class.getDeclaredField("_userAccountDao");
59+
daoField.setAccessible(true);
60+
daoField.set(authenticator, userAccountDao);
61+
62+
Field userDaoField = SAML2UserAuthenticator.class.getDeclaredField("_userDao");
63+
userDaoField.setAccessible(true);
64+
userDaoField.set(authenticator, userDao);
65+
66+
UserAccountVO account = new UserAccountVO();
67+
account.setPassword("5f4dcc3b5aa765d61d8327deb882cf99");
68+
account.setId(1L);
69+
70+
UserVO user = new UserVO();
71+
user.setUuid(SAMLUtils.createSAMLId("someUID"));
72+
73+
Mockito.when(userAccountDao.getUserAccount(Mockito.anyString(), Mockito.anyLong())).thenReturn(account);
74+
Mockito.when(userDao.getUser(Mockito.anyLong())).thenReturn(user);
75+
76+
// When there is no SAMLRequest in params
77+
Pair<Boolean, ActionOnFailedAuthentication> pair1 = authenticator.authenticate(SAMLUtils.createSAMLId("user1234"), "random", 1l, null);
78+
Assert.assertFalse(pair1.first());
3779

80+
// When there is SAMLRequest in params
81+
Map<String, Object[]> params = new HashMap<String, Object[]>();
82+
params.put(SAMLUtils.SAML_RESPONSE, new Object[]{});
83+
Pair<Boolean, ActionOnFailedAuthentication> pair2 = authenticator.authenticate(SAMLUtils.createSAMLId("user1234"), "random", 1l, params);
84+
Assert.assertTrue(pair2.first());
3885
}
3986
}
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
package org.apache.cloudstack.api.command;
21+
22+
import com.cloud.utils.HttpUtils;
23+
import org.apache.cloudstack.api.ApiServerService;
24+
import org.apache.cloudstack.api.auth.APIAuthenticationType;
25+
import org.apache.cloudstack.saml.SAML2AuthManager;
26+
import org.apache.cloudstack.utils.auth.SAMLUtils;
27+
import org.junit.Assert;
28+
import org.junit.Test;
29+
import org.junit.runner.RunWith;
30+
import org.mockito.Mock;
31+
import org.mockito.Mockito;
32+
import org.mockito.runners.MockitoJUnitRunner;
33+
34+
import javax.servlet.http.HttpServletResponse;
35+
import javax.servlet.http.HttpSession;
36+
import java.lang.reflect.Field;
37+
import java.security.InvalidKeyException;
38+
import java.security.NoSuchAlgorithmException;
39+
import java.security.NoSuchProviderException;
40+
import java.security.SignatureException;
41+
import java.security.cert.CertificateEncodingException;
42+
import java.security.cert.CertificateParsingException;
43+
import java.security.cert.X509Certificate;
44+
45+
@RunWith(MockitoJUnitRunner.class)
46+
public class GetServiceProviderMetaDataCmdTest {
47+
48+
@Mock
49+
ApiServerService apiServer;
50+
51+
@Mock
52+
SAML2AuthManager samlAuthManager;
53+
54+
@Mock
55+
HttpSession session;
56+
57+
@Mock
58+
HttpServletResponse resp;
59+
60+
@Test
61+
public void testAuthenticate() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException, CertificateParsingException, CertificateEncodingException, NoSuchAlgorithmException, InvalidKeyException, NoSuchProviderException, SignatureException {
62+
GetServiceProviderMetaDataCmd cmd = new GetServiceProviderMetaDataCmd();
63+
64+
Field apiServerField = GetServiceProviderMetaDataCmd.class.getDeclaredField("_apiServer");
65+
apiServerField.setAccessible(true);
66+
apiServerField.set(cmd, apiServer);
67+
68+
Field managerField = GetServiceProviderMetaDataCmd.class.getDeclaredField("_samlAuthManager");
69+
managerField.setAccessible(true);
70+
managerField.set(cmd, samlAuthManager);
71+
72+
String spId = "someSPID";
73+
String url = "someUrl";
74+
X509Certificate cert = SAMLUtils.generateRandomX509Certification();
75+
Mockito.when(samlAuthManager.getServiceProviderId()).thenReturn(spId);
76+
Mockito.when(samlAuthManager.getIdpSigningKey()).thenReturn(cert);
77+
Mockito.when(samlAuthManager.getIdpSingleLogOutUrl()).thenReturn(url);
78+
Mockito.when(samlAuthManager.getSpSingleLogOutUrl()).thenReturn(url);
79+
80+
String result = cmd.authenticate("command", null, session, "random", HttpUtils.RESPONSE_TYPE_JSON, new StringBuilder(), resp);
81+
Assert.assertTrue(result.contains("md:EntityDescriptor"));
82+
83+
Mockito.verify(samlAuthManager, Mockito.atLeast(1)).getServiceProviderId();
84+
Mockito.verify(samlAuthManager, Mockito.atLeast(1)).getSpSingleSignOnUrl();
85+
Mockito.verify(samlAuthManager, Mockito.atLeast(1)).getSpSingleLogOutUrl();
86+
Mockito.verify(samlAuthManager, Mockito.never()).getIdpSingleSignOnUrl();
87+
Mockito.verify(samlAuthManager, Mockito.never()).getIdpSingleLogOutUrl();
88+
}
89+
90+
@Test
91+
public void testGetAPIType() {
92+
Assert.assertTrue(new GetServiceProviderMetaDataCmd().getAPIType() == APIAuthenticationType.LOGIN_API);
93+
}
94+
}

0 commit comments

Comments
 (0)