Firebase Rules Protobuf Validation
Quick Note
This is an experimental plugin for Security Rules, which means you should always validate these by hand before you decide to deploy these to a production environment.
Status
This repository is maintained by Googlers but is not a supported Firebase product. Issues here are answered by maintainers and other community members on GitHub on a best-effort basis.
Introduction
This is an experimental protoc
plugin
that generates Firebase Rules for Cloud
Firestore based
on Google's Protocol Buffer
format.
This allows you to easily validate your data in a platform independent manner.
Here is a quick example:
syntax = "proto2";
package tutorial;
message Person {
required string name = 1;
optional string email = 2;
enum PhoneType {
MOBILE = 0;
HOME = 1;
WORK = 2;
}
message PhoneNumber {
optional string number = 1;
optional PhoneType type = 2;
}
optional PhoneNumber phone = 3;
}
This plugin generates the following Firebase Rules function that can be used to validate your incoming data.
// @@START_GENERATED_FUNCTIONS@@
function isPersonMessage(resource) {
return resource.keys().hasAll(['name']) &&
(resource.keys().hasOnly(['name','phone','email'])) &&
((resource.name is string)) &&
((!resource.keys().hasAny(['email'])) || (resource.email is string)) &&
((!resource.keys().hasAny(['phone'])) || (isPerson_PhoneNumberMessage(resource.phone)));
}
function isPerson_PhoneNumberMessage(resource) {
return resource.keys().hasAll([]) &&
(resource.keys().hasOnly(['type','number'])) &&
((!resource.keys().hasAny(['number'])) || (resource.number is string)) &&
((!resource.keys().hasAny(['type'])) || (isPerson_PhoneTypeEnum(resource.type)));
}
function isPerson_PhoneTypeEnum(resource) {
return resource == 0 ||
resource == 1 ||
resource == 2;
}
// @@END_GENERATED_FUNCTIONS@@
// Start your rules (these don't get generated!)
service cloud.firestore {
match /databases/{database}/documents {
match /users/{userId} {
allow read: if request.auth.uid == userId;
allow write: if isPersonMessage(request.resource.data) &&
request.auth.uid == userId;
}
}
}
Usage
To use this protoc
plugin once you have your protocol buffers defined, follow
these steps:
- Make sure to install the latest version of
protoc
- Download the latest release from GitHub
- Either put the plugin binary on your
$PATH
or use the--plugin=protoc-gen-firebase_rules=./path/to/protoc-gen-firebase_rules
option - Invoke the
protoc
tool using the--firebase_rules_out=./directory
flag to output yourfirestore.rules
file with generated functions - If you're importing
"firebase_rules_options.proto"
like the below example the--proto_path=./directory
flag will need to be added to include the directory of thefirebase_rules_options.proto
file along with the protobuf files from thesrc
directory of the Google Protobuf repo. An more indepth discussion of this can be found in this issue.
If you run into trouble feel free to check out our
example_usage.sh
script or file an issue
Advanced Usage
syntax = "proto3";
package tutorial;
import "firebase_rules_options.proto";
option (google.firebase.rules.firebase_rules).full_package_names = true;
message Person {
string name = 1;
string email = 2 [(google.firebase.rules.firebase_rules_field).validate =
"resource.email.matches('.*@domain\\.com')"];
enum PhoneType {
option (google.firebase.rules.firebase_rules_enum).string_values = true;
MOBILE = 0;
HOME = 1;
WORK = 2;
}
message PhoneNumber {
string number = 1;
PhoneType type = 2;
option (google.firebase.rules.firebase_rules_message).extra_properties =
true;
}
PhoneNumber phone = 3;
// Currently, we can only check this is a list :(
repeated string starred_websites = 4;
// This message must have either a phone or an email.
option (google.firebase.rules.firebase_rules_message).validate =
"resource.keys().hasAny(['email', 'phone'])";
}
This would generate the following functions.
// @@START_GENERATED_FUNCTIONS@@
function istutorial_PersonMessage(resource) {
return resource.keys().hasAll([]) &&
(resource.keys().hasOnly(['starredWebsites','phone','email','name'])) &&
((!resource.keys().hasAny(['name'])) || (resource.name is string)) &&
((!resource.keys().hasAny(['email'])) || (resource.email is string && (resource.email.matches('.*@domain\.com')))) &&
((!resource.keys().hasAny(['phone'])) || (istutorial_Person_PhoneNumberMessage(resource.phone))) &&
((!resource.keys().hasAny(['starredWebsites'])) || (resource.starredWebsites is list)) &&
(resource.keys().hasAny(['email', 'phone']));
}
function istutorial_Person_PhoneNumberMessage(resource) {
return resource.keys().hasAll([]) &&
((!resource.keys().hasAny(['number'])) || (resource.number is string)) &&
((!resource.keys().hasAny(['type'])) || (istutorial_Person_PhoneTypeEnum(resource.type)));
}
function istutorial_Person_PhoneTypeEnum(resource) {
return resource == 'MOBILE' ||
resource == 'HOME' ||
resource == 'WORK';
}
// @@END_GENERATED_FUNCTIONS@@
// Start your rules...
Standalone usage
-
Install Bazel.
-
Build with
bazel build //...
-
A sample invocation of the plugin,
protoc-gen-firebase_rules
, is available inexample_usage.sh
. This script can be run from the command line.
Using with bazel
It's easy to use protobuf_rules_gen if your project already uses Bazel.
- Add protobuf_rules_gen to your WORKSPACE:
proto_gen_firebase_rules_commit = "TODO"
http_archive(
name = "proto_gen_firebase_rules",
sha256 = "TODO",
strip_prefix = "protobuf-rules-gen-" + proto_gen_firebase_rules_commit,
url = "http://github.com/FirebaseExtended/protobuf-rules-gen/archive/" + proto_gen_firebase_rules_commit + ".tar.gz",
)
load("@proto_gen_firebase_rules//bazel:repositories.bzl", "protobuf_rules_gen_repositories")
protobuf_rules_gen_repositories()
- Update your BUILD file:
load("@proto_gen_firebase_rules//bazel:defs.bzl", "firestore_rules_proto_library", "firestore_rules_binary")
There are three rules available:
- firestore_rules_proto_library generates a .rules file from the protobuf schema
- firestore_rules_binary combines multiple .rules files (e.g. the auto generated rules with your ACLs that use them)
- firestore_rules_library wraps up one or more .rules files so that a firestore_rules_binary can depend on it.
See example/BUILD for an example of how to use these rules.
Releasing
-
Build the
proto-gen-firebase_rules
binary viabazel build //...
-
Ensure all the tests pass via
bazel test //...
-
Build a binary for each platform (windows, linux, and darwin).
-
Tag a GitHub release and attach each prebuilt binary to the release.
Authors
protobuf-rules-gen
was initiated with
Disclaimer
This is not an official Google product (experimental or otherwise), it is just code that happens to be owned by Google.