User System - Multiple Roles in MySQL Database - mysql

So I am in the process of attempting to create a basic user system and within this system I want users to be able to have multiple roles.
Say for example I have the roles as follows: Administrator, Events Organiser, Donator
What is the best way to assign these multiple roles to a user and then check if they have any of these roles for showing certain permissions.
If it was only one role per person then it wouldn't be a problem as I'd just assign say Administrator = 10, Organiser = 5 and Donator = 1 and then do an if statement to check if the MySQL data is equal to any of those three numbers.
I can't imagine there is a way to add a MySQL Field and fill it with say "Administrator,Donator" and therefore that user would have both of those roles?
Is it just a case of I would need to create 3 separate fields and put a 0 or a 1 in those fields and check each one separately?

Use multiple tables and join them:
User
--------------
id name
1 test
Role
--------------
id name
1 Donator
2 Organizer
3 Administrator
User_Role
--------------
id user_id role_id
1 1 1
2 1 3
SELECT * FROM User u
LEFT JOIN User_Role ur ON u.id = ur.user_id
LEFT JOIN Role r ON ur.role_id = r.id
WHERE r.name = "Administrator";
The query is easier if you know you only have 3 roles and they are easy to remember.
SELECT * FROM User u LEFT JOIN User_Role ur ON u.id = ur.user_id WHERE ur.role_id = 3;

You will have a roles, users and users_roles tables:
The roles table will hold the various roles your users can have. In my example data I've declared Administrator and Donator roles.
roles
id unsigned int(P)
description varchar(15)
+----+---------------+
| id | description |
+----+---------------+
| 1 | Administrator |
| 2 | Donator |
| .. | ............. |
+----+---------------+
And of course you'll have to store information about your users.
users
id unsigned int(P)
username varchar(32)
password varbinary(255)
etc.
+----+----------+----------+-----+
| id | username | password | ... |
+----+----------+----------+-----+
| 1 | bob | ******** | ... |
| 2 | mary | ******** | ... |
| .. | ........ | ........ | ... |
+----+----------+----------+-----+
Finally you'll tie the two together in the users_roles table. In my example data you can see that bob is a Donator and mary is both an Administrator and a Donator. The user_id and role_id are both foreign keys to their respective tables and together they form the primary key for this table.
users_roles
user_id unsigned int(F user.id)\_(P)
role_id unsigned int(F role.id)/
+----+---------+---------+
| id | user_id | role_id |
+----+---------+---------+
| 1 | 1 | 2 |
| 2 | 2 | 1 |
| 3 | 2 | 2 |
| .. | ....... | ....... |
+----+---------+---------+
This way a user can have an unlimited number of roles.

If you don't want to create two new table for such small thing.
You can utilize MySQL built in SET data type or you can store comma separated role in varchar and do query operation using FIND_IN_SET()
Eg.
SELECT * FROM user WHERE FIND_IN_SET('admin', role)>0;
+----+----------+----------+----------------+
| id | username | password | role (varchar) |
+----+----------+----------+-----------------+
| 1 | bob | ******** | admin |
| 2 | mary | ******** | admin,role1 |
| .. | ........ | ........ | role1,role2 |
+----+----------+----------+-----------------+

Related

join two tables in mysql and get records

I have two tables "contacts" and "users". Users table storing data with "," separated. Need to distinct data in "Contacts" column from "Contacts" table. And need to join with "Users" table, and get the records.
Contacts Table
--------------------------
id | user_Id | contats
--------------------------
1 | 2147483647 | 90123456789,90123456789,90123456789,90123456789
2 | 2147483647 | 90123456789,90123456789,90123456789,90123456789
3 | 919444894154 | 90123456789,90123456789,90123456789,90123456789
Users Table
-----------------------------
id | username | email | phone
-----------------------------
1 | bhavan | bhavanram93#gmail.com | 90123456789
2 | bhavan | bhavanram93#gmail.com | 90123456789
3 | prince | prince#gmail.com | 1234567980
4 | bhavan | bhavanram93#gmail.com | 90123456789
5 | hello | hello#gmail.com | 1234567890
6 | bhavan | bhavanram93#gmail.com | 90123456789
Your table Contacts shouldn't be constructed this way.
Since you want 1 Users table containing all the data about a user, and 1 Contacts table containing links between different users, you'd rather do this kind of table structure :
Contacts table
id | user_id | contact_id
-------------------------
1 | 1 | 2
2 | 1 | 3
3 | 2 | 3
That'll allow you to do something like :
SELECT *
FROM Users
JOIN Contacts ON (Users.id = Contacts.contact_id)
WHERE Contacts.user_id = 1
Which will return all the data of the contacts of the user 1.
Your current structure is a huge ongoing mess, it's the opposite of being flexible.
You should restructure your db to a normalized format as Steve suggest.
But if you cant:
SELECT *
FROM Users
JOIN Contacts
ON CONCAT(',', Contacts.contacts, ',') like
CONCAT('%,', Users.phone, ',%')
WHERE Contacts.user_id = 1
the idea is you convert your contacts to
, <numbers> ,
,90123456789,90123456789,90123456789,90123456789,
and try to match with
%,90123456789,%
Note this approach cant use any index so will have bad performance with many
rows. if you are in the order of 1k-10k rows may be ok. More than that you need consider restructure your db.

