/*******************************************************************************
* Cloud Foundry
* Copyright (c) [2009-2015] 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;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import org.springframework.util.StringUtils;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import java.io.IOException;
import java.io.StringReader;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@JsonIgnoreProperties(ignoreUnknown = true)
public class SamlIdentityProviderDefinition extends ExternalIdentityProviderDefinition {
public enum MetadataLocation {
URL,
DATA,
UNKNOWN
}
public enum ExternalGroupMappingMode {
EXPLICITLY_MAPPED,
AS_SCOPES
}
private String metaDataLocation;
private String idpEntityAlias;
private String zoneId;
private String nameID;
private int assertionConsumerIndex;
private boolean metadataTrustCheck;
private boolean showSamlLink;
private String linkText;
private String iconUrl;
private ExternalGroupMappingMode groupMappingMode = ExternalGroupMappingMode.EXPLICITLY_MAPPED;
private boolean skipSslValidation = false;
public SamlIdentityProviderDefinition() {}
public SamlIdentityProviderDefinition clone() {
List<String> emailDomain = getEmailDomain() != null ? new ArrayList<>(getEmailDomain()) : null;
List<String> externalGroupsWhitelist = getExternalGroupsWhitelist() != null ? new ArrayList<>(getExternalGroupsWhitelist()) : null;
Map<String, Object> attributeMappings = getAttributeMappings() != null ? new HashMap(getAttributeMappings()) : null;
SamlIdentityProviderDefinition def = new SamlIdentityProviderDefinition();
def.setMetaDataLocation(metaDataLocation);
def.setIdpEntityAlias(idpEntityAlias);
def.setZoneId(zoneId);
def.setNameID(nameID);
def.setAssertionConsumerIndex(assertionConsumerIndex);
def.setMetadataTrustCheck(metadataTrustCheck);
def.setShowSamlLink(showSamlLink);
def.setLinkText(linkText);
def.setIconUrl(iconUrl);
def.setAddShadowUserOnLogin(isAddShadowUserOnLogin());
def.setEmailDomain(emailDomain);
def.setExternalGroupsWhitelist(externalGroupsWhitelist);
def.setAttributeMappings(attributeMappings);
def.setAdditionalConfiguration(getAdditionalConfiguration());
def.setProviderDescription(getProviderDescription());
def.setGroupMappingMode(getGroupMappingMode());
def.setSocketFactoryClassName(getSocketFactoryClassName());
def.setSkipSslValidation(isSkipSslValidation());
def.setStoreCustomAttributes(isStoreCustomAttributes());
return def;
}
@JsonIgnore
public MetadataLocation getType() {
String trimmedLocation = metaDataLocation.trim();
if (trimmedLocation.startsWith("<?xml") ||
trimmedLocation.startsWith("<md:EntityDescriptor") ||
trimmedLocation.startsWith("<EntityDescriptor")) {
if(validateXml(trimmedLocation)) {
return MetadataLocation.DATA;
}
} else if (trimmedLocation.startsWith("http")) {
try {
URL uri = new URL(trimmedLocation);
return MetadataLocation.URL;
} catch (MalformedURLException e) {
//invalid URL
}
}
return MetadataLocation.UNKNOWN;
}
private boolean validateXml(String xml) {
if (xml==null || xml.toUpperCase().contains("<!DOCTYPE")) {
return false;
}
try {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setExpandEntityReferences(false);
DocumentBuilder builder = factory.newDocumentBuilder();
builder.parse(new InputSource(new StringReader(xml)));
} catch (ParserConfigurationException | SAXException | IOException e) {
return false;
}
return true;
}
public String getMetaDataLocation() {
return metaDataLocation;
}
public SamlIdentityProviderDefinition setMetaDataLocation(String metaDataLocation) {
this.metaDataLocation = metaDataLocation;
return this;
}
public String getIdpEntityAlias() {
return idpEntityAlias;
}
public SamlIdentityProviderDefinition setIdpEntityAlias(String idpEntityAlias) {
this.idpEntityAlias = idpEntityAlias;
return this;
}
public String getNameID() {
return nameID;
}
public SamlIdentityProviderDefinition setNameID(String nameID) {
this.nameID = nameID;
return this;
}
public int getAssertionConsumerIndex() {
return assertionConsumerIndex;
}
public SamlIdentityProviderDefinition setAssertionConsumerIndex(int assertionConsumerIndex) {
this.assertionConsumerIndex = assertionConsumerIndex;
return this;
}
public boolean isMetadataTrustCheck() {
return metadataTrustCheck;
}
public SamlIdentityProviderDefinition setMetadataTrustCheck(boolean metadataTrustCheck) {
this.metadataTrustCheck = metadataTrustCheck;
return this;
}
public boolean isShowSamlLink() {
return showSamlLink;
}
public SamlIdentityProviderDefinition setShowSamlLink(boolean showSamlLink) {
this.showSamlLink = showSamlLink;
return this;
}
public ExternalGroupMappingMode getGroupMappingMode() {
return groupMappingMode;
}
public void setGroupMappingMode(ExternalGroupMappingMode asScopes) {
this.groupMappingMode = asScopes;
}
public String getSocketFactoryClassName() {
return null;
}
public SamlIdentityProviderDefinition setSocketFactoryClassName(String socketFactoryClassName) {
//no op
return this;
}
public String getLinkText() {
return StringUtils.hasText(linkText) ? linkText : idpEntityAlias;
}
public SamlIdentityProviderDefinition setLinkText(String linkText) {
this.linkText = linkText;
return this;
}
public String getIconUrl() {
return iconUrl;
}
public SamlIdentityProviderDefinition setIconUrl(String iconUrl) {
this.iconUrl = iconUrl;
return this;
}
public String getZoneId() {
return zoneId;
}
public SamlIdentityProviderDefinition setZoneId(String zoneId) {
this.zoneId = zoneId;
return this;
}
public boolean isSkipSslValidation() {
return skipSslValidation;
}
public void setSkipSslValidation(boolean skipSslValidation) {
this.skipSslValidation = skipSslValidation;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
if (!super.equals(o)) return false;
SamlIdentityProviderDefinition that = (SamlIdentityProviderDefinition) o;
return Objects.equals(getUniqueAlias(), that.getUniqueAlias());
}
@Override
public int hashCode() {
String alias = getUniqueAlias();
return alias==null ? 0 : alias.hashCode();
}
@JsonIgnore
public String getUniqueAlias() {
return getIdpEntityAlias()+"###"+getZoneId();
}
@Override
public String toString() {
return "SamlIdentityProviderDefinition{" +
"idpEntityAlias='" + idpEntityAlias + '\'' +
", metaDataLocation='" + metaDataLocation + '\'' +
", nameID='" + nameID + '\'' +
", assertionConsumerIndex=" + assertionConsumerIndex +
", metadataTrustCheck=" + metadataTrustCheck +
", showSamlLink=" + showSamlLink +
", socketFactoryClassName='deprected-not used'" +
", skipSslValidation=" + skipSslValidation +
", linkText='" + linkText + '\'' +
", iconUrl='" + iconUrl + '\'' +
", zoneId='" + zoneId + '\'' +
", addShadowUserOnLogin='" + isAddShadowUserOnLogin() + '\'' +
'}';
}
}