RLMSyncUser.mm 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687
  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:30
  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. errorHandler:nullptr];
  204. syncConfig.urlPrefix = urlPrefix;
  205. syncConfig.enableSSLValidation = enableSSLValidation;
  206. syncConfig.pinnedCertificateURL = RLMSyncManager.sharedManager.pinnedCertificatePaths[syncConfig.realmURL.host];
  207. RLMRealmConfiguration *config = [[RLMRealmConfiguration alloc] init];
  208. config.syncConfiguration = syncConfig;
  209. return config;
  210. }
  211. - (void)logOut {
  212. if (!_user) {
  213. return;
  214. }
  215. _user->log_out();
  216. context_for(_user).invalidate_all_handles();
  217. }
  218. - (void)invalidate {
  219. if (!_user) {
  220. return;
  221. }
  222. context_for(_user).invalidate_all_handles();
  223. _user = nullptr;
  224. }
  225. - (RLMUserErrorReportingBlock)errorHandler {
  226. if (!_user) {
  227. return nil;
  228. }
  229. return context_for(_user).error_handler();
  230. }
  231. - (void)setErrorHandler:(RLMUserErrorReportingBlock)errorHandler {
  232. if (!_user) {
  233. return;
  234. }
  235. context_for(_user).set_error_handler([errorHandler copy]);
  236. }
  237. - (nullable RLMSyncSession *)sessionForURL:(NSURL *)url {
  238. if (!_user) {
  239. return nil;
  240. }
  241. auto path = SyncManager::shared().path_for_realm(*_user, [url.absoluteString UTF8String]);
  242. if (auto session = _user->session_for_on_disk_path(path)) {
  243. return [[RLMSyncSession alloc] initWithSyncSession:session];
  244. }
  245. return nil;
  246. }
  247. - (NSArray<RLMSyncSession *> *)allSessions {
  248. if (!_user) {
  249. return @[];
  250. }
  251. NSMutableArray<RLMSyncSession *> *buffer = [NSMutableArray array];
  252. auto sessions = _user->all_sessions();
  253. for (auto session : sessions) {
  254. [buffer addObject:[[RLMSyncSession alloc] initWithSyncSession:std::move(session)]];
  255. }
  256. return [buffer copy];
  257. }
  258. - (NSString *)identity {
  259. if (!_user) {
  260. return nil;
  261. }
  262. return @(_user->identity().c_str());
  263. }
  264. - (RLMSyncUserState)state {
  265. if (!_user) {
  266. return RLMSyncUserStateError;
  267. }
  268. switch (_user->state()) {
  269. case SyncUser::State::Active:
  270. return RLMSyncUserStateActive;
  271. case SyncUser::State::LoggedOut:
  272. return RLMSyncUserStateLoggedOut;
  273. case SyncUser::State::Error:
  274. return RLMSyncUserStateError;
  275. }
  276. }
  277. - (NSURL *)authenticationServer {
  278. if (!_user || _user->token_type() == SyncUser::TokenType::Admin) {
  279. return nil;
  280. }
  281. return [NSURL URLWithString:@(_user->server_url().c_str())];
  282. }
  283. - (BOOL)isAdmin {
  284. if (!_user) {
  285. return NO;
  286. }
  287. return _user->is_admin();
  288. }
  289. #pragma mark - Passwords
  290. - (void)changePassword:(NSString *)newPassword completion:(RLMPasswordChangeStatusBlock)completion {
  291. [self changePassword:newPassword forUserID:self.identity completion:completion];
  292. }
  293. - (void)changePassword:(NSString *)newPassword forUserID:(NSString *)userID completion:(RLMPasswordChangeStatusBlock)completion {
  294. if (self.state != RLMSyncUserStateActive) {
  295. completion([NSError errorWithDomain:RLMSyncErrorDomain
  296. code:RLMSyncErrorClientSessionError
  297. userInfo:nil]);
  298. return;
  299. }
  300. [RLMSyncChangePasswordEndpoint sendRequestToServer:self.authenticationServer
  301. JSON:@{kRLMSyncTokenKey: self.refreshToken,
  302. kRLMSyncUserIDKey: userID,
  303. kRLMSyncDataKey: @{kRLMSyncNewPasswordKey: newPassword}}
  304. completion:completion];
  305. }
  306. + (void)requestPasswordResetForAuthServer:(NSURL *)serverURL
  307. userEmail:(NSString *)email
  308. completion:(RLMPasswordChangeStatusBlock)completion {
  309. [RLMSyncUpdateAccountEndpoint sendRequestToServer:serverURL
  310. JSON:@{@"provider_id": email, @"data": @{@"action": @"reset_password"}}
  311. completion:completion];
  312. }
  313. + (void)completePasswordResetForAuthServer:(NSURL *)serverURL
  314. token:(NSString *)token
  315. password:(NSString *)newPassword
  316. completion:(RLMPasswordChangeStatusBlock)completion {
  317. [RLMSyncUpdateAccountEndpoint sendRequestToServer:serverURL
  318. JSON:@{@"data": @{@"action": @"complete_reset",
  319. @"token": token,
  320. @"new_password": newPassword}}
  321. completion:completion];
  322. }
  323. + (void)requestEmailConfirmationForAuthServer:(NSURL *)serverURL
  324. userEmail:(NSString *)email
  325. completion:(RLMPasswordChangeStatusBlock)completion {
  326. [RLMSyncUpdateAccountEndpoint sendRequestToServer:serverURL
  327. JSON:@{@"provider_id": email,
  328. @"data": @{@"action": @"request_email_confirmation"}}
  329. completion:completion];
  330. }
  331. + (void)confirmEmailForAuthServer:(NSURL *)serverURL
  332. token:(NSString *)token
  333. completion:(RLMPasswordChangeStatusBlock)completion {
  334. [RLMSyncUpdateAccountEndpoint sendRequestToServer:serverURL
  335. JSON:@{@"data": @{@"action": @"confirm_email",
  336. @"token": token}}
  337. completion:completion];
  338. }
  339. #pragma mark - Administrator API
  340. - (void)retrieveInfoForUser:(NSString *)providerUserIdentity
  341. identityProvider:(RLMIdentityProvider)provider
  342. completion:(RLMRetrieveUserBlock)completion {
  343. [RLMSyncGetUserInfoEndpoint sendRequestToServer:self.authenticationServer
  344. JSON:@{kRLMSyncProviderKey: provider,
  345. kRLMSyncProviderIDKey: providerUserIdentity,
  346. kRLMSyncTokenKey: self.refreshToken}
  347. timeout:60
  348. completion:^(NSError *error, NSDictionary *json) {
  349. if (error) {
  350. return completion(nil, error);
  351. }
  352. RLMUserResponseModel *model = [[RLMUserResponseModel alloc] initWithDictionary:json];
  353. if (!model) {
  354. return completion(nil, make_auth_error_bad_response(json));
  355. }
  356. completion([RLMSyncUserInfo syncUserInfoWithModel:model], nil);
  357. }];
  358. }
  359. #pragma mark - Permissions API
  360. namespace {
  361. NSError *checkUser(std::shared_ptr<SyncUser> const& user, NSString *msg) {
  362. if (user && user->state() != SyncUser::State::Error) {
  363. return nil;
  364. }
  365. msg = [NSString stringWithFormat:@"Permissions cannot be %@ using an invalid user.", msg];
  366. return [NSError errorWithDomain:RLMSyncPermissionErrorDomain code:RLMSyncAuthErrorInvalidParameters
  367. userInfo:@{NSLocalizedFailureReasonErrorKey: msg}];
  368. }
  369. }
  370. - (void)retrievePermissionsWithCallback:(RLMPermissionResultsBlock)callback {
  371. if (NSError *error = checkUser(_user, @"retrieved")) {
  372. callback(nullptr, error);
  373. return;
  374. }
  375. [RLMSyncGetPermissionsEndpoint
  376. sendRequestToServer:self.authenticationServer
  377. JSON:@{kRLMSyncTokenKey: self.refreshToken}
  378. timeout:60.0
  379. completion:^(NSError *error, NSDictionary *json) {
  380. if (error) {
  381. return callback(nil, error);
  382. }
  383. // FIXME: ROS currently gives duplicated results for 'all' due to an incorrect query
  384. NSMutableSet *permissions = [NSMutableSet new];
  385. for (NSDictionary *permission in json[@"permissions"]) {
  386. // ROS reports the permission for __wildcardpermissions, which we
  387. // don't want to include
  388. if ([permission[@"path"] hasPrefix:@"/__"]) {
  389. continue;
  390. }
  391. // Wildcard permissions are reported as a null userId
  392. id userId = permission[@"userId"];
  393. if (userId == NSNull.null) {
  394. userId = @"*";
  395. }
  396. [permissions addObject:[[RLMSyncPermission alloc]
  397. initWithRealmPath:permission[@"path"]
  398. identity:userId
  399. accessLevel:RLMSyncAccessLevelFromString(permission[@"accessLevel"])]];
  400. }
  401. callback(permissions.allObjects, nil);
  402. }];
  403. }
  404. - (void)applyPermission:(RLMSyncPermission *)permission callback:(RLMPermissionStatusBlock)callback {
  405. if (NSError *error = checkUser(_user, @"applied")) {
  406. callback(error);
  407. return;
  408. }
  409. id condition;
  410. if (permission.identity) {
  411. condition = @{@"userId": permission.identity};
  412. }
  413. else {
  414. condition = @{@"metadataKey": permission.key, @"metadataValue": permission.value};
  415. }
  416. [RLMSyncApplyPermissionsEndpoint
  417. sendRequestToServer:self.authenticationServer
  418. JSON:@{kRLMSyncTokenKey: self.refreshToken,
  419. @"condition": condition,
  420. @"realmPath": permission.path,
  421. @"accessLevel": RLMSyncAccessLevelToString(permission.accessLevel)}
  422. completion:callback];
  423. }
  424. - (void)createOfferForRealmAtURL:(NSURL *)url
  425. accessLevel:(RLMSyncAccessLevel)accessLevel
  426. expiration:(NSDate *)expirationDate
  427. callback:(RLMPermissionOfferStatusBlock)callback {
  428. if (NSError *error = checkUser(_user, @"offered")) {
  429. callback(nil, error);
  430. return;
  431. }
  432. NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
  433. dateFormatter.locale = [NSLocale localeWithLocaleIdentifier:@"en_US_POSIX"];
  434. dateFormatter.dateFormat = @"yyyy-MM-dd'T'HH:mm:ssZZZZZ";
  435. dateFormatter.calendar = [NSCalendar calendarWithIdentifier:NSCalendarIdentifierGregorian];
  436. [RLMSyncOfferPermissionsEndpoint
  437. sendRequestToServer:self.authenticationServer
  438. JSON:@{kRLMSyncTokenKey: self.refreshToken,
  439. @"expiresAt": expirationDate ? [RLMISO8601Formatter() stringFromDate:expirationDate] : NSNull.null,
  440. @"realmPath": url.path,
  441. @"accessLevel": RLMSyncAccessLevelToString(accessLevel)}
  442. timeout:60.0
  443. completion:^(NSError *error, NSDictionary *json) {
  444. callback(json[@"token"], error);
  445. }];
  446. }
  447. - (void)acceptOfferForToken:(NSString *)token
  448. callback:(RLMPermissionOfferResponseStatusBlock)callback {
  449. if (NSError *error = checkUser(_user, @"accepted")) {
  450. callback(nil, error);
  451. return;
  452. }
  453. [RLMSyncAcceptPermissionOfferEndpoint
  454. sendRequestToServer:self.authenticationServer
  455. JSON:@{kRLMSyncTokenKey: self.refreshToken, @"offerToken": token}
  456. timeout:60.0
  457. completion:^(NSError *error, NSDictionary *json) {
  458. callback([self urlForPath:json[@"path"]], error);
  459. }];
  460. }
  461. - (void)invalidateOfferForToken:(NSString *)token
  462. callback:(RLMPermissionStatusBlock)callback {
  463. if (NSError *error = checkUser(_user, @"invalidated")) {
  464. callback(error);
  465. return;
  466. }
  467. [RLMSyncInvalidatePermissionOfferEndpoint
  468. sendRequestToServer:self.authenticationServer
  469. JSON:@{kRLMSyncTokenKey: self.refreshToken, @"offerToken": token}
  470. timeout:60.0
  471. completion:^(NSError *error, NSDictionary *) {
  472. callback(error);
  473. }];
  474. }
  475. - (void)retrievePermissionOffersWithCallback:(RLMPermissionOfferResultsBlock)callback {
  476. if (NSError *error = checkUser(_user, @"retrieved")) {
  477. callback(nullptr, error);
  478. return;
  479. }
  480. [RLMSyncGetPermissionOffersEndpoint
  481. sendRequestToServer:self.authenticationServer
  482. JSON:@{kRLMSyncTokenKey: self.refreshToken}
  483. timeout:60.0
  484. completion:^(NSError *error, NSDictionary *json) {
  485. if (error) {
  486. return callback(nil, error);
  487. }
  488. NSMutableArray *offers = [NSMutableArray new];
  489. NSDateFormatter *formatter = RLMISO8601Formatter();
  490. for (NSDictionary *offer in json[@"offers"]) {
  491. NSString *expiresAt = RLMCoerceToNil(offer[@"expiresAt"]);
  492. NSString *createdAt = RLMCoerceToNil(offer[@"createdAt"]);
  493. [offers addObject:[[RLMSyncPermissionOffer alloc]
  494. initWithRealmPath:offer[@"realmPath"]
  495. token:offer[@"token"]
  496. expiresAt:expiresAt ? [formatter dateFromString:expiresAt] : nil
  497. createdAt:createdAt ? [formatter dateFromString:createdAt] : nil
  498. accessLevel:RLMSyncAccessLevelFromString(offer[@"accessLevel"])]];
  499. }
  500. callback(offers, nil);
  501. }];
  502. }
  503. #pragma mark - Private API
  504. - (NSURL *)urlForPath:(nullable NSString *)path {
  505. if (!path) {
  506. return nil;
  507. }
  508. NSURLComponents *components = [NSURLComponents componentsWithURL:self.authenticationServer resolvingAgainstBaseURL:YES];
  509. if ([components.scheme caseInsensitiveCompare:@"http"] == NSOrderedSame)
  510. components.scheme = @"realm";
  511. else if ([components.scheme caseInsensitiveCompare:@"https"] == NSOrderedSame)
  512. components.scheme = @"realms";
  513. else
  514. @throw RLMException(@"The provided user's authentication server URL (%@) was not valid.", self.authenticationServer);
  515. components.path = path;
  516. return components.URL;
  517. }
  518. - (NSURL *)defaultRealmURL {
  519. return [self urlForPath:@"/default"];
  520. }
  521. + (void)_setUpBindingContextFactory {
  522. SyncUser::set_binding_context_factory([] {
  523. return std::make_shared<CocoaSyncUserContext>();
  524. });
  525. }
  526. - (NSString *)refreshToken {
  527. if (!_user) {
  528. return nil;
  529. }
  530. return @(_user->refresh_token().c_str());
  531. }
  532. - (std::shared_ptr<SyncUser>)_syncUser {
  533. return _user;
  534. }
  535. + (void)_performLoginForDirectAccessTokenCredentials:(RLMSyncCredentials *)credentials
  536. authServerURL:(NSURL *)serverURL
  537. completionBlock:(nonnull RLMUserCompletionBlock)completion {
  538. NSString *identity = credentials.userInfo[kRLMSyncIdentityKey];
  539. std::shared_ptr<SyncUser> sync_user;
  540. if (serverURL) {
  541. NSString *scheme = serverURL.scheme;
  542. if (![scheme isEqualToString:@"http"] && ![scheme isEqualToString:@"https"]) {
  543. @throw RLMException(@"The Realm Object Server authentication URL provided for this user, \"%@\", "
  544. @" is invalid. It must begin with http:// or https://.", serverURL);
  545. }
  546. // Retrieve the user based on the auth server URL.
  547. util::Optional<std::string> identity_string;
  548. if (identity) {
  549. identity_string = std::string(identity.UTF8String);
  550. }
  551. sync_user = SyncManager::shared().get_admin_token_user([serverURL absoluteString].UTF8String,
  552. credentials.token.UTF8String,
  553. std::move(identity_string));
  554. } else {
  555. // Retrieve the user based on the identity.
  556. if (!identity) {
  557. @throw RLMException(@"A direct access credential must specify either an identity, a server URL, or both.");
  558. }
  559. sync_user = SyncManager::shared().get_admin_token_user_from_identity(identity.UTF8String,
  560. none,
  561. credentials.token.UTF8String);
  562. }
  563. if (!sync_user) {
  564. completion(nil, make_auth_error_client_issue());
  565. return;
  566. }
  567. completion([[RLMSyncUser alloc] initWithSyncUser:std::move(sync_user)], nil);
  568. }
  569. + (void)_performLoginForCustomRefreshTokenCredentials:(RLMSyncCredentials *)credentials
  570. authServerURL:(NSURL *)serverURL
  571. completionBlock:(nonnull RLMUserCompletionBlock)completion {
  572. NSString *scheme = serverURL.scheme;
  573. if (![scheme isEqualToString:@"http"] && ![scheme isEqualToString:@"https"]) {
  574. @throw RLMException(@"The Realm Object Server authentication URL provided for this user, \"%@\", "
  575. @" is invalid. It must begin with http:// or https://.", serverURL);
  576. }
  577. NSString *identity = credentials.userInfo[kRLMSyncIdentityKey];
  578. SyncUserIdentifier identifier{identity.UTF8String, serverURL.absoluteString.UTF8String};
  579. std::shared_ptr<SyncUser> sync_user = SyncManager::shared().get_user(std::move(identifier), credentials.token.UTF8String);
  580. if (!sync_user) {
  581. completion(nil, make_auth_error_client_issue());
  582. return;
  583. }
  584. NSNumber *isAdmin = credentials.userInfo[kRLMSyncIsAdminKey];
  585. sync_user->set_is_admin(isAdmin.boolValue);
  586. completion([[RLMSyncUser alloc] initWithSyncUser:std::move(sync_user)], nil);
  587. }
  588. @end
  589. #pragma mark - RLMSyncUserInfo
  590. @implementation RLMSyncUserInfo
  591. - (instancetype)initPrivate {
  592. return [super init];
  593. }
  594. + (instancetype)syncUserInfoWithModel:(RLMUserResponseModel *)model {
  595. RLMSyncUserInfo *info = [[RLMSyncUserInfo alloc] initPrivate];
  596. info.accounts = model.accounts;
  597. info.metadata = model.metadata;
  598. info.isAdmin = model.isAdmin;
  599. info.identity = model.identity;
  600. return info;
  601. }
  602. @end