Skip to content

Commit

Permalink
When refreshing SAML providers, ensure that non existent providers
Browse files Browse the repository at this point in the history
(deleted from DB) are also purged from the Spring Security SAML memory
https://www.pivotaltracker.com/story/show/109817758
[#109817758]
  • Loading branch information
fhanik committed Dec 14, 2015
1 parent c5afd09 commit e788482
Show file tree
Hide file tree
Showing 3 changed files with 56 additions and 115 deletions.
Expand Up @@ -61,34 +61,14 @@ public class SamlIdentityProviderConfigurator implements InitializingBean {
private BasicParserPool parserPool;

private Timer dummyTimer = new Timer() {

@Override
public void cancel() {
super.cancel();
}

@Override
public int purge() {
return 0;
}

@Override
public void schedule(TimerTask task, long delay) {}

@Override
public void schedule(TimerTask task, long delay, long period) {}

@Override
public void schedule(TimerTask task, Date firstTime, long period) {}

@Override
public void schedule(TimerTask task, Date time) {}

@Override
public void scheduleAtFixedRate(TimerTask task, long delay, long period) {}

@Override
public void scheduleAtFixedRate(TimerTask task, Date firstTime, long period) {}
@Override public void cancel() { super.cancel(); }
@Override public int purge() {return 0; }
@Override public void schedule(TimerTask task, long delay) {}
@Override public void schedule(TimerTask task, long delay, long period) {}
@Override public void schedule(TimerTask task, Date firstTime, long period) {}
@Override public void schedule(TimerTask task, Date time) {}
@Override public void scheduleAtFixedRate(TimerTask task, long delay, long period) {}
@Override public void scheduleAtFixedRate(TimerTask task, Date firstTime, long period) {}
};

public SamlIdentityProviderConfigurator() {
Expand Down
Expand Up @@ -17,9 +17,9 @@
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.cloudfoundry.identity.uaa.constants.OriginKeys;
import org.cloudfoundry.identity.uaa.provider.IdentityProvider;
import org.cloudfoundry.identity.uaa.provider.SamlIdentityProviderDefinition;
import org.cloudfoundry.identity.uaa.util.JsonUtils;
import org.cloudfoundry.identity.uaa.provider.IdentityProvider;
import org.cloudfoundry.identity.uaa.zone.IdentityProviderProvisioning;
import org.cloudfoundry.identity.uaa.zone.IdentityZone;
import org.cloudfoundry.identity.uaa.zone.IdentityZoneHolder;
Expand Down Expand Up @@ -49,6 +49,7 @@
import javax.xml.namespace.QName;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
Expand Down Expand Up @@ -129,7 +130,9 @@ protected void refreshAllProviders(boolean ignoreTimestamp) throws MetadataProvi
for (IdentityZone zone : zoneDao.retrieveAll()) {
ExtensionMetadataManager manager = getManager(zone);
boolean hasChanges = false;
List<SamlIdentityProviderDefinition> zoneDefinitions = new LinkedList(configurator.getIdentityProviderDefinitionsForZone(zone));
for (IdentityProvider provider : providerDao.retrieveAll(false,zone.getId())) {
zoneDefinitions.remove(provider.getConfig());
if (OriginKeys.SAML.equals(provider.getType()) && (ignoreTimestamp || lastRefresh < provider.getLastModified().getTime())) {
try {
SamlIdentityProviderDefinition definition = (SamlIdentityProviderDefinition)provider.getConfig();
Expand All @@ -142,11 +145,7 @@ protected void refreshAllProviders(boolean ignoreTimestamp) throws MetadataProvi
}
manager.addMetadataProvider(delegates[0]);
} else {
log.info("Removing SAML IDP zone[" + zone.getId() + "] alias[" + definition.getIdpEntityAlias() + "]");
ExtendedMetadataDelegate delegate = configurator.removeIdentityProviderDefinition(definition);
if (delegate!=null) {
manager.removeMetadataProvider(delegate);
}
removeSamlProvider(zone, manager, definition);
}
hasChanges = true;
} catch (MetadataProviderException e) {
Expand All @@ -157,13 +156,25 @@ protected void refreshAllProviders(boolean ignoreTimestamp) throws MetadataProvi
}
}
}
for (SamlIdentityProviderDefinition definition : zoneDefinitions) {
removeSamlProvider(zone, manager, definition);
hasChanges = true;
}
if (hasChanges) {
refreshZoneManager(manager);
}
}
lastRefresh = System.currentTimeMillis();
}

protected void removeSamlProvider(IdentityZone zone, ExtensionMetadataManager manager, SamlIdentityProviderDefinition definition) {
log.info("Removing SAML IDP zone[" + zone.getId() + "] alias[" + definition.getIdpEntityAlias() + "]");
ExtendedMetadataDelegate delegate = configurator.removeIdentityProviderDefinition(definition);
if (delegate!=null) {
manager.removeMetadataProvider(delegate);
}
}

