[BEEPING]
DAVID EAST: I don't even think I clipped on the last one.
Maybe.
I don't know.
Let's make this one count.
Hey, everyone.
Welcome to the eighth lesson in the Firebase Database
for SQL Developers series.
And today, we're going to learn all about protecting your data
structure with security rules.
So let's dive right down into the laptop and begin.
To insert data into a SQL database,
you first need to create a table.
And this table must have a schema.
And a schema is made up of a column and then a series
of constraints.
So this column of ID has a not null constraint,
and we're also constraining the data type to be an integer.
So we can also specify other columns,
and they can also have constraints.
And we must provide a primary key constraint,
which is the unique identifier for every record in this table.
The Firebase database doesn't have tables,
and it doesn't have schemas.
Instead of tables with rows and columns,
it has JSON with keys and values.
So at the root, we can create a key of users,
and then below, we can create child keys
that have values for our users.
So we can insert whatever we want.
So that means for this user, we can
create properties of name, bio, and profile image,
and then we can insert another user
that has name, bio, profile image, and favorite color.
And you also notice that profile image, in this case,
is actually a number.
And there's nothing that stops us from doing this.
And also, there's nothing that stops someone
from just deleting your data unless you
specify security rules.
So there's three things to learn when
you're starting with security rules,
and the first is paths and rules.
Security rules are written in a JSON file,
and this is totally separate from the database.
And every security rules file starts with a rules key.
And then below there, we can map our rules
to our data structure in the database.
So since we have a path of users in the database,
we'll create a path of users in our security rules.
And then once we have our paths, we can specify our rules.
And there are three rules you can use.
There is read, which determines who
can read the data at this path.
There is write, which determines who can write data
to this path, and lastly validate,
which determines what the data structure is at this path.
And all of these rules must evaluate out to an expression.
So it's either true or false.
If it's true, the write to the database or the read
will be accepted.
But if it's false, it will be rejected.
So right now, this is not really a useful rule.
We're essentially saying that anyone can read or write data
at users, and we can use any data structure that we like.
And also, what's not very useful is that we're not actually
writing rules for any child users.
We're writing a rule for specifically the user's key.
So we want to be able to dynamically apply
these rules to any child users.
And to do that, we can use wildcards.
So right now, we're only applying this rule
to the users key.
But if we specify a wildcard, which
starts with a dollar sign, we're essentially
creating a variable.
And we're saying that this rule applies to any children
underneath the users key.
And we can specify what the key is with the dollar wildcard.
And in this case, we're calling it dollar UID.
But since it's a variable, you can really
call it whatever you want.
And dollar UID will hold the child key value
that we're trying to access.
So if you're trying to access /user/1,
dollar UID will represent 1.
But if you try to access /user/2,
dollar UID will hold the value of 2.
And we can see how this actually looks like in code.
So if we create a root reference to the database and then
nest down to users, and then we can create a variable of UID.
And then from this variable, we can create a reference
to a specific user.
And this is exactly what the dollar UID wildcard
is doing in our security rules.
So now that we have the ability to write
this rule for any child, we actually
want to do something useful rather than just say true
for all of our rules.
And to write a useful expression,
we need to use server variables.
There are six server variables that the Firebase database
provides you with.
There is auth, which is the currently authenticated user.
There is data, which is the data that exists at that path.
There is new data, which is the newly-posted data at that path.
And this is only available in .write and .validate,
because those are the only times we'll actually have new data
being posted.
There is now, which is a server time
stamp of the current time on the server, and root, which
will bring you to the root of your database,
and also the dollar wild cards, which
we created with dollar UID.
So now that we have access to these server variables,
we can write useful rules.
For .read and .write, we can create rules that say only
users can access their own data.
By saying auth.uid is equal to the dollar UID wildcard,
we're essentially saying that if the currently-authenticated
user's UID is equal to the UID that we're trying to access,
that person can read or write data.
So if it's /users/1 and the currently authenticated user
has the ID of 1, we can allow that read or write.
But if the currently authenticated user is 2
and we're trying to access /users/1, that will fail,
because 1 is not equal to 2.
And with validate, we're looking at the newly-posted data,
and we're just checking to see that it exists,
that no one is posting null.
But we actually can use other methods to check the data.
By calling .child on new data, we can pass in a string and see
if a child property exists.
And we can even check its data type
by calling the is string method.
And there's also other methods for other data types.
And since this is just an expression,
we can actually tack on other conditions.
So in this case, we can check for the bio
and see if it's a string, and then we
can even check the bio string length
and make sure that it's less than 500,
and then also check the profile image
and ensure that it's a string.
Previously when we tried to add the user of Alice,
we were able to, even though profile image was a number.
But now with these security rules applied, this would fail.
So if we went and changed the profile image from a number
to a string, it would pass.
Another thing that you can do with security rules
is you can create foreign key like constructs.
So in our database, we have our list of users,
and we can have a property called location.
And this is where the user lives.
So David lives in SF.
And we can also provide a lookup,
which contains all of the valid locations
that we support in the database.
And if we want to make sure that we are only
adding valid locations, we can write a validate rule
that takes us to the roots, then goes to the child of location.
So now we have access to all of the locations that
exist in the database.
And then from there, we can go into the newly-posted data set
and call that child of location to get back
the new location that's attempting
to be added to the database.
We can get its value, and then after that, we can call exists.
And what this will do is it will make sure
that the newly-added location exists in the database.
And if it doesn't, the write will be rejected.
So securing and protecting your data structure
is really important.
So you probably want to be able to test out that what you're
doing is actually working.
And to do that, you can use the simulator.
So right here, we have a Firebase database opened up
in the Firebase console.
And you can see that we're using similar data
structures that we've created in this series.
We have our users data structure.
And you can see that we're storing a user named Margaret
that lives in San Francisco.
And then we also have a locations lookup
for all the valid locations we can accept.
So now if we tap over to the Rules tab,
we can write some security rules.
And you can see to the left that we have this Simulator panel.
And if this isn't already opened up,
you can just click the button, and it slides right in.
And then now we want to write a rule for our users.
So let's delete read true write true,
because that's basically saying do
whatever you want with my database,
and that is not very safe.
So we'll start out by writing a rule for our users path.
And we have to make sure to use the dollar UID wildcard,
because that way, these are rules
for any child underneath users.
And we'll set or read rules and our write rules.
I'm going to set read to true, because I
want the users profile data to be public information.
But I only want users to be able to write their own information.
So to do that, I'll say auth.uid so the currently authenticated
user's UID is equal to the wildcard, so dollar UID.
And now I can actually go and simulate this.
So I'll go and simulate a read.
So I'll make sure that /users-- and then this is slash the key
for Margaret.
And we can even simulate authentication.
For now, I'm going to turn this off, and then I'm going to run.
And you can see that a read was allowed,
because reads are public.
But now if I try to do a write and run,
the write was denied, because you
have to be authenticated to read your own data.
So now if I turn on authentication
and I use the same key that I'm trying to access, and I run,
you can see that the rule was allowed,
because the user is allowed to modify their own data.
So now let's write a rule to establish
the foreign key like construct.
I'll write a rule for .validate, and we'll start to go
to the root of our database, and we will read the existing
locations by calling .child of locations.
So now I want to make sure that the newly-added location
for the user exists in the database.
So to do that, I'll call .child.
And then from here, I'll say new data.child of location,
because this is the newly-posted location data.
And then I'll get its value.
And then lastly, at the end, I'll call .exists.
So this record currently exists in our lookup.
And so right here, I'm saying location
of LA, which does not exist.
And then when I run, you can see that the simulate write
was denied even though we were authenticated, because we are
making sure that only the existing locations exist
in our database.
But if I move it to an existing location of SF and hit Run,
they both are allowed.
Security rules are how you secure your data
against unauthorized users and protect your data structure.
And since security rules can read data from the database,
you can do really powerful things,
like establish foreign key like constructs.
So this is actually the last episode
in the Firebase Database for SQL Developers series.
So if you want to see more, make sure to like and subscribe,
and also leave us a comment and tell us what you want to learn.
So that's all, and I will see you all later.
Thanks for watching our video.
Yeah, we really appreciated it.
You should watch this video, too.
And this one.
Yeah.
And make sure that you subscribe.
It's really cool.