Skip to content

Commit 11a863b

Browse files
authored
Adding the rules API to the public API surface (#625)
* Added rules API to the public admin namespace * Updated docs * Addressing comments regarding the d.ts file * Updated App typings
1 parent b452a29 commit 11a863b

File tree

6 files changed

+325
-0
lines changed

6 files changed

+325
-0
lines changed

docgen/content-sources/node/toc.yaml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,20 @@ toc:
178178
- title: "ShaCertificate"
179179
path: /docs/reference/admin/node/admin.projectManagement.ShaCertificate
180180

181+
- title: "admin.securityRules"
182+
path: /docs/reference/admin/node/admin.securityRules
183+
section:
184+
- title: "RulesFile"
185+
path: /docs/reference/admin/node/admin.securityRules.RulesFile
186+
- title: "Ruleset"
187+
path: /docs/reference/admin/node/admin.securityRules.Ruleset
188+
- title: "RulesetMetadata"
189+
path: /docs/reference/admin/node/admin.securityRules.RulesetMetadata
190+
- title: "RulesetMetadataList"
191+
path: /docs/reference/admin/node/admin.securityRules.RulesetMetadataList
192+
- title: "SecurityRules"
193+
path: /docs/reference/admin/node/admin.securityRules.SecurityRules
194+
181195
- title: "admin.storage"
182196
path: /docs/reference/admin/node/admin.storage
183197
section:

src/firebase-app.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import {Firestore} from '@google-cloud/firestore';
3030
import {FirestoreService} from './firestore/firestore';
3131
import {InstanceId} from './instance-id/instance-id';
3232
import {ProjectManagement} from './project-management/project-management';
33+
import {SecurityRules} from './security-rules/security-rules';
3334

3435
import {Agent} from 'http';
3536

@@ -366,6 +367,19 @@ export class FirebaseApp {
366367
});
367368
}
368369

370+
/**
371+
* Returns the SecurityRules service instance associated with this app.
372+
*
373+
* @return {SecurityRules} The SecurityRules service instance of this app.
374+
*/
375+
public securityRules(): SecurityRules {
376+
return this.ensureService_('security-rules', () => {
377+
const securityRulesService: typeof SecurityRules =
378+
require('./security-rules/security-rules').SecurityRules;
379+
return new securityRulesService(this);
380+
});
381+
}
382+
369383
/**
370384
* Returns the name of the FirebaseApp instance.
371385
*

src/firebase-namespace.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ import {Database} from '@firebase/database';
3434
import {Firestore} from '@google-cloud/firestore';
3535
import {InstanceId} from './instance-id/instance-id';
3636
import {ProjectManagement} from './project-management/project-management';
37+
import { SecurityRules } from './security-rules/security-rules';
3738

3839
import * as validator from './utils/validator';
3940

@@ -419,6 +420,18 @@ export class FirebaseNamespace {
419420
return Object.assign(fn, {ProjectManagement: projectManagement});
420421
}
421422

423+
/**
424+
* Gets the `SecurityRules` service namespace. The returned namespace can be used to get the
425+
* `SecurityRules` service for the default app or an explicitly specified app.
426+
*/
427+
get securityRules(): FirebaseServiceNamespace<SecurityRules> {
428+
const fn: FirebaseServiceNamespace<SecurityRules> = (app?: FirebaseApp) => {
429+
return this.ensureApp(app).securityRules();
430+
};
431+
const securityRules = require('./security-rules/security-rules').SecurityRules;
432+
return Object.assign(fn, {SecurityRules: securityRules});
433+
}
434+
422435
/**
423436
* Initializes the FirebaseApp instance.
424437
*

src/index.d.ts

Lines changed: 222 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -363,6 +363,37 @@ declare namespace admin {
363363
* `ProjectManagement` service associated with the provided app.
364364
*/
365365
function projectManagement(app?: admin.app.App): admin.projectManagement.ProjectManagement;
366+
367+
/**
368+
* Gets the {@link admin.securityRules.SecurityRules
369+
* `SecurityRules`} service for the default app or a given app.
370+
*
371+
* `admin.securityRules()` can be called with no arguments to access the
372+
* default app's {@link admin.securityRules.SecurityRules
373+
* `SecurityRules`} service, or as `admin.securityRules(app)` to access
374+
* the {@link admin.securityRules.SecurityRules `SecurityRules`}
375+
* service associated with a specific app.
376+
*
377+
* @example
378+
* ```javascript
379+
* // Get the SecurityRules service for the default app
380+
* var defaultSecurityRules = admin.securityRules();
381+
* ```
382+
*
383+
* @example
384+
* ```javascript
385+
* // Get the SecurityRules service for a given app
386+
* var otherSecurityRules = admin.securityRules(otherApp);
387+
* ```
388+
*
389+
* @param app Optional app to return the `SecurityRules` service
390+
* for. If not provided, the default `SecurityRules` service will
391+
* be returned.
392+
* @return The default `SecurityRules` service if no app is provided, or the
393+
* `SecurityRules` service associated with the provided app.
394+
*/
395+
function securityRules(app?: admin.app.App): admin.securityRules.SecurityRules;
396+
366397
function initializeApp(options?: admin.AppOptions, name?: string): admin.app.App;
367398
}
368399

