Skip to content
Open
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,6 @@

package org.wso2.carbon.identity.api.server.application.management.v1.core;

import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.ArrayUtils;
Expand Down Expand Up @@ -91,6 +88,13 @@
import org.wso2.carbon.identity.api.server.common.ContextLoader;
import org.wso2.carbon.identity.api.server.common.Util;
import org.wso2.carbon.identity.api.server.common.error.APIError;
import org.wso2.carbon.identity.api.server.common.file.FileContent;
import org.wso2.carbon.identity.api.server.common.file.FileSerializationConfig;
import org.wso2.carbon.identity.api.server.common.file.FileSerializationException;
import org.wso2.carbon.identity.api.server.common.file.FileSerializationUtil;
import org.wso2.carbon.identity.api.server.common.file.JsonConfig;
import org.wso2.carbon.identity.api.server.common.file.XmlConfig;
import org.wso2.carbon.identity.api.server.common.file.YamlConfig;
import org.wso2.carbon.identity.application.authentication.framework.util.FrameworkConstants;
import org.wso2.carbon.identity.application.authentication.framework.util.FrameworkConstants.StandardInboundProtocols;
import org.wso2.carbon.identity.application.common.IdentityApplicationManagementClientException;
Expand Down Expand Up @@ -147,19 +151,10 @@
import org.wso2.carbon.identity.template.mgt.model.Template;
import org.wso2.carbon.user.core.common.AbstractUserStoreManager;
import org.wso2.carbon.user.core.service.RealmService;
import org.yaml.snakeyaml.DumperOptions;
import org.yaml.snakeyaml.LoaderOptions;
import org.yaml.snakeyaml.TypeDescription;
import org.yaml.snakeyaml.Yaml;
import org.yaml.snakeyaml.constructor.Constructor;
import org.yaml.snakeyaml.error.YAMLException;
import org.yaml.snakeyaml.inspector.TagInspector;
import org.yaml.snakeyaml.inspector.TrustedPrefixesTagInspector;

import java.io.IOException;
import java.io.InputStream;
import java.io.StringReader;
import java.io.StringWriter;
import java.net.MalformedURLException;
import java.net.URISyntaxException;
import java.net.URL;
Expand All @@ -179,10 +174,7 @@

import javax.activation.MimeType;
import javax.activation.MimeTypeParseException;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;

