blob: 4f2e432bde8ad992b6bf9b54d5e460c8195204f8 [file] [log] [blame]
// 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 "sql/database.h"
#include "sql/meta_table.h"
#include "sql/statement.h"
#include "sql/transaction.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
#include "url/gurl.h"
namespace permissions {
namespace {
// 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) {
db_.set_histogram_tag("Permission Auditing Logs");
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.BindTime(2, session.usage_start);
statement.BindTime(3, 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.BindTime(2, start_time.is_null() ? base::Time::Min() : start_time);
while (statement.Step()) {
sessions.push_back({.origin = origin,
.type = type,
.usage_start = statement.ColumnTime(0),
.usage_end = statement.ColumnTime(1),
.had_user_activation = statement.ColumnBool(2),
.was_foreground = statement.ColumnBool(3),
.had_focus = statement.ColumnBool(4)});
}
return sessions;
}
absl::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));
absl::optional<base::Time> last_usage;
if (statement.Step()) {
last_usage = statement.ColumnTime(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.BindTime(0, new_end_time);
statement.BindString(1, origin.Serialize());
statement.BindInt(2, static_cast<int32_t>(type));
statement.BindTime(3, 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 ?"));
statement.BindTime(0, start_time.is_null() ? base::Time::Min() : start_time);
statement.BindTime(1, end_time.is_null() ? base::Time::Max() : end_time);
statement.BindTime(2, start_time.is_null() ? base::Time::Min() : start_time);
statement.BindTime(3, end_time.is_null() ? base::Time::Max() : end_time);
sql::Transaction transaction(&db_);
if (!transaction.Begin()) {
return false;
}
if (!statement.Run()) {
return false;
}
return transaction.Commit();
}
} // namespace permissions