Skip to content

Instantly share code, notes, and snippets.

@bgentry
Last active August 9, 2019 10:57
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save bgentry/da703027ed80002b9686f2f90ab3ab2c to your computer and use it in GitHub Desktop.
Save bgentry/da703027ed80002b9686f2f90ab3ab2c to your computer and use it in GitHub Desktop.
graphql-ruby Pundit-based access control instrumentation
class FieldAccessInstrumentation
attr_reader :current_user_key
def initialize(current_user_key = :current_user)
@current_user_key = current_user_key
end
def instrument(type, field)
field = instrument_type(type, field) if type.metadata[:access]
# only add the field access check if it exists and isn't identical to the
# type access check:
if field.metadata[:access] && field.metadata[:access] != type.metadata[:access]
field = instrument_field(type, field)
end
field
end
protected
def instrument_field(type, field)
wrap_with_policy_check(
field.metadata[:access],
field,
"You are not allowed to access #{field.name} on that #{type.name}",
)
end
def instrument_type(type, field)
wrap_with_policy_check(
type.metadata[:access],
field,
"You are not allowed to access that #{type.name}",
)
end
def wrap_with_policy_check(policy_action, field, msg)
policy_method = :"#{policy_action}?"
old_resolve_proc = field.resolve_proc
new_resolve_proc = ->(obj, args, ctx) {
policy = Pundit.policy!(ctx[current_user_key], obj)
if policy.public_send(policy_method)
old_resolve_proc.call(obj, args, ctx)
else
raise GraphQL::AnalysisError, msg
end
}
# Return a copy of `field`, with a new resolve proc
field.redefine do
resolve(new_resolve_proc)
end
end
end
GraphQL::Field.accepts_definitions(
access: GraphQL::Define.assign_metadata_key(:access),
)
GraphQL::ObjectType.accepts_definitions(
access: GraphQL::Define.assign_metadata_key(:access),
)
MySchema = GraphQL::Schema.define do
mutation(Types::MutationType)
query(Types::QueryType)
instrument :field, AccessInstrumentation.new(:current_entity)
end
Types::UserType = GraphQL::ObjectType.define do
field :secretField, !types.String, property: :secret_field do
access :owner
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment