Skip to content

Update login events public SDK to V2 #8620

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

Merged
merged 7 commits into from
Mar 28, 2025
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -68,9 +68,13 @@ public class GatewayBridge {

/** User tracking tags that will force the collection of request headers */
private static final String[] USER_TRACKING_TAGS = {
"appsec.events.users.login.success.track", "appsec.events.users.login.failure.track"
"appsec.events.users.login.success.track",
"appsec.events.users.login.failure.track",
"appsec.events.users.signup.track"
};

private static final String USER_COLLECTION_MODE_TAG = "_dd.appsec.user.collection_mode";

private static final Map<LoginEvent, Address<?>> EVENT_MAPPINGS = new EnumMap<>(LoginEvent.class);

static {
Expand Down Expand Up @@ -708,7 +712,7 @@ private NoopFlow onRequestEnded(RequestContext ctx_, IGSpanInfo spanInfo) {
StackUtils.addStacktraceEventsToMetaStruct(ctx_, METASTRUCT_EXPLOIT, stackTraces);
}

} else if (hasUserTrackingEvent(traceSeg)) {
} else if (hasUserInfo(traceSeg)) {
// Report all collected request headers on user tracking event
writeRequestHeaders(traceSeg, REQUEST_HEADERS_ALLOW_LIST, ctx.getRequestHeaders());
} else {
Expand Down Expand Up @@ -803,6 +807,10 @@ public void stop() {
subscriptionService.reset();
}

private static boolean hasUserInfo(final TraceSegment traceSegment) {
return hasUserTrackingEvent(traceSegment) || hasUserCollectionEvent(traceSegment);
}

private static boolean hasUserTrackingEvent(final TraceSegment traceSeg) {
for (String tagName : USER_TRACKING_TAGS) {
final Object value = traceSeg.getTagTop(tagName);
Expand All @@ -813,6 +821,10 @@ private static boolean hasUserTrackingEvent(final TraceSegment traceSeg) {
return false;
}

private static boolean hasUserCollectionEvent(final TraceSegment traceSeg) {
return traceSeg.getTagTop(USER_COLLECTION_MODE_TAG) != null;
}

private static void writeRequestHeaders(
final TraceSegment traceSeg,
final Set<String> allowed,
Expand Down

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.datadog.appsec.user

import datadog.appsec.api.login.EventTrackerV2
import datadog.appsec.api.user.User
import datadog.trace.api.GlobalTracer
import datadog.trace.api.UserIdCollectionMode
Expand All @@ -26,6 +27,7 @@ class EventTrackerAppSecDisabledForkedTest extends DDSpecification {
void setup() {
tracker = new AppSecEventTracker()
GlobalTracer.setEventTracker(tracker)
EventTrackerV2.setEventTrackerService(tracker)
User.setUserService(tracker)
traceSegment = Mock(TraceSegment)
final tracer = Stub(AgentTracer.TracerAPI) {
Expand All @@ -40,15 +42,15 @@ class EventTrackerAppSecDisabledForkedTest extends DDSpecification {
GlobalTracer.getEventTracker().trackLoginSuccessEvent('user', ['key1': 'value1', 'key2': 'value2'])

then:
1 * traceSegment.setTagTop('_dd.appsec.events.users.login.success.sdk', true)
1 * traceSegment.setTagTop('_dd.appsec.events.users.login.success.sdk', true, true)
}

void 'test track login failure event (SDK)'() {
when:
GlobalTracer.getEventTracker().trackLoginFailureEvent('user', true, ['key1': 'value1', 'key2': 'value2'])

then:
1 * traceSegment.setTagTop('_dd.appsec.events.users.login.failure.sdk', true)
1 * traceSegment.setTagTop('_dd.appsec.events.users.login.failure.sdk', true, true)
}

void 'test track custom event (SDK)'() {
Expand All @@ -59,6 +61,30 @@ class EventTrackerAppSecDisabledForkedTest extends DDSpecification {
1 * traceSegment.setTagTop('_dd.appsec.events.myevent.sdk', true, true)
}

void 'test track login success event V2 (SDK)'() {
when:
EventTrackerV2.trackUserLoginSuccess('user', 'id', ['key1': 'value1', 'key2': 'value2'])

then:
1 * traceSegment.setTagTop('_dd.appsec.events.users.login.success.sdk', true, true)
}

void 'test track login failure event V2 (SDK)'() {
when:
EventTrackerV2.trackUserLoginFailure('user', true, ['key1': 'value1', 'key2': 'value2'])

then:
1 * traceSegment.setTagTop('_dd.appsec.events.users.login.failure.sdk', true, true)
}

void 'test track custom event V2 (SDK)'() {
when:
EventTrackerV2.trackCustomEvent('myevent', ['key1': 'value1', 'key2': 'value2'])

then:
1 * traceSegment.setTagTop('_dd.appsec.events.myevent.sdk', true, true)
}

void 'test onSignup'() {
when:
tracker.onSignupEvent(IDENTIFICATION, 'user', ['key1': 'value1', 'key2': 'value2'])
Expand Down
1 change: 1 addition & 0 deletions dd-trace-api/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ excludedClassesCoverage += [
'datadog.trace.api.experimental.DataStreamsContextCarrier.NoOp',
'datadog.appsec.api.blocking.*',
'datadog.appsec.api.user.*',
'datadog.appsec.api.login.*',
// Default fallback methods to not break legacy API
'datadog.trace.context.TraceScope',
'datadog.trace.context.NoopTraceScope.NoopContinuation',
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package datadog.appsec.api.login;

import java.util.Map;

public interface EventTrackerService {

EventTrackerService NO_OP =
new EventTrackerService() {
@Override
public void trackUserLoginSuccess(
final String login, final String userId, final Map<String, String> metadata) {}

@Override
public void trackUserLoginFailure(
final String login, final boolean exists, final Map<String, String> metadata) {}

@Override
public void trackCustomEvent(final String eventName, final Map<String, String> metadata) {}
};

void trackUserLoginSuccess(String login, String userId, Map<String, String> metadata);

void trackUserLoginFailure(String login, boolean exists, Map<String, String> metadata);

void trackCustomEvent(String eventName, Map<String, String> metadata);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package datadog.appsec.api.login;

import java.util.Map;

public class EventTrackerV2 {

private static volatile EventTrackerService SERVICE = EventTrackerService.NO_OP;

/**
* Controls the implementation for service. The AppSec subsystem calls this method on startup.
* This can be called explicitly for e.g. testing purposes.
*
* @param service the implementation for the user service.
*/
public static void setEventTrackerService(final EventTrackerService service) {
SERVICE = service;
}

/**
* Tracks a successful user login event.
*
* @param login the non-null login data (e.g., username or email) used for authentication.
* @param userId an optional user ID string; can be null.
* @param metadata optional metadata for the login event; can be null.
*/
public static void trackUserLoginSuccess(
final String login, final String userId, Map<String, String> metadata) {
SERVICE.trackUserLoginSuccess(login, userId, metadata);
}

/**
* Tracks a failed user login event.
*
* @param login the non-null login data (e.g., username or email) used for authentication.
* @param exists indicates whether the provided login identifier corresponds to an existing user.
* @param metadata optional metadata for the login event; can be null.
*/
public static void trackUserLoginFailure(
String login, boolean exists, Map<String, String> metadata) {
SERVICE.trackUserLoginFailure(login, exists, metadata);
}

/**
* Method for tracking custom events.
*
* @param eventName name of the custom event
* @param metadata custom metadata data represented as key/value map
*/
public static void trackCustomEvent(String eventName, Map<String, String> metadata) {
SERVICE.trackCustomEvent(eventName, metadata);
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
package datadog.trace.api;

import datadog.appsec.api.login.EventTrackerV2;
import java.util.Map;

/** This class has been deprecated in favor of {@link EventTrackerV2} */
@Deprecated
public class EventTracker {

public static final EventTracker NO_EVENT_TRACKER = new EventTracker();
Expand All @@ -12,7 +15,9 @@ public class EventTracker {
*
* @param userId user id used for login
* @param metadata custom metadata data represented as key/value map
* @deprecated use {@link EventTrackerV2#trackUserLoginSuccess(String, String, Map)}
*/
@Deprecated
public void trackLoginSuccessEvent(String userId, Map<String, String> metadata) {}

/**
Expand All @@ -22,6 +27,7 @@ public void trackLoginSuccessEvent(String userId, Map<String, String> metadata)
* @param userId user id used for login
* @param exists flag indicates if provided userId exists
* @param metadata custom metadata data represented as key/value map
* @deprecated use {@link EventTrackerV2#trackUserLoginFailure(String, boolean, Map)}
*/
public void trackLoginFailureEvent(String userId, boolean exists, Map<String, String> metadata) {}

Expand All @@ -31,6 +37,7 @@ public void trackLoginFailureEvent(String userId, boolean exists, Map<String, St
*
* @param eventName name of the custom event
* @param metadata custom metadata data represented as key/value map
* @deprecated use {@link EventTrackerV2#trackCustomEvent(String, Map)}
*/
public void trackCustomEvent(String eventName, Map<String, String> metadata) {}
}
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,8 @@ public static Tracer get() {
return provider;
}

/** @deprecated use static methods in {@link EventTrackerV2} directly */
@Deprecated
public static EventTracker getEventTracker() {
return eventTracker;
}
Expand Down
Loading