You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
683 lines
27 KiB
683 lines
27 KiB
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// 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 "RLMSyncConfiguration.h"
|
|
#import "RLMSyncConfiguration_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<Realm::Config(std::shared_ptr<SyncUser>, std::string)>;
|
|
|
|
namespace {
|
|
|
|
std::function<void(Results, std::exception_ptr)> 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<std::mutex> 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<std::mutex> lock(m_mutex);
|
|
m_refresh_handles.erase(path);
|
|
}
|
|
|
|
void CocoaSyncUserContext::invalidate_all_handles()
|
|
{
|
|
std::lock_guard<std::mutex> lock(m_mutex);
|
|
for (auto& it : m_refresh_handles) {
|
|
[it.second invalidate];
|
|
}
|
|
m_refresh_handles.clear();
|
|
}
|
|
|
|
RLMUserErrorReportingBlock CocoaSyncUserContext::error_handler() const
|
|
{
|
|
std::lock_guard<std::mutex> lock(m_error_handler_mutex);
|
|
return m_error_handler;
|
|
}
|
|
|
|
void CocoaSyncUserContext::set_error_handler(RLMUserErrorReportingBlock block)
|
|
{
|
|
std::lock_guard<std::mutex> 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<SyncUser> _user;
|
|
// FIXME: remove this when the object store ConfigMaker goes away
|
|
std::unique_ptr<ConfigMaker> _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<ConfigMaker>([](std::shared_ptr<SyncUser> user, std::string url) {
|
|
NSURL *objCUrl = [NSURL URLWithString:@(url.c_str())];
|
|
RLMSyncUser *objCUser = [[RLMSyncUser alloc] initWithSyncUser:std::move(user)];
|
|
return [objCUser configurationWithURL:objCUrl fullSynchronization:true].config;
|
|
});
|
|
return self;
|
|
}
|
|
return nil;
|
|
}
|
|
|
|
- (instancetype)initWithSyncUser:(std::shared_ptr<SyncUser>)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];
|
|
}
|
|
|
|
- (RLMRealmConfiguration *)configuration {
|
|
return [self configurationWithURL:nil
|
|
fullSynchronization:NO
|
|
enableSSLValidation:YES
|
|
urlPrefix:nil];
|
|
}
|
|
|
|
- (RLMRealmConfiguration *)configurationWithURL:(NSURL *)url {
|
|
return [self configurationWithURL:url
|
|
fullSynchronization:NO
|
|
enableSSLValidation:YES
|
|
urlPrefix:nil];
|
|
}
|
|
|
|
- (RLMRealmConfiguration *)configurationWithURL:(NSURL *)url fullSynchronization:(bool)fullSynchronization {
|
|
return [self configurationWithURL:url
|
|
fullSynchronization:fullSynchronization
|
|
enableSSLValidation:YES
|
|
urlPrefix:nil];
|
|
}
|
|
|
|
- (RLMRealmConfiguration *)configurationWithURL:(NSURL *)url
|
|
fullSynchronization:(bool)fullSynchronization
|
|
enableSSLValidation:(bool)enableSSLValidation
|
|
urlPrefix:(NSString * _Nullable)urlPrefix {
|
|
auto syncConfig = [[RLMSyncConfiguration alloc] initWithUser:self
|
|
realmURL:url ?: self.defaultRealmURL
|
|
customFileURL:nil
|
|
isPartial:!fullSynchronization
|
|
stopPolicy:RLMSyncStopPolicyAfterChangesUploaded
|
|
errorHandler:nullptr];
|
|
syncConfig.urlPrefix = urlPrefix;
|
|
syncConfig.enableSSLValidation = enableSSLValidation;
|
|
syncConfig.pinnedCertificateURL = RLMSyncManager.sharedManager.pinnedCertificatePaths[syncConfig.realmURL.host];
|
|
RLMRealmConfiguration *config = [[RLMRealmConfiguration alloc] init];
|
|
config.syncConfiguration = syncConfig;
|
|
return config;
|
|
}
|
|
|
|
- (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<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 *)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;
|
|
}
|
|
[RLMSyncChangePasswordEndpoint sendRequestToServer:self.authenticationServer
|
|
JSON:@{kRLMSyncTokenKey: self.refreshToken,
|
|
kRLMSyncUserIDKey: userID,
|
|
kRLMSyncDataKey: @{kRLMSyncNewPasswordKey: newPassword}}
|
|
options:[[RLMSyncManager sharedManager] networkRequestOptions]
|
|
completion:completion];
|
|
}
|
|
|
|
+ (void)requestPasswordResetForAuthServer:(NSURL *)serverURL
|
|
userEmail:(NSString *)email
|
|
completion:(RLMPasswordChangeStatusBlock)completion {
|
|
[RLMSyncUpdateAccountEndpoint sendRequestToServer:serverURL
|
|
JSON:@{@"provider_id": email, @"data": @{@"action": @"reset_password"}}
|
|
options:[[RLMSyncManager sharedManager] networkRequestOptions]
|
|
completion:completion];
|
|
}
|
|
|
|
+ (void)completePasswordResetForAuthServer:(NSURL *)serverURL
|
|
token:(NSString *)token
|
|
password:(NSString *)newPassword
|
|
completion:(RLMPasswordChangeStatusBlock)completion {
|
|
[RLMSyncUpdateAccountEndpoint sendRequestToServer:serverURL
|
|
JSON:@{@"data": @{@"action": @"complete_reset",
|
|
@"token": token,
|
|
@"new_password": newPassword}}
|
|
options:[[RLMSyncManager sharedManager] networkRequestOptions]
|
|
completion:completion];
|
|
}
|
|
|
|
+ (void)requestEmailConfirmationForAuthServer:(NSURL *)serverURL
|
|
userEmail:(NSString *)email
|
|
completion:(RLMPasswordChangeStatusBlock)completion {
|
|
[RLMSyncUpdateAccountEndpoint sendRequestToServer:serverURL
|
|
JSON:@{@"provider_id": email, @"data": @{@"action": @"request_email_confirmation"}}
|
|
options:[[RLMSyncManager sharedManager] networkRequestOptions]
|
|
completion:completion];
|
|
}
|
|
|
|
+ (void)confirmEmailForAuthServer:(NSURL *)serverURL
|
|
token:(NSString *)token
|
|
completion:(RLMPasswordChangeStatusBlock)completion {
|
|
[RLMSyncUpdateAccountEndpoint sendRequestToServer:serverURL
|
|
JSON:@{@"data": @{@"action": @"confirm_email",
|
|
@"token": token}}
|
|
options:[[RLMSyncManager sharedManager] networkRequestOptions]
|
|
completion:completion];
|
|
}
|
|
|
|
#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
|
|
}
|
|
timeout:60
|
|
options:[[RLMSyncManager sharedManager] networkRequestOptions]
|
|
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<std::string> 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<std::string> 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
|
|
|
|
- (NSURL *)defaultRealmURL
|
|
{
|
|
NSURLComponents *components = [NSURLComponents componentsWithURL:self.authenticationServer resolvingAgainstBaseURL:YES];
|
|
if ([components.scheme caseInsensitiveCompare:@"http"] == NSOrderedSame)
|
|
components.scheme = @"realm";
|
|
else if ([components.scheme caseInsensitiveCompare:@"https"] == NSOrderedSame)
|
|
components.scheme = @"realms";
|
|
else
|
|
@throw RLMException(@"The provided user's authentication server URL (%@) was not valid.", self.authenticationServer);
|
|
|
|
components.path = @"/default";
|
|
return components.URL;
|
|
}
|
|
|
|
+ (void)_setUpBindingContextFactory {
|
|
SyncUser::set_binding_context_factory([] {
|
|
return std::make_shared<CocoaSyncUserContext>();
|
|
});
|
|
}
|
|
|
|
- (NSString *)refreshToken {
|
|
if (!_user) {
|
|
return nil;
|
|
}
|
|
return @(_user->refresh_token().c_str());
|
|
}
|
|
|
|
- (std::shared_ptr<SyncUser>)_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
|
|
options:[[RLMSyncManager sharedManager] networkRequestOptions]
|
|
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<SyncUser> 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<std::string> 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
|
|
|