Question

We are using several tables that look roughly like this:

| ID  | DepartmentId | Data |
| 1   | 1            | ...  |
| 2   | 1            | ...  |
| 3   | 2            | ...  |
| 4   | 3            | ...  |
| 5   | 2            | ...  |

We have about 200 MySQL users, each of which is associated with one or more departments (via a simple Username/DepartmentId table) We would like to limit the access (SELECT, UPDATE) to only the correct subset of the records for each user.

For example, a

SELECT * FROM DataTable

by user "Bob" who is associated with departments 1 and 3 should return records 1, 2 and 4. The same query for user "Alice", associated with department 1, should only return records 1 and 2.

What is the best way to accomplish this?

Was it helpful?

Solution

MySQL does not have row based permissions. The best you could do is construct views to show certain records. A simple way would be to add a mapping table with the mysql user names to department id's that they have access to and have the view select on that mapping table joined to the original where limited to the current requesting user. You can use CURRENT_USER() to get the current mysql user.

Then limit the users to the view, not the original table.

Here's a breakdown of the steps:

   CREATE TABLE `mysqluser_dept` (
     `mysqluser` varchar(255) NOT NULL DEFAULT '',
     `DepartmentId` int(11) NOT NULL DEFAULT '0',
       PRIMARY KEY (`mysqluser`,`DepartmentId`)
   ) ENGINE=InnoDB DEFAULT CHARSET=utf8;

    CREATE SQL SECURITY INVOKER VIEW filtered_view AS 
           SELECT o.* FROM original_table o 
               JOIN mysqluser_dept m on m.DepartmentId = o.DepartmentId
                    WHERE m.mysqluser = current_user() GROUP by o.id;

You can limit the select o.* in the view as needed to a specific set of fields if you don't want to show all the columns of the original table. Now populate the mapping table of mysqlusers to departmet column as needed. The Current user returns exact user, so use full user string ( username@localhost, username@%, etc...) or change the view to chop off the host in the comparison.

Now a user can access the filtered_view and only see the rows they have access to by department. Bonus: since this is many-to-many, you can have people in multiple departments if needed--the group by on the primary key of the original table avoids duplicate records.

You'll probably also want to lock down permissions to the mapping table mysqluser_department.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top