Skip to content

Commit 3411210

Browse files
author
vineet-suri
committed
RFIT-188 basic casbin flow with option for using casbin policy or giving provider with business logic
1 parent c166348 commit 3411210

14 files changed

+4800
-671
lines changed

package-lock.json

Lines changed: 4672 additions & 477 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@
5151
"@loopback/rest": "^1.16.3"
5252
},
5353
"dependencies": {
54+
"@sourceloop/core": "^1.0.0-alpha.9",
5455
"casbin": "^5.1.3",
5556
"casbin-pg-adapter": "^1.4.0",
5657
"lodash": "^4.17.15"
@@ -59,7 +60,7 @@
5960
"@loopback/boot": "^1.4.4",
6061
"@loopback/build": "^2.0.3",
6162
"@loopback/context": "^3.8.2",
62-
"@loopback/core": "^2.7.0",
63+
"@loopback/core": "^2.9.5",
6364
"@loopback/rest": "^1.16.3",
6465
"@loopback/testlab": "^1.6.3",
6566
"@loopback/tslint-config": "^2.1.0",

src/component.ts

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,44 @@
1-
import { Component, ProviderMap, Binding, inject } from '@loopback/core';
2-
1+
import { bind, Binding, Component, ContextTags, CoreBindings, inject, ProviderMap } from '@loopback/core';
2+
import { RestApplication } from '@loopback/rest';
33
import { AuthorizationBindings } from './keys';
44
import { AuthorizeActionProvider } from './providers/authorization-action.provider';
55
import { AuthorizationMetadataProvider } from './providers/authorization-metadata.provider';
6+
import { CasbinAuthorizationProvider } from './providers/casbin-authorization-action.provider';
67
import { UserPermissionsProvider } from './providers/user-permissions.provider';
78
import { AuthorizationConfig } from './types';
8-
import { CasbinAuthorizationProvider } from './providers/casbin-authorization-action.provider';
9-
import { CasbinAuthorizationMetadataProvider } from './providers/casbin-authorisation-metadata.provider';
10-
import { CasbinEnforcerProvider } from './providers';
119

10+
11+
@bind({ tags: { [ContextTags.KEY]: AuthorizationBindings.COMPONENT.key } })
1212
export class AuthorizationComponent implements Component {
1313
providers?: ProviderMap;
14-
bindings?: Binding[];
14+
bindings1?: Binding[];
1515

1616
constructor(
17+
@inject(CoreBindings.APPLICATION_INSTANCE)
18+
private readonly application: RestApplication,
1719
@inject(AuthorizationBindings.CONFIG)
1820
private readonly config?: AuthorizationConfig,
1921
) {
2022
this.providers = {
2123
[AuthorizationBindings.AUTHORIZE_ACTION.key]: AuthorizeActionProvider,
2224
[AuthorizationBindings.CASBIN_AUTHORIZE_ACTION.key]: CasbinAuthorizationProvider,
2325
[AuthorizationBindings.METADATA.key]: AuthorizationMetadataProvider,
24-
[AuthorizationBindings.CASBIN_METADATA.key]: CasbinAuthorizationMetadataProvider,
2526
[AuthorizationBindings.USER_PERMISSIONS.key]: UserPermissionsProvider,
26-
[AuthorizationBindings.CASBIN_ENFORCER.key]: CasbinEnforcerProvider,
2727
};
2828

29+
2930
if (
3031
config &&
3132
config.allowAlwaysPaths &&
3233
config.allowAlwaysPaths.length > 0
3334
) {
34-
this.bindings = [
35+
this.bindings1 = [
3536
Binding.bind(AuthorizationBindings.PATHS_TO_ALLOW_ALWAYS).to(
3637
config.allowAlwaysPaths,
3738
),
3839
];
3940
} else {
40-
this.bindings = [
41+
this.bindings1 = [
4142
Binding.bind(AuthorizationBindings.PATHS_TO_ALLOW_ALWAYS).to([
4243
'/explorer',
4344
]),
Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
1-
import {MethodDecoratorFactory} from '@loopback/core';
2-
import {AuthorizationMetadata} from '../types';
3-
import {AUTHORIZATION_METADATA_ACCESSOR} from '../keys';
1+
import { MethodDecoratorFactory } from '@loopback/core';
2+
import { AuthorizationMetadata } from '../types';
3+
import { AUTHORIZATION_METADATA_ACCESSOR } from '../keys';
44

5-
export function authorize(permissions: string[]) {
5+
export function authorize(metadata: AuthorizationMetadata) {
66
return MethodDecoratorFactory.createDecorator<AuthorizationMetadata>(
77
AUTHORIZATION_METADATA_ACCESSOR,
88
{
9-
permissions: permissions || [],
9+
permissions: metadata.permissions || [],
10+
resource: metadata.resource || '',
11+
isCasbinPolicy: metadata.isCasbinPolicy || false
1012
},
1113
);
1214
}

src/decorators/casbin-authorise.decorator.ts

Lines changed: 0 additions & 16 deletions
This file was deleted.

src/decorators/index.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1 @@
11
export * from './authorize.decorator';
2-
export * from './casbin-authorise.decorator';

src/keys.ts

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,22 @@ import {
55
AuthorizationMetadata,
66
UserPermissionsFn,
77
AuthorizationConfig,
8-
CasbinAuthorizationMetadata,
98
CasbinEnforcerFn,
109
CasbinAuthorizeFn,
10+
CasbinEnforcerConfigGetterFn,
1111
} from './types';
12+
import { CoreBindings } from '@loopback/core';
13+
import { AuthorizationComponent } from './component';
1214

1315
/**
1416
* Binding keys used by this component.
1517
*/
1618
export namespace AuthorizationBindings {
19+
20+
export const COMPONENT = BindingKey.create<AuthorizationComponent>(
21+
`${CoreBindings.COMPONENTS}.AuthorizationComponent`,
22+
);
23+
1724
export const AUTHORIZE_ACTION = BindingKey.create<AuthorizeFn>(
1825
'sf.userAuthorization.actions.authorize',
1926
);
@@ -26,10 +33,6 @@ export namespace AuthorizationBindings {
2633
'sf.userAuthorization.operationMetadata',
2734
);
2835

29-
export const CASBIN_METADATA = BindingKey.create<CasbinAuthorizationMetadata | undefined>(
30-
'sf.userCasbinAuthorization.operationMetadata',
31-
);
32-
3336
export const USER_PERMISSIONS = BindingKey.create<UserPermissionsFn<string>>(
3437
'sf.userAuthorization.actions.userPermissions',
3538
);
@@ -38,6 +41,10 @@ export namespace AuthorizationBindings {
3841
'sf.userCasbinAuthorization.casbinenforcer',
3942
);
4043

44+
export const CASBIN_ENFORCER_CONFIG_GETTER = BindingKey.create<CasbinEnforcerConfigGetterFn>(
45+
'sf.userCasbinAuthorization.casbinEnforcerConfigGetter',
46+
);
47+
4148
export const CONFIG = BindingKey.create<AuthorizationConfig>(
4249
'sf.userAuthorization.config',
4350
);
@@ -53,7 +60,4 @@ export const AUTHORIZATION_METADATA_ACCESSOR = MetadataAccessor.create<
5360
MethodDecorator
5461
>('sf.userAuthorization.accessor.operationMetadata');
5562

56-
export const CASBIN_AUTHORIZATION_METADATA_ACCESSOR = MetadataAccessor.create<
57-
CasbinAuthorizationMetadata,
58-
MethodDecorator
59-
>('sf.userCasbinAuthorization.accessor.operationMetadata');
63+

src/policy.csv

Whitespace-only changes.

src/providers/casbin-authorisation-metadata.provider.ts

Lines changed: 0 additions & 36 deletions
This file was deleted.
Lines changed: 56 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,58 +1,86 @@
1-
import { inject, Provider, Getter, InvocationContext } from '@loopback/core';
1+
import { Getter, inject, Provider } from '@loopback/core';
2+
import { IAuthUserWithPermissions } from '@sourceloop/core';
23
import * as casbin from 'casbin';
3-
import { CasbinAuthorizeFn, CasbinAuthorizationMetadata } from '../types';
4+
import * as fs from 'fs';
5+
import * as path from 'path';
46
import { AuthorizationBindings } from '../keys';
7+
import { AuthorizationMetadata, CasbinAuthorizeFn, CasbinEnforcerConfigGetterFn } from '../types';
8+
const fsPromises = fs.promises;
9+
510
const DEFAULT_SCOPE = 'execute';
611

712
export class CasbinAuthorizationProvider implements Provider<CasbinAuthorizeFn> {
813
constructor(
9-
@inject.getter(AuthorizationBindings.CASBIN_METADATA)
10-
private readonly getCasbinMetadata: Getter<CasbinAuthorizationMetadata>,
11-
private readonly invocationCtx: InvocationContext
14+
@inject.getter(AuthorizationBindings.METADATA)
15+
private readonly getCasbinMetadata: Getter<AuthorizationMetadata>,
16+
@inject.getter(AuthorizationBindings.CASBIN_ENFORCER_CONFIG_GETTER)
17+
private readonly getCasbinEnforcerConfig: Getter<CasbinEnforcerConfigGetterFn>,
1218
) { }
1319

1420
value(): CasbinAuthorizeFn {
15-
return (response, req) => this.action(response, req);
21+
return (response) => this.action(response);
1622
}
1723

18-
async action(enforcer: casbin.Enforcer, userId: string): Promise<boolean> {
24+
async action(user: IAuthUserWithPermissions): Promise<boolean> {
25+
let authDecision = false;
26+
try {
27+
const metadata: AuthorizationMetadata = await this.getCasbinMetadata();
1928

20-
// await enforcer.loadPolicy();
29+
const subject = this.getUserName(`${user.id}`);
2130

22-
const metadata: CasbinAuthorizationMetadata = await this.getCasbinMetadata();
31+
const object = metadata.resource;
2332

24-
console.log(this.invocationCtx);
33+
const action = metadata.permissions && metadata.permissions.length > 0 ? metadata.permissions[0] : DEFAULT_SCOPE;
2534

26-
const subject = this.getUserName(userId);
35+
const fn = await this.getCasbinEnforcerConfig();
2736

28-
const object = metadata.resource;
37+
const result = await fn(user, metadata.resource);
2938

30-
const action = metadata.scopes && metadata.scopes.length > 0 ? metadata.scopes[0] : DEFAULT_SCOPE;
39+
let enforcer: casbin.Enforcer;
3140

32-
const request = {
33-
subject,
34-
object,
35-
action,
36-
};
41+
if (metadata.isCasbinPolicy) {
42+
enforcer = await casbin.newEnforcer(result.model, result.policy);
43+
} else if (!metadata.isCasbinPolicy && result.allowedRes) {
44+
const policy = this.createCasbinPolicy(result.allowedRes, subject, action);
45+
const baseDir = path.join(__dirname, '../../src/policy.csv');
46+
await fsPromises.writeFile(baseDir, policy);
3747

38-
const allowedRoles = metadata.allowedRoles;
48+
enforcer = await casbin.newEnforcer(result.model, baseDir);
49+
} else {
50+
return false;
51+
}
3952

40-
if (!allowedRoles) return true;
41-
if (allowedRoles.length < 1) return false;
53+
authDecision = await enforcer.enforce(
54+
subject,
55+
object,
56+
action,
57+
);
58+
}
4259

43-
const allowedByRole = await enforcer.enforce(
44-
request.subject,
45-
request.object,
46-
request.action,
47-
);
60+
catch (err) {
61+
console.log(err);
62+
}
4863

49-
return allowedByRole;
64+
return authDecision;
5065
}
5166

5267
// Generate the user name according to the naming convention
5368
// in casbin policy
54-
// A user's name would be `u${id}`
69+
// A user's name would be `u${ id }`
5570
getUserName(id: string): string {
5671
return `u${id}`;
5772
}
73+
74+
createCasbinPolicy(allowedRes: string[], subject: string, action: string): string {
75+
//Expected format for allowedRes: ['ping', 'ping2', 'ping3'];
76+
77+
let result = '';
78+
allowedRes.forEach(res => {
79+
const policy = `p, ${subject}, ${res}, ${action}
80+
`;
81+
result += policy;
82+
})
83+
84+
return result;
85+
}
5886
}

0 commit comments

Comments
 (0)