RLMSyncUser.mm 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686
  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 "RLMSyncPermission.h"
  29. #import "RLMSyncSessionRefreshHandle.hpp"
  30. #import "RLMSyncSession_Private.hpp"
  31. #import "RLMSyncUtil_Private.hpp"
  32. #import "RLMUtil.hpp"
  33. #import "sync/sync_manager.hpp"
  34. #import "sync/sync_session.hpp"
  35. #import "sync/sync_user.hpp"
  36. using namespace realm;
  37. void CocoaSyncUserContext::register_refresh_handle(const std::string& path, RLMSyncSessionRefreshHandle *handle)
  38. {
  39. REALM_ASSERT(handle);
  40. std::lock_guard<std::mutex> lock(m_mutex);
  41. auto& refresh_handle = m_refresh_handles[path];
  42. [refresh_handle invalidate];
  43. refresh_handle = handle;
  44. }
  45. void CocoaSyncUserContext::unregister_refresh_handle(const std::string& path)
  46. {
  47. std::lock_guard<std::mutex> lock(m_mutex);
  48. m_refresh_handles.erase(path);
  49. }
  50. void CocoaSyncUserContext::invalidate_all_handles()
  51. {
  52. std::lock_guard<std::mutex> lock(m_mutex);
  53. for (auto& it : m_refresh_handles) {
  54. [it.second invalidate];
  55. }
  56. m_refresh_handles.clear();
  57. }
  58. RLMUserErrorReportingBlock CocoaSyncUserContext::error_handler() const
  59. {
  60. std::lock_guard<std::mutex> lock(m_error_handler_mutex);
  61. return m_error_handler;
  62. }
  63. void CocoaSyncUserContext::set_error_handler(RLMUserErrorReportingBlock block)
  64. {
  65. std::lock_guard<std::mutex> lock(m_error_handler_mutex);
  66. m_error_handler = block;
  67. }
  68. @interface RLMSyncUserInfo ()
  69. @property (nonatomic, readwrite) NSArray *accounts;
  70. @property (nonatomic, readwrite) NSDictionary *metadata;
  71. @property (nonatomic, readwrite) NSString *identity;
  72. @property (nonatomic, readwrite) BOOL isAdmin;
  73. + (instancetype)syncUserInfoWithModel:(RLMUserResponseModel *)model;
  74. @end
  75. @interface RLMSyncUser () {
  76. std::shared_ptr<SyncUser> _user;
  77. }
  78. @end
  79. @implementation RLMSyncUser
  80. #pragma mark - static API
  81. + (NSDictionary *)allUsers {
  82. NSArray *allUsers = [[RLMSyncManager sharedManager] _allUsers];
  83. return [NSDictionary dictionaryWithObjects:allUsers
  84. forKeys:[allUsers valueForKey:@"identity"]];
  85. }
  86. + (RLMSyncUser *)currentUser {
  87. NSArray *allUsers = [[RLMSyncManager sharedManager] _allUsers];
  88. if (allUsers.count > 1 && [NSSet setWithArray:[allUsers valueForKey:@"identity"]].count > 1) {
  89. @throw RLMException(@"+currentUser cannot be called if more that one valid, logged-in user exists.");
  90. }
  91. return allUsers.firstObject;
  92. }
  93. #pragma mark - API
  94. - (instancetype)initWithSyncUser:(std::shared_ptr<SyncUser>)user {
  95. if (self = [super init]) {
  96. _user = user;
  97. return self;
  98. }
  99. return nil;
  100. }
  101. - (BOOL)isEqual:(id)object {
  102. if (![object isKindOfClass:[RLMSyncUser class]]) {
  103. return NO;
  104. }
  105. return _user == ((RLMSyncUser *)object)->_user;
  106. }
  107. + (void)logInWithCredentials:(RLMSyncCredentials *)credential
  108. authServerURL:(NSURL *)authServerURL
  109. onCompletion:(RLMUserCompletionBlock)completion {
  110. [self logInWithCredentials:credential
  111. authServerURL:authServerURL
  112. timeout:0 // use timeout from RLMSyncManager
  113. callbackQueue:dispatch_get_main_queue()
  114. onCompletion:completion];
  115. }
  116. + (void)logInWithCredentials:(RLMSyncCredentials *)credentials
  117. authServerURL:(NSURL *)authServerURL
  118. timeout:(NSTimeInterval)timeout
  119. callbackQueue:(dispatch_queue_t)callbackQueue
  120. onCompletion:(RLMUserCompletionBlock)completion {
  121. // Special credential login should be treated differently.
  122. if (credentials.provider == RLMIdentityProviderAccessToken) {
  123. [self _performLoginForDirectAccessTokenCredentials:credentials
  124. authServerURL:authServerURL
  125. completionBlock:completion];
  126. return;
  127. }
  128. if (credentials.provider == RLMIdentityProviderCustomRefreshToken) {
  129. [self _performLoginForCustomRefreshTokenCredentials:credentials
  130. authServerURL:authServerURL
  131. completionBlock:completion];
  132. return;
  133. }
  134. if (!authServerURL) {
  135. @throw RLMException(@"A user cannot be logged in without specifying an authentication server URL.");
  136. }
  137. // Prepare login network request
  138. NSMutableDictionary *json = [@{
  139. kRLMSyncProviderKey: credentials.provider,
  140. kRLMSyncDataKey: credentials.token,
  141. kRLMSyncAppIDKey: RLMSyncManager.sharedManager.appID,
  142. } mutableCopy];
  143. if (credentials.userInfo.count) {
  144. // Munge user info into the JSON request.
  145. json[@"user_info"] = credentials.userInfo;
  146. }
  147. RLMSyncCompletionBlock handler = ^(NSError *error, NSDictionary *json) {
  148. if (error) {
  149. return completion(nil, error);
  150. }
  151. RLMAuthResponseModel *model = [[RLMAuthResponseModel alloc] initWithDictionary:json
  152. requireAccessToken:NO
  153. requireRefreshToken:YES];
  154. if (!model) {
  155. // Malformed JSON
  156. return completion(nil, make_auth_error_bad_response(json));
  157. }
  158. SyncUserIdentifier identity{model.refreshToken.tokenData.identity.UTF8String,
  159. authServerURL.absoluteString.UTF8String};
  160. auto sync_user = SyncManager::shared().get_user(identity , [model.refreshToken.token UTF8String]);
  161. if (!sync_user) {
  162. return completion(nil, make_auth_error_client_issue());
  163. }
  164. sync_user->set_is_admin(model.refreshToken.tokenData.isAdmin);
  165. return completion([[RLMSyncUser alloc] initWithSyncUser:std::move(sync_user)], nil);
  166. };
  167. [RLMSyncAuthEndpoint sendRequestToServer:authServerURL
  168. JSON:json
  169. timeout:timeout
  170. completion:^(NSError *error, NSDictionary *dictionary) {
  171. dispatch_async(callbackQueue, ^{
  172. handler(error, dictionary);
  173. });
  174. }];
  175. }
  176. - (RLMRealmConfiguration *)configuration {
  177. return [self configurationWithURL:nil
  178. fullSynchronization:NO
  179. enableSSLValidation:YES
  180. urlPrefix:nil];
  181. }
  182. - (RLMRealmConfiguration *)configurationWithURL:(NSURL *)url {
  183. return [self configurationWithURL:url
  184. fullSynchronization:NO
  185. enableSSLValidation:YES
  186. urlPrefix:nil];
  187. }
  188. - (RLMRealmConfiguration *)configurationWithURL:(NSURL *)url fullSynchronization:(bool)fullSynchronization {
  189. return [self configurationWithURL:url
  190. fullSynchronization:fullSynchronization
  191. enableSSLValidation:YES
  192. urlPrefix:nil];
  193. }
  194. - (RLMRealmConfiguration *)configurationWithURL:(NSURL *)url
  195. fullSynchronization:(bool)fullSynchronization
  196. enableSSLValidation:(bool)enableSSLValidation
  197. urlPrefix:(NSString * _Nullable)urlPrefix {
  198. auto syncConfig = [[RLMSyncConfiguration alloc] initWithUser:self
  199. realmURL:url ?: self.defaultRealmURL
  200. customFileURL:nil
  201. isPartial:!fullSynchronization
  202. stopPolicy:RLMSyncStopPolicyAfterChangesUploaded];
  203. syncConfig.urlPrefix = urlPrefix;
  204. syncConfig.enableSSLValidation = enableSSLValidation;
  205. syncConfig.pinnedCertificateURL = RLMSyncManager.sharedManager.pinnedCertificatePaths[syncConfig.realmURL.host];
  206. RLMRealmConfiguration *config = [[RLMRealmConfiguration alloc] init];
  207. config.syncConfiguration = syncConfig;
  208. return config;
  209. }
  210. - (void)logOut {
  211. if (!_user) {
  212. return;
  213. }
  214. _user->log_out();
  215. context_for(_user).invalidate_all_handles();
  216. }
  217. - (void)invalidate {
  218. if (!_user) {
  219. return;
  220. }
  221. context_for(_user).invalidate_all_handles();
  222. _user = nullptr;
  223. }
  224. - (RLMUserErrorReportingBlock)errorHandler {
  225. if (!_user) {
  226. return nil;
  227. }
  228. return context_for(_user).error_handler();
  229. }
  230. - (void)setErrorHandler:(RLMUserErrorReportingBlock)errorHandler {
  231. if (!_user) {
  232. return;
  233. }
  234. context_for(_user).set_error_handler([errorHandler copy]);
  235. }
  236. - (nullable RLMSyncSession *)sessionForURL:(NSURL *)url {
  237. if (!_user) {
  238. return nil;
  239. }
  240. auto path = SyncManager::shared().path_for_realm(*_user, [url.absoluteString UTF8String]);
  241. if (auto session = _user->session_for_on_disk_path(path)) {
  242. return [[RLMSyncSession alloc] initWithSyncSession:session];
  243. }
  244. return nil;
  245. }
  246. - (NSArray<RLMSyncSession *> *)allSessions {
  247. if (!_user) {
  248. return @[];
  249. }
  250. NSMutableArray<RLMSyncSession *> *buffer = [NSMutableArray array];
  251. auto sessions = _user->all_sessions();
  252. for (auto session : sessions) {
  253. [buffer addObject:[[RLMSyncSession alloc] initWithSyncSession:std::move(session)]];
  254. }
  255. return [buffer copy];
  256. }
  257. - (NSString *)identity {
  258. if (!_user) {
  259. return nil;
  260. }
  261. return @(_user->identity().c_str());
  262. }
  263. - (RLMSyncUserState)state {
  264. if (!_user) {
  265. return RLMSyncUserStateError;
  266. }
  267. switch (_user->state()) {
  268. case SyncUser::State::Active:
  269. return RLMSyncUserStateActive;
  270. case SyncUser::State::LoggedOut:
  271. return RLMSyncUserStateLoggedOut;
  272. case SyncUser::State::Error:
  273. return RLMSyncUserStateError;
  274. }
  275. }
  276. - (NSURL *)authenticationServer {
  277. if (!_user || _user->token_type() == SyncUser::TokenType::Admin) {
  278. return nil;
  279. }
  280. return [NSURL URLWithString:@(_user->server_url().c_str())];
  281. }
  282. - (BOOL)isAdmin {
  283. if (!_user) {
  284. return NO;
  285. }
  286. return _user->is_admin();
  287. }
  288. #pragma mark - Passwords
  289. - (void)changePassword:(NSString *)newPassword completion:(RLMPasswordChangeStatusBlock)completion {
  290. [self changePassword:newPassword forUserID:self.identity completion:completion];
  291. }
  292. - (void)changePassword:(NSString *)newPassword forUserID:(NSString *)userID completion:(RLMPasswordChangeStatusBlock)completion {
  293. if (self.state != RLMSyncUserStateActive) {
  294. completion([NSError errorWithDomain:RLMSyncErrorDomain
  295. code:RLMSyncErrorClientSessionError
  296. userInfo:nil]);
  297. return;
  298. }
  299. [RLMSyncChangePasswordEndpoint sendRequestToServer:self.authenticationServer
  300. JSON:@{kRLMSyncTokenKey: self.refreshToken,
  301. kRLMSyncUserIDKey: userID,
  302. kRLMSyncDataKey: @{kRLMSyncNewPasswordKey: newPassword}}
  303. completion:completion];
  304. }
  305. + (void)requestPasswordResetForAuthServer:(NSURL *)serverURL
  306. userEmail:(NSString *)email
  307. completion:(RLMPasswordChangeStatusBlock)completion {
  308. [RLMSyncUpdateAccountEndpoint sendRequestToServer:serverURL
  309. JSON:@{@"provider_id": email, @"data": @{@"action": @"reset_password"}}
  310. completion:completion];
  311. }
  312. + (void)completePasswordResetForAuthServer:(NSURL *)serverURL
  313. token:(NSString *)token
  314. password:(NSString *)newPassword
  315. completion:(RLMPasswordChangeStatusBlock)completion {
  316. [RLMSyncUpdateAccountEndpoint sendRequestToServer:serverURL
  317. JSON:@{@"data": @{@"action": @"complete_reset",
  318. @"token": token,
  319. @"new_password": newPassword}}
  320. completion:completion];
  321. }
  322. + (void)requestEmailConfirmationForAuthServer:(NSURL *)serverURL
  323. userEmail:(NSString *)email
  324. completion:(RLMPasswordChangeStatusBlock)completion {
  325. [RLMSyncUpdateAccountEndpoint sendRequestToServer:serverURL
  326. JSON:@{@"provider_id": email,
  327. @"data": @{@"action": @"request_email_confirmation"}}
  328. completion:completion];
  329. }
  330. + (void)confirmEmailForAuthServer:(NSURL *)serverURL
  331. token:(NSString *)token
  332. completion:(RLMPasswordChangeStatusBlock)completion {
  333. [RLMSyncUpdateAccountEndpoint sendRequestToServer:serverURL
  334. JSON:@{@"data": @{@"action": @"confirm_email",
  335. @"token": token}}
  336. completion:completion];
  337. }
  338. #pragma mark - Administrator API
  339. - (void)retrieveInfoForUser:(NSString *)providerUserIdentity
  340. identityProvider:(RLMIdentityProvider)provider
  341. completion:(RLMRetrieveUserBlock)completion {
  342. [RLMSyncGetUserInfoEndpoint sendRequestToServer:self.authenticationServer
  343. JSON:@{kRLMSyncProviderKey: provider,
  344. kRLMSyncProviderIDKey: providerUserIdentity,
  345. kRLMSyncTokenKey: self.refreshToken}
  346. timeout:60
  347. completion:^(NSError *error, NSDictionary *json) {
  348. if (error) {
  349. return completion(nil, error);
  350. }
  351. RLMUserResponseModel *model = [[RLMUserResponseModel alloc] initWithDictionary:json];
  352. if (!model) {
  353. return completion(nil, make_auth_error_bad_response(json));
  354. }
  355. completion([RLMSyncUserInfo syncUserInfoWithModel:model], nil);
  356. }];
  357. }
  358. #pragma mark - Permissions API
  359. namespace {
  360. NSError *checkUser(std::shared_ptr<SyncUser> const& user, NSString *msg) {
  361. if (user && user->state() != SyncUser::State::Error) {
  362. return nil;
  363. }
  364. msg = [NSString stringWithFormat:@"Permissions cannot be %@ using an invalid user.", msg];
  365. return [NSError errorWithDomain:RLMSyncPermissionErrorDomain code:RLMSyncAuthErrorInvalidParameters
  366. userInfo:@{NSLocalizedFailureReasonErrorKey: msg}];
  367. }
  368. }
  369. - (void)retrievePermissionsWithCallback:(RLMPermissionResultsBlock)callback {
  370. if (NSError *error = checkUser(_user, @"retrieved")) {
  371. callback(nullptr, error);
  372. return;
  373. }
  374. [RLMSyncGetPermissionsEndpoint
  375. sendRequestToServer:self.authenticationServer
  376. JSON:@{kRLMSyncTokenKey: self.refreshToken}
  377. timeout:60.0
  378. completion:^(NSError *error, NSDictionary *json) {
  379. if (error) {
  380. return callback(nil, error);
  381. }
  382. // FIXME: ROS currently gives duplicated results for 'all' due to an incorrect query
  383. NSMutableSet *permissions = [NSMutableSet new];
  384. for (NSDictionary *permission in json[@"permissions"]) {
  385. // ROS reports the permission for __wildcardpermissions, which we
  386. // don't want to include
  387. if ([permission[@"path"] hasPrefix:@"/__"]) {
  388. continue;
  389. }
  390. // Wildcard permissions are reported as a null userId
  391. id userId = permission[@"userId"];
  392. if (userId == NSNull.null) {
  393. userId = @"*";
  394. }
  395. [permissions addObject:[[RLMSyncPermission alloc]
  396. initWithRealmPath:permission[@"path"]
  397. identity:userId
  398. accessLevel:RLMSyncAccessLevelFromString(permission[@"accessLevel"])]];
  399. }
  400. callback(permissions.allObjects, nil);
  401. }];
  402. }
  403. - (void)applyPermission:(RLMSyncPermission *)permission callback:(RLMPermissionStatusBlock)callback {
  404. if (NSError *error = checkUser(_user, @"applied")) {
  405. callback(error);
  406. return;
  407. }
  408. id condition;
  409. if (permission.identity) {
  410. condition = @{@"userId": permission.identity};
  411. }
  412. else {
  413. condition = @{@"metadataKey": permission.key, @"metadataValue": permission.value};
  414. }
  415. [RLMSyncApplyPermissionsEndpoint
  416. sendRequestToServer:self.authenticationServer
  417. JSON:@{kRLMSyncTokenKey: self.refreshToken,
  418. @"condition": condition,
  419. @"realmPath": permission.path,
  420. @"accessLevel": RLMSyncAccessLevelToString(permission.accessLevel)}
  421. completion:callback];
  422. }
  423. - (void)createOfferForRealmAtURL:(NSURL *)url
  424. accessLevel:(RLMSyncAccessLevel)accessLevel
  425. expiration:(NSDate *)expirationDate
  426. callback:(RLMPermissionOfferStatusBlock)callback {
  427. if (NSError *error = checkUser(_user, @"offered")) {
  428. callback(nil, error);
  429. return;
  430. }
  431. NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
  432. dateFormatter.locale = [NSLocale localeWithLocaleIdentifier:@"en_US_POSIX"];
  433. dateFormatter.dateFormat = @"yyyy-MM-dd'T'HH:mm:ssZZZZZ";
  434. dateFormatter.calendar = [NSCalendar calendarWithIdentifier:NSCalendarIdentifierGregorian];
  435. [RLMSyncOfferPermissionsEndpoint
  436. sendRequestToServer:self.authenticationServer
  437. JSON:@{kRLMSyncTokenKey: self.refreshToken,
  438. @"expiresAt": expirationDate ? [RLMISO8601Formatter() stringFromDate:expirationDate] : NSNull.null,
  439. @"realmPath": url.path,
  440. @"accessLevel": RLMSyncAccessLevelToString(accessLevel)}
  441. timeout:60.0
  442. completion:^(NSError *error, NSDictionary *json) {
  443. callback(json[@"token"], error);
  444. }];
  445. }
  446. - (void)acceptOfferForToken:(NSString *)token
  447. callback:(RLMPermissionOfferResponseStatusBlock)callback {
  448. if (NSError *error = checkUser(_user, @"accepted")) {
  449. callback(nil, error);
  450. return;
  451. }
  452. [RLMSyncAcceptPermissionOfferEndpoint
  453. sendRequestToServer:self.authenticationServer
  454. JSON:@{kRLMSyncTokenKey: self.refreshToken, @"offerToken": token}
  455. timeout:60.0
  456. completion:^(NSError *error, NSDictionary *json) {
  457. callback([self urlForPath:json[@"path"]], error);
  458. }];
  459. }
  460. - (void)invalidateOfferForToken:(NSString *)token
  461. callback:(RLMPermissionStatusBlock)callback {
  462. if (NSError *error = checkUser(_user, @"invalidated")) {
  463. callback(error);
  464. return;
  465. }
  466. [RLMSyncInvalidatePermissionOfferEndpoint
  467. sendRequestToServer:self.authenticationServer
  468. JSON:@{kRLMSyncTokenKey: self.refreshToken, @"offerToken": token}
  469. timeout:60.0
  470. completion:^(NSError *error, NSDictionary *) {
  471. callback(error);
  472. }];
  473. }
  474. - (void)retrievePermissionOffersWithCallback:(RLMPermissionOfferResultsBlock)callback {
  475. if (NSError *error = checkUser(_user, @"retrieved")) {
  476. callback(nullptr, error);
  477. return;
  478. }
  479. [RLMSyncGetPermissionOffersEndpoint
  480. sendRequestToServer:self.authenticationServer
  481. JSON:@{kRLMSyncTokenKey: self.refreshToken}
  482. timeout:60.0
  483. completion:^(NSError *error, NSDictionary *json) {
  484. if (error) {
  485. return callback(nil, error);
  486. }
  487. NSMutableArray *offers = [NSMutableArray new];
  488. NSDateFormatter *formatter = RLMISO8601Formatter();
  489. for (NSDictionary *offer in json[@"offers"]) {
  490. NSString *expiresAt = RLMCoerceToNil(offer[@"expiresAt"]);
  491. NSString *createdAt = RLMCoerceToNil(offer[@"createdAt"]);
  492. [offers addObject:[[RLMSyncPermissionOffer alloc]
  493. initWithRealmPath:offer[@"realmPath"]
  494. token:offer[@"token"]
  495. expiresAt:expiresAt ? [formatter dateFromString:expiresAt] : nil
  496. createdAt:createdAt ? [formatter dateFromString:createdAt] : nil
  497. accessLevel:RLMSyncAccessLevelFromString(offer[@"accessLevel"])]];
  498. }
  499. callback(offers, nil);
  500. }];
  501. }
  502. #pragma mark - Private API
  503. - (NSURL *)urlForPath:(nullable NSString *)path {
  504. if (!path) {
  505. return nil;
  506. }
  507. NSURLComponents *components = [NSURLComponents componentsWithURL:self.authenticationServer resolvingAgainstBaseURL:YES];
  508. if ([components.scheme caseInsensitiveCompare:@"http"] == NSOrderedSame)
  509. components.scheme = @"realm";
  510. else if ([components.scheme caseInsensitiveCompare:@"https"] == NSOrderedSame)
  511. components.scheme = @"realms";
  512. else
  513. @throw RLMException(@"The provided user's authentication server URL (%@) was not valid.", self.authenticationServer);
  514. components.path = path;
  515. return components.URL;
  516. }
  517. - (NSURL *)defaultRealmURL {
  518. return [self urlForPath:@"/default"];
  519. }
  520. + (void)_setUpBindingContextFactory {
  521. SyncUser::set_binding_context_factory([] {
  522. return std::make_shared<CocoaSyncUserContext>();
  523. });
  524. }
  525. - (NSString *)refreshToken {
  526. if (!_user) {
  527. return nil;
  528. }
  529. return @(_user->refresh_token().c_str());
  530. }
  531. - (std::shared_ptr<SyncUser>)_syncUser {
  532. return _user;
  533. }
  534. + (void)_performLoginForDirectAccessTokenCredentials:(RLMSyncCredentials *)credentials
  535. authServerURL:(NSURL *)serverURL
  536. completionBlock:(nonnull RLMUserCompletionBlock)completion {
  537. NSString *identity = credentials.userInfo[kRLMSyncIdentityKey];
  538. std::shared_ptr<SyncUser> sync_user;
  539. if (serverURL) {
  540. NSString *scheme = serverURL.scheme;
  541. if (![scheme isEqualToString:@"http"] && ![scheme isEqualToString:@"https"]) {
  542. @throw RLMException(@"The Realm Object Server authentication URL provided for this user, \"%@\", "
  543. @" is invalid. It must begin with http:// or https://.", serverURL);
  544. }
  545. // Retrieve the user based on the auth server URL.
  546. util::Optional<std::string> identity_string;
  547. if (identity) {
  548. identity_string = std::string(identity.UTF8String);
  549. }
  550. sync_user = SyncManager::shared().get_admin_token_user([serverURL absoluteString].UTF8String,
  551. credentials.token.UTF8String,
  552. std::move(identity_string));
  553. } else {
  554. // Retrieve the user based on the identity.
  555. if (!identity) {
  556. @throw RLMException(@"A direct access credential must specify either an identity, a server URL, or both.");
  557. }
  558. sync_user = SyncManager::shared().get_admin_token_user_from_identity(identity.UTF8String,
  559. none,
  560. credentials.token.UTF8String);
  561. }
  562. if (!sync_user) {
  563. completion(nil, make_auth_error_client_issue());
  564. return;
  565. }
  566. completion([[RLMSyncUser alloc] initWithSyncUser:std::move(sync_user)], nil);
  567. }
  568. + (void)_performLoginForCustomRefreshTokenCredentials:(RLMSyncCredentials *)credentials
  569. authServerURL:(NSURL *)serverURL
  570. completionBlock:(nonnull RLMUserCompletionBlock)completion {
  571. NSString *scheme = serverURL.scheme;
  572. if (![scheme isEqualToString:@"http"] && ![scheme isEqualToString:@"https"]) {
  573. @throw RLMException(@"The Realm Object Server authentication URL provided for this user, \"%@\", "
  574. @" is invalid. It must begin with http:// or https://.", serverURL);
  575. }
  576. NSString *identity = credentials.userInfo[kRLMSyncIdentityKey];
  577. SyncUserIdentifier identifier{identity.UTF8String, serverURL.absoluteString.UTF8String};
  578. std::shared_ptr<SyncUser> sync_user = SyncManager::shared().get_user(std::move(identifier), credentials.token.UTF8String);
  579. if (!sync_user) {
  580. completion(nil, make_auth_error_client_issue());
  581. return;
  582. }
  583. NSNumber *isAdmin = credentials.userInfo[kRLMSyncIsAdminKey];
  584. sync_user->set_is_admin(isAdmin.boolValue);
  585. completion([[RLMSyncUser alloc] initWithSyncUser:std::move(sync_user)], nil);
  586. }
  587. @end
  588. #pragma mark - RLMSyncUserInfo
  589. @implementation RLMSyncUserInfo
  590. - (instancetype)initPrivate {
  591. return [super init];
  592. }
  593. + (instancetype)syncUserInfoWithModel:(RLMUserResponseModel *)model {
  594. RLMSyncUserInfo *info = [[RLMSyncUserInfo alloc] initPrivate];
  595. info.accounts = model.accounts;
  596. info.metadata = model.metadata;
  597. info.isAdmin = model.isAdmin;
  598. info.identity = model.identity;
  599. return info;
  600. }
  601. @end