Skip to content

cedar permissions#1627

Open
Artuomka wants to merge 6 commits intomainfrom
backend_permissions_reworking
Open

cedar permissions#1627
Artuomka wants to merge 6 commits intomainfrom
backend_permissions_reworking

Conversation

@Artuomka
Copy link
Collaborator

No description provided.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Introduces Cedar-based authorization as an optional (“CEDAR_AUTHORIZATION_ENABLED”) authorization path, with policy generation persisted on groups and cached per connection, plus extensive E2E/unit tests to validate behavior.

Changes:

  • Add Cedar authorization service/module, schema, entity builder, and policy generator; integrate Cedar checks into existing guards with legacy fallback intent.
  • Persist generated Cedar policy text on GroupEntity and invalidate a new per-connection Cedar policy cache when groups/permissions change.
  • Add/adjust AVA E2E + unit tests for Cedar authorization behavior and update expected error codes/messages.

Reviewed changes

Copilot reviewed 42 out of 43 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
yarn.lock Locks new @cedar-policy/cedar-wasm dependency.
backend/package.json Adds @cedar-policy/cedar-wasm runtime dependency.
backend/.development.env Adds CEDAR_AUTHORIZATION_ENABLED dev env flag.
backend/src/app.module.ts Registers global CedarAuthorizationModule.
backend/src/helpers/constants/constants.ts Adds Cedar policy cache defaults.
backend/src/helpers/cache/cacher.ts Adds Cedar policy cache + invalidation helpers.
backend/src/migrations/1771545600000-AddCedarPolicyToGroup.ts Adds cedarPolicy column to group table.
backend/src/entities/group/group.entity.ts Adds cedarPolicy field to GroupEntity.
backend/src/entities/permission/use-cases/create-or-update-permissions.use.case.ts Generates/saves Cedar policy on permission updates; invalidates cache.
backend/src/entities/connection/use-cases/create-group-in-connection.use.case.ts Initializes new group Cedar policy; invalidates cache.
backend/src/entities/connection/use-cases/create-connection.use.case.ts Writes admin group Cedar policy at connection creation.
backend/src/entities/cedar-authorization/cedar-action-map.ts Defines Cedar action/resource identifiers.
backend/src/entities/cedar-authorization/cedar-authorization.module.ts Provides Cedar auth service as a global module.
backend/src/entities/cedar-authorization/cedar-authorization.service.interface.ts Defines Cedar auth service interface.
backend/src/entities/cedar-authorization/cedar-authorization.service.ts Implements Cedar evaluation, policy loading, and caching.
backend/src/entities/cedar-authorization/cedar-entity-builder.ts Builds Cedar entities (user/group/connection/table).
backend/src/entities/cedar-authorization/cedar-policy-generator.ts Generates Cedar policy text from complex permissions.
backend/src/entities/cedar-authorization/cedar-schema.ts Adds Cedar schema (TS).
backend/src/entities/cedar-authorization/cedar-schema.json Adds Cedar schema (JSON).
backend/src/entities/cedar-authorization/scripts/migrate-permissions-to-cedar.ts Adds backfill script to generate policies from existing permissions.
backend/src/guards/connection-read.guard.ts Adds Cedar-first check with intended legacy fallback.
backend/src/guards/connection-edit.guard.ts Adds Cedar-first check with intended legacy fallback.
backend/src/guards/group-read.guard.ts Adds Cedar-first check with intended legacy fallback.
backend/src/guards/group-edit.guard.ts Adds Cedar-first check with intended legacy fallback.
backend/src/guards/table-read.guard.ts Adds Cedar-first check with intended legacy fallback.
backend/src/guards/table-add.guard.ts Adds Cedar-first check with intended legacy fallback.
backend/src/guards/table-edit.guard.ts Adds Cedar-first check with intended legacy fallback.
backend/src/guards/table-delete.guard.ts Adds Cedar-first check with intended legacy fallback.
backend/test/utils/user-with-different-permissions-utils.ts Adds test helper for “connection:edit only” user scenario.
backend/test/ava-tests/saas-tests/saas-cedar-permissions-e2e.test.ts New SaaS Cedar E2E test suite.
backend/test/ava-tests/non-saas-tests/non-saas-cedar-permissions-e2e.test.ts New non-SaaS Cedar E2E test suite.
backend/test/ava-tests/non-saas-tests/non-saas-cedar-policy-generator.test.ts New unit tests for policy generator.
backend/test/ava-tests/non-saas-tests/non-saas-cedar-entity-builder.test.ts New unit tests for entity builder.
backend/test/ava-tests/*/group-e2e.test.ts Updates expected status/messages to 403/DONT_HAVE_PERMISSIONS for Cedar path.
backend/test/ava-tests//user--permissions-e2e.test.ts Updates expected status/messages to 403/DONT_HAVE_PERMISSIONS for Cedar path.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +120 to +156
const policies = await this.loadPoliciesForConnection(connectionId);
if (!policies) return false;

const entities = buildCedarEntities(userId, userGroups, connectionId, tableName);

const call = {
principal: { type: CEDAR_USER_TYPE, id: userId },
action: { type: CEDAR_ACTION_TYPE, id: action },
resource: { type: resourceType as string, id: resourceId },
context: {},
policies: { staticPolicies: policies },
entities: entities,
schema: this.schema,
};

const result = this.cedarModule.isAuthorized(call as Parameters<typeof this.cedarModule.isAuthorized>[0]);
if (result.type === 'success') {
return result.response.decision === 'allow';
}

this.logger.warn(`Cedar authorization error: ${JSON.stringify(result.errors)}`);
return false;
}

private async loadPoliciesForConnection(connectionId: string): Promise<string | null> {
const cached = Cacher.getCedarPolicyCache(connectionId);
if (cached !== null) return cached;

const groups = await this.groupRepository.findAllGroupsInConnection(connectionId);
const policyTexts = groups.map((g) => g.cedarPolicy).filter(Boolean);

if (policyTexts.length === 0) return null;

const combined = policyTexts.join('\n\n');
Cacher.setCedarPolicyCache(connectionId, combined);
return combined;
}
Copy link

Copilot AI Feb 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

evaluate() returns false when loadPoliciesForConnection() returns null/empty or when Cedar returns a non-success result. In the guards, a false return becomes a hard 403 and does not trigger the intended legacy fallback (fallback only happens on thrown exceptions). This can lock out users if Cedar is enabled but policies haven’t been backfilled yet, or if Cedar returns structured errors instead of throwing. Consider throwing on Cedar evaluation errors / missing policy set (or returning a tri-state like {allowed, shouldFallback}) so the guards can correctly fall back to legacy auth when Cedar can’t make a decision.

Copilot uses AI. Check for mistakes.
Comment on lines +5 to +12
import { PermissionEntity } from '../../permission/permission.entity.js';
import { IComplexPermission, ITablePermissionData } from '../../permission/permission.interface.js';
import { generateCedarPolicyForGroup } from '../cedar-policy-generator.js';

export async function migratePermissionsToCedar(dataSource: DataSource): Promise<void> {
const connectionRepository = dataSource.getRepository(ConnectionEntity);
const groupRepository = dataSource.getRepository(GroupEntity);
const permissionRepository = dataSource.getRepository(PermissionEntity);
Copy link

Copilot AI Feb 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

permissionRepository is declared but never used, which will be flagged by the repo’s Biome noUnusedVariables rule. Remove it (or use it) to keep the script lint-clean.

Suggested change
import { PermissionEntity } from '../../permission/permission.entity.js';
import { IComplexPermission, ITablePermissionData } from '../../permission/permission.interface.js';
import { generateCedarPolicyForGroup } from '../cedar-policy-generator.js';
export async function migratePermissionsToCedar(dataSource: DataSource): Promise<void> {
const connectionRepository = dataSource.getRepository(ConnectionEntity);
const groupRepository = dataSource.getRepository(GroupEntity);
const permissionRepository = dataSource.getRepository(PermissionEntity);
import { IComplexPermission, ITablePermissionData } from '../../permission/permission.interface.js';
import { generateCedarPolicyForGroup } from '../cedar-policy-generator.js';
export async function migratePermissionsToCedar(dataSource: DataSource): Promise<void> {
const connectionRepository = dataSource.getRepository(ConnectionEntity);
const groupRepository = dataSource.getRepository(GroupEntity);

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants