Skip to content

Commit da0b62c

Browse files
Merge pull request #1679 from rocket-admin/fix/cedar-policy-wildcard-generation
fix: use action in [...] and remove resource filter for all-tables/al…
2 parents b4dfb28 + d815756 commit da0b62c

3 files changed

Lines changed: 42 additions & 12 deletions

File tree

backend/src/entities/cedar-authorization/cedar-policy-parser.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,10 @@ function extractPermitStatements(policyText: string): ParsedPermitStatement[] {
9595

9696
let i = permitIndex + permitKeyword.length;
9797
// Skip whitespace after "permit"
98-
while (i < policyText.length && (policyText[i] === ' ' || policyText[i] === '\t' || policyText[i] === '\n' || policyText[i] === '\r')) {
98+
while (
99+
i < policyText.length &&
100+
(policyText[i] === ' ' || policyText[i] === '\t' || policyText[i] === '\n' || policyText[i] === '\r')
101+
) {
99102
i++;
100103
}
101104

@@ -122,7 +125,10 @@ function extractPermitStatements(policyText: string): ParsedPermitStatement[] {
122125
const body = policyText.slice(bodyStart, i);
123126
// Skip past ')' and optional whitespace, expect ';'
124127
let j = i + 1;
125-
while (j < policyText.length && (policyText[j] === ' ' || policyText[j] === '\t' || policyText[j] === '\n' || policyText[j] === '\r')) {
128+
while (
129+
j < policyText.length &&
130+
(policyText[j] === ' ' || policyText[j] === '\t' || policyText[j] === '\n' || policyText[j] === '\r')
131+
) {
126132
j++;
127133
}
128134

frontend/src/app/lib/cedar-policy-items.ts

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -132,10 +132,7 @@ export function policyItemsToCedarPolicy(items: CedarPolicyItem[], connectionId:
132132
return policies.join('\n\n');
133133
}
134134

135-
const actionRef =
136-
item.action === 'table:*' || item.action === 'dashboard:*'
137-
? `action like RocketAdmin::Action::"${item.action}"`
138-
: `action == RocketAdmin::Action::"${item.action}"`;
135+
const actionRef = buildActionRef(item.action);
139136
let resource: string;
140137

141138
if (item.action.startsWith('table:')) {
@@ -154,9 +151,24 @@ export function policyItemsToCedarPolicy(items: CedarPolicyItem[], connectionId:
154151
return policies.join('\n\n');
155152
}
156153

154+
const TABLE_ACTIONS = ['table:read', 'table:add', 'table:edit', 'table:delete'];
155+
const DASHBOARD_ACTIONS = ['dashboard:read', 'dashboard:create', 'dashboard:edit', 'dashboard:delete'];
156+
157+
function buildActionRef(action: string): string {
158+
if (action === 'table:*') {
159+
const list = TABLE_ACTIONS.map((a) => `RocketAdmin::Action::"${a}"`).join(', ');
160+
return `action in [${list}]`;
161+
}
162+
if (action === 'dashboard:*') {
163+
const list = DASHBOARD_ACTIONS.map((a) => `RocketAdmin::Action::"${a}"`).join(', ');
164+
return `action in [${list}]`;
165+
}
166+
return `action == RocketAdmin::Action::"${action}"`;
167+
}
168+
157169
function buildResourceRef(type: string, connectionId: string, id: string | undefined): string {
158170
if (id === '*') {
159-
return `resource like RocketAdmin::${type}::"${connectionId}/*"`;
171+
return `resource`;
160172
}
161173
return `resource == RocketAdmin::${type}::"${connectionId}/${id}"`;
162174
}

frontend/src/app/lib/cedar-policy-parser.ts

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { CedarPolicyItem } from './cedar-policy-items';
33

44
interface ParsedPermitStatement {
55
action: string | null;
6+
actions: string[] | null;
67
resourceType: string | null;
78
resourceId: string | null;
89
isWildcard: boolean;
@@ -225,12 +226,18 @@ function extractPermitStatements(policyText: string): ParsedPermitStatement[] {
225226
}
226227
}
227228

228-
return results;
229+
return results.flatMap(expandActionIn);
230+
}
231+
232+
function expandActionIn(stmt: ParsedPermitStatement): ParsedPermitStatement[] {
233+
if (!stmt.actions || stmt.actions.length === 0) return [stmt];
234+
return stmt.actions.map((action) => ({ ...stmt, action, actions: null }));
229235
}
230236

231237
function parsePermitBody(body: string): ParsedPermitStatement {
232238
const result: ParsedPermitStatement = {
233239
action: null,
240+
actions: null,
234241
resourceType: null,
235242
resourceId: null,
236243
isWildcard: false,
@@ -240,9 +247,14 @@ function parsePermitBody(body: string): ParsedPermitStatement {
240247
if (actionMatch) {
241248
result.action = actionMatch[1];
242249
} else {
243-
const actionClause = body.match(/,\s*(action)\s*,/);
244-
if (actionClause) {
245-
result.isWildcard = true;
250+
const actionInMatch = body.match(/action\s+in\s*\[([^\]]+)\]/);
251+
if (actionInMatch) {
252+
result.actions = [...actionInMatch[1].matchAll(/RocketAdmin::Action::"([^"]+)"/g)].map((m) => m[1]);
253+
} else {
254+
const actionClause = body.match(/,\s*(action)\s*,/);
255+
if (actionClause) {
256+
result.isWildcard = true;
257+
}
246258
}
247259
}
248260

@@ -261,7 +273,7 @@ function parsePermitBody(body: string): ParsedPermitStatement {
261273
}
262274

263275
function extractResourceSuffix(resourceId: string | null, connectionId: string): string | null {
264-
if (!resourceId) return null;
276+
if (!resourceId) return '*';
265277
const prefix = `${connectionId}/`;
266278
if (resourceId.startsWith(prefix)) {
267279
return resourceId.slice(prefix.length);

0 commit comments

Comments
 (0)