Align permission API's with Java/Swift (#2036)
This commit is contained in:
parent
188101fac2
commit
434e8ca2bc
11
CHANGELOG.md
11
CHANGELOG.md
|
@ -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)
|
||||
|
|
|
@ -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) {}
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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' {
|
||||
|
|
117
src/js_realm.hpp
117
src/js_realm.hpp
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
};
|
||||
|
|
|
@ -1335,5 +1335,6 @@ module.exports = {
|
|||
encryptedRealmCopy.close();
|
||||
|
||||
realm.close();
|
||||
}
|
||||
},
|
||||
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue