Я нашел один способ вызывать обработчик авторизации для каждого запроса. Это все еще не совсем правильно, поэтому, возможно, есть лучшее решение.
В application.ts
вы можете настроить метаданные авторизации по умолчанию и предоставить более простой voter
, который всегда голосует DENY
. После этого все вызовы контроллера вызовут обработчики авторизации, независимо от того, присутствует ли декоратор @authorize()
или нет. Вот настройка:
// setup authorization
const noWayJose = (): Promise<AuthorizationDecision> => {
return new Promise(resolve => {
resolve(AuthorizationDecision.DENY);
});
};
this.component(AuthorizationComponent);
this.configure(AuthorizationBindings.COMPONENT).to({
defaultDecision: AuthorizationDecision.DENY,
precedence: AuthorizationDecision.ALLOW,
defaultMetadata: {
voters: [noWayJose],
},
});
this.bind('authorizationProviders.my-authorization-provider')
.toProvider(MyAuthorizationProvider)
.tag(AuthorizationTags.AUTHORIZER);
Теперь конечная точка /nope
в контроллере будет обрабатываться обработчиками авторизации даже без декоратора.
export class YoController {
constructor() {}
@authorize({scopes: ['IS_COOL', 'IS_OKAY']})
@get('/yo')
yo(@inject(SecurityBindings.USER) user: UserProfile): string {
return `yo, ${user.name}!`;
}
@authorize({allowedRoles: [EVERYONE]})
@get('/sup')
sup(): string {
return `sup, dude.`;
}
@get('/nope')
nope(): string {
return `sorry dude.`;
}
@authorize({allowedRoles: [EVERYONE]})
@get('/yay')
yay(
@inject(SecurityBindings.USER, {optional: true}) user: UserProfile,
): string {
if (user) {
return `yay ${user.name}!`;
}
return `yay!`;
}
}
Другая вещь, которую вам нужно сделать, это не выдает ошибку, когда аутентификация не может найти пользователя. Это происходит потому, что авторизация не выполняется до тех пор, пока функция invoke()
не вызовет все перехватчики. Поэтому вы должны проглотить эту ошибку и позволить авторизации сказать:
// from sequence.ts
async handle(context: RequestContext) {
try {
const {request, response} = context;
const route = this.findRoute(request);
//call authentication action
console.log(`request path = ${request.path}`);
try {
await this.authenticateRequest(request);
} catch (authenticationError) {
if (authenticationError.code === USER_PROFILE_NOT_FOUND) {
console.log(
"didn't find user. let's wait and see what authorization says.",
);
} else {
throw authenticationError;
}
}
// Authentication step done, proceed to invoke controller
const args = await this.parseParams(request, route);
// Authorization happens within invoke()
const result = await this.invoke(route, args);
this.send(response, result);
} catch (error) {
if (
error.code === AUTHENTICATION_STRATEGY_NOT_FOUND ||
error.code === USER_PROFILE_NOT_FOUND
) {
Object.assign(error, {statusCode: 401 /* Unauthorized */});
}
this.reject(context, error);
}
}
Это все подходит для моего варианта использования. Я хотел, чтобы в глобальных значениях по умолчанию все конечные точки были заблокированы при наличии декораторов @authenticate
и @authorize()
. Я планирую добавить @authorize()
только в те места, где я хочу открыть вещи. Это потому, что я собираюсь автоматически сгенерировать тонну контроллеров и захочу выставить только часть конечных точек вручную.