protected ExtensionMetadataManager getManager(IdentityZone zone) {
if (metadataManagers==null) { //called during super constructor
metadataManagers = new ConcurrentHashMap<>();
Expand Down
Expand Up @@ -12,6 +12,7 @@
*******************************************************************************/
package org.cloudfoundry.identity.uaa.login.saml;

import org.cloudfoundry.identity.uaa.event.EntityDeletedEvent;
import org.cloudfoundry.identity.uaa.provider.SamlIdentityProviderDefinition;
import org.cloudfoundry.identity.uaa.zone.IdentityZoneConfiguration;
import org.cloudfoundry.identity.uaa.zone.SamlConfig;
Expand Down Expand Up @@ -125,42 +126,51 @@ public void testFallbackIDP_shows_Error_Message_Instead_Of_Default() throws Exce

@Test
public void testThatDBAddedXMLProviderShowsOnLoginPage() throws Exception {
assertEquals(1, zoneAwareMetadataManager.getAvailableProviders().size());
IdentityProvider<SamlIdentityProviderDefinition> provider = createSamlProvider(DEFAULT_SIMPLE_SAML_METADATA, "simplesamlphp", "Log in with Simple Saml PHP Config");
addXmlProviderToDatabase();
}

@Test
public void testThatDBDeletedXMLProviderDoesNotShowOnLoginPage() throws Exception {
IdentityProvider<SamlIdentityProviderDefinition> provider = addXmlProviderToDatabase();
SamlIdentityProviderDefinition definition = provider.getConfig();
//ensure that the listener was not the one who created the provider
assertEquals(1, zoneAwareMetadataManager.getAvailableProviders().size());
//this simulates what the timer does
//delete from DB
EntityDeletedEvent event = new EntityDeletedEvent(provider);
getWebApplicationContext().publishEvent(event);
//verify that provider is deleted
assertEquals(0, getWebApplicationContext().getBean(JdbcTemplate.class).queryForInt("select count(*) from identity_provider where id=?", provider.getId()));
//issue a timer
zoneAwareMetadataManager.refreshAllProviders();
assertEquals(2, zoneAwareMetadataManager.getAvailableProviders().size());

//ensure that we have an actual SAML provider created


//ensure that it exists in the link
//ensure that it the link doesn't show up
getMockMvc().perform(get("/login").accept(TEXT_HTML))
.andExpect(status().isOk())
.andExpect(xpath("//a[text()='" + definition.getLinkText() + "']").exists());
.andExpect(xpath("//a[text()='" + definition.getLinkText() + "']").doesNotExist());
//and provider should be gone
assertEquals(1, zoneAwareMetadataManager.getAvailableProviders().size());
}

@Test
public void test_Reject_Duplicate_Alias_and_Duplicate_Entity_ID() throws Exception {

protected IdentityProvider<SamlIdentityProviderDefinition> addXmlProviderToDatabase() throws Exception {
assertEquals(1, zoneAwareMetadataManager.getAvailableProviders().size());
IdentityProvider<SamlIdentityProviderDefinition> provider = createSamlProvider(DEFAULT_SIMPLE_SAML_METADATA, "simplesamlphp", "Log in with Simple Saml PHP Config");
SamlIdentityProviderDefinition definition = provider.getConfig();
//ensure that the listener was not the one who created the provider
assertEquals(1, zoneAwareMetadataManager.getAvailableProviders().size());
//this simulates what the timer does
zoneAwareMetadataManager.refreshAllProviders();
assertEquals(2, zoneAwareMetadataManager.getAvailableProviders().size());

//ensure that we have an actual SAML provider created

assertEquals(2, zoneAwareMetadataManager.getAvailableProviders().size());
//ensure that it exists in the link
getMockMvc().perform(get("/login").accept(TEXT_HTML))
.andExpect(status().isOk())
.andExpect(xpath("//a[text()='" + definition.getLinkText() + "']").exists());

return provider;
}

@Test
public void test_Reject_Duplicate_Alias_and_Duplicate_Entity_ID() throws Exception {
IdentityProvider<SamlIdentityProviderDefinition> provider = addXmlProviderToDatabase();

//
try {
createSamlProvider(DEFAULT_SIMPLE_SAML_METADATA, "simplesamlphp", "Log in with Simple Saml PHP Config");
Expand All @@ -175,7 +185,7 @@ public void test_Reject_Duplicate_Alias_and_Duplicate_Entity_ID() throws Excepti
zoneAwareMetadataManager.refreshAllProviders();
assertEquals(2, zoneAwareMetadataManager.getAvailableProviders().size());

definition = provider.getConfig();
SamlIdentityProviderDefinition definition = provider.getConfig();
//ensure that it exists in the link
getMockMvc().perform(get("/login").accept(TEXT_HTML))
.andExpect(status().isOk())
Expand All @@ -184,26 +194,11 @@ public void test_Reject_Duplicate_Alias_and_Duplicate_Entity_ID() throws Excepti

@Test
public void testThatDBXMLDisabledProvider() throws Exception {
assertEquals(1, zoneAwareMetadataManager.getAvailableProviders().size());
IdentityProvider<SamlIdentityProviderDefinition> provider = createSamlProvider(DEFAULT_SIMPLE_SAML_METADATA, "simplesamlphp", "Log in with Simple Saml PHP Config");
SamlIdentityProviderDefinition definition = provider.getConfig();
//ensure that the listener was not the one who created the provider
assertEquals(1, zoneAwareMetadataManager.getAvailableProviders().size());

//this simulates what the timer does
zoneAwareMetadataManager.refreshAllProviders();

//ensure that we have an actual SAML provider created
assertEquals(2, zoneAwareMetadataManager.getAvailableProviders().size());

//ensure that it exists in the link
getMockMvc().perform(get("/login").accept(TEXT_HTML))
.andExpect(status().isOk())
.andExpect(xpath("//a[text()='" + definition.getLinkText() + "']").exists());
IdentityProvider<SamlIdentityProviderDefinition> provider = addXmlProviderToDatabase();

provider.setActive(false);
provider = providerProvisioning.update(provider);
definition = provider.getConfig();
SamlIdentityProviderDefinition definition = provider.getConfig();

//this simulates what the timer does
zoneAwareMetadataManager.refreshAllProviders();
Expand All @@ -219,59 +214,14 @@ public void testThatDBXMLDisabledProvider() throws Exception {

@Test
public void testThatDBAddedFileProviderShowsOnLoginPage() throws Exception {
assertEquals(1, zoneAwareMetadataManager.getAvailableProviders().size());
IdentityProvider<SamlIdentityProviderDefinition> provider = createSamlProvider(DEFAULT_SIMPLE_SAML_METADATA, "simplesamlphp", "Log in with Simple Saml PHP File");
IdentityProvider<SamlIdentityProviderDefinition> provider = addXmlProviderToDatabase();
SamlIdentityProviderDefinition definition = provider.getConfig();
//ensure that the listener was not the one who created the provider
assertEquals(1, zoneAwareMetadataManager.getAvailableProviders().size());

//this simulates what the timer does
zoneAwareMetadataManager.refreshAllProviders();
assertEquals(2, zoneAwareMetadataManager.getAvailableProviders().size());

//ensure that we have an actual SAML provider created


//ensure that it exists in the link
getMockMvc().perform(get("/login").accept(TEXT_HTML))
.andExpect(status().isOk())
.andExpect(xpath("//a[text()='" + definition.getLinkText() + "']").exists());
}

@Test
public void testThatDBFileDisabledProvider() throws Exception {
assertEquals(1, zoneAwareMetadataManager.getAvailableProviders().size());
IdentityProvider<SamlIdentityProviderDefinition> provider = createSamlProvider(DEFAULT_SIMPLE_SAML_METADATA, "simplesamlphp", "Log in with Simple Saml PHP File");
SamlIdentityProviderDefinition definition = provider.getConfig();
//ensure that the listener was not the one who created the provider
assertEquals(1, zoneAwareMetadataManager.getAvailableProviders().size());

//this simulates what the timer does
zoneAwareMetadataManager.refreshAllProviders();

//ensure that we have an actual SAML provider created
assertEquals(2, zoneAwareMetadataManager.getAvailableProviders().size());

//ensure that it exists in the link
getMockMvc().perform(get("/login").accept(TEXT_HTML))
.andExpect(status().isOk())
.andExpect(xpath("//a[text()='" + definition.getLinkText() + "']").exists());

provider.setActive(false);
provider = providerProvisioning.update(provider);
definition = provider.getConfig();

//this simulates what the timer does
zoneAwareMetadataManager.refreshAllProviders();

//ensure that we have an actual SAML provider created
assertEquals(1, zoneAwareMetadataManager.getAvailableProviders().size());

//ensure that it exists in the link
getMockMvc().perform(get("/login").accept(TEXT_HTML))
.andExpect(status().isOk())
.andExpect(xpath("//a[text()='" + definition.getLinkText() + "']").doesNotExist());
}

@Test
public void testThatDBAddedUrlProviderShowsOnLoginPage() throws Exception {
Expand Down

0 comments on commit e788482

Please sign in to comment.