DEV Community

Sospeter Mong'are
Sospeter Mong'are

Posted on

Implementing Role-Based Access Control (RBAC) in MySQL

Introduction

Role-Based Access Control (RBAC) is a security model that restricts system access based on user roles. It simplifies permission management by grouping privileges into roles, which are then assigned to users. This article provides a complete MySQL implementation for an RBAC system, including core tables, relationships, and optional enhancements.


Core RBAC Tables

1. Users Table

Stores user account information.

CREATE TABLE users (
    user_id INT AUTO_INCREMENT PRIMARY KEY,
    username VARCHAR(50) NOT NULL UNIQUE,
    password_hash VARCHAR(255) NOT NULL,
    email VARCHAR(100) UNIQUE,
    is_active BOOLEAN DEFAULT TRUE,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);
Enter fullscreen mode Exit fullscreen mode

2. Roles Table

Defines different roles (e.g., Admin, Manager, User).

CREATE TABLE roles (
    role_id INT AUTO_INCREMENT PRIMARY KEY,
    role_name VARCHAR(50) NOT NULL UNIQUE,
    description TEXT,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
Enter fullscreen mode Exit fullscreen mode

3. Permissions Table

Lists all possible actions (e.g., users.create, products.delete).

CREATE TABLE permissions (
    permission_id INT AUTO_INCREMENT PRIMARY KEY,
    permission_name VARCHAR(100) NOT NULL UNIQUE,
    description TEXT,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
Enter fullscreen mode Exit fullscreen mode

Relationship Tables

4. User-Roles Assignment (Many-to-Many)

Links users to their assigned roles.

CREATE TABLE user_roles (
    user_id INT NOT NULL,
    role_id INT NOT NULL,
    assigned_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    PRIMARY KEY (user_id, role_id),
    FOREIGN KEY (user_id) REFERENCES users(user_id) ON DELETE CASCADE,
    FOREIGN KEY (role_id) REFERENCES roles(role_id) ON DELETE CASCADE
);
Enter fullscreen mode Exit fullscreen mode

5. Role-Permissions Assignment (Many-to-Many)

Specifies which permissions each role has.

CREATE TABLE role_permissions (
    role_id INT NOT NULL,
    permission_id INT NOT NULL,
    assigned_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    PRIMARY KEY (role_id, permission_id),
    FOREIGN KEY (role_id) REFERENCES roles(role_id) ON DELETE CASCADE,
    FOREIGN KEY (permission_id) REFERENCES permissions(permission_id) ON DELETE CASCADE
);
Enter fullscreen mode Exit fullscreen mode

Optional Enhancements

6. Permission Categories (For Better Organization)

Groups permissions into logical categories (e.g., "User Management," "Inventory").

CREATE TABLE permission_categories (
    category_id INT AUTO_INCREMENT PRIMARY KEY,
    category_name VARCHAR(50) NOT NULL UNIQUE,
    description TEXT
);

ALTER TABLE permissions ADD COLUMN category_id INT;
ALTER TABLE permissions ADD FOREIGN KEY (category_id) REFERENCES permission_categories(category_id);
Enter fullscreen mode Exit fullscreen mode

7. Audit Log (For Tracking Access)

Records user actions for security and compliance.

CREATE TABLE access_audit_log (
    log_id INT AUTO_INCREMENT PRIMARY KEY,
    user_id INT,
    action VARCHAR(100) NOT NULL,
    entity_type VARCHAR(50),
    entity_id INT,
    ip_address VARCHAR(45),
    user_agent TEXT,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (user_id) REFERENCES users(user_id) ON DELETE SET NULL
);
Enter fullscreen mode Exit fullscreen mode

Sample Data Setup

Inserting Roles and Permissions

-- Add roles
INSERT INTO roles (role_name, description) VALUES 
('admin', 'Full system access'),
('manager', 'Can manage products and users'),
('user', 'Basic access');

-- Add permissions
INSERT INTO permissions (permission_name, description) VALUES
('users.create', 'Create new users'),
('users.read', 'View users'),
('products.edit', 'Modify products');

-- Assign permissions to roles
INSERT INTO role_permissions (role_id, permission_id) VALUES
(1, 1), (1, 2), (1, 3), -- Admin has all permissions
(2, 2), (2, 3); -- Manager can view users and edit products
Enter fullscreen mode Exit fullscreen mode

Assign Roles to Users

INSERT INTO user_roles (user_id, role_id) VALUES
(1, 1), -- User 1 is an Admin
(2, 2); -- User 2 is a Manager
Enter fullscreen mode Exit fullscreen mode

Querying Permissions

Check if a User Has a Specific Permission

SELECT COUNT(*) > 0 AS has_permission
FROM users u
JOIN user_roles ur ON u.user_id = ur.user_id
JOIN role_permissions rp ON ur.role_id = rp.role_id
JOIN permissions p ON rp.permission_id = p.permission_id
WHERE u.user_id = 1 AND p.permission_name = 'users.create';
Enter fullscreen mode Exit fullscreen mode

List All Permissions for a User

SELECT p.permission_name
FROM users u
JOIN user_roles ur ON u.user_id = ur.user_id
JOIN role_permissions rp ON ur.role_id = rp.role_id
JOIN permissions p ON rp.permission_id = p.permission_id
WHERE u.user_id = 1;
Enter fullscreen mode Exit fullscreen mode

Best Practices

  1. Use CASCADE Deletes

    • Ensures that when a user or role is deleted, their assignments are automatically removed.
  2. Index Frequently Queried Columns

    • Add indexes on user_id, role_id, and permission_name for faster lookups.
  3. Regularly Audit Permissions

    • Use the access_audit_log to monitor suspicious activity.
  4. Avoid Over-Permissioning

    • Follow the Principle of Least Privilegeβ€”only grant necessary permissions.

Conclusion

This MySQL RBAC structure provides a scalable, secure, and flexible way to manage user permissions. By separating roles and permissions, you can easily adjust access levels without modifying individual user settings.

For smaller applications, you can omit optional tables like permission_categories and audit_log. For enterprise systems, consider adding hierarchical roles (where roles inherit permissions from parent roles).

Top comments (0)