So this product management part
is pretty much finished.
There are just some minor adjustments
or notes that I want to make.
Let's say we add a new product again,
the keyboard again.
If we do that,
we could theoretically
actually disable validation here in the browser,
with the dev tools, for example,
and actually submit an invalid product.
I'm not going to do it here,
but this would work
because for the product management,
if you have a look at our backend code
in the admin controller,
we added no validation.
I'm not checking the user input.
And I did mention earlier
that you should always validate
the user input on the server side.
Now feel free to add this server side validation
and maybe flashing error messages
onto sessions as extra practice.
I'm not doing it here because
this admin area is in the end for myself.
I'm building my own website here
and I have no intention of hacking my own website.
And since this is in the administration area,
no other visitors will use that.
And therefore, I can safely not have
any server side validation
since I won't hack myself.
Now, you still might want to add it as extra practice,
but that's why I didn't add it here.
And instead I just well,
rely on the front end validation.
Now, another note that I want to make
is related to 404 errors.
If I do click view and edit, I load this product.
If I enter a wrong ID here,
I actually get this error though.
The default error handling page.
That's better than the server crashing,
but still it's not what I want to do here.
And you might recall that in my admin controller,
no, in my product model,
I actually did add this extra 404 code
to some error objects
for the case that loading an item failed.
Now, I added this arbitrary code property
to the error object
because it's this error object,
which I'm forwarding inside of my admin controller
with help of the next function.
And it's therefore this error object
that reaches my default error handler in app.js.
This error handler middleware,
which we defined in the middlewares folder.
There, we got this error-handler.js file.
In there, I log the error and then I always send back
this 500 error page.
Now, since we know that for some errors,
we'll have that code property
and then that code property
might hold a value of 404,
we can actually add a if-check here
and check if error.code is equal to 404.
And if that's the case,
we might want to return a response,
where the status code is set to 404,
and where we render shared 404.
So a different error page.
And I return here so that this code isn't executed
if we made it into this if-block.
For most errors, this will not be the case.
And that's okay.
We have the default error page for those cases.
But for some errors, we do have a code of 404,
and we can then handle it like this.
So with that, back in our templates,
in the shared templates,
we can duplicate the 500.ejs file
and add a 404.ejs file.
And then change the text here
to "Could not find the resource,"
and you can of course change the content
however you want.
Here I'll say, "Unfortunately,
"we could not find the requested resource,"
like this.
Now, with that done,
if I reload this page with the incorrect ID,
I get the 404 page instead.
And that's simply a little improvement.
Of course, we can still load products
with a correct ID though.
Now, as a last step,
I want to work on a huge security
or access issue that I have on this website.
I'm currently logged in as an admin.
Now let's see if I log out here
and I then try to access admin/products.
Oh, that works.
Clicking delete also works.
And that should not happen.
Now, why does this all work?
The reason for that is simple.
I'm not protecting these admin routes at all.
These are regular routes defined here
in the admin.routes.js file,
and I load them in app.js
without any extra protection.
If you know the path, you can send the request.
Of course, we do have login built into our website,
but we're not using the information
whenever a user is logged in or not
if we ignore the navigation bar.
Now that's the first step.
It ensures that you don't see options,
which you shouldn't be able to access,
but you can still enter paths and send requests manually
if you play around with the site a bit.
And that's of course a huge security issue
for our website
and for the data of our website.
So, to protect against that,
I want to make sure that requests
to /admin routes, to all the routes in there,
can only be sent if you are an admin.
This means that I will add a new middleware,
which I want to run before requests
reach these admin routes.
Now for this,
I'll go to the middlewares folder
and add a new file in there,
which I'll name protect-routes.js.
And in this file, we can add a function
which can also be named protectRoutes,
which will be a middleware function,
and which therefore gets a request, a response,
and this next function.
And which I want to make available outside of this file,
so I will export it like this.
And then in this function,
I want to do two things.
First of all,
I want to check if you're maybe not authenticated
and for this, I can check
if res.locals has a specific value,
which I do set in check-auth.
Check-auth is also one of our middleware functions.
And there I just look into the incoming request
and see if this user has a session
that identifies him or her as logged in.
And if that's the case, I set isAuth to true.
And I also check, or I set if the user
is an admin.
And that's based on data
that's also stored in the session.
Now we can use isAuth and isAdmin
in protect routes to now deny access,
because checkAuthStatus does not deny any access.
Here we just extract data
from the incoming request.
But now in protect Routes,
I can use res.locals isAuth and isAdmin
to deny access to certain paths.
So here I check if not res.locals.isAuth,
which means you're not authenticated.
And in that case,
if that middleware becomes active,
which it will only for certain routes,
I want to deny access.
So then I'll simply return
an error or let's say a redirect
to a 401 route,
which we have yet to add.
401 because that will be the status code,
which is typically used for telling a user
that he or she is not authenticated.
Now, of course you might be authenticated
and still not be an admin.
For some routes, that's enough.
A lot of routes will be accessible
to regular authenticated users.
But for the admin routes,
you also need to be an admin.
Therefore I'll check if the request path,
so the path of the request you sent us,
actually starts with /admin.
And startsWith is a built-in method,
which you can call on strings in JavaScript,
and request path yields a string.
And this will return true if the path,
in this case, to which a request was sent,
starts with /admin,
which tells me that you're trying
to access one of our admin routes.
If that's the case,
I also want to check if you maybe aren't an admin,
so isAdmin is not true.
Hence the exclamation mark.
And in that case,
I know that you're trying to an admin route,
even though you aren't an admin.
Now, that means that here,
I also don't want to continue in this middleware.
Instead I'll redirect to 403.
And that's another route we have yet to add.
403 because that will be the status code
I'll then use later
because that's the status code for telling a user
or the browser that you're not authorized.
So you might be authenticated,
but you still don't have access
to this specific resource
you're trying to access.
And if you make it past these if-checks,
then you either didn't visit an admin route,
but you are authenticated
or you did try to access an admin route
and you are an admin.
And either way, I want to allow you to proceed.
So then I call next so that the next middleware
or route handling function in line can take over.
And now protectRoutes should be applied
to some routes in app.js.
I want to run it in front of my admin routes.
The baseRoutes, authRoutes, and productsRoutes
are all accessible to everyone,
also not logged in users.
So we don't need to protect those.
But before we try to handle a request
with the adminRoutes,
I want to run that request
through that protectRoutes middleware we just worked on,
which might redirect the response
if the user tries to access a route
or tries to send a request to a route
where authentication
or administration privileges are needed.
So for this, let's import this middleware here
in app.js.
The protectRoutesMiddleware
can be imported by requiring
middlewares protect-routes.
And now we use the protectRoutesMiddleware down here
with app use just like this.
So that will now protect all the routes
that come thereafter, including this admin route,
not the errorHandlerMiddleware.
That will still work.
But now we won't allow unauthenticated
or unauthorized access to certain routes,
in this case to the admin routes.
Now since I'm redirecting to 401 and 403 in here,
I want to make sure that we have those routes.
And for that I'll start with the views for those routes.
I'll copy the 404.ejs file like this
and rename it to 401.ejs,
and then here on 401.ejs,
I'll say "Not authenticated."
And in here say "You are not authenticated" maybe.
And then I'll copy it again to have a 403.ejs file.
And in that file, I'll say "Not authorized,"
which is a different thing as you learned,
and I'll say "You are not authorized."
And of course you can fine-tune these pages
to your likings.
Now these are the views, not the routes.
We also need the routes, and these will be some base routes,
which I want to register here.
So here in base.routes.js,
I now want to define base routes
for these 401 and so on views.
And we actually don't have any controller
for these routes.
We could add one,
but since this will all be very simple routes,
I will just write them all with anonymous functions here,
but adding a separate controller, of course,
also would be fine.
Here I want to handle /401,
and in that case execute a function,
which gets a request and a response,
where I send back res.render.
And then here I render shared 401.
So this 401.ejs file we just added.
That's the template I render here.
And then I register another get route for 403,
which is that other route we tried to redirect to
in our protectRoutesMiddleware in some cases.
And here we render the 403 view.
And actually I'll also set status codes here
to 401 and to 403,
to have the appropriate status codes for these responses,
to make it clear why this kind of response was sent.
That's a technical detail,
but still a good idea to set.
With that, if I now try to reload,
I get "Not authenticated"
because I'm not logged in.
And if I do log in with my administrator,
so with the correct administration credentials,
then of course I can visit this.
If I log out and I log in with another user,
who has an account,
but who is not an administrator,
then you will see that if I now try
to access /admin/products,
I get the "Not authorized" page.
So now this protection works
and it ensures that we can't visit pages
or send requests,
which we shouldn't be allowed to visit or send.