Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -95,4 +95,25 @@ public static Properties reloadSynapseProperties() {
public static String getPropertyValue(String key, String defaultValue) {
return MiscellaneousUtil.getProperty(loadSynapseProperties(), key, defaultValue);
}

/**
* Get the boolean value of the property from the synapse properties.
*
* @param name name of the config property
* @param def default value to return if the property is not set
* @return the value of the property to be used
*/
public static Boolean getBooleanProperty(String name, Boolean def) {
String val = MiscellaneousUtil.getProperty(loadSynapseProperties(), name, String.valueOf(def));
if (val == null) {
if (log.isDebugEnabled()) {
log.debug("Parameter : " + name + " is not defined in the synapse.properties file.");
}
return def;
}
if (log.isDebugEnabled()) {
log.debug("synapse.properties parameter : " + name + " = " + val);
}
return Boolean.valueOf(val);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,12 @@
package org.apache.synapse.mediators.builtin;

import org.apache.synapse.MessageContext;
import org.apache.synapse.SynapseConstants;
import org.apache.synapse.SynapseLog;
import org.apache.synapse.core.axis2.Axis2Sender;
import org.apache.synapse.mediators.AbstractMediator;
import org.apache.synapse.rest.cors.CORSHelper;
import org.apache.synapse.rest.cors.SynapseCORSConfiguration;

/**
* Halts further processing/mediation of the current message and return the current message back to client
Expand Down Expand Up @@ -51,6 +54,14 @@ public boolean mediate(MessageContext synCtx) {

synCtx.setTo(null);
synCtx.setResponse(true);

// if this is not a response from a proxy service
String proxyName = (String) synCtx.getProperty(SynapseConstants.PROXY_SERVICE);
if (proxyName == null || proxyName.isEmpty()) {
// Add CORS headers for API response
CORSHelper.handleCORSHeadersForResponse(SynapseCORSConfiguration.getInstance(), synCtx);
}

Axis2Sender.sendBack(synCtx);

if (isTraceOrDebugEnabled) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,4 +53,33 @@ public static enum METHODS {
/** The Synapse MC property that marks if the message was denied on the accessed transport */
public static final String REST_API_TRANSPORT_DENIED = "REST_API_TRANSPORT_DENIED";

public static final String CORS_HEADER_ACCESS_CTL_ALLOW_ORIGIN = "Access-Control-Allow-Origin";
public static final String CORS_HEADER_ACCESS_CTL_ALLOW_METHODS = "Access-Control-Allow-Methods";
public static final String CORS_HEADER_ACCESS_CTL_ALLOW_HEADERS = "Access-Control-Allow-Headers";
public static final String CORS_HEADER_ORIGIN = "Origin";
/**
* CORS related configuration in synapse.properties
*/
// enable/disable CORS support
public static final String CORS_CONFIGURATION_ENABLED = "synapse.rest.CORSConfig.enabled";
// List of allowed origins (comma separated)
public static final String CORS_CONFIGURATION_ACCESS_CTL_ALLOW_ORIGIN =
"synapse.rest.CORSConfig.Access-Control-Allow-Origin";
// List of allowed headers (comma separated)
public static final String CORS_CONFIGURATION_ACCESS_CTL_ALLOW_HEADERS =
"synapse.rest.CORSConfig.Access-Control-Allow-Headers";

/**
* Constant prefix for rest related internal properties
*/
public static final String _SYNAPSE_INTERNAL_ = "_SYNAPSE_INTERNAL_REST_";

public static final String INTERNAL_CORS_HEADER_ACCESS_CTL_ALLOW_ORIGIN =
_SYNAPSE_INTERNAL_ + "Access-Control-Allow-Origin";
public static final String INTERNAL_CORS_HEADER_ACCESS_CTL_ALLOW_METHODS =
_SYNAPSE_INTERNAL_+ "Access-Control-Allow-Methods";
public static final String INTERNAL_CORS_HEADER_ACCESS_CTL_ALLOW_HEADERS =
_SYNAPSE_INTERNAL_+ "Access-Control-Allow-Headers";
public static final String INTERNAL_CORS_HEADER_ORIGIN = _SYNAPSE_INTERNAL_+ "Origin";

}
13 changes: 13 additions & 0 deletions modules/core/src/main/java/org/apache/synapse/rest/Resource.java
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@
import org.apache.synapse.core.axis2.Axis2Sender;
import org.apache.synapse.mediators.MediatorFaultHandler;
import org.apache.synapse.mediators.base.SequenceMediator;
import org.apache.synapse.rest.cors.CORSHelper;
import org.apache.synapse.rest.cors.SynapseCORSConfiguration;
import org.apache.synapse.rest.dispatch.DispatcherHelper;
import org.apache.synapse.transport.nhttp.NhttpConstants;

Expand Down Expand Up @@ -268,6 +270,10 @@ void process(MessageContext synCtx) {
String method = (String) synCtx.getProperty(RESTConstants.REST_METHOD);
if (RESTConstants.METHOD_OPTIONS.equals(method) && sendOptions(synCtx)) {
return;
} else {
// Handle CORS for other HTTP Methods
CORSHelper.handleCORSHeaders(SynapseCORSConfiguration.getInstance(),
synCtx, getSupportedMethods(), false);
}

synCtx.setProperty(RESTConstants.SYNAPSE_RESOURCE, name);
Expand All @@ -291,6 +297,9 @@ void process(MessageContext synCtx) {
}
}
}
} else {
// Add CORS headers for response message
CORSHelper.handleCORSHeadersForResponse(SynapseCORSConfiguration.getInstance(), synCtx);
}

