RLMSyncPermission.mm 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382
  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 "RLMSyncPermission_Private.hpp"
  19. #import "RLMArray.h"
  20. #import "RLMObjectSchema.h"
  21. #import "RLMRealm_Dynamic.h"
  22. #import "RLMSyncUtil_Private.hpp"
  23. #import "RLMUtil.hpp"
  24. #import "RLMResults.h"
  25. using namespace realm;
  26. using ConditionType = Permission::Condition::Type;
  27. static void verifyInWriteTransaction(__unsafe_unretained RLMRealm *const realm, SEL sel) {
  28. if (!realm) {
  29. @throw RLMException(@"Cannot call %@ on an unmanaged object.", NSStringFromSelector(sel));
  30. }
  31. if (!realm.inWriteTransaction) {
  32. @throw RLMException(@"Cannot call %@ outside of a write transaction.", NSStringFromSelector(sel));
  33. }
  34. }
  35. id RLMPermissionForRole(RLMArray *array, id role) {
  36. RLMResults *filtered = [array objectsWhere:@"role.name = %@", [role name]];
  37. RLMPermission *permission;
  38. for (RLMPermission *p in filtered) {
  39. if (permission == nil) {
  40. permission = p;
  41. }
  42. // If there's more than one permission for the role, merge it into the first
  43. // one and then delete it as otherwise revoking permissions won't actually work
  44. else {
  45. if (p.canRead && !permission.canRead) {
  46. permission.canRead = true;
  47. }
  48. if (p.canUpdate && !permission.canUpdate) {
  49. permission.canUpdate = true;
  50. }
  51. if (p.canDelete && !permission.canDelete) {
  52. permission.canDelete = true;
  53. }
  54. if (p.canSetPermissions && !permission.canSetPermissions) {
  55. permission.canSetPermissions = true;
  56. }
  57. if (p.canQuery && !permission.canQuery) {
  58. permission.canQuery = true;
  59. }
  60. if (p.canCreate && !permission.canCreate) {
  61. permission.canCreate = true;
  62. }
  63. if (p.canModifySchema && !permission.canModifySchema) {
  64. permission.canModifySchema = true;
  65. }
  66. [array.realm deleteObject:p];
  67. }
  68. }
  69. if (!permission) {
  70. // Use the dynamic API to create the appropriate Permission class for the array
  71. permission = (id)[array.realm createObject:array.objectClassName withValue:@[role]];
  72. [array addObject:permission];
  73. }
  74. return permission;
  75. }
  76. @implementation RLMPermissionRole
  77. + (NSString *)_realmObjectName {
  78. return @"__Role";
  79. }
  80. + (NSString *)primaryKey {
  81. return @"name";
  82. }
  83. + (NSArray *)requiredProperties {
  84. return @[@"name"];
  85. }
  86. + (NSDictionary *)_realmColumnNames {
  87. return @{@"users": @"members"};
  88. }
  89. @end
  90. @implementation RLMPermissionUser
  91. + (NSString *)_realmObjectName {
  92. return @"__User";
  93. }
  94. + (NSString *)primaryKey {
  95. return @"identity";
  96. }
  97. + (NSArray *)requiredProperties {
  98. return @[@"identity"];
  99. }
  100. + (NSDictionary *)_realmColumnNames {
  101. return @{@"identity": @"id", @"role": @"role"};
  102. }
  103. + (NSDictionary *)linkingObjectsProperties {
  104. return @{@"roles": [RLMPropertyDescriptor descriptorWithClass:RLMPermissionRole.class propertyName:@"users"]};
  105. }
  106. + (RLMPermissionUser *)userInRealm:(RLMRealm *)realm withIdentity:(NSString *)identity {
  107. return [self createOrUpdateInRealm:realm withValue:@[identity]];
  108. }
  109. @end
  110. @implementation RLMPermission
  111. + (NSString *)_realmObjectName {
  112. return @"__Permission";
  113. }
  114. + (NSDictionary *)defaultPropertyValues {
  115. return @{@"canRead": @NO,
  116. @"canUpdate": @NO,
  117. @"canDelete": @NO,
  118. @"canSetPermissions": @NO,
  119. @"canQuery": @NO,
  120. @"canCreate": @NO,
  121. @"canModifySchema": @NO};
  122. }
  123. + (RLMPermission *)permissionForRole:(RLMPermissionRole *)role inArray:(RLMArray<RLMPermission *><RLMPermission> *)array {
  124. verifyInWriteTransaction(array.realm, _cmd);
  125. auto index = [array indexOfObjectWhere:@"role = %@", role];
  126. if (index != NSNotFound) {
  127. return array[index];
  128. }
  129. RLMPermission *permission = [RLMPermission createInRealm:role.realm withValue:@[role]];
  130. [array addObject:permission];
  131. return permission;
  132. }
  133. + (RLMPermission *)permissionForRoleNamed:(NSString *)roleName inArray:(RLMArray<RLMPermission *><RLMPermission> *)array {
  134. verifyInWriteTransaction(array.realm, _cmd);
  135. return RLMPermissionForRole(array, [RLMPermissionRole createOrUpdateInRealm:array.realm withValue:@{@"name": roleName}]);
  136. }
  137. + (RLMPermission *)permissionForRoleNamed:(NSString *)roleName onRealm:(RLMRealm *)realm {
  138. verifyInWriteTransaction(realm, _cmd);
  139. return [self permissionForRoleNamed:roleName
  140. inArray:[RLMRealmPermission objectInRealm:realm].permissions];
  141. }
  142. + (RLMPermission *)permissionForRoleNamed:(NSString *)roleName onClass:(Class)cls realm:(RLMRealm *)realm {
  143. verifyInWriteTransaction(realm, _cmd);
  144. return [self permissionForRoleNamed:roleName
  145. inArray:[RLMClassPermission objectInRealm:realm forClass:cls].permissions];
  146. }
  147. + (RLMPermission *)permissionForRoleNamed:(NSString *)roleName onClassNamed:(NSString *)className realm:(RLMRealm *)realm {
  148. verifyInWriteTransaction(realm, _cmd);
  149. return [self permissionForRoleNamed:roleName
  150. inArray:[RLMClassPermission objectInRealm:realm forClassNamed:className].permissions];
  151. }
  152. + (RLMPermission *)permissionForRoleNamed:(NSString *)roleName onObject:(RLMObject *)object {
  153. verifyInWriteTransaction(object.realm, _cmd);
  154. for (RLMProperty *prop in object.objectSchema.properties) {
  155. if (prop.array && [prop.objectClassName isEqualToString:@"RLMPermission"]) {
  156. return [self permissionForRoleNamed:roleName
  157. inArray:[object valueForKey:prop.name]];
  158. }
  159. }
  160. @throw RLMException(@"Object %@ does not have a RLMArray<RLMPermission *> property.", object);
  161. }
  162. @end
  163. @implementation RLMClassPermission
  164. + (NSString *)_realmObjectName {
  165. return @"__Class";
  166. }
  167. + (NSString *)primaryKey {
  168. return @"name";
  169. }
  170. + (NSArray *)requiredProperties {
  171. return @[@"name"];
  172. }
  173. + (instancetype)objectInRealm:(RLMRealm *)realm forClassNamed:(NSString *)name {
  174. return [RLMClassPermission objectInRealm:realm forPrimaryKey:name];
  175. }
  176. + (instancetype)objectInRealm:(RLMRealm *)realm forClass:(Class)cls {
  177. return [RLMClassPermission objectInRealm:realm forPrimaryKey:[cls _realmObjectName] ?: [cls className]];
  178. }
  179. @end
  180. @interface RLMRealmPermission ()
  181. @property (nonatomic) int pk;
  182. @end
  183. @implementation RLMRealmPermission
  184. + (NSString *)_realmObjectName {
  185. return @"__Realm";
  186. }
  187. + (NSDictionary *)_realmColumnNames {
  188. return @{@"pk": @"id"};
  189. }
  190. + (NSString *)primaryKey {
  191. return @"pk";
  192. }
  193. + (instancetype)objectInRealm:(RLMRealm *)realm {
  194. return [RLMRealmPermission objectInRealm:realm forPrimaryKey:@0];
  195. }
  196. @end
  197. #pragma mark - Permission
  198. @interface RLMSyncPermission () {
  199. @private
  200. NSString *_identity;
  201. NSString *_key;
  202. NSString *_value;
  203. util::Optional<Permission> _underlying;
  204. RLMSyncAccessLevel _accessLevel;
  205. NSString *_path;
  206. NSDate *_updatedAt;
  207. }
  208. @end
  209. @implementation RLMSyncPermission
  210. - (instancetype)initWithRealmPath:(NSString *)path
  211. identity:(NSString *)identity
  212. accessLevel:(RLMSyncAccessLevel)accessLevel {
  213. if (self = [super init]) {
  214. _accessLevel = accessLevel;
  215. _path = path;
  216. _identity = identity;
  217. if (!identity) {
  218. @throw RLMException(@"A permission value cannot be created without a valid user ID");
  219. }
  220. _updatedAt = [NSDate date];
  221. }
  222. return self;
  223. }
  224. - (instancetype)initWithRealmPath:(NSString *)path
  225. username:(NSString *)username
  226. accessLevel:(RLMSyncAccessLevel)accessLevel {
  227. if (self = [super init]) {
  228. _accessLevel = accessLevel;
  229. _path = path;
  230. _identity = nil;
  231. _key = @"email";
  232. _value = username;
  233. _updatedAt = [NSDate date];
  234. }
  235. return self;
  236. }
  237. - (instancetype)initWithPermission:(Permission)permission {
  238. if (self = [super init]) {
  239. _underlying = util::make_optional<Permission>(std::move(permission));
  240. return self;
  241. }
  242. return nil;
  243. }
  244. - (NSString *)path {
  245. if (!_underlying) {
  246. REALM_ASSERT(_path);
  247. return _path;
  248. }
  249. return @(_underlying->path.c_str());
  250. }
  251. - (RLMSyncAccessLevel)accessLevel {
  252. if (!_underlying) {
  253. return _accessLevel;
  254. }
  255. return objCAccessLevelForAccessLevel(_underlying->access);
  256. }
  257. - (BOOL)mayRead {
  258. return self.accessLevel > RLMSyncAccessLevelNone;
  259. }
  260. - (BOOL)mayWrite {
  261. return self.accessLevel > RLMSyncAccessLevelRead;
  262. }
  263. - (BOOL)mayManage {
  264. return self.accessLevel == RLMSyncAccessLevelAdmin;
  265. }
  266. - (NSString *)identity {
  267. if (!_underlying) {
  268. return _identity;
  269. }
  270. if (_underlying->condition.type == ConditionType::UserId) {
  271. return @(_underlying->condition.user_id.c_str());
  272. }
  273. return nil;
  274. }
  275. - (NSString *)key {
  276. if (!_underlying) {
  277. return _key;
  278. }
  279. if (_underlying->condition.type == ConditionType::KeyValue) {
  280. return @(_underlying->condition.key_value.first.c_str());
  281. }
  282. return nil;
  283. }
  284. - (NSString *)value {
  285. if (!_underlying) {
  286. return _value;
  287. }
  288. if (_underlying->condition.type == ConditionType::KeyValue) {
  289. return @(_underlying->condition.key_value.second.c_str());
  290. }
  291. return nil;
  292. }
  293. - (NSDate *)updatedAt {
  294. if (!_underlying) {
  295. return _updatedAt;
  296. }
  297. return RLMTimestampToNSDate(_underlying->updated_at);
  298. }
  299. - (realm::Permission)rawPermission {
  300. if (_underlying) {
  301. return *_underlying;
  302. }
  303. auto condition = (_identity
  304. ? Permission::Condition([_identity UTF8String])
  305. : Permission::Condition([_key UTF8String], [_value UTF8String]));
  306. return Permission{
  307. [_path UTF8String],
  308. accessLevelForObjCAccessLevel(_accessLevel),
  309. std::move(condition)
  310. };
  311. }
  312. - (NSUInteger)hash {
  313. return [self.identity hash] ^ self.accessLevel;
  314. }
  315. - (BOOL)isEqual:(id)object {
  316. if (self == object) {
  317. return YES;
  318. }
  319. if ([object isKindOfClass:[RLMSyncPermission class]]) {
  320. RLMSyncPermission *that = (RLMSyncPermission *)object;
  321. return (self.accessLevel == that.accessLevel
  322. && Permission::paths_are_equivalent([self.path UTF8String], [that.path UTF8String],
  323. [self.identity UTF8String], [that.identity UTF8String])
  324. && [self.identity isEqualToString:that.identity]);
  325. }
  326. return NO;
  327. }
  328. - (NSString *)description {
  329. NSString *typeDescription = nil;
  330. if (self.identity) {
  331. typeDescription = [NSString stringWithFormat:@"identity: %@", self.identity];
  332. } else {
  333. typeDescription = [NSString stringWithFormat:@"key: %@, value: %@", self.key, self.value];
  334. }
  335. return [NSString stringWithFormat:@"<RLMSyncPermission> %@, path: %@, access level: %@",
  336. typeDescription,
  337. self.path,
  338. @(Permission::description_for_access_level(accessLevelForObjCAccessLevel(self.accessLevel)).c_str())];
  339. }
  340. @end