/*
* ****************************************************************************
* Cloud Foundry
* Copyright (c) [2009-2017] Pivotal Software, Inc. All Rights Reserved.
*
* This product is licensed to you under the Apache License, Version 2.0 (the "License").
* You may not use this product except in compliance with the License.
*
* This product includes a number of subcomponents with
* separate copyright notices and license terms. Your use of these
* subcomponents is subject to the terms and conditions of the
* subcomponent's license, as noted in the LICENSE file.
* ****************************************************************************
*/
package org.cloudfoundry.identity.uaa.provider.saml.idp;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.cloudfoundry.identity.uaa.zone.IdentityZone;
import org.cloudfoundry.identity.uaa.zone.IdentityZoneHolder;
import org.opensaml.common.xml.SAMLConstants;
import org.opensaml.saml2.metadata.EntityDescriptor;
import org.opensaml.saml2.metadata.IDPSSODescriptor;
import org.opensaml.saml2.metadata.RoleDescriptor;
import org.opensaml.saml2.metadata.SPSSODescriptor;
import org.opensaml.saml2.metadata.provider.MetadataProvider;
import org.opensaml.saml2.metadata.provider.MetadataProviderException;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.saml.key.KeyManager;
import org.springframework.security.saml.metadata.ExtendedMetadata;
import org.springframework.security.saml.metadata.ExtendedMetadataDelegate;
import org.springframework.security.saml.metadata.ExtendedMetadataProvider;
import org.springframework.security.saml.metadata.MetadataMemoryProvider;
import org.springframework.security.saml.trust.httpclient.TLSProtocolConfigurer;
import org.springframework.security.saml.util.SAMLUtil;
import org.springframework.util.StringUtils;
import javax.xml.namespace.QName;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
public class NonSnarlIdpMetadataManager extends IdpMetadataManager implements ExtendedMetadataProvider, InitializingBean, DisposableBean, BeanNameAware {
private static final Log logger = LogFactory.getLog(NonSnarlIdpMetadataManager.class);
private SamlServiceProviderConfigurator configurator;
private IdpMetadataGenerator generator;
private Map<String, String> zoneHostedIdpNames;
private ExtendedMetadata defaultExtendedMetadata;
private String beanName = NonSnarlIdpMetadataManager.class.getName() + "-" + System.identityHashCode(this);
public NonSnarlIdpMetadataManager(SamlServiceProviderConfigurator configurator) throws MetadataProviderException {
super(Collections.<MetadataProvider>emptyList());
this.configurator = configurator;
super.setKeyManager(IdentityZoneHolder.getSamlSPKeyManager());
//disable internal timer
super.setRefreshCheckInterval(0);
logger.info("-----> Internal Timer is disabled");
this.defaultExtendedMetadata = new ExtendedMetadata();
if (zoneHostedIdpNames == null) {
zoneHostedIdpNames = new ConcurrentHashMap<>();
}
}
@Override
public void setBeanName(String name) {
this.beanName = name;
}
@Override
public void setProviders(List<MetadataProvider> newProviders) throws MetadataProviderException {
}
@Override
public void refreshMetadata() {
}
@Override
public void addMetadataProvider(MetadataProvider newProvider) throws MetadataProviderException {
}
@Override
public void removeMetadataProvider(MetadataProvider provider) {
}
@Override
public List<MetadataProvider> getProviders() {
List<MetadataProvider> result = new ArrayList<>();
for (ExtendedMetadataDelegate delegate : getAvailableProviders()) {
result.add(delegate);
}
return result;
}
@Override
public List<ExtendedMetadataDelegate> getAvailableProviders() {
IdentityZone zone = IdentityZoneHolder.get();
List<ExtendedMetadataDelegate> result = new ArrayList<>();
try {
result.add(getLocalIdp());
} catch (MetadataProviderException e) {
throw new IllegalStateException(e);
}
for (SamlServiceProviderHolder holder : configurator.getSamlServiceProviders()) {
log.info("Adding SAML SP zone[" + zone.getId() + "] alias[" + holder.getSamlServiceProvider().getEntityId() + "]");
try {
ExtendedMetadataDelegate delegate = holder.getExtendedMetadataDelegate();
initializeProvider(delegate);
initializeProviderData(delegate);
initializeProviderFilters(delegate);
result.add(delegate);
} catch (MetadataProviderException e) {
log.error("Invalid SAML IDP zone[" + zone.getId() + "] alias[" + holder.getSamlServiceProvider().getEntityId() + "]", e);
}
}
return result;
}
public ExtendedMetadataDelegate getLocalIdp() throws MetadataProviderException {
EntityDescriptor descriptor = generator.generateMetadata();
ExtendedMetadata extendedMetadata = generator.generateExtendedMetadata();
log.info("Initialized local identity provider for entityID: " + descriptor.getEntityID());
MetadataMemoryProvider memoryProvider = new MetadataMemoryProvider(descriptor);
memoryProvider.initialize();
return new ExtendedMetadataDelegate(memoryProvider, extendedMetadata);
}
@Override
protected void initializeProvider(ExtendedMetadataDelegate provider) throws MetadataProviderException {
log.debug("Initializing extendedMetadataDelegate {}", provider);
provider.initialize();
}
@Override
protected void initializeProviderData(ExtendedMetadataDelegate provider) throws MetadataProviderException {
}
/* @Override
protected void initializeProviderFilters(ExtendedMetadataDelegate provider) throws MetadataProviderException {
getManager().initializeProviderFilters(provider);
}*/
@Override
public Set<String> getIDPEntityNames() {
Set<String> result = new HashSet<>();
ExtendedMetadataDelegate delegate = null;
try {
delegate = getLocalIdp();
String idp = getProviderIdpAlias(delegate);
if (StringUtils.hasText(idp)) {
result.add(idp);
}
} catch (MetadataProviderException e) {
log.error("Unable to get IDP alias for:" + delegate, e);
}
return result;
}
protected String getProviderIdpAlias(ExtendedMetadataDelegate provider) throws MetadataProviderException {
List<String> stringSet = parseProvider(provider);
for (String key : stringSet) {
RoleDescriptor idpRoleDescriptor = provider.getRole(key, IDPSSODescriptor.DEFAULT_ELEMENT_NAME, SAMLConstants.SAML20P_NS);
if (idpRoleDescriptor != null) {
return key;
}
}
return null;
}
@Override
public Set<String> getSPEntityNames() {
Set<String> result = new HashSet<>();
for (ExtendedMetadataDelegate delegate : getAvailableProviders()) {
try {
String sp = getSpName(delegate);
if (StringUtils.hasText(sp)) {
result.add(sp);
}
} catch (MetadataProviderException e) {
log.error("Unable to get IDP alias for:" + delegate, e);
}
}
return result;
}
protected String getSpName(ExtendedMetadataDelegate provider) throws MetadataProviderException {
List<String> stringSet = parseProvider(provider);
for (String key : stringSet) {
RoleDescriptor spRoleDescriptor = provider.getRole(key, SPSSODescriptor.DEFAULT_ELEMENT_NAME, SAMLConstants.SAML20P_NS);
if (spRoleDescriptor != null) {
ExtendedMetadata extendedMetadata = getExtendedMetadata(key, provider);
if (extendedMetadata != null) {
return key;
}
}
}
return null;
}
protected String getHostedSpName(ExtendedMetadataDelegate provider) throws MetadataProviderException {
String key = getSpName(provider);
ExtendedMetadata extendedMetadata = getExtendedMetadata(key, provider);
if (extendedMetadata.isLocal()) {
return key;
} else {
return null;
}
}
/**
* {@inheritDoc}
*/
public List<RoleDescriptor> getRole(String entityID, QName roleName) throws MetadataProviderException {
List<RoleDescriptor> roleDescriptors = null;
for (MetadataProvider provider : getProviders()) {
log.debug("Checking child metadata provider for entity descriptor with entity ID: {}", entityID);
try {
roleDescriptors = provider.getRole(entityID, roleName);
if (roleDescriptors != null && !roleDescriptors.isEmpty()) {
break;
}
} catch (MetadataProviderException e) {
log.warn("Error retrieving metadata from provider of type {}, proceeding to next provider",
provider.getClass().getName(), e);
continue;
}
}
return roleDescriptors;
}
/**
* {@inheritDoc}
*/
@Override
public RoleDescriptor getRole(String entityID, QName roleName, String supportedProtocol)
throws MetadataProviderException {
RoleDescriptor roleDescriptor = null;
for (MetadataProvider provider : getProviders()) {
log.debug("Checking child metadata provider for entity descriptor with entity ID: {}", entityID);
try {
roleDescriptor = provider.getRole(entityID, roleName, supportedProtocol);
if (roleDescriptor != null) {
break;
}
} catch (MetadataProviderException e) {
log.warn("Error retrieving metadata from provider of type {}, proceeding to next provider",
provider.getClass().getName(), e);
continue;
}
}
return roleDescriptor;
}
@Override
public boolean isIDPValid(String idpID) {
return getIDPEntityNames().contains(idpID);
}
@Override
public boolean isSPValid(String spID) {
return getSPEntityNames().contains(spID);
}
@Override
public String getHostedIdpName() {
return zoneHostedIdpNames.get(IdentityZoneHolder.get().getId());
}
@Override
public void setHostedIdpName(String hostedIdpName) {
String zoneId = IdentityZoneHolder.get().getId();
zoneHostedIdpNames.put(zoneId, hostedIdpName);
}
@Override
public String getHostedSPName() {
for (ExtendedMetadataDelegate delegate : getAvailableProviders()) {
try {
String spName = getHostedSpName(delegate);
if (StringUtils.hasText(spName)) {
return spName;
}
} catch (MetadataProviderException e) {
log.error("Unable to find hosted SP name:" + delegate, e);
}
}
return null;
}
@Override
public void setHostedSPName(String hostedSPName) {
}
@Override
public String getDefaultIDP() throws MetadataProviderException {
Iterator<String> iterator = getIDPEntityNames().iterator();
if (iterator.hasNext()) {
return iterator.next();
} else {
throw new MetadataProviderException("No IDP was configured, please update included metadata with at least one IDP");
}
}
@Override
public EntityDescriptor getEntityDescriptor(String entityID) throws MetadataProviderException {
EntityDescriptor descriptor = null;
for (MetadataProvider provider : getProviders()) {
log.debug("Checking child metadata provider for entity descriptor with entity ID: {}", entityID);
try {
descriptor = provider.getEntityDescriptor(entityID);
if (descriptor != null) {
break;
}
} catch (MetadataProviderException e) {
log.warn("Error retrieving metadata from provider of type {}, proceeding to next provider",
provider.getClass().getName(), e);
continue;
}
}
return descriptor;
}
@Override
public EntityDescriptor getEntityDescriptor(byte[] hash) throws MetadataProviderException {
for (String sp : getSPEntityNames()) {
if (SAMLUtil.compare(hash, sp)) {
return getEntityDescriptor(sp);
}
}
for (String idp : getIDPEntityNames()) {
if (SAMLUtil.compare(hash, idp)) {
return getEntityDescriptor(idp);
}
}
return null;
}
@Override
public String getEntityIdForAlias(String entityAlias) throws MetadataProviderException {
if (entityAlias == null) {
return null;
}
String entityId = null;
for (String sp : getSPEntityNames()) {
ExtendedMetadata extendedMetadata = getExtendedMetadata(sp);
if (entityAlias.equals(extendedMetadata.getAlias())) {
if (entityId != null && !entityId.equals(sp)) {
throw new MetadataProviderException("Alias " + entityAlias + " is used both for entity " + entityId + " and " + sp);
} else {
entityId = sp;
}
}
}
for (String idp : getIDPEntityNames()) {
ExtendedMetadata extendedMetadata = getExtendedMetadata(idp);
if (entityAlias.equals(extendedMetadata.getAlias())) {
if (entityId != null && !entityId.equals(idp)) {
throw new MetadataProviderException("Alias " + entityAlias + " is used both for entity " + entityId + " and " + idp);
} else {
entityId = idp;
}
}
}
return entityId;
}
@Override
public ExtendedMetadata getDefaultExtendedMetadata() {
return defaultExtendedMetadata;
}
@Override
public void setDefaultExtendedMetadata(ExtendedMetadata defaultExtendedMetadata) {
this.defaultExtendedMetadata = defaultExtendedMetadata;
}
@Override
public boolean isRefreshRequired() {
return false;
}
@Override
public void setRefreshRequired(boolean refreshRequired) {
//no op
}
@Override
public void setKeyManager(KeyManager keyManager) {
this.keyManager = keyManager;
super.setKeyManager(keyManager);
}
@Autowired(required = false)
public void setTLSConfigurer(TLSProtocolConfigurer configurer) {
// Only explicit dependency
}
@Override
public void destroy() {
}
@Override
public ExtendedMetadata getExtendedMetadata(String entityID) throws MetadataProviderException {
for (MetadataProvider provider : getAvailableProviders()) {
ExtendedMetadata extendedMetadata = getExtendedMetadata(entityID, provider);
if (extendedMetadata != null) {
return extendedMetadata;
}
}
return getDefaultExtendedMetadata().clone();
}
private ExtendedMetadata getExtendedMetadata(String entityID, MetadataProvider provider) throws MetadataProviderException {
if (provider instanceof ExtendedMetadataProvider) {
ExtendedMetadataProvider extendedProvider = (ExtendedMetadataProvider) provider;
ExtendedMetadata extendedMetadata = extendedProvider.getExtendedMetadata(entityID);
if (extendedMetadata != null) {
return extendedMetadata.clone();
}
}
return null;
}
public IdpMetadataGenerator getGenerator() {
return generator;
}
public void setGenerator(IdpMetadataGenerator generator) {
this.generator = generator;
}
}