File access levels
When adding the Storage category, you configure the level of access users have to your S3 bucket. You can configure separate rules for authenticated vs. guest users. When using the Storage category to upload files, you can also specify an access level for each individual file: guest, protected, or private.
Guest access does not mean that your files are totally public. A "guest" is a user of your application who has not yet signed in. To enable access at this level, you will still be required to configured Authentication in your app. The user must be able to assume an unauthenticated role from your Cognito Identity Pool.
For protected and private access, the
[IDENTITY_ID]
below corresponds to the unique ID of the user. Once the user has signed in, the
[IDENTITY_ID]
can be retrieved from the session by accessing the identity id. See
Accessing credentials
to retrieve the identity id, and use this as the unique ID of the authenticated user.
Protected access
After the user has signed in, create an options object specifying the
protected
access level to allow other users to read the object:
1private void uploadFile(String key, File file) {2 StorageUploadFileOptions options = StorageUploadFileOptions.builder()3 .accessLevel(StorageAccessLevel.PROTECTED)4 .build();5
6 Amplify.Storage.uploadFile(key, file, options,7 result -> Log.i("MyAmplifyApp", "Successfully uploaded: " + key),8 error -> Log.e("MyAmplifyApp", "Upload failed", error)9 );10}
1private fun uploadFile(key: String, file: File) {2 val options = StorageUploadFileOptions.builder()3 .accessLevel(StorageAccessLevel.PROTECTED)4 .build()5 6 Amplify.Storage.uploadFile(key, file, options,7 { Log.i("MyAmplifyApp", "Successfully uploaded: $key" ) },
8 { error -> Log.e("MyAmplifyApp", "Upload failed", error) }9 )10}
1private suspend fun uploadFile(key: String, file: File) {2 val options = StorageUploadFileOptions.builder()3 .accessLevel(StorageAccessLevel.PROTECTED)4 .build()5 val upload = Amplify.Storage.uploadFile(key, file, options)6 try {7 upload.result()8 Log.i("MyAmplifyApp", "Successfully uploaded: $key")9 } catch (error: StorageException) {10 Log.e("MyAmplifyApp", "Upload failed", error)11 }12}
1private void uploadFile(String key, File file) {2 StorageUploadFileOptions options = StorageUploadFileOptions.builder()3 .accessLevel(StorageAccessLevel.PROTECTED)4 .build();5
6 RxProgressAwareSingleOperation<StorageUploadFileResult> upload =7 RxAmplify.Storage.uploadFile("ExampleKey", exampleFile, options);8
9 upload10 .observeResult()11 .subscribe(12 result -> Log.i("MyAmplifyApp", "Successfully uploaded: " + result.getKey()),
13 error -> Log.e("MyAmplifyApp", "Upload failed", error)14 );15}
This will upload with the prefix
/protected/[IDENTITY_ID]/
followed by the
key
.
For other users to read the file, you must specify the access level as
protected
and the identity ID of the user who uploaded it in the options.
1private void downloadFile(File file, String key, String otherIdentityId) {2 StorageDownloadFileOptions options = StorageDownloadFileOptions.builder()3 .accessLevel(StorageAccessLevel.PROTECTED)4 .targetIdentityId(otherIdentityId)5 .build();6
7 Amplify.Storage.downloadFile(key, file, options,8 result -> Log.i("MyAmplifyApp", "Successfully downloaded: " + key),9 error -> Log.e("MyAmplifyApp", "Download failed", error)10 );11}
1private fun downloadFile(file: File, key: String, otherIdentityId: String) {2 val options = StorageDownloadFileOptions.builder()3 .accessLevel(StorageAccessLevel.PROTECTED)4 .targetIdentityId(otherIdentityId)5 .build()6 7 Amplify.Storage.downloadFile(key, file, options,8 { Log.i("MyAmplifyApp", "Successfully downloaded: $key") },9 { error -> Log.e("MyAmplifyApp", "Download failed", error) }
10 )11}
1private suspend fun downloadFile(file: File, key: String, otherIdentityId: String) {2 val options = StorageDownloadFileOptions.builder()3 .accessLevel(StorageAccessLevel.PROTECTED)4 .targetIdentityId(otherIdentityId)5 .build()6 7 val download = Amplify.Storage.downloadFile(key, file, options)8 9 try {10 download.result()11 Log.i("MyAmplifyApp", "Successfully downloaded: $key")12 } catch (error: StorageException) {13 Log.e("MyAmplifyApp", "Download failed", error)14 }15}
Private Access
Create an options object specifying the private access level to only allow an object to be accessed by the uploading user
1private void uploadFile(String key, File file) {2 StorageUploadFileOptions options = StorageUploadFileOptions.builder()3 .accessLevel(StorageAccessLevel.PRIVATE)4 .build();5
6 Amplify.Storage.uploadFile(key, file, options,7 result -> Log.i("MyAmplifyApp", "Successfully uploaded: " + key),8 error -> Log.e("MyAmplifyApp", "Upload failed", error)
9 );10}
1private fun uploadFile(key: String, file: File) {2 val options = StorageUploadFileOptions.builder()3 .accessLevel(StorageAccessLevel.PRIVATE)4 .build()5 6 Amplify.Storage.uploadFile(key, file, options,7 { Log.i("MyAmplifyApp", "Successfully uploaded: $key") },8 { error -> Log.e("MyAmplifyApp", "Upload failed", error) }9 )10}
1private suspend fun uploadFile(key: String, file: File) {2 val options = StorageUploadFileOptions.builder()3 .accessLevel(StorageAccessLevel.PRIVATE)4 .build()5
6 val upload = Amplify.Storage.uploadFile(key, file, options)7 8 try {9 upload.result()10 Log.i("MyAmplifyApp", "Successfully uploaded: $key")11 } catch (error: StorageException) {12 Log.e("MyAmplifyApp", "Upload failed", error)13 }14}
This will upload with the prefix
/private/[IDENTITY_ID]/
, followed by the
key
.
For the uploading user to read the file, specify the same access level (
private
) and key you used to upload:
1StorageDownloadFileOptions options = StorageDownloadFileOptions.builder()2 .accessLevel(StorageAccessLevel.PRIVATE)3 .build();
1val options = StorageDownloadFileOptions.builder()2 .accessLevel(StorageAccessLevel.PRIVATE)3 .build()
Customization
Customize Object Key Path
You can customize your key path by defining a prefix resolver:
1// Define your own prefix resolver, that conforms to `AWSS3StoragePluginPrefixResolver`2public class MyDeveloperPrefixResolver implements AWSS3PluginPrefixResolver {3
4 @Override5 public void resolvePrefix(@NonNull StorageAccessLevel accessLevel, @Nullable String targetIdentity,6 @NonNull Consumer<String> onSuccess, @NonNull Consumer<StorageException> onError) {7 switch (accessLevel) {8 case PUBLIC: {9 onSuccess.accept("myPublicPrefix/");10 }11 case PROTECTED: {12 onSuccess.accept("myProtectedPrefix/" + targetIdentity + "/");13 }14 case PRIVATE: {15 onSuccess.accept("myPrivatePrefix/" + targetIdentity + "/");16 }17 }18 }19}
1// Define your own prefix resolver, that conforms to `AWSS3StoragePluginPrefixResolver`
2class MyDeveloperPrefixResolver: AWSS3PluginPrefixResolver {3
4 override fun resolvePrefix(5 accessLevel: StorageAccessLevel,6 targetIdentity: String?,7 onSuccess: Consumer<String>,8 onError: Consumer<StorageException>9 ) {10 when(accessLevel) {11 StorageAccessLevel.PUBLIC -> {12 onSuccess.accept("myPublicPrefix/")13 }14 StorageAccessLevel.PROTECTED -> {15 onSuccess.accept("myProtectedPrefix/$targetIdentity/")16 }17 StorageAccessLevel.PRIVATE -> {18 onSuccess.accept("myPrivatePrefix/$targetIdentity/")19 }20 }21 }22}
Then configure the storage plugin with the resolver.
1AWSS3StoragePluginConfiguration.Builder builder = new AWSS3StoragePluginConfiguration.Builder();2builder.setAwsS3PluginPrefixResolver(new MyDeveloperPrefixResolver());3Amplify.addPlugin(new AWSS3StoragePlugin(builder.build()));
1Amplify.addPlugin<Plugin<*>>(2 AWSS3StoragePlugin(3 AWSS3StoragePluginConfiguration {4 awsS3PluginPrefixResolver = MyDeveloperPrefixResolver()5 }6 )7)
Add the IAM policy that corresponds with the prefixes defined above to enable read, write and delete operation for all the objects under path myPublicPrefix/ :
1"Statement": [2 {3 "Effect": "Allow",4 "Action": [5 "s3:GetObject",6 "s3:PutObject",7 "s3:DeleteObject"8 ],9 "Resource": [10 "arn:aws:s3:::your-s3-bucket/myPublicPrefix/*",11 ]12 }13]
If you want to have custom private path prefix like myPrivatePrefix/ , you need to add it into your IAM policy:
1"Statement": [2 {3 "Effect": "Allow",4 "Action": [5 "s3:GetObject",6 "s3:PutObject",7 "s3:DeleteObject"8 ],9 "Resource": [10 "arn:aws:s3:::your-s3-bucket/myPrivatePrefix/${cognito-identity.amazonaws.com:sub}/*"11 ]12 }13]
Passthrough PrefixResolver
If you would like no prefix resolution logic, such as performing S3 requests at the root of the bucket, create a prefix resolver that returns an empty string: