Page Contents
Users are expected to program policies that enforce access control in two of the following options:
Authorizerfunctions- The authorizer functions are applied globally, i.e, they are enforced on all endpoints in the application
Voterfunctions- voters are specific for the endpoint that is decorated with it
- multiple voters can be configured for an endpoint
Usually the
authorizefunctions are bound through a provider as below
- The
AuthorizationContextparameter of theauthorizefunction contains the current principal (in the example given above,that would be the current user invokingcancelOrder) and details of the invoked endpoint.- The
AuthorizationMetadataparameter of theauthorizefunction contains all the details provided in the invoked method’s decorator.
- The
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.
src/application.ts
export class MyApplication extends BootMixin(
ServiceMixin(RepositoryMixin(RestApplication)),
) {
constructor(options: ApplicationConfig = {}) {
super(options);
// bind the authorizer provider
this.bind('authorizationProviders.my-authorizer-provider')
.toProvider(MyAuthorizationProvider)
.tag(AuthorizationTags.AUTHORIZER);
}
}
- This creates a list of
authorize()functions.- The
authorize(AuthorizationContext, AuthorizationMetadata)function in the provider class is expected to be called by theAuthorization Interceptorwhich is called for every API endpoint decorated with@authorize(). - The authorize interceptor gets the list of functions tagged with
AuthorizationTags.AUTHORIZER(and also the voters listed in the@authorizedecorator per endpoint) and calls the functions one after another. - The
authorize()function is expected to return an object of typeAuthorizationDecision. If the type returned isAuthorizationDecision.ALLOWthe currentPrincipalhas passed the executedauthorize()function’s criteria.
- The
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.