stalniy / casl

CASL is an isomorphic authorization JavaScript library which restricts what resources a given user is allowed to access
https://casl.js.org/
MIT License
5.93k stars 269 forks source link

error on new Ability(rules) #305

Closed raugaral closed 4 years ago

raugaral commented 4 years ago

Hi, I'm using casl angular and have alredy update the version from 3.X to 4. I have updated my code a little but doesn't work.

Current version: "@casl/ability": "^4.0.8", "@casl/angular": "^4.0.4",

private defineAbilityFor(user: Employee) {
    const { can, cannot, rules } = new AbilityBuilder();
    if (user.isAdmin) {
      can('manage', 'all');
    } else if (user.isUser) {
      can('read', 'all');
      for (const projectData of user.MandatoryProjectList) {
        can(['share', 'edit', 'assign'], 'Project', { idProject: projectData.idProject });
      }
      cannot(['edit', 'delete'], Company);
    }

    // return rules;
    return new Ability(rules);
  }

Returns a error on the line return new Ability(rules);

ERROR in src/app/services/auth.service.ts:86:24 - error TS2345: Argument of type '(ClaimRawRule | LegacyClaimRawRule | SubjectRawRule<string, SubjectType, unknown> | LegacySubjectRawRule<string, SubjectType, unknown>)[]' is not assignable to parameter of type '(SubjectRawRule<string, SubjectType, unknown> | LegacySubjectRawRule<string, SubjectType, unknown>)[]'. Type 'ClaimRawRule | LegacyClaimRawRule | SubjectRawRule<string, SubjectType, unknown> | LegacySubjectRawRule<string, SubjectType, unknown>' is not assignable to type 'SubjectRawRule<string, SubjectType, unknown> | LegacySubjectRawRule<string, SubjectType, unknown>'. Type 'ClaimRawRule' is not assignable to type 'SubjectRawRule<string, SubjectType, unknown> | LegacySubjectRawRule<string, SubjectType, unknown>'. Type 'ClaimRawRule' is not assignable to type 'SubjectRawRule<string, SubjectType, unknown>'. Property 'subject' is optional in type 'ClaimRawRule' but required in type 'SubjectRawRule<string, SubjectType, unknown>'.

86 return new Ability(rules);


Angular CLI: 9.1.4 Node: 10.15.3 OS: darwin x64

Angular: 9.1.4 ... animations, cli, common, compiler, compiler-cli, core, forms ... language-service, platform-browser, platform-browser-dynamic ... router Ivy Workspace: Yes

Package Version

@angular-devkit/architect 0.900.7 @angular-devkit/build-angular 0.900.7 @angular-devkit/build-optimizer 0.900.7 @angular-devkit/build-webpack 0.900.7 @angular-devkit/core 9.0.7 @angular-devkit/schematics 9.1.4 @angular/cdk 9.2.2 @angular/flex-layout 9.0.0-beta.29 @angular/material 9.2.2 @angular/material-moment-adapter 9.2.2 @ngtools/webpack 9.0.7 @schematics/angular 9.1.4 @schematics/update 0.901.4 rxjs 6.5.5 typescript 3.7.5 webpack 4.41.2

stalniy commented 4 years ago

Starting from 4.0, AbilityBuilder accepts optional generic parameter TAbility, by default this parameter equals PureAbility. What you need to do is to change how you create AbilityBuilder:

const { can, cannot, rules } = new AbilityBuilder<Ability>();

For more details check the API docs

stalniy commented 4 years ago

By the way, you can simplify your rules by changing:

for (const projectData of user.MandatoryProjectList) {
        can(['share', 'edit', 'assign'], 'Project', { idProject: projectData.idProject });
      }

to

const projectIds = user.MandatoryProjectList.map(projectData => projectData.idProject)
can(['share', 'edit', 'assign'], 'Project', { idProject: { $in: projectIds } });
raugaral commented 4 years ago

I tried the minumum code, but the error persist... I tried too remove the node_modules folder and install another time, but nothing changes.

private defineAbilityFor(user: Employee) {
    const { can, cannot, rules } = new AbilityBuilder();
    can('manage', 'all');

    return new Ability(rules);
 }
stalniy commented 4 years ago

Did you tried what I suggested?

stalniy commented 4 years ago

Please also check the new angular example - https://github.com/stalniy/casl-examples/blob/master/packages/angular-todo/src/services/AppAbility.ts

raugaral commented 4 years ago

I have tried what you sugget, but the problem is on the constructor from Ability, because not allow rules.

if i do that:

  private defineAbilityFor(user: Employee) {
    const { can, cannot, rules } = new AbilityBuilder();
    return new Ability(rules);
  }

the error still

Argument of type '(ClaimRawRule | LegacyClaimRawRule | SubjectRawRule<string, SubjectType, unknown> | LegacySubjectRawRule<string, SubjectType, unknown>)[]' is not assignable to parameter of type '(SubjectRawRule<string, SubjectType, unknown> | LegacySubjectRawRule<string, SubjectType, unknown>)[]'. Type 'ClaimRawRule | LegacyClaimRawRule | SubjectRawRule<string, SubjectType, unknown> | LegacySubjectRawRule<string, SubjectType, unknown>' is not assignable to type 'SubjectRawRule<string, SubjectType, unknown> | LegacySubjectRawRule<string, SubjectType, unknown>'. Type 'ClaimRawRule' is not assignable to type 'SubjectRawRule<string, SubjectType, unknown> | LegacySubjectRawRule<string, SubjectType, unknown>'. Type 'ClaimRawRule' is not assignable to type 'SubjectRawRule<string, SubjectType, unknown>'. Property 'subject' is optional in type 'ClaimRawRule' but required in type 'SubjectRawRule<string, SubjectType, unknown>'.

raugaral commented 4 years ago

i'm trying that example, but it's a little complicated, and i can not apply it to my current project...

raugaral commented 4 years ago

my code is:

export class AuthService {
  constructor(private http: HttpClient, private ability: Ability) { }

  private defineAbilityFor(user: Employee) {
    const { can, cannot, rules } = new AbilityBuilder(Ability);
    this.ability.update(rules);
  }
}

And the error is on line this.ability.update(rules)

Argument of type '(ClaimRawRule<string> | LegacyClaimRawRule<string> | SubjectRawRule<string, SubjectType, unknown> | LegacySubjectRawRule<string, SubjectType, unknown>)[]' is not assignable to parameter of type '(SubjectRawRule<string, SubjectType, Record<string | number | symbol, string | number | boolean | Record<string | number | symbol, any> | MongoQueryOperators>> | LegacySubjectRawRule<...>)[]'.
  Type 'ClaimRawRule<string> | LegacyClaimRawRule<string> | SubjectRawRule<string, SubjectType, unknown> | LegacySubjectRawRule<string, SubjectType, unknown>' is not assignable to type 'SubjectRawRule<string, SubjectType, Record<string | number | symbol, string | number | boolean | Record<string | number | symbol, any> | MongoQueryOperators>> | LegacySubjectRawRule<...>'.
    Type 'ClaimRawRule<string>' is not assignable to type 'SubjectRawRule<string, SubjectType, Record<string | number | symbol, string | number | boolean | Record<string | number | symbol, any> | MongoQueryOperators>> | LegacySubjectRawRule<...>'.
      Type 'ClaimRawRule<string>' is not assignable to type 'SubjectRawRule<string, SubjectType, Record<string | number | symbol, string | number | boolean | Record<string | number | symbol, any> | MongoQueryOperators>>'.
        Property 'subject' is optional in type 'ClaimRawRule<string>' but required in type 'SubjectRawRule<string, SubjectType, Record<string | number | symbol, string | number | boolean | Record<string | number | symbol, any> | MongoQueryOperators>>'.

Where come that unknown of SubjectRawRule<string, SubjectType, unknown>?

stalniy commented 4 years ago

The problem on the new Ability line because types of rules generated by AbilityBuilder are not compatible with the types that Ability expects. They have different defaults. That's why you need to provide Ability as a generic type to AbilityBuilder.

stalniy commented 4 years ago

I have tried what you sugget, but the problem is on the constructor from Ability, because not allow rules.

if i do that:

  private defineAbilityFor(user: Employee) {
    const { can, cannot, rules } = new AbilityBuilder();
    return new Ability(rules);
  }

the error still

Argument of type '(ClaimRawRule | LegacyClaimRawRule | SubjectRawRule<string, SubjectType, unknown> | LegacySubjectRawRule<string, SubjectType, unknown>)[]' is not assignable to parameter of type '(SubjectRawRule<string, SubjectType, unknown> | LegacySubjectRawRule<string, SubjectType, unknown>)[]'. Type 'ClaimRawRule | LegacyClaimRawRule | SubjectRawRule<string, SubjectType, unknown> | LegacySubjectRawRule<string, SubjectType, unknown>' is not assignable to type 'SubjectRawRule<string, SubjectType, unknown> | LegacySubjectRawRule<string, SubjectType, unknown>'. Type 'ClaimRawRule' is not assignable to type 'SubjectRawRule<string, SubjectType, unknown> | LegacySubjectRawRule<string, SubjectType, unknown>'. Type 'ClaimRawRule' is not assignable to type 'SubjectRawRule<string, SubjectType, unknown>'. Property 'subject' is optional in type 'ClaimRawRule' but required in type 'SubjectRawRule<string, SubjectType, unknown>'.

If you think you know better, why do you ask me after all? :)

I created a simple example for you, so you can make sure this is true - https://codesandbox.io/s/typescript-examples-l0p8b?file=/src/index.ts

stalniy commented 4 years ago

Close this as there is nothing to do from my side. Please check the docs and the examples

Lippur commented 4 years ago

Ran into the same issue. Even the very first example code snippet from the README produces this error, so it is a bit rich to suggest checking the docs, in my opinion. Adding the generic parameter does fix it, but is mentioned nowhere in the entire README, which is where most people trying out the package will look for (working) examples.

Glad there's an easy solution, though!

stalniy commented 4 years ago

Thanks for the note. I’ll fix the example in README. But I’d recommend to check docs instead - https://stalniy.github.io/casl/v4/en/guide/intro You will find there a lot more, including a separate page for typescript support

in CASL v5, I’ll remove some legacy types and errors will be easier to understand.

theGC commented 3 years ago

@stalniy is this the issue I'm seeing on the below Sandbox:

https://codesandbox.io/s/wonderful-meadow-02kls?file=/src/index.ts

I'm trying to test a basic RBAC set up and hit a similar typescript error. I thought the workaround might be to remove the Ability class on line 32:

const { can, build } = new AbilityBuilder<AppAbility>(Ability)

but that causes the results to be incorrect (all conditions turn true):

image

Any help in understanding the correct set up would be amazing. I'm trying to follow your cookbook recipe but it doesn't seem to validate so I created this slimmed-down version to try and see why :)

https://casl.js.org/v4/en/cookbook/roles-with-static-permissions

stalniy commented 3 years ago

@theGC there are some typings issues in 4.x which I fixed in prerelease branch for v5. For now, you need to do this:

import { AbilityClass, Ability } from '@casl/ability'

type AppAbility = ...
const AppAbility = Ability as AbilityClass<AppAbility>;

const { can, build } = new AbilityBuilder<AppAbility>(AppAbility);
theGC commented 3 years ago

@stalniy that's got it, thank you and thanks for all your hard work on CASL. Been using it within Feathers for a few days now and it's making things much clearer in their structure and logic. Looking forward to v5 👍

In case it helps anyone else, I've updated the sandbox and pulled it more in line with the cookbook article:

https://codesandbox.io/s/casl-demo-roles-with-static-permissions-02kls?file=/src/index.ts