Action Policy GraphQL
This gem provides an integration for using Action Policy as an authorization framework for GraphQL applications (built with graphql
ruby gem).
This integration includes the following features:
📑 Documentation
Installation
Add this line to your application's Gemfile:
gem "action_policy-graphql"
Usage
NOTE: this is a quick overview of the functionality provided by the gem. For more information see the documentation.
To start using Action Policy in GraphQL-related code, you need to enhance your base classes with ActionPolicy::GraphQL::Behaviour
:
class Types::BaseObject < GraphQL::Schema::Object
include ActionPolicy::GraphQL::Behaviour
end
class Types::BaseMutation < GraphQL::Schema::Mutation
include ActionPolicy::GraphQL::Behaviour
end
class Types::BaseResolver < GraphQL::Schema::Resolver
include ActionPolicy::GraphQL::Behaviour
end
authorize: *
You can add authorization to the fields by specifying the authorize: *
option:
field :home, Home, null: false, authorize: true do
argument :id, ID, required: true
end
def home(id:)
Home.find(id)
end
The code above is equal to:
field :home, Home, null: false do
argument :id, ID, required: true
end
def home(id:)
Home.find(id).tap { |home| authorize! home, to: :show? }
end
You can customize the authorization options, e.g. authorize: {to: :preview?, with: CustomPolicy}
.
If you don't want to raise an exception but return a null instead, you should set a raise: false
option.
Note: it does not make too much sense to use authorize
in mutations since it's checking authorization rules after mutation is executed. Therefore authorize
marked as deprecated when used in mutations and will raise error in future releases.
authorized_scope: *
You can add authorized_scope: true
option to the field (list or connection field) to
apply the corresponding policy rules to the data:
class CityType < ::Common::Graphql::Type
field :events, EventType.connection_type, null: false, authorized_scope: true
field :events, EventType.connection_type, null: false, authorized_scope: {with: CustomEventPolicy}
end
NOTE: you cannot use authorize: *
and authorized_scope: *
at the same time but you can combine preauthorize: *
or authorize_field: *
with authorized_scope: *
.
preauthorize: *
If you want to perform authorization before resolving the field value, you can use preauthorize: *
option:
field :homes, [Home], null: false, preauthorize: {with: HomePolicy}
def homes
Home.all
end
The code above is equal to:
field :homes, [Home], null: false
def homes
authorize! "homes", to: :index?, with: HomePolicy
Home.all
end
NOTE: we pass the field's name as the record
to the policy rule. We assume that preauthorization rules do not depend on
the record itself and pass the field's name for debugging purposes only.
You can customize the authorization options, e.g. preauthorize: {to: :preview?, with: CustomPolicy}
.
NOTE: unlike authorize: *
you MUST specify the with: SomePolicy
option.
The default authorization rule depends on the type of the field:
- for lists we use
index?
(configured by ActionPolicy::GraphQL.default_preauthorize_list_rule
parameter) - for singleton fields we use
show?
(configured by ActionPolicy::GraphQL.default_preauthorize_node_rule
parameter)
authorize_field: *
If you want to perform authorization before resolving the field value on the base of the upper object, you can use authorize_field: *
option:
field :homes, Home, null: false, authorize_field: true
def homes
Home.all
end
The code above is equal to:
field :homes, [Home], null: false
def homes
authorize! object, to: :homes?
Home.all
end
By default we use #{underscored_field_name}?
authorization rule.
You can customize the authorization options, e.g. authorize_field: {to: :preview?, with: CustomPolicy}
.
expose_authorization_rules
You can add permissions/authorization exposing fields to "tell" clients which actions could be performed against the object or not (and why).
For example:
class ProfileType < ::Common::Graphql::Type
expose_authorization_rules :edit?, :destroy?, prefix: "can_"
end
Then the client could perform the following query:
{
post(id: $id) {
canEdit {
value
message
reasons {
details
fullMessages
}
}
canDestroy {
}
}
}
You can specify a custom field name as well (only for a single rule):
class ProfileType < ::Common::Graphql::Type
expose_authorization_rules :create?, with: PostPolicy, field_name: "can_create_post"
end
Contributing
Bug reports and pull requests are welcome on GitHub at https://github.com/palkan/action_policy-graphql.
License
The gem is available as open source under the terms of the MIT License.