//////////////////////////////////////////////////////////////////////////// // // Copyright 2017 Realm Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //////////////////////////////////////////////////////////////////////////// 'use strict'; var Realm = require('realm'); var TestCase = require('./asserts'); function uuid() { return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) { var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8); return v.toString(16); }); } function createUsersWithTestRealms(count) { const createUserWithTestRealm = () => { return Realm.Sync.User.register('http://localhost:9080', uuid(), 'password') .then(user => { new Realm({sync: {user, url: 'realm://localhost:9080/~/test'}}).close(); return user; }); }; return Promise.all(Array.from({length: count}, createUserWithTestRealm)); } function wait(t) { return new Promise(resolve => setTimeout(resolve, t)); } function repeatUntil(fn, predicate) { let retries = 0 function check() { if (retries > 3) { return Promise.reject(new Error("operation timed out")); } ++retries; return fn().then(x => predicate(x) ? x : wait(100).then(check)); } return check; } function subscribe(results) { const subscription = results.subscribe() return new Promise((resolve, reject) => { subscription.addListener((subscription, state) => { if (state == Realm.Sync.SubscriptionState.Complete) { resolve(); } else if (state == Realm.Sync.SubscriptionState.Error) { reject(); } }); setTimeout(() => reject("listener never called"), 5000); }); } function waitForUpload(realm) { let session = realm.syncSession; return new Promise(resolve => { let callback = (transferred, total) => { if (transferred === total) { session.removeProgressNotification(callback); resolve(realm); } } session.addProgressNotification('upload', 'forCurrentlyOutstandingWork', callback); }); } module.exports = { testApplyAndGetGrantedPermissions() { return createUsersWithTestRealms(1) .then(([user]) => { return user.applyPermissions({ userId: `${user.identity}` }, `/${user.identity}/test`, 'read') .then(repeatUntil(() => user.getGrantedPermissions('any'), permissions => permissions.length > 1)) .then(permissions => { TestCase.assertEqual(permissions[0].path, `/${user.identity}/test`); TestCase.assertEqual(permissions[0].mayRead, true); TestCase.assertEqual(permissions[0].mayWrite, false); TestCase.assertEqual(permissions[0].mayManage, false); }); }); }, testOfferPermissions() { return createUsersWithTestRealms(2) .then(([user1, user2]) => { return user1.offerPermissions(`/${user1.identity}/test`, 'read') .then(token => user2.acceptPermissionOffer(token)) .then(realmUrl => { TestCase.assertEqual(realmUrl, `/${user1.identity}/test`); return realmUrl; }) .then(repeatUntil(() => user2.getGrantedPermissions('any'), permissions => permissions.length > 1)) .then(permissions => { TestCase.assertEqual(permissions[2].path, `/${user1.identity}/test`); TestCase.assertEqual(permissions[2].mayRead, true); TestCase.assertEqual(permissions[2].mayWrite, false); TestCase.assertEqual(permissions[2].mayManage, false); }); }); }, testInvalidatePermissionOffer() { let user1, user2, token; return createUsersWithTestRealms(2) .then(users => { user1 = users[0]; user2 = users[1]; return user1.offerPermissions(`/${user1.identity}/test`, 'read'); }) .then(t => { token = t; return user1.invalidatePermissionOffer(token); }) // Since we don't yet support notification when the invalidation has gone through, // wait for a bit and hope the server is done processing. .then(wait(100)) .then(() => user2.acceptPermissionOffer(token)) // We want the call to fail, i.e. the catch() below should be called. .then(() => { throw new Error("User was able to accept an invalid permission offer token"); }) .catch(error => { try { TestCase.assertEqual(error.message, 'The permission offer is expired.'); TestCase.assertEqual(error.statusCode, 701); } catch (e) { throw new Error(e); } }); }, testObjectPermissions() { let config = (user, url) => { return { schema: [ Realm.Permissions.Permission, Realm.Permissions.User, Realm.Permissions.Role, { name: 'Object', properties: { value: 'int', permissions: '__Permission[]' } } ], sync: {user, url, partial: true} }; }; let owner, otherUser return Realm.Sync.User.register('http://localhost:9080', uuid(), 'password') .then(user => { owner = user; new Realm({sync: {user, url: 'realm://localhost:9080/~/test'}}).close(); return Realm.Sync.User.register('http://localhost:9080', uuid(), 'password') }) .then(user => { otherUser = user; return owner.applyPermissions({userId: otherUser.identity}, `/${owner.identity}/test`, 'read') }) .then(() => { let realm = new Realm(config(owner, 'realm://localhost:9080/~/test')); realm.write(() => { let user = realm.create(Realm.Permissions.User, {id: otherUser.identity}) let role = realm.create(Realm.Permissions.Role, {name: 'reader'}) role.members.push(user) let obj1 = realm.create('Object', {value: 1}) let obj2 = realm.create('Object', {value: 2}) obj2.permissions.push(realm.create(Realm.Permissions.Permission, {role: role, canRead: true, canUpdate: false})) }); return waitForUpload(realm).then(() => realm.close()); }) .then(() => Realm.open(config(otherUser, `realm://localhost:9080/${owner.identity}/test`))) .then((realm) => subscribe(realm.objects('Object')).then(() => realm)) .then((realm) => { // Should have full access to the Realm as a whole TestCase.assertSimilar('object', realm.privileges(), {read: true, update: true, modifySchema: true, setPermissions: true}); TestCase.assertSimilar('object', realm.privileges('Object'), {read: true, update: true, create: true, subscribe: true, setPermissions: true}); // Verify that checking via constructor works too TestCase.assertSimilar('object', realm.privileges(Realm.Permissions.User), {read: true, update: true, create: true, subscribe: true, setPermissions: true}); // Should only be able to see the second object let results = realm.objects('Object') TestCase.assertEqual(results.length, 1); TestCase.assertEqual(results[0].value, 2); TestCase.assertSimilar('object', realm.privileges(results[0]), {read: true, update: false, delete: false, setPermissions: false}); realm.close(); }); } }