Thiết kế Kiến trúc Phân quyền Dữ liệu theo Cấp Hành chính
Bài viết này phân tích chi tiết kiến trúc phân quyền dữ liệu cho các hệ thống có cấu trúc phân cấp — từ chính phủ, tập đoàn đa quốc gia, đến chuỗi bán lẻ với hàng nghìn chi nhánh.
Làm thế nào để xây dựng một hệ thống mà cấp trên có thể giám sát toàn bộ dữ liệu cấp dưới, trong khi các đơn vị ngang hàng hoàn toàn cách ly với nhau? Bài viết này phân tích chi tiết kiến trúc phân quyền dữ liệu cho các hệ thống có cấu trúc phân cấp — từ chính phủ, tập đoàn đa quốc gia, đến chuỗi bán lẻ với hàng nghìn chi nhánh.
Phần 1: Bài toán Phân quyền Phân cấp
1.1. Đặc điểm của hệ thống phân cấp

Nhiều tổ chức vận hành theo cấu trúc phân cấp (hierarchical structure): tập đoàn có công ty con, công ty con có chi nhánh, chi nhánh có phòng ban. Chính phủ có bộ ngành, tỉnh thành, quận huyện, phường xã. Chuỗi bán lẻ có vùng miền, khu vực, cửa hàng.
Điểm chung của các cấu trúc này là mối quan hệ parent-child giữa các đơn vị, tạo thành một cây (tree) với các đặc tính:
- Mỗi đơn vị (trừ root) có đúng một đơn vị cha
- Mỗi đơn vị có thể có nhiều đơn vị con
- Độ sâu của cây có thể khác nhau ở các nhánh
- Cấu trúc có thể thay đổi theo thời gian (sáp nhập, chia tách, tái cơ cấu)
1.2. Yêu cầu phân quyền đặc thù

Hệ thống phân cấp đặt ra những yêu cầu phân quyền đặc biệt mà mô hình phân quyền phẳng (flat permission model) không đáp ứng được:
Vertical Access (Truy cập dọc): Cấp trên cần xem được dữ liệu của tất cả cấp dưới trực thuộc. CEO cần xem báo cáo của toàn tập đoàn, bao gồm mọi công ty con và chi nhánh. Regional Manager cần xem dữ liệu của tất cả cửa hàng trong vùng.
Horizontal Isolation (Cách ly ngang): Các đơn vị cùng cấp không được xem dữ liệu của nhau. Chi nhánh A không được xem doanh thu của Chi nhánh B, dù cả hai cùng thuộc một công ty con. Điều này đảm bảo tính cạnh tranh công bằng và bảo mật thông tin kinh doanh.
Contextual Scope (Phạm vi ngữ cảnh): Cùng một role nhưng phạm vi khác nhau. "Branch Manager" ở Chi nhánh A chỉ quản lý Chi nhánh A; "Branch Manager" ở Chi nhánh B chỉ quản lý Chi nhánh B. Role giống nhau, quyền hạn giống nhau, nhưng dữ liệu được phép truy cập hoàn toàn khác.
Inheritance with Boundaries (Kế thừa có giới hạn): Quyền được kế thừa theo chiều dọc nhưng không lan theo chiều ngang. Country Director kế thừa tất cả quyền của Regional Manager trong phạm vi quốc gia đó, nhưng không có quyền gì ở quốc gia khác.
1.3. Tại sao phân quyền phẳng thất bại
Mô hình phân quyền phẳng gán trực tiếp permission cho user hoặc role, không có khái niệm hierarchy. Khi áp dụng cho hệ thống phân cấp, nó gặp nhiều vấn đề:
Bùng nổ số lượng role: Nếu có 10 loại role và 1.000 đơn vị, về lý thuyết cần 10.000 role riêng biệt (mỗi role-đơn vị là một combination). Thêm một loại role mới đồng nghĩa với việc tạo 1.000 role mới.
Khó bảo trì tính nhất quán: Khi đơn vị tái cơ cấu (sáp nhập, chia tách), tất cả role liên quan cần cập nhật thủ công. Sai sót dẫn đến lỗ hổng bảo mật hoặc mất quyền truy cập hợp lệ.
Không hỗ trợ kế thừa tự nhiên: Để cấp trên xem được dữ liệu cấp dưới, phải grant quyền vào từng đơn vị cấp dưới một cách thủ công. Khi thêm đơn vị mới, dễ quên grant quyền cho cấp trên.
Query phức tạp: Mỗi truy vấn dữ liệu cần kèm theo danh sách dài các đơn vị được phép, làm query phức tạp và chậm.
1.4. Quy mô và độ phức tạp

Độ phức tạp của bài toán tăng theo:
- Số lượng đơn vị: Từ vài chục đến hàng chục nghìn
- Độ sâu của cây: Từ 2-3 cấp đến 5-6 cấp hoặc hơn
- Tính động của cấu trúc: Cấu trúc cố định vs thường xuyên thay đổi
- Số lượng user: Từ vài trăm đến hàng triệu
- Yêu cầu về latency: Milliseconds cho ứng dụng realtime
- Yêu cầu compliance: Audit trail, data residency, encryption
Phần 2: Nền tảng Lý thuyết
2.1. RBAC và các biến thể theo chuẩn NIST

Role-Based Access Control (RBAC) được chuẩn hóa bởi NIST trong tiêu chuẩn ANSI/INCITS 359-2004, phân thành 4 mức độ:
RBAC0 — Core RBAC: Mô hình cơ sở với ba thành phần: Users, Roles, và Permissions. User được gán vào Role, Role được gán Permission. Đây là nền tảng nhưng thiếu khái niệm kế thừa, chưa phù hợp cho hệ thống phân cấp.
RBAC1 — Hierarchical RBAC: Bổ sung Role Hierarchy, cho phép role cấp cao thừa kế tất cả permission của role cấp thấp. Ví dụ: Senior Manager thừa kế permission của Manager, Manager thừa kế permission của Staff. Đây là mô hình phù hợp nhất cho cấu trúc tổ chức phân cấp.
Có hai loại hierarchy:
- General Hierarchy: Cho phép multiple inheritance — một role có thể kế thừa từ nhiều role khác
- Limited Hierarchy: Chỉ cho phép single inheritance — mỗi role chỉ kế thừa từ một role duy nhất, tạo thành cây đơn giản
RBAC2 — Constrained RBAC: Bổ sung các ràng buộc (constraints), quan trọng nhất là Separation of Duties (SoD):
- Static SoD: Ngăn một user đồng thời giữ các role xung đột. Ví dụ: không thể vừa là "Người tạo đơn hàng" vừa là "Người phê duyệt đơn hàng".
- Dynamic SoD: Cho phép giữ nhiều role xung đột nhưng không kích hoạt cùng lúc trong một session. User có thể có cả role "Nhập liệu" và "Phê duyệt" nhưng phải chọn một khi đăng nhập.
RBAC3 — Symmetric RBAC: Kết hợp RBAC1 và RBAC2, cung cấp đầy đủ tính năng hierarchy và constraints.
2.2. ABAC — Attribute-Based Access Control

ABAC mở rộng RBAC bằng cách đánh giá nhiều thuộc tính (attributes) khi ra quyết định phân quyền:
- Subject Attributes: Thuộc tính của người dùng — department, job title, clearance level, location
- Resource Attributes: Thuộc tính của tài nguyên — classification, owner, creation date, sensitivity level
- Environment Attributes: Thuộc tính ngữ cảnh — time of day, IP address, device type, threat level
- Action Attributes: Loại hành động — read, write, delete, approve
Policy ABAC được viết dưới dạng rules. Ví dụ:
IF subject.department = resource.owner_department
AND subject.clearance_level >= resource.sensitivity_level
AND environment.time IN business_hours
AND environment.ip_range IN corporate_network
THEN ALLOW action
ABAC mạnh mẽ và linh hoạt nhưng phức tạp trong triển khai, khó debug, và có thể ảnh hưởng performance nếu policy phức tạp. Khuyến nghị sử dụng ABAC bổ sung cho RBAC trong các trường hợp cần kiểm soát chi tiết, không thay thế hoàn toàn.
2.3. Multi-tenancy và các mô hình
Multi-tenancy là kiến trúc cho phép một hệ thống phục vụ nhiều tenant (đơn vị/tổ chức) độc lập với dữ liệu được cách ly:
Siloed Model — Database per Tenant: Mỗi tenant có database riêng biệt. Cách ly tuyệt đối, dễ customize per tenant, dễ comply với data residency requirements. Nhược điểm: chi phí infrastructure cao, khó maintain khi số tenant lớn, cross-tenant reporting phức tạp.
Bridge Model — Schema per Tenant: Các tenant dùng chung database instance nhưng mỗi tenant có schema riêng. Cân bằng giữa isolation và efficiency. Nhược điểm: số lượng schema có giới hạn trong một database, migration phức tạp.
Pooled Model — Shared Everything: Tất cả tenant dùng chung database và schema, phân biệt bằng tenant_id column. Chi phí thấp nhất, dễ scale, dễ maintain. Nhược điểm: cần cơ chế isolation mạnh ở application và database layer, một bug có thể ảnh hưởng tất cả tenant.
Khuyến nghị: Pooled Model với Row-Level Security (RLS) cho hầu hết trường hợp. Chỉ dùng Siloed Model khi có yêu cầu đặc biệt về data residency hoặc tenant cần customization sâu.
2.4. Hierarchical Multi-tenancy

