You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
This gem has an authorization feature implemented using a hidden [directive]. It leverages the ability of directives to have [events] triggered during [requests], the ability to add directives to both fields and types (which embeds the events), and the ability to use shared directives.
Setting Up
The hidden [hidden directive] @authorize is responsible for dealing with the authorize events and holding the possible settings as its arguments. It is the component that you will set up for controlling the authorization.
Parameters
You can add parameters (aka [arguments]) to the directive by calling parameter. It works just as any [argument addition], including the [one_of] special type.
GraphQL::Authorization.parameter(:reading,:boolean)# Same asGraphQL::Directive::AuthorizeDirective.argument(...)LEVELS=%w[guestsupportadmin]GraphQL::Authorization.parameter(:level,:one_of,LEVELS)
Rules
You can add rules (aka [event listeners]) to the directive by calling rule. It works just like adding a authorize [event listeners] to directives, including the [filters].
GraphQL::Authorization.ruledo |context|
accept!ifcontext.current_user.admin?end# Same asGraphQL::Directive::AuthorizeDirective.on(:authorize){ ... }GraphQL::Authorization.rule(on: 'User'){ ... }
Using
You have some options for attaching the directive to fields:
Directly
You can use the use method on the fields where you want to attach a directive, where you can also set up the values for parameters.
use:authorize,reading: true
Field List
For any field list, you can use the [directive attacher] method.
attach:authorize,reading: true,to: %i[usersuser]
Creator
You can use the create method to initialize an instance and attach it to fields provided in the for named argument, which works with any [field list].
The directive has some special instance methods that can be used to control the result of the authorization process.
accept!
It will approve the authorization and prevent any other callback from being invoked.
reject!
It will disapprove the authorization and prevent any other callback from being invoked.
apply!(value)
It will conditionally approve, disapprove, or move to the next callback based on
the provided value (true, false, and nil, respectively).
CanCanCan Implementation
Here is an example of how you can implement this feature when combined with [sources] and the famous authorization gem CanCanCan.
# app/graphql/authorization.rbmoduleGraphQL# Add the action parameter for the fieldsAuthorization.parameter(:action,:string,null: false)# Add the rule based on the abilityAuthorization.ruledo |context,field,event|
reject!unlesscontext.current_user# Get the model from the return typemodel=field.type_class.assigned_class# If the field has an id argument, get it as part of the checkargs={id: event.argument(:id)}iffield.has_argument?(:id)# Apply the result of the abilityapply!context.current_user.ability.can?(args.action,model, *args)end# Create shared instances for common actionsindex_action=Authorization.new(action: 'index')show_action=Authorization.new(action: 'show')create_action=Authorization.new(action: 'create')update_action=Authorization.new(action: 'update')destroy_action=Authorization.new(action: 'destroy')# Create a new prepare callback to use the accessible_bybefore_index=->domodel=field.type_class.assigned_classmodel.accessible_by(context.current_user.ability)end# Force the Type Map to load all the dependencies so classes are definedtype_map.load_dependencies!# We create a module to hook into the attach fields process of sourcesActiveRecordSource.extendModule.newdo# Call a helper based on the type being attacheddefattach_fields!(type,fields)helper="add_#{type}_authorization"send(helper,fields)ifrespond_to?(helper)superend# helper for query fieldsdefadd_query_authorization(fields)fields[singular.to_sym]&.use(show_action)fields[plural.to_sym]&.use(show_action)fields[plural.to_sym]&.prepare(&before_index)end# helper for mutation fieldsdefadd_mutation_authorization(fields)fields[:"create_#{singular}"]&.use(create_action)fields[:"update_#{singular}"]&.use(update_action)fields[:"destroy_#{singular}"]&.use(destroy_action)endendend# app/graphql/app_schema.rbmoduleGraphQLclassAppSchema < GraphQL::Schemaload_scalars%i[bigintdate_time]load_directory'sources'require_relative'authorization'endend
The above will also work with custom fields and custom actions.
Proposal
This gem has an authorization feature implemented using a hidden [directive]. It leverages the ability of directives to have [events] triggered during [requests], the ability to add directives to both fields and types (which embeds the events), and the ability to use shared directives.
Setting Up
The hidden [hidden directive]
@authorize
is responsible for dealing with theauthorize
events and holding the possible settings as its arguments. It is the component that you will set up for controlling the authorization.Parameters
You can add parameters (aka [arguments]) to the directive by calling
parameter
. It works just as any [argument addition], including the [one_of
] special type.Rules
You can add rules (aka [event listeners]) to the directive by calling
rule
. It works just like adding aauthorize
[event listeners] to directives, including the [filters
].Using
You have some options for attaching the directive to fields:
Directly
You can use the
use
method on the fields where you want to attach a directive, where you can also set up the values for parameters.Field List
For any field list, you can use the [directive attacher] method.
Creator
You can use the
create
method to initialize an instance and attach it to fields provided in thefor
named argument, which works with any [field list].Especial Methods
The directive has some special instance methods that can be used to control the result of the authorization process.
accept!
It will approve the authorization and prevent any other callback from being invoked.
reject!
It will disapprove the authorization and prevent any other callback from being invoked.
apply!(value)
It will conditionally approve, disapprove, or move to the next callback based on
the provided
value
(true
,false
, andnil
, respectively).CanCanCan Implementation
Here is an example of how you can implement this feature when combined with [sources] and the famous authorization gem CanCanCan.
The above will also work with custom fields and custom actions.
The text was updated successfully, but these errors were encountered: