Search code examples
casl

Getting the right permitted fields of conditions


Currently, I'm building an app with with following similar logic:

...
const user = {
  isAdmin: true,
  company: '5faa6a847b42bf47b8f785a1',
  projects: ['5faa6a847b42bf47b8f785a2']
}

function defineAbilityForUser(user) {
  return defineAbility((can) => {
    if (user.isAdmin) {
      can('create', 'ProjectTime', {
          company: user.company,
        }
      );
    }
    
    can(
      'create', 
      'ProjectTime', 
      ["company", "project", "user", "start", "end"], 
      {
        company: user.company,
        project: {
          $in: user.projects
        }
      }
    );
  });
}

const userAbility = defineAbilityForUser(user); //
console.log( permittedFieldsOf(userAbility, 'create', 'ProjectTime') );

// console output: ['company', 'project', 'user', 'start', 'end']

Basically an admin should be allowed to create a project time with no field restrictions. And a none admin user should only be allowed to set the specified fields for projects to which he belongs.

The problem is that I would expect to get [] as output because an admin should be allowed to set all fields for a project time.

The only solution I found was to set all fields on the admin user condition. But this requires a lot of migration work later when new fields are added to the project time model. (also wrapping the second condition in an else-block is not possible in my case)

Is there any other better way to do this? Or maybe, would it be better if the permittedFieldsOf-function would prioritize the condition with no field restrictions?


Solution

  • There is actually no way for casl to know what means all fields in context of your models. It knows almost nothing about their shapes and relies on conditions you provide it to check that objects later. So, it does not have full information.

    What you need to do is to pass the 4th argument to override fieldsFrom callback. Check the api docs and reference implementation in @casl/mongoose

    In casl v5, that parameter is mandatory. So, this confusion will disappear very soon