AdonisJS User Role Management

In this lesson, we'll learn how to create a user management screen that'll allow administrators to change any of our registered user's roles

Published
Nov 13, 22
Duration
17m 5s

Developer & dog lover. I teach AdonisJS, a full-featured Node.js framework, at Adocasts where I publish weekly lessons. Professionally, I work with JavaScript, .NET, and SQL Server.

Adocasts

Burlington, KY

Get the Code

Download or explore the source code for this lesson on GitHub

Repository

In this lesson, we'll learn how to create a user management screen that'll allow administrators to change any of our registered user's roles. For this, we'll create a role middleware, allowing us to restrict actions to admins. We'll also add the ability to delete users from our application as well.

👍 If You Enjoy This Video, Consider Hitting The Like Button!
🥁 Subscribe To Stay Notified For New Lessons
📺 View on YouTube
👨‍💻 Find The Code For This Lesson Here

📚 Chapters:
00:00 - Creating Files
00:28 - User Management Route Handler
01:08 - Creating A Layout
02:04 - User Management View
04:08 - Updating A User's Role
08:15 - Creating A Role Middleware
11:16 - Handling Self-Demotion
13:04 - Deleting Users
16:30 - Wrapping Up


📜 Transcript:

0:00

in this lesson we're going to expand

0:01

upon our last list in just a little bit

0:02

by adding a user management screen that

0:04

will allow us to alter the role of any

0:06

user and delete users as well so let's

0:08

go ahead and dive in and get the view

0:10

set up for this first so let's jump into

0:12

our terminal and do node Ace make

0:14

controller and we'll call this users and

0:16

then let's also do node Ace make View

0:18

and let's put this inside of a user's

0:20

directory but call the actual file

0:21

manage next let's dive into that

0:23

controller so let's jump into our code

0:24

base go to users controller uncomment

0:26

the HTTP context and let's do public

0:28

async manage we should just need view

0:31

out of this grab our HTTP context

0:33

contract for that and first let's go

0:35

ahead and grab all of our users so let's

0:36

do const users equals await user query

0:40

and let's order them by their email now

0:43

we could also do pagination here but we

0:45

will have a specific lesson on

0:46

pagination coming up so we'll skip over

0:48

that for right now and let's also grab

0:50

our roles as well so let's do const

0:52

rolls equals await role query and let's

0:56

order those by their name lastly let's

0:58

return that to our view so View render

1:01

users slash manage and provided the

1:04

users and the roles give that a save now

1:06

we can jump into our role page so let's

1:09

go users manage and let's actually set

1:11

up a layout for these two views so let's

1:12

copy what we have within our welcome

1:13

page under views let's do a new file

1:16

call a directory layouts and then we'll

1:18

just do app.edge within that layouts

1:20

directory let's paste all of our welcome

1:22

page in and then get rid of everything

1:23

inside of main then inside of main let's

1:26

do section content next let's jump back

1:29

into our welcome page and let's actually

1:30

rig that up so grab everything inside of

1:32

main cut that out and then get rid of

1:34

everything inside of the file and we can

1:36

do at layout is layout so slash app at

1:40

section is content and then let's end

1:43

our section pasting our contents of main

1:46

back inside of here give that a save we

1:48

can boot our server up real quick just

1:49

to make sure that everything went okay

1:50

with that jump back into the browser and

1:52

refresh okay looks like that one okay

1:54

now let's do the same for manage so at

1:57

layout is layouts at section is content

2:01

and then at end our section inside of

2:05

our section I'll put an extra div here

2:07

and then we'll do an H1 specifying that

2:08

this is our user management screen then

2:11

we'll want a table with a t head and a

2:14

table row and then our th for the First

2:17

Column will be our email our second th

2:20

will be the user's role third will be

2:23

when they joined and then lastly we will

2:25

have an actions column then we could do

2:27

a t body and let's Loop over each of our

2:29

