123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375 |
- ////////////////////////////////////////////////////////////////////////////
- //
- // Copyright 2017 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 "RLMSyncPermission.h"
- #import "RLMArray.h"
- #import "RLMObjectSchema.h"
- #import "RLMRealm_Dynamic.h"
- #import "RLMResults.h"
- #import "RLMSyncUser.h"
- #import "RLMSyncUtil_Private.hpp"
- #import "RLMUtil.hpp"
- using namespace realm;
- static void verifyInWriteTransaction(__unsafe_unretained RLMRealm *const realm, SEL sel) {
- if (!realm) {
- @throw RLMException(@"Cannot call %@ on an unmanaged object.", NSStringFromSelector(sel));
- }
- if (!realm.inWriteTransaction) {
- @throw RLMException(@"Cannot call %@ outside of a write transaction.", NSStringFromSelector(sel));
- }
- }
- id RLMPermissionForRole(RLMArray *array, id role) {
- RLMResults *filtered = [array objectsWhere:@"role.name = %@", [role name]];
- RLMPermission *permission;
- for (RLMPermission *p in filtered) {
- if (permission == nil) {
- permission = p;
- }
- // If there's more than one permission for the role, merge it into the first
- // one and then delete it as otherwise revoking permissions won't actually work
- else {
- if (p.canRead && !permission.canRead) {
- permission.canRead = true;
- }
- if (p.canUpdate && !permission.canUpdate) {
- permission.canUpdate = true;
- }
- if (p.canDelete && !permission.canDelete) {
- permission.canDelete = true;
- }
- if (p.canSetPermissions && !permission.canSetPermissions) {
- permission.canSetPermissions = true;
- }
- if (p.canQuery && !permission.canQuery) {
- permission.canQuery = true;
- }
- if (p.canCreate && !permission.canCreate) {
- permission.canCreate = true;
- }
- if (p.canModifySchema && !permission.canModifySchema) {
- permission.canModifySchema = true;
- }
- [array.realm deleteObject:p];
- }
- }
- if (!permission) {
- // Use the dynamic API to create the appropriate Permission class for the array
- permission = (id)[array.realm createObject:array.objectClassName withValue:@[role]];
- [array addObject:permission];
- }
- return permission;
- }
- @implementation RLMPermissionRole
- + (NSString *)_realmObjectName {
- return @"__Role";
- }
- + (NSString *)primaryKey {
- return @"name";
- }
- + (NSArray *)requiredProperties {
- return @[@"name"];
- }
- + (NSDictionary *)_realmColumnNames {
- return @{@"users": @"members"};
- }
- @end
- @implementation RLMPermissionUser
- + (NSString *)_realmObjectName {
- return @"__User";
- }
- + (NSString *)primaryKey {
- return @"identity";
- }
- + (NSArray *)requiredProperties {
- return @[@"identity"];
- }
- + (NSDictionary *)_realmColumnNames {
- return @{@"identity": @"id", @"role": @"role"};
- }
- + (NSDictionary *)linkingObjectsProperties {
- return @{@"roles": [RLMPropertyDescriptor descriptorWithClass:RLMPermissionRole.class propertyName:@"users"]};
- }
- + (RLMPermissionUser *)userInRealm:(RLMRealm *)realm withIdentity:(NSString *)identity {
- return [self createOrUpdateInRealm:realm withValue:@[identity]];
- }
- @end
- @implementation RLMPermission
- + (NSString *)_realmObjectName {
- return @"__Permission";
- }
- + (NSDictionary *)defaultPropertyValues {
- return @{@"canRead": @NO,
- @"canUpdate": @NO,
- @"canDelete": @NO,
- @"canSetPermissions": @NO,
- @"canQuery": @NO,
- @"canCreate": @NO,
- @"canModifySchema": @NO};
- }
- + (RLMPermission *)permissionForRole:(RLMPermissionRole *)role inArray:(RLMArray<RLMPermission *><RLMPermission> *)array {
- verifyInWriteTransaction(array.realm, _cmd);
- auto index = [array indexOfObjectWhere:@"role = %@", role];
- if (index != NSNotFound) {
- return array[index];
- }
- RLMPermission *permission = [RLMPermission createInRealm:role.realm withValue:@[role]];
- [array addObject:permission];
- return permission;
- }
- + (RLMPermission *)permissionForRoleNamed:(NSString *)roleName inArray:(RLMArray<RLMPermission *><RLMPermission> *)array {
- verifyInWriteTransaction(array.realm, _cmd);
- return RLMPermissionForRole(array, [RLMPermissionRole createOrUpdateInRealm:array.realm withValue:@{@"name": roleName}]);
- }
- + (RLMPermission *)permissionForRoleNamed:(NSString *)roleName onRealm:(RLMRealm *)realm {
- verifyInWriteTransaction(realm, _cmd);
- return [self permissionForRoleNamed:roleName
- inArray:[RLMRealmPermission objectInRealm:realm].permissions];
- }
- + (RLMPermission *)permissionForRoleNamed:(NSString *)roleName onClass:(Class)cls realm:(RLMRealm *)realm {
- verifyInWriteTransaction(realm, _cmd);
- return [self permissionForRoleNamed:roleName
- inArray:[RLMClassPermission objectInRealm:realm forClass:cls].permissions];
- }
- + (RLMPermission *)permissionForRoleNamed:(NSString *)roleName onClassNamed:(NSString *)className realm:(RLMRealm *)realm {
- verifyInWriteTransaction(realm, _cmd);
- return [self permissionForRoleNamed:roleName
- inArray:[RLMClassPermission objectInRealm:realm forClassNamed:className].permissions];
- }
- + (RLMPermission *)permissionForRoleNamed:(NSString *)roleName onObject:(RLMObject *)object {
- verifyInWriteTransaction(object.realm, _cmd);
- for (RLMProperty *prop in object.objectSchema.properties) {
- if (prop.array && [prop.objectClassName isEqualToString:@"RLMPermission"]) {
- return [self permissionForRoleNamed:roleName
- inArray:[object valueForKey:prop.name]];
- }
- }
- @throw RLMException(@"Object %@ does not have a RLMArray<RLMPermission *> property.", object);
- }
- @end
- @implementation RLMClassPermission
- + (NSString *)_realmObjectName {
- return @"__Class";
- }
- + (NSString *)primaryKey {
- return @"name";
- }
- + (NSArray *)requiredProperties {
- return @[@"name"];
- }
- + (instancetype)objectInRealm:(RLMRealm *)realm forClassNamed:(NSString *)name {
- return [RLMClassPermission objectInRealm:realm forPrimaryKey:name];
- }
- + (instancetype)objectInRealm:(RLMRealm *)realm forClass:(Class)cls {
- return [RLMClassPermission objectInRealm:realm forPrimaryKey:[cls _realmObjectName] ?: [cls className]];
- }
- @end
- @interface RLMRealmPermission ()
- @property (nonatomic) int pk;
- @end
- @implementation RLMRealmPermission
- + (NSString *)_realmObjectName {
- return @"__Realm";
- }
- + (NSDictionary *)_realmColumnNames {
- return @{@"pk": @"id"};
- }
- + (NSString *)primaryKey {
- return @"pk";
- }
- + (instancetype)objectInRealm:(RLMRealm *)realm {
- return [RLMRealmPermission objectInRealm:realm forPrimaryKey:@0];
- }
- @end
- #pragma mark - Permission
- @implementation RLMSyncPermission
- - (instancetype)initWithRealmPath:(NSString *)path
- identity:(NSString *)identity
- accessLevel:(RLMSyncAccessLevel)accessLevel {
- if (self = [super init]) {
- _accessLevel = accessLevel;
- _path = path;
- _identity = identity;
- if (!identity) {
- @throw RLMException(@"A permission value cannot be created without a valid user ID");
- }
- _updatedAt = [NSDate date];
- }
- return self;
- }
- - (instancetype)initWithRealmPath:(NSString *)path
- username:(NSString *)username
- accessLevel:(RLMSyncAccessLevel)accessLevel {
- if (self = [super init]) {
- _accessLevel = accessLevel;
- _path = path;
- _identity = nil;
- _key = @"email";
- _value = username;
- _updatedAt = [NSDate date];
- }
- return self;
- }
- + (BOOL)accessInstanceVariablesDirectly {
- return NO;
- }
- - (BOOL)mayRead {
- return self.accessLevel > RLMSyncAccessLevelNone;
- }
- - (BOOL)mayWrite {
- return self.accessLevel > RLMSyncAccessLevelRead;
- }
- - (BOOL)mayManage {
- return self.accessLevel == RLMSyncAccessLevelAdmin;
- }
- - (NSUInteger)hash {
- return self.identity.hash ^ self.accessLevel ^ self.path.hash ^ self.key.hash ^ self.value.hash;
- }
- - (BOOL)isEqual:(id)object {
- if (self == object) {
- return YES;
- }
- if (![object isKindOfClass:[RLMSyncPermission class]]) {
- return NO;
- }
- RLMSyncPermission *that = (RLMSyncPermission *)object;
- if (self.accessLevel != that.accessLevel) {
- return NO;
- }
- if (![self.path isEqualToString:that.path]) {
- return NO;
- }
- if (self.identity) {
- if (!that.identity || ![self.identity isEqualToString:that.identity]) {
- return NO;
- }
- }
- else {
- if (that.identity || ![self.key isEqualToString:that.key] || ![self.value isEqualToString:that.value]) {
- return NO;
- }
- }
- return YES;
- }
- - (RLMSyncPermission *)tildeExpandedSyncPermissionForUser:(RLMSyncUser *)user {
- if (![self.path hasPrefix:@"/~/"]) {
- return self;
- }
- NSString *path = [self.path stringByReplacingCharactersInRange:{1, 1} withString:user.identity];
- if (_identity) {
- return [[RLMSyncPermission alloc] initWithRealmPath:path identity:_identity accessLevel:_accessLevel];
- }
- return [[RLMSyncPermission alloc] initWithRealmPath:path username:_value accessLevel:_accessLevel];
- }
- - (NSString *)description {
- NSString *typeDescription = nil;
- if (self.identity) {
- typeDescription = [NSString stringWithFormat:@"identity: %@", self.identity];
- } else {
- typeDescription = [NSString stringWithFormat:@"key: %@, value: %@", self.key, self.value];
- }
- return [NSString stringWithFormat:@"<RLMSyncPermission> %@, path: %@, access level: %@",
- typeDescription, self.path, RLMSyncAccessLevelToString(self.accessLevel)];
- }
- NSString *RLMSyncAccessLevelToString(RLMSyncAccessLevel level) {
- switch (level) {
- case RLMSyncAccessLevelNone: return @"none";
- case RLMSyncAccessLevelRead: return @"read";
- case RLMSyncAccessLevelWrite: return @"write";
- case RLMSyncAccessLevelAdmin: return @"admin";
- }
- return @"unknown";
- }
- RLMSyncAccessLevel RLMSyncAccessLevelFromString(NSString *level) {
- if ([level isEqualToString:@"none"]) {
- return RLMSyncAccessLevelNone;
- }
- if ([level isEqualToString:@"read"]) {
- return RLMSyncAccessLevelRead;
- }
- if ([level isEqualToString:@"write"]) {
- return RLMSyncAccessLevelWrite;
- }
- if ([level isEqualToString:@"admin"]) {
- return RLMSyncAccessLevelAdmin;
- }
- @throw RLMException(@"Invalid access level %@", level);
- }
- @end
- @implementation RLMSyncPermissionOffer
- - (instancetype)initWithRealmPath:(NSString *)path
- token:(NSString *)token
- expiresAt:(NSDate *)expiresAt
- createdAt:(NSDate *)createdAt
- accessLevel:(RLMSyncAccessLevel)accessLevel {
- if (self = [super init]) {
- _realmPath = path;
- _token = token;
- _expiresAt = expiresAt;
- _createdAt = createdAt;
- _accessLevel = accessLevel;
- }
- return self;
- }
- + (BOOL)accessInstanceVariablesDirectly {
- return NO;
- }
- - (NSString *)description {
- return [NSString stringWithFormat:@"<RLMSyncPermissionOffer: %p> path: %@, access level: %@, token: %@, createdAt: %@, expiresAt: %@",
- self, _realmPath, RLMSyncAccessLevelToString(_accessLevel),
- _token, _createdAt, _expiresAt];
- }
- @end
|