import static org.wso2.carbon.identity.api.server.application.management.common.ApplicationManagementConstants.ADVANCED_CONFIGURATIONS;
import static org.wso2.carbon.identity.api.server.application.management.common.ApplicationManagementConstants.APPLICATION_BASED_OUTBOUND_PROVISIONING_ENABLED;
Expand Down Expand Up @@ -267,6 +259,8 @@ public ServerApplicationManagementService(ApplicationManagementService applicati
private static final String[] VALID_MEDIA_TYPES_JSON = {"application/json", "text/json"};
private static final Class<?>[] INBOUND_CONFIG_PROTOCOLS = new Class<?>[]{ServiceProvider.class,
SAMLSSOServiceProviderDTO.class, OAuthAppDO.class};
private static final Class<?>[] ADDITIONAL_INBOUND_CONFIG_PROTOCOLS = new Class<?>[]{
SAMLSSOServiceProviderDTO.class, OAuthAppDO.class};

static {
SUPPORTED_FILTER_ATTRIBUTES.add(NAME);
Expand Down Expand Up @@ -586,90 +580,68 @@ private TransferResource generateFileFromModel(String fileType, ServiceProvider
log.debug("Generating file content from model for application: " + serviceProvider.getApplicationName());
}

StringBuilder fileNameSB = new StringBuilder(serviceProvider.getApplicationName());
String fileContent;

if (Arrays.asList(VALID_MEDIA_TYPES_XML).contains(fileType)) {
fileContent = parseXmlFromServiceProvider(serviceProvider);
fileNameSB.append(XML_FILE_EXTENSION);
} else if (Arrays.asList(VALID_MEDIA_TYPES_YAML).contains(fileType)) {
fileContent = parseYamlFromServiceProvider(serviceProvider);
fileNameSB.append(YML_FILE_EXTENSION);
} else if (Arrays.asList(VALID_MEDIA_TYPES_JSON).contains(fileType)) {
fileContent = parseJsonFromServiceProvider(serviceProvider);
fileNameSB.append(JSON_FILE_EXTENSION);
} else {
throw Utils.buildServerError("Unsupported media type: " + fileType + "."
+ " Supported media types are " + Arrays.toString(VALID_MEDIA_TYPES_XML) + ", "
+ Arrays.toString(VALID_MEDIA_TYPES_YAML) + ", " + Arrays.toString(VALID_MEDIA_TYPES_JSON));
}

try {
FileContent fileContent =
FileSerializationUtil.serialize(
serviceProvider,
serviceProvider.getApplicationName(),
fileType,
buildSerializationConfig()
);

return new TransferResource(
fileNameSB.toString(),
fileContent.getBytes(StandardCharsets.UTF_8),
fileContent.getFileName(),
fileContent.getContent().getBytes(StandardCharsets.UTF_8),
new MimeType("application/octet-stream")
);
} catch (FileSerializationException e) {
throw Utils.buildServerError("Error exporting application from file.", e);
} catch (MimeTypeParseException e) {
throw new RuntimeException("Failed to parse MIME type", e);
}
}

private String parseXmlFromServiceProvider(ServiceProvider serviceProvider) {
private FileSerializationConfig buildSerializationConfig() {

JAXBContext jaxbContext;
try {
jaxbContext = JAXBContext.newInstance(INBOUND_CONFIG_PROTOCOLS);
Marshaller marshaller = jaxbContext.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
FileSerializationConfig config = new FileSerializationConfig();

XmlConfig xmlConfig = new XmlConfig();
xmlConfig.setAdditionalJaxbClasses(ADDITIONAL_INBOUND_CONFIG_PROTOCOLS);
xmlConfig.setMarshallerCustomizer(marshaller -> {
marshaller.setListener(new Marshaller.Listener() {
@Override
public void beforeMarshal(Object source) {
if (source instanceof InboundAuthenticationConfig) {
InboundAuthenticationConfig config = (InboundAuthenticationConfig) source;
InboundAuthenticationConfig authConfig = (InboundAuthenticationConfig) source;
for (InboundAuthenticationRequestConfig requestConfig
: config.getInboundAuthenticationRequestConfigs()) {
: authConfig.getInboundAuthenticationRequestConfigs()) {
requestConfig.setInboundConfiguration(null);
}
}
}
});
StringWriter stringWriter = new StringWriter();
marshaller.marshal(serviceProvider, stringWriter);
return stringWriter.toString();
} catch (JAXBException e) {
throw Utils.buildServerError("Error exporting application from XML file.", e);
}
}

private String parseYamlFromServiceProvider(ServiceProvider serviceProvider) {
});
config.setXmlConfig(xmlConfig);

Constructor constructor = new Constructor(new LoaderOptions());
CustomRepresenter representer = new CustomRepresenter(new DumperOptions());

for (Class<?> protocol : INBOUND_CONFIG_PROTOCOLS) {
TypeDescription description = new TypeDescription(InboundConfigurationProtocol.class);
description.addPropertyParameters("type", protocol);
constructor.addTypeDescription(description);
}
YamlConfig yamlConfig = new YamlConfig();
yamlConfig.setConstructorCustomizer(constructor -> {
for (Class<?> protocol : INBOUND_CONFIG_PROTOCOLS) {
TypeDescription description = new TypeDescription(InboundConfigurationProtocol.class);
description.addPropertyParameters("type", protocol);
constructor.addTypeDescription(description);
}
});
yamlConfig.setRepresenterFactory(CustomRepresenter::new);
config.setYamlConfig(yamlConfig);

Yaml yaml = new Yaml(constructor, representer);
try {
return yaml.dump(serviceProvider);
} catch (YAMLException e) {
throw Utils.buildServerError("Error exporting application from YAML file.", e);
}
}
JsonConfig jsonConfig = new JsonConfig();
jsonConfig.setSubtypes(INBOUND_CONFIG_PROTOCOLS);
config.setJsonConfig(jsonConfig);

private String parseJsonFromServiceProvider(ServiceProvider serviceProvider) {
config.setSerializeDefault(FileSerializationConfig.DefaultFormat.ERROR);
config.setDeserializeDefault(FileSerializationConfig.DefaultFormat.XML);

ObjectMapper objectMapper = new ObjectMapper(new JsonFactory());
objectMapper.registerSubtypes(INBOUND_CONFIG_PROTOCOLS);
try {
return objectMapper.writeValueAsString(serviceProvider);
} catch (JsonProcessingException e) {
throw Utils.buildServerError("Error exporting application from JSON file.", e);
}
return config;
}

/**
Expand Down Expand Up @@ -758,74 +730,42 @@ private ServiceProvider parseSP(SpFileContent spFileContent, String fileType, St
spFileContent.getFileName(), tenantDomain));
}

if (containsValidMediaType(fileType, VALID_MEDIA_TYPES_XML)) {
return parseServiceProviderFromXml(spFileContent, tenantDomain);
} else if (containsValidMediaType(fileType, VALID_MEDIA_TYPES_YAML)) {
return parseServiceProviderFromYaml(spFileContent, tenantDomain);
} else if (containsValidMediaType(fileType, VALID_MEDIA_TYPES_JSON)) {
return parseServiceProviderFromJson(spFileContent, tenantDomain);
} else {
log.warn("Unsupported file type " + fileType + " for file " + spFileContent.getFileName() + " . " +
"Defaulting to XML parsing");
return parseServiceProviderFromXml(spFileContent, tenantDomain);
}
}

private boolean containsValidMediaType(String fileType, String[] mediaTypes) {

for (String mediaType : mediaTypes) {
if (fileType.contains(mediaType)) {
return true;
}
}
return false;
}

private ServiceProvider parseServiceProviderFromXml(SpFileContent spFileContent, String tenantDomain)
throws IdentityApplicationManagementException {

try {
JAXBContext jaxbContext = JAXBContext.newInstance(INBOUND_CONFIG_PROTOCOLS);
Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
return (ServiceProvider) unmarshaller.unmarshal(new StringReader(spFileContent.getContent()));
} catch (JAXBException e) {
throw new IdentityApplicationManagementException(String.format("Error in reading XML Service Provider " +
FileContent fileContent =
new FileContent(
spFileContent.getFileName(),
fileType,
spFileContent.getContent()
);

return FileSerializationUtil.deserialize(
fileContent,
ServiceProvider.class,
buildDeserializationConfig()
);
} catch (FileSerializationException e) {
throw new IdentityApplicationManagementException(String.format("Error in reading Service Provider " +
"configuration file %s uploaded by tenant: %s", spFileContent.getFileName(), tenantDomain), e);
}
}

private ServiceProvider parseServiceProviderFromYaml(SpFileContent spFileContent, String tenantDomain)
throws IdentityApplicationManagementException {
private FileSerializationConfig buildDeserializationConfig() {

try {
// Add trusted tags included in the SP YAML file.
List<String> trustedTagList = new ArrayList<>();
trustedTagList.add(ServiceProvider.class.getName());
trustedTagList.add(OAuthAppDO.class.getName());
trustedTagList.add(SAMLSSOServiceProviderDTO.class.getName());

LoaderOptions loaderOptions = new LoaderOptions();
TagInspector tagInspector = new TrustedPrefixesTagInspector(trustedTagList);
loaderOptions.setTagInspector(tagInspector);
Yaml yaml = new Yaml(new Constructor(ServiceProvider.class, loaderOptions));
return yaml.loadAs(spFileContent.getContent(), ServiceProvider.class);
} catch (YAMLException e) {
throw new IdentityApplicationManagementException(String.format("Error in reading YAML Service Provider " +
"configuration file %s uploaded by tenant: %s", spFileContent.getFileName(), tenantDomain), e);
}
}
FileSerializationConfig config = new FileSerializationConfig();

private ServiceProvider parseServiceProviderFromJson(SpFileContent spFileContent, String tenantDomain)
throws IdentityApplicationManagementException {
XmlConfig xmlConfig = new XmlConfig();
xmlConfig.setAdditionalJaxbClasses(ADDITIONAL_INBOUND_CONFIG_PROTOCOLS);
config.setXmlConfig(xmlConfig);

try {
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.registerSubtypes(INBOUND_CONFIG_PROTOCOLS);
return objectMapper.readValue(spFileContent.getContent(), ServiceProvider.class);
} catch (JsonProcessingException e) {
throw new IdentityApplicationManagementException(String.format("Error in reading JSON Service Provider " +
"configuration file %s uploaded by tenant: %s", spFileContent.getFileName(), tenantDomain), e);
}
YamlConfig yamlConfig = new YamlConfig();
yamlConfig.setAdditionalTrustedClasses(OAuthAppDO.class, SAMLSSOServiceProviderDTO.class);
config.setYamlConfig(yamlConfig);

JsonConfig jsonConfig = new JsonConfig();
jsonConfig.setSubtypes(INBOUND_CONFIG_PROTOCOLS);
config.setJsonConfig(jsonConfig);

return config;
}

private SpFileContent buildSpFileContent(InputStream fileInputStream, Attachment fileDetail) throws IOException {
Expand Down
Loading