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
This commit is contained in:
Thomas Goyne 2018-01-11 05:47:54 -08:00 committed by Kenneth Geisshirt
parent 795be904a1
commit ce67446976
5 changed files with 79 additions and 31 deletions

View File

@ -242,7 +242,7 @@ class User {
* @return {Promise<User>} Returns a promise with a user * @return {Promise<User>} Returns a promise with a user
*/ */
static authenticate(server, provider, options) {} static authenticate(server, provider, options) {}
/** /**
* Register/login a sync user using an external login provider. * Register/login a sync user using an external login provider.
* @param {string} server - authentication server * @param {string} server - authentication server
@ -312,11 +312,16 @@ class User {
get token() {} get token() {}
/** /**
* Returns true if this user is an administrator * Returns true if this user is an administrator.
* @type {bool} * @type {bool}
*/ */
get isAdmin() {} get isAdmin() {}
/**
* Returns true if the token is an administrator token.
*/
get isAdminToken() {}
/** /**
* Logs out the user from the Realm Object Server. * Logs out the user from the Realm Object Server.
*/ */

11
lib/index.d.ts vendored
View File

@ -145,9 +145,9 @@ declare namespace Realm {
*/ */
isValid(): boolean; isValid(): boolean;
min(property?: string): number|Date|null; min(property?: string): number | Date | null;
max(property?: string): number|Date|null; max(property?: string): number | Date | null;
sum(property?: string): number|null; sum(property?: string): number | null;
avg(property?: string): number; avg(property?: string): number;
/** /**
@ -268,6 +268,7 @@ declare namespace Realm.Sync {
static readonly current: User; static readonly current: User;
readonly identity: string; readonly identity: string;
readonly isAdmin: boolean; readonly isAdmin: boolean;
readonly isAdminToken: boolean;
readonly server: string; readonly server: string;
readonly token: string; readonly token: string;
static adminUser(adminToken: string, server?: string): User; static adminUser(adminToken: string, server?: string): User;
@ -349,9 +350,9 @@ declare namespace Realm.Sync {
interface SyncError { interface SyncError {
name: string; name: string;
message: string; message: string;
isFatal: boolean; isFatal: boolean;
category?: string; category?: string;
code: number; code: number;
} }

View File

@ -46,11 +46,8 @@ const postHeaders = {
'accept': 'application/json' 'accept': 'application/json'
}; };
function auth_url(server) { function append_url(server, path) {
if (server.charAt(server.length - 1) != '/') { return server + (server.charAt(server.length - 1) != '/' ? '/' : '') + path;
return server + '/auth';
}
return server + 'auth';
} }
function scheduleAccessTokenRefresh(user, localRealmPath, realmUrl, expirationDate) { function scheduleAccessTokenRefresh(user, localRealmPath, realmUrl, expirationDate) {
@ -63,13 +60,63 @@ function print_error() {
(console.error || console.log).apply(console, arguments); (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) { function refreshAccessToken(user, localRealmPath, realmUrl) {
if (!user.server) { if (!user.server) {
throw new Error("Server for user must be specified"); throw new Error("Server for user must be specified");
} }
let parsedRealmUrl = url_parse(realmUrl); 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 = { const options = {
method: 'POST', method: 'POST',
body: JSON.stringify({ body: JSON.stringify({
@ -91,26 +138,13 @@ function refreshAccessToken(user, localRealmPath, realmUrl) {
// Look up a fresh instance of the user. // Look up a fresh instance of the user.
// We do this because in React Native Remote Debugging // We do this because in React Native Remote Debugging
// `Realm.clearTestState()` will have invalidated the user object // `Realm.clearTestState()` will have invalidated the user object
let newUser = user.constructor.all[user.identity]; const newUser = user.constructor.all[user.identity];
if (!newUser) { if (!newUser) {
return; 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; const session = validateRefresh(newUser, localRealmPath, response, json);
if (response.status != 200) { if (!session) {
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;
} }
@ -119,6 +153,7 @@ function refreshAccessToken(user, localRealmPath, realmUrl) {
parsedRealmUrl.set('pathname', tokenData.path); parsedRealmUrl.set('pathname', tokenData.path);
session._refreshAccessToken(json.access_token.token, parsedRealmUrl.href, tokenData.sync_label); session._refreshAccessToken(json.access_token.token, parsedRealmUrl.href, tokenData.sync_label);
const errorHandler = session.config.error;
if (errorHandler && errorHandler._notifyOnAccessTokenRefreshed) { if (errorHandler && errorHandler._notifyOnAccessTokenRefreshed) {
errorHandler(session, errorHandler._notifyOnAccessTokenRefreshed) errorHandler(session, errorHandler._notifyOnAccessTokenRefreshed)
} }
@ -144,7 +179,7 @@ function refreshAccessToken(user, localRealmPath, realmUrl) {
*/ */
function _authenticate(userConstructor, server, json, callback) { function _authenticate(userConstructor, server, json, callback) {
json.app_id = ''; json.app_id = '';
const url = auth_url(server); const url = append_url(server, 'auth');
const options = { const options = {
method: 'POST', method: 'POST',
body: JSON.stringify(json), body: JSON.stringify(json),

View File

@ -69,12 +69,14 @@ public:
static void get_identity(ContextType, ObjectType, ReturnValue &); static void get_identity(ContextType, ObjectType, ReturnValue &);
static void get_token(ContextType, ObjectType, ReturnValue &); static void get_token(ContextType, ObjectType, ReturnValue &);
static void is_admin(ContextType, ObjectType, ReturnValue &); static void is_admin(ContextType, ObjectType, ReturnValue &);
static void is_admin_token(ContextType, ObjectType, ReturnValue &);
PropertyMap<T> const properties = { PropertyMap<T> const properties = {
{"server", {wrap<get_server>, nullptr}}, {"server", {wrap<get_server>, nullptr}},
{"identity", {wrap<get_identity>, nullptr}}, {"identity", {wrap<get_identity>, nullptr}},
{"token", {wrap<get_token>, nullptr}}, {"token", {wrap<get_token>, nullptr}},
{"isAdmin", {wrap<is_admin>, nullptr}}, {"isAdmin", {wrap<is_admin>, nullptr}},
{"isAdminToken", {wrap<is_admin_token>, nullptr}},
}; };
static void create_user(ContextType, FunctionType, ObjectType, size_t, const ValueType[], ReturnValue &); static void create_user(ContextType, FunctionType, ObjectType, size_t, const ValueType[], ReturnValue &);
@ -125,6 +127,11 @@ void UserClass<T>::is_admin(ContextType ctx, ObjectType object, ReturnValue &ret
return_value.set(get_internal<T, UserClass<T>>(object)->get()->is_admin()); return_value.set(get_internal<T, UserClass<T>>(object)->get()->is_admin());
} }
template<typename T>
void UserClass<T>::is_admin_token(ContextType ctx, ObjectType object, ReturnValue &return_value) {
return_value.set(get_internal<T, UserClass<T>>(object)->get()->token_type() == SyncUser::TokenType::Admin);
}
template<typename T> template<typename T>
void UserClass<T>::create_user(ContextType ctx, FunctionType, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) { void UserClass<T>::create_user(ContextType ctx, FunctionType, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
validate_argument_count(argc, 3, 5); validate_argument_count(argc, 3, 5);

@ -1 +1 @@
Subproject commit 21e671a155989f4e34e279fc8a7ace5817495de0 Subproject commit 8517ee7f4378fe0f54945b3e4973766ff65e455d