In modern applications, managing user permissions efficiently is crucial for security and scalability. Traditional methods like storing permissions as comma-separated values can lead to performance issues and data inconsistency. In this blog, we will explore a robust PostgreSQL-based table architecture for dynamic permission management, ensuring scalability, flexibility, and efficient querying.
CREATE TABLE users (
id SERIAL PRIMARY KEY,
mobile_number VARCHAR(10) UNIQUE NOT NULL,
first_name VARCHAR(100),
middle_name VARCHAR(100),
last_name VARCHAR(100),
email VARCHAR(255) UNIQUE,
password_hash TEXT NOT NULL,
role_id INT, -- Role assigned to the user
is_active BOOLEAN DEFAULT TRUE,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (role_id) REFERENCES roles(id) ON DELETE SET NULL
);
CREATE TABLE roles (
id SERIAL PRIMARY KEY,
name VARCHAR(255) UNIQUE NOT NULL,
description TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE modules (
id SERIAL PRIMARY KEY,
name VARCHAR(255) UNIQUE NOT NULL,
parent_id INT NULL, -- Supports hierarchical module structure
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (parent_id) REFERENCES modules(id) ON DELETE SET NULL
);
CREATE TABLE permissions (
id SERIAL PRIMARY KEY,
module_id INT NOT NULL,
slug VARCHAR(255) UNIQUE NOT NULL, -- Unique slug (e.g., "user.create", "user.delete")
name VARCHAR(255) NOT NULL,
description TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (module_id) REFERENCES modules(id) ON DELETE CASCADE
);
CREATE TABLE role_permissions (
id SERIAL PRIMARY KEY,
role_id INT NOT NULL,
permission_id INT NOT NULL,
UNIQUE (role_id, permission_id),
FOREIGN KEY (role_id) REFERENCES roles(id) ON DELETE CASCADE,
FOREIGN KEY (permission_id) REFERENCES permissions(id) ON DELETE CASCADE
);
CREATE TABLE user_permissions (
id SERIAL PRIMARY KEY,
user_id INT NOT NULL,
permission_id INT NOT NULL,
UNIQUE (user_id, permission_id),
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
FOREIGN KEY (permission_id) REFERENCES permissions(id) ON DELETE CASCADE
);
CREATE TABLE audit_logs (
id SERIAL PRIMARY KEY,
user_id INT NOT NULL,
action TEXT NOT NULL,
details JSONB NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE SET NULL
);
SELECT p.slug, p.name
FROM permissions p
JOIN role_permissions rp ON p.id = rp.permission_id
JOIN users u ON rp.role_id = u.role_id
WHERE u.id = 5
UNION
SELECT p.slug, p.name
FROM permissions p
JOIN user_permissions up ON p.id = up.permission_id
WHERE up.user_id = 5;
SELECT * FROM audit_logs WHERE user_id = 5 ORDER BY created_at DESC;
See who modified permissions for user ID 5.
INSERT INTO user_permissions (user_id, permission_id) VALUES (5, 10);
A well-designed permission management system in PostgreSQL ensures secure, scalable, and efficient user access control. By implementing the above architecture, you can streamline role-based and user-specific permissions while maintaining data integrity and audit trails. This approach provides a future-proof solution for applications with growing user bases and complex permission requirements.
✅ Implement this architecture in your PostgreSQL-based project today and ensure a secure, scalable permission management system!
Comments