Class: GraphQL::Schema::Warden Private

Inherits:
Object
  • Object
show all
Defined in:
lib/graphql/schema/warden.rb

Overview

This class is part of a private API. You should avoid using this class if possible, as it may be removed or be changed in the future.

Restrict access to a GraphQL::Schema with a user-defined filter.

When validating and executing a query, all access to schema members should go through a warden. If you access the schema directly, you may show a client something that it shouldn’t be allowed to see.

Examples:

Hiding private fields

private_members = -> (member, ctx) { member.[:private] }
result = Schema.execute(query_string, except: private_members)

Custom filter implementation

# It must respond to `#call(member)`.
class MissingRequiredFlags
  def initialize(user)
    @user = user
  end

  # Return `false` if any required flags are missing
  def call(member, ctx)
    member.[:required_flags].any? do |flag|
      !@user.has_flag?(flag)
    end
  end
end

# Then, use the custom filter in query:
missing_required_flags = MissingRequiredFlags.new(current_user)

# This query can only access members which match the user's flags
result = Schema.execute(query_string, except: missing_required_flags)

Direct Known Subclasses

Query::NullContext::NullWarden

Defined Under Namespace

Classes: PassThruWarden

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(filter, context:, schema:) ⇒ Warden

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns a new instance of Warden.

Parameters:



91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
# File 'lib/graphql/schema/warden.rb', line 91

def initialize(filter, context:, schema:)
  @schema = schema
  # Cache these to avoid repeated hits to the inheritance chain when one isn't present
  @query = @schema.query
  @mutation = @schema.mutation
  @subscription = @schema.subscription
  @context = context
  @visibility_cache = read_through { |m| filter.call(m, context) }
  # Initialize all ivars to improve object shape consistency:
  @types = @visible_types = @reachable_types = @visible_parent_fields =
    @visible_possible_types = @visible_fields = @visible_arguments = @visible_enum_arrays =
    @visible_enum_values = @visible_interfaces = @type_visibility = @type_memberships =
    @visible_and_reachable_type = @unions = @unfiltered_interfaces = @references_to =
    @reachable_type_set =
      nil
end

Class Method Details

.from_context(context) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



40
41
42
# File 'lib/graphql/schema/warden.rb', line 40

def self.from_context(context)
  (context.respond_to?(:warden) && context.warden) || PassThruWarden
end

.visible_entry?(visibility_method, entry, context, warden = Warden.from_context(context)) ⇒ Object?

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Parameters:

  • visibility_method (Symbol)

    a Warden method to call for this entry

  • entry (Object, Array<Object>)

    One or more definitions for a given name in a GraphQL Schema

  • context (GraphQL::Query::Context)
  • warden (Warden) (defaults to: Warden.from_context(context))

Returns:

  • (Object)

    entry or one of entry’s items if exactly one of them is visible for this context

  • (nil)

    If neither entry nor any of entry’s items are visible for this context



50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
# File 'lib/graphql/schema/warden.rb', line 50

def self.visible_entry?(visibility_method, entry, context, warden = Warden.from_context(context))
  if entry.is_a?(Array)
    visible_item = nil
    entry.each do |item|
      if warden.public_send(visibility_method, item, context)
        if visible_item.nil?
          visible_item = item
        else
          raise DuplicateNamesError.new(
            duplicated_name: item.path, duplicated_definition_1: visible_item.inspect, duplicated_definition_2: item.inspect
          )
        end
      end
    end
    visible_item
  elsif warden.public_send(visibility_method, entry, context)
    entry
  else
    nil
  end
end

Instance Method Details

#arguments(argument_owner, ctx = nil) ⇒ Array<GraphQL::Argument>

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns Visible arguments on argument_owner.

Parameters:

  • argument_owner (GraphQL::Field, GraphQL::InputObjectType)

Returns:

  • (Array<GraphQL::Argument>)

    Visible arguments on argument_owner



186
187
188
189
# File 'lib/graphql/schema/warden.rb', line 186

def arguments(argument_owner, ctx = nil)
  @visible_arguments ||= read_through { |o| o.arguments(@context).each_value.select { |a| visible_argument?(a, @context) } }
  @visible_arguments[argument_owner]
end

