New user is able to access the photos uploaded by another user - android

These are my Firebase Storage rules.
match
/{allPaths=**} {
allow read, write: if request.auth != null;
This is how users are adding pictures to the storage
StorageReference filepathOne = mStorage.child(user_data.getString("uidkey", null)).child("Photos").child("id_one");
As you can see I am using the UID as part of the filepath name. My assumption was that, whenever a user uploads the picture they want, only they would be able to access it as they have the unique UID. However, I created a new user account and for some reason, that user is able to access the photos uploaded by another user even though they have different UID.
I should also point out that I am storing the URI in a sharedpref, and then later converting it into a string to have it display the picture.
This is how I store in the SharedPref
Uri downloadUrl = taskSnapshot.getDownloadUrl();
Picasso.with(getContext())
.load(downloadUrl)
.fit().centerCrop()
.into(image);
editor.putString(user_idone, (taskSnapshot.getDownloadUrl()).toString());
editor.commit();
This is how I extract from the Pref and display.
Picasso.with(getContext())
.load(Uri.parse(user_data.getString("idonekey", null)))
.fit().centerCrop()
.into((ImageView) getView().findViewById(R.id.image));

Currently your rules say "anyone can read/write anything, so long as they are authenticated." It sounds like you want to change the rules to mirror your file structure and provide tighter security:
service firebase.storage {
match /b/<your-bucket-name>/o {
match /{userId}/Photos/{fileName} {
allow read: if request.auth.uid == userId;
}
}
}
Our rules docs talk in greater depth on user and group based security.

Related

Flutter file picker to store multiple files to Firebase Storage at once

So i know there is an example available on pub.dev for multiple files method for file picker such as below.
FilePickerResult? result = await FilePicker.platform.pickFiles(allowMultiple: true);
if (result != null) {
List<File> files = result.paths.map((path) => File(path)).toList();
} else {
// User canceled the picker
}
What i would like to ask for help is how can i get the files for this allow multiple method and store it into my firebase storage ? I am able to storage and retrieve files with the single method but for multiple files i just cant find the correct way because i would like the user to be able to directly select all the desired files and upload to storage at once. Please help.
How can i continue from the above example ?

How to handle application created images?

So, I would like to upload files to server, and my application can run without network, and because of this, I have to save the files, that the user picks and upload later.
I don't want to save thease pictures into database, bacause as I read it is a bad practice, so I decided to I create a copy of the file, and after that I save the copied file uri string to database.
It is working good, but if I close the application, and opened it again, the uri is not valid, or probably I don't have permission anymore to read or write.
So how can I perform this behavior?
And why lose the application the uri permission, if it is created by himself?
I tired to get persistable uri permission, but I get an java.lang.SecurityException: No persistable permission grants found exception.
File copy
val copyOfFile = createFile(
application.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS),
fileMetaData.extension,
fileMetaData.fileName,
true
)
val outPut = FileOutputStream(copyOfFile)
inputStream.copyTo(outPut)
val newFileUri =
FileProvider.getUriForFile(application, application.packageName, copyOfFile)
application.contentResolver.takePersistableUriPermission(
newFileUri,
Intent.FLAG_GRANT_WRITE_URI_PERMISSION
)
return CopyFileResult(
copyOfFile.name,
fileMetaData.extension,
newFileUri.toString()
)
Open uri
val uri = Uri.parse(document.uriString!!)
val bitmap = documentManagerService.getBitmapFromUri(uri)
I found my mistake.
I save the filePath to database instead of uri string, and later the file is exists and I can use.

Managing local images with persistent data

