crystal
crystal copied to clipboard
A solution to add authorization on root fields via the makeWrapPlansPlugin in V5
After considering multiple solutions (that were known to me) including a schema-first approach with directives I've started discussing with @benjie where he pointed me to some great alternatives for achieving authorization on root fields.
His proposal:
makeWrapPlansPlugin({
Mutation: {
updateCustomer(plan, _, { $customerId }) {
const $hasWriteAccess = currentUserHasWriteAccessToCustomerId($customerId);
sideEffect($hasWriteAccess, (hwa) => {
if (!hwa) throw new Error("No write access!")
});
return plan();
}
}
})
With currentUserHasWriteAccessToCustomerId
being:
function currentUserHasWriteAccessToCustomerId($customerId) {
const $currentUserId = context().get('currentUserId');
const $access = customerPrivileges.find();
$access.where(sql`${$access}.user_id = ${$access.placeholder($currentUserId, TYPES.int)}`);
const $customerIds = applyTransforms(each($access, $row => $row.get('customer_id')));
return lambda([$customerIds, $customerId], ([customerIds, customerId]) => customerIds.includes(customerId));
I settled on an implementation that worked for my use case, which is as follows:
import { context, lambda, sideEffect } from "postgraphile/grafast";
import { makeWrapPlansPlugin } from "postgraphile/utils";
import { GraphQLError } from "postgraphile/graphql";
const USER_ROLE_ADMIN = "ADMIN";
const getUserRoleFromContext = () => {
return context().get("pgSettings").get("myapp.user.userRole");
};
const userHasAdminRole = (userRole: string) => {
if (userRole !== USER_ROLE_ADMIN) {
throw new GraphQLError(
"User does not have the correct role to access this resource.",
);
}
};
// I've added :any for the plans here, since the proper type: SmartFieldPlanResolver is not exported and TS doesn't infer it correctly.
export const AuthorizationPlugin = makeWrapPlansPlugin({
Mutation: {
createCustomer(plan: any) {
sideEffect(getUserRoleFromContext(), userHasAdminRole);
return plan();
},
editCustomerById(plan: any) {
sideEffect(getUserRoleFromContext(), userHasAdminRole);
return plan();
},
},
});