Đây là extension của multi-tenancy cho cấu trúc phân cấp, nơi tenant được tổ chức thành cây thay vì flat list:
Root Tenant (Headquarters)
├── Sub-tenant: Region North
│ ├── Sub-sub-tenant: Branch A
│ ├── Sub-sub-tenant: Branch B
│ └── Sub-sub-tenant: Branch C
├── Sub-tenant: Region South
│ ├── Sub-sub-tenant: Branch D
│ └── Sub-sub-tenant: Branch E
└── Sub-tenant: Region West
└── Sub-sub-tenant: Branch F
Quy tắc truy cập:
- Parent tenant có thể xem dữ liệu của tất cả descendant tenants
- Sibling tenants (cùng cấp, cùng parent) không thể xem dữ liệu của nhau
- Child tenant không thể xem dữ liệu của parent (trừ dữ liệu được share explicitly)
Mô hình này map tự nhiên vào cấu trúc tổ chức và đơn giản hóa việc quản lý quyền: thay vì grant quyền vào từng đơn vị, chỉ cần xác định vị trí của user trong cây.
Phần 3: Thiết kế Data Model cho Hierarchy
3.1. Các cách biểu diễn cây trong database
Việc biểu diễn cấu trúc cây trong relational database có nhiều cách tiếp cận, mỗi cách có trade-off riêng:
Adjacency List: Mỗi node lưu ID của parent trực tiếp. Đây là cách đơn giản nhất và tự nhiên nhất.
Ưu điểm: Dễ hiểu, dễ implement, insert/update/delete đơn giản, integrity dễ enforce bằng foreign key.
Nhược điểm: Query để lấy tất cả descendants hoặc ancestors đòi hỏi recursive query (WITH RECURSIVE trong SQL), có thể chậm với cây sâu hoặc lớn.
Phù hợp khi: Cây không quá sâu (< 10 levels), chủ yếu query parent-child trực tiếp, cấu trúc thay đổi thường xuyên.
Nested Set: Mỗi node lưu hai giá trị: left và right. Tất cả descendants của một node có left/right nằm trong khoảng (parent.left, parent.right).
Ưu điểm: Query descendants cực nhanh (chỉ cần điều kiện BETWEEN), không cần recursion.
Nhược điểm: Insert/update/delete rất chậm vì phải cập nhật left/right của nhiều nodes khác. Concurrent modification phức tạp.
Phù hợp khi: Cây ít thay đổi, query descendants rất thường xuyên, có thể accept write performance trade-off.
Closure Table: Lưu tất cả cặp ancestor-descendant trong một bảng riêng, kèm theo depth. Ví dụ: nếu A → B → C, bảng closure chứa (A,A,0), (A,B,1), (A,C,2), (B,B,0), (B,C,1), (C,C,0).
Ưu điểm: Query ancestors và descendants đều nhanh, không cần recursion. Hỗ trợ tốt cho các query phức tạp như "tất cả nodes ở depth 2 từ node X".
Nhược điểm: Tốn không gian lưu trữ (O(n²) trong worst case). Insert/delete cần cập nhật nhiều rows trong closure table.
Phù hợp khi: Cần query cả ancestors và descendants thường xuyên, cây không quá lớn, có thể accept storage trade-off.
Materialized Path: Mỗi node lưu đường dẫn từ root đến chính nó, thường dưới dạng string (ví dụ: "/1/5/12/") hoặc array (ví dụ: [1, 5, 12]).
Ưu điểm: Query descendants dễ dàng (LIKE '/1/5/%' hoặc array contains). Insert đơn giản (chỉ cần biết path của parent). Có thể index hiệu quả.
Nhược điểm: Move subtree đòi hỏi update path của tất cả descendants. Path có thể dài với cây sâu.
Phù hợp khi: Cây ít thay đổi cấu trúc (move/reparent hiếm), query descendants thường xuyên, cần balance giữa read và write performance.
3.2. Khuyến nghị: Materialized Path với Array
Đối với hệ thống phân cấp hành chính/tổ chức, Materialized Path sử dụng array là lựa chọn tối ưu vì:
- Cấu trúc tổ chức thay đổi không thường xuyên (vài lần/năm)
- Query "tất cả đơn vị con của X" rất phổ biến (cho phân quyền)
- PostgreSQL và các database hiện đại hỗ trợ array với GIN index hiệu quả
- Dễ kết hợp với RLS policies
Thiết kế bảng organizational_units:
| Column | Type | Description |
|---|---|---|
| id | UUID | Primary key |
| code | VARCHAR | Mã đơn vị (unique) |
| name | VARCHAR | Tên đơn vị |
| level | ENUM | Cấp độ (headquarters, region, branch, ...) |
| parent_id | UUID | FK đến parent (nullable cho root) |
| ancestor_path | UUID[] | Mảng ID từ root đến parent |
| created_at | TIMESTAMP | Thời điểm tạo |
| updated_at | TIMESTAMP | Thời điểm cập nhật |
Ví dụ dữ liệu:
| id | name | level | parent_id | ancestor_path |
|---|---|---|---|---|
| uuid-1 | Headquarters | hq | null | [] |
| uuid-2 | Region North | region | uuid-1 | [uuid-1] |
| uuid-3 | Branch A | branch | uuid-2 | [uuid-1, uuid-2] |
| uuid-4 | Branch B | branch | uuid-2 | [uuid-1, uuid-2] |
Query tất cả descendants của Region North (uuid-2):
SELECT * FROM organizational_units
WHERE uuid-2 = ANY(ancestor_path);
Query này trả về Branch A và Branch B — tất cả các đơn vị có uuid-2 trong ancestor_path.
3.3. Indexing Strategy
Để đảm bảo performance với dataset lớn:
GIN Index cho ancestor_path: Cho phép query "X = ANY(ancestor_path)" chạy nhanh.
B-tree Index cho parent_id: Cho query parent-child trực tiếp.
Composite Index cho (level, parent_id): Cho query "tất cả branches thuộc region X".
Partial Index cho active records: Nếu có soft-delete, index chỉ các records active.
3.4. Handling Structural Changes
Khi cấu trúc thay đổi (move đơn vị từ parent này sang parent khác), cần cập nhật ancestor_path của đơn vị đó và tất cả descendants:
Bước 1: Tính ancestor_path mới = [new_parent.ancestor_path, new_parent.id]
Bước 2: Với mỗi descendant, thay thế phần prefix cũ bằng prefix mới trong ancestor_path
Lưu ý: Đây là operation nặng nếu subtree lớn. Nên thực hiện trong off-peak hours, có thể cần batch processing và progress tracking.
Phần 4: Row-Level Security — Lớp Bảo vệ Cuối cùng

4.1. Tại sao cần RLS
Application-level authorization kiểm tra quyền trong code ứng dụng. Đây là cách phổ biến nhưng có điểm yếu:
- Bug trong code: Một developer quên thêm kiểm tra quyền vào API mới
- SQL Injection: Attacker bypass application layer, truy cập database trực tiếp
- Direct Database Access: DBA, BI tools, hoặc hacker có credentials truy cập database
- Microservices Complexity: Nhiều services truy cập cùng database, khó đảm bảo tất cả đều kiểm tra quyền đúng
Row-Level Security (RLS) là tính năng của database (PostgreSQL, SQL Server, Oracle) cho phép định nghĩa policy kiểm soát truy cập ở mức row. Policy được database engine enforce, không thể bypass từ application.
Defense in Depth: Dù application có bug, dù attacker có SQL injection, database vẫn chỉ trả về những rows mà current user được phép xem. RLS là lớp bảo vệ cuối cùng, không thay thế application-level checks nhưng bổ sung thêm một lớp an toàn.
4.2. Cách hoạt động của RLS
Bước 1 — Enable RLS trên bảng: Mặc định RLS tắt. Khi bật, tất cả queries trên bảng đó sẽ được filter qua policies.
Bước 2 — Định nghĩa Policy: Policy là một expression trả về boolean, xác định row nào được phép truy cập. Policy có thể áp dụng cho SELECT, INSERT, UPDATE, DELETE riêng biệt hoặc tất cả.
Bước 3 — Set Session Context: Application set các biến session (ví dụ: current_user_id, current_org_unit_id) trước khi execute queries. Policy sử dụng các biến này để filter.
Bước 4 — Query Execution: Database tự động thêm điều kiện từ policy vào WHERE clause của mọi query. User không cần (và không thể) thay đổi điều này.
4.3. Thiết kế Policy cho Hierarchical Access
Với mô hình ancestor_path đã thiết kế, policy cho phép hierarchical access:
Logic: User thuộc đơn vị X được phép truy cập record nếu:
- Record thuộc chính đơn vị X, HOẶC
- Đơn vị X nằm trong ancestor_path của đơn vị sở hữu record (tức X là ancestor của đơn vị đó)
Diễn giải:
- Cán bộ Branch A (uuid-3) chỉ xem được records có org_unit_id = uuid-3
- Manager Region North (uuid-2) xem được records có org_unit_id = uuid-2, uuid-3, uuid-4 (region và tất cả branches thuộc region)
- Executive Headquarters (uuid-1) xem được tất cả records
4.4. Session Context Management
RLS policy cần biết "current user thuộc đơn vị nào". Thông tin này được truyền qua session variables:
Trong PostgreSQL: Sử dụng SET và current_setting():
-- Application set context sau khi xác thực user
SET LOCAL app.current_user_id = 'user-uuid';
SET LOCAL app.current_org_unit_id = 'uuid-3';
-- Policy đọc context
current_setting('app.current_org_unit_id', true)
Lưu ý quan trọng:
- Sử dụng
SET LOCAL(chỉ có hiệu lực trong transaction) thay vìSET(có hiệu lực trong session) để tránh context leak giữa các requests - Luôn set context ở đầu mỗi transaction
- Xử lý trường hợp context chưa được set (default deny)
4.5. Performance Considerations
RLS policy được evaluate cho mỗi row, có thể ảnh hưởng performance:
Đảm bảo policy sử dụng indexed columns: Nếu policy check org_unit_id = ANY(...), cần index trên org_unit_id.
Tránh function calls phức tạp trong policy: Mỗi row đều gọi function đó. Nếu function query database, sẽ có N+1 problem.
Sử dụng STABLE/IMMUTABLE functions: Cho phép PostgreSQL cache kết quả trong một query.
Consider materialized permissions: Thay vì tính toán permissions realtime, có thể pre-compute và lưu vào bảng riêng, policy chỉ cần lookup.
4.6. Bypass RLS cho Admin Operations
Một số trường hợp cần bypass RLS:
- System migrations
- Batch processing jobs
- Reporting across all tenants
- Emergency access
Cách an toàn:
- Tạo separate database role với BYPASSRLS privilege
- Role này chỉ được sử dụng bởi specific service accounts
- Mọi access bằng role này được log chi tiết
- Regular audit để đảm bảo role không bị lạm dụng
Phần 5: Role Hierarchy và Permission Design
5.1. Tách biệt Role và Scope
Một sai lầm phổ biến là gộp role và scope vào cùng một entity. Ví dụ: tạo role "Branch_A_Manager", "Branch_B_Manager", "Region_North_Manager". Điều này dẫn đến bùng nổ số lượng roles.
Thiết kế tốt hơn: Tách role (chức năng) và scope (phạm vi):
| User | Role | Scope (Org Unit) |
|---|---|---|
| Alice | Manager | Branch A |
| Bob | Manager | Branch B |
| Carol | Manager | Region North |
| Dave | Analyst | Headquarters |
Cùng role "Manager" nhưng scope khác nhau. Permission của Manager được định nghĩa một lần, scope xác định dữ liệu nào được phép truy cập.
5.2. Role Hierarchy Design
Functional Roles (theo chức năng):
| Role | Description | Typical Permissions |
|---|---|---|
| Viewer | Xem dữ liệu, báo cáo | Read |
| Operator | Xử lý nghiệp vụ hàng ngày | Read, Create, Update |
| Manager | Quản lý team, phê duyệt | Read, Create, Update, Approve |
| Administrator | Quản lý cấu hình | Read, Create, Update, Delete, Configure |
| Auditor | Kiểm toán | Read (bao gồm audit logs) |
Role Inheritance:
Administrator
↓ inherits
Manager
↓ inherits
Operator
↓ inherits
Viewer
Administrator tự động có tất cả permissions của Manager, Operator, và Viewer.
Auditor thường không nằm trong hierarchy này vì có quyền đặc biệt (xem audit logs) nhưng không có quyền modify.
5.3. Permission Granularity
Permissions có thể được định nghĩa ở nhiều mức độ chi tiết:
Coarse-grained (thô):
records:read— đọc tất cả loại recordsrecords:write— tạo/sửa tất cả loại records
Fine-grained (chi tiết):
customer_records:readcustomer_records:createcustomer_records:updatecustomer_records:deletefinancial_records:readfinancial_records:approve
Khuyến nghị: Bắt đầu với coarse-grained, refine khi có nhu cầu thực tế. Over-engineering permissions từ đầu dẫn đến hệ thống phức tạp, khó quản lý.
5.4. Separation of Duties Implementation
SoD ngăn chặn fraud và errors bằng cách yêu cầu nhiều người tham gia vào một quy trình.
Static SoD — Conflicting Roles:
| Role A | Role B | Reason |
|---|---|---|
| Requester | Approver | Không tự phê duyệt yêu cầu của mình |
| Data Entry | Auditor | Auditor phải độc lập |
| Developer | Deployer | Tách biệt dev và ops |
Khi gán role cho user, kiểm tra xem user đã có role xung đột chưa. Nếu có, từ chối gán.
Dynamic SoD — Conflicting Activations:
Cho phép user giữ nhiều roles nhưng chỉ active một role tại một thời điểm. Ví dụ: User có thể có role "Data Entry" và "Reviewer", nhưng khi đăng nhập phải chọn một. Điều này cho phép flexibility (cùng một người có thể làm nhiều việc) trong khi vẫn đảm bảo một transaction cụ thể không bị một người kiểm soát hoàn toàn.
Transaction-based SoD:
Kiểm tra trong từng transaction. Ví dụ: Order có trường created_by và approved_by. System enforce approved_by != created_by. Người tạo đơn không thể là người phê duyệt chính đơn đó.
Phần 6: Handling Sensitive Data
6.1. Data Classification
Không phải tất cả dữ liệu đều cần mức bảo vệ như nhau. Phân loại dữ liệu là bước đầu tiên:
| Classification | Examples | Protection Level |
|---|---|---|
| Public | Company name, public announcements | Minimal |
| Internal | Internal memos, org charts | Standard access control |
| Confidential | Financial reports, customer lists | Restricted access, audit logging |
| Sensitive | PII, health records, salary info | Encryption, strict access, detailed audit |
| Restricted | Trade secrets, M&A plans | Need-to-know basis, special approval |
6.2. Field-Level Access Control
RLS kiểm soát ở mức row. Nhưng đôi khi cần kiểm soát ở mức field (column):
Ví dụ: Bảng employees có các cột: id, name, email, department, salary, ssn. HR có thể xem tất cả. Manager có thể xem id, name, email, department của team mình nhưng không xem salary và ssn.
Implementation approaches:
View-based: Tạo các views với subset of columns cho từng role. Manager query view không có salary/ssn.
Application-level projection: Application chỉ SELECT các columns mà role được phép xem.
Column-level encryption: Encrypt các columns nhạy cảm, chỉ decrypt cho roles có key.
Dynamic data masking: Database trả về masked values (ví dụ: xxx-xx-1234 cho SSN) cho roles không có full access.
6.3. Encryption Strategies
Encryption at Rest: Encrypt toàn bộ database files trên disk. Bảo vệ khỏi physical access (theft, improper disposal). Transparent với application — không cần thay đổi code.
Transparent Data Encryption (TDE): Database tự động encrypt khi write, decrypt khi read. Bảo vệ data files và backups. Không bảo vệ khỏi authorized database users.
Application-Level Encryption: Application encrypt trước khi gửi đến database, decrypt sau khi nhận. Bảo vệ khỏi database admins và anyone có database access. Nhược điểm: không thể query trên encrypted data (trừ khi dùng searchable encryption).
Column-Level Encryption: Chỉ encrypt specific columns. Cân bằng giữa security và usability. Có thể query trên non-encrypted columns.
Khuyến nghị cho sensitive data:
- Encryption at Rest: Luôn bật (baseline protection)
- Application-Level Encryption: Cho highly sensitive fields (SSN, health data)
- Column-Level Encryption: Cho moderately sensitive fields cần occasional search
6.4. Key Management
Encryption chỉ mạnh bằng key management:
Key Storage: Không bao giờ lưu keys trong code, config files, hoặc cùng database với encrypted data. Sử dụng dedicated Key Management System (KMS) như AWS KMS, HashiCorp Vault, Azure Key Vault.
Key Rotation: Thay đổi keys định kỳ (ví dụ: hàng năm) và khi có sự cố (key có thể bị lộ). Re-encrypt data với key mới.
Key Hierarchy: Master Key encrypt Data Keys. Data Keys encrypt actual data. Nếu cần rotate, chỉ cần re-encrypt Data Keys với Master Key mới, không cần re-encrypt tất cả data.
Access to Keys: Principle of least privilege. Chỉ services cần decrypt mới có access to keys. Audit mọi key access.
Phần 7: Audit Trail và Compliance
7.1. What to Log
Audit logging cần capture đủ thông tin để trả lời: Who did What to Which resource, When, from Where, and Why (if available).
Authentication Events:
- Login success/failure
- Logout
- Password change/reset
- MFA events
- Session timeout
Authorization Events:
- Access granted
- Access denied
- Privilege escalation attempts
- Role/permission changes
Data Access Events:
- Read sensitive data (mandatory)
- Read normal data (optional, based on requirements)
- Create records
- Update records (with before/after values)
- Delete records
Configuration Changes:
- System settings modified
- User/role management
- Policy changes
- Integration configurations
Anomalies:
- Unusual access patterns
- Multiple failed attempts
- Access from new location/device
- Bulk data access
7.2. Log Entry Structure
Mỗi log entry cần chứa:
| Field | Description | Example |
|---|---|---|
| timestamp | Thời điểm (với timezone) | 2025-01-15T14:30:00Z |
| event_id | Unique identifier | uuid |
| event_type | Loại sự kiện | DATA_ACCESS |
| action | Hành động cụ thể | READ |
| actor_id | User thực hiện | user-uuid |
| actor_role | Role hiện tại | manager |
| actor_org_unit | Đơn vị của user | branch-uuid |
| resource_type | Loại tài nguyên | customer_record |
| resource_id | ID tài nguyên | record-uuid |
| resource_org_unit | Đơn vị sở hữu | branch-uuid |
| result | Kết quả | SUCCESS/DENIED |
| ip_address | IP nguồn | 192.168.1.100 |
| user_agent | Client info | Mozilla/5.0... |
| session_id | Session identifier | session-uuid |
| details | Thông tin bổ sung | JSON object |
7.3. Log Integrity and Retention
Immutability: Audit logs phải immutable — không ai được phép modify hoặc delete. Sử dụng:
- Write-once storage (WORM)
- Append-only tables với triggers ngăn update/delete
- Blockchain-based verification
- Regular hash chains để detect tampering
Retention Policy:
- Active storage: 90 days (quick access for investigations)
- Archive storage: 1-7 years (depending on compliance requirements)
- Define clear policy và automate archival/purging
Access Control for Logs:
- Separate permission cho audit log access
- Chỉ security/compliance team có access
- Log access to audit logs (meta-auditing)
7.4. Monitoring và Alerting
Logs không có giá trị nếu không ai xem. Implement:
Real-time Alerts:
- Multiple failed login attempts → Possible brute force
- Access denied spikes → Possible unauthorized access attempt
- Bulk data access → Possible data exfiltration
- Access from unusual location → Possible compromised account
- Off-hours access to sensitive data → Needs investigation
Periodic Reviews:
- Weekly: Review access denied patterns
- Monthly: Review role assignments, look for over-privileged accounts
- Quarterly: Full access review, remove unused permissions
- Annually: Policy review, update based on organizational changes
Phần 8: Integration Patterns
8.1. Identity Provider Integration
Hầu hết organizations đã có Identity Provider (IdP) như Active Directory, Okta, Auth0, Keycloak. Hệ thống phân quyền cần integrate:
SAML 2.0: Standard cho enterprise SSO. IdP authenticate user, gửi SAML Assertion chứa user identity và attributes. Service Provider (ứng dụng) trust assertion và create session.
OAuth 2.0 / OpenID Connect: Modern standard, phổ biến cho web và mobile. IdP issue JWT tokens chứa claims về user. Application validate token và extract claims.
Claims to Include in Token:
- sub: User identifier
- roles: Array of role names
- org_unit_id: Primary organizational unit
- org_unit_path: Full path từ root đến org unit
- permissions: (Optional) Explicit permissions nếu không derive từ roles
8.2. API Gateway và Authorization
API Gateway là điểm đầu vào cho tất cả API requests. Đây là nơi lý tưởng để implement authorization layer đầu tiên:
Token Validation: Verify JWT signature, check expiration, validate issuer.
Coarse-grained Authorization: Check user có permission gọi API này không (based on roles in token).
Rate Limiting: Ngăn abuse, có thể differentiate limits by role.
Context Injection: Extract claims từ token, inject vào request headers để downstream services sử dụng.
Lưu ý: API Gateway chỉ nên làm coarse-grained checks. Fine-grained authorization (ví dụ: user có quyền access record cụ thể này không) nên để application và database (RLS) xử lý.
8.3. Service-to-Service Authorization
Trong microservices architecture, services gọi lẫn nhau. Cần authorize các calls này:
Service Accounts: Mỗi service có identity riêng (service account). Khi Service A gọi Service B, Service B verify identity của A và check A có permission không.
Token Propagation: User's token được forward từ service này sang service khác. Downstream service authorize based on original user's permissions.
Hybrid Approach: Kết hợp cả hai. Service A call Service B with both: Service A's identity (for service-level auth) và User's token (for user-level auth). Service B check cả hai.
8.4. Caching Authorization Decisions
Authorization checks có thể expensive, đặc biệt với ABAC phức tạp. Caching giúp improve performance:
Permission Cache: Cache kết quả "user X có permission Y không" trong short TTL (vài phút). Invalidate khi role/permission thay đổi.
Policy Decision Cache: Cache kết quả của complex ABAC policies. Key = hash của all attributes involved.
Negative Caching: Cache cả kết quả "denied". Nhưng cẩn thận với false negatives nếu permission mới được grant.
Cache Invalidation Strategy:
- Time-based: TTL ngắn (1-5 phút)
- Event-based: Invalidate khi role assignment thay đổi
- Hybrid: TTL + event-based invalidation
Phần 9: Testing và Validation
9.1. Authorization Testing Pyramid
Unit Tests: Test individual permission check functions. Given user with role X, can they perform action Y? Test edge cases: null inputs, invalid roles, expired sessions.
Integration Tests: Test end-to-end từ API đến database. Verify RLS policies work correctly. Test with real database, not mocks.
Negative Tests: Quan trọng không kém positive tests. Verify user CANNOT access what they shouldn't. Dễ bị bỏ qua nhưng critical for security.
Cross-tenant Tests: Verify data isolation. User of Tenant A query → should never return Tenant B data.
Privilege Escalation Tests: Attempt to bypass authorization. Try direct database access, manipulate tokens, forge session context.
9.2. Test Scenarios for Hierarchical Access
Scenario 1: Vertical Access
- User ở Region level query → Should return Region data + all Branch data under that Region
- User ở Branch level query → Should return only that Branch's data
Scenario 2: Horizontal Isolation
- User ở Branch A query → Should NOT return Branch B data
- User ở Region North query → Should NOT return Region South data
Scenario 3: Move Operations
- Branch moved from Region North to Region South
- User ở Region North query → Should NOT return moved Branch data anymore
- User ở Region South query → Should return moved Branch data
Scenario 4: Multi-org Users
- User belongs to multiple org units (rare but possible)
- Query should return union of data from all assigned units
9.3. Security Testing
Penetration Testing:
- Hire external team hoặc use tools như OWASP ZAP, Burp Suite
- Focus on authorization bypass vulnerabilities
- Test token manipulation, parameter tampering, direct object references
Code Review:
- Review tất cả authorization-related code
- Check for missing authorization checks in new endpoints
- Verify RLS policies cover all tables with sensitive data
Configuration Audit:
- Review role definitions và assignments
- Look for over-privileged accounts
- Check for orphaned permissions (assigned but not used)
Phần 10: Operational Considerations
10.1. Deployment Strategy
Phased Rollout:
Phase 1 — Foundation (Month 1-2):
- Deploy org unit hierarchy model
- Implement basic RBAC với role inheritance
- Enable RLS on critical tables
Phase 2 — Hardening (Month 3-4):
- Comprehensive audit logging
- SoD constraints
- Encryption for sensitive data
- Security testing
Phase 3 — Advanced (Month 5-6):
- ABAC cho complex scenarios
- Field-level access control
- Self-service permission requests
- Analytics và anomaly detection
10.2. Handling Edge Cases
User with No Org Unit: Default deny. User phải được assign org unit trước khi access data.
User with Multiple Org Units: Union of accessible data. Cần careful design để avoid confusion.
Org Unit Restructuring: Plan downtime hoặc implement gradual migration. Communicate changes.
Emergency Access: "Break glass" procedure cho emergencies. Heavily logged, requires justification, auto-expires.
Orphaned Data: Data thuộc org unit đã bị delete. Define policy: archive, migrate, hoặc delete.
10.3. Monitoring và Health Checks
Metrics to Track:
- Authorization decision latency (P50, P95, P99)
- Cache hit rate
- Số lượng access denied events
- RLS policy execution time
- Session context set failures
Health Checks:
- RLS policies enabled on all required tables
- Session context properly set for all requests
- IdP connectivity
- Audit log ingestion rate
Alerting Thresholds:
- Authorization latency > 100ms (P95)
- Access denied rate spike > 200% baseline
- RLS policy evaluation errors
- Audit log gaps
10.4. Disaster Recovery
Backup Requirements:
- Database with RLS policies
- IdP configuration (roles, groups, mappings)
- Application authorization configuration
- Audit logs (critical for compliance)
Recovery Procedures:
- Restore database và verify RLS policies intact
- Verify IdP sync
- Test authorization với known scenarios
- Review audit logs for gaps
Emergency Procedures:
- Procedure to revoke all access quickly (trong trường hợp breach)
- Procedure to restore specific user's access
- Escalation contacts
Kết luận: Principles to Remember
Xây dựng hệ thống phân quyền cho cấu trúc phân cấp là bài toán phức tạp nhưng có thể giải quyết với các nguyên tắc đúng:
Defense in Depth: Không tin tưởng hoàn toàn vào bất kỳ layer nào. API Gateway + Application + Database RLS tạo nhiều lớp bảo vệ.
Least Privilege: Grant quyền tối thiểu cần thiết. Dễ thêm quyền hơn là thu hồi quyền đã grant.
Separation of Concerns: Tách role (chức năng) và scope (phạm vi). Role định nghĩa "làm được gì", scope định nghĩa "làm ở đâu".
Hierarchical Inheritance: Tận dụng cấu trúc cây để tự động derive quyền. Cấp trên inherit quyền view của cấp dưới.
Horizontal Isolation: Các đơn vị ngang hàng phải hoàn toàn cách ly. Không có con đường truy cập sang sibling.
Audit Everything: Log đủ chi tiết để reconstruct "ai làm gì, khi nào, ở đâu". Đây là requirement cho compliance và investigation.
Test Negative Cases: Verify không chỉ "có thể access" mà cả "không thể access". Negative tests thường bị bỏ qua nhưng critical.
Plan for Change: Cấu trúc tổ chức sẽ thay đổi. Design cho flexibility: easy to add units, move units, restructure hierarchy.
Với các nguyên tắc này và kiến trúc đề xuất (Hierarchical RBAC + Sub-tenant + RLS + Comprehensive Audit), bạn có thể xây dựng hệ thống phân quyền robust, scalable, và secure cho bất kỳ tổ chức phân cấp nào.
Code Demo tại đây https://github.com/xdev-asia-labs/spring-multitenant-rbac
