Java Callout Security Exception when using Jackson or Gson to parse JSON

Not applicable

When we are executing custom jar file in apigee getting following exception,

Exception Occured :java.security.AccessControlException: access denied ("java.lang.RuntimePermission" "accessDeclaredMembers")

Note:All variables and methods inside the class declared as public.

0 13 1,595
13 REPLIES 13

Yes. You will need to examine the stack trace more closely. The exception may not refer to your class; it may refer to a different class. In fact the permission violation may be due to some logic in a 3rd-party class that your class uses. If the 3rd-party class uses reflection, or other prohibited Java operations, you will see a similar security exception.

Look closely at the stack trace. You may get a better clue.

We are only using following two maven dependency in java callout jar,

  1. <dependency>

<groupId>com.auth0</groupId>

<artifactId>java-jwt</artifactId>

<version>3.3.0</version>

</dependency>

2.<dependency>

<groupId>com.google.code.gson</groupId>

<artifactId>gson</artifactId>

<version>2.8.2</version>

</dependency>

How to view full Exception log in apigee for java callout errors?

Your Java code needs to catch the exception, generate the stacktrace, and then store it into a context variable.

For example:

public ExecutionResult execute(MessageContext msgCtxt, ExecutionContext exeCtxt) {
  ExecutionResult calloutResult = ExecutionResult.ABORT;
  ..
  try {
      // do the thing
      calloutResult = ExecutionResult.SUCCESS;
  }
  catch (Exception e) {
      String stacktrace = ExceptionUtils.getStackTrace(e);
      msgCtxt.setVariable(varName("stacktrace"), stacktrace);
      String error = e.toString();
      msgCtxt.setVariable(varName("exception"), error);
  }
  return calloutResult;
}

Here is some working example code.

Hi Currently

I able to get Exception logs in my policy response.Following sample of code used for converting java object to JSON string using Gson external library jar(This external jar included my java callout jar).

import com.apigee.flow.execution.Action;
import com.apigee.flow.execution.ExecutionContext;
import com.apigee.flow.execution.ExecutionResult;
import com.apigee.flow.execution.spi.Execution;
import com.apigee.flow.message.MessageContext;
import com.google.gson.Gson;
import com.dnb.apigee.TestPOJO;
import org.apache.commons.lang.exception.ExceptionUtils;


public class MainClassTest implements Execution{


	public ExecutionResult execute(MessageContext messageContext, ExecutionContext arg1) {
		
		try{
		Gson gson = new Gson();
		TestPOJO testPOJO = new TestPOJO();
		testPOJO.setName("AAA");
		testPOJO.setCompany("BBBB");
	 	String finalValue= gson.toJson(testPOJO);
	    messageContext.setVariable("Final Respopnse  :" ,finalValue);
		}catch(RuntimeException  ex){
			 ExecutionResult executionResult = new ExecutionResult(false, Action.ABORT);
	            executionResult.setErrorResponse(ex.getMessage());
	            executionResult.addErrorResponseHeader("ExceptionClass", ex.getClass().getName());
	            messageContext.setVariable("JAVA_ERROR", ex.getMessage());
	            messageContext.setVariable("JAVA_STACKTRACE", ExceptionUtils.getStackTrace(ex));
	            return executionResult;
		}
		return ExecutionResult.SUCCESS;  
	}
}


					

I am not able to call external library jar methods getting the following exception,

