diff --git a/dependencies.list b/dependencies.list index 9bab0550..d7c40809 100644 --- a/dependencies.list +++ b/dependencies.list @@ -2,4 +2,4 @@ PACKAGE_NAME=realm-js VERSION=1.8.3 REALM_CORE_VERSION=2.8.4 REALM_SYNC_VERSION=1.10.1 -REALM_OBJECT_SERVER_VERSION=1.7.6 +REALM_OBJECT_SERVER_VERSION=1.8.1 diff --git a/lib/index.d.ts b/lib/index.d.ts index 382db510..099c27bb 100644 --- a/lib/index.d.ts +++ b/lib/index.d.ts @@ -240,6 +240,17 @@ declare namespace Realm { */ declare namespace Realm.Sync { + interface UserInfo { + id: string; + isAdmin: boolean; + } + + interface Account { + provider_id: string; + provider: string; + user: UserInfo + } + /** * User * @see { @link https://realm.io/docs/javascript/latest/api/Realm.Sync.User.html } @@ -258,6 +269,7 @@ declare namespace Realm.Sync { static registerWithProvider(server: string, options: { provider: string, providerToken: string, userInfo: any }, callback: (error: Error | null, user: User | null) => void): void; logout(): void; openManagementRealm(): Realm; + retrieveAccount(provider: string, username: string): Promise; } interface SyncConfiguration { diff --git a/lib/user-methods.js b/lib/user-methods.js index b2fd221c..4ac77723 100644 --- a/lib/user-methods.js +++ b/lib/user-methods.js @@ -27,7 +27,7 @@ function node_require(module) { function checkTypes(args, types) { args = Array.prototype.slice.call(args); for (var i = 0; i < types.length; ++i) { - if (typeof args[i] !== types[i]) { + if (typeof args[i] !== types[i]) { throw new TypeError('param ' + i + ' must be of type ' + types[i]); } } @@ -37,13 +37,13 @@ const performFetch = typeof fetch === 'undefined' ? node_require('node-fetch') : const url_parse = require('url-parse'); -const postHeaders = { +const postHeaders = { 'content-type': 'application/json;charset=utf-8', 'accept': 'application/json' }; function auth_url(server) { - if (server.charAt(server.length-1) != '/') { + if (server.charAt(server.length - 1) != '/') { return server + '/auth'; } return server + 'auth'; @@ -122,7 +122,7 @@ function _authenticate(userConstructor, server, json, callback) { open_timeout: 5000 }; performFetch(url, options) - .then((response) => { + .then((response) => { if (response.status !== 200) { return response.json().then((body) => callback(new AuthError(body))); } else { @@ -132,7 +132,7 @@ function _authenticate(userConstructor, server, json, callback) { const identity = body.refresh_token.token_data.identity; const isAdmin = body.refresh_token.token_data.is_admin; callback(undefined, userConstructor.createUser(server, identity, token, false, isAdmin)); - }) + }) } }) .catch(callback); @@ -154,8 +154,8 @@ module.exports = { adminUser(token) { checkTypes(arguments, ['string']); - var uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) { - var r = Math.random()*16|0, v = c == 'x' ? r : (r&0x3|0x8); + var uuid = '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); }); var user = this.createUser('', uuid, token, true); @@ -164,18 +164,18 @@ module.exports = { register(server, username, password, callback) { checkTypes(arguments, ['string', 'string', 'string', 'function']); - _authenticate(this, server, { - provider: 'password', - user_info: { password: password, register: true }, + _authenticate(this, server, { + provider: 'password', + user_info: { password: password, register: true }, data: username }, callback); }, login(server, username, password, callback) { checkTypes(arguments, ['string', 'string', 'string', 'function']); - _authenticate(this, server, { - provider: 'password', - user_info: { password: password }, + _authenticate(this, server, { + provider: 'password', + user_info: { password: password }, data: username }, callback); }, @@ -190,7 +190,7 @@ module.exports = { provider: arguments[1], providerToken: arguments[2] }; - callback = arguments[3]; + callback = arguments[3]; } else { checkTypes(arguments, ['string', 'object', 'function']); } @@ -229,6 +229,33 @@ module.exports = { url: url.href } }); - } - } + }, + retrieveAccount(provider, provider_id) { + checkTypes(arguments, ['string', 'string']); + + const url = url_parse(this.server); + url.set('pathname', `/api/providers/${provider}/accounts/${provider_id}`); + const headers = { + Authorization: this.token + }; + + const options = { + method: 'GET', + headers, + open_timeout: 5000 + }; + return performFetch(url.href, options) + .then((response) => { + if (response.status !== 200) { + return response.json() + .then(body => { + throw new AuthError(body); + }); + } else { + + return response.json(); + } + }); + }, + }, }; \ No newline at end of file diff --git a/tests/js/admin-user-helper.js b/tests/js/admin-user-helper.js new file mode 100644 index 00000000..99bd4d4e --- /dev/null +++ b/tests/js/admin-user-helper.js @@ -0,0 +1,81 @@ +'use strict'; +function node_require(module) { + return require(module); + +} +let fs = node_require("fs"); +let path = node_require("path"); +var Realm = node_require('realm'); + +function random(min, max) { + min = Math.ceil(min); + max = Math.floor(max); + return Math.floor(Math.random() * (max - min + 1)) + min; +} + +exports.createAdminUser = function () { + return new Promise((resolve, reject) => { + let isAdminRetryCounter = 0; + let newAdminName = 'admin' + random(1, 100000); + let password = '123'; + Realm.Sync.User.register('http://localhost:9080', newAdminName, password, (error, user) => { + if (error) { + reject(error); + } else { + let userIdentity = user.identity; + user.logout(); + + let admin_token_user = Realm.Sync.User.adminUser(fs.readFileSync(path.join(__dirname, '/../../object-server-for-testing/admin_token.base64'), 'utf-8')); + + const config = { + sync: { + user: admin_token_user, + url: `realm://localhost:9080/__admin`, + error: err => + console.log('Error opening __admin realm ' + err.user + ' ' + err.url + ' ' + err.state), + } + }; + + Realm.open(config).then(realm => { + let pendingAdminUser = realm.objectForPrimaryKey('User', userIdentity); + realm.write(() => { + pendingAdminUser.isAdmin = true; + }); + + admin_token_user.logout(); + }).then(() => { + let waitForServerToUpdateAdminUser = function () { + isAdminRetryCounter++; + if (isAdminRetryCounter > 10) { + reject("admin-user-helper: Create admin user timeout"); + return; + } + + Realm.Sync.User.login('http://localhost:9080', newAdminName, password, (error, newAdminUser) => { + if (error) { + reject(error); + } else { + let isAdmin = newAdminUser.isAdmin; + user.logout(); + if (!isAdmin) { + setTimeout(() => { + waitForServerToUpdateAdminUser(); + }, 500); + return; + } + + resolve({ + username: newAdminName, + password + }); + } + }); + } + + waitForServerToUpdateAdminUser(); + }); + } + }); + }); +} + diff --git a/tests/js/download-api-helper.js b/tests/js/download-api-helper.js index 674e468b..67a697d6 100644 --- a/tests/js/download-api-helper.js +++ b/tests/js/download-api-helper.js @@ -27,7 +27,7 @@ Realm.Sync.User.register('http://localhost:9080', username, 'password', (error, }); console.log("Dogs count " + realm.objects('Dog').length); - setTimeout(_ => process.exit(0), 3000); + setTimeout(() => process.exit(0), 3000); } }); diff --git a/tests/js/index.js b/tests/js/index.js index 7de4bfe1..61f41aa1 100644 --- a/tests/js/index.js +++ b/tests/js/index.js @@ -41,10 +41,11 @@ if (Realm.Sync) { TESTS.SessionTests = require('./session-tests'); } -function node_require(module) { return require(module); } +function node_require(module) { return require(module); } // If on node, run the async tests -if (typeof process === 'object' && process + '' === '[object process]') { +const isNodeProcess = typeof process === 'object' && process + '' === '[object process]'; +if (isNodeProcess) { TESTS.AsyncTests = node_require('./async-tests'); } @@ -73,6 +74,22 @@ exports.registerTests = function(tests) { } }; +exports.prepare = function(done) { + if (!isNodeProcess || global.testAdminUserInfo) { + done(); + } + + let helper = require('./admin-user-helper'); + helper.createAdminUser().then(userInfo => { + global.testAdminUserInfo = userInfo; + done(); + }) + .catch(error => { + console.error("Error running admin-user-helper: " + error); + done(); + }); +}; + exports.runTest = function(suiteName, testName) { var testSuite = TESTS[suiteName]; var testMethod = testSuite && testSuite[testName]; @@ -103,4 +120,4 @@ exports.runTest = function(suiteName, testName) { } else if (!testSuite || !(testName in SPECIAL_METHODS)) { throw new Error('Missing test: ' + suiteName + '.' + testName); } -}; +} diff --git a/tests/js/user-tests.js b/tests/js/user-tests.js index a0b08cc7..4d2a24ab 100644 --- a/tests/js/user-tests.js +++ b/tests/js/user-tests.js @@ -22,6 +22,7 @@ const Realm = require('realm'); const TestCase = require('./asserts'); +const isNodeProcess = typeof process === 'object' && process + '' === '[object process]'; function uuid() { return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) { @@ -295,6 +296,76 @@ module.exports = { }); }, + testRetrieveAccount() { + return new Promise((resolve, reject) => { + if (!isNodeProcess) { + resolve(); + } + + if (!global.testAdminUserInfo) { + reject("Test requires an admin user"); + } + + Realm.Sync.User.login('http://localhost:9080', global.testAdminUserInfo.username, global.testAdminUserInfo.password, (error, user) => { + if (error) { + reject(error); + } + + TestCase.assertTrue(user.isAdmin, "Test requires an admin user"); + + user.retrieveAccount('password', global.testAdminUserInfo.username) + .then(account => { + // { + // "provider_id": "admin", + // "provider": "password", + // "user": { + // "id": "07ac9a0a-a97a-4ee1-b53c-b05a6542035a", + // "isAdmin": true, + // } + // } + + TestCase.assertEqual(account.provider_id, global.testAdminUserInfo.username); + TestCase.assertEqual(account.provider, 'password'); + TestCase.assertTrue(account.user); + TestCase.assertTrue(account.user.isAdmin !== undefined); + TestCase.assertTrue(account.user.id); + resolve(); + }) + .catch(e => reject(e)); + }) + }); + }, + + testRetrieveNotExistingAccount() { + return new Promise((resolve, reject) => { + if (!isNodeProcess) { + resolve(); + } + + if (!global.testAdminUserInfo) { + reject("Test requires an admin user"); + } + + Realm.Sync.User.login('http://localhost:9080', global.testAdminUserInfo.username, global.testAdminUserInfo.password, (error, user) => { + if (error) { + reject(error); + } + + TestCase.assertTrue(user.isAdmin, "Test requires an admin user"); + + let notExistingUsername = uuid(); + user.retrieveAccount('password', notExistingUsername) + .then(account => { + reject("Retrieving not existing account should fail"); + }) + .catch(e => { + TestCase.assertEqual(e.code, 404); + resolve() + }); + }) + }); + }, + /* This test fails because of realm-object-store #243 . We should use 2 users. testSynchronizeChangesWithTwoClientsAndOneUser() { // Test Schema diff --git a/tests/spec/unit_tests.js b/tests/spec/unit_tests.js index 2826381b..00a37752 100644 --- a/tests/spec/unit_tests.js +++ b/tests/spec/unit_tests.js @@ -55,6 +55,9 @@ Realm.copyBundledRealmFiles = function() { const tests = RealmTests.getTestNames(); for (const suiteName in tests) { describe(suiteName, () => { + + beforeAll(done => RealmTests.prepare(done)); + beforeEach(() => RealmTests.runTest(suiteName, 'beforeEach')); for (const testName of tests[suiteName]) {