Commit 131a0fdc authored by Igor Tukh's avatar Igor Tukh Committed by Commit Bot

[Permissions Auditing] Add PermissionAuditingDatabase.

PermissionAuditingDatabase is a SQL-based storage for keeping
information about the permissions usage. The information is represented
in source code with PermissionUsageRecord structure.


R=engedy@chromium.org

Bug: 169305053
Change-Id: I65da241073a82baac4c02d240da36f6c379b7c56
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2435307
Commit-Queue: Balazs Engedy <engedy@chromium.org>
Reviewed-by: default avatarVictor Costan <pwnall@chromium.org>
Reviewed-by: default avatarBalazs Engedy <engedy@chromium.org>
Cr-Commit-Position: refs/heads/master@{#827261}
parent aa38a52d
...@@ -20,6 +20,8 @@ source_set("permissions") { ...@@ -20,6 +20,8 @@ source_set("permissions") {
"features.h", "features.h",
"notification_permission_ui_selector.cc", "notification_permission_ui_selector.cc",
"notification_permission_ui_selector.h", "notification_permission_ui_selector.h",
"permission_auditing_database.cc",
"permission_auditing_database.h",
"permission_context_base.cc", "permission_context_base.cc",
"permission_context_base.h", "permission_context_base.h",
"permission_decision_auto_blocker.cc", "permission_decision_auto_blocker.cc",
...@@ -39,6 +41,8 @@ source_set("permissions") { ...@@ -39,6 +41,8 @@ source_set("permissions") {
"permission_result.h", "permission_result.h",
"permission_uma_util.cc", "permission_uma_util.cc",
"permission_uma_util.h", "permission_uma_util.h",
"permission_usage_session.cc",
"permission_usage_session.h",
"permission_util.cc", "permission_util.cc",
"permission_util.h", "permission_util.h",
"permissions_client.cc", "permissions_client.cc",
...@@ -64,7 +68,9 @@ source_set("permissions") { ...@@ -64,7 +68,9 @@ source_set("permissions") {
"//components/vector_icons", "//components/vector_icons",
"//content/public/browser", "//content/public/browser",
"//services/metrics/public/cpp:ukm_builders", "//services/metrics/public/cpp:ukm_builders",
"//sql",
"//third_party/blink/public/common", "//third_party/blink/public/common",
"//third_party/sqlite",
"//ui/base", "//ui/base",
"//url", "//url",
] ]
...@@ -133,6 +139,7 @@ source_set("unit_tests") { ...@@ -133,6 +139,7 @@ source_set("unit_tests") {
sources = [ sources = [
"chooser_context_base_unittest.cc", "chooser_context_base_unittest.cc",
"contexts/geolocation_permission_context_unittest.cc", "contexts/geolocation_permission_context_unittest.cc",
"permission_auditing_database_unittest.cc",
"permission_context_base_unittest.cc", "permission_context_base_unittest.cc",
"permission_decision_auto_blocker_unittest.cc", "permission_decision_auto_blocker_unittest.cc",
"permission_manager_unittest.cc", "permission_manager_unittest.cc",
...@@ -153,7 +160,11 @@ source_set("unit_tests") { ...@@ -153,7 +160,11 @@ source_set("unit_tests") {
"//components/ukm/content", "//components/ukm/content",
"//components/variations", "//components/variations",
"//content/test:test_support", "//content/test:test_support",
"//sql",
"//sql:test_support",
"//testing/gtest", "//testing/gtest",
"//third_party/sqlite",
"//url",
] ]
if (is_android) { if (is_android) {
deps += [ deps += [
......
...@@ -15,9 +15,11 @@ include_rules = [ ...@@ -15,9 +15,11 @@ include_rules = [
"+content/public/test", "+content/public/test",
"+media/base/android/media_drm_bridge.h", "+media/base/android/media_drm_bridge.h",
"+services/metrics/public/cpp", "+services/metrics/public/cpp",
"+sql",
"+third_party/blink/public/common/loader/network_utils.h", "+third_party/blink/public/common/loader/network_utils.h",
"+third_party/blink/public/mojom/feature_policy", "+third_party/blink/public/mojom/feature_policy",
"+third_party/blink/public/mojom/quota", "+third_party/blink/public/mojom/quota",
"+third_party/sqlite",
"+ui/base", "+ui/base",
"+ui/gfx", "+ui/gfx",
] ]
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/permissions/permission_auditing_database.h"
#include <algorithm>
#include <iostream>
#include <limits>
#include <memory>
#include <string>
#include <vector>
#include "base/check.h"
#include "base/files/file_path.h"
#include "base/optional.h"
#include "base/strings/stringprintf.h"
#include "sql/database.h"
#include "sql/meta_table.h"
#include "sql/statement.h"
#include "sql/transaction.h"
#include "url/gurl.h"
namespace permissions {
namespace {
int64_t TimeToInt64(base::Time time) {
return time.ToDeltaSinceWindowsEpoch().InMicroseconds();
}
base::Time Int64ToTime(const int64_t& time) {
return base::Time::FromDeltaSinceWindowsEpoch(
base::TimeDelta::FromMicroseconds(time));
}
// For this database, schema migration is supported for at least 1 year.
// This means we can deprecate old versions that landed more than a year ago.
//
// Version 1: https://crrev.com/c/2435307 - 2020-11-04
// The current number of the database schema.
constexpr int kVersion = 1;
// The lowest version of the database schema such that a legacy code can still
// read/write the current database.
constexpr int kCompatibleVersion = 1;
} // namespace
PermissionAuditingDatabase::PermissionAuditingDatabase() = default;
PermissionAuditingDatabase::~PermissionAuditingDatabase() = default;
bool PermissionAuditingDatabase::Init(const base::FilePath& path) {
if (!db_.Open(path)) {
return false;
}
sql::MetaTable metatable;
if (!metatable.Init(&db_, kVersion, kCompatibleVersion)) {
db_.Poison();
return false;
}
if (metatable.GetCompatibleVersionNumber() > kVersion) {
db_.Poison();
return false;
}
if (!db_.DoesTableExist("uses")) {
if (!CreateSchema()) {
db_.Poison();
return false;
}
}
// TODO(anyone): perform migration if metatable.GetVersionNumber() < kVersion
return true;
}
bool PermissionAuditingDatabase::CreateSchema() {
sql::Transaction transaction(&db_);
if (!transaction.Begin()) {
return false;
}
if (!db_.Execute("CREATE TABLE uses("
" id INTEGER PRIMARY KEY AUTOINCREMENT,"
" origin TEXT NOT NULL,"
" content_setting_type INTEGER NOT NULL,"
" usage_start_time INTEGER NOT NULL,"
" usage_end_time INTEGER NOT NULL,"
" had_user_activation INTEGER NOT NULL,"
" was_foreground INTEGER NOT NULL,"
" had_focus INTEGER NOT NULL"
")")) {
return false;
}
if (!db_.Execute("CREATE UNIQUE INDEX setting_origin_start_time ON "
"uses(origin, content_setting_type,"
"usage_start_time)")) {
return false;
}
if (!db_.Execute("CREATE INDEX setting_origin_end_time ON "
"uses(origin, content_setting_type,"
"usage_end_time)")) {
return false;
}
if (!db_.Execute("CREATE INDEX start_time ON "
"uses(usage_start_time)")) {
return false;
}
if (!db_.Execute("CREATE INDEX end_time ON "
"uses(usage_end_time)")) {
return false;
}
return transaction.Commit();
}
bool PermissionAuditingDatabase::StorePermissionUsage(
const PermissionUsageSession& session) {
DCHECK(session.IsValid());
sql::Statement statement(
db_.GetCachedStatement(SQL_FROM_HERE,
"INSERT INTO uses(origin, content_setting_type,"
"usage_start_time, usage_end_time,"
"had_user_activation, was_foreground, had_focus)"
"VALUES (?, ?, ?, ?, ?, ?, ?)"));
statement.BindString(0, session.origin.Serialize());
statement.BindInt(1, static_cast<int32_t>(session.type));
statement.BindInt64(2, TimeToInt64(session.usage_start));
statement.BindInt64(3, TimeToInt64(session.usage_end));
statement.BindBool(4, session.had_user_activation);
statement.BindBool(5, session.was_foreground);
statement.BindBool(6, session.had_focus);
sql::Transaction transaction(&db_);
if (!transaction.Begin()) {
return false;
}
if (!statement.Run()) {
return false;
}
return transaction.Commit();
}
std::vector<PermissionUsageSession>
PermissionAuditingDatabase::GetPermissionUsageHistory(ContentSettingsType type,
const url::Origin& origin,
base::Time start_time) {
DCHECK(!origin.opaque());
std::vector<PermissionUsageSession> sessions;
sql::Statement statement(db_.GetCachedStatement(
SQL_FROM_HERE,
"SELECT usage_start_time, usage_end_time, had_user_activation,"
"was_foreground, had_focus "
"FROM uses "
"WHERE origin = ? AND content_setting_type = ? "
"AND usage_end_time >= ?"));
statement.BindString(0, origin.Serialize());
statement.BindInt(1, static_cast<int32_t>(type));
statement.BindInt64(2, start_time.is_null()
? std::numeric_limits<int64_t>::min()
: TimeToInt64(start_time));
while (statement.Step()) {
sessions.push_back({.origin = origin,
.type = type,
.usage_start = Int64ToTime(statement.ColumnInt64(0)),
.usage_end = Int64ToTime(statement.ColumnInt64(1)),
.had_user_activation = statement.ColumnBool(2),
.was_foreground = statement.ColumnBool(3),
.had_focus = statement.ColumnBool(4)});
}
return sessions;
}
base::Optional<base::Time>
PermissionAuditingDatabase::GetLastPermissionUsageTime(
ContentSettingsType type,
const url::Origin& origin) {
DCHECK(!origin.opaque());
sql::Statement statement(
db_.GetCachedStatement(SQL_FROM_HERE,
"SELECT usage_end_time "
"FROM uses "
"WHERE origin = ? AND content_setting_type = ? "
"ORDER BY usage_end_time DESC "
"LIMIT 1"));
statement.BindString(0, origin.Serialize());
statement.BindInt(1, static_cast<int32_t>(type));
base::Optional<base::Time> last_usage;
if (statement.Step()) {
last_usage = Int64ToTime(statement.ColumnInt64(0));
}
return last_usage;
}
bool PermissionAuditingDatabase::UpdateEndTime(ContentSettingsType type,
const url::Origin& origin,
base::Time start_time,
base::Time new_end_time) {
DCHECK(!origin.opaque());
DCHECK(!start_time.is_null());
DCHECK(!new_end_time.is_null());
DCHECK_LE(start_time, new_end_time);
sql::Statement statement(
db_.GetCachedStatement(SQL_FROM_HERE,
"UPDATE uses "
"SET usage_end_time = ? "
"WHERE origin = ? AND content_setting_type = ? "
"AND usage_start_time = ?"));
statement.BindInt64(0, TimeToInt64(new_end_time));
statement.BindString(1, origin.Serialize());
statement.BindInt(2, static_cast<int32_t>(type));
statement.BindInt64(3, TimeToInt64(start_time));
sql::Transaction transaction(&db_);
if (!transaction.Begin()) {
return false;
}
if (!statement.Run()) {
return false;
}
return transaction.Commit();
}
bool PermissionAuditingDatabase::DeleteSessionsBetween(base::Time start_time,
base::Time end_time) {
std::vector<int> ids;
sql::Statement statement(
db_.GetCachedStatement(SQL_FROM_HERE,
"DELETE FROM uses "
"WHERE usage_start_time BETWEEN ? AND ? "
"OR usage_end_time BETWEEN ? AND ?"));
auto start = start_time.is_null() ? std::numeric_limits<int64_t>::min()
: TimeToInt64(start_time);
auto end = end_time.is_null() ? std::numeric_limits<int64_t>::max()
: TimeToInt64(end_time);
statement.BindInt64(0, start);
statement.BindInt64(1, end);
statement.BindInt64(2, start);
statement.BindInt64(3, end);
sql::Transaction transaction(&db_);
if (!transaction.Begin()) {
return false;
}
if (!statement.Run()) {
return false;
}
return transaction.Commit();
}
} // namespace permissions
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef COMPONENTS_PERMISSIONS_PERMISSION_AUDITING_DATABASE_H_
#define COMPONENTS_PERMISSIONS_PERMISSION_AUDITING_DATABASE_H_
#include <vector>
#include "base/macros.h"
#include "base/optional.h"
#include "base/time/time.h"
#include "components/content_settings/core/common/content_settings_types.h"
#include "components/permissions/permission_usage_session.h"
#include "sql/database.h"
#include "url/origin.h"
namespace base {
class FilePath;
} // namespace base
namespace permissions {
// Stores permission usage sessions for specific url origin and
// ContentSettingType in an SQLite database. Additionally, handles the queries
// about the last permission usage time for a specific origin.
// Threading constraints:
// 1) This class is not thread-safe, so each instance must be used on the same
// sequence;
// 2) Instances must be used on a sequence that can execute blocking tasks.
class PermissionAuditingDatabase {
public:
PermissionAuditingDatabase();
~PermissionAuditingDatabase();
PermissionAuditingDatabase(const PermissionAuditingDatabase&) = delete;
PermissionAuditingDatabase& operator=(const PermissionAuditingDatabase&) =
delete;
PermissionAuditingDatabase(PermissionAuditingDatabase&&) = delete;
PermissionAuditingDatabase& operator=(const PermissionAuditingDatabase&&) =
delete;
// Opens an existing database at `path` or creates a new one if none exists,
// and returns true on success.
bool Init(const base::FilePath& path);
// Appends a new permission usage `session` of the given permission `type` on
// a given `origin`. The `session` must be valid according to IsValid().
// Operation will fail in case if a session with the same primary key, that
// is, origin, type, and usage start time, already exists in the database.
// Returns if the operation was successful.
bool StorePermissionUsage(const PermissionUsageSession& session);
// Returns the detailed history stored for the permission `type` on a given
// `origin` from the specified `start_time`. The `origin` must not be opaque.
std::vector<PermissionUsageSession> GetPermissionUsageHistory(
ContentSettingsType type,
const url::Origin& origin,
base::Time start_time);
// Returns when the given permission `type` was last used on a given `origin`.
// Returns nullopt if no permission usages match the given constraints. The
// `origin` must not be opaque.
base::Optional<base::Time> GetLastPermissionUsageTime(
ContentSettingsType type,
const url::Origin& origin);
// Updates the usage end time for a specific usage session. The session is
// identified by the primary key {`type`, `origin`, `start_time`}, and must
// already exist. `start_time` must be less than or equal to `new_end_time`.
// Operation will fail if `start_time` or `new_end_time` is null. Returns if
// the operation was successful.
bool UpdateEndTime(ContentSettingsType type,
const url::Origin& origin,
base::Time start_time,
base::Time new_end_time);
// Deletes permission usage sessions, which started or ended in the given
// time range. A null `start_time` or `end_time` time is treated as -inf and
// +inf, respectively. Returns if the operation was successful.
bool DeleteSessionsBetween(base::Time start_time, base::Time end_time);
private:
bool CreateSchema();
// The SQL connection to database.
sql::Database db_;
};
} // namespace permissions
#endif // COMPONENTS_PERMISSIONS_PERMISSION_AUDITING_DATABASE_H_
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/permissions/permission_auditing_database.h"
#include <memory>
#include "base/bind.h"
#include "base/check.h"
#include "base/files/file_util.h"
#include "base/files/scoped_temp_dir.h"
#include "base/macros.h"
#include "base/stl_util.h"
#include "base/strings/stringprintf.h"
#include "base/time/time.h"
#include "sql/test/scoped_error_expecter.h"
#include "testing/gmock/include/gmock/gmock-matchers.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"
namespace permissions {
using ::testing::ElementsAre;
using ::testing::IsEmpty;
namespace {
base::Time TimeFromTimestamp(const int64_t& time) {
return base::Time::FromDeltaSinceWindowsEpoch(
base::TimeDelta::FromMicroseconds(time));
}
constexpr ContentSettingsType kTestTypes[] = {
ContentSettingsType::GEOLOCATION, ContentSettingsType::NOTIFICATIONS};
constexpr char kTestUrl1[] = "http://www.example1.com";
constexpr char kTestUrl2[] = "http://www.example2.com";
std::string GetUniqueUrl(int id) {
return base::StringPrintf("http://www.example%d.com", id);
}
url::Origin GetOrigin(const char* url) {
return url::Origin::Create(GURL(url));
}
PermissionUsageSession SessionLike(ContentSettingsType type,
const char* url,
const PermissionUsageSession& session) {
return {.origin = GetOrigin(url),
.type = type,
.usage_start = session.usage_start,
.usage_end = session.usage_end,
.had_user_activation = session.had_user_activation,
.was_foreground = session.was_foreground,
.had_focus = session.had_focus};
}
} // namespace
class PermissionAuditingDatabaseTest : public testing::Test {
public:
PermissionAuditingDatabaseTest()
: test_sessions_(GeneratePermissionSessions()) {}
protected:
void SetUp() override {
ASSERT_TRUE(temp_directory_.CreateUniqueTempDir());
path_ = temp_directory_.GetPath().Append(
FILE_PATH_LITERAL("test_permission_auditing_database"));
ASSERT_TRUE(db_.Init(path_));
}
std::vector<PermissionUsageSession> GetPermissionUsageHistory(
ContentSettingsType type,
const char* url,
base::Time starting_from = base::Time()) {
return db_.GetPermissionUsageHistory(type, GetOrigin(url), starting_from);
}
base::Optional<base::Time> GetLastUsageTime(ContentSettingsType type,
const char* url) {
return db_.GetLastPermissionUsageTime(type, GetOrigin(url));
}
bool UpdateEndTime(ContentSettingsType type,
const char* url,
base::Time ordering_time,
base::Time new_end_time) {
return db_.UpdateEndTime(type, GetOrigin(url), ordering_time, new_end_time);
}
void StoreSessionForEachTime() {
for (const auto& time : test_times_) {
ASSERT_TRUE(db().StorePermissionUsage({.origin = GetOrigin(kTestUrl1),
.type = kTestTypes[0],
.usage_start = time,
.usage_end = time,
.had_user_activation = false,
.was_foreground = false,
.had_focus = false}));
}
}
PermissionAuditingDatabase& db() { return db_; }
const base::Time test_times_[3] = {TimeFromTimestamp(12864787200000000),
TimeFromTimestamp(12864787200000001),
TimeFromTimestamp(12864787200000002)};
const std::vector<PermissionUsageSession> test_sessions_;
private:
std::vector<PermissionUsageSession> GeneratePermissionSessions() {
std::vector<PermissionUsageSession> sessions;
for (size_t i = 0; i < base::size(test_times_); ++i) {
for (size_t j = i + 1; j <= base::size(test_times_); ++j) {
for (bool had_user_activation : {false, true}) {
for (bool was_foreground : {false, true}) {
for (bool had_focus : {false, true}) {
base::Time start = test_times_[i];
base::Time end = (j == base::size(test_times_)) ? test_times_[i]
: test_times_[j];
sessions.push_back({.usage_start = start,
.usage_end = end,
.had_user_activation = had_user_activation,
.was_foreground = was_foreground,
.had_focus = had_focus});
}
}
}
}
}
return sessions;
}
PermissionAuditingDatabase db_;
base::ScopedTempDir temp_directory_;
base::FilePath path_;
DISALLOW_COPY_AND_ASSIGN(PermissionAuditingDatabaseTest);
};
TEST_F(PermissionAuditingDatabaseTest, IsUsageHistorySizeCorrect) {
auto session = SessionLike(kTestTypes[0], kTestUrl1, test_sessions_[0]);
EXPECT_THAT(GetPermissionUsageHistory(kTestTypes[0], kTestUrl1), IsEmpty());
size_t current_size = 0;
for (const auto& time : test_times_) {
session.usage_start = time;
session.usage_end = time;
ASSERT_TRUE(db().StorePermissionUsage(
SessionLike(kTestTypes[0], kTestUrl1, session)));
ASSERT_EQ(GetPermissionUsageHistory(kTestTypes[0], kTestUrl1).size(),
++current_size);
}
}
TEST_F(PermissionAuditingDatabaseTest,
IsUsageHistoryDifferentForDifferentPermissionsAndOrigins) {
const auto& session1 =
SessionLike(kTestTypes[0], kTestUrl1, test_sessions_[0]);
const auto& session2 =
SessionLike(kTestTypes[1], kTestUrl2, test_sessions_[1]);
ASSERT_TRUE(db().StorePermissionUsage(session1));
ASSERT_TRUE(db().StorePermissionUsage(session2));
EXPECT_THAT(GetPermissionUsageHistory(kTestTypes[0], kTestUrl1),
ElementsAre(session1));
EXPECT_THAT(GetPermissionUsageHistory(kTestTypes[1], kTestUrl2),
ElementsAre(session2));
EXPECT_THAT(GetPermissionUsageHistory(kTestTypes[0], kTestUrl2), IsEmpty());
EXPECT_THAT(GetPermissionUsageHistory(kTestTypes[1], kTestUrl1), IsEmpty());
}
TEST_F(PermissionAuditingDatabaseTest, AreFieldsStoredCorrectlyInUsageHistory) {
int counter = 0;
for (const auto& session : test_sessions_) {
const std::string url = GetUniqueUrl(++counter);
auto updated_session = SessionLike(kTestTypes[0], url.c_str(), session);
ASSERT_TRUE(db().StorePermissionUsage(updated_session));
EXPECT_THAT(GetPermissionUsageHistory(kTestTypes[0], url.c_str()),
ElementsAre(updated_session));
}
}
TEST_F(PermissionAuditingDatabaseTest, UsageHistoryContainsOnlyLastSessions) {
for (const auto time : test_times_) {
ASSERT_TRUE(db().StorePermissionUsage(
{.origin = GetOrigin(kTestUrl1),
.type = kTestTypes[0],
.usage_start = time,
.usage_end = time + base::TimeDelta::FromMicroseconds(1),
.had_user_activation = false,
.was_foreground = false,
.had_focus = false}));
}
EXPECT_EQ(GetPermissionUsageHistory(kTestTypes[0], kTestUrl1).size(),
base::size(test_times_));
for (size_t i = 0; i < base::size(test_times_); ++i) {
EXPECT_EQ(GetPermissionUsageHistory(
kTestTypes[0], kTestUrl1,
test_times_[i] + base::TimeDelta::FromMicroseconds(2))
.size(),
base::size(test_times_) - i - 1);
}
}
TEST_F(PermissionAuditingDatabaseTest, GetLastPermissionUsageTime) {
EXPECT_FALSE(GetLastUsageTime(kTestTypes[0], kTestUrl1));
for (const auto& time : test_times_) {
ASSERT_TRUE(db().StorePermissionUsage({.origin = GetOrigin(kTestUrl1),
.type = kTestTypes[0],
.usage_start = time,
.usage_end = time,
.had_user_activation = false,
.was_foreground = false,
.had_focus = false}));
EXPECT_EQ(GetLastUsageTime(kTestTypes[0], kTestUrl1), time);
}
}
TEST_F(PermissionAuditingDatabaseTest, UpdateEndTime) {
int counter = 0;
for (const auto& session : test_sessions_) {
const std::string url = GetUniqueUrl(++counter);
ASSERT_TRUE(db().StorePermissionUsage(
SessionLike(kTestTypes[0], url.c_str(), session)));
const auto& end_time = session.usage_end;
auto tomorrow = end_time + base::TimeDelta::FromDays(1);
ASSERT_TRUE(GetLastUsageTime(kTestTypes[0], url.c_str()) == end_time);
ASSERT_TRUE(UpdateEndTime(kTestTypes[0], url.c_str(), session.usage_start,
tomorrow));
EXPECT_EQ(GetLastUsageTime(kTestTypes[0], url.c_str()), tomorrow);
auto history = GetPermissionUsageHistory(kTestTypes[0], url.c_str());
ASSERT_EQ(history.size(), 1u);
EXPECT_EQ(history[0].usage_end, tomorrow);
}
}
TEST_F(PermissionAuditingDatabaseTest, DeleteSessionsBetween) {
size_t current_size = base::size(test_times_);
StoreSessionForEachTime();
for (const auto& time : test_times_) {
ASSERT_TRUE(db().DeleteSessionsBetween(time, time));
ASSERT_EQ(GetPermissionUsageHistory(kTestTypes[0], kTestUrl1).size(),
--current_size);
}
}
TEST_F(PermissionAuditingDatabaseTest,
DeleteSessionsBetweenWithUnspecifiedEndTime) {
StoreSessionForEachTime();
ASSERT_TRUE(db().DeleteSessionsBetween(test_times_[1], base::Time()));
ASSERT_EQ(GetPermissionUsageHistory(kTestTypes[0], kTestUrl1).size(), 1u);
ASSERT_TRUE(db().DeleteSessionsBetween(test_times_[0], base::Time()));
EXPECT_THAT(GetPermissionUsageHistory(kTestTypes[0], kTestUrl1), IsEmpty());
}
TEST_F(PermissionAuditingDatabaseTest,
DeleteSessionsBetweenWithUnspecifiedStartTime) {
StoreSessionForEachTime();
ASSERT_TRUE(db().DeleteSessionsBetween(
base::Time(), test_times_[base::size(test_times_) - 2]));
ASSERT_EQ(GetPermissionUsageHistory(kTestTypes[0], kTestUrl1).size(), 1u);
ASSERT_TRUE(db().DeleteSessionsBetween(
base::Time(), test_times_[base::size(test_times_) - 1]));
EXPECT_THAT(GetPermissionUsageHistory(kTestTypes[0], kTestUrl1), IsEmpty());
}
TEST_F(PermissionAuditingDatabaseTest,
DeleteSessionsBetweenWithUnspecifiedStartAndEndTime) {
StoreSessionForEachTime();
ASSERT_EQ(GetPermissionUsageHistory(kTestTypes[0], kTestUrl1).size(),
base::size(test_times_));
ASSERT_TRUE(db().DeleteSessionsBetween(base::Time(), base::Time()));
EXPECT_THAT(GetPermissionUsageHistory(kTestTypes[0], kTestUrl1), IsEmpty());
}
TEST_F(PermissionAuditingDatabaseTest,
StorePermissionUsageChecksTimerangeConstraint) {
auto session = SessionLike(kTestTypes[0], kTestUrl1, test_sessions_[0]);
session.usage_start = session.usage_end;
EXPECT_TRUE(db().StorePermissionUsage(session));
}
TEST_F(PermissionAuditingDatabaseTest,
StorePermissionUsageDoesntAccpetExistingRecord) {
auto session = SessionLike(kTestTypes[0], kTestUrl1, test_sessions_[0]);
EXPECT_TRUE(db().StorePermissionUsage(session));
{
sql::test::ScopedErrorExpecter expecter;
expecter.ExpectError(SQLITE_CONSTRAINT);
EXPECT_FALSE(db().StorePermissionUsage(session));
EXPECT_TRUE(expecter.SawExpectedErrors());
}
}
TEST_F(PermissionAuditingDatabaseTest, UpdateEndTimeChecksTimerangeConstraint) {
auto session = SessionLike(kTestTypes[0], kTestUrl1, test_sessions_[0]);
ASSERT_TRUE(db().StorePermissionUsage(session));
EXPECT_TRUE(UpdateEndTime(kTestTypes[0], kTestUrl1, session.usage_start,
session.usage_start));
}
} // namespace permissions
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/permissions/permission_usage_session.h"
#include <tuple>
namespace permissions {
bool PermissionUsageSession::operator==(
const PermissionUsageSession& other) const {
return std::tie(origin, type, usage_start, usage_end, had_user_activation,
was_foreground, had_focus) ==
std::tie(other.origin, other.type, other.usage_start, other.usage_end,
other.had_user_activation, other.was_foreground,
other.had_focus);
}
bool PermissionUsageSession::operator!=(
const PermissionUsageSession& other) const {
return !(*this == other);
}
bool PermissionUsageSession::IsValid() const {
return !(origin.opaque() || usage_start.is_null() || usage_end.is_null() ||
usage_end < usage_start);
}
} // namespace permissions
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef COMPONENTS_PERMISSIONS_PERMISSION_USAGE_SESSION_H_
#define COMPONENTS_PERMISSIONS_PERMISSION_USAGE_SESSION_H_
#include "base/time/time.h"
#include "components/content_settings/core/common/content_settings_types.h"
#include "url/origin.h"
namespace permissions {
// Stores information about a permission usage session, which is a continuous
// time interval during which some permission was used by some site. For
// instance, a usage session could be a time interval during which a site
// accessed the camera. {type, origin, usage_start} forms the primary of a
// session.
struct PermissionUsageSession {
// The `origin` accessing the capability. Must not be opaque.
url::Origin origin;
ContentSettingsType type;
// The time interval in which the capability was accessed, such that
// `usage_start` <= `usage_end`, and neither is null.
base::Time usage_start;
base::Time usage_end;
// Specifies if the permission usage started with the browsing context having
// transient user activation.
bool had_user_activation;
// Specifies if the permission usage started in the foreground.
bool was_foreground;
// Specifies if the requesting frame had focus at the time the permission.
// usage started.
bool had_focus;
bool operator==(const PermissionUsageSession& other) const;
bool operator!=(const PermissionUsageSession& other) const;
// Checks if the session satisfies the following constraints:
// 1) `origin` is not opaque;
// 2) `usage_start` and `usage_end` are not null;
// 3) `usage_start` <= `usage_end`.
bool IsValid() const;
};
} // namespace permissions
#endif // COMPONENTS_PERMISSIONS_PERMISSION_USAGE_SESSION_H_
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment