You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
622 lines
28 KiB
622 lines
28 KiB
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Copyright 2018 Realm Inc.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
#import <XCTest/XCTest.h>
|
|
|
|
#import "RLMSyncTestCase.h"
|
|
|
|
#import "RLMTestUtils.h"
|
|
|
|
#define APPLY_PERMISSION(ma_permission, ma_user) do { \
|
|
XCTestExpectation *ex = [self expectationWithDescription:@"apply permission"]; \
|
|
[ma_user applyPermission:ma_permission callback:^(NSError *err) { \
|
|
XCTAssertNil(err, @"Received an error when applying permission: %@", err); \
|
|
[ex fulfill]; \
|
|
}]; \
|
|
[self waitForExpectationsWithTimeout:10.0 handler:nil]; \
|
|
} while (0) \
|
|
|
|
@interface ObjectWithPermissions : RLMObject
|
|
@property (nonatomic) int value;
|
|
@property (nonatomic) RLMArray<RLMPermission *><RLMPermission> *permissions;
|
|
@end
|
|
@implementation ObjectWithPermissions
|
|
@end
|
|
|
|
@interface LinkToObjectWithPermissions : RLMObject
|
|
@property (nonatomic) int value;
|
|
@property (nonatomic) ObjectWithPermissions *link;
|
|
@property (nonatomic) RLMArray<RLMPermission *><RLMPermission> *permissions;
|
|
@end
|
|
@implementation LinkToObjectWithPermissions
|
|
@end
|
|
|
|
@interface RLMPermissionsTests : RLMSyncTestCase
|
|
@property (nonatomic, strong) RLMSyncUser *userA;
|
|
@property (nonatomic, strong) RLMSyncUser *userB;
|
|
@property (nonatomic, strong) RLMSyncUser *userC;
|
|
|
|
@property (nonatomic, strong) void (^errorBlock)(NSError *);
|
|
@end
|
|
|
|
@implementation RLMPermissionsTests
|
|
|
|
- (void)setUp {
|
|
[super setUp];
|
|
NSString *accountNameBase = [[NSUUID UUID] UUIDString];
|
|
NSString *userNameA = [accountNameBase stringByAppendingString:@"a"];
|
|
self.userA = [self logInUserForCredentials:[RLMSyncTestCase basicCredentialsWithName:userNameA register:YES]
|
|
server:[RLMSyncTestCase authServerURL]];
|
|
|
|
NSString *userNameB = [accountNameBase stringByAppendingString:@"b"];
|
|
self.userB = [self logInUserForCredentials:[RLMSyncTestCase basicCredentialsWithName:userNameB register:YES]
|
|
server:[RLMSyncTestCase authServerURL]];
|
|
|
|
NSString *userNameC = [accountNameBase stringByAppendingString:@"c"];
|
|
self.userC = [self logInUserForCredentials:[RLMSyncTestCase basicCredentialsWithName:userNameC register:YES]
|
|
server:[RLMSyncTestCase authServerURL]];
|
|
|
|
RLMSyncManager.sharedManager.errorHandler = ^(NSError *error, __unused RLMSyncSession *session) {
|
|
if (self.errorBlock) {
|
|
self.errorBlock(error);
|
|
self.errorBlock = nil;
|
|
} else {
|
|
XCTFail(@"Error handler should not be called unless explicitly expected. Error: %@", error);
|
|
}
|
|
};
|
|
}
|
|
|
|
- (void)tearDown {
|
|
[self.userA logOut];
|
|
[self.userB logOut];
|
|
[self.userC logOut];
|
|
RLMSyncManager.sharedManager.errorHandler = nil;
|
|
[super tearDown];
|
|
}
|
|
|
|
#pragma mark - Helper methods
|
|
|
|
- (BOOL)isPartial {
|
|
return YES;
|
|
}
|
|
|
|
- (NSError *)subscribeToRealm:(RLMRealm *)realm type:(Class)cls where:(NSString *)pred {
|
|
__block NSError *error;
|
|
XCTestExpectation *ex = [self expectationWithDescription:@"Should be able to successfully complete a query"];
|
|
[realm subscribeToObjects:cls where:pred callback:^(__unused RLMResults *results, NSError *err) {
|
|
error = err;
|
|
[ex fulfill];
|
|
}];
|
|
[self waitForExpectations:@[ex] timeout:20.0];
|
|
return error;
|
|
}
|
|
|
|
- (NSURL *)createRealmWithName:(SEL)sel permissions:(void (^)(RLMRealm *realm))block {
|
|
// Create a new Realm with an admin user
|
|
RLMSyncUser *admin = [self createAdminUserForURL:[RLMSyncTestCase authServerURL]
|
|
username:[[NSUUID UUID] UUIDString]];
|
|
|
|
auto url = [[NSURL alloc] initWithString:[NSString stringWithFormat:@"realm://127.0.0.1:9080/%@", NSStringFromSelector(sel)]];
|
|
RLMRealm *adminRealm = [self openRealmForURL:url user:admin];
|
|
[self addSyncObjectsToRealm:adminRealm descriptions:@[@"child-1", @"child-2", @"child-3"]];
|
|
CHECK_COUNT(3, SyncObject, adminRealm);
|
|
[self waitForUploadsForRealm:adminRealm error:nil];
|
|
[self waitForDownloadsForRealm:adminRealm error:nil];
|
|
|
|
// FIXME: we currently need to add a subscription to get the permissions types sent to us
|
|
[adminRealm refresh];
|
|
CHECK_COUNT(0, SyncObject, adminRealm);
|
|
[self subscribeToRealm:adminRealm type:[SyncObject class] where:@"TRUEPREDICATE"];
|
|
CHECK_COUNT(3, SyncObject, adminRealm);
|
|
|
|
// Set up permissions on the Realm
|
|
[adminRealm transactionWithBlock:^{ block(adminRealm); }];
|
|
|
|
// FIXME: we currently need to also add the old realm-level permissions
|
|
RLMSyncPermission *p = [[RLMSyncPermission alloc] initWithRealmPath:[url path]
|
|
identity:self.userA.identity
|
|
accessLevel:RLMSyncAccessLevelRead];
|
|
APPLY_PERMISSION(p, admin);
|
|
p = [[RLMSyncPermission alloc] initWithRealmPath:[url path] identity:self.userB.identity
|
|
accessLevel:RLMSyncAccessLevelRead];
|
|
APPLY_PERMISSION(p, admin);
|
|
p = [[RLMSyncPermission alloc] initWithRealmPath:[url path] identity:self.userC.identity
|
|
accessLevel:RLMSyncAccessLevelRead];
|
|
APPLY_PERMISSION(p, admin);
|
|
[self waitForSync:adminRealm];
|
|
|
|
return url;
|
|
}
|
|
|
|
- (void)waitForSync:(RLMRealm *)realm {
|
|
[self waitForUploadsForRealm:realm error:nil];
|
|
[self waitForDownloadsForRealm:realm error:nil];
|
|
[realm refresh];
|
|
}
|
|
|
|
#pragma mark - Permissions
|
|
|
|
static RLMPermissionRole *getRole(RLMRealm *realm, NSString *roleName) {
|
|
return [RLMPermissionRole createOrUpdateInRealm:realm withValue:@{@"name": roleName}];
|
|
}
|
|
|
|
static void addUserToRole(RLMRealm *realm, NSString *roleName, NSString *user) {
|
|
[getRole(realm, roleName).users addObject:[RLMPermissionUser userInRealm:realm withIdentity:user]];
|
|
}
|
|
|
|
static void createPermissions(RLMArray<RLMPermission> *permissions) {
|
|
auto permission = [RLMPermission permissionForRoleNamed:@"everyone" inArray:permissions];
|
|
permission.canCreate = false;
|
|
permission.canRead = false;
|
|
permission.canQuery = false;
|
|
permission.canDelete = false;
|
|
permission.canUpdate = false;
|
|
permission.canModifySchema = false;
|
|
permission.canSetPermissions = false;
|
|
|
|
permission = [RLMPermission permissionForRoleNamed:@"reader" inArray:permissions];
|
|
permission.canRead = true;
|
|
permission.canQuery = true;
|
|
|
|
permission = [RLMPermission permissionForRoleNamed:@"writer" inArray:permissions];
|
|
permission.canUpdate = true;
|
|
permission.canCreate = true;
|
|
permission.canDelete = true;
|
|
|
|
permission = [RLMPermission permissionForRoleNamed:@"admin" inArray:permissions];
|
|
permission.canSetPermissions = true;
|
|
}
|
|
|
|
#define CHECK_REALM_PRIVILEGE(realm, ...) do { \
|
|
RLMRealmPrivileges expected{__VA_ARGS__}; \
|
|
auto actual = [realm privilegesForRealm]; \
|
|
XCTAssertEqual(expected.read, actual.read); \
|
|
XCTAssertEqual(expected.update, actual.update); \
|
|
XCTAssertEqual(expected.setPermissions, actual.setPermissions); \
|
|
XCTAssertEqual(expected.modifySchema, actual.modifySchema); \
|
|
} while (0)
|
|
|
|
#define CHECK_CLASS_PRIVILEGE(realm, ...) do { \
|
|
RLMClassPrivileges expected{__VA_ARGS__}; \
|
|
auto actual = [realm privilegesForClass:SyncObject.class]; \
|
|
XCTAssertEqual(expected.read, actual.read); \
|
|
XCTAssertEqual(expected.create, actual.create); \
|
|
XCTAssertEqual(expected.update, actual.update); \
|
|
XCTAssertEqual(expected.subscribe, actual.subscribe); \
|
|
XCTAssertEqual(expected.setPermissions, actual.setPermissions); \
|
|
} while (0)
|
|
|
|
#define CHECK_OBJECT_PRIVILEGE(realm, ...) do { \
|
|
RLMObjectPrivileges expected{__VA_ARGS__}; \
|
|
auto actual = [realm privilegesForObject:[SyncObject allObjectsInRealm:realm].firstObject]; \
|
|
XCTAssertEqual(expected.read, actual.read); \
|
|
XCTAssertEqual(expected.del, actual.del); \
|
|
XCTAssertEqual(expected.update, actual.update); \
|
|
XCTAssertEqual(expected.setPermissions, actual.setPermissions); \
|
|
} while (0)
|
|
|
|
- (void)testRealmReadAccess {
|
|
NSURL *url = [self createRealmWithName:_cmd permissions:^(RLMRealm *realm) {
|
|
createPermissions([RLMRealmPermission objectInRealm:realm].permissions);
|
|
addUserToRole(realm, @"reader", self.userA.identity);
|
|
}];
|
|
|
|
// userA should now be able to open the Realm and see objects
|
|
RLMRealm *userARealm = [self openRealmForURL:url user:self.userA];
|
|
[self subscribeToRealm:userARealm type:[SyncObject class] where:@"TRUEPREDICATE"];
|
|
CHECK_REALM_PRIVILEGE(userARealm, .read = true);
|
|
CHECK_CLASS_PRIVILEGE(userARealm, .read = true, .subscribe = true);
|
|
CHECK_OBJECT_PRIVILEGE(userARealm, .read = true);
|
|
|
|
// userA should not be able to create new objects
|
|
CHECK_COUNT(3, SyncObject, userARealm);
|
|
[self addSyncObjectsToRealm:userARealm descriptions:@[@"child-4", @"child-5", @"child-6"]];
|
|
CHECK_COUNT(6, SyncObject, userARealm);
|
|
[self waitForSync:userARealm];
|
|
CHECK_COUNT(3, SyncObject, userARealm);
|
|
|
|
// userB should not be able to read any objects
|
|
RLMRealm *userBRealm = [self openRealmForURL:url user:self.userB];
|
|
[self subscribeToRealm:userBRealm type:[SyncObject class] where:@"TRUEPREDICATE"];
|
|
CHECK_REALM_PRIVILEGE(userBRealm, .read = false);
|
|
CHECK_CLASS_PRIVILEGE(userBRealm, .read = false);
|
|
CHECK_COUNT(0, SyncObject, userBRealm);
|
|
}
|
|
|
|
- (void)testRealmWriteAccess {
|
|
NSURL *url = [self createRealmWithName:_cmd permissions:^(RLMRealm *realm) {
|
|
createPermissions([RLMRealmPermission objectInRealm:realm].permissions);
|
|
|
|
addUserToRole(realm, @"reader", self.userA.identity);
|
|
addUserToRole(realm, @"writer", self.userA.identity);
|
|
|
|
addUserToRole(realm, @"reader", self.userB.identity);
|
|
}];
|
|
|
|
// userA should be able to add objects
|
|
RLMRealm *userARealm = [self openRealmForURL:url user:self.userA];
|
|
[self subscribeToRealm:userARealm type:[SyncObject class] where:@"TRUEPREDICATE"];
|
|
CHECK_REALM_PRIVILEGE(userARealm, .read = true, .update = true);
|
|
CHECK_CLASS_PRIVILEGE(userARealm, .read = true, .subscribe = true,
|
|
.update = true, .create = true, .setPermissions = true);
|
|
CHECK_OBJECT_PRIVILEGE(userARealm, .read = true, .update = true, .del = true, .setPermissions = true);
|
|
|
|
CHECK_COUNT(3, SyncObject, userARealm);
|
|
[self addSyncObjectsToRealm:userARealm descriptions:@[@"child-4", @"child-5", @"child-6"]];
|
|
CHECK_COUNT(6, SyncObject, userARealm);
|
|
[self waitForSync:userARealm];
|
|
CHECK_COUNT(6, SyncObject, userARealm);
|
|
|
|
// userB's insertions should be reverted
|
|
RLMRealm *userBRealm = [self openRealmForURL:url user:self.userB];
|
|
[self subscribeToRealm:userBRealm type:[SyncObject class] where:@"TRUEPREDICATE"];
|
|
CHECK_REALM_PRIVILEGE(userBRealm, .read = true);
|
|
CHECK_CLASS_PRIVILEGE(userBRealm, .read = true, .subscribe = true);
|
|
CHECK_OBJECT_PRIVILEGE(userBRealm, .read = true);
|
|
|
|
CHECK_COUNT(6, SyncObject, userBRealm);
|
|
[self addSyncObjectsToRealm:userBRealm descriptions:@[@"child-4", @"child-5", @"child-6"]];
|
|
CHECK_COUNT(9, SyncObject, userBRealm);
|
|
[self waitForSync:userBRealm];
|
|
CHECK_COUNT(6, SyncObject, userBRealm);
|
|
}
|
|
|
|
- (void)testRealmManagePermissions {
|
|
// FIXME: this test is wrong; setPermission doesn't govern adding users to roles
|
|
#if 0
|
|
NSURL *url = [self createRealmWithName:_cmd permissions:^(RLMRealm *realm) {
|
|
createPermissions([RLMRealmPermission objectInRealm:realm].permissions);
|
|
|
|
addUserToRole(realm, @"reader", self.userA.identity);
|
|
addUserToRole(realm, @"writer", self.userA.identity);
|
|
addUserToRole(realm, @"admin", self.userA.identity);
|
|
|
|
addUserToRole(realm, @"reader", self.userB.identity);
|
|
addUserToRole(realm, @"writer", self.userB.identity);
|
|
|
|
addUserToRole(realm, @"reader", self.userC.identity);
|
|
}];
|
|
|
|
RLMRealm *userARealm = [self openRealmForURL:url user:self.userA];
|
|
RLMRealm *userBRealm = [self openRealmForURL:url user:self.userB];
|
|
RLMRealm *userCRealm = [self openRealmForURL:url user:self.userC];
|
|
|
|
// userC should initially not be able to write to the Realm
|
|
[self subscribeToRealm:userCRealm type:[SyncObject class] where:@"TRUEPREDICATE"];
|
|
CHECK_REALM_PRIVILEGE(userCRealm, .read = true);
|
|
CHECK_CLASS_PRIVILEGE(userCRealm, .read = true, .subscribe = true);
|
|
CHECK_OBJECT_PRIVILEGE(userCRealm, .read = true);
|
|
|
|
[self addSyncObjectsToRealm:userCRealm descriptions:@[@"child-4", @"child-5", @"child-6"]];
|
|
[self waitForSync:userCRealm];
|
|
CHECK_COUNT(3, SyncObject, userCRealm);
|
|
|
|
// userB should not be able to grant write permissions to userC
|
|
[userBRealm transactionWithBlock:^{
|
|
addUserToRole(userBRealm, @"writer", self.userC.identity);
|
|
}];
|
|
[self waitForSync:userBRealm];
|
|
[self waitForSync:userCRealm];
|
|
|
|
CHECK_REALM_PRIVILEGE(userCRealm, .read = true);
|
|
CHECK_CLASS_PRIVILEGE(userCRealm, .read = true, .subscribe = true);
|
|
CHECK_OBJECT_PRIVILEGE(userCRealm, .read = true);
|
|
[self addSyncObjectsToRealm:userCRealm descriptions:@[@"child-4", @"child-5", @"child-6"]];
|
|
[self waitForSync:userCRealm];
|
|
CHECK_COUNT(3, SyncObject, userCRealm);
|
|
|
|
// userA should be able to grant write permissions to userC
|
|
[userARealm transactionWithBlock:^{
|
|
addUserToRole(userARealm, @"writer", self.userC.identity);
|
|
}];
|
|
[self waitForSync:userARealm];
|
|
[self waitForSync:userCRealm];
|
|
|
|
CHECK_REALM_PRIVILEGE(userCRealm, .read = true, .update = true);
|
|
CHECK_CLASS_PRIVILEGE(userCRealm, .read = true, .subscribe = true, .update = true, .create = true);
|
|
CHECK_OBJECT_PRIVILEGE(userCRealm, .read = true, .update = true, .del = true);
|
|
[self addSyncObjectsToRealm:userCRealm descriptions:@[@"child-4", @"child-5", @"child-6"]];
|
|
[self waitForSync:userCRealm];
|
|
CHECK_COUNT(6, SyncObject, userCRealm);
|
|
#endif
|
|
}
|
|
|
|
- (void)testRealmModifySchema {
|
|
// awkward to test due to that reverts will normally crash
|
|
// probably need to spawn a child process?
|
|
}
|
|
|
|
- (void)testClassRead {
|
|
NSURL *url = [self createRealmWithName:_cmd permissions:^(RLMRealm *realm) {
|
|
createPermissions([RLMClassPermission objectInRealm:realm forClass:SyncObject.class].permissions);
|
|
addUserToRole(realm, @"reader", self.userA.identity);
|
|
}];
|
|
|
|
// userA should now be able to open the Realm and see objects
|
|
RLMRealm *userARealm = [self openRealmForURL:url user:self.userA];
|
|
[self subscribeToRealm:userARealm type:[SyncObject class] where:@"TRUEPREDICATE"];
|
|
CHECK_REALM_PRIVILEGE(userARealm, .read = true, .update = true, .setPermissions = true, .modifySchema = true);
|
|
CHECK_CLASS_PRIVILEGE(userARealm, .read = true, .subscribe = true);
|
|
CHECK_OBJECT_PRIVILEGE(userARealm, .read = true);
|
|
CHECK_COUNT(3, SyncObject, userARealm);
|
|
|
|
// userA should not be able to create new objects
|
|
[self addSyncObjectsToRealm:userARealm descriptions:@[@"child-4", @"child-5", @"child-6"]];
|
|
CHECK_COUNT(6, SyncObject, userARealm);
|
|
[self waitForSync:userARealm];
|
|
CHECK_COUNT(3, SyncObject, userARealm);
|
|
|
|
// userB should not be able to read any objects
|
|
RLMRealm *userBRealm = [self openRealmForURL:url user:self.userB];
|
|
[self subscribeToRealm:userBRealm type:[SyncObject class] where:@"TRUEPREDICATE"];
|
|
CHECK_REALM_PRIVILEGE(userBRealm, .read = true, .update = true, .setPermissions = true, .modifySchema = true);
|
|
CHECK_CLASS_PRIVILEGE(userBRealm, .read = false);
|
|
CHECK_COUNT(0, SyncObject, userBRealm);
|
|
}
|
|
|
|
- (void)testClassUpdate {
|
|
NSURL *url = [self createRealmWithName:_cmd permissions:^(RLMRealm *realm) {
|
|
createPermissions([RLMClassPermission objectInRealm:realm forClass:SyncObject.class].permissions);
|
|
|
|
addUserToRole(realm, @"reader", self.userA.identity);
|
|
addUserToRole(realm, @"writer", self.userA.identity);
|
|
|
|
addUserToRole(realm, @"reader", self.userB.identity);
|
|
}];
|
|
|
|
// userA should be able to mutate objects
|
|
RLMRealm *userARealm = [self openRealmForURL:url user:self.userA];
|
|
[self subscribeToRealm:userARealm type:[SyncObject class] where:@"TRUEPREDICATE"];
|
|
CHECK_REALM_PRIVILEGE(userARealm, .read = true, .update = true, .setPermissions = true, .modifySchema = true);
|
|
CHECK_CLASS_PRIVILEGE(userARealm, .read = true, .subscribe = true, .update = true, .create = true);
|
|
CHECK_OBJECT_PRIVILEGE(userARealm, .read = true, .update = true, .del = true, .setPermissions = true);
|
|
|
|
SyncObject *objA = [SyncObject allObjectsInRealm:userARealm].firstObject;
|
|
[userARealm transactionWithBlock:^{
|
|
objA.stringProp = @"new value";
|
|
}];
|
|
[self waitForSync:userARealm];
|
|
XCTAssertEqualObjects(objA.stringProp, @"new value");
|
|
|
|
// userB's mutations should be reverted
|
|
RLMRealm *userBRealm = [self openRealmForURL:url user:self.userB];
|
|
[self subscribeToRealm:userBRealm type:[SyncObject class] where:@"TRUEPREDICATE"];
|
|
CHECK_REALM_PRIVILEGE(userBRealm, .read = true, .update = true, .setPermissions = true, .modifySchema = true);
|
|
CHECK_CLASS_PRIVILEGE(userBRealm, .read = true, .subscribe = true);
|
|
CHECK_OBJECT_PRIVILEGE(userBRealm, .read = true);
|
|
|
|
SyncObject *objB = [SyncObject allObjectsInRealm:userBRealm].firstObject;
|
|
[userBRealm transactionWithBlock:^{
|
|
objB.stringProp = @"new value 2";
|
|
}];
|
|
XCTAssertEqualObjects(objB.stringProp, @"new value 2");
|
|
[self waitForSync:userBRealm];
|
|
XCTAssertEqualObjects(objB.stringProp, @"new value");
|
|
}
|
|
|
|
- (void)testClassCreate {
|
|
NSURL *url = [self createRealmWithName:_cmd permissions:^(RLMRealm *realm) {
|
|
createPermissions([RLMClassPermission objectInRealm:realm forClass:SyncObject.class].permissions);
|
|
|
|
addUserToRole(realm, @"reader", self.userA.identity);
|
|
addUserToRole(realm, @"writer", self.userA.identity);
|
|
|
|
addUserToRole(realm, @"reader", self.userB.identity);
|
|
}];
|
|
|
|
// userA should be able to add objects
|
|
RLMRealm *userARealm = [self openRealmForURL:url user:self.userA];
|
|
[self subscribeToRealm:userARealm type:[SyncObject class] where:@"TRUEPREDICATE"];
|
|
CHECK_REALM_PRIVILEGE(userARealm, .read = true, .update = true, .setPermissions = true, .modifySchema = true);
|
|
CHECK_CLASS_PRIVILEGE(userARealm, .read = true, .subscribe = true, .update = true, .create = true);
|
|
CHECK_OBJECT_PRIVILEGE(userARealm, .read = true, .update = true, .del = true, .setPermissions = true);
|
|
|
|
CHECK_COUNT(3, SyncObject, userARealm);
|
|
[self addSyncObjectsToRealm:userARealm descriptions:@[@"child-4", @"child-5", @"child-6"]];
|
|
CHECK_COUNT(6, SyncObject, userARealm);
|
|
[self waitForSync:userARealm];
|
|
CHECK_COUNT(6, SyncObject, userARealm);
|
|
|
|
// userB's insertions should be reverted
|
|
RLMRealm *userBRealm = [self openRealmForURL:url user:self.userB];
|
|
[self subscribeToRealm:userBRealm type:[SyncObject class] where:@"TRUEPREDICATE"];
|
|
CHECK_REALM_PRIVILEGE(userBRealm, .read = true, .update = true, .setPermissions = true, .modifySchema = true);
|
|
CHECK_CLASS_PRIVILEGE(userBRealm, .read = true, .subscribe = true);
|
|
CHECK_OBJECT_PRIVILEGE(userBRealm, .read = true);
|
|
|
|
CHECK_COUNT(6, SyncObject, userBRealm);
|
|
[self addSyncObjectsToRealm:userBRealm descriptions:@[@"child-4", @"child-5", @"child-6"]];
|
|
CHECK_COUNT(9, SyncObject, userBRealm);
|
|
[self waitForSync:userBRealm];
|
|
CHECK_COUNT(6, SyncObject, userBRealm);
|
|
}
|
|
|
|
- (void)testClassSetPermissions {
|
|
NSURL *url = [self createRealmWithName:_cmd permissions:^(RLMRealm *realm) {
|
|
createPermissions([RLMClassPermission objectInRealm:realm forClass:SyncObject.class].permissions);
|
|
|
|
addUserToRole(realm, @"reader", self.userA.identity);
|
|
addUserToRole(realm, @"writer", self.userA.identity);
|
|
addUserToRole(realm, @"admin", self.userA.identity);
|
|
|
|
addUserToRole(realm, @"reader", self.userB.identity);
|
|
addUserToRole(realm, @"writer", self.userB.identity);
|
|
|
|
addUserToRole(realm, @"reader", self.userC.identity);
|
|
}];
|
|
|
|
// Despite having write access userB should not be able to add "update" access to "reader"
|
|
RLMRealm *userBRealm = [self openRealmForURL:url user:self.userB];
|
|
[self subscribeToRealm:userBRealm type:[SyncObject class] where:@"TRUEPREDICATE"];
|
|
[userBRealm transactionWithBlock:^{
|
|
auto permission = [RLMPermission permissionForRoleNamed:@"reader" onClass:SyncObject.class realm:userBRealm];
|
|
permission.canCreate = true;
|
|
permission.canUpdate = true;
|
|
|
|
}];
|
|
[self waitForSync:userBRealm];
|
|
|
|
// userC should be unable to create objects
|
|
RLMRealm *userCRealm = [self openRealmForURL:url user:self.userC];
|
|
[self subscribeToRealm:userCRealm type:[SyncObject class] where:@"TRUEPREDICATE"];
|
|
CHECK_REALM_PRIVILEGE(userCRealm, .read = true, .update = true, .setPermissions = true, .modifySchema = true);
|
|
CHECK_CLASS_PRIVILEGE(userCRealm, .read = true, .subscribe = true);
|
|
CHECK_OBJECT_PRIVILEGE(userCRealm, .read = true);
|
|
|
|
[self addSyncObjectsToRealm:userCRealm descriptions:@[@"child-4", @"child-5", @"child-6"]];
|
|
CHECK_COUNT(6, SyncObject, userCRealm);
|
|
[self waitForSync:userCRealm];
|
|
CHECK_COUNT(3, SyncObject, userCRealm);
|
|
|
|
// userA should able to add "update" access to "reader"
|
|
RLMRealm *userARealm = [self openRealmForURL:url user:self.userA];
|
|
[self subscribeToRealm:userARealm type:[SyncObject class] where:@"TRUEPREDICATE"];
|
|
[userARealm transactionWithBlock:^{
|
|
auto permission = [RLMPermission permissionForRoleNamed:@"reader" onClass:SyncObject.class realm:userARealm];
|
|
permission.canCreate = true;
|
|
permission.canUpdate = true;
|
|
}];
|
|
[self waitForSync:userARealm];
|
|
|
|
// userC should now be able to create objects
|
|
[self waitForSync:userCRealm];
|
|
CHECK_CLASS_PRIVILEGE(userCRealm, .read = true, .subscribe = true, .update = true, .create = true);
|
|
CHECK_OBJECT_PRIVILEGE(userCRealm, .read = true, .update = true, .del = true, .setPermissions = true);
|
|
|
|
[self addSyncObjectsToRealm:userCRealm descriptions:@[@"child-4", @"child-5", @"child-6"]];
|
|
CHECK_COUNT(6, SyncObject, userCRealm);
|
|
[self waitForSync:userCRealm];
|
|
CHECK_COUNT(6, SyncObject, userCRealm);
|
|
}
|
|
|
|
- (void)testObjectRead {
|
|
NSURL *url = [self createRealmWithName:_cmd permissions:^(RLMRealm *realm) {
|
|
addUserToRole(realm, @"reader", self.userA.identity);
|
|
auto obj1 = [ObjectWithPermissions createInRealm:realm withValue:@[@1]];
|
|
createPermissions(obj1.permissions);
|
|
[ObjectWithPermissions createInRealm:realm withValue:@[@2]];
|
|
}];
|
|
|
|
// userA should be able to see both objects
|
|
RLMRealm *userARealm = [self openRealmForURL:url user:self.userA];
|
|
[self subscribeToRealm:userARealm type:[ObjectWithPermissions class] where:@"TRUEPREDICATE"];
|
|
CHECK_COUNT(1, ObjectWithPermissions, userARealm);
|
|
|
|
// userB should not be able to read any objects
|
|
RLMRealm *userBRealm = [self openRealmForURL:url user:self.userB];
|
|
[self subscribeToRealm:userBRealm type:[ObjectWithPermissions class] where:@"TRUEPREDICATE"];
|
|
CHECK_COUNT(0, ObjectWithPermissions, userBRealm);
|
|
}
|
|
|
|
- (void)testObjectTransitiveRead {
|
|
}
|
|
|
|
- (void)testObjectUpdate {
|
|
NSURL *url = [self createRealmWithName:_cmd permissions:^(RLMRealm *realm) {
|
|
addUserToRole(realm, @"reader", self.userA.identity);
|
|
addUserToRole(realm, @"reader", self.userB.identity);
|
|
addUserToRole(realm, @"writer", self.userB.identity);
|
|
auto obj1 = [ObjectWithPermissions createInRealm:realm withValue:@[@1]];
|
|
createPermissions(obj1.permissions);
|
|
}];
|
|
|
|
RLMRealm *userARealm = [self openRealmForURL:url user:self.userA];
|
|
[self subscribeToRealm:userARealm type:[ObjectWithPermissions class] where:@"TRUEPREDICATE"];
|
|
ObjectWithPermissions *objA = [ObjectWithPermissions allObjectsInRealm:userARealm].firstObject;
|
|
[userARealm transactionWithBlock:^{
|
|
objA.value = 3;
|
|
}];
|
|
[self waitForSync:userARealm];
|
|
XCTAssertEqual(objA.value, 1);
|
|
|
|
RLMRealm *userBRealm = [self openRealmForURL:url user:self.userB];
|
|
[self subscribeToRealm:userBRealm type:[ObjectWithPermissions class] where:@"TRUEPREDICATE"];
|
|
ObjectWithPermissions *objB = [ObjectWithPermissions allObjectsInRealm:userBRealm].firstObject;
|
|
[userBRealm transactionWithBlock:^{
|
|
objB.value = 3;
|
|
}];
|
|
[self waitForSync:userBRealm];
|
|
[self waitForSync:userARealm];
|
|
|
|
XCTAssertEqual(objA.value, 3);
|
|
XCTAssertEqual(objB.value, 3);
|
|
}
|
|
|
|
- (void)testObjectDelete {
|
|
NSURL *url = [self createRealmWithName:_cmd permissions:^(RLMRealm *realm) {
|
|
addUserToRole(realm, @"reader", self.userA.identity);
|
|
addUserToRole(realm, @"reader", self.userB.identity);
|
|
addUserToRole(realm, @"writer", self.userB.identity);
|
|
auto obj1 = [ObjectWithPermissions createInRealm:realm withValue:@[@1]];
|
|
createPermissions(obj1.permissions);
|
|
}];
|
|
|
|
RLMRealm *userARealm = [self openRealmForURL:url user:self.userA];
|
|
[self subscribeToRealm:userARealm type:[ObjectWithPermissions class] where:@"TRUEPREDICATE"];
|
|
ObjectWithPermissions *objA = [ObjectWithPermissions allObjectsInRealm:userARealm].firstObject;
|
|
[userARealm transactionWithBlock:^{
|
|
[userARealm deleteObject:objA];
|
|
}];
|
|
CHECK_COUNT(0, ObjectWithPermissions, userARealm);
|
|
[self waitForSync:userARealm];
|
|
CHECK_COUNT(1, ObjectWithPermissions, userARealm);
|
|
objA = [ObjectWithPermissions allObjectsInRealm:userARealm].firstObject;
|
|
|
|
RLMRealm *userBRealm = [self openRealmForURL:url user:self.userB];
|
|
[self subscribeToRealm:userBRealm type:[ObjectWithPermissions class] where:@"TRUEPREDICATE"];
|
|
ObjectWithPermissions *objB = [ObjectWithPermissions allObjectsInRealm:userBRealm].firstObject;
|
|
[userBRealm transactionWithBlock:^{
|
|
[userBRealm deleteObject:objB];
|
|
}];
|
|
[self waitForSync:userBRealm];
|
|
[self waitForSync:userARealm];
|
|
|
|
CHECK_COUNT(0, ObjectWithPermissions, userARealm);
|
|
CHECK_COUNT(0, ObjectWithPermissions, userBRealm);
|
|
XCTAssertTrue(objA.invalidated);
|
|
XCTAssertTrue(objB.invalidated);
|
|
}
|
|
|
|
- (void)testObjectSetPermissions {
|
|
NSURL *url = [self createRealmWithName:_cmd permissions:^(RLMRealm *) {}];
|
|
|
|
RLMRealm *userARealm = [self openRealmForURL:url user:self.userA];
|
|
[self subscribeToRealm:userARealm type:[ObjectWithPermissions class] where:@"TRUEPREDICATE"];
|
|
[userARealm transactionWithBlock:^{
|
|
auto obj = [ObjectWithPermissions createInRealm:userARealm withValue:@[@1]];
|
|
auto permissions = [RLMPermission permissionForRoleNamed:@"foo" onObject:obj];
|
|
permissions.canRead = true;
|
|
addUserToRole(userARealm, @"foo", self.userB.identity);
|
|
}];
|
|
|
|
CHECK_COUNT(1, ObjectWithPermissions, userARealm);
|
|
[self waitForSync:userARealm];
|
|
CHECK_COUNT(0, ObjectWithPermissions, userARealm);
|
|
|
|
RLMRealm *userBRealm = [self openRealmForURL:url user:self.userB];
|
|
[self subscribeToRealm:userBRealm type:[ObjectWithPermissions class] where:@"TRUEPREDICATE"];
|
|
CHECK_COUNT(1, ObjectWithPermissions, userBRealm);
|
|
}
|
|
|
|
- (void)testRetrieveClassPermissionsForRenamedClass {
|
|
[self createRealmWithName:_cmd permissions:^(RLMRealm *realm) {
|
|
XCTAssertNotNil([RLMClassPermission objectInRealm:realm forClass:RLMPermissionRole.class]);
|
|
}];
|
|
}
|
|
|
|
@end
|
|
|