Skip to content

Commit d2afb27

Browse files
committed
重构权限模型,使用映射结构替代对象定义,更新相关接口和测试用例
1 parent ffe9849 commit d2afb27

File tree

4 files changed

+48
-34
lines changed

4 files changed

+48
-34
lines changed

docs/guide/security-guide.md

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -19,16 +19,19 @@ The security configuration supports both **Role-Based Access Control (RBAC)** an
1919

2020
## 2. Policy Definition (`.policy.yml`)
2121

22-
A **Policy** is a reusable collection of permission statements without being tied to a specific user identity. It defines "what can be done".
22+
A **Policy** is a reusable collection of permission statements without being tied to a specific user identity.
23+
24+
To facilitate storage in database JSONB columns and efficient querying, the structure uses a **Map** keyed by object name.
2325

2426
**File:** `/security/policies/contract_manage.policy.yml`
2527

2628
```yaml
2729
name: contract_manage
2830
description: Standard contract management rules
2931

30-
statements:
31-
- object: contracts
32+
# Map structure: Key is the Object Name
33+
permissions:
34+
contracts:
3235
actions: [read, create, update] # No delete permission
3336

3437
# Row Level Security (RLS)
@@ -43,7 +46,7 @@ statements:
4346
4447
## 3. Role Definition (`.role.yml`)
4548

46-
A **Role** defines an identity and assigns permissions. It can compose permissions by referencing **Managed Policies** or defining **Inline Policies**.
49+
A **Role** defines an identity and assigns permissions. It can compose permissions by referencing **Managed Policies** or defining **Online Permissions**.
4750

4851
**File:** `/security/roles/sales_rep.role.yml`
4952

@@ -58,10 +61,10 @@ policies:
5861
- contract_manage # References contract_manage.policy.yml
5962
- base_read_only # References base_read_only.policy.yml
6063
61-
# 2. Inline Policies (Specific)
64+
# 2. Inline Permissions (Specific)
6265
# Valid specifically for this role, not shared
63-
inline_policies:
64-
- object: leads
66+
permissions:
67+
leads:
6568
actions: [read, create]
6669
filters:
6770
- ['status', '=', 'new']
@@ -71,7 +74,7 @@ inline_policies:
7174

7275
When a user with the role `sales_rep` executes a query:
7376
1. The system loads all referenced **Managed Policies**.
74-
2. The system loads all **Inline Policies**.
75-
3. All policies are **merged (Union)**.
77+
2. The system loads all **Inline Permissions**.
78+
3. All permissions are **merged (Union)**.
7679
* If *any* policy allows access to an object, access is granted.
7780
* If multiple policies define RLS filters for the same object, they are typically combined with `OR`.

packages/core/src/metadata.ts

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -104,9 +104,6 @@ export interface FieldConfig {
104104
* Defines a permission rule for a specific object.
105105
*/
106106
export interface PolicyStatement {
107-
/** The object API name. Use '*' (wildcard) carefully. */
108-
object: string;
109-
110107
/** Allowed actions. */
111108
actions: Array<'read' | 'create' | 'update' | 'delete' | '*'>;
112109

@@ -129,7 +126,8 @@ export interface PolicyStatement {
129126
export interface PolicyConfig {
130127
name: string;
131128
description?: string;
132-
statements: PolicyStatement[];
129+
/** Map of Object Name to Permission Rules */
130+
permissions: Record<string, PolicyStatement>;
133131
}
134132

135133
/**
@@ -141,8 +139,8 @@ export interface RoleConfig {
141139
description?: string;
142140
/** List of policy names to include. */
143141
policies?: string[];
144-
/** Specific rules defined directly in this role. */
145-
inline_policies?: PolicyStatement[];
142+
/** Map of inline permissions. */
143+
permissions?: Record<string, PolicyStatement>;
146144
}
147145

148146
export interface ActionConfig {

packages/core/src/security.ts

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -36,17 +36,27 @@ export class SecurityEngine {
3636
if (role.policies) {
3737
for (const policyName of role.policies) {
3838
const policy = this.policies.get(policyName);
39-
if (policy) {
40-
const statements = policy.statements.filter(s => s.object === objectName || s.object === '*');
41-
effectiveStatements.push(...statements);
39+
if (policy && policy.permissions) {
40+
// Check for specific object permission
41+
if (policy.permissions[objectName]) {
42+
effectiveStatements.push(policy.permissions[objectName]);
43+
}
44+
// Check for wildcard object permission
45+
if (policy.permissions['*']) {
46+
effectiveStatements.push(policy.permissions['*']);
47+
}
4248
}
4349
}
4450
}
4551

4652
// Inline Policies
47-
if (role.inline_policies) {
48-
const statements = role.inline_policies.filter(s => s.object === objectName || s.object === '*');
49-
effectiveStatements.push(...statements);
53+
if (role.permissions) {
54+
if (role.permissions[objectName]) {
55+
effectiveStatements.push(role.permissions[objectName]);
56+
}
57+
if (role.permissions['*']) {
58+
effectiveStatements.push(role.permissions['*']);
59+
}
5060
}
5161
}
5262

packages/core/test/security.test.ts

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ describe('SecurityEngine', () => {
1616
});
1717

1818
it('should deny access if role has no permission', () => {
19-
const role: RoleConfig = { name: 'guest', inline_policies: [] };
19+
const role: RoleConfig = { name: 'guest', permissions: {} };
2020
security.registerRole(role);
2121

2222
const ctx: any = { roles: ['guest'], object: {} as any, transaction: {} as any, sudo: () => ctx };
@@ -27,9 +27,9 @@ describe('SecurityEngine', () => {
2727
it('should allow access via inline policy', () => {
2828
const role: RoleConfig = {
2929
name: 'admin',
30-
inline_policies: [
31-
{ object: 'project', actions: ['read'] }
32-
]
30+
permissions: {
31+
project: { actions: ['read'] }
32+
}
3333
};
3434
security.registerRole(role);
3535

@@ -41,9 +41,9 @@ describe('SecurityEngine', () => {
4141
it('should allow access via managed policy', () => {
4242
const policy: PolicyConfig = {
4343
name: 'read_access',
44-
statements: [
45-
{ object: 'project', actions: ['read'] }
46-
]
44+
permissions: {
45+
project: { actions: ['read'] }
46+
}
4747
};
4848
const role: RoleConfig = {
4949
name: 'viewer',
@@ -60,13 +60,12 @@ describe('SecurityEngine', () => {
6060
it('should return RLS filters', () => {
6161
const policy: PolicyConfig = {
6262
name: 'owner_only',
63-
statements: [
64-
{
65-
object: 'project',
63+
permissions: {
64+
project: {
6665
actions: ['read'],
6766
filters: [['owner', '=', '$user.id']]
6867
}
69-
]
68+
}
7069
};
7170
const role: RoleConfig = {
7271
name: 'user',
@@ -86,12 +85,16 @@ describe('SecurityEngine', () => {
8685
// Policy 1: Own projects
8786
const p1: PolicyConfig = {
8887
name: 'own_projects',
89-
statements: [{ object: 'project', actions: ['read'], filters: [['owner', '=', 'me']] }]
88+
permissions: {
89+
project: { actions: ['read'], filters: [['owner', '=', 'me']] }
90+
}
9091
};
9192
// Policy 2: Public projects
9293
const p2: PolicyConfig = {
9394
name: 'public_projects',
94-
statements: [{ object: 'project', actions: ['read'], filters: [['public', '=', true]] }]
95+
permissions: {
96+
project: { actions: ['read'], filters: [['public', '=', true]] }
97+
}
9598
};
9699

97100
const role: RoleConfig = {

0 commit comments

Comments
 (0)