Skip to content
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

Fix Brave Ads confirmation systemTimestamp user data should be recreated for retries #17584

Merged
merged 1 commit into from
Mar 22, 2023
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
4 changes: 4 additions & 0 deletions components/brave_ads/core/internal/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ source_set("internal") {
"account/account_observer.h",
"account/account_util.cc",
"account/account_util.h",
"account/confirmations/confirmation_dynamic_user_data_builder.cc",
"account/confirmations/confirmation_dynamic_user_data_builder.h",
"account/confirmations/confirmation_info.cc",
"account/confirmations/confirmation_info.h",
"account/confirmations/confirmation_payload_json_writer.cc",
Expand All @@ -38,6 +40,8 @@ source_set("internal") {
"account/confirmations/opted_in_credential_json_writer.h",
"account/confirmations/opted_in_info.cc",
"account/confirmations/opted_in_info.h",
"account/confirmations/opted_in_user_data_info.cc",
"account/confirmations/opted_in_user_data_info.h",
"account/deposits/cash_deposit.cc",
"account/deposits/cash_deposit.h",
"account/deposits/deposit_builder.cc",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/* Copyright (c) 2023 The Brave Authors. All rights reserved.
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at https://mozilla.org/MPL/2.0/. */

#include "brave/components/brave_ads/core/internal/account/confirmations/confirmation_dynamic_user_data_builder.h"

#include <utility>

#include "base/functional/callback.h"
#include "base/values.h"
#include "brave/components/brave_ads/core/internal/account/user_data/diagnostic_id_user_data.h"
#include "brave/components/brave_ads/core/internal/account/user_data/system_timestamp_user_data.h"

namespace brave_ads {

void ConfirmationDynamicUserDataBuilder::Build(
UserDataBuilderCallback callback) const {
base::Value::Dict user_data;
user_data.Merge(user_data::GetDiagnosticId());
user_data.Merge(user_data::GetSystemTimestamp());

std::move(callback).Run(std::move(user_data));
}

} // namespace brave_ads
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/* Copyright (c) 2023 The Brave Authors. All rights reserved.
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at https://mozilla.org/MPL/2.0/. */

#ifndef BRAVE_COMPONENTS_BRAVE_ADS_CORE_INTERNAL_ACCOUNT_CONFIRMATIONS_CONFIRMATION_DYNAMIC_USER_DATA_BUILDER_H_
#define BRAVE_COMPONENTS_BRAVE_ADS_CORE_INTERNAL_ACCOUNT_CONFIRMATIONS_CONFIRMATION_DYNAMIC_USER_DATA_BUILDER_H_

#include "brave/components/brave_ads/core/internal/account/user_data/user_data_builder_interface.h"

namespace brave_ads {

class ConfirmationDynamicUserDataBuilder final
: public UserDataBuilderInterface {
public:
void Build(UserDataBuilderCallback callback) const override;
};

} // namespace brave_ads

#endif // BRAVE_COMPONENTS_BRAVE_ADS_CORE_INTERNAL_ACCOUNT_CONFIRMATIONS_CONFIRMATION_DYNAMIC_USER_DATA_BUILDER_H_
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/* Copyright (c) 2020 The Brave Authors. All rights reserved.
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at https://mozilla.org/MPL/2.0/. */

#include <string>

#include "base/functional/bind.h"
#include "base/json/json_writer.h"
#include "base/values.h"
#include "brave/components/brave_ads/common/pref_names.h"
#include "brave/components/brave_ads/core/internal/account/confirmations/confirmation_dynamic_user_data_builder.h"
#include "brave/components/brave_ads/core/internal/common/unittest/unittest_base.h"
#include "brave/components/brave_ads/core/internal/common/unittest/unittest_mock_util.h"
#include "brave/components/brave_ads/core/internal/common/unittest/unittest_time_util.h"

// npm run test -- brave_unit_tests --filter=BatAds*

namespace brave_ads {

class BatAdsConfirmationDynamicUserDataBuilderTest : public UnitTestBase {};

TEST_F(BatAdsConfirmationDynamicUserDataBuilderTest, Build) {
// Arrange
AdsClientHelper::GetInstance()->SetStringPref(
prefs::kDiagnosticId, "c1298fde-7fdb-401f-a3ce-0b58fe86e6e2");

const base::Time time =
TimeFromString("November 18 2020 12:34:56.789", /*is_local*/ false);
AdvanceClockTo(time);

// Act

// Assert
const ConfirmationDynamicUserDataBuilder user_data_builder;
user_data_builder.Build(base::BindOnce([](base::Value::Dict user_data) {
std::string json;
ASSERT_TRUE(base::JSONWriter::Write(user_data, &json));

const std::string expected_json =
R"~({"diagnosticId":"c1298fde-7fdb-401f-a3ce-0b58fe86e6e2","systemTimestamp":"2020-11-18T12:00:00.000Z"})~";
EXPECT_EQ(expected_json, json);
}));
}

} // namespace brave_ads
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,9 @@ std::string WriteConfirmationPayload(const ConfirmationInfo& confirmation) {
payload.Set(kPublicKeyKey, *value);
}

payload.Merge(confirmation.opted_in->user_data.Clone());
payload.Merge(confirmation.opted_in->user_data.dynamic.Clone());

payload.Merge(confirmation.opted_in->user_data.fixed.Clone());
}

std::string json;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#include "brave/components/brave_ads/core/internal/account/confirmations/confirmation_util.h"
#include "brave/components/brave_ads/core/internal/account/confirmations/opted_in_credential_json_writer.h"
#include "brave/components/brave_ads/core/internal/account/confirmations/opted_in_info.h"
#include "brave/components/brave_ads/core/internal/account/confirmations/opted_in_user_data_info.h"
#include "brave/components/brave_ads/core/internal/common/logging_util.h"
#include "brave/components/brave_ads/core/internal/common/unittest/unittest_time_util.h"
#include "brave/components/brave_ads/core/internal/privacy/challenge_bypass_ristretto/blinded_token.h"
Expand All @@ -29,8 +30,9 @@ namespace brave_ads {

namespace {

absl::optional<OptedInInfo> CreateOptedIn(const ConfirmationInfo& confirmation,
const base::Value::Dict& user_data) {
absl::optional<OptedInInfo> CreateOptedIn(
const ConfirmationInfo& confirmation,
const OptedInUserDataInfo& opted_in_user_data) {
DCHECK(ShouldRewardUser());

OptedInInfo opted_in;
Expand Down Expand Up @@ -61,7 +63,7 @@ absl::optional<OptedInInfo> CreateOptedIn(const ConfirmationInfo& confirmation,
opted_in.unblinded_token = *unblinded_token;

// User data
opted_in.user_data = user_data.Clone();
opted_in.user_data = opted_in_user_data;

// Credential
ConfirmationInfo new_confirmation = confirmation;
Expand Down Expand Up @@ -91,7 +93,7 @@ absl::optional<ConfirmationInfo> CreateConfirmation(
const std::string& creative_instance_id,
const ConfirmationType& confirmation_type,
const AdType& ad_type,
const base::Value::Dict& user_data) {
const OptedInUserDataInfo& opted_in_user_data) {
DCHECK(!created_at.is_null());
DCHECK(!transaction_id.empty());
DCHECK(!creative_instance_id.empty());
Expand All @@ -110,7 +112,7 @@ absl::optional<ConfirmationInfo> CreateConfirmation(
}

const absl::optional<OptedInInfo> opted_in =
CreateOptedIn(confirmation, user_data);
CreateOptedIn(confirmation, opted_in_user_data);
if (!opted_in) {
BLOG(0, "Failed to create opted-in");
return absl::nullopt;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,12 @@
#include "brave/components/brave_ads/core/internal/account/user_data/catalog_user_data.h"
#include "brave/components/brave_ads/core/internal/account/user_data/conversion_user_data.h"
#include "brave/components/brave_ads/core/internal/account/user_data/created_at_timestamp_user_data.h"
#include "brave/components/brave_ads/core/internal/account/user_data/diagnostic_id_user_data.h"
#include "brave/components/brave_ads/core/internal/account/user_data/locale_user_data.h"
#include "brave/components/brave_ads/core/internal/account/user_data/mutated_user_data.h"
#include "brave/components/brave_ads/core/internal/account/user_data/odyssey_user_data.h"
#include "brave/components/brave_ads/core/internal/account/user_data/platform_user_data.h"
#include "brave/components/brave_ads/core/internal/account/user_data/rotating_hash_user_data.h"
#include "brave/components/brave_ads/core/internal/account/user_data/studies_user_data.h"
#include "brave/components/brave_ads/core/internal/account/user_data/system_timestamp_user_data.h"
#include "brave/components/brave_ads/core/internal/account/user_data/version_number_user_data.h"

namespace brave_ads {
Expand All @@ -46,20 +44,20 @@ void ConfirmationUserDataBuilder::Build(
base::Unretained(this), std::move(callback)));
}

///////////////////////////////////////////////////////////////////////////////

void ConfirmationUserDataBuilder::OnGetConversion(
UserDataBuilderCallback callback,
base::Value::Dict user_data) const {
user_data.Merge(user_data::GetBuildChannel());
user_data.Merge(user_data::GetCatalog());
user_data.Merge(user_data::GetCreatedAtTimestamp(created_at_));
user_data.Merge(user_data::GetDiagnosticId());
user_data.Merge(user_data::GetLocale());
user_data.Merge(user_data::GetMutated());
user_data.Merge(user_data::GetOdyssey());
user_data.Merge(user_data::GetPlatform());
user_data.Merge(user_data::GetRotatingHash(creative_instance_id_));
user_data.Merge(user_data::GetStudies());
user_data.Merge(user_data::GetSystemTimestamp());
user_data.Merge(user_data::GetVersionNumber());

std::move(callback).Run(std::move(user_data));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ TEST_F(BatAdsConfirmationUserDataBuilderTest,
ASSERT_TRUE(base::JSONWriter::Write(user_data, &json));

const std::string pattern =
R"~({"buildChannel":"release","catalog":\[{"id":"29e5c8bc0ba319069980bb390d8e8f9b58c05a20"}],"countryCode":"US","createdAtTimestamp":"2020-11-18T12:00:00.000Z","mutated":true,"odyssey":"host","platform":"windows","rotating_hash":"p3QDOuQ3HakWNXLBZCP8dktH\+zyu7FsHpKONKhWliJE=","studies":\[],"systemTimestamp":"2020-11-18T12:00:00.000Z","versionNumber":"\d{1,}\.\d{1,}\.\d{1,}\.\d{1,}"})~";
R"~({"buildChannel":"release","catalog":\[{"id":"29e5c8bc0ba319069980bb390d8e8f9b58c05a20"}],"countryCode":"US","createdAtTimestamp":"2020-11-18T12:00:00.000Z","mutated":true,"odyssey":"host","platform":"windows","rotating_hash":"p3QDOuQ3HakWNXLBZCP8dktH\+zyu7FsHpKONKhWliJE=","studies":\[],"versionNumber":"\d{1,}\.\d{1,}\.\d{1,}\.\d{1,}"})~";
EXPECT_TRUE(RE2::FullMatch(json, pattern));
}));
}
Expand Down Expand Up @@ -95,7 +95,7 @@ TEST_F(BatAdsConfirmationUserDataBuilderTest,
ASSERT_TRUE(base::JSONWriter::Write(user_data, &json));

const std::string pattern =
R"~({"buildChannel":"release","catalog":\[{"id":"29e5c8bc0ba319069980bb390d8e8f9b58c05a20"}],"conversionEnvelope":{"alg":"crypto_box_curve25519xsalsa20poly1305","ciphertext":"(.{64})","epk":"(.{44})","nonce":"(.{32})"},"countryCode":"US","createdAtTimestamp":"2020-11-18T12:00:00.000Z","mutated":true,"odyssey":"host","platform":"windows","rotating_hash":"p3QDOuQ3HakWNXLBZCP8dktH\+zyu7FsHpKONKhWliJE=","studies":\[],"systemTimestamp":"2020-11-18T12:00:00.000Z","versionNumber":"\d{1,}\.\d{1,}\.\d{1,}\.\d{1,}"})~";
R"~({"buildChannel":"release","catalog":\[{"id":"29e5c8bc0ba319069980bb390d8e8f9b58c05a20"}],"conversionEnvelope":{"alg":"crypto_box_curve25519xsalsa20poly1305","ciphertext":"(.{64})","epk":"(.{44})","nonce":"(.{32})"},"countryCode":"US","createdAtTimestamp":"2020-11-18T12:00:00.000Z","mutated":true,"odyssey":"host","platform":"windows","rotating_hash":"p3QDOuQ3HakWNXLBZCP8dktH\+zyu7FsHpKONKhWliJE=","studies":\[],"versionNumber":"\d{1,}\.\d{1,}\.\d{1,}\.\d{1,}"})~";
EXPECT_TRUE(RE2::FullMatch(json, pattern));
}));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#include "brave/components/brave_ads/core/internal/account/confirmations/confirmation_payload_json_writer.h"
#include "brave/components/brave_ads/core/internal/account/confirmations/opted_in_credential_json_writer.h"
#include "brave/components/brave_ads/core/internal/account/confirmations/opted_in_info.h"
#include "brave/components/brave_ads/core/internal/account/confirmations/opted_in_user_data_info.h"
#include "brave/components/brave_ads/core/internal/common/logging_util.h"
#include "brave/components/brave_ads/core/internal/deprecated/confirmations/confirmation_state_manager.h"
#include "brave/components/brave_ads/core/internal/privacy/challenge_bypass_ristretto/blinded_token.h"
Expand All @@ -41,7 +42,7 @@ constexpr char kVerificationSignatureKey[] = "signature";
absl::optional<OptedInInfo> CreateOptedIn(
privacy::TokenGeneratorInterface* token_generator,
const ConfirmationInfo& confirmation,
base::Value::Dict user_data) {
const OptedInUserDataInfo& opted_in_user_data) {
DCHECK(token_generator);
DCHECK(ShouldRewardUser());

Expand Down Expand Up @@ -74,16 +75,28 @@ absl::optional<OptedInInfo> CreateOptedIn(
opted_in.unblinded_token = *unblinded_token;

// User data
opted_in.user_data = std::move(user_data);
opted_in.user_data = opted_in_user_data;

// Credential
ConfirmationInfo new_confirmation = confirmation;
new_confirmation.opted_in = opted_in;
ConfirmationInfo confirmation_copy = confirmation;
confirmation_copy.opted_in = opted_in;
opted_in.credential_base64url = CreateOptedInCredential(confirmation_copy);

return opted_in;
}

} // namespace

absl::optional<std::string> CreateOptedInCredential(
const ConfirmationInfo& confirmation) {
if (!confirmation.opted_in) {
return absl::nullopt;
}

const absl::optional<std::string> credential =
json::writer::WriteOptedInCredential(
*unblinded_token,
json::writer::WriteConfirmationPayload(new_confirmation));
confirmation.opted_in->unblinded_token,
json::writer::WriteConfirmationPayload(confirmation));
if (!credential) {
BLOG(0, "Failed to create opted-in credential");
return absl::nullopt;
Expand All @@ -93,21 +106,17 @@ absl::optional<OptedInInfo> CreateOptedIn(
base::Base64UrlEncode(*credential,
base::Base64UrlEncodePolicy::INCLUDE_PADDING,
&credential_base64url);
opted_in.credential_base64url = credential_base64url;

return opted_in;
return credential_base64url;
}

} // namespace

absl::optional<ConfirmationInfo> CreateConfirmation(
privacy::TokenGeneratorInterface* token_generator,
const base::Time created_at,
const std::string& transaction_id,
const std::string& creative_instance_id,
const ConfirmationType& confirmation_type,
const AdType& ad_type,
base::Value::Dict user_data) {
const OptedInUserDataInfo& opted_in_user_data) {
DCHECK(token_generator);
DCHECK(!created_at.is_null());
DCHECK(!transaction_id.empty());
Expand All @@ -127,7 +136,7 @@ absl::optional<ConfirmationInfo> CreateConfirmation(
}

const absl::optional<OptedInInfo> opted_in =
CreateOptedIn(token_generator, confirmation, std::move(user_data));
CreateOptedIn(token_generator, confirmation, opted_in_user_data);
if (!opted_in) {
BLOG(0, "Failed to create opted-in");
return absl::nullopt;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@

#include <string>

#include "base/values.h"
#include "third_party/abseil-cpp/absl/types/optional.h"

namespace base {
Expand All @@ -24,6 +23,10 @@ class TokenGeneratorInterface;
class AdType;
class ConfirmationType;
struct ConfirmationInfo;
struct OptedInUserDataInfo;

absl::optional<std::string> CreateOptedInCredential(
const ConfirmationInfo& confirmation);

absl::optional<ConfirmationInfo> CreateConfirmation(
privacy::TokenGeneratorInterface* token_generator,
Expand All @@ -32,7 +35,7 @@ absl::optional<ConfirmationInfo> CreateConfirmation(
const std::string& creative_instance_id,
const ConfirmationType& confirmation_type,
const AdType& ad_type,
base::Value::Dict user_data);
const OptedInUserDataInfo& opted_in_user_data);

bool IsValid(const ConfirmationInfo& confirmation);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ TEST_F(BatAdsConfirmationUtilTest, CreateConfirmationForNonOptedInUser) {
const absl::optional<ConfirmationInfo> confirmation = CreateConfirmation(
token_generator_mock_.get(), /*created_at*/ Now(), kTransactionId,
kCreativeInstanceId, ConfirmationType::kViewed, AdType::kNotificationAd,
base::Value::Dict());
/*user_data*/ {});
ASSERT_TRUE(confirmation);

// Assert
Expand Down Expand Up @@ -88,7 +88,7 @@ TEST_F(BatAdsConfirmationUtilTest, CreateConfirmationForOptedInUser) {
const absl::optional<ConfirmationInfo> confirmation = CreateConfirmation(
token_generator_mock_.get(), /*created_at*/ Now(), kTransactionId,
kCreativeInstanceId, ConfirmationType::kViewed, AdType::kNotificationAd,
base::Value::Dict());
/*user_data*/ {});
ASSERT_TRUE(confirmation);

// Assert
Expand All @@ -105,7 +105,7 @@ TEST_F(BatAdsConfirmationUtilTest, FailToCreateConfirmationForOptedInUser) {
const absl::optional<ConfirmationInfo> confirmation = CreateConfirmation(
token_generator_mock_.get(), /*created_at*/ Now(), kTransactionId,
kCreativeInstanceId, ConfirmationType::kViewed, AdType::kNotificationAd,
base::Value::Dict());
/*user_data*/ {});

// Assert
EXPECT_FALSE(confirmation);
Expand Down
Loading