Skip to content
This repository was archived by the owner on Aug 25, 2018. It is now read-only.

Repository files navigation

Status: Archived

This repository has been archived and is no longer maintained.

status: inactive

DEPRECATED - NO LONGER MAINTAINED

If you're still interested in using a Firebase Database Security Rules compiler, check out the experimental Bolt compiler.

Blaze Security Compiler for Firebase

The blaze compiler simplifies building security rules for your Firebase database. It drastically reduces the amount of copy and pasting involved. Blaze compiler security rules are shorter, and the syntax is less fussy.

Getting started

npm install -g blaze_compiler

create a rules.yaml containing the following code

functions:
- isLoggedIn(): auth.uid !== nullschema: {}access:
- location: /read: truewrite: true && isLoggedIn()

now compile it from the commandline with

blaze rules.yaml

A rules.json will be generated which you can upload to Firebase!

You can find more about the functions, simpler rule expressions, the schema definitions, access control or inline tests.

Functions

Common expressions for reuse are defined in the functions list. A function can take arguments (they are functions).

functions:
- isLoggedIn(): auth.username !== null
- isUser(username): auth.username === username

You can then use them anywhere a security expression would be expected, for example, in the access control section:-

access:
location: /users/$userid/
write: isUser($userid)

Simple Security Expressions

Security expressions are the strings that used to go in write/read/validate portions of the old security rules. Blaze expressions have similar semantics but shorter syntax.

Variables renamed

data and newData have been renamed prev and next. root has the same meaning.

Child selection

The expression for selecting a child is now an array-like syntax. What was:

root.child('users')

is now

root['users']

In the common case that you are selecting a child using a single literal, you can select the child as if it were a property. So you can also write the above as:

root.users

Coercion of .val()

In the new syntax, .val() is inserted if the expression is next to an operator or in an array like child selector. You only need to use .val() if you are using a method of a value type like .length, .beginsWith().contains(...). So

newData.child('counter').val() == data.child('counter').val() + 1

is simplified to just

next.counter == prev.counter + 1

Schema

The schema section describes the layout of the data tree. It is strongly suggested you use schema to describe the layout of your Firebase, however it is possible to describe just the end points with access controls. A compiler warning is emitted if you give access to a path that is not described by the data schema.

Types

A Firebase database schema node is either a leaf type (string, number, boolean) or an object which contains more child schema nodes. The type is specified with "type". Children of objects are specified as a map under "properties"

schema:
type: objectproperties:
string_child: {type: string}boolean_child: {type: boolean}number_child: {type: number}anything_child: {}

In the above example you could set {string_child: "blah"} at the root of your database but not {string_child: true}

You can leave a schema unspecified with {} or with type: "any".

required

The required keyword states which children must be present. The required keyword is only valid for schema nodes with the object or any types.

schema:
type: objectrequired: [child1, child2]

additionalProperties

By default, objects can have additional children not mentioned. If additionalProperties is set to false, however, only children explicitly mentioned in the properties are allowed. The additionalProperties keyword is only valid for object and non-typed schemas.

schema:
type: objectadditionalProperties: falseproperties:
string_child: {type: string}

would not accept {number_child: 5} in the root, but without additionalProperties it would.

enum

The enum keyword constrains the value of a string types to be one of the predefined array elements.

schema:
type: stringenum: [yes, no, maybe]

indexOn

The indexOn keyword adds an index for querying. This can either be specified as a single string or an array, applied to non-typed or object types only.

schema:
indexOn: name$user:
indexOn: [inbox, outbox]

ranges

The minimum keyword constrains the minimum value of a number type. You set exclusiveMinimum to true, otherwise the minimum is inclusive. Maximum and exclusiveMaximum follow the pattern

schema:
type: numberminimum: 0maximum: 10exclusiveMaximum: trueexamples:
- 0
- 9.9nonexamples:
- 10

$wildchild

An object can have many children bound to a path variable denoted with a keyword starting with $. Note that wildchilds are not put in the properties definition. The following shows how to accept many objects as children of "/users/"

schema:
type: objectproperties:
users:
type: object$userid: {}

The use of a wildchild prevents all ascendents from being writable.

~$wilderchild

schema:
type: objectproperties:
users:
type: object~$userid: {type: string, constraint: next != null}

Wilderchilds are an unsafe but more flexible wildchild. Use them with caution. Wilderchilds do not lock the parent against writing. Wilderchildren's constraints are respected only when next!=null. This implies wilderchild can be set to null whenever user has write access to them, either by writing to the parent or the wilderchild location directly. In the above example, despite the guard against being set to null in the constraint, the constraint is not evaluated when the wilder child is set to null and thus has no effect. Wilderchilds are useful because their enclosing location can still be written, but be aware of the drawbacks.

Constraints

The semantics of enforcing data integrity is different from the original rules. There is no overriding of constraints, nor separate read/write/validate expressions. There is just one field for expressing data integrity named constraint. All ancestors and descendant constraints must evaluate to true for a write to be allowed.

The following example, fixes the id of a user to equal the key, and makes the account writable only at creation time.

schema:
type: objectproperties:
users:
type: object$userid:
properties:
id:
type: stringconstraint: next == $useridconstraint: (!prev.exists())

You can be sure all constraints above and below evaluate to true for a write to be allowed. The only quirk is related to wildchilds. You can't write anything above a wildchild that includes the wildchild as a descendant. They do inherit their parents constraints though, as do their siblings, so the use of wildchilds never makes the database less constrained accidentally.

Model reuse

Denormalization of data requires replicating a model in multiple places in a schema. JSON Schema allows importing of models across the Internet or within a document through URLs. Currently, blaze only supports in-document reuse.

Model definitions are declared in the keyword definitions object, and references are made using the $ref keyword as follows:

schema:
definitions:
stamped_value:
type: objectproperties:
modified: {type: number}required: [value, modified]constraint: next.value == prev.value || next.modified == nowtype: object$data: {$ref: "#/definitions/stamped_value"}

In JSON Schema you are able to extend model objects using the allOf modeling construct (example). However, blaze does not currently support this. Let us know if you need it!

Inline testing

Writing a complex schema can be difficult. For example, a typo in a required field could enforce the existence of a child other than the one intended. For that reason blaze provides keywords for inline testing of nested schema at compile time.

examples is a list of JSONs that you expect to be accepted by the JSON schema node.

nonexamples is a list of JSONs that you expect to be rejected by the JSON schema node.

These inline tests are good for documenting intent and providing fast feedback when authoring a new schema. Note that inline tests cannot understand the constraint field, they can only test the schema.

schema:
type: objectproperties:
object: {type: object}string: {type: string }boolean: {type: boolean}number: {type: number }additionalProperties: falseexamples:
- {object: {name: "hello"}} # you can have extra children in objects by default
- {string: string}
- {boolean: true}
- {number: 4.6}nonexamples:
- {object: true}
- {string: {grandchild: true}}
- {boolean: "true"}
- {number: "4.6"}
- {extra: "4.6"} #additionProperties is false, so no unexpected properties allowed

Access Control

The schema portion of the rules YAML file is for specifying the data layout and constraints. Read/write access is described in a separate access control list under "access". For each entry, the scope of the rule is a subtree at, or below, the path indicated in the location field. Read access is granted to that subtree if the read expression evaluates to true, and write access is granted if the write expression evaluates to true.

functions:
- isLoggedIn(): auth !== null
...
access:
- location: "/"read: isLoggedIn()
- location: "/users/$userid/"write: auth.username === $userid

Only one access control entry needs to evaluate to true for an operation to be permitted.

Example

This is an example that exploits most of the new features. It is a messaging system where users can send messages to each other, by posting to other user's inboxes

functions: #reusable boolean functions
- isLoggedIn(): auth.username !== null
- createOnly(): next.exists() && !prev.exists()
- deleteOnly(): prev.exists() && !next.exists()
- createOrDelete(): createOnly() || deleteOnly()schema:
definitions: #create a reusable message modelmessage: #for use in the in and out boxestype: objectproperties:
from:
type: string#enforce the from field is *always* correct on creation,#and that only the *box owner* can deleteconstraint: (auth.username == next && createOnly()) ||($userid === auth.username && deleteOnly())#you can't delete single field due to parent's requiredto: {type: string, constraint: createOrDelete()}message: {type: string, constraint: createOrDelete()}required: [from, to, message] # all messages require all the fields to be defined#(or none if the message does not exist)additionalProperties: false #prevent spurious data being part of a messageexamples: #examples of inline testing
- {from: "bill", to: "tom", message: "hey Tom!"}nonexamples:
- {to: "tom", message: "hey Tom!"} #not allowed because from is missingtype: objectproperties:
users: # the users subtree is a collection of userstype: object$userid: #wildchild expression of many childrentype: objectproperties: #each user has an optional inbox and outboxinbox:
type: object$message: {$ref: "#/definitions/message"}outbox:
type: object$message: {$ref: "#/definitions/message"}additionalProperties: falseaccess:
#append only write is granted to anyone's inbox,#so users can send messages to strangers
- location: users/$userid/inbox/write: createOnly() && isLoggedIn()#the inbox owner can delete their incoming mail
- location: users/$userid/inbox/write: deleteOnly() && $userid === auth.username#write and delete is given to owners outbox
- location: users/$userid/outbox/write: true#owners can read everything in their inbox and outbox
- location: users/$userid/read: $userid === auth.username

