Can people access the Android realm file easily? - java

Can people hack the realm file to get the data in Android apps? I need to have a persistent object to save data to, so users can load it back when they restart.
RealmConfiguration realmConfiguration = new RealmConfiguration.Builder(this).build();
/* May need to take out the folioing line. */
Realm.deleteRealm(realmConfiguration);
Realm.setDefaultConfiguration(realmConfiguration);
realm = Realm.getDefaultInstance();

When the the RealmConfiguration is built through Builder(android.content.Context context) , the Realm file will be stored in /data/data/<packagename>/files. It is in the Android internal storage, and by default, only your app has the permission to access. See Saving files - Android doc. For a developer, there are some ways to get the file from the internal storage even he is not the owner of your app (eg.: root the phone :P).
You can choose to use Realm's encryption feature, which means even if others get the Realm file, it is still not possible to decode the data from the file. See Realm Encryption for more details.

Related

How to save Offline Server Data in Application

How to save the data on the server like in the khta book application and after reinstalling the application, how to save that data offline in the application
If you keep the data on the server always in sync with the data in the application, then after you reinstall the application and you log into your account you can just retrieve the data normally and have all your previous data.
If you want to include an initial Firestore data set with the application, so that the user can use this right after they install the app (even if they're offline), have a look at the documentation about data bundles.
Data bundles allow you to create a bundle of Firestore documents/queries, that you can then include in the APK, and load the bundle to Firestore's cache when the app is first started.
Also see the solutions guide on serving data bundles.

Firebase: Setting up different environments for Auth and Notification

I have 2 mobile apps and one web app which use the same Firebase Auth and Notification services. I only use Auth and Notification services, nothing else.
I require to have a staging environment and production environment, and both need to run in parallel.
I do not see other option in Firebase rather than creating two different projects. But that's risky, because I have to replace the JSON files in mobile app, replace Admin SDK stuff in web app and so on.
So what's the best option for this? I use Firebase only for Auth and Notification. In notification I send messages to individual devices and user groups.
maybe asking the users to use different email accounts for testing and production is the way to go? (Because then the Auth data will not be messed up even though the same Auth space is used)
You could have only one single Firebase project and inside this project create several apps. For instance, you could create an app for production with the package name(android)/bundle id(iOS) "com.example.myapp" and another for staging "com.example.myapp.staging". That way, you can download a single json file with both configurations. In your android studio/xcode, you have to set up those package name/bundle id with the corresponding build configuration.

Changing firebase database

I have an android project which is connected to a firebase database, is there any way that I can unlink my project from current database and connect my app to a new firebase database?
If you meant to link your android project to a different firebase project, then of course you can.
At the android side the "connection" is merely a json file stored at
yourProject/app/google-services.json.
You can simply replace it with another file that you may download from the settings page of your new firebase project.

Google Cloud Storage with a service account in Java - 403 Caller does not have storage.objects.list access to bucket

We want to download files from Google Storage in our application server. It is important to have read-only restricted access to a single bucket and nothing else.
At first I used a regular user account (not a service account) which have permissions to access all buckets in our Google Cloud project, and everything worked fine - my Java code opened buckets and downloaded files without problems.
Storage storage = StorageOptions.getDefaultInstance().getService();
Bucket b = storage.get( "mybucketname" );
Then I wanted to switch to use a specially created service account which has access to a single bucket only. So I created a service account, gave permissions to read a single bucket, and downloaded its key file. The permissions in Google Cloud Console are named as:
Storage Object Viewer (3 members) Read access to GCS objects.
gsutil command line utility works fine with this account - from the command line it allows accessing this bucket but not the others.
The initialization from the command line is done using the following command:
gcloud --project myprojectname auth activate-service-account files-viewer2#myprojectname.iam.gserviceaccount.com --key-file=/.../keyfilename.json
I even tried two different service accounts which have access to different buckets, and from the command line I can switch between them and gsutil gives access to a relevant bucket only, and for any other it returns the error:
"AccessDeniedException: 403 Caller does not have storage.objects.list access to bucket xxxxxxxxxx."
So, from the command line everything worked fine.
But in Java there is some problem with the authentication.
The default authentication I previously used with a regular user account stopped working - it reports the error:
com.google.cloud.storage.StorageException: Anonymous users does not have storage.buckets.get access to bucket xxxxxxxxxx.
Then I've tried the following code (this is the simplest variant because it relies on the key json file, but I've already tried a number of other variants found in various forums, with no success):
FileInputStream fis = new FileInputStream( "/path/to/the/key-file.json" );
ServiceAccountCredentials credentials = ServiceAccountCredentials.fromStream( fis );
Storage storage = StorageOptions.newBuilder().setCredentials( credentials )
.setProjectId( "myprojectid" ).build().getService();
Bucket b = storage.get( "mybucketname" );
And all I receive is this error:
com.google.cloud.storage.StorageException: Caller does not have storage.buckets.get access to bucket mybucketname.
Caused by: com.google.api.client.googleapis.json.GoogleJsonResponseException: 403 Forbidden
The same error is returned no matter to what buckets I'm trying to access (even non-existing).
What confuses me is that the same service account, initialized with the same JSON key file, works fine from the command line.
So I think something is missing in Java code that ensures correct authentication.
TL;DR - If you're using Application Default Credentials (which BTW you are when you do StorageOptions.getDefaultInstance().getService();), and if you need to use the credentials from a service account, you can do so without changing your code. All you need to do is set the GOOGLE_APPLICATION_CREDENTIALS environment variable to the full path of your service account json file and you are all set.
Longer version of the solution using Application Default Credentials
Use your original code as-is
Storage storage = StorageOptions.getDefaultInstance().getService();
Bucket b = storage.get( "mybucketname" );
Set the environment variable GOOGLE_APPLICATION_CREDENTIALS to the full path of your json file containing the service account credentials.
export GOOGLE_APPLICATION_CREDENTIALS=/path/to/service_account_credentials.json
Run your java application once again to verify that it is working as expected.
Alternate solution using hard-coded Service Account Credentials
The code example you posted for initializing ServiceAccountCredentials looks valid to me on a quick glance. I tried the following code snippet and it is working for me as expected.
String SERVICE_ACCOUNT_JSON_PATH = "/path/to/service_account_credentials.json";
Storage storage =
StorageOptions.newBuilder()
.setCredentials(
ServiceAccountCredentials.fromStream(
new FileInputStream(SERVICE_ACCOUNT_JSON_PATH)))
.build()
.getService();
Bucket b = storage.get("mybucketname");
When specifying a service account credential, the project ID is automatically picked up from the information present in the json file. So you do not have to specify it once again. I'm not entirely sure though if this is related to the issue you're observing.
Application Default Credentials
Here is the full documentation regarding Application Default Credentials explaining which credentials are picked up based on your environment.
How the Application Default Credentials work
You can get Application Default Credentials by making a single client
library call. The credentials returned are determined by the
environment the code is running in. Conditions are checked in the
following order:
The environment variable GOOGLE_APPLICATION_CREDENTIALS is checked. If this variable is specified it should point to a file that
defines the credentials. The simplest way to get a credential for this
purpose is to create a Service account key in the Google API Console:
a. Go to the API Console Credentials page.
b. From the project drop-down, select your project.
c. On the Credentials page, select the Create credentials drop-down,
then select Service account key.
d.From the Service account drop-down, select an existing service
account or create a new one.
e. For Key type, select the JSON key option, then select Create. The
file automatically downloads to your computer.
f. Put the *.json file you just downloaded in a directory of your
choosing. This directory must be private (you can't let anyone get
access to this), but accessible to your web server code.
g. Set the environment variable GOOGLE_APPLICATION_CREDENTIALS to
the path of the JSON file downloaded.
If you have installed the Google Cloud SDK on your machine and have run the command gcloud auth application-default login, your
identity can be used as a proxy to test code calling APIs from that
machine.
If you are running in Google App Engine production, the built-in service account associated with the application will be used.
If you are running in Google Compute Engine production, the built-in service account associated with the virtual machine instance
will be used.
If none of these conditions is true, an error will occur.
IAM roles
I would recommend going over the IAM permissions and the IAM roles available for Cloud Storage. These provide control at project and bucket level. In addition, you can use ACLs to control permissions at the object level within the bucket.
If your use case involves just invoking storage.get(bucketName). This operation will require just storage.buckets.get permission and the best IAM role for just this permission is roles/storage.legacyObjectReader.
If you also want to grant the service account permissions to get (storage.objects.get) and list (storage.objects.list) individual objects, then also add the role roles/storage.objectViewer to the service account.
Thanks to #Taxdude's long explanation, I understood that my Java code should be all right, and started looking at other possible reasons for the problem.
One of additional things I've tried were the permissions set to the service account, and there I've found the solution – it was unexpected, actually.
When a service account is created, it must not be given permissions to read from Google Storage, because then it will have read permissions to ALL buckets, and it is impossible to change that (not sure why), because the system marks these permissions as "inherited".
Therefore, you have to:
Create a "blank" service account with no permissions, and
Configure permissions from the bucket configuration
To do so:
Open Google Cloud Web console
Open Storage Browser
Select your bucket
Open the INFO PANEL with Permissions
Add the service account with the Storage Object Viewer permission, but there are also permissions named Storage Legacy Object Reader and Storage Legacy Bucket Reader
Because of the word "Legacy" I thought those should not be used – they look like something kept for backward compatibility. And after experimenting and adding these "legacy" permissions, all of a sudden the same code I was trying all the time started working properly.
I'm still not entirely sure what is the minimal set of permissions I should assign to a service account, but at least now it works with all three "read" permissions on the bucket – two "legacy" and one "normal".

Use pre-populated databases with Realm

How could we convert SQLite database to a Realm database?
Is there any way to use pre-populated databases with Realm on Android?
Currently there is no way to automatically convert a SQLite database to a Realm database, you would have to manually read all data from the SQLite database and insert them into Realm.
An alternative could be the Realm browser that might make this easier, but it is currently available for MacOS X only. You can read more here: https://github.com/realm/realm-java/issues/435
For the second part: As Realm databases are just a single file so you can easily add a pre-populated realm database to you app and reference it with Realm.getInstance().
This is an answer to the second part of the question.
While shipping a prepopulated database in the assets folder of your app, analogous to the official Objective-C to bundle of Realm with your app, is still probably the fastest way to get a prepopulated Realm if you have a lot of data. But as of Realm Java 0.89 there is now an official way to prepopulate a Realm database on Android.
There is now a method that allows for specifying a transaction to be run when a Realm database is created for the first time. You should call this method, initialData(Realm.Transaction transaction), as part of setting up the RealmConfiguration Builder.
For example
RealmConfiguration config = new RealmConfiguration.Builder(context)
.name("myrealm.realm")
.initialData(new MyInitialDataRealmTransaction()),
.build();

Categories

Resources