5

I downloaded my service account credential json file from Firebase console placed it earlier in the main directory of GAE endpoint project when I run my backed locally it gives Security exception.

java.security.AccessControlException: access denied ("java.io.FilePermission" "\src\main\secret.json" "read")

I tried placing the .json file under the src directory also but no help.

tharindu_DG
  • 8,900
  • 6
  • 52
  • 64
Manish Patiyal
  • 4,427
  • 5
  • 21
  • 35
  • I am having the exact same issue right now. I've tried placing it in my drive and accessing like this: `.setServiceAccount(new URL("https://drive.google.com/file/...").openStream())` but I get access denied despite no permissions preventing it. I'd be happy to see what people come up with – booky99 Jun 19 '16 at 00:46
  • I will post the solution I found tonight. I have a couple work arounds for you. Give me about 2 hours. – booky99 Jun 20 '16 at 20:30

3 Answers3

0

You should place the json file in src/main/resources

Mithun
  • 7,747
  • 6
  • 52
  • 68
0

I found a couple ways to approach this. First is by getting it from a file over an internet stream. The other is locally.

INTERNET WAY

My first method involved storing the file on my public dropbox folder. I got the shareable link (make sure it ends in .json) and pasted it in the string example "https://dl.dropboxusercontent.com/..EXAMPLE-CREDENTIALS"

/** A simple endpoint method that takes a name and says Hi back */
    @ApiMethod(name = "sayHi")
    public MyBean sayHi(@Named("name") String name) {

        MyBean mModelClassObject = null;

        String text = "";

        try {
            String line = "";
            StringBuilder builder = new StringBuilder();
            URL url = new URL("https://dl.dropboxusercontent.com/..EXAMPLE-CREDENTIALS");
            BufferedReader reader = new BufferedReader(new InputStreamReader(url.openStream()));


            while ((line = reader.readLine()) != null) {
                // ...
                builder.append(line);
            }
            reader.close();

            text = builder.toString();
        } catch (MalformedURLException e) {
            // ...
        } catch (IOException e) {
            // ...
        }

        InputStream stream = new ByteArrayInputStream(text.getBytes(StandardCharsets.UTF_8));

        FirebaseOptions options = null;
        options = new FirebaseOptions.Builder()
                .setServiceAccount(stream)
                .setDatabaseUrl("https://[PROJECT-ID].firebaseio.com/")
                .build();
        FirebaseApp.initializeApp(options);

        DatabaseReference ref = FirebaseDatabase.getInstance().getReference();
        final TaskCompletionSource<MyBean> tcs = new TaskCompletionSource<>();


        Task<MyBean> tcsTask = tcs.getTask();

        ref.addListenerForSingleValueEvent(new ValueEventListener() {
            @Override
            public void onDataChange(DataSnapshot dataSnapshot) {
                MyBean result = dataSnapshot.getValue(MyBean.class);
                if(result != null){
                    tcs.setResult(result);
                }
            }

            @Override
            public void onCancelled(DatabaseError databaseError){
                //handle error
            }
        });

        try {
            mModelClassObject = Tasks.await(tcsTask);
        }catch(ExecutionException e){
            //handle exception
        }catch (InterruptedException e){
            //handle exception
        }

        return mModelClassObject;
    }

LOCAL WAY

The other way is taking the version above and skipping something like dropbox

/** A simple endpoint method that takes a name and says Hi back */
        @ApiMethod(name = "sayHi")
    public MyBean sayHi(@Named("name") String name) {

        MyBean mModelClassObject = null;

        String text = "JUST PASTE YOUR JSON CONTENTS HERE";

        InputStream stream = new ByteArrayInputStream(text.getBytes(StandardCharsets.UTF_8));

        FirebaseOptions options = null;
        options = new FirebaseOptions.Builder()
                .setServiceAccount(stream)
                .setDatabaseUrl("https://[PROJECT-ID].firebaseio.com/")
                .build();
        FirebaseApp.initializeApp(options);

        DatabaseReference ref = FirebaseDatabase.getInstance().getReference();
        final TaskCompletionSource<MyBean> tcs = new TaskCompletionSource<>();


        Task<MyBean> tcsTask = tcs.getTask();

        ref.addListenerForSingleValueEvent(new ValueEventListener() {
            @Override
            public void onDataChange(DataSnapshot dataSnapshot) {
                MyBean result = dataSnapshot.getValue(MyBean.class);
                if(result != null){
                    tcs.setResult(result);
                }
            }

            @Override
            public void onCancelled(DatabaseError databaseError){
                //handle error
            }
        });

        try {
            mModelClassObject = Tasks.await(tcsTask);
        }catch(ExecutionException e){
            //handle exception
        }catch (InterruptedException e){
            //handle exception
        }

        return mModelClassObject;
    }

I don't know if this follows best practice but my project is working now. I also included firebase's code for getting info. check out this answer to a question i asked recently on reading and writing to firebase.

EDIT

cleaned up version which doesnt throw errors

public class MyEndpoint {

    private FirebaseOptions options;
    private DatabaseReference ref;
    private String serviceAccountJSON = "i took mine out for security reasons";

    // create firebase instance if need be
    private void connectToFirebase(){
        if (options == null) {
            options = null;
            options = new FirebaseOptions.Builder()
                    .setServiceAccount(new ByteArrayInputStream(serviceAccountJSON.getBytes(StandardCharsets.UTF_8)))
                    .setDatabaseUrl("https://[PROJECT-ID].firebaseio.com/")
                    .build();
            FirebaseApp.initializeApp(options);
        }
        if(ref == null) {
            ref = FirebaseDatabase.getInstance().getReference();
        }
    }

    /** A simple endpoint method that takes a name and says Hi back */
    @ApiMethod(name = "sayHi")
    public MyBean sayHi(@Named("name") String name) {

        // always do this first
        connectToFirebase();

        MyBean mModelClassObject = null;

        final TaskCompletionSource<MyBean> tcs = new TaskCompletionSource<>();
        Task<MyBean> tcsTask = tcs.getTask();

        // get the info
        ref.addListenerForSingleValueEvent(new ValueEventListener() {
            @Override
            public void onDataChange(DataSnapshot dataSnapshot) {
                MyBean result = dataSnapshot.getValue(MyBean.class);
                if(result != null){
                    tcs.setResult(result);
                }
            }

            @Override
            public void onCancelled(DatabaseError databaseError){
                //handle error
            }
        });

        // wait for it
        try {
            mModelClassObject = Tasks.await(tcsTask);
        }catch(ExecutionException e){
            //handle exception
        }catch (InterruptedException e){
            //handle exception
        }

        mModelClassObject.setData(mModelClassObject.getData() + name);

        return mModelClassObject;
    }
}
Community
  • 1
  • 1
booky99
  • 1,436
  • 4
  • 27
  • 45
0

Finally, I found the solution, Its written under the APIs and references section of Google App Engine in this link, that we need to add such files in the appengine-web.xml file under the <resource-files> tag, using <include path=""/> property. After doing so its works for me. I placed the .json file containing project credentials in the WEB-INF directory and then entered its relative path in <resource-files> tag.

Manish Patiyal
  • 4,427
  • 5
  • 21
  • 35
  • What does your relative path look like? I'm still getting this error after adding it to the resource-files tag. Maybe it has to do with the path? Also, is there anything different in how you initialize firebase? Like what the path looks like for referencing the file there as well? Thanks – Daniel George Sep 23 '17 at 03:23