RLMPermissionsAPITests.m 58 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196
  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. // FIXME: Many permission tests appears to fail with the ROS 3.0.0 alpha releases.
  20. #import "RLMSyncTestCase.h"
  21. #import "RLMTestUtils.h"
  22. #define APPLY_PERMISSION(ma_permission, ma_user) \
  23. APPLY_PERMISSION_WITH_MESSAGE(ma_permission, ma_user, @"Setting a permission should work")
  24. #define APPLY_PERMISSION_WITH_MESSAGE(ma_permission, ma_user, ma_message) { \
  25. XCTestExpectation *ex = [self expectationWithDescription:ma_message]; \
  26. [ma_user applyPermission:ma_permission callback:^(NSError *err) { \
  27. XCTAssertNil(err, @"Received an error when applying permission: %@", err); \
  28. [ex fulfill]; \
  29. }]; \
  30. [self waitForExpectationsWithTimeout:10.0 handler:nil]; \
  31. } \
  32. static NSURL *makeTestURL(NSString *name, RLMSyncUser *owner) {
  33. NSString *userID = [owner identity] ?: @"~";
  34. return [[NSURL alloc] initWithString:[NSString stringWithFormat:@"realm://127.0.0.1:9080/%@/%@", userID, name]];
  35. }
  36. static NSURL *makeTestGlobalURL(NSString *name) {
  37. return [[NSURL alloc] initWithString:[NSString stringWithFormat:@"realm://127.0.0.1:9080/%@", name]];
  38. }
  39. static NSURL *makeTildeSubstitutedURL(NSURL *url, RLMSyncUser *user) {
  40. return [NSURL URLWithString:[[url absoluteString] stringByReplacingOccurrencesOfString:@"~" withString:user.identity]];
  41. }
  42. @interface RLMPermissionsAPITests : RLMSyncTestCase
  43. @property (nonatomic, strong) NSString *currentUsernameBase;
  44. @property (nonatomic, strong) RLMSyncUser *userA;
  45. @property (nonatomic, strong) RLMSyncUser *userB;
  46. @property (nonatomic, strong) RLMSyncUser *userC;
  47. @property (nonatomic, strong) NSString *userBUsername;
  48. @end
  49. @implementation RLMPermissionsAPITests
  50. - (void)setUp {
  51. [super setUp];
  52. NSString *accountNameBase = [[NSUUID UUID] UUIDString];
  53. self.currentUsernameBase = accountNameBase;
  54. NSString *userNameA = [accountNameBase stringByAppendingString:@"a"];
  55. self.userA = [self logInUserForCredentials:[RLMSyncTestCase basicCredentialsWithName:userNameA register:YES]
  56. server:[RLMSyncTestCase authServerURL]];
  57. NSString *userNameB = [accountNameBase stringByAppendingString:@"b"];
  58. self.userBUsername = userNameB;
  59. self.userB = [self logInUserForCredentials:[RLMSyncTestCase basicCredentialsWithName:userNameB register:YES]
  60. server:[RLMSyncTestCase authServerURL]];
  61. NSString *userNameC = [accountNameBase stringByAppendingString:@"c"];
  62. self.userC = [self logInUserForCredentials:[RLMSyncTestCase basicCredentialsWithName:userNameC register:YES]
  63. server:[RLMSyncTestCase authServerURL]];
  64. }
  65. - (void)tearDown {
  66. self.currentUsernameBase = nil;
  67. [self.userA logOut];
  68. [self.userB logOut];
  69. [self.userC logOut];
  70. self.userBUsername = nil;
  71. [super tearDown];
  72. }
  73. #pragma mark - Permission validation methods
  74. // This macro is only used for the validation methods below.
  75. #define RECORD_FAILURE(ma_msg) [self recordFailureWithDescription:ma_msg inFile:file atLine:line expected:YES]
  76. #define CHECK_PERMISSION_PRESENT(ma_results, ma_permission) \
  77. [self checkPresenceOfPermission:ma_permission inResults:ma_results line:__LINE__ file:@(__FILE__)]
  78. /// Check that the targeted permission is present in, or eventually appears in the results.
  79. - (void)checkPresenceOfPermission:(RLMSyncPermission *)permission
  80. inResults:(RLMResults<RLMSyncPermission *> *)results
  81. line:(NSUInteger)line
  82. file:(NSString *)file {
  83. XCTestExpectation *ex = [self expectationWithDescription:@"Checking presence of permission..."];
  84. RLMNotificationToken *token = [results addNotificationBlock:^(RLMResults *r, __unused id c, NSError *err) {
  85. if (err) {
  86. RECORD_FAILURE(@"Failed to retrieve permissions.");
  87. [ex fulfill];
  88. return;
  89. }
  90. if ([r indexOfObject:permission] != NSNotFound) {
  91. [ex fulfill];
  92. }
  93. }];
  94. [self waitForExpectationsWithTimeout:2.0 handler:^(NSError *error) {
  95. if (error) {
  96. NSLog(@"Timed out. The final state of the permissions is %@; the desired permission was %@",
  97. results, permission);
  98. }
  99. }];
  100. [token invalidate];
  101. }
  102. #define CHECK_PERMISSION_ABSENT(ma_results, ma_permission) \
  103. [self checkAbsenceOfPermission:ma_permission inResults:ma_results line:__LINE__ file:@(__FILE__)]
  104. /// Check that the targeted permission is absent from, or eventually disappears from the results.
  105. - (void)checkAbsenceOfPermission:(RLMSyncPermission *)permission
  106. inResults:(RLMResults<RLMSyncPermission *> *)results
  107. line:(NSUInteger)line
  108. file:(NSString *)file {
  109. XCTestExpectation *ex = [self expectationWithDescription:@"Checking absence of permission..."];
  110. RLMNotificationToken *token = [results addNotificationBlock:^(RLMResults *r, __unused id c, NSError *err) {
  111. if (err) {
  112. RECORD_FAILURE(@"Failed to retrieve permissions.");
  113. [ex fulfill];
  114. return;
  115. }
  116. if ([r indexOfObject:permission] == NSNotFound) {
  117. [ex fulfill];
  118. }
  119. }];
  120. [self waitForExpectationsWithTimeout:2.0 handler:^(NSError *error) {
  121. if (error) {
  122. NSLog(@"Timed out. The final state of the permissions is %@; the permission to check for %@",
  123. results, permission);
  124. }
  125. }];
  126. [token invalidate];
  127. }
  128. #define CHECK_PERMISSION_COUNT_AT_LEAST(ma_results, ma_count) \
  129. [self checkPermissionCountOfResults:ma_results atLeast:ma_count exact:NO line:__LINE__ file:@(__FILE__)];
  130. #define CHECK_PERMISSION_COUNT(ma_results, ma_count) \
  131. [self checkPermissionCountOfResults:ma_results atLeast:ma_count exact:YES line:__LINE__ file:@(__FILE__)];
  132. - (void)checkPermissionCountOfResults:(RLMResults<RLMSyncPermission *> *)results
  133. atLeast:(NSInteger)count
  134. exact:(BOOL)exact
  135. line:(NSUInteger)line
  136. file:(NSString *)file {
  137. // Check first.
  138. if ((NSInteger)results.count == count || (!exact && (NSInteger)results.count > count)) {
  139. return;
  140. }
  141. XCTestExpectation *ex = [self expectationWithDescription:@"Checking presence of permission..."];
  142. RLMNotificationToken *token = [results addNotificationBlock:^(RLMResults *r, __unused id c, NSError *err) {
  143. if (err) {
  144. RECORD_FAILURE(@"Failed to retrieve permissions.");
  145. [ex fulfill];
  146. return;
  147. }
  148. NSInteger actualCount = (NSInteger)r.count;
  149. if (actualCount == count || (!exact && actualCount > count)) {
  150. [ex fulfill];
  151. return;
  152. }
  153. }];
  154. [self waitForExpectations:@[ex] timeout:20.0];
  155. [token invalidate];
  156. }
  157. #undef RECORD_FAILURE
  158. #pragma mark - Helper methods
  159. - (RLMResults<RLMSyncPermission *> *)getPermissionResultsFor:(RLMSyncUser *)user {
  160. return [self getPermissionResultsFor:user message:@"Get permission results"];
  161. }
  162. - (RLMResults<RLMSyncPermission *> *)getPermissionResultsFor:(RLMSyncUser *)user message:(NSString *)message {
  163. // Get a reference to the permission results.
  164. XCTestExpectation *ex = [self expectationWithDescription:message];
  165. __block RLMResults<RLMSyncPermission *> *results = nil;
  166. [user retrievePermissionsWithCallback:^(RLMResults<RLMSyncPermission *> *r, NSError *error) {
  167. XCTAssertNil(error);
  168. XCTAssertNotNil(r);
  169. results = r;
  170. [ex fulfill];
  171. }];
  172. [self waitForExpectationsWithTimeout:2.0 handler:nil];
  173. XCTAssertNotNil(results, @"getPermissionResultsFor: failed for user %@. No results.", user.identity);
  174. return results;
  175. }
  176. #pragma mark - Permissions
  177. // FIXME ROS 2.0: works when ROS is manually provided, not when ROS is run as part of tests
  178. /// If user A grants user B read access to a Realm, user B should be able to read from it.
  179. - (void)testReadAccess {
  180. __block void(^errorBlock)(NSError *) = nil;
  181. [[RLMSyncManager sharedManager] setErrorHandler:^(NSError *error, __unused RLMSyncSession *session) {
  182. if (errorBlock) {
  183. errorBlock(error);
  184. errorBlock = nil;
  185. } else {
  186. XCTFail(@"Error handler should not be called unless explicitly expected. Error: %@", error);
  187. }
  188. }];
  189. NSString *testName = NSStringFromSelector(_cmd);
  190. // Open a Realm for user A.
  191. NSURL *userAURL = makeTestURL(testName, nil);
  192. RLMRealm *userARealm = [self openRealmForURL:userAURL user:self.userA];
  193. // Have user A add some items to the Realm.
  194. [self addSyncObjectsToRealm:userARealm descriptions:@[@"child-1", @"child-2", @"child-3"]];
  195. [self waitForUploadsForUser:self.userA url:userAURL];
  196. CHECK_COUNT(3, SyncObject, userARealm);
  197. // Give user B read permissions to that Realm.
  198. RLMSyncPermission *p = [[RLMSyncPermission alloc] initWithRealmPath:[userAURL path]
  199. identity:self.userB.identity
  200. accessLevel:RLMSyncAccessLevelRead];
  201. // Set the read permission.
  202. APPLY_PERMISSION(p, self.userA);
  203. // Open the same Realm for user B.
  204. NSURL *userBURL = makeTestURL(testName, self.userA);
  205. RLMRealmConfiguration *userBConfig = [RLMRealmConfiguration defaultConfiguration];
  206. userBConfig.syncConfiguration = [[RLMSyncConfiguration alloc] initWithUser:self.userB realmURL:userBURL];
  207. __block RLMRealm *userBRealm = nil;
  208. XCTestExpectation *asyncOpenEx = [self expectationWithDescription:@"Should asynchronously open a Realm"];
  209. [RLMRealm asyncOpenWithConfiguration:userBConfig
  210. callbackQueue:dispatch_get_main_queue()
  211. callback:^(RLMRealm *realm, NSError *err){
  212. XCTAssertNil(err);
  213. XCTAssertNotNil(realm);
  214. userBRealm = realm;
  215. [asyncOpenEx fulfill];
  216. }];
  217. [self waitForExpectationsWithTimeout:10.0 handler:nil];
  218. CHECK_COUNT(3, SyncObject, userBRealm);
  219. // Ensure user B can't actually write to the Realm.
  220. // Run this portion of the test on a background queue, since the error handler is dispatched onto the main queue.
  221. XCTestExpectation *deniedEx = [self expectationWithDescription:@"Expect a permission denied error."];
  222. errorBlock = ^(NSError *err) {
  223. // Expect an error from the global error handler.
  224. XCTAssertNotNil(err);
  225. XCTAssertEqual(err.code, RLMSyncErrorPermissionDeniedError);
  226. [deniedEx fulfill];
  227. };
  228. [self addSyncObjectsToRealm:userBRealm descriptions:@[@"child-4", @"child-5", @"child-6"]];
  229. [self waitForExpectations:@[deniedEx] timeout:20.0];
  230. // TODO: if we can get the session itself we can check to see if it's been errored out (as expected).
  231. // Perhaps obviously, there should be no new objects.
  232. CHECK_COUNT_PENDING_DOWNLOAD(3, SyncObject, userARealm);
  233. // Administering the Realm should fail.
  234. RLMSyncPermission *p2 = [[RLMSyncPermission alloc] initWithRealmPath:[userBURL path]
  235. identity:self.userC.identity
  236. accessLevel:RLMSyncAccessLevelRead];
  237. XCTestExpectation *manageEx = [self expectationWithDescription:@"Managing a Realm you can't manage should fail."];
  238. [self.userB applyPermission:p2 callback:^(NSError *error) {
  239. XCTAssertNotNil(error);
  240. [manageEx fulfill];
  241. }];
  242. [self waitForExpectationsWithTimeout:2.0 handler:nil];
  243. }
  244. /// If user A grants user B write access to a Realm, user B should be able to write to it.
  245. - (void)testWriteAccess {
  246. __block void(^errorBlock)(NSError *) = nil;
  247. [[RLMSyncManager sharedManager] setErrorHandler:^(NSError *error, __unused RLMSyncSession *session) {
  248. if (errorBlock) {
  249. errorBlock(error);
  250. errorBlock = nil;
  251. } else {
  252. XCTFail(@"Error handler should not be called unless explicitly expected. Error: %@", error);
  253. }
  254. }];
  255. NSString *testName = NSStringFromSelector(_cmd);
  256. // Open a Realm for user A.
  257. NSURL *userAURL = makeTestURL(testName, nil);
  258. RLMRealm *userARealm = [self openRealmForURL:userAURL user:self.userA];
  259. // Have user A add some items to the Realm.
  260. [self addSyncObjectsToRealm:userARealm descriptions:@[@"child-1", @"child-2", @"child-3"]];
  261. [self waitForUploadsForUser:self.userA url:userAURL];
  262. CHECK_COUNT(3, SyncObject, userARealm);
  263. // Give user B write permissions to that Realm.
  264. RLMSyncPermission *p = [[RLMSyncPermission alloc] initWithRealmPath:[userAURL path]
  265. identity:self.userB.identity
  266. accessLevel:RLMSyncAccessLevelWrite];
  267. // Set the permission.
  268. APPLY_PERMISSION(p, self.userA);
  269. // Open the Realm for user B. Since user B has write privileges, they should be able to open it 'normally'.
  270. NSURL *userBURL = makeTestURL(testName, self.userA);
  271. RLMRealm *userBRealm = [self openRealmForURL:userBURL user:self.userB];
  272. CHECK_COUNT_PENDING_DOWNLOAD(3, SyncObject, userBRealm);
  273. // Add some objects using user B.
  274. [self addSyncObjectsToRealm:userBRealm descriptions:@[@"child-4", @"child-5"]];
  275. [self waitForUploadsForUser:self.userB url:userBURL];
  276. CHECK_COUNT(5, SyncObject, userBRealm);
  277. CHECK_COUNT_PENDING_DOWNLOAD(5, SyncObject, userARealm);
  278. // Administering the Realm should fail.
  279. RLMSyncPermission *p2 = [[RLMSyncPermission alloc] initWithRealmPath:[userBURL path]
  280. identity:self.userC.identity
  281. accessLevel:RLMSyncAccessLevelRead];
  282. XCTestExpectation *manageEx = [self expectationWithDescription:@"Managing a Realm you can't manage should fail."];
  283. [self.userB applyPermission:p2 callback:^(NSError *error) {
  284. XCTAssertNotNil(error);
  285. [manageEx fulfill];
  286. }];
  287. [self waitForExpectationsWithTimeout:2.0 handler:nil];
  288. }
  289. /// If user A grants user B manage access to a Realm, user B should be able to set a permission for user C.
  290. - (void)testManageAccess {
  291. __block void(^errorBlock)(NSError *) = nil;
  292. [[RLMSyncManager sharedManager] setErrorHandler:^(NSError *error, __unused RLMSyncSession *session) {
  293. if (errorBlock) {
  294. errorBlock(error);
  295. errorBlock = nil;
  296. } else {
  297. XCTFail(@"Error handler should not be called unless explicitly expected. Error: %@", error);
  298. }
  299. }];
  300. NSString *testName = NSStringFromSelector(_cmd);
  301. // Unresolved URL: ~/testManageAccess
  302. NSURL *userAURLUnresolved = makeTestURL(testName, nil);
  303. // Resolved URL: <User A ID>/testManageAccess
  304. NSURL *userAURLResolved = makeTestURL(testName, self.userA);
  305. // Open a Realm for user A.
  306. RLMRealm *userARealm = [self openRealmForURL:userAURLUnresolved user:self.userA];
  307. // Have user A add some items to the Realm.
  308. [self addSyncObjectsToRealm:userARealm descriptions:@[@"child-1", @"child-2", @"child-3"]];
  309. [self waitForUploadsForUser:self.userA url:userAURLUnresolved];
  310. CHECK_COUNT(3, SyncObject, userARealm);
  311. // Give user B admin permissions to that Realm.
  312. RLMSyncPermission *p = [[RLMSyncPermission alloc] initWithRealmPath:[userAURLUnresolved path]
  313. identity:self.userB.identity
  314. accessLevel:RLMSyncAccessLevelAdmin];
  315. // Set the permission.
  316. APPLY_PERMISSION(p, self.userA);
  317. // Open the Realm for user B. Since user B has admin privileges, they should be able to open it 'normally'.
  318. RLMRealm *userBRealm = [self openRealmForURL:userAURLResolved user:self.userB];
  319. CHECK_COUNT_PENDING_DOWNLOAD(3, SyncObject, userBRealm);
  320. // Add some objects using user B.
  321. [self addSyncObjectsToRealm:userBRealm descriptions:@[@"child-4", @"child-5"]];
  322. [self waitForUploadsForUser:self.userB url:userAURLResolved];
  323. CHECK_COUNT(5, SyncObject, userBRealm);
  324. CHECK_COUNT_PENDING_DOWNLOAD(5, SyncObject, userARealm);
  325. // User B should be able to give user C write permissions to user A's Realm.
  326. RLMSyncPermission *p2 = [[RLMSyncPermission alloc] initWithRealmPath:[userAURLResolved path]
  327. identity:self.userC.identity
  328. accessLevel:RLMSyncAccessLevelWrite];
  329. APPLY_PERMISSION_WITH_MESSAGE(p2, self.userB, @"User B should be able to give C write permissions to A's Realm.");
  330. // User C should be able to write to the Realm.
  331. RLMRealm *userCRealm = [self openRealmForURL:userAURLResolved user:self.userC];
  332. CHECK_COUNT_PENDING_DOWNLOAD(5, SyncObject, userCRealm);
  333. [self addSyncObjectsToRealm:userCRealm descriptions:@[@"child-6", @"child-7", @"child-8"]];
  334. [self waitForUploadsForUser:self.userC url:userAURLResolved];
  335. CHECK_COUNT(8, SyncObject, userCRealm);
  336. CHECK_COUNT_PENDING_DOWNLOAD(8, SyncObject, userARealm);
  337. CHECK_COUNT_PENDING_DOWNLOAD(8, SyncObject, userBRealm);
  338. }
  339. /// If user A grants user B write access to a Realm via username, user B should be able to write to it.
  340. - (void)testWriteAccessViaUsername {
  341. __block void(^workBlock)(NSError *) = ^(NSError *err) {
  342. XCTFail(@"Error handler should not be called unless explicitly expected. Error: %@", err);
  343. };
  344. [[RLMSyncManager sharedManager] setErrorHandler:^(NSError *error, __unused RLMSyncSession *session) {
  345. if (workBlock) {
  346. workBlock(error);
  347. }
  348. }];
  349. NSString *testName = NSStringFromSelector(_cmd);
  350. // Open a Realm for user A.
  351. NSURL *userAURL = makeTestURL(testName, nil);
  352. RLMRealm *userARealm = [self openRealmForURL:userAURL user:self.userA];
  353. // Have user A add some items to the Realm.
  354. [self addSyncObjectsToRealm:userARealm descriptions:@[@"child-1", @"child-2", @"child-3"]];
  355. [self waitForUploadsForUser:self.userA url:userAURL];
  356. CHECK_COUNT(3, SyncObject, userARealm);
  357. // Give user B write permissions to that Realm via user B's username.
  358. NSString *userAFullPath = [makeTildeSubstitutedURL(userAURL, self.userA) path];
  359. RLMSyncPermission *p = [[RLMSyncPermission alloc] initWithRealmPath:userAFullPath
  360. username:self.userBUsername
  361. accessLevel:RLMSyncAccessLevelWrite];
  362. // Set the permission.
  363. APPLY_PERMISSION(p, self.userA);
  364. // Open the Realm for user B. Since user B has write privileges, they should be able to open it 'normally'.
  365. NSURL *userBURL = makeTestURL(testName, self.userA);
  366. RLMRealm *userBRealm = [self openRealmForURL:userBURL user:self.userB];
  367. CHECK_COUNT_PENDING_DOWNLOAD(3, SyncObject, userBRealm);
  368. // Add some objects using user B.
  369. [self addSyncObjectsToRealm:userBRealm descriptions:@[@"child-4", @"child-5"]];
  370. [self waitForUploadsForUser:self.userB url:userBURL];
  371. CHECK_COUNT(5, SyncObject, userBRealm);
  372. CHECK_COUNT_PENDING_DOWNLOAD(5, SyncObject, userARealm);
  373. }
  374. /// Setting a permission for all users should work.
  375. - (void)testWildcardWriteAccess {
  376. // Open a Realm for user A.
  377. NSString *testName = NSStringFromSelector(_cmd);
  378. NSURL *ownerURL = makeTestURL(testName, nil);
  379. NSURL *guestURL = makeTestURL(testName, self.userA);
  380. RLMRealm *userARealm = [self openRealmForURL:ownerURL user:self.userA];
  381. // Give all users write permissions to that Realm.
  382. RLMSyncPermission *p = [[RLMSyncPermission alloc] initWithRealmPath:[ownerURL path]
  383. identity:@"*"
  384. accessLevel:RLMSyncAccessLevelWrite];
  385. // Set the permission.
  386. APPLY_PERMISSION(p, self.userA);
  387. // Have user A write a few objects first.
  388. [self addSyncObjectsToRealm:userARealm descriptions:@[@"child-1", @"child-2", @"child-3"]];
  389. [self waitForUploadsForUser:self.userA url:ownerURL];
  390. CHECK_COUNT(3, SyncObject, userARealm);
  391. // User B should be able to write to the Realm.
  392. RLMRealm *userBRealm = [self openRealmForURL:guestURL user:self.userB];
  393. CHECK_COUNT_PENDING_DOWNLOAD(3, SyncObject, userBRealm);
  394. [self addSyncObjectsToRealm:userBRealm descriptions:@[@"child-4", @"child-5"]];
  395. [self waitForUploadsForUser:self.userB url:guestURL];
  396. CHECK_COUNT(5, SyncObject, userBRealm);
  397. // User C should be able to write to the Realm.
  398. RLMRealm *userCRealm = [self openRealmForURL:guestURL user:self.userC];
  399. CHECK_COUNT_PENDING_DOWNLOAD(5, SyncObject, userCRealm);
  400. [self addSyncObjectsToRealm:userCRealm descriptions:@[@"child-6", @"child-7", @"child-8", @"child-9"]];
  401. [self waitForUploadsForUser:self.userC url:guestURL];
  402. CHECK_COUNT(9, SyncObject, userCRealm);
  403. }
  404. /// It should be possible to grant read-only access to a global Realm.
  405. - (void)testWildcardGlobalRealmReadAccess {
  406. RLMSyncUser *admin = [self createAdminUserForURL:[RLMSyncTestCase authServerURL]
  407. username:[[NSUUID UUID] UUIDString]];
  408. // Open a Realm for the admin user.
  409. NSString *testName = NSStringFromSelector(_cmd);
  410. NSURL *globalRealmURL = makeTestGlobalURL(testName);
  411. RLMRealm *adminUserRealm = [self openRealmForURL:globalRealmURL user:admin];
  412. // Give all users read permissions to that Realm.
  413. RLMSyncPermission *p = [[RLMSyncPermission alloc] initWithRealmPath:[globalRealmURL path]
  414. identity:@"*"
  415. accessLevel:RLMSyncAccessLevelRead];
  416. // Set the permission.
  417. APPLY_PERMISSION_WITH_MESSAGE(p, admin, @"Setting wildcard permission should work.");
  418. // Have the admin user write a few objects first.
  419. [self addSyncObjectsToRealm:adminUserRealm descriptions:@[@"child-1", @"child-2", @"child-3"]];
  420. [self waitForUploadsForUser:admin url:globalRealmURL];
  421. CHECK_COUNT(3, SyncObject, adminUserRealm);
  422. // User B should be able to read from the Realm.
  423. __block RLMRealm *userBRealm = nil;
  424. RLMRealmConfiguration *userBConfig = [RLMRealmConfiguration defaultConfiguration];
  425. userBConfig.syncConfiguration = [[RLMSyncConfiguration alloc] initWithUser:self.userB realmURL:globalRealmURL];
  426. XCTestExpectation *asyncOpenEx = [self expectationWithDescription:@"Should asynchronously open a Realm"];
  427. [RLMRealm asyncOpenWithConfiguration:userBConfig
  428. callbackQueue:dispatch_get_main_queue()
  429. callback:^(RLMRealm *realm, NSError *err){
  430. XCTAssertNil(err);
  431. XCTAssertNotNil(realm);
  432. userBRealm = realm;
  433. [asyncOpenEx fulfill];
  434. }];
  435. [self waitForExpectationsWithTimeout:10.0 handler:nil];
  436. CHECK_COUNT(3, SyncObject, userBRealm);
  437. // User C should be able to read from the Realm.
  438. __block RLMRealm *userCRealm = nil;
  439. RLMRealmConfiguration *userCConfig = [RLMRealmConfiguration defaultConfiguration];
  440. userCConfig.syncConfiguration = [[RLMSyncConfiguration alloc] initWithUser:self.userC realmURL:globalRealmURL];
  441. XCTestExpectation *asyncOpenEx2 = [self expectationWithDescription:@"Should asynchronously open a Realm"];
  442. [RLMRealm asyncOpenWithConfiguration:userCConfig
  443. callbackQueue:dispatch_get_main_queue()
  444. callback:^(RLMRealm *realm, NSError *err){
  445. XCTAssertNil(err);
  446. XCTAssertNotNil(realm);
  447. userCRealm = realm;
  448. [asyncOpenEx2 fulfill];
  449. }];
  450. [self waitForExpectationsWithTimeout:10.0 handler:nil];
  451. CHECK_COUNT(3, SyncObject, userCRealm);
  452. }
  453. /// Setting a permission for all users on a global Realm (no `~`) should work.
  454. - (void)testWildcardGlobalRealmWriteAccess {
  455. RLMSyncUser *admin = [self createAdminUserForURL:[RLMSyncTestCase authServerURL]
  456. username:[[NSUUID UUID] UUIDString]];
  457. // Open a Realm for the admin user.
  458. NSString *testName = NSStringFromSelector(_cmd);
  459. NSURL *globalRealmURL = makeTestGlobalURL(testName);
  460. RLMRealm *adminUserRealm = [self openRealmForURL:globalRealmURL user:admin];
  461. // Give all users write permissions to that Realm.
  462. RLMSyncPermission *p = [[RLMSyncPermission alloc] initWithRealmPath:[globalRealmURL path]
  463. identity:@"*"
  464. accessLevel:RLMSyncAccessLevelWrite];
  465. // Set the permission.
  466. APPLY_PERMISSION(p, admin);
  467. // Have the admin user write a few objects first.
  468. [self addSyncObjectsToRealm:adminUserRealm descriptions:@[@"child-1", @"child-2", @"child-3"]];
  469. [self waitForUploadsForUser:admin url:globalRealmURL];
  470. CHECK_COUNT(3, SyncObject, adminUserRealm);
  471. // User B should be able to write to the Realm.
  472. RLMRealm *userBRealm = [self openRealmForURL:globalRealmURL user:self.userB];
  473. CHECK_COUNT_PENDING_DOWNLOAD(3, SyncObject, userBRealm);
  474. [self addSyncObjectsToRealm:userBRealm descriptions:@[@"child-4", @"child-5"]];
  475. [self waitForUploadsForUser:self.userB url:globalRealmURL];
  476. CHECK_COUNT(5, SyncObject, userBRealm);
  477. // User C should be able to write to the Realm.
  478. RLMRealm *userCRealm = [self openRealmForURL:globalRealmURL user:self.userC];
  479. CHECK_COUNT_PENDING_DOWNLOAD(5, SyncObject, userCRealm);
  480. [self addSyncObjectsToRealm:userCRealm descriptions:@[@"child-6", @"child-7", @"child-8", @"child-9"]];
  481. [self waitForUploadsForUser:self.userC url:globalRealmURL];
  482. CHECK_COUNT(9, SyncObject, userCRealm);
  483. }
  484. #pragma mark - Permission change API
  485. /// Setting a permission should work, and then that permission should be able to be retrieved.
  486. - (void)testSettingPermission {
  487. // First, there should be no permissions.
  488. RLMResults<RLMSyncPermission *> *results = [self getPermissionResultsFor:self.userA];
  489. CHECK_PERMISSION_COUNT(results, 0);
  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. results = [self getPermissionResultsFor:self.userB message:@"One permission after setting the permission."];
  501. // Expected permission: applies to user B, but for user A's Realm.
  502. CHECK_PERMISSION_PRESENT(results, p);
  503. // Check getting permission by its index.
  504. NSUInteger index = [results indexOfObject:p];
  505. XCTAssertNotEqual(index, NSNotFound);
  506. XCTAssertEqualObjects(p, [results objectAtIndex:index]);
  507. }
  508. /// Deleting a permission should work.
  509. - (void)testDeletingPermission {
  510. // Open a Realm for user A.
  511. NSURL *url = REALM_URL();
  512. [self openRealmForURL:url user:self.userA];
  513. // Give user B read permissions to that Realm.
  514. RLMSyncPermission *p = [[RLMSyncPermission alloc] initWithRealmPath:[makeTildeSubstitutedURL(url, self.userA) path]
  515. identity:self.userB.identity
  516. accessLevel:RLMSyncAccessLevelRead];
  517. // Set the permission.
  518. APPLY_PERMISSION(p, self.userA);
  519. // Now retrieve the permissions again and make sure the new permission is properly set.
  520. RLMResults<RLMSyncPermission *> *results = [self getPermissionResultsFor:self.userB
  521. message:@"Setting new permission."];
  522. CHECK_PERMISSION_PRESENT(results, p);
  523. // Delete the permission.
  524. XCTestExpectation *ex3 = [self expectationWithDescription:@"Deleting a permission should work."];
  525. [self.userA revokePermission:p callback:^(NSError *error) {
  526. XCTAssertNil(error);
  527. [ex3 fulfill];
  528. }];
  529. [self waitForExpectationsWithTimeout:2.0 handler:nil];
  530. // Make sure the permission deletion is properly reflected.
  531. CHECK_PERMISSION_COUNT(results, 0);
  532. }
  533. /// Observing permission changes should work.
  534. - (void)testObservingPermission {
  535. // Get a reference to the permission results.
  536. RLMResults<RLMSyncPermission *> *results = [self getPermissionResultsFor:self.userB];
  537. // Open a Realm for user A.
  538. NSURL *url = REALM_URL();
  539. [self openRealmForURL:url user:self.userA];
  540. // Register notifications.
  541. XCTestExpectation *noteEx = [self expectationWithDescription:@"Notification should fire."];
  542. RLMNotificationToken *token = [results addNotificationBlock:^(__unused id r, __unused id c, NSError *error) {
  543. XCTAssertNil(error);
  544. if (results.count > 0) {
  545. [noteEx fulfill];
  546. }
  547. }];
  548. // Give user B read permissions to that Realm.
  549. RLMSyncPermission *p = [[RLMSyncPermission alloc] initWithRealmPath:[makeTildeSubstitutedURL(url, self.userA) path]
  550. identity:self.userB.identity
  551. accessLevel:RLMSyncAccessLevelRead];
  552. // Set the permission.
  553. APPLY_PERMISSION(p, self.userA);
  554. // Wait for the notification to be fired.
  555. [self waitForExpectations:@[noteEx] timeout:2.0];
  556. [token invalidate];
  557. CHECK_PERMISSION_PRESENT(results, p);
  558. }
  559. /// KVC getting and setting should work properly for `RLMResults<RLMSyncPermission>`.
  560. - (void)testKVCWithPermissionsResults {
  561. // Get a reference to the permission results.
  562. RLMResults<RLMSyncPermission *> *results = [self getPermissionResultsFor:self.userB];
  563. NSURL *url1 = CUSTOM_REALM_URL(@"r1");
  564. NSURL *url2 = CUSTOM_REALM_URL(@"r2");
  565. __attribute__((objc_precise_lifetime)) RLMRealm *r1 = [self openRealmForURL:url1 user:self.userA];
  566. __attribute__((objc_precise_lifetime)) RLMRealm *r2 = [self openRealmForURL:url2 user:self.userA];
  567. NSString *uB = self.userB.identity;
  568. // Give user B read permissions to r1 and r2.
  569. NSString *path1 = [makeTildeSubstitutedURL(url1, self.userA) path];
  570. id p1 = [[RLMSyncPermission alloc] initWithRealmPath:path1
  571. identity:uB
  572. accessLevel:RLMSyncAccessLevelRead];
  573. APPLY_PERMISSION_WITH_MESSAGE(p1, self.userA, @"Setting r1 permission for user B should work.");
  574. NSString *path2 = [makeTildeSubstitutedURL(url2, self.userA) path];
  575. id p2 = [[RLMSyncPermission alloc] initWithRealmPath:path2
  576. identity:uB
  577. accessLevel:RLMSyncAccessLevelRead];
  578. APPLY_PERMISSION_WITH_MESSAGE(p2, self.userA, @"Setting r2 permission for user B should work.");
  579. // Wait for all the permissions to show up.
  580. CHECK_PERMISSION_PRESENT(results, p1);
  581. CHECK_PERMISSION_PRESENT(results, p2);
  582. // Now use `valueForKey`
  583. NSArray *selfValues = [results valueForKey:@"self"];
  584. XCTAssert(selfValues.count == results.count);
  585. for (id object in selfValues) {
  586. XCTAssert([object isKindOfClass:[RLMSyncPermission class]]);
  587. }
  588. NSArray *identityValues = [results valueForKey:@"path"];
  589. XCTAssert(identityValues.count == results.count);
  590. XCTAssert([identityValues containsObject:path1]);
  591. XCTAssert([identityValues containsObject:path2]);
  592. // Since `RLMSyncPermission`s are read-only, KVC setting should fail.
  593. RLMAssertThrows([results setValue:@"foobar" forKey:@"path"]);
  594. }
  595. /// Filtering permissions results should work.
  596. - (void)testFilteringPermissions {
  597. // Get a reference to the permission results.
  598. RLMResults<RLMSyncPermission *> *results = [self getPermissionResultsFor:self.userB];
  599. // Open two Realms
  600. NSURL *url1 = CUSTOM_REALM_URL(@"r1");
  601. NSURL *url2 = CUSTOM_REALM_URL(@"r2");
  602. NSURL *url3 = CUSTOM_REALM_URL(@"r3");
  603. __attribute__((objc_precise_lifetime)) RLMRealm *r1 = [self openRealmForURL:url1 user:self.userA];
  604. __attribute__((objc_precise_lifetime)) RLMRealm *r2 = [self openRealmForURL:url2 user:self.userA];
  605. __attribute__((objc_precise_lifetime)) RLMRealm *r3 = [self openRealmForURL:url3 user:self.userA];
  606. NSString *uB = self.userB.identity;
  607. // Give user B permissions to realms r1, r2, and r3.
  608. id p1 = [[RLMSyncPermission alloc] initWithRealmPath:[makeTildeSubstitutedURL(url1, self.userA) path]
  609. identity:uB
  610. accessLevel:RLMSyncAccessLevelRead];
  611. APPLY_PERMISSION_WITH_MESSAGE(p1, self.userA, @"Setting r1 permission for user B should work.");
  612. NSString *finalPath = [makeTildeSubstitutedURL(url2, self.userA) path];
  613. id p2 = [[RLMSyncPermission alloc] initWithRealmPath:finalPath
  614. identity:uB
  615. accessLevel:RLMSyncAccessLevelRead];
  616. APPLY_PERMISSION_WITH_MESSAGE(p2, self.userA, @"Setting r2 permission for user B should work.");
  617. id p3 = [[RLMSyncPermission alloc] initWithRealmPath:[makeTildeSubstitutedURL(url3, self.userA) path]
  618. identity:uB
  619. accessLevel:RLMSyncAccessLevelRead];
  620. APPLY_PERMISSION_WITH_MESSAGE(p3, self.userA, @"Setting r3 permission for user B should work.");
  621. // Wait for all the permissions to show up.
  622. CHECK_PERMISSION_PRESENT(results, p1);
  623. CHECK_PERMISSION_PRESENT(results, p2);
  624. CHECK_PERMISSION_PRESENT(results, p3);
  625. // Now make a filter.
  626. RLMResults<RLMSyncPermission *> *filtered = [results objectsWithPredicate:[NSPredicate predicateWithFormat:@"%K == %@",
  627. RLMSyncPermissionSortPropertyPath,
  628. finalPath]];
  629. CHECK_PERMISSION_ABSENT(filtered, p1);
  630. CHECK_PERMISSION_PRESENT(filtered, p2);
  631. CHECK_PERMISSION_ABSENT(filtered, p3);
  632. }
  633. - (void)testSortingPermissionsOnUserID {
  634. // Get a reference to my own permission results.
  635. RLMResults<RLMSyncPermission *> *results = [self getPermissionResultsFor:self.userA];
  636. // Open my Realm.
  637. NSURL *url = REALM_URL();
  638. __attribute__((objc_precise_lifetime)) RLMRealm *r = [self openRealmForURL:url user:self.userA];
  639. // Give users B and C access to my Realm.
  640. id p1 = [[RLMSyncPermission alloc] initWithRealmPath:[makeTildeSubstitutedURL(url, self.userA) path]
  641. identity:self.userB.identity
  642. accessLevel:RLMSyncAccessLevelRead];
  643. APPLY_PERMISSION_WITH_MESSAGE(p1, self.userA, @"Setting r permission for user B should work.");
  644. id p2 = [[RLMSyncPermission alloc] initWithRealmPath:[makeTildeSubstitutedURL(url, self.userA) path]
  645. identity:self.userC.identity
  646. accessLevel:RLMSyncAccessLevelRead];
  647. APPLY_PERMISSION_WITH_MESSAGE(p2, self.userA, @"Setting r permission for user C should work.");
  648. // Now sort on user ID.
  649. RLMResults<RLMSyncPermission *> *sorted = [results sortedResultsUsingKeyPath:RLMSyncPermissionSortPropertyUserID
  650. ascending:YES];
  651. // Wait for changes to propagate
  652. CHECK_PERMISSION_COUNT(sorted, 3);
  653. NSMutableArray *sortedIDs = [NSMutableArray array];
  654. for (NSUInteger i = 0; i < sorted.count; i++) {
  655. [sortedIDs addObject:[sorted objectAtIndex:i].identity];
  656. }
  657. // Make sure the IDs in sortedIDs are actually sorted.
  658. for (NSUInteger i = 0; i < sorted.count - 1; i++) {
  659. XCTAssertEqual([sortedIDs[i] compare:sortedIDs[i + 1]], NSOrderedAscending);
  660. }
  661. // Make sure the IDs in sortedIDs contain all 3 users' IDs.
  662. NSSet *sortedIDSet = [NSSet setWithArray:sortedIDs];
  663. XCTAssertTrue([sortedIDSet containsObject:self.userA.identity]);
  664. XCTAssertTrue([sortedIDSet containsObject:self.userB.identity]);
  665. XCTAssertTrue([sortedIDSet containsObject:self.userC.identity]);
  666. }
  667. - (void)testSortingPermissionsOnPath {
  668. // Get a reference to the permission results.
  669. RLMResults<RLMSyncPermission *> *results = [self getPermissionResultsFor:self.userB];
  670. // Open three Realms
  671. NSURL *url1 = CUSTOM_REALM_URL(@"r1");
  672. NSURL *url2 = CUSTOM_REALM_URL(@"r2");
  673. NSURL *url3 = CUSTOM_REALM_URL(@"r3");
  674. __attribute__((objc_precise_lifetime)) RLMRealm *r1 = [self openRealmForURL:url1 user:self.userA];
  675. __attribute__((objc_precise_lifetime)) RLMRealm *r2 = [self openRealmForURL:url2 user:self.userA];
  676. __attribute__((objc_precise_lifetime)) RLMRealm *r3 = [self openRealmForURL:url3 user:self.userA];
  677. NSString *uB = self.userB.identity;
  678. // Give user B read permissions for all three Realms.
  679. id p1 = [[RLMSyncPermission alloc] initWithRealmPath:[makeTildeSubstitutedURL(url1, self.userA) path]
  680. identity:uB
  681. accessLevel:RLMSyncAccessLevelRead];
  682. APPLY_PERMISSION_WITH_MESSAGE(p1, self.userA, @"Setting r1 permission for user B should work.");
  683. id p2 = [[RLMSyncPermission alloc] initWithRealmPath:[makeTildeSubstitutedURL(url2, self.userA) path]
  684. identity:uB
  685. accessLevel:RLMSyncAccessLevelRead];
  686. APPLY_PERMISSION_WITH_MESSAGE(p2, self.userA, @"Setting r2 permission for user B should work.");
  687. id p3 = [[RLMSyncPermission alloc] initWithRealmPath:[makeTildeSubstitutedURL(url3, self.userA) path]
  688. identity:uB
  689. accessLevel:RLMSyncAccessLevelRead];
  690. APPLY_PERMISSION_WITH_MESSAGE(p3, self.userA, @"Setting r3 permission for user B should work.");
  691. // Now sort on Realm URL.
  692. RLMResults<RLMSyncPermission *> *sorted = [results sortedResultsUsingKeyPath:RLMSyncPermissionSortPropertyPath
  693. ascending:YES];
  694. // Wait for changes to propagate
  695. CHECK_PERMISSION_COUNT(sorted, 3);
  696. CHECK_PERMISSION_PRESENT(sorted, p1);
  697. CHECK_PERMISSION_PRESENT(sorted, p2);
  698. CHECK_PERMISSION_PRESENT(sorted, p3);
  699. NSUInteger idx1 = [sorted indexOfObject:p1];
  700. NSUInteger idx2 = [sorted indexOfObject:p2];
  701. NSUInteger idx3 = [sorted indexOfObject:p3];
  702. // Make sure they are actually in ascending order.
  703. XCTAssertNotEqual(idx1, NSNotFound);
  704. XCTAssertNotEqual(idx2, NSNotFound);
  705. XCTAssertNotEqual(idx3, NSNotFound);
  706. XCTAssertLessThan(idx1, idx2);
  707. XCTAssertLessThan(idx2, idx3);
  708. }
  709. - (void)testSortingPermissionsOnDate {
  710. // Get a reference to the permission results.
  711. RLMResults<RLMSyncPermission *> *results = [self getPermissionResultsFor:self.userB];
  712. // Open three Realms
  713. NSURL *url1 = CUSTOM_REALM_URL(@"-r1");
  714. NSURL *url2 = CUSTOM_REALM_URL(@"-r2");
  715. NSURL *url3 = CUSTOM_REALM_URL(@"-r3");
  716. __attribute__((objc_precise_lifetime)) RLMRealm *r1 = [self openRealmForURL:url1 user:self.userA];
  717. __attribute__((objc_precise_lifetime)) RLMRealm *r2 = [self openRealmForURL:url2 user:self.userA];
  718. __attribute__((objc_precise_lifetime)) RLMRealm *r3 = [self openRealmForURL:url3 user:self.userA];
  719. NSString *uB = self.userB.identity;
  720. // Give user B read permissions for all three Realms.
  721. id p1 = [[RLMSyncPermission alloc] initWithRealmPath:[makeTildeSubstitutedURL(url3, self.userA) path]
  722. identity:uB
  723. accessLevel:RLMSyncAccessLevelRead];
  724. APPLY_PERMISSION_WITH_MESSAGE(p1, self.userA, @"Setting r3 permission for user B should work.");
  725. id p2 = [[RLMSyncPermission alloc] initWithRealmPath:[makeTildeSubstitutedURL(url1, self.userA) path]
  726. identity:uB
  727. accessLevel:RLMSyncAccessLevelRead];
  728. APPLY_PERMISSION_WITH_MESSAGE(p2, self.userA, @"Setting r1 permission for user B should work.");
  729. id p3 = [[RLMSyncPermission alloc] initWithRealmPath:[makeTildeSubstitutedURL(url2, self.userA) path]
  730. identity:uB
  731. accessLevel:RLMSyncAccessLevelRead];
  732. APPLY_PERMISSION_WITH_MESSAGE(p3, self.userA, @"Setting r2 permission for user B should work.");
  733. // Now sort on date.
  734. RLMResults<RLMSyncPermission *> *sorted = [results sortedResultsUsingKeyPath:RLMSyncPermissionSortPropertyUpdated
  735. ascending:YES];
  736. // Wait for changes to propagate
  737. CHECK_PERMISSION_COUNT(sorted, 3);
  738. RLMSyncPermission *n1 = [sorted objectAtIndex:0];
  739. RLMSyncPermission *n2 = [sorted objectAtIndex:1];
  740. RLMSyncPermission *n3 = [sorted objectAtIndex:2];
  741. XCTAssertTrue([n1.path rangeOfString:@"r3"].location != NSNotFound);
  742. XCTAssertTrue([n2.path rangeOfString:@"r1"].location != NSNotFound);
  743. XCTAssertTrue([n3.path rangeOfString:@"r2"].location != NSNotFound);
  744. // Make sure they are actually in ascending order.
  745. XCTAssertLessThan([n1.updatedAt timeIntervalSinceReferenceDate], [n2.updatedAt timeIntervalSinceReferenceDate]);
  746. XCTAssertLessThan([n2.updatedAt timeIntervalSinceReferenceDate], [n3.updatedAt timeIntervalSinceReferenceDate]);
  747. }
  748. - (void)testPermissionResultsIndexOfObject {
  749. // Get a reference to the permission results.
  750. RLMResults<RLMSyncPermission *> *results = [self getPermissionResultsFor:self.userB];
  751. NSString *uB = self.userB.identity;
  752. // Have A open a Realm and grant a permission to B.
  753. NSURL *url = REALM_URL();
  754. NSString *tildeSubstitutedPath = [makeTildeSubstitutedURL(url, self.userA) path];
  755. __attribute__((objc_precise_lifetime)) RLMRealm *r = [self openRealmForURL:url user:self.userA];
  756. id p1 = [[RLMSyncPermission alloc] initWithRealmPath:tildeSubstitutedPath
  757. identity:uB
  758. accessLevel:RLMSyncAccessLevelRead];
  759. APPLY_PERMISSION_WITH_MESSAGE(p1, self.userA, @"Setting r permission for user B should work.");
  760. // Wait for the permission to show up.
  761. CHECK_PERMISSION_COUNT(results, 1);
  762. // Should be able to get the permission based on the actual permission.
  763. XCTAssertEqual(((NSInteger)[results indexOfObject:p1]), 0);
  764. // A permission with a differing access level should not match.
  765. id p2 = [[RLMSyncPermission alloc] initWithRealmPath:tildeSubstitutedPath
  766. identity:uB
  767. accessLevel:RLMSyncAccessLevelAdmin];
  768. XCTAssertEqual([results indexOfObject:p2], NSNotFound);
  769. // A permission with a differing identity should not match.
  770. id p3 = [[RLMSyncPermission alloc] initWithRealmPath:tildeSubstitutedPath
  771. identity:self.userA.identity
  772. accessLevel:RLMSyncAccessLevelRead];
  773. XCTAssertEqual([results indexOfObject:p3], NSNotFound);
  774. // A permission with a differing path should not match.
  775. id p4 = [[RLMSyncPermission alloc] initWithRealmPath:[makeTildeSubstitutedURL(url, self.userB) path]
  776. identity:uB
  777. accessLevel:RLMSyncAccessLevelRead];
  778. XCTAssertEqual([results indexOfObject:p4], NSNotFound);
  779. }
  780. - (void)testPermissionResultsIndexOfObjectWithPredicate {
  781. // Get a reference to the permission results.
  782. RLMResults<RLMSyncPermission *> *results = [self getPermissionResultsFor:self.userB];
  783. NSString *uB = self.userB.identity;
  784. // Open a Realm
  785. {
  786. NSURL *url = CUSTOM_REALM_URL(@"r1");
  787. __attribute__((objc_precise_lifetime)) RLMRealm *realm = [self openRealmForURL:url user:self.userA];
  788. // Give user B read permission for the Realm.
  789. RLMSyncPermission *p = [[RLMSyncPermission alloc] initWithRealmPath:[makeTildeSubstitutedURL(url, self.userA) path]
  790. identity:uB
  791. accessLevel:RLMSyncAccessLevelRead];
  792. APPLY_PERMISSION_WITH_MESSAGE(p, self.userA, @"Setting r1 permission for user B should work.");
  793. }
  794. NSString *finalPath;
  795. {
  796. // Do this again so there's more than one permission in the permission Realm.
  797. NSURL *url = CUSTOM_REALM_URL(@"r2");
  798. __attribute__((objc_precise_lifetime)) RLMRealm *realm = [self openRealmForURL:url user:self.userA];
  799. // Give user B read permission for the Realm.
  800. finalPath = [makeTildeSubstitutedURL(url, self.userA) path];
  801. RLMSyncPermission *p = [[RLMSyncPermission alloc] initWithRealmPath:finalPath
  802. identity:uB
  803. accessLevel:RLMSyncAccessLevelRead];
  804. APPLY_PERMISSION_WITH_MESSAGE(p, self.userA, @"Setting r2 permission for user B should work.");
  805. }
  806. // Wait for changes to propagate
  807. CHECK_PERMISSION_COUNT_AT_LEAST(results, 2);
  808. // Create the predicate and retrieve the index of the object.
  809. NSPredicate *pred = [NSPredicate predicateWithFormat:@"%K == %@", RLMSyncPermissionSortPropertyPath, finalPath];
  810. NSUInteger index = [results indexOfObjectWithPredicate:pred];
  811. XCTAssertNotEqual(index, NSNotFound);
  812. if (index == NSNotFound) {
  813. return;
  814. }
  815. RLMSyncPermission *target = [results objectAtIndex:index];
  816. XCTAssertEqualObjects(target.path, finalPath);
  817. }
  818. /// User should not be able to change a permission for a Realm they don't own.
  819. - (void)testSettingUnownedRealmPermission {
  820. // Open a Realm for user A.
  821. NSURL *url = REALM_URL();
  822. [self openRealmForURL:url user:self.userA];
  823. // Try to have user B give user C permissions to that Realm.
  824. RLMSyncPermission *p = [[RLMSyncPermission alloc] initWithRealmPath:[makeTildeSubstitutedURL(url, self.userA) path]
  825. identity:self.userC.identity
  826. accessLevel:RLMSyncAccessLevelRead];
  827. // Set the permission.
  828. XCTestExpectation *ex2 = [self expectationWithDescription:@"Setting an invalid permission should fail."];
  829. [self.userB applyPermission:p callback:^(NSError *error) {
  830. XCTAssertNotNil(error);
  831. XCTAssertEqual(error.domain, RLMSyncPermissionErrorDomain);
  832. XCTAssertEqual(error.code, RLMSyncPermissionErrorChangeFailed);
  833. [ex2 fulfill];
  834. }];
  835. [self waitForExpectationsWithTimeout:2.0 handler:nil];
  836. // Now retrieve the permissions again and make sure the new permission was not set.
  837. RLMResults<RLMSyncPermission *> *results = [self getPermissionResultsFor:self.userB
  838. message:@"Retrieving the results should work."];
  839. CHECK_PERMISSION_ABSENT(results, p);
  840. }
  841. - (void)testRetrievingPermissionsChecksThreadHasRunLoop {
  842. [self dispatchAsyncAndWait:^{
  843. RLMAssertThrowsWithReason([self.userA retrievePermissionsWithCallback:^(__unused RLMResults *r, __unused NSError *e) {
  844. XCTFail(@"callback should not have been invoked");
  845. }], @"Can only access or modify permissions from a thread which has a run loop");
  846. }];
  847. }
  848. #pragma mark - Permission offer/response
  849. /// Get a token which can be used to offer the permissions as defined
  850. - (void)testPermissionOffer {
  851. NSURL *url = REALM_URL();
  852. // Open the Realm.
  853. __unused RLMRealm *realm = [self openRealmForURL:url user:self.userA];
  854. // Create the offer.
  855. __block NSString *token = nil;
  856. XCTestExpectation *ex = [self expectationWithDescription:@"Should get a token when making an offer."];
  857. [self.userA createOfferForRealmAtURL:url
  858. accessLevel:RLMSyncAccessLevelWrite
  859. expiration:[NSDate dateWithTimeIntervalSinceNow:30 * 24 * 60 * 60]
  860. callback:^(NSString *t, NSError *error) {
  861. XCTAssertNil(error);
  862. XCTAssertNotNil(t);
  863. token = t;
  864. [ex fulfill];
  865. }];
  866. [self waitForExpectations:@[ex] timeout:10.0];
  867. XCTAssertTrue([token length] > 0);
  868. }
  869. /// Failed to process a permission offer object due to the offer expired
  870. - (void)testPermissionOfferIsExpired {
  871. NSURL *url = REALM_URL();
  872. // Open the Realm.
  873. __unused RLMRealm *realm = [self openRealmForURL:url user:self.userA];
  874. // Create the offer.
  875. __block NSError *error = nil;
  876. XCTestExpectation *ex = [self expectationWithDescription:@"Server should process the permission offer."];
  877. [self.userA createOfferForRealmAtURL:url
  878. accessLevel:RLMSyncAccessLevelWrite
  879. expiration:[NSDate dateWithTimeIntervalSinceNow:-30 * 24 * 60 * 60]
  880. callback:^(NSString *token, NSError *err) {
  881. XCTAssertNotNil(err);
  882. XCTAssertNil(token);
  883. error = err;
  884. [ex fulfill];
  885. }];
  886. [self waitForExpectations:@[ex] timeout:10.0];
  887. XCTAssertEqual(error.code, RLMSyncPermissionErrorOfferFailed);
  888. XCTAssertEqualObjects(error.userInfo[NSLocalizedDescriptionKey], @"The permission offer is expired.");
  889. }
  890. /// Get a permission offer token, then permission offer response will be processed, then open another user's Realm file
  891. - (void)testPermissionOfferResponse {
  892. NSURL *url = REALM_URL();
  893. // Open the Realm.
  894. __unused RLMRealm *realm = [self openRealmForURL:url user:self.userA];
  895. // Create the offer.
  896. __block NSString *token = nil;
  897. XCTestExpectation *ex = [self expectationWithDescription:@"Should get a token when making an offer."];
  898. [self.userA createOfferForRealmAtURL:url
  899. accessLevel:RLMSyncAccessLevelWrite
  900. expiration:[NSDate dateWithTimeIntervalSinceNow:30 * 24 * 60 * 60]
  901. callback:^(NSString *t, NSError *error) {
  902. XCTAssertNil(error);
  903. XCTAssertNotNil(t);
  904. token = t;
  905. [ex fulfill];
  906. }];
  907. [self waitForExpectations:@[ex] timeout:10.0];
  908. XCTAssertTrue([token length] > 0);
  909. // Accept the offer.
  910. __block NSURL *realmURL = nil;
  911. XCTestExpectation *ex2 = [self expectationWithDescription:@"Server should process offer acceptance."];
  912. [self.userB acceptOfferForToken:token callback:^(NSURL *returnedURL, NSError *error) {
  913. XCTAssertNil(error);
  914. XCTAssertNotNil(returnedURL);
  915. realmURL = returnedURL;
  916. [ex2 fulfill];
  917. }];
  918. [self waitForExpectations:@[ex2] timeout:20.0];
  919. XCTAssertEqualObjects([realmURL path], [makeTildeSubstitutedURL(url, self.userA) path]);
  920. // Open the Realm.
  921. XCTAssertNotNil([self openRealmForURL:realmURL user:self.userB]);
  922. }
  923. /// Failed to process a permission offer response object due to `token` is invalid
  924. - (void)testPermissionOfferResponseInvalidToken {
  925. NSString *badToken = @"invalid token";
  926. // Expect an error.
  927. __block NSError *error = nil;
  928. XCTestExpectation *ex = [self expectationWithDescription:@"Server should process offer acceptance."];
  929. [self.userA acceptOfferForToken:badToken callback:^(NSURL *returnedURL, NSError *err) {
  930. XCTAssertNil(returnedURL);
  931. XCTAssertNotNil(err);
  932. error = err;
  933. [ex fulfill];
  934. }];
  935. [self waitForExpectations:@[ex] timeout:20.0];
  936. XCTAssertEqual(error.code, RLMSyncPermissionErrorAcceptOfferFailed);
  937. XCTAssertEqualObjects(error.userInfo[NSLocalizedDescriptionKey], @"Your request parameters did not validate.");
  938. }
  939. /// Failed to process a permission offer response object due to `token` represents a Realm that does not exist
  940. - (void)testPermissionOfferResponseTokenNotExist {
  941. NSString *fakeToken = @"00000000000000000000000000000000:00000000-0000-0000-0000-000000000000";
  942. // Expect an error.
  943. __block NSError *error = nil;
  944. XCTestExpectation *ex = [self expectationWithDescription:@"Server should process offer acceptance."];
  945. [self.userA acceptOfferForToken:fakeToken callback:^(NSURL *returnedURL, NSError *err) {
  946. XCTAssertNil(returnedURL);
  947. XCTAssertNotNil(err);
  948. error = err;
  949. [ex fulfill];
  950. }];
  951. [self waitForExpectations:@[ex] timeout:20.0];
  952. XCTAssertEqual(error.code, RLMSyncPermissionErrorAcceptOfferFailed);
  953. XCTAssertEqualObjects(error.userInfo[NSLocalizedDescriptionKey], @"Your request parameters did not validate.");
  954. }
  955. #pragma mark - Delete Realm upon permission denied
  956. // FIXME ROS 2.0: works when ROS is manually provided, not when ROS is run as part of tests
  957. /// A Realm which is opened improperly should report an error allowing the app to recover.
  958. - (void)testDeleteRealmUponPermissionDenied {
  959. __block void(^errorBlock)(NSError *, RLMSyncSession *session) = nil;
  960. [[RLMSyncManager sharedManager] setErrorHandler:^(NSError *error, RLMSyncSession *session) {
  961. if (errorBlock) {
  962. errorBlock(error, session);
  963. errorBlock = nil;
  964. } else {
  965. XCTFail(@"Error handler should not be called unless explicitly expected. Error: %@", error);
  966. }
  967. }];
  968. NSString *testName = NSStringFromSelector(_cmd);
  969. // Open a Realm for user A.
  970. NSURL *userAURL = makeTestURL(testName, nil);
  971. RLMRealm *userARealm = [self openRealmForURL:userAURL user:self.userA];
  972. // Have user A add some items to the Realm.
  973. [self addSyncObjectsToRealm:userARealm descriptions:@[@"child-1", @"child-2", @"child-3"]];
  974. [self waitForUploadsForUser:self.userA url:userAURL];
  975. CHECK_COUNT(3, SyncObject, userARealm);
  976. // Give user B read permissions to that Realm.
  977. RLMSyncPermission *p = [[RLMSyncPermission alloc] initWithRealmPath:[userAURL path]
  978. identity:self.userB.identity
  979. accessLevel:RLMSyncAccessLevelRead];
  980. // Set the read permission.
  981. APPLY_PERMISSION(p, self.userA);
  982. NSURL *userBURL = makeTestURL(testName, self.userA);
  983. RLMRealmConfiguration *userBConfig = [RLMRealmConfiguration defaultConfiguration];
  984. userBConfig.syncConfiguration = [[RLMSyncConfiguration alloc] initWithUser:self.userB realmURL:userBURL];
  985. __block NSError *theError = nil;
  986. // Incorrectly open the Realm for user B.
  987. NSURL *onDiskPath;
  988. @autoreleasepool {
  989. NSString *sessionName = NSStringFromSelector(_cmd);
  990. XCTestExpectation *ex2 = [self expectationWithDescription:@"We should get a permission denied error."];
  991. errorBlock = ^(NSError *err, RLMSyncSession *session) {
  992. // Make sure we're actually looking at the right session.
  993. XCTAssertTrue([[session.realmURL absoluteString] rangeOfString:sessionName].location != NSNotFound);
  994. theError = err;
  995. [ex2 fulfill];
  996. };
  997. __attribute__((objc_precise_lifetime)) RLMRealm *bad = [RLMRealm realmWithConfiguration:userBConfig error:nil];
  998. [self waitForExpectationsWithTimeout:10.0 handler:nil];
  999. onDiskPath = [RLMSyncTestCase onDiskPathForSyncedRealm:bad];
  1000. }
  1001. XCTAssertNotNil(onDiskPath);
  1002. XCTAssertTrue([[NSFileManager defaultManager] fileExistsAtPath:[onDiskPath path]]);
  1003. // Check the error and perform the Realm deletion.
  1004. XCTAssertNotNil(theError);
  1005. RLMSyncErrorActionToken *errorToken = [theError rlmSync_errorActionToken];
  1006. XCTAssertNotNil(errorToken);
  1007. [RLMSyncSession immediatelyHandleError:errorToken];
  1008. // Ensure the file is no longer on disk.
  1009. XCTAssertFalse([[NSFileManager defaultManager] fileExistsAtPath:[onDiskPath path]]);
  1010. // Correctly open the same Realm for user B.
  1011. __block RLMRealm *userBRealm = nil;
  1012. XCTestExpectation *asyncOpenEx = [self expectationWithDescription:@"Should asynchronously open a Realm"];
  1013. [RLMRealm asyncOpenWithConfiguration:userBConfig
  1014. callbackQueue:dispatch_get_main_queue()
  1015. callback:^(RLMRealm *realm, NSError *err){
  1016. XCTAssertNil(err);
  1017. XCTAssertNotNil(realm);
  1018. userBRealm = realm;
  1019. [asyncOpenEx fulfill];
  1020. }];
  1021. [self waitForExpectationsWithTimeout:10.0 handler:nil];
  1022. CHECK_COUNT(3, SyncObject, userBRealm);
  1023. }
  1024. @end