├── LICENSE └── README.md /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Tony OHagan 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # FireStore & FireStorage Security Rules - Quick Reference 2 | 3 | I found the official Firebase references for security rules a bit slow to use (too much detail to scan) so I created this condensed Quick Reference as a faster way to look up methods, properties and function names and as reminder of constructs and patterns. Since Firebase developers are already JavaScript coders, you'll likely already be very familier with the meaning of the most of the functions and method names available in FireStore and StoreStorage security rules languages. 4 | 5 | This is still a work in progress so feel free to contribute or fix but please retain the compact format. I think it really needs some "best practice" pattern examples - perhaps on a separate page. 6 | 7 | NOTE: This is NOT an official Firebase reference and may be incorrect or out of date. By using this quick reference you agree that it is solely your responsability to confirm the validity and correctness of this information. 8 | 9 | ## Official References 10 | 11 | **Firestore** 12 | 13 | - [Guide: Firebase Security Rules](https://firebase.google.com/docs/rules) 14 | - [Reference: Firestore Security Rules](https://firebase.google.com/docs/reference/rules/rules) 15 | - [Blog Jun 17, 2020: Firestore Security Rules Improvements](https://firebase.googleblog.com/2020/06/new-firestore-security-rules-features.html) 16 | - [Release Notes](https://firebase.google.com/support/release-notes/security-rules) 17 | - [Library that provides programmatic access to test Firestore security rules.](https://github.com/psvensson/firestore-security-rules) 18 | 19 | **Storage** 20 | 21 | - [Guide: Storage Rules](https://firebase.google.com/docs/storage/security) 22 | - [Reference: Storage Rules](https://firebase.google.com/docs/reference/security/storage) 23 | 24 | **Tech Support** 25 | 26 | - [Firestore Google Group](https://groups.google.com/g/google-cloud-firestore-discuss) 27 | 28 | ## Other Resources 29 | 30 | - [Firestore Security Rules Cookbook](https://fireship.io/snippets/firestore-rules-recipes/) 31 | - [What does it mean that “Firestore security rules are not filters”?](https://medium.com/firebase-developers/what-does-it-mean-that-firestore-security-rules-are-not-filters-68ec14f3d003) 32 | * [The top 10 things to know about Firestore when choosing a database for your app](https://medium.com/firebase-developers/the-top-10-things-to-know-about-firestore-when-choosing-a-database-for-your-app-a3b71b80d979) 33 | 34 | ## Videos 35 | 36 | [![Security Rules! 🔑 | Get to know Cloud Firestore #6](https://img.youtube.com/vi/eW5MdE3ZcAw/0.jpg)](https://www.youtube.com/watch?v=eW5MdE3ZcAw "Security Rules! 🔑 | Get to know Cloud Firestore #6") 37 | 38 | [![Unit testing security rules with the Firebase Emulator Suite](https://img.youtube.com/vi/VDulvfBpzZE/0.jpg)](https://www.youtube.com/watch?v=VDulvfBpzZE "Unit testing security rules with the Firebase Emulator Suite") 39 | 40 | [![Introduction to Firebase security rules - Firecasts](https://img.youtube.com/vi/QEuu9X9L-MU/0.jpg)](https://www.youtube.com/watch?v=QEuu9X9L-MU "Introduction to Firebase security rules - Firecasts") 41 | 42 | 43 | ## Security Rules 44 | 45 | ```js 46 | [rules_version = <>] 47 | service <> { 48 | // Match the resource path. 49 | match <> { 50 | // Allow the request if the following conditions are true. 51 | allow <> : if <> 52 | } 53 | } 54 | ``` 55 | 56 | - ``<>`` ::= ``'2'`` 57 | - ``<>`` ::= ``cloud.firestore`` | ``cloud.firestorage`` 58 | - ``<>`` ::= database or storage location 59 | - `<>` ::= `get` | `list` | `create` | `update` | `delete` | `read` | `write` 60 | - `<>` ::== A condition based on `request` & `resource` objects and bound variables from `<>`. 61 | 62 | ## Permissions Methods 63 | 64 | - `allow get` = Allow a single document read 65 | - `allow list` = Allow collection reads and queries `db.collection("users").orderBy("name")` 66 | - `allow read` = Allow `get` and `list` 67 | - `allow create` - Allow `docRef.set()` or `collectionRef.add()` 68 | - `allow update` - Allow `docRef.update()` or `docRef.set()` 69 | - `allow delete` - Allow `docRef.delete()` 70 | - `allow write` - Allow `create`, `update` or `delete`. 71 | 72 | ## Conditions and Errors 73 | 74 | Error values don't stop computation of conditions: 75 | 76 | ```js 77 | error && true => error 78 | error && false => false 79 | error || true => true 80 | error || false => error 81 | ``` 82 | ## Ternary 83 | 84 | According to [this post](https://stackoverflow.com/questions/60217594/is-there-a-way-to-make-the-ternary-operator-work-for-cloud-firestore-security-ru), the ternary expression has now been added. 85 | 86 | `<>` ? `<>` : `<>` 87 | 88 | ## FireStore Validation 89 | 90 | - `request` = auth, time, path and create/update/delete data for this operation 91 | - `resource` = Document BEFORE this change 92 | - `request.resource` = Document AFTER this change ... if it were successful! 93 | 94 | ## FireStore Resource 95 | 96 | - `resource` = Requested document 97 | - `resource['__name__']` = Document path 98 | - `resource.id` = Document key 99 | - e.g. `resource['__name__'] == /databases/(default)/documents/collection/$(resource.id)` 100 | - `resource.data.{field}` = Current field values (field => value) 101 | - `request.resource.data.{field}` = Pending document state after operation 102 | 103 | ## FireStore Request 104 | 105 | - `request.resource.data` - field values AFTER the requested opertion if successful. 106 | - `request.auth` 107 | - `request.auth.uid` 108 | - `request.auth.token.email` 109 | - `request.auth.token.email_verified` 110 | - `request.auth.token.phone_number` 111 | - `request.auth.token.name` 112 | - `request.auth.token.firebase.identities` 113 | - `request.auth.token.firebase.sign_in_provider` 114 | - `request.{get|list|create|update|delete}` = Operation type 115 | - `request.path` = Resource path 116 | - `request.query.{limit|offset|orderBy}` = Optional Query 117 | - e.g. `allow list: if request.query.limit <= 50` 118 | - `request.time` = Time of request 119 | - e.g. `request.time == request.resource.data.updateAt` 120 | 121 | ## Custom Claims 122 | 123 | - Sets values in `request.auth.token.` 124 | - [Control Access with Custom Claims and Security Rules](https://firebase.google.com/docs/auth/admin/custom-claims) 125 | 126 | Reserved claim keys: 127 | - `amr`, `at_hash`, `aud `, `auth_time`, `azp`, `c_hash `, `cnf`, `exp`, `firebase`, `iat`, `iss`, `jti`, `nbf`, `nonce`, `sub` 128 | 129 | ## Types 130 | 131 | - `String`, `Integer`, `Float`, `Boolean`, `Bytes` 132 | - `Number` = `Integer` or `Float` 133 | - `Duration`, `Timestamp` 134 | - `LatLng` 135 | - `List`, `Map`, `MapDiff`, `Set` 136 | - `Path`, `Request`, `Response` 137 | 138 | ## Namespace Functions 139 | 140 | - `debug(value)` - Returns `value`. Use in conditions. Outputs to `firestore-debug.log` in Emulator only. 141 | 142 | - `maths.abs()/.ceil()/.floor()/.isInfinite()/.isNan()/.pow()/.round()/.sqrt()`, 143 | 144 | - `timestamp.date(year, month, day): Timestamp` 145 | - `timestamp.value(epoch): Timestamp` 146 | - `duration.time(hours, mins, secs, nanos)` 147 | - `duration.value(integer, unit)` unit is w=weeks,d=days,h=hours,m=minutes,s=seconds,ms=millisecs,ns=nanosecs 148 | - `latlng.value(lat, lng): LatLng`, `LatLng.distance(): Float`, `LatLng.latitude():Float`, `LatLng.longiture():Float` 149 | - `hashing.crc32(bytes)/.csc32c(bytes)/.md5(bytes)/.sha256(bytes)` 150 | - `hashing.crc32(string)/.csc32c(string)/.md5(string)/.sha256(string)` 151 | 152 | ## Classes 153 | - String: 154 | e.g. `string(true) == "true"`, `string(1) == "1"`, `string(2.0) == "2.0"`, `string(null) == "null"` 155 | - `str.lower()`, `str.upper()`, 156 | - `str.matches(re)` - re = Google RE Syntax 157 | - `str.replace(re, sub)` 158 | - `str.size()`, `str.split()`, `str.trim()` 159 | - `str.toUtf8(): Bytes` 160 | 161 | - Timestamp 162 | - `timestamp + duration` 163 | - `timestamp - duration` 164 | - `duration + timestamp` 165 | - `timestamp.date()/.time()` 166 | - `timestamp.year()/.month()/.day()` 167 | - `timestamp.hours()/.minutes()/.seconds()/.nanos()` 168 | - `timestamp.toMillis()/.dayofYear()` 169 | - `timestamp.dayofWeek()/.dayofYear()` 170 | 171 | - Duration 172 | - `timestamp - timestamp` 173 | - `duration + duration` 174 | - `duration - duration` 175 | - Use Namespace functions to create a Duration 176 | 177 | - List: 178 | - e.g. `['a', 'b']` 179 | - `list.hasAll(list): Set` 180 | - `list.hasAny(list): Boolean` 181 | - `list.hasOnly(list): Boolean` 182 | - `list.join(separator): String` 183 | - `list.size(): Integer` 184 | - `list.toSet(): Set` 185 | 186 | - Set: 187 | - e.g. `['a', 'b'].toSet()` - How to create a Set 188 | - `set.difference(set): Set` 189 | - `set.hasAll(list): Set` 190 | - `set.hasAny(list): Boolean` 191 | - `set.hasOnly(list): Boolean` 192 | - `set.intersection(set): Set` 193 | - `set.union(set): Set` 194 | - `set.size(set): Integer` 195 | - Map: 196 | - `map.get(key, default): Value` 197 | - `map.keys(): List` 198 | - `map.values(): List` 199 | - `map.size(): Integer` 200 | - `map.diff(map): MapDiff` - used to validate changes at the field level 201 | - `MapDiff.affectedKeys/addedKeys().changedKeys()/.removedKeys()/.unchangedKeys()` 202 | - e.g. `request.resource.data.diff(resource.data).affectedKeys().hasOnly(["a"]);` 203 | = was only field 'a' added/removed/updated ? 204 | 205 | ## FireStore only functions: 206 | 207 | - `exists(path: Path)` - Does document exist? 208 | - e.g. `allow write: if exists(/databases/$(database)/documents/things/other)` 209 | - `existsAfter(path: Path)` - Will document exist after this operation? 210 | - same as `getAfter(path) != null` 211 | - `get(path: Path)` - Get Document content 212 | - `getAfter(path: Path)` - Get what the document would be after this operation. Useful in batch writes. 213 | 214 | ## Storage API - Request 215 | 216 | - `request.time: Timestamp` - Time of request 217 | - `request.resource.name` - Full file name (including path) 218 | - `request.resource.bucket` - Cloud Storage Buckct 219 | - `request.resource.metadata` - Map of custom properties 220 | - `request.resource.size` - File size in bytes 221 | - `request.resource.contentType` - MIME content type (e.g 'image/jpg') 222 | 223 | ## Storage API - Resource 224 | 225 | - `resource.name: String` = Full file name (including path) 226 | - `resource.bucket: String` = Google Cloud Storage bucket 227 | - `resource.size: Integer` = File size in bytes 228 | - `resource.generation` - Object generation. Used for object versioning. 229 | - `resource.metageneration` - Object generation. Used for object versioning. 230 | - `resource.timeCreated: Timestamp` - create at 231 | - `resource.updated: Timestamp` - last update at 232 | - `resource.md5Hash: String` - MD5 Hash 233 | - `resource.crc32c: String` - CRC-32C Hash 234 | 235 | Headers sent when downloading the file: 236 | 237 | - `resource.etag` - [ETag](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/ETag) 238 | - `resource.cachecontrol` - [Cache-Control](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control) 239 | - `resource.contentDisposition` - [Content-Disposition](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Disposition) 240 | - `resource.contentEncoding` - [Content-Encoding](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Encoding) of the file (for example 'gzip') 241 | - `resource.contentLanguage` - [Content-Language](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Language) of the file content (e.g. 'en' or 'es') 242 | - `resource.contentType` - MIME [Content-Type](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Type) (e.g 'image/jpg') 243 | - `resource.metadata: Map` - Developer provided fields 244 | 245 | ## Storage API - Samples 246 | 247 | ```js 248 | // Allow a read if the file was created less than one hour ago 249 | allow read: if request.time < resource.timeCreated + duration.value(1, 'h'); 250 | ``` 251 | 252 | ## Storage Rules 253 | 254 | ```js 255 | // Specify the Storage service with $bucket name 256 | service firebase.storage { 257 | match /b/{bucket}/o { 258 | ... 259 | } 260 | } 261 | ``` 262 | 263 | ```js 264 | allow read; 265 | allow write; 266 | allow read, write; 267 | allow read: if ; 268 | allow write: if ; 269 | 270 | // Allow read at "path/to/object" if condition evaluates to true 271 | match /path { 272 | match /to { 273 | match /object { 274 | allow read: if ; 275 | } 276 | } 277 | } 278 | ``` 279 | 280 | ```js 281 | // Allow read at any path "/path/*/newPath/*", if condition evaluates to true 282 | match /path/{str1} { // Binds str1: String 283 | match /newPath/{str2} { // Binds str2: String 284 | // Matches "path/to/newPath/newObject" or "path/from/newPath/oldObject" 285 | allow read: if ; 286 | } 287 | } 288 | ``` 289 | 290 | ```js 291 | // Allow read at any path "/**", if condition evaluates to true 292 | match /{path1=**} { // Binds path1: Path 293 | // Matches anything at or below this, from "path", "path/to", "path/to/object", ... 294 | allow read: if ; 295 | } 296 | ``` 297 | --------------------------------------------------------------------------------