Introduction
Enterprise applications contain highly sensitive data. Not every user is allowed to see every row or column. In many systems, permissions are hardcoded inside service classes or SQL queries. This becomes a maintenance burden when business rules change frequently.
A Dynamic Row and Column Permission System solves this problem by:
Decoupling permissions from code
Loading access rules from database
Enforcing restrictions at API level
Applying field-level masking at run-time
Allowing admin users to define rules without deployment
Providing audit logs for compliance
This article explains a complete implementation using:
Angular front-end
.NET backend (Web API + Middleware + Rule Engine)
SQL Server metadata store
It also includes workflow diagrams, flowcharts, and production-ready patterns.
The goal is to design a system that can scale across departments, tenants, and modules without code changes.
High-level requirements
A dynamic permission system must support:
Row-level security
Example: A Sales Executive should only see documents of their region.
Example: A Manager can see all employees under their business unit.
Column-level security
Example: A support user should not see salary column.
Example: A vendor user should not see internal cost price.
Field masking
Show partial values like:
Phone: 98XXXX432
Email: j***@company.com
Rule-based evaluation
Conditions defined using attributes like Role, Department, Location, Grade, Ownership.
Versioned rules
Update rules without affecting existing sessions.
Caching layer
Improves performance for large rule sets.
Audit logging
Who accessed what data and using which permission.
Overall architecture
Angular UI
|
v
.NET API
|
v
Rule Engine (Row Rules + Column Rules)
|
v
Data Access Layer
|
v
SQL Server (Data + Permission Metadata)
Workflow diagram (end-to-end flow)
+---------------+
| Angular App |
| Makes Request|
+-------+-------+
|
v
+---------------------+
| .NET API Controller |
+---------+-----------+
|
v
+---------------------------+
| Permission Rule Evaluator |
+------+--------------------+
| Row Filter
| Column Filter
v
+-------------------------+
| Data Access + SQL Query |
+-----------+-------------+
|
v
+--------------------------+
| Filtered Result Returned |
+--------------------------+
Flowchart (permission evaluation)
+----------------------+
| Incoming API Request |
+----------+-----------+
|
v
+----------------------+
| Identify User Roles |
+----------+-----------+
|
v
+-------------------------------+
| Load Row + Column Rules |
| From Database / Cache |
+---------------+---------------+
|
v
+--------------------------+
| Apply Row Filter |
| Create SQL Predicate |
+-------------+------------+
|
v
+--------------------------+
| Fetch Data from SQL |
+-------------+------------+
|
v
+--------------------------+
| Apply Column-Level Rules |
| Remove/Mask Columns |
+-------------+------------+
|
v
+--------------------------+
| Send Secured Response |
+--------------------------+
Data model design
table: PermissionRuleSet
Stores metadata for each module or table.
RuleSetId INT PK
ModuleName VARCHAR(200)
EntityName VARCHAR(200)
IsActive BIT
Version INT
table: RowPermissionRule
Defines row-level access conditions.
RuleId INT PK
RuleSetId INT FK
LeftOperand VARCHAR(200) -- Column Name
Operator VARCHAR(20) -- =, !=, IN, LIKE, etc.
RightOperand VARCHAR(500) -- Value or token like {User.RegionId}
LogicGroup VARCHAR(5) -- AND/OR
table: ColumnPermissionRule
Defines which fields to hide or mask.
RuleId INT PK
RuleSetId INT FK
ColumnName VARCHAR(200)
AccessType VARCHAR(20) -- FULL, HIDDEN, MASK
MaskPattern VARCHAR(100) -- ***####*, etc.
table: PermissionAssignment
Maps which roles or users get which rule sets.
AssignmentId INT PK
RuleSetId INT FK
RoleName VARCHAR(200)
UserId INT NULL

Join the conversation! Your thoughts help the community grow.