diff --git a/contract-tests/service.rb b/contract-tests/service.rb index d7d58720..2442a1c9 100644 --- a/contract-tests/service.rb +++ b/contract-tests/service.rb @@ -37,6 +37,7 @@ 'event-sampling', 'context-comparison', 'inline-context', + 'anonymous-redaction', ], }.to_json end diff --git a/lib/ldclient-rb/events.rb b/lib/ldclient-rb/events.rb index f0ea7fd5..4800163b 100644 --- a/lib/ldclient-rb/events.rb +++ b/lib/ldclient-rb/events.rb @@ -481,12 +481,14 @@ def make_output_events(events, summary) key: event.key, value: event.value, } + out[:default] = event.default unless event.default.nil? out[:variation] = event.variation unless event.variation.nil? out[:version] = event.version unless event.version.nil? out[:prereqOf] = event.prereq_of unless event.prereq_of.nil? - out[:context] = @context_filter.filter(event.context) + out[:context] = @context_filter.filter_redact_anonymous(event.context) out[:reason] = event.reason unless event.reason.nil? + out when LaunchDarkly::Impl::MigrationOpEvent diff --git a/lib/ldclient-rb/impl/context_filter.rb b/lib/ldclient-rb/impl/context_filter.rb index 8ed0c19f..bec00400 100644 --- a/lib/ldclient-rb/impl/context_filter.rb +++ b/lib/ldclient-rb/impl/context_filter.rb @@ -23,14 +23,32 @@ def initialize(all_attributes_private, private_attributes) # @return [Hash] # def filter(context) - return filter_single_context(context, true) unless context.multi_kind? + internal_filter(context, false) + end + + # + # Return a hash representation of the provided context with attribute + # redaction applied. + # + # If a context is anonyomous, all attributes will be redacted except + # for key, kind, and anonymous. + # + # @param context [LaunchDarkly::LDContext] + # @return [Hash] + # + def filter_redact_anonymous(context) + internal_filter(context, true) + end + + private def internal_filter(context, redact_anonymous) + return filter_single_context(context, true, redact_anonymous) unless context.multi_kind? filtered = {kind: 'multi'} (0...context.individual_context_count).each do |i| c = context.individual_context(i) next if c.nil? - filtered[c.kind] = filter_single_context(c, false) + filtered[c.kind] = filter_single_context(c, false, redact_anonymous) end filtered @@ -43,22 +61,24 @@ def filter(context) # @param include_kind [Boolean] # @return [Hash] # - private def filter_single_context(context, include_kind) + private def filter_single_context(context, include_kind, redact_anonymous) filtered = {key: context.key} filtered[:kind] = context.kind if include_kind - filtered[:anonymous] = true if context.get_value(:anonymous) + + anonymous = context.get_value(:anonymous) + filtered[:anonymous] = true if anonymous redacted = [] private_attributes = @private_attributes.concat(context.private_attributes) name = context.get_value(:name) - if !name.nil? && !check_whole_attribute_private(:name, private_attributes, redacted) + if !name.nil? && !check_whole_attribute_private(:name, private_attributes, redacted, anonymous && redact_anonymous) filtered[:name] = name end context.get_custom_attribute_names.each do |attribute| - unless check_whole_attribute_private(attribute, private_attributes, redacted) + unless check_whole_attribute_private(attribute, private_attributes, redacted, anonymous && redact_anonymous) value = context.get_value(attribute) filtered[attribute] = redact_json_value(nil, attribute, value, private_attributes, redacted) end @@ -75,10 +95,11 @@ def filter(context) # @param attribute [Symbol] # @param private_attributes [Array] # @param redacted [Array] + # @param redact_all [Boolean] # @return [Boolean] # - private def check_whole_attribute_private(attribute, private_attributes, redacted) - if @all_attributes_private + private def check_whole_attribute_private(attribute, private_attributes, redacted, redact_all) + if @all_attributes_private || redact_all redacted << attribute return true end diff --git a/spec/events_spec.rb b/spec/events_spec.rb index 1cdf84ae..f10100fd 100644 --- a/spec/events_spec.rb +++ b/spec/events_spec.rb @@ -634,10 +634,12 @@ def identify_event(config, context, timestamp = starting_timestamp) # def feature_event(config, flag, context, variation, value, timestamp = starting_timestamp) context_filter = Impl::ContextFilter.new(config.all_attributes_private, config.private_attributes) + redacted_context = context_filter.filter_redact_anonymous(context) + out = { kind: 'feature', creationDate: timestamp, - context: context_filter.filter(context), + context: redacted_context, key: flag[:key], variation: variation, version: flag[:version],