docs/site/Authorization-component-authorizer.md
Users are expected to program policies that enforce access control in two of the following options:
Authorizer functions
Voter functions
Usually the
authorizefunctions are bound through a provider as below
AuthorizationContext parameter of the authorize function contains the
current principal (in the example given above,that would be the current user
invoking cancelOrder) and details of the invoked endpoint.
AuthorizationMetadata parameter of the authorize function contains
all the details provided in the invoked method's decorator.class MyAuthorizationProvider implements Provider<Authorizer> {
/**
* @returns an authorizer function
*
*/
value(): Authorizer {
return this.authorize.bind(this);
}
async authorize(
context: AuthorizationContext,
metadata: AuthorizationMetadata,
) {
events.push(context.resource);
if (
context.resource === 'OrderController.prototype.cancelOrder' &&
context.principals[0].name === 'user-01'
) {
return AuthorizationDecision.DENY;
}
return AuthorizationDecision.ALLOW;
}
}
the
authorizefunction is then tagged to an application asAuthorizationTags.AUTHORIZERas below.
{% include code-caption.html content="src/application.ts" %}
export class MyApplication extends BootMixin(
ServiceMixin(RepositoryMixin(RestApplication)),
) {
constructor(options: ApplicationConfig = {}) {
super(options);
// mount authorization component
this.component(AuthorizationComponent);
// bind the authorizer provider
this.bind('authorizationProviders.my-authorizer-provider')
.toProvider(MyAuthorizationProvider)
.tag(AuthorizationTags.AUTHORIZER);
}
}
authorize() functions.
authorize(AuthorizationContext, AuthorizationMetadata) function in the
provider class is expected to be called by the Authorization Interceptor
which is called for every API endpoint decorated with @authorize().AuthorizationTags.AUTHORIZER (and also the voters listed in the
@authorize decorator per endpoint) and calls the functions one after
another.authorize() function is expected to return an object of type
AuthorizationDecision. If the type returned is
AuthorizationDecision.ALLOW the current Principal has passed the
executed authorize() function's criteria.Voter functions are directly provided in the decorator of the remote method
async function compareId(
authorizationCtx: AuthorizationContext,
metadata: MyAuthorizationMetadata,
) {
let currentUser: UserProfile;
if (authorizationCtx.principals.length > 0) {
const user = _.pick(authorizationCtx.principals[0], [
'id',
'name',
'email',
]);
return AuthorizationDecision.ALLOW;
} else {
return AuthorizationDecision.DENY;
}
}
@authenticate('jwt')
@authorize({resource: 'order', scopes: ['patch'], voters: [compareId]})
async patchOrders(
@param.path.string('userId') userId: string,
@requestBody() order: Partial<Order>,
@param.query.string('where') where?: Where<Order>,
): Promise<Count> {
return this.userRepo.orders(userId).patch(order, where);
}
In the above example compareId() is an authorizing function which is provided
as a voter in the decorator for the patchOrders() method.