В этом методе декоратор 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;
}
}
}
}