Skip to content

Commit 0f5129c

Browse files
committed
Allow callables for authenticate option
1 parent dfda728 commit 0f5129c

5 files changed

Lines changed: 91 additions & 35 deletions

File tree

lib/graphql_devise/schema_plugin.rb

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ def trace(event, trace_data)
4646

4747
if auth_required && !(public_introspection && introspection_field?(field))
4848
context = set_current_resource(context)
49-
raise_on_missing_resource(context, field)
49+
raise_on_missing_resource(context, field, auth_required)
5050
end
5151

5252
yield
@@ -75,8 +75,12 @@ def set_current_resource(context)
7575
context
7676
end
7777

78-
def raise_on_missing_resource(context, field)
78+
def raise_on_missing_resource(context, field, auth_required)
7979
@unauthenticated_proc.call(field.name) if context[:current_resource].blank?
80+
81+
if auth_required.respond_to?(:call) && !auth_required.call(context[:current_resource])
82+
@unauthenticated_proc.call(field.name)
83+
end
8084
end
8185

8286
def context_from_data(trace_data)

spec/dummy/app/graphql/types/query_type.rb

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ class QueryType < Types::BaseObject
55
field :user, resolver: Resolvers::UserShow
66
field :public_field, String, null: false, authenticate: false
77
field :private_field, String, null: false, authenticate: true
8+
field :vip_field, String, null: false, authenticate: ->(user) { user.is_a?(User) && user.vip? }
89

910
def public_field
1011
'Field does not require authentication'
@@ -13,5 +14,9 @@ def public_field
1314
def private_field
1415
'Field will always require authentication'
1516
end
17+
18+
def vip_field
19+
'Field available only for VIP Users'
20+
end
1621
end
1722
end
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
class AddVipToUsers < ActiveRecord::Migration[6.1]
2+
def change
3+
add_column :users, :vip, :boolean, null: false, default: false
4+
end
5+
end

spec/dummy/db/schema.rb

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,15 @@
22
# of editing this file, please use the migrations feature of Active Record to
33
# incrementally modify your database, and then regenerate this schema definition.
44
#
5-
# This file is the source Rails uses to define your schema when running `rails
6-
# db:schema:load`. When creating a new database, `rails db:schema:load` tends to
5+
# This file is the source Rails uses to define your schema when running `bin/rails
6+
# db:schema:load`. When creating a new database, `bin/rails db:schema:load` tends to
77
# be faster and is potentially less error prone than running all of your
88
# migrations from scratch. Old migrations may fail to apply correctly if those
99
# migrations use external dependencies or application code.
1010
#
1111
# It's strongly recommended that you check this file into your version control system.
1212

13-
ActiveRecord::Schema.define(version: 2020_06_23_003142) do
13+
ActiveRecord::Schema.define(version: 2021_05_16_211417) do
1414

1515
create_table "admins", force: :cascade do |t|
1616
t.string "provider", default: "email", null: false
@@ -105,6 +105,7 @@
105105
t.datetime "created_at", null: false
106106
t.datetime "updated_at", null: false
107107
t.boolean "auth_available", default: true, null: false
108+
t.boolean "vip", default: false, null: false
108109
t.index ["confirmation_token"], name: "index_users_on_confirmation_token", unique: true
109110
t.index ["email"], name: "index_users_on_email", unique: true
110111
t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true

spec/requests/user_controller_spec.rb

Lines changed: 71 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,14 @@
55
RSpec.describe "Integrations with the user's controller" do
66
include_context 'with graphql query request'
77

8+
shared_examples 'returns a must authenticate error' do |field|
9+
it 'returns a must sign in error' do
10+
expect(json_response[:errors]).to contain_exactly(
11+
hash_including(message: "#{field} field requires authentication", extensions: { code: 'AUTHENTICATION_ERROR' })
12+
)
13+
end
14+
end
15+
816
let(:user) { create(:user, :confirmed) }
917

1018
describe 'publicField' do
@@ -54,11 +62,7 @@
5462
end
5563

5664
context 'when user is not authenticated' do
57-
it 'returns a must sign in error' do
58-
expect(json_response[:errors]).to contain_exactly(
59-
hash_including(message: 'privateField field requires authentication', extensions: { code: 'AUTHENTICATION_ERROR' })
60-
)
61-
end
65+
it_behaves_like 'returns a must authenticate error', 'privateField'
6266
end
6367
end
6468

