Skip to content

Commit

Permalink
feat(appsec: extension): create new function for automated user signu…
Browse files Browse the repository at this point in the history
…p event

Signed-off-by: Alexandre Rulleau <[email protected]>
  • Loading branch information
Leiyks committed Dec 3, 2024
1 parent 89d9529 commit 22d8216
Show file tree
Hide file tree
Showing 22 changed files with 285 additions and 158 deletions.
174 changes: 122 additions & 52 deletions appsec/src/extension/tags.c
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,11 @@
#define DD_METRIC_ENABLED "_dd.appsec.enabled"
#define DD_APPSEC_EVENTS_PREFIX "appsec.events."
#define DD_SIGNUP_EVENT DD_APPSEC_EVENTS_PREFIX "users.signup"
#define DD_SIGNUP_EVENT_LOGIN DD_APPSEC_EVENTS_PREFIX "users.signup.usr.login"
#define DD_LOGIN_SUCCESS_EVENT DD_APPSEC_EVENTS_PREFIX "users.login.success"
#define DD_LOGIN_FAILURE_EVENT DD_APPSEC_EVENTS_PREFIX "users.login.failure"
#define DD_APPSEC_USR_ID "_dd.appsec.usr.id"
#define DD_APPSEC_USR_LOGIN "_dd.appsec.usr.login"
#define DD_EVENTS_USER_SIGNUP_AUTO_MODE \
"_dd.appsec.events.users.signup.auto.mode"
#define DD_EVENTS_USER_LOGIN_SUCCESS_AUTO_MODE \
Expand Down Expand Up @@ -79,9 +82,12 @@ static zend_string *_dd_tag_rh_content_language; // response
static zend_string *_dd_tag_user_id;
static zend_string *_dd_metric_enabled;
static zend_string *_dd_signup_event;
static zend_string *_dd_signup_event_login;
static zend_string *_dd_login_success_event;
static zend_string *_dd_login_failure_event;
static zend_string *_dd_login_failure_event;
static zend_string *_dd_appsec_user_id;
static zend_string *_dd_appsec_user_login;
static zend_string *_dd_signup_event_auto_mode;
static zend_string *_dd_login_success_event_auto_mode;
static zend_string *_dd_login_failure_event_auto_mode;
Expand Down Expand Up @@ -177,10 +183,16 @@ void dd_tags_startup()
_track_zstr =
zend_string_init_interned(LSTRARG("track"), 1 /* permanent */);
_dd_signup_event = zend_string_init_interned(LSTRARG(DD_SIGNUP_EVENT), 1);
_dd_signup_event_login =
zend_string_init_interned(LSTRARG(DD_SIGNUP_EVENT_LOGIN), 1);
_dd_login_success_event =
zend_string_init_interned(LSTRARG(DD_LOGIN_SUCCESS_EVENT), 1);
_dd_login_failure_event =
zend_string_init_interned(LSTRARG(DD_LOGIN_FAILURE_EVENT), 1);
_dd_appsec_user_id =
zend_string_init_interned(LSTRARG(DD_APPSEC_USR_ID), 1);
_dd_appsec_user_login =
zend_string_init_interned(LSTRARG(DD_APPSEC_USR_LOGIN), 1);
_dd_signup_event_auto_mode =
zend_string_init_interned(LSTRARG(DD_EVENTS_USER_SIGNUP_AUTO_MODE), 1);
_dd_login_success_event_auto_mode = zend_string_init_interned(
Expand Down Expand Up @@ -877,7 +889,7 @@ static zval *nullable _root_span_get_meta()
return meta;
}

static PHP_FUNCTION(datadog_appsec_track_user_signup_event)
static PHP_FUNCTION(datadog_appsec_track_user_signup_event_automated)
{
UNUSED(return_value);
if (!DDAPPSEC_G(active)) {
Expand All @@ -886,83 +898,136 @@ static PHP_FUNCTION(datadog_appsec_track_user_signup_event)
return;
}

zend_string *user_id = NULL;
zend_string *user_login = NULL;
zend_string *user_id = NULL;
HashTable *metadata = NULL;
zend_bool automated = false; // Don't document. Only internal usage
zend_bool copy_user_id = true;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "SS|hb", &user_id, &user_login,
&metadata, &automated) == FAILURE) {
zend_bool copy_user_info = true;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "SS|h", &user_login, &user_id,
&metadata) == FAILURE) {
mlog(dd_log_warning, "Unexpected parameter combination, expected "
"(user_id, metadata)");
"(user_login, user_id, metadata)");
return;
}

if (automated) {
user_collection_mode mode = dd_get_user_collection_mode();
if (mode == user_mode_disabled ||
!get_DD_APPSEC_AUTOMATED_USER_EVENTS_TRACKING_ENABLED()) {
return;
}
if (user_login == NULL || ZSTR_LEN(user_login) == 0) {
mlog(dd_log_warning, "Unexpected empty user login");
return;
}

if (mode == user_mode_anon) {
// Anonymize the user ID and ensure it isn't copied twice
user_id = dd_user_id_anonymize(user_id);
if (user_id == NULL) {
mlog(dd_log_debug, "Failed to anonymize user ID");
return;
}
user_collection_mode mode = dd_get_user_collection_mode();
if (mode == user_mode_disabled ||
!get_DD_APPSEC_AUTOMATED_USER_EVENTS_TRACKING_ENABLED()) {
return;
}

copy_user_id = false;
if (mode == user_mode_anon) {
// Anonymize the user ID and ensure it isn't copied twice
user_id = dd_user_info_anonymize(user_id);
if (user_id == NULL) {
mlog(dd_log_debug, "Failed to anonymize user ID");
return;
}
} else {
if (user_id == NULL || ZSTR_LEN(user_id) == 0) {
mlog(dd_log_warning, "Unexpected empty user id");

user_login = dd_user_info_anonymize(user_login);
if (user_login == NULL) {
mlog(dd_log_debug, "Failed to anonymize user login");
return;
}

copy_user_info = false;
}

zval *nullable meta = _root_span_get_meta();
if (!meta) {
if (!copy_user_id) {
if (!copy_user_info) {
zend_string_release(user_id);
zend_string_release(user_login);
}
return;
}

_user_event_triggered = true;

zend_array *meta_ht = Z_ARRVAL_P(meta);
bool override = !automated;

if (user_id && ZSTR_LEN(user_id) > 0) {
// usr.id = <user_id>
_add_new_zstr_to_meta(
meta_ht, _dd_tag_user_id, user_id, copy_user_id, override);
meta_ht, _dd_tag_user_id, user_id, copy_user_info, false);

// _dd.appsec.usr.id = <user_id>
_add_new_zstr_to_meta(
meta_ht, _dd_appsec_user_id, user_id, copy_user_info, true);
}

if (automated) {
// In automated mode, metadata must no longer be sent
// _dd.appsec.events.users.signup.auto.mode =
// <DD_APPSEC_AUTOMATED_USER_EVENTS_TRACKING>
if (mode != user_mode_disabled) {
_add_new_zstr_to_meta(meta_ht, _dd_signup_event_auto_mode,
dd_get_user_collection_mode_zstr(), true, false);
}

// _dd.appsec.events.users.signup.auto.mode =
// <DD_APPSEC_AUTOMATED_USER_EVENTS_TRACKING>
if (dd_get_user_collection_mode() != user_mode_disabled) {
_add_new_zstr_to_meta(meta_ht, _dd_signup_event_auto_mode,
dd_get_user_collection_mode_zstr(), true, override);
}
} else {
// _dd.appsec.events.users.signup.sdk = true
_add_new_zstr_to_meta(
meta_ht, _dd_signup_event_sdk, _true_zstr, true, override);
// _dd.appsec.events.users.signup.usr.login = <user_login>
_add_new_zstr_to_meta(
meta_ht, _dd_signup_event_login, user_login, copy_user_info, true);

// appsec.events.users.signup.<key> = <value>
_add_custom_event_metadata(
meta_ht, _dd_signup_event, metadata, override);
// _dd.appsec.usr.login = <user_login>
_add_new_zstr_to_meta(
meta_ht, _dd_appsec_user_login, user_login, copy_user_info, true);

// appsec.events.users.login.success.track = true
_add_custom_event_keyval(
meta_ht, _dd_signup_event, _track_zstr, _true_zstr, true, false);

dd_tags_set_sampling_priority();
}

static PHP_FUNCTION(datadog_appsec_track_user_signup_event)
{
UNUSED(return_value);
if (!DDAPPSEC_G(active)) {
mlog(dd_log_debug, "Trying to access to track_user_signup_event "
"function while appsec is disabled");
return;
}

zend_string *user_id = NULL;
HashTable *metadata = NULL;
zend_bool automated = false; // Don't document. Only internal usage
zend_bool copy_user_id = true;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "S|hb", &user_id, &metadata,
&automated) == FAILURE) {
mlog(dd_log_warning, "Unexpected parameter combination, expected "
"(user_id, metadata)");
return;
}

if (user_id == NULL || ZSTR_LEN(user_id) == 0) {
mlog(dd_log_warning, "Unexpected empty user id");
return;
}

zval *nullable meta = _root_span_get_meta();
if (!meta) {
return;
}

_user_event_triggered = true;
zend_array *meta_ht = Z_ARRVAL_P(meta);

// usr.id = <user_id>
_add_new_zstr_to_meta(
meta_ht, _dd_tag_user_id, user_id, copy_user_id, true);

// _dd.appsec.events.users.signup.sdk = true
_add_new_zstr_to_meta(
meta_ht, _dd_signup_event_sdk, _true_zstr, true, true);

// appsec.events.users.signup.<key> = <value>
_add_custom_event_metadata(meta_ht, _dd_signup_event, metadata, true);

// appsec.events.users.login.success.track = true
_add_custom_event_keyval(
meta_ht, _dd_signup_event, _track_zstr, _true_zstr, true, override);
meta_ht, _dd_signup_event, _track_zstr, _true_zstr, true, true);

dd_tags_set_sampling_priority();
}
Expand Down Expand Up @@ -995,7 +1060,7 @@ static PHP_FUNCTION(datadog_appsec_track_user_login_success_event)
}

if (mode == user_mode_anon) {
user_id = dd_user_id_anonymize(user_id);
user_id = dd_user_info_anonymize(user_id);
if (user_id == NULL) {
mlog(dd_log_debug, "Failed to anonymize user ID");
return;
Expand Down Expand Up @@ -1086,7 +1151,7 @@ static PHP_FUNCTION(datadog_appsec_track_user_login_failure_event)
}

if (mode == user_mode_anon) {
user_id = dd_user_id_anonymize(user_id);
user_id = dd_user_info_anonymize(user_id);
if (user_id == NULL) {
mlog(dd_log_debug, "Failed to anonymize user ID");
return;
Expand Down Expand Up @@ -1254,23 +1319,27 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(add_ancillary_tags, 0, 1, IS_VOID, 0)
ZEND_ARG_TYPE_INFO(2, "_server", IS_ARRAY, 0)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(track_user_login_success_event_arginfo, 0, 0, IS_VOID, 4)
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(track_user_login_success_event_arginfo, 0, 0, IS_VOID, 3)
ZEND_ARG_INFO(0, user_id)
ZEND_ARG_INFO(0, metadata)
ZEND_ARG_INFO(0, automated)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(track_user_signup_event_automated_arginfo, 0, 0, IS_VOID, 4)
ZEND_ARG_INFO(0, user_login)
ZEND_ARG_INFO(0, user_id)
ZEND_ARG_INFO(0, metadata)
ZEND_ARG_INFO(0, automated)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(datadog_appsec_track_user_signup_event_arginfo, 0, 0, IS_VOID, 4)
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(track_user_signup_event_arginfo, 0, 0, IS_VOID, 3)
ZEND_ARG_INFO(0, user_id)
ZEND_ARG_INFO(0, user_login)
ZEND_ARG_INFO(0, metadata)
ZEND_ARG_INFO(0, automated)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(track_user_login_failure_event_arginfo, 0, 0, IS_VOID, 5)
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(track_user_login_failure_event_arginfo, 0, 0, IS_VOID, 4)
ZEND_ARG_INFO(0, user_id)
ZEND_ARG_INFO(0, user_login)
ZEND_ARG_INFO(0, exists)
ZEND_ARG_INFO(0, metadata)
ZEND_ARG_INFO(0, automated)
Expand All @@ -1282,7 +1351,8 @@ ZEND_ARG_INFO(0, metadata)
ZEND_END_ARG_INFO()

static const zend_function_entry functions[] = {
ZEND_RAW_FENTRY(DD_APPSEC_NS "track_user_signup_event", PHP_FN(datadog_appsec_track_user_signup_event), datadog_appsec_track_user_signup_event_arginfo, 0, NULL, NULL)
ZEND_RAW_FENTRY(DD_APPSEC_NS "track_user_signup_event_automated", PHP_FN(datadog_appsec_track_user_signup_event_automated), track_user_signup_event_automated_arginfo, 0, NULL, NULL)
ZEND_RAW_FENTRY(DD_APPSEC_NS "track_user_signup_event", PHP_FN(datadog_appsec_track_user_signup_event), track_user_signup_event_arginfo, 0, NULL, NULL)
ZEND_RAW_FENTRY(DD_APPSEC_NS "track_user_login_success_event", PHP_FN(datadog_appsec_track_user_login_success_event), track_user_login_success_event_arginfo, 0, NULL, NULL)
ZEND_RAW_FENTRY(DD_APPSEC_NS "track_user_login_failure_event", PHP_FN(datadog_appsec_track_user_login_failure_event), track_user_login_failure_event_arginfo, 0, NULL, NULL)
ZEND_RAW_FENTRY(DD_APPSEC_NS "track_custom_event", PHP_FN(datadog_appsec_track_custom_event), track_custom_event_arginfo, 0, NULL, NULL)
Expand Down
2 changes: 1 addition & 1 deletion appsec/src/extension/user_tracking.c
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ bool dd_parse_user_collection_mode(
return true;
}

zend_string *nullable dd_user_id_anonymize(zend_string *nonnull user_id)
zend_string *nullable dd_user_info_anonymize(zend_string *nonnull user_id)
{
zend_string *digest;
const php_hash_ops *ops;
Expand Down
4 changes: 2 additions & 2 deletions appsec/src/extension/user_tracking.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
// (https://www.datadoghq.com/). Copyright 2021 Datadog, Inc.
#pragma once

#include "configuration.h"
#include "attributes.h"
#include "configuration.h"
#include <zend.h>

typedef enum _user_collection_mode {
Expand All @@ -23,7 +23,7 @@ void dd_find_and_apply_verdict_for_user(zend_string *nonnull user_id);
bool dd_parse_user_collection_mode(
zai_str value, zval *nonnull decoded_value, bool persistent);

zend_string*nullable dd_user_id_anonymize(zend_string *nonnull user_id);
zend_string *nullable dd_user_info_anonymize(zend_string *nonnull user_id);

user_collection_mode dd_get_user_collection_mode(void);
zend_string *nonnull dd_get_user_collection_mode_zstr(void);
6 changes: 3 additions & 3 deletions appsec/tests/extension/track_user_signup_event.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,12 @@ include __DIR__ . '/inc/ddtrace_version.php';

ddtrace_version_at_least('0.79.0');

track_user_signup_event("Admin", "1234",
track_user_signup_event("sdkID",
[
"value" => "something",
"metadata" => "some other metadata",
"email" => "[email protected]"
], false);
]);

rshutdown();

Expand All @@ -34,7 +34,7 @@ root_span_get_meta():
Array
(
[runtime-id] => %s
[usr.id] => Admin
[usr.id] => sdkID
[_dd.appsec.events.users.signup.sdk] => true
[appsec.events.users.signup.value] => something
[appsec.events.users.signup.metadata] => some other metadata
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,17 @@ DD_APPSEC_AUTO_USER_INSTRUMENTATION_MODE=anon
--FILE--
<?php
use function datadog\appsec\testing\root_span_get_meta;
use function datadog\appsec\track_user_signup_event;
use function datadog\appsec\track_user_signup_event_automated;
include __DIR__ . '/inc/ddtrace_version.php';

ddtrace_version_at_least('0.79.0');

track_user_signup_event("1234", "1234",
track_user_signup_event_automated("login", "automatedID",
[
"value" => "something",
"metadata" => "some other metadata",
"email" => "[email protected]"
],
true);
]);

echo "root_span_get_meta():\n";
print_r(root_span_get_meta());
Expand All @@ -29,11 +28,10 @@ root_span_get_meta():
Array
(
[runtime-id] => %s
[usr.id] => anon_03ac674216f3e15c761ee1a5e255f067
[usr.id] => anon_b3ddafd7029d645b44fb990eea55b003
[_dd.appsec.usr.id] => anon_b3ddafd7029d645b44fb990eea55b003
[_dd.appsec.events.users.signup.auto.mode] => anonymization
[appsec.events.users.signup.usr.login] => anon_428821350e9691491f616b754cd8315f
[_dd.appsec.usr.login] => anon_428821350e9691491f616b754cd8315f
[appsec.events.users.signup.track] => true
[_dd.appsec.usr.id] => anon_03ac674216f3e15c761ee1a5e255f067
[appsec.events.users.signup.usr.id] => anon_03ac674216f3e15c761ee1a5e255f067
[_dd.appsec.usr.login] => anon_03ac674216f3e15c761ee1a5e255f067
[appsec.events.users.signup.usr.login] => anon_03ac674216f3e15c761ee1a5e255f067
)
Loading

0 comments on commit 22d8216

Please sign in to comment.