X-Git-Url: https://git.mdrn.pl/wl-app.git/blobdiff_plain/53b27422d140022594fc241cca91c3183be57bca..48b2fe9f7c2dc3d9aeaaa6dbfb27c7da4f3235ff:/iOS/Pods/Realm/Realm/RLMSyncUser.mm?ds=inline diff --git a/iOS/Pods/Realm/Realm/RLMSyncUser.mm b/iOS/Pods/Realm/Realm/RLMSyncUser.mm new file mode 100644 index 0000000..f843efb --- /dev/null +++ b/iOS/Pods/Realm/Realm/RLMSyncUser.mm @@ -0,0 +1,590 @@ +//////////////////////////////////////////////////////////////////////////// +// +// 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 "RLMSyncUser_Private.hpp" + +#import "RLMJSONModels.h" +#import "RLMNetworkClient.h" +#import "RLMRealmConfiguration+Sync.h" +#import "RLMRealmConfiguration_Private.hpp" +#import "RLMRealmUtil.hpp" +#import "RLMResults_Private.hpp" +#import "RLMSyncManager_Private.h" +#import "RLMSyncPermissionResults.h" +#import "RLMSyncPermission_Private.hpp" +#import "RLMSyncSessionRefreshHandle.hpp" +#import "RLMSyncSession_Private.hpp" +#import "RLMSyncUtil_Private.hpp" +#import "RLMUtil.hpp" + +#import "sync/sync_manager.hpp" +#import "sync/sync_session.hpp" +#import "sync/sync_user.hpp" + +using namespace realm; +using ConfigMaker = std::function, std::string)>; + +namespace { + +std::function RLMWrapPermissionResultsCallback(RLMPermissionResultsBlock callback) { + return [callback](Results results, std::exception_ptr ptr) { + if (ptr) { + NSError *error = translateSyncExceptionPtrToError(std::move(ptr), RLMPermissionActionTypeGet); + REALM_ASSERT(error); + callback(nil, error); + } else { + // Finished successfully + callback([[RLMSyncPermissionResults alloc] initWithResults:std::move(results)], nil); + } + }; +} + +NSString *tildeSubstitutedPathForRealmURL(NSURL *url, NSString *identity) { + return [[url path] stringByReplacingOccurrencesOfString:@"~" withString:identity]; +} + +} + +void CocoaSyncUserContext::register_refresh_handle(const std::string& path, RLMSyncSessionRefreshHandle *handle) +{ + REALM_ASSERT(handle); + std::lock_guard lock(m_mutex); + auto it = m_refresh_handles.find(path); + if (it != m_refresh_handles.end()) { + [it->second invalidate]; + m_refresh_handles.erase(it); + } + m_refresh_handles.insert({path, handle}); +} + +void CocoaSyncUserContext::unregister_refresh_handle(const std::string& path) +{ + std::lock_guard lock(m_mutex); + m_refresh_handles.erase(path); +} + +void CocoaSyncUserContext::invalidate_all_handles() +{ + std::lock_guard lock(m_mutex); + for (auto& it : m_refresh_handles) { + [it.second invalidate]; + } + m_refresh_handles.clear(); +} + +RLMUserErrorReportingBlock CocoaSyncUserContext::error_handler() const +{ + std::lock_guard lock(m_error_handler_mutex); + return m_error_handler; +} + +void CocoaSyncUserContext::set_error_handler(RLMUserErrorReportingBlock block) +{ + std::lock_guard lock(m_error_handler_mutex); + m_error_handler = block; +} + +PermissionChangeCallback RLMWrapPermissionStatusCallback(RLMPermissionStatusBlock callback) { + return [callback](std::exception_ptr ptr) { + if (ptr) { + NSError *error = translateSyncExceptionPtrToError(std::move(ptr), RLMPermissionActionTypeChange); + REALM_ASSERT(error); + callback(error); + } else { + // Finished successfully + callback(nil); + } + }; +} + +@interface RLMSyncUserInfo () + +@property (nonatomic, readwrite) NSArray *accounts; +@property (nonatomic, readwrite) NSDictionary *metadata; +@property (nonatomic, readwrite) NSString *identity; +@property (nonatomic, readwrite) BOOL isAdmin; + ++ (instancetype)syncUserInfoWithModel:(RLMUserResponseModel *)model; + +@end + +@interface RLMSyncUser () { + std::shared_ptr _user; + // FIXME: remove this when the object store ConfigMaker goes away + std::unique_ptr _configMaker; +} + +- (instancetype)initPrivate NS_DESIGNATED_INITIALIZER; + +@end + +@implementation RLMSyncUser + +#pragma mark - static API + ++ (NSDictionary *)allUsers { + NSArray *allUsers = [[RLMSyncManager sharedManager] _allUsers]; + return [NSDictionary dictionaryWithObjects:allUsers + forKeys:[allUsers valueForKey:@"identity"]]; +} + ++ (RLMSyncUser *)currentUser { + NSArray *allUsers = [[RLMSyncManager sharedManager] _allUsers]; + if (allUsers.count > 1) { + @throw RLMException(@"+currentUser cannot be called if more that one valid, logged-in user exists."); + } + return allUsers.firstObject; +} + +#pragma mark - API + +- (instancetype)initPrivate { + if (self = [super init]) { + _configMaker = std::make_unique([](std::shared_ptr user, std::string url) { + RLMRealmConfiguration *config = [RLMRealmConfiguration defaultConfiguration]; + NSURL *objCUrl = [NSURL URLWithString:@(url.c_str())]; + RLMSyncUser *objCUser = [[RLMSyncUser alloc] initWithSyncUser:std::move(user)]; + config.syncConfiguration = [[RLMSyncConfiguration alloc] initWithUser:objCUser realmURL:objCUrl]; + return [config config]; + }); + return self; + } + return nil; +} + +- (instancetype)initWithSyncUser:(std::shared_ptr)user { + if (self = [self initPrivate]) { + _user = user; + return self; + } + return nil; +} + +- (BOOL)isEqual:(id)object { + if (![object isKindOfClass:[RLMSyncUser class]]) { + return NO; + } + return _user == ((RLMSyncUser *)object)->_user; +} + ++ (void)logInWithCredentials:(RLMSyncCredentials *)credential + authServerURL:(NSURL *)authServerURL + onCompletion:(RLMUserCompletionBlock)completion { + [self logInWithCredentials:credential + authServerURL:authServerURL + timeout:30 + callbackQueue:dispatch_get_main_queue() + onCompletion:completion]; +} + ++ (void)logInWithCredentials:(RLMSyncCredentials *)credential + authServerURL:(NSURL *)authServerURL + timeout:(NSTimeInterval)timeout + callbackQueue:(dispatch_queue_t)callbackQueue + onCompletion:(RLMUserCompletionBlock)completion { + RLMSyncUser *user = [[RLMSyncUser alloc] initPrivate]; + [RLMSyncUser _performLogInForUser:user + credentials:credential + authServerURL:authServerURL + timeout:timeout + callbackQueue:callbackQueue + completionBlock:completion]; +} + +- (void)logOut { + if (!_user) { + return; + } + _user->log_out(); + context_for(_user).invalidate_all_handles(); +} + +- (RLMUserErrorReportingBlock)errorHandler { + if (!_user) { + return nil; + } + return context_for(_user).error_handler(); +} + +- (void)setErrorHandler:(RLMUserErrorReportingBlock)errorHandler { + if (!_user) { + return; + } + context_for(_user).set_error_handler([errorHandler copy]); +} + +- (nullable RLMSyncSession *)sessionForURL:(NSURL *)url { + if (!_user) { + return nil; + } + auto path = SyncManager::shared().path_for_realm(*_user, [url.absoluteString UTF8String]); + if (auto session = _user->session_for_on_disk_path(path)) { + return [[RLMSyncSession alloc] initWithSyncSession:session]; + } + return nil; +} + +- (NSArray *)allSessions { + if (!_user) { + return @[]; + } + NSMutableArray *buffer = [NSMutableArray array]; + auto sessions = _user->all_sessions(); + for (auto session : sessions) { + [buffer addObject:[[RLMSyncSession alloc] initWithSyncSession:std::move(session)]]; + } + return [buffer copy]; +} + +- (NSString *)identity { + if (!_user) { + return nil; + } + return @(_user->identity().c_str()); +} + +- (RLMSyncUserState)state { + if (!_user) { + return RLMSyncUserStateError; + } + switch (_user->state()) { + case SyncUser::State::Active: + return RLMSyncUserStateActive; + case SyncUser::State::LoggedOut: + return RLMSyncUserStateLoggedOut; + case SyncUser::State::Error: + return RLMSyncUserStateError; + } +} + +- (NSURL *)authenticationServer { + if (!_user || _user->token_type() == SyncUser::TokenType::Admin) { + return nil; + } + return [NSURL URLWithString:@(_user->server_url().c_str())]; +} + +- (BOOL)isAdmin { + if (!_user) { + return NO; + } + return _user->is_admin(); +} + +#pragma mark - Passwords + +- (void)changePassword:(NSString *)newPassword completion:(RLMPasswordChangeStatusBlock)completion { + [self changePassword:newPassword forUserID:self.identity completion:completion]; +} + +- (void)changePassword:(NSString *)newPassword forUserID:(NSString *)userID completion:(RLMPasswordChangeStatusBlock)completion { + if (self.state != RLMSyncUserStateActive) { + completion([NSError errorWithDomain:RLMSyncErrorDomain + code:RLMSyncErrorClientSessionError + userInfo:nil]); + return; + } + [RLMNetworkClient sendRequestToEndpoint:[RLMSyncChangePasswordEndpoint endpoint] + server:self.authenticationServer + JSON:@{kRLMSyncTokenKey: self._refreshToken, + kRLMSyncUserIDKey: userID, + kRLMSyncDataKey: @{ kRLMSyncNewPasswordKey: newPassword } + } + timeout:60 + completion:^(NSError *error, __unused NSDictionary *json) { + completion(error); + }]; +} + +#pragma mark - Administrator API + +- (void)retrieveInfoForUser:(NSString *)providerUserIdentity + identityProvider:(RLMIdentityProvider)provider + completion:(RLMRetrieveUserBlock)completion { + [RLMNetworkClient sendRequestToEndpoint:[RLMSyncGetUserInfoEndpoint endpoint] + server:self.authenticationServer + JSON:@{ + kRLMSyncProviderKey: provider, + kRLMSyncProviderIDKey: providerUserIdentity, + kRLMSyncTokenKey: self._refreshToken + } + completion:^(NSError *error, NSDictionary *json) { + if (error) { + completion(nil, error); + return; + } + RLMUserResponseModel *model = [[RLMUserResponseModel alloc] initWithDictionary:json]; + if (!model) { + completion(nil, make_auth_error_bad_response(json)); + return; + } + completion([RLMSyncUserInfo syncUserInfoWithModel:model], nil); + }]; +} + +#pragma mark - Permissions API + +static void verifyInRunLoop() { + if (!RLMIsInRunLoop()) { + @throw RLMException(@"Can only access or modify permissions from a thread which has a run loop (by default, only the main thread)."); + } +} + +- (void)retrievePermissionsWithCallback:(RLMPermissionResultsBlock)callback { + verifyInRunLoop(); + if (!_user || _user->state() == SyncUser::State::Error) { + callback(nullptr, make_permission_error_get(@"Permissions cannot be retrieved using an invalid user.")); + return; + } + Permissions::get_permissions(_user, RLMWrapPermissionResultsCallback(callback), *_configMaker); +} + +- (void)applyPermission:(RLMSyncPermission *)permission callback:(RLMPermissionStatusBlock)callback { + verifyInRunLoop(); + if (!_user || _user->state() == SyncUser::State::Error) { + callback(make_permission_error_change(@"Permissions cannot be applied using an invalid user.")); + return; + } + Permissions::set_permission(_user, + [permission rawPermission], + RLMWrapPermissionStatusCallback(callback), + *_configMaker); +} + +- (void)revokePermission:(RLMSyncPermission *)permission callback:(RLMPermissionStatusBlock)callback { + verifyInRunLoop(); + if (!_user || _user->state() == SyncUser::State::Error) { + callback(make_permission_error_change(@"Permissions cannot be revoked using an invalid user.")); + return; + } + Permissions::delete_permission(_user, + [permission rawPermission], + RLMWrapPermissionStatusCallback(callback), + *_configMaker); +} + +- (void)createOfferForRealmAtURL:(NSURL *)url + accessLevel:(RLMSyncAccessLevel)accessLevel + expiration:(NSDate *)expirationDate + callback:(RLMPermissionOfferStatusBlock)callback { + verifyInRunLoop(); + if (!_user || _user->state() == SyncUser::State::Error) { + callback(nil, make_permission_error_change(@"A permission offer cannot be created using an invalid user.")); + return; + } + auto cb = [callback](util::Optional token, std::exception_ptr ptr) { + if (ptr) { + NSError *error = translateSyncExceptionPtrToError(std::move(ptr), RLMPermissionActionTypeOffer); + REALM_ASSERT_DEBUG(error); + callback(nil, error); + } else { + REALM_ASSERT_DEBUG(token); + callback(@(token->c_str()), nil); + } + }; + auto offer = PermissionOffer{ + [tildeSubstitutedPathForRealmURL(url, self.identity) UTF8String], + accessLevelForObjCAccessLevel(accessLevel), + RLMTimestampForNSDate(expirationDate), + }; + Permissions::make_offer(_user, std::move(offer), std::move(cb), *_configMaker); +} + +- (void)acceptOfferForToken:(NSString *)token + callback:(RLMPermissionOfferResponseStatusBlock)callback { + verifyInRunLoop(); + if (!_user || _user->state() == SyncUser::State::Error) { + callback(nil, make_permission_error_change(@"A permission offer cannot be accepted by an invalid user.")); + return; + } + NSURLComponents *baseURL = [NSURLComponents componentsWithURL:self.authenticationServer + resolvingAgainstBaseURL:YES]; + if ([baseURL.scheme isEqualToString:@"http"]) { + baseURL.scheme = @"realm"; + } else if ([baseURL.scheme isEqualToString:@"https"]) { + baseURL.scheme = @"realms"; + } + auto cb = [baseURL, callback](util::Optional raw_path, std::exception_ptr ptr) { + if (ptr) { + NSError *error = translateSyncExceptionPtrToError(std::move(ptr), RLMPermissionActionTypeAcceptOffer); + REALM_ASSERT_DEBUG(error); + callback(nil, error); + } else { + // Note that ROS currently vends the path to the Realm, so we need to construct the full URL ourselves. + REALM_ASSERT_DEBUG(raw_path); + baseURL.path = @(raw_path->c_str()); + callback([baseURL URL], nil); + } + }; + Permissions::accept_offer(_user, [token UTF8String], std::move(cb), *_configMaker); +} + +#pragma mark - Private API + ++ (void)_setUpBindingContextFactory { + SyncUser::set_binding_context_factory([] { + return std::make_shared(); + }); +} + +- (NSString *)_refreshToken { + if (!_user) { + return nil; + } + return @(_user->refresh_token().c_str()); +} + +- (std::shared_ptr)_syncUser { + return _user; +} + ++ (void)_performLogInForUser:(RLMSyncUser *)user + credentials:(RLMSyncCredentials *)credentials + authServerURL:(NSURL *)authServerURL + timeout:(NSTimeInterval)timeout + callbackQueue:(dispatch_queue_t)callbackQueue + completionBlock:(RLMUserCompletionBlock)completion { + // Special credential login should be treated differently. + if (credentials.provider == RLMIdentityProviderAccessToken) { + [self _performLoginForDirectAccessTokenCredentials:credentials + user:user + authServerURL:authServerURL + completionBlock:completion]; + return; + } + if (!authServerURL) { + @throw RLMException(@"A user cannot be logged in without specifying an authentication server URL."); + } + + // Prepare login network request + NSMutableDictionary *json = [@{ + kRLMSyncProviderKey: credentials.provider, + kRLMSyncDataKey: credentials.token, + kRLMSyncAppIDKey: [RLMSyncManager sharedManager].appID, + } mutableCopy]; + NSMutableDictionary *info = [(credentials.userInfo ?: @{}) mutableCopy]; + + if ([info count] > 0) { + // Munge user info into the JSON request. + json[@"user_info"] = info; + } + + RLMSyncCompletionBlock handler = ^(NSError *error, NSDictionary *json) { + if (json && !error) { + RLMAuthResponseModel *model = [[RLMAuthResponseModel alloc] initWithDictionary:json + requireAccessToken:NO + requireRefreshToken:YES]; + if (!model) { + // Malformed JSON + NSError *badResponseError = make_auth_error_bad_response(json); + dispatch_async(callbackQueue, ^{ + completion(nil, badResponseError); + }); + return; + } else { + std::string server_url = authServerURL.absoluteString.UTF8String; + SyncUserIdentifier identity{[model.refreshToken.tokenData.identity UTF8String], std::move(server_url)}; + auto sync_user = SyncManager::shared().get_user(identity , [model.refreshToken.token UTF8String]); + if (!sync_user) { + NSError *authError = make_auth_error_client_issue(); + dispatch_async(callbackQueue, ^{ + completion(nil, authError); + }); + return; + } + sync_user->set_is_admin(model.refreshToken.tokenData.isAdmin); + user->_user = sync_user; + dispatch_async(callbackQueue, ^{ + completion(user, nil); + }); + } + } else { + // Something else went wrong + dispatch_async(callbackQueue, ^{ + completion(nil, error); + }); + } + }; + [RLMNetworkClient sendRequestToEndpoint:[RLMSyncAuthEndpoint endpoint] + server:authServerURL + JSON:json + timeout:timeout + completion:^(NSError *error, NSDictionary *dictionary) { + dispatch_async(callbackQueue, ^{ + handler(error, dictionary); + }); + }]; +} + ++ (void)_performLoginForDirectAccessTokenCredentials:(RLMSyncCredentials *)credentials + user:(RLMSyncUser *)user + authServerURL:(NSURL *)serverURL + completionBlock:(nonnull RLMUserCompletionBlock)completion { + NSString *identity = credentials.userInfo[kRLMSyncIdentityKey]; + std::shared_ptr sync_user; + if (serverURL) { + NSString *scheme = serverURL.scheme; + if (![scheme isEqualToString:@"http"] && ![scheme isEqualToString:@"https"]) { + @throw RLMException(@"The Realm Object Server authentication URL provided for this user, \"%@\", " + @" is invalid. It must begin with http:// or https://.", serverURL); + } + // Retrieve the user based on the auth server URL. + util::Optional identity_string; + if (identity) { + identity_string = std::string(identity.UTF8String); + } + sync_user = SyncManager::shared().get_admin_token_user([serverURL absoluteString].UTF8String, + credentials.token.UTF8String, + std::move(identity_string)); + } else { + // Retrieve the user based on the identity. + if (!identity) { + @throw RLMException(@"A direct access credential must specify either an identity, a server URL, or both."); + } + sync_user = SyncManager::shared().get_admin_token_user_from_identity(identity.UTF8String, + none, + credentials.token.UTF8String); + } + if (!sync_user) { + completion(nil, make_auth_error_client_issue()); + return; + } + user->_user = sync_user; + completion(user, nil); +} + +@end + +#pragma mark - RLMSyncUserInfo + +@implementation RLMSyncUserInfo + +- (instancetype)initPrivate { + return [super init]; +} + ++ (instancetype)syncUserInfoWithModel:(RLMUserResponseModel *)model { + RLMSyncUserInfo *info = [[RLMSyncUserInfo alloc] initPrivate]; + info.accounts = model.accounts; + info.metadata = model.metadata; + info.isAdmin = model.isAdmin; + info.identity = model.identity; + return info; +} + +@end