vincent15000's avatar

Laravel with multiple role user : is my database design ok ?

Hello,

It's the first time that I work on an app with multiple roles. And I'd like your advice if I have a good design for the database and rights management.

I have thought about using Spatie permissions and roles, but I don't think that I really need such a package. And perhaps also the desire to do it by myself (good idea ?).

Here are my tables.

users: id, name, email
companies: id, name
members: id, company_id, user_id
roles: member_id, role_id
...
trainings: id, name, company_id
training_trainer: training_id, trainer_id
training_student: training_id, student_id

The roles are written in a configuration file, I have these ones.

owner: 0 // can access to all datas and create / update / delete what he wants
admin: 1 // can access to all datas and create / update / delete what he wants except the company and the owner
manager: 2 // can access to datas like trainings, trainers
accountant: 3 // can only check the number of hours per trainer for each training
trainer: 4 // can only access to the calendar of the trainings to which he is assigned and can store tests / results for each student
student: 5 // can only access to the calendar of the training to which he is subscribed

I manage the rights with policies. For example if a user connect, he will be attached to a company and I check the roles of this user for that company. And to write the policy of a training, I will write a condition to not allow a trainer (role 4) to create or update or delete a training, but only view those one to which he is assigned.

Is this a good pratice ? Do I have the right approach ?

Instead of having a roles table, I thought about writing the roles in a JSON column in the members file.

members: id, company_id, user_id, roles // there are easy to use queries (```whereJsonContains()```) to check some data inside a JSON field

With a JSON field, it's more easy to check the roles for each member, for example exclude some possibilities like : a member cannot be trainer and student at the same time for the same company.

Well ... for the moment I can manage the roles quite well, but I'm afraid that this structure can add complexity when adding new functionalities to the app.

What can you suggest me ? Do I have the good approach ? Perhaps a better database structure ? Other advice ?

If you accept to share your experience, it would be great ;).

Thanks a lot.

V

0 likes
13 replies
saedyousef's avatar

@vincent15000 In our application, we have groups table has name and json field permissions, and also another column for the shop_id (because we've multiple shops). Every user in the app is assigned to a group, and we've auth service built on top of laravel, and that service has a method called allowed(string $action) and it returns boolean. Anyway, in the groups.permissions you would store action names e.g : {"edit_profile":1}, and in the editProfile() method you would check if the user is allowed to do this action by authService->user()->allowed('edit_profile') and you do your thing. In this case, you've more flexibility and you don't have to hard check for user role. For complete access, we've a group with the permission "superuser:1" and the authService knows that user with this permission will always be allowed to access everything.

1 like
vincent15000's avatar

@saedyousef That's effectively interesting ... I didn't thought about the fact that you don't have to check the role given you handle the permissions.

For the moment I only need to assign permissions to the roles and not to the users. So each user is allowed to do something according to his role.

Is your allowed() method similar to the policy authorization $this->authorize() ? Or do you use the alllowed() method inside the policies ?

What you suggest me would be to have this configuration ?

members: id, company_id, user_id, roles
vincent15000's avatar

@saedyousef No, the members table is to attach some users to a company. Each user can possibly work for several companies, but not necessarily with the same roles.

You have the details at the beginning of the post.

saedyousef's avatar

@vincent15000 Ah now I see. I think the group of permissions approach would be more useful. In your approach, if you decided to change permissions for users with role accountant, you need to update all the permissions of the users one by one, which is not handy. If you take the groups approach, you will have extra tables yes, but you will not get to this situation.

1 like
vincent15000's avatar

@saedyousef For the moment, I don't need to manage the permissions from the frontend, all permissions are definitively declared inside the code. What do you suggest me according to the informations I gave at the top of the post ?

saedyousef's avatar

@vincent15000 No, we don't use the Policy at all. The allowed() returns boolean and you decide what to do with this response.

1 like
jlrdw's avatar
jlrdw
Best Answer
Level 75

@vincent15000 I look at it like:

  • Bob is an admin

  • Suzy is admin and does bookkeeping

  • Mary is a bookkeeper only

  • If Bob is logged in, Bob can only do admin stuff and all access to user stuff. But Bob cannot mess with bookkeeping.

  • If Suzy is logged in she can access admin stuff and bookkeeping and accounting stuff.

  • If Mary is logged in she cannot mess with admin stuff, but has access to bookkeeping and accounting stuff.

So I just check at method level if the logged in users role can or cannot access that method / function.

And use query scopes to let a user edit / view their own data or an admin can access all users data.

Each app will be different as to who can do what.

So in pseudocode:

public function makeInvoice()
    {
        if (a required role of bkeep is not true here) {   // bkeep = bookkeeper
            return redirect('somewhere'); // whereever you redirect to if not authorized
        }
        // Rest of method here is accomplished if 
        // the logged in user has the required role of 'bkeep'.
    }

Again just examples.

Another example I saw:

public function update(Request $request, Post $post) {
    if ($post->author !== auth()->user()->id || auth()->user()->cannot('edit posts'))
        abort(404);// or redirect, or whatever action 
    }
    //rest of method if all okay
}

In summary RBAC is at least 3 main steps:

  • A login required
  • An authorization implementation to determine what the logged in person with role can or cannot do
  • Protection of URL and parameters, checking that the logged in users id matches the id used in a query

Each application will require unique tweaks in RBAC, no two apps are exactly the same.

1 like
vincent15000's avatar

@jlrdw Ok thank you very much. That's very clear now. I never really had RBAC steps in mind, but effectively it's the basis. Thank you ;).

Lara_Love's avatar

hello . i add field is_admin in table user and i try solve all problem with it 🙂

1 like

Please or to participate in this conversation.