SequenceMediator sequence = synCtx.isResponse() ? outSequence : inSequence;
Expand Down Expand Up @@ -357,6 +366,8 @@ private boolean sendOptions(MessageContext synCtx) {
synCtx.setResponse(true);
synCtx.setTo(null);
transportHeaders.put(HttpHeaders.ALLOW, getSupportedMethods());
CORSHelper.handleCORSHeaders(SynapseCORSConfiguration.getInstance(),
synCtx, getSupportedMethods(),true);
Axis2Sender.sendBack(synCtx);
return true;
} else {
Expand All @@ -370,6 +381,8 @@ private boolean sendOptions(MessageContext synCtx) {
synCtx.setResponse(true);
synCtx.setTo(null);
transportHeaders.put(HttpHeaders.ALLOW, getSupportedMethods());
CORSHelper.handleCORSHeaders(SynapseCORSConfiguration.getInstance(),
synCtx, getSupportedMethods(), true);
Axis2Sender.sendBack(synCtx);
return true;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

package org.apache.synapse.rest.cors;

import java.util.Set;

/**
* {@code CORSConfiguration} is the interface that need to be implemented in order hold the CORS configuration information
*/
public interface CORSConfiguration {

/**
* Returns allowed origins in the configuration.
*
* @return allowed origins
*/
Set<String> getAllowedOrigins();

/**
* Returns allowed headers in the configuration.
*
* @return allowed headers
*/
String getAllowedHeaders();

/**
* Returns if CORS is enabled.
*
* @return boolean enabled
*/
boolean isEnabled();

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

package org.apache.synapse.rest.cors;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.http.HttpStatus;
import org.apache.synapse.MessageContext;
import org.apache.synapse.core.axis2.Axis2MessageContext;
import org.apache.synapse.rest.RESTConstants;
import org.apache.synapse.transport.passthru.PassThroughConstants;

import java.util.Map;
import java.util.Set;

/**
* This class provides util functions for all CORS related activities.
*/
public class CORSHelper {

private static final Log log = LogFactory.getLog(CORSHelper.class);

/**
* Function to retrieve allowed origin header string
*
* @param origin Received origin
* @param allowedOrigins allowed origin set
* @return
*/
public static String getAllowedOrigins(String origin, Set<String> allowedOrigins) {

if (allowedOrigins.contains("*")) {
return "*";
} else if (allowedOrigins.contains(origin)) {
return origin;
} else {
return "";
}
}

/**
* Functions to handle CORS Headers
*
* @param synCtx Synapse message context
* @param corsConfiguration of the API
* @param supportedMethods
* @param updateHeaders Boolean
*/
public static void handleCORSHeaders(CORSConfiguration corsConfiguration, MessageContext synCtx,
String supportedMethods, boolean updateHeaders) {

if (corsConfiguration.isEnabled()) {
org.apache.axis2.context.MessageContext msgCtx = ((Axis2MessageContext) synCtx).getAxis2MessageContext();
Map<String, String> transportHeaders = (Map<String, String>) msgCtx.getProperty(
org.apache.axis2.context.MessageContext.TRANSPORT_HEADERS);
if (transportHeaders != null) {
String allowedOrigin = getAllowedOrigins(transportHeaders.get(RESTConstants.CORS_HEADER_ORIGIN),
corsConfiguration.getAllowedOrigins());
if (updateHeaders) {
transportHeaders.put(RESTConstants.CORS_HEADER_ACCESS_CTL_ALLOW_METHODS, supportedMethods);
transportHeaders.put(RESTConstants.CORS_HEADER_ACCESS_CTL_ALLOW_ORIGIN, allowedOrigin);
transportHeaders.put(RESTConstants.CORS_HEADER_ACCESS_CTL_ALLOW_HEADERS,
corsConfiguration.getAllowedHeaders());
}

synCtx.setProperty(RESTConstants.INTERNAL_CORS_HEADER_ACCESS_CTL_ALLOW_METHODS, supportedMethods);
synCtx.setProperty(RESTConstants.INTERNAL_CORS_HEADER_ACCESS_CTL_ALLOW_ORIGIN, allowedOrigin);
synCtx.setProperty(RESTConstants.INTERNAL_CORS_HEADER_ACCESS_CTL_ALLOW_HEADERS,
corsConfiguration.getAllowedHeaders());
synCtx.setProperty(RESTConstants.INTERNAL_CORS_HEADER_ORIGIN,
transportHeaders.get(RESTConstants.CORS_HEADER_ORIGIN));

// If the request origin is not allowed, set the status code to 403
if (isOptionsRequest(synCtx) && allowedOrigin.isEmpty()) {
((Axis2MessageContext) synCtx).getAxis2MessageContext()
.setProperty(PassThroughConstants.HTTP_SC, HttpStatus.SC_FORBIDDEN);
}
}
}

}

/**
* Function to set CORS headers to response message transport headers extracting from synapse message context
*
* @param synCtx
* @param corsConfiguration of the API
*/
public static void handleCORSHeadersForResponse(CORSConfiguration corsConfiguration, MessageContext synCtx) {

if (corsConfiguration.isEnabled()) {
org.apache.axis2.context.MessageContext msgCtx = ((Axis2MessageContext) synCtx).getAxis2MessageContext();
Map<String, String> transportHeaders = (Map<String, String>) msgCtx.getProperty(
org.apache.axis2.context.MessageContext.TRANSPORT_HEADERS);
if (transportHeaders != null) {
if (synCtx.getProperty(RESTConstants.INTERNAL_CORS_HEADER_ACCESS_CTL_ALLOW_METHODS) != null) {
transportHeaders.put(RESTConstants.CORS_HEADER_ACCESS_CTL_ALLOW_METHODS,
(String) synCtx.getProperty(RESTConstants.INTERNAL_CORS_HEADER_ACCESS_CTL_ALLOW_METHODS));
}

if (synCtx.getProperty(RESTConstants.INTERNAL_CORS_HEADER_ACCESS_CTL_ALLOW_ORIGIN) != null) {
transportHeaders.put(RESTConstants.CORS_HEADER_ACCESS_CTL_ALLOW_ORIGIN,
(String) synCtx.getProperty(RESTConstants.INTERNAL_CORS_HEADER_ACCESS_CTL_ALLOW_ORIGIN));
}

if (synCtx.getProperty(RESTConstants.INTERNAL_CORS_HEADER_ACCESS_CTL_ALLOW_HEADERS) != null) {
transportHeaders.put(RESTConstants.CORS_HEADER_ACCESS_CTL_ALLOW_HEADERS,
(String) synCtx.getProperty(RESTConstants.INTERNAL_CORS_HEADER_ACCESS_CTL_ALLOW_HEADERS));
}

if (synCtx.getProperty(RESTConstants.INTERNAL_CORS_HEADER_ORIGIN) != null) {
transportHeaders.put(RESTConstants.CORS_HEADER_ORIGIN,
(String) synCtx.getProperty(RESTConstants.INTERNAL_CORS_HEADER_ORIGIN));
}
}
}
}

private static boolean isOptionsRequest(MessageContext synCtx) {

String method = (String) synCtx.getProperty(RESTConstants.REST_METHOD);
return RESTConstants.METHOD_OPTIONS.equals(method);
}
}
Loading
Loading