users so each user and users at and each

2:33

will do a table Row for each one of our

2:36

users TD the first one will be our

2:38

user.email second for right now let's

2:40

just print out their role ID so roll ID

2:43

third user dot created at dot two Locale

2:48

string and then lastly we will put a to

2:50

do on our actions and we'll break that

2:52

up to delete here in a bit let's

2:54

actually jump back into our welcome page

2:55

and add a link for this as well so we'll

2:58

ultimately want this to be restricted to

2:59

administrators So within in our is admin

3:01

check we can put the link inside of

3:03

there so let's do a href route and we

3:05

need to actually create this route but

3:07

it will be users.manage and we can call

3:09

this manage users all right let's go and

3:11

create that route as well so route dot

3:13

get users slash manage users controller

3:17

dot manage as users.manage actually

3:20

let's put this inside of a group so

3:24

route.group.prefix users as users and

3:28

then get rid of that user's prefix there

3:29

and here okay good so now we should be

3:32

able to jump back into our browser give

3:34

it a refresh let's log in with an

3:36

administrator so that should be Test

3:37

Plus admin all right cool there we go

3:39

and here we have manage users and log

3:42

out let's actually grab that inside of a

3:43

separate div so that everything flows so

3:46

let's wrap this section here inside of a

3:48

div and each one of these as well there

3:51

we go that's a little bit better all

3:52

right now let's jump into our manage

3:54

user screen and we can see our user

3:56

management with our table of four users

3:58

I did go ahead and register two extra

4:00

users here so that we can test our

4:02

deletion and we can see at the present

4:03

moment we only have one user with a roll

4:05

ID of two so only one administrator so

4:08

next let's go ahead and rig up our roll

4:10

column here to a select and then anytime

4:12

that select box changes we want to

4:14

persist the user's role change so let's

4:16

jump back into that page and for our row

4:20

column here we can get rid of everything

4:21

that we have so far and let's put this

4:23

inside of a form the action here we will

4:25

create in a second method for that will

4:27

be post and then all we need inside of

4:29

this form is a single select name of

4:32

role ID and then we'll want to Loop over

4:34

each of our roles so at each row in

4:37

rolls and then we'll want to put our

4:39

option with the value of that role ID

4:41

and then the contents will be the

4:43

rule.name and then we also want to add

4:45

an additional check here so if the roll

4:47

dot ID equals the user.roll ID then we

4:51

want to mark this as selected otherwise

4:54

we can just put an empty string there so

4:56

let's give that a save jump back into

4:57

our browser and we should now see a

4:59

select box looks like they are all

5:01

defined oh you know what we forgot to do

5:03

in our last lesson was actually rig up

5:05

the name column to our role so let's do

5:08

column public name String for some

5:11

reason I completely forgot about

5:12

everything inside of the models last

5:13

lesson but we did actually Circle back

5:15

and fill out our user I just forgot that

5:17

role name needed to be created there as

5:19

well so now that we have that saved

5:21

let's jump back into our browser refresh

5:22

and there we go so now we see our actual

5:24

role names with user user admin and user

5:27

so next whenever we actually change a

5:29

role for a user we want that to persist

5:30

so right now if I refresh that's just

5:32

going to reset back to user so let's

5:34

jump back into our code base and take

5:36

the care of that let's go ahead and just

5:37

do an inline on change equals this dot

5:40

parentelement dot submit I know some

5:43

people don't like putting their event

5:44

handlers directly on as attributes but

5:47

if we imported Alpine it would look

5:48

pretty much the same so we're just going

5:49

to move forward with that for right now

5:51

and then we need to set up our forms

5:53

action so that that actually has

5:54

something to submit to so let's jump

5:56

back into our users controller and let's

5:58

do public now you can call this

5:59

something like change rolls roll change

6:01

I'm just going to call it roll we'll

6:03

want our request response and params and

6:06

we're also going to want to import the

6:08

validator so let's import schema and

6:10

rules from at ioc Adonis or validator

6:15

for this validator we will do const row

6:18

schema equals schema.create role ID ish

6:22

schema.number and then we'll do rules

6:24

dot exists so that we can check whether

6:26

or not the role ID provided actually

6:28

exists within our database table roles

6:31

column of ID that should be all that we

6:34

need for our schema we'll grab the

6:36

actual user's ID off of the params so

6:38

then we can do const user equals await

6:41

user dot find or fail and then just

6:44

provide it the params.id oh we also want

6:47

to validate our data so data equals 08

6:50

request validate schema is roll schema

6:54

and then we could do a weight user.merge

6:56

data and save lastly let's return

6:59

response Dot redirect and redirect them

7:02

directly back to the form give that a

7:04

save and let's break that up to a route

7:05

so routes inside of our group here let's

7:08

do route Dot and we'll set this up as a

7:10

patch and we'll want this to have a

7:11

param of ID and then we'll suffix it

7:13

with Slash role and then this will be

7:15

users controller dot roll as roll and

7:19

then we need to rig it up to our form so

7:21

jump back into manage for our action

7:23

let's do route users dot roll and then

7:25

the ID will be user dot ID then we also

7:28

need to specify that the query string

7:30

underscore method should be patch and

7:34

then lastly let's go enable method

7:36

spoofing so let's jump into our app.ts

7:38

right here where we have allow method

7:40

spoofing change that from false to true

7:42

and we should be good to test this out

7:43

so let's jump back into our browser give

7:45

this a refresh to pick up those changes

7:47

and let's change Butters here from user

7:49

to admin all right we did see that that

7:51

did do a soft Refresh on us and it is

7:54

still admin let's give it a hard refresh

7:56

just to test it out and it is still

7:58

admin we can jump into our actual

8:00

database here give that a refresh and

8:02

test that out and you can see Butters is

8:03

indeed a roll ID of two so he is

8:06

officially an admin we can test it again

8:08

by changing them back to a user refresh

8:09

once more and he's back to one so that

8:12

is working A-Okay now before we go any

8:14

further we want to restrict this page

8:16

and that action to administrators only

8:19

it doesn't make sense to allow just any

8:20

old user to pop into this page see all

8:23

of our users and be able to change all

8:24

of their roles so we'll want to create a

8:26

middleware for that action now if you're

8:28

using bouncer that would be a perfectly

8:29

valid solution but I think we can do

8:32

this one check within a single

8:33

middleware so let's go ahead and stop

8:36

our server and let's do node Ace make

8:39

middleware and let's call this roll so

8:41

we're not going to call this admin or

8:42

user we'll just call this roll and then

8:45

we'll be able to provide a role into

8:46

this middleware so that we can check

8:48

against it to see whether or not our

8:49

user has it to allow or disallow the

8:52

particular action so let's go ahead and

8:54

register that role real quick so let's

8:56

jump down to our kernel underneath auth

8:58

here let's do roll and import app

9:01

middleware and roll give that a save and

9:04

then let's jump into our roll middleware

9:05

and we'll want to accept in the

9:08

particular roles that we want to allow

9:09

into this middleware check as the third

9:12

argument we can call this guards and

9:14

this will come through as a string array

9:15

and so the guards that we'll want to

9:17

provide here will look something like

9:18

this so we'll have dot middleware to

9:20

actually apply in the middleware

9:21

probably have off prior to it and then

9:24

we would have our roll middleware which

9:25

is this one here colon and then a list

9:28

of any roles that we want to allow into

9:31

this particular route so we would want

9:33

admin for this one and say in the future

9:34

we were to add a moderator role we could

9:36

allow them in here by doing a comma

9:38

delimited list and just adding them in

9:40

there like that so this is on the route

9:41

registration this is what it would look

9:43

like so what we need to do here is

9:44

change our guards string array from a

9:46

string array of admin moderator or

9:49

whatever into a list of IDs for those

9:52

roles that they represent so we can do

9:54

const roll IDs equals then we can Loop

9:57

over our guards as guard import our

10:00

rolls enum and then try to find that

10:02

guard key and we also want to make this

10:04

to uppercase so that it changes from

10:07

lowercase admin to the uppercase admin

10:09

that would match our roll key and then

10:11

let's also grab our response and auth

10:14

out of our HTTP context if not our role

10:18

IDs includes our auth user role ID then

10:23

we want to stop this request for the

10:25

user and kick them out now you can

10:26

either redirect them to a page that

10:28

they're allowed at and give them some

10:29

session message or you can just end the

10:31

request with an error so let's return

10:33

response Dot and let's do unauthorized

10:36

here to just kick them out with an error

10:38

and we can say this is restricted to

10:41

guards.join make that a common delimited

10:44

list there oop I should have an s on it

10:46

there we go users so whenever we

10:48

restrict our route to a roll of just

10:50

admins if the user is not an

10:52

administrator this will kick them out

10:53

with an unauthorized error otherwise if

10:56

they are an admin they will bypass that

10:58

if statement and go directly to next

10:59

allow them into the request Handler so

11:02

this should be all that we need to do

11:03

here let's give that a save jump back

11:04

into our routes and rig that up to our

11:07

user group so we can do middleware

11:09

provided off first and then roll and

11:12

specify that we only want to allow

11:13

admins give that a save and let's change

11:16

one more thing so within our users

11:18

controller if a currently authenticated

11:20

administrator demotes themselves to a

11:22

user we don't want to just redirect them

11:24

back because now since this page is

11:25

restricted to just administrators

11:27

whenever we actually try to redirect

11:28

them back since they're no longer an

11:30

admin they would just be reached with an

11:32

error so let's go ahead and change this

11:34

so let's grab our auth off of our HTTP

11:36

contract as well all right and let's do

11:39

const is auth user equals user.id equals

11:43

our auth.user ID and then we can do a

11:46

ternary check here is auth user and

11:50

user.roll ID does not equal roles dot

11:54

admin otherwise we can redirect them

11:56

back so here we would want to do

11:58

response redirect and and then two and

12:01

we'll just do path and point them back

12:03

to the home page so if the auth user is

12:05

changing their role and that role is not

12:07

to an admin which in this current point

12:09

in time would be true for anything since

12:10

we only have two roles then we would

12:12

want to kick them back to the home page

12:13

otherwise we're okay to just return them

12:15

back so let's give this a save jump back

12:17

into our terminal boot back up our

12:18

server and let's jump back into our

12:20

browser give that a refresh and let's

12:22

try changing a role that is not our

12:24

currently authenticated user first so

12:26

let's change apples to admin okay good

12:29

so we got redirected back to this page

12:30

just like we should have been and now

12:32

let's change our test admin from an

12:34

admin to a user and there we go we got

12:36

redirected back to the home page

12:38

preventing an error for us and you can

12:40

note that we also do not see manage

12:41

users as a link here either let's try to

12:44

manually go into that page now so let's

12:45

go users manage and we are greeted by an

12:49

error so this is being successfully

12:51

restricted to just administrators let's

12:53

log out and let's log in as our admin so

12:56

that I think that's apples at test.com

12:58

okay and let's begin manage our users

13:00

again and give test admin administrator

13:03

capabilities once more there we go and

13:05

next let's go ahead and register up the

13:07

deletion So within our manage page we

13:10

will want to take out this to do replace

13:12

this with a form action which we will

13:14

again fill out here in a second method

13:16

of post and instead of a select or

13:18

anything in here all that we want is a

13:20

button type equals and let's actually

13:22

set this to a button inside of here

13:24

let's just put delete and then similar

13:26

to our previous one we're just going to

13:28

do an inline event handler here and

13:30

instead of just directly submitting

13:31

which is why we've set this to a button

13:33

let's actually do on click equals and

13:36

let's provide a confirm and let's say

13:38

are you sure you want to delete this

13:42

user and then if that comes back truthy

13:44

we can do and and this dot parent

13:47

element dot submit so if the user

13:50

confirms that they want to delete the

13:51

user then this will come back truthy and

13:53

we'll move on to our and check which

13:55

would be our submission of our form

13:57

otherwise if this comes back falsy the

13:59

and check would be just bypassed so this

14:01

will never run and let's next go ahead

14:03

and set up that route Handler so inside

14:05

of our user's controller public async

14:08

destroy we'll want our response params

14:11

and auth out of here as well so let's

14:13

grab our user first so const user equals

14:16

await user.find or fail rams.id let's do

14:20

our is auth user check again so is auth

14:22

user equals user.id off user ID and then

14:27

we can do await user dot delete and then

14:30

return is off user and we don't need to

14:33

do any additional check here so if it's

14:35

the auth user deleting themselves then

14:37

we just want to return them back to the

14:39

home page so response redirect to path

14:41

home otherwise we can just return them

14:43

back so response dot redirect back and

14:46

that should be all that we need to do

14:47

there since we don't have any additional

14:49

relationships bound off of our user we

14:51

don't need to worry about deleting those

14:52

out prior to our user installation so at

14:54

this point it should be pretty

14:55

simplistic just as we have it let's go

14:57

ahead and rig up that route so routes

14:59

route delete ID and then users

15:03

controller.destroy as destroy give that

15:06

a save and let's go rig that up to our

15:08

form so action route users dot destroy

15:12

provided the ID of user.id and then our

15:16

query string of underscore method is

15:19

delete and you can put these different

15:21

methods buffing forms into actual Edge

15:24

components we have a lesson on that

15:26

specifically if you are interested in

15:27

learning about that approach but for

15:29

right now we will just leave this as is

15:30

and let's go ahead and give this a test

15:32

so let's jump back into our browser give

15:34

this a refresh and we should now see

15:36

delete buttons which we do now we're

15:37

logged in as Apple so let's not delete

15:39

him quite yet and let's actually try to

15:41

delete Butters instead so let's click on

15:43

delete we are greeted with our are you

15:44

sure you want to delete this user

15:45

message let's first try cancel to make

15:47

sure that that works okay refresh to

15:49

make sure Butters is still there and he

15:50

is so let's try deleting again click OK

15:52

and buy those Butters now we can try

15:55

that again with apples which we are

15:56

logged in as actually we can do one more

15:58

thing here before we delete out off user

16:00

of apples let's jump back into our page

16:02

and where we have user email Let's do an

16:04

additional check here so let's plop this

16:06

down onto its own line and let's do at

16:08

if user.id equals auth user ID and if

16:13

and then we can put in parentheses U so

16:16

we can specify that this is the

16:17

authenticated user by telling them this

16:19

is you and there we go so you can see

16:20

apples has U so now we can tell that

16:22

this is us and we can try to delete hit

16:25

OK and we are redirected back to the

16:28

login or the home page just fine so

16:30

that's working A-Okay so that gives you

16:32

an idea on how to start out a user

16:34

management screen it's at its very

16:35

simplistic State at this point in time

16:37

but we'll expand upon this further in

16:38

later lessons particularly next we'll go

16:41

in and add user blocking so that we can

16:43

restrict users from being able to log in

16:45

or access our application

16:47

[Music]

17:03

thank you

Join The Discussion! (2 Comments)

Please sign in or sign up for free to join in on the dicussion.

  1. Commented 2 years ago

    you are hero man

    1

    Please sign in or sign up for free to reply

    1. Commented 2 years ago

      Thank you!! 😊

      1

      Please sign in or sign up for free to reply

Playing Next Lesson In
seconds