Skip to content

Commit 9c20916

Browse files
authored
Add App Check support to Database (#1260)
* Add App Check logic to database * Formatting * Fix lint issues * Fix for unit tests * Formatting * Update the WebSocket app check token * Update persistent_connection.cc * Update connection.cc * Update connection.cc * Changes to address feedback, and other fixes * Formatting * Use a const ref string with the function registry * Formatting * Fixes for the App Check Android testapp * Revert "Fixes for the App Check Android testapp" This reverts commit 9583509.
1 parent 3f64f97 commit 9c20916

16 files changed

+386
-90
lines changed

app_check/integration_test/src/integration_test.cc

Lines changed: 63 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,7 @@ class FirebaseAppCheckTest : public FirebaseTest {
131131

132132
firebase::database::DatabaseReference CreateWorkingPath(
133133
bool suppress_cleanup = false);
134+
void CleanupDatabase(int expected_error);
134135

135136
firebase::firestore::CollectionReference GetFirestoreCollection();
136137
firebase::firestore::DocumentReference CreateFirestoreDoc();
@@ -312,20 +313,7 @@ void FirebaseAppCheckTest::TerminateDatabase() {
312313
if (!initialized_) return;
313314

314315
if (database_) {
315-
if (!database_cleanup_.empty() && database_ && app_) {
316-
LogDebug("Cleaning up...");
317-
std::vector<firebase::Future<void>> cleanups;
318-
cleanups.reserve(database_cleanup_.size());
319-
for (int i = 0; i < database_cleanup_.size(); ++i) {
320-
cleanups.push_back(database_cleanup_[i].RemoveValue());
321-
}
322-
for (int i = 0; i < cleanups.size(); ++i) {
323-
std::string cleanup_name =
324-
"Cleanup (" + database_cleanup_[i].url() + ")";
325-
WaitForCompletion(cleanups[i], cleanup_name.c_str());
326-
}
327-
database_cleanup_.clear();
328-
}
316+
CleanupDatabase(0);
329317

330318
LogDebug("Shutdown the Database library.");
331319
delete database_;
@@ -336,6 +324,22 @@ void FirebaseAppCheckTest::TerminateDatabase() {
336324
ProcessEvents(100);
337325
}
338326

327+
void FirebaseAppCheckTest::CleanupDatabase(int expected_error) {
328+
if (!database_cleanup_.empty()) {
329+
LogDebug("Cleaning up Database...");
330+
std::vector<firebase::Future<void>> cleanups;
331+
cleanups.reserve(database_cleanup_.size());
332+
for (int i = 0; i < database_cleanup_.size(); ++i) {
333+
cleanups.push_back(database_cleanup_[i].RemoveValue());
334+
}
335+
for (int i = 0; i < cleanups.size(); ++i) {
336+
std::string cleanup_name = "Cleanup (" + database_cleanup_[i].url() + ")";
337+
WaitForCompletion(cleanups[i], cleanup_name.c_str(), expected_error);
338+
}
339+
database_cleanup_.clear();
340+
}
341+
}
342+
339343
void FirebaseAppCheckTest::InitializeAppAuthDatabase() {
340344
InitializeApp();
341345
InitializeAuth();
@@ -743,18 +747,19 @@ TEST_F(FirebaseAppCheckTest, TestPlayIntegrityProvider) {
743747
#endif
744748
}
745749

746-
// Disabling the database tests for now, since they are crashing or hanging.
747-
TEST_F(FirebaseAppCheckTest, DISABLED_TestDatabaseFailure) {
750+
TEST_F(FirebaseAppCheckTest, TestDatabaseFailure) {
751+
firebase::SetLogLevel(firebase::kLogLevelVerbose);
748752
// Don't initialize App Check this time. Database should fail.
749753
InitializeAppAuthDatabase();
750754
firebase::database::DatabaseReference ref = CreateWorkingPath();
751755
const char* test_name = test_info_->name();
752756
firebase::Future<void> f = ref.Child(test_name).SetValue("test");
753-
// It is unclear if this should fail, or hang, so disabled for now.
754-
WaitForCompletion(f, "SetString");
757+
WaitForCompletion(f, "SetString", firebase::database::kErrorDisconnected);
758+
759+
CleanupDatabase(firebase::database::kErrorOperationFailed);
755760
}
756761

757-
TEST_F(FirebaseAppCheckTest, DISABLED_TestDatabaseCreateWorkingPath) {
762+
TEST_F(FirebaseAppCheckTest, TestDatabaseCreateWorkingPath) {
758763
InitializeAppCheckWithDebug();
759764
InitializeAppAuthDatabase();
760765
firebase::database::DatabaseReference working_path = CreateWorkingPath();
@@ -769,7 +774,7 @@ TEST_F(FirebaseAppCheckTest, DISABLED_TestDatabaseCreateWorkingPath) {
769774

770775
static const char kSimpleString[] = "Some simple string";
771776

772-
TEST_F(FirebaseAppCheckTest, DISABLED_TestDatabaseSetAndGet) {
777+
TEST_F(FirebaseAppCheckTest, TestDatabaseSetAndGet) {
773778
InitializeAppCheckWithDebug();
774779
InitializeAppAuthDatabase();
775780

@@ -795,7 +800,7 @@ TEST_F(FirebaseAppCheckTest, DISABLED_TestDatabaseSetAndGet) {
795800
}
796801
}
797802

798-
TEST_F(FirebaseAppCheckTest, DISABLED_TestRunTransaction) {
803+
TEST_F(FirebaseAppCheckTest, TestDatabaseRunTransaction) {
799804
InitializeAppCheckWithDebug();
800805
InitializeAppAuthDatabase();
801806

@@ -849,6 +854,42 @@ TEST_F(FirebaseAppCheckTest, DISABLED_TestRunTransaction) {
849854
}
850855
}
851856

857+
TEST_F(FirebaseAppCheckTest, TestDatabaseUpdateToken) {
858+
// Test that after forcing an App Check token update, the database connection
859+
// still works.
860+
InitializeAppCheckWithDebug();
861+
InitializeAppAuthDatabase();
862+
863+
const char* test_name = test_info_->name();
864+
firebase::database::DatabaseReference ref = CreateWorkingPath();
865+
866+
{
867+
LogDebug("Setting value.");
868+
firebase::Future<void> f1 =
869+
ref.Child(test_name).Child("String").SetValue(kSimpleString);
870+
WaitForCompletion(f1, "SetSimpleString");
871+
}
872+
873+
// Force App Check to update its token.
874+
::firebase::app_check::AppCheck* app_check =
875+
::firebase::app_check::AppCheck::GetInstance(app_);
876+
ASSERT_NE(app_check, nullptr);
877+
firebase::Future<::firebase::app_check::AppCheckToken> future =
878+
app_check->GetAppCheckToken(true);
879+
EXPECT_TRUE(WaitForCompletion(future, "GetAppCheckToken"));
880+
881+
// Get the values that we just set, and confirm that they match what we
882+
// set them to.
883+
{
884+
LogDebug("Getting value.");
885+
firebase::Future<firebase::database::DataSnapshot> f1 =
886+
ref.Child(test_name).Child("String").GetValue();
887+
WaitForCompletion(f1, "GetSimpleString");
888+
889+
EXPECT_EQ(f1.result()->value().AsString(), kSimpleString);
890+
}
891+
}
892+
852893
TEST_F(FirebaseAppCheckTest, TestStorageReadFile) {
853894
InitializeAppCheckWithDebug();
854895
InitializeAppAuthStorage();
@@ -1030,11 +1071,7 @@ TEST_F(FirebaseAppCheckTest, TestFirestoreListenerFailure) {
10301071
const firebase::firestore::DocumentSnapshot& result,
10311072
firebase::firestore::Error error_code,
10321073
const std::string& error_message) {
1033-
if (error_code == firebase::firestore::kErrorNone) {
1034-
// If we receive a success, it should only be for the cache.
1035-
EXPECT_TRUE(result.metadata().has_pending_writes());
1036-
EXPECT_TRUE(result.metadata().is_from_cache());
1037-
} else {
1074+
if (error_code != firebase::firestore::kErrorNone) {
10381075
// We expect one call with a Permission Denied error, from the
10391076
// server.
10401077
std::lock_guard<std::mutex> lock(mutex);

app_check/src/desktop/app_check_desktop.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ namespace internal {
3131

3232
// The callback type for psuedo-AppCheckListeners added via the
3333
// function registry.
34-
using FunctionRegistryCallback = void (*)(std::string, void*);
34+
using FunctionRegistryCallback = void (*)(const std::string&, void*);
3535

3636
class FunctionRegistryAppCheckListener : public AppCheckListener {
3737
public:

app_check/src/desktop/debug_provider_desktop.cc

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,12 @@ void DebugAppCheckProvider::GetToken(
9494
// options.
9595
const char* debug_token = std::getenv("APP_CHECK_DEBUG_TOKEN");
9696

97+
if (!debug_token) {
98+
completion_callback({}, kAppCheckErrorInvalidConfiguration,
99+
"Missing debug token");
100+
return;
101+
}
102+
97103
// Exchange debug token with the backend to get a proper attestation token.
98104
auto request = MakeShared<DebugTokenRequest>(app_);
99105
request->SetDebugToken(debug_token);

database/src/desktop/connection/connection.cc

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,13 @@
1616

1717
#include <cstdlib>
1818
#include <cstring>
19+
#include <string>
1920

2021
#include "app/src/assert.h"
2122
#include "app/src/log.h"
2223
#include "app/src/variant_util.h"
2324
#include "database/src/desktop/connection/util_connection.h"
25+
#include "database/src/desktop/connection/web_socket_client_impl.h"
2426

2527
namespace firebase {
2628
namespace database {
@@ -52,7 +54,8 @@ compat::Atomic<uint32_t> Connection::next_log_id_(0);
5254

5355
Connection::Connection(scheduler::Scheduler* scheduler, const HostInfo& info,
5456
const char* opt_last_session_id,
55-
ConnectionEventHandler* event_handler, Logger* logger)
57+
ConnectionEventHandler* event_handler, Logger* logger,
58+
const std::string& app_check_token)
5659
: safe_this_(this),
5760
event_handler_(event_handler),
5861
scheduler_(scheduler),
@@ -72,7 +75,7 @@ Connection::Connection(scheduler::Scheduler* scheduler, const HostInfo& info,
7275

7376
// Create web socket client regardless of its implementation
7477
client_ = CreateWebSocketClient(host_info_, this, opt_last_session_id, logger,
75-
scheduler);
78+
scheduler, app_check_token);
7679
}
7780

7881
Connection::~Connection() {
@@ -404,7 +407,10 @@ void Connection::OnConnectionShutdown(const std::string& reason) {
404407

405408
event_handler_->OnKill(reason);
406409

407-
Close(kDisconnectReasonShutdownMessage);
410+
// OnKill can result in the client being torn down, so check for that.
411+
if (client_) {
412+
Close(kDisconnectReasonShutdownMessage);
413+
}
408414
}
409415

410416
void Connection::OnHandshake(const Variant& handshake) {
@@ -462,6 +468,14 @@ void Connection::OnReset(const std::string& host) {
462468
Close(kDisconnectReasonServerReset);
463469
}
464470

471+
void Connection::RefreshAppCheckToken(const std::string& token) {
472+
WebSocketClientImpl* client_impl =
473+
dynamic_cast<WebSocketClientImpl*>(client_.get());
474+
if (client_impl) {
475+
client_impl->RefreshAppCheckToken(token);
476+
}
477+
}
478+
465479
} // namespace connection
466480
} // namespace internal
467481
} // namespace database

database/src/desktop/connection/connection.h

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,8 @@ class Connection : public WebSocketClientEventHandler {
7575

7676
explicit Connection(scheduler::Scheduler* scheduler, const HostInfo& info,
7777
const char* opt_last_session_id,
78-
ConnectionEventHandler* event_handler, Logger* logger);
78+
ConnectionEventHandler* event_handler, Logger* logger,
79+
const std::string& app_check_token = "");
7980
~Connection() override;
8081

8182
// Connection is neither copyable nor movable.
@@ -104,6 +105,12 @@ class Connection : public WebSocketClientEventHandler {
104105
void OnError(const WebSocketClientErrorData& error_data) override;
105106
// END WebSocketClientEventHandler
106107

108+
// Refresh the stored App Check token being used by the connection.
109+
// This doesn't change the connection itself, just the data used for
110+
// establishing new connections.
111+
// Expect to be called from scheduler thread.
112+
void RefreshAppCheckToken(const std::string& token);
113+
107114
private:
108115
// State of the connection
109116
enum State {

0 commit comments

Comments
 (0)