[Business Logic] DataOutputStream leads to java.security.AccessControlException

Hi,

when using DataOutputStream to access Apple server for IAP receipt verification in Custom Business Logic, running on CodeRunner leads to “java.security.AccessControlException: You have no permission to thread manipulation”.

Is there any workaround for this? Or am I missing something?

Java code is as below:
Thank you.


public class IAPVerifier {
    private static final String kReceiptValidationKey = "receipt-data";          // JSON key expected by Apple receipt validation server
    private static final String kValidationResponseStatusKey = "status";          // key in JSON response that indicates whether receipt is valid


    private static final String validationServerURL = "https://sandbox.itunes.apple.com/verifyReceipt";        // Sandbox Server
    // private static final String validationServerURL = "https://buy.itunes.apple.com/verifyReceipt";         // Production Server


    public static boolean verify(String receipt, String bundle_id, String product_id) throws Exception {


         JSONObject validationBodyJSON = new JSONObject();


        try {
            validationBodyJSON.put(kReceiptValidationKey, receipt);
        } catch (JSONException e) {
            return false;
        }


        String validationBodyString = validationBodyJSON.toString();
        System.out.println("validating receipt");
        HttpURLConnection con = null;
        try {
            URL obj = new URL(validationServerURL);


            con = (HttpURLConnection) obj.openConnection();
            con.setRequestMethod("POST");
            con.setRequestProperty("Content-Type", "application/json");
            con.setDoOutput(true);
            con.setDoOutput(true);
            DataOutputStream wr = new DataOutputStream(con.getOutputStream());
            wr.writeBytes(validationBodyString);


            //add request header


            int responseCode = con.getResponseCode();


            BufferedReader in = new BufferedReader(
                    new InputStreamReader(con.getInputStream()));
            String inputLine;
            StringBuffer response = new StringBuffer();


            while ((inputLine = in.readLine()) != null) {
                response.append(inputLine);
            }
            in.close();


            JSONObject serverResponseJSON;


            try {
                serverResponseJSON = new JSONObject(response.toString());
            } catch (JSONException e) {
                return false;
            }




            int validationStatus = -1;
            try {
                if (!serverResponseJSON.isNull(kValidationResponseStatusKey)) {
                    validationStatus = serverResponseJSON.getInt(kValidationResponseStatusKey);
                }
            } catch (JSONException e) {
                return false;
            }


            // Take action based on receipt validation
            if (validationStatus == 0) {
                if (serverResponseJSON.has("bundle_id")) {
                    if (!serverResponseJSON.getString("bundle_id").equals(bundle_id)) {
                        return false;
                    }
                } else {
                    return false;
                }
                if (serverResponseJSON.has("in_app")) {
                    JSONArray inAppArray = serverResponseJSON.getJSONArray("in_app");
                    boolean res = false;
                    for (int i = 0; i < inAppArray.length(); ++i) {
                        JSONObject object = inAppArray.getJSONObject(i);
                        if (object.has("product_id")) {
                            if (object.getString("product_id").equals(product_id)) {
                                res = true;
                                break;
                            }
                        }
                    }
                    return res;
                } else {
                    return false;
                }


            } else {


                return false;
            }
        }catch (MalformedURLException e){
            throw  e;
        }catch (ProtocolException e){
            throw e;
        }catch (IOException e){
            throw e;
        }finally {
            if(con != null) {
                con.disconnect();
            }
        }
    }


}

Hi, Shunpei,
Could you please also provide the full error stacktrace?

Hi Sergey,

thank you for response.
Stacktrace is below:









javax.net.ssl.SSLException: java.security.AccessControlException: You have no permission to thread manipulation

	at sun.security.ssl.Alerts.getSSLException(Alerts.java:208)

	at sun.security.ssl.SSLSocketImpl.fatal(SSLSocketImpl.java:1937)

	at sun.security.ssl.SSLSocketImpl.fatal(SSLSocketImpl.java:1894)

	at sun.security.ssl.SSLSocketImpl.handleException(SSLSocketImpl.java:1877)

	at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1398)

	at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1375)

	at sun.net.www.protocol.https.HttpsClient.afterConnect(HttpsClient.java:563)

	at sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect(AbstractDelegateHttpsURLConnection.java:185)

	at sun.net.www.protocol.http.HttpURLConnection.getOutputStream0(HttpURLConnection.java:1282)

	at sun.net.www.protocol.http.HttpURLConnection.getOutputStream(HttpURLConnection.java:1257)

	at sun.net.www.protocol.https.HttpsURLConnectionImpl.getOutputStream(HttpsURLConnectionImpl.java:250)

	at com.backendless.mizica.misc.IAPVerifier.verify(IAPVerifier.java:52)

	at com.backendless.mizica.events.persistence_service.UserStampTableEventHandler.beforeCreate(UserStampTableEventHandler.java:23)

	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)

	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)

	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)

	at java.lang.reflect.Method.invoke(Method.java:483)

	at com.backendless.coderunner.runtime.InvocationTask.runImpl(InvocationTask.java:95)

	at com.backendless.coderunner.runtime.concurrent.ExtendedRunnable.run(ExtendedRunnable.java:22)

	at java.lang.Thread.run(Thread.java:745)

Caused by: java.security.AccessControlException: You have no permission to thread manipulation

	at com.backendless.coderunner.runtime.security.CodeRunnerSecurityManager.getThreadGroup(CodeRunnerSecurityManager.java:36)

	at java.lang.Thread.init(Thread.java:379)

	at java.lang.Thread.init(Thread.java:349)

	at java.lang.Thread.&lt;init&gt;(Thread.java:508)

	at sun.security.ssl.SSLSocketImpl$NotifyHandshakeThread.&lt;init&gt;(SSLSocketImpl.java:2596)

	at sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:1082)

	at sun.security.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1363)

	at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1391)

	... 15 more

Does Apple allow you to connect through http instead of https?

The reason you get this exception is that HTTPUrlConnection implicitly creates a new thread while authorizing SSL and we currently do not allow creating new threads. Using http instead of https might solve your problem.

Hi,

thanks for the solution, but I tested it with http and get 503, so Apple does not allow HTTP for the endpoint, though HTTPUrlConnection works fine this time.
So there are no workaround to access external host with SSL from CBL?

I guess I should use our own external server, or search some library that does not create new thread to verify SSL…

Thank you

Hi Shunpei,

We will be making changes to allow an additional thread in CBL.

Regards,
Mark

Hi Mark,

Thank you for the decision to make changes about threading!
I’m looking forward to the deployment.

Hey guys any update on this? I am trying to use the Unirest library to make some REST calls, which will require HTTPS though I am currently only using HTTP, to another server after a user registers and I get the same “You have no permission to thread manipulation” error.

It doea appear that Unirest is using Apache’s httpclient. If there is no quick solution to this do you guys know any other way I could make some fairly simply POST commands to interact with REST APIs from the business logic?

Thanks!

There will be an update soon, which will allow to increase the number of threads through the Marketplace. We can’t provide an exact date of an update yet. though.