VQH DEV
VQH DEV

Reputation: 73

Handling Unique Permissions with CASL and TypeORM in NestJs

I'm using CASL for authorization in my NestJS application with TypeORM for persistence. I've defined permissions with unique constraints on "action" and "subject" columns to prevent duplicates. However, I'm facing an issue when trying to create a permission with the same action and subject but different fields or conditions.

Here's my current setup:

Entities:

CASL Ability:

import { User } from './users/entities/user.entity';
import {
  ForcedSubject,
  MongoAbility,
  RawRuleOf,
  createMongoAbility,
} from '@casl/ability';
import { get } from 'lodash';
import { Permission } from './database/entities/permission.entity';
export const actions = [
  'manage',
  'create',
  'read',
  'update',
  'delete',
] as const;
export const subjects = ['User', 'Post', 'all'] as const;
export type AppAbilities = [
  (typeof actions)[number],
  (
    | (typeof subjects)[number]
    | ForcedSubject<Exclude<(typeof subjects)[number], 'all'>>
  ),
];
export type AppAbility = MongoAbility<AppAbilities>;
export const createAbility = (rules: RawRuleOf<AppAbility>[]) => {
  return createMongoAbility<AppAbilities>(rules);
};
export const createUserAbility = (user: User, vars: any = {}) => {
  if (!vars.hasOwnProperty('user')) {
    vars.user = user;
  }
  return createAbility(createUserPermissions(user, vars));
};
function interpolate(template: string, vars: object) {
  return JSON.parse(template, (_, rawValue) => {
    if (rawValue[0] !== '$') {
      return rawValue;
    }
    const name = rawValue.slice(2, -1);
    const value = get(vars, name);
    if (typeof value === 'undefined') {
      throw new ReferenceError(`Variable ${name} is not defined`);
    }
    return value;
  });
}

function createUserPermissions(user: User, vars: object = {}) {
  const permissions = user.roles.reduce((permissions, role) => {
    return [...permissions, ...normalizePermissions(role.permissions)];
  }, []);
  return interpolate(JSON.stringify(permissions), vars);
}

function normalizePermissions(permissions: Permission[]) {
  return permissions.map((permission) => {
    const { fields, ...rest } = permission;
    return fields && fields.length ? { ...rest, fields } : rest;
  });
}

Example Permissions:

Problem:

I want to add a new permission, "ABC", which allows updating any user with any fields. However, the unique constraint on "action" and "subject" prevents this because "update" and "User" already exist with different fields and conditions.

Possible Solutions:

  1. Extend Unique Constraint: Should I add fields and conditions to the unique constraint: @Unique(['action', 'subject', 'fields', 'conditions'])? This seems excessive and might lead to unnecessary complexity.
  2. Alternative Approach: Is there a better way to handle this scenario while maintaining efficient permission management and avoiding duplicates?

I'd appreciate any insights or recommendations on how to best address this challenge. Thanks!

Upvotes: 0

Views: 588

Answers (0)

Related Questions