Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(storage): ensure emulator is used for different storage buckets #7892

Merged
merged 3 commits into from
Jul 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion .github/workflows/scripts/storage.rules
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
rules_version = '2';
service firebase.storage {
match /b/{bucket}/o {
match /b/react-native-firebase-testing.appspot.com/o {
match /{document=**} {
allow read, write: if false;
}
Expand All @@ -18,4 +18,10 @@ service firebase.storage {
allow read, write: if true;
}
}

match /b/react-native-firebase-testing/o {
match /only-second-bucket/{document=**} {
allow read, write: if true;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -281,12 +281,15 @@ public void setMaxUploadRetryTime(String appName, double milliseconds, Promise p
* @link https://firebase.google.com/docs/reference/js/firebase.storage.Storage#useEmulator
*/
@ReactMethod
public void useEmulator(String appName, String host, int port, Promise promise) {
public void useEmulator(String appName, String host, int port, String bucketUrl, Promise promise) {
FirebaseApp firebaseApp = FirebaseApp.getInstance(appName);
FirebaseStorage firebaseStorage = FirebaseStorage.getInstance(firebaseApp);
if (emulatorConfigs.get(appName) == null) {

FirebaseStorage firebaseStorage = FirebaseStorage.getInstance(firebaseApp, bucketUrl);
String emulatorKey = appName + ":" + bucketUrl;

if (emulatorConfigs.get(emulatorKey) == null) {
firebaseStorage.useEmulator(host, port);
emulatorConfigs.put(appName, "true");
emulatorConfigs.put(emulatorKey, "true");
}
promise.resolve(null);
}
Expand Down
38 changes: 38 additions & 0 deletions packages/storage/e2e/StorageReference.e2e.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,44 @@ describe('storage() -> StorageReference', function () {
await seed(PATH);
});

describe('second storage bucket writes to Storage emulator', function () {
let secondStorage;
// Same bucket defined in app.js when setting up emulator
const secondStorageBucket = 'gs://react-native-firebase-testing';

before(function () {
const { getStorage } = storageModular;
secondStorage = getStorage(firebase.app(), secondStorageBucket);
});

it('should write a file to the second storage bucket', async function () {
const { ref } = storageModular;

// "only-second-bucket" is not an allowable path on live project for either bucket
const storageReference = ref(secondStorage, 'only-second-bucket/ok.txt');

await storageReference.putString('Hello World');
});

it('should throw exception on path not allowed on second bucket security rules', async function () {
const { ref } = storageModular;

// "react-native-firebase-testing" is not an allowed on second bucket, only "ony-second-bucket"
const storageReference = ref(
secondStorage,
'react-native-firebase-testing/should-fail.txt',
);

try {
await storageReference.putString('Hello World');
return Promise.reject(new Error('Did not throw'));
} catch (error) {
error.code.should.equal('storage/unauthorized');
return Promise.resolve();
}
});
});

describe('firebase v8 compatibility', function () {
describe('toString()', function () {
it('returns the correct bucket path to the file', function () {
Expand Down
8 changes: 7 additions & 1 deletion packages/storage/e2e/helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ exports.seed = async function seed(path) {
storage: {
rules: `rules_version = '2';
service firebase.storage {
match /b/{bucket}/o {
match /b/react-native-firebase-testing.appspot.com/o {
match /{document=**} {
allow read, write: if false;
}
Expand All @@ -35,6 +35,12 @@ exports.seed = async function seed(path) {
allow read, write: if true;
}
}

match /b/react-native-firebase-testing/o {
match /only-second-bucket/{document=**} {
allow read, write: if true;
}
}
}`,
host: getE2eEmulatorHost(),
port: 9199,
Expand Down
21 changes: 14 additions & 7 deletions packages/storage/ios/RNFBStorage/RNFBStorageModule.m
Original file line number Diff line number Diff line change
Expand Up @@ -511,12 +511,15 @@ - (void)invalidate {
RCT_EXPORT_METHOD(useEmulator
: (FIRApp *)firebaseApp
: (nonnull NSString *)host
: (NSInteger)port) {
: (NSInteger)port
: (NSString *)bucketUrl) {
emulatorHost = host;
emulatorPort = port;
if (!emulatorConfigs[firebaseApp.name]) {
[[FIRStorage storageForApp:firebaseApp] useEmulatorWithHost:host port:port];
emulatorConfigs[firebaseApp.name] = @YES;
NSString *key = [self createEmulatorKey:bucketUrl appName:firebaseApp.name];

if (!emulatorConfigs[key]) {
[[FIRStorage storageForApp:firebaseApp URL:bucketUrl] useEmulatorWithHost:host port:port];
emulatorConfigs[key] = @YES;
}
}

Expand Down Expand Up @@ -557,6 +560,10 @@ - (void)invalidate {
#pragma mark -
#pragma mark Firebase Storage Internals

- (NSString *)createEmulatorKey:(NSString *)bucketUrl appName:(NSString *)appName {
return [NSString stringWithFormat:@"%@-%@", appName, bucketUrl];
}

- (void)addUploadTaskObservers:(FIRStorageUploadTask *)uploadTask
appDisplayName:(NSString *)appDisplayName
taskId:(NSNumber *)taskId
Expand Down Expand Up @@ -675,11 +682,11 @@ - (FIRStorageReference *)getReferenceFromUrl:(NSString *)url app:(FIRApp *)fireb
storage = [FIRStorage storageForApp:firebaseApp URL:bucket];

NSLog(@"Setting emulator - host %@ port %ld", emulatorHost, (long)emulatorPort);
if (![emulatorHost isEqual:[NSNull null]] && emulatorHost != nil &&
!emulatorConfigs[firebaseApp.name]) {
NSString *key = [self createEmulatorKey:bucket appName:firebaseApp.name];
if (![emulatorHost isEqual:[NSNull null]] && emulatorHost != nil && !emulatorConfigs[key]) {
@try {
[storage useEmulatorWithHost:emulatorHost port:emulatorPort];
emulatorConfigs[firebaseApp.name] = @YES;
emulatorConfigs[key] = @YES;
} @catch (NSException *e) {
NSLog(@"WARNING: Unable to set the Firebase Storage emulator settings. These must be set "
@"before any usages of Firebase Storage. If you see this log after a hot "
Expand Down
2 changes: 1 addition & 1 deletion packages/storage/lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@ class FirebaseStorageModule extends FirebaseModule {
}
this.emulatorHost = host;
this.emulatorPort = port;
this.native.useEmulator(_host, port);
this.native.useEmulator(_host, port, this._customUrlOrRegion);
return [_host, port]; // undocumented return, just used to unit test android host remapping
}
}
Expand Down
4 changes: 3 additions & 1 deletion tests/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -82,8 +82,10 @@ function loadTests(_) {
// as data from previous runs pollutes following runs until re-install the app. Clear it.
firebase.firestore().clearPersistence();
}
if (platformSupportedModules.includes('storage'))
if (platformSupportedModules.includes('storage')) {
firebase.storage().useEmulator('localhost', 9199);
firebase.app().storage('gs://react-native-firebase-testing').useEmulator('localhost', 9199);
}
});

afterEach(async function afterEachTest() {
Expand Down
Loading