#directivesObject

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



214
215
216
# File 'lib/graphql/schema/warden.rb', line 214

def directives
  @schema.directives.each_value.select { |d| visible?(d) }
end

#enum_values(enum_defn) ⇒ Array<GraphQL::EnumType::EnumValue>

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns Visible members of enum_defn.

Returns:

  • (Array<GraphQL::EnumType::EnumValue>)

    Visible members of enum_defn



192
193
194
195
196
197
198
199
200
201
# File 'lib/graphql/schema/warden.rb', line 192

def enum_values(enum_defn)
  @visible_enum_arrays ||= read_through { |e|
    values = e.enum_values(@context)
    if values.size == 0
      raise GraphQL::Schema::Enum::MissingValuesError.new(e)
    end
    values
  }
  @visible_enum_arrays[enum_defn]
end

#fields(type_defn) ⇒ Array<GraphQL::Field>

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns Fields on type_defn.

Parameters:

  • type_defn (GraphQL::ObjectType, GraphQL::InterfaceType)

Returns:

  • (Array<GraphQL::Field>)

    Fields on type_defn



179
180
181
182
# File 'lib/graphql/schema/warden.rb', line 179

def fields(type_defn)
  @visible_fields ||= read_through { |t| @schema.get_fields(t, @context).values }
  @visible_fields[type_defn]
end

#get_argument(parent_type, argument_name) ⇒ GraphQL::Argument?

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns The argument named argument_name on parent_type, if it exists and is visible.

Returns:

  • (GraphQL::Argument, nil)

    The argument named argument_name on parent_type, if it exists and is visible



163
164
165
166
# File 'lib/graphql/schema/warden.rb', line 163

def get_argument(parent_type, argument_name)
  argument = parent_type.get_argument(argument_name, @context)
  return argument if argument && visible_argument?(argument, @context)
end

#get_field(parent_type, field_name) ⇒ GraphQL::Field?

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns The field named field_name on parent_type, if it exists.

Returns:

  • (GraphQL::Field, nil)

    The field named field_name on parent_type, if it exists



147
148
149
150
151
152
153
154
155
156
157
158
159
160
# File 'lib/graphql/schema/warden.rb', line 147

def get_field(parent_type, field_name)
  @visible_parent_fields ||= read_through do |type|
    read_through do |f_name|
      field_defn = @schema.get_field(type, f_name, @context)
      if field_defn && visible_field?(field_defn, nil, type)
        field_defn
      else
        nil
      end
    end
  end

  @visible_parent_fields[parent_type][field_name]
end

#get_type(type_name) ⇒ GraphQL::BaseType?

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns The type named type_name, if it exists (else nil).

Returns:

  • (GraphQL::BaseType, nil)

    The type named type_name, if it exists (else nil)



122
123
124
125
126
127
128
129
130
131
132
133
# File 'lib/graphql/schema/warden.rb', line 122

def get_type(type_name)
  @visible_types ||= read_through do |name|
    type_defn = @schema.get_type(name, @context)
    if type_defn && visible_and_reachable_type?(type_defn)
      type_defn
    else
      nil
    end
  end

  @visible_types[type_name]
end

#interface_type_memberships(obj_type, _ctx = nil) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



251
252
253
254
255
256
# File 'lib/graphql/schema/warden.rb', line 251

def interface_type_memberships(obj_type, _ctx = nil)
  @type_memberships ||= read_through do |obj_t|
    obj_t.interface_type_memberships
  end
  @type_memberships[obj_type]
end

#interfaces(obj_type) ⇒ Array<GraphQL::InterfaceType>

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns Visible interfaces implemented by obj_type.

Returns:

  • (Array<GraphQL::InterfaceType>)

    Visible interfaces implemented by obj_type



209
210
211
212
# File 'lib/graphql/schema/warden.rb', line 209

def interfaces(obj_type)
  @visible_interfaces ||= read_through { |t| t.interfaces(@context).select { |i| visible_type?(i) } }
  @visible_interfaces[obj_type]
end

#possible_types(type_defn) ⇒ Array<GraphQL::BaseType>

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns The types which may be member of type_defn.

Returns:

  • (Array<GraphQL::BaseType>)

    The types which may be member of type_defn



