DOUG STEVENSON: Hey, everyone.
Welcome back to this series on Firebase security
rules for Cloud Firestore.
Last time, I talked about matching documents
in Cloud Firestore using wildcards
in order to control access to them with conditions
you specify.
There's still more to know about matching documents,
and in particular, how to use a special kind of wildcard, which
I will call a glob wildcard.
At the end of the last video, I left you with this rule right
here, the default rules when you create your database
in test mode.
Notice the wild card in curly braces with a document=**.
This glob wildcard is actually matching
all of the documents in the entire database,
and the allow expression is granting full read and right
access to everyone.
This is handy for new projects where
you're experimenting with Firestore,
but you definitely do not want to publish
your app with this rule in place for obvious reasons.
In the last video, I recommended that you remove the default
rule entirely before you began writing any other rule.
That's because of this one very important property
of security rules.
If a document is granted access by any rule,
that access can't be revoked by any other rule.
For example, say you want to add in the rule
from last time that controls access to documents
in a collection called users.
Adding this new rule makes absolutely no change to the way
the entire rule set works.
All documents are still given full read and write access
by the rule with the glob that matches everything.
That's why it's important to get rid of it entirely.
In fact, I suggest incorporating security rules
into your app as soon as possible,
and keep them up to date with the client code
that's affected by those rules.
Not having good rules can be a costly mistake
and can be exploited by those who would like to see or delete
all your data.
OK, back to matching documents.
Unlike normal wildcards that only match one path segment
at a time, glob wildcards can match multiple path segments.
That's why this rule matches all documents in the database,
both top level collections, and those
organized in subcollections.
Here's another rule that uses a glob wildcard
at the end of a path match using a collection called users.
This rule not only matches all the documents in the users
collection, but also all documents
in all of its subcollections, and it gives everyone
read access to that entire set.
This can save you some typing if you
want to apply the same rules to all the documents,
avoiding the need to call out each subcollection
in its own individual rule.
When this rule evaluates for a document,
there will be a variable called document in the rule
which contains a path type object that
contains all the path segments of that document matched
by the wildcard.
I'll talk more about path objects in another video,
but you should know that this data is
available with glob wildcards.
So if I execute this query on the users collection,
the value of the document variable
contains a path with one segment,
which is the string sparky.
And if I execute this other query against a subcollection
organized under users called history,
the value of the document path variable
becomes the path you see here.
Note that it contains all of the path segments of the document
after users.
The values of these glob matched strings are not typically
too useful in security rules, unless you're
going to do something advanced with them,
such as checking the past segments
with a regular expression.
I'll talk about that in a future video.
The one thing you do need to know
is that glob wildcards match at least one
or more path segments.
So if you change the rule to look
like this, which now includes a uid wildcard
before the glob wildcard, it would still
match requests for documents in subcollections.
Notice that the glob wildcard no longer
includes sparky's uid, which is in its own uid variable now.
But for queries that only look at a document in the users
collection, like this one, the same rule
would not match that document, because the glob wildcard
must match at least one path segment, which is not present
here.
So if you want to write a rule that
applies to all documents in a collection
and all of its subcollections, and capture the uid of the user
document, you would have to split it up
into two matches, one with the glob and one without.
But there is something super important
that you need to know about the way glob wildcards work,
and that's the fact that they've changed behavior in version
2 of Firebase security rules.
I'll break it down.
By default, if you don't specify a rules version,
they will use version 1 behavior,
which I just mentioned.
It requires that globs must match one or more path
segments, and they are only effective at the end of a match
path.
But if you change the rules to version 2,
globs will match zero or more path segments,
and you can now use them at the beginning of a match.
Why this change in behavior?
Well, that's to support the new collection group
queries in Cloud Firestore.
And I'll show you that in a bit, but first,
let's see how you specify version 2 in your rule set.
All you have to do is add a single line
at the very top of your rules, like this.
And now, you get the new glob behavior.
I recommend always starting out your rules
with this new behavior so you won't have any surprises if you
end up using collection group queries at some point.
OK, let's go back to the last rule I showed before
on the users collection.
Remember that this query didn't match with rules
version 1, because the glob wildcard required
at least one path segment, which wasn't present in the query.
But if I update the rules version to 2,
both the uid wildcard and the document glob wildcard
will get values.
This document glob now matches zero path segments.
This lets you write a single rule that
applies to all documents under users
and all of its subcollections.
Since we now always have a value for uid,
we can use it in the new rule and it applies
to both of the queries shown.
Here's the rule saying that everyone can read everything
under the users collection and all its subcollections,
but only the user making the request
can write any of their own documents.
Now let's take a look at how security rules work
with collection group queries.
A normal query in Cloud Firestore
considers only documents in a single collection that's
uniquely identified in the query,
but a collection group query is a special kind of query
that considers all of the collections and subcollections
of the exact same name, no matter
how deeply nested they are.
In my prior examples, it was implied
that sparky had a subcollection called history,
and maybe its documents contain action
he's taken in an app, such as click and swipe.
Well, each user is obviously going
to have their own history as well,
each with their own history subcollection.
Here's Switch, my DnD alter ego, obviously, a rogue.
What a collection group query allows me to do
is query across all of the subcollections named history,
no matter where they're organized.
So this query here is going to try
to fetch all four documents across everyone's history
subcollections.
Now, let's just say, ignoring all the prior rules
I've shown so far, I want to write a new rule that
says only click type actions can be queried in this way,
so this query should only return one document
from the data shown here.
The first part of the solution here
is to write a rule that applies to each of the subcollections
named history.
Since this collection group query
is always going to consider all documents with this name,
it's going to require the rules version 2
glob wildcard behavior.
The document match has to look like this.
Notice that we have the glob wildcard
called prefix at the front of the path this time,
and a normal wildcard ID at the end
with the history path segment in the middle.
This lets the rule apply to any document in any collection
named history.
For collection group queries, the glob wildcard
must absolutely be at the beginning of the path
here, because this rule has to match all possible collections
called history.
Now, to check that the document must contain an activity
field with the value click, I'll use the resource variable
to indicate that I only want documents
satisfying that requirement.
If you're not fully familiar with everything resource does,
I'll cover that in a future video.
OK, the rule looks good to me, but there's still
one problem here.
As you know, the collection group query
is effectively asking for all documents in all subcollections
named history, but the rule only allows
certain documents to be read.
The rule will make this query fail every single time,
even if all possible documents happen to match.
This might be surprising to you at first
until you realize that security rules are not filters.
You can't write a rule that limits
the results of a query from what's being requested.
The rule knows internally that the query
is asking for more than what's allowed,
so it simply rejects the query without any more consideration.
Not a single document is read or returned,
and the query fails on the client.
The solution here is to make sure the query is
in sync with the rules.
Since the rules say that only queries where an activity
string is click can be satisfied,
we can add a filter to the query to match the rules,
and now everything is good.
Of course, this is the JavaScript syntax
for the filter, and other languages will look different.
I hope by now you have a pretty good understanding
about how both normal and glob wildcards work
when matching documents in your Cloud Firestore rules.
But there's still a lot to learn about security rules,
especially the expression language you
can write for your conditions.
Next time, I'm going to talk about the different sort
of permissions you have available in your rules.
Turns out there are more options than just the read
and write permissions that you've
seen so far in this series.
So be sure to join me right here on the Firebase
Channel on YouTube to learn more when the next video is ready.
I'll see you then.
[MUSIC PLAYING]