1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295
|
// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CONTENT_BROWSER_BTM_BTM_DATABASE_H_
#define CONTENT_BROWSER_BTM_BTM_DATABASE_H_
#include <stddef.h>
#include <string>
#include <vector>
#include "base/strings/cstring_view.h"
#include "base/time/clock.h"
#include "base/time/default_clock.h"
#include "content/browser/btm/btm_utils.h"
#include "content/common/content_export.h"
#include "content/public/common/content_features.h"
#include "sql/database.h"
#include "sql/init_status.h"
#include "sql/meta_table.h"
#include "sql/statement.h"
namespace content {
// Encapsulates an SQL database that holds DIPS info.
class CONTENT_EXPORT BtmDatabase {
public:
// Version number of the database schema.
// NOTE: When changing the version, add a new golden file for the new version
// at `//chrome/test/data/dips/v<N>.sql`.
static constexpr int kLatestSchemaVersion = 9;
// The minimum database schema version this Chrome code is compatible with.
static constexpr int kMinCompatibleSchemaVersion = 9;
static constexpr char kPrepopulatedKey[] = "prepopulated";
// The length of time that will be waited between emitting db health metrics.
static constexpr base::TimeDelta kMetricsInterval = base::Hours(24);
// How long DIPS maintains popups in storage (for recording Popup Heuristic
// storage accesses).
static constexpr base::TimeDelta kPopupTtl = base::Days(60);
// Passing in an std::nullopt `db_path` causes the db to be created in
// memory. Init() must be called before using the BtmDatabase to make sure it
// is initialized.
explicit BtmDatabase(const std::optional<base::FilePath>& db_path);
// This object must be destroyed on the thread where all accesses are
// happening to avoid thread-safety problems.
~BtmDatabase();
BtmDatabase(const BtmDatabase&) = delete;
BtmDatabase& operator=(const BtmDatabase&) = delete;
// DIPS Bounce table functions -----------------------------------------------
bool Write(const std::string& site,
const TimestampRange& storage_times,
const TimestampRange& user_activation_times,
const TimestampRange& stateful_bounce_times,
const TimestampRange& bounce_times,
const TimestampRange& web_authn_assertion_times);
bool WritePopup(const std::string& opener_site,
const std::string& popup_site,
const uint64_t access_id,
const base::Time& popup_time,
bool is_current_interaction,
bool is_authentication_interaction);
// This is implicitly `inline`. Don't move its definition to the .cc file.
bool HasExpired(std::optional<base::Time> time) {
return time.has_value() &&
(time.value() + features::kBtmInteractionTtl.Get()) < clock_->Now();
}
std::optional<StateValue> Read(const std::string& site);
std::optional<PopupsStateValue> ReadPopup(const std::string& opener_site,
const std::string& popup_site);
// Returns all entries from the `popups` table with a current interaction,
// where the last popup time was more recent than `lookback` ago.
std::vector<PopupWithTime> ReadRecentPopupsWithInteraction(
const base::TimeDelta& lookback);
// Note: this doesn't clear expired interactions from the database unlike
// the other database querying methods.
std::vector<std::string> GetAllSitesForTesting(const BtmDatabaseTable table);
// Returns the subset of sites in |sites| WITH a protective event recorded.
// A protective event is a user activation or successful WebAuthn assertion.
//
// NOTE: This method's main procedure is performed after calling
// `ClearExpiredRows()`.
//
// TODO(njeunje): Consider making a method FilterSites(set<string> sites,
// FilterType filter) that we call from this method, where FilterType lets us
// specify if we want to filter out user activations, WebAuthn assertions, or
// both. There may be other criteria that we want to filter for in the future.
std::set<std::string> FilterSitesWithProtectiveEvent(
const std::set<std::string>& sites);
// Returns all sites which bounced the user and aren't protected from DIPS.
//
// A site can be protected in several ways:
// - it's still in its grace period after the first bounce
// - it received user activation or WAA before the first bounce
// - it received user activation or WAA in the grace period after the first
// bounce.
//
// NOTE: This method's main procedure is performed after calling
// `ClearExpiredRows()`.
std::vector<std::string> GetSitesThatBounced(base::TimeDelta grace_period);
// Returns all sites which used storage and aren't protected from DIPS.
//
// A site can be protected in several ways:
// - it's still in its grace period after the first storage
// - it received user activation or WAA before the first storage
// - it received user activation or WAA in the grace period after the first
// storage.
//
// NOTE: This method's main procedure is performed after calling
// `ClearExpiredRows()`.
std::vector<std::string> GetSitesThatUsedStorage(
base::TimeDelta grace_period);
// Returns all sites which statefully bounced the user and aren't protected
// from DIPS.
//
// A site can be protected in several ways:
// - it's still in its grace period after the first stateful bounce
// - it received user activation or WAA before the first stateful bounce
// - it received user activation or WAA in the grace period after the first
// stateful bounce.
//
// NOTE: This method's main procedure is performed after calling
// `ClearExpiredRows()`.
std::vector<std::string> GetSitesThatBouncedWithState(
base::TimeDelta grace_period);
// Deletes all rows in the database whose interactions have expired out.
//
// When an interaction happens before a DIPS-triggering action or during the
// following grace-period, it protects that site from its data being cleared
// by DIPS. Further interactions will prolong that protection until the last
// one reaches the `interaction_ttl`.
//
// Clearing expired interactions effectively restarts the DIPS procedure for
// determining if a site is a tracker for sites that are cleared.
//
// Returns the number of rows that are removed.
size_t ClearExpiredRows();
// Delete the row from the bounces table for `site`. Returns true if query
// executes successfully.
//
// NOTE: This method's main procedure is performed after calling
// `ClearExpiredRows()`.
bool RemoveRow(const BtmDatabaseTable table, const std::string& site);
bool RemoveRows(const BtmDatabaseTable table,
const std::vector<std::string>& sites);
// NOTE: This method's main procedure is performed after calling
// `ClearExpiredRows()`.
bool RemoveEventsByTime(const base::Time& delete_begin,
const base::Time& delete_end,
const BtmEventRemovalType type);
// Because |sites| is taken from a ClearDataFilter, this only removes
// storage and stateful bounce timestamps at the moment.
bool RemoveEventsBySite(bool preserve,
const std::vector<std::string>& sites,
const BtmEventRemovalType type);
// Returns the number of entries present in the database.
//
// NOTE: This method's main procedure is performed after calling
// `ClearExpiredRows()`.
size_t GetEntryCount(const BtmDatabaseTable table);
// If the number of entries in the database is greater than
// |GetMaxEntries()|, garbage collect. Returns the number of entries deleted
// (useful for debugging).
size_t GarbageCollect();
// Removes the |purge_goal| entries with the oldest
// |MAX(last_user_activation_time,last_site_storage_time)| value. Returns the
// number of entries deleted.
//
// NOTE: The SQLITE sub-query in this method must match that of
// `GetGarbageCollectOldestSitesForTesting()`'s query.
size_t GarbageCollectOldest(const BtmDatabaseTable table, int purge_goal);
// Used for testing the intended behavior `GarbageCollectOldest()`.
//
// NOTE: The SQLITE query in this method must match that of
// `GarbageCollectOldest()`'s sub-query.
std::vector<std::string> GetGarbageCollectOldestSitesForTesting(
const BtmDatabaseTable table);
bool in_memory() const {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return db_path_.empty();
}
// Checks that the internal SQLite database is initialized.
bool CheckDBInit();
size_t GetMaxEntries() const { return max_entries_; }
size_t GetPurgeEntries() const { return purge_entries_; }
std::optional<base::Time> GetTimerLastFired();
bool SetTimerLastFired(base::Time time);
// Testing functions --------------------------------------------------
void SetMaxEntriesForTesting(size_t entries) { max_entries_ = entries; }
void SetPurgeEntriesForTesting(size_t entries) { purge_entries_ = entries; }
void SetClockForTesting(base::Clock* clock) { clock_ = clock; }
bool ExecuteSqlForTesting(const base::cstring_view sql);
bool SetConfigValueForTesting(std::string_view name, int64_t value) {
return SetConfigValue(name, value);
}
std::optional<int64_t> GetConfigValueForTesting(std::string_view name) {
return GetConfigValue(name);
}
protected:
// Initialization functions --------------------------------------------------
sql::InitStatus Init();
sql::InitStatus InitImpl();
sql::InitStatus OpenDatabase();
// Creates the bounce table following the latest schema.
bool InitTables();
// Internal utility functions ------------------------------------------------
// NOTE: This method's main procedure is performed after calling
// `ClearExpiredRows()`.
bool ClearTimestamps(const base::Time& delete_begin,
const base::Time& delete_end,
const BtmEventRemovalType type);
bool ClearTimestampsBySite(bool preserve,
const std::vector<std::string>& sites,
const BtmEventRemovalType type);
bool RemoveEmptyRows();
void LogDatabaseMetrics();
private:
// Callback for database errors.
void DatabaseErrorCallback(int extended_error, sql::Statement* stmt);
// Only ClearTimestamps() should call this method.
//
// NOTE: This method's main procedure is performed after calling
// `ClearExpiredRows()`.
bool AdjustFirstTimestamps(const base::Time& delete_begin,
const base::Time& delete_end,
const BtmEventRemovalType type);
// Only ClearTimestamps() should call this method.
//
// NOTE: This method's main procedure is performed after calling
// `ClearExpiredRows()`.
bool AdjustLastTimestamps(const base::Time& delete_begin,
const base::Time& delete_end,
const BtmEventRemovalType type);
// Upsert the row for `key` in the config table to contain `value`.
bool SetConfigValue(std::string_view key, int64_t value);
// Get the value for `key` from the config table, or nullopt if absent.
std::optional<int64_t> GetConfigValue(std::string_view key);
// When the number of entries in the database exceeds |max_entries_|, purge
// down to |max_entries_| - |purge_entries_|.
size_t max_entries_ = 3500;
size_t purge_entries_ = 300;
const base::FilePath db_path_ GUARDED_BY_CONTEXT(sequence_checker_);
std::unique_ptr<sql::Database> db_ GUARDED_BY_CONTEXT(sequence_checker_);
bool db_init_ = false;
raw_ptr<base::Clock> clock_ = base::DefaultClock::GetInstance();
sql::MetaTable meta_table_ GUARDED_BY_CONTEXT(sequence_checker_);
mutable base::Time last_health_metrics_time_
GUARDED_BY_CONTEXT(sequence_checker_) = base::Time::Min();
SEQUENCE_CHECKER(sequence_checker_);
};
} // namespace content
#endif // CONTENT_BROWSER_BTM_BTM_DATABASE_H_
|