RLMSyncUser.mm 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683
  1. ////////////////////////////////////////////////////////////////////////////
  2. //
  3. // Copyright 2016 Realm Inc.
  4. //
  5. // Licensed under the Apache License, Version 2.0 (the "License");
  6. // you may not use this file except in compliance with the License.
  7. // You may obtain a copy of the License at
  8. //
  9. // http://www.apache.org/licenses/LICENSE-2.0
  10. //
  11. // Unless required by applicable law or agreed to in writing, software
  12. // distributed under the License is distributed on an "AS IS" BASIS,
  13. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. // See the License for the specific language governing permissions and
  15. // limitations under the License.
  16. //
  17. ////////////////////////////////////////////////////////////////////////////
  18. #import "RLMSyncUser_Private.hpp"
  19. #import "RLMJSONModels.h"
  20. #import "RLMNetworkClient.h"
  21. #import "RLMRealmConfiguration+Sync.h"
  22. #import "RLMRealmConfiguration_Private.hpp"
  23. #import "RLMRealmUtil.hpp"
  24. #import "RLMResults_Private.hpp"
  25. #import "RLMSyncConfiguration.h"
  26. #import "RLMSyncConfiguration_Private.hpp"
  27. #import "RLMSyncManager_Private.h"
  28. #import "RLMSyncPermissionResults.h"
  29. #import "RLMSyncPermission_Private.hpp"
  30. #import "RLMSyncSessionRefreshHandle.hpp"
  31. #import "RLMSyncSession_Private.hpp"
  32. #import "RLMSyncUtil_Private.hpp"
  33. #import "RLMUtil.hpp"
  34. #import "sync/sync_manager.hpp"
  35. #import "sync/sync_session.hpp"
  36. #import "sync/sync_user.hpp"
  37. using namespace realm;
  38. using ConfigMaker = std::function<Realm::Config(std::shared_ptr<SyncUser>, std::string)>;
  39. namespace {
  40. std::function<void(Results, std::exception_ptr)> RLMWrapPermissionResultsCallback(RLMPermissionResultsBlock callback) {
  41. return [callback](Results results, std::exception_ptr ptr) {
  42. if (ptr) {
  43. NSError *error = translateSyncExceptionPtrToError(std::move(ptr), RLMPermissionActionTypeGet);
  44. REALM_ASSERT(error);
  45. callback(nil, error);
  46. } else {
  47. // Finished successfully
  48. callback([[RLMSyncPermissionResults alloc] initWithResults:std::move(results)], nil);
  49. }
  50. };
  51. }
  52. NSString *tildeSubstitutedPathForRealmURL(NSURL *url, NSString *identity) {
  53. return [[url path] stringByReplacingOccurrencesOfString:@"~" withString:identity];
  54. }
  55. }
  56. void CocoaSyncUserContext::register_refresh_handle(const std::string& path, RLMSyncSessionRefreshHandle *handle)
  57. {
  58. REALM_ASSERT(handle);
  59. std::lock_guard<std::mutex> lock(m_mutex);
  60. auto it = m_refresh_handles.find(path);
  61. if (it != m_refresh_handles.end()) {
  62. [it->second invalidate];
  63. m_refresh_handles.erase(it);
  64. }
  65. m_refresh_handles.insert({path, handle});
  66. }
  67. void CocoaSyncUserContext::unregister_refresh_handle(const std::string& path)
  68. {
  69. std::lock_guard<std::mutex> lock(m_mutex);
  70. m_refresh_handles.erase(path);
  71. }
  72. void CocoaSyncUserContext::invalidate_all_handles()
  73. {
  74. std::lock_guard<std::mutex> lock(m_mutex);
  75. for (auto& it : m_refresh_handles) {
  76. [it.second invalidate];
  77. }
  78. m_refresh_handles.clear();
  79. }
  80. RLMUserErrorReportingBlock CocoaSyncUserContext::error_handler() const
  81. {
  82. std::lock_guard<std::mutex> lock(m_error_handler_mutex);
  83. return m_error_handler;
  84. }
  85. void CocoaSyncUserContext::set_error_handler(RLMUserErrorReportingBlock block)
  86. {
  87. std::lock_guard<std::mutex> lock(m_error_handler_mutex);
  88. m_error_handler = block;
  89. }
  90. PermissionChangeCallback RLMWrapPermissionStatusCallback(RLMPermissionStatusBlock callback) {
  91. return [callback](std::exception_ptr ptr) {
  92. if (ptr) {
  93. NSError *error = translateSyncExceptionPtrToError(std::move(ptr), RLMPermissionActionTypeChange);
  94. REALM_ASSERT(error);
  95. callback(error);
  96. } else {
  97. // Finished successfully
  98. callback(nil);
  99. }
  100. };
  101. }
  102. @interface RLMSyncUserInfo ()
  103. @property (nonatomic, readwrite) NSArray *accounts;
  104. @property (nonatomic, readwrite) NSDictionary *metadata;
  105. @property (nonatomic, readwrite) NSString *identity;
  106. @property (nonatomic, readwrite) BOOL isAdmin;
  107. + (instancetype)syncUserInfoWithModel:(RLMUserResponseModel *)model;
  108. @end
  109. @interface RLMSyncUser () {
  110. std::shared_ptr<SyncUser> _user;
  111. // FIXME: remove this when the object store ConfigMaker goes away
  112. std::unique_ptr<ConfigMaker> _configMaker;
  113. }
  114. - (instancetype)initPrivate NS_DESIGNATED_INITIALIZER;
  115. @end
  116. @implementation RLMSyncUser
  117. #pragma mark - static API
  118. + (NSDictionary *)allUsers {
  119. NSArray *allUsers = [[RLMSyncManager sharedManager] _allUsers];
  120. return [NSDictionary dictionaryWithObjects:allUsers
  121. forKeys:[allUsers valueForKey:@"identity"]];
  122. }
  123. + (RLMSyncUser *)currentUser {
  124. NSArray *allUsers = [[RLMSyncManager sharedManager] _allUsers];
  125. if (allUsers.count > 1) {
  126. @throw RLMException(@"+currentUser cannot be called if more that one valid, logged-in user exists.");
  127. }
  128. return allUsers.firstObject;
  129. }
  130. #pragma mark - API
  131. - (instancetype)initPrivate {
  132. if (self = [super init]) {
  133. _configMaker = std::make_unique<ConfigMaker>([](std::shared_ptr<SyncUser> user, std::string url) {
  134. NSURL *objCUrl = [NSURL URLWithString:@(url.c_str())];
  135. RLMSyncUser *objCUser = [[RLMSyncUser alloc] initWithSyncUser:std::move(user)];
  136. return [objCUser configurationWithURL:objCUrl fullSynchronization:true].config;
  137. });
  138. return self;
  139. }
  140. return nil;
  141. }
  142. - (instancetype)initWithSyncUser:(std::shared_ptr<SyncUser>)user {
  143. if (self = [self initPrivate]) {
  144. _user = user;
  145. return self;
  146. }
  147. return nil;
  148. }
  149. - (BOOL)isEqual:(id)object {
  150. if (![object isKindOfClass:[RLMSyncUser class]]) {
  151. return NO;
  152. }
  153. return _user == ((RLMSyncUser *)object)->_user;
  154. }
  155. + (void)logInWithCredentials:(RLMSyncCredentials *)credential
  156. authServerURL:(NSURL *)authServerURL
  157. onCompletion:(RLMUserCompletionBlock)completion {
  158. [self logInWithCredentials:credential
  159. authServerURL:authServerURL
  160. timeout:30
  161. callbackQueue:dispatch_get_main_queue()
  162. onCompletion:completion];
  163. }
  164. + (void)logInWithCredentials:(RLMSyncCredentials *)credential
  165. authServerURL:(NSURL *)authServerURL
  166. timeout:(NSTimeInterval)timeout
  167. callbackQueue:(dispatch_queue_t)callbackQueue
  168. onCompletion:(RLMUserCompletionBlock)completion {
  169. RLMSyncUser *user = [[RLMSyncUser alloc] initPrivate];
  170. [RLMSyncUser _performLogInForUser:user
  171. credentials:credential
  172. authServerURL:authServerURL
  173. timeout:timeout
  174. callbackQueue:callbackQueue
  175. completionBlock:completion];
  176. }
  177. - (RLMRealmConfiguration *)configuration {
  178. return [self configurationWithURL:nil
  179. fullSynchronization:NO
  180. enableSSLValidation:YES
  181. urlPrefix:nil];
  182. }
  183. - (RLMRealmConfiguration *)configurationWithURL:(NSURL *)url {
  184. return [self configurationWithURL:url
  185. fullSynchronization:NO
  186. enableSSLValidation:YES
  187. urlPrefix:nil];
  188. }
  189. - (RLMRealmConfiguration *)configurationWithURL:(NSURL *)url fullSynchronization:(bool)fullSynchronization {
  190. return [self configurationWithURL:url
  191. fullSynchronization:fullSynchronization
  192. enableSSLValidation:YES
  193. urlPrefix:nil];
  194. }
  195. - (RLMRealmConfiguration *)configurationWithURL:(NSURL *)url
  196. fullSynchronization:(bool)fullSynchronization
  197. enableSSLValidation:(bool)enableSSLValidation
  198. urlPrefix:(NSString * _Nullable)urlPrefix {
  199. auto syncConfig = [[RLMSyncConfiguration alloc] initWithUser:self
  200. realmURL:url ?: self.defaultRealmURL
  201. customFileURL:nil
  202. isPartial:!fullSynchronization
  203. stopPolicy:RLMSyncStopPolicyAfterChangesUploaded
  204. errorHandler:nullptr];
  205. syncConfig.urlPrefix = urlPrefix;
  206. syncConfig.enableSSLValidation = enableSSLValidation;
  207. syncConfig.pinnedCertificateURL = RLMSyncManager.sharedManager.pinnedCertificatePaths[syncConfig.realmURL.host];
  208. RLMRealmConfiguration *config = [[RLMRealmConfiguration alloc] init];
  209. config.syncConfiguration = syncConfig;
  210. return config;
  211. }
  212. - (void)logOut {
  213. if (!_user) {
  214. return;
  215. }
  216. _user->log_out();
  217. context_for(_user).invalidate_all_handles();
  218. }
  219. - (RLMUserErrorReportingBlock)errorHandler {
  220. if (!_user) {
  221. return nil;
  222. }
  223. return context_for(_user).error_handler();
  224. }
  225. - (void)setErrorHandler:(RLMUserErrorReportingBlock)errorHandler {
  226. if (!_user) {
  227. return;
  228. }
  229. context_for(_user).set_error_handler([errorHandler copy]);
  230. }
  231. - (nullable RLMSyncSession *)sessionForURL:(NSURL *)url {
  232. if (!_user) {
  233. return nil;
  234. }
  235. auto path = SyncManager::shared().path_for_realm(*_user, [url.absoluteString UTF8String]);
  236. if (auto session = _user->session_for_on_disk_path(path)) {
  237. return [[RLMSyncSession alloc] initWithSyncSession:session];
  238. }
  239. return nil;
  240. }
  241. - (NSArray<RLMSyncSession *> *)allSessions {
  242. if (!_user) {
  243. return @[];
  244. }
  245. NSMutableArray<RLMSyncSession *> *buffer = [NSMutableArray array];
  246. auto sessions = _user->all_sessions();
  247. for (auto session : sessions) {
  248. [buffer addObject:[[RLMSyncSession alloc] initWithSyncSession:std::move(session)]];
  249. }
  250. return [buffer copy];
  251. }
  252. - (NSString *)identity {
  253. if (!_user) {
  254. return nil;
  255. }
  256. return @(_user->identity().c_str());
  257. }
  258. - (RLMSyncUserState)state {
  259. if (!_user) {
  260. return RLMSyncUserStateError;
  261. }
  262. switch (_user->state()) {
  263. case SyncUser::State::Active:
  264. return RLMSyncUserStateActive;
  265. case SyncUser::State::LoggedOut:
  266. return RLMSyncUserStateLoggedOut;
  267. case SyncUser::State::Error:
  268. return RLMSyncUserStateError;
  269. }
  270. }
  271. - (NSURL *)authenticationServer {
  272. if (!_user || _user->token_type() == SyncUser::TokenType::Admin) {
  273. return nil;
  274. }
  275. return [NSURL URLWithString:@(_user->server_url().c_str())];
  276. }
  277. - (BOOL)isAdmin {
  278. if (!_user) {
  279. return NO;
  280. }
  281. return _user->is_admin();
  282. }
  283. #pragma mark - Passwords
  284. - (void)changePassword:(NSString *)newPassword completion:(RLMPasswordChangeStatusBlock)completion {
  285. [self changePassword:newPassword forUserID:self.identity completion:completion];
  286. }
  287. - (void)changePassword:(NSString *)newPassword forUserID:(NSString *)userID completion:(RLMPasswordChangeStatusBlock)completion {
  288. if (self.state != RLMSyncUserStateActive) {
  289. completion([NSError errorWithDomain:RLMSyncErrorDomain
  290. code:RLMSyncErrorClientSessionError
  291. userInfo:nil]);
  292. return;
  293. }
  294. [RLMSyncChangePasswordEndpoint sendRequestToServer:self.authenticationServer
  295. JSON:@{kRLMSyncTokenKey: self.refreshToken,
  296. kRLMSyncUserIDKey: userID,
  297. kRLMSyncDataKey: @{kRLMSyncNewPasswordKey: newPassword}}
  298. options:[[RLMSyncManager sharedManager] networkRequestOptions]
  299. completion:completion];
  300. }
  301. + (void)requestPasswordResetForAuthServer:(NSURL *)serverURL
  302. userEmail:(NSString *)email
  303. completion:(RLMPasswordChangeStatusBlock)completion {
  304. [RLMSyncUpdateAccountEndpoint sendRequestToServer:serverURL
  305. JSON:@{@"provider_id": email, @"data": @{@"action": @"reset_password"}}
  306. options:[[RLMSyncManager sharedManager] networkRequestOptions]
  307. completion:completion];
  308. }
  309. + (void)completePasswordResetForAuthServer:(NSURL *)serverURL
  310. token:(NSString *)token
  311. password:(NSString *)newPassword
  312. completion:(RLMPasswordChangeStatusBlock)completion {
  313. [RLMSyncUpdateAccountEndpoint sendRequestToServer:serverURL
  314. JSON:@{@"data": @{@"action": @"complete_reset",
  315. @"token": token,
  316. @"new_password": newPassword}}
  317. options:[[RLMSyncManager sharedManager] networkRequestOptions]
  318. completion:completion];
  319. }
  320. + (void)requestEmailConfirmationForAuthServer:(NSURL *)serverURL
  321. userEmail:(NSString *)email
  322. completion:(RLMPasswordChangeStatusBlock)completion {
  323. [RLMSyncUpdateAccountEndpoint sendRequestToServer:serverURL
  324. JSON:@{@"provider_id": email, @"data": @{@"action": @"request_email_confirmation"}}
  325. options:[[RLMSyncManager sharedManager] networkRequestOptions]
  326. completion:completion];
  327. }
  328. + (void)confirmEmailForAuthServer:(NSURL *)serverURL
  329. token:(NSString *)token
  330. completion:(RLMPasswordChangeStatusBlock)completion {
  331. [RLMSyncUpdateAccountEndpoint sendRequestToServer:serverURL
  332. JSON:@{@"data": @{@"action": @"confirm_email",
  333. @"token": token}}
  334. options:[[RLMSyncManager sharedManager] networkRequestOptions]
  335. completion:completion];
  336. }
  337. #pragma mark - Administrator API
  338. - (void)retrieveInfoForUser:(NSString *)providerUserIdentity
  339. identityProvider:(RLMIdentityProvider)provider
  340. completion:(RLMRetrieveUserBlock)completion {
  341. [RLMNetworkClient sendRequestToEndpoint:[RLMSyncGetUserInfoEndpoint endpoint]
  342. server:self.authenticationServer
  343. JSON:@{
  344. kRLMSyncProviderKey: provider,
  345. kRLMSyncProviderIDKey: providerUserIdentity,
  346. kRLMSyncTokenKey: self.refreshToken
  347. }
  348. timeout:60
  349. options:[[RLMSyncManager sharedManager] networkRequestOptions]
  350. completion:^(NSError *error, NSDictionary *json) {
  351. if (error) {
  352. completion(nil, error);
  353. return;
  354. }
  355. RLMUserResponseModel *model = [[RLMUserResponseModel alloc] initWithDictionary:json];
  356. if (!model) {
  357. completion(nil, make_auth_error_bad_response(json));
  358. return;
  359. }
  360. completion([RLMSyncUserInfo syncUserInfoWithModel:model], nil);
  361. }];
  362. }
  363. #pragma mark - Permissions API
  364. static void verifyInRunLoop() {
  365. if (!RLMIsInRunLoop()) {
  366. @throw RLMException(@"Can only access or modify permissions from a thread which has a run loop (by default, only the main thread).");
  367. }
  368. }
  369. - (void)retrievePermissionsWithCallback:(RLMPermissionResultsBlock)callback {
  370. verifyInRunLoop();
  371. if (!_user || _user->state() == SyncUser::State::Error) {
  372. callback(nullptr, make_permission_error_get(@"Permissions cannot be retrieved using an invalid user."));
  373. return;
  374. }
  375. Permissions::get_permissions(_user, RLMWrapPermissionResultsCallback(callback), *_configMaker);
  376. }
  377. - (void)applyPermission:(RLMSyncPermission *)permission callback:(RLMPermissionStatusBlock)callback {
  378. verifyInRunLoop();
  379. if (!_user || _user->state() == SyncUser::State::Error) {
  380. callback(make_permission_error_change(@"Permissions cannot be applied using an invalid user."));
  381. return;
  382. }
  383. Permissions::set_permission(_user,
  384. [permission rawPermission],
  385. RLMWrapPermissionStatusCallback(callback),
  386. *_configMaker);
  387. }
  388. - (void)revokePermission:(RLMSyncPermission *)permission callback:(RLMPermissionStatusBlock)callback {
  389. verifyInRunLoop();
  390. if (!_user || _user->state() == SyncUser::State::Error) {
  391. callback(make_permission_error_change(@"Permissions cannot be revoked using an invalid user."));
  392. return;
  393. }
  394. Permissions::delete_permission(_user,
  395. [permission rawPermission],
  396. RLMWrapPermissionStatusCallback(callback),
  397. *_configMaker);
  398. }
  399. - (void)createOfferForRealmAtURL:(NSURL *)url
  400. accessLevel:(RLMSyncAccessLevel)accessLevel
  401. expiration:(NSDate *)expirationDate
  402. callback:(RLMPermissionOfferStatusBlock)callback {
  403. verifyInRunLoop();
  404. if (!_user || _user->state() == SyncUser::State::Error) {
  405. callback(nil, make_permission_error_change(@"A permission offer cannot be created using an invalid user."));
  406. return;
  407. }
  408. auto cb = [callback](util::Optional<std::string> token, std::exception_ptr ptr) {
  409. if (ptr) {
  410. NSError *error = translateSyncExceptionPtrToError(std::move(ptr), RLMPermissionActionTypeOffer);
  411. REALM_ASSERT_DEBUG(error);
  412. callback(nil, error);
  413. } else {
  414. REALM_ASSERT_DEBUG(token);
  415. callback(@(token->c_str()), nil);
  416. }
  417. };
  418. auto offer = PermissionOffer{
  419. [tildeSubstitutedPathForRealmURL(url, self.identity) UTF8String],
  420. accessLevelForObjCAccessLevel(accessLevel),
  421. RLMTimestampForNSDate(expirationDate),
  422. };
  423. Permissions::make_offer(_user, std::move(offer), std::move(cb), *_configMaker);
  424. }
  425. - (void)acceptOfferForToken:(NSString *)token
  426. callback:(RLMPermissionOfferResponseStatusBlock)callback {
  427. verifyInRunLoop();
  428. if (!_user || _user->state() == SyncUser::State::Error) {
  429. callback(nil, make_permission_error_change(@"A permission offer cannot be accepted by an invalid user."));
  430. return;
  431. }
  432. NSURLComponents *baseURL = [NSURLComponents componentsWithURL:self.authenticationServer
  433. resolvingAgainstBaseURL:YES];
  434. if ([baseURL.scheme isEqualToString:@"http"]) {
  435. baseURL.scheme = @"realm";
  436. } else if ([baseURL.scheme isEqualToString:@"https"]) {
  437. baseURL.scheme = @"realms";
  438. }
  439. auto cb = [baseURL, callback](util::Optional<std::string> raw_path, std::exception_ptr ptr) {
  440. if (ptr) {
  441. NSError *error = translateSyncExceptionPtrToError(std::move(ptr), RLMPermissionActionTypeAcceptOffer);
  442. REALM_ASSERT_DEBUG(error);
  443. callback(nil, error);
  444. } else {
  445. // Note that ROS currently vends the path to the Realm, so we need to construct the full URL ourselves.
  446. REALM_ASSERT_DEBUG(raw_path);
  447. baseURL.path = @(raw_path->c_str());
  448. callback([baseURL URL], nil);
  449. }
  450. };
  451. Permissions::accept_offer(_user, [token UTF8String], std::move(cb), *_configMaker);
  452. }
  453. #pragma mark - Private API
  454. - (NSURL *)defaultRealmURL
  455. {
  456. NSURLComponents *components = [NSURLComponents componentsWithURL:self.authenticationServer resolvingAgainstBaseURL:YES];
  457. if ([components.scheme caseInsensitiveCompare:@"http"] == NSOrderedSame)
  458. components.scheme = @"realm";
  459. else if ([components.scheme caseInsensitiveCompare:@"https"] == NSOrderedSame)
  460. components.scheme = @"realms";
  461. else
  462. @throw RLMException(@"The provided user's authentication server URL (%@) was not valid.", self.authenticationServer);
  463. components.path = @"/default";
  464. return components.URL;
  465. }
  466. + (void)_setUpBindingContextFactory {
  467. SyncUser::set_binding_context_factory([] {
  468. return std::make_shared<CocoaSyncUserContext>();
  469. });
  470. }
  471. - (NSString *)refreshToken {
  472. if (!_user) {
  473. return nil;
  474. }
  475. return @(_user->refresh_token().c_str());
  476. }
  477. - (std::shared_ptr<SyncUser>)_syncUser {
  478. return _user;
  479. }
  480. + (void)_performLogInForUser:(RLMSyncUser *)user
  481. credentials:(RLMSyncCredentials *)credentials
  482. authServerURL:(NSURL *)authServerURL
  483. timeout:(NSTimeInterval)timeout
  484. callbackQueue:(dispatch_queue_t)callbackQueue
  485. completionBlock:(RLMUserCompletionBlock)completion {
  486. // Special credential login should be treated differently.
  487. if (credentials.provider == RLMIdentityProviderAccessToken) {
  488. [self _performLoginForDirectAccessTokenCredentials:credentials
  489. user:user
  490. authServerURL:authServerURL
  491. completionBlock:completion];
  492. return;
  493. }
  494. if (!authServerURL) {
  495. @throw RLMException(@"A user cannot be logged in without specifying an authentication server URL.");
  496. }
  497. // Prepare login network request
  498. NSMutableDictionary *json = [@{
  499. kRLMSyncProviderKey: credentials.provider,
  500. kRLMSyncDataKey: credentials.token,
  501. kRLMSyncAppIDKey: [RLMSyncManager sharedManager].appID,
  502. } mutableCopy];
  503. NSMutableDictionary *info = [(credentials.userInfo ?: @{}) mutableCopy];
  504. if ([info count] > 0) {
  505. // Munge user info into the JSON request.
  506. json[@"user_info"] = info;
  507. }
  508. RLMSyncCompletionBlock handler = ^(NSError *error, NSDictionary *json) {
  509. if (json && !error) {
  510. RLMAuthResponseModel *model = [[RLMAuthResponseModel alloc] initWithDictionary:json
  511. requireAccessToken:NO
  512. requireRefreshToken:YES];
  513. if (!model) {
  514. // Malformed JSON
  515. NSError *badResponseError = make_auth_error_bad_response(json);
  516. dispatch_async(callbackQueue, ^{
  517. completion(nil, badResponseError);
  518. });
  519. return;
  520. } else {
  521. std::string server_url = authServerURL.absoluteString.UTF8String;
  522. SyncUserIdentifier identity{[model.refreshToken.tokenData.identity UTF8String], std::move(server_url)};
  523. auto sync_user = SyncManager::shared().get_user(identity , [model.refreshToken.token UTF8String]);
  524. if (!sync_user) {
  525. NSError *authError = make_auth_error_client_issue();
  526. dispatch_async(callbackQueue, ^{
  527. completion(nil, authError);
  528. });
  529. return;
  530. }
  531. sync_user->set_is_admin(model.refreshToken.tokenData.isAdmin);
  532. user->_user = sync_user;
  533. dispatch_async(callbackQueue, ^{
  534. completion(user, nil);
  535. });
  536. }
  537. } else {
  538. // Something else went wrong
  539. dispatch_async(callbackQueue, ^{
  540. completion(nil, error);
  541. });
  542. }
  543. };
  544. [RLMNetworkClient sendRequestToEndpoint:[RLMSyncAuthEndpoint endpoint]
  545. server:authServerURL
  546. JSON:json
  547. timeout:timeout
  548. options:[[RLMSyncManager sharedManager] networkRequestOptions]
  549. completion:^(NSError *error, NSDictionary *dictionary) {
  550. dispatch_async(callbackQueue, ^{
  551. handler(error, dictionary);
  552. });
  553. }];
  554. }
  555. + (void)_performLoginForDirectAccessTokenCredentials:(RLMSyncCredentials *)credentials
  556. user:(RLMSyncUser *)user
  557. authServerURL:(NSURL *)serverURL
  558. completionBlock:(nonnull RLMUserCompletionBlock)completion {
  559. NSString *identity = credentials.userInfo[kRLMSyncIdentityKey];
  560. std::shared_ptr<SyncUser> sync_user;
  561. if (serverURL) {
  562. NSString *scheme = serverURL.scheme;
  563. if (![scheme isEqualToString:@"http"] && ![scheme isEqualToString:@"https"]) {
  564. @throw RLMException(@"The Realm Object Server authentication URL provided for this user, \"%@\", "
  565. @" is invalid. It must begin with http:// or https://.", serverURL);
  566. }
  567. // Retrieve the user based on the auth server URL.
  568. util::Optional<std::string> identity_string;
  569. if (identity) {
  570. identity_string = std::string(identity.UTF8String);
  571. }
  572. sync_user = SyncManager::shared().get_admin_token_user([serverURL absoluteString].UTF8String,
  573. credentials.token.UTF8String,
  574. std::move(identity_string));
  575. } else {
  576. // Retrieve the user based on the identity.
  577. if (!identity) {
  578. @throw RLMException(@"A direct access credential must specify either an identity, a server URL, or both.");
  579. }
  580. sync_user = SyncManager::shared().get_admin_token_user_from_identity(identity.UTF8String,
  581. none,
  582. credentials.token.UTF8String);
  583. }
  584. if (!sync_user) {
  585. completion(nil, make_auth_error_client_issue());
  586. return;
  587. }
  588. user->_user = sync_user;
  589. completion(user, nil);
  590. }
  591. @end
  592. #pragma mark - RLMSyncUserInfo
  593. @implementation RLMSyncUserInfo
  594. - (instancetype)initPrivate {
  595. return [super init];
  596. }
  597. + (instancetype)syncUserInfoWithModel:(RLMUserResponseModel *)model {
  598. RLMSyncUserInfo *info = [[RLMSyncUserInfo alloc] initPrivate];
  599. info.accounts = model.accounts;
  600. info.metadata = model.metadata;
  601. info.isAdmin = model.isAdmin;
  602. info.identity = model.identity;
  603. return info;
  604. }
  605. @end