Skip to content

Commit a69a763

Browse files
committed
Allow controller level authentication
1 parent e623227 commit a69a763

9 files changed

Lines changed: 74 additions & 12 deletions

File tree

app/controllers/graphql_devise/concerns/set_user_by_token.rb

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,39 @@ module Concerns
77
SetUserByToken.module_eval do
88
attr_accessor :client_id, :token, :resource
99

10+
class_methods do
11+
def set_resource_by_model(models, **kwargs)
12+
Array(models).each do |model|
13+
GraphqlDevise.add_mapping(GraphqlDevise.to_mapping_name(model.to_s), model.to_s)
14+
end
15+
GraphqlDevise.reconfigure_warden!
16+
17+
before_action(**kwargs) do
18+
authenticate_model(models)
19+
end
20+
end
21+
end
22+
23+
def authenticate_model(models)
24+
Array(models).each do |model|
25+
set_resource_by_token(model)
26+
return @resource
27+
end
28+
29+
nil
30+
end
31+
32+
def resource_class(resource = nil)
33+
return super unless resource.is_a?(Class)
34+
35+
if (Object.const_defined?('ActiveRecord::Base') && resource < ActiveRecord::Base) ||
36+
(Object.const_defined?('Mongoid::Document') && resource < Mongoid::Document)
37+
return resource
38+
end
39+
40+
super
41+
end
42+
1043
def full_url_without_params
1144
request.base_url + request.path
1245
end
@@ -16,10 +49,13 @@ def set_resource_by_token(resource)
1649
end
1750

1851
def graphql_context(resource_name)
19-
{
52+
context = {
2053
resource_name: resource_name,
2154
controller: self
2255
}
56+
context[:current_resource] = @resource if @resource.present?
57+
58+
context
2359
end
2460

2561
def build_redirect_headers(access_token, client, redirect_header_options = {})

app/helpers/graphql_devise/mailer_helper.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
module GraphqlDevise
44
module MailerHelper
55
def confirmation_query(resource_name:, token:, redirect_url:)
6-
name = "#{resource_name.underscore.tr('/', '_').camelize(:lower)}ConfirmAccount"
6+
name = "#{GraphqlDevise.to_mapping_name(resource_name).camelize(:lower)}ConfirmAccount"
77
raw = <<-GRAPHQL
88
query($token:String!,$redirectUrl:String!){
99
#{name}(confirmationToken:$token,redirectUrl:$redirectUrl){
@@ -19,7 +19,7 @@ def confirmation_query(resource_name:, token:, redirect_url:)
1919
end
2020

2121
def password_reset_query(token:, redirect_url:, resource_name:)
22-
name = "#{resource_name.underscore.tr('/', '_').camelize(:lower)}CheckPasswordToken"
22+
name = "#{GraphqlDevise.to_mapping_name(resource_name).camelize(:lower)}CheckPasswordToken"
2323
raw = <<-GRAPHQL
2424
query($token:String!,$redirectUrl:String!){
2525
#{name}(resetPasswordToken:$token,redirectUrl:$redirectUrl){

lib/graphql_devise.rb

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,13 +29,22 @@ def self.mount_resource(mapping_name)
2929
end
3030

3131
def self.add_mapping(mapping_name, resource)
32-
return if Devise.mappings.key?(mapping_name)
32+
return if Devise.mappings.key?(mapping_name.to_sym)
3333

3434
Devise.add_mapping(
3535
mapping_name.to_s.pluralize.to_sym,
3636
module: :devise, class_name: resource
3737
)
3838
end
39+
40+
def self.reconfigure_warden!
41+
Devise.class_variable_set(:@@warden_configured, nil)
42+
Devise.configure_warden!
43+
end
44+
45+
def self.to_mapping_name(resource)
46+
resource.to_s.underscore.tr('/', '_')
47+
end
3948
end
4049

4150
require 'graphql_devise/engine'

lib/graphql_devise/resource_loader.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ def initialize(resource, options = {}, routing = false)
1010
end
1111

1212
def call(query, mutation)
13-
mapping_name = @resource.to_s.underscore.tr('/', '_').to_sym
13+
mapping_name = GraphqlDevise.to_mapping_name(@resource).to_sym
1414

1515
# clean_options responds to all keys defined in GraphqlDevise::MountMethod::SUPPORTED_OPTIONS
1616
clean_options = GraphqlDevise::MountMethod::OptionSanitizer.new(@options).call!

lib/graphql_devise/schema_plugin.rb

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ def initialize(query: nil, mutation: nil, authenticate_default: true, public_int
1616

1717
# Must happen on initialize so operations are loaded before the types are added to the schema on GQL < 1.10
1818
load_fields
19-
reconfigure_warden!
19+
GraphqlDevise.reconfigure_warden!
2020
end
2121

2222
def use(schema_definition)
@@ -102,11 +102,6 @@ def authenticate_option(field, trace_data)
102102
auth_required.nil? ? @authenticate_default : auth_required
103103
end
104104

105-
def reconfigure_warden!
106-
Devise.class_variable_set(:@@warden_configured, nil)
107-
Devise.configure_warden!
108-
end
109-
110105
def load_fields
111106
@resource_loaders.each do |resource_loader|
112107
raise Error, 'Invalid resource loader instance' unless resource_loader.instance_of?(GraphqlDevise::ResourceLoader)

spec/dummy/app/controllers/api/v1/graphql_controller.rb

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ module V1
55
class GraphqlController < ApplicationController
66
include GraphqlDevise::Concerns::SetUserByToken
77

8+
set_resource_by_model SchemaUser, only: [:controller_auth]
9+
810
def graphql
911
result = DummySchema.execute(params[:query], **execute_params(params))
1012

@@ -19,6 +21,12 @@ def failing_resource_name
1921
render json: DummySchema.execute(params[:query], context: graphql_context([:user, :fail]))
2022
end
2123

24+
def controller_auth
25+
result = DummySchema.execute(params[:query], **execute_params(params))
26+
27+
render json: result unless performed?
28+
end
29+
2230
private
2331

2432
def execute_params(item)

spec/dummy/config/routes.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,4 +37,5 @@
3737
post '/api/v1/graphql', to: 'api/v1/graphql#graphql'
3838
post '/api/v1/interpreter', to: 'api/v1/graphql#interpreter'
3939
post '/api/v1/failing', to: 'api/v1/graphql#failing_resource_name'
40+
post '/api/v1/controller_auth', to: 'api/v1/graphql#controller_auth'
4041
end

spec/graphql/user_queries_spec.rb

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,9 @@
7878
let(:query) do
7979
<<-GRAPHQL
8080
query {
81-
user(id: #{user.id}) {
81+
user(
82+
id: #{user.id}
83+
) {
8284
id
8385
email
8486
}

spec/requests/user_controller_spec.rb

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,17 @@
4242
GRAPHQL
4343
end
4444

45+
context 'when authenticating before using the GQL schema' do
46+
let(:headers) { create(:schema_user).create_new_auth_token }
47+
48+
before { post_request('/api/v1/controller_auth') }
49+
50+
it 'allows authentication at the controller level' do
51+
pp json_response
52+
expect(json_response[:data][:privateField]).to eq('Field will always require authentication')
53+
end
54+
end
55+
4556
context 'when using a regular schema' do
4657
before { post_request('/api/v1/graphql') }
4758

0 commit comments

Comments
 (0)