@@ -82,11 +86,7 @@
8286
end
8387

8488
context 'when user is not authenticated' do
85-
it 'returns a must sign in error' do
86-
expect(json_response[:errors]).to contain_exactly(
87-
hash_including(message: 'privateField field requires authentication', extensions: { code: 'AUTHENTICATION_ERROR' })
88-
)
89-
end
89+
it_behaves_like 'returns a must authenticate error', 'privateField'
9090
end
9191

9292
context 'when using the failing route' do
@@ -111,11 +111,7 @@
111111
end
112112

113113
context 'when user is not authenticated' do
114-
it 'returns a must sign in error' do
115-
expect(json_response[:errors]).to contain_exactly(
116-
hash_including(message: 'privateField field requires authentication', extensions: { code: 'AUTHENTICATION_ERROR' })
117-
)
118-
end
114+
it_behaves_like 'returns a must authenticate error', 'privateField'
119115
end
120116
end
121117
end
@@ -141,11 +137,7 @@
141137
end
142138

143139
context 'when user is not authenticated' do
144-
it 'returns a must sign in error' do
145-
expect(json_response[:errors]).to contain_exactly(
146-
hash_including(message: 'dummyMutation field requires authentication', extensions: { code: 'AUTHENTICATION_ERROR' })
147-
)
148-
end
140+
it_behaves_like 'returns a must authenticate error', 'dummyMutation'
149141
end
150142
end
151143

@@ -161,11 +153,7 @@
161153
end
162154

163155
context 'when user is not authenticated' do
164-
it 'returns a must sign in error' do
165-
expect(json_response[:errors]).to contain_exactly(
166-
hash_including(message: 'dummyMutation field requires authentication', extensions: { code: 'AUTHENTICATION_ERROR' })
167-
)
168-
end
156+
it_behaves_like 'returns a must authenticate error', 'dummyMutation'
169157
end
170158
end
171159
end
@@ -199,11 +187,7 @@
199187
end
200188

201189
context 'when user is not authenticated' do
202-
it 'returns a must sign in error' do
203-
expect(json_response[:errors]).to contain_exactly(
204-
hash_including(message: 'user field requires authentication', extensions: { code: 'AUTHENTICATION_ERROR' })
205-
)
206-
end
190+
it_behaves_like 'returns a must authenticate error', 'user'
207191
end
208192
end
209193

@@ -271,4 +255,61 @@
271255
)
272256
end
273257
end
258+
259+
describe 'vipField' do
260+
let(:error_message) { 'Field available only for VIP Users' }
261+
let(:query) do
262+
<<-GRAPHQL
263+
query { vipField }
264+
GRAPHQL
265+
end
266+
267+
context 'when using a regular schema' do
268+
before { post_request('/api/v1/graphql') }
269+
270+
context 'when user is authenticated' do
271+
let(:headers) { user.create_new_auth_token }
272+
273+
context 'when schema user is VIP' do
274+
let(:user) { create(:user, :confirmed, vip: true) }
275+
276+
it 'allows to perform the query' do
277+
expect(json_response[:data][:vipField]).to eq(error_message)
278+
end
279+
end
280+
281+
context 'when schema user is not VIP' do
282+
it_behaves_like 'returns a must authenticate error', 'vipField'
283+
end
284+
end
285+
286+
context 'when user is not authenticated' do
287+
it_behaves_like 'returns a must authenticate error', 'vipField'
288+
end
289+
end
290+
291+
context 'when using the interpreter schema' do
292+
before { post_request('/api/v1/interpreter') }
293+
294+
context 'when user is authenticated' do
295+
let(:headers) { user.create_new_auth_token }
296+
297+
context 'when schema user is VIP' do
298+
let(:user) { create(:user, :confirmed, vip: true) }
299+
300+
it 'allows to perform the query' do
301+
expect(json_response[:data][:vipField]).to eq(error_message)
302+
end
303+
end
304+
305+
context 'when schema user is not VIP' do
306+
it_behaves_like 'returns a must authenticate error', 'vipField'
307+
end
308+
end
309+
310+
context 'when user is not authenticated' do
311+
it_behaves_like 'returns a must authenticate error', 'vipField'
312+
end
313+
end
314+
end
274315
end

0 commit comments

Comments
 (0)