SwiftPermissionsAPITests.swift 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306
  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
  19. import RealmSwift
  20. class SwiftPermissionsAPITests: SwiftSyncTestCase {
  21. var userA: SyncUser!
  22. var userB: SyncUser!
  23. var userC: SyncUser!
  24. override func setUp() {
  25. super.setUp()
  26. let baseName = UUID().uuidString
  27. userA = try! synchronouslyLogInUser(for: .usernamePassword(username: baseName + "a", password: "a", register: true),
  28. server: SwiftSyncTestCase.authServerURL())
  29. userB = try! synchronouslyLogInUser(for: .usernamePassword(username: baseName + "b", password: "a", register: true),
  30. server: SwiftSyncTestCase.authServerURL())
  31. userC = try! synchronouslyLogInUser(for: .usernamePassword(username: baseName + "c", password: "a", register: true),
  32. server: SwiftSyncTestCase.authServerURL())
  33. }
  34. override func tearDown() {
  35. userA.logOut()
  36. userB.logOut()
  37. userC.logOut()
  38. super.tearDown()
  39. }
  40. private func checkPermissionCount(results: SyncPermissionResults,
  41. expected: Int,
  42. file: StaticString = #file,
  43. line: UInt = #line) {
  44. let ex = expectation(description: "Checking permission count")
  45. let token = results.observe { (change) in
  46. if case let .error(theError) = change {
  47. XCTFail("Notification returned error '\(theError)' when running test at \(file):\(line)")
  48. return
  49. }
  50. if results.count == expected {
  51. ex.fulfill()
  52. }
  53. }
  54. waitForExpectations(timeout: 2.0, handler: nil)
  55. token.invalidate()
  56. }
  57. private func get(permission: SyncPermission,
  58. from results: SyncPermissionResults,
  59. file: StaticString = #file,
  60. line: UInt = #line) -> SyncPermission? {
  61. let ex = expectation(description: "Retrieving permission")
  62. var finalValue: SyncPermission?
  63. let token = results.observe { (change) in
  64. if case let .error(theError) = change {
  65. XCTFail("Notification returned error '\(theError)' when running test at \(file):\(line)")
  66. return
  67. }
  68. for result in results where result == permission {
  69. finalValue = result
  70. ex.fulfill()
  71. return
  72. }
  73. }
  74. waitForExpectations(timeout: 2.0, handler: nil)
  75. token.invalidate()
  76. return finalValue
  77. }
  78. /// Ensure the absence of a permission from a results after an elapsed time interval.
  79. /// This method is intended to be used to check that a permission never becomes
  80. /// present within a results to begin with.
  81. private func ensureAbsence(of permission: SyncPermission,
  82. from results: SyncPermissionResults,
  83. after wait: Double = 0.5,
  84. file: StaticString = #file,
  85. line: UInt = #line) {
  86. let ex = expectation(description: "Looking for permission")
  87. var isPresent = false
  88. let token = results.observe { (change) in
  89. if case let .error(theError) = change {
  90. XCTFail("Notification returned error '\(theError)' when running test at \(file):\(line)")
  91. return
  92. }
  93. isPresent = results.contains(permission)
  94. }
  95. DispatchQueue.main.asyncAfter(deadline: .now() + wait) {
  96. ex.fulfill()
  97. }
  98. waitForExpectations(timeout: wait + 1.0, handler: nil)
  99. token.invalidate()
  100. XCTAssertFalse(isPresent, "Permission '\(permission)' was spuriously present (\(file):\(line))")
  101. }
  102. private func tildeSubstitutedURL(for url: URL, user: SyncUser) -> URL {
  103. XCTAssertNotNil(user.identity)
  104. let identity = user.identity!
  105. return URL(string: url.absoluteString.replacingOccurrences(of: "~", with: identity))!
  106. }
  107. /// Setting a permission should work, and then that permission should be able to be retrieved.
  108. func testSettingPermissions() {
  109. // First, there should be no permissions.
  110. let ex = expectation(description: "No permissions for newly created user.")
  111. var results: SyncPermissionResults!
  112. userB.retrievePermissions { (r, error) in
  113. XCTAssertNil(error)
  114. XCTAssertNotNil(r)
  115. results = r
  116. ex.fulfill()
  117. }
  118. waitForExpectations(timeout: 2.0, handler: nil)
  119. checkPermissionCount(results: results, expected: 0)
  120. // Open a Realm for user A.
  121. let uuid = UUID().uuidString
  122. let url = SwiftSyncTestCase.uniqueRealmURL(customName: uuid)
  123. _ = try! synchronouslyOpenRealm(url: url, user: userA)
  124. // Give user B read permissions to that Realm.
  125. let p = SyncPermission(realmPath: tildeSubstitutedURL(for: url, user: userA).path,
  126. identity: userB.identity!,
  127. accessLevel: .read)
  128. // Set the permission.
  129. let ex2 = expectation(description: "Setting a permission should work.")
  130. userA.apply(p) { (error) in
  131. XCTAssertNil(error)
  132. ex2.fulfill()
  133. }
  134. waitForExpectations(timeout: 2.0, handler: nil)
  135. // Now retrieve the permissions again and make sure the new permission is properly set.
  136. let ex3 = expectation(description: "One permission in results after setting the permission.")
  137. userB.retrievePermissions { (r, error) in
  138. XCTAssertNil(error)
  139. XCTAssertNotNil(r)
  140. results = r
  141. ex3.fulfill()
  142. }
  143. waitForExpectations(timeout: 2.0, handler: nil)
  144. // Expected permission: applies to user B, but for user A's Realm.
  145. let finalValue = get(permission: p, from: results)
  146. XCTAssertNotNil(finalValue, "Did not find the permission \(p)")
  147. // Check getting permission by its index.
  148. let index = results.index(of: p)
  149. XCTAssertNotNil(index)
  150. XCTAssertTrue(p == results[index!])
  151. }
  152. /// Observing permission changes should work.
  153. func testObservingPermissions() {
  154. // Get a reference to the permission results.
  155. let ex = expectation(description: "Retrieve permission results.")
  156. var results: SyncPermissionResults!
  157. userB.retrievePermissions { (r, error) in
  158. XCTAssertNil(error)
  159. XCTAssertNotNil(r)
  160. results = r
  161. ex.fulfill()
  162. }
  163. waitForExpectations(timeout: 2.0, handler: nil)
  164. // Open a Realm for user A.
  165. let uuid = UUID().uuidString
  166. let url = SwiftSyncTestCase.uniqueRealmURL(customName: uuid)
  167. _ = try! synchronouslyOpenRealm(url: url, user: userA)
  168. // Register notifications.
  169. let noteEx = expectation(description: "Notification should fire")
  170. let token = results.observe { (change) in
  171. if case .error = change {
  172. XCTFail("Should not return an error")
  173. return
  174. }
  175. if results.count > 0 {
  176. noteEx.fulfill()
  177. }
  178. }
  179. // Give user B read permissions to that Realm.
  180. let p = SyncPermission(realmPath: tildeSubstitutedURL(for: url, user: userA).path,
  181. identity: userB.identity!,
  182. accessLevel: .read)
  183. // Set the permission.
  184. let ex2 = expectation(description: "Setting a permission should work.")
  185. userA.apply(p) { (error) in
  186. XCTAssertNil(error)
  187. ex2.fulfill()
  188. }
  189. waitForExpectations(timeout: 2.0, handler: nil)
  190. // Wait for the notification to be fired.
  191. wait(for: [noteEx], timeout: 2.0)
  192. token.invalidate()
  193. let finalValue = get(permission: p, from: results)
  194. XCTAssertNotNil(finalValue, "Did not find the permission \(p)")
  195. }
  196. /// User should not be able to change a permission for a Realm they don't own.
  197. func testSettingUnownedRealmPermission() {
  198. // Open a Realm for user A.
  199. let uuid = UUID().uuidString
  200. let url = SwiftSyncTestCase.uniqueRealmURL(customName: uuid)
  201. _ = try! synchronouslyOpenRealm(url: url, user: userA)
  202. // Try to have user B give user C permissions to that Realm.
  203. let p = SyncPermission(realmPath: url.path, identity: userC.identity!, accessLevel: .read)
  204. // Attempt to set the permission.
  205. let ex2 = expectation(description: "Setting an invalid permission should fail.")
  206. userB.apply(p) { (error) in
  207. XCTAssertNotNil(error)
  208. ex2.fulfill()
  209. }
  210. waitForExpectations(timeout: 2.0, handler: nil)
  211. // Now retrieve the permissions again and make sure the new permission was not set.
  212. var results: SyncPermissionResults!
  213. let ex3 = expectation(description: "Retrieving the results should work.")
  214. userB.retrievePermissions { (r, error) in
  215. XCTAssertNil(error)
  216. XCTAssertNotNil(r)
  217. results = r
  218. ex3.fulfill()
  219. }
  220. waitForExpectations(timeout: 2.0, handler: nil)
  221. ensureAbsence(of: p, from: results)
  222. }
  223. // MARK: - Offer/response
  224. func testPermissionOffer() {
  225. _ = try! synchronouslyOpenRealm(url: realmURL, user: userA)
  226. var token: String?
  227. // Create an offer.
  228. let ex = expectation(description: "A new permission offer will be processed by the server.")
  229. userA.createOfferForRealm(at: realmURL, accessLevel: .write) { (t, error) in
  230. XCTAssertNil(error)
  231. XCTAssertNotNil(t)
  232. token = t
  233. ex.fulfill()
  234. }
  235. waitForExpectations(timeout: 10.0, handler: nil)
  236. XCTAssertGreaterThan(token!.lengthOfBytes(using: .utf8), 0)
  237. }
  238. func testPermissionOfferResponse() {
  239. _ = try! synchronouslyOpenRealm(url: realmURL, user: userA)
  240. var token: String?
  241. // Create an offer.
  242. let ex = expectation(description: "A new permission offer will be processed by the server.")
  243. userA.createOfferForRealm(at: realmURL, accessLevel: .write) { (t, error) in
  244. XCTAssertNil(error)
  245. XCTAssertNotNil(t)
  246. token = t
  247. ex.fulfill()
  248. }
  249. waitForExpectations(timeout: 10.0, handler: nil)
  250. guard let theToken = token else {
  251. XCTFail("We expected an offer token, but did not get one. Aborting the test.")
  252. return
  253. }
  254. XCTAssertGreaterThan(theToken.lengthOfBytes(using: .utf8), 0)
  255. // Accept the offer.
  256. let ex2 = expectation(description: "A permission offer response will be processed by the server.")
  257. var url: URL?
  258. userB.acceptOffer(forToken: theToken) { (u, error) in
  259. XCTAssertNil(error)
  260. XCTAssertNotNil(u)
  261. url = u
  262. ex2.fulfill()
  263. }
  264. waitForExpectations(timeout: 10.0, handler: nil)
  265. guard let theURL = url else {
  266. XCTFail("We expected a Realm URL, but did not get one. Aborting the test.")
  267. return
  268. }
  269. XCTAssertEqual(theURL.path, tildeSubstitutedURL(for: realmURL, user: userA).path)
  270. do {
  271. _ = try synchronouslyOpenRealm(url: theURL, user: userB)
  272. } catch {
  273. XCTFail("Was not able to successfully open the Realm with user B after accepting the offer.")
  274. }
  275. }
  276. }