Вот пример утечки данных, которая возникает при использовании стандартной установки graphql-ruby.
При использовании вложенного запроса graphql ниже, ответ возвращает данные, вложенные в компанию 1, которая принадлежит компании 2. Я ожидаюответ должен быть ограничен журналом бухгалтеров, который принадлежит компании, в которую он вложен.
Таким образом, это утечка информации.
Вопрос в том, как мы можем исправить этоутечка, поэтому единственными данными, возвращаемыми в ответе и его вложенных объектах, являются данные, принадлежащие компании (корневому объекту).
Этот запрос:
query {
company(id: "1") {
id
name
activityLog {
id
activityAt
accountant {
id
name
}
companyId
}
accountants {
id
name
activityLog {
id
activityAt
companyId
}
}
}
}
возвращает этот ответ:
{
"data": {
"company": {
"id": "1",
"name": "AwsomeCo",
"activityLog": [
{
"id": "1",
"activityAt": "2019-10-12 16:40:13 UTC",
"accountant": {
"id": "100",
"name": "Mr Smith",
},
"companyId": "1"
}
],
"accountants": [
{
"id": "100",
"name": "Mr Smith"
"activityLog": [
{
"id": "1",
"activityAt": "2019-10-12 16:40:13 UTC",
"companyId": "1"
},
{
"id": "2",
"activityAt": "2019-10-11 16:40:13 UTC",
"companyId": "2" // OTHER COMPANY, NEED TO PRESERVE PARENT SCOPE
},
],
}
}
}
}
}
утечка данных журнала транзакций компании 2 в рамках вложенных элементов компании 1.
Опять вопрос: Как сохранить область действия, отображая данные только в контекстекомпания, которую он отображает?
Код для воспроизведения:
Типы GraphQL (с использованием gem graphql-ruby)
#query_type.rb
module Types
class QueryType < Types::BaseObject
# Add root-level fields here.
# They will be entry points for queries on your schema.
field :company_leak, Types::ExampleLeak::CompanyType, null: false do
argument :id, ID, required: true
end
field :companies_leak, [ Types::ExampleLeak::CompanyType ], null: false
def company_leak(id: )
Company.find(id)
end
def companies_leak
Company.all
end
end
end
module Types
module ExampleLeak
class CompanyType < BaseObject
field :id, ID, null: false
field :name, String, null: false
field :transaction_logs, [Types::ExampleLeak::TransactionLogType], null: true
field :accountants, [Types::ExampleLeak::AccountantType], null: true
end
end
end
module Types
module ExampleLeak
class AccountantType < BaseObject
field :id, ID, null: false
field :name, String, null: false
field :transaction_logs, [Types::ExampleLeak::TransactionLogType], null: true
field :companies, [Types::ExampleLeak::CompanyType], null: true
end
end
end
module Types
module ExampleLeak
class TransactionLogType < BaseObject
field :id, ID, null: false
field :activity_at, String, null: false
field :company_id, ID, null: false
field :accountant, Types::ExampleLeak::AccountantType, null: false
end
end
end
Модели
class Company < ApplicationRecord
has_and_belongs_to_many :accountants
has_many :transaction_logs
end
class Accountant < ApplicationRecord
has_and_belongs_to_many :companies
has_many :transaction_logs
end
class TransactionLog < ApplicationRecord
belongs_to :company
belongs_to :accountant
end
семена.rb
awesomeco = Company.create!(name: 'AwesomeCo')
boringco = Company.create!(name: 'BoringCo')
mr_smith = Accountant.create!(name: "Mr. Smith")
awesomeco.accountants << mr_smith
boringco.accountants << mr_smith
mr_smith.transaction_logs.create!(company: awesomeco, activity_at: 1.day.ago)
mr_smith.transaction_logs.create!(company: boringco, activity_at: 2.days.ago)
Публичное репо с полным кодом, предназначенное в качестве учебного ресурса:
https://github.com/rubynor/graphql-ruby-training-ground