Merge pull request #8596 from tchak/secu-log-graphql-queries
secu(graphql): log full queries and variables
This commit is contained in:
commit
a217072b4f
4 changed files with 4 additions and 125 deletions
|
@ -1,6 +1,4 @@
|
|||
class API::V2::GraphqlController < API::V2::BaseController
|
||||
include GraphqlOperationLogConcern
|
||||
|
||||
def execute
|
||||
result = API::V2::Schema.execute(query,
|
||||
variables: variables,
|
||||
|
@ -24,7 +22,8 @@ class API::V2::GraphqlController < API::V2::BaseController
|
|||
super
|
||||
|
||||
payload.merge!({
|
||||
graphql_operation: operation_log(query(fallback: ''), params[:operationName], to_unsafe_hash(params[:variables]))
|
||||
graphql_query: query(fallback: params[:queryId]),
|
||||
graphql_variables: to_unsafe_hash(params[:variables]).to_json
|
||||
})
|
||||
end
|
||||
|
||||
|
|
|
@ -1,62 +0,0 @@
|
|||
module GraphqlOperationLogConcern
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
# This method parses GraphQL query and creates a short description of the query. It is useful for logging.
|
||||
def operation_log(query, operation_name, variables)
|
||||
return "NoQuery" if query.nil?
|
||||
|
||||
operation = parse_graphql_query(query, operation_name)
|
||||
|
||||
return "InvalidQuery" if operation.nil?
|
||||
return "IntrospectionQuery" if operation.name == "IntrospectionQuery"
|
||||
|
||||
message = "#{operation.operation_type}: "
|
||||
message += if operation.name.present?
|
||||
"#{operation.name} { "
|
||||
else
|
||||
"{ "
|
||||
end
|
||||
message += operation.selections.map(&:name).join(', ')
|
||||
message += " } "
|
||||
message += if variables.present?
|
||||
variables.flat_map do |(name, value)|
|
||||
format_graphql_variable(name, value)
|
||||
end
|
||||
else
|
||||
operation.selections.flat_map(&:arguments).flat_map do |argument|
|
||||
format_graphql_variable(argument.name, argument.value)
|
||||
end
|
||||
end.join(', ')
|
||||
|
||||
message.strip
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def parse_graphql_query(query, operation_name)
|
||||
operations = GraphQL.parse(query).children.filter do |node|
|
||||
node.is_a?(GraphQL::Language::Nodes::OperationDefinition)
|
||||
end
|
||||
if operations.size == 1
|
||||
operations.first
|
||||
else
|
||||
operations.find { |node| node.name == operation_name }
|
||||
end
|
||||
rescue
|
||||
nil
|
||||
end
|
||||
|
||||
def format_graphql_variable(name, value)
|
||||
if value.is_a?(Hash)
|
||||
value.map do |(name, value)|
|
||||
format_graphql_variable(name, value)
|
||||
end
|
||||
elsif value.is_a?(GraphQL::Language::Nodes::InputObject)
|
||||
value.arguments.map do |argument|
|
||||
format_graphql_variable(argument.name, argument.value)
|
||||
end
|
||||
else
|
||||
"#{name}: \"#{value.to_s.truncate(10)}\""
|
||||
end
|
||||
end
|
||||
end
|
|
@ -14,7 +14,8 @@ Rails.application.configure do
|
|||
user_email: event.payload[:user_email],
|
||||
user_roles: event.payload[:user_roles],
|
||||
user_agent: event.payload[:user_agent],
|
||||
graphql_operation: event.payload[:graphql_operation],
|
||||
graphql_query: event.payload[:graphql_query],
|
||||
graphql_variables: event.payload[:graphql_variables],
|
||||
browser: event.payload[:browser],
|
||||
browser_version: event.payload[:browser_version],
|
||||
platform: event.payload[:platform]
|
||||
|
|
|
@ -1,59 +0,0 @@
|
|||
RSpec.describe GraphqlOperationLogConcern, type: :controller do
|
||||
class TestController < ActionController::Base
|
||||
include GraphqlOperationLogConcern
|
||||
end
|
||||
|
||||
controller TestController do
|
||||
end
|
||||
|
||||
describe '#operation_log' do
|
||||
let(:query) { nil }
|
||||
let(:variables) { nil }
|
||||
let(:operation_name) { nil }
|
||||
|
||||
subject { controller.operation_log(query, operation_name, variables) }
|
||||
|
||||
context 'with no query' do
|
||||
it { expect(subject).to eq('NoQuery') }
|
||||
end
|
||||
|
||||
context 'with invalid query' do
|
||||
let(:query) { 'query { demarche {} }' }
|
||||
|
||||
it { expect(subject).to eq('InvalidQuery') }
|
||||
end
|
||||
|
||||
context 'with two queries' do
|
||||
let(:query) { 'query demarche { demarche } query dossier { dossier }' }
|
||||
let(:operation_name) { 'dossier' }
|
||||
|
||||
it { expect(subject).to eq('query: dossier { dossier }') }
|
||||
end
|
||||
|
||||
context 'with arguments' do
|
||||
let(:query) { 'query demarche { demarche(number: 123) { id } }' }
|
||||
|
||||
it { expect(subject).to eq('query: demarche { demarche } number: "123"') }
|
||||
end
|
||||
|
||||
context 'with variables' do
|
||||
let(:query) { 'query { demarche(number: 123) { id } }' }
|
||||
let(:variables) { { number: 124 } }
|
||||
|
||||
it { expect(subject).to eq('query: { demarche } number: "124"') }
|
||||
end
|
||||
|
||||
context 'with mutation and arguments' do
|
||||
let(:query) { 'mutation { passerDossierEnInstruction(input: { number: 123 }) { id } }' }
|
||||
|
||||
it { expect(subject).to eq('mutation: { passerDossierEnInstruction } number: "123"') }
|
||||
end
|
||||
|
||||
context 'with mutation and variables' do
|
||||
let(:query) { 'mutation { passerDossierEnInstruction(input: { number: 123 }) { id } }' }
|
||||
let(:variables) { { input: { number: 124 } } }
|
||||
|
||||
it { expect(subject).to eq('mutation: { passerDossierEnInstruction } number: "124"') }
|
||||
end
|
||||
end
|
||||
end
|
Loading…
Add table
Reference in a new issue