Skip to content

Commit

Permalink
Fix decorators and add AppRbacGuard
Browse files Browse the repository at this point in the history
  • Loading branch information
hhfrancois committed Dec 8, 2023
1 parent 641e640 commit d7a0c00
Show file tree
Hide file tree
Showing 9 changed files with 125 additions and 9 deletions.
117 changes: 116 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,117 @@
# granted
# GRANTED Module for Nestjs

[![Build hhnest/granted](https://github.com/hhnest/granted/actions/workflows/main.yml/badge.svg)](https://github.com/hhnest/granted/actions/workflows/main.yml)

[![Publish hhnest/granted to NPM](https://github.com/hhnest/granted/actions/workflows/tag.yml/badge.svg)](https://github.com/hhnest/granted/actions/workflows/tag.yml)

[![npm version](https://badge.fury.io/js/@hhnest%2Fgranted.svg)](https://badge.fury.io/js/@hhnest%2Fgranted)

This module allow you to use RBAC security on you endpoint nestjs

## Install @hhnest/granted

You can use either the npm or yarn command-line tool to install the `package`.
Use whichever is appropriate for your project in the examples below.

### NPM

```bash
# @hhnest/granted
npm install @hhnest/granted --save
```

### YARN

```bash
# @hhnest/granted
yarn add @hhnest/granted
```

### Peer dependence

| name | version |
|---|---|
| @nestjs/common | ^10.0.0 |
| @nestjs/core | ^10.0.0 |
| @nestjs/platform-express | ^10.0.0 |

## Configuration

Just import the module `GrantedModule`, specify the implementation for fetch username, roles and you can use the `annotations`.

`AppModule.ts`
```typescript
@Module({
imports: [
GrantedModule.forRoot({apply: environment.applyAuthGuard}),
],
providers: [
{ provide: APP_GUARD, useClass: AppRbacGuard },
],
})
export class AppModule {}
```


## Use

### Inject informations

The module allow you to inject informations in your endpoints:

```typescript
@Get('username')
username(@Username() userId: string): Observable<string> {
return of(userId);
}

@Get('roles')
roles(@Roles() roles: string[]): Observable<string[]> {
return of(roles);
}

@Get('groups')
groups(@Groups() groups: string[]): Observable<string[]> {
return of(groups);
}
@Get('locale')
groups(@Locale() locale: string): Observable<string> {
return of(locale);
}
```

### Secure endpoints

```typescript
@Get('username')
@GrantedTo(and(isAuthenticated(), hasRole('GET_USERNAME'))
username(@Username() userId: string): Observable<string> {
return of(userId);
}
```
### Details
```typescript
GrantedTo(...booleanSpecs: BooleanSpec[])
```
```typescript
// AndSpec
and(...booleanSpecs: BooleanSpec[]): BooleanSpec
// IsTrueSpec
isTrue(): BooleanSpec
// HasRoleSpec
hasRole(role: string): BooleanSpec
// IsAuthenticatedSpec
isAuthenticated(): BooleanSpec
// IsFalseSpec
isFalse(): BooleanSpec
// NotSpec
not(booleanSpec: BooleanSpec): BooleanSpec
// OrSpec
or(...booleanSpecs: BooleanSpec[]): BooleanSpec
// IsUserSpec
isUser(type: 'Param' | 'Query' | 'Body', field?: string): BooleanSpec
```
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
},
"scripts": {
"build": "nest build ; cp package.json dist/package.json ; cp README.md dist/README.md",
"cp:panoptes-srv": "cp -r dist/* ../../hhdevelopment/panoptes-srv/node_modules/@hhnest/granted/",
"format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\" \"libs/**/*.ts\"",
"lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix"
},
Expand Down
2 changes: 1 addition & 1 deletion src/decorators/groups.decorator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@ import { IncomingMessage } from 'http';
export const Groups = createParamDecorator(
(config: void, ctx: ExecutionContext) => {
const incomingMessage: IncomingMessage = ctx.switchToHttp().getRequest<IncomingMessage>();
return (incomingMessage.headers['groups'] as string || '').split(',').map((group: string) => group.trim());
return JSON.parse(incomingMessage.headers['groups'] as string || '[]');
},
);
2 changes: 1 addition & 1 deletion src/decorators/locale.decorator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@ import { IncomingMessage } from 'http';
export const Locale = createParamDecorator(
(data: string, ctx: ExecutionContext) => {
const incomingMessage: IncomingMessage = ctx.switchToHttp().getRequest<IncomingMessage>();
return (incomingMessage.headers['accept-language'] || 'en-us');
return (incomingMessage.headers['accept-language'] || 'en');
},
);
2 changes: 1 addition & 1 deletion src/decorators/roles.decorator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@ import { IncomingMessage } from 'http';
export const Roles = createParamDecorator(
(config: void, ctx: ExecutionContext) => {
const incomingMessage: IncomingMessage = ctx.switchToHttp().getRequest<IncomingMessage>();
return (incomingMessage.headers['roles'] as string || '').split(',').map((role: string) => role.trim());
return JSON.parse(incomingMessage.headers['roles'] as string || '[]');
},
);
4 changes: 2 additions & 2 deletions src/granted.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ export class GrantedModule {
return {
module: GrantedModule,
providers: [
{ provide: "GRANTED_MODULE_OPTIONS", useValue: opts }
{ provide: 'GRANTED_MODULE_OPTIONS', useValue: opts }
],

exports: ['GRANTED_MODULE_OPTIONS']
};
}
}
2 changes: 1 addition & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
export * from './granted.module';
export * from './decorators';
export * from './security/boolean-spec';
export * from './security/granted.guard';
export * from './security/app-rbac.guard';
export * from './models/granted-module-options';
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { GrantedModuleOptions } from 'src/models/granted-module-options';
import { BooleanSpec } from './boolean-spec';

@Injectable()
export class Granted implements CanActivate {
export class AppRbacGuard implements CanActivate {
constructor(
@Inject('GRANTED_MODULE_OPTIONS') private readonly options: GrantedModuleOptions,
private reflector: Reflector
Expand Down
2 changes: 1 addition & 1 deletion src/security/boolean-spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ export function or(...booleanSpecs: BooleanSpec[]): BooleanSpec {
}};
}
// IsUserSpec
export function isUser(type: 'Param' | 'Query' | 'Body', field: string) {
export function isUser(type: 'Param' | 'Query' | 'Body', field: string): BooleanSpec {
return {id: `isUser(${type}, ${field})`, apply: (request: Request, username: string, roles: string[]): boolean => {
let user: string = '';
if (type === 'Param') {
Expand Down

0 comments on commit d7a0c00

Please sign in to comment.