java.security.AccessControlException: access denied ("java.lang.reflect.ReflectPermission" "suppressAccessChecks") at java.security.AccessControlContext.checkPermission(AccessControlContext.java:472) at
java.security.AccessController.checkPermission(AccessController.java:884) at java.lang.SecurityManager.checkPermission(SecurityManager.java:549) at
com.apigee.securitypolicy.InternalSecurityManager.checkPermission(InternalSecurityManager.java:84) at java.lang.reflect.AccessibleObject.setAccessible(AccessibleObject.java:128) at
com.google.gson.internal.ConstructorConstructor.newDefaultConstructor(ConstructorConstructor.java:101) at com.google.gson.internal.ConstructorConstructor.get(ConstructorConstructor.java:83) at
com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.create(ReflectiveTypeAdapterFactory.java:99) at com.google.gson.Gson.getDelegateAdapter(Gson.java:506) at
com.google.gson.internal.bind.TreeTypeAdapter.delegate(TreeTypeAdapter.java:89) at com.google.gson.internal.bind.TreeTypeAdapter.write(TreeTypeAdapter.java:74) at com.google.gson.Gson.toJson(Gson.java:669) at
com.google.gson.Gson.toJson(Gson.java:648) at com.google.gson.Gson.toJson(Gson.java:603) at com.google.gson.Gson.toJson(Gson.java:583) at com.dnb.apigee.MainClassTest.execute(MainClassTest.java:47) at
com.apigee.steps.javacallout.JavaCalloutStepDefinition$ClassLoadWrappedExecution.execute(JavaCalloutStepDefinition.java:202) at
com.apigee.steps.javacallout.JavaCalloutStepDefinition$SecurityWrappedExecution$1.run(JavaCalloutStepDefinition.java:268) at
com.apigee.steps.javacallout.JavaCalloutStepDefinition$SecurityWrappedExecution$1.run(JavaCalloutStepDefinition.java:266) at java.security.AccessController.doPrivileged(Native Method) at
com.apigee.steps.javacallout.JavaCalloutStepDefinition$SecurityWrappedExecution.execute(JavaCalloutStepDefinition.java:266) at
com.apigee.steps.javacallout.JavaCalloutStepDefinition$CallOutWrapper.execute(JavaCalloutStepDefinition.java:138) at com.apigee.messaging.runtime.steps.StepExecution.execute(StepExecution.java:146) at
com.apigee.flow.execution.AbstractAsyncExecutionStrategy$AsyncExecutionTask.call(AbstractAsyncExecutionStrategy.java:74) at
com.apigee.flow.execution.AbstractAsyncExecutionStrategy$AsyncExecutionTask.call(AbstractAsyncExecutionStrategy.java:45) at java.util.concurrent.FutureTask.run(FutureTask.java:266) at
java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511) at java.util.concurrent.FutureTask.run(FutureTask.java:266) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) at
java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) at java.lang.Thread.run(Thread.java:748)

Yes, Reflection is disallowed in Java callouts in Apigee Edge.

You can't do what you're trying to do.

You can serialize and de-serialize a Map.

Map<String,Object> works just fine. It does not require reflection. It will work with Gson or Jackson.

Serializing and deserializing a custom POJO will not work, with either library.

To use an existing POJO, you will need to implement adapters for the Map. For example, to deserialize, deserialize into a Map then create a custom constructor on the POJO that initializes the instance via the Map. And also do the converse for serialization: create a Map first then serialize THAT.

Is there a working example for Map serialization/deserialization? I am still getting the same security exception with with a straight Map not involving a POJO

ObjectMapper om = new ObjectMapper();
TypeFactory typeFactory = om.getTypeFactory();
Map<String,Object> map = om.readValue("\"foo\": \"bar\"", HashMap.class);
msgCtxt.setVariable(varName("output"), map);

Are you sure you're getting the same exception?

That String is not valid JSON. You are missing opening and closing curlies. So maybe you are getting an "invalid json" exception.

Shrinivas, this USED TO WORK. But it no longer works. See my answer below for an explanation and a repo that shows a possible workaround.

Thanks Dino, unfortunately I don't think that will work for my particular use case of converting to/from POJOs. The only workaround I have found so far for Jackson library is to use custom serializer/deserializer and hand build the JSON/populating the POJO. Quite unfortunate

I agree, that IS quite unfortunate.

I've looked into this and it seems to me that now, Apigee prohibits the use of both Jackson or Gson for JSON parsing. That;s a shame. Both of those libraries use reflection, and if you try to use them, you get a stacktrace like this:

java.security.AccessControlException: access denied ("java.lang.RuntimePermission" "accessDeclaredMembers")
 at java.security.AccessControlContext.checkPermission(AccessControlContext.java:472)
 at java.security.AccessController.checkPermission(AccessController.java:884)
 at java.lang.SecurityManager.checkPermission(SecurityManager.java:549)
 at com.apigee.securitypolicy.InternalSecurityManager.checkPermission(InternalSecurityManager.java:85)
 at java.lang.Class.checkMemberAccess(Class.java:2348)
 at java.lang.Class.getDeclaredConstructor(Class.java:2177)
 at com.google.gson.internal.ConstructorConstructor.newDefaultConstructor(ConstructorConstructor.java:101)
 at com.google.gson.internal.ConstructorConstructor.get(ConstructorConstructor.java:85)
 at com.google.gson.internal.bind.MapTypeAdapterFactory.create(MapTypeAdapterFactory.java:127)
 at com.google.gson.Gson.getAdapter(Gson.java:458)
 at com.google.gson.Gson.fromJson(Gson.java:931)
 at com.google.gson.Gson.fromJson(Gson.java:870)
 at com.google.apigee.edgecallouts.GsonTest.execute(GsonTest.java:37)
 at com.apigee.steps.javacallout.JavaCalloutStepDefinition$ClassLoadWrappedExecution.execute(JavaCalloutStepDefinition.java:204)
 at com.apigee.steps.javacallout.JavaCalloutStepDefinition$SecurityWrappedExecution$1.run(JavaCalloutStepDefinition.java:271)
 at com.apigee.steps.javacallout.JavaCalloutStepDefinition$SecurityWrappedExecution$1.run(JavaCalloutStepDefinition.java:269)
 at java.security.AccessController.doPrivileged(Native Method)
 at com.apigee.steps.javacallout.JavaCalloutStepDefinition$SecurityWrappedExecution.execute(JavaCalloutStepDefinition.java:269)
 at com.apigee.steps.javacallout.JavaCalloutStepDefinition$CallOutWrapper.execute(JavaCalloutStepDefinition.java:138)
 at com.apigee.messaging.runtime.steps.StepExecution.execute(StepExecution.java:156)
 at com.apigee.flow.execution.AbstractAsyncExecutionStrategy$AsyncExecutionTask.call(AbstractAsyncExecutionStrategy.java:74)
 at com.apigee.flow.execution.AbstractAsyncExecutionStrategy$AsyncExecutionTask.call(AbstractAsyncExecutionStrategy.java:45)
 at com.apigee.threadpool.CallableWrapperForMDCPreservation.call(CallableWrapperForMDCPreservation.java:26)
 at java.util.concurrent.FutureTask.run(FutureTask.java:266)
 at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
 at java.util.concurrent.FutureTask.run(FutureTask.java:266)
 at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
 at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
 at java.lang.Thread.run(Thread.java:748)

There is a workaround, which may work for some basic cases. Use the javax.json parser.

Here's a repo that shows how to do it.

Dino-at-Google I used the GSON approach for your GitHub repo to convert my JSON string to Map. But still, I am getting a security violation exception. Below is the GitHub link I used.

https://github.com/DinoChiesa/ApigeeEdge-Java-Json-Parse/blob/master/callout/src/main/java/com/googl...

Below is the sample code I used from your example.

StringReader reader = <strong>new</strong> StringReader(serviceAccountJson);<br>Map<String, Object> serviceAccountInfo = <strong><em>gson</em></strong>.fromJson(reader, Map.<strong>class</strong>);

Yes. As I stated: I've looked into this and it seems to me that now, Apigee prohibits the use of both Jackson or Gson for JSON parsing.

You must use the javax.json parser.

Read my post above a little more carefully. GSON will fail with the security violation. That is expected.