/*******************************************************************************
* Cloud Foundry
* Copyright (c) [2009-2016] 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.impl.config;
import org.cloudfoundry.identity.uaa.constants.OriginKeys;
import org.cloudfoundry.identity.uaa.provider.AbstractIdentityProviderDefinition;
import org.cloudfoundry.identity.uaa.provider.AbstractXOAuthIdentityProviderDefinition;
import org.cloudfoundry.identity.uaa.provider.IdentityProvider;
import org.cloudfoundry.identity.uaa.provider.IdentityProviderProvisioning;
import org.cloudfoundry.identity.uaa.provider.KeystoneIdentityProviderDefinition;
import org.cloudfoundry.identity.uaa.provider.LdapIdentityProviderDefinition;
import org.cloudfoundry.identity.uaa.provider.LockoutPolicy;
import org.cloudfoundry.identity.uaa.provider.PasswordPolicy;
import org.cloudfoundry.identity.uaa.provider.RawXOAuthIdentityProviderDefinition;
import org.cloudfoundry.identity.uaa.provider.SamlIdentityProviderDefinition;
import org.cloudfoundry.identity.uaa.provider.UaaIdentityProviderDefinition;
import org.cloudfoundry.identity.uaa.provider.OIDCIdentityProviderDefinition;
import org.cloudfoundry.identity.uaa.provider.saml.BootstrapSamlIdentityProviderConfigurator;
import org.cloudfoundry.identity.uaa.util.JsonUtils;
import org.cloudfoundry.identity.uaa.util.LdapUtils;
import org.cloudfoundry.identity.uaa.util.UaaMapUtils;
import org.cloudfoundry.identity.uaa.zone.IdentityZone;
import org.json.JSONException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.core.env.AbstractEnvironment;
import org.springframework.core.env.Environment;
import org.springframework.dao.EmptyResultDataAccessException;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import static org.cloudfoundry.identity.uaa.provider.LdapIdentityProviderDefinition.LDAP;
import static org.cloudfoundry.identity.uaa.provider.LdapIdentityProviderDefinition.LDAP_PROPERTY_NAMES;
import static org.cloudfoundry.identity.uaa.provider.LdapIdentityProviderDefinition.LDAP_PROPERTY_TYPES;
public class IdentityProviderBootstrap implements InitializingBean {
private IdentityProviderProvisioning provisioning;
private List<IdentityProvider> providers = new LinkedList<>();
private BootstrapSamlIdentityProviderConfigurator configurator;
private Map<String, AbstractXOAuthIdentityProviderDefinition> oauthIdpDefintions;
private Map<String, Object> ldapConfig;
private Map<String, Object> keystoneConfig;
private Environment environment;
private PasswordPolicy defaultPasswordPolicy;
private LockoutPolicy defaultLockoutPolicy;
private boolean disableInternalUserManagement;
public IdentityProviderBootstrap(IdentityProviderProvisioning provisioning, Environment environment) {
if (provisioning==null) {
throw new NullPointerException("Constructor argument can't be null.");
}
this.provisioning = provisioning;
this.environment = environment;
}
private void addOauthProviders() {
if (oauthIdpDefintions == null) {
return;
}
for (Map.Entry<String, AbstractXOAuthIdentityProviderDefinition> definition : oauthIdpDefintions.entrySet()) {
validateDuplicateAlias(definition.getKey());
IdentityProvider provider = new IdentityProvider();
if (RawXOAuthIdentityProviderDefinition.class.isAssignableFrom(definition.getValue().getClass())) {
provider.setType(OriginKeys.OAUTH20);
} else if(OIDCIdentityProviderDefinition.class.isAssignableFrom(definition.getValue().getClass())) {
provider.setType(OriginKeys.OIDC10);
} else {
throw new IllegalArgumentException("Unknown provider type.");
}
provider.setOriginKey(definition.getKey());
provider.setName("UAA Oauth Identity Provider["+provider.getOriginKey()+"]");
provider.setActive(true);
try {
provider.setConfig(definition.getValue());
} catch (JsonUtils.JsonUtilException x) {
throw new RuntimeException("Non serializable Oauth config");
}
providers.add(provider);
}
}
public void validateDuplicateAlias(String originKey) {
for (IdentityProvider provider: providers) {
if (provider.getOriginKey().equals(originKey)) {
throw new IllegalArgumentException("Provider alias " + originKey + " is not unique.");
}
}
}
public void setSamlProviders(BootstrapSamlIdentityProviderConfigurator configurator) {
this.configurator = configurator;
}
protected void addSamlProviders() {
if (configurator==null) {
return;
}
for (SamlIdentityProviderDefinition def : configurator.getIdentityProviderDefinitions()) {
validateDuplicateAlias(def.getIdpEntityAlias());
IdentityProvider provider = new IdentityProvider();
provider.setType(OriginKeys.SAML);
provider.setOriginKey(def.getIdpEntityAlias());
provider.setName("UAA SAML Identity Provider["+provider.getOriginKey()+"]");
provider.setActive(true);
try {
provider.setConfig(def);
} catch (JsonUtils.JsonUtilException x) {
throw new RuntimeException("Non serializable SAML config");
}
providers.add(provider);
}
}
public void setLdapConfig(HashMap<String, Object> ldapConfig) {
this.ldapConfig = ldapConfig;
}
protected void addLdapProvider() {
boolean ldapProfile = Arrays.asList(environment.getActiveProfiles()).contains(OriginKeys.LDAP);
if (ldapConfig != null || ldapProfile) {
IdentityProvider provider = new IdentityProvider();
provider.setActive(ldapProfile);
provider.setOriginKey(OriginKeys.LDAP);
provider.setType(OriginKeys.LDAP);
provider.setName("UAA LDAP Provider");
Map<String,Object> ldap = new HashMap<>();
ldap.put(LDAP, ldapConfig);
LdapIdentityProviderDefinition json = getLdapConfigAsDefinition(ldap);
provider.setConfig(json);
provider.setActive(ldapProfile && json.isConfigured());
providers.add(provider);
}
}
protected LdapIdentityProviderDefinition getLdapConfigAsDefinition(Map<String, Object> ldapConfig) {
ldapConfig = UaaMapUtils.flatten(ldapConfig);
populateLdapEnvironment(ldapConfig);
if (ldapConfig.isEmpty()) {
return new LdapIdentityProviderDefinition();
}
return LdapUtils.fromConfig(ldapConfig);
}
protected void populateLdapEnvironment(Map<String, Object> ldapConfig) {
//this method reads the environment and overwrites values (needed by LdapMockMvcTests that overrides properties through env)
AbstractEnvironment env = (AbstractEnvironment)environment;
//these are our known complex data structures in the properties
for (String property : LDAP_PROPERTY_NAMES) {
if (env.containsProperty(property) && LDAP_PROPERTY_TYPES.get(property)!=null) {
ldapConfig.put(property, env.getProperty(property, LDAP_PROPERTY_TYPES.get(property)));
}
}
//but we can also have string properties like ldap.attributeMappings.user.attribute.mapToAttributeName=mapFromAttributeName
Map<String,Object> stringProperties = UaaMapUtils.getPropertiesStartingWith(env, "ldap.");
for (Map.Entry<String, Object> entry : stringProperties.entrySet()) {
if (!LDAP_PROPERTY_NAMES.contains(entry.getKey())) {
ldapConfig.put(entry.getKey(), entry.getValue());
}
}
}
public void setKeystoneConfig(HashMap<String, Object> keystoneConfig) {
this.keystoneConfig = keystoneConfig;
}
protected AbstractIdentityProviderDefinition getKeystoneDefinition(Map<String, Object> config) {
return new KeystoneIdentityProviderDefinition(config);
}
protected void addKeystoneProvider() {
boolean keystoneProfile = Arrays.asList(environment.getActiveProfiles()).contains(OriginKeys.KEYSTONE);
if (keystoneConfig != null || keystoneProfile) {
boolean active = keystoneProfile && keystoneConfig!=null;
IdentityProvider provider = new IdentityProvider();
provider.setOriginKey(OriginKeys.KEYSTONE);
provider.setType(OriginKeys.KEYSTONE);
provider.setName("UAA Keystone Provider");
provider.setActive(active);
provider.setConfig(getKeystoneDefinition(keystoneConfig));
providers.add(provider);
}
}
@Override
public void afterPropertiesSet() throws Exception {
providers.clear();
addLdapProvider();
addSamlProviders();
addOauthProviders();
addKeystoneProvider();
String zoneId = IdentityZone.getUaa().getId();
//deactivate all providers that are no longer present
deactivateUnusedProviders(zoneId);
for (IdentityProvider provider: providers) {
IdentityProvider existing = null;
try {
existing = provisioning.retrieveByOrigin(provider.getOriginKey(), zoneId);
}catch (EmptyResultDataAccessException x){
}
provider.setIdentityZoneId(zoneId);
if (existing==null) {
provisioning.create(provider);
} else {
provider.setId(existing.getId());
provider.setCreated(existing.getCreated());
provider.setVersion(existing.getVersion());
provider.setLastModified(new Date(System.currentTimeMillis()));
provisioning.update(provider);
}
}
updateDefaultZoneUaaIDP();
}
private void deactivateUnusedProviders(String zoneId) {
for (IdentityProvider provider: provisioning.retrieveAll(false, zoneId)) {
if (!OriginKeys.UAA.equals(provider.getType())) {
if (!isAmongProviders(provider.getOriginKey(), provider.getType())) {
provider.setActive(false);
provisioning.update(provider);
}
}
}
}
protected void updateDefaultZoneUaaIDP() throws JSONException {
IdentityProvider internalIDP = provisioning.retrieveByOrigin(OriginKeys.UAA, IdentityZone.getUaa().getId());
UaaIdentityProviderDefinition identityProviderDefinition = new UaaIdentityProviderDefinition(defaultPasswordPolicy, defaultLockoutPolicy, disableInternalUserManagement);
internalIDP.setConfig(identityProviderDefinition);
String disableInternalAuth = environment.getProperty("disableInternalAuth");
internalIDP.setActive(!getBooleanValue(disableInternalAuth, false));
provisioning.update(internalIDP);
}
protected boolean getBooleanValue(String s, boolean defaultValue) {
if (s != null) {
return Boolean.valueOf(s);
} else {
return defaultValue;
}
}
private boolean isAmongProviders(String originKey, String type) {
for (IdentityProvider provider: providers) {
if (provider.getOriginKey().equals(originKey) && provider.getType().equals(type)) {
return true;
}
}
return false;
}
public void setDefaultPasswordPolicy(PasswordPolicy defaultPasswordPolicy) {
this.defaultPasswordPolicy = defaultPasswordPolicy;
}
public void setDefaultLockoutPolicy(LockoutPolicy defaultLockoutPolicy) {
this.defaultLockoutPolicy = defaultLockoutPolicy;
}
public boolean isDisableInternalUserManagement() {
return disableInternalUserManagement;
}
public void setDisableInternalUserManagement(boolean disableInternalUserManagement) {
this.disableInternalUserManagement = disableInternalUserManagement;
}
public void setOauthIdpDefinitions(Map<String, AbstractXOAuthIdentityProviderDefinition> oauthIdpDefintions) {
this.oauthIdpDefintions = oauthIdpDefintions;
}
}