@@ -423,6 +454,7 @@ declare namespace admin.app {
423454
instanceId(): admin.instanceId.InstanceId;
424455
messaging(): admin.messaging.Messaging;
425456
projectManagement(): admin.projectManagement.ProjectManagement;
457+
securityRules(): admin.securityRules.SecurityRules;
426458
storage(): admin.storage.Storage;
427459

428460
/**
@@ -5169,6 +5201,196 @@ declare namespace admin.projectManagement {
51695201
}
51705202
}
51715203

5204+
declare namespace admin.securityRules {
5205+
/**
5206+
* A source file containing some Firebase security rules. The content includes raw
5207+
* source code including text formatting, indentation and comments. Use the
5208+
* [`securityRules.createRulesFileFromSource()`](admin.securityRules.SecurityRules#createRulesFileFromSource)
5209+
* method to create new instances of this type.
5210+
*/
5211+
interface RulesFile {
5212+
readonly name: string;
5213+
readonly content: string;
5214+
}
5215+
5216+
/**
5217+
* Required metadata associated with a ruleset.
5218+
*/
5219+
interface RulesetMetadata {
5220+
/**
5221+
* Name of the `Ruleset` as a short string. This can be directly passed into APIs
5222+
* like [`securityRules.getRuleset()`](admin.securityRules.SecurityRules#getRuleset)
5223+
* and [`securityRules.deleteRuleset()`](admin.securityRules.SecurityRules#deleteRuleset).
5224+
*/
5225+
readonly name: string;
5226+
5227+
/**
5228+
* Creation time of the `Ruleset` as a UTC timestamp string.
5229+
*/
5230+
readonly createTime: string;
5231+
}
5232+
5233+
/**
5234+
* A set of Firebase security rules.
5235+
*/
5236+
interface Ruleset extends RulesetMetadata {
5237+
readonly source: RulesFile[];
5238+
}
5239+
5240+
interface RulesetMetadataList {
5241+
/**
5242+
* A batch of ruleset metadata.
5243+
*/
5244+
readonly rulesets: RulesetMetadata[];
5245+
5246+
/**
5247+
* The next page token if available. This is needed to retrieve the next batch.
5248+
*/
5249+
readonly nextPageToken?: string;
5250+
}
5251+
5252+
/**
5253+
* The Firebase `SecurityRules` service interface.
5254+
*
5255+
* Do not call this constructor directly. Instead, use
5256+
* [`admin.securityRules()`](admin.securityRules#securityRules).
5257+
*/
5258+
interface SecurityRules {
5259+
app: admin.app.App;
5260+
5261+
/**
5262+
* Creates a {@link admin.securityRules.RulesFile `RuleFile`} with the given name
5263+
* and source. Throws an error if any of the arguments are invalid. This is a local
5264+
* operation, and does not involve any network API calls.
5265+
*
5266+
* @example
5267+
* ```javascript
5268+
* const source = '// Some rules source';
5269+
* const rulesFile = admin.securityRules().createRulesFileFromSource(
5270+
* 'firestore.rules', source);
5271+
* ```
5272+
*
5273+
* @param name Name to assign to the rules file. This is usually a short file name that
5274+
* helps identify the file in a ruleset.
5275+
* @param source Contents of the rules file.
5276+
* @return A new rules file instance.
5277+
*/
5278+
createRulesFileFromSource(name: string, source: string | Buffer): RulesFile;
5279+
5280+
/**
5281+
* Creates a new {@link admin.securityRules.Ruleset `Ruleset`} from the given
5282+
* {@link admin.securityRules.RulesFile `RuleFile`}.
5283+
*
5284+
* @param file Rules file to include in the new `Ruleset`.
5285+
* @returns A promise that fulfills with the newly created `Ruleset`.
5286+
*/
5287+
createRuleset(file: RulesFile): Promise<Ruleset>;
5288+
5289+
/**
5290+
* Gets the {@link admin.securityRules.Ruleset `Ruleset`} identified by the given
5291+
* name. The input name should be the short name string without the project ID
5292+
* prefix. For example, to retrieve the `projects/project-id/rulesets/my-ruleset`,
5293+
* pass the short name "my-ruleset". Rejects with a `not-found` error if the
5294+
* specified `Ruleset` cannot be found.
5295+
*
5296+
* @param name Name of the `Ruleset` to retrieve.
5297+
* @return A promise that fulfills with the specified `Ruleset`.
5298+
*/
5299+
getRuleset(name: string): Promise<Ruleset>;
5300+
5301+
/**
5302+
* Deletes the {@link admin.securityRules.Ruleset `Ruleset`} identified by the given
5303+
* name. The input name should be the short name string without the project ID
5304+
* prefix. For example, to delete the `projects/project-id/rulesets/my-ruleset`,
5305+
* pass the short name "my-ruleset". Rejects with a `not-found` error if the
5306+
* specified `Ruleset` cannot be found.
5307+
*
5308+
* @param name Name of the `Ruleset` to delete.
5309+
* @return A promise that fulfills when the `Ruleset` is deleted.
5310+
*/
5311+
deleteRuleset(name: string): Promise<void>;
5312+
5313+
/**
5314+
* Retrieves a page of ruleset metadata.
5315+
*
5316+
* @param pageSize The page size, 100 if undefined. This is also the maximum allowed
5317+
* limit.
5318+
* @param nextPageToken The next page token. If not specified, returns rulesets
5319+
* starting without any offset.
5320+
* @return A promise that fulfills a page of rulesets.
5321+
*/
5322+
listRulesetMetadata(
5323+
pageSize?: number, nextPageToken?: string): Promise<RulesetMetadataList>;
5324+
5325+
/**
5326+
* Gets the {@link admin.securityRules.Ruleset `Ruleset`} currently applied to
5327+
* Cloud Firestore. Rejects with a `not-found` error if no ruleset is applied
5328+
* on Firestore.
5329+
*
5330+
* @return A promise that fulfills with the Firestore ruleset.
5331+
*/
5332+
getFirestoreRuleset(): Promise<Ruleset>;
5333+
5334+
/**
5335+
* Creates a new {@link admin.securityRules.Ruleset `Ruleset`} from the given
5336+
* source, and applies it to Cloud Firestore.
5337+
*
5338+
* @param source Rules source to apply.
5339+
* @return A promise that fulfills when the ruleset is created and released.
5340+
*/
5341+
releaseFirestoreRulesetFromSource(source: string | Buffer): Promise<Ruleset>;
5342+
5343+
/**
5344+
* Applies the specified {@link admin.securityRules.Ruleset `Ruleset`} ruleset
5345+
* to Cloud Firestore.
5346+
*
5347+
* @param ruleset Name of the ruleset to apply or a `RulesetMetadata` object
5348+
* containing the name.
5349+
* @return A promise that fulfills when the ruleset is released.
5350+
*/
5351+
releaseFirestoreRuleset(ruleset: string | RulesetMetadata): Promise<void>;
5352+
5353+
/**
5354+
* Gets the {@link admin.securityRules.Ruleset `Ruleset`} currently applied to a
5355+
* Cloud Storage bucket. Rejects with a `not-found` error if no ruleset is applied
5356+
* on the bucket.
5357+
*
5358+
* @param bucket Optional name of the Cloud Storage bucket to be retrieved. If not
5359+
* specified, retrieves the ruleset applied on the default bucket configured via
5360+
* `AppOptions`.
5361+
* @return A promise that fulfills with the Cloud Storage ruleset.
5362+
*/
5363+
getStorageRuleset(bucket?: string): Promise<Ruleset>;
5364+
5365+
/**
5366+
* Creates a new {@link admin.securityRules.Ruleset `Ruleset`} from the given
5367+
* source, and applies it to a Cloud Storage bucket.
5368+
*
5369+
* @param source Rules source to apply.
5370+
* @param bucket Optional name of the Cloud Storage bucket to apply the rules on. If
5371+
* not specified, applies the ruleset on the default bucket configured via
5372+
* {@link admin.AppOptions `AppOptions`}.
5373+
* @return A promise that fulfills when the ruleset is created and released.
5374+
*/
5375+
releaseStorageRulesetFromSource(
5376+
source: string | Buffer, bucket?: string): Promise<Ruleset>;
5377+
5378+
/**
5379+
* Applies the specified {@link admin.securityRules.Ruleset `Ruleset`} ruleset
5380+
* to a Cloud Storage bucket.
5381+
*
5382+
* @param ruleset Name of the ruleset to apply or a `RulesetMetadata` object
5383+
* containing the name.
5384+
* @param bucket Optional name of the Cloud Storage bucket to apply the rules on. If
5385+
* not specified, applies the ruleset on the default bucket configured via
5386+
* {@link admin.AppOptions `AppOptions`}.
5387+
* @return A promise that fulfills when the ruleset is released.
5388+
*/
5389+
releaseStorageRuleset(
5390+
ruleset: string | RulesetMetadata, bucket?: string): Promise<void>;
5391+
}
5392+
}
5393+
51725394
declare module 'firebase-admin' {
51735395
}
51745396

