From ce674469766f91f197c5afe4fc7b6ac1cbd4c0e4 Mon Sep 17 00:00:00 2001 From: Thomas Goyne Date: Thu, 11 Jan 2018 05:47:54 -0800 Subject: [PATCH] Perform a pseudo-refresh for admin token users (#1599) * Perform a pseudo-refresh for admin token users Although we don't need to actually refresh the token, when multiplexing is enabled we do still need to hit ROS to let it know we're accessing a Realm and get the sync label. * Adding brief JSdoc and TS definition for isAdminToken. * Perform a pseudo-refresh for admin token users Although we don't need to actually refresh the token, when multiplexing is enabled we do still need to hit ROS to let it know we're accessing a Realm and get the sync label. * Adding brief JSdoc and TS definition for isAdminToken. * Make the timeout consistent * Add basic docs for multiprocess notifications (#1594) * Add basic docs for multiprocess notifications * Adopt core's query parser and update to core v5.1.2 (#1563) * Adopt core's query parser. * Realm Core 5.1.2 * Realm Sync 2.2.9 * Remove tar.xz (#1585) * remove tar.xz decompression and lzma deps * Fixing merge * Updating object store --- docs/sync.js | 9 +++-- lib/index.d.ts | 11 +++--- lib/user-methods.js | 81 ++++++++++++++++++++++++++++++++------------- src/js_sync.hpp | 7 ++++ src/object-store | 2 +- 5 files changed, 79 insertions(+), 31 deletions(-) diff --git a/docs/sync.js b/docs/sync.js index 290b9d5b..ff4dbf26 100644 --- a/docs/sync.js +++ b/docs/sync.js @@ -242,7 +242,7 @@ class User { * @return {Promise} Returns a promise with a user */ static authenticate(server, provider, options) {} - + /** * Register/login a sync user using an external login provider. * @param {string} server - authentication server @@ -312,11 +312,16 @@ class User { get token() {} /** - * Returns true if this user is an administrator + * Returns true if this user is an administrator. * @type {bool} */ get isAdmin() {} + /** + * Returns true if the token is an administrator token. + */ + get isAdminToken() {} + /** * Logs out the user from the Realm Object Server. */ diff --git a/lib/index.d.ts b/lib/index.d.ts index 7f4ecf37..746f84ad 100644 --- a/lib/index.d.ts +++ b/lib/index.d.ts @@ -145,9 +145,9 @@ declare namespace Realm { */ isValid(): boolean; - min(property?: string): number|Date|null; - max(property?: string): number|Date|null; - sum(property?: string): number|null; + min(property?: string): number | Date | null; + max(property?: string): number | Date | null; + sum(property?: string): number | null; avg(property?: string): number; /** @@ -268,6 +268,7 @@ declare namespace Realm.Sync { static readonly current: User; readonly identity: string; readonly isAdmin: boolean; + readonly isAdminToken: boolean; readonly server: string; readonly token: string; static adminUser(adminToken: string, server?: string): User; @@ -349,9 +350,9 @@ declare namespace Realm.Sync { interface SyncError { name: string; - message: string; + message: string; isFatal: boolean; - category?: string; + category?: string; code: number; } diff --git a/lib/user-methods.js b/lib/user-methods.js index 09bc5ecf..3cc6ca61 100644 --- a/lib/user-methods.js +++ b/lib/user-methods.js @@ -46,11 +46,8 @@ const postHeaders = { 'accept': 'application/json' }; -function auth_url(server) { - if (server.charAt(server.length - 1) != '/') { - return server + '/auth'; - } - return server + 'auth'; +function append_url(server, path) { + return server + (server.charAt(server.length - 1) != '/' ? '/' : '') + path; } function scheduleAccessTokenRefresh(user, localRealmPath, realmUrl, expirationDate) { @@ -63,13 +60,63 @@ function print_error() { (console.error || console.log).apply(console, arguments); } +function validateRefresh(user, localRealmPath, response, json) { + let session = user._sessionForOnDiskPath(localRealmPath); + if (!session) { + print_error(`Unhandled session token refresh error: could not look up session at path ${localRealmPath}`); + return; + } + + const errorHandler = session.config.error; + if (response.status != 200) { + let error = new AuthError(json); + if (errorHandler) { + errorHandler(session, error); + } else { + print_error('Unhandled session token refresh error', error); + } + return; + } + if (session.state === 'invalid') { + return; + } + return session; +} + +function refreshAdminToken(user, localRealmPath, realmUrl) { + const token = user.token; + const server = user.server; + + // We don't need to actually refresh the token, but we need to let ROS know + // we're accessing the file and get the sync label for multiplexing + let parsedRealmUrl = url_parse(realmUrl); + const url = append_url(user.server, 'realms/files/' + encodeURIComponent(parsedRealmUrl.pathname)); + performFetch(url, {method: 'GET', timeout: 10000.0, headers: {Authorization: user.token}}) + .then((response) => response.json().then((json) => { return { response, json }; })) + .then((responseAndJson) => { + const response = responseAndJson.response; + const json = responseAndJson.json; + + const newUser = user.constructor.adminUser(token, server); + const session = validateRefresh(newUser, localRealmPath, response, json); + if (session) { + parsedRealmUrl.set('pathname', json.path); + session._refreshAccessToken(user.token, parsedRealmUrl.href, json.syncLabel); + } + }); +} + function refreshAccessToken(user, localRealmPath, realmUrl) { if (!user.server) { throw new Error("Server for user must be specified"); } let parsedRealmUrl = url_parse(realmUrl); - const url = auth_url(user.server); + if (user.isAdminToken) { + return refreshAdminToken(user, localRealmPath, realmUrl); + } + + const url = append_url(user.server, 'auth'); const options = { method: 'POST', body: JSON.stringify({ @@ -91,26 +138,13 @@ function refreshAccessToken(user, localRealmPath, realmUrl) { // Look up a fresh instance of the user. // We do this because in React Native Remote Debugging // `Realm.clearTestState()` will have invalidated the user object - let newUser = user.constructor.all[user.identity]; + const newUser = user.constructor.all[user.identity]; if (!newUser) { return; } - let session = newUser._sessionForOnDiskPath(localRealmPath); - if (!session) { - print_error(`Unhandled session token refresh error: could not look up session at path ${localRealmPath}`); - } - const errorHandler = session.config.error; - if (response.status != 200) { - let error = new AuthError(json); - if (errorHandler) { - errorHandler(session, error); - } else { - print_error('Unhandled session token refresh error', error); - } - return; - } - if (session.state === 'invalid') { + const session = validateRefresh(newUser, localRealmPath, response, json); + if (!session) { return; } @@ -119,6 +153,7 @@ function refreshAccessToken(user, localRealmPath, realmUrl) { parsedRealmUrl.set('pathname', tokenData.path); session._refreshAccessToken(json.access_token.token, parsedRealmUrl.href, tokenData.sync_label); + const errorHandler = session.config.error; if (errorHandler && errorHandler._notifyOnAccessTokenRefreshed) { errorHandler(session, errorHandler._notifyOnAccessTokenRefreshed) } @@ -144,7 +179,7 @@ function refreshAccessToken(user, localRealmPath, realmUrl) { */ function _authenticate(userConstructor, server, json, callback) { json.app_id = ''; - const url = auth_url(server); + const url = append_url(server, 'auth'); const options = { method: 'POST', body: JSON.stringify(json), diff --git a/src/js_sync.hpp b/src/js_sync.hpp index d373d863..13951839 100644 --- a/src/js_sync.hpp +++ b/src/js_sync.hpp @@ -69,12 +69,14 @@ public: static void get_identity(ContextType, ObjectType, ReturnValue &); static void get_token(ContextType, ObjectType, ReturnValue &); static void is_admin(ContextType, ObjectType, ReturnValue &); + static void is_admin_token(ContextType, ObjectType, ReturnValue &); PropertyMap const properties = { {"server", {wrap, nullptr}}, {"identity", {wrap, nullptr}}, {"token", {wrap, nullptr}}, {"isAdmin", {wrap, nullptr}}, + {"isAdminToken", {wrap, nullptr}}, }; static void create_user(ContextType, FunctionType, ObjectType, size_t, const ValueType[], ReturnValue &); @@ -125,6 +127,11 @@ void UserClass::is_admin(ContextType ctx, ObjectType object, ReturnValue &ret return_value.set(get_internal>(object)->get()->is_admin()); } +template +void UserClass::is_admin_token(ContextType ctx, ObjectType object, ReturnValue &return_value) { + return_value.set(get_internal>(object)->get()->token_type() == SyncUser::TokenType::Admin); +} + template void UserClass::create_user(ContextType ctx, FunctionType, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) { validate_argument_count(argc, 3, 5); diff --git a/src/object-store b/src/object-store index 21e671a1..8517ee7f 160000 --- a/src/object-store +++ b/src/object-store @@ -1 +1 @@ -Subproject commit 21e671a155989f4e34e279fc8a7ace5817495de0 +Subproject commit 8517ee7f4378fe0f54945b3e4973766ff65e455d