Skip to content
Open
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
27 changes: 26 additions & 1 deletion src/init/features/storage.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import * as sinon from "sinon";
import { Config } from "../../config";
import { askQuestions, actuate } from "./storage";
import * as prompt from "../../prompt";

import * as gcp from "../../gcp";
describe("storage", () => {
const sandbox: sinon.SinonSandbox = sinon.createSandbox();
let askWriteProjectFileStub: sinon.SinonStub;
Expand Down Expand Up @@ -39,4 +39,29 @@ describe("storage", () => {
expect(_.get(setup, "config.storage.rules")).to.deep.equal("storage.rules");
});
});
it("should download rules from console", async () => {
const setup = {
config: {},
rcfile: { projects: {}, targets: {}, etags: {} },
projectId: "test-project",
instructions: [],
};
const config = new Config({}, { projectDir: "test", cwd: "test" });
const getRulesetNameStub = sandbox
.stub(gcp.rules, "getLatestRulesetName")
.resolves("ruleset-name");
const getRulesetContentStub = sandbox
.stub(gcp.rules, "getRulesetContent")
.resolves([{ name: "file.rules", content: "console rules" }]);
const writeStub = sandbox.stub(config, "confirmWriteProjectFile").resolves(true);
promptStub.returns("storage.rules");

await askQuestions(setup, config);
await actuate(setup, config);

expect(getRulesetNameStub.calledOnceWith("test-project", "firebase.storage")).to.be.true;
expect(getRulesetContentStub.calledOnceWith("ruleset-name")).to.be.true;
expect(writeStub.calledOnceWith("storage.rules", "console rules")).to.be.true;
expect(_.get(setup, "featureInfo.storage.rules")).to.equal("console rules");
});
});
27 changes: 25 additions & 2 deletions src/init/features/storage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ import { readTemplateSync } from "../../templates";
import { Config } from "../../config";
import { Setup } from "..";
import { FirebaseError } from "../../error";

import * as gcp from "../../gcp";
import * as utils from "../../utils";
export interface RequiredInfo {
rulesFilename: string;
rules: string;
Expand All @@ -15,7 +16,6 @@ export interface RequiredInfo {

const RULES_TEMPLATE = readTemplateSync("init/storage/storage.rules");
const DEFAULT_RULES_FILE = "storage.rules";

export async function askQuestions(setup: Setup, config: Config): Promise<void> {
logger.info();
logger.info("Firebase Storage Security Rules allow you to define how and when to allow");
Expand All @@ -32,6 +32,14 @@ export async function askQuestions(setup: Setup, config: Config): Promise<void>
message: "What file should be used for Storage Rules?",
default: DEFAULT_RULES_FILE,
});
if (setup.projectId) {
const downloadedRules = await getRulesFromConsole(setup.projectId);
if (downloadedRules) {
info.rules = downloadedRules;
utils.logBullet(`Downloaded the existing Storage Security Rules from the Firebase console`);
}
}

info.writeRules = await config.confirmWriteProjectFile(info.rulesFilename, info.rules);
// Populate featureInfo for the actuate step later.
setup.featureInfo = setup.featureInfo || {};
Expand All @@ -54,3 +62,18 @@ export async function actuate(setup: Setup, config: Config): Promise<void> {
config.writeProjectFile(info.rulesFilename, info.rules);
}
}
async function getRulesFromConsole(projectId: string): Promise<string | null> {
const name = await gcp.rules.getLatestRulesetName(projectId, "firebase.storage");
if (!name) {
return null;
}
const rules = await gcp.rules.getRulesetContent(name);
if (rules.length === 0) {
return utils.reject("Ruleset has no files", { exit: 1 });
}

if (rules.length > 1) {
return utils.reject(`Ruleset has too many files: ${rules.length}`, { exit: 1 });
}
return rules[0].content;
}