Skip to content

Commit 48fd26c

Browse files
committed
feat(module options): add usernameFromContext
add `usernameFromContext` to extract username
1 parent da6b9c4 commit 48fd26c

File tree

4 files changed

+66
-44
lines changed

4 files changed

+66
-44
lines changed

src/authz.guard.ts

+42-37
Original file line numberDiff line numberDiff line change
@@ -6,65 +6,70 @@ import {
66
} from '@nestjs/common';
77
import { Reflector } from '@nestjs/core';
88
import { Observable } from 'rxjs';
9-
import { AUTHZ_ENFORCER, PERMISSIONS_METADATA } from './authz.constants';
9+
import {
10+
AUTHZ_ENFORCER,
11+
PERMISSIONS_METADATA,
12+
AUTHZ_MODULE_OPTIONS
13+
} from './authz.constants';
1014
import * as casbin from 'casbin';
1115
import { Permission } from './interfaces/permission.interface';
1216
import { UnauthorizedException } from '@nestjs/common';
1317
import { AuthPossession } from './types';
18+
import { AuthZModuleOptions } from './interfaces/authz-module-options.interface';
1419

1520
@Injectable()
1621
export class AuthZGuard implements CanActivate {
1722
constructor(
1823
private readonly reflector: Reflector,
19-
@Inject(AUTHZ_ENFORCER) public enforcer: casbin.Enforcer
24+
@Inject(AUTHZ_ENFORCER) private enforcer: casbin.Enforcer,
25+
@Inject(AUTHZ_MODULE_OPTIONS) private options: AuthZModuleOptions
2026
) {}
2127

2228
canActivate(
2329
context: ExecutionContext
2430
): boolean | Promise<boolean> | Observable<boolean> {
25-
const permissions: Permission[] = this.reflector.get<Permission[]>(
26-
PERMISSIONS_METADATA,
27-
context.getHandler()
28-
);
29-
30-
if (!permissions) {
31-
return true;
32-
}
33-
34-
const request = context.switchToHttp().getRequest();
35-
const user = request.user;
36-
if (!user) {
37-
throw new UnauthorizedException();
38-
}
31+
try {
32+
const permissions: Permission[] = this.reflector.get<Permission[]>(
33+
PERMISSIONS_METADATA,
34+
context.getHandler()
35+
);
3936

40-
const { username: uname } = user;
37+
if (!permissions) {
38+
return true;
39+
}
4140

42-
const hasPermission = (
43-
username: string,
44-
permission: Permission
45-
): boolean => {
46-
const { possession, resource, action } = permission;
47-
const poss = [];
41+
const username = this.options.usernameFromContext(context);
4842

49-
if (possession === AuthPossession.OWN_ANY) {
50-
poss.push(AuthPossession.ANY, AuthPossession.OWN);
51-
} else {
52-
poss.push(possession);
43+
if (!username) {
44+
throw new UnauthorizedException();
5345
}
5446

55-
return poss.some(p => {
56-
if (p === AuthPossession.OWN) {
57-
return (permission as any).isOwn(request);
47+
const hasPermission = (user: string, permission: Permission): boolean => {
48+
const { possession, resource, action } = permission;
49+
const poss = [];
50+
51+
if (possession === AuthPossession.OWN_ANY) {
52+
poss.push(AuthPossession.ANY, AuthPossession.OWN);
5853
} else {
59-
return this.enforcer.enforce(username, resource, `${action}:${p}`);
54+
poss.push(possession);
6055
}
61-
});
62-
};
6356

64-
const result = permissions.every(permission =>
65-
hasPermission(uname, permission)
66-
);
57+
return poss.some(p => {
58+
if (p === AuthPossession.OWN) {
59+
return (permission as any).isOwn(context);
60+
} else {
61+
return this.enforcer.enforce(user, resource, `${action}:${p}`);
62+
}
63+
});
64+
};
6765

68-
return result;
66+
const result = permissions.every(permission =>
67+
hasPermission(username, permission)
68+
);
69+
70+
return result;
71+
} catch (e) {
72+
throw e;
73+
}
6974
}
7075
}
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1+
import { ExecutionContext } from '@nestjs/common';
2+
13
export interface AuthZModuleOptions<T = any> {
24
model: string;
35
policy: string | Promise<T>;
6+
usernameFromContext: (context: ExecutionContext) => string;
47
}

src/types.ts

+15-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
export enum AuthAction {
1+
export enum AuthActionVerb {
22
CREATE = 'create',
33
UPDATE = 'update',
44
DELETE = 'delete',
@@ -10,3 +10,17 @@ export enum AuthPossession {
1010
OWN = 'own',
1111
OWN_ANY = 'own|any'
1212
}
13+
14+
export enum AuthAction {
15+
CREATE_ANY = 'create:any',
16+
CREATE_OWN = 'create:own',
17+
18+
UPDATE_ANY = 'update:any',
19+
UPDATE_OWN = 'update:own',
20+
21+
DELETE_ANY = 'delete:any',
22+
DELETE_OWN = 'delete:own',
23+
24+
READ_ANY = 'read:any',
25+
READ_OWN = 'read:own'
26+
}

test/use-permissions.decorator.spec.ts

+6-6
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,18 @@ import {
22
UsePermissions,
33
PERMISSIONS_METADATA,
44
Permission,
5-
AuthAction,
6-
AuthPossession,
5+
AuthActionVerb,
6+
AuthPossession
77
} from '../src';
88

99
describe('@UsePermissions()', () => {
1010
it('should set metadata correctly', () => {
1111
const permissions: Permission[] = [
1212
{
1313
resource: 'test',
14-
action: AuthAction.READ,
15-
possession: AuthPossession.ANY,
16-
},
14+
action: AuthActionVerb.READ,
15+
possession: AuthPossession.ANY
16+
}
1717
];
1818
class TestController {
1919
@UsePermissions(...permissions)
@@ -23,7 +23,7 @@ describe('@UsePermissions()', () => {
2323
}
2424
const res = Reflect.getMetadata(
2525
PERMISSIONS_METADATA,
26-
TestController.prototype.getData,
26+
TestController.prototype.getData
2727
);
2828
expect(res).toEqual(permissions);
2929
});

0 commit comments

Comments
 (0)