Skip to content

Commit 1e8a1bb

Browse files
author
vineet-suri
committed
RFIT-188 read me updated, and few changes in authorise action provider
1 parent 48e97a2 commit 1e8a1bb

File tree

5 files changed

+204
-103
lines changed

5 files changed

+204
-103
lines changed

README.md

Lines changed: 106 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@ It provides three ways of integration
2121
2. **Role based permissions** - Permissions are associated to roles and users have a specific role attached. This actually reduces redundancy in DB a lot, as most of the time, users will have many common permissions. If that is not the case for you, then, use method #1 above.
2222
3. **Role based permissions with user level override** - This is the most flexible architecture. In this case, method #2 is implemented as is. On top of it, we also add user-level permissions override, allow/deny permissions over role permissions. So, say there is user who can perform all admin role actions except he cannot remove users from the system. So, DeleteUser permission can be denied at user level and role can be set as Admin for the user.
2323

24+
As a further enhancement to these methods, we are using [casbin library!](https://casbin.org/docs/en/overview) to define permissions at level of entity or resource associated with an API call. Casbin authorisation implementation can be performed in two ways:
25+
1. **Using default casbin policy document** - Define policy document in default casbin format in the app, and configure authorise decorator to use those policies.
26+
2. **Defining custom logic to form dynamic policies** - Implement dynamic permissions based on app logic in casbin-enforcer-config provider. Authorisation extension will dynamically create casbin policy using this business logic to give the authorisation decisions.
27+
2428
Refer to the usage section below for details on integration
2529

2630
## Install
@@ -37,13 +41,21 @@ For a quick starter guide, you can refer to our [loopback 4 starter](https://git
3741

3842
In order to use this component into your LoopBack application, please follow below steps.
3943

40-
- Add component to application.
44+
- Add component to application. Also add providers to implement casbin authorisation.
4145

4246
```ts
4347
this.bind(AuthorizationBindings.CONFIG).to({
4448
allowAlwaysPaths: ['/explorer'],
4549
});
4650
this.component(AuthorizationComponent);
51+
52+
this.bind(AuthorizationBindings.CASBIN_ENFORCER_CONFIG_GETTER).toProvider(
53+
CasbinEnforcerConfigProvider,
54+
);
55+
56+
this.bind(AuthorizationBindings.CASBIN_RESOURCE_MODIFIER_FN).toProvider(
57+
CasbinResValModifierProvider,
58+
);
4759
```
4860

4961
- If using method #1 from above, implement Permissions interface in User model and add permissions array.
@@ -117,6 +129,81 @@ export class User extends Entity implements UserPermissionsOverride<string> {
117129
}
118130
```
119131

132+
- Implement the **Casbin Resource value modifier provider**. Customise the resource value based on business logic using route arguments parameter in the provider.
133+
134+
```ts
135+
import {Getter, inject, Provider} from '@loopback/context';
136+
import {HttpErrors} from '@loopback/rest';
137+
import {
138+
AuthorizationBindings,
139+
AuthorizationMetadata,
140+
CasbinResourceModifierFn
141+
} from 'loopback4-authorization';
142+
143+
export class CasbinResValModifierProvider
144+
implements Provider<CasbinResourceModifierFn> {
145+
constructor(
146+
@inject.getter(AuthorizationBindings.METADATA)
147+
private readonly getCasbinMetadata: Getter<AuthorizationMetadata>,
148+
) {}
149+
150+
value(): CasbinResourceModifierFn {
151+
return (pathParams: string[]) => this.action(pathParams);
152+
}
153+
154+
async action(pathParams: string[]): Promise<string> {
155+
const metadata: AuthorizationMetadata = await this.getCasbinMetadata();
156+
if (!metadata) {
157+
throw new HttpErrors.InternalServerError(`Metadata object not found`);
158+
}
159+
const res = metadata.resource;
160+
161+
// Now modify the resource parameter using on path params, as per business logic.
162+
// Returning resource value as such for default case.
163+
164+
return `${res}`;
165+
}
166+
}
167+
168+
```
169+
- Implement the **casbin enforcer config provider** . Provide the casbin model path. In case 1 of using [default casbin format policy!](https://casbin.org/docs/en/how-it-works), provide the casbin policy path. In other case of creating dynamic policy, provide the array of Resource-Permission objects for a given user, based on business logic.
170+
171+
```ts
172+
import {Provider} from '@loopback/context';
173+
import {CasbinConfig, CasbinEnforcerConfigGetterFn, IAuthUserWithPermissions} from 'loopback4-authorization';
174+
import * as path from 'path';
175+
176+
export class CasbinEnforcerConfigProvider
177+
implements Provider<CasbinEnforcerConfigGetterFn> {
178+
constructor() {}
179+
180+
value(): CasbinEnforcerConfigGetterFn {
181+
return (authUser: IAuthUserWithPermissions, resource: string, isCasbinPolicy?: boolean) =>
182+
this.action(authUser, resource, isCasbinPolicy);
183+
}
184+
185+
async action(authUser: IAuthUserWithPermissions, resource: string, isCasbinPolicy?: boolean): Promise<CasbinConfig> {
186+
const model = path.resolve(
187+
__dirname,
188+
'./../../fixtures/casbin/model.conf',
189+
);
190+
191+
// Write business logic to find out the allowed resource-permission sets for this user. Below is a dummy value.
192+
//const allowedRes = [{resource: 'session', permission: "CreateMeetingSession"}];
193+
194+
const policy = path.resolve(__dirname, './../../fixtures/casbin/policy.csv');
195+
196+
const result: CasbinConfig = {
197+
model,
198+
//allowedRes,
199+
policy
200+
}
201+
return result;
202+
}
203+
}
204+
```
205+
206+
120207
- For method #3, we also provide a simple provider function [_AuthorizationBindings.USER_PERMISSIONS_](<[./src/providers/user-permissions.provider.ts](https://github.com/sourcefuse/loopback4-authorization/blob/master/src/providers/user-permissions.provider.ts)>) to evaluate the user permissions based on its role permissions and user-level overrides. Just inject it
121208

122209
```ts
@@ -130,6 +217,16 @@ and invoke it
130217
const permissions = this.getUserPermissions(user.permissions, role.permissions);
131218
```
132219

220+
221+
- Add the dependency injections for resource value modifer provider, and casbin authorisation function in the sequence.ts
222+
223+
```ts
224+
@inject(AuthorizationBindings.CASBIN_AUTHORIZE_ACTION)
225+
protected checkAuthorisation: CasbinAuthorizeFn,
226+
@inject(AuthorizationBindings.CASBIN_RESOURCE_MODIFIER_FN)
227+
protected casbinResModifierFn: CasbinResourceModifierFn,
228+
```
229+
133230
- Add a step in custom sequence to check for authorization whenever any end
134231
point is hit.
135232

@@ -186,14 +283,13 @@ export class MySequence implements SequenceHandler {
186283
await this.authenticateRequestClient(request);
187284
const authUser: User = await this.authenticateRequest(request);
188285

189-
// Do ths if you are using method #3
190-
const permissions = this.getUserPermissions(
191-
authUser.permissions,
192-
authUser.role.permissions,
193-
);
194-
// This is the important line added for authorization. Needed for all 3 methods
286+
// Invoke Resource value modifier
287+
const resVal = await this.casbinResModifierFn(args);
288+
289+
// Check authorisation
195290
const isAccessAllowed: boolean = await this.checkAuthorisation(
196-
permissions, // do authUser.permissions if using method #1
291+
authUser,
292+
resVal,
197293
request,
198294
);
199295
// Checking access to route here
@@ -213,10 +309,10 @@ export class MySequence implements SequenceHandler {
213309
The above sequence also contains user authentication using [loopback4-authentication](https://github.com/sourcefuse/loopback4-authentication) package. You can refer to the documentation for the same for more details.
214310

215311
- Now we can add access permission keys to the controller methods using authorize
216-
decorator as below.
312+
decorator as below. Set isCasbinPolicy parameter to use casbin default policy format. Default is false.
217313

218314
```ts
219-
@authorize(['CreateRole'])
315+
@authorize({permissions: ['CreateRole'], resource:'role', isCasbinPolicy: true})
220316
@post(rolesPath, {
221317
responses: {
222318
[STATUS_CODE.OK]: {

package-lock.json

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

0 commit comments

Comments
 (0)