169
170
171
172
173
174
175
# File 'lib/graphql/schema/warden.rb', line 169

def possible_types(type_defn)
  @visible_possible_types ||= read_through { |type_defn|
    pt = @schema.possible_types(type_defn, @context)
    pt.select { |t| visible_and_reachable_type?(t) }
  }
  @visible_possible_types[type_defn]
end

#reachable_type?(type_name) ⇒ Boolean

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns Boolean True if the type is visible and reachable in the schema.

Returns:

  • (Boolean)

    Boolean True if the type is visible and reachable in the schema



141
142
143
144
# File 'lib/graphql/schema/warden.rb', line 141

def reachable_type?(type_name)
  type = get_type(type_name) # rubocop:disable Development/ContextIsPassedCop -- `self` is query-aware
  type && reachable_type_set.include?(type)
end

#reachable_typesArray<GraphQL::BaseType>

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns Visible and reachable types in the schema.

Returns:

  • (Array<GraphQL::BaseType>)

    Visible and reachable types in the schema



136
137
138
# File 'lib/graphql/schema/warden.rb', line 136

def reachable_types
  @reachable_types ||= reachable_type_set.to_a
end

#root_type_for_operation(op_name) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



218
219
220
221
222
223
224
225
# File 'lib/graphql/schema/warden.rb', line 218

def root_type_for_operation(op_name)
  root_type = @schema.root_type_for_operation(op_name)
  if root_type && visible?(root_type)
    root_type
  else
    nil
  end
end

#typesHash<String, GraphQL::BaseType>

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns Visible types in the schema.

Returns:

  • (Hash<String, GraphQL::BaseType>)

    Visible types in the schema



109
110
111
112
113
114
115
116
117
118
119
# File 'lib/graphql/schema/warden.rb', line 109

def types
  @types ||= begin
    vis_types = {}
    @schema.types(@context).each do |n, t|
      if visible_and_reachable_type?(t)
        vis_types[n] = t
      end
    end
    vis_types
  end
end

#visible_argument?(arg_defn, _ctx = nil) ⇒ Boolean

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns:

  • (Boolean)


238
239
240
# File 'lib/graphql/schema/warden.rb', line 238

def visible_argument?(arg_defn, _ctx = nil)
  visible?(arg_defn) && visible_and_reachable_type?(arg_defn.type.unwrap)
end

#visible_enum_value?(enum_value, _ctx = nil) ⇒ Boolean

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns:

  • (Boolean)


203
204
205
206
# File 'lib/graphql/schema/warden.rb', line 203

def visible_enum_value?(enum_value, _ctx = nil)
  @visible_enum_values ||= read_through { |ev| visible?(ev) }
  @visible_enum_values[enum_value]
end

#visible_field?(field_defn, _ctx = nil, owner = field_defn.owner) ⇒ Boolean

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Parameters:

  • owner (Class, Module) (defaults to: field_defn.owner)

    If provided, confirm that field has the given owner.

Returns:

  • (Boolean)


228
229
230
231
232
233
234
235
236
# File 'lib/graphql/schema/warden.rb', line 228

def visible_field?(field_defn, _ctx = nil, owner = field_defn.owner)
  # This field is visible in its own right
  visible?(field_defn) &&
    # This field's return type is visible
    visible_and_reachable_type?(field_defn.type.unwrap) &&
    # This field is either defined on this object type,
    # or the interface it's inherited from is also visible
    ((field_defn.respond_to?(:owner) && field_defn.owner == owner) || field_on_visible_interface?(field_defn, owner))
end

#visible_type?(type_defn, _ctx = nil) ⇒ Boolean

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns:

  • (Boolean)


242
243
244
245
# File 'lib/graphql/schema/warden.rb', line 242

def visible_type?(type_defn, _ctx = nil)
  @type_visibility ||= read_through { |type_defn| visible?(type_defn) }
  @type_visibility[type_defn]
end

#visible_type_membership?(type_membership, _ctx = nil) ⇒ Boolean

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns:

  • (Boolean)


247
248
249
# File 'lib/graphql/schema/warden.rb', line 247

def visible_type_membership?(type_membership, _ctx = nil)
  visible?(type_membership)
end