如何使用CASL处理参考字段猫鼬的条件

问题描述

在将Casl用于一些简单的项目之后,我正在尝试实现更复杂的东西。我正在尝试将Roles with persisted permissions与网站上描述的JWT混合使用。

对于此基本示例,我尝试授予 User 主题 read 操作权限,但仅授予属于组织一部分的用户条目:

我的用户模型

interface UserAttrs {
  email: string;
  firstName: string;
  lastName: string;
  password: string;
  role: RoleDoc;
  organization: OrganizationDoc;
}
interface usermodel extends mongoose.Model<UserDoc> {
  build(attrs: UserAttrs): UserDoc;
}
interface UserDoc extends mongoose.Document {
  email: string;
  firstName: string;
  lastName: string;
  active: boolean;
  password: string;
  createdAt: Date;
  updatedAt: Date;
  role: RoleDoc;
  organization: OrganizationDoc;
}

const userSchema = new mongoose.Schema(
  {
    email: {
      type: String,required: true,unique: true,trim: true,match: [/.+\@.+\..+/,'Please fill a valid email address'],},firstName: {
      type: String,lastName: {
      type: String,active: {
      type: Boolean,default: true,password: {
      type: String,createdAt: {
      type: Date,default: Date.Now,updatedAt: {
      type: Date,role: {
      type: mongoose.Schema.Types.ObjectId,ref: 'Role',organization: {
      type: mongoose.Schema.Types.ObjectId,ref: 'Organization',{
    toJSON: {
      transform(_doc,ret) {
        ret.id = ret._id;
        delete ret._id;
        delete ret.password;
        delete ret.__v;
      },}
);

userSchema.pre('save',async function (done) {
  if (this.isModified('password')) {
    const hashed = await Password.toHash(this.get('password'));
    this.set('password',hashed);
    this.set('updatedAt',Date.Now);
  }
  done();
});

userSchema.statics.build = (attrs: UserAttrs) => {
  return new User(attrs);
};

const User = mongoose.model<UserDoc,usermodel>('User',userSchema);

export { User };

组织模型


interface OrganizationAttrs {
  id: string;
  name: string;
}
interface OrganizationModel extends mongoose.Model<OrganizationDoc> {
  build(attrs: OrganizationAttrs): OrganizationDoc;
}

export interface OrganizationDoc extends mongoose.Document {
  name: string;
  version: number;
}

const organizationSchema = new mongoose.Schema(
  {
    name: {
      type: String,ret) {
        ret.id = ret._id;
        delete ret._id;
      },}
);

organizationSchema.set('versionKey','version');
organizationSchema.plugin(updateIfCurrentPlugin);

organizationSchema.statics.findByEvent = (event: {
  id: string;
  version: number;
}) => {
  return Organization.findOne({
    _id: event.id,version: event.version - 1,});
};

organizationSchema.statics.build = (attrs: OrganizationAttrs) => {
  return new Organization({
    _id: attrs.id,name: attrs.name,});
};

const Organization = mongoose.model<OrganizationDoc,OrganizationModel>(
  'Organization',organizationSchema
);

export { Organization };

角色模型


interface RoleAttrs {
  name: string;
  permissions: string;
  organization: OrganizationDoc;
}

interface RoleModel extends mongoose.Model<RoleDoc> {
  build(attrs: RoleAttrs): RoleDoc;
}

export interface RoleDoc extends mongoose.Document {
  name: string;
  permissions: string;
  organization: OrganizationDoc;
}

const roleSchema = new mongoose.Schema(
  {
    name: {
      type: String,permissions: {
      type: String,ret) {
        ret.id = ret._id;
        delete ret._id;
        delete ret.__v;
      },}
);

roleSchema.pre('save',async function (done) {
  done();
});

roleSchema.statics.build = (attrs: RoleAttrs) => {
  return new Role(attrs);
};

const Role = mongoose.model<RoleDoc,RoleModel>('Role',roleSchema);

export { Role };

角色的权限字段存储为字符串。用户登录后,我将权限添加到JWT令牌

const existingUser = await User.findOne({ email,active: true })
      .populate('role')
      .populate('organization');

// Check if user is valid...

// userPermissions = [{ action: 'read',subject: 'User',conditions: { organization: '{{organization.id}}' },](as a string)
const userPermissions = Mustache.render(
      existingUser.role.permissions,existingUser
    );
    console.log(userPermissions); 
// result ==> [{"action":"read","subject":"User","conditions":{"organization":"5f4bc664e27664265cb033d7"}}]
    // Genereate json web token JWT
    const userJWT = jwt.sign(
      {
        id: existingUser.id,email: existingUser.email,organizationId: existingUser.organization.id,userRolePermissions: userPermissions,process.env.JWT_KEY!
    );

然后在中间件中,我创建类似于here

功能
const { id,email,organizationId,userRolePermissions } = jwt.verify(
        req.session.jwt,process.env.JWT_KEY!
      ) as Token;
const currentUser: UserPayload = {
        id: id,email: email,organizationId: organizationId,userRolePermissions: createAbility(JSON.parse(userRolePermissions)),};

createAbility的结果是

i {
        s: false,v: [Object: null prototype] {},p: [Object: null prototype] {},g: [Object: null prototype] {
          User: [Object: null prototype] {
            read: [Object: null prototype] {
              '0': t {
                t: [Function],i: undefined,action: 'read',inverted: false,conditions: { organization: '5f4bc8d85dc07d269e4f303d' },reason: undefined,fields: undefined
              }
            }
          }
        },j: [
          {
            action: 'read',conditions: { organization: '5f4bc8d85dc07d269e4f303d' }
          }
        ],O: {
          conditionsMatcher: [Function],fieldMatcher: [Function: j],resolveAction: [Function: u]
        }
      }

如果我执行


const organizationId = req.params.organizationId as Object;

const users = await User.find({ organization: organizationId });

// req.currentUser contain the user with the userRolePermissions above
ForbiddenError.from(req.currentUser!.userRolePermissions).throwUnlessCan(
        'read',subject('User',users)
      );

我收到消息:“无法对“用户”执行“读取””。我们如何处理ref字段?

我不知道它是否可以解决问题,但是如果我将权限更改为:

  • “管理”和“全部”(无条件)
  • “读取”和“用户”(无条件)

有效。

如果我在用户上填充组织字段(然后我将有一个对象),casl的工作原理?我是否应该在角色的权限字段中制定两个规则(如果一个则填充一个规则,否则填充一个规则)?

解决方法

暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!

如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。

小编邮箱:dio#foxmail.com (将#修改为@)