//////////////////////////////////////////////////////////////////////////// // // Copyright 2016 Realm Inc. // // 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 "RLMUser_Private.hpp" #import "RLMAPIKeyAuth.h" #import "RLMApp_Private.hpp" #import "RLMBSON_Private.hpp" #import "RLMCredentials_Private.hpp" #import "RLMMongoClient_Private.hpp" #import "RLMRealmConfiguration+Sync.h" #import "RLMSyncConfiguration_Private.hpp" #import "RLMSyncSession_Private.hpp" #import <realm/object-store/sync/sync_manager.hpp> #import <realm/object-store/sync/sync_session.hpp> #import <realm/object-store/sync/sync_user.hpp> #import <realm/object-store/util/bson/bson.hpp> using namespace realm; @interface RLMUser () { std::shared_ptr<SyncUser> _user; } @end @implementation RLMUserSubscriptionToken { @public std::unique_ptr<realm::Subscribable<SyncUser>::Token> _token; } - (instancetype)initWithToken:(realm::Subscribable<SyncUser>::Token&&)token { if (self = [super init]) { _token = std::make_unique<realm::Subscribable<SyncUser>::Token>(std::move(token)); return self; } return nil; } - (NSUInteger)value { return _token->value(); } @end @implementation RLMUser #pragma mark - API - (instancetype)initWithUser:(std::shared_ptr<SyncUser>)user app:(RLMApp *)app { if (self = [super init]) { _user = user; _app = app; return self; } return nil; } - (BOOL)isEqual:(id)object { if (![object isKindOfClass:[RLMUser class]]) { return NO; } return _user == ((RLMUser *)object)->_user; } - (RLMRealmConfiguration *)configurationWithPartitionValue:(nullable id<RLMBSON>)partitionValue { auto syncConfig = [[RLMSyncConfiguration alloc] initWithUser:self partitionValue:partitionValue customFileURL:nil stopPolicy:RLMSyncStopPolicyAfterChangesUploaded]; RLMRealmConfiguration *config = [[RLMRealmConfiguration alloc] init]; config.syncConfiguration = syncConfig; return config; } - (void)logOut { if (!_user) { return; } _user->log_out(); } - (BOOL)isLoggedIn { return _user->is_logged_in(); } - (void)invalidate { if (!_user) { return; } _user = nullptr; } - (std::string)pathForPartitionValue:(std::string const&)partitionValue { if (!_user) { return ""; } auto path = _user->sync_manager()->path_for_realm(*_user, partitionValue); if ([NSFileManager.defaultManager fileExistsAtPath:@(path.c_str())]) { return path; } // Previous versions converted the partition value to a path *twice*, // so if the file resulting from that exists open it instead NSString *encodedPartitionValue = [@(partitionValue.data()) stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLPathAllowedCharacterSet]]; NSString *overEncodedRealmName = [[NSString alloc] initWithFormat:@"%@/%@", self.identifier, encodedPartitionValue]; auto legacyPath = _user->sync_manager()->path_for_realm(*_user, overEncodedRealmName.UTF8String); if ([NSFileManager.defaultManager fileExistsAtPath:@(legacyPath.c_str())]) { return legacyPath; } return path; } - (nullable RLMSyncSession *)sessionForPartitionValue:(id<RLMBSON>)partitionValue { if (!_user) { return nil; } std::stringstream s; s << RLMConvertRLMBSONToBson(partitionValue); auto path = [self pathForPartitionValue:s.str()]; if (auto session = _user->session_for_on_disk_path(path)) { return [[RLMSyncSession alloc] initWithSyncSession:session]; } return nil; } - (NSArray<RLMSyncSession *> *)allSessions { if (!_user) { return @[]; } NSMutableArray<RLMSyncSession *> *buffer = [NSMutableArray array]; auto sessions = _user->all_sessions(); for (auto session : sessions) { [buffer addObject:[[RLMSyncSession alloc] initWithSyncSession:std::move(session)]]; } return [buffer copy]; } - (NSString *)identifier { if (!_user) { return @""; } return @(_user->identity().c_str()); } - (NSArray<RLMUserIdentity *> *)identities { if (!_user) { return @[]; } NSMutableArray<RLMUserIdentity *> *buffer = [NSMutableArray array]; auto identities = _user->identities(); for (auto& identity : identities) { [buffer addObject: [[RLMUserIdentity alloc] initUserIdentityWithProviderType:@(identity.provider_type.c_str()) identifier:@(identity.id.c_str())]]; } return [buffer copy]; } - (RLMUserState)state { if (!_user) { return RLMUserStateRemoved; } switch (_user->state()) { case SyncUser::State::LoggedIn: return RLMUserStateLoggedIn; case SyncUser::State::LoggedOut: return RLMUserStateLoggedOut; case SyncUser::State::Removed: return RLMUserStateRemoved; } } - (void)refreshCustomDataWithCompletion:(RLMUserCustomDataBlock)completion { _user->refresh_custom_data([completion, self](util::Optional<app::AppError> error) { if (!error) { return completion([self customData], nil); } completion(nil, RLMAppErrorToNSError(*error)); }); } - (void)linkUserWithCredentials:(RLMCredentials *)credentials completion:(RLMOptionalUserBlock)completion { _app._realmApp->link_user(_user, credentials.appCredentials, ^(std::shared_ptr<SyncUser> user, util::Optional<app::AppError> error) { if (error && error->error_code) { return completion(nil, RLMAppErrorToNSError(*error)); } completion([[RLMUser alloc] initWithUser:user app:_app], nil); }); } - (void)removeWithCompletion:(RLMOptionalErrorBlock)completion { _app._realmApp->remove_user(_user, ^(realm::util::Optional<app::AppError> error) { [self handleResponse:error completion:completion]; }); } - (void)logOutWithCompletion:(RLMOptionalErrorBlock)completion { _app._realmApp->log_out(_user, ^(realm::util::Optional<app::AppError> error) { [self handleResponse:error completion:completion]; }); } - (RLMAPIKeyAuth *)apiKeysAuth { return [[RLMAPIKeyAuth alloc] initWithApp: _app]; } - (RLMMongoClient *)mongoClientWithServiceName:(NSString *)serviceName { return [[RLMMongoClient alloc] initWithUser:self serviceName:serviceName]; } - (void)callFunctionNamed:(NSString *)name arguments:(NSArray<id<RLMBSON>> *)arguments completionBlock:(RLMCallFunctionCompletionBlock)completionBlock { bson::BsonArray args; for (id<RLMBSON> argument in arguments) { args.push_back(RLMConvertRLMBSONToBson(argument)); } _app._realmApp->call_function(_user, std::string(name.UTF8String), args, [completionBlock](util::Optional<app::AppError> error, util::Optional<bson::Bson> response) { if (error) { return completionBlock(nil, RLMAppErrorToNSError(*error)); } completionBlock(RLMConvertBsonToRLMBSON(*response), nil); }); } - (void)handleResponse:(realm::util::Optional<realm::app::AppError>)error completion:(RLMOptionalErrorBlock)completion { if (error && error->error_code) { return completion(RLMAppErrorToNSError(*error)); } completion(nil); } #pragma mark - Private API + (void)_setUpBindingContextFactory { SyncUser::set_binding_context_factory([] { return std::make_shared<CocoaSyncUserContext>(); }); } - (NSString *)refreshToken { if (!_user || _user->refresh_token().empty()) { return nil; } return @(_user->refresh_token().c_str()); } - (NSString *)accessToken { if (!_user || _user->access_token().empty()) { return nil; } return @(_user->access_token().c_str()); } - (NSDictionary *)customData { if (!_user || !_user->custom_data()) { return @{}; } return (NSDictionary *)RLMConvertBsonToRLMBSON(*_user->custom_data()); } - (std::shared_ptr<SyncUser>)_syncUser { return _user; } - (RLMUserSubscriptionToken *)subscribe:(RLMUserNotificationBlock) block { return [[RLMUserSubscriptionToken alloc] initWithToken:_user->subscribe([block, self] (auto&) { block(self); })]; } - (void)unsubscribe:(RLMUserSubscriptionToken *)token { _user->unsubscribe(*token->_token); } @end #pragma mark - RLMUserIdentity @implementation RLMUserIdentity - (instancetype)initUserIdentityWithProviderType:(NSString *)providerType identifier:(NSString *)identifier { if (self = [super init]) { _providerType = providerType; _identifier = identifier; } return self; } @end