realm / realm-object-server

Tracking of issues related to the Realm Object Server and other general issues not related to the specific SDK's
https://realm.io
293 stars 42 forks source link

Revoking permissions does not work correctly #334

Closed Taco55 closed 6 years ago

Taco55 commented 6 years ago

Goals

To revoke permissions of a specific realm and to determine the remaining permissions with retrievePermissions.

This is a continuation of issue #244

Expected Results

To see the revoked permissions actually revoked.

Actual Results

Revoked permissions are still there when "remaining" permissions are retrieved using retrievePermissions

Steps to Reproduce

Output will be:

Permissions of user1 (id: d91f828965a3ad646f9240a42f9eb322:
Results<RLMSyncPermission> <0x7fb4d3d09c60> (
[0] <RLMSyncPermission> identity: d91f828965a3ad646f9240a42f9eb322, path: /d91f828965a3ad646f9240a42f9eb322/permissiondemo, access level: admin,
[1] <RLMSyncPermission> identity: 3368977af096b70b2fcc4b31df02e9e5, path: /d91f828965a3ad646f9240a42f9eb322/permissiondemo, access level: write
)
Permissions of user2 (id: 3368977af096b70b2fcc4b31df02e9e5:
Results<RLMSyncPermission> <0x7fb4d68196a0> (
[0] <RLMSyncPermission> identity: 3368977af096b70b2fcc4b31df02e9e5, path: /3368977af096b70b2fcc4b31df02e9e5/permissiondemo, access level: admin,
[1] <RLMSyncPermission> identity: 3368977af096b70b2fcc4b31df02e9e5, path: /d91f828965a3ad646f9240a42f9eb322/permissiondemo, access level: write
)

After revoking the permission that was previously granted by user1 to user2, the permissions are still unchanged:

Permissions of user1 (id: d91f828965a3ad646f9240a42f9eb322:
Results<RLMSyncPermission> <0x7fb4d3f0f200> (
[0] <RLMSyncPermission> identity: d91f828965a3ad646f9240a42f9eb322, path: /d91f828965a3ad646f9240a42f9eb322/permissiondemo, access level: admin,
[1] <RLMSyncPermission> identity: 3368977af096b70b2fcc4b31df02e9e5, path: /d91f828965a3ad646f9240a42f9eb322/permissiondemo, access level: write
)
Permissions of user2 (id: 3368977af096b70b2fcc4b31df02e9e5:
Results<RLMSyncPermission> <0x7fb4d3d08e90> (
[0] <RLMSyncPermission> identity: 3368977af096b70b2fcc4b31df02e9e5, path: /3368977af096b70b2fcc4b31df02e9e5/permissiondemo, access level: admin,
[1] <RLMSyncPermission> identity: 3368977af096b70b2fcc4b31df02e9e5, path: /d91f828965a3ad646f9240a42f9eb322/permissiondemo, access level: write
)

Code Sample

import UIKit
import RealmSwift

struct HostConstants {
    static let host = "localhost"
    static let appPath = "permissiondemo"
    static let syncServerURL = URL(string: "realm://\(host):9080/")!
    static let realmURL = syncServerURL.appendingPathComponent("~/\(appPath)")
    static let serverURL = URL(string: "http://\(host):9080")!
}

struct Credentials {
    var id: String
    var username: String
    var password: String

    init(username: String, password: String, id: String = "") {
        self.username = username
        self.password = password
        self.id = id
    }
}

var user1: Credentials = Credentials(username: "user1", password: "user1")
var user2: Credentials = Credentials(username: "user2", password: "user2")

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {

    var window: UIWindow?

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {

        SyncManager.shared.logLevel = .warn
        SyncUser.all.forEach{$0.value.logOut()}

        //
        // Get identities of RealmUsers and save in customUser struct
        self.getUserId(for: user1) { id in
            user1.id = id
            self.getUserId(for: user2) { id in
                user2.id = id

                // Grant user2 write permission to user1's realm
                self.setPermissionUser1() {

                    // Retrieve permissions of each user
                    self.retrievePermission(for: user1) {
                        self.retrievePermission(for: user2) {

                            // Revoke permissions set by user1
                            print("\nRevoke permission granted by user1 to user2, and show remaining permissions:\n")
                            self.revokePermission(for: user1, grantedToUser: user2) {

                                // Retrieve remaining permissions of each user
                                self.retrievePermission(for: user1) {
                                    self.retrievePermission(for: user2) {
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
        return true
    }

    func setPermissionUser1(completionHandler: (() -> ())? ) {
        if let user = SyncUser.current { user.logOut() }
        let credentials = SyncCredentials.usernamePassword(username: user1.username, password: user1.password, register: false)
        SyncUser.logIn(with: credentials, server: HostConstants.serverURL) { realmUser, error in
            if let error = error { print(error); return }

            // Ensure that a Realm exists before permissions are changed
            guard let user = realmUser else { return }
            let config = self.getRealmConfiguration(for:user)
            Realm.asyncOpen(configuration: config) { realm, error in
                let components = HostConstants.realmURL.absoluteString.components(separatedBy: "/")
                let realmPath = "/" + components[3] + "/" + components[4]

                // Change permission of user2
                let permission = SyncPermission(realmPath: realmPath, identity: user2.id, accessLevel: .write)
                user.apply(permission) { error in
                    if let error = error { print(error); return }
                    completionHandler?()
                }
            }
        }
    }

    func retrievePermission(for customUser: Credentials, completionHandler: (() -> ())?) {
        if let user = SyncUser.current { user.logOut() }
        let credentials = SyncCredentials.usernamePassword(username: customUser.username, password: customUser.password, register: false)
        SyncUser.logIn(with: credentials, server: HostConstants.serverURL) { realmUser, error in
            if let error = error { print(error); return }

            // Ensure that a Realm exists before permissions are changed
            guard let user = realmUser else { return }
            let config = self.getRealmConfiguration(for:user)
            Realm.asyncOpen(configuration: config) { realm, error in
                // Get permissions
                SyncUser.current!.retrievePermissions { permissions, error in
                    if let error = error { print(error); return }
                    guard let permissions = permissions else { print("No permissions found"); return }
                    print("Permissions of \(customUser.username) (id: \(customUser.id):\n", permissions)
                    completionHandler?()
                }
            }
        }
    }

    func revokePermission(for customUser: Credentials, grantedToUser: Credentials, completionHandler: (() -> ())?) {
        if let user = SyncUser.current { user.logOut() }
        let credentials = SyncCredentials.usernamePassword(username: customUser.username, password: customUser.password, register: false)

        SyncUser.logIn(with: credentials, server: HostConstants.serverURL) { realmUser, error in
            if let error = error { print(error); return }

            // Ensure that a Realm exists before permissions are revoked
            guard let user = realmUser else { return }
            let config = self.getRealmConfiguration(for:user)
            Realm.asyncOpen(configuration: config) { realm, error in
                let components = HostConstants.realmURL.absoluteString.components(separatedBy: "/")
                let realmPath = "/" + components[3] + "/" + components[4]

                let permission = SyncPermission(realmPath: realmPath, identity: grantedToUser.id, accessLevel: .none)
                user.revokePermission(permission) { error in
                    if let error = error { print(error); return }
                    completionHandler?()
                }
            }
        }
    }

    func getRealmConfiguration(for user: SyncUser) -> Realm.Configuration {
        let configuration = Realm.Configuration(
            inMemoryIdentifier: "inMemoryRealm",
            syncConfiguration: SyncConfiguration(user: user, realmURL: HostConstants.realmURL),
            schemaVersion: 1,
            migrationBlock: { migration, oldSchemaVersion in }
        )
        return configuration
    }

    func getUserId(for customUser: Credentials, completionHandler: ((_ id: String) -> ())? ) {
        if let user = SyncUser.current { user.logOut() }
        let credentials = SyncCredentials.usernamePassword(username: customUser.username, password: customUser.password, register: false)
        SyncUser.logIn(with: credentials, server: HostConstants.serverURL) { realmUser, error in
            if let error = error { print(error); return }
            if let identity = realmUser!.identity {
                completionHandler?(identity)
            }
        }
    }
}

Version of Realm and Tooling

Taco55 commented 6 years ago

In addition, to the actual results described above. When the granted permission of user2 to user1's Realm is revoked on user2, the permissionRealm of user2 is correctly updated, but not for user1.

Remaining permissions of user1 (id: d91f828965a3ad646f9240a42f9eb322) after revoke (incorrect):

 Results<RLMSyncPermission> <0x7fbfe4f0ad70> (
    [0] <RLMSyncPermission> identity: d91f828965a3ad646f9240a42f9eb322, path: /d91f828965a3ad646f9240a42f9eb322/permissiondemo, access level: admin,
    [1] <RLMSyncPermission> identity: 3368977af096b70b2fcc4b31df02e9e5, path: /d91f828965a3ad646f9240a42f9eb322/permissiondemo, access level: write
)

Remaining permissions of user2 (id: 3368977af096b70b2fcc4b31df02e9e5) after revoke (correct):

 Results<RLMSyncPermission> <0x7fbfe4f0bd90> (
    [0] <RLMSyncPermission> identity: 3368977af096b70b2fcc4b31df02e9e5, path: /3368977af096b70b2fcc4b31df02e9e5/permissiondemo, access level: admin
)

Nevertheless, revoking the granted permissions of user1 directly on itself does not update the permissionRealm correctly as stated in the issue description.

bmunkholm commented 6 years ago

@Taco55 Thanks for reporting this! We are currently enhancing the permissions system and will look into this.

Taco55 commented 6 years ago

Are there any updates on the permission system or are there any ideas when it will be enhanced?

I reported a closely related issue for the first time at 29 March 2017 (#160), more than a year ago, and it seems that not much has changed (at least on the client side)...

astigsen commented 6 years ago

Closing this for now as it relates to an outdated version of ROS. The permission system has been totally revamped since then. Please reopen if the problem stil persists.