I'm struggling to understand what could be a simple workaround for associating images and persistent data objects in android. In more details, i've put up a simple room persistence architecture and now I need to add a field "image" to the java persisted object. I've tried to work with uri but my knowledge of Android is very poor, and what I get is that the uri I recover when picking an image with the android file manager is only valid until reboot, so if I would save the so obtained uri in the database, it would make no sense when recovered later. How should I manage?
Basically what I need is a simple way to link an object to a local image stored in the phone (or captured on the fly with the camera), no worries about image deletion by the user or anything, just a simple way.
For istance I tried to tinker with the google code example but i clearly failed because I don't know what i'm doing
private Bitmap getBitmapFromUri(Uri uri) throws IOException {
ParcelFileDescriptor parcelFileDescriptor =
getContentResolver().takePersistableUriPermission(uri, Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
FileDescriptor fileDescriptor = parcelFileDescriptor.getFileDescriptor();
Bitmap image = BitmapFactory.decodeFileDescriptor(fileDescriptor);
parcelFileDescriptor.close();
return image;
}
this code results in compilation error, with required: parcedDescriptor... and VOID found, on the call of takePersistableUriPermission. I don't even know if that is a solution to my problem.
this is the code I use to get the uri from the local image, but I'm planning also to let the Camera snap a photo and pass it to for saving/linking it
// ACTION_OPEN_DOCUMENT is the intent to choose a file via the system's file
// browser.
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
// Filter to only show results that can be "opened", such as a
// file (as opposed to a list of contacts or timezones)
intent.addCategory(Intent.CATEGORY_OPENABLE);
// Filter to show only images, using the image MIME data type.
// If one wanted to search for ogg vorbis files, the type would be "audio/ogg".
// To search for all documents available via installed storage providers,
// it would be "*/*".
intent.setType("image/*");
startActivityForResult(intent, READ_REQUEST_CODE);
the uri I recover when picking an image with the android file manager is only valid until reboot, so if I would save the so obtained uri in the database, it would make no sense when recovered later
That's not quite accurate.
A Uri that you pull in via ACTION_OPEN_DOCUMENT will be good for whatever activity gets the Uri via onActivityResult(). If you pass that Uri to another component, you can use FLAG_GRANT_READ_URI_PERMISSION to allow that component to read the content at that Uri. But once your process ends, your access to that content goes away.
Since you used ACTION_OPEN_DOCUMENT, you can use takePersistableUriPermission() request to have long-term access to the content, but that still only works if the content is still there. If the user deletes the content, or perhaps even moves it, you will lose access.
For istance I tried to tinker with the google code example but i clearly failed because I don't know what i'm doing
takePersistableUriPermission() does not return a ParcelFileDescriptor. Otherwise, that particular call seems OK.
With respect to loading the image, please use an existing image-loading library (e.g., Glide, Picasso).

Firebase storage rules cause user does not have access

I am trying to load a user profile image from firebase storage but I keep running into Cause (1 of 1):
class com.google.firebase.storage.StorageException: User does not have permission to access this object.
Authenticating user with:
mFirebaseAuth.signInWithEmailAndPassword(mEmailView.getText().toString(), mPasswordView.getText().toString())
Here are my firebase rules :
service firebase.storage {
match /b/brainbeats-e9839.appspot.com/o {
match /user/{userId}/{allPaths=**} {
allow read, write: if request.auth.uid == userId;
}
}
}
Loading with glide like this:
StorageReference storageReference = FirebaseStorage.getInstance().getReference();
StorageReference imageStorage = storageReference.child("images/" + user.getArtistProfileImage());
GlideApp.with(this)
.load(imageStorage)
.into(mArtistCoverImage);
Well your rule grants access for the following :
/user/{userId}/{allPaths=**}
so any path under user then a userid location and then anything
But you are accessing
/images/xyzpath
Now since this isn't under /user/ variable userid/ , you get an exception.
why not shift images under user node like :
/user/{userId}/images/yourpath
This should stop giving errors if the auth rule matches. If it's an unauthorized user accessing another userid, it will still crash so best put everything in a try catch block

java.io.IOException: Cannot make changes to file

I am using JAudioTagger library for reading and writing tags for an audio file. I am able to read the tags but unable to write them.
I am retrieving audio file path like this :
private String getSongPath(long songId) {
String path = null;
ContentResolver contentResolver = getContentResolver();
Uri uri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
String[] projection = {MediaStore.Audio.Media.DATA};
String selection = MediaStore.Audio.Media._ID + " == ?";
String[] selectionArgs = {String.valueOf(songId)};
Cursor cursor = contentResolver.query(uri, projection, selection, selectionArgs, null);
if (cursor != null) {
int pathCol = cursor.getColumnIndexOrThrow(projection[0]);
cursor.moveToFirst();
path = cursor.getString(pathCol);
cursor.close();
}
return path;
}
Then to write tags using JAudioTagger :
File songFile = new File(path); // path looks like /storage/3932-3434/Music/xyz.mp3
AudioFile audiofile = = AudioFileIO.read(songFile);
Tag tag = = audiofile.getTag();
tag.setField(FieldKey.TITLE, title);
// some more setField calls for different feilds
audiofile.commit();
The commit() method is giving following Exception :
org.jaudiotagger.audio.exceptions.CannotWriteException:
java.io.IOException: Cannot make changes to file xyz.mp3 at
org.jaudiotagger.audio.mp3.MP3File.commit(MP3File.java:799) at
com.techapps.musicplayerplus.MainActivity$17.onClick(MainActivity.java:2125)
at
android.support.v7.app.AlertController$ButtonHandler.handleMessage(AlertController.java:157)
at android.os.Handler.dispatchMessage(Handler.java:102) at
android.os.Looper.loop(Looper.java:148) at
android.app.ActivityThread.main(ActivityThread.java:5417) 06-18
10:59:48.134 8802-8802/com.techapps.musicplayerplus W/System.err:
at java.lang.reflect.Method.invoke(Native Method) at
com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616) Caused
by: java.io.IOException: Cannot make changes to file Saibo.mp3 at
org.jaudiotagger.audio.mp3.MP3File.precheck(MP3File.java:824) at
org.jaudiotagger.audio.mp3.MP3File.save(MP3File.java:850) at
org.jaudiotagger.audio.mp3.MP3File.save(MP3File.java:783) at
org.jaudiotagger.audio.mp3.MP3File.commit(MP3File.java:795)
I am running this code on Android 6 while my app is targeted at SDK 22. I have also mentioned following permission in manifest.
android.permission.WRITE_EXTERNAL_STORAGE
Still I am unable to write to SD card. Please help me. Thanks in advance.
You have to use Storage Access Framework (SAF) to access SD Card from API 19 (Kitkat) onward.
First we need to ask user to provide a URI of the folder we want to access. If we want access to entire SD card, user needs to provide URI of SD card's root folder.
For example, when user hits Edit button, we have to first show hint dialog box, asking user to select required directory in SD Card which we want to access. You can display following image in hint dialog box to ask user to select root directory of SD Card :
When user dismisses hint dialog box, you need to trigger Storage Access Framework :
private void triggerStorageAccessFramework() {
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
startActivityForResult(intent, REQUEST_CODE_STORAGE_ACCESS);
}
public final void onActivityResult(final int requestCode, final int resultCode, final Intent resultData) {
if (resultCode == Activity.RESULT_OK) {
if (requestCode == REQUEST_CODE_STORAGE_ACCESS) {
Uri treeUri = null;
// Get Uri from Storage Access Framework.
treeUri = resultData.getData();
pickedDir= DocumentFile.fromTreeUri(this, treeUri);
if (!isSDCardRootDirectoryUri(treeUri)) {
Toast.makeText(this, "Wrong directory selected. Please select SD Card root directory.", Toast.LENGTH_LONG).show();
createSDCardHintDialog().show();
return;
}
// Persist URI in shared preference so that you can use it later.
SharedPreferences sharedPreferences = getSharedPreferences(App.PREFERENCE_FILENAME, Context.MODE_PRIVATE);
SharedPreferences.Editor editor = sharedPreferences.edit();
editor.putString(App.SDCARD_URI_KEY, treeUri.toString());
editor.apply();
// Persist access permissions, so you dont have to ask again
final int takeFlags = resultData.getFlags() & (Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
getContentResolver().takePersistableUriPermission(treeUri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
}
}
private boolean isSDCardRootDirectoryUri(Uri treeUri) {
String uriString = treeUri.toString();
return uriString.endsWith("%3A");
}
Once you get Uri of user picked directory, you can perform write operation using SAF : (creadit : this answer )
public void writeFile(DocumentFile pickedDir) {
try {
DocumentFile file = pickedDir.createFile("image/jpeg", "try2.jpg");
OutputStream out = getContentResolver().openOutputStream(file.getUri());
try {
// write the image content
} finally {
out.close();
}
} catch (IOException e) {
throw new RuntimeException("Something went wrong : " + e.getMessage(), e);
}
}
It could be that you pointing to non existing file.
Check your path file by using Log.
Log.d("Activity", "path = " + path);
Android-M or API 23 introduced Runtime Permissions for reducing security flaws in android device.
To update your apps using Google Play services to handle Android 6.0 permissions, it’s good practice to manage the user’s expectations in setting permissions that the runtime may require. The following link will help you avoid potential issues.
https://developer.android.com/training/permissions/requesting.html
have you declared the permission
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> ?
I saw that you already created an issue in the JAudioTagger GitHub repository which was advisable, but never got a universally working solution. My findings so far:
The answer mentioning SAF is correct, but it won't help you as SAF will provide a DocumentFile, not a File.
You might try to modify JAudioTagger to your needs, replacing File with DocumentFile, but the latter one has not all functions you will need.
Also InputStream and OutputStream will not help you, as JAudioTagger needs File and internally heavily uses RandomAccessFile which is not available either.
Google "forgot" to provide some getRandomAccessFileFromUri() which makes things even worse (Yes, there are hacks using Java reflection to work around this limitation...).
The "/proc/self/fd" method (How to handle SAF when I can only handle File or file-path?) will also not work immediately, as JAudioTagger needs copy and renaming functions that are not applicable to this kind of files. Particularly JAudioTagger will not find a suitable file name extension like ".m4a". Of course you could try to change JAudioTagger accordingly.
You might follow the advice to make a copy of the file to your personal storage, then apply JAudioTagger to it and finally copy it back to SD card, but:
If you want to use JAudioTagger to read from SD card, this will, as announced by Google, fail with Android 10. Starting with that version, you will not even have read access to the SD card via the File interface.
Further, the File interface gives you read access to SD cards with Android 9 and below, but not to other SAF devices, like USB OTG memory or SMB shares etc.
Of course you could also copy each file in order to read its metadata, but this will be awfully slow and is not suitable if you have more than a few files.
So my current advices are:
Try the "/proc/self/fd" method and modify JAudioTagger accordingly.
If the changes are too heavy, use the fd method for reading the tags and the copy method for writing.
BTW: I am currently modifying an older version of JAudioTagger for using both File and DocumentFile transparently, but the changes are tremendous, bear a high risk, need some help classes, and the work is unfinished, yet.
BTSW: The DocumentFile functions are painfully slow, compared to the File functions.

Categories

Resources