Changelog

  • 25th Sep 2015:

    • schema padding emits a warning when applied to help visibility
    • performance schema padding efficiency greatly improved
    • performance of optimization routines improved
  • 20th Aug 2015:

    • tailored error message when a wild(er)child is used in a properties section
  • 10th Aug 2015:

    • Allowed repeat examples and non-example, as the error message can be unclearly attached to something unrelated (see repeatExample.yaml)
    • upgrade source-map-repository so blaze_compiler continues to fix issue with io.js
    • Allowed any type to have the required keyword
    • Schema is padded to match with ACL if the ACL is bigger
  • 19th May 2015:

    • Improved optimization use a less verbose object detection notation and spurious parent is an object checks
  • 22nd April 2015:

    • Special cased forgetting to .val() before using an inbuilt string method with an error message
  • 21th April 2015:

    • $ref not importing into (non)example schema fragments properly
  • 10th April 2015:

    • fixed erroneous substitution of parameters into member expressions
  • 12th Jan 2015:

    • improved messaging if blah.child('name') syntax is erroneously used
  • 23rd December 2014:

    • bugfix: function with next or prev were not moved around when in constraints properly (similar to Nov 4th bug)
    • bugfix: minimum and maximum
  • 20th November 2014:

    • support for indexOn
  • 4th November 2014:

    • bugfix: functions in ACL resolved properly
  • 3rd November 2014:

    • bugfix: wilderchild matching fix in ACL
  • 1st November 2014:

    • bugfix: wilderchild overwriting parent constraints bug fixed
    • bugfix: access control constraints localised properly
    • bugfix: regex detection firing erroneously on strings starting with '/' fixed
  • 20th October 2014:

    • optimizations added to reduce code bloat
    • sensitization bug regarding regexes fixed
  • 28th August 2014:ß

    • range constraints for number type added
  • 26th August 2014:

    • wilderchilds introduced, ~$ allows nullable wildchilds whose parents can be written to.
    • sanitized expressions bug fix
  • 18th August 2014:

    • predicates renamed to functions
  • 14th July 2014:

    • improved error reporting
    • updated installation
  • 9th July 2014:

    • support for rules in JSON
  • 30th June 2014:

    • removed trailing /* from access location syntax
    • allowed untyped schema if type is not specified

About

blaze_compiler

Resources

Stars

175 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
 blocks
(function() {
function addCopyButtons() {
document.querySelectorAll('pre code').forEach(function(codeBlock) {
if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
codeBlock.parentElement.setAttribute('data-copy-added', 'true');
var btn = document.createElement('button');
btn.textContent = 'Copy';
btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';
btn.onmouseover = function() { this.style.opacity = '1'; };
btn.onmouseout = function() { this.style.opacity = '0.7'; };
btn.onclick = function() {
navigator.clipboard.writeText(codeBlock.textContent).then(function() {
btn.textContent = 'Copied!';
setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
});
};
codeBlock.parentElement.style.position = 'relative';
codeBlock.parentElement.appendChild(btn);
});
}
addCopyButtons();
// Re-run on dynamic content
var observer = new MutationObserver(addCopyButtons);
observer.observe(document.body, { childList: true, subtree: true });
})();
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
GitHub - googlearchive/blaze_compiler: blaze_compiler · GitHub
Skip to content
This repository was archived by the owner on Aug 25, 2018. It is now read-only.

Repository files navigation

Status: Archived

This repository has been archived and is no longer maintained.

status: inactive

DEPRECATED - NO LONGER MAINTAINED

If you're still interested in using a Firebase Database Security Rules compiler, check out the experimental Bolt compiler.

Blaze Security Compiler for Firebase

The blaze compiler simplifies building security rules for your Firebase database. It drastically reduces the amount of copy and pasting involved. Blaze compiler security rules are shorter, and the syntax is less fussy.

Getting started

npm install -g blaze_compiler

create a rules.yaml containing the following code

functions:
- isLoggedIn(): auth.uid !== nullschema: {}access:
- location: /read: truewrite: true && isLoggedIn()

now compile it from the commandline with

blaze rules.yaml

A rules.json will be generated which you can upload to Firebase!

You can find more about the functions, simpler rule expressions, the schema definitions, access control or inline tests.

Functions

Common expressions for reuse are defined in the functions list. A function can take arguments (they are functions).

functions:
- isLoggedIn(): auth.username !== null
- isUser(username): auth.username === username

You can then use them anywhere a security expression would be expected, for example, in the access control section:-

access:
location: /users/$userid/
write: isUser($userid)

Simple Security Expressions

Security expressions are the strings that used to go in write/read/validate portions of the old security rules. Blaze expressions have similar semantics but shorter syntax.

Variables renamed

data and newData have been renamed prev and next. root has the same meaning.

Child selection

The expression for selecting a child is now an array-like syntax. What was:

root.child('users')

is now

root['users']

In the common case that you are selecting a child using a single literal, you can select the child as if it were a property. So you can also write the above as:

root.users

Coercion of .val()

In the new syntax, .val() is inserted if the expression is next to an operator or in an array like child selector. You only need to use .val() if you are using a method of a value type like .length, .beginsWith().contains(...). So

newData.child('counter').val() == data.child('counter').val() + 1

is simplified to just

next.counter == prev.counter + 1

Schema

The schema section describes the layout of the data tree. It is strongly suggested you use schema to describe the layout of your Firebase, however it is possible to describe just the end points with access controls. A compiler warning is emitted if you give access to a path that is not described by the data schema.

Types

A Firebase database schema node is either a leaf type (string, number, boolean) or an object which contains more child schema nodes. The type is specified with "type". Children of objects are specified as a map under "properties"

schema:
type: objectproperties:
string_child: {type: string}boolean_child: {type: boolean}number_child: {type: number}anything_child: {}

In the above example you could set {string_child: "blah"} at the root of your database but not {string_child: true}

You can leave a schema unspecified with {} or with type: "any".

required

The required keyword states which children must be present. The required keyword is only valid for schema nodes with the object or any types.

schema:
type: objectrequired: [child1, child2]

additionalProperties

By default, objects can have additional children not mentioned. If additionalProperties is set to false, however, only children explicitly mentioned in the properties are allowed. The additionalProperties keyword is only valid for object and non-typed schemas.

schema:
type: objectadditionalProperties: falseproperties:
string_child: {type: string}

would not accept {number_child: 5} in the root, but without additionalProperties it would.

enum

The enum keyword constrains the value of a string types to be one of the predefined array elements.

schema:
type: stringenum: [yes, no, maybe]

indexOn

The indexOn keyword adds an index for querying. This can either be specified as a single string or an array, applied to non-typed or object types only.

schema:
indexOn: name$user:
indexOn: [inbox, outbox]

ranges

The minimum keyword constrains the minimum value of a number type. You set exclusiveMinimum to true, otherwise the minimum is inclusive. Maximum and exclusiveMaximum follow the pattern

schema:
type: numberminimum: 0maximum: 10exclusiveMaximum: trueexamples:
- 0
- 9.9nonexamples:
- 10

$wildchild

An object can have many children bound to a path variable denoted with a keyword starting with $. Note that wildchilds are not put in the properties definition. The following shows how to accept many objects as children of "/users/"

schema:
type: objectproperties:
users:
type: object$userid: {}

The use of a wildchild prevents all ascendents from being writable.

~$wilderchild

schema:
type: objectproperties:
users:
type: object~$userid: {type: string, constraint: next != null}

Wilderchilds are an unsafe but more flexible wildchild. Use them with caution. Wilderchilds do not lock the parent against writing. Wilderchildren's constraints are respected only when next!=null. This implies wilderchild can be set to null whenever user has write access to them, either by writing to the parent or the wilderchild location directly. In the above example, despite the guard against being set to null in the constraint, the constraint is not evaluated when the wilder child is set to null and thus has no effect. Wilderchilds are useful because their enclosing location can still be written, but be aware of the drawbacks.

Constraints

The semantics of enforcing data integrity is different from the original rules. There is no overriding of constraints, nor separate read/write/validate expressions. There is just one field for expressing data integrity named constraint. All ancestors and descendant constraints must evaluate to true for a write to be allowed.

The following example, fixes the id of a user to equal the key, and makes the account writable only at creation time.

schema:
type: objectproperties:
users:
type: object$userid:
properties:
id:
type: stringconstraint: next == $useridconstraint: (!prev.exists())

You can be sure all constraints above and below evaluate to true for a write to be allowed. The only quirk is related to wildchilds. You can't write anything above a wildchild that includes the wildchild as a descendant. They do inherit their parents constraints though, as do their siblings, so the use of wildchilds never makes the database less constrained accidentally.

Model reuse

Denormalization of data requires replicating a model in multiple places in a schema. JSON Schema allows importing of models across the Internet or within a document through URLs. Currently, blaze only supports in-document reuse.

Model definitions are declared in the keyword definitions object, and references are made using the $ref keyword as follows:

schema:
definitions:
stamped_value:
type: objectproperties:
modified: {type: number}required: [value, modified]constraint: next.value == prev.value || next.modified == nowtype: object$data: {$ref: "#/definitions/stamped_value"}

In JSON Schema you are able to extend model objects using the allOf modeling construct (example). However, blaze does not currently support this. Let us know if you need it!

Inline testing

Writing a complex schema can be difficult. For example, a typo in a required field could enforce the existence of a child other than the one intended. For that reason blaze provides keywords for inline testing of nested schema at compile time.

examples is a list of JSONs that you expect to be accepted by the JSON schema node.

nonexamples is a list of JSONs that you expect to be rejected by the JSON schema node.

These inline tests are good for documenting intent and providing fast feedback when authoring a new schema. Note that inline tests cannot understand the constraint field, they can only test the schema.

schema:
type: objectproperties:
object: {type: object}string: {type: string }boolean: {type: boolean}number: {type: number }additionalProperties: falseexamples:
- {object: {name: "hello"}} # you can have extra children in objects by default
- {string: string}
- {boolean: true}
- {number: 4.6}nonexamples:
- {object: true}
- {string: {grandchild: true}}
- {boolean: "true"}
- {number: "4.6"}
- {extra: "4.6"} #additionProperties is false, so no unexpected properties allowed

Access Control

The schema portion of the rules YAML file is for specifying the data layout and constraints. Read/write access is described in a separate access control list under "access". For each entry, the scope of the rule is a subtree at, or below, the path indicated in the location field. Read access is granted to that subtree if the read expression evaluates to true, and write access is granted if the write expression evaluates to true.

functions:
- isLoggedIn(): auth !== null
...
access:
- location: "/"read: isLoggedIn()
- location: "/users/$userid/"write: auth.username === $userid

Only one access control entry needs to evaluate to true for an operation to be permitted.

Example

This is an example that exploits most of the new features. It is a messaging system where users can send messages to each other, by posting to other user's inboxes

functions: #reusable boolean functions
- isLoggedIn(): auth.username !== null
- createOnly(): next.exists() && !prev.exists()
- deleteOnly(): prev.exists() && !next.exists()
- createOrDelete(): createOnly() || deleteOnly()schema:
definitions: #create a reusable message modelmessage: #for use in the in and out boxestype: objectproperties:
from:
type: string#enforce the from field is *always* correct on creation,#and that only the *box owner* can deleteconstraint: (auth.username == next && createOnly()) ||($userid === auth.username && deleteOnly())#you can't delete single field due to parent's requiredto: {type: string, constraint: createOrDelete()}message: {type: string, constraint: createOrDelete()}required: [from, to, message] # all messages require all the fields to be defined#(or none if the message does not exist)additionalProperties: false #prevent spurious data being part of a messageexamples: #examples of inline testing
- {from: "bill", to: "tom", message: "hey Tom!"}nonexamples:
- {to: "tom", message: "hey Tom!"} #not allowed because from is missingtype: objectproperties:
users: # the users subtree is a collection of userstype: object$userid: #wildchild expression of many childrentype: objectproperties: #each user has an optional inbox and outboxinbox:
type: object$message: {$ref: "#/definitions/message"}outbox:
type: object$message: {$ref: "#/definitions/message"}additionalProperties: falseaccess:
#append only write is granted to anyone's inbox,#so users can send messages to strangers
- location: users/$userid/inbox/write: createOnly() && isLoggedIn()#the inbox owner can delete their incoming mail
- location: users/$userid/inbox/write: deleteOnly() && $userid === auth.username#write and delete is given to owners outbox
- location: users/$userid/outbox/write: true#owners can read everything in their inbox and outbox
- location: users/$userid/read: $userid === auth.username

Changelog

  • 25th Sep 2015:

    • schema padding emits a warning when applied to help visibility
    • performance schema padding efficiency greatly improved
    • performance of optimization routines improved
  • 20th Aug 2015:

    • tailored error message when a wild(er)child is used in a properties section
  • 10th Aug 2015:

    • Allowed repeat examples and non-example, as the error message can be unclearly attached to something unrelated (see repeatExample.yaml)
    • upgrade source-map-repository so blaze_compiler continues to fix issue with io.js
    • Allowed any type to have the required keyword
    • Schema is padded to match with ACL if the ACL is bigger
  • 19th May 2015:

    • Improved optimization use a less verbose object detection notation and spurious parent is an object checks
  • 22nd April 2015:

    • Special cased forgetting to .val() before using an inbuilt string method with an error message
  • 21th April 2015:

    • $ref not importing into (non)example schema fragments properly
  • 10th April 2015:

    • fixed erroneous substitution of parameters into member expressions
  • 12th Jan 2015:

    • improved messaging if blah.child('name') syntax is erroneously used
  • 23rd December 2014:

    • bugfix: function with next or prev were not moved around when in constraints properly (similar to Nov 4th bug)
    • bugfix: minimum and maximum
  • 20th November 2014:

    • support for indexOn
  • 4th November 2014:

    • bugfix: functions in ACL resolved properly
  • 3rd November 2014:

    • bugfix: wilderchild matching fix in ACL
  • 1st November 2014:

    • bugfix: wilderchild overwriting parent constraints bug fixed
    • bugfix: access control constraints localised properly
    • bugfix: regex detection firing erroneously on strings starting with '/' fixed
  • 20th October 2014:

    • optimizations added to reduce code bloat
    • sensitization bug regarding regexes fixed
  • 28th August 2014:ß

    • range constraints for number type added
  • 26th August 2014:

    • wilderchilds introduced, ~$ allows nullable wildchilds whose parents can be written to.
    • sanitized expressions bug fix
  • 18th August 2014:

    • predicates renamed to functions
  • 14th July 2014:

    • improved error reporting
    • updated installation
  • 9th July 2014:

    • support for rules in JSON
  • 30th June 2014:

    • removed trailing /* from access location syntax
    • allowed untyped schema if type is not specified

About

blaze_compiler

Resources

Stars

175 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' GitHub - googlearchive/blaze_compiler: blaze_compiler · GitHub
Skip to content
This repository was archived by the owner on Aug 25, 2018. It is now read-only.

Repository files navigation

Status: Archived

This repository has been archived and is no longer maintained.

status: inactive

DEPRECATED - NO LONGER MAINTAINED

If you're still interested in using a Firebase Database Security Rules compiler, check out the experimental Bolt compiler.

Blaze Security Compiler for Firebase

The blaze compiler simplifies building security rules for your Firebase database. It drastically reduces the amount of copy and pasting involved. Blaze compiler security rules are shorter, and the syntax is less fussy.

Getting started

npm install -g blaze_compiler

create a rules.yaml containing the following code

functions:
- isLoggedIn(): auth.uid !== nullschema: {}access:
- location: /read: truewrite: true && isLoggedIn()

now compile it from the commandline with

blaze rules.yaml

A rules.json will be generated which you can upload to Firebase!

You can find more about the functions, simpler rule expressions, the schema definitions, access control or inline tests.

Functions

Common expressions for reuse are defined in the functions list. A function can take arguments (they are functions).

functions:
- isLoggedIn(): auth.username !== null
- isUser(username): auth.username === username

You can then use them anywhere a security expression would be expected, for example, in the access control section:-

access:
location: /users/$userid/
write: isUser($userid)

Simple Security Expressions

Security expressions are the strings that used to go in write/read/validate portions of the old security rules. Blaze expressions have similar semantics but shorter syntax.

Variables renamed

data and newData have been renamed prev and next. root has the same meaning.

Child selection

The expression for selecting a child is now an array-like syntax. What was:

root.child('users')

is now

root['users']

In the common case that you are selecting a child using a single literal, you can select the child as if it were a property. So you can also write the above as:

root.users

Coercion of .val()

In the new syntax, .val() is inserted if the expression is next to an operator or in an array like child selector. You only need to use .val() if you are using a method of a value type like .length, .beginsWith().contains(...). So

newData.child('counter').val() == data.child('counter').val() + 1

is simplified to just

next.counter == prev.counter + 1

Schema

The schema section describes the layout of the data tree. It is strongly suggested you use schema to describe the layout of your Firebase, however it is possible to describe just the end points with access controls. A compiler warning is emitted if you give access to a path that is not described by the data schema.

Types

A Firebase database schema node is either a leaf type (string, number, boolean) or an object which contains more child schema nodes. The type is specified with "type". Children of objects are specified as a map under "properties"

schema:
type: objectproperties:
string_child: {type: string}boolean_child: {type: boolean}number_child: {type: number}anything_child: {}

In the above example you could set {string_child: "blah"} at the root of your database but not {string_child: true}

You can leave a schema unspecified with {} or with type: "any".

required

The required keyword states which children must be present. The required keyword is only valid for schema nodes with the object or any types.

schema:
type: objectrequired: [child1, child2]

additionalProperties

By default, objects can have additional children not mentioned. If additionalProperties is set to false, however, only children explicitly mentioned in the properties are allowed. The additionalProperties keyword is only valid for object and non-typed schemas.

schema:
type: objectadditionalProperties: falseproperties:
string_child: {type: string}

would not accept {number_child: 5} in the root, but without additionalProperties it would.

enum

The enum keyword constrains the value of a string types to be one of the predefined array elements.

schema:
type: stringenum: [yes, no, maybe]

indexOn

The indexOn keyword adds an index for querying. This can either be specified as a single string or an array, applied to non-typed or object types only.

schema:
indexOn: name$user:
indexOn: [inbox, outbox]

ranges

The minimum keyword constrains the minimum value of a number type. You set exclusiveMinimum to true, otherwise the minimum is inclusive. Maximum and exclusiveMaximum follow the pattern

schema:
type: numberminimum: 0maximum: 10exclusiveMaximum: trueexamples:
- 0
- 9.9nonexamples:
- 10

$wildchild

An object can have many children bound to a path variable denoted with a keyword starting with $. Note that wildchilds are not put in the properties definition. The following shows how to accept many objects as children of "/users/"

schema:
type: objectproperties:
users:
type: object$userid: {}

The use of a wildchild prevents all ascendents from being writable.

~$wilderchild

schema:
type: objectproperties:
users:
type: object~$userid: {type: string, constraint: next != null}

Wilderchilds are an unsafe but more flexible wildchild. Use them with caution. Wilderchilds do not lock the parent against writing. Wilderchildren's constraints are respected only when next!=null. This implies wilderchild can be set to null whenever user has write access to them, either by writing to the parent or the wilderchild location directly. In the above example, despite the guard against being set to null in the constraint, the constraint is not evaluated when the wilder child is set to null and thus has no effect. Wilderchilds are useful because their enclosing location can still be written, but be aware of the drawbacks.

Constraints

The semantics of enforcing data integrity is different from the original rules. There is no overriding of constraints, nor separate read/write/validate expressions. There is just one field for expressing data integrity named constraint. All ancestors and descendant constraints must evaluate to true for a write to be allowed.

The following example, fixes the id of a user to equal the key, and makes the account writable only at creation time.

schema:
type: objectproperties:
users:
type: object$userid:
properties:
id:
type: stringconstraint: next == $useridconstraint: (!prev.exists())

You can be sure all constraints above and below evaluate to true for a write to be allowed. The only quirk is related to wildchilds. You can't write anything above a wildchild that includes the wildchild as a descendant. They do inherit their parents constraints though, as do their siblings, so the use of wildchilds never makes the database less constrained accidentally.

Model reuse

Denormalization of data requires replicating a model in multiple places in a schema. JSON Schema allows importing of models across the Internet or within a document through URLs. Currently, blaze only supports in-document reuse.

Model definitions are declared in the keyword definitions object, and references are made using the $ref keyword as follows:

schema:
definitions:
stamped_value:
type: objectproperties:
modified: {type: number}required: [value, modified]constraint: next.value == prev.value || next.modified == nowtype: object$data: {$ref: "#/definitions/stamped_value"}

In JSON Schema you are able to extend model objects using the allOf modeling construct (example). However, blaze does not currently support this. Let us know if you need it!

Inline testing

Writing a complex schema can be difficult. For example, a typo in a required field could enforce the existence of a child other than the one intended. For that reason blaze provides keywords for inline testing of nested schema at compile time.

examples is a list of JSONs that you expect to be accepted by the JSON schema node.

nonexamples is a list of JSONs that you expect to be rejected by the JSON schema node.

These inline tests are good for documenting intent and providing fast feedback when authoring a new schema. Note that inline tests cannot understand the constraint field, they can only test the schema.

schema:
type: objectproperties:
object: {type: object}string: {type: string }boolean: {type: boolean}number: {type: number }additionalProperties: falseexamples:
- {object: {name: "hello"}} # you can have extra children in objects by default
- {string: string}
- {boolean: true}
- {number: 4.6}nonexamples:
- {object: true}
- {string: {grandchild: true}}
- {boolean: "true"}
- {number: "4.6"}
- {extra: "4.6"} #additionProperties is false, so no unexpected properties allowed

Access Control

The schema portion of the rules YAML file is for specifying the data layout and constraints. Read/write access is described in a separate access control list under "access". For each entry, the scope of the rule is a subtree at, or below, the path indicated in the location field. Read access is granted to that subtree if the read expression evaluates to true, and write access is granted if the write expression evaluates to true.

functions:
- isLoggedIn(): auth !== null
...
access:
- location: "/"read: isLoggedIn()
- location: "/users/$userid/"write: auth.username === $userid

Only one access control entry needs to evaluate to true for an operation to be permitted.

Example

This is an example that exploits most of the new features. It is a messaging system where users can send messages to each other, by posting to other user's inboxes

functions: #reusable boolean functions
- isLoggedIn(): auth.username !== null
- createOnly(): next.exists() && !prev.exists()
- deleteOnly(): prev.exists() && !next.exists()
- createOrDelete(): createOnly() || deleteOnly()schema:
definitions: #create a reusable message modelmessage: #for use in the in and out boxestype: objectproperties:
from:
type: string#enforce the from field is *always* correct on creation,#and that only the *box owner* can deleteconstraint: (auth.username == next && createOnly()) ||($userid === auth.username && deleteOnly())#you can't delete single field due to parent's requiredto: {type: string, constraint: createOrDelete()}message: {type: string, constraint: createOrDelete()}required: [from, to, message] # all messages require all the fields to be defined#(or none if the message does not exist)additionalProperties: false #prevent spurious data being part of a messageexamples: #examples of inline testing
- {from: "bill", to: "tom", message: "hey Tom!"}nonexamples:
- {to: "tom", message: "hey Tom!"} #not allowed because from is missingtype: objectproperties:
users: # the users subtree is a collection of userstype: object$userid: #wildchild expression of many childrentype: objectproperties: #each user has an optional inbox and outboxinbox:
type: object$message: {$ref: "#/definitions/message"}outbox:
type: object$message: {$ref: "#/definitions/message"}additionalProperties: falseaccess:
#append only write is granted to anyone's inbox,#so users can send messages to strangers
- location: users/$userid/inbox/write: createOnly() && isLoggedIn()#the inbox owner can delete their incoming mail
- location: users/$userid/inbox/write: deleteOnly() && $userid === auth.username#write and delete is given to owners outbox
- location: users/$userid/outbox/write: true#owners can read everything in their inbox and outbox
- location: users/$userid/read: $userid === auth.username

Changelog

  • 25th Sep 2015:

    • schema padding emits a warning when applied to help visibility
    • performance schema padding efficiency greatly improved
    • performance of optimization routines improved
  • 20th Aug 2015:

    • tailored error message when a wild(er)child is used in a properties section
  • 10th Aug 2015:

    • Allowed repeat examples and non-example, as the error message can be unclearly attached to something unrelated (see repeatExample.yaml)
    • upgrade source-map-repository so blaze_compiler continues to fix issue with io.js
    • Allowed any type to have the required keyword
    • Schema is padded to match with ACL if the ACL is bigger
  • 19th May 2015:

    • Improved optimization use a less verbose object detection notation and spurious parent is an object checks
  • 22nd April 2015:

    • Special cased forgetting to .val() before using an inbuilt string method with an error message
  • 21th April 2015:

    • $ref not importing into (non)example schema fragments properly
  • 10th April 2015:

    • fixed erroneous substitution of parameters into member expressions
  • 12th Jan 2015:

    • improved messaging if blah.child('name') syntax is erroneously used
  • 23rd December 2014:

    • bugfix: function with next or prev were not moved around when in constraints properly (similar to Nov 4th bug)
    • bugfix: minimum and maximum
  • 20th November 2014:

    • support for indexOn
  • 4th November 2014:

    • bugfix: functions in ACL resolved properly
  • 3rd November 2014:

    • bugfix: wilderchild matching fix in ACL
  • 1st November 2014:

    • bugfix: wilderchild overwriting parent constraints bug fixed
    • bugfix: access control constraints localised properly
    • bugfix: regex detection firing erroneously on strings starting with '/' fixed
  • 20th October 2014:

    • optimizations added to reduce code bloat
    • sensitization bug regarding regexes fixed
  • 28th August 2014:ß

    • range constraints for number type added
  • 26th August 2014:

    • wilderchilds introduced, ~$ allows nullable wildchilds whose parents can be written to.
    • sanitized expressions bug fix
  • 18th August 2014:

    • predicates renamed to functions
  • 14th July 2014:

    • improved error reporting
    • updated installation
  • 9th July 2014:

    • support for rules in JSON
  • 30th June 2014:

    • removed trailing /* from access location syntax
    • allowed untyped schema if type is not specified

About

blaze_compiler

Resources

Stars

175 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Highlight search terms from Google/DuckDuckGo/Bing referrer (function() { var ref = document.referrer; var terms = []; if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) { var url = new URL(ref); var q = url.searchParams.get('q') || url.searchParams.get('p'); if (q) { terms = q.split(/\s+/).filter(function(t) { return t.length > 2; }); } } if (terms.length === 0) return; var style = document.createElement('style'); style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }'; document.head.appendChild(style); function highlight(node) { if (node.nodeType === 3) { // text node var text = node.textContent; var found = false; terms.forEach(function(term) { var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' GitHub - googlearchive/blaze_compiler: blaze_compiler · GitHub
Skip to content
This repository was archived by the owner on Aug 25, 2018. It is now read-only.

Repository files navigation

Status: Archived

This repository has been archived and is no longer maintained.

status: inactive

DEPRECATED - NO LONGER MAINTAINED

If you're still interested in using a Firebase Database Security Rules compiler, check out the experimental Bolt compiler.

Blaze Security Compiler for Firebase

The blaze compiler simplifies building security rules for your Firebase database. It drastically reduces the amount of copy and pasting involved. Blaze compiler security rules are shorter, and the syntax is less fussy.

Getting started

npm install -g blaze_compiler

create a rules.yaml containing the following code

functions:
- isLoggedIn(): auth.uid !== nullschema: {}access:
- location: /read: truewrite: true && isLoggedIn()

now compile it from the commandline with

blaze rules.yaml

A rules.json will be generated which you can upload to Firebase!

You can find more about the functions, simpler rule expressions, the schema definitions, access control or inline tests.

Functions

Common expressions for reuse are defined in the functions list. A function can take arguments (they are functions).

functions:
- isLoggedIn(): auth.username !== null
- isUser(username): auth.username === username

You can then use them anywhere a security expression would be expected, for example, in the access control section:-

access:
location: /users/$userid/
write: isUser($userid)

Simple Security Expressions

Security expressions are the strings that used to go in write/read/validate portions of the old security rules. Blaze expressions have similar semantics but shorter syntax.

Variables renamed

data and newData have been renamed prev and next. root has the same meaning.

Child selection

The expression for selecting a child is now an array-like syntax. What was:

root.child('users')

is now

root['users']

In the common case that you are selecting a child using a single literal, you can select the child as if it were a property. So you can also write the above as:

root.users

Coercion of .val()

In the new syntax, .val() is inserted if the expression is next to an operator or in an array like child selector. You only need to use .val() if you are using a method of a value type like .length, .beginsWith().contains(...). So

newData.child('counter').val() == data.child('counter').val() + 1

is simplified to just

next.counter == prev.counter + 1

Schema

The schema section describes the layout of the data tree. It is strongly suggested you use schema to describe the layout of your Firebase, however it is possible to describe just the end points with access controls. A compiler warning is emitted if you give access to a path that is not described by the data schema.

Types

A Firebase database schema node is either a leaf type (string, number, boolean) or an object which contains more child schema nodes. The type is specified with "type". Children of objects are specified as a map under "properties"

schema:
type: objectproperties:
string_child: {type: string}boolean_child: {type: boolean}number_child: {type: number}anything_child: {}

In the above example you could set {string_child: "blah"} at the root of your database but not {string_child: true}

You can leave a schema unspecified with {} or with type: "any".

required

The required keyword states which children must be present. The required keyword is only valid for schema nodes with the object or any types.

schema:
type: objectrequired: [child1, child2]

additionalProperties

By default, objects can have additional children not mentioned. If additionalProperties is set to false, however, only children explicitly mentioned in the properties are allowed. The additionalProperties keyword is only valid for object and non-typed schemas.

schema:
type: objectadditionalProperties: falseproperties:
string_child: {type: string}

would not accept {number_child: 5} in the root, but without additionalProperties it would.

enum

The enum keyword constrains the value of a string types to be one of the predefined array elements.

schema:
type: stringenum: [yes, no, maybe]

indexOn

The indexOn keyword adds an index for querying. This can either be specified as a single string or an array, applied to non-typed or object types only.

schema:
indexOn: name$user:
indexOn: [inbox, outbox]

ranges

The minimum keyword constrains the minimum value of a number type. You set exclusiveMinimum to true, otherwise the minimum is inclusive. Maximum and exclusiveMaximum follow the pattern

schema:
type: numberminimum: 0maximum: 10exclusiveMaximum: trueexamples:
- 0
- 9.9nonexamples:
- 10

$wildchild

An object can have many children bound to a path variable denoted with a keyword starting with $. Note that wildchilds are not put in the properties definition. The following shows how to accept many objects as children of "/users/"

schema:
type: objectproperties:
users:
type: object$userid: {}

The use of a wildchild prevents all ascendents from being writable.

~$wilderchild

schema:
type: objectproperties:
users:
type: object~$userid: {type: string, constraint: next != null}

Wilderchilds are an unsafe but more flexible wildchild. Use them with caution. Wilderchilds do not lock the parent against writing. Wilderchildren's constraints are respected only when next!=null. This implies wilderchild can be set to null whenever user has write access to them, either by writing to the parent or the wilderchild location directly. In the above example, despite the guard against being set to null in the constraint, the constraint is not evaluated when the wilder child is set to null and thus has no effect. Wilderchilds are useful because their enclosing location can still be written, but be aware of the drawbacks.

Constraints

The semantics of enforcing data integrity is different from the original rules. There is no overriding of constraints, nor separate read/write/validate expressions. There is just one field for expressing data integrity named constraint. All ancestors and descendant constraints must evaluate to true for a write to be allowed.

The following example, fixes the id of a user to equal the key, and makes the account writable only at creation time.

schema:
type: objectproperties:
users:
type: object$userid:
properties:
id:
type: stringconstraint: next == $useridconstraint: (!prev.exists())

You can be sure all constraints above and below evaluate to true for a write to be allowed. The only quirk is related to wildchilds. You can't write anything above a wildchild that includes the wildchild as a descendant. They do inherit their parents constraints though, as do their siblings, so the use of wildchilds never makes the database less constrained accidentally.

Model reuse

Denormalization of data requires replicating a model in multiple places in a schema. JSON Schema allows importing of models across the Internet or within a document through URLs. Currently, blaze only supports in-document reuse.

Model definitions are declared in the keyword definitions object, and references are made using the $ref keyword as follows:

schema:
definitions:
stamped_value:
type: objectproperties:
modified: {type: number}required: [value, modified]constraint: next.value == prev.value || next.modified == nowtype: object$data: {$ref: "#/definitions/stamped_value"}

In JSON Schema you are able to extend model objects using the allOf modeling construct (example). However, blaze does not currently support this. Let us know if you need it!

Inline testing

Writing a complex schema can be difficult. For example, a typo in a required field could enforce the existence of a child other than the one intended. For that reason blaze provides keywords for inline testing of nested schema at compile time.

examples is a list of JSONs that you expect to be accepted by the JSON schema node.

nonexamples is a list of JSONs that you expect to be rejected by the JSON schema node.

These inline tests are good for documenting intent and providing fast feedback when authoring a new schema. Note that inline tests cannot understand the constraint field, they can only test the schema.

schema:
type: objectproperties:
object: {type: object}string: {type: string }boolean: {type: boolean}number: {type: number }additionalProperties: falseexamples:
- {object: {name: "hello"}} # you can have extra children in objects by default
- {string: string}
- {boolean: true}
- {number: 4.6}nonexamples:
- {object: true}
- {string: {grandchild: true}}
- {boolean: "true"}
- {number: "4.6"}
- {extra: "4.6"} #additionProperties is false, so no unexpected properties allowed

Access Control

The schema portion of the rules YAML file is for specifying the data layout and constraints. Read/write access is described in a separate access control list under "access". For each entry, the scope of the rule is a subtree at, or below, the path indicated in the location field. Read access is granted to that subtree if the read expression evaluates to true, and write access is granted if the write expression evaluates to true.

functions:
- isLoggedIn(): auth !== null
...
access:
- location: "/"read: isLoggedIn()
- location: "/users/$userid/"write: auth.username === $userid

Only one access control entry needs to evaluate to true for an operation to be permitted.

Example

This is an example that exploits most of the new features. It is a messaging system where users can send messages to each other, by posting to other user's inboxes

functions: #reusable boolean functions
- isLoggedIn(): auth.username !== null
- createOnly(): next.exists() && !prev.exists()
- deleteOnly(): prev.exists() && !next.exists()
- createOrDelete(): createOnly() || deleteOnly()schema:
definitions: #create a reusable message modelmessage: #for use in the in and out boxestype: objectproperties:
from:
type: string#enforce the from field is *always* correct on creation,#and that only the *box owner* can deleteconstraint: (auth.username == next && createOnly()) ||($userid === auth.username && deleteOnly())#you can't delete single field due to parent's requiredto: {type: string, constraint: createOrDelete()}message: {type: string, constraint: createOrDelete()}required: [from, to, message] # all messages require all the fields to be defined#(or none if the message does not exist)additionalProperties: false #prevent spurious data being part of a messageexamples: #examples of inline testing
- {from: "bill", to: "tom", message: "hey Tom!"}nonexamples:
- {to: "tom", message: "hey Tom!"} #not allowed because from is missingtype: objectproperties:
users: # the users subtree is a collection of userstype: object$userid: #wildchild expression of many childrentype: objectproperties: #each user has an optional inbox and outboxinbox:
type: object$message: {$ref: "#/definitions/message"}outbox:
type: object$message: {$ref: "#/definitions/message"}additionalProperties: falseaccess:
#append only write is granted to anyone's inbox,#so users can send messages to strangers
- location: users/$userid/inbox/write: createOnly() && isLoggedIn()#the inbox owner can delete their incoming mail
- location: users/$userid/inbox/write: deleteOnly() && $userid === auth.username#write and delete is given to owners outbox
- location: users/$userid/outbox/write: true#owners can read everything in their inbox and outbox
- location: users/$userid/read: $userid === auth.username

Changelog

  • 25th Sep 2015:

    • schema padding emits a warning when applied to help visibility
    • performance schema padding efficiency greatly improved
    • performance of optimization routines improved
  • 20th Aug 2015:

    • tailored error message when a wild(er)child is used in a properties section
  • 10th Aug 2015:

    • Allowed repeat examples and non-example, as the error message can be unclearly attached to something unrelated (see repeatExample.yaml)
    • upgrade source-map-repository so blaze_compiler continues to fix issue with io.js
    • Allowed any type to have the required keyword
    • Schema is padded to match with ACL if the ACL is bigger
  • 19th May 2015:

    • Improved optimization use a less verbose object detection notation and spurious parent is an object checks
  • 22nd April 2015:

    • Special cased forgetting to .val() before using an inbuilt string method with an error message
  • 21th April 2015:

    • $ref not importing into (non)example schema fragments properly
  • 10th April 2015:

    • fixed erroneous substitution of parameters into member expressions
  • 12th Jan 2015:

    • improved messaging if blah.child('name') syntax is erroneously used
  • 23rd December 2014:

    • bugfix: function with next or prev were not moved around when in constraints properly (similar to Nov 4th bug)
    • bugfix: minimum and maximum
  • 20th November 2014:

    • support for indexOn
  • 4th November 2014:

    • bugfix: functions in ACL resolved properly
  • 3rd November 2014:

    • bugfix: wilderchild matching fix in ACL
  • 1st November 2014:

    • bugfix: wilderchild overwriting parent constraints bug fixed
    • bugfix: access control constraints localised properly
    • bugfix: regex detection firing erroneously on strings starting with '/' fixed
  • 20th October 2014:

    • optimizations added to reduce code bloat
    • sensitization bug regarding regexes fixed
  • 28th August 2014:ß

    • range constraints for number type added
  • 26th August 2014:

    • wilderchilds introduced, ~$ allows nullable wildchilds whose parents can be written to.
    • sanitized expressions bug fix
  • 18th August 2014:

    • predicates renamed to functions
  • 14th July 2014:

    • improved error reporting
    • updated installation
  • 9th July 2014:

    • support for rules in JSON
  • 30th June 2014:

    • removed trailing /* from access location syntax
    • allowed untyped schema if type is not specified

About

blaze_compiler

Resources

Stars

175 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ' GitHub - googlearchive/blaze_compiler: blaze_compiler · GitHub
Skip to content
This repository was archived by the owner on Aug 25, 2018. It is now read-only.

Repository files navigation

Status: Archived

This repository has been archived and is no longer maintained.

status: inactive

DEPRECATED - NO LONGER MAINTAINED

If you're still interested in using a Firebase Database Security Rules compiler, check out the experimental Bolt compiler.

Blaze Security Compiler for Firebase

The blaze compiler simplifies building security rules for your Firebase database. It drastically reduces the amount of copy and pasting involved. Blaze compiler security rules are shorter, and the syntax is less fussy.

Getting started

npm install -g blaze_compiler

create a rules.yaml containing the following code

functions:
- isLoggedIn(): auth.uid !== nullschema: {}access:
- location: /read: truewrite: true && isLoggedIn()

now compile it from the commandline with

blaze rules.yaml

A rules.json will be generated which you can upload to Firebase!

You can find more about the functions, simpler rule expressions, the schema definitions, access control or inline tests.

Functions

Common expressions for reuse are defined in the functions list. A function can take arguments (they are functions).

functions:
- isLoggedIn(): auth.username !== null
- isUser(username): auth.username === username

You can then use them anywhere a security expression would be expected, for example, in the access control section:-

access:
location: /users/$userid/
write: isUser($userid)

Simple Security Expressions

Security expressions are the strings that used to go in write/read/validate portions of the old security rules. Blaze expressions have similar semantics but shorter syntax.

Variables renamed

data and newData have been renamed prev and next. root has the same meaning.

Child selection

The expression for selecting a child is now an array-like syntax. What was:

root.child('users')

is now

root['users']

In the common case that you are selecting a child using a single literal, you can select the child as if it were a property. So you can also write the above as:

root.users

Coercion of .val()

In the new syntax, .val() is inserted if the expression is next to an operator or in an array like child selector. You only need to use .val() if you are using a method of a value type like .length, .beginsWith().contains(...). So

newData.child('counter').val() == data.child('counter').val() + 1

is simplified to just

next.counter == prev.counter + 1

Schema

The schema section describes the layout of the data tree. It is strongly suggested you use schema to describe the layout of your Firebase, however it is possible to describe just the end points with access controls. A compiler warning is emitted if you give access to a path that is not described by the data schema.

Types

A Firebase database schema node is either a leaf type (string, number, boolean) or an object which contains more child schema nodes. The type is specified with "type". Children of objects are specified as a map under "properties"

schema:
type: objectproperties:
string_child: {type: string}boolean_child: {type: boolean}number_child: {type: number}anything_child: {}

In the above example you could set {string_child: "blah"} at the root of your database but not {string_child: true}

You can leave a schema unspecified with {} or with type: "any".

required

The required keyword states which children must be present. The required keyword is only valid for schema nodes with the object or any types.

schema:
type: objectrequired: [child1, child2]

additionalProperties

By default, objects can have additional children not mentioned. If additionalProperties is set to false, however, only children explicitly mentioned in the properties are allowed. The additionalProperties keyword is only valid for object and non-typed schemas.

schema:
type: objectadditionalProperties: falseproperties:
string_child: {type: string}

would not accept {number_child: 5} in the root, but without additionalProperties it would.

enum

The enum keyword constrains the value of a string types to be one of the predefined array elements.

schema:
type: stringenum: [yes, no, maybe]

indexOn

The indexOn keyword adds an index for querying. This can either be specified as a single string or an array, applied to non-typed or object types only.

schema:
indexOn: name$user:
indexOn: [inbox, outbox]

ranges

The minimum keyword constrains the minimum value of a number type. You set exclusiveMinimum to true, otherwise the minimum is inclusive. Maximum and exclusiveMaximum follow the pattern

schema:
type: numberminimum: 0maximum: 10exclusiveMaximum: trueexamples:
- 0
- 9.9nonexamples:
- 10

$wildchild

An object can have many children bound to a path variable denoted with a keyword starting with $. Note that wildchilds are not put in the properties definition. The following shows how to accept many objects as children of "/users/"

schema:
type: objectproperties:
users:
type: object$userid: {}

The use of a wildchild prevents all ascendents from being writable.

~$wilderchild

schema:
type: objectproperties:
users:
type: object~$userid: {type: string, constraint: next != null}

Wilderchilds are an unsafe but more flexible wildchild. Use them with caution. Wilderchilds do not lock the parent against writing. Wilderchildren's constraints are respected only when next!=null. This implies wilderchild can be set to null whenever user has write access to them, either by writing to the parent or the wilderchild location directly. In the above example, despite the guard against being set to null in the constraint, the constraint is not evaluated when the wilder child is set to null and thus has no effect. Wilderchilds are useful because their enclosing location can still be written, but be aware of the drawbacks.

Constraints

The semantics of enforcing data integrity is different from the original rules. There is no overriding of constraints, nor separate read/write/validate expressions. There is just one field for expressing data integrity named constraint. All ancestors and descendant constraints must evaluate to true for a write to be allowed.

The following example, fixes the id of a user to equal the key, and makes the account writable only at creation time.

schema:
type: objectproperties:
users:
type: object$userid:
properties:
id:
type: stringconstraint: next == $useridconstraint: (!prev.exists())

You can be sure all constraints above and below evaluate to true for a write to be allowed. The only quirk is related to wildchilds. You can't write anything above a wildchild that includes the wildchild as a descendant. They do inherit their parents constraints though, as do their siblings, so the use of wildchilds never makes the database less constrained accidentally.

Model reuse

Denormalization of data requires replicating a model in multiple places in a schema. JSON Schema allows importing of models across the Internet or within a document through URLs. Currently, blaze only supports in-document reuse.

Model definitions are declared in the keyword definitions object, and references are made using the $ref keyword as follows:

schema:
definitions:
stamped_value:
type: objectproperties:
modified: {type: number}required: [value, modified]constraint: next.value == prev.value || next.modified == nowtype: object$data: {$ref: "#/definitions/stamped_value"}

In JSON Schema you are able to extend model objects using the allOf modeling construct (example). However, blaze does not currently support this. Let us know if you need it!

Inline testing

Writing a complex schema can be difficult. For example, a typo in a required field could enforce the existence of a child other than the one intended. For that reason blaze provides keywords for inline testing of nested schema at compile time.

examples is a list of JSONs that you expect to be accepted by the JSON schema node.

nonexamples is a list of JSONs that you expect to be rejected by the JSON schema node.

These inline tests are good for documenting intent and providing fast feedback when authoring a new schema. Note that inline tests cannot understand the constraint field, they can only test the schema.

schema:
type: objectproperties:
object: {type: object}string: {type: string }boolean: {type: boolean}number: {type: number }additionalProperties: falseexamples:
- {object: {name: "hello"}} # you can have extra children in objects by default
- {string: string}
- {boolean: true}
- {number: 4.6}nonexamples:
- {object: true}
- {string: {grandchild: true}}
- {boolean: "true"}
- {number: "4.6"}
- {extra: "4.6"} #additionProperties is false, so no unexpected properties allowed

Access Control

The schema portion of the rules YAML file is for specifying the data layout and constraints. Read/write access is described in a separate access control list under "access". For each entry, the scope of the rule is a subtree at, or below, the path indicated in the location field. Read access is granted to that subtree if the read expression evaluates to true, and write access is granted if the write expression evaluates to true.

functions:
- isLoggedIn(): auth !== null
...
access:
- location: "/"read: isLoggedIn()
- location: "/users/$userid/"write: auth.username === $userid

Only one access control entry needs to evaluate to true for an operation to be permitted.

Example

This is an example that exploits most of the new features. It is a messaging system where users can send messages to each other, by posting to other user's inboxes

functions: #reusable boolean functions
- isLoggedIn(): auth.username !== null
- createOnly(): next.exists() && !prev.exists()
- deleteOnly(): prev.exists() && !next.exists()
- createOrDelete(): createOnly() || deleteOnly()schema:
definitions: #create a reusable message modelmessage: #for use in the in and out boxestype: objectproperties:
from:
type: string#enforce the from field is *always* correct on creation,#and that only the *box owner* can deleteconstraint: (auth.username == next && createOnly()) ||($userid === auth.username && deleteOnly())#you can't delete single field due to parent's requiredto: {type: string, constraint: createOrDelete()}message: {type: string, constraint: createOrDelete()}required: [from, to, message] # all messages require all the fields to be defined#(or none if the message does not exist)additionalProperties: false #prevent spurious data being part of a messageexamples: #examples of inline testing
- {from: "bill", to: "tom", message: "hey Tom!"}nonexamples:
- {to: "tom", message: "hey Tom!"} #not allowed because from is missingtype: objectproperties:
users: # the users subtree is a collection of userstype: object$userid: #wildchild expression of many childrentype: objectproperties: #each user has an optional inbox and outboxinbox:
type: object$message: {$ref: "#/definitions/message"}outbox:
type: object$message: {$ref: "#/definitions/message"}additionalProperties: falseaccess:
#append only write is granted to anyone's inbox,#so users can send messages to strangers
- location: users/$userid/inbox/write: createOnly() && isLoggedIn()#the inbox owner can delete their incoming mail
- location: users/$userid/inbox/write: deleteOnly() && $userid === auth.username#write and delete is given to owners outbox
- location: users/$userid/outbox/write: true#owners can read everything in their inbox and outbox
- location: users/$userid/read: $userid === auth.username

Changelog

  • 25th Sep 2015:

    • schema padding emits a warning when applied to help visibility
    • performance schema padding efficiency greatly improved
    • performance of optimization routines improved
  • 20th Aug 2015:

    • tailored error message when a wild(er)child is used in a properties section
  • 10th Aug 2015:

    • Allowed repeat examples and non-example, as the error message can be unclearly attached to something unrelated (see repeatExample.yaml)
    • upgrade source-map-repository so blaze_compiler continues to fix issue with io.js
    • Allowed any type to have the required keyword
    • Schema is padded to match with ACL if the ACL is bigger
  • 19th May 2015:

    • Improved optimization use a less verbose object detection notation and spurious parent is an object checks
  • 22nd April 2015:

    • Special cased forgetting to .val() before using an inbuilt string method with an error message
  • 21th April 2015:

    • $ref not importing into (non)example schema fragments properly
  • 10th April 2015:

    • fixed erroneous substitution of parameters into member expressions
  • 12th Jan 2015:

    • improved messaging if blah.child('name') syntax is erroneously used
  • 23rd December 2014:

    • bugfix: function with next or prev were not moved around when in constraints properly (similar to Nov 4th bug)
    • bugfix: minimum and maximum
  • 20th November 2014:

    • support for indexOn
  • 4th November 2014:

    • bugfix: functions in ACL resolved properly
  • 3rd November 2014:

    • bugfix: wilderchild matching fix in ACL
  • 1st November 2014:

    • bugfix: wilderchild overwriting parent constraints bug fixed
    • bugfix: access control constraints localised properly
    • bugfix: regex detection firing erroneously on strings starting with '/' fixed
  • 20th October 2014:

    • optimizations added to reduce code bloat
    • sensitization bug regarding regexes fixed
  • 28th August 2014:ß

    • range constraints for number type added
  • 26th August 2014:

    • wilderchilds introduced, ~$ allows nullable wildchilds whose parents can be written to.
    • sanitized expressions bug fix
  • 18th August 2014:

    • predicates renamed to functions
  • 14th July 2014:

    • improved error reporting
    • updated installation
  • 9th July 2014:

    • support for rules in JSON
  • 30th June 2014:

    • removed trailing /* from access location syntax
    • allowed untyped schema if type is not specified

About

blaze_compiler

Resources

Stars

175 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' GitHub - googlearchive/blaze_compiler: blaze_compiler · GitHub
Skip to content
This repository was archived by the owner on Aug 25, 2018. It is now read-only.

Repository files navigation

Status: Archived

This repository has been archived and is no longer maintained.

status: inactive

DEPRECATED - NO LONGER MAINTAINED

If you're still interested in using a Firebase Database Security Rules compiler, check out the experimental Bolt compiler.

Blaze Security Compiler for Firebase

The blaze compiler simplifies building security rules for your Firebase database. It drastically reduces the amount of copy and pasting involved. Blaze compiler security rules are shorter, and the syntax is less fussy.

Getting started

npm install -g blaze_compiler

create a rules.yaml containing the following code

functions:
- isLoggedIn(): auth.uid !== nullschema: {}access:
- location: /read: truewrite: true && isLoggedIn()

now compile it from the commandline with

blaze rules.yaml

A rules.json will be generated which you can upload to Firebase!

You can find more about the functions, simpler rule expressions, the schema definitions, access control or inline tests.

Functions

Common expressions for reuse are defined in the functions list. A function can take arguments (they are functions).

functions:
- isLoggedIn(): auth.username !== null
- isUser(username): auth.username === username

You can then use them anywhere a security expression would be expected, for example, in the access control section:-

access:
location: /users/$userid/
write: isUser($userid)

Simple Security Expressions

Security expressions are the strings that used to go in write/read/validate portions of the old security rules. Blaze expressions have similar semantics but shorter syntax.

Variables renamed

data and newData have been renamed prev and next. root has the same meaning.

Child selection

The expression for selecting a child is now an array-like syntax. What was:

root.child('users')

is now

root['users']

In the common case that you are selecting a child using a single literal, you can select the child as if it were a property. So you can also write the above as:

root.users

Coercion of .val()

In the new syntax, .val() is inserted if the expression is next to an operator or in an array like child selector. You only need to use .val() if you are using a method of a value type like .length, .beginsWith().contains(...). So

newData.child('counter').val() == data.child('counter').val() + 1

is simplified to just

next.counter == prev.counter + 1

Schema

The schema section describes the layout of the data tree. It is strongly suggested you use schema to describe the layout of your Firebase, however it is possible to describe just the end points with access controls. A compiler warning is emitted if you give access to a path that is not described by the data schema.

Types

A Firebase database schema node is either a leaf type (string, number, boolean) or an object which contains more child schema nodes. The type is specified with "type". Children of objects are specified as a map under "properties"

schema:
type: objectproperties:
string_child: {type: string}boolean_child: {type: boolean}number_child: {type: number}anything_child: {}

In the above example you could set {string_child: "blah"} at the root of your database but not {string_child: true}

You can leave a schema unspecified with {} or with type: "any".

required

The required keyword states which children must be present. The required keyword is only valid for schema nodes with the object or any types.

schema:
type: objectrequired: [child1, child2]

additionalProperties

By default, objects can have additional children not mentioned. If additionalProperties is set to false, however, only children explicitly mentioned in the properties are allowed. The additionalProperties keyword is only valid for object and non-typed schemas.

schema:
type: objectadditionalProperties: falseproperties:
string_child: {type: string}

would not accept {number_child: 5} in the root, but without additionalProperties it would.

enum

The enum keyword constrains the value of a string types to be one of the predefined array elements.

schema:
type: stringenum: [yes, no, maybe]

indexOn

The indexOn keyword adds an index for querying. This can either be specified as a single string or an array, applied to non-typed or object types only.

schema:
indexOn: name$user:
indexOn: [inbox, outbox]

ranges

The minimum keyword constrains the minimum value of a number type. You set exclusiveMinimum to true, otherwise the minimum is inclusive. Maximum and exclusiveMaximum follow the pattern

schema:
type: numberminimum: 0maximum: 10exclusiveMaximum: trueexamples:
- 0
- 9.9nonexamples:
- 10

$wildchild

An object can have many children bound to a path variable denoted with a keyword starting with $. Note that wildchilds are not put in the properties definition. The following shows how to accept many objects as children of "/users/"

schema:
type: objectproperties:
users:
type: object$userid: {}

The use of a wildchild prevents all ascendents from being writable.

~$wilderchild

schema:
type: objectproperties:
users:
type: object~$userid: {type: string, constraint: next != null}

Wilderchilds are an unsafe but more flexible wildchild. Use them with caution. Wilderchilds do not lock the parent against writing. Wilderchildren's constraints are respected only when next!=null. This implies wilderchild can be set to null whenever user has write access to them, either by writing to the parent or the wilderchild location directly. In the above example, despite the guard against being set to null in the constraint, the constraint is not evaluated when the wilder child is set to null and thus has no effect. Wilderchilds are useful because their enclosing location can still be written, but be aware of the drawbacks.

Constraints

The semantics of enforcing data integrity is different from the original rules. There is no overriding of constraints, nor separate read/write/validate expressions. There is just one field for expressing data integrity named constraint. All ancestors and descendant constraints must evaluate to true for a write to be allowed.

The following example, fixes the id of a user to equal the key, and makes the account writable only at creation time.

schema:
type: objectproperties:
users:
type: object$userid:
properties:
id:
type: stringconstraint: next == $useridconstraint: (!prev.exists())

You can be sure all constraints above and below evaluate to true for a write to be allowed. The only quirk is related to wildchilds. You can't write anything above a wildchild that includes the wildchild as a descendant. They do inherit their parents constraints though, as do their siblings, so the use of wildchilds never makes the database less constrained accidentally.

Model reuse

Denormalization of data requires replicating a model in multiple places in a schema. JSON Schema allows importing of models across the Internet or within a document through URLs. Currently, blaze only supports in-document reuse.

Model definitions are declared in the keyword definitions object, and references are made using the $ref keyword as follows:

schema:
definitions:
stamped_value:
type: objectproperties:
modified: {type: number}required: [value, modified]constraint: next.value == prev.value || next.modified == nowtype: object$data: {$ref: "#/definitions/stamped_value"}

In JSON Schema you are able to extend model objects using the allOf modeling construct (example). However, blaze does not currently support this. Let us know if you need it!

Inline testing

Writing a complex schema can be difficult. For example, a typo in a required field could enforce the existence of a child other than the one intended. For that reason blaze provides keywords for inline testing of nested schema at compile time.

examples is a list of JSONs that you expect to be accepted by the JSON schema node.

nonexamples is a list of JSONs that you expect to be rejected by the JSON schema node.

These inline tests are good for documenting intent and providing fast feedback when authoring a new schema. Note that inline tests cannot understand the constraint field, they can only test the schema.

schema:
type: objectproperties:
object: {type: object}string: {type: string }boolean: {type: boolean}number: {type: number }additionalProperties: falseexamples:
- {object: {name: "hello"}} # you can have extra children in objects by default
- {string: string}
- {boolean: true}
- {number: 4.6}nonexamples:
- {object: true}
- {string: {grandchild: true}}
- {boolean: "true"}
- {number: "4.6"}
- {extra: "4.6"} #additionProperties is false, so no unexpected properties allowed

Access Control

The schema portion of the rules YAML file is for specifying the data layout and constraints. Read/write access is described in a separate access control list under "access". For each entry, the scope of the rule is a subtree at, or below, the path indicated in the location field. Read access is granted to that subtree if the read expression evaluates to true, and write access is granted if the write expression evaluates to true.

functions:
- isLoggedIn(): auth !== null
...
access:
- location: "/"read: isLoggedIn()
- location: "/users/$userid/"write: auth.username === $userid

Only one access control entry needs to evaluate to true for an operation to be permitted.

Example

This is an example that exploits most of the new features. It is a messaging system where users can send messages to each other, by posting to other user's inboxes

functions: #reusable boolean functions
- isLoggedIn(): auth.username !== null
- createOnly(): next.exists() && !prev.exists()
- deleteOnly(): prev.exists() && !next.exists()
- createOrDelete(): createOnly() || deleteOnly()schema:
definitions: #create a reusable message modelmessage: #for use in the in and out boxestype: objectproperties:
from:
type: string#enforce the from field is *always* correct on creation,#and that only the *box owner* can deleteconstraint: (auth.username == next && createOnly()) ||($userid === auth.username && deleteOnly())#you can't delete single field due to parent's requiredto: {type: string, constraint: createOrDelete()}message: {type: string, constraint: createOrDelete()}required: [from, to, message] # all messages require all the fields to be defined#(or none if the message does not exist)additionalProperties: false #prevent spurious data being part of a messageexamples: #examples of inline testing
- {from: "bill", to: "tom", message: "hey Tom!"}nonexamples:
- {to: "tom", message: "hey Tom!"} #not allowed because from is missingtype: objectproperties:
users: # the users subtree is a collection of userstype: object$userid: #wildchild expression of many childrentype: objectproperties: #each user has an optional inbox and outboxinbox:
type: object$message: {$ref: "#/definitions/message"}outbox:
type: object$message: {$ref: "#/definitions/message"}additionalProperties: falseaccess:
#append only write is granted to anyone's inbox,#so users can send messages to strangers
- location: users/$userid/inbox/write: createOnly() && isLoggedIn()#the inbox owner can delete their incoming mail
- location: users/$userid/inbox/write: deleteOnly() && $userid === auth.username#write and delete is given to owners outbox
- location: users/$userid/outbox/write: true#owners can read everything in their inbox and outbox
- location: users/$userid/read: $userid === auth.username

Changelog

  • 25th Sep 2015:

    • schema padding emits a warning when applied to help visibility
    • performance schema padding efficiency greatly improved
    • performance of optimization routines improved
  • 20th Aug 2015:

    • tailored error message when a wild(er)child is used in a properties section
  • 10th Aug 2015:

    • Allowed repeat examples and non-example, as the error message can be unclearly attached to something unrelated (see repeatExample.yaml)
    • upgrade source-map-repository so blaze_compiler continues to fix issue with io.js
    • Allowed any type to have the required keyword
    • Schema is padded to match with ACL if the ACL is bigger
  • 19th May 2015:

    • Improved optimization use a less verbose object detection notation and spurious parent is an object checks
  • 22nd April 2015:

    • Special cased forgetting to .val() before using an inbuilt string method with an error message
  • 21th April 2015:

    • $ref not importing into (non)example schema fragments properly
  • 10th April 2015:

    • fixed erroneous substitution of parameters into member expressions
  • 12th Jan 2015:

    • improved messaging if blah.child('name') syntax is erroneously used
  • 23rd December 2014:

    • bugfix: function with next or prev were not moved around when in constraints properly (similar to Nov 4th bug)
    • bugfix: minimum and maximum
  • 20th November 2014:

    • support for indexOn
  • 4th November 2014:

    • bugfix: functions in ACL resolved properly
  • 3rd November 2014:

    • bugfix: wilderchild matching fix in ACL
  • 1st November 2014:

    • bugfix: wilderchild overwriting parent constraints bug fixed
    • bugfix: access control constraints localised properly
    • bugfix: regex detection firing erroneously on strings starting with '/' fixed
  • 20th October 2014:

    • optimizations added to reduce code bloat
    • sensitization bug regarding regexes fixed
  • 28th August 2014:ß

    • range constraints for number type added
  • 26th August 2014:

    • wilderchilds introduced, ~$ allows nullable wildchilds whose parents can be written to.
    • sanitized expressions bug fix
  • 18th August 2014:

    • predicates renamed to functions
  • 14th July 2014:

    • improved error reporting
    • updated installation
  • 9th July 2014:

    • support for rules in JSON
  • 30th June 2014:

    • removed trailing /* from access location syntax
    • allowed untyped schema if type is not specified

About

blaze_compiler

Resources

Stars

175 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' GitHub - googlearchive/blaze_compiler: blaze_compiler · GitHub
Skip to content
This repository was archived by the owner on Aug 25, 2018. It is now read-only.

Repository files navigation

Status: Archived

This repository has been archived and is no longer maintained.

status: inactive

DEPRECATED - NO LONGER MAINTAINED

If you're still interested in using a Firebase Database Security Rules compiler, check out the experimental Bolt compiler.

Blaze Security Compiler for Firebase

The blaze compiler simplifies building security rules for your Firebase database. It drastically reduces the amount of copy and pasting involved. Blaze compiler security rules are shorter, and the syntax is less fussy.

Getting started

npm install -g blaze_compiler

create a rules.yaml containing the following code

functions:
- isLoggedIn(): auth.uid !== nullschema: {}access:
- location: /read: truewrite: true && isLoggedIn()

now compile it from the commandline with

blaze rules.yaml

A rules.json will be generated which you can upload to Firebase!

You can find more about the functions, simpler rule expressions, the schema definitions, access control or inline tests.

Functions

Common expressions for reuse are defined in the functions list. A function can take arguments (they are functions).

functions:
- isLoggedIn(): auth.username !== null
- isUser(username): auth.username === username

You can then use them anywhere a security expression would be expected, for example, in the access control section:-

access:
location: /users/$userid/
write: isUser($userid)

Simple Security Expressions

Security expressions are the strings that used to go in write/read/validate portions of the old security rules. Blaze expressions have similar semantics but shorter syntax.

Variables renamed

data and newData have been renamed prev and next. root has the same meaning.

Child selection

The expression for selecting a child is now an array-like syntax. What was:

root.child('users')

is now

root['users']

In the common case that you are selecting a child using a single literal, you can select the child as if it were a property. So you can also write the above as:

root.users

Coercion of .val()

In the new syntax, .val() is inserted if the expression is next to an operator or in an array like child selector. You only need to use .val() if you are using a method of a value type like .length, .beginsWith().contains(...). So

newData.child('counter').val() == data.child('counter').val() + 1

is simplified to just

next.counter == prev.counter + 1

Schema

The schema section describes the layout of the data tree. It is strongly suggested you use schema to describe the layout of your Firebase, however it is possible to describe just the end points with access controls. A compiler warning is emitted if you give access to a path that is not described by the data schema.

Types

A Firebase database schema node is either a leaf type (string, number, boolean) or an object which contains more child schema nodes. The type is specified with "type". Children of objects are specified as a map under "properties"

schema:
type: objectproperties:
string_child: {type: string}boolean_child: {type: boolean}number_child: {type: number}anything_child: {}

In the above example you could set {string_child: "blah"} at the root of your database but not {string_child: true}

You can leave a schema unspecified with {} or with type: "any".

required

The required keyword states which children must be present. The required keyword is only valid for schema nodes with the object or any types.

schema:
type: objectrequired: [child1, child2]

additionalProperties

By default, objects can have additional children not mentioned. If additionalProperties is set to false, however, only children explicitly mentioned in the properties are allowed. The additionalProperties keyword is only valid for object and non-typed schemas.

schema:
type: objectadditionalProperties: falseproperties:
string_child: {type: string}

would not accept {number_child: 5} in the root, but without additionalProperties it would.

enum

The enum keyword constrains the value of a string types to be one of the predefined array elements.

schema:
type: stringenum: [yes, no, maybe]

indexOn

The indexOn keyword adds an index for querying. This can either be specified as a single string or an array, applied to non-typed or object types only.

schema:
indexOn: name$user:
indexOn: [inbox, outbox]

ranges

The minimum keyword constrains the minimum value of a number type. You set exclusiveMinimum to true, otherwise the minimum is inclusive. Maximum and exclusiveMaximum follow the pattern

schema:
type: numberminimum: 0maximum: 10exclusiveMaximum: trueexamples:
- 0
- 9.9nonexamples:
- 10

$wildchild

An object can have many children bound to a path variable denoted with a keyword starting with $. Note that wildchilds are not put in the properties definition. The following shows how to accept many objects as children of "/users/"

schema:
type: objectproperties:
users:
type: object$userid: {}

The use of a wildchild prevents all ascendents from being writable.

~$wilderchild

schema:
type: objectproperties:
users:
type: object~$userid: {type: string, constraint: next != null}

Wilderchilds are an unsafe but more flexible wildchild. Use them with caution. Wilderchilds do not lock the parent against writing. Wilderchildren's constraints are respected only when next!=null. This implies wilderchild can be set to null whenever user has write access to them, either by writing to the parent or the wilderchild location directly. In the above example, despite the guard against being set to null in the constraint, the constraint is not evaluated when the wilder child is set to null and thus has no effect. Wilderchilds are useful because their enclosing location can still be written, but be aware of the drawbacks.

Constraints

The semantics of enforcing data integrity is different from the original rules. There is no overriding of constraints, nor separate read/write/validate expressions. There is just one field for expressing data integrity named constraint. All ancestors and descendant constraints must evaluate to true for a write to be allowed.

The following example, fixes the id of a user to equal the key, and makes the account writable only at creation time.

schema:
type: objectproperties:
users:
type: object$userid:
properties:
id:
type: stringconstraint: next == $useridconstraint: (!prev.exists())

You can be sure all constraints above and below evaluate to true for a write to be allowed. The only quirk is related to wildchilds. You can't write anything above a wildchild that includes the wildchild as a descendant. They do inherit their parents constraints though, as do their siblings, so the use of wildchilds never makes the database less constrained accidentally.

Model reuse

Denormalization of data requires replicating a model in multiple places in a schema. JSON Schema allows importing of models across the Internet or within a document through URLs. Currently, blaze only supports in-document reuse.

Model definitions are declared in the keyword definitions object, and references are made using the $ref keyword as follows:

schema:
definitions:
stamped_value:
type: objectproperties:
modified: {type: number}required: [value, modified]constraint: next.value == prev.value || next.modified == nowtype: object$data: {$ref: "#/definitions/stamped_value"}

In JSON Schema you are able to extend model objects using the allOf modeling construct (example). However, blaze does not currently support this. Let us know if you need it!

Inline testing

Writing a complex schema can be difficult. For example, a typo in a required field could enforce the existence of a child other than the one intended. For that reason blaze provides keywords for inline testing of nested schema at compile time.

examples is a list of JSONs that you expect to be accepted by the JSON schema node.

nonexamples is a list of JSONs that you expect to be rejected by the JSON schema node.

These inline tests are good for documenting intent and providing fast feedback when authoring a new schema. Note that inline tests cannot understand the constraint field, they can only test the schema.

schema:
type: objectproperties:
object: {type: object}string: {type: string }boolean: {type: boolean}number: {type: number }additionalProperties: falseexamples:
- {object: {name: "hello"}} # you can have extra children in objects by default
- {string: string}
- {boolean: true}
- {number: 4.6}nonexamples:
- {object: true}
- {string: {grandchild: true}}
- {boolean: "true"}
- {number: "4.6"}
- {extra: "4.6"} #additionProperties is false, so no unexpected properties allowed

Access Control

The schema portion of the rules YAML file is for specifying the data layout and constraints. Read/write access is described in a separate access control list under "access". For each entry, the scope of the rule is a subtree at, or below, the path indicated in the location field. Read access is granted to that subtree if the read expression evaluates to true, and write access is granted if the write expression evaluates to true.

functions:
- isLoggedIn(): auth !== null
...
access:
- location: "/"read: isLoggedIn()
- location: "/users/$userid/"write: auth.username === $userid

Only one access control entry needs to evaluate to true for an operation to be permitted.

Example

This is an example that exploits most of the new features. It is a messaging system where users can send messages to each other, by posting to other user's inboxes

functions: #reusable boolean functions
- isLoggedIn(): auth.username !== null
- createOnly(): next.exists() && !prev.exists()
- deleteOnly(): prev.exists() && !next.exists()
- createOrDelete(): createOnly() || deleteOnly()schema:
definitions: #create a reusable message modelmessage: #for use in the in and out boxestype: objectproperties:
from:
type: string#enforce the from field is *always* correct on creation,#and that only the *box owner* can deleteconstraint: (auth.username == next && createOnly()) ||($userid === auth.username && deleteOnly())#you can't delete single field due to parent's requiredto: {type: string, constraint: createOrDelete()}message: {type: string, constraint: createOrDelete()}required: [from, to, message] # all messages require all the fields to be defined#(or none if the message does not exist)additionalProperties: false #prevent spurious data being part of a messageexamples: #examples of inline testing
- {from: "bill", to: "tom", message: "hey Tom!"}nonexamples:
- {to: "tom", message: "hey Tom!"} #not allowed because from is missingtype: objectproperties:
users: # the users subtree is a collection of userstype: object$userid: #wildchild expression of many childrentype: objectproperties: #each user has an optional inbox and outboxinbox:
type: object$message: {$ref: "#/definitions/message"}outbox:
type: object$message: {$ref: "#/definitions/message"}additionalProperties: falseaccess:
#append only write is granted to anyone's inbox,#so users can send messages to strangers
- location: users/$userid/inbox/write: createOnly() && isLoggedIn()#the inbox owner can delete their incoming mail
- location: users/$userid/inbox/write: deleteOnly() && $userid === auth.username#write and delete is given to owners outbox
- location: users/$userid/outbox/write: true#owners can read everything in their inbox and outbox
- location: users/$userid/read: $userid === auth.username

Changelog

  • 25th Sep 2015:

    • schema padding emits a warning when applied to help visibility
    • performance schema padding efficiency greatly improved
    • performance of optimization routines improved
  • 20th Aug 2015:

    • tailored error message when a wild(er)child is used in a properties section
  • 10th Aug 2015:

    • Allowed repeat examples and non-example, as the error message can be unclearly attached to something unrelated (see repeatExample.yaml)
    • upgrade source-map-repository so blaze_compiler continues to fix issue with io.js
    • Allowed any type to have the required keyword
    • Schema is padded to match with ACL if the ACL is bigger
  • 19th May 2015:

    • Improved optimization use a less verbose object detection notation and spurious parent is an object checks
  • 22nd April 2015:

    • Special cased forgetting to .val() before using an inbuilt string method with an error message
  • 21th April 2015:

    • $ref not importing into (non)example schema fragments properly
  • 10th April 2015:

    • fixed erroneous substitution of parameters into member expressions
  • 12th Jan 2015:

    • improved messaging if blah.child('name') syntax is erroneously used
  • 23rd December 2014:

    • bugfix: function with next or prev were not moved around when in constraints properly (similar to Nov 4th bug)
    • bugfix: minimum and maximum
  • 20th November 2014:

    • support for indexOn
  • 4th November 2014:

    • bugfix: functions in ACL resolved properly
  • 3rd November 2014:

    • bugfix: wilderchild matching fix in ACL
  • 1st November 2014:

    • bugfix: wilderchild overwriting parent constraints bug fixed
    • bugfix: access control constraints localised properly
    • bugfix: regex detection firing erroneously on strings starting with '/' fixed
  • 20th October 2014:

    • optimizations added to reduce code bloat
    • sensitization bug regarding regexes fixed
  • 28th August 2014:ß

    • range constraints for number type added
  • 26th August 2014:

    • wilderchilds introduced, ~$ allows nullable wildchilds whose parents can be written to.
    • sanitized expressions bug fix
  • 18th August 2014:

    • predicates renamed to functions
  • 14th July 2014:

    • improved error reporting
    • updated installation
  • 9th July 2014:

    • support for rules in JSON
  • 30th June 2014:

    • removed trailing /* from access location syntax
    • allowed untyped schema if type is not specified

About

blaze_compiler

Resources

Stars

175 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Universal Dark Mode - works on any site (function() { var enabled = true; function applyDarkMode() { if (!enabled) return; // Create style element if it doesn't exist var style = document.getElementById('universal-dark-mode-style'); if (!style) { style = document.createElement('style'); style.id = 'universal-dark-mode-style'; document.head.appendChild(style); } // Dark mode CSS - inverts colors but preserves images/video style.textContent = ' /* Invert everything except media */ html { filter: invert(1) hue-rotate(180deg) !important; background: #1a1a2e !important; } /* Restore images, videos, iframes, canvas */ img, video, iframe, canvas, svg, picture, [style*="background-image"] { filter: invert(1) hue-rotate(180deg) !important; } /* Preserve specific elements that should not be inverted */ .no-dark-mode, .no-dark-mode *, [data-theme="light"], [data-theme="light"], .ace_editor, .ace_editor *, .CodeMirror, .CodeMirror *, .monaco-editor, .monaco-editor *, .markdown-body pre, .markdown-body pre *, .highlight, .highlight *, pre code, pre code * { filter: none !important; } /* Fix common UI elements */ .modal, .popup, .dropdown-menu, .tooltip, .popover { filter: invert(1) hue-rotate(180deg) !important; background: #2d2d44 !important; border-color: #444 !important; } /* Scrollbars */ ::-webkit-scrollbar { background: #1a1a2e !important; } ::-webkit-scrollbar-thumb { background: #444 !important; } ::-webkit-scrollbar-thumb:hover { background: #555 !important; } /* Selection */ ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; } ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; } '; } function removeDarkMode() { var style = document.getElementById('universal-dark-mode-style'); if (style) style.remove(); } // Toggle with Alt+Shift+D document.addEventListener('keydown', function(e) { if (e.altKey && e.shiftKey && e.key === 'D') { e.preventDefault(); enabled = !enabled; if (enabled) { applyDarkMode(); console.log('[Universal Dark Mode] Enabled'); } else { removeDarkMode(); console.log('[Universal Dark Mode] Disabled'); } } }); // Apply on load applyDarkMode(); // Re-apply on dynamic content var observer = new MutationObserver(function(mutations) { if (enabled && !document.getElementById('universal-dark-mode-style')) { applyDarkMode(); } }); observer.observe(document.head, { childList: true }); console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle'); })(); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })(); GitHub - googlearchive/blaze_compiler: blaze_compiler · GitHub
Skip to content
This repository was archived by the owner on Aug 25, 2018. It is now read-only.

Repository files navigation

Status: Archived

This repository has been archived and is no longer maintained.

status: inactive

DEPRECATED - NO LONGER MAINTAINED

If you're still interested in using a Firebase Database Security Rules compiler, check out the experimental Bolt compiler.

Blaze Security Compiler for Firebase

The blaze compiler simplifies building security rules for your Firebase database. It drastically reduces the amount of copy and pasting involved. Blaze compiler security rules are shorter, and the syntax is less fussy.

Getting started

npm install -g blaze_compiler

create a rules.yaml containing the following code

functions:
- isLoggedIn(): auth.uid !== nullschema: {}access:
- location: /read: truewrite: true && isLoggedIn()

now compile it from the commandline with

blaze rules.yaml

A rules.json will be generated which you can upload to Firebase!

You can find more about the functions, simpler rule expressions, the schema definitions, access control or inline tests.

Functions

Common expressions for reuse are defined in the functions list. A function can take arguments (they are functions).

functions:
- isLoggedIn(): auth.username !== null
- isUser(username): auth.username === username

You can then use them anywhere a security expression would be expected, for example, in the access control section:-

access:
location: /users/$userid/
write: isUser($userid)

Simple Security Expressions

Security expressions are the strings that used to go in write/read/validate portions of the old security rules. Blaze expressions have similar semantics but shorter syntax.

Variables renamed

data and newData have been renamed prev and next. root has the same meaning.

Child selection

The expression for selecting a child is now an array-like syntax. What was:

root.child('users')

is now

root['users']

In the common case that you are selecting a child using a single literal, you can select the child as if it were a property. So you can also write the above as:

root.users

Coercion of .val()

In the new syntax, .val() is inserted if the expression is next to an operator or in an array like child selector. You only need to use .val() if you are using a method of a value type like .length, .beginsWith().contains(...). So

newData.child('counter').val() == data.child('counter').val() + 1

is simplified to just

next.counter == prev.counter + 1

Schema

The schema section describes the layout of the data tree. It is strongly suggested you use schema to describe the layout of your Firebase, however it is possible to describe just the end points with access controls. A compiler warning is emitted if you give access to a path that is not described by the data schema.

Types

A Firebase database schema node is either a leaf type (string, number, boolean) or an object which contains more child schema nodes. The type is specified with "type". Children of objects are specified as a map under "properties"

schema:
type: objectproperties:
string_child: {type: string}boolean_child: {type: boolean}number_child: {type: number}anything_child: {}

In the above example you could set {string_child: "blah"} at the root of your database but not {string_child: true}

You can leave a schema unspecified with {} or with type: "any".

required

The required keyword states which children must be present. The required keyword is only valid for schema nodes with the object or any types.

schema:
type: objectrequired: [child1, child2]

additionalProperties

By default, objects can have additional children not mentioned. If additionalProperties is set to false, however, only children explicitly mentioned in the properties are allowed. The additionalProperties keyword is only valid for object and non-typed schemas.

schema:
type: objectadditionalProperties: falseproperties:
string_child: {type: string}

would not accept {number_child: 5} in the root, but without additionalProperties it would.

enum

The enum keyword constrains the value of a string types to be one of the predefined array elements.

schema:
type: stringenum: [yes, no, maybe]

indexOn

The indexOn keyword adds an index for querying. This can either be specified as a single string or an array, applied to non-typed or object types only.

schema:
indexOn: name$user:
indexOn: [inbox, outbox]

ranges

The minimum keyword constrains the minimum value of a number type. You set exclusiveMinimum to true, otherwise the minimum is inclusive. Maximum and exclusiveMaximum follow the pattern

schema:
type: numberminimum: 0maximum: 10exclusiveMaximum: trueexamples:
- 0
- 9.9nonexamples:
- 10

$wildchild

An object can have many children bound to a path variable denoted with a keyword starting with $. Note that wildchilds are not put in the properties definition. The following shows how to accept many objects as children of "/users/"

schema:
type: objectproperties:
users:
type: object$userid: {}

The use of a wildchild prevents all ascendents from being writable.

~$wilderchild

schema:
type: objectproperties:
users:
type: object~$userid: {type: string, constraint: next != null}

Wilderchilds are an unsafe but more flexible wildchild. Use them with caution. Wilderchilds do not lock the parent against writing. Wilderchildren's constraints are respected only when next!=null. This implies wilderchild can be set to null whenever user has write access to them, either by writing to the parent or the wilderchild location directly. In the above example, despite the guard against being set to null in the constraint, the constraint is not evaluated when the wilder child is set to null and thus has no effect. Wilderchilds are useful because their enclosing location can still be written, but be aware of the drawbacks.

Constraints

The semantics of enforcing data integrity is different from the original rules. There is no overriding of constraints, nor separate read/write/validate expressions. There is just one field for expressing data integrity named constraint. All ancestors and descendant constraints must evaluate to true for a write to be allowed.

The following example, fixes the id of a user to equal the key, and makes the account writable only at creation time.

schema:
type: objectproperties:
users:
type: object$userid:
properties:
id:
type: stringconstraint: next == $useridconstraint: (!prev.exists())

You can be sure all constraints above and below evaluate to true for a write to be allowed. The only quirk is related to wildchilds. You can't write anything above a wildchild that includes the wildchild as a descendant. They do inherit their parents constraints though, as do their siblings, so the use of wildchilds never makes the database less constrained accidentally.

Model reuse

Denormalization of data requires replicating a model in multiple places in a schema. JSON Schema allows importing of models across the Internet or within a document through URLs. Currently, blaze only supports in-document reuse.

Model definitions are declared in the keyword definitions object, and references are made using the $ref keyword as follows:

schema:
definitions:
stamped_value:
type: objectproperties:
modified: {type: number}required: [value, modified]constraint: next.value == prev.value || next.modified == nowtype: object$data: {$ref: "#/definitions/stamped_value"}

In JSON Schema you are able to extend model objects using the allOf modeling construct (example). However, blaze does not currently support this. Let us know if you need it!

Inline testing

Writing a complex schema can be difficult. For example, a typo in a required field could enforce the existence of a child other than the one intended. For that reason blaze provides keywords for inline testing of nested schema at compile time.

examples is a list of JSONs that you expect to be accepted by the JSON schema node.

nonexamples is a list of JSONs that you expect to be rejected by the JSON schema node.

These inline tests are good for documenting intent and providing fast feedback when authoring a new schema. Note that inline tests cannot understand the constraint field, they can only test the schema.

schema:
type: objectproperties:
object: {type: object}string: {type: string }boolean: {type: boolean}number: {type: number }additionalProperties: falseexamples:
- {object: {name: "hello"}} # you can have extra children in objects by default
- {string: string}
- {boolean: true}
- {number: 4.6}nonexamples:
- {object: true}
- {string: {grandchild: true}}
- {boolean: "true"}
- {number: "4.6"}
- {extra: "4.6"} #additionProperties is false, so no unexpected properties allowed

Access Control

The schema portion of the rules YAML file is for specifying the data layout and constraints. Read/write access is described in a separate access control list under "access". For each entry, the scope of the rule is a subtree at, or below, the path indicated in the location field. Read access is granted to that subtree if the read expression evaluates to true, and write access is granted if the write expression evaluates to true.

functions:
- isLoggedIn(): auth !== null
...
access:
- location: "/"read: isLoggedIn()
- location: "/users/$userid/"write: auth.username === $userid

Only one access control entry needs to evaluate to true for an operation to be permitted.

Example

This is an example that exploits most of the new features. It is a messaging system where users can send messages to each other, by posting to other user's inboxes

functions: #reusable boolean functions
- isLoggedIn(): auth.username !== null
- createOnly(): next.exists() && !prev.exists()
- deleteOnly(): prev.exists() && !next.exists()
- createOrDelete(): createOnly() || deleteOnly()schema:
definitions: #create a reusable message modelmessage: #for use in the in and out boxestype: objectproperties:
from:
type: string#enforce the from field is *always* correct on creation,#and that only the *box owner* can deleteconstraint: (auth.username == next && createOnly()) ||($userid === auth.username && deleteOnly())#you can't delete single field due to parent's requiredto: {type: string, constraint: createOrDelete()}message: {type: string, constraint: createOrDelete()}required: [from, to, message] # all messages require all the fields to be defined#(or none if the message does not exist)additionalProperties: false #prevent spurious data being part of a messageexamples: #examples of inline testing
- {from: "bill", to: "tom", message: "hey Tom!"}nonexamples:
- {to: "tom", message: "hey Tom!"} #not allowed because from is missingtype: objectproperties:
users: # the users subtree is a collection of userstype: object$userid: #wildchild expression of many childrentype: objectproperties: #each user has an optional inbox and outboxinbox:
type: object$message: {$ref: "#/definitions/message"}outbox:
type: object$message: {$ref: "#/definitions/message"}additionalProperties: falseaccess:
#append only write is granted to anyone's inbox,#so users can send messages to strangers
- location: users/$userid/inbox/write: createOnly() && isLoggedIn()#the inbox owner can delete their incoming mail
- location: users/$userid/inbox/write: deleteOnly() && $userid === auth.username#write and delete is given to owners outbox
- location: users/$userid/outbox/write: true#owners can read everything in their inbox and outbox
- location: users/$userid/read: $userid === auth.username

Changelog

  • 25th Sep 2015:

    • schema padding emits a warning when applied to help visibility
    • performance schema padding efficiency greatly improved
    • performance of optimization routines improved
  • 20th Aug 2015:

    • tailored error message when a wild(er)child is used in a properties section
  • 10th Aug 2015:

    • Allowed repeat examples and non-example, as the error message can be unclearly attached to something unrelated (see repeatExample.yaml)
    • upgrade source-map-repository so blaze_compiler continues to fix issue with io.js
    • Allowed any type to have the required keyword
    • Schema is padded to match with ACL if the ACL is bigger
  • 19th May 2015:

    • Improved optimization use a less verbose object detection notation and spurious parent is an object checks
  • 22nd April 2015:

    • Special cased forgetting to .val() before using an inbuilt string method with an error message
  • 21th April 2015:

    • $ref not importing into (non)example schema fragments properly
  • 10th April 2015:

    • fixed erroneous substitution of parameters into member expressions
  • 12th Jan 2015:

    • improved messaging if blah.child('name') syntax is erroneously used
  • 23rd December 2014:

    • bugfix: function with next or prev were not moved around when in constraints properly (similar to Nov 4th bug)
    • bugfix: minimum and maximum
  • 20th November 2014:

    • support for indexOn
  • 4th November 2014:

    • bugfix: functions in ACL resolved properly
  • 3rd November 2014:

    • bugfix: wilderchild matching fix in ACL
  • 1st November 2014:

    • bugfix: wilderchild overwriting parent constraints bug fixed
    • bugfix: access control constraints localised properly
    • bugfix: regex detection firing erroneously on strings starting with '/' fixed
  • 20th October 2014:

    • optimizations added to reduce code bloat
    • sensitization bug regarding regexes fixed
  • 28th August 2014:ß

    • range constraints for number type added
  • 26th August 2014:

    • wilderchilds introduced, ~$ allows nullable wildchilds whose parents can be written to.
    • sanitized expressions bug fix
  • 18th August 2014:

    • predicates renamed to functions
  • 14th July 2014:

    • improved error reporting
    • updated installation
  • 9th July 2014:

    • support for rules in JSON
  • 30th June 2014:

    • removed trailing /* from access location syntax
    • allowed untyped schema if type is not specified

About

blaze_compiler

Resources

Stars

175 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages