0

Thiết kế DB Hệ thống Phân quyền IAM

Thiết kế DB Hệ thống Phân quyền IAM (Identity and Access Management)

image.png

1. Tổng quan Hệ thống

Hệ thống IAM này được thiết kế theo mô hình phân quyền dựa trên Role-Based Access Control (RBAC) kết hợp với Record-Level Security. Hệ thống cho phép quản lý người dùng, công ty, nhóm quyền, và kiểm soát truy cập chi tiết đến từng model và bản ghi cụ thể.

Các thành phần chính:

  • Quản lý Người dùng & Công ty: User, Company và mối quan hệ đa-đa giữa chúng
  • Quản lý Nhóm quyền: Group và cơ chế kế thừa quyền
  • Kiểm soát truy cập Model: Phân quyền CRUD trên toàn bộ model
  • Kiểm soát truy cập Record: Phân quyền chi tiết đến từng bản ghi theo điều kiện

2. Chi tiết các Bảng

2.1. IAM_USER - Bảng Người dùng

Mục đích: Lưu trữ thông tin tài khoản người dùng trong hệ thống.

Cột Kiểu dữ liệu Ràng buộc Mô tả
id bigint PK Mã định danh duy nhất của người dùng
email text NOT NULL, UNIQUE Địa chỉ email đăng nhập
password_hash text NOT NULL Mật khẩu đã được băm (bcrypt/argon2)
is_active boolean DEFAULT true Trạng thái kích hoạt tài khoản
company_id bigint FK → IAM_COMPANY(id) Công ty hiện tại của user (quan hệ N:1)
created_at timestamptz DEFAULT now() Thời điểm tạo tài khoản

Lưu ý quan trọng:

  • Một user có thể thuộc nhiều company (qua IAM_USER_COMPANY)
  • company_id trong bảng này là company "hiện tại" đang làm việc
  • Email phải unique trong toàn hệ thống

2.2. IAM_COMPANY - Bảng Công ty

Mục đích: Quản lý thông tin các công ty/tổ chức trong hệ thống multi-tenant.

Cột Kiểu dữ liệu Ràng buộc Mô tả
id bigint PK Mã định danh công ty
name text NOT NULL Tên công ty
created_at timestamptz DEFAULT now() Thời điểm tạo

Use case:

  • Hỗ trợ multi-tenancy: phân tách dữ liệu giữa các công ty
  • Một user có thể làm việc cho nhiều công ty khác nhau

2.3. IAM_USER_COMPANY - Bảng Quan hệ User-Company (Many-to-Many)

Mục đích: Liên kết người dùng với các công ty mà họ có quyền truy cập.

Cột Kiểu dữ liệu Ràng buộc Mô tả
user_id bigint PK, FK → IAM_USER(id) Mã người dùng
company_id bigint PK, FK → IAM_COMPANY(id) Mã công ty

Composite Primary Key: (user_id, company_id)

Ví dụ:

-- User A làm việc cho cả Company 1 và Company 2
INSERT INTO IAM_USER_COMPANY VALUES (1, 1), (1, 2);

2.4. IAM_GROUP - Bảng Nhóm Quyền

Mục đích: Định nghĩa các nhóm quyền (roles) trong hệ thống.

Cột Kiểu dữ liệu Ràng buộc Mô tả
id bigint PK Mã định danh nhóm
code text UNIQUE, NOT NULL Mã code nhóm (system_admin, sales_manager...)
name text NOT NULL Tên hiển thị của nhóm
category text Phân loại nhóm (system, business, technical...)
created_at timestamptz DEFAULT now() Thời điểm tạo

Ví dụ các nhóm thường gặp:

  • system_admin - Quản trị viên hệ thống
  • company_admin - Quản trị công ty
  • sales_manager - Quản lý bán hàng
  • accountant - Kế toán
  • employee - Nhân viên thông thường

2.5. IAM_USER_GROUP - Bảng Gán User vào Group

Mục đích: Liên kết người dùng với các nhóm quyền.

Cột Kiểu dữ liệu Ràng buộc Mô tả
user_id bigint PK, FK → IAM_USER(id) Mã người dùng
group_id bigint PK, FK → IAM_GROUP(id) Mã nhóm quyền

Composite Primary Key: (user_id, group_id)

Use case:

  • Một user có thể thuộc nhiều group
  • Một group có thể chứa nhiều user

2.6. IAM_GROUP_IMPLIED - Bảng Kế thừa Quyền giữa các Group

Mục đích: Định nghĩa mối quan hệ kế thừa quyền giữa các nhóm (role hierarchy).

Cột Kiểu dữ liệu Ràng buộc Mô tả
group_id bigint PK, FK → IAM_GROUP(id) Nhóm cha (kế thừa quyền)
implied_group_id bigint PK, FK → IAM_GROUP(id) Nhóm con (được kế thừa)

Composite Primary Key: (group_id, implied_group_id)

Cơ chế hoạt động:

Nếu Group A implies Group B:
→ User thuộc Group A sẽ tự động có TẤT CẢ quyền của Group B

Ví dụ:

-- System Admin kế thừa tất cả quyền của Company Admin
INSERT INTO IAM_GROUP_IMPLIED VALUES 
  (1, 2); -- group_id=1 (system_admin) implies group_id=2 (company_admin)

-- Company Admin kế thừa quyền của Employee
INSERT INTO IAM_GROUP_IMPLIED VALUES 
  (2, 3); -- group_id=2 (company_admin) implies group_id=3 (employee)

-- Kết quả: System Admin có quyền của cả Company Admin VÀ Employee

2.7. IAM_MODEL - Bảng Định nghĩa Model

Mục đích: Lưu trữ danh sách các model (bảng/entity) trong hệ thống cần được quản lý quyền.

Cột Kiểu dữ liệu Ràng buộc Mô tả
id bigint PK Mã định danh model
model text UNIQUE, NOT NULL Tên kỹ thuật của model (sale.order, product.product)
name text NOT NULL Tên hiển thị của model (Sales Order, Product)

Ví dụ:

INSERT INTO IAM_MODEL (model, name) VALUES 
  ('sale.order', 'Sales Order'),
  ('product.product', 'Product'),
  ('account.invoice', 'Invoice');

2.8. IAM_MODEL_ACCESS - Bảng Phân quyền Model-Level

Mục đích: Định nghĩa quyền CRUD cho từng nhóm trên từng model.

Cột Kiểu dữ liệu Ràng buộc Mô tả
id bigint PK Mã định danh
model_id bigint FK → IAM_MODEL(id) Model được phân quyền
group_id bigint FK → IAM_GROUP(id) Nhóm được cấp quyền
perm_read boolean DEFAULT false Quyền đọc (SELECT)
perm_write boolean DEFAULT false Quyền sửa (UPDATE)
perm_create boolean DEFAULT false Quyền tạo mới (INSERT)
perm_unlink boolean DEFAULT false Quyền xóa (DELETE)
active boolean DEFAULT true Trạng thái kích hoạt rule

Unique Constraint: (model_id, group_id)

Ví dụ:

-- Sales Manager có full quyền trên Sales Order
INSERT INTO IAM_MODEL_ACCESS 
  (model_id, group_id, perm_read, perm_write, perm_create, perm_unlink) 
VALUES 
  (1, 2, true, true, true, true);

-- Employee chỉ có quyền đọc Sales Order
INSERT INTO IAM_MODEL_ACCESS 
  (model_id, group_id, perm_read, perm_write, perm_create, perm_unlink) 
VALUES 
  (1, 3, true, false, false, false);

2.9. IAM_RECORD_RULE - Bảng Quy tắc Record-Level Security

Mục đích: Định nghĩa các điều kiện lọc bản ghi (row-level security) cho từng model.

Cột Kiểu dữ liệu Ràng buộc Mô tả
id bigint PK Mã định danh rule
model_id bigint FK → IAM_MODEL(id) Model áp dụng rule
name text NOT NULL Tên mô tả rule
domain jsonb Điều kiện lọc dạng domain filter (JSON)
perm_read boolean DEFAULT false Quyền đọc các bản ghi thỏa điều kiện
perm_write boolean DEFAULT false Quyền sửa các bản ghi thỏa điều kiện
perm_create boolean DEFAULT false Quyền tạo bản ghi mới
perm_unlink boolean DEFAULT false Quyền xóa các bản ghi thỏa điều kiện
is_global boolean DEFAULT false Rule áp dụng cho toàn bộ user hay chỉ các group cụ thể
active boolean DEFAULT true Trạng thái kích hoạt

Cấu trúc Domain Filter (JSONB):

[
  ["field_name", "operator", "value"],
  ["user_id", "=", "current_user_id"],
  ["company_id", "=", "current_company_id"]
]

Ví dụ:

-- Rule: Sales Manager chỉ thấy đơn hàng của công ty mình
INSERT INTO IAM_RECORD_RULE 
  (model_id, name, domain, perm_read, perm_write, is_global) 
VALUES 
  (1, 'Own Company Orders Only', 
   '[["company_id", "=", "current_company_id"]]'::jsonb,
   true, true, false);

-- Rule: Nhân viên chỉ thấy đơn hàng của chính mình
INSERT INTO IAM_RECORD_RULE 
  (model_id, name, domain, perm_read, is_global) 
VALUES 
  (1, 'Own Orders Only', 
   '[["salesperson_id", "=", "current_user_id"]]'::jsonb,
   true, false);

2.10. IAM_RECORD_RULE_GROUP - Bảng Gán Rule cho Group

Mục đích: Liên kết các record rule với các nhóm quyền.

Cột Kiểu dữ liệu Ràng buộc Mô tả
rule_id bigint PK, FK → IAM_RECORD_RULE(id) Mã rule
group_id bigint PK, FK → IAM_GROUP(id) Mã nhóm áp dụng

Composite Primary Key: (rule_id, group_id)

Use case:

  • Một rule có thể áp dụng cho nhiều group
  • Một group có thể có nhiều rule khác nhau

3. Luồng Kiểm tra Quyền (Authorization Flow)

3.1. Kiểm tra Quyền Model-Level

1. User đăng nhập → Lấy danh sách Groups của user (qua IAM_USER_GROUP)
2. Áp dụng Group Inheritance (qua IAM_GROUP_IMPLIED) 
   → Tạo danh sách "Effective Groups"
3. Kiểm tra IAM_MODEL_ACCESS:
   - Lọc theo model_id và effective groups
   - Tổng hợp quyền: Nếu BẤT KỲ group nào có quyền → User có quyền đó
4. Kết quả: {read: true, write: false, create: true, unlink: false}

3.2. Kiểm tra Quyền Record-Level

1. Sau khi pass qua Model-Level check
2. Lấy tất cả Record Rules áp dụng cho model và effective groups
3. Với mỗi operation (read/write/create/delete):
   a. Lọc các rules có quyền tương ứng = true
   b. Tổng hợp tất cả domain filters bằng OR logic
   c. Áp dụng filter vào SQL query
4. Kết quả: SQL với WHERE clause đã được inject domain filters

Ví dụ SQL được sinh ra:

-- User thuộc group "Sales Manager"
-- Rule: Chỉ xem đơn hàng của công ty mình
SELECT * FROM sale_order 
WHERE company_id = 1  -- Từ record rule domain
  AND is_deleted = false;

4. Các Trường hợp Sử dụng Thực tế

Case 1: Multi-Tenant với phân quyền theo công ty

-- Tạo 2 công ty
INSERT INTO IAM_COMPANY (name) VALUES ('Company A'), ('Company B');

-- User 1 làm việc cho cả 2 công ty, nhưng hiện đang ở Company A
INSERT INTO IAM_USER (email, password_hash, company_id) 
VALUES ('user1@example.com', 'hash', 1);

INSERT INTO IAM_USER_COMPANY VALUES (1, 1), (1, 2);

-- Record Rule: Chỉ xem data của công ty hiện tại
INSERT INTO IAM_RECORD_RULE (model_id, name, domain, perm_read) 
VALUES (1, 'Current Company Only', 
        '[["company_id", "=", "current_company_id"]]'::jsonb, true);

Case 2: Phân cấp quyền theo vai trò

-- Tạo hierarchy: Admin > Manager > Employee
INSERT INTO IAM_GROUP (code, name) VALUES 
  ('admin', 'Administrator'),
  ('manager', 'Manager'),
  ('employee', 'Employee');

-- Admin kế thừa quyền Manager
INSERT INTO IAM_GROUP_IMPLIED VALUES (1, 2);
-- Manager kế thừa quyền Employee
INSERT INTO IAM_GROUP_IMPLIED VALUES (2, 3);

-- Employee: Chỉ đọc
INSERT INTO IAM_MODEL_ACCESS (model_id, group_id, perm_read) 
VALUES (1, 3, true);

-- Manager: Đọc + Ghi
INSERT INTO IAM_MODEL_ACCESS (model_id, group_id, perm_read, perm_write) 
VALUES (1, 2, true, true);

-- Admin: Full quyền (kế thừa read+write từ Manager, tự có create+delete)
INSERT INTO IAM_MODEL_ACCESS (model_id, group_id, perm_create, perm_unlink) 
VALUES (1, 1, true, true);

Case 3: Nhân viên chỉ xem data của mình

