Skip to content

[ruby] Add support for ATO SDK v2 endpoints #4909

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 14 additions & 14 deletions manifests/ruby.yml
Original file line number Diff line number Diff line change
Expand Up @@ -437,20 +437,20 @@ tests/:
Test_UserLoginSuccessEvent: v1.9.0
Test_UserLoginSuccessEvent_Metrics: missing_feature
test_event_tracking_v2.py:
Test_UserLoginFailureEventV2_HeaderCollection_AppsecDisabled: missing_feature
Test_UserLoginFailureEventV2_HeaderCollection_AppsecEnabled: missing_feature
Test_UserLoginFailureEventV2_Libddwaf: missing_feature
Test_UserLoginFailureEventV2_Metrics_AppsecDisabled: missing_feature
Test_UserLoginFailureEventV2_Metrics_AppsecEnabled: missing_feature
Test_UserLoginFailureEventV2_Tags_AppsecDisabled: missing_feature
Test_UserLoginFailureEventV2_Tags_AppsecEnabled: missing_feature
Test_UserLoginSuccessEventV2_HeaderCollection_AppsecDisabled: missing_feature
Test_UserLoginSuccessEventV2_HeaderCollection_AppsecEnabled: missing_feature
Test_UserLoginSuccessEventV2_Libddwaf: missing_feature
Test_UserLoginSuccessEventV2_Metrics_AppsecDisabled: missing_feature
Test_UserLoginSuccessEventV2_Metrics_AppsecEnabled: missing_feature
Test_UserLoginSuccessEventV2_Tags_AppsecDisabled: missing_feature
Test_UserLoginSuccessEventV2_Tags_AppsecEnabled: missing_feature
Test_UserLoginFailureEventV2_HeaderCollection_AppsecDisabled: v2.19.0
Test_UserLoginFailureEventV2_HeaderCollection_AppsecEnabled: v2.19.0
Test_UserLoginFailureEventV2_Libddwaf: v2.19.0
Test_UserLoginFailureEventV2_Metrics_AppsecDisabled: v2.19.0
Test_UserLoginFailureEventV2_Metrics_AppsecEnabled: v2.19.0
Test_UserLoginFailureEventV2_Tags_AppsecDisabled: missing_feature (no service entry span in tracer)
Test_UserLoginFailureEventV2_Tags_AppsecEnabled: v2.19.0
Test_UserLoginSuccessEventV2_HeaderCollection_AppsecDisabled: v2.19.0
Test_UserLoginSuccessEventV2_HeaderCollection_AppsecEnabled: v2.19.0
Test_UserLoginSuccessEventV2_Libddwaf: v2.19.0
Test_UserLoginSuccessEventV2_Metrics_AppsecDisabled: v2.19.0
Test_UserLoginSuccessEventV2_Metrics_AppsecEnabled: v2.19.0
Test_UserLoginSuccessEventV2_Tags_AppsecDisabled: missing_feature (no service entry span in tracer)
Test_UserLoginSuccessEventV2_Tags_AppsecEnabled: v2.19.0
test_extended_header_collection.py:
Test_ExtendedHeaderCollection: missing_feature
test_extended_request_body_collection.py:
Expand Down
4 changes: 4 additions & 0 deletions tests/appsec/test_event_tracking_v2.py
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ def setup_user_login_success_event_multi_type_metadata(self):

self.r = weblog.post("/user_login_success_event_v2", json=data, headers=headers)

@irrelevant(library="ruby", reason="dd-trace-rb only accepts string metadata values")
@irrelevant(library="golang", reason="dd-trace-go only accepts string metadata values")
@irrelevant(library="java", reason="dd-trace-java only accepts string metadata values")
@irrelevant(library="php", reason="dd-trace-php only accepts string metadata values")
Expand Down Expand Up @@ -147,6 +148,7 @@ def test_user_login_success_event_no_metadata(self):
self.r, validator=self.get_user_login_success_tags_validator(LOGIN_SAFE, USER_ID_SAFE)
)

@irrelevant(library="ruby", reason="dd-trace-rb only accepts string metadata values")
@irrelevant(library="java", reason="dd-trace-java only accepts string metadata values")
def setup_user_login_success_event_deep_metadata(self):
headers = {
Expand All @@ -162,6 +164,7 @@ def setup_user_login_success_event_deep_metadata(self):

self.r = weblog.post("/user_login_success_event_v2", json=data, headers=headers)

@irrelevant(library="ruby", reason="dd-trace-rb only accepts string metadata values")
@irrelevant(library="golang", reason="dd-trace-go only accepts string metadata values")
@irrelevant(library="java", reason="dd-trace-java only accepts string metadata values")
@irrelevant(library="dotnet", reason="dd-trace-dotnet only accepts string metadata values")
Expand Down Expand Up @@ -423,6 +426,7 @@ def setup_user_login_failure_event_deep_metadata(self):

self.r = weblog.post("/user_login_failure_event_v2", json=data, headers=headers)

@irrelevant(library="ruby", reason="dd-trace-rb only accepts string metadata values")
@irrelevant(library="golang", reason="dd-trace-go only accepts string metadata values")
@irrelevant(library="java", reason="dd-trace-java only accepts string metadata values")
@irrelevant(library="dotnet", reason="dd-trace-dotnet only accepts string metadata values")
Expand Down
5 changes: 4 additions & 1 deletion utils/_context/_scenarios/endtoend.py
Original file line number Diff line number Diff line change
Expand Up @@ -539,7 +539,10 @@ def _wait_and_stop_containers(self, *, force_interface_timout_to_zero: bool):
interfaces.library, 0 if force_interface_timout_to_zero else self.library_interface_timeout
)

if self.library in ("nodejs",):
if self.library in (
"nodejs",
"ruby",
):
from utils import weblog # TODO better interface

# for weblogs who supports it, call the flush endpoint
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
require 'json'

require 'datadog/kit/appsec/events'
require 'datadog/kit/appsec/events/v2'

class BusinessLogicEventsController < ApplicationController
skip_before_action :verify_authenticity_token

def user_login_success_event
Datadog::Kit::AppSec::Events.track_login_success(
Datadog::Tracing.active_trace,
user: {id: 'system_tests_user'},
metadata0: "value0",
metadata1: "value1"
)

render plain: 'Hello, world!'
end

def user_login_failure_event
Datadog::Kit::AppSec::Events.track_login_failure(
Datadog::Tracing.active_trace,
user_id: 'system_tests_user',
user_exists: true,
metadata0: "value0",
metadata1: "value1"
)

render plain: 'Hello, world!'
end

def custom_event
Datadog::Kit::AppSec::Events.track(
'system_tests_event',
Datadog::Tracing.active_trace,
metadata0: "value0",
metadata1: "value1"
)

render plain: 'Hello, world!'
end

def user_login_success_event_v2
payload = params.to_unsafe_h

Datadog::Kit::AppSec::Events::V2.track_user_login_success(
payload['login'],
payload['user_id'],
**payload.fetch('metadata', {}).symbolize_keys
)

render plain: 'OK'
end

def user_login_failure_event_v2
payload = params.to_unsafe_h

Datadog::Kit::AppSec::Events::V2.track_user_login_failure(
payload['login'],
payload.fetch('exists', 'false') == 'true',
**payload.fetch('metadata', {}).symbolize_keys
)

render plain: 'OK'
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
class InternalController < ApplicationController
skip_before_action :verify_authenticity_token

def healthcheck
gemspec = Gem.loaded_specs['datadog'] || Gem.loaded_specs['ddtrace']
version = gemspec.version.to_s
version = "#{version}-dev" unless gemspec.source.is_a?(Bundler::Source::Rubygems)

render json: {status: 'ok', library: {name: 'ruby', version: version}}
end

def flush
# NOTE: If anything needs to be flushed here before the test suite ends,
# this is the place to do it.
# See https://github.com/DataDog/system-tests/blob/64539d1d19d14e0ab040d8e4a01562da1531b7d5/docs/internals/flushing.md
if telemetry = Datadog.send(:components)&.telemetry
telemetry.instance_variable_get(:@worker)&.loop_wait_time = 0

# HACK: In the current implementation there is no way to force the flushing.
# Instead we are giving us a fraction of time after setting `loop_wait_time`
# and just wait till all penging messages are flushed.
#
# NOTE: Be aware that system-tests doesn't like slow responses, so change that
# value carefully.
sleep 0.2
end

render plain: 'OK'
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,6 @@ def root
render plain: 'Hello, world!'
end

def healthcheck
gemspec = Gem.loaded_specs['datadog'] || Gem.loaded_specs['ddtrace']
version = gemspec.version.to_s
version = "#{version}-dev" unless gemspec.source.is_a?(Bundler::Source::Rubygems)
render json: {
status: 'ok',
library: {
name: 'ruby',
version: version
}
}
end

def waf
render plain: 'Hello, world!'
end
Expand Down Expand Up @@ -97,28 +84,6 @@ def make_distant_call
render json: result
end

def user_login_success_event
Datadog::Kit::AppSec::Events.track_login_success(
Datadog::Tracing.active_trace, user: {id: 'system_tests_user'}, metadata0: "value0", metadata1: "value1"
)

render plain: 'Hello, world!'
end

def user_login_failure_event
Datadog::Kit::AppSec::Events.track_login_failure(
Datadog::Tracing.active_trace, user_id: 'system_tests_user', user_exists: true, metadata0: "value0", metadata1: "value1"
)

render plain: 'Hello, world!'
end

def custom_event
Datadog::Kit::AppSec::Events.track('system_tests_event', Datadog::Tracing.active_trace, metadata0: "value0", metadata1: "value1")

render plain: 'Hello, world!'
end

def tag_value
event_value = params[:tag_value]
status_code = params[:status_code]
Expand Down
9 changes: 6 additions & 3 deletions utils/build/docker/ruby/graphql23/config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@
get '/' => 'system_test#root'
post '/' => 'system_test#root'

get '/healthcheck' => 'system_test#healthcheck'
get '/healthcheck' => 'internal#healthcheck'
get '/flush' => 'internal#flush'

get '/waf' => 'system_test#waf'
post '/waf' => 'system_test#waf'
Expand All @@ -23,9 +24,11 @@
get '/headers' => 'system_test#test_headers'
get '/identify' => 'system_test#identify'

get 'user_login_success_event' => 'system_test#user_login_success_event'
get 'user_login_failure_event' => 'system_test#user_login_failure_event'
get 'user_login_success_event' => 'business_logic_events#user_login_success_event'
get 'user_login_failure_event' => 'business_logic_events#user_login_failure_event'
get 'custom_event' => 'system_test#custom_event'
post 'user_login_success_event_v2' => 'business_logic_events#user_login_success_event_v2'
post 'user_login_failure_event_v2' => 'business_logic_events#user_login_failure_event_v2'

%i(get post).each do |request_method|
send(request_method, '/tag_value/:tag_value/:status_code' => 'system_test#tag_value')
Expand Down
68 changes: 68 additions & 0 deletions utils/build/docker/ruby/rack/config.ru
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@ begin
rescue LoadError
require 'ddtrace/auto_instrument'
end

require 'datadog/kit/appsec/events'
require 'datadog/kit/appsec/events/v2'

Datadog.configure do |c|
c.diagnostics.debug = true
Expand Down Expand Up @@ -337,6 +339,42 @@ module ApiSecuritySampling
end
end

# POST /user_login_success_event_v2
module UserLoginSuccessEventV2
module_function

def run(request)
request.body.rewind
payload = JSON.parse(request.body.read)

Datadog::Kit::AppSec::Events::V2.track_user_login_success(
payload['login'],
payload['user_id'],
payload.fetch('metadata', {}).transform_keys(&:to_sym)
)

[200, { 'Content-Type' => 'text/plain' }, ['OK']]
end
end

# POST /user_login_failure_event_v2
module UserLoginFailureEventV2
module_function

def run(request)
request.body.rewind
payload = JSON.parse(request.body.read)

Datadog::Kit::AppSec::Events::V2.track_user_login_failure(
payload['login'],
payload.fetch('exists', 'false') == 'true',
payload.fetch('metadata', {}).transform_keys(&:to_sym)
)

[200, { 'Content-Type' => 'text/plain' }, ['OK']]
end
end

# any other route
module NotFound
module_function
Expand All @@ -362,6 +400,30 @@ end

use TraceSamplingMiddleware

# /flush
module Flush
module_function

def run(request)
# NOTE: If anything needs to be flushed here before the test suite ends,
# this is the place to do it.
# See https://github.com/DataDog/system-tests/blob/64539d1d19d14e0ab040d8e4a01562da1531b7d5/docs/internals/flushing.md
if (telemetry = Datadog.send(:components)&.telemetry)
telemetry.instance_variable_get(:@worker)&.loop_wait_time = 0

# HACK: In the current implementation there is no way to force the flushing.
# Instead we are giving us a fraction of time after setting `loop_wait_time`
# and just wait till all penging messages are flushed.
#
# NOTE: Be aware that system-tests doesn't like slow responses, so change that
# value carefully.
sleep 0.2
end

[200, { 'Content-Type' => 'text/plain' }, ['OK']]
end
end

# trivial rack endpoint. We use a proc instead of Rack Builder because
# we compare the request path using regexp and include?
app = proc do |env|
Expand Down Expand Up @@ -405,6 +467,12 @@ app = proc do |env|
ApiSecurityWithSampling.run(request)
elsif request.path.include?('/api_security_sampling/')
ApiSecuritySampling.run(request)
elsif request.path == '/user_login_success_event_v2'
UserLoginSuccessEventV2.run(request)
elsif request.path == '/user_login_failure_event_v2'
UserLoginFailureEventV2.run(request)
elsif request.path == '/flush'
Flush.run(request)
else
NotFound.run
end
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
class ApiSecurityController < ApplicationController
skip_before_action :verify_authenticity_token

def sample_rate_route
render plain: 'OK'
end

def sampling_by_path
render plain: 'Hello!'
end

def sampling_by_status
render plain: 'OK', status: params.fetch(:status, 200).to_i
end
end
Loading
Loading