All Projects → eggjs → Egg Cancan

eggjs / Egg Cancan

Licence: mit
cancancan like authorization plugin for Egg.js

Programming Languages

javascript
184084 projects - #8 most used programming language

Projects that are alternatives of or similar to Egg Cancan

egg-sentry
Sentry Plugin For Egg.js
Stars: ✭ 18 (-61.7%)
Mutual labels:  egg, egg-plugin, eggjs
Egg Authz
egg-authz is an authorization middleware for Egg.js based on Casbin
Stars: ✭ 50 (+6.38%)
Mutual labels:  egg, eggjs, egg-plugin
Egg 24time
A Twitter-like news and social server for Egg. 微信小程序社区全栈解决方案
Stars: ✭ 493 (+948.94%)
Mutual labels:  egg, eggjs, egg-plugin
Egg Oauth2 Server
🌟 OAuth2 server plugin for egg.js based on node-oauth2-server
Stars: ✭ 174 (+270.21%)
Mutual labels:  egg, eggjs, egg-plugin
Cool Admin Api
cool-admin-api 是基于egg.js、typeorm、jwt等封装的api开发脚手架、快速开发api接口
Stars: ✭ 188 (+300%)
Mutual labels:  egg, eggjs, egg-plugin
Egg Graphql
Stars: ✭ 345 (+634.04%)
Mutual labels:  egg, eggjs, egg-plugin
Egg Router Plus
The missing router feature for eggjs
Stars: ✭ 117 (+148.94%)
Mutual labels:  egg, eggjs, egg-plugin
egg-rbac
Role Based Access Control for eggjs
Stars: ✭ 32 (-31.91%)
Mutual labels:  egg, egg-plugin, eggjs
egg-parameters
Merge all parameters (ctx.params, ctx.request.query, ctx.request.body) into ctx.params like Rails application.
Stars: ✭ 24 (-48.94%)
Mutual labels:  egg, egg-plugin, eggjs
egg-vue-typescript-boilerplate
Egg Vue TypeScript Server Side Render (SSR) / Client Side Render (CSR)
Stars: ✭ 69 (+46.81%)
Mutual labels:  egg, eggjs
Nideadmin
【未完成】NideAdmin - 基于 Vue.js + Egg.js 的微信小程序后台框架
Stars: ✭ 35 (-25.53%)
Mutual labels:  egg, eggjs
Eggjs Note
《Egg.js 深入浅出学习笔记》(暂时停更)
Stars: ✭ 502 (+968.09%)
Mutual labels:  egg, eggjs
Egg Restfulapi
🏅 基于Egg.js 2.0 & {mongoose,jwt}RESTful API 模板,用于快速集成开发RESTful前后端分离的服务端。
Stars: ✭ 524 (+1014.89%)
Mutual labels:  egg, egg-plugin
egg-view-vue-ssr
Egg Vue Server Side Render (SSR) Plugin
Stars: ✭ 90 (+91.49%)
Mutual labels:  egg, egg-plugin
egg-y-validator
☯️ Egg Magic Validator (Egg 魔法验证工具)
Stars: ✭ 30 (-36.17%)
Mutual labels:  egg, egg-plugin
egg-session
session plugin for egg
Stars: ✭ 48 (+2.13%)
Mutual labels:  egg, egg-plugin
egg-logger-sls
Logger transport for aliyun sls.
Stars: ✭ 14 (-70.21%)
Mutual labels:  egg-plugin, eggjs
egg-nuxt-blog
Nuxt.js(web端) + Egg.js(api 服务) + Vue.js(管理后台)+ ElementUI(通用组件库)服务器渲染(SSR)搭建的个人博客系统
Stars: ✭ 16 (-65.96%)
Mutual labels:  egg, eggjs
egg-rest
Restful API plugin for egg
Stars: ✭ 106 (+125.53%)
Mutual labels:  egg, egg-plugin
docker
Egg official docker image
Stars: ✭ 26 (-44.68%)
Mutual labels:  egg, eggjs

egg-cancan

Cancancan like authorization plugin for Egg.js

This plugin is our best practice from we developing yuque.com.

NPM version build status Test coverage David deps Known Vulnerabilities npm download

Install

$ npm i egg-cancan --save

Usage

// {app_root}/config/plugin.js
exports.cancan = {
  enable: true,
  package: 'egg-cancan',
};

Configuration

// {app_root}/config/config.default.js
exports.cancan = {
// method name of current logined user instance
  contextUserMethod: 'user',
  // Enable disable Ability check result cache
  cache: false,
  // Enable log authorize check result
  log: false,
};

Defining Abilities

You must create app/ability.js file

The Ability class is where all user permissions are defined. An example class looks like this.

'use strict';

const { BaseAbility } = require('egg-cancan');

class Ability extends BaseAbility {
  constructor(ctx, user) {
    super(ctx, user)
  }

  async rules(action, obj, options = {}) {
    const { type } = options;

    if (type === 'topic') {
      if (action === 'update') {
        return await this.canUpdateTopic(obj);
      }

      if (action === 'delete') {
        return await this.canDeleteTopic(obj);
      }
    }

    return true;
  }

  async canUpdateTopic(obj) {
    if (topic.user_id === this.user_id) return true;
    return false;
  }

  async canDeleteTopic(obj) {
    if (this.user.admin) return true;
    return false;
  }
}

Action alias

Action Alias
read show, read
update edit, update
create new, create
delete destroy, delete

Cache check result in same Context

Ability support cache Ability check result in ctx, you can enable it by change config/config.default.js

exports.cancan = {
  // defalut is disabled
  cache: true,
};

When you enable that, you call can method will hit cache:

ctx.can('read', user);
- check cache in ability._cache
    found -> return
  not exist ->
    execute `rules` to real check
    write to _cache
    return

Its use action + obj + options stringify as default cache key:

ability.cacheKey('read', { id: 1 }, { type: 'user' });
=> 'read-{id:1}-{type:"user"}'

You can rewrite it by override the cacheKey method, for example:

class Ability extends BaseAbility {
  cacheKey(action, obj, options) {
    return [action, obj.cacheKey, options.type].join(':');
  }
}

Check Abilities

The ctx.can method:

can = await ctx.can('create', topic, { type: 'topic' });
can = await ctx.can('read', topic, { type: 'topic' });
can = await ctx.can('update', topic, { type: 'topic' });
can = await ctx.can('delete', topic, { type: 'topic' });

can = await ctx.can('update', user, { type: 'user' });

// For egg-sequelize model instance, not need pass `:type` option
const topic = await ctx.model.Topic.findById(...);
can = await ctx.can('update', topic);

The ctx.authorize method:

await ctx.authorize('read', topic);
// when permission is ok, not happend
// when no permission, will throw CanCanAccessDenied

Handle Unauthorized Access

If the ctx.authorize check fails, a CanCanAccessDenied error will be throw. You can catch this and modify its behavior:

Add new file: app/middleware/handle_authorize.js

module.exports = () => {
  return async handleAuthorize(next) {
    try {
      await next();
    } catch (e) {
      if (e.name === 'CanCanAccessDenied') {
        this.status = 403;
        this.body = 'Access Denied';
      } else {
        throw e;
      }
    }
  }
}

And enable this middleware by modify config/config.default.js:

exports.middleware = [
  ...
  'handleAuthorize',
  ...
];

Testing your abilities

When you wrote app/ability.js, you may need to write test case.

  • egg-sequelize
  • factory-girl-sequelize

Create a test file: test/ability.test.js

'use strict';

describe('Ability', () => {
  let allow, user, ability, anonymousAbility;

  beforeAll(async () => {
    user = await create('user');
    ability = new app.Ability(ctx, user);
  });

  describe('Topic', () => {
    describe('Anonymous', () => {
      it('should work', async () => {
        const topic = await create('topic');
        allow = await ability.can('create', topic);
        assert.equal(true, allow);
        allow = await ability.can('read', topic);
        assert.equal(true, allow);
        allow = await ability.can('update', topic);
        assert.equal(false, allow);
        allow = await ability.can('destroy', topic);
        assert.equal(false, allow);
      });
    });

    describe('Author', () => {
      it('should work', async () => {
        const topic = await create('topic', { user_id: user.id });
        allow = await ability.can('create', topic);
        assert.equal(true, allow);
        allow = await ability.can('read', topic);
        assert.equal(true, allow);
        allow = await ability.can('update', topic);
        assert.equal(true, allow);
        allow = await ability.can('destroy', topic);
        assert.equal(true, allow);
      });
    })
  });
});

License

MIT

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].