this is porting from egg-cancan
.vscode= A folder,- containing a
settings.jsonwhich activates the deno language server for this workspace - containing a
extensions.jsonwith recommended vscode extensions for this workspace
- containing a
example= A folder, containing entry deno files for demonstrating the modules functionalities- contains
main.ts- the default file for examples
- contains
dependencies= A folder, including dependency re-exportslib= A folder containing more source files which are exported bymod.ts- Hint: you may create multiple of them to structure your module.
.gitignore= A normal gitingore filedeno.json- a config file for the deno cli- includes tasks (a.k.a aliases for long commands) with
deno task
- includes tasks (a.k.a aliases for long commands) with
LICENSEmod.ts= the entrypoint for this deno module, which exports all functionality of this moduleReadme.md= A normal Readme file
see tasks property in deno.json Run each key there with
deno task <task-key>
- replace
lib/startKia.tswith a file which makes more sense for your deno module
see https://deno.land/x/cancan
import {
BaseAbility,
CheckRecordOptions,
} from "https://deno.land/x/cancan/mod.ts";
class Ability extends BaseAbility {
constructor(ctx: any, user: any) {
super(ctx, user);
}
// override
async rules(action: string, obj: any, options: CheckRecordOptions): boolean {
const { type } = options;
if (type === "topic") {
if (action === "update") {
return await this.canUpdateTopic(obj);
}
if (action === "delete") {
return await this.canDeleteTopic(obj);
}
}
return true;
}
canUpdateTopic(obj: any) {
if (obj.user_id === this.user.user_id) return true;
return false;
}
canDeleteTopic(obj: any) {
if (this.user.role === "admin") return true;
return false;
}
}| Action | Alias |
|---|---|
| read | show, read |
| update | edit, update |
| create | new, create |
| delete | destroy, delete |
Ability support cache Ability check result in ctx, you can enable it by change
.env.example
LOG_ENABLE=false
CACHE_ENABLE=trueWhen 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(":");
}
}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 CanCanAccessDeniedIf 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
export const handleAuthorize = async function (ctx, next) {
try {
// do your code
await next();
} catch (e) {
if (e.name === "CanCanAccessDenied") {
this.status = 403;
this.body = "Access Denied";
} else {
throw e;
}
}
};WIP
Chinese Guide https://segmentfault.com/a/1190000041396991