From 3e7c062f3baca83fae1937bf60865be0cd18f96d Mon Sep 17 00:00:00 2001 From: zxu Date: Tue, 27 Feb 2018 14:04:39 -0500 Subject: replacing Auth by C++ auth implementation (#802) * lazy replacing FST(Firebase)CredentialsProvider by (Firebase)CredentialsProvider * lazy replacing FSTUser by User * adding error-code parameter to TokenListener * actually use const user& instead of pointer; also add an error util * add HashUser and pass into the unordered_map * use User in test * use c++ CredentialsProvider and subclass in test * fix unit test * use explicit capture in lambda instead of capture all by reference * cache currentUser explicitly when reset sync engineer test driver * objc object should be captured by value in lambda * replacing Auth/FSTUser by C++ auth implementation * address changes * replacing FSTGetTokenResult by C++ Token implementation * address changes * fix unintentional change in merging * patch the change in objc Auth up-stream * somehow, the lambda-version of set-user-change-listener does not work... fallback to block * address changes * fix another const& v.s. dispatch bug * fix more const& v.s. dispatch bug zxu123 committed * fix a bad sync line * address changes * address change * address change * fix upstream change from merge * fix upstream changes * Suggested fixes for cpp/port_auth (#846) * Get rid of MockDatastore factory This avoids the need to statically allocate (and leak) a credentials provider * Use absl::make_unique std::make_unique technically does not exist until C++14. * #include for std::move * Use std::future for the initial user * fix style --- .../Example/Tests/Integration/FSTDatastoreTests.mm | 9 +- .../Example/Tests/Integration/FSTStreamTests.mm | 10 +- .../Example/Tests/SpecTests/FSTMockDatastore.h | 2 - .../Example/Tests/SpecTests/FSTMockDatastore.mm | 35 ++--- .../Tests/SpecTests/FSTSyncEngineTestDriver.mm | 14 +- .../Example/Tests/Util/FSTIntegrationTestCase.mm | 20 ++- Firestore/Source/API/FIRFirestore+Internal.h | 7 +- Firestore/Source/API/FIRFirestore.mm | 24 ++-- Firestore/Source/Auth/FSTCredentialsProvider.h | 88 ------------ Firestore/Source/Auth/FSTCredentialsProvider.mm | 157 --------------------- .../Source/Auth/FSTEmptyCredentialsProvider.h | 28 ---- .../Source/Auth/FSTEmptyCredentialsProvider.mm | 53 ------- Firestore/Source/Core/FSTFirestoreClient.h | 5 +- Firestore/Source/Core/FSTFirestoreClient.mm | 59 ++++---- Firestore/Source/Remote/FSTDatastore.h | 9 +- Firestore/Source/Remote/FSTDatastore.mm | 51 ++++--- Firestore/Source/Remote/FSTStream.h | 18 ++- Firestore/Source/Remote/FSTStream.mm | 30 ++-- .../include/firebase/firestore/firestore_errors.h | 115 +++++++++++++++ .../firebase/firestore/auth/credentials_provider.h | 7 +- .../firestore/auth/empty_credentials_provider.cc | 3 +- .../auth/firebase_credentials_provider_apple.mm | 7 +- .../core/src/firebase/firestore/util/error_apple.h | 52 +++++++ .../firestore/auth/credentials_provider_test.cc | 7 +- .../auth/empty_credentials_provider_test.cc | 8 +- .../auth/firebase_credentials_provider_test.mm | 12 +- 26 files changed, 360 insertions(+), 470 deletions(-) delete mode 100644 Firestore/Source/Auth/FSTCredentialsProvider.h delete mode 100644 Firestore/Source/Auth/FSTCredentialsProvider.mm delete mode 100644 Firestore/Source/Auth/FSTEmptyCredentialsProvider.h delete mode 100644 Firestore/Source/Auth/FSTEmptyCredentialsProvider.mm create mode 100644 Firestore/core/include/firebase/firestore/firestore_errors.h create mode 100644 Firestore/core/src/firebase/firestore/util/error_apple.h (limited to 'Firestore') diff --git a/Firestore/Example/Tests/Integration/FSTDatastoreTests.mm b/Firestore/Example/Tests/Integration/FSTDatastoreTests.mm index c38e8f3..4323ccd 100644 --- a/Firestore/Example/Tests/Integration/FSTDatastoreTests.mm +++ b/Firestore/Example/Tests/Integration/FSTDatastoreTests.mm @@ -23,7 +23,6 @@ #import "Firestore/Source/API/FIRDocumentReference+Internal.h" #import "Firestore/Source/API/FSTUserDataConverter.h" -#import "Firestore/Source/Auth/FSTEmptyCredentialsProvider.h" #import "Firestore/Source/Core/FSTFirestoreClient.h" #import "Firestore/Source/Core/FSTQuery.h" #import "Firestore/Source/Core/FSTSnapshotVersion.h" @@ -41,11 +40,13 @@ #import "Firestore/Example/Tests/Util/FSTIntegrationTestCase.h" +#include "Firestore/core/src/firebase/firestore/auth/empty_credentials_provider.h" #include "Firestore/core/src/firebase/firestore/core/database_info.h" #include "Firestore/core/src/firebase/firestore/model/database_id.h" #include "Firestore/core/src/firebase/firestore/util/string_apple.h" namespace util = firebase::firestore::util; +using firebase::firestore::auth::EmptyCredentialsProvider; using firebase::firestore::core::DatabaseInfo; using firebase::firestore::model::DatabaseId; @@ -141,7 +142,7 @@ NS_ASSUME_NONNULL_BEGIN @implementation FSTDatastoreTests { FSTDispatchQueue *_testWorkerQueue; FSTLocalStore *_localStore; - id _credentials; + EmptyCredentialsProvider _credentials; DatabaseInfo _databaseInfo; FSTDatastore *_datastore; @@ -170,11 +171,9 @@ NS_ASSUME_NONNULL_BEGIN queueWith:dispatch_queue_create("com.google.firestore.FSTDatastoreTestsWorkerQueue", DISPATCH_QUEUE_SERIAL)]; - _credentials = [[FSTEmptyCredentialsProvider alloc] init]; - _datastore = [FSTDatastore datastoreWithDatabase:&_databaseInfo workerDispatchQueue:_testWorkerQueue - credentials:_credentials]; + credentials:&_credentials]; _remoteStore = [FSTRemoteStore remoteStoreWithLocalStore:_localStore datastore:_datastore]; diff --git a/Firestore/Example/Tests/Integration/FSTStreamTests.mm b/Firestore/Example/Tests/Integration/FSTStreamTests.mm index 9105b74..6550368 100644 --- a/Firestore/Example/Tests/Integration/FSTStreamTests.mm +++ b/Firestore/Example/Tests/Integration/FSTStreamTests.mm @@ -22,16 +22,17 @@ #import "Firestore/Example/Tests/Util/FSTHelpers.h" #import "Firestore/Example/Tests/Util/FSTIntegrationTestCase.h" -#import "Firestore/Source/Auth/FSTEmptyCredentialsProvider.h" #import "Firestore/Source/Remote/FSTDatastore.h" #import "Firestore/Source/Remote/FSTStream.h" #import "Firestore/Source/Util/FSTAssert.h" +#include "Firestore/core/src/firebase/firestore/auth/empty_credentials_provider.h" #include "Firestore/core/src/firebase/firestore/core/database_info.h" #include "Firestore/core/src/firebase/firestore/model/database_id.h" #include "Firestore/core/src/firebase/firestore/util/string_apple.h" namespace util = firebase::firestore::util; +using firebase::firestore::auth::EmptyCredentialsProvider; using firebase::firestore::core::DatabaseInfo; using firebase::firestore::model::DatabaseId; @@ -136,7 +137,7 @@ using firebase::firestore::model::DatabaseId; dispatch_queue_t _testQueue; FSTDispatchQueue *_workerDispatchQueue; DatabaseInfo _databaseInfo; - FSTEmptyCredentialsProvider *_credentials; + EmptyCredentialsProvider _credentials; FSTStreamStatusDelegate *_delegate; /** Single mutation to send to the write stream. */ @@ -155,7 +156,6 @@ using firebase::firestore::model::DatabaseId; _databaseInfo = DatabaseInfo(database_id, "test-key", util::MakeStringView(settings.host), settings.sslEnabled); - _credentials = [[FSTEmptyCredentialsProvider alloc] init]; _delegate = [[FSTStreamStatusDelegate alloc] initWithTestCase:self queue:_workerDispatchQueue]; @@ -165,14 +165,14 @@ using firebase::firestore::model::DatabaseId; - (FSTWriteStream *)setUpWriteStream { FSTDatastore *datastore = [[FSTDatastore alloc] initWithDatabaseInfo:&_databaseInfo workerDispatchQueue:_workerDispatchQueue - credentials:_credentials]; + credentials:&_credentials]; return [datastore createWriteStream]; } - (FSTWatchStream *)setUpWatchStream { FSTDatastore *datastore = [[FSTDatastore alloc] initWithDatabaseInfo:&_databaseInfo workerDispatchQueue:_workerDispatchQueue - credentials:_credentials]; + credentials:&_credentials]; return [datastore createWatchStream]; } diff --git a/Firestore/Example/Tests/SpecTests/FSTMockDatastore.h b/Firestore/Example/Tests/SpecTests/FSTMockDatastore.h index 5420c2e..e1ea2fb 100644 --- a/Firestore/Example/Tests/SpecTests/FSTMockDatastore.h +++ b/Firestore/Example/Tests/SpecTests/FSTMockDatastore.h @@ -34,8 +34,6 @@ NS_ASSUME_NONNULL_BEGIN */ @property(nonatomic) int writeStreamRequestCount; -+ (instancetype)mockDatastoreWithWorkerDispatchQueue:(FSTDispatchQueue *)workerDispatchQueue; - #pragma mark - Watch Stream manipulation. /** Injects an Added WatchChange containing the given targetIDs. */ diff --git a/Firestore/Example/Tests/SpecTests/FSTMockDatastore.mm b/Firestore/Example/Tests/SpecTests/FSTMockDatastore.mm index d3c4171..6715b24 100644 --- a/Firestore/Example/Tests/SpecTests/FSTMockDatastore.mm +++ b/Firestore/Example/Tests/SpecTests/FSTMockDatastore.mm @@ -16,7 +16,6 @@ #import "Firestore/Example/Tests/SpecTests/FSTMockDatastore.h" -#import "Firestore/Source/Auth/FSTEmptyCredentialsProvider.h" #import "Firestore/Source/Core/FSTSnapshotVersion.h" #import "Firestore/Source/Local/FSTQueryData.h" #import "Firestore/Source/Model/FSTMutation.h" @@ -27,10 +26,14 @@ #import "Firestore/Example/Tests/Remote/FSTWatchChange+Testing.h" +#include "Firestore/core/src/firebase/firestore/auth/credentials_provider.h" +#include "Firestore/core/src/firebase/firestore/auth/empty_credentials_provider.h" #include "Firestore/core/src/firebase/firestore/core/database_info.h" #include "Firestore/core/src/firebase/firestore/model/database_id.h" #include "Firestore/core/src/firebase/firestore/util/string_apple.h" +using firebase::firestore::auth::CredentialsProvider; +using firebase::firestore::auth::EmptyCredentialsProvider; using firebase::firestore::core::DatabaseInfo; using firebase::firestore::model::DatabaseId; @@ -44,17 +47,17 @@ NS_ASSUME_NONNULL_BEGIN - (instancetype)initWithDatastore:(FSTMockDatastore *)datastore workerDispatchQueue:(FSTDispatchQueue *)workerDispatchQueue - credentials:(id)credentials + credentials:(CredentialsProvider *)credentials serializer:(FSTSerializerBeta *)serializer NS_DESIGNATED_INITIALIZER; - (instancetype)initWithDatabase:(const DatabaseInfo *)database workerDispatchQueue:(FSTDispatchQueue *)workerDispatchQueue - credentials:(id)credentials + credentials:(CredentialsProvider *)credentials serializer:(FSTSerializerBeta *)serializer NS_UNAVAILABLE; - (instancetype)initWithDatabase:(const DatabaseInfo *)database workerDispatchQueue:(FSTDispatchQueue *)workerDispatchQueue - credentials:(id)credentials + credentials:(CredentialsProvider *)credentials responseMessageClass:(Class)responseMessageClass NS_UNAVAILABLE; @property(nonatomic, assign) BOOL open; @@ -69,7 +72,7 @@ NS_ASSUME_NONNULL_BEGIN - (instancetype)initWithDatastore:(FSTMockDatastore *)datastore workerDispatchQueue:(FSTDispatchQueue *)workerDispatchQueue - credentials:(id)credentials + credentials:(CredentialsProvider *)credentials serializer:(FSTSerializerBeta *)serializer { self = [super initWithDatabase:datastore.databaseInfo workerDispatchQueue:workerDispatchQueue @@ -170,17 +173,17 @@ NS_ASSUME_NONNULL_BEGIN - (instancetype)initWithDatastore:(FSTMockDatastore *)datastore workerDispatchQueue:(FSTDispatchQueue *)workerDispatchQueue - credentials:(id)credentials + credentials:(CredentialsProvider *)credentials serializer:(FSTSerializerBeta *)serializer NS_DESIGNATED_INITIALIZER; - (instancetype)initWithDatabase:(const DatabaseInfo *)database workerDispatchQueue:(FSTDispatchQueue *)workerDispatchQueue - credentials:(id)credentials + credentials:(CredentialsProvider *)credentials serializer:(FSTSerializerBeta *)serializer NS_UNAVAILABLE; - (instancetype)initWithDatabase:(const DatabaseInfo *)database workerDispatchQueue:(FSTDispatchQueue *)workerDispatchQueue - credentials:(id)credentials + credentials:(CredentialsProvider *)credentials responseMessageClass:(Class)responseMessageClass NS_UNAVAILABLE; @property(nonatomic, strong, readonly) FSTMockDatastore *datastore; @@ -193,7 +196,7 @@ NS_ASSUME_NONNULL_BEGIN - (instancetype)initWithDatastore:(FSTMockDatastore *)datastore workerDispatchQueue:(FSTDispatchQueue *)workerDispatchQueue - credentials:(id)credentials + credentials:(CredentialsProvider *)credentials serializer:(FSTSerializerBeta *)serializer { self = [super initWithDatabase:datastore.databaseInfo workerDispatchQueue:workerDispatchQueue @@ -279,24 +282,12 @@ NS_ASSUME_NONNULL_BEGIN /** Properties implemented in FSTDatastore that are nonpublic. */ @property(nonatomic, strong, readonly) FSTDispatchQueue *workerDispatchQueue; -@property(nonatomic, strong, readonly) id credentials; +@property(nonatomic, assign, readonly) CredentialsProvider *credentials; @end @implementation FSTMockDatastore -+ (instancetype)mockDatastoreWithWorkerDispatchQueue:(FSTDispatchQueue *)workerDispatchQueue { - // This owns the DatabaseInfos since we do not have FirestoreClient instance to own them. - static DatabaseInfo database_info{DatabaseId{"project", "database"}, "persistence", "host", - false}; - - FSTEmptyCredentialsProvider *credentials = [[FSTEmptyCredentialsProvider alloc] init]; - - return [[FSTMockDatastore alloc] initWithDatabaseInfo:&database_info - workerDispatchQueue:workerDispatchQueue - credentials:credentials]; -} - #pragma mark - Overridden FSTDatastore methods. - (FSTWatchStream *)createWatchStream { diff --git a/Firestore/Example/Tests/SpecTests/FSTSyncEngineTestDriver.mm b/Firestore/Example/Tests/SpecTests/FSTSyncEngineTestDriver.mm index 1c7eadf..07dc84f 100644 --- a/Firestore/Example/Tests/SpecTests/FSTSyncEngineTestDriver.mm +++ b/Firestore/Example/Tests/SpecTests/FSTSyncEngineTestDriver.mm @@ -37,10 +37,16 @@ #import "Firestore/Example/Tests/Core/FSTSyncEngine+Testing.h" #import "Firestore/Example/Tests/SpecTests/FSTMockDatastore.h" +#include "Firestore/core/src/firebase/firestore/auth/empty_credentials_provider.h" #include "Firestore/core/src/firebase/firestore/auth/user.h" +#include "Firestore/core/src/firebase/firestore/core/database_info.h" +#include "Firestore/core/src/firebase/firestore/model/database_id.h" +using firebase::firestore::auth::EmptyCredentialsProvider; using firebase::firestore::auth::HashUser; using firebase::firestore::auth::User; +using firebase::firestore::core::DatabaseInfo; +using firebase::firestore::model::DatabaseId; NS_ASSUME_NONNULL_BEGIN @@ -83,7 +89,9 @@ NS_ASSUME_NONNULL_BEGIN // ivar is declared as mutable. std::unordered_map *, HashUser> _outstandingWrites; + DatabaseInfo _databaseInfo; User _currentUser; + EmptyCredentialsProvider _credentialProvider; } - (instancetype)initWithPersistence:(id)persistence @@ -106,13 +114,17 @@ NS_ASSUME_NONNULL_BEGIN _events = [NSMutableArray array]; + _databaseInfo = {DatabaseId{"project", "database"}, "persistence", "host", false}; + // Set up the sync engine and various stores. dispatch_queue_t mainQueue = dispatch_get_main_queue(); FSTDispatchQueue *dispatchQueue = [FSTDispatchQueue queueWith:mainQueue]; _localStore = [[FSTLocalStore alloc] initWithPersistence:persistence garbageCollector:garbageCollector initialUser:initialUser]; - _datastore = [FSTMockDatastore mockDatastoreWithWorkerDispatchQueue:dispatchQueue]; + _datastore = [[FSTMockDatastore alloc] initWithDatabaseInfo:&_databaseInfo + workerDispatchQueue:dispatchQueue + credentials:&_credentialProvider]; _remoteStore = [FSTRemoteStore remoteStoreWithLocalStore:_localStore datastore:_datastore]; diff --git a/Firestore/Example/Tests/Util/FSTIntegrationTestCase.mm b/Firestore/Example/Tests/Util/FSTIntegrationTestCase.mm index 3c80d16..059f257 100644 --- a/Firestore/Example/Tests/Util/FSTIntegrationTestCase.mm +++ b/Firestore/Example/Tests/Util/FSTIntegrationTestCase.mm @@ -16,25 +16,30 @@ #import "Firestore/Example/Tests/Util/FSTIntegrationTestCase.h" +#include +#include + #import #import #import #import +#include "Firestore/core/src/firebase/firestore/auth/empty_credentials_provider.h" +#include "Firestore/core/src/firebase/firestore/model/database_id.h" #include "Firestore/core/src/firebase/firestore/util/autoid.h" +#include "Firestore/core/src/firebase/firestore/util/string_apple.h" +#include "absl/memory/memory.h" #import "Firestore/Source/API/FIRFirestore+Internal.h" -#import "Firestore/Source/Auth/FSTEmptyCredentialsProvider.h" #import "Firestore/Source/Core/FSTFirestoreClient.h" #import "Firestore/Source/Local/FSTLevelDB.h" #import "Firestore/Source/Util/FSTDispatchQueue.h" #import "Firestore/Example/Tests/Util/FSTEventAccumulator.h" -#include "Firestore/core/src/firebase/firestore/model/database_id.h" -#include "Firestore/core/src/firebase/firestore/util/string_apple.h" - namespace util = firebase::firestore::util; +using firebase::firestore::auth::CredentialsProvider; +using firebase::firestore::auth::EmptyCredentialsProvider; using firebase::firestore::model::DatabaseId; using firebase::firestore::util::CreateAutoId; @@ -135,15 +140,16 @@ NS_ASSUME_NONNULL_BEGIN FSTDispatchQueue *workerDispatchQueue = [FSTDispatchQueue queueWith:dispatch_queue_create("com.google.firebase.firestore", DISPATCH_QUEUE_SERIAL)]; - FSTEmptyCredentialsProvider *credentialsProvider = [[FSTEmptyCredentialsProvider alloc] init]; - FIRSetLoggerLevel(FIRLoggerLevelDebug); // HACK: FIRFirestore expects a non-nil app, but for tests we cheat. FIRApp *app = nil; + std::unique_ptr credentials_provider = + absl::make_unique(); + FIRFirestore *firestore = [[FIRFirestore alloc] initWithProjectID:util::MakeStringView(projectID) database:DatabaseId::kDefault persistenceKey:persistenceKey - credentialsProvider:credentialsProvider + credentialsProvider:std::move(credentials_provider) workerDispatchQueue:workerDispatchQueue firebaseApp:app]; diff --git a/Firestore/Source/API/FIRFirestore+Internal.h b/Firestore/Source/API/FIRFirestore+Internal.h index 7bc419a..a52533d 100644 --- a/Firestore/Source/API/FIRFirestore+Internal.h +++ b/Firestore/Source/API/FIRFirestore+Internal.h @@ -16,6 +16,9 @@ #import "FIRFirestore.h" +#include + +#include "Firestore/core/src/firebase/firestore/auth/credentials_provider.h" #include "Firestore/core/src/firebase/firestore/model/database_id.h" #include "absl/strings/string_view.h" @@ -24,7 +27,6 @@ NS_ASSUME_NONNULL_BEGIN @class FSTDispatchQueue; @class FSTFirestoreClient; @class FSTUserDataConverter; -@protocol FSTCredentialsProvider; @interface FIRFirestore (/* Init */) @@ -35,7 +37,8 @@ NS_ASSUME_NONNULL_BEGIN - (instancetype)initWithProjectID:(const absl::string_view)projectID database:(const absl::string_view)database persistenceKey:(NSString *)persistenceKey - credentialsProvider:(id)credentialsProvider + credentialsProvider:(std::unique_ptr) + credentialsProvider workerDispatchQueue:(FSTDispatchQueue *)workerDispatchQueue firebaseApp:(FIRApp *)app; diff --git a/Firestore/Source/API/FIRFirestore.mm b/Firestore/Source/API/FIRFirestore.mm index ce8f8ab..eff0605 100644 --- a/Firestore/Source/API/FIRFirestore.mm +++ b/Firestore/Source/API/FIRFirestore.mm @@ -16,6 +16,10 @@ #import "FIRFirestore.h" +#include +#include + +#import #import #import #import @@ -28,7 +32,6 @@ #import "Firestore/Source/API/FIRWriteBatch+Internal.h" #import "Firestore/Source/API/FSTUserDataConverter.h" -#import "Firestore/Source/Auth/FSTCredentialsProvider.h" #import "Firestore/Source/Core/FSTFirestoreClient.h" #import "Firestore/Source/Model/FSTDocumentKey.h" #import "Firestore/Source/Model/FSTPath.h" @@ -37,11 +40,16 @@ #import "Firestore/Source/Util/FSTLogger.h" #import "Firestore/Source/Util/FSTUsageValidation.h" +#include "Firestore/core/src/firebase/firestore/auth/credentials_provider.h" +#include "Firestore/core/src/firebase/firestore/auth/firebase_credentials_provider_apple.h" #include "Firestore/core/src/firebase/firestore/core/database_info.h" #include "Firestore/core/src/firebase/firestore/model/database_id.h" #include "Firestore/core/src/firebase/firestore/util/string_apple.h" +#include "absl/memory/memory.h" namespace util = firebase::firestore::util; +using firebase::firestore::auth::CredentialsProvider; +using firebase::firestore::auth::FirebaseCredentialsProvider; using firebase::firestore::core::DatabaseInfo; using firebase::firestore::model::DatabaseId; @@ -52,10 +60,10 @@ extern "C" NSString *const FIRFirestoreErrorDomain = @"FIRFirestoreErrorDomain"; @interface FIRFirestore () { /** The actual owned DatabaseId instance is allocated in FIRFirestore. */ DatabaseId _databaseID; + std::unique_ptr _credentialsProvider; } @property(nonatomic, strong) NSString *persistenceKey; -@property(nonatomic, strong) id credentialsProvider; @property(nonatomic, strong) FSTDispatchQueue *workerDispatchQueue; // Note that `client` is updated after initialization, but marking this readwrite would generate an @@ -152,15 +160,15 @@ extern "C" NSString *const FIRFirestoreErrorDomain = @"FIRFirestoreErrorDomain"; FSTDispatchQueue *workerDispatchQueue = [FSTDispatchQueue queueWith:dispatch_queue_create("com.google.firebase.firestore", DISPATCH_QUEUE_SERIAL)]; - id credentialsProvider; - credentialsProvider = [[FSTFirebaseCredentialsProvider alloc] initWithApp:app]; + std::unique_ptr credentials_provider = + absl::make_unique(app); NSString *persistenceKey = app.name; firestore = [[FIRFirestore alloc] initWithProjectID:util::MakeStringView(projectID) database:util::MakeStringView(database) persistenceKey:persistenceKey - credentialsProvider:credentialsProvider + credentialsProvider:std::move(credentials_provider) workerDispatchQueue:workerDispatchQueue firebaseApp:app]; instances[key] = firestore; @@ -173,7 +181,7 @@ extern "C" NSString *const FIRFirestoreErrorDomain = @"FIRFirestoreErrorDomain"; - (instancetype)initWithProjectID:(const absl::string_view)projectID database:(const absl::string_view)database persistenceKey:(NSString *)persistenceKey - credentialsProvider:(id)credentialsProvider + credentialsProvider:(std::unique_ptr)credentialsProvider workerDispatchQueue:(FSTDispatchQueue *)workerDispatchQueue firebaseApp:(FIRApp *)app { if (self = [super init]) { @@ -190,7 +198,7 @@ extern "C" NSString *const FIRFirestoreErrorDomain = @"FIRFirestoreErrorDomain"; _dataConverter = [[FSTUserDataConverter alloc] initWithDatabaseID:&_databaseID preConverter:block]; _persistenceKey = persistenceKey; - _credentialsProvider = credentialsProvider; + _credentialsProvider = std::move(credentialsProvider); _workerDispatchQueue = workerDispatchQueue; _app = app; _settings = [[FIRFirestoreSettings alloc] init]; @@ -241,7 +249,7 @@ extern "C" NSString *const FIRFirestoreErrorDomain = @"FIRFirestoreErrorDomain"; _client = [FSTFirestoreClient clientWithDatabaseInfo:database_info usePersistence:_settings.persistenceEnabled - credentialsProvider:_credentialsProvider + credentialsProvider:_credentialsProvider.get() userDispatchQueue:userDispatchQueue workerDispatchQueue:_workerDispatchQueue]; } diff --git a/Firestore/Source/Auth/FSTCredentialsProvider.h b/Firestore/Source/Auth/FSTCredentialsProvider.h deleted file mode 100644 index d2f04e0..0000000 --- a/Firestore/Source/Auth/FSTCredentialsProvider.h +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright 2017 Google - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#import - -#include "Firestore/core/src/firebase/firestore/auth/token.h" -#include "Firestore/core/src/firebase/firestore/auth/user.h" - -NS_ASSUME_NONNULL_BEGIN - -@class FIRApp; -@class FSTDispatchQueue; - -#pragma mark - Typedefs - -/** - * `FSTVoidTokenErrorBlock` is a block that gets a token or an error. - * - * @param token An auth token, either valid or invalid when error occurred. - * @param error The error if one occurred, or else `nil`. - */ -typedef void (^FSTVoidGetTokenResultBlock)(firebase::firestore::auth::Token token, - NSError *_Nullable error); - -/** Listener block notified with a User. */ -typedef void (^FSTVoidUserBlock)(firebase::firestore::auth::User user); - -#pragma mark - FSTCredentialsProvider - -/** Provides methods for getting the uid and token for the current user and listen for changes. */ -@protocol FSTCredentialsProvider - -/** Requests token for the current user, optionally forcing a refreshed token to be fetched. */ -- (void)getTokenForcingRefresh:(BOOL)forceRefresh completion:(FSTVoidGetTokenResultBlock)completion; - -/** - * A listener to be notified of user changes (sign-in / sign-out). It is immediately called once - * with the initial user. - * - * Note that this block will be called back on an arbitrary thread that is not the normal Firestore - * worker thread. - */ -@property(nonatomic, copy, nullable, readwrite) FSTVoidUserBlock userChangeListener; - -@end - -#pragma mark - FSTFirebaseCredentialsProvider - -/** - * `FSTFirebaseCredentialsProvider` uses Firebase Auth via `FIRApp` to get an auth token. - * - * NOTE: To simplify the implementation, it requires that you set `userChangeListener` with a - * non-`nil` value no more than once and don't call `getTokenForcingRefresh:` after setting - * it to `nil`. - * - * This class must be implemented in a thread-safe manner since it is accessed from the thread - * backing our internal worker queue and the callbacks from FIRAuth will be executed on an - * arbitrary different thread. - */ -@interface FSTFirebaseCredentialsProvider : NSObject - -/** - * Initializes a new FSTFirebaseCredentialsProvider. - * - * @param app The Firebase app from which to get credentials. - * - * @return A new instance of FSTFirebaseCredentialsProvider. - */ -- (instancetype)initWithApp:(FIRApp *)app NS_DESIGNATED_INITIALIZER; - -- (id)init NS_UNAVAILABLE; - -@end - -NS_ASSUME_NONNULL_END diff --git a/Firestore/Source/Auth/FSTCredentialsProvider.mm b/Firestore/Source/Auth/FSTCredentialsProvider.mm deleted file mode 100644 index e0dc8aa..0000000 --- a/Firestore/Source/Auth/FSTCredentialsProvider.mm +++ /dev/null @@ -1,157 +0,0 @@ -/* - * Copyright 2017 Google - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#import "Firestore/Source/Auth/FSTCredentialsProvider.h" - -#import -#import -#import - -#import "FIRFirestoreErrors.h" -#import "Firestore/Source/Util/FSTAssert.h" -#import "Firestore/Source/Util/FSTClasses.h" -#import "Firestore/Source/Util/FSTDispatchQueue.h" - -#include "Firestore/core/src/firebase/firestore/auth/token.h" -#include "Firestore/core/src/firebase/firestore/auth/user.h" -#include "Firestore/core/src/firebase/firestore/util/string_apple.h" - -namespace util = firebase::firestore::util; -using firebase::firestore::auth::Token; -using firebase::firestore::auth::User; - -NS_ASSUME_NONNULL_BEGIN - -#pragma mark - FSTFirebaseCredentialsProvider -@interface FSTFirebaseCredentialsProvider () { - /** The current user as reported to us via our AuthStateDidChangeListener. */ - User _currentUser; -} - -@property(nonatomic, strong, readonly) FIRApp *app; - -/** Handle used to stop receiving auth changes once userChangeListener is removed. */ -@property(nonatomic, strong, nullable, readwrite) id authListenerHandle; - -/** - * Counter used to detect if the user changed while a -getTokenForcingRefresh: request was - * outstanding. - */ -@property(nonatomic, assign, readwrite) int userCounter; - -@end - -@implementation FSTFirebaseCredentialsProvider { - FSTVoidUserBlock _userChangeListener; -} - -- (instancetype)initWithApp:(FIRApp *)app { - self = [super init]; - if (self) { - _app = app; - _currentUser = User::FromUid([self.app getUID]); - _userCounter = 0; - - // Register for user changes so that we can internally track the current user. - FSTWeakify(self); - _authListenerHandle = [[NSNotificationCenter defaultCenter] - addObserverForName:FIRAuthStateDidChangeInternalNotification - object:nil - queue:nil - usingBlock:^(NSNotification *notification) { - FSTStrongify(self); - if (self) { - @synchronized(self) { - NSDictionary *userInfo = notification.userInfo; - - // ensure we're only notifiying for the current app. - FIRApp *notifiedApp = - userInfo[FIRAuthStateDidChangeInternalNotificationAppKey]; - if (![self.app isEqual:notifiedApp]) { - return; - } - - NSString *uid = userInfo[FIRAuthStateDidChangeInternalNotificationUIDKey]; - User newUser = User::FromUid(uid); - if (newUser != self->_currentUser) { - self->_currentUser = newUser; - self.userCounter++; - FSTVoidUserBlock listenerBlock = self.userChangeListener; - if (listenerBlock) { - listenerBlock(self->_currentUser); - } - } - } - } - }]; - } - return self; -} - -- (void)getTokenForcingRefresh:(BOOL)forceRefresh - completion:(FSTVoidGetTokenResultBlock)completion { - FSTAssert(self.authListenerHandle, @"getToken cannot be called after listener removed."); - - // Take note of the current value of the userCounter so that this method can fail (with a - // FIRFirestoreErrorCodeAborted error) if there is a user change while the request is outstanding. - int initialUserCounter = self.userCounter; - - void (^getTokenCallback)(NSString *, NSError *) = - ^(NSString *_Nullable token, NSError *_Nullable error) { - @synchronized(self) { - if (initialUserCounter != self.userCounter) { - // Cancel the request since the user changed while the request was outstanding so the - // response is likely for a previous user (which user, we can't be sure). - NSDictionary *errorInfo = @{@"details" : @"getToken aborted due to user change."}; - NSError *cancelError = [NSError errorWithDomain:FIRFirestoreErrorDomain - code:FIRFirestoreErrorCodeAborted - userInfo:errorInfo]; - completion(Token::Invalid(), cancelError); - } else { - completion(Token(util::MakeStringView(token), self->_currentUser), error); - } - }; - }; - - [self.app getTokenForcingRefresh:forceRefresh withCallback:getTokenCallback]; -} - -- (void)setUserChangeListener:(nullable FSTVoidUserBlock)block { - @synchronized(self) { - if (block) { - FSTAssert(!_userChangeListener, @"UserChangeListener set twice!"); - - // Fire initial event. - block(_currentUser); - } else { - FSTAssert(self.authListenerHandle, @"UserChangeListener removed twice!"); - FSTAssert(_userChangeListener, @"UserChangeListener removed without being set!"); - [[NSNotificationCenter defaultCenter] removeObserver:self.authListenerHandle]; - self.authListenerHandle = nil; - } - _userChangeListener = block; - } -} - -- (nullable FSTVoidUserBlock)userChangeListener { - @synchronized(self) { - return _userChangeListener; - } -} - -@end - -NS_ASSUME_NONNULL_END diff --git a/Firestore/Source/Auth/FSTEmptyCredentialsProvider.h b/Firestore/Source/Auth/FSTEmptyCredentialsProvider.h deleted file mode 100644 index f805363..0000000 --- a/Firestore/Source/Auth/FSTEmptyCredentialsProvider.h +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright 2017 Google - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#import - -#import "Firestore/Source/Auth/FSTCredentialsProvider.h" - -NS_ASSUME_NONNULL_BEGIN - -/** `FSTEmptyCredentialsProvider` always yields an empty token. */ -@interface FSTEmptyCredentialsProvider : NSObject - -@end - -NS_ASSUME_NONNULL_END diff --git a/Firestore/Source/Auth/FSTEmptyCredentialsProvider.mm b/Firestore/Source/Auth/FSTEmptyCredentialsProvider.mm deleted file mode 100644 index 77c08d1..0000000 --- a/Firestore/Source/Auth/FSTEmptyCredentialsProvider.mm +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright 2017 Google - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#import "Firestore/Source/Auth/FSTEmptyCredentialsProvider.h" - -#import "Firestore/Source/Util/FSTAssert.h" -#import "Firestore/Source/Util/FSTDispatchQueue.h" - -#include "Firestore/core/src/firebase/firestore/auth/token.h" -#include "Firestore/core/src/firebase/firestore/auth/user.h" - -using firebase::firestore::auth::Token; -using firebase::firestore::auth::User; - -NS_ASSUME_NONNULL_BEGIN - -@implementation FSTEmptyCredentialsProvider - -- (void)getTokenForcingRefresh:(BOOL)forceRefresh - completion:(FSTVoidGetTokenResultBlock)completion { - // Invalid token will force the GRPC fallback to use default settings. - completion(Token::Invalid(), nil); -} - -- (void)setUserChangeListener:(nullable FSTVoidUserBlock)block { - // Since the user never changes, we just need to fire the initial event and don't need to hang - // onto the block. - if (block) { - block(User::Unauthenticated()); - } -} - -- (nullable FSTVoidUserBlock)userChangeListener { - // TODO(mikelehen): Implementation omitted for convenience since it's not actually required. - FSTFail(@"Not implemented."); -} - -@end - -NS_ASSUME_NONNULL_END diff --git a/Firestore/Source/Core/FSTFirestoreClient.h b/Firestore/Source/Core/FSTFirestoreClient.h index 56101ab..6da5ed3 100644 --- a/Firestore/Source/Core/FSTFirestoreClient.h +++ b/Firestore/Source/Core/FSTFirestoreClient.h @@ -20,6 +20,7 @@ #import "Firestore/Source/Core/FSTViewSnapshot.h" #import "Firestore/Source/Remote/FSTRemoteStore.h" +#include "Firestore/core/src/firebase/firestore/auth/credentials_provider.h" #include "Firestore/core/src/firebase/firestore/core/database_info.h" #include "Firestore/core/src/firebase/firestore/model/database_id.h" @@ -30,7 +31,6 @@ @class FSTQuery; @class FSTQueryListener; @class FSTTransaction; -@protocol FSTCredentialsProvider; NS_ASSUME_NONNULL_BEGIN @@ -48,7 +48,8 @@ NS_ASSUME_NONNULL_BEGIN */ + (instancetype)clientWithDatabaseInfo:(const firebase::firestore::core::DatabaseInfo &)databaseInfo usePersistence:(BOOL)usePersistence - credentialsProvider:(id)credentialsProvider + credentialsProvider:(firebase::firestore::auth::CredentialsProvider *) + credentialsProvider // no passing ownership userDispatchQueue:(FSTDispatchQueue *)userDispatchQueue workerDispatchQueue:(FSTDispatchQueue *)workerDispatchQueue; diff --git a/Firestore/Source/Core/FSTFirestoreClient.mm b/Firestore/Source/Core/FSTFirestoreClient.mm index c60bb7c..fb86e0b 100644 --- a/Firestore/Source/Core/FSTFirestoreClient.mm +++ b/Firestore/Source/Core/FSTFirestoreClient.mm @@ -16,7 +16,8 @@ #import "Firestore/Source/Core/FSTFirestoreClient.h" -#import "Firestore/Source/Auth/FSTCredentialsProvider.h" +#import + #import "Firestore/Source/Core/FSTEventManager.h" #import "Firestore/Source/Core/FSTSyncEngine.h" #import "Firestore/Source/Core/FSTTransaction.h" @@ -34,11 +35,13 @@ #import "Firestore/Source/Util/FSTDispatchQueue.h" #import "Firestore/Source/Util/FSTLogger.h" +#include "Firestore/core/src/firebase/firestore/auth/credentials_provider.h" #include "Firestore/core/src/firebase/firestore/core/database_info.h" #include "Firestore/core/src/firebase/firestore/model/database_id.h" #include "Firestore/core/src/firebase/firestore/util/string_apple.h" namespace util = firebase::firestore::util; +using firebase::firestore::auth::CredentialsProvider; using firebase::firestore::auth::User; using firebase::firestore::core::DatabaseInfo; using firebase::firestore::model::DatabaseId; @@ -51,7 +54,8 @@ NS_ASSUME_NONNULL_BEGIN - (instancetype)initWithDatabaseInfo:(const DatabaseInfo &)databaseInfo usePersistence:(BOOL)usePersistence - credentialsProvider:(id)credentialsProvider + credentialsProvider: + (CredentialsProvider *)credentialsProvider // no passing ownership userDispatchQueue:(FSTDispatchQueue *)userDispatchQueue workerDispatchQueue:(FSTDispatchQueue *)queue NS_DESIGNATED_INITIALIZER; @@ -70,7 +74,8 @@ NS_ASSUME_NONNULL_BEGIN */ @property(nonatomic, strong, readonly) FSTDispatchQueue *workerDispatchQueue; -@property(nonatomic, strong, readonly) id credentialsProvider; +// Does not own the CredentialsProvider instance. +@property(nonatomic, assign, readonly) CredentialsProvider *credentialsProvider; @end @@ -78,7 +83,8 @@ NS_ASSUME_NONNULL_BEGIN + (instancetype)clientWithDatabaseInfo:(const DatabaseInfo &)databaseInfo usePersistence:(BOOL)usePersistence - credentialsProvider:(id)credentialsProvider + credentialsProvider: + (CredentialsProvider *)credentialsProvider // no passing ownership userDispatchQueue:(FSTDispatchQueue *)userDispatchQueue workerDispatchQueue:(FSTDispatchQueue *)workerDispatchQueue { return [[FSTFirestoreClient alloc] initWithDatabaseInfo:databaseInfo @@ -90,7 +96,8 @@ NS_ASSUME_NONNULL_BEGIN - (instancetype)initWithDatabaseInfo:(const DatabaseInfo &)databaseInfo usePersistence:(BOOL)usePersistence - credentialsProvider:(id)credentialsProvider + credentialsProvider: + (CredentialsProvider *)credentialsProvider // no passing ownership userDispatchQueue:(FSTDispatchQueue *)userDispatchQueue workerDispatchQueue:(FSTDispatchQueue *)workerDispatchQueue { if (self = [super init]) { @@ -99,32 +106,32 @@ NS_ASSUME_NONNULL_BEGIN _userDispatchQueue = userDispatchQueue; _workerDispatchQueue = workerDispatchQueue; - dispatch_semaphore_t initialUserAvailable = dispatch_semaphore_create(0); - __block bool initialized = false; - __block User initialUser; - FSTWeakify(self); - _credentialsProvider.userChangeListener = ^(User user) { - FSTStrongify(self); - if (self) { - if (!initialized) { - initialUser = user; - initialized = true; - dispatch_semaphore_signal(initialUserAvailable); - } else { - [workerDispatchQueue dispatchAsync:^{ - [self userDidChange:user]; - }]; - } + auto userPromise = std::make_shared>(); + + __weak typeof(self) weakSelf = self; + auto userChangeListener = [initialized = false, userPromise, weakSelf, + workerDispatchQueue](User user) mutable { + typeof(self) strongSelf = weakSelf; + if (!strongSelf) return; + + if (!initialized) { + initialized = true; + userPromise->set_value(user); + } else { + [workerDispatchQueue dispatchAsync:^{ + [strongSelf userDidChange:user]; + }]; } }; + _credentialsProvider->SetUserChangeListener(userChangeListener); + // Defer initialization until we get the current user from the userChangeListener. This is // guaranteed to be synchronously dispatched onto our worker queue, so we will be initialized // before any subsequently queued work runs. [_workerDispatchQueue dispatchAsync:^{ - dispatch_semaphore_wait(initialUserAvailable, DISPATCH_TIME_FOREVER); - - [self initializeWithUser:initialUser usePersistence:usePersistence]; + User user = userPromise->get_future().get(); + [self initializeWithUser:user usePersistence:usePersistence]; }]; } return self; @@ -172,7 +179,7 @@ NS_ASSUME_NONNULL_BEGIN FSTDatastore *datastore = [FSTDatastore datastoreWithDatabase:self.databaseInfo workerDispatchQueue:self.workerDispatchQueue - credentials:self.credentialsProvider]; + credentials:_credentialsProvider]; _remoteStore = [FSTRemoteStore remoteStoreWithLocalStore:_localStore datastore:datastore]; @@ -229,7 +236,7 @@ NS_ASSUME_NONNULL_BEGIN - (void)shutdownWithCompletion:(nullable FSTVoidErrorBlock)completion { [self.workerDispatchQueue dispatchAsync:^{ - self.credentialsProvider.userChangeListener = nil; + self->_credentialsProvider->SetUserChangeListener(nullptr); [self.remoteStore shutdown]; [self.localStore shutdown]; diff --git a/Firestore/Source/Remote/FSTDatastore.h b/Firestore/Source/Remote/FSTDatastore.h index 481b6e8..7b8274c 100644 --- a/Firestore/Source/Remote/FSTDatastore.h +++ b/Firestore/Source/Remote/FSTDatastore.h @@ -18,6 +18,7 @@ #import "Firestore/Source/Core/FSTTypes.h" +#include "Firestore/core/src/firebase/firestore/auth/credentials_provider.h" #include "Firestore/core/src/firebase/firestore/core/database_info.h" #include "Firestore/core/src/firebase/firestore/model/database_id.h" #include "absl/strings/string_view.h" @@ -35,8 +36,6 @@ @class GRPCCall; @class GRXWriter; -@protocol FSTCredentialsProvider; - NS_ASSUME_NONNULL_BEGIN /** @@ -56,13 +55,15 @@ NS_ASSUME_NONNULL_BEGIN /** Creates a new Datastore instance with the given database info. */ + (instancetype)datastoreWithDatabase:(const firebase::firestore::core::DatabaseInfo *)databaseInfo workerDispatchQueue:(FSTDispatchQueue *)workerDispatchQueue - credentials:(id)credentials; + credentials:(firebase::firestore::auth::CredentialsProvider *) + credentials; // no passing ownership - (instancetype)init __attribute__((unavailable("Use a static constructor method."))); - (instancetype)initWithDatabaseInfo:(const firebase::firestore::core::DatabaseInfo *)databaseInfo workerDispatchQueue:(FSTDispatchQueue *)workerDispatchQueue - credentials:(id)credentials + credentials:(firebase::firestore::auth::CredentialsProvider *) + credentials // no passing ownership NS_DESIGNATED_INITIALIZER; /** diff --git a/Firestore/Source/Remote/FSTDatastore.mm b/Firestore/Source/Remote/FSTDatastore.mm index a6029ee..cb4516e 100644 --- a/Firestore/Source/Remote/FSTDatastore.mm +++ b/Firestore/Source/Remote/FSTDatastore.mm @@ -22,7 +22,6 @@ #import "FIRFirestoreErrors.h" #import "Firestore/Source/API/FIRFirestore+Internal.h" #import "Firestore/Source/API/FIRFirestoreVersion.h" -#import "Firestore/Source/Auth/FSTCredentialsProvider.h" #import "Firestore/Source/Local/FSTLocalStore.h" #import "Firestore/Source/Model/FSTDocument.h" #import "Firestore/Source/Model/FSTDocumentKey.h" @@ -35,12 +34,15 @@ #import "Firestore/Protos/objc/google/firestore/v1beta1/Firestore.pbrpc.h" +#include "Firestore/core/src/firebase/firestore/auth/credentials_provider.h" #include "Firestore/core/src/firebase/firestore/auth/token.h" #include "Firestore/core/src/firebase/firestore/core/database_info.h" #include "Firestore/core/src/firebase/firestore/model/database_id.h" +#include "Firestore/core/src/firebase/firestore/util/error_apple.h" #include "Firestore/core/src/firebase/firestore/util/string_apple.h" namespace util = firebase::firestore::util; +using firebase::firestore::auth::CredentialsProvider; using firebase::firestore::auth::Token; using firebase::firestore::core::DatabaseInfo; using firebase::firestore::model::DatabaseId; @@ -70,8 +72,11 @@ typedef GRPCProtoCall * (^RPCFactory)(void); @property(nonatomic, strong, readonly) FSTDispatchQueue *workerDispatchQueue; -/** An object for getting an auth token before each request. */ -@property(nonatomic, strong, readonly) id credentials; +/** + * An object for getting an auth token before each request. Does not own the CredentialsProvider + * instance. + */ +@property(nonatomic, assign, readonly) CredentialsProvider *credentials; @property(nonatomic, strong, readonly) FSTSerializerBeta *serializer; @@ -81,7 +86,7 @@ typedef GRPCProtoCall * (^RPCFactory)(void); + (instancetype)datastoreWithDatabase:(const DatabaseInfo *)databaseInfo workerDispatchQueue:(FSTDispatchQueue *)workerDispatchQueue - credentials:(id)credentials { + credentials:(CredentialsProvider *)credentials { return [[FSTDatastore alloc] initWithDatabaseInfo:databaseInfo workerDispatchQueue:workerDispatchQueue credentials:credentials]; @@ -89,7 +94,7 @@ typedef GRPCProtoCall * (^RPCFactory)(void); - (instancetype)initWithDatabaseInfo:(const DatabaseInfo *)databaseInfo workerDispatchQueue:(FSTDispatchQueue *)workerDispatchQueue - credentials:(id)credentials { + credentials:(CredentialsProvider *)credentials { if (self = [super init]) { _databaseInfo = databaseInfo; NSString *host = util::WrapNSStringNoCopy(databaseInfo->host()); @@ -301,24 +306,24 @@ typedef GRPCProtoCall * (^RPCFactory)(void); errorHandler:(FSTVoidErrorBlock)errorHandler { // TODO(mikelehen): We should force a refresh if the previous RPC failed due to an expired token, // but I'm not sure how to detect that right now. http://b/32762461 - [self.credentials - getTokenForcingRefresh:NO - completion:^(Token result, NSError *_Nullable error) { - error = [FSTDatastore firestoreErrorForError:error]; - [self.workerDispatchQueue dispatchAsyncAllowingSameQueue:^{ - if (error) { - errorHandler(error); - } else { - GRPCProtoCall *rpc = rpcFactory(); - [FSTDatastore - prepareHeadersForRPC:rpc - databaseID:&self.databaseInfo->database_id() - token:(result.is_valid() ? result.token() - : absl::string_view())]; - [rpc start]; - } - }]; - }]; + _credentials->GetToken( + /*force_refresh=*/false, + [self, rpcFactory, errorHandler](Token result, const int64_t error_code, + const absl::string_view error_msg) { + NSError *error = util::WrapNSError(error_code, error_msg); + [self.workerDispatchQueue dispatchAsyncAllowingSameQueue:^{ + if (error) { + errorHandler(error); + } else { + GRPCProtoCall *rpc = rpcFactory(); + [FSTDatastore + prepareHeadersForRPC:rpc + databaseID:&self.databaseInfo->database_id() + token:(result.is_valid() ? result.token() : absl::string_view())]; + [rpc start]; + } + }]; + }); } - (FSTWatchStream *)createWatchStream { diff --git a/Firestore/Source/Remote/FSTStream.h b/Firestore/Source/Remote/FSTStream.h index 297d016..e48f1da 100644 --- a/Firestore/Source/Remote/FSTStream.h +++ b/Firestore/Source/Remote/FSTStream.h @@ -19,6 +19,7 @@ #import "Firestore/Source/Core/FSTTypes.h" #import "Firestore/Source/Util/FSTDispatchQueue.h" +#include "Firestore/core/src/firebase/firestore/auth/credentials_provider.h" #include "Firestore/core/src/firebase/firestore/core/database_info.h" @class FSTDocumentKey; @@ -34,7 +35,6 @@ @class GRPCCall; @class GRXWriter; -@protocol FSTCredentialsProvider; @protocol FSTWatchStreamDelegate; @protocol FSTWriteStreamDelegate; @@ -47,7 +47,7 @@ NS_ASSUME_NONNULL_BEGIN * * - Restarting a stream is allowed (after failure) * - Exponential backoff on failure (independent of the underlying channel) - * - Authentication via FSTCredentialsProvider + * - Authentication via CredentialsProvider * - Dispatching all callbacks into the shared worker queue * * Subclasses of FSTStream implement serialization of models to and from bytes (via protocol @@ -94,7 +94,7 @@ NS_ASSUME_NONNULL_BEGIN workerDispatchQueue:(FSTDispatchQueue *)workerDispatchQueue connectionTimerID:(FSTTimerID)connectionTimerID idleTimerID:(FSTTimerID)idleTimerID - credentials:(id)credentials + credentials:(firebase::firestore::auth::CredentialsProvider *)credentials // no passing ownership responseMessageClass:(Class)responseMessageClass NS_DESIGNATED_INITIALIZER; - (instancetype)init NS_UNAVAILABLE; @@ -208,14 +208,16 @@ NS_ASSUME_NONNULL_BEGIN */ - (instancetype)initWithDatabase:(const firebase::firestore::core::DatabaseInfo *)database workerDispatchQueue:(FSTDispatchQueue *)workerDispatchQueue - credentials:(id)credentials + credentials:(firebase::firestore::auth::CredentialsProvider *) + credentials // no passsing ownership serializer:(FSTSerializerBeta *)serializer NS_DESIGNATED_INITIALIZER; - (instancetype)initWithDatabase:(const firebase::firestore::core::DatabaseInfo *)database workerDispatchQueue:(FSTDispatchQueue *)workerDispatchQueue connectionTimerID:(FSTTimerID)connectionTimerID idleTimerID:(FSTTimerID)idleTimerID - credentials:(id)credentials + credentials:(firebase::firestore::auth::CredentialsProvider *) + credentials // no passing ownership responseMessageClass:(Class)responseMessageClass NS_UNAVAILABLE; - (instancetype)init NS_UNAVAILABLE; @@ -284,14 +286,16 @@ NS_ASSUME_NONNULL_BEGIN */ - (instancetype)initWithDatabase:(const firebase::firestore::core::DatabaseInfo *)database workerDispatchQueue:(FSTDispatchQueue *)workerDispatchQueue - credentials:(id)credentials + credentials:(firebase::firestore::auth::CredentialsProvider *) + credentials // no passing ownership serializer:(FSTSerializerBeta *)serializer; - (instancetype)initWithDatabase:(const firebase::firestore::core::DatabaseInfo *)database workerDispatchQueue:(FSTDispatchQueue *)workerDispatchQueue connectionTimerID:(FSTTimerID)connectionTimerID idleTimerID:(FSTTimerID)idleTimerID - credentials:(id)credentials + credentials:(firebase::firestore::auth::CredentialsProvider *) + credentials // no passing ownership responseMessageClass:(Class)responseMessageClass NS_UNAVAILABLE; - (instancetype)init NS_UNAVAILABLE; diff --git a/Firestore/Source/Remote/FSTStream.mm b/Firestore/Source/Remote/FSTStream.mm index a9aa245..6bec3ad 100644 --- a/Firestore/Source/Remote/FSTStream.mm +++ b/Firestore/Source/Remote/FSTStream.mm @@ -21,7 +21,6 @@ #import "FIRFirestoreErrors.h" #import "Firestore/Source/API/FIRFirestore+Internal.h" -#import "Firestore/Source/Auth/FSTCredentialsProvider.h" #import "Firestore/Source/Local/FSTQueryData.h" #import "Firestore/Source/Model/FSTMutation.h" #import "Firestore/Source/Remote/FSTBufferedWriter.h" @@ -38,9 +37,11 @@ #include "Firestore/core/src/firebase/firestore/auth/token.h" #include "Firestore/core/src/firebase/firestore/core/database_info.h" #include "Firestore/core/src/firebase/firestore/model/database_id.h" +#include "Firestore/core/src/firebase/firestore/util/error_apple.h" #include "Firestore/core/src/firebase/firestore/util/string_apple.h" namespace util = firebase::firestore::util; +using firebase::firestore::auth::CredentialsProvider; using firebase::firestore::auth::Token; using firebase::firestore::core::DatabaseInfo; using firebase::firestore::model::DatabaseId; @@ -103,12 +104,12 @@ typedef NS_ENUM(NSInteger, FSTStreamState) { */ - (instancetype)initWithDatabase:(const DatabaseInfo *)database workerDispatchQueue:(FSTDispatchQueue *)workerDispatchQueue - credentials:(id)credentials + credentials:(CredentialsProvider *)credentials serializer:(FSTSerializerBeta *)serializer NS_DESIGNATED_INITIALIZER; - (instancetype)initWithDatabase:(const DatabaseInfo *)database workerDispatchQueue:(FSTDispatchQueue *)workerDispatchQueue - credentials:(id)credentials + credentials:(CredentialsProvider *)credentials responseMessageClass:(Class)responseMessageClass NS_UNAVAILABLE; @end @@ -126,7 +127,7 @@ typedef NS_ENUM(NSInteger, FSTStreamState) { // Does not own this DatabaseInfo. @property(nonatomic, assign, readonly) const DatabaseInfo *databaseInfo; @property(nonatomic, strong, readonly) FSTDispatchQueue *workerDispatchQueue; -@property(nonatomic, strong, readonly) id credentials; +@property(nonatomic, assign, readonly) CredentialsProvider *credentials; @property(nonatomic, unsafe_unretained, readonly) Class responseMessageClass; @property(nonatomic, strong, readonly) FSTExponentialBackoff *backoff; @@ -213,7 +214,7 @@ static const NSTimeInterval kIdleTimeout = 60.0; workerDispatchQueue:(FSTDispatchQueue *)workerDispatchQueue connectionTimerID:(FSTTimerID)connectionTimerID idleTimerID:(FSTTimerID)idleTimerID - credentials:(id)credentials + credentials:(CredentialsProvider *)credentials responseMessageClass:(Class)responseMessageClass { if (self = [super init]) { _databaseInfo = database; @@ -263,13 +264,14 @@ static const NSTimeInterval kIdleTimeout = 60.0; FSTAssert(_delegate == nil, @"Delegate must be nil"); _delegate = delegate; - [self.credentials getTokenForcingRefresh:NO - completion:^(Token result, NSError *_Nullable error) { - error = [FSTDatastore firestoreErrorForError:error]; - [self.workerDispatchQueue dispatchAsyncAllowingSameQueue:^{ - [self resumeStartWithToken:result error:error]; - }]; - }]; + _credentials->GetToken( + /*force_refresh=*/false, + [self](Token result, const int64_t error_code, const absl::string_view error_msg) { + NSError *error = util::WrapNSError(error_code, error_msg); + [self.workerDispatchQueue dispatchAsyncAllowingSameQueue:^{ + [self resumeStartWithToken:result error:error]; + }]; + }); } /** Add an access token to our RPC, after obtaining one from the credentials provider. */ @@ -614,7 +616,7 @@ static const NSTimeInterval kIdleTimeout = 60.0; - (instancetype)initWithDatabase:(const DatabaseInfo *)database workerDispatchQueue:(FSTDispatchQueue *)workerDispatchQueue - credentials:(id)credentials + credentials:(CredentialsProvider *)credentials serializer:(FSTSerializerBeta *)serializer { self = [super initWithDatabase:database workerDispatchQueue:workerDispatchQueue @@ -699,7 +701,7 @@ static const NSTimeInterval kIdleTimeout = 60.0; - (instancetype)initWithDatabase:(const DatabaseInfo *)database workerDispatchQueue:(FSTDispatchQueue *)workerDispatchQueue - credentials:(id)credentials + credentials:(CredentialsProvider *)credentials serializer:(FSTSerializerBeta *)serializer { self = [super initWithDatabase:database workerDispatchQueue:workerDispatchQueue diff --git a/Firestore/core/include/firebase/firestore/firestore_errors.h b/Firestore/core/include/firebase/firestore/firestore_errors.h new file mode 100644 index 0000000..7a0ff7c --- /dev/null +++ b/Firestore/core/include/firebase/firestore/firestore_errors.h @@ -0,0 +1,115 @@ +/* + * Copyright 2018 Google + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FIRESTORE_CORE_INCLUDE_FIREBASE_FIRESTORE_FIRESTORE_ERRORS_H_ +#define FIRESTORE_CORE_INCLUDE_FIREBASE_FIRESTORE_FIRESTORE_ERRORS_H_ + +namespace firebase { +namespace firestore { + +/** + * Error codes used by Cloud Firestore. + * + * The codes are in sync with the Firestore iOS client SDK header file + * FIRFirestoreErrors.h. + */ +enum FirestoreErrorCode { + /** + * The operation completed successfully. NSError objects will never have a + * code with this value. + */ + Ok = 0, + + /** The operation was cancelled (typically by the caller). */ + Cancelled = 1, + + /** Unknown error or an error from a different error domain. */ + Unknown = 2, + + /** + * Client specified an invalid argument. Note that this differs from + * FailedPrecondition. InvalidArgument indicates arguments that are + * problematic regardless of the state of the system (e.g., an invalid field + * name). + */ + InvalidArgument = 3, + + /** + * Deadline expired before operation could complete. For operations that + * change the state of the system, this error may be returned even if the + * operation has completed successfully. For example, a successful response + * from a server could have been delayed long enough for the deadline to + * expire. + */ + DeadlineExceeded = 4, + + /** Some requested document was not found. */ + NotFound = 5, + + /** Some document that we attempted to create already exists. */ + AlreadyExists = 6, + + /** The caller does not have permission to execute the specified operation. */ + PermissionDenied = 7, + + /** + * Some resource has been exhausted, perhaps a per-user quota, or perhaps the + * entire file system is out of space. + */ + ResourceExhausted = 8, + + /** + * Operation was rejected because the system is not in a state required for + * the operation's execution. + */ + FailedPrecondition = 9, + + /** + * The operation was aborted, typically due to a concurrency issue like + * transaction aborts, etc. + */ + Aborted = 10, + + /** Operation was attempted past the valid range. */ + OutOfRange = 11, + + /** Operation is not implemented or not supported/enabled. */ + Unimplemented = 12, + + /** + * Internal errors. Means some invariants expected by underlying system has + * been broken. If you see one of these errors, something is very broken. + */ + Internal = 13, + + /** + * The service is currently unavailable. This is a most likely a transient + * condition and may be corrected by retrying with a backoff. + */ + Unavailable = 14, + + /** Unrecoverable data loss or corruption. */ + DataLoss = 15, + + /** The request does not have valid authentication credentials for the + operation. */ + Unauthenticated = 16 +}; + +} // namespace firestore +} // namespace firebase + +#endif // FIRESTORE_CORE_INCLUDE_FIREBASE_FIRESTORE_FIRESTORE_ERRORS_H_ diff --git a/Firestore/core/src/firebase/firestore/auth/credentials_provider.h b/Firestore/core/src/firebase/firestore/auth/credentials_provider.h index 917f8e1..b9a8a24 100644 --- a/Firestore/core/src/firebase/firestore/auth/credentials_provider.h +++ b/Firestore/core/src/firebase/firestore/auth/credentials_provider.h @@ -20,6 +20,7 @@ #include #include +#include "Firestore/core/include/firebase/firestore/firestore_errors.h" #include "Firestore/core/src/firebase/firestore/auth/token.h" #include "Firestore/core/src/firebase/firestore/auth/user.h" #include "absl/strings/string_view.h" @@ -30,8 +31,10 @@ namespace auth { // `TokenErrorListener` is a listener that gets a token or an error. // token: An auth token as a string, or nullptr if error occurred. -// error: The error if one occurred, or else nullptr. -typedef std::function +// error_code: The error code if one occurred, or else FirestoreErrorCode::Ok. +// error_msg: The error if one occurred, or else nullptr. +typedef std::function TokenListener; // Listener notified with a User change. diff --git a/Firestore/core/src/firebase/firestore/auth/empty_credentials_provider.cc b/Firestore/core/src/firebase/firestore/auth/empty_credentials_provider.cc index 6ee7f61..0fa70c0 100644 --- a/Firestore/core/src/firebase/firestore/auth/empty_credentials_provider.cc +++ b/Firestore/core/src/firebase/firestore/auth/empty_credentials_provider.cc @@ -26,7 +26,8 @@ void EmptyCredentialsProvider::GetToken(bool force_refresh, TokenListener completion) { UNUSED(force_refresh); if (completion) { - completion({"", User::Unauthenticated()}, ""); + // Invalid token will force the GRPC fallback to use default settings. + completion(Token::Invalid(), FirestoreErrorCode::Ok, ""); } } diff --git a/Firestore/core/src/firebase/firestore/auth/firebase_credentials_provider_apple.mm b/Firestore/core/src/firebase/firestore/auth/firebase_credentials_provider_apple.mm index fe3cb24..1babe82 100644 --- a/Firestore/core/src/firebase/firestore/auth/firebase_credentials_provider_apple.mm +++ b/Firestore/core/src/firebase/firestore/auth/firebase_credentials_provider_apple.mm @@ -53,7 +53,7 @@ FirebaseCredentialsProvider::FirebaseCredentialsProvider(FIRApp* app) NSString* user_id = user_info[FIRAuthStateDidChangeInternalNotificationUIDKey]; - User new_user(util::MakeStringView(user_id)); + User new_user = User::FromUid(user_id); if (new_user != contents->current_user) { contents->current_user = new_user; contents->user_counter++; @@ -96,11 +96,12 @@ void FirebaseCredentialsProvider::GetToken(bool force_refresh, // Cancel the request since the user changed while the request was // outstanding so the response is likely for a previous user (which // user, we can't be sure). - completion({"", User::Unauthenticated()}, + completion(Token::Invalid(), FirestoreErrorCode::Aborted, "getToken aborted due to user change."); } else { completion( - {util::MakeStringView(token), contents->current_user}, + Token{util::MakeStringView(token), contents->current_user}, + error == nil ? FirestoreErrorCode::Ok : error.code, error == nil ? "" : util::MakeStringView(error.localizedDescription)); } }; diff --git a/Firestore/core/src/firebase/firestore/util/error_apple.h b/Firestore/core/src/firebase/firestore/util/error_apple.h new file mode 100644 index 0000000..e31cfd6 --- /dev/null +++ b/Firestore/core/src/firebase/firestore/util/error_apple.h @@ -0,0 +1,52 @@ +/* + * Copyright 2018 Google + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_UTIL_ERROR_APPLE_H_ +#define FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_UTIL_ERROR_APPLE_H_ + +// Everything in this header exists for compatibility with Objective-C. +#if __OBJC__ + +#import + +#include "Firestore/Source/Public/FIRFirestoreErrors.h" // for FIRFirestoreErrorDomain +#include "Firestore/core/include/firebase/firestore/firestore_errors.h" +#include "Firestore/core/src/firebase/firestore/util/string_apple.h" +#include "absl/strings/string_view.h" + +namespace firebase { +namespace firestore { +namespace util { + +// Translates a set of error_code and error_msg to an NSError. +inline NSError* WrapNSError(const int64_t error_code, + const absl::string_view error_msg) { + if (error_code == FirestoreErrorCode::Ok) { + return nil; + } + return [NSError + errorWithDomain:FIRFirestoreErrorDomain + code:error_code + userInfo:@{NSLocalizedDescriptionKey : WrapNSString(error_msg)}]; +} + +} // namespace util +} // namespace firestore +} // namespace firebase + +#endif // __OBJC__ + +#endif // FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_UTIL_ERROR_APPLE_H_ diff --git a/Firestore/core/test/firebase/firestore/auth/credentials_provider_test.cc b/Firestore/core/test/firebase/firestore/auth/credentials_provider_test.cc index 9ae71ba..6895061 100644 --- a/Firestore/core/test/firebase/firestore/auth/credentials_provider_test.cc +++ b/Firestore/core/test/firebase/firestore/auth/credentials_provider_test.cc @@ -25,10 +25,11 @@ namespace auth { #define UNUSED(x) (void)(x) TEST(CredentialsProvider, Typedef) { - TokenListener token_listener = [](Token token, - const absl::string_view error) { + TokenListener token_listener = [](Token token, const int64_t error_code, + const absl::string_view error_msg) { UNUSED(token); - UNUSED(error); + UNUSED(error_code); + UNUSED(error_msg); }; EXPECT_NE(nullptr, token_listener); EXPECT_TRUE(token_listener); diff --git a/Firestore/core/test/firebase/firestore/auth/empty_credentials_provider_test.cc b/Firestore/core/test/firebase/firestore/auth/empty_credentials_provider_test.cc index 39012f0..3b487f3 100644 --- a/Firestore/core/test/firebase/firestore/auth/empty_credentials_provider_test.cc +++ b/Firestore/core/test/firebase/firestore/auth/empty_credentials_provider_test.cc @@ -25,12 +25,14 @@ namespace auth { TEST(EmptyCredentialsProvider, GetToken) { EmptyCredentialsProvider credentials_provider; credentials_provider.GetToken( - /*force_refresh=*/true, [](Token token, const absl::string_view error) { - EXPECT_EQ("", token.token()); + /*force_refresh=*/true, [](Token token, const int64_t error_code, + const absl::string_view error_msg) { + EXPECT_FALSE(token.is_valid()); const User& user = token.user(); EXPECT_EQ("", user.uid()); EXPECT_FALSE(user.is_authenticated()); - EXPECT_EQ("", error); + EXPECT_EQ(FirestoreErrorCode::Ok, error_code); + EXPECT_EQ("", error_msg); }); } diff --git a/Firestore/core/test/firebase/firestore/auth/firebase_credentials_provider_test.mm b/Firestore/core/test/firebase/firestore/auth/firebase_credentials_provider_test.mm index a6ccc4a..3660d53 100644 --- a/Firestore/core/test/firebase/firestore/auth/firebase_credentials_provider_test.mm +++ b/Firestore/core/test/firebase/firestore/auth/firebase_credentials_provider_test.mm @@ -42,12 +42,14 @@ TEST(FirebaseCredentialsProviderTest, GetTokenUnauthenticated) { FirebaseCredentialsProvider credentials_provider(app); credentials_provider.GetToken( - /*force_refresh=*/true, [](Token token, const absl::string_view error) { + /*force_refresh=*/true, [](Token token, const int64_t error_code, + const absl::string_view error_msg) { EXPECT_EQ("", token.token()); const User& user = token.user(); EXPECT_EQ("", user.uid()); EXPECT_FALSE(user.is_authenticated()); - EXPECT_EQ("", error) << error; + EXPECT_EQ(FirestoreErrorCode::Ok, error_code) << error_code; + EXPECT_EQ("", error_msg) << error_msg; }); } @@ -56,12 +58,14 @@ TEST(FirebaseCredentialsProviderTest, GetToken) { FirebaseCredentialsProvider credentials_provider(app); credentials_provider.GetToken( - /*force_refresh=*/true, [](Token token, const absl::string_view error) { + /*force_refresh=*/true, [](Token token, const int64_t error_code, + const absl::string_view error_msg) { EXPECT_EQ("", token.token()); const User& user = token.user(); EXPECT_EQ("fake uid", user.uid()); EXPECT_TRUE(user.is_authenticated()); - EXPECT_EQ("", error) << error; + EXPECT_EQ(FirestoreErrorCode::Ok, error_code) << error_code; + EXPECT_EQ("", error_msg) << error_msg; }); } -- cgit v1.2.3