Skip to content

Commit

Permalink
Align permission API's with Java/Swift (#2036)
Browse files Browse the repository at this point in the history
  • Loading branch information
cmelchior authored Oct 4, 2018
1 parent 188101f commit 434e8ca
Show file tree
Hide file tree
Showing 10 changed files with 435 additions and 131 deletions.
11 changes: 9 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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)
Expand Down
39 changes: 39 additions & 0 deletions docs/permission.js
Original file line number Diff line number Diff line change
Expand Up @@ -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) {}
}

/**
Expand All @@ -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) {}
}
11 changes: 11 additions & 0 deletions docs/realm.js
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
1 change: 1 addition & 0 deletions lib/browser/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
137 changes: 121 additions & 16 deletions lib/extensions.js
Original file line number Diff line number Diff line change
Expand Up @@ -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'));
Expand All @@ -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 = {}; }
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -221,7 +262,7 @@ module.exports = function(realmConstructor) {
}
};
return config;
}
};

if (realmConstructor.Sync._setFeatureToken) {
realmConstructor.Sync.setFeatureToken = function(featureToken) {
Expand All @@ -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',
Expand All @@ -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: {
Expand All @@ -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: {
Expand All @@ -285,31 +322,99 @@ module.exports = function(realmConstructor) {
}
});

permissionsSchema.Class.schema = Object.freeze({
const Class = function() {};
Class.schema = Object.freeze({
name: '__Class',
primaryKey: 'name',
properties: {
name: 'string',
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: {
id: 'int',
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',
Expand Down
9 changes: 9 additions & 0 deletions lib/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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' {
Expand Down
Loading

0 comments on commit 434e8ca

Please sign in to comment.