Graphql-ruby scoping в habtm, утечка данных во вложенном запросе - PullRequest
1 голос
/ 13 октября 2019

Вот пример утечки данных, которая возникает при использовании стандартной установки 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

1 Ответ

1 голос
/ 28 октября 2019

Мы можем обновить поле в class AccountantType < BaseObject следующим образом для разрешения журналов транзакций:

  field :transaction_logs, [Types::ExampleLeak::TransactionLogType], null: true,
    resolve: ->(obj, args, ctx) {
      company_leak = ctx.irep_node.parent.parent.arguments[:id]
      companies_leak = ctx.parent.parent.object.object.id
      TransactionLog.where(id: company_leak.present? ? company_leak : companies_leak)
    }

Если для идентификатора компании задан аргумент, он будет извлекать журналы транзакций для этого идентификатора, в противном случае - для егородитель Accountant

...