-- Record Rule: Salesperson chỉ xem đơn hàng của mình
INSERT INTO IAM_RECORD_RULE (model_id, name, domain, perm_read, perm_write) 
VALUES (1, 'Own Sales Only', 
        '[["salesperson_id", "=", "current_user_id"]]'::jsonb, 
        true, true);

-- Gán rule cho nhóm "Salesperson"
INSERT INTO IAM_RECORD_RULE_GROUP VALUES (1, 4);

5. Lưu ý Thiết kế & Best Practices

5.1. Performance Considerations

  • Index quan trọng:

    CREATE INDEX idx_user_group ON IAM_USER_GROUP(user_id);
    CREATE INDEX idx_model_access ON IAM_MODEL_ACCESS(model_id, group_id);
    CREATE INDEX idx_record_rule_group ON IAM_RECORD_RULE_GROUP(group_id);
    CREATE INDEX idx_record_rule_domain ON IAM_RECORD_RULE USING gin(domain);
    
  • Cache effective groups: Tính toán trước và cache danh sách groups (kể cả implied) của mỗi user

5.2. Security Best Practices

  • Principle of Least Privilege: Mặc định tất cả permissions = false
  • Explicit Deny: Không có permission = deny, không cần "deny rule"
  • Audit Trail: Nên thêm các cột created_by, updated_at, updated_by cho audit

5.3. Domain Filter Guidelines

  • Hỗ trợ các operator: =, !=, >, <, >=, <=, in, not in, like, ilike
  • Hỗ trợ các biến động: current_user_id, current_company_id, current_date
  • Sử dụng AND/OR logic kết hợp:
    [
      "&",  // AND operator
      ["company_id", "=", "current_company_id"],
      "|",  // OR operator
      ["manager_id", "=", "current_user_id"],
      ["salesperson_id", "=", "current_user_id"]
    ]
    

5.4. Soft Delete

Nên implement soft delete cho tất cả các bảng phân quyền:

ALTER TABLE IAM_MODEL_ACCESS ADD COLUMN deleted_at timestamptz;
ALTER TABLE IAM_RECORD_RULE ADD COLUMN deleted_at timestamptz;

6. Migration & Seeding Strategy

6.1. Thứ tự Migration

  1. IAM_COMPANY
  2. IAM_USER
  3. IAM_USER_COMPANY
  4. IAM_GROUP
  5. IAM_USER_GROUP
  6. IAM_GROUP_IMPLIED
  7. IAM_MODEL
  8. IAM_MODEL_ACCESS
  9. IAM_RECORD_RULE
  10. IAM_RECORD_RULE_GROUP

6.2. Seed Data Cơ bản

-- 1. Tạo System Admin Group
INSERT INTO IAM_GROUP (code, name, category) 
VALUES ('system_admin', 'System Administrator', 'system');

-- 2. Tạo default company
INSERT INTO IAM_COMPANY (name) VALUES ('Default Company');

-- 3. Tạo super admin user
INSERT INTO IAM_USER (email, password_hash, company_id, is_active) 
VALUES ('admin@system.com', 'hashed_password', 1, true);

-- 4. Gán user vào System Admin group
INSERT INTO IAM_USER_GROUP VALUES (1, 1);

-- 5. Cấp full quyền cho System Admin trên tất cả models
INSERT INTO IAM_MODEL_ACCESS (model_id, group_id, perm_read, perm_write, perm_create, perm_unlink)
SELECT id, 1, true, true, true, true FROM IAM_MODEL;

7. Mở rộng & Tính năng Nâng cao

7.1. Field-Level Security (Tương lai)

Có thể mở rộng với bảng:

CREATE TABLE IAM_FIELD_ACCESS (
    id bigint PRIMARY KEY,
    model_id bigint REFERENCES IAM_MODEL(id),
    field_name text,
    group_id bigint REFERENCES IAM_GROUP(id),
    perm_read boolean,
    perm_write boolean
);

7.2. Time-Based Access Control

Thêm cột vào IAM_MODEL_ACCESS:

ALTER TABLE IAM_MODEL_ACCESS 
ADD COLUMN valid_from timestamptz,
ADD COLUMN valid_until timestamptz;

7.3. Delegation & Temporary Access

CREATE TABLE IAM_USER_DELEGATION (
    id bigint PRIMARY KEY,
    delegator_user_id bigint,
    delegate_user_id bigint,
    valid_from timestamptz,
    valid_until timestamptz
);

Hệ thống phân quyền này cung cấp khả năng kiểm soát truy cập linh hoạt từ model-level đến record-level, hỗ trợ multi-tenancy, và có khả năng mở rộng cao cho các hệ thống enterprise.


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí