2016-10-04 22:02:51 +00:00
////////////////////////////////////////////////////////////////////////////
//
// Copyright 2016 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.
//
////////////////////////////////////////////////////////////////////////////
# pragma once
2017-09-22 08:29:37 +00:00
# include <mutex>
# include <condition_variable>
2016-10-04 22:02:51 +00:00
2017-01-23 11:37:13 +00:00
# include "event_loop_dispatcher.hpp"
2016-10-20 00:55:46 +00:00
# include "platform.hpp"
2016-10-04 22:02:51 +00:00
# include "js_class.hpp"
# include "js_collection.hpp"
2016-10-20 00:55:46 +00:00
# include "sync/sync_manager.hpp"
# include "sync/sync_config.hpp"
# include "sync/sync_session.hpp"
# include "sync/sync_user.hpp"
2016-10-04 22:02:51 +00:00
# include "realm/util/logger.hpp"
# include "realm/util/uri.hpp"
namespace realm {
namespace js {
2017-09-27 20:31:27 +00:00
inline realm : : SyncManager & syncManagerShared ( ) {
2017-10-11 12:54:52 +00:00
static std : : once_flag flag ;
std : : call_once ( flag , [ ] {
2017-09-27 18:16:44 +00:00
ensure_directory_exists_for_file ( default_realm_file_directory ( ) ) ;
2017-09-27 19:02:09 +00:00
SyncManager : : shared ( ) . configure_file_system ( default_realm_file_directory ( ) , SyncManager : : MetadataMode : : NoEncryption ) ;
2017-10-11 12:54:52 +00:00
} ) ;
2017-09-27 19:02:09 +00:00
return SyncManager : : shared ( ) ;
2017-09-27 18:16:44 +00:00
}
2017-02-01 13:18:59 +00:00
using SharedUser = std : : shared_ptr < realm : : SyncUser > ;
using WeakSession = std : : weak_ptr < realm : : SyncSession > ;
2016-10-20 00:55:46 +00:00
template < typename T >
class UserClass : public ClassDefinition < T , SharedUser > {
using GlobalContextType = typename T : : GlobalContext ;
using ContextType = typename T : : Context ;
using FunctionType = typename T : : Function ;
using ObjectType = typename T : : Object ;
using ValueType = typename T : : Value ;
using String = js : : String < T > ;
using Object = js : : Object < T > ;
using Value = js : : Value < T > ;
using Function = js : : Function < T > ;
using ReturnValue = js : : ReturnValue < T > ;
2018-01-11 15:00:31 +00:00
using Arguments = js : : Arguments < T > ;
2016-10-20 00:55:46 +00:00
public :
std : : string const name = " User " ;
static FunctionType create_constructor ( ContextType ) ;
static void get_server ( ContextType , ObjectType , ReturnValue & ) ;
static void get_identity ( ContextType , ObjectType , ReturnValue & ) ;
static void get_token ( ContextType , ObjectType , ReturnValue & ) ;
static void is_admin ( ContextType , ObjectType , ReturnValue & ) ;
2018-01-11 13:47:54 +00:00
static void is_admin_token ( ContextType , ObjectType , ReturnValue & ) ;
2016-10-20 00:55:46 +00:00
PropertyMap < T > const properties = {
{ " server " , { wrap < get_server > , nullptr } } ,
{ " identity " , { wrap < get_identity > , nullptr } } ,
{ " token " , { wrap < get_token > , nullptr } } ,
{ " isAdmin " , { wrap < is_admin > , nullptr } } ,
2018-01-11 13:47:54 +00:00
{ " isAdminToken " , { wrap < is_admin_token > , nullptr } } ,
2016-10-20 00:55:46 +00:00
} ;
2017-02-07 10:01:26 +00:00
static void create_user ( ContextType , FunctionType , ObjectType , size_t , const ValueType [ ] , ReturnValue & ) ;
2017-08-31 19:38:10 +00:00
static void admin_user ( ContextType , FunctionType , ObjectType , size_t , const ValueType [ ] , ReturnValue & ) ;
2018-01-11 15:00:31 +00:00
static void get_existing_user ( ContextType , ObjectType , Arguments , ReturnValue & ) ;
2016-10-20 00:55:46 +00:00
MethodMap < T > const static_methods = {
2017-08-31 19:38:10 +00:00
{ " createUser " , wrap < create_user > } ,
2018-01-11 15:00:31 +00:00
{ " _adminUser " , wrap < admin_user > } ,
{ " _getExistingUser " , wrap < get_existing_user > } ,
2016-10-20 00:55:46 +00:00
} ;
2016-10-20 01:59:59 +00:00
2017-01-31 13:07:29 +00:00
/*static void current_user(ContextType ctx, ObjectType object, ReturnValue &return_value);*/
2016-10-20 01:59:59 +00:00
static void all_users ( ContextType ctx , ObjectType object , ReturnValue & return_value ) ;
PropertyMap < T > const static_properties = {
2017-01-31 13:07:29 +00:00
/*{"current", {wrap<current_user>, nullptr}},*/
2016-10-20 01:59:59 +00:00
{ " all " , { wrap < all_users > , nullptr } } ,
} ;
2017-02-07 10:01:26 +00:00
static void logout ( ContextType , FunctionType , ObjectType , size_t , const ValueType [ ] , ReturnValue & ) ;
static void session_for_on_disk_path ( ContextType , FunctionType , ObjectType , size_t , const ValueType [ ] , ReturnValue & ) ;
2016-10-20 20:07:45 +00:00
MethodMap < T > const methods = {
2017-02-01 13:18:59 +00:00
{ " logout " , wrap < logout > } ,
{ " _sessionForOnDiskPath " , wrap < session_for_on_disk_path > }
2016-10-20 20:07:45 +00:00
} ;
2016-10-20 00:55:46 +00:00
} ;
template < typename T >
void UserClass < T > : : get_server ( ContextType ctx , ObjectType object , ReturnValue & return_value ) {
2016-10-20 03:25:10 +00:00
std : : string server = get_internal < T , UserClass < T > > ( object ) - > get ( ) - > server_url ( ) ;
2016-10-20 00:55:46 +00:00
return_value . set ( server ) ;
}
template < typename T >
void UserClass < T > : : get_identity ( ContextType ctx , ObjectType object , ReturnValue & return_value ) {
std : : string identity = get_internal < T , UserClass < T > > ( object ) - > get ( ) - > identity ( ) ;
return_value . set ( identity ) ;
}
template < typename T >
void UserClass < T > : : get_token ( ContextType ctx , ObjectType object , ReturnValue & return_value ) {
std : : string token = get_internal < T , UserClass < T > > ( object ) - > get ( ) - > refresh_token ( ) ;
return_value . set ( token ) ;
}
template < typename T >
void UserClass < T > : : is_admin ( ContextType ctx , ObjectType object , ReturnValue & return_value ) {
return_value . set ( get_internal < T , UserClass < T > > ( object ) - > get ( ) - > is_admin ( ) ) ;
}
2018-01-11 13:47:54 +00:00
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 ) ;
}
2016-10-20 00:55:46 +00:00
template < typename T >
2017-02-07 10:01:26 +00:00
void UserClass < T > : : create_user ( ContextType ctx , FunctionType , ObjectType this_object , size_t argc , const ValueType arguments [ ] , ReturnValue & return_value ) {
2017-06-17 14:59:15 +00:00
validate_argument_count ( argc , 3 , 5 ) ;
2017-08-16 15:11:46 +00:00
SyncUserIdentifier userIdentifier {
2017-08-17 09:37:16 +00:00
Value : : validated_to_string ( ctx , arguments [ 1 ] , " identity " ) ,
Value : : validated_to_string ( ctx , arguments [ 0 ] , " authServerUrl " )
2017-08-16 15:11:46 +00:00
} ;
2017-09-27 18:16:44 +00:00
SharedUser * user = new SharedUser ( syncManagerShared ( ) . get_user (
2017-08-16 15:11:46 +00:00
userIdentifier ,
2017-08-17 09:37:16 +00:00
Value : : validated_to_string ( ctx , arguments [ 2 ] , " refreshToken " )
2017-08-16 15:11:46 +00:00
) ) ;
2017-06-17 14:59:15 +00:00
if ( argc = = 5 ) {
( * user ) - > set_is_admin ( Value : : validated_to_boolean ( ctx , arguments [ 4 ] , " isAdmin " ) ) ;
}
2016-10-20 00:55:46 +00:00
return_value . set ( create_object < T , UserClass < T > > ( ctx , user ) ) ;
}
2017-08-31 19:38:10 +00:00
template < typename T >
void UserClass < T > : : admin_user ( ContextType ctx , FunctionType , ObjectType this_object , size_t argc , const ValueType arguments [ ] , ReturnValue & return_value ) {
validate_argument_count ( argc , 2 , 2 ) ;
2017-09-27 18:16:44 +00:00
SharedUser * user = new SharedUser ( syncManagerShared ( ) . get_admin_token_user (
2017-08-31 19:38:10 +00:00
Value : : validated_to_string ( ctx , arguments [ 0 ] , " authServerUrl " ) ,
Value : : validated_to_string ( ctx , arguments [ 1 ] , " refreshToken " )
) ) ;
return_value . set ( create_object < T , UserClass < T > > ( ctx , user ) ) ;
}
2018-01-11 15:00:31 +00:00
template < typename T >
void UserClass < T > : : get_existing_user ( ContextType ctx , ObjectType , Arguments arguments , ReturnValue & return_value ) {
arguments . validate_count ( 2 ) ;
if ( auto user = syncManagerShared ( ) . get_existing_logged_in_user ( SyncUserIdentifier {
Value : : validated_to_string ( ctx , arguments [ 1 ] , " identity " ) ,
Value : : validated_to_string ( ctx , arguments [ 0 ] , " authServerUrl " ) } ) ) {
return_value . set ( create_object < T , UserClass < T > > ( ctx , new SharedUser ( std : : move ( user ) ) ) ) ;
}
}
2016-10-20 01:59:59 +00:00
template < typename T >
void UserClass < T > : : all_users ( ContextType ctx , ObjectType object , ReturnValue & return_value ) {
2016-11-10 21:55:22 +00:00
auto users = Object : : create_empty ( ctx ) ;
2017-09-27 18:16:44 +00:00
for ( auto user : syncManagerShared ( ) . all_logged_in_users ( ) ) {
2017-06-27 18:32:34 +00:00
if ( user - > token_type ( ) = = SyncUser : : TokenType : : Normal ) {
2016-11-10 21:55:22 +00:00
Object : : set_property ( ctx , users , user - > identity ( ) , create_object < T , UserClass < T > > ( ctx , new SharedUser ( user ) ) , ReadOnly | DontDelete ) ;
2016-11-10 20:53:36 +00:00
}
2016-10-20 01:59:59 +00:00
}
2016-11-10 21:55:22 +00:00
return_value . set ( users ) ;
2016-10-20 01:59:59 +00:00
}
2016-10-20 20:07:45 +00:00
template < typename T >
2017-02-07 10:01:26 +00:00
void UserClass < T > : : logout ( ContextType ctx , FunctionType , ObjectType this_object , size_t , const ValueType [ ] , ReturnValue & ) {
get_internal < T , UserClass < T > > ( this_object ) - > get ( ) - > log_out ( ) ;
2016-10-20 20:07:45 +00:00
}
2017-02-01 13:18:59 +00:00
template < typename T >
class SessionClass : public ClassDefinition < T , WeakSession > {
using ContextType = typename T : : Context ;
using FunctionType = typename T : : Function ;
using ObjectType = typename T : : Object ;
using ValueType = typename T : : Value ;
using String = js : : String < T > ;
using Object = js : : Object < T > ;
using Value = js : : Value < T > ;
using ReturnValue = js : : ReturnValue < T > ;
2017-10-11 21:31:43 +00:00
using Arguments = js : : Arguments < T > ;
2017-05-05 09:48:34 +00:00
2017-02-01 13:18:59 +00:00
public :
std : : string const name = " Session " ;
2017-09-01 15:12:06 +00:00
using ProgressHandler = void ( uint64_t transferred_bytes , uint64_t transferrable_bytes ) ;
2017-02-01 13:18:59 +00:00
static FunctionType create_constructor ( ContextType ) ;
2017-05-05 09:48:34 +00:00
2017-02-01 13:18:59 +00:00
static void get_config ( ContextType , ObjectType , ReturnValue & ) ;
static void get_user ( ContextType , ObjectType , ReturnValue & ) ;
static void get_url ( ContextType , ObjectType , ReturnValue & ) ;
static void get_state ( ContextType , ObjectType , ReturnValue & ) ;
2017-02-07 10:01:26 +00:00
static void simulate_error ( ContextType , FunctionType , ObjectType , size_t , const ValueType [ ] , ReturnValue & ) ;
static void refresh_access_token ( ContextType , FunctionType , ObjectType , size_t , const ValueType [ ] , ReturnValue & ) ;
2017-09-01 15:12:06 +00:00
static void add_progress_notification ( ContextType ctx , FunctionType , ObjectType this_object , size_t argc , const ValueType arguments [ ] , ReturnValue & ) ;
static void remove_progress_notification ( ContextType ctx , FunctionType , ObjectType this_object , size_t argc , const ValueType arguments [ ] , ReturnValue & ) ;
2017-02-01 13:18:59 +00:00
2017-10-11 21:31:43 +00:00
static void override_server ( ContextType ctx , ObjectType this_object , Arguments args , ReturnValue & ) ;
2017-02-01 13:18:59 +00:00
PropertyMap < T > const properties = {
{ " config " , { wrap < get_config > , nullptr } } ,
{ " user " , { wrap < get_user > , nullptr } } ,
{ " url " , { wrap < get_url > , nullptr } } ,
{ " state " , { wrap < get_state > , nullptr } }
} ;
MethodMap < T > const methods = {
{ " _simulateError " , wrap < simulate_error > } ,
2017-09-01 15:12:06 +00:00
{ " _refreshAccessToken " , wrap < refresh_access_token > } ,
2017-10-11 21:31:43 +00:00
{ " _overrideServer " , wrap < override_server > } ,
2017-09-01 15:12:06 +00:00
{ " addProgressNotification " , wrap < add_progress_notification > } ,
{ " removeProgressNotification " , wrap < remove_progress_notification > } ,
2017-02-01 13:18:59 +00:00
} ;
} ;
template < typename T >
2017-02-07 10:01:26 +00:00
class SyncSessionErrorHandlerFunctor {
public :
SyncSessionErrorHandlerFunctor ( typename T : : Context ctx , typename T : : Function error_func )
: m_ctx ( Context < T > : : get_global_context ( ctx ) )
, m_func ( ctx , error_func )
{ }
typename T : : Function func ( ) const { return m_func ; }
void operator ( ) ( std : : shared_ptr < SyncSession > session , SyncError error ) {
HANDLESCOPE
2017-11-06 12:51:33 +00:00
std : : string name = " Error " ;
2017-02-07 10:01:26 +00:00
auto error_object = Object < T > : : create_empty ( m_ctx ) ;
2017-11-15 00:09:50 +00:00
2017-10-02 17:44:24 +00:00
if ( error . is_client_reset_requested ( ) ) {
auto config_object = Object < T > : : create_empty ( m_ctx ) ;
Object < T > : : set_property ( m_ctx , config_object , " path " , Value < T > : : from_string ( m_ctx , error . user_info [ SyncError : : c_recovery_file_path_key ] ) ) ;
Object < T > : : set_property ( m_ctx , config_object , " readOnly " , Value < T > : : from_boolean ( m_ctx , true ) ) ;
Object < T > : : set_property ( m_ctx , error_object , " config " , config_object ) ;
2017-11-06 12:51:33 +00:00
name = " ClientReset " ;
2017-10-02 17:44:24 +00:00
}
2017-11-06 12:51:33 +00:00
Object < T > : : set_property ( m_ctx , error_object , " name " , Value < T > : : from_string ( m_ctx , name ) ) ;
2017-02-07 10:01:26 +00:00
Object < T > : : set_property ( m_ctx , error_object , " message " , Value < T > : : from_string ( m_ctx , error . message ) ) ;
Object < T > : : set_property ( m_ctx , error_object , " isFatal " , Value < T > : : from_boolean ( m_ctx , error . is_fatal ) ) ;
Object < T > : : set_property ( m_ctx , error_object , " category " , Value < T > : : from_string ( m_ctx , error . error_code . category ( ) . name ( ) ) ) ;
2017-11-06 12:51:33 +00:00
Object < T > : : set_property ( m_ctx , error_object , " code " , Value < T > : : from_number ( m_ctx , error . error_code . value ( ) ) ) ;
2017-02-07 10:01:26 +00:00
auto user_info = Object < T > : : create_empty ( m_ctx ) ;
for ( auto & kvp : error . user_info ) {
Object < T > : : set_property ( m_ctx , user_info , kvp . first , Value < T > : : from_string ( m_ctx , kvp . second ) ) ;
}
Object < T > : : set_property ( m_ctx , error_object , " userInfo " , user_info ) ;
typename T : : Value arguments [ 2 ] ;
arguments [ 0 ] = create_object < T , SessionClass < T > > ( m_ctx , new WeakSession ( session ) ) ;
arguments [ 1 ] = error_object ;
2017-06-19 11:19:43 +00:00
Function < T > : : callback ( m_ctx , m_func , typename T : : Object ( ) , 2 , arguments ) ;
2017-02-07 10:01:26 +00:00
}
private :
const Protected < typename T : : GlobalContext > m_ctx ;
const Protected < typename T : : Function > m_func ;
} ;
2017-09-22 08:29:37 +00:00
2017-09-22 09:17:59 +00:00
// An object of type SSLVerifyCallbackSyncThreadFunctor is registered with the sync client in order
// to verify SSL certificates. The SSLVerifyCallbackSyncThreadFunctor object's operator() is called
// on the sync client's event loop thread.
2017-09-22 08:29:37 +00:00
template < typename T >
class SSLVerifyCallbackSyncThreadFunctor {
public :
SSLVerifyCallbackSyncThreadFunctor ( typename T : : Context ctx , typename T : : Function ssl_verify_func )
: m_ctx ( Context < T > : : get_global_context ( ctx ) )
, m_func ( ctx , ssl_verify_func )
, m_event_loop_dispatcher { SSLVerifyCallbackSyncThreadFunctor < T > : : main_loop_handler }
, m_mutex { new std : : mutex }
, m_cond_var { new std : : condition_variable }
{
}
2017-09-22 09:17:59 +00:00
// This function is called on the sync client's event loop thread.
2017-09-22 08:29:37 +00:00
bool operator ( ) ( const std : : string & server_address , sync : : Session : : port_type server_port , const char * pem_data , size_t pem_size , int preverify_ok , int depth )
{
const std : : string pem_certificate { pem_data , pem_size } ;
{
std : : lock_guard < std : : mutex > lock { * m_mutex } ;
m_ssl_certificate_callback_done = false ;
}
2017-09-22 09:17:59 +00:00
// Dispatch the call to the main_loop_handler on the node.js thread.
2017-09-22 08:29:37 +00:00
m_event_loop_dispatcher ( this , server_address , server_port , pem_certificate , preverify_ok , depth ) ;
bool ssl_certificate_accepted = false ;
{
2017-09-22 09:17:59 +00:00
// Wait for the return value of the callback function on the node.js main thread.
// The sync client blocks during this wait.
2017-09-22 08:29:37 +00:00
std : : unique_lock < std : : mutex > lock ( * m_mutex ) ;
m_cond_var - > wait ( lock , [ this ] { return this - > m_ssl_certificate_callback_done ; } ) ;
ssl_certificate_accepted = m_ssl_certificate_accepted ;
}
return ssl_certificate_accepted ;
}
2017-09-22 09:17:59 +00:00
// main_loop_handler is called on the node.js main thread.
// main_loop_handler calls the user callback (m_func) and sends the return value
// back to the sync client's event loop thread through a condition variable.
2017-09-22 08:29:37 +00:00
static void main_loop_handler ( SSLVerifyCallbackSyncThreadFunctor < T > * this_object ,
const std : : string & server_address ,
sync : : Session : : port_type server_port ,
const std : : string & pem_certificate ,
int preverify_ok ,
int depth )
{
HANDLESCOPE
const Protected < typename T : : GlobalContext > & ctx = this_object - > m_ctx ;
typename T : : Object ssl_certificate_object = Object < T > : : create_empty ( ctx ) ;
Object < T > : : set_property ( ctx , ssl_certificate_object , " serverAddress " , Value < T > : : from_string ( ctx , server_address ) ) ;
Object < T > : : set_property ( ctx , ssl_certificate_object , " serverPort " , Value < T > : : from_number ( ctx , double ( server_port ) ) ) ;
Object < T > : : set_property ( ctx , ssl_certificate_object , " pemCertificate " , Value < T > : : from_string ( ctx , pem_certificate ) ) ;
2017-09-25 09:05:16 +00:00
Object < T > : : set_property ( ctx , ssl_certificate_object , " acceptedByOpenSSL " , Value < T > : : from_boolean ( ctx , preverify_ok ! = 0 ) ) ;
2017-09-22 08:29:37 +00:00
Object < T > : : set_property ( ctx , ssl_certificate_object , " depth " , Value < T > : : from_number ( ctx , double ( depth ) ) ) ;
const int argc = 1 ;
typename T : : Value arguments [ argc ] = { ssl_certificate_object } ;
typename T : : Value ret_val = Function < T > : : callback ( ctx , this_object - > m_func , typename T : : Object ( ) , 1 , arguments ) ;
bool ret_val_bool = Value < T > : : to_boolean ( ctx , ret_val ) ;
{
std : : lock_guard < std : : mutex > lock { * this_object - > m_mutex } ;
this_object - > m_ssl_certificate_callback_done = true ;
this_object - > m_ssl_certificate_accepted = ret_val_bool ;
}
this_object - > m_cond_var - > notify_one ( ) ;
} ;
private :
const Protected < typename T : : GlobalContext > m_ctx ;
const Protected < typename T : : Function > m_func ;
EventLoopDispatcher < void ( SSLVerifyCallbackSyncThreadFunctor < T > * this_object ,
const std : : string & server_address ,
sync : : Session : : port_type server_port ,
const std : : string & pem_certificate ,
int preverify_ok ,
int depth ) > m_event_loop_dispatcher ;
bool m_ssl_certificate_callback_done = false ;
bool m_ssl_certificate_accepted = false ;
std : : shared_ptr < std : : mutex > m_mutex ;
std : : shared_ptr < std : : condition_variable > m_cond_var ;
} ;
2017-02-07 10:01:26 +00:00
template < typename T >
void UserClass < T > : : session_for_on_disk_path ( ContextType ctx , FunctionType , ObjectType this_object , size_t argc , const ValueType arguments [ ] , ReturnValue & return_value ) {
auto user = * get_internal < T , UserClass < T > > ( this_object ) ;
2017-02-01 13:18:59 +00:00
if ( auto session = user - > session_for_on_disk_path ( Value : : validated_to_string ( ctx , arguments [ 0 ] ) ) ) {
return_value . set ( create_object < T , SessionClass < T > > ( ctx , new WeakSession ( session ) ) ) ;
} else {
return_value . set_undefined ( ) ;
}
}
template < typename T >
void SessionClass < T > : : get_config ( ContextType ctx , ObjectType object , ReturnValue & return_value ) {
if ( auto session = get_internal < T , SessionClass < T > > ( object ) - > lock ( ) ) {
ObjectType config = Object : : create_empty ( ctx ) ;
Object : : set_property ( ctx , config , " user " , create_object < T , UserClass < T > > ( ctx , new SharedUser ( session - > config ( ) . user ) ) ) ;
2017-09-28 22:31:22 +00:00
Object : : set_property ( ctx , config , " url " , Value : : from_string ( ctx , session - > config ( ) . realm_url ( ) ) ) ;
2018-01-17 11:35:50 +00:00
if ( auto dispatcher = session - > config ( ) . error_handler . template target < EventLoopDispatcher < SyncSessionErrorHandler > > ( ) ) {
if ( auto handler = dispatcher - > func ( ) . template target < SyncSessionErrorHandlerFunctor < T > > ( ) ) {
Object : : set_property ( ctx , config , " error " , handler - > func ( ) ) ;
}
2017-02-07 10:01:26 +00:00
}
2017-02-01 13:18:59 +00:00
return_value . set ( config ) ;
} else {
return_value . set_undefined ( ) ;
}
}
template < typename T >
void SessionClass < T > : : get_user ( ContextType ctx , ObjectType object , ReturnValue & return_value ) {
if ( auto session = get_internal < T , SessionClass < T > > ( object ) - > lock ( ) ) {
return_value . set ( create_object < T , UserClass < T > > ( ctx , new SharedUser ( session - > config ( ) . user ) ) ) ;
} else {
return_value . set_undefined ( ) ;
}
}
template < typename T >
void SessionClass < T > : : get_url ( ContextType ctx , ObjectType object , ReturnValue & return_value ) {
if ( auto session = get_internal < T , SessionClass < T > > ( object ) - > lock ( ) ) {
if ( util : : Optional < std : : string > url = session - > full_realm_url ( ) ) {
return_value . set ( * url ) ;
return ;
}
}
return_value . set_undefined ( ) ;
}
template < typename T >
void SessionClass < T > : : get_state ( ContextType ctx , ObjectType object , ReturnValue & return_value ) {
static const std : : string invalid ( " invalid " ) ;
static const std : : string inactive ( " inactive " ) ;
2017-05-05 09:48:34 +00:00
static const std : : string active ( " active " ) ;
2017-02-01 13:18:59 +00:00
return_value . set ( invalid ) ;
if ( auto session = get_internal < T , SessionClass < T > > ( object ) - > lock ( ) ) {
if ( session - > state ( ) = = SyncSession : : PublicState : : Inactive ) {
return_value . set ( inactive ) ;
} else if ( session - > state ( ) ! = SyncSession : : PublicState : : Error ) {
return_value . set ( active ) ;
}
}
}
template < typename T >
2017-02-07 10:01:26 +00:00
void SessionClass < T > : : simulate_error ( ContextType ctx , FunctionType , ObjectType this_object , size_t argc , const ValueType arguments [ ] , ReturnValue & ) {
2017-02-01 13:18:59 +00:00
validate_argument_count ( argc , 2 ) ;
2017-02-07 10:01:26 +00:00
if ( auto session = get_internal < T , SessionClass < T > > ( this_object ) - > lock ( ) ) {
2017-11-17 18:43:24 +00:00
std : : error_code error_code ( Value : : validated_to_number ( ctx , arguments [ 0 ] ) , realm : : sync : : protocol_error_category ( ) ) ;
std : : string message = Value : : validated_to_string ( ctx , arguments [ 1 ] ) ;
SyncSession : : OnlyForTesting : : handle_error ( * session , SyncError ( error_code , message , false ) ) ;
2017-02-01 13:18:59 +00:00
}
}
template < typename T >
2017-02-07 10:01:26 +00:00
void SessionClass < T > : : refresh_access_token ( ContextType ctx , FunctionType , ObjectType this_object , size_t argc , const ValueType arguments [ ] , ReturnValue & ) {
2017-11-15 00:09:50 +00:00
validate_argument_count ( argc , 3 ) ;
2017-02-01 13:18:59 +00:00
2017-02-07 10:01:26 +00:00
if ( auto session = get_internal < T , SessionClass < T > > ( this_object ) - > lock ( ) ) {
2017-11-15 00:09:50 +00:00
std : : string sync_label = Value : : validated_to_string ( ctx , arguments [ 2 ] , " syncLabel " ) ;
session - > set_multiplex_identifier ( std : : move ( sync_label ) ) ;
2017-02-01 13:18:59 +00:00
std : : string access_token = Value : : validated_to_string ( ctx , arguments [ 0 ] , " accessToken " ) ;
std : : string realm_url = Value : : validated_to_string ( ctx , arguments [ 1 ] , " realmUrl " ) ;
session - > refresh_access_token ( std : : move ( access_token ) , std : : move ( realm_url ) ) ;
}
}
2016-10-04 22:02:51 +00:00
template < typename T >
2017-09-01 15:12:06 +00:00
void SessionClass < T > : : add_progress_notification ( ContextType ctx , FunctionType , ObjectType this_object , size_t argc , const ValueType arguments [ ] , ReturnValue & return_value ) {
validate_argument_count ( argc , 3 ) ;
if ( auto session = get_internal < T , SessionClass < T > > ( this_object ) - > lock ( ) ) {
2017-09-22 09:17:59 +00:00
2017-09-01 15:12:06 +00:00
std : : string direction = Value : : validated_to_string ( ctx , arguments [ 0 ] , " direction " ) ;
std : : string mode = Value : : validated_to_string ( ctx , arguments [ 1 ] , " mode " ) ;
SyncSession : : NotifierType notifierType ;
if ( direction = = " download " ) {
notifierType = SyncSession : : NotifierType : : download ;
}
else if ( direction = = " upload " ) {
notifierType = SyncSession : : NotifierType : : upload ;
}
else {
throw std : : invalid_argument ( " Invalid argument 'direction'. Only 'download' and 'upload' progress notification directions are supported " ) ;
}
bool is_streaming = false ;
if ( mode = = " reportIndefinitely " ) {
is_streaming = true ;
}
else if ( mode = = " forCurrentlyOutstandingWork " ) {
is_streaming = false ;
}
else {
throw std : : invalid_argument ( " Invalid argument 'mode'. Only 'reportIndefinitely' and 'forCurrentlyOutstandingWork' progress notification modes are supported " ) ;
}
auto callback_function = Value : : validated_to_function ( ctx , arguments [ 2 ] , " callback " ) ;
Protected < FunctionType > protected_callback ( ctx , callback_function ) ;
Protected < ObjectType > protected_this ( ctx , this_object ) ;
Protected < typename T : : GlobalContext > protected_ctx ( Context < T > : : get_global_context ( ctx ) ) ;
2017-09-22 09:17:59 +00:00
std : : function < ProgressHandler > progressFunc ;
2017-09-01 15:12:06 +00:00
EventLoopDispatcher < ProgressHandler > progress_handler ( [ = ] ( uint64_t transferred_bytes , uint64_t transferrable_bytes ) {
HANDLESCOPE
ValueType callback_arguments [ 2 ] ;
callback_arguments [ 0 ] = Value : : from_number ( protected_ctx , transferred_bytes ) ;
callback_arguments [ 1 ] = Value : : from_number ( protected_ctx , transferrable_bytes ) ;
2017-09-22 09:17:59 +00:00
2017-09-11 23:47:07 +00:00
Function < T > : : callback ( protected_ctx , protected_callback , typename T : : Object ( ) , 2 , callback_arguments ) ;
2017-09-01 15:12:06 +00:00
} ) ;
progressFunc = std : : move ( progress_handler ) ;
2017-09-20 18:59:07 +00:00
auto registrationToken = session - > register_progress_notifier ( std : : move ( progressFunc ) , notifierType , is_streaming ) ;
2017-09-01 15:12:06 +00:00
auto syncSession = create_object < T , SessionClass < T > > ( ctx , new WeakSession ( session ) ) ;
PropertyAttributes attributes = ReadOnly | DontEnum | DontDelete ;
2017-09-11 12:28:05 +00:00
Object : : set_property ( ctx , callback_function , " _syncSession " , syncSession , attributes ) ;
Object : : set_property ( ctx , callback_function , " _registrationToken " , Value : : from_number ( protected_ctx , registrationToken ) , attributes ) ;
2017-09-01 15:12:06 +00:00
}
}
template < typename T >
void SessionClass < T > : : remove_progress_notification ( ContextType ctx , FunctionType , ObjectType this_object , size_t argc , const ValueType arguments [ ] , ReturnValue & return_value ) {
validate_argument_count ( argc , 1 ) ;
auto callback_function = Value : : validated_to_function ( ctx , arguments [ 0 ] , " callback " ) ;
2017-09-11 12:28:05 +00:00
auto syncSessionProp = Object : : get_property ( ctx , callback_function , " _syncSession " ) ;
2017-09-08 23:00:46 +00:00
if ( Value : : is_undefined ( ctx , syncSessionProp ) | | Value : : is_null ( ctx , syncSessionProp ) ) {
return ;
}
2017-09-01 15:12:06 +00:00
auto syncSession = Value : : validated_to_object ( ctx , syncSessionProp ) ;
2017-09-11 12:28:05 +00:00
auto registrationToken = Object : : get_property ( ctx , callback_function , " _registrationToken " ) ;
2017-09-01 15:12:06 +00:00
if ( auto session = get_internal < T , SessionClass < T > > ( syncSession ) - > lock ( ) ) {
auto reg = Value : : validated_to_number ( ctx , registrationToken ) ;
session - > unregister_progress_notifier ( reg ) ;
}
}
2017-10-11 21:31:43 +00:00
template < typename T >
void SessionClass < T > : : override_server ( ContextType ctx , ObjectType this_object , Arguments args , ReturnValue & ) {
args . validate_count ( 2 ) ;
std : : string address = Value : : validated_to_string ( ctx , args [ 0 ] , " address " ) ;
double port = Value : : validated_to_number ( ctx , args [ 1 ] , " port " ) ;
if ( port < 1 | | port > 65535 | | uint16_t ( port ) ! = port ) {
std : : ostringstream message ;
message < < " Invalid port number. Expected an integer in the range 1-65,535, got ' " < < port < < " ' " ;
throw std : : invalid_argument ( message . str ( ) ) ;
}
if ( auto session = get_internal < T , SessionClass < T > > ( this_object ) - > lock ( ) ) {
session - > override_server ( std : : move ( address ) , uint16_t ( port ) ) ;
}
}
2017-09-01 15:12:06 +00:00
template < typename T >
class SyncClass : public ClassDefinition < T , void * > {
2016-10-04 22:02:51 +00:00
using GlobalContextType = typename T : : GlobalContext ;
using ContextType = typename T : : Context ;
using FunctionType = typename T : : Function ;
using ObjectType = typename T : : Object ;
using ValueType = typename T : : Value ;
using String = js : : String < T > ;
using Object = js : : Object < T > ;
using Value = js : : Value < T > ;
using Function = js : : Function < T > ;
using ReturnValue = js : : ReturnValue < T > ;
public :
std : : string const name = " Sync " ;
static FunctionType create_constructor ( ContextType ) ;
2017-02-07 10:01:26 +00:00
static void set_sync_log_level ( ContextType , FunctionType , ObjectType , size_t , const ValueType [ ] , ReturnValue & ) ;
2017-10-02 17:44:24 +00:00
static void initiate_client_reset ( ContextType , FunctionType , ObjectType , size_t , const ValueType [ ] , ReturnValue & ) ;
2016-10-04 22:02:51 +00:00
// private
2017-09-11 10:21:44 +00:00
static std : : function < SyncBindSessionHandler > session_bind_callback ( ContextType ctx , ObjectType sync_constructor ) ;
2016-11-11 23:05:08 +00:00
static void populate_sync_config ( ContextType , ObjectType realm_constructor , ObjectType config_object , Realm : : Config & ) ;
2016-10-04 22:02:51 +00:00
// static properties
static void get_is_developer_edition ( ContextType , ObjectType , ReturnValue & ) ;
MethodMap < T > const static_methods = {
{ " setLogLevel " , wrap < set_sync_log_level > } ,
2017-10-02 17:44:24 +00:00
{ " initiateClientReset " , wrap < initiate_client_reset > } ,
2016-10-04 22:02:51 +00:00
} ;
} ;
template < typename T >
inline typename T : : Function SyncClass < T > : : create_constructor ( ContextType ctx ) {
FunctionType sync_constructor = ObjectWrap < T , SyncClass < T > > : : create_constructor ( ctx ) ;
2016-10-20 00:55:46 +00:00
PropertyAttributes attributes = ReadOnly | DontEnum | DontDelete ;
Object : : set_property ( ctx , sync_constructor , " User " , ObjectWrap < T , UserClass < T > > : : create_constructor ( ctx ) , attributes ) ;
2017-02-01 13:18:59 +00:00
Object : : set_property ( ctx , sync_constructor , " Session " , ObjectWrap < T , SessionClass < T > > : : create_constructor ( ctx ) , attributes ) ;
2017-05-05 09:48:34 +00:00
2016-10-04 22:02:51 +00:00
return sync_constructor ;
}
2017-10-02 17:44:24 +00:00
template < typename T >
void SyncClass < T > : : initiate_client_reset ( ContextType ctx , FunctionType , ObjectType this_object , size_t argc , const ValueType arguments [ ] , ReturnValue & return_value ) {
validate_argument_count ( argc , 1 ) ;
std : : string path = Value : : validated_to_string ( ctx , arguments [ 0 ] ) ;
if ( ! SyncManager : : shared ( ) . immediately_run_file_actions ( std : : string ( path ) ) ) {
throw std : : runtime_error ( util : : format ( " Realm was not configured correctly. Client Reset could not be run for Realm at: %1 " , path ) ) ;
}
}
2016-10-04 22:02:51 +00:00
template < typename T >
2017-02-07 10:01:26 +00:00
void SyncClass < T > : : set_sync_log_level ( ContextType ctx , FunctionType , ObjectType this_object , size_t argc , const ValueType arguments [ ] , ReturnValue & return_value ) {
2016-10-04 22:02:51 +00:00
validate_argument_count ( argc , 1 ) ;
std : : string log_level = Value : : validated_to_string ( ctx , arguments [ 0 ] ) ;
std : : istringstream in ( log_level ) ; // Throws
in . imbue ( std : : locale : : classic ( ) ) ; // Throws
in . unsetf ( std : : ios_base : : skipws ) ;
util : : Logger : : Level log_level_2 = util : : Logger : : Level ( ) ;
in > > log_level_2 ; // Throws
if ( ! in | | ! in . eof ( ) )
throw std : : runtime_error ( " Bad log level " ) ;
2017-09-27 18:16:44 +00:00
syncManagerShared ( ) . set_log_level ( log_level_2 ) ;
2016-10-04 22:02:51 +00:00
}
2017-09-11 10:21:44 +00:00
template < typename T >
std : : function < SyncBindSessionHandler > SyncClass < T > : : session_bind_callback ( ContextType ctx , ObjectType sync_constructor )
{
Protected < typename T : : GlobalContext > protected_ctx ( Context < T > : : get_global_context ( ctx ) ) ;
Protected < ObjectType > protected_sync_constructor ( ctx , sync_constructor ) ;
return EventLoopDispatcher < SyncBindSessionHandler > ( [ protected_ctx , protected_sync_constructor ] ( const std : : string & path , const realm : : SyncConfig & config , std : : shared_ptr < SyncSession > ) {
HANDLESCOPE
ObjectType user_constructor = Object : : validated_get_object ( protected_ctx , protected_sync_constructor , " User " ) ;
FunctionType refreshAccessToken = Object : : validated_get_function ( protected_ctx , user_constructor , " _refreshAccessToken " ) ;
ValueType arguments [ 3 ] ;
arguments [ 0 ] = create_object < T , UserClass < T > > ( protected_ctx , new SharedUser ( config . user ) ) ;
arguments [ 1 ] = Value : : from_string ( protected_ctx , path ) ;
2017-09-28 22:31:22 +00:00
arguments [ 2 ] = Value : : from_string ( protected_ctx , config . realm_url ( ) ) ;
2017-09-11 10:21:44 +00:00
Function : : call ( protected_ctx , refreshAccessToken , 3 , arguments ) ;
} ) ;
}
2016-10-04 22:02:51 +00:00
template < typename T >
2016-12-09 11:49:59 +00:00
void SyncClass < T > : : populate_sync_config ( ContextType ctx , ObjectType realm_constructor , ObjectType config_object , Realm : : Config & config )
{
2016-10-04 22:02:51 +00:00
ValueType sync_config_value = Object : : get_property ( ctx , config_object , " sync " ) ;
2017-04-18 10:41:07 +00:00
if ( Value : : is_boolean ( ctx , sync_config_value ) ) {
config . force_sync_history = Value : : to_boolean ( ctx , sync_config_value ) ;
} else if ( ! Value : : is_undefined ( ctx , sync_config_value ) ) {
2016-10-04 22:02:51 +00:00
auto sync_config_object = Value : : validated_to_object ( ctx , sync_config_value ) ;
2017-09-11 10:21:44 +00:00
ObjectType sync_constructor = Object : : validated_get_object ( ctx , realm_constructor , " Sync " ) ;
auto bind = session_bind_callback ( ctx , sync_constructor ) ;
2016-10-20 00:55:46 +00:00
2017-02-01 13:18:59 +00:00
std : : function < SyncSessionErrorHandler > error_handler ;
ValueType error_func = Object : : get_property ( ctx , sync_config_object , " error " ) ;
if ( ! Value : : is_undefined ( ctx , error_func ) ) {
2017-02-07 10:01:26 +00:00
error_handler = EventLoopDispatcher < SyncSessionErrorHandler > ( SyncSessionErrorHandlerFunctor < T > ( ctx , Value : : validated_to_function ( ctx , error_func ) ) ) ;
2017-02-01 13:18:59 +00:00
}
2017-05-05 09:48:34 +00:00
2016-10-04 22:02:51 +00:00
ObjectType user = Object : : validated_get_object ( ctx , sync_config_object , " user " ) ;
2016-10-20 00:55:46 +00:00
SharedUser shared_user = * get_internal < T , UserClass < T > > ( user ) ;
2016-11-11 08:14:52 +00:00
if ( shared_user - > state ( ) ! = SyncUser : : State : : Active ) {
throw std : : runtime_error ( " User is no longer valid. " ) ;
}
2016-10-20 00:55:46 +00:00
std : : string raw_realm_url = Object : : validated_get_string ( ctx , sync_config_object , " url " ) ;
2017-07-10 13:04:55 +00:00
if ( shared_user - > token_type ( ) = = SyncUser : : TokenType : : Admin ) {
2017-10-11 12:54:52 +00:00
size_t pos = raw_realm_url . find ( " /~/ " ) ;
if ( pos ! = std : : string : : npos ) {
raw_realm_url . replace ( pos + 1 , 1 , " __auth " ) ;
}
2017-07-10 13:04:55 +00:00
}
2017-09-22 09:17:59 +00:00
2017-05-05 11:50:45 +00:00
bool client_validate_ssl = true ;
ValueType validate_ssl_temp = Object : : get_property ( ctx , sync_config_object , " validate_ssl " ) ;
if ( ! Value : : is_undefined ( ctx , validate_ssl_temp ) ) {
client_validate_ssl = Value : : validated_to_boolean ( ctx , validate_ssl_temp , " validate_ssl " ) ;
}
util : : Optional < std : : string > ssl_trust_certificate_path ;
ValueType trust_certificate_path_temp = Object : : get_property ( ctx , sync_config_object , " ssl_trust_certificate_path " ) ;
if ( ! Value : : is_undefined ( ctx , trust_certificate_path_temp ) ) {
ssl_trust_certificate_path = std : : string ( Value : : validated_to_string ( ctx , trust_certificate_path_temp , " ssl_trust_certificate_path " ) ) ;
}
else {
ssl_trust_certificate_path = util : : none ;
}
2017-09-22 08:29:37 +00:00
std : : function < sync : : Session : : SSLVerifyCallback > ssl_verify_callback ;
2017-09-25 09:05:16 +00:00
ValueType ssl_verify_func = Object : : get_property ( ctx , sync_config_object , " open_ssl_verify_callback " ) ;
2017-09-22 08:29:37 +00:00
if ( ! Value : : is_undefined ( ctx , ssl_verify_func ) ) {
SSLVerifyCallbackSyncThreadFunctor < T > ssl_verify_functor { ctx , Value : : validated_to_function ( ctx , ssl_verify_func ) } ;
ssl_verify_callback = std : : move ( ssl_verify_functor ) ;
}
2017-10-02 18:29:36 +00:00
bool is_partial = false ;
ValueType partial_value = Object : : get_property ( ctx , sync_config_object , " partial " ) ;
if ( ! Value : : is_undefined ( ctx , partial_value ) ) {
is_partial = Value : : validated_to_boolean ( ctx , partial_value ) ;
}
2017-11-09 22:04:49 +00:00
config . sync_config = std : : make_shared < SyncConfig > ( shared_user , std : : move ( raw_realm_url ) ) ;
config . sync_config - > bind_session_handler = std : : move ( bind ) ;
config . sync_config - > error_handler = std : : move ( error_handler ) ;
config . sync_config - > client_validate_ssl = client_validate_ssl ;
config . sync_config - > ssl_trust_certificate_path = ssl_trust_certificate_path ;
config . sync_config - > ssl_verify_callback = std : : move ( ssl_verify_callback ) ;
config . sync_config - > is_partial = is_partial ;
2017-09-22 08:29:37 +00:00
2016-10-04 22:02:51 +00:00
config . schema_mode = SchemaMode : : Additive ;
2017-10-04 09:28:50 +00:00
config . path = syncManagerShared ( ) . path_for_realm ( * shared_user , config . sync_config - > realm_url ( ) ) ;
2017-03-28 12:34:27 +00:00
if ( ! config . encryption_key . empty ( ) ) {
config . sync_config - > realm_encryption_key = std : : array < char , 64 > ( ) ;
std : : copy_n ( config . encryption_key . begin ( ) , config . sync_config - > realm_encryption_key - > size ( ) , config . sync_config - > realm_encryption_key - > begin ( ) ) ;
}
2016-10-04 22:02:51 +00:00
}
}
} // js
} // realm