From dda409730b43c48b73109bcd62ef5db41f1b031e Mon Sep 17 00:00:00 2001 From: Muhammad Aaqil Date: Thu, 6 Jun 2024 12:37:17 +0500 Subject: [PATCH] feat: support authentication for rest-crud based APIs Signed-off-by: Muhammad Aaqil --- packages/rest-crud/README.md | 12 +++++++- .../rest-crud/src/crud-rest.controller.ts | 30 ++++++++++++++++++- 2 files changed, 40 insertions(+), 2 deletions(-) diff --git a/packages/rest-crud/README.md b/packages/rest-crud/README.md index 50d47a545743..ef193bc817ef 100644 --- a/packages/rest-crud/README.md +++ b/packages/rest-crud/README.md @@ -43,7 +43,7 @@ export class TryApplication extends BootMixin( Create a new file for the configuration, e.g. `src/model-endpoints/product.rest-config.ts` that defines the `model`, -`pattern`, `dataSource`, `basePath`, and `readonly` properties: +`pattern`, `dataSource`, `basePath`, `readonly` and `auth` properties: ```ts import {ModelCrudRestApiConfig} from '@loopback/rest-crud'; @@ -55,6 +55,16 @@ module.exports = { dataSource: 'db', basePath: '/products', readonly: false, + auth: { + count: true, + get: true, + getById: true, + post: true, + patch: true, + patchById: true, + putById: true, + deleteById: true, + }, }; ``` diff --git a/packages/rest-crud/src/crud-rest.controller.ts b/packages/rest-crud/src/crud-rest.controller.ts index 0b8446712c9c..a69578577590 100644 --- a/packages/rest-crud/src/crud-rest.controller.ts +++ b/packages/rest-crud/src/crud-rest.controller.ts @@ -33,6 +33,7 @@ import { SchemaObject, } from '@loopback/rest'; import assert from 'assert'; +import {authenticate} from '@loopback/authentication'; // Ideally, this file should simply `export class CrudRestController<...>{}` // Unfortunately, that's not possible for several reasons. @@ -105,6 +106,19 @@ export interface CrudRestControllerOptions { * Whether to generate readonly APIs */ readonly?: boolean; + /** + * Whether to add authentication decorator or not + */ + auth?: { + count?: boolean; + get?: boolean; + getById?: boolean; + post?: boolean; + patch?: boolean; + patchById?: boolean; + putById?: boolean; + deleteById?: boolean; + }; } /** @@ -154,7 +168,7 @@ export function defineCrudRestController< constructor( public readonly repository: EntityCrudRepository, ) {} - + @authenticatedMethod(options.auth?.get) @get('/', { ...response.array(200, `Array of ${modelName} instances`, modelCtor, { includeRelations: true, @@ -172,6 +186,7 @@ export function defineCrudRestController< includeRelations: true, }), }) + @authenticatedMethod(options.auth?.getById) async findById( @param(idPathParam) id: IdType, @param.query.object( @@ -186,6 +201,7 @@ export function defineCrudRestController< @get('/count', { ...response(200, `${modelName} count`, {schema: CountSchema}), }) + @authenticatedMethod(options.auth?.count) async count( @param.where(modelCtor) where?: Where, @@ -205,6 +221,7 @@ export function defineCrudRestController< @post('/', { ...response.model(200, `${modelName} instance created`, modelCtor), }) + @authenticatedMethod(options?.auth?.post) async create( @body(modelCtor, { title: `New${modelName}`, @@ -224,6 +241,7 @@ export function defineCrudRestController< schema: CountSchema, }), }) + @authenticatedMethod(options?.auth?.patch) async updateAll( @body(modelCtor, {partial: true}) data: Partial, @param.where(modelCtor) @@ -242,6 +260,7 @@ export function defineCrudRestController< '204': {description: `${modelName} was updated`}, }, }) + @authenticatedMethod(options?.auth?.patchById) async updateById( @param(idPathParam) id: IdType, @body(modelCtor, {partial: true}) data: Partial, @@ -259,6 +278,7 @@ export function defineCrudRestController< '204': {description: `${modelName} was updated`}, }, }) + @authenticatedMethod(options.auth?.putById) async replaceById( @param(idPathParam) id: IdType, @body(modelCtor) data: T, @@ -271,6 +291,7 @@ export function defineCrudRestController< '204': {description: `${modelName} was deleted`}, }, }) + @authenticatedMethod(options.auth?.deleteById) async deleteById(@param(idPathParam) id: IdType): Promise { await this.repository.deleteById(id); } @@ -361,3 +382,10 @@ namespace response { }); } } + +// Helper function to conditionally add @authenticate decorator +function authenticatedMethod(applyAuth: boolean | undefined) { + return applyAuth + ? authenticate('jwt') + : (target: Object, key: string, descriptor: PropertyDescriptor) => {}; +}