-
Notifications
You must be signed in to change notification settings - Fork 902
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add StorageRulesManagerRegistry to handle rules files for multiple ta…
…rgets (#4281) * Address PR feedback; add JSDocs for new method * Make StorageRulesManager an interface; add StorageRulesManagerRegistry * Modify RulesetProvider to take resource parameter * Rebase; fix lint error * Change emulator arg to take SourceFile only * PR feedback, mostly re: API usage * Don't delete source file or ruleset on stop(); add CHANGELOG * Address PR feedback * Moved Storage rules integration tests under scripts/ (#4309) * Make StorageRulesManager an interface; add StorageRulesManagerRegistry * Modify RulesetProvider to take resource parameter * Change emulator arg to take SourceFile only * PR feedback, mostly re: API usage * Don't delete source file or ruleset on stop(); add CHANGELOG * Address PR feedback * Fix lint * Add test for failing to find rules for given resource
- Loading branch information
1 parent
1322164
commit c69b437
Showing
13 changed files
with
286 additions
and
144 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
- Adds support for configuration with multiple storage targets (#4281). |
117 changes: 67 additions & 50 deletions
117
scripts/storage-emulator-integration/rules/manager.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,92 +1,109 @@ | ||
import { expect } from "chai"; | ||
import { tmpdir } from "os"; | ||
import { v4 as uuidv4 } from "uuid"; | ||
|
||
import { FirebaseError } from "../../../src/error"; | ||
import { StorageRulesFiles, TIMEOUT_MED } from "../../../src/test/emulators/fixtures"; | ||
import { StorageRulesManager } from "../../../src/emulator/storage/rules/manager"; | ||
import { | ||
createTmpDir, | ||
StorageRulesFiles, | ||
TIMEOUT_LONG, | ||
} from "../../../src/test/emulators/fixtures"; | ||
import { | ||
createStorageRulesManager, | ||
StorageRulesManager, | ||
} from "../../../src/emulator/storage/rules/manager"; | ||
import { StorageRulesRuntime } from "../../../src/emulator/storage/rules/runtime"; | ||
import { Persistence } from "../../../src/emulator/storage/persistence"; | ||
import { RulesetOperationMethod } from "../../../src/emulator/storage/rules/types"; | ||
import { RulesetOperationMethod, SourceFile } from "../../../src/emulator/storage/rules/types"; | ||
import { isPermitted } from "../../../src/emulator/storage/rules/utils"; | ||
import { readFile } from "../../../src/fsutils"; | ||
|
||
describe("Storage Rules Manager", function () { | ||
const rulesRuntime = new StorageRulesRuntime(); | ||
const rulesManager = new StorageRulesManager(rulesRuntime); | ||
const rules = [ | ||
{ resource: "bucket_0", rules: StorageRulesFiles.readWriteIfTrue }, | ||
{ resource: "bucket_1", rules: StorageRulesFiles.readWriteIfAuth }, | ||
]; | ||
const opts = { method: RulesetOperationMethod.GET, file: {}, path: "/b/bucket_2/o/" }; | ||
let rulesManager: StorageRulesManager; | ||
|
||
// eslint-disable-next-line @typescript-eslint/no-invalid-this | ||
this.timeout(TIMEOUT_MED); | ||
this.timeout(TIMEOUT_LONG); | ||
|
||
before(async () => { | ||
beforeEach(async () => { | ||
await rulesRuntime.start(); | ||
|
||
rulesManager = createStorageRulesManager(rules, rulesRuntime); | ||
await rulesManager.start(); | ||
}); | ||
|
||
after(async () => { | ||
afterEach(async () => { | ||
rulesRuntime.stop(); | ||
await rulesManager.close(); | ||
await rulesManager.stop(); | ||
}); | ||
|
||
it("should load ruleset from SourceFile object", async () => { | ||
await rulesManager.setSourceFile(StorageRulesFiles.readWriteIfTrue); | ||
expect(rulesManager.ruleset).not.to.be.undefined; | ||
it("should load multiple rulesets on start", async () => { | ||
const bucket0Ruleset = rulesManager.getRuleset("bucket_0"); | ||
expect(await isPermitted({ ...opts, ruleset: bucket0Ruleset! })).to.be.true; | ||
|
||
const bucket1Ruleset = rulesManager.getRuleset("bucket_1"); | ||
expect(await isPermitted({ ...opts, ruleset: bucket1Ruleset! })).to.be.false; | ||
}); | ||
|
||
it("should load ruleset from file path", async () => { | ||
// Write rules to file | ||
const fileName = "storage.rules"; | ||
const testDir = `${tmpdir()}/${uuidv4()}`; | ||
const persistence = new Persistence(testDir); | ||
persistence.appendBytes(fileName, Buffer.from(StorageRulesFiles.readWriteIfTrue.content)); | ||
it("should load single ruleset on start", async () => { | ||
const otherRulesManager = createStorageRulesManager( | ||
StorageRulesFiles.readWriteIfTrue, | ||
rulesRuntime | ||
); | ||
await otherRulesManager.start(); | ||
|
||
await rulesManager.setSourceFile(`${testDir}/${fileName}`); | ||
const ruleset = otherRulesManager.getRuleset("default"); | ||
expect(await isPermitted({ ...opts, ruleset: ruleset! })).to.be.true; | ||
|
||
expect(rulesManager.ruleset).not.to.be.undefined; | ||
await otherRulesManager.stop(); | ||
}); | ||
|
||
it("should load ruleset on update with SourceFile object", async () => { | ||
expect(rulesManager.getRuleset("bucket_2")).to.be.undefined; | ||
await rulesManager.updateSourceFile(StorageRulesFiles.readWriteIfTrue, "bucket_2"); | ||
expect(rulesManager.getRuleset("bucket_2")).not.to.be.undefined; | ||
}); | ||
|
||
it("should set source file", async () => { | ||
await rulesManager.setSourceFile(StorageRulesFiles.readWriteIfTrue); | ||
const opts = { method: RulesetOperationMethod.GET, file: {}, path: "/b/bucket/o/" }; | ||
expect((await rulesManager.ruleset!.verify(opts)).permitted).to.be.true; | ||
await rulesManager.updateSourceFile(StorageRulesFiles.readWriteIfTrue, "bucket_2"); | ||
|
||
const issues = await rulesManager.setSourceFile(StorageRulesFiles.readWriteIfAuth); | ||
expect(await isPermitted({ ...opts, ruleset: rulesManager.getRuleset("bucket_2")! })).to.be | ||
.true; | ||
|
||
const issues = await rulesManager.updateSourceFile( | ||
StorageRulesFiles.readWriteIfAuth, | ||
"bucket_2" | ||
); | ||
|
||
expect(issues.errors.length).to.equal(0); | ||
expect(issues.warnings.length).to.equal(0); | ||
expect((await rulesManager.ruleset!.verify(opts)).permitted).to.be.false; | ||
expect(await isPermitted({ ...opts, ruleset: rulesManager.getRuleset("bucket_2")! })).to.be | ||
.false; | ||
}); | ||
|
||
it("should reload ruleset on changes to source file", async () => { | ||
const opts = { method: RulesetOperationMethod.GET, file: {}, path: "/b/bucket/o/" }; | ||
|
||
// Write rules to file | ||
const fileName = "storage.rules"; | ||
const testDir = `${tmpdir()}/${uuidv4()}`; | ||
const testDir = createTmpDir("storage-files"); | ||
const persistence = new Persistence(testDir); | ||
persistence.appendBytes(fileName, Buffer.from(StorageRulesFiles.readWriteIfTrue.content)); | ||
|
||
await rulesManager.setSourceFile(`${testDir}/${fileName}`); | ||
expect((await rulesManager.ruleset!.verify(opts)).permitted).to.be.true; | ||
const sourceFile = getSourceFile(testDir, fileName); | ||
await rulesManager.updateSourceFile(sourceFile, "bucket_2"); | ||
expect(await isPermitted({ ...opts, ruleset: rulesManager.getRuleset("bucket_2")! })).to.be | ||
.true; | ||
|
||
// Write new rules to file | ||
persistence.deleteFile(fileName); | ||
persistence.appendBytes(fileName, Buffer.from(StorageRulesFiles.readWriteIfAuth.content)); | ||
|
||
await rulesManager.setSourceFile(`${testDir}/${fileName}`); | ||
expect((await rulesManager.ruleset!.verify(opts)).permitted).to.be.false; | ||
}); | ||
|
||
it("should throw FirebaseError when attempting to set invalid source file", async () => { | ||
const invalidFileName = "foo"; | ||
await expect(rulesManager.setSourceFile(invalidFileName)).to.be.rejectedWith( | ||
FirebaseError, | ||
`File not found: ${invalidFileName}` | ||
); | ||
}); | ||
|
||
it("should delete ruleset when storage manager is closed", async () => { | ||
await rulesManager.setSourceFile(StorageRulesFiles.readWriteIfTrue); | ||
expect(rulesManager.ruleset).not.to.be.undefined; | ||
|
||
await rulesManager.close(); | ||
expect(rulesManager.ruleset).to.be.undefined; | ||
expect(await isPermitted(opts)).to.be.false; | ||
}); | ||
}); | ||
|
||
function getSourceFile(testDir: string, fileName: string): SourceFile { | ||
const filePath = `${testDir}/${fileName}`; | ||
return { name: filePath, content: readFile(filePath) }; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.