test/unit/firebase-app.spec.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ import {Firestore} from '@google-cloud/firestore';
4040
import {Database} from '@firebase/database';
4141
import {InstanceId} from '../../src/instance-id/instance-id';
4242
import {ProjectManagement} from '../../src/project-management/project-management';
43+
import { SecurityRules } from '../../src/security-rules/security-rules';
4344
import { FirebaseAppError, AppErrorCodes } from '../../src/utils/error';
4445

4546
chai.should();
@@ -584,6 +585,32 @@ describe('FirebaseApp', () => {
584585
});
585586
});
586587

588+
describe('securityRules()', () => {
589+
it('should throw if the app has already been deleted', () => {
590+
const app = firebaseNamespace.initializeApp(mocks.appOptions, mocks.appName);
591+
592+
return app.delete().then(() => {
593+
expect(() => {
594+
return app.securityRules();
595+
}).to.throw(`Firebase app named "${mocks.appName}" has already been deleted.`);
596+
});
597+
});
598+
599+
it('should return the securityRules client', () => {
600+
const app = firebaseNamespace.initializeApp(mocks.appOptions, mocks.appName);
601+
602+
const securityRules: SecurityRules = app.securityRules();
603+
expect(securityRules).to.not.be.null;
604+
});
605+
606+
it('should return a cached version of SecurityRules on subsequent calls', () => {
607+
const app = firebaseNamespace.initializeApp(mocks.appOptions, mocks.appName);
608+
const service1: SecurityRules = app.securityRules();
609+
const service2: SecurityRules = app.securityRules();
610+
expect(service1).to.equal(service2);
611+
});
612+
});
613+
587614
describe('#[service]()', () => {
588615
it('should throw if the app has already been deleted', () => {
589616
firebaseNamespace.INTERNAL.registerService(mocks.serviceName, mockServiceFactory);

0 commit comments

Comments
 (0)