package org.cloudfoundry.identity.uaa.provider;
import org.cloudfoundry.identity.uaa.constants.OriginKeys;
import org.cloudfoundry.identity.uaa.provider.saml.SamlIdentityProviderConfigurator;
import org.cloudfoundry.identity.uaa.scim.ScimGroupExternalMembershipManager;
import org.cloudfoundry.identity.uaa.scim.ScimGroupProvisioning;
import org.cloudfoundry.identity.uaa.zone.IdentityZone;
import org.junit.Before;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.Matchers;
import org.mockito.Mockito;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.Supplier;
import static org.cloudfoundry.identity.uaa.constants.OriginKeys.LDAP;
import static org.cloudfoundry.identity.uaa.constants.OriginKeys.OAUTH20;
import static org.cloudfoundry.identity.uaa.constants.OriginKeys.OIDC10;
import static org.cloudfoundry.identity.uaa.constants.OriginKeys.UNKNOWN;
import static org.cloudfoundry.identity.uaa.provider.ExternalIdentityProviderDefinition.USER_NAME_ATTRIBUTE_NAME;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.Matchers.anyBoolean;
import static org.mockito.Matchers.anyObject;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
public class IdentityProviderEndpointsTest {
private IdentityProviderEndpoints identityProviderEndpoints;
private IdentityProviderProvisioning identityProviderProvisioning;
private ScimGroupExternalMembershipManager scimGroupExternalMembershipManager;
private ScimGroupProvisioning scimGroupProvisioning;
private SamlIdentityProviderConfigurator samlConfigurator;
private IdentityProviderConfigValidationDelegator configValidator;
@Before
public void setup() {
configValidator = mock(IdentityProviderConfigValidationDelegator.class);
identityProviderProvisioning = mock(IdentityProviderProvisioning.class);
identityProviderEndpoints = new IdentityProviderEndpoints(identityProviderProvisioning, scimGroupExternalMembershipManager, scimGroupProvisioning, samlConfigurator, configValidator);
}
public IdentityProvider<AbstractXOAuthIdentityProviderDefinition> getXOAuthProvider() {
IdentityProvider identityProvider = new IdentityProvider<>();
identityProvider.setName("my oidc provider");
identityProvider.setIdentityZoneId(OriginKeys.UAA);
OIDCIdentityProviderDefinition config = new OIDCIdentityProviderDefinition();
config.addAttributeMapping(USER_NAME_ATTRIBUTE_NAME, "user_name");
config.addAttributeMapping("user.attribute." + "the_client_id", "cid");
config.setStoreCustomAttributes(true);
String urlBase = "http://localhost:8080/";
try {
config.setAuthUrl(new URL(urlBase + "/oauth/authorize"));
config.setTokenUrl(new URL(urlBase + "/oauth/token"));
config.setTokenKeyUrl(new URL(urlBase + "/token_key"));
config.setIssuer(urlBase + "/oauth/token");
config.setUserInfoUrl(new URL(urlBase+"/userinfo"));
} catch (MalformedURLException e) {
throw new RuntimeException(e);
}
config.setShowLinkText(true);
config.setLinkText("My OIDC Provider");
config.setSkipSslValidation(true);
config.setRelyingPartyId("identity");
config.setRelyingPartySecret("identitysecret");
List<String> requestedScopes = new ArrayList<>();
requestedScopes.add("openid");
requestedScopes.add("cloud_controller.read");
config.setScopes(requestedScopes);
identityProvider.setConfig(config);
identityProvider.setOriginKey("puppy");
identityProvider.setIdentityZoneId(IdentityZone.getUaa().getId());
return identityProvider;
}
public IdentityProvider<LdapIdentityProviderDefinition> getLdapDefinition() {
String ldapProfile = "ldap-search-and-bind.xml";
//String ldapProfile = "ldap-search-and-compare.xml";
String ldapGroup = "ldap-groups-null.xml";
LdapIdentityProviderDefinition definition = new LdapIdentityProviderDefinition();
definition.setLdapProfileFile("ldap/" + ldapProfile);
definition.setLdapGroupFile("ldap/" + ldapGroup);
definition.setMaxGroupSearchDepth(10);
definition.setBaseUrl("ldap://localhost");
definition.setBindUserDn("cn=admin,ou=Users,dc=test,dc=com");
definition.setBindPassword("adminsecret");
definition.setSkipSSLVerification(true);
definition.setTlsConfiguration("none");
definition.setMailAttributeName("mail");
definition.setReferral("ignore");
IdentityProvider<LdapIdentityProviderDefinition> ldapProvider = new IdentityProvider<>();
ldapProvider.setOriginKey(LDAP);
ldapProvider.setConfig(definition);
ldapProvider.setType(LDAP);
ldapProvider.setId("id");
return ldapProvider;
}
@Test
public void retrieve_oauth_provider_by_id_redacts_password() throws Exception {
retrieve_oauth_provider_by_id("", OriginKeys.OAUTH20);
retrieve_oauth_provider_by_id("", OriginKeys.OIDC10);
}
public IdentityProvider<LdapIdentityProviderDefinition> retrieve_oauth_provider_by_id(String id, String type) throws Exception {
IdentityProvider provider = getXOAuthProvider();
provider.setType(type);
when(identityProviderProvisioning.retrieve(anyString())).thenReturn(provider);
ResponseEntity<IdentityProvider> oauth = identityProviderEndpoints.retrieveIdentityProvider(id, true);
assertNotNull(oauth);
assertEquals(200,oauth.getStatusCode().value());
assertNotNull(oauth.getBody());
assertNotNull(oauth.getBody().getConfig());
assertTrue(oauth.getBody().getConfig() instanceof AbstractXOAuthIdentityProviderDefinition);
assertNull(((AbstractXOAuthIdentityProviderDefinition)oauth.getBody().getConfig()).getRelyingPartySecret());
return oauth.getBody();
}
@Test
public void retrieve_ldap_provider_by_id_redacts_password() throws Exception {
retrieve_ldap_provider_by_id("");
}
public IdentityProvider<LdapIdentityProviderDefinition> retrieve_ldap_provider_by_id(String id) throws Exception {
when(identityProviderProvisioning.retrieve(anyString())).thenReturn(getLdapDefinition());
ResponseEntity<IdentityProvider> ldap = identityProviderEndpoints.retrieveIdentityProvider(id, true);
assertNotNull(ldap);
assertEquals(200,ldap.getStatusCode().value());
assertNotNull(ldap.getBody());
assertNotNull(ldap.getBody().getConfig());
assertTrue(ldap.getBody().getConfig() instanceof LdapIdentityProviderDefinition);
assertNull(((LdapIdentityProviderDefinition)ldap.getBody().getConfig()).getBindPassword());
return ldap.getBody();
}
@Test
public void remove_bind_password() throws Exception {
remove_sensitive_data(() -> getLdapDefinition(),
LDAP,
(spy) -> verify((LdapIdentityProviderDefinition)spy, times(1)).setBindPassword(Matchers.isNull(String.class)));
}
@Test
public void remove_client_secret() throws Exception {
for (String type : Arrays.asList(OIDC10, OAUTH20)) {
remove_sensitive_data(() -> getXOAuthProvider(),
type,
(spy) -> verify((AbstractXOAuthIdentityProviderDefinition)spy, times(1)).setRelyingPartySecret(Matchers.isNull(String.class)));
}
}
public void remove_sensitive_data(Supplier<IdentityProvider> getProvider, String type, Consumer<AbstractIdentityProviderDefinition> validator) {
IdentityProvider provider = getProvider.get();
AbstractIdentityProviderDefinition spy = Mockito.spy(provider.getConfig());
provider.setConfig(spy);
provider.setType(type);
identityProviderEndpoints.redactSensitiveData(provider);
validator.accept(spy);
}
@Test
public void remove_client_secret_wrong_origin() throws Exception {
IdentityProvider provider = getXOAuthProvider();
AbstractXOAuthIdentityProviderDefinition spy = Mockito.spy((AbstractXOAuthIdentityProviderDefinition) provider.getConfig());
provider.setConfig(spy);
provider.setType(UNKNOWN);
identityProviderEndpoints.redactSensitiveData(provider);
verify(spy, never()).setRelyingPartySecret(Matchers.isNull(String.class));
}
@Test
public void remove_bind_password_non_ldap() throws Exception {
IdentityProvider provider = getLdapDefinition();
LdapIdentityProviderDefinition spy = Mockito.spy((LdapIdentityProviderDefinition)provider.getConfig());
provider.setConfig(spy);
provider.setType(OriginKeys.UNKNOWN);
identityProviderEndpoints.redactSensitiveData(provider);
verify(spy, never()).setBindPassword(Matchers.isNull(String.class));
}
@Test
public void patch_bind_password() throws Exception {
IdentityProvider provider = getLdapDefinition();
LdapIdentityProviderDefinition def = (LdapIdentityProviderDefinition) provider.getConfig();
def.setBindPassword(null);
LdapIdentityProviderDefinition spy = Mockito.spy(def);
provider.setConfig(spy);
reset(identityProviderProvisioning);
when(identityProviderProvisioning.retrieve(eq(provider.getId()))).thenReturn(getLdapDefinition());
identityProviderEndpoints.patchSensitiveData(provider.getId(), provider);
verify(spy, times(1)).setBindPassword(eq(getLdapDefinition().getConfig().getBindPassword()));
}
@Test
public void patch_client_secret() throws Exception {
for (String type : Arrays.asList(OIDC10, OAUTH20)) {
IdentityProvider<AbstractXOAuthIdentityProviderDefinition> provider = getXOAuthProvider();
AbstractXOAuthIdentityProviderDefinition def = provider.getConfig();
def.setRelyingPartySecret(null);
AbstractXOAuthIdentityProviderDefinition spy = Mockito.spy(def);
provider.setConfig(spy);
provider.setType(type);
reset(identityProviderProvisioning);
when(identityProviderProvisioning.retrieve(eq(provider.getId()))).thenReturn(getXOAuthProvider());
identityProviderEndpoints.patchSensitiveData(provider.getId(), provider);
verify(spy, times(1)).setRelyingPartySecret(eq(getXOAuthProvider().getConfig().getRelyingPartySecret()));
}
}
@Test
public void patch_bind_password_non_ldap() throws Exception {
IdentityProvider provider = getLdapDefinition();
LdapIdentityProviderDefinition spy = Mockito.spy((LdapIdentityProviderDefinition)provider.getConfig());
provider.setConfig(spy);
provider.setType(OriginKeys.UNKNOWN);
identityProviderEndpoints.redactSensitiveData(provider);
verify(spy, never()).setBindPassword(anyObject());
}
@Test
public void retrieve_all_providers_redacts_data() throws Exception {
when(identityProviderProvisioning.retrieveAll(anyBoolean(), anyString()))
.thenReturn(Arrays.asList(getLdapDefinition(), getXOAuthProvider()));
ResponseEntity<List<IdentityProvider>> ldapList = identityProviderEndpoints.retrieveIdentityProviders("false", true);
assertNotNull(ldapList);
assertNotNull(ldapList.getBody());
assertEquals(2, ldapList.getBody().size());
IdentityProvider<LdapIdentityProviderDefinition> ldap = ldapList.getBody().get(0);
assertNotNull(ldap);
assertNotNull(ldap.getConfig());
assertTrue(ldap.getConfig() instanceof LdapIdentityProviderDefinition);
assertNull(ldap.getConfig().getBindPassword());
IdentityProvider<AbstractXOAuthIdentityProviderDefinition> oauth = ldapList.getBody().get(1);
assertNotNull(oauth);
assertNotNull(oauth.getConfig());
assertTrue(oauth.getConfig() instanceof AbstractXOAuthIdentityProviderDefinition);
assertNull(oauth.getConfig().getRelyingPartySecret());
}
@Test
public void update_ldap_provider_patches_password() throws Exception {
IdentityProvider<LdapIdentityProviderDefinition> provider = retrieve_ldap_provider_by_id("id");
provider.getConfig().setBindPassword(null);
LdapIdentityProviderDefinition spy = Mockito.spy(provider.getConfig());
provider.setConfig(spy);
reset(identityProviderProvisioning);
when(identityProviderProvisioning.retrieve(eq(provider.getId()))).thenReturn(getLdapDefinition());
when(identityProviderProvisioning.update(anyObject())).thenReturn(getLdapDefinition());
ResponseEntity<IdentityProvider> response = identityProviderEndpoints.updateIdentityProvider(provider.getId(), provider, true);
verify(spy, times(1)).setBindPassword(eq(getLdapDefinition().getConfig().getBindPassword()));
ArgumentCaptor<IdentityProvider> captor = ArgumentCaptor.forClass(IdentityProvider.class);
verify(identityProviderProvisioning, times(1)).update(captor.capture());
assertNotNull(captor.getValue());
assertEquals(1, captor.getAllValues().size());
assertEquals(getLdapDefinition().getConfig().getBindPassword(), ((LdapIdentityProviderDefinition)captor.getValue().getConfig()).getBindPassword());
assertNotNull(response);
assertEquals(200, response.getStatusCode().value());
assertNotNull(response.getBody());
assertNotNull(response.getBody().getConfig());
assertTrue(response.getBody().getConfig() instanceof LdapIdentityProviderDefinition);
assertNull(((LdapIdentityProviderDefinition)response.getBody().getConfig()).getBindPassword());
}
@Test
public void update_ldap_provider_takes_new_password() throws Exception {
IdentityProvider<LdapIdentityProviderDefinition> provider = retrieve_ldap_provider_by_id("id");
LdapIdentityProviderDefinition spy = Mockito.spy(provider.getConfig());
provider.setConfig(spy);
spy.setBindPassword("newpassword");
reset(identityProviderProvisioning);
when(identityProviderProvisioning.retrieve(eq(provider.getId()))).thenReturn(getLdapDefinition());
when(identityProviderProvisioning.update(anyObject())).thenReturn(getLdapDefinition());
ResponseEntity<IdentityProvider> response = identityProviderEndpoints.updateIdentityProvider(provider.getId(), provider, true);
verify(spy, times(1)).setBindPassword(eq("newpassword"));
ArgumentCaptor<IdentityProvider> captor = ArgumentCaptor.forClass(IdentityProvider.class);
verify(identityProviderProvisioning, times(1)).update(captor.capture());
assertNotNull(captor.getValue());
assertEquals(1, captor.getAllValues().size());
assertEquals("newpassword", ((LdapIdentityProviderDefinition)captor.getValue().getConfig()).getBindPassword());
assertNotNull(response);
assertEquals(200, response.getStatusCode().value());
assertNotNull(response.getBody());
assertNotNull(response.getBody().getConfig());
assertTrue(response.getBody().getConfig() instanceof LdapIdentityProviderDefinition);
assertNull(((LdapIdentityProviderDefinition)response.getBody().getConfig()).getBindPassword());
}
@Test
public void create_ldap_provider_removes_password() throws Exception {
IdentityProvider<LdapIdentityProviderDefinition> ldapDefinition = getLdapDefinition();
assertNotNull(ldapDefinition.getConfig().getBindPassword());
when(identityProviderProvisioning.create(anyObject())).thenReturn(ldapDefinition);
ResponseEntity<IdentityProvider> response = identityProviderEndpoints.createIdentityProvider(ldapDefinition, true);
IdentityProvider created = response.getBody();
assertNotNull(created);
assertEquals(LDAP, created.getType());
assertNotNull(created.getConfig());
assertTrue(created.getConfig() instanceof LdapIdentityProviderDefinition);
assertNull(((LdapIdentityProviderDefinition)created.getConfig()).getBindPassword());
}
@Test
public void create_oauth_provider_removes_password() throws Exception {
for (String type : Arrays.asList(OIDC10, OAUTH20)) {
IdentityProvider<AbstractXOAuthIdentityProviderDefinition> xoauthDefinition = getXOAuthProvider();
assertNotNull(xoauthDefinition.getConfig().getRelyingPartySecret());
xoauthDefinition.setType(type);
when(identityProviderProvisioning.create(anyObject())).thenReturn(xoauthDefinition);
ResponseEntity<IdentityProvider> response = identityProviderEndpoints.createIdentityProvider(xoauthDefinition, true);
IdentityProvider created = response.getBody();
assertNotNull(created);
assertEquals(type, created.getType());
assertNotNull(created.getConfig());
assertTrue(created.getConfig() instanceof AbstractXOAuthIdentityProviderDefinition);
assertNull(((AbstractXOAuthIdentityProviderDefinition) created.getConfig()).getRelyingPartySecret());
}
}
@Test
public void testPatchIdentityProviderStatusInvalidPayload () {
IdentityProviderStatus identityProviderStatus = new IdentityProviderStatus();
ResponseEntity responseEntity = identityProviderEndpoints.updateIdentityProviderStatus("123", identityProviderStatus);
assertEquals(HttpStatus.UNPROCESSABLE_ENTITY, responseEntity.getStatusCode());
}
@Test
public void testPatchIdentityProviderStatusInvalidIDP () {
IdentityProviderStatus identityProviderStatus = new IdentityProviderStatus();
identityProviderStatus.setRequirePasswordChange(true);
IdentityProvider notUAAIDP = new IdentityProvider();
notUAAIDP.setType("NOT_UAA");
notUAAIDP.setConfig(new SamlIdentityProviderDefinition());
when(identityProviderProvisioning.retrieve(anyString())).thenReturn(notUAAIDP);
ResponseEntity responseEntity = identityProviderEndpoints.updateIdentityProviderStatus("123", identityProviderStatus);
assertEquals(HttpStatus.UNPROCESSABLE_ENTITY, responseEntity.getStatusCode());
}
@Test
public void testPatchIdentityProviderStatusWithNoIDPDefinition () {
IdentityProviderStatus identityProviderStatus = new IdentityProviderStatus();
identityProviderStatus.setRequirePasswordChange(true);
IdentityProvider invalidIDP = new IdentityProvider();
invalidIDP.setConfig(null);
invalidIDP.setType(OriginKeys.UAA);
when(identityProviderProvisioning.retrieve(anyString())).thenReturn(invalidIDP);
ResponseEntity responseEntity = identityProviderEndpoints.updateIdentityProviderStatus("123", identityProviderStatus);
assertEquals(HttpStatus.UNPROCESSABLE_ENTITY, responseEntity.getStatusCode());
}
@Test
public void testPatchIdentityProviderStatusWithNoPasswordPolicy () {
IdentityProviderStatus identityProviderStatus = new IdentityProviderStatus();
identityProviderStatus.setRequirePasswordChange(true);
IdentityProvider invalidIDP = new IdentityProvider();
invalidIDP.setType(OriginKeys.UAA);
invalidIDP.setConfig(new UaaIdentityProviderDefinition(null, null));
when(identityProviderProvisioning.retrieve(anyString())).thenReturn(invalidIDP);
ResponseEntity responseEntity = identityProviderEndpoints.updateIdentityProviderStatus("123", identityProviderStatus);
assertEquals(HttpStatus.UNPROCESSABLE_ENTITY, responseEntity.getStatusCode());
}
@Test
public void testPatchIdentityProviderStatus () {
IdentityProviderStatus identityProviderStatus = new IdentityProviderStatus();
identityProviderStatus.setRequirePasswordChange(true);
IdentityProvider validIDP = new IdentityProvider();
validIDP.setType(OriginKeys.UAA);
validIDP.setConfig(new UaaIdentityProviderDefinition(new PasswordPolicy(), null));
when(identityProviderProvisioning.retrieve(anyString())).thenReturn(validIDP);
ResponseEntity responseEntity = identityProviderEndpoints.updateIdentityProviderStatus("123", identityProviderStatus);
assertEquals(HttpStatus.OK, responseEntity.getStatusCode());
}
}