firebase / firebase-admin-node

Firebase Admin Node.js SDK
https://firebase.google.com/docs/admin/setup
Apache License 2.0
1.63k stars 371 forks source link

FR: [Database] Allow getting and setting the security rules #504

Closed jsayol closed 5 years ago

jsayol commented 5 years ago

[REQUIRED] Step 2: Describe your environment

[REQUIRED] Step 3: Describe the problem

It would be useful to be able to get and set the database security rules through the Admin SDK.

Workaround

I'm currently calling the REST API to read and write the database rules, obtaining the admin access token from admin.app().INTERNAL.getToken() (although I could also do it properly by getting an access token through a service account file, for example).

Steps to reproduce:

n/a

Relevant Code:

n/a

jsayol commented 5 years ago

Off the top of my head, a possible solution may be to add a couple of methods .getRules() and .setRules() to admin.database(), which would internally call the REST API to get and set the security rules.

If you're open to adding a couple such methods to the Admin SDK API, I'd be happy to look further into it and submit a PR.

hiranya911 commented 5 years ago

Hi @jsayol. Interestingly enough we have had some early discussions about such a feature. Are you more interested in RTDB rules or Firestore rules?

hiranya911 commented 5 years ago

@schmidt-sebastian wdyt?

jsayol commented 5 years ago

Hey @hiranya911, that's good to hear! I'm more interested in RTDB rules at the moment, but I would also like to be able to do it for Firestore rules. I just haven't started looking into what the current options are for the latter.

jsayol commented 5 years ago

I've been thinking about my initial suggestion of implementing this under admin.database() and that might not be the best idea.

The first call to that method instantiates a Database instance, which in turn will have several side effects like establishing a network connection to the database backend. In cases where you just want to get or set the rules, you don't need that connection to happen so it might be a waste of resources. Plus, if these methods were to be present in the Database object, then they would either need to be implemented in the @firebase/database package (which would bloat the client SDK unnecessarily) or they would need to be monkey-patched by the admin SDK after instantiating the database. None of those options sound very appealing to me.

A better option in my opinion would be to add this as part of the ProjectManagement namespace. While rule handling is not technically part of the Project Management API, I think it does fit well in there for the purpose of the admin SDK. So, just an idea:

// For RTDB
admin.projectManagement().getDatabaseRules();
admin.projectManagement().setDatabaseRules();

// For Firestore
admin.projectManagement().getFirestoreRules();
admin.projectManagement().setFirestoreRules();

I think this would be the cleanest option. Thoughts?

jsayol commented 5 years ago

Here's a possible implementation for getting and setting the RTDB rules: https://github.com/jsayol/firebase-admin-node/compare/master...jsayol:rtdb-rules

Let me know if this is an acceptable solution and I'll send a PR with these changes.

jsayol commented 5 years ago

I took a look at how deploymet of Firestore rules works and it seems slightly more complicated, since it involves versioning/releases. Besides setting and getting the current rules, the SDK could also offer a way to get a list of releases and to get the contents of a specific one.

To keep things simple I would start with the basic get and set, like with the RTDB rules, and then maybe add those extra features.

The good news here is that since Firestore and Storage rules work the same way through the same API, once the Firestore methods are implemented it will be trivial to add support for Storage with something like .getStorageRules() and .setStorageRules().

hiranya911 commented 5 years ago

Thanks @jsayol. Just a few random points of information, in no particular order:

  1. Up until now we were mostly interested in adding an API for managing Firebase security rules (i.e. Firestore and Storage). RTDB rules are bit of a black sheep (different syntax, different API), but it's not terribly difficult to support them in a new Admin API.
  2. Adding new methods to admin.database() is not really feasible. So admin.projectManagement() is probably the way to go, and is more in-lined with what I had in mind.
  3. This effort needs some buy-in from the Firebase Rules team. I would let them make the final call on whether to actually support this or not (@rachelmyers do you have any thoughts?)
  4. All new APIs should go through the Firebase API review process. So lets wait until we have a more fleshed out API design, before we start writing any code. I do have some preliminary designs on my computer at work. I'll add them here when I get back to my desk on Monday.
schmidt-sebastian commented 5 years ago

@jsayol Thanks for this suggestion! I am very supportive, but would like to trick you into also thinking about the rule upload/retrieval for Firebase Storage. If we cover Firestore, the RTDB and Storage, then this feature would be exhaustive and indeed a very good candidate for the Project Management API.

With that said, I think we should also consider a more generic API that allows us to set the rules for any service (e.g. instead of setDatabaseRules/setStorageRules/setFirestoreRules, we might want to expose setRules(service: string, rules:string)). We will also need to come up with APIs for Java Project Management SDK and Python.

hiranya911 commented 5 years ago

I've been thinking along the following lines:

Ruleset
    id: string
    createTime: string
    rulesFiles: RulesFile[]
    firestoreRules?: RulesFile
    storageRules?: RulesFile
RulesFile
    name: string
    content: string
ProjectManagement
    rulesFile(name: string, content: string): RulesFile
    rulesFileFromPath(path: string): RulesFile
    getRulesets(): Promise<Ruleset[]>
    getRuleset(id: string): Promise<Ruleset>
    updateRuleset(rulesFiles: RulesFile[]): : Promise<FirebaseRuleset>
    updateFirestoreRules(content: string): Promise<Ruleset>
    updateStorageRules(content: string): Promise<Ruleset>
    deleteRuleset(id: string): Promise<void>
    getCurrentFirestoreRules(): Promise<RulesFile>

Doesn't take RTDB into consideration. But we can certainly add it to the mix.

rachelmyers commented 5 years ago

This is interesting. What use case are you solving for? It sounds like you're dynamically updating Security Rules? If so, I'm curious what kinds of rules you're updating.

My initial take is that fetching the current rules would be straightforward. Setting the rules could lead to unexpected behavior, because an update takes several seconds; and I have some concerns about the workflows it enables. To the first point, if a developer updates the rules and then immediately hits the database, they'll still get the old rules. This isn't a new problem, but because most rules updates are manual right now, via the console or CLI, it's pretty minor.

To the second point, we want to encourage developers to test their rules and to gate rules deploys on those tests passing. Updating rules on the fly could streamline interesting functionality, but also makes it easier than ever to deploy rules that aren't understood or tested. For both concerns, it would help to understand the use cases.

jsayol commented 5 years ago

This is interesting. What use case are you solving for? It sounds like you're dynamically updating Security Rules? If so, I'm curious what kinds of rules you're updating.

Hi @rachelmyers! That's what I'm doing, updating the RTDB rules. The use case might be a bit tricky to explain here, since it's in relation to a Firebase product that's still in EAP and not public. The gist of it is that I have a set of Cloud Functions that get deployed to a user's project. These functions need to store some temporary data in the database (at a path chosen by the user when deploying the functions). Since this temporary data can contain sensitive information like auth tokens, I want to ensure that the path is secured with the appropriate security rules. For that purpose I included an initialization function that updates said rules for the path chosen by the user, as long as these don't already exist.

I'm not sure if this makes much sense, so feel free to reach out privately and I can provide more details.

Setting the rules could lead to unexpected behavior, because an update takes several seconds; and I have some concerns about the workflows it enables. To the first point, if a developer updates the rules and then immediately hits the database, they'll still get the old rules.

As long as this behavior is properly documented, I don't think this should be a blocker. As an example, we find a very similar behavior when updating a deployed Cloud Function. The newly deployed code for the function might take several seconds to propagate, so if a developer triggers it immediatley after updating it they might still hit the old version. In short, I don't think this should be an issue as long as it's well documented.

To the second point, we want to encourage developers to test their rules and to gate rules deploys on those tests passing. Updating rules on the fly could streamline interesting functionality, but also makes it easier than ever to deploy rules that aren't understood or tested.

I understand the concern and I agree that rules should be properly tested before being deployed, but that's really the developer's responsibility. It's also important to keep in mind that this quick workflow is already perfectly possible using the CLI. A developer can make changes to their local Firestore rules file, for example, and immediatley deploy them with firebase deploy --only firestore without any prior testing. This workflow is also possible through the Firebase Console, for that matter, which is arguably even easier.

Anyway, these are just my 2 cents about this :)

jsayol commented 5 years ago

@hiranya911 & @schmidt-sebastian I like that approach of going for a more generic API, although I think it should be simplified.

I expect the most common operations to be setting new rules and getting the current ones, so those should be the main methods.

At the moment it's not possible to use more than one rules file. And even though the backend API seems to allow it, neither the Console nor the CLI support this feature, plus it's not documented what the behavior would be in that case.

I think that rulesets, rulesets files, and releases should be treated as an internal implementation detail that don't need to be exposed to the user.

So with that in mind, here's my simplified API proposal (I'll be using TS syntax):

type Service = 'firestore' | 'storage' | 'database';

interface ProjectManagement {
  /**
   * Gets the current rules for the service:
   *   - For RTDB that's whatever `.settings/rules.json` returns.
   *   - For Firestore/Storage it's the contents of the ruleset for the release
   *     associated with that service.
   */
  getRules(service: Service): Promise<string>;

  /**
   * Sets the new rules to be used with the service:
   *   - For RTDB it PUTs them to `.settings/rules.json`.
   *   - For Firestore/Storage it creates a new ruleset with the specified
   *     content and updates/creates the appropriate release for the
   *     service with that ruleset.
   */
  setRules(service: Service, content: string): Promise<void>;

  /**
   * Like `setRules()` but reads the rules content from a file.
   * (Just for convenience for the user, but this one could be skiped)
   */
  setRulesFromFile(service: Service, filePath: string): Promise<void>;
}

The only drawback I see is that if in the future you decide to start supporting multiple rules files, then the signature of the getRules() method would need to change to return Promise<string[]> instead. I see 2 options:

  1. Leave it as a single string for now, and change it to an array of strings once multiple rules files are officially suported. This would become a breaking change.
  2. Introduce it as an array from the beginning and explain that for now it contains a single element.

The decision on which path to take largely depends on how likely it is that you'll support multiple rules files in the future. Since the functionality is potentially already there, I'd say this is quite likely so option 2 might be best. For setRules() the method could be overloaded to accept both content: string and contents: string[], so this is not as much of a concern in this case.


Leaving that aside, if you also want to introduce some methods so that the user can directly manipulate rulesets and releases then I would propose this API, which pretty much maps directly to what the backend offers:

interface Ruleset {
  /**
   * The name of the ruleset.
   * Format: `projects/{projectId}/rulesets/{uuid}`
   */
  name: string;

  /**
   * Timestamp in ISO 8601 format.
   */
  createTime: string;

  /**
   * Array of ruleset files.
   * This is only present when getting a specific Ruleset, but not when
   * listing them with `listRulesets()`.
   */
  files?: RulesetFile[];
}

interface RulesetFile {
  /**
   * Name of the file.
   */
  name: string;

  /**
   * The content of the file (the actual rules).
   */
  content: string;
}

/**
 * Possible filters when listing releases. All optional and can be combined.
 */
interface ListRulesReleasesFilter {
  releaseName?: string;
  rulesetName?: string;
  testSuiteName?: string; // Maybe not? Totally undocumented AFAIK.
}

/**
 * The result of listing rules releases.
 */
interface ListRulesReleasesResult {
  releases: RulesRelease[];
  pageToken?: string;
}

interface RulesRelease {
  /**
   * The name of the release.
   * Format: `projects/{projectId}/releases/{id}`.
   * `id` would be either `cloud.storage` or `cloud.firestore`.
   */
  name: string;

  /**
   * Name of the ruleset associated with the release.
   * Format: `projects/{projectId}/rulesets/{uuid}`
   */
  rulesetName: string;

  /**
   * Timestamp in ISO 8601 format.
   */
  createTime: string;

  /**
   * Timestamp in ISO 8601 format.
   * Note: this might only be present if the release has been updated, I'm
   * not sure. Should check.
   */
  updateTime: string;
}

/**
 * The result of listing rulesets.
 */
interface ListRulesetsResult {
  rulesets: Ruleset[];
  pageToken?: string;
}

interface ProjectManagement {
  /**
   * Gets the list of rules releases for the project.
   * 
   * It optionally accepts an object specifying filters to use.
   *
   * The maximum number of rulesets to return is determined by the optional
   * `maxResults` argument. Defaults to 10, maximum is 100 (according to API docs).
   */
  listRulesReleases(
    filter?: ListRulesReleasesFilter,
    maxResults?: number,
    pageToken?: string
  ): Promise<ListRulesReleasesResult>;

  /**
   * Gets the named rules release.
   */
  getRulesRelease(name: string): Promise<RulesRelease>;

  /**
   * Creates a new rules release with the given name and associated to
   * the given ruleset name.
   */
  createRulesRelease(name: string, rulesetName: string): Promise<RulesRelease>;

  /**
   * Deletes the named rules release.
   * Note: I'm not sure what happens when you do this. For example, if you
   * delete the release for Firestore rules, does Firestore stop working?
   * This method's behavior should be properly documented if it's included.
   */
  deleteRulesRelease(name: string): Promise<void>;

  /**
   * Gets the list of rulesets for the project. The Rulesets only contain
   * metadata (`name` and `createTime`), not the actual files.
   *
   * The maximum number of rulesets to return is determined by the optional
   * `maxResults` argument. Defaults to 10, maximum is 100 (according to API docs).
   *
   * It optionally accepts a pageToken returned from a previous call, in order
   * to get the next set of results if there's more.
   */
  listRulesets(
    maxResults?: number,
    pageToken?: string
  ): Promise<ListRulesetsResult>;

  /**
   * Gets the named ruleset. The returned Ruleset contains its files.
   */
  getRuleset(name: string): Promise<Ruleset>;

  /**
   * Creates a new ruleset with the given files.
   */
  createRuleset(files: RulesetFile[]): Promise<Ruleset>;

  /**
   * Deletes the named rules ruleset.
   * Note: I'm not sure what happens when you do this. For example, if you
   * delete the ruleset currently associated with the release for Firestore
   * rules, does Firestore stop working? Is the release automatically
   * associated with the most recent previous ruleset? No idea.
   * This method's behavior should be properly documented if it's included.
   */
  deleteRuleset(name: string): Promise<void>;
}

I've represented the names in their long form just as they are returned by the backend API, but for simplicity maybe the SDK could replace them with their "short" form. So for example, when returning a list of Ruleset from listRulesets(), their name could be replaced from projects/{projectId}/rulesets/{uuid} to just uuid. If we go this route, then this should also be taken into account in methods that take a name. For example, getRuleset(name) would take a uuid as the name and internally convert it to projects/{projectId}/rulesets/{uuid}.

P.S.: Everything I've written here is all based on the little scrap of documentation I found, so some adjustments might be necessary.

jsayol commented 5 years ago

Just a quick amendment to my proposal. I think the Ruleset interface should be split in 2, one that includes files and one that doesn't, and used accordingly. For example:

interface Ruleset {
  /**
   * The name of the ruleset.
   * Format: `projects/{projectId}/rulesets/{uuid}`
   */
  name: string;

  /**
   * Timestamp in ISO 8601 format.
   */
  createTime: string;
}

interface RulesetWithFiles extends Ruleset {
  /**
   * Array of ruleset files.
   * This is only present when getting a specific Ruleset, but not when
   * listing them with `listRulesets()`.
   */
  files: RulesetFile[];
}

interface ProjectManagement {
  // ...
  getRuleset(name: string): Promise<RulesetWithFiles>;
  // ...
}

Or something along those lines.

rachelmyers commented 5 years ago

Quick correction:

At the moment it's not possible to use more than one rules file

This is true for Firestore. Storage supports different rules files for each storage bucket. The RealTime Database supports multiple instances per project, where each instance has it's own rules file. It's fair that these are not as documented as they should be.

jsayol commented 5 years ago

Ah, you're right! If I'm not mistaken though, this shouldn't be an issue since the options object passed to admin.initializeApp() already contains the databaseURL and storageBucket to use, or the default ones are picked otherwise. Would that cover it?

