Skip to content

Commit

Permalink
fix(storage): ensure bucket value is used to create FirebaseStorage i…
Browse files Browse the repository at this point in the history
…nstance to stop incorrect default bucket usage (#11844)
  • Loading branch information
russellwheatley authored Nov 9, 2023
1 parent f78838a commit 49542f3
Show file tree
Hide file tree
Showing 24 changed files with 750 additions and 340 deletions.
23 changes: 22 additions & 1 deletion .github/workflows/scripts/storage.rules
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
rules_version = '2';
service firebase.storage {
match /b/{bucket}/o {
// Default bucket
match /b/flutterfire-e2e-tests.appspot.com/o {
match /{document=**} {
allow read, write: if false;
}
Expand All @@ -14,4 +15,24 @@ service firebase.storage {
allow read, write: if true;
}
}
// Second bucket
match /b/flutterfire-e2e-tests-two/o {
match /{document=**} {
allow read, write: if false;
}

match /allowable-lists-2nd-bucket/{document=**} {
allow read, write: if true;
}

match /writeOnly.txt {
allow read: if false;
allow write: if true;
}

match /flutter-tests/{fileName} {
// Blocks read and write access if the file name exactly matches 'second-bucket-not-allowed', regardless of the file extension
allow read, write: if !(fileName == 'second-bucket-not-allowed.jpeg' || fileName == 'second-bucket-not-allowed.txt');
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@ public class FlutterFirebaseStoragePlugin

private MethodChannel channel;
@Nullable private BinaryMessenger messenger;

static final String STORAGE_METHOD_CHANNEL_NAME = "plugins.flutter.io/firebase_storage";
static final String STORAGE_TASK_EVENT_NAME = "taskEvent";
static final String DEFAULT_ERROR_CODE = "firebase_storage";
Expand Down Expand Up @@ -178,62 +177,18 @@ private void removeEventListeners() {

private FirebaseStorage getStorageFromPigeon(
GeneratedAndroidFirebaseStorage.PigeonStorageFirebaseApp app) {
return getStorageFromPigeon(app, null);
}

private FirebaseStorage getStorageFromPigeon(
GeneratedAndroidFirebaseStorage.PigeonStorageFirebaseApp app, @Nullable String bucket) {
FirebaseApp androidApp = FirebaseApp.getInstance(app.getAppName());
if (bucket == null) {
return FirebaseStorage.getInstance(androidApp);
} else {
return FirebaseStorage.getInstance(androidApp, "gs://" + bucket);
}
}

private FirebaseStorage getStorage(Map<String, Object> arguments) {
String appName = (String) Objects.requireNonNull(arguments.get("appName"));
FirebaseApp app = FirebaseApp.getInstance(appName);
String bucket = (String) arguments.get("bucket");

FirebaseStorage storage;

if (bucket == null) {
storage = FirebaseStorage.getInstance(app);
} else {
storage = FirebaseStorage.getInstance(app, "gs://" + bucket);
}

Object maxOperationRetryTime = arguments.get("maxOperationRetryTime");
if (maxOperationRetryTime != null) {
storage.setMaxOperationRetryTimeMillis(getLongValue(maxOperationRetryTime));
}

Object maxDownloadRetryTime = arguments.get("maxDownloadRetryTime");
if (maxDownloadRetryTime != null) {
storage.setMaxDownloadRetryTimeMillis(getLongValue(maxDownloadRetryTime));
}

Object maxUploadRetryTime = arguments.get("maxUploadRetryTime");
if (maxUploadRetryTime != null) {
storage.setMaxUploadRetryTimeMillis(getLongValue(maxUploadRetryTime));
}

return storage;
return FirebaseStorage.getInstance(androidApp, "gs://" + app.getBucket());
}

private StorageReference getReferenceFromPigeon(
GeneratedAndroidFirebaseStorage.PigeonStorageFirebaseApp app,
GeneratedAndroidFirebaseStorage.PigeonStorageReference reference) {
FirebaseStorage androidStorage = getStorageFromPigeon(app, reference.getBucket());
FirebaseStorage androidStorage = getStorageFromPigeon(app);
return androidStorage.getReference(reference.getFullPath());
}

private StorageReference getReference(Map<String, Object> arguments) {
String path = (String) Objects.requireNonNull(arguments.get("path"));
return getStorage(arguments).getReference(path);
}

private GeneratedAndroidFirebaseStorage.PigeonStorageReference convertToPigeonReference(
StorageReference reference) {
return new GeneratedAndroidFirebaseStorage.PigeonStorageReference.Builder()
Expand All @@ -252,34 +207,11 @@ public void getReferencebyPath(
GeneratedAndroidFirebaseStorage.Result<
GeneratedAndroidFirebaseStorage.PigeonStorageReference>
result) {
StorageReference androidReference = getStorageFromPigeon(app, bucket).getReference(path);
StorageReference androidReference = getStorageFromPigeon(app).getReference(path);

result.success(convertToPigeonReference(androidReference));
}

private Map<String, Object> parseListResult(ListResult listResult) {
Map<String, Object> out = new HashMap<>();

if (listResult.getPageToken() != null) {
out.put("nextPageToken", listResult.getPageToken());
}

List<String> items = new ArrayList<>();
List<String> prefixes = new ArrayList<>();

for (StorageReference reference : listResult.getItems()) {
items.add(reference.getPath());
}

for (StorageReference reference : listResult.getPrefixes()) {
prefixes.add(reference.getPath());
}

out.put("items", items);
out.put("prefixes", prefixes);
return out;
}

@Override
public void useStorageEmulator(
@NonNull GeneratedAndroidFirebaseStorage.PigeonStorageFirebaseApp app,
Expand Down Expand Up @@ -322,6 +254,7 @@ public void referenceGetDownloadURL(
@NonNull GeneratedAndroidFirebaseStorage.PigeonStorageReference reference,
@NonNull GeneratedAndroidFirebaseStorage.Result<String> result) {
FirebaseStorage firebaseStorage = getStorageFromPigeon(app);

StorageReference androidReference = firebaseStorage.getReference(reference.getFullPath());
androidReference
.getDownloadUrl()
Expand Down Expand Up @@ -417,11 +350,11 @@ public void referenceList(
FirebaseStorage firebaseStorage = getStorageFromPigeon(app);
StorageReference androidReference = firebaseStorage.getReference(reference.getFullPath());
Task<ListResult> androidResult;
if (options.getPageToken() == null) {
androidResult = androidReference.list(options.getMaxResults().intValue());
} else {
if (options.getPageToken() != null) {
androidResult =
androidReference.list(options.getMaxResults().intValue(), options.getPageToken());
} else {
androidResult = androidReference.list(options.getMaxResults().intValue());
}
androidResult.addOnCompleteListener(
task -> {
Expand Down Expand Up @@ -725,40 +658,6 @@ public void setMaxDownloadRetryTime(
result.success(null);
}

private StorageMetadata parseToStorageMetadata(Map<String, Object> metadata) {
if (metadata == null) {
return null;
}

StorageMetadata.Builder builder = new StorageMetadata.Builder();

if (metadata.get("cacheControl") != null) {
builder.setCacheControl((String) metadata.get("cacheControl"));
}
if (metadata.get("contentDisposition") != null) {
builder.setContentDisposition((String) metadata.get("contentDisposition"));
}
if (metadata.get("contentEncoding") != null) {
builder.setContentEncoding((String) metadata.get("contentEncoding"));
}
if (metadata.get("contentLanguage") != null) {
builder.setContentLanguage((String) metadata.get("contentLanguage"));
}
if (metadata.get("contentType") != null) {
builder.setContentType((String) metadata.get("contentType"));
}
if (metadata.get("customMetadata") != null) {
@SuppressWarnings("unchecked")
Map<String, String> customMetadata =
(Map<String, String>) Objects.requireNonNull(metadata.get("customMetadata"));
for (String key : customMetadata.keySet()) {
builder.setCustomMetadata(key, customMetadata.get(key));
}
}

return builder.build();
}

private byte[] stringToByteData(@NonNull String data, int format) {
switch (format) {
case 1: // PutStringFormat.base64
Expand All @@ -770,16 +669,6 @@ private byte[] stringToByteData(@NonNull String data, int format) {
}
}

private Long getLongValue(Object value) {
if (value instanceof Long) {
return (Long) value;
} else if (value instanceof Integer) {
return Long.valueOf((Integer) value);
} else {
return 0L;
}
}

@Override
public Task<Map<String, Object>> getPluginConstantsForFirebaseApp(FirebaseApp firebaseApp) {
TaskCompletionSource<Map<String, Object>> taskCompletionSource = new TaskCompletionSource<>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -178,21 +178,6 @@ TaskStateChannelStreamHandler startTaskWithMethodChannel(@NonNull MethodChannel
return new TaskStateChannelStreamHandler(this, reference.getStorage(), storageTask);
}

private Map<String, Object> getTaskEventMap(
@Nullable Object snapshot, @Nullable Exception exception) {
Map<String, Object> arguments = new HashMap<>();
arguments.put("handle", handle);
arguments.put("appName", reference.getStorage().getApp().getName());
arguments.put("bucket", reference.getBucket());
if (snapshot != null) {
arguments.put("snapshot", parseTaskSnapshot(snapshot));
}
if (exception != null) {
arguments.put("error", FlutterFirebaseStoragePlugin.getExceptionDetails(exception));
}
return arguments;
}

public Object getSnapshot() {
return storageTask.getSnapshot();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,19 @@ public void setTenantId(@Nullable String setterArg) {
this.tenantId = setterArg;
}

private @NonNull String bucket;

public @NonNull String getBucket() {
return bucket;
}

public void setBucket(@NonNull String setterArg) {
if (setterArg == null) {
throw new IllegalStateException("Nonnull field \"bucket\" is null.");
}
this.bucket = setterArg;
}

/** Constructor is non-public to enforce null safety; use Builder. */
PigeonStorageFirebaseApp() {}

Expand All @@ -120,19 +133,28 @@ public static final class Builder {
return this;
}

private @Nullable String bucket;

public @NonNull Builder setBucket(@NonNull String setterArg) {
this.bucket = setterArg;
return this;
}

public @NonNull PigeonStorageFirebaseApp build() {
PigeonStorageFirebaseApp pigeonReturn = new PigeonStorageFirebaseApp();
pigeonReturn.setAppName(appName);
pigeonReturn.setTenantId(tenantId);
pigeonReturn.setBucket(bucket);
return pigeonReturn;
}
}

@NonNull
ArrayList<Object> toList() {
ArrayList<Object> toListResult = new ArrayList<Object>(2);
public ArrayList<Object> toList() {
ArrayList<Object> toListResult = new ArrayList<Object>(3);
toListResult.add(appName);
toListResult.add(tenantId);
toListResult.add(bucket);
return toListResult;
}

Expand All @@ -142,6 +164,8 @@ ArrayList<Object> toList() {
pigeonResult.setAppName((String) appName);
Object tenantId = list.get(1);
pigeonResult.setTenantId((String) tenantId);
Object bucket = list.get(2);
pigeonResult.setBucket((String) bucket);
return pigeonResult;
}
}
Expand Down Expand Up @@ -223,7 +247,7 @@ public static final class Builder {
}

@NonNull
ArrayList<Object> toList() {
public ArrayList<Object> toList() {
ArrayList<Object> toListResult = new ArrayList<Object>(3);
toListResult.add(bucket);
toListResult.add(fullPath);
Expand Down Expand Up @@ -272,7 +296,7 @@ public static final class Builder {
}

@NonNull
ArrayList<Object> toList() {
public ArrayList<Object> toList() {
ArrayList<Object> toListResult = new ArrayList<Object>(1);
toListResult.add(metadata);
return toListResult;
Expand All @@ -293,13 +317,16 @@ public static final class PigeonListOptions {
*
* <p>The default and maximum maxResults is 1000.
*/
private @Nullable Long maxResults;
private @NonNull Long maxResults;

public @Nullable Long getMaxResults() {
public @NonNull Long getMaxResults() {
return maxResults;
}

public void setMaxResults(@Nullable Long setterArg) {
public void setMaxResults(@NonNull Long setterArg) {
if (setterArg == null) {
throw new IllegalStateException("Nonnull field \"maxResults\" is null.");
}
this.maxResults = setterArg;
}

Expand All @@ -318,11 +345,14 @@ public void setPageToken(@Nullable String setterArg) {
this.pageToken = setterArg;
}

/** Constructor is non-public to enforce null safety; use Builder. */
PigeonListOptions() {}

public static final class Builder {

private @Nullable Long maxResults;

public @NonNull Builder setMaxResults(@Nullable Long setterArg) {
public @NonNull Builder setMaxResults(@NonNull Long setterArg) {
this.maxResults = setterArg;
return this;
}
Expand All @@ -343,7 +373,7 @@ public static final class Builder {
}

@NonNull
ArrayList<Object> toList() {
public ArrayList<Object> toList() {
ArrayList<Object> toListResult = new ArrayList<Object>(2);
toListResult.add(maxResults);
toListResult.add(pageToken);
Expand Down Expand Up @@ -508,7 +538,7 @@ public static final class Builder {
}

@NonNull
ArrayList<Object> toList() {
public ArrayList<Object> toList() {
ArrayList<Object> toListResult = new ArrayList<Object>(6);
toListResult.add(cacheControl);
toListResult.add(contentDisposition);
Expand Down Expand Up @@ -611,7 +641,7 @@ public static final class Builder {
}

@NonNull
ArrayList<Object> toList() {
public ArrayList<Object> toList() {
ArrayList<Object> toListResult = new ArrayList<Object>(3);
toListResult.add(items);
toListResult.add(pageToken);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,6 @@
<string>arm64</string>
</array>
<key>MinimumOSVersion</key>
<string>9.0</string>
<string>11.0</string>
</dict>
</plist>
Loading

0 comments on commit 49542f3

Please sign in to comment.