First and foremost, I’m so sorry about how long this question is and appreciate your time in reading it!
We are currently developing what I prefer to describe as a web based platform that holds a number of applications within it and have come across a permissions problem. This is an internal application for our business and isn’t public, for this reason we have unique needs.
We cannot use a strict group based permissions system because it is too general making it too restrictive, some staff will need access to multiple applications, multiple departments (some may call them groups) within the applications and only a few functions within each department.
We also need to be able to display the data dynamically, as we are developing a number of applications for the platform we need to be able to register information in a database, it will then populate the appropriate checkboxes and check them if the user has access or leave them empty if the user doesn’t have access to the application, department or function.
For these reasons we need 3-tiers for permissions that consist of
- the apps the user can access
- the departments the user can access
- the specific functions the user has access to
In theory this should be very simple, I’m sure the best solution will be quite simple in fact but I think we’re either overlooking something or looking too closely at something.
Before I go any further, I understand that there are a number of ways to deal with this situation in it’s existing structure, we have 6 running and working correctly however these are using “unusual” methods and we really do need a better solution and I believe this starts at the database design.
We currently have tried a 3-table systems that has the following tables relating to permissions
Application register: this holds a list of all applications within the platform
app_id (primary AI) | perm_id (int) | app_name | app_code | app_location | tab_location | access_key
Application permissions register: this holds a list of every permission for every application regardless if it is an application, department or function (defined under access_type below)
permission_id (primary AI) | perm_id (int) | app_id | permission_code | permission_name | permission_description | access_type
Specific user permissions: this holds the specific permissions that relate to an application, department and function
permission_id (primary AI) | perm_id (varchar) | app_permission_id | app_id | user_id | access_status | function_code
The problem comes in with assigning permissions as when we get down to a function level we have found that the mySQL query needs to be too specific in order to get the user access information and we cannot get the full list of functions for that department, no NULL values are being returned in the LEFT JOIN where they should – this is attributed to a particular field that has a common name in multiple tables being joined but different data in each one. We specify all the fields we want returned from each query but for some reason this 1 field keeps coming up and the wrong data is being joined.
So, I’m thinking of implementing the following structure:
Application register: to hold a list of all applications with a URI and navigation system reference
app_id (primary AI) | app_name | app_uri | app_tab
Department register: to hold a list of all departments and the relationship to an application
dept_id (primary AI) | app_id | dept_name
Functions register: to hold a list of all functions and the relationship to a department, the department in the “department register” relates to the application
function_id (primary AI) | dept_id | function_name | function_description
Specific user permissions: to hold a list of all functions a user can perform, this is associated to the functions register, that associates itself to the department register, that relates to the application register
permission_id (primary AI) | user_id (index) | function_id | permission_status (int - 0 = not allowed access | 1 = allowed access)
EDIT START
I just realized that something isn’t quite right on the specific user permissions table as shown above, I’m not taking into account application or department permissions, so how does this look?
I have changed function_id to permit_item to hold the ID of the item then added in permit_type to define if it is an application, department or function that the user has access to.
permission_id (primary AI) | user_id (index) | permit_item | permit_type | permission_status (int - 0 = not allowed access | 1 = allowed access)
EDIT END
From here we can do a few joins in a query, for example (just to show the concept for illustration purposes only, I realize that this is not the correct query or best way to write it in production)
SELECT * FROM app_register
JOIN dept_register ON app.register.app_id=dept_register.app_id
JOIN function_register ON dept_register.dept_id=function_register.dept_id
JOIN user_permissions ON app_register.app_id=user_permissions.app_id
WHERE user_permissions.user_id='1'
This should give me a simplified joined permissions table that relates all of the functions, departments and applications then lists what applications the user has access to.
But I need input from impartial observers rather than emotionally invested database designers and his friends.
Is there anything that I have overlooked or looked at too hard at?
Is there another better structure we can try that you can think of to handle this data?
I really appreciate everyone’s help and sorry again for the length of this question!
Your proposed solution seems to make the most sense to me.
Regarding your issue of
LEFT JOINworking oddly, I might suggest a simple technique we use. We simply prefix each table and its fields with a unique value. For example, the tables:apples: [id, brand, organic]
brands: [id, name]
Would become:
app_apples: [app_id, appbrd_brand, app_organic]
brd_brands: [brd_id, brd_name]
This accomplishes two things. Each field is guaranteed to be unique throughout the system (thus should resolve all
JOINissues), and that relationships between data can also be seen and resolved very quickly.Just my two cents.