Maybe I misunderstood, though. When I said that it's not possible to use more than one rules file I was referring to uploading 2 or more separate rules files that would apply to the same instance of a service. I initially mentioned this since the Firebase Rules API seems to allow multiple files per Ruleset, which then is associated with a Release and applied to a service. ~For example, I truly have no idea what would happen if a Ruleset with multiple files were associated to the cloud.firestore release of a project.~ (it returns a 400 "invalid argument" error, so this is definitely not allowed)

P.S: This is totally off-topic @rachelmyers but I just stumbled upon your #MeetFirebase interview with Doug, and Louise the sock puppet was hilarious haha :grin:

rachelmyers commented 5 years ago

Yeah, you can absolutely implement these methods with multiple files. My bottom line is that I think this enables interesting workflows and would like to see it implemented.

One thing to be aware of: There's a max number of Rulesets for Firestore and Storage instances (in the hundreds), and when someone hits it, they can't update their rules until they delete an old rules version. (There are buttons in the console to delete old Firestore rules and a delete Ruleset API for both Firestore and Storage.) Ideally, the SDK would let people clean up as well as create Rulesets. I don't want to block this on also solving deletes, but want to flag the problem.

P.S. Thanks. Louise is definitely a handful.

jsayol commented 5 years ago

One thing to be aware of: There's a max number of Rulesets for Firestore and Storage instances (in the hundreds), and when someone hits it, they can't update their rules until they delete an old rules version. [...] Ideally, the SDK would let people clean up as well as create Rulesets.

That's a good point. I think this means the SDK should definitely include the "extended" API to allow managing releases and rulesets, besides the getRules() and setRules() methods.

The CLI handles the quota exceeded error by offering the user to delete the 10 oldest unreleased rulesets (as in, the ones that are not currently associated with a release). I think the admin SDK should simply return the appropriate error so the developer can decide how to handle it. So for example it could allow writing something like this:

async function updateFirestoreRules() {
  const myRules = getMyNewFirestoreRules();

  try {
    return admin.projectManagement().setRules("firestore", myRules);
  } catch (err) {
    if (err.code === 'project-management/resource-exhausted') {
      // Delete old rulesets
      await cleanUpMyOldestRulesets();

      // Try again
      return updateFirestoreRules();
    } else {
      throw err;
    }
  }
}

async function cleanUpMyOldestRulesets() {
  // list rulesets, filter them, delete oldest ones, etc
}

Just an example of how this could be handled but you get the idea. Does that sound like an acceptable solution? Alternatively, the SDK could provide a helper function to delete the X oldest unreleased rulesets, but I think this can be added later if it's necessary.

If it's OK with you all, I'm gonna have a go at implementing this API :)

Louise is definitely a handful.

https://www.youtube.com/watch?v=H_NG1yXT6QY

jsayol commented 5 years ago

Hey @rachelmyers, I've got a few questions about the rules API. I want to make sure I handle the responses correctly to detect any possible errors :)

  1. When listing rulesets: if there are none, ~does it return a rulesets field with an empty array or is the rulesets field not present in the response?~ (I assume the behavior is the same as with releases, where the field is not present.)

  2. When listing releases: 2.1. If there are none, ~does it return a releases field with an empty array or is the releases field not present in the response?~ (The field is not present.) 2.2. I've found no docs about the test_suite_name filter. Should I allow using it, or should this be ignored for now? 2.3. How should I pass more than one filter in the filters field? For example, which one would be correct:

    • name=prod*; ruleset_name=123*
    • name=prod*, ruleset_name=123*
    • name=prod* ruleset_name=123*
    • Something else?
  3. ~When deleting a ruleset: If I delete a ruleset that is currently associated with a release, ~what happens? Does the rules API return some relevant error? If not, should I check first or is this something that should be allowed?~ (Ok, backend rejects with a 400 error.)

  4. When deleting a release: If I delete a release that is currently being used (for example the projects/{projectId}/releases/cloud.firestore release), ~what happens? Does the rules API return some relevant error? If not, should I check first or is this something that should be allowed?~ (The backend allows it, which is a bit surprising. For Firestore (haven't tried for storage) the rules that came with that release remain applied even if the release is deleted, though.)

  5. When listing and getting releases: if a release has never been updated, ~is the updateTime field present with null? With an empty string? Or is it not present in the response?~ (It's present with the same value as the createTime field.)

That's all, thanks!

jsayol commented 5 years ago

Since I managed to figure out most of the questions I had, I just opened the Pull Request: https://github.com/firebase/firebase-admin-node/pull/507

If the API proposed there seems acceptable, I can look into adapting in to the Java and Python admin SDKs.

rachelmyers commented 5 years ago

I had the concern that programmatic rules changes don't allow you to use a TDD workflow when writing rules. I want to offer one option that would allow programmatic rules updates and testing rules: If we expose the TestRuleset API, it would enable people to test a Ruleset change, wait for the response, and if it succeeds, update the rules. The TestRuleset API takes an array of use cases and returns an array of results, so it could be one test or a small suite. What do you think?

hiranya911 commented 5 years ago

Personally I think it's a very good addition (and something we should do). But in that case we should probably house the Admin SDK rules API in a whole new module (admin.rules()). That gives us more room for further rules-related integrations in the future, without any impact on the existing projectManagement module.

jsayol commented 5 years ago

That sounds like a great idea @rachelmyers! I see 2 ways to approach this:

Option 1: Make it integral to the setRules() method

The method would take the new rules to apply along with the array of test cases, create a new ruleset, test it, and if the tests all succeed then immediately apply the new ruleset to the appropriate release.

For the developer it would work like this:

async function updateRules {
  const rules = '/* contents of the new rules */';
  const tests = [
    {/* Test 1 */},
    {/* Test 2 */},
    {/*  ....  */},
  ];
  try {
    await something.setRules('firestore', rules, tests);
    console.log('Rules updated!');
  } catch (error) {
    console.error('Something went wrong:', error);
  }
}

If going this route, would it be compulsory to pass at least one test case in order to update the rules? Or should the method also allow updating them without any tests?

Option 2: Make it into its own method

There would be an additional method that would test a ruleset and return the results of those tests, and once the result is successful the developer would update the rules.

It could look approximately like this:

async function updateRules {
  const rules = '/* contents of the new rules */';
  const tests = [
    {/* Test 1 */},
    {/* Test 2 */},
    {/*  ....  */},
  ];

  try {
    const ruleset = await something.createRuleset(rules);
    const rulesetTest = await something.testRuleset(ruleset.id, tests);

    if (rulesetTest.isSuccessful()) {
      console.log('Ruleset tests passed successfuly');
      await something.applyRuleset('firestore', ruleset.id);
      console.log('Rules updated!');
    } else {
      console.error('Ruleset tests failed:', rulesetTest.errors);
    }
  } catch (error) {
    console.error('Something went wrong:', error);
  }
}

In this case, should testing the ruleset be mandatory before applying it to a service/release?

These options aside, I like @hiranya911's idea of grouping all rules-related methods into its own rules module rather than polluting the projectManagement one :)

rachelmyers commented 5 years ago

Testing should be possible but not mandatory; if it's mandatory, people will write fake if true kind of tests to enable their workflow. 😏 I like the idea of building out a new admin.rules module, and I'm happy to help out with that after next week.

jsayol commented 5 years ago

Yeah, you're definitely right. Is there any public documentation on the TestRuleset API so that I can start looking into it? And which of the 2 approaches do you think would work best? I guess the first one is the simplest for the developer, specially if you want to prevent exposing the whole rulesets/releases internals.

hiranya911 commented 5 years ago

@rachelmyers and I have been pushing this forward. We still have ways to go but for now this is what we are thinking:

* admin.securityRules(app?: admin.App): admin.securityRules.SecurityRules
* admin.App.securityRules(): admin.securityRules.SecurityRules
* admin.securityRules.SecurityRules [Class]
    - getDatabaseRules(url?: string): Promise<string>
    - setDatabaseRules(content: string | Buffer, url?: string): Promise<void>
    - getFirestoreRules(): Promise<string>
    - setFirestoreRules(content: string | Buffer): Promise<void>
    - getStorageRules(bucket?: string): Promise<string>
    - setStorageRules(content: string | Buffer, bucket?: string): Promise<void>
    - getRuleset(id: string): Promise<Ruleset>
    - listFirestoreHistory(pageSize = MAX_100, pageToken?: string): Promise<ListRulesetHistory>
    - listStorageHistory(pageSize = MAX_100, pageToken?: string): Promise<ListRulesetHistory>
    - testFirestoreRules(rules: string | Buffer, testCases: TestCase[]): Promise<TestResults>
    - testStorageRules(rules: string | Buffer, testCases: TestCase[]): Promise<TestResults>
* admin.securityRules.Ruleset
    - name: string
    - createTime: string
    - getContent(): Promise<string>
    - delete(): Promise<void>
* admin.securityRules.ListRulesetHistory
    - ruleSets: Ruleset[]
    - pageToken?: string

Following types are proposed to support the rules testing use cases:

* admin.securityRules.TestCase
    - expectation: ‘allow’ | ‘deny’
    - method: ‘get’ | ‘create’ | ‘update’ | ‘delete’
    - path: string
    - auth?: {uid?: string, token?: object, {[key: string]: any}}
    - name?: string
* admin.securityRules.TestResults
    - results: TestResult[]
    - successCount: number
    - failureCount: number
* admin.securityRules.TestResult
    - state: ‘success’ | ‘failure’ | ‘error’
    - visitedExpressions: VisitedExpression[]
    - name?: string
* admin.securityRules.VisitedExpression
    - value: boolean
    - sourcePosition: {line: number, column: number} 
rachelmyers commented 5 years ago

Great news! We can move forward with the API changes for the SDK, with a few modifications:

The SDK changes we can go forward with now are:

* admin [Existing namespace]

    - securityRules(app?: admin.App): admin.securityRules.SecurityRules

* admin.App [Existing class]

    - securityRules(): admin.securityRules.SecurityRules

* admin.securityRules.SecurityRules [Class]

    - getFirestoreRuleset(): Promise<Ruleset>
    - releaseFirestoreRulesetFromSource(source: string | Buffer): Promise<Ruleset>

    - releaseFirestoreRuleset(ruleset: string | RulesetMetadata): Promise<void>

    - getStorageRuleset(bucket?: string): Promise<Ruleset>

    - releaseStorageRulesetFromSource(source: string | Buffer, bucket?: string): Promise<Ruleset>

    - releaseStorageRuleset(ruleset: string | RulesetMetadata, bucket?: string): Promise<void>

    - createRulesFileFromSource(name: string, source: string | Buffer): RulesFile

    - createRuleset(files...: RulesFile): Promise<Ruleset>

    - getRuleset(name: string): Promise<Ruleset>

    - deleteRuleset(name: string): Promise<void>

    - listRulesetMetadata(pageSize: number = MAX_100, nextPageToken?: string): Promise<RulesetMetadataList>

* admin.securityRules.RulesetMetadataList

    - rulesets: RulesetMetadata[]

    - nextPageToken?: string

* admin.securityRules.RulesetMetadata

    - name: string

    - createTime: string

* admin.securityRules.Ruleset extends RulesetMetadata

    - source: RulesFile[]

* admin.securityRules.RulesFile

    - name: string

    - content: string

* admin.database.Database [Existing Class]

    - getRules(): Promise<string>

    - getRulesJSON(): Promise<object>

    - setRules(source: string | Buffer | object): Promise<void>

* admin.securityRules.FirebaseSecurityRulesError extends admin.FirebaseError

    - All error codes will be prefixed by the string “security-rules/”
aggmoulik commented 5 years ago

@hiranya911 I think this issue request is completed? If there is anything to work please let me know..

hiranya911 commented 5 years ago

I'm basically keeping this open until the new admin.securityRules() module is released. Code is currently sitting in https://github.com/firebase/firebase-admin-node/tree/rules.

hiranya911 commented 5 years ago

admin.securityRules module is available as of v8.6.0 release.