Concatenate values for concatenated IDs

Database: MySQL
I have two tables, one for user's assigned roles and one that contains the role information. My problem is that the assigned roles are stored in a single field, separated by commas. I need to build a report that lists the roles by name, not the id, but still be in a single field separated by columns.
I'm thinking GROUP_CONCAT might be the solution but I've seen it used to create a concatenated list, not use one that already exists.
Table 1:USERS
ID | FNAME | LNAME | ROLE_IDS
------------------------------------------
1 | Bob | Jones | 445,44,45,449,459
2 | Mark | Doe | 426,459,445
3 | Jeff | Apple | 444,45
Table 2: ROLES
ID | ROLE_NAME
------------------------------------
4 | Basic
13 | Reporting
16 | Advanced
44 | Admin
45 | Super User
426 | Accounting
444 | User
445 | Receivables
449 | Processing
459 | Research
Expected Query Results:
ID | FNAME | LNAME | ROLES
-------------------------------------------
1 | Bob | Jones | Receivables, Admin, Super User, Processing, Research
2 | Mark | Doe | Accounting, Research, Receivables
3 | Jeff | Apple | User, Super User
For getting referencing role names, you can use GROUP_CONCAT like this :
SELECT us.ID,us.FNAME,us.LNAME,
GROUP_CONCAT(ro.ROLE_NAME) ROLES_NAME
FROM USERS us
INNER JOIN ROLES ro
ON FIND_IN_SET(ro.ID, us.ROLE_IDS) > 0
GROUP BY us.ID
I've tested it in SQLFIDDLE and working fine.

How to keep staff and users in separate tables with another table that gives them a general accountID?

I have two tables, staff and users. Below are shortened versions of the tables.
(* = PK, # = FK)
staff { staffID*, username, ... }
users { userID*, username, ... }
I want to make it so that these tables have a table that links them together in a way that they are given an accountID.
I was thinking that this will be something along the lines of...
accounts { accountID*, accountType, localID# }
...where localID is either; the staffID if the account is from the staff table, or the userID if the account is from the users table. The accountType would be the used to tell which table the account is from and would have possible values of staff and user.
First off - is this possible to accomplish this way? Is this a good idea? Since both the staff and users tables have the field username, should I instead use username in the accounts table in place of accountType and localID? (If there exists a record in the staff table with the username 'foo', then there cannot be a record in the user table with the username 'foo')
Secondly - If the tables above are the way that I should implement this, what constraint would I need to add to make it so accountType & localID are unique such that all the examples in Figure 1 would be accepted, whereas the examples in Figure 2 that are duplicate combinations of accountType and localID would not?
Figure 1
|-----------|-------------|---------|
| accountID | accountType | localID |
|-----------|-------------|---------|
| 1 | A | 1 |
| 2 | B | 1 |
| 3 | A | 2 |
| 4 | B | 2 |
|-----------|-------------|---------|
Figure 2
|-----------|-------------|---------|
| accountID | accountType | localID |
|-----------|-------------|---------|
| 1 | A | 1 |
| 2 | A | 1 |
| 3 | B | 2 |
| 4 | B | 2 |
|-----------|-------------|---------|
Reasoning - I want these separate tables for staff and users to because I want role based access control, but I want it such that there are roles only a staff account can have & there are other roles that only a user account can have.
Thanks in advance for any help/guidance offered.
Add the accountId in staff and user table, that would accomplish what your looking for.

Should I make 1 or 2 tables for Lecturers and Students in MySql? [duplicate]

This question already has an answer here:
Hierarchical database model
(1 answer)
Closed 8 years ago.
I am currently working on a project where you save the details of a lecturer and student.
I am not sure if I should use one table User or two tables Lecturer and Student.
When you log in as a lecturer you have special privileges as its a group management page for projects, on the group page when it loads that a student will not have. In User tbl there will be a column
status where on register, the page you can choose to be student or lecturer and enter a special
lecturer code. I am are using PHP with mySql.
In Summary, should I use 1 User table for both Student and lecturer, or have 2 separate Student and Lecturer tables.
Additional Information: 1 course could have many lecturers and students, but 1 student would have 1 course where as lecturer has many courses.
Great question!
It may seem over complicated, but if you want to scale this system, I highly suggest modeling this a little more "normalized". You are already on the right track by realizing that both lecturers and students are the same entity (people/users). The trick is that you should model "roles", and then model user's roles as well. That makes 3 total tables for this small portion of your model.
USERS USER_ROLES ROLES
+------------+ +----------+ +--------+
| id | <--> | user_id | /-->| id |
| login_name | | role_id | <--/ | name |
| etc | +----------+ +--------+
+------------+
users
======
id
login_name
etc
roles
=======
id
name
user_roles
===========
user_id
role_id
since
Sample Data
USERS
+----+------------+
| id | login_name |
+----+------------+
| 1 | Chris |
+----+------------+
| 2 | Cherri |
+----+------------+
ROLES
+----+------------+
| id | name |
+----+------------+
| 1 | Lecturer |
+----+------------+
| 2 | Student |
+----+------------+
USER_ROLES
+---------+---------+
| user_id | role_id |
+---------+---------+
| 1 | 1 | <-- Chris is a Lecturer
+---------+---------+
| 2 | 2 | <-- Cherri is a student
+---------+---------+
| 2 | 1 | <-- Cherri is also a lecturer
+---------+---------+
Use a single table with a field that indicates if it's a student or lecturer. It can be a simple integer column name "role" where role=0 means student and role=1 means lecturer.
This is simple, quick to implement, and meets the requirements.

Most efficient/easy method for permission system?

I'm creating an app using CakePHP and have hit a mental barrier when trying to figure out a permission system for the app. I've narrowed it down to a couple different methods, and I'm looking for some information about which would be a) most easily implemented and b) most efficient (obviously there can be trade-off between these two).
The app has many different models, but for simplification I'll just use User, Department, and Event. I want to be able to individually control CRUD permissions for each user, on each model.
Cake ACLs
Though poorly documented, I've got somewhat of an idea of how the ACL system works, and considered creating AROs as follows:
[1]user
create
read
update
delete
[2]department
...
etc. This would require users being in many different groups, and from what I've seen, Cake doesn't easily support this. Is there possibly a better way to do this, or is ACL not suitable for this situation?
Permission Flags in DB
This one is pretty straightforward, obviously having a flag in the user's record for
create_users, read_users, etc. With 4-5 models, this would mean 16-20 fields for permissions, which made me consider either using bit masks, or using a joined table. Is one of these better than the other? Which one is faster with less overhead?
Overall, I guess I really want to know what approach makes the most sense in the scale of the application from an efficiency and ease-of-development standpoint. I'm also open to other suggestions of how to go about this, if you have experience from a past project. Thanks in advance!
This is generally how I set up permissions - you have actions that can be performed, roles that can perform those actions and users who have roles. The examples I've put here are based on what you've requested though I think you'll find it rare you have a user who can do nothing but "create new user records" or "update department records".
actions
id varchar(50)
description varchar(200)
+-------------------+----------------------------------------------+
| id | description |
+-------------------+----------------------------------------------+
| USER_CREATE | Allow the user to create USERS records. |
| USER_DELETE | Allow the user to delete USERS records. |
| USER_READ | Allow the user to read USERS records. |
| USER_UPDATE | Allow the user to update USERS records. |
| DEPARTMENT_CREATE | Allow the user to create DEPARTMENT records. |
| ................. | ............................................ |
+-------------------+----------------------------------------------+
roles
id unsigned int(P)
description varchar(50)
+----+--------------------+
| id | description |
+----+--------------------+
| 1 | Manage users |
| 2 | Manage departments |
| .. | .................. |
+----+--------------------+
roles_actions
id unsigned int(P)
role_id unsigned int(F roles.id)
action_id varchar(50)(F actions.id)
+----+---------+-------------------+
| id | role_id | action_id |
+----+---------+-------------------+
| 1 | 1 | USER_CREATE |
| 2 | 1 | USER_DELETE |
| 3 | 1 | USER_READ |
| 4 | 1 | USER_UPDATE |
| 5 | 2 | DEPARTMENT_CREATE |
| 6 | 2 | DEPARTMENT_DELETE |
| .. | ....... | ................. |
+----+---------+-------------------+
users
id unsigned int(P)
username varchar(32)(U)
password varchar(123) // Hashed, like my potatoes
...
+----+----------+----------+-----+
| id | username | password | ... |
+----+----------+----------+-----+
| 1 | bob | ******** | ... |
| 2 | april | ******** | ... |
| 3 | grant | ******** | ... |
| .. | ........ | ........ | ... |
+----+----------+----------+-----+
users_roles
id unsigned int(P)
user_id unsigned int(F users.id)
role_id unsigned int(F roles.id)
+----+---------+---------+
| id | user_id | role_id |
+----+---------+---------+
| 1 | 1 | 1 |
| 2 | 2 | 2 |
| .. | ....... | ....... |
+----+---------+---------+
To determine if a user has a particular permission you could execute a query like this:
SELECT COUNT( roles_actions.id )
FROM users
LEFT JOIN users_roles ON users.id = users_roles.user_id
LEFT JOIN roles_actions ON users_roles.role_id = roles_actions.role_id
WHERE roles_actions.action_id = '<action.id>'