Align permission API's with Java/Swift (#2036)

This commit is contained in:
Christian Melchior 2018-10-04 10:09:31 +02:00 committed by GitHub
parent 188101fac2
commit 434e8ca2bc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 435 additions and 131 deletions

View File

@ -1,8 +1,12 @@
x.x.x Release notes (yyyy-MM-dd)
=============================================================
## Enhancements
* None
* Added support for finding Realm-level permissions in Query-based Realms using `realm.getPermissions()` ([#2036](https://github.com/realm/realm-js/pull/2036)).
* Added support for finding Class-level permissions in Query-based Realms using `realm.getPermissions(className)`
([#2036](https://github.com/realm/realm-js/pull/2036)).
* Added `Realm.Permissions.Realm.findOrCreate(roleName)` and `Realm.Permissions.Class.findOrCreate(roleName)` which
makes it easier to find or create permissions for a given role when using Query-based Realms ([#2036](https://github.com/realm/realm-js/pull/2036)).
### Fixes
* <How to hit and notice issue? what was the impact?> ([#????](https://github.com/realm/realm-js/issues/????), since v?.?.?)
* None
@ -91,6 +95,9 @@ If you try to connect to a ROS v3.10.x or previous, you will see an error like `
* As part of including the permission schema implicitly when using query based Realm, the schema `Realm.Permissions.Realm` was missing, which may break any query including it. ([#2016](https://github.com/realm/realm-js/issues/2016), since v2.3.0)
* Fixed the type definition for `Realm.getPrivileges()`, `Realm.getPrivileges(className)` and `Realm.getPrivileges(object)`. ([#2030](https://github.com/realm/realm-js/pull/2030), since v2.2.14)
### Enhancements
* None
### Compatibility
* Realm Object Server: 3.0.0 or later
* File format: ver 7. (upgrades from previous formats automatically)

View File

@ -370,14 +370,37 @@ class Class {
/**
* The name of the class which these permissions apply to.
* @type {string}
* @deprecated Use name() instead.
*/
get class_name() {}
/**
* The name of the class which these permissions apply to.
* @type {string}
* @since 2.18.0
*/
get name() {}
/**
* The permissions for this class.
* @type {Array<Realm.Permissions.Permission>}
*/
get permissions() {}
/**
* Finds the Class-level permissions associated with the named Role. If either the role or the permission
* object doesn't exist, it will be created.
*
* If the Permission object is created because one didn't exist already, it will be
* created with all privileges disabled.
*
* If the Role object is created because one didn't exist, it will be created
* with no members.
*
* @type {Realm.Permissions.Permission}
* @since 2.18.0
*/
findOrCreate(roleName) {}
}
/**
@ -389,9 +412,25 @@ class Class {
* @memberof Realm.Permissions
*/
class Realm {
/**
* The permissions for the Realm.
* @type {Array<Realm.Permissions.Permission>}
*/
get permissions() {}
/**
* Finds the Realm-level permissions associated with the named Role. If either the role or the permission
* object doesn't exist, it will be created.
*
* If the Permission object is created because one didn't exist already, it will be
* created with all privileges disabled.
*
* If the Role object is created because one didn't exist, it will be created
* with no members.
*
* @type {Realm.Permissions.Permission}
* @since 2.17.0
*/
findOrCreate(roleName) {}
}

View File

@ -174,6 +174,17 @@ class Realm {
*/
privileges(arg) {}
/**
* Returns the fine-grained permissions object associated with either the Realm itself or a Realm model class.
*
* @param {Realm~ObjectType} [arg] - If no argument is provided, the Realm-level permissions are returned.
* Otherwise, the Class-level permissions for the provided type is returned.
* @returns {Object} The permissions object
* @since 2.18.0
* @see {Realm.Permissions} for details of priviliges and roles.
*/
permissions(arg) {}
/**
* Create a new Realm object of the given type and with the specified properties.
* @param {Realm~ObjectType} type - The type of Realm object to create.

View File

@ -77,6 +77,7 @@ function getObjectType(realm, type) {
export default class Realm {
constructor(config) {
config = this._constructor(config);
let schemas = typeof config == 'object' && config.schema;
let constructors = schemas ? {} : null;

View File

@ -52,6 +52,47 @@ function waitForDownloadConfig(config) {
return config;
}
/**
* Finds the permissions associated with a given Role or create them as needed.
*
* @param {RealmObject} Container RealmObject holding the permission list.
* @param {List<Realm.Permissions.Permission>} list of permissions.
* @param {string} name of the role to find or create permissions for.
*/
function findOrCreatePermissionForRole(realmObject, permissions, roleName) {
let realm = realmObject._realm;
if (!realm.isInTransaction) {
throw Error("'findOrCreate' can only be called inside a write transaction.");
}
let permissionsObj = permissions.filtered(`role.name = '${roleName}'`)[0];
if (permissionsObj === undefined) {
let role = realm.objects("__Role").filtered(`name = '${roleName}'`)[0];
if (role === undefined) {
role = realm.create("__Role", {'name': roleName});
}
// Create new permissions object with all privileges disabled
permissionsObj = realm.create("__Permission", { 'role': role });
permissions.push(permissionsObj);
}
return permissionsObj;
}
/**
* Adds the schema object if one isn't already defined
*/
function addSchemaIfNeeded(schemaList, schemaObj) {
for (var i = 0; i < schemaList.length; i++) {
const obj = schemaList[i];
if (obj === undefined) {
continue;
}
if (schemaObj.name === obj.name || (obj.schema !== undefined && (schemaObj.name === obj.schema.name))) {
return;
}
}
schemaList.push(schemaObj);
}
module.exports = function(realmConstructor) {
// Add the specified Array methods to the Collection prototype.
Object.defineProperties(realmConstructor.Collection.prototype, require('./collection-methods'));
@ -61,8 +102,9 @@ module.exports = function(realmConstructor) {
setConstructorOnPrototype(realmConstructor.Results);
setConstructorOnPrototype(realmConstructor.Object);
//Add async open API
//Add static methods to the Realm object
Object.defineProperties(realmConstructor, getOwnPropertyDescriptors({
open(config) {
// If no config is defined, we should just open the default realm
if (config === undefined) { config = {}; }
@ -171,7 +213,6 @@ module.exports = function(realmConstructor) {
Object.defineProperty(realmConstructor.Sync.User, '_realmConstructor', { value: realmConstructor });
realmConstructor.Sync.Credentials = {};
Object.defineProperties(realmConstructor.Sync.Credentials, getOwnPropertyDescriptors(userMethods.credentials));
realmConstructor.Sync.AuthError = require('./errors').AuthError;
if (realmConstructor.Sync.removeAllListeners) {
@ -221,7 +262,7 @@ module.exports = function(realmConstructor) {
}
};
return config;
}
};
if (realmConstructor.Sync._setFeatureToken) {
realmConstructor.Sync.setFeatureToken = function(featureToken) {
@ -242,18 +283,12 @@ module.exports = function(realmConstructor) {
Disconnected: "disconnected",
Connecting: "connecting",
Connected: "connected",
}
};
// Define the permission schemas as constructors so that they can be
// passed into directly to functions which want object type names
const permissionsSchema = Object.freeze({
Class: function() {},
Permission: function() {},
Realm: function() {},
Role: function() {},
User: function() {},
});
permissionsSchema.Permission.schema = Object.freeze({
const Permission = function() {};
Permission.schema = Object.freeze({
name: '__Permission',
properties: {
role: '__Role',
@ -267,7 +302,8 @@ module.exports = function(realmConstructor) {
}
});
permissionsSchema.User.schema = Object.freeze({
const User = function() {};
User.schema = Object.freeze({
name: '__User',
primaryKey: 'id',
properties: {
@ -276,7 +312,8 @@ module.exports = function(realmConstructor) {
}
});
permissionsSchema.Role.schema = Object.freeze({
const Role = function() {};
Role.schema = Object.freeze({
name: '__Role',
primaryKey: 'name',
properties: {
@ -285,7 +322,8 @@ module.exports = function(realmConstructor) {
}
});
permissionsSchema.Class.schema = Object.freeze({
const Class = function() {};
Class.schema = Object.freeze({
name: '__Class',
primaryKey: 'name',
properties: {
@ -293,8 +331,12 @@ module.exports = function(realmConstructor) {
permissions: '__Permission[]'
}
});
Class.prototype.findOrCreate = function(roleName) {
return findOrCreatePermissionForRole(this, this.permissions, roleName);
};
permissionsSchema.Realm.schema = Object.freeze({
const Realm = function() {};
Realm.schema = Object.freeze({
name: '__Realm',
primaryKey: 'id',
properties: {
@ -302,14 +344,77 @@ module.exports = function(realmConstructor) {
permissions: '__Permission[]'
}
});
Realm.prototype.findOrCreate = function(roleName) {
return findOrCreatePermissionForRole(this, this.permissions, roleName);
};
const permissionsSchema = {
'Class': Class,
'Permission': Permission,
'Realm': Realm,
'Role': Role,
'User': User,
};
if (!realmConstructor.Permissions) {
Object.defineProperty(realmConstructor, 'Permissions', {
value: permissionsSchema,
configurable: false
});
}
// Add instance methods to the Realm object that are only applied if Sync is
Object.defineProperties(realmConstructor.prototype, getOwnPropertyDescriptors({
permissions(arg) {
// If no argument is provided, return the Realm-level permissions
if (arg === undefined) {
return this.objects('__Realm').filtered(`id = 0`)[0];
} else {
// Else try to find the corresponding Class-level permissions
let schemaName = this._schemaName(arg);
let classPermissions = this.objects('__Class').filtered(`name = '${schemaName}'`);
if (classPermissions.length === 0) {
throw Error(`Could not find Class-level permissions for '${schemaName}'`);
}
return classPermissions[0];
}
},
}));
}
// Realm instance methods that are always available
Object.defineProperties(realmConstructor.prototype, getOwnPropertyDescriptors({
/**
* Extra internal constructor callback called by the C++ side.
* Used to work around the fact that we cannot override the original constructor,
* but still need to modify any input config.
*/
_constructor(config) {
// Even though this runs code only available for Sync it requires some serious misconfiguration
// for this to happen
if (config && config.sync) {
if (!Realm.Sync) {
throw new Error("Realm is not compiled with Sync, but the configuration contains sync features.");
}
// Only inject schemas on query-based Realms
if (config.sync.partial === true || config.sync.fullSynchronization === false) {
if (!config.schema) {
config['schema'] = [];
}
addSchemaIfNeeded(config.schema, realmConstructor.Permissions.Class);
addSchemaIfNeeded(config.schema, realmConstructor.Permissions.Permission);
addSchemaIfNeeded(config.schema, realmConstructor.Permissions.Realm);
addSchemaIfNeeded(config.schema, realmConstructor.Permissions.Role);
addSchemaIfNeeded(config.schema, realmConstructor.Permissions.User);
}
}
return config;
},
}));
// TODO: Remove this now useless object.
var types = Object.freeze({
'BOOL': 'bool',

9
lib/index.d.ts vendored
View File

@ -603,12 +603,16 @@ declare namespace Realm.Permissions {
class Class {
static schema: ObjectSchema;
class_name: string;
name: string;
permissions: Permission[];
findOrCreate(roleName: string): Permission;
}
class Realm {
static schema: ObjectSchema;
id: number;
permissions: Permission[];
findOrCreate(roleName: string): Permission;
}
class RealmPrivileges {
@ -801,9 +805,14 @@ declare class Realm {
*/
writeCopyTo(path: string, encryptionKey?: ArrayBuffer | ArrayBufferView): void;
privileges() : Realm.Permissions.Realm;
privileges() : Realm.Permissions.RealmPrivileges;
privileges(objectType: string | Realm.ObjectSchema | Function) : Realm.Permissions.ClassPrivileges;
privileges(obj: Realm.Object) : Realm.Permissions.ObjectPrivileges;
permissions() : Realm.Permissions.Realm;
permissions(objectType: string | Realm.ObjectSchema | Function) : Realm.Permissions.Class;
}
declare module 'realm' {

View File

@ -237,6 +237,7 @@ public:
static void delete_model(ContextType, ObjectType, Arguments, ReturnValue &);
static void object_for_object_id(ContextType, ObjectType, Arguments, ReturnValue&);
static void privileges(ContextType, ObjectType, Arguments, ReturnValue&);
static void get_schema_name_from_object(ContextType, ObjectType, Arguments, ReturnValue&);
// properties
static void get_empty(ContextType, ObjectType, ReturnValue &);
@ -296,6 +297,7 @@ public:
{"deleteModel", wrap<delete_model>},
{"privileges", wrap<privileges>},
{"_objectForObjectId", wrap<object_for_object_id>},
{"_schemaName", wrap<get_schema_name_from_object>},
#if REALM_ENABLE_SYNC
{"_waitForDownload", wrap<wait_for_download_completion>},
#endif
@ -346,6 +348,8 @@ public:
static const ObjectSchema& validated_object_schema_for_value(ContextType ctx, const SharedRealm &realm, const ValueType &value) {
std::string object_type;
// If argument is a constructor function, expect that the same constructor was used when specifying the schema.
if (Value::is_constructor(ctx, value)) {
FunctionType constructor = Value::to_constructor(ctx, value);
@ -362,6 +366,8 @@ public:
}
}
else {
// Any other argument is expected to be able to be converted to a String containg the name of the
// internal class.
object_type = Value::validated_to_string(ctx, value, "objectType");
if (object_type.empty()) {
throw std::runtime_error("objectType cannot be empty");
@ -435,16 +441,26 @@ static inline void convert_outdated_datetime_columns(const SharedRealm &realm) {
template<typename T>
void RealmClass<T>::constructor(ContextType ctx, ObjectType this_object, size_t argc, const ValueType arguments[]) {
if (argc > 1) {
throw std::runtime_error("Invalid arguments when constructing 'Realm'");
}
// Callback to custom constructor
// This is used to work around the fact that we cannot reliably wrap the the Realm constructor
// without risking breaking existing code, so instead we make an extra roundtrip to this method.
ValueType modifiedConfig = Object::call_method(ctx, this_object, "_constructor", argc, arguments);
// Continue with C++ construction
realm::Realm::Config config;
ObjectDefaultsMap defaults;
ConstructorMap constructors;
bool schema_updated = false;
if (argc == 0) {
if (Value::is_undefined(ctx, modifiedConfig)) {
config.path = default_path();
}
else if (argc == 1) {
ValueType value = arguments[0];
ValueType value = modifiedConfig;
if (Value::is_string(ctx, value)) {
config.path = Value::validated_to_string(ctx, value, "path");
}
@ -501,89 +517,6 @@ void RealmClass<T>::constructor(ContextType ctx, ObjectType this_object, size_t
schema_updated = true;
}
#if REALM_ENABLE_SYNC
// Include permission schema for query-based sync
if (config.sync_config && config.sync_config->is_partial) {
std::vector<ObjectSchema> objectsSchema;
if (!config.schema) {
config.schema.emplace(realm::Schema(objectsSchema));
}
auto it = config.schema->find("__Class");
if (it == config.schema->end()) {
realm::ObjectSchema clazz = {"__Class", {
{"name", realm::PropertyType::String, Property::IsPrimary{true}},
{"permissions", realm::PropertyType::Object|realm::PropertyType::Array, "__Permission"}
}};
objectsSchema.emplace_back(std::move(clazz));
schema_updated = true;
}
it = config.schema->find("__Permission");
if (it == config.schema->end()) {
realm::ObjectSchema permission = {"__Permission", {
{"role", realm::PropertyType::Object|realm::PropertyType::Nullable, "__Role"},
{"canRead", realm::PropertyType::Bool},
{"canUpdate", realm::PropertyType::Bool},
{"canDelete", realm::PropertyType::Bool},
{"canSetPermissions", realm::PropertyType::Bool},
{"canQuery", realm::PropertyType::Bool},
{"canCreate", realm::PropertyType::Bool},
{"canModifySchema", realm::PropertyType::Bool}
}};
objectsSchema.emplace_back(std::move(permission));
// adding default values
std::map<std::string, Protected<ValueType>> object_defaults;
object_defaults.emplace("canRead", Protected<ValueType>(ctx, Value::from_boolean(ctx, false)));
object_defaults.emplace("canUpdate", Protected<ValueType>(ctx, Value::from_boolean(ctx, false)));
object_defaults.emplace("canDelete", Protected<ValueType>(ctx, Value::from_boolean(ctx, false)));
object_defaults.emplace("canSetPermissions", Protected<ValueType>(ctx, Value::from_boolean(ctx, false)));
object_defaults.emplace("canQuery", Protected<ValueType>(ctx, Value::from_boolean(ctx, false)));
object_defaults.emplace("canCreate", Protected<ValueType>(ctx, Value::from_boolean(ctx, false)));
object_defaults.emplace("canModifySchema", Protected<ValueType>(ctx, Value::from_boolean(ctx, false)));
defaults.emplace("__Permission", std::move(object_defaults));
schema_updated = true;
}
it = config.schema->find("__Realm");
if (it == config.schema->end()) {
realm::ObjectSchema realm = {"__Realm", {
{"id", realm::PropertyType::Int, realm::Property::IsPrimary{true}},
{"permissions", realm::PropertyType::Object|realm::PropertyType::Array, "__Permission"}
}};
objectsSchema.emplace_back(std::move(realm));
schema_updated = true;
}
it = config.schema->find("__Role");
if (it == config.schema->end()) {
realm::ObjectSchema role = {"__Role", {
{"name", realm::PropertyType::String, realm::Property::IsPrimary{true}},
{"members", realm::PropertyType::Object|realm::PropertyType::Array, "__User"}
}};
objectsSchema.emplace_back(std::move(role));
schema_updated = true;
}
it = config.schema->find("__User");
if (it == config.schema->end()) {
realm::ObjectSchema user = {"__User", {
{"id", realm::PropertyType::String, realm::Property::IsPrimary{true}},
{"role", realm::PropertyType::Object|realm::PropertyType::Nullable, "__Role"}
}};
objectsSchema.emplace_back(std::move(user));
schema_updated = true;
}
objectsSchema.insert(objectsSchema.end(), std::make_move_iterator(config.schema->begin()),
std::make_move_iterator(config.schema->end()));
config.schema.emplace(realm::Schema(objectsSchema));
}
#endif
static const String schema_version_string = "schemaVersion";
ValueType version_value = Object::get_property(ctx, object, schema_version_string);
if (!Value::is_undefined(ctx, version_value)) {
@ -664,9 +597,6 @@ void RealmClass<T>::constructor(ContextType ctx, ObjectType this_object, size_t
}
}
}
else {
throw std::runtime_error("Invalid arguments when constructing 'Realm'");
}
config.path = normalize_realm_path(config.path);
ensure_directory_exists_for_file(config.path);
@ -1224,6 +1154,17 @@ void RealmClass<T>::object_for_object_id(ContextType ctx, ObjectType this_object
#endif // REALM_ENABLE_SYNC
}
template<typename T>
void RealmClass<T>::get_schema_name_from_object(ContextType ctx, ObjectType this_object, Arguments args, ReturnValue& return_value) {
args.validate_count(1);
// Try to map the input to the internal schema name for the given input. This should work for managed objects and
// schema objects. Pure strings and functions are expected to return a correct value.
SharedRealm realm = *get_internal<T, RealmClass<T>>(this_object);
auto &object_schema = validated_object_schema_for_value(ctx, realm, args[0]);
return_value.set(object_schema.name);
}
template<typename T>
void RealmClass<T>::privileges(ContextType ctx, ObjectType this_object, Arguments args, ReturnValue &return_value) {
args.validate_maximum(1);

View File

@ -18,12 +18,19 @@
#pragma once
namespace realm {
namespace js {
template<typename> struct RealmObjectClass;
}
}
#include "object_accessor.hpp"
#include "object_store.hpp"
#include "js_class.hpp"
#include "js_types.hpp"
#include "js_util.hpp"
#include "js_realm.hpp"
#include "js_schema.hpp"
namespace realm {
@ -58,6 +65,8 @@ struct RealmObjectClass : ClassDefinition<T, realm::Object> {
static void is_same_object(ContextType, ObjectType, Arguments, ReturnValue &);
static void set_link(ContextType, ObjectType, Arguments, ReturnValue &);
static void get_realm(ContextType, ObjectType, ReturnValue &);
const std::string name = "RealmObject";
const StringPropertyType<T> string_accessor = {
@ -75,6 +84,10 @@ struct RealmObjectClass : ClassDefinition<T, realm::Object> {
{"_isSameObject", wrap<is_same_object>},
{"_setLink", wrap<set_link>},
};
PropertyMap<T> const properties = {
{"_realm", {wrap<get_realm>, nullptr}},
};
};
template<typename T>
@ -189,6 +202,16 @@ void RealmObjectClass<T>::set_link(ContextType ctx, ObjectType object, Arguments
}
}
template<typename T>
void RealmObjectClass<T>::get_realm(ContextType ctx, ObjectType object, ReturnValue& return_value) {
return_value.set_undefined();
auto realm_object = get_internal<T, RealmObjectClass<T>>(object);
if (realm_object) {
ObjectType realm_obj = create_object<T, RealmClass<T>>(ctx, new SharedRealm(realm_object->realm()));
return_value.set(realm_obj);
}
}
template<typename T>
std::vector<String<T>> RealmObjectClass<T>::get_property_names(ContextType ctx, ObjectType object) {
auto realm_object = get_internal<T, RealmObjectClass<T>>(object);

View File

@ -85,6 +85,19 @@ function waitForUpload(realm) {
});
}
function waitForDownload(realm) {
let session = realm.syncSession;
return new Promise(resolve => {
let callback = (transferred, total) => {
if (transferred === total) {
session.removeProgressNotification(callback);
resolve(realm);
}
};
session.addProgressNotification('download', 'forCurrentlyOutstandingWork', callback);
});
}
function permissionForPath(permissions, path) {
for (const permission of permissions) {
if (permission.path == path) {
@ -93,6 +106,35 @@ function permissionForPath(permissions, path) {
}
}
const getPartialRealm = () => {
const testID = uuid();
return Realm.Sync.User
.login('http://localhost:9080', Realm.Sync.Credentials.nickname("user-" + testID, true))
.then(user => {
const config = user.createConfiguration({
sync: {
url: 'realm://localhost:9080/test_' + testID,
fullSynchronization: false,
}
});
return Realm.open(config); // Creates the Realm on the server
}).then(realm => {
return waitForUpload(realm);
}).then(realm => {
return waitForDownload(realm);
});
};
const assertFullAccess= function(permission) {
TestCase.assertTrue(permission.canCreate);
TestCase.assertTrue(permission.canRead);
TestCase.assertTrue(permission.canUpdate);
TestCase.assertTrue(permission.canDelete);
TestCase.assertTrue(permission.canQuery);
TestCase.assertTrue(permission.canModifySchema);
TestCase.assertTrue(permission.canSetPermissions);
}
module.exports = {
testApplyAndGetGrantedPermissions() {
return createUsersWithTestRealms(1).then(([user]) => {
@ -243,10 +285,10 @@ module.exports = {
fullSynchronization: false,
}
};
let realm = new Realm(config);
TestCase.assertTrue(realm.empty);
TestCase.assertEqual(realm.schema.length, 5);
TestCase.assertEqual(realm.schema.filter(schema => schema.name === '__Class').length, 1);
TestCase.assertEqual(realm.schema.filter(schema => schema.name === '__Permission').length, 1);
@ -272,7 +314,7 @@ module.exports = {
realm.close();
Realm.deleteFile(config);
resolve();
resolve();
}).catch(error => reject(error));
});
},
@ -296,11 +338,11 @@ module.exports = {
let subscription = rooms.subscribe();
subscription.addListener((sub, state) => {
if (state === Realm.Sync.SubscriptionState.Complete) {
let roles = realm.objects(Realm.Permissions.Role.schema.name).filtered(`name = '__User:${user.identity}'`);
let roles = realm.objects(Realm.Permissions.Role).filtered(`name = '__User:${user.identity}'`);
TestCase.assertEqual(roles.length, 1);
realm.write(() => {
const permission = realm.create(Realm.Permissions.Permission.schema.name,
const permission = realm.create(Realm.Permissions.Permission,
{ canUpdate: true, canRead: true, canQuery: true, role: roles[0] });
let room = realm.create(PrivateChatRoomSchema.name, { name: `#sales_${uuid()}` });
@ -312,28 +354,153 @@ module.exports = {
Realm.deleteFile(config);
// connecting with an empty schema should be possible, permission is added implicitly
Realm.open(user.createConfiguration()).then((realm) => {
let permissions = realm.objects(Realm.Permissions.Permission.schema.name).filtered(`role.name = '__User:${user.identity}'`);
let subscription = permissions.subscribe();
subscription.addListener((sub, state) => {
if (state === Realm.Sync.SubscriptionState.Complete) {
TestCase.assertEqual(permissions.length, 1);
TestCase.assertTrue(permissions[0].canRead);
TestCase.assertTrue(permissions[0].canQuery);
TestCase.assertTrue(permissions[0].canUpdate);
TestCase.assertFalse(permissions[0].canDelete);
TestCase.assertFalse(permissions[0].canSetPermissions);
TestCase.assertFalse(permissions[0].canCreate);
TestCase.assertFalse(permissions[0].canModifySchema);
realm.close();
resolve();
}
});
return waitForUpload(realm);
}).then((realm) => {
return waitForDownload(realm);
}).then((realm) => {
let permissions = realm.objects(Realm.Permissions.Permission).filtered(`role.name = '__User:${user.identity}'`);
TestCase.assertEqual(permissions.length, 1);
TestCase.assertTrue(permissions[0].canRead);
TestCase.assertTrue(permissions[0].canQuery);
TestCase.assertTrue(permissions[0].canUpdate);
TestCase.assertFalse(permissions[0].canDelete);
TestCase.assertFalse(permissions[0].canSetPermissions);
TestCase.assertFalse(permissions[0].canCreate);
TestCase.assertFalse(permissions[0].canModifySchema);
realm.close();
resolve();
});
});
}
});
}).catch(error => reject(error));
});
}
}
},
testFindOrCreate_realmPermissions() {
return getPartialRealm().then(realm => {
return new Promise((resolve, reject) => {
let realmPermissions = realm.permissions();
TestCase.assertEqual(2, realm.objects('__Role').length); // [ "everyone", "__User:<xxx>" ]
realm.write(() => {
let permissions = realmPermissions.findOrCreate("foo");
TestCase.assertEqual("foo", permissions.role.name);
TestCase.assertEqual(0, permissions.role.members.length);
TestCase.assertFalse(permissions.canCreate);
TestCase.assertFalse(permissions.canRead);
TestCase.assertFalse(permissions.canUpdate);
TestCase.assertFalse(permissions.canDelete);
TestCase.assertFalse(permissions.canQuery);
TestCase.assertFalse(permissions.canModifySchema);
TestCase.assertFalse(permissions.canSetPermissions);
TestCase.assertEqual(3, realm.objects('__Role').length); // [ "everyone", "__User:<xxx>", "foo" ]
});
resolve();
});
});
},
testFindOrCreate_existingRole() {
return getPartialRealm().then(realm => {
return new Promise((resolve, reject) => {
realm.write(() => {
realm.create('__Role', {'name':'foo'});
});
TestCase.assertEqual(3, realm.objects('__Role').length); // [ "everyone", "__User:xxx", "foo" ]
let realmPermissions = realm.permissions();
realm.write(() => {
let permissions = realmPermissions.findOrCreate("foo");
TestCase.assertEqual("foo", permissions.role.name);
TestCase.assertFalse(permissions.canCreate);
TestCase.assertFalse(permissions.canRead);
TestCase.assertFalse(permissions.canUpdate);
TestCase.assertFalse(permissions.canDelete);
TestCase.assertFalse(permissions.canQuery);
TestCase.assertFalse(permissions.canModifySchema);
TestCase.assertFalse(permissions.canSetPermissions);
TestCase.assertEqual(3, realm.objects('__Role').length); // [ "everyone", "__User:xxx", "foo" ]
});
resolve();
});
});
},
testFindOrCreate_classPermissions() {
return getPartialRealm().then(realm => {
return new Promise((resolve, reject) => {
let classPermissions = realm.permissions('__Class');
TestCase.assertEqual(2, realm.objects('__Role').length); // [ "everyone", "__User:xxx" ]
realm.write(() => {
let permissions = classPermissions.findOrCreate("foo");
TestCase.assertEqual("foo", permissions.role.name);
TestCase.assertEqual(0, permissions.role.members.length);
TestCase.assertFalse(permissions.canCreate);
TestCase.assertFalse(permissions.canRead);
TestCase.assertFalse(permissions.canUpdate);
TestCase.assertFalse(permissions.canDelete);
TestCase.assertFalse(permissions.canQuery);
TestCase.assertFalse(permissions.canModifySchema);
TestCase.assertFalse(permissions.canSetPermissions);
TestCase.assertEqual(3, realm.objects('__Role').length); // [ "everyone", "__User:xxx", "foo" ]
});
resolve();
});
});
},
testFindOrCreate_throwsOutsideWrite() {
return getPartialRealm().then(realm => {
return new Promise((resolve, reject) => {
let realmPermissions = realm.permissions();
TestCase.assertThrows(() => realmPermissions.findOrCreate("foo"));
let classPermissions = realm.permissions('__Class');
TestCase.assertThrows(() => classPermissions.findOrCreate("foo"));
resolve();
});
});
},
testPermissions_Realm: function() {
return getPartialRealm().then(realm => {
return new Promise((resolve, reject) => {
let permissions = realm.permissions();
TestCase.assertEqual(1, permissions.permissions.length);
let perm = permissions.permissions[0];
TestCase.assertEqual("everyone", perm.role.name);
assertFullAccess(perm);
resolve();
});
});
},
testPermissions_Class: function() {
return getPartialRealm().then(realm => {
return new Promise((resolve, reject) => {
let permissions = realm.permissions('__Class');
TestCase.assertEqual('__Class', permissions.name)
TestCase.assertEqual(1, permissions.permissions.length);
let perm = permissions.permissions[0];
TestCase.assertEqual("everyone", perm.role.name);
TestCase.assertTrue(perm.canCreate);
TestCase.assertTrue(perm.canRead);
TestCase.assertTrue(perm.canUpdate);
TestCase.assertFalse(perm.canDelete);
TestCase.assertTrue(perm.canQuery);
TestCase.assertFalse(perm.canModifySchema);
TestCase.assertTrue(perm.canSetPermissions);
resolve();
});
});
},
testPermissions_Class_InvalidClassArgument: function() {
return getPartialRealm().then(realm => {
return new Promise((resolve, reject) => {
TestCase.assertThrows(() => realm.permissions('foo'));
resolve();
});
});
},
};

View File

@ -1335,5 +1335,6 @@ module.exports = {
encryptedRealmCopy.close();
realm.close();
}
},
};