RLMPermissionsAPITests.m 52 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065
  1. ////////////////////////////////////////////////////////////////////////////
  2. //
  3. // Copyright 2017 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 <XCTest/XCTest.h>
  19. #import "RLMSyncTestCase.h"
  20. #import "RLMTestUtils.h"
  21. @interface RLMSyncPermission ()
  22. - (RLMSyncPermission *)tildeExpandedSyncPermissionForUser:(RLMSyncUser *)user;
  23. @end
  24. @interface RLMSyncUser ()
  25. - (void)invalidate;
  26. @end
  27. #define APPLY_PERMISSION(ma_permission, ma_user) \
  28. APPLY_PERMISSION_WITH_MESSAGE(ma_permission, ma_user, ma_user, @"Setting a permission should work")
  29. #define APPLY_PERMISSION_WITH_MESSAGE(ma_permission, ma_user, ma_target_user, ma_message) do { \
  30. APPLY_PERMISSION_UNCHECKED(ma_permission, ma_user, ma_message); \
  31. CHECK_PERMISSION_PRESENT([self getPermissionResultsFor:ma_target_user], ma_permission, ma_user); \
  32. } while (0)
  33. #define APPLY_PERMISSION_UNCHECKED(ma_permission, ma_user, ma_message) do { \
  34. XCTestExpectation *ex = [self expectationWithDescription:ma_message]; \
  35. [ma_user applyPermission:ma_permission callback:^(NSError *err) { \
  36. XCTAssertNil(err, @"Received an error when applying permission: %@", err); \
  37. [ex fulfill]; \
  38. }]; \
  39. [self waitForExpectations:@[ex] timeout:2.0]; \
  40. } while (0)
  41. #define REVOKE_PERMISSION(ma_permission, ma_user) do { \
  42. XCTestExpectation *ex = [self expectationWithDescription:@"revoke permission"]; \
  43. [ma_user applyPermission:ma_permission callback:^(NSError *err) { \
  44. XCTAssertNil(err, @"Received an error when applying permission: %@", err); \
  45. [ex fulfill]; \
  46. }]; \
  47. [self waitForExpectations:@[ex] timeout:2.0]; \
  48. CHECK_PERMISSION_ABSENT([self getPermissionResultsFor:ma_user], ma_permission, ma_user); \
  49. } while (0)
  50. #define CHECK_COUNT_PENDING_DOWNLOAD(expected_count, m_type, m_realm) \
  51. CHECK_COUNT_PENDING_DOWNLOAD_CUSTOM_EXPECTATION(expected_count, m_type, m_realm, nil)
  52. /// This macro tries ten times to wait for downloads and then check for object count.
  53. /// If the object count does not match, it waits 0.1 second before trying again.
  54. /// It is most useful in cases where the test ROS might be expected to take some
  55. /// non-negligible amount of time performing an operation whose completion is required
  56. /// for the test on the client side to proceed.
  57. #define CHECK_COUNT_PENDING_DOWNLOAD_CUSTOM_EXPECTATION(expected_count, m_type, m_realm, m_exp) do { \
  58. RLMSyncConfiguration *m_config = m_realm.configuration.syncConfiguration; \
  59. XCTAssertNotNil(m_config, @"Realm passed to CHECK_COUNT_PENDING_DOWNLOAD() doesn't have a sync config!"); \
  60. RLMSyncUser *m_user = m_config.user; \
  61. NSURL *m_url = m_config.realmURL; \
  62. for (int i=0; i<10; i++) { \
  63. [self waitForDownloadsForUser:m_user url:m_url expectation:m_exp error:nil]; \
  64. if (expected_count == [m_type allObjectsInRealm:m_realm].count) { break; } \
  65. [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; \
  66. } \
  67. CHECK_COUNT(expected_count, m_type, m_realm); \
  68. } while (0)
  69. static NSURL *makeTestURL(NSString *name, RLMSyncUser *owner) {
  70. NSString *userID = [owner identity] ?: @"~";
  71. return [[NSURL alloc] initWithString:[NSString stringWithFormat:@"realm://127.0.0.1:9080/%@/%@", userID, name]];
  72. }
  73. static NSURL *makeTestGlobalURL(NSString *name) {
  74. return [[NSURL alloc] initWithString:[NSString stringWithFormat:@"realm://127.0.0.1:9080/%@", name]];
  75. }
  76. static NSURL *makeTildeSubstitutedURL(NSURL *url, RLMSyncUser *user) {
  77. return [NSURL URLWithString:[[url absoluteString] stringByReplacingOccurrencesOfString:@"~" withString:user.identity]];
  78. }
  79. @interface RLMPermissionsAPITests : RLMSyncTestCase
  80. @property (nonatomic, strong) NSString *currentUsernameBase;
  81. @property (nonatomic, strong) RLMSyncUser *userA;
  82. @property (nonatomic, strong) RLMSyncUser *userB;
  83. @property (nonatomic, strong) RLMSyncUser *userC;
  84. @property (nonatomic, strong) NSString *userBUsername;
  85. @end
  86. @implementation RLMPermissionsAPITests
  87. - (void)setUp {
  88. [super setUp];
  89. NSString *accountNameBase = [[NSUUID UUID] UUIDString];
  90. self.currentUsernameBase = accountNameBase;
  91. NSString *userNameA = [accountNameBase stringByAppendingString:@"a"];
  92. self.userA = [self logInUserForCredentials:[RLMSyncTestCase basicCredentialsWithName:userNameA register:YES]
  93. server:[RLMSyncTestCase authServerURL]];
  94. NSString *userNameB = [accountNameBase stringByAppendingString:@"b"];
  95. self.userBUsername = userNameB;
  96. self.userB = [self logInUserForCredentials:[RLMSyncTestCase basicCredentialsWithName:userNameB register:YES]
  97. server:[RLMSyncTestCase authServerURL]];
  98. NSString *userNameC = [accountNameBase stringByAppendingString:@"c"];
  99. self.userC = [self logInUserForCredentials:[RLMSyncTestCase basicCredentialsWithName:userNameC register:YES]
  100. server:[RLMSyncTestCase authServerURL]];
  101. RLMSyncManager.sharedManager.errorHandler = ^(NSError *error, __unused RLMSyncSession *session) {
  102. XCTFail(@"Error handler should not be called unless explicitly expected. Error: %@", error);
  103. };
  104. }
  105. - (void)tearDown {
  106. self.currentUsernameBase = nil;
  107. [self.userA logOut];
  108. [self.userB logOut];
  109. [self.userC logOut];
  110. self.userBUsername = nil;
  111. [super tearDown];
  112. }
  113. #pragma mark - Permission validation methods
  114. #define CHECK_PERMISSION_PRESENT(ma_results, ma_permission, ma_user) \
  115. XCTAssertNotEqual([ma_results indexOfObject:[ma_permission tildeExpandedSyncPermissionForUser:ma_user]], NSNotFound)
  116. #define CHECK_PERMISSION_ABSENT(ma_results, ma_permission, ma_user) \
  117. XCTAssertEqual([ma_results indexOfObject:[ma_permission tildeExpandedSyncPermissionForUser:ma_user]], NSNotFound)
  118. #define CHECK_PERMISSION_COUNT_AT_LEAST(ma_results, ma_count) \
  119. XCTAssertGreaterThanOrEqual(ma_results.count, ma_count)
  120. #define CHECK_PERMISSION_COUNT(ma_results, ma_count) \
  121. XCTAssertEqual(ma_results.count, ma_count)
  122. #pragma mark - Helper methods
  123. - (NSArray<RLMSyncPermission *> *)getPermissionResultsFor:(RLMSyncUser *)user {
  124. return [self getPermissionResultsFor:user message:@"Get permission results"];
  125. }
  126. - (NSArray<RLMSyncPermission *> *)getPermissionResultsFor:(RLMSyncUser *)user message:(NSString *)message {
  127. // Get a reference to the permission results.
  128. XCTestExpectation *ex = [self expectationWithDescription:message];
  129. __block NSArray<RLMSyncPermission *> *results = nil;
  130. [user retrievePermissionsWithCallback:^(NSArray<RLMSyncPermission *> *r, NSError *error) {
  131. XCTAssertNil(error);
  132. XCTAssertNotNil(r);
  133. results = r;
  134. [ex fulfill];
  135. }];
  136. [self waitForExpectationsWithTimeout:2.0 handler:nil];
  137. XCTAssertNotNil(results, @"getPermissionResultsFor: failed for user %@. No results.", user.identity);
  138. return results;
  139. }
  140. - (void)testErrorHandlingForInvalidUser {
  141. [self.userA invalidate];
  142. RLMSyncPermission *p;
  143. NSURL *url;
  144. __block bool called = false;
  145. void (^checkError)(NSError *) = ^(NSError *err) {
  146. XCTAssertNotNil(err);
  147. XCTAssertEqual(err.code, RLMSyncAuthErrorInvalidParameters);
  148. called = true;
  149. };
  150. [self.userA retrievePermissionsWithCallback:^(id permissions, NSError *err) {
  151. XCTAssertNil(permissions);
  152. checkError(err);
  153. }];
  154. XCTAssertTrue(called);
  155. called = false;
  156. [self.userA applyPermission:p callback:^(NSError *err) {
  157. checkError(err);
  158. }];
  159. XCTAssertTrue(called);
  160. called = false;
  161. [self.userA createOfferForRealmAtURL:url accessLevel:RLMSyncAccessLevelWrite expiration:nil callback:^(NSString *token, NSError *err) {
  162. XCTAssertNil(token);
  163. checkError(err);
  164. }];
  165. XCTAssertTrue(called);
  166. called = false;
  167. [self.userA acceptOfferForToken:@"" callback:^(NSURL *url, NSError *err) {
  168. XCTAssertNil(url);
  169. checkError(err);
  170. }];
  171. XCTAssertTrue(called);
  172. called = false;
  173. [self.userA invalidateOfferForToken:@"" callback:^(NSError *err) {
  174. checkError(err);
  175. }];
  176. XCTAssertTrue(called);
  177. }
  178. #pragma mark - Permissions
  179. /// If user A grants user B read access to a Realm, user B should be able to read from it.
  180. - (void)testReadAccess {
  181. NSString *testName = NSStringFromSelector(_cmd);
  182. // Open a Realm for user A.
  183. NSURL *userAURL = makeTestURL(testName, nil);
  184. RLMRealm *userARealm = [self openRealmForURL:userAURL user:self.userA];
  185. // Have user A add some items to the Realm.
  186. [self addSyncObjectsToRealm:userARealm descriptions:@[@"child-1", @"child-2", @"child-3"]];
  187. [self waitForUploadsForRealm:userARealm];
  188. CHECK_COUNT(3, SyncObject, userARealm);
  189. // Give user B read permissions to that Realm.
  190. RLMSyncPermission *p = [[RLMSyncPermission alloc] initWithRealmPath:[userAURL path]
  191. identity:self.userB.identity
  192. accessLevel:RLMSyncAccessLevelRead];
  193. // Set the read permission.
  194. APPLY_PERMISSION(p, self.userA);
  195. // Open the same Realm for user B.
  196. NSURL *userBURL = makeTestURL(testName, self.userA);
  197. RLMRealmConfiguration *userBConfig = [self.userB configurationWithURL:userBURL fullSynchronization:YES];
  198. __block RLMRealm *userBRealm = nil;
  199. XCTestExpectation *asyncOpenEx = [self expectationWithDescription:@"Should asynchronously open a Realm"];
  200. [RLMRealm asyncOpenWithConfiguration:userBConfig
  201. callbackQueue:dispatch_get_main_queue()
  202. callback:^(RLMRealm *realm, NSError *err){
  203. XCTAssertNil(err);
  204. XCTAssertNotNil(realm);
  205. userBRealm = realm;
  206. [asyncOpenEx fulfill];
  207. }];
  208. [self waitForExpectationsWithTimeout:10.0 handler:nil];
  209. CHECK_COUNT(3, SyncObject, userBRealm);
  210. // Ensure user B can't actually write to the Realm.
  211. // Run this portion of the test on a background queue, since the error handler is dispatched onto the main queue.
  212. XCTestExpectation *deniedEx = [self expectationWithDescription:@"Expect a permission denied error."];
  213. RLMSyncManager.sharedManager.errorHandler = ^(NSError *err, __unused RLMSyncSession *session) {
  214. // Expect an error from the global error handler.
  215. XCTAssertNotNil(err);
  216. XCTAssertEqual(err.code, RLMSyncErrorPermissionDeniedError);
  217. [deniedEx fulfill];
  218. };
  219. [self addSyncObjectsToRealm:userBRealm descriptions:@[@"child-4", @"child-5", @"child-6"]];
  220. [self waitForExpectations:@[deniedEx] timeout:20.0];
  221. // TODO: if we can get the session itself we can check to see if it's been errored out (as expected).
  222. // Perhaps obviously, there should be no new objects.
  223. CHECK_COUNT_PENDING_DOWNLOAD(3, SyncObject, userARealm);
  224. // Administering the Realm should fail.
  225. RLMSyncPermission *p2 = [[RLMSyncPermission alloc] initWithRealmPath:[userBURL path]
  226. identity:self.userC.identity
  227. accessLevel:RLMSyncAccessLevelRead];
  228. XCTestExpectation *manageEx = [self expectationWithDescription:@"Managing a Realm you can't manage should fail."];
  229. [self.userB applyPermission:p2 callback:^(NSError *error) {
  230. XCTAssertNotNil(error);
  231. [manageEx fulfill];
  232. }];
  233. [self waitForExpectationsWithTimeout:2.0 handler:nil];
  234. }
  235. /// If user A grants user B write access to a Realm, user B should be able to write to it.
  236. - (void)testWriteAccess {
  237. NSString *testName = NSStringFromSelector(_cmd);
  238. // Open a Realm for user A.
  239. NSURL *userAURL = makeTestURL(testName, nil);
  240. RLMRealm *userARealm = [self openRealmForURL:userAURL user:self.userA];
  241. // Have user A add some items to the Realm.
  242. [self addSyncObjectsToRealm:userARealm descriptions:@[@"child-1", @"child-2", @"child-3"]];
  243. [self waitForUploadsForRealm:userARealm];
  244. CHECK_COUNT(3, SyncObject, userARealm);
  245. // Give user B write permissions to that Realm.
  246. RLMSyncPermission *p = [[RLMSyncPermission alloc] initWithRealmPath:[userAURL path]
  247. identity:self.userB.identity
  248. accessLevel:RLMSyncAccessLevelWrite];
  249. // Set the permission.
  250. APPLY_PERMISSION(p, self.userA);
  251. // Open the Realm for user B. Since user B has write privileges, they should be able to open it 'normally'.
  252. NSURL *userBURL = makeTestURL(testName, self.userA);
  253. RLMRealm *userBRealm = [self openRealmForURL:userBURL user:self.userB];
  254. CHECK_COUNT_PENDING_DOWNLOAD(3, SyncObject, userBRealm);
  255. // Add some objects using user B.
  256. [self addSyncObjectsToRealm:userBRealm descriptions:@[@"child-4", @"child-5"]];
  257. [self waitForUploadsForRealm:userBRealm];
  258. CHECK_COUNT(5, SyncObject, userBRealm);
  259. CHECK_COUNT_PENDING_DOWNLOAD(5, SyncObject, userARealm);
  260. // Administering the Realm should fail.
  261. RLMSyncPermission *p2 = [[RLMSyncPermission alloc] initWithRealmPath:[userBURL path]
  262. identity:self.userC.identity
  263. accessLevel:RLMSyncAccessLevelRead];
  264. XCTestExpectation *manageEx = [self expectationWithDescription:@"Managing a Realm you can't manage should fail."];
  265. [self.userB applyPermission:p2 callback:^(NSError *error) {
  266. XCTAssertNotNil(error);
  267. [manageEx fulfill];
  268. }];
  269. [self waitForExpectationsWithTimeout:2.0 handler:nil];
  270. }
  271. /// If user A grants user B manage access to a Realm, user B should be able to set a permission for user C.
  272. - (void)testManageAccess {
  273. NSString *testName = NSStringFromSelector(_cmd);
  274. // Unresolved URL: ~/testManageAccess
  275. NSURL *userAURLUnresolved = makeTestURL(testName, nil);
  276. // Resolved URL: <User A ID>/testManageAccess
  277. NSURL *userAURLResolved = makeTestURL(testName, self.userA);
  278. // Open a Realm for user A.
  279. RLMRealm *userARealm = [self openRealmForURL:userAURLUnresolved user:self.userA];
  280. // Have user A add some items to the Realm.
  281. [self addSyncObjectsToRealm:userARealm descriptions:@[@"child-1", @"child-2", @"child-3"]];
  282. [self waitForUploadsForRealm:userARealm];
  283. CHECK_COUNT(3, SyncObject, userARealm);
  284. // Give user B admin permissions to that Realm.
  285. RLMSyncPermission *p = [[RLMSyncPermission alloc] initWithRealmPath:[userAURLUnresolved path]
  286. identity:self.userB.identity
  287. accessLevel:RLMSyncAccessLevelAdmin];
  288. // Set the permission.
  289. APPLY_PERMISSION(p, self.userA);
  290. // Open the Realm for user B. Since user B has admin privileges, they should be able to open it 'normally'.
  291. RLMRealm *userBRealm = [self openRealmForURL:userAURLResolved user:self.userB];
  292. CHECK_COUNT_PENDING_DOWNLOAD(3, SyncObject, userBRealm);
  293. // Add some objects using user B.
  294. [self addSyncObjectsToRealm:userBRealm descriptions:@[@"child-4", @"child-5"]];
  295. [self waitForUploadsForRealm:userBRealm];
  296. CHECK_COUNT(5, SyncObject, userBRealm);
  297. CHECK_COUNT_PENDING_DOWNLOAD(5, SyncObject, userARealm);
  298. // User B should be able to give user C write permissions to user A's Realm.
  299. RLMSyncPermission *p2 = [[RLMSyncPermission alloc] initWithRealmPath:[userAURLResolved path]
  300. identity:self.userC.identity
  301. accessLevel:RLMSyncAccessLevelWrite];
  302. APPLY_PERMISSION_WITH_MESSAGE(p2, self.userB, self.userC,
  303. @"User B should be able to give C write permissions to A's Realm.");
  304. // User C should be able to write to the Realm.
  305. RLMRealm *userCRealm = [self openRealmForURL:userAURLResolved user:self.userC];
  306. CHECK_COUNT_PENDING_DOWNLOAD(5, SyncObject, userCRealm);
  307. [self addSyncObjectsToRealm:userCRealm descriptions:@[@"child-6", @"child-7", @"child-8"]];
  308. [self waitForUploadsForRealm:userCRealm];
  309. CHECK_COUNT(8, SyncObject, userCRealm);
  310. CHECK_COUNT_PENDING_DOWNLOAD(8, SyncObject, userARealm);
  311. CHECK_COUNT_PENDING_DOWNLOAD(8, SyncObject, userBRealm);
  312. }
  313. /// If user A grants user B write access to a Realm via username, user B should be able to write to it.
  314. - (void)testWriteAccessViaUsername {
  315. NSString *testName = NSStringFromSelector(_cmd);
  316. // Open a Realm for user A.
  317. NSURL *userAURL = makeTestURL(testName, nil);
  318. RLMRealm *userARealm = [self openRealmForURL:userAURL user:self.userA];
  319. // Have user A add some items to the Realm.
  320. [self addSyncObjectsToRealm:userARealm descriptions:@[@"child-1", @"child-2", @"child-3"]];
  321. [self waitForUploadsForRealm:userARealm];
  322. CHECK_COUNT(3, SyncObject, userARealm);
  323. // Give user B write permissions to that Realm via user B's username.
  324. NSString *userAFullPath = [makeTildeSubstitutedURL(userAURL, self.userA) path];
  325. RLMSyncPermission *p = [[RLMSyncPermission alloc] initWithRealmPath:userAURL.path
  326. username:self.userBUsername
  327. accessLevel:RLMSyncAccessLevelWrite];
  328. // Set the permission.
  329. APPLY_PERMISSION_UNCHECKED(p, self.userA, @"Grant permission via email");
  330. RLMSyncPermission *expected = [[RLMSyncPermission alloc] initWithRealmPath:userAFullPath
  331. identity:self.userB.identity
  332. accessLevel:RLMSyncAccessLevelWrite];
  333. CHECK_PERMISSION_PRESENT([self getPermissionResultsFor:self.userB], expected, self.userB);
  334. // Open the Realm for user B. Since user B has write privileges, they should be able to open it 'normally'.
  335. NSURL *userBURL = makeTestURL(testName, self.userA);
  336. RLMRealm *userBRealm = [self openRealmForURL:userBURL user:self.userB];
  337. CHECK_COUNT_PENDING_DOWNLOAD(3, SyncObject, userBRealm);
  338. // Add some objects using user B.
  339. [self addSyncObjectsToRealm:userBRealm descriptions:@[@"child-4", @"child-5"]];
  340. [self waitForUploadsForRealm:userBRealm];
  341. CHECK_COUNT(5, SyncObject, userBRealm);
  342. CHECK_COUNT_PENDING_DOWNLOAD(5, SyncObject, userARealm);
  343. }
  344. /// Setting a permission for all users should work.
  345. - (void)testWildcardWriteAccess {
  346. // Open a Realm for user A.
  347. NSString *testName = NSStringFromSelector(_cmd);
  348. NSURL *ownerURL = makeTestURL(testName, nil);
  349. NSURL *guestURL = makeTestURL(testName, self.userA);
  350. RLMRealm *userARealm = [self openRealmForURL:ownerURL user:self.userA];
  351. // Give all users write permissions to that Realm.
  352. RLMSyncPermission *p = [[RLMSyncPermission alloc] initWithRealmPath:[ownerURL path]
  353. identity:@"*"
  354. accessLevel:RLMSyncAccessLevelWrite];
  355. // Set the permission.
  356. APPLY_PERMISSION(p, self.userA);
  357. // Have user A write a few objects first.
  358. [self addSyncObjectsToRealm:userARealm descriptions:@[@"child-1", @"child-2", @"child-3"]];
  359. [self waitForUploadsForRealm:userARealm];
  360. CHECK_COUNT(3, SyncObject, userARealm);
  361. // User B should be able to write to the Realm.
  362. RLMRealm *userBRealm = [self openRealmForURL:guestURL user:self.userB];
  363. CHECK_COUNT_PENDING_DOWNLOAD(3, SyncObject, userBRealm);
  364. [self addSyncObjectsToRealm:userBRealm descriptions:@[@"child-4", @"child-5"]];
  365. [self waitForUploadsForRealm:userBRealm];
  366. CHECK_COUNT(5, SyncObject, userBRealm);
  367. // User C should be able to write to the Realm.
  368. RLMRealm *userCRealm = [self openRealmForURL:guestURL user:self.userC];
  369. CHECK_COUNT_PENDING_DOWNLOAD(5, SyncObject, userCRealm);
  370. [self addSyncObjectsToRealm:userCRealm descriptions:@[@"child-6", @"child-7", @"child-8", @"child-9"]];
  371. [self waitForUploadsForRealm:userCRealm];
  372. CHECK_COUNT(9, SyncObject, userCRealm);
  373. p = [[RLMSyncPermission alloc] initWithRealmPath:[ownerURL path]
  374. identity:@"*"
  375. accessLevel:RLMSyncAccessLevelNone];
  376. REVOKE_PERMISSION(p, self.userA);
  377. }
  378. /// It should be possible to grant read-only access to a global Realm.
  379. - (void)testWildcardGlobalRealmReadAccess {
  380. RLMSyncUser *admin = [self createAdminUserForURL:[RLMSyncTestCase authServerURL]
  381. username:[[NSUUID UUID] UUIDString]];
  382. // Open a Realm for the admin user.
  383. NSString *testName = NSStringFromSelector(_cmd);
  384. NSURL *globalRealmURL = makeTestGlobalURL(testName);
  385. RLMRealm *adminUserRealm = [self openRealmForURL:globalRealmURL user:admin];
  386. // Give all users read permissions to that Realm.
  387. RLMSyncPermission *p = [[RLMSyncPermission alloc] initWithRealmPath:[globalRealmURL path]
  388. identity:@"*"
  389. accessLevel:RLMSyncAccessLevelRead];
  390. // Set the permission.
  391. APPLY_PERMISSION_WITH_MESSAGE(p, admin, self.userA, @"Setting wildcard permission should work.");
  392. // Have the admin user write a few objects first.
  393. [self addSyncObjectsToRealm:adminUserRealm descriptions:@[@"child-1", @"child-2", @"child-3"]];
  394. [self waitForUploadsForRealm:adminUserRealm];
  395. CHECK_COUNT(3, SyncObject, adminUserRealm);
  396. // User B should be able to read from the Realm.
  397. __block RLMRealm *userBRealm = nil;
  398. RLMRealmConfiguration *userBConfig = [self.userB configurationWithURL:globalRealmURL fullSynchronization:YES];
  399. XCTestExpectation *asyncOpenEx = [self expectationWithDescription:@"Should asynchronously open a Realm"];
  400. [RLMRealm asyncOpenWithConfiguration:userBConfig
  401. callbackQueue:dispatch_get_main_queue()
  402. callback:^(RLMRealm *realm, NSError *err){
  403. XCTAssertNil(err);
  404. XCTAssertNotNil(realm);
  405. userBRealm = realm;
  406. [asyncOpenEx fulfill];
  407. }];
  408. [self waitForExpectationsWithTimeout:10.0 handler:nil];
  409. CHECK_COUNT(3, SyncObject, userBRealm);
  410. // User C should be able to read from the Realm.
  411. __block RLMRealm *userCRealm = nil;
  412. RLMRealmConfiguration *userCConfig = [self.userC configurationWithURL:globalRealmURL fullSynchronization:YES];
  413. XCTestExpectation *asyncOpenEx2 = [self expectationWithDescription:@"Should asynchronously open a Realm"];
  414. [RLMRealm asyncOpenWithConfiguration:userCConfig
  415. callbackQueue:dispatch_get_main_queue()
  416. callback:^(RLMRealm *realm, NSError *err){
  417. XCTAssertNil(err);
  418. XCTAssertNotNil(realm);
  419. userCRealm = realm;
  420. [asyncOpenEx2 fulfill];
  421. }];
  422. [self waitForExpectationsWithTimeout:10.0 handler:nil];
  423. CHECK_COUNT(3, SyncObject, userCRealm);
  424. p = [[RLMSyncPermission alloc] initWithRealmPath:[globalRealmURL path]
  425. identity:@"*"
  426. accessLevel:RLMSyncAccessLevelNone];
  427. REVOKE_PERMISSION(p, admin);
  428. }
  429. /// Setting a permission for all users on a global Realm (no `~`) should work.
  430. - (void)testWildcardGlobalRealmWriteAccess {
  431. RLMSyncUser *admin = [self createAdminUserForURL:[RLMSyncTestCase authServerURL]
  432. username:[[NSUUID UUID] UUIDString]];
  433. // Open a Realm for the admin user.
  434. NSString *testName = NSStringFromSelector(_cmd);
  435. NSURL *globalRealmURL = makeTestGlobalURL(testName);
  436. RLMRealm *adminUserRealm = [self openRealmForURL:globalRealmURL user:admin];
  437. // Give all users write permissions to that Realm.
  438. RLMSyncPermission *p = [[RLMSyncPermission alloc] initWithRealmPath:[globalRealmURL path]
  439. identity:@"*"
  440. accessLevel:RLMSyncAccessLevelWrite];
  441. // Set the permission.
  442. APPLY_PERMISSION_WITH_MESSAGE(p, admin, self.userA, @"Should grant access to all users");
  443. // Have the admin user write a few objects first.
  444. [self addSyncObjectsToRealm:adminUserRealm descriptions:@[@"child-1", @"child-2", @"child-3"]];
  445. [self waitForUploadsForRealm:adminUserRealm];
  446. CHECK_COUNT(3, SyncObject, adminUserRealm);
  447. // User B should be able to write to the Realm.
  448. RLMRealm *userBRealm = [self openRealmForURL:globalRealmURL user:self.userB];
  449. CHECK_COUNT_PENDING_DOWNLOAD(3, SyncObject, userBRealm);
  450. [self addSyncObjectsToRealm:userBRealm descriptions:@[@"child-4", @"child-5"]];
  451. [self waitForUploadsForRealm:userBRealm];
  452. CHECK_COUNT(5, SyncObject, userBRealm);
  453. // User C should be able to write to the Realm.
  454. RLMRealm *userCRealm = [self openRealmForURL:globalRealmURL user:self.userC];
  455. CHECK_COUNT_PENDING_DOWNLOAD(5, SyncObject, userCRealm);
  456. [self addSyncObjectsToRealm:userCRealm descriptions:@[@"child-6", @"child-7", @"child-8", @"child-9"]];
  457. [self waitForUploadsForRealm:userCRealm];
  458. CHECK_COUNT(9, SyncObject, userCRealm);
  459. p = [[RLMSyncPermission alloc] initWithRealmPath:[globalRealmURL path]
  460. identity:@"*"
  461. accessLevel:RLMSyncAccessLevelNone];
  462. REVOKE_PERMISSION(p, admin);
  463. }
  464. #pragma mark - Permission change API
  465. /// Setting a permission should work, and then that permission should be able to be retrieved.
  466. - (void)testSettingPermission {
  467. // First, there should be no permissions.
  468. NSArray<RLMSyncPermission *> *results = [self getPermissionResultsFor:self.userA];
  469. CHECK_PERMISSION_COUNT(results, 0);
  470. // Open a Realm for user A.
  471. NSURL *url = REALM_URL();
  472. [self openRealmForURL:url user:self.userA];
  473. // Give user B read permissions to that Realm.
  474. RLMSyncPermission *p = [[RLMSyncPermission alloc] initWithRealmPath:[makeTildeSubstitutedURL(url, self.userA) path]
  475. identity:self.userB.identity
  476. accessLevel:RLMSyncAccessLevelRead];
  477. // Set the permission.
  478. APPLY_PERMISSION(p, self.userA);
  479. // Now retrieve the permissions again and make sure the new permission is properly set.
  480. results = [self getPermissionResultsFor:self.userB message:@"One permission after setting the permission."];
  481. // Expected permission: applies to user B, but for user A's Realm.
  482. CHECK_PERMISSION_PRESENT(results, p, self.userA);
  483. // Check getting permission by its index.
  484. NSUInteger index = [results indexOfObject:p];
  485. XCTAssertNotEqual(index, NSNotFound);
  486. XCTAssertEqualObjects(p, [results objectAtIndex:index]);
  487. }
  488. /// Deleting a permission should work.
  489. - (void)testDeletingPermission {
  490. // Open a Realm for user A.
  491. NSURL *url = REALM_URL();
  492. [self openRealmForURL:url user:self.userA];
  493. // Give user B read permissions to that Realm.
  494. RLMSyncPermission *p = [[RLMSyncPermission alloc] initWithRealmPath:[makeTildeSubstitutedURL(url, self.userA) path]
  495. identity:self.userB.identity
  496. accessLevel:RLMSyncAccessLevelRead];
  497. // Set the permission.
  498. APPLY_PERMISSION(p, self.userA);
  499. // Now retrieve the permissions again and make sure the new permission is properly set.
  500. NSArray<RLMSyncPermission *> *results = [self getPermissionResultsFor:self.userB
  501. message:@"Setting new permission."];
  502. CHECK_PERMISSION_PRESENT(results, p, self.userA);
  503. // Delete the permission.
  504. RLMSyncPermission *p2 = [[RLMSyncPermission alloc] initWithRealmPath:url.path
  505. identity:self.userB.identity
  506. accessLevel:RLMSyncAccessLevelNone];
  507. REVOKE_PERMISSION(p2, self.userA);
  508. // Make sure the permission deletion is properly reflected.
  509. results = [self getPermissionResultsFor:self.userB message:@"Setting new permission."];
  510. CHECK_PERMISSION_COUNT(results, 0);
  511. }
  512. /// KVC getting and setting should work properly for `NSArray<RLMSyncPermission>`.
  513. - (void)testKVCWithPermissionsResults {
  514. NSURL *url1 = CUSTOM_REALM_URL(@"r1");
  515. NSURL *url2 = CUSTOM_REALM_URL(@"r2");
  516. __attribute__((objc_precise_lifetime)) RLMRealm *r1 = [self openRealmForURL:url1 user:self.userA];
  517. __attribute__((objc_precise_lifetime)) RLMRealm *r2 = [self openRealmForURL:url2 user:self.userA];
  518. NSString *uB = self.userB.identity;
  519. // Give user B read permissions to r1 and r2.
  520. NSString *path1 = [makeTildeSubstitutedURL(url1, self.userA) path];
  521. id p1 = [[RLMSyncPermission alloc] initWithRealmPath:path1
  522. identity:uB
  523. accessLevel:RLMSyncAccessLevelRead];
  524. APPLY_PERMISSION_WITH_MESSAGE(p1, self.userA, self.userA, @"Setting r1 permission for user B should work.");
  525. NSString *path2 = [makeTildeSubstitutedURL(url2, self.userA) path];
  526. id p2 = [[RLMSyncPermission alloc] initWithRealmPath:path2
  527. identity:uB
  528. accessLevel:RLMSyncAccessLevelRead];
  529. APPLY_PERMISSION_WITH_MESSAGE(p2, self.userA, self.userA, @"Setting r2 permission for user B should work.");
  530. // Wait for all the permissions to show up.
  531. NSArray<RLMSyncPermission *> *results = [self getPermissionResultsFor:self.userB];
  532. CHECK_PERMISSION_PRESENT(results, p1, self.userA);
  533. CHECK_PERMISSION_PRESENT(results, p2, self.userA);
  534. // Now use `valueForKey`
  535. NSArray *selfValues = [results valueForKey:@"self"];
  536. XCTAssert(selfValues.count == results.count);
  537. for (id object in selfValues) {
  538. XCTAssert([object isKindOfClass:[RLMSyncPermission class]]);
  539. }
  540. NSArray *identityValues = [results valueForKey:@"path"];
  541. XCTAssert(identityValues.count == results.count);
  542. XCTAssert([identityValues containsObject:path1]);
  543. XCTAssert([identityValues containsObject:path2]);
  544. // Since `RLMSyncPermission`s are read-only, KVC setting should fail.
  545. RLMAssertThrows([results setValue:@"foobar" forKey:@"path"]);
  546. }
  547. /// Filtering permissions results should work.
  548. - (void)testFilteringPermissions {
  549. // Open two Realms
  550. NSURL *url1 = CUSTOM_REALM_URL(@"r1");
  551. NSURL *url2 = CUSTOM_REALM_URL(@"r2");
  552. NSURL *url3 = CUSTOM_REALM_URL(@"r3");
  553. __attribute__((objc_precise_lifetime)) RLMRealm *r1 = [self openRealmForURL:url1 user:self.userA];
  554. __attribute__((objc_precise_lifetime)) RLMRealm *r2 = [self openRealmForURL:url2 user:self.userA];
  555. __attribute__((objc_precise_lifetime)) RLMRealm *r3 = [self openRealmForURL:url3 user:self.userA];
  556. NSString *uB = self.userB.identity;
  557. // Give user B permissions to realms r1, r2, and r3.
  558. id p1 = [[RLMSyncPermission alloc] initWithRealmPath:url1.path
  559. identity:uB
  560. accessLevel:RLMSyncAccessLevelRead];
  561. APPLY_PERMISSION_WITH_MESSAGE(p1, self.userA, self.userA, @"Setting r1 permission for user B should work.");
  562. NSString *finalPath = [makeTildeSubstitutedURL(url2, self.userA) path];
  563. id p2 = [[RLMSyncPermission alloc] initWithRealmPath:finalPath
  564. identity:uB
  565. accessLevel:RLMSyncAccessLevelRead];
  566. APPLY_PERMISSION_WITH_MESSAGE(p2, self.userA, self.userA, @"Setting r2 permission for user B should work.");
  567. id p3 = [[RLMSyncPermission alloc] initWithRealmPath:url3.path
  568. identity:uB
  569. accessLevel:RLMSyncAccessLevelRead];
  570. APPLY_PERMISSION_WITH_MESSAGE(p3, self.userA, self.userA, @"Setting r3 permission for user B should work.");
  571. // Wait for all the permissions to show up.
  572. NSArray<RLMSyncPermission *> *results = [self getPermissionResultsFor:self.userB];
  573. CHECK_PERMISSION_PRESENT(results, p1, self.userA);
  574. CHECK_PERMISSION_PRESENT(results, p2, self.userA);
  575. CHECK_PERMISSION_PRESENT(results, p3, self.userA);
  576. // Now make a filter.
  577. NSArray<RLMSyncPermission *> *filtered = [results filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"path == %@", finalPath]];
  578. CHECK_PERMISSION_ABSENT(filtered, p1, self.userA);
  579. CHECK_PERMISSION_PRESENT(filtered, p2, self.userA);
  580. CHECK_PERMISSION_ABSENT(filtered, p3, self.userA);
  581. }
  582. - (void)testSortingPermissionsOnUserID {
  583. NSURL *url = REALM_URL();
  584. __attribute__((objc_precise_lifetime)) RLMRealm *r = [self openRealmForURL:url user:self.userA];
  585. // Give users B and C access to my Realm.
  586. id p1 = [[RLMSyncPermission alloc] initWithRealmPath:[makeTildeSubstitutedURL(url, self.userA) path]
  587. identity:self.userB.identity
  588. accessLevel:RLMSyncAccessLevelRead];
  589. APPLY_PERMISSION_WITH_MESSAGE(p1, self.userA, self.userA, @"Setting r permission for user B should work.");
  590. id p2 = [[RLMSyncPermission alloc] initWithRealmPath:[makeTildeSubstitutedURL(url, self.userA) path]
  591. identity:self.userC.identity
  592. accessLevel:RLMSyncAccessLevelRead];
  593. APPLY_PERMISSION_WITH_MESSAGE(p2, self.userA, self.userA, @"Setting r permission for user C should work.");
  594. // Now sort on user ID.
  595. NSArray<RLMSyncPermission *> *results = [self getPermissionResultsFor:self.userA];
  596. NSArray<RLMSyncPermission *> *sorted = [results sortedArrayUsingDescriptors:@[[NSSortDescriptor sortDescriptorWithKey:@"identity" ascending:YES]]];
  597. CHECK_PERMISSION_COUNT(sorted, 3);
  598. NSArray *sortedIDs = [sorted valueForKey:@"identity"];
  599. // Make sure the IDs in sortedIDs are actually sorted.
  600. for (NSUInteger i = 0; i < sorted.count - 1; i++) {
  601. XCTAssertEqual([sortedIDs[i] compare:sortedIDs[i + 1]], NSOrderedAscending);
  602. }
  603. // Make sure the IDs in sortedIDs contain all 3 users' IDs.
  604. NSSet *sortedIDSet = [NSSet setWithArray:sortedIDs];
  605. XCTAssertTrue([sortedIDSet containsObject:self.userA.identity]);
  606. XCTAssertTrue([sortedIDSet containsObject:self.userB.identity]);
  607. XCTAssertTrue([sortedIDSet containsObject:self.userC.identity]);
  608. }
  609. - (void)testPermissionResultsIndexOfObject {
  610. NSString *uB = self.userB.identity;
  611. // Have A open a Realm and grant a permission to B.
  612. NSURL *url = REALM_URL();
  613. NSString *tildeSubstitutedPath = [makeTildeSubstitutedURL(url, self.userA) path];
  614. __attribute__((objc_precise_lifetime)) RLMRealm *r = [self openRealmForURL:url user:self.userA];
  615. id p1 = [[RLMSyncPermission alloc] initWithRealmPath:tildeSubstitutedPath
  616. identity:uB
  617. accessLevel:RLMSyncAccessLevelRead];
  618. APPLY_PERMISSION_WITH_MESSAGE(p1, self.userA, self.userA,
  619. @"Setting read permission for user B should work.");
  620. // Wait for the permission to show up.
  621. NSArray<RLMSyncPermission *> *results = [self getPermissionResultsFor:self.userB];
  622. CHECK_PERMISSION_COUNT(results, 1);
  623. // Should be able to get the permission based on the actual permission.
  624. XCTAssertEqual(((NSInteger)[results indexOfObject:p1]), 0);
  625. // A permission with a differing access level should not match.
  626. id p2 = [[RLMSyncPermission alloc] initWithRealmPath:tildeSubstitutedPath
  627. identity:uB
  628. accessLevel:RLMSyncAccessLevelAdmin];
  629. XCTAssertEqual([results indexOfObject:p2], NSNotFound);
  630. // A permission with a differing identity should not match.
  631. id p3 = [[RLMSyncPermission alloc] initWithRealmPath:tildeSubstitutedPath
  632. identity:self.userA.identity
  633. accessLevel:RLMSyncAccessLevelRead];
  634. XCTAssertEqual([results indexOfObject:p3], NSNotFound);
  635. // A permission with a differing path should not match.
  636. id p4 = [[RLMSyncPermission alloc] initWithRealmPath:[makeTildeSubstitutedURL(url, self.userB) path]
  637. identity:uB
  638. accessLevel:RLMSyncAccessLevelRead];
  639. XCTAssertEqual([results indexOfObject:p4], NSNotFound);
  640. }
  641. /// User should not be able to change a permission for a Realm they don't own.
  642. - (void)testSettingUnownedRealmPermission {
  643. // Open a Realm for user A.
  644. NSURL *url = REALM_URL();
  645. [self openRealmForURL:url user:self.userA];
  646. // Try to have user B give user C permissions to that Realm.
  647. RLMSyncPermission *p = [[RLMSyncPermission alloc] initWithRealmPath:[makeTildeSubstitutedURL(url, self.userA) path]
  648. identity:self.userC.identity
  649. accessLevel:RLMSyncAccessLevelRead];
  650. // Set the permission.
  651. XCTestExpectation *ex2 = [self expectationWithDescription:@"Setting an invalid permission should fail."];
  652. [self.userB applyPermission:p callback:^(NSError *error) {
  653. XCTAssertNotNil(error);
  654. XCTAssertEqual(error.domain, RLMSyncAuthErrorDomain);
  655. XCTAssertEqual(error.code, RLMSyncAuthErrorAccessDeniedOrInvalidPath);
  656. [ex2 fulfill];
  657. }];
  658. [self waitForExpectationsWithTimeout:2.0 handler:nil];
  659. // Now retrieve the permissions again and make sure the new permission was not set.
  660. NSArray<RLMSyncPermission *> *results = [self getPermissionResultsFor:self.userB
  661. message:@"Retrieving the results should work."];
  662. CHECK_PERMISSION_ABSENT(results, p, self.userA);
  663. }
  664. #pragma mark - Permission offer/response
  665. - (NSString *)createOfferForRealmAtURL:(NSURL *)url
  666. user:(RLMSyncUser *)user
  667. accessLevel:(RLMSyncAccessLevel)level
  668. expiration:(NSDate *)expiration {
  669. __block NSString *token;
  670. XCTestExpectation *ex = [self expectationWithDescription:@"Should get a token when making an offer."];
  671. [user createOfferForRealmAtURL:url
  672. accessLevel:level
  673. expiration:expiration
  674. callback:^(NSString *t, NSError *error) {
  675. XCTAssertNil(error);
  676. token = t;
  677. XCTAssertNotNil(token);
  678. XCTAssertGreaterThan(token.length, 0);
  679. [ex fulfill];
  680. }];
  681. [self waitForExpectations:@[ex] timeout:10.0];
  682. return token;
  683. }
  684. /// Get a token which can be used to offer the permissions as defined
  685. - (void)testPermissionOffer {
  686. NSURL *url = REALM_URL();
  687. [self openRealmForURL:url user:self.userA];
  688. [self createOfferForRealmAtURL:url user:self.userA accessLevel:RLMSyncAccessLevelWrite expiration:nil];
  689. }
  690. /// Failed to process a permission offer object due to the offer expired
  691. - (void)testPermissionOfferIsExpired {
  692. NSURL *url = REALM_URL();
  693. // Create the Realm
  694. [self openRealmForURL:url user:self.userA];
  695. XCTestExpectation *ex = [self expectationWithDescription:@"Server should process the permission offer."];
  696. [self.userA createOfferForRealmAtURL:url
  697. accessLevel:RLMSyncAccessLevelWrite
  698. expiration:[NSDate dateWithTimeIntervalSinceNow:-30 * 24 * 60 * 60]
  699. callback:^(NSString *token, NSError *error) {
  700. XCTAssertNotNil(error);
  701. XCTAssertNil(token);
  702. XCTAssertEqual(error.code, RLMSyncAuthErrorExpiredPermissionOffer);
  703. XCTAssertEqualObjects(error.userInfo[NSLocalizedDescriptionKey], @"The permission offer is expired.");
  704. [ex fulfill];
  705. }];
  706. [self waitForExpectations:@[ex] timeout:10.0];
  707. }
  708. /// Get a permission offer token, then permission offer response will be processed, then open another user's Realm file
  709. - (void)testPermissionOfferResponse {
  710. NSURL *url = REALM_URL();
  711. // Create the Realm
  712. [self openRealmForURL:url user:self.userA];
  713. NSString *token = [self createOfferForRealmAtURL:url user:self.userA
  714. accessLevel:RLMSyncAccessLevelWrite expiration:nil];
  715. // Accept the offer.
  716. __block NSURL *realmURL = nil;
  717. XCTestExpectation *ex = [self expectationWithDescription:@"Server should process offer acceptance."];
  718. [self.userB acceptOfferForToken:token callback:^(NSURL *returnedURL, NSError *error) {
  719. XCTAssertNil(error);
  720. XCTAssertNotNil(returnedURL);
  721. realmURL = returnedURL;
  722. [ex fulfill];
  723. }];
  724. [self waitForExpectations:@[ex] timeout:20.0];
  725. XCTAssertEqualObjects([realmURL path], [makeTildeSubstitutedURL(url, self.userA) path]);
  726. // Open the Realm.
  727. XCTAssertNotNil([self openRealmForURL:realmURL user:self.userB]);
  728. }
  729. /// Failed to process a permission offer response object due to `token` is invalid
  730. - (void)testPermissionOfferResponseInvalidToken {
  731. NSString *badToken = @"invalid token";
  732. // Expect an error.
  733. __block NSError *error = nil;
  734. XCTestExpectation *ex = [self expectationWithDescription:@"Server should process offer acceptance."];
  735. [self.userA acceptOfferForToken:badToken callback:^(NSURL *returnedURL, NSError *err) {
  736. XCTAssertNil(returnedURL);
  737. XCTAssertNotNil(err);
  738. error = err;
  739. [ex fulfill];
  740. }];
  741. [self waitForExpectations:@[ex] timeout:20.0];
  742. XCTAssertEqual(error.code, RLMSyncAuthErrorInvalidParameters);
  743. XCTAssertEqualObjects(error.userInfo[NSLocalizedDescriptionKey],
  744. @"Your request parameters did not validate. token: Invalid parameter 'token'!;");
  745. }
  746. /// Failed to process a permission offer response object due to `token` represents a Realm that does not exist
  747. - (void)testPermissionOfferResponseTokenNotExist {
  748. NSString *fakeToken = @"00000000000000000000000000000000:00000000-0000-0000-0000-000000000000";
  749. // Expect an error.
  750. __block NSError *error = nil;
  751. XCTestExpectation *ex = [self expectationWithDescription:@"Server should process offer acceptance."];
  752. [self.userA acceptOfferForToken:fakeToken callback:^(NSURL *returnedURL, NSError *err) {
  753. XCTAssertNil(returnedURL);
  754. XCTAssertNotNil(err);
  755. error = err;
  756. [ex fulfill];
  757. }];
  758. [self waitForExpectations:@[ex] timeout:20.0];
  759. XCTAssertEqual(error.code, RLMSyncAuthErrorInvalidParameters);
  760. XCTAssertEqualObjects(error.userInfo[NSLocalizedDescriptionKey],
  761. @"Your request parameters did not validate. token: Invalid parameter 'token'!;");
  762. }
  763. - (void)testInvalidatePermissionOffer {
  764. NSURL *url = REALM_URL();
  765. // Create the Realm
  766. [self openRealmForURL:url user:self.userA];
  767. // Create an offer
  768. NSString *token = [self createOfferForRealmAtURL:url user:self.userA
  769. accessLevel:RLMSyncAccessLevelWrite expiration:nil];
  770. // Invalidate it
  771. XCTestExpectation *ex2 = [self expectationWithDescription:@"Should invalidate a offer token."];
  772. [self.userA invalidateOfferForToken:token callback:^(NSError *error) {
  773. XCTAssertNil(error);
  774. [ex2 fulfill];
  775. }];
  776. [self waitForExpectations:@[ex2] timeout:10.0];
  777. // Fail to accept the offer
  778. XCTestExpectation *ex3 = [self expectationWithDescription:@"Server should reject invalidated offer"];
  779. [self.userB acceptOfferForToken:token callback:^(NSURL *returnedURL, NSError *error) {
  780. XCTAssertNil(returnedURL);
  781. XCTAssertNotNil(error);
  782. XCTAssertEqual(error.code, RLMSyncAuthErrorExpiredPermissionOffer);
  783. XCTAssertEqualObjects(error.userInfo[NSLocalizedDescriptionKey], @"The permission offer is expired.");
  784. [ex3 fulfill];
  785. }];
  786. [self waitForExpectations:@[ex3] timeout:20.0];
  787. }
  788. - (void)testRetrievePermissionOffers {
  789. NSURL *url = REALM_URL();
  790. NSURL *expandedURL = makeTildeSubstitutedURL(url, self.userA);
  791. // Create the Realm
  792. [self openRealmForURL:url user:self.userA];
  793. NSDate *createdAt = [NSDate date];
  794. NSDate *expiresAt = [NSDate dateWithTimeIntervalSinceNow:100.0];
  795. // Create two offers
  796. NSString *token1 = [self createOfferForRealmAtURL:url user:self.userA
  797. accessLevel:RLMSyncAccessLevelRead expiration:expiresAt];
  798. NSString *token2 = [self createOfferForRealmAtURL:url user:self.userA
  799. accessLevel:RLMSyncAccessLevelWrite expiration:nil];
  800. id ex1 = [self expectationWithDescription:@"Retrieve offers"];
  801. [self.userA retrievePermissionOffersWithCallback:^(NSArray<RLMSyncPermissionOffer *> *offers, NSError *error) {
  802. XCTAssertNil(error);
  803. XCTAssertEqual(offers.count, 2U);
  804. for (RLMSyncPermissionOffer *offer in offers) {
  805. bool isFirst = [offer.token isEqualToString:token1];
  806. XCTAssertEqualObjects(offer.realmPath, expandedURL.path);
  807. XCTAssertGreaterThan(offer.createdAt.timeIntervalSince1970, createdAt.timeIntervalSince1970);
  808. if (isFirst) {
  809. XCTAssertEqualObjects(offer.token, token1);
  810. // May be up to a half ms off due to rounding
  811. XCTAssertLessThan(fabs([offer.expiresAt timeIntervalSinceDate:expiresAt]), 0.001);
  812. XCTAssertEqual(offer.accessLevel, RLMSyncAccessLevelRead);
  813. }
  814. else {
  815. XCTAssertEqualObjects(offer.token, token2);
  816. XCTAssertNil(offer.expiresAt);
  817. XCTAssertEqual(offer.accessLevel, RLMSyncAccessLevelWrite);
  818. }
  819. }
  820. [ex1 fulfill];
  821. }];
  822. [self waitForExpectations:@[ex1] timeout:10.0];
  823. // Invalidate one of the offers
  824. XCTestExpectation *ex2 = [self expectationWithDescription:@"Should invalidate a offer token."];
  825. [self.userA invalidateOfferForToken:token1 callback:^(NSError *error) {
  826. XCTAssertNil(error);
  827. [ex2 fulfill];
  828. }];
  829. [self waitForExpectations:@[ex2] timeout:10.0];
  830. // Verify that we only get non-invalidated offers
  831. id ex3 = [self expectationWithDescription:@"Retrieve offers"];
  832. [self.userA retrievePermissionOffersWithCallback:^(NSArray<RLMSyncPermissionOffer *> *offers, NSError *error) {
  833. XCTAssertNil(error);
  834. XCTAssertEqual(offers.count, 1U);
  835. RLMSyncPermissionOffer *offer = offers[0];
  836. XCTAssertEqualObjects(offer.realmPath, expandedURL.path);
  837. XCTAssertGreaterThan(offer.createdAt.timeIntervalSince1970, createdAt.timeIntervalSince1970);
  838. XCTAssertEqualObjects(offer.token, token2);
  839. XCTAssertNil(offer.expiresAt);
  840. XCTAssertEqual(offer.accessLevel, RLMSyncAccessLevelWrite);
  841. [ex3 fulfill];
  842. }];
  843. [self waitForExpectations:@[ex3] timeout:10.0];
  844. }
  845. #pragma mark - Delete Realm upon permission denied
  846. /// A Realm which is opened improperly should report an error allowing the app to recover.
  847. - (void)testDeleteRealmUponPermissionDenied {
  848. NSString *testName = NSStringFromSelector(_cmd);
  849. // Open a Realm for user A.
  850. NSURL *userAURL = makeTestURL(testName, nil);
  851. RLMRealm *userARealm = [self openRealmForURL:userAURL user:self.userA];
  852. // Have user A add some items to the Realm.
  853. [self addSyncObjectsToRealm:userARealm descriptions:@[@"child-1", @"child-2", @"child-3"]];
  854. [self waitForUploadsForRealm:userARealm];
  855. CHECK_COUNT(3, SyncObject, userARealm);
  856. // Give user B read permissions to that Realm.
  857. RLMSyncPermission *p = [[RLMSyncPermission alloc] initWithRealmPath:[userAURL path]
  858. identity:self.userB.identity
  859. accessLevel:RLMSyncAccessLevelRead];
  860. // Set the read permission.
  861. APPLY_PERMISSION(p, self.userA);
  862. NSURL *userBURL = makeTestURL(testName, self.userA);
  863. RLMRealmConfiguration *userBConfig = [self.userB configurationWithURL:userBURL fullSynchronization:YES];
  864. __block NSError *theError = nil;
  865. // Incorrectly open the Realm for user B.
  866. NSURL *onDiskPath;
  867. @autoreleasepool {
  868. NSString *sessionName = NSStringFromSelector(_cmd);
  869. XCTestExpectation *ex2 = [self expectationWithDescription:@"We should get a permission denied error."];
  870. RLMSyncManager.sharedManager.errorHandler = ^(NSError *err, RLMSyncSession *session) {
  871. // Make sure we're actually looking at the right session.
  872. XCTAssertTrue([[session.realmURL absoluteString] rangeOfString:sessionName].location != NSNotFound);
  873. theError = err;
  874. [ex2 fulfill];
  875. };
  876. __attribute__((objc_precise_lifetime)) RLMRealm *bad = [RLMRealm realmWithConfiguration:userBConfig error:nil];
  877. [self waitForExpectationsWithTimeout:10.0 handler:nil];
  878. onDiskPath = [RLMSyncTestCase onDiskPathForSyncedRealm:bad];
  879. }
  880. XCTAssertNotNil(onDiskPath);
  881. XCTAssertTrue([[NSFileManager defaultManager] fileExistsAtPath:[onDiskPath path]]);
  882. // Check the error and perform the Realm deletion.
  883. XCTAssertNotNil(theError);
  884. RLMSyncErrorActionToken *errorToken = [theError rlmSync_errorActionToken];
  885. XCTAssertNotNil(errorToken);
  886. [RLMSyncSession immediatelyHandleError:errorToken];
  887. // Ensure the file is no longer on disk.
  888. XCTAssertFalse([[NSFileManager defaultManager] fileExistsAtPath:[onDiskPath path]]);
  889. // Correctly open the same Realm for user B.
  890. __block RLMRealm *userBRealm = nil;
  891. XCTestExpectation *asyncOpenEx = [self expectationWithDescription:@"Should asynchronously open a Realm"];
  892. [RLMRealm asyncOpenWithConfiguration:userBConfig
  893. callbackQueue:dispatch_get_main_queue()
  894. callback:^(RLMRealm *realm, NSError *err){
  895. XCTAssertNil(err);
  896. XCTAssertNotNil(realm);
  897. userBRealm = realm;
  898. [asyncOpenEx fulfill];
  899. }];
  900. [self waitForExpectationsWithTimeout:10.0 handler:nil];
  901. CHECK_COUNT(3, SyncObject, userBRealm);
  902. }
  903. @end