Как заблокировать метод декоратором в Loopback V4 NodeJS - PullRequest
0 голосов
/ 02 июля 2019

В этом методе декоратор secured имеет опцию DENY_ALL. Таким образом, он должен обеспечить блокировку этого метода для запуска. Декоратор возвращает json со статусом http и блокирует метод. Как я могу заблокировать этот метод для запуска?

API МЕТОД

export class PingController {
  constructor(
    @inject(RestBindings.Http.REQUEST) private req: Request,
    @inject(AuthenticationBindings.CURRENT_USER, { optional: true }) private currentUser: JWTUser,
  ) { }

  @get('/ping/deny-all')
  @secured(SecuredType.DENY_ALL) // this decorator returns a response
  testDenyAll() {
    console.log("this method should be blocked by decorator");
    console.log("this method should not run");
    return { message: 'denyAll: OK' };
  }
}

АУТЕНТИФИКАЦИЯ

// the JWT_secret to encrypt and decrypt JWT token
export const JWT_SECRET = TOKEN_KEY.SECRET;

// the decorator function, every required param has its own default
// so we can supply empty param when calling this decorartor.
// we will use 'secured' to match Spring Security annotation.
export function secured(
    type: SecuredType = SecuredType.IS_AUTHENTICATED, // more on this below
    roles: string[] = [],
    strategy: string = 'jwt',
    options?: object,
) {
    // we will use a custom interface. more on this below
    console.log("secured");
    return MethodDecoratorFactory.createDecorator<MyAuthenticationMetadata>(AUTHENTICATION_METADATA_KEY, {
        type,
        strategy,
        options,
    });
}

// enum for available secured type,
export enum SecuredType {
    DENY_ALL, // you shall not pass!
    PERMIT_ALL, // bypass security check, permit everyone
    IS_AUTHENTICATED, // any authenticated user
    IS_AUTHORIZED, // any authorized user
}

// extended interface of the default AuthenticationMetadata which only has `strategy` and `options`
export interface MyAuthenticationMetadata extends AuthenticationMetadata {
    type: SecuredType;
}

// metadata provider for `MyAuthenticationMetadata`. Will supply method's metadata when injected
export class MyAuthMetadataProvider extends AuthMetadataProvider {
    constructor(
        @inject(CoreBindings.CONTROLLER_CLASS, { optional: true }) protected _controllerClass: Constructor<{}>,
        @inject(CoreBindings.CONTROLLER_METHOD_NAME, { optional: true }) protected _methodName: string,
    ) {
        super(_controllerClass, _methodName);
    }

    value(): MyAuthenticationMetadata | undefined {
        console.log("MyAuthMetadataProvider value");
        if (!this._controllerClass || !this._methodName) {
            return;
        }
        return MetadataInspector.getMethodMetadata<MyAuthenticationMetadata>(
            AUTHENTICATION_METADATA_KEY,
            this._controllerClass.prototype,
            this._methodName
        );
    }
}

// implement custom namespace bindings
export namespace MyAuthBindings {
    export const STRATEGY = BindingKey.create<Strategy | undefined>('authentication.strategy');
}

// the strategy provider will parse the specifed strategy, and act accordingly
export class MyAuthStrategyProvider implements Provider<Strategy | undefined> {
    constructor(
        @inject(AuthenticationBindings.METADATA) private metadata: MyAuthenticationMetadata,
        @inject(RestBindings.Http.REQUEST) private request: Request,
        @inject(RestBindings.Http.RESPONSE) protected response: Response,
        @repository(UserRepository) private userRepository: UserRepository,
        @inject('tokenKey.provider') private tokenKeyProvider: TokenKeyProvider
    ) { }

    value(): ValueOrPromise<Strategy | undefined> {
        console.log("MyAuthStrategyProvider value");
        if (!this.metadata) return;

        const { strategy } = this.metadata;
        if (strategy === 'jwt') {
            return new JwtStrategy(
                {
                    secretOrKey: JWT_SECRET,
                    jwtFromRequest: ExtractJwt.fromAuthHeaderWithScheme('jwt'),
                },
                (payload, done) => this.verifyToken(payload, done)
            );
        }
    }

    async verifyToken(
        payload: any,
        done: (err: Error | null, user?: JWTUser | false, info?: Object) => void
    ) {
        const { type } = this.metadata;

        const userId: string = payload.id;
        const user: User | null = await this.userRepository.findOne({ where: { id: userId } });
        if (!user) {
            this.response.status(404).json({
                message: 'User not exist',
            });
            return;
        } else {
            if (type === SecuredType.IS_AUTHORIZED && !(await this.checkAuthorization(<User>user))) {
                this.response.status(401).json({
                    message: 'Unauthorized',
                });
            } else {
                done(null,
                    {
                        id: user.id,
                        email: user.email,
                        username: user.username,
                        firstName: user.firstName,
                        lastName: user.lastName,
                        gender: user.gender
                    }
                );
            }
        }
    }

    // verify user's role based on the SecuredType
    async checkAuthorization(user: User): Promise<boolean> {
        return true;
    }
}

// the entry point for authentication.
export class MyAuthActionProvider implements Provider<AuthenticateFn> {
    constructor(
        @inject.getter(MyAuthBindings.STRATEGY) readonly getStrategy: Getter<Strategy>,
        @inject.setter(AuthenticationBindings.CURRENT_USER) readonly setCurrentUser: Setter<JWTUser>,
        @inject.getter(AuthenticationBindings.METADATA) readonly getMetadata: Getter<MyAuthenticationMetadata>,
        @inject(RestBindings.Http.RESPONSE) protected response: Response,
    ) {
    }

    value(): AuthenticateFn {
        return request => this.action(request);
    }

    async action(request: Request): Promise<JWTUser | undefined> {
        const metadata = await this.getMetadata();
        if (metadata) {
            // ACCESS CONTROL
            if (metadata.type === SecuredType.DENY_ALL) { // ACCESS DENIED
                this.response.status(403).json({
                    message: 'Forbidden',
                    statusCode: '403'
                });
                return;
            } else if (
                [
                    SecuredType.IS_AUTHENTICATED,
                    SecuredType.IS_AUTHORIZED,
                    SecuredType.PERMIT_ALL
                ].includes(metadata.type)
            ) {
                const strategy = await this.getStrategy();
                if (!strategy) return;
                const strategyAdapter = new StrategyAdapter(strategy);
                const user = await strategyAdapter.authenticate(request);
                this.setCurrentUser(user);
                return user;
            }
        }
    }
}
...