Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

A solution to add authorization on root fields via the makeWrapPlansPlugin in V5 #2209

Open
maikdiepenbroek opened this issue Oct 7, 2024 · 0 comments

Comments

@maikdiepenbroek
Copy link

maikdiepenbroek commented Oct 7, 2024

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();
    },
  },
});
@github-project-automation github-project-automation bot moved this to 🌳 Triage in V5.0.0 Oct 7, 2024
@benjie benjie moved this from 🌳 Triage to 📝 Docs Improvements in V5.0.0 Oct 7, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Status: 📝 Docs Improvements
Development

No branches or pull requests

3 participants
@benjie @maikdiepenbroek and others