Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Align permission API's with Java/Swift #2036

Merged
merged 14 commits into from
Oct 4, 2018
Merged
12 changes: 10 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
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]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please move . to end of sentence: "Added ... using realm.getPermissions(). ([..."

(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 All @@ -26,6 +31,9 @@ x.x.x Release notes (yyyy-MM-dd)
* 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.17.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 exists, it will be created.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"exists" -> "exist"

*
* 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 exists, it will be created
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"exists" -> "exist"

* with no members.
*
* @type {Realm.Permissions.Permission}
* @since 2.17.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 exists, it will be created.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"exists" -> "exist"

*
* 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 exists, 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 are returned.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"are are" -> "are"

* Otherwise, the Class-level permissions for the provided type is returned.
* @returns {Object} The permissions object
* @since 2.17.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 available always available
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove one "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