mirror of
https://github.com/status-im/realm-js.git
synced 2025-01-12 07:14:23 +00:00
Merge pull request #1790 from realm/yg/new-ssl-config-api
Group SSL configuration options in a new config object
This commit is contained in:
commit
f311fc7ba5
@ -1,4 +1,4 @@
|
|||||||
X.Y.Z Release notes ()
|
x.y.z Release notes (TBD)
|
||||||
=============================================================
|
=============================================================
|
||||||
### Compatibility
|
### Compatibility
|
||||||
* Sync protocol: 24
|
* Sync protocol: 24
|
||||||
@ -10,10 +10,12 @@ X.Y.Z Release notes ()
|
|||||||
* None.
|
* None.
|
||||||
|
|
||||||
### Enhancements
|
### Enhancements
|
||||||
|
* [Sync] The SSL configuration options are now grouped in a new config object. (#1465)
|
||||||
|
* [Sync] The Adapter can accept a new config parameter that specifies SSL settings for spawned sync sessions.
|
||||||
* Added `Object.linkingObjectsCount()` method, that returns total count of incoming links.
|
* Added `Object.linkingObjectsCount()` method, that returns total count of incoming links.
|
||||||
|
|
||||||
### Bug fixes
|
### Bug fixes
|
||||||
* None
|
* None.
|
||||||
|
|
||||||
### Internal
|
### Internal
|
||||||
* None.
|
* None.
|
||||||
|
@ -347,52 +347,7 @@ Realm.defaultPath;
|
|||||||
* If omitted, the schema will be read from the existing Realm file.
|
* If omitted, the schema will be read from the existing Realm file.
|
||||||
* @property {number} [schemaVersion] - **Required** (and must be incremented) after
|
* @property {number} [schemaVersion] - **Required** (and must be incremented) after
|
||||||
* changing the `schema`.
|
* changing the `schema`.
|
||||||
* @property {Object} [sync] - Sync configuration parameters with the following
|
* @property {Realm.Sync~SyncConfiguration} [sync] - Sync configuration parameters.
|
||||||
* child properties:
|
|
||||||
* - `user` - A `User` object obtained by calling `Realm.Sync.User.login`
|
|
||||||
* - `url` - A `string` which contains a valid Realm Sync url
|
|
||||||
* - `error` - A callback function which is called in error situations.
|
|
||||||
* The `error` callback can take up to five optional arguments: `name`, `message`, `isFatal`,
|
|
||||||
* `category`, and `code`.
|
|
||||||
* - `validate_ssl` - Indicating if SSL certificates must be validated
|
|
||||||
* - `ssl_trust_certificate_path` - A path where to find trusted SSL certificates
|
|
||||||
* - `open_ssl_verify_callback` - A callback function used to accept or reject the server's
|
|
||||||
* SSL certificate. open_ssl_verify_callback is called with an object of type
|
|
||||||
* <code>
|
|
||||||
* {
|
|
||||||
* serverAddress: String,
|
|
||||||
* serverPort: Number,
|
|
||||||
* pemCertificate: String,
|
|
||||||
* acceptedByOpenSSL: Boolean,
|
|
||||||
* depth: Number
|
|
||||||
* }
|
|
||||||
* </code>
|
|
||||||
* The return value of open_ssl_verify_callback decides whether the certificate is accepted (true)
|
|
||||||
* or rejected (false). The open_ssl_verify_callback function is only respected on platforms where
|
|
||||||
* OpenSSL is used for the sync client, e.g. Linux. The open_ssl_verify_callback function is not
|
|
||||||
* allowed to throw exceptions. If the operations needed to verify the certificate lead to an exception,
|
|
||||||
* the exception must be caught explicitly before returning. The return value would typically be false
|
|
||||||
* in case of an exception.
|
|
||||||
*
|
|
||||||
* When the sync client has received the server's certificate chain, it presents every certificate in
|
|
||||||
* the chain to the open_ssl_verify_callback function. The depth argument specifies the position of the
|
|
||||||
* certificate in the chain. depth = 0 represents the actual server certificate. The root
|
|
||||||
* certificate has the highest depth. The certificate of highest depth will be presented first.
|
|
||||||
*
|
|
||||||
* acceptedByOpenSSL is true if OpenSSL has accepted the certificate, and false if OpenSSL has rejected it.
|
|
||||||
* It is generally safe to return true when acceptedByOpenSSL is true. If acceptedByOpenSSL is false, an
|
|
||||||
* independent verification should be made.
|
|
||||||
*
|
|
||||||
* One possible way of using the open_ssl_verify_callback function is to embed the known server certificate
|
|
||||||
* in the client and accept the presented certificate if and only if it is equal to the known certificate.
|
|
||||||
*
|
|
||||||
* The purpose of open_ssl_verify_callback is to enable custom certificate handling and to solve cases where
|
|
||||||
* OpenSSL erroneously rejects valid certificates possibly because OpenSSL doesn't have access to the
|
|
||||||
* proper trust certificates.
|
|
||||||
* - `partial` - Whether this Realm should be opened in 'partial synchronization' mode.
|
|
||||||
* Partial synchronisation only synchronizes those objects that match the query specified in contrast
|
|
||||||
* to the normal mode of operation that synchronises all objects in a remote Realm.
|
|
||||||
* **Partial synchronization is a tech preview. Its APIs are subject to change.**
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
65
docs/sync.js
65
docs/sync.js
@ -16,6 +16,68 @@
|
|||||||
//
|
//
|
||||||
////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This describes the different options used to create a {@link Realm} instance with Realm Platform synchronization.
|
||||||
|
* @typedef {Object} Realm.Sync~SyncConfiguration
|
||||||
|
* @property {Realm.Sync.User} user - A {@link Realm.Sync.User} object obtained by calling `Realm.Sync.User.login`.
|
||||||
|
* @property {string} url - A `string` which contains a valid Realm Sync url.
|
||||||
|
* @property {function} [error] - A callback function which is called in error situations.
|
||||||
|
* The `error` callback can take up to five optional arguments: `name`, `message`, `isFatal`,
|
||||||
|
* `category`, and `code`.
|
||||||
|
*
|
||||||
|
* @deprecated
|
||||||
|
* @property {boolean} [validate_ssl] - Indicating if SSL certificates must be validated.
|
||||||
|
* @deprecated
|
||||||
|
* @property {string} [ssl_trust_certificate_path] - A path where to find trusted SSL certificates.
|
||||||
|
* @deprecated
|
||||||
|
* @property {Realm.Sync~sslValidateCallback} [open_ssl_verify_callback] - A callback function used to
|
||||||
|
* accept or reject the server's SSL certificate.
|
||||||
|
*
|
||||||
|
* @property {Realm.Sync~SSLConfiguration} [ssl] - SSL configuration.
|
||||||
|
* @property {boolean} [partial] - Whether this Realm should be opened in 'partial synchronization' mode.
|
||||||
|
* Partial synchronisation only synchronizes those objects that match the query specified in contrast
|
||||||
|
* to the normal mode of operation that synchronises all objects in a remote Realm.
|
||||||
|
* **Partial synchronization is a tech preview. Its APIs are subject to change.**
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This describes the different options used to create a {@link Realm} instance with Realm Platform synchronization.
|
||||||
|
* @typedef {Object} Realm.Sync~SSLConfiguration
|
||||||
|
* @property {boolean} validate - Indicating if SSL certificates must be validated. Default is `true`.
|
||||||
|
* @property {string} certificatePath - A path where to find trusted SSL certificates.
|
||||||
|
* @property {Realm.Sync~sslValidateCallback} validateCallback - A callback function used to
|
||||||
|
* accept or reject the server's SSL certificate.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When the sync client has received the server's certificate chain, it presents every certificate in
|
||||||
|
* the chain to the {@link Realm.Sync~sslValidateCallback} callback.
|
||||||
|
*
|
||||||
|
* The return value of the callback decides whether the certificate is accepted (`true`)
|
||||||
|
* or rejected (`false`). {@link Realm.Sync~sslValidateCallback} is only respected on platforms where
|
||||||
|
* OpenSSL is used for the sync client, e.g. Linux. The callback is not
|
||||||
|
* allowed to throw exceptions. If the operations needed to verify the certificate lead to an exception,
|
||||||
|
* the exception must be caught explicitly before returning. The return value would typically be false
|
||||||
|
* in case of an exception.
|
||||||
|
* @callback Realm.Sync~sslValidateCallback
|
||||||
|
* @param {Realm.Sync~SSLCertificateValidationInfo} validationInfo
|
||||||
|
* @return {boolean}
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {Object} Realm.Sync~SSLCertificateValidationInfo
|
||||||
|
* @property {string} serverAddress
|
||||||
|
* @property {number} serverPort
|
||||||
|
* @property {string} pemCertificate
|
||||||
|
* @property {boolean} acceptedByOpenSSL - `true` if OpenSSL has accepted the certificate,
|
||||||
|
* and `false` if OpenSSL has rejected it.
|
||||||
|
* It is generally safe to return true when `acceptedByOpenSSL` is `true`. If `acceptedByOpenSSL` is `false`,
|
||||||
|
* an independent verification should be made.
|
||||||
|
* @property {number} depth - Specifies the position of the certificate in the chain.
|
||||||
|
* `depth = 0` represents the actual server certificate. The root
|
||||||
|
* certificate has the highest depth. The certificate of highest depth will be presented first.
|
||||||
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* When opening a Realm created with Realm Mobile Platform v1.x, it is automatically
|
* When opening a Realm created with Realm Mobile Platform v1.x, it is automatically
|
||||||
* migrated to the v2.x format. In case this migration
|
* migrated to the v2.x format. In case this migration
|
||||||
@ -658,8 +720,9 @@ class Adapter {
|
|||||||
* use `.*` to match all all Realms
|
* use `.*` to match all all Realms
|
||||||
* @param {function(realmPath)} changeCallback - called when a new transaction is available
|
* @param {function(realmPath)} changeCallback - called when a new transaction is available
|
||||||
* to process for the given realm_path
|
* to process for the given realm_path
|
||||||
|
* @param {Realm.Sync~SSLConfiguration} [ssl] - SSL configuration for the spawned sync sessions.
|
||||||
*/
|
*/
|
||||||
constructor(localPath, serverUrl, adminUser, regex, changeCallback) {}
|
constructor(localPath, serverUrl, adminUser, regex, changeCallback, ssl) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the Array of current instructions for the given Realm.
|
* Get the Array of current instructions for the given Realm.
|
||||||
|
13
lib/index.d.ts
vendored
13
lib/index.d.ts
vendored
@ -392,12 +392,22 @@ declare namespace Realm.Sync {
|
|||||||
type ErrorCallback = (session: Session, error: SyncError) => void;
|
type ErrorCallback = (session: Session, error: SyncError) => void;
|
||||||
type SSLVerifyCallback = (sslVerifyObject: SSLVerifyObject) => boolean;
|
type SSLVerifyCallback = (sslVerifyObject: SSLVerifyObject) => boolean;
|
||||||
|
|
||||||
|
interface SSLConfiguration {
|
||||||
|
validate?: boolean;
|
||||||
|
certificatePath?: string;
|
||||||
|
validateCallback?: SSLVerifyCallback;
|
||||||
|
}
|
||||||
|
|
||||||
interface SyncConfiguration {
|
interface SyncConfiguration {
|
||||||
user: User;
|
user: User;
|
||||||
url: string;
|
url: string;
|
||||||
|
/** @deprecated use `ssl` instead */
|
||||||
validate_ssl?: boolean;
|
validate_ssl?: boolean;
|
||||||
|
/** @deprecated use `ssl` instead */
|
||||||
ssl_trust_certificate_path?: string;
|
ssl_trust_certificate_path?: string;
|
||||||
|
/** @deprecated use `ssl` instead */
|
||||||
open_ssl_verify_callback?: SSLVerifyCallback;
|
open_ssl_verify_callback?: SSLVerifyCallback;
|
||||||
|
ssl?: SSLConfiguration;
|
||||||
error?: ErrorCallback;
|
error?: ErrorCallback;
|
||||||
partial?: boolean;
|
partial?: boolean;
|
||||||
_disablePartialSyncUrlChecks?:boolean;
|
_disablePartialSyncUrlChecks?:boolean;
|
||||||
@ -496,7 +506,8 @@ declare namespace Realm.Sync {
|
|||||||
server_url: string,
|
server_url: string,
|
||||||
admin_user: User,
|
admin_user: User,
|
||||||
regex: string,
|
regex: string,
|
||||||
change_callback: Function
|
change_callback: Function,
|
||||||
|
ssl?: SSLConfiguration
|
||||||
)
|
)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -716,6 +716,7 @@ public:
|
|||||||
// private
|
// private
|
||||||
static std::function<SyncBindSessionHandler> session_bind_callback(ContextType ctx, ObjectType sync_constructor);
|
static std::function<SyncBindSessionHandler> session_bind_callback(ContextType ctx, ObjectType sync_constructor);
|
||||||
static void populate_sync_config(ContextType, ObjectType realm_constructor, ObjectType config_object, Realm::Config&);
|
static void populate_sync_config(ContextType, ObjectType realm_constructor, ObjectType config_object, Realm::Config&);
|
||||||
|
static void populate_sync_config_for_ssl(ContextType, ObjectType config_object, SyncConfig&);
|
||||||
|
|
||||||
// static properties
|
// static properties
|
||||||
static void get_is_developer_edition(ContextType, ObjectType, ReturnValue &);
|
static void get_is_developer_edition(ContextType, ObjectType, ReturnValue &);
|
||||||
@ -831,33 +832,7 @@ void SyncClass<T>::populate_sync_config(ContextType ctx, ObjectType realm_constr
|
|||||||
SSLVerifyCallbackSyncThreadFunctor<T> ssl_verify_functor {ctx, Value::validated_to_function(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);
|
ssl_verify_callback = std::move(ssl_verify_functor);
|
||||||
}
|
}
|
||||||
#if REALM_ANDROID
|
|
||||||
// For React Native Android, if the user didn't define the ssl_verify_callback, we provide a default
|
|
||||||
// implementation for him, otherwise all SSL validation will fail, since the Sync client doesn't have
|
|
||||||
// access to the Android Keystore.
|
|
||||||
// This default implementation will perform a JNI call to invoke a Java method defined at the `SSLHelper`
|
|
||||||
// to perform the certificate verification.
|
|
||||||
else {
|
|
||||||
auto ssl_verify_functor =
|
|
||||||
[](const std::string server_address, realm::sync::Session::port_type server_port,
|
|
||||||
const char* pem_data, size_t pem_size, int preverify_ok, int depth) {
|
|
||||||
JNIEnv* env = realm::jni_util::JniUtils::get_env(true);
|
|
||||||
static jmethodID java_certificate_verifier = env->GetStaticMethodID(ssl_helper_class, "certificateVerifier", "(Ljava/lang/String;Ljava/lang/String;I)Z");
|
|
||||||
jstring jserver_address = env->NewStringUTF(server_address.c_str());
|
|
||||||
// deep copy the pem_data into a string so DeleteLocalRef delete the local reference not the original const char
|
|
||||||
std::string pem(pem_data, pem_size);
|
|
||||||
jstring jpem = env->NewStringUTF(pem.c_str());
|
|
||||||
|
|
||||||
bool isValid = env->CallStaticBooleanMethod(ssl_helper_class, java_certificate_verifier,
|
|
||||||
jserver_address,
|
|
||||||
jpem, depth) == JNI_TRUE;
|
|
||||||
env->DeleteLocalRef(jserver_address);
|
|
||||||
env->DeleteLocalRef(jpem);
|
|
||||||
return isValid;
|
|
||||||
};
|
|
||||||
ssl_verify_callback = std::move(ssl_verify_functor);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
bool is_partial = false;
|
bool is_partial = false;
|
||||||
ValueType partial_value = Object::get_property(ctx, sync_config_object, "partial");
|
ValueType partial_value = Object::get_property(ctx, sync_config_object, "partial");
|
||||||
if (!Value::is_undefined(ctx, partial_value)) {
|
if (!Value::is_undefined(ctx, partial_value)) {
|
||||||
@ -879,10 +854,18 @@ void SyncClass<T>::populate_sync_config(ContextType ctx, ObjectType realm_constr
|
|||||||
}
|
}
|
||||||
config.sync_config->bind_session_handler = std::move(bind);
|
config.sync_config->bind_session_handler = std::move(bind);
|
||||||
config.sync_config->error_handler = std::move(error_handler);
|
config.sync_config->error_handler = std::move(error_handler);
|
||||||
|
config.sync_config->is_partial = is_partial;
|
||||||
|
|
||||||
|
// TODO: remove
|
||||||
config.sync_config->client_validate_ssl = client_validate_ssl;
|
config.sync_config->client_validate_ssl = client_validate_ssl;
|
||||||
config.sync_config->ssl_trust_certificate_path = ssl_trust_certificate_path;
|
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->ssl_verify_callback = std::move(ssl_verify_callback);
|
||||||
config.sync_config->is_partial = is_partial;
|
|
||||||
|
ValueType ssl_config_value = Object::get_property(ctx, sync_config_object, "ssl");
|
||||||
|
if (Value::is_object(ctx, ssl_config_value)) {
|
||||||
|
auto ssl_config_object = Value::to_object(ctx, ssl_config_value);
|
||||||
|
populate_sync_config_for_ssl(ctx, ssl_config_object, *config.sync_config);
|
||||||
|
}
|
||||||
|
|
||||||
config.schema_mode = SchemaMode::Additive;
|
config.schema_mode = SchemaMode::Additive;
|
||||||
config.path = syncManagerShared().path_for_realm(*shared_user, config.sync_config->realm_url());
|
config.path = syncManagerShared().path_for_realm(*shared_user, config.sync_config->realm_url());
|
||||||
@ -891,7 +874,55 @@ void SyncClass<T>::populate_sync_config(ContextType ctx, ObjectType realm_constr
|
|||||||
config.sync_config->realm_encryption_key = std::array<char, 64>();
|
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());
|
std::copy_n(config.encryption_key.begin(), config.sync_config->realm_encryption_key->size(), config.sync_config->realm_encryption_key->begin());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if REALM_ANDROID
|
||||||
|
// For React Native Android, if the user didn't define the ssl_verify_callback, we provide a default
|
||||||
|
// implementation for him, otherwise all SSL validation will fail, since the Sync client doesn't have
|
||||||
|
// access to the Android Keystore.
|
||||||
|
// This default implementation will perform a JNI call to invoke a Java method defined at the `SSLHelper`
|
||||||
|
// to perform the certificate verification.
|
||||||
|
if (!config.sync_config->ssl_verify_callback) {
|
||||||
|
auto ssl_verify_functor =
|
||||||
|
[](const std::string server_address, realm::sync::Session::port_type server_port,
|
||||||
|
const char* pem_data, size_t pem_size, int preverify_ok, int depth) {
|
||||||
|
JNIEnv* env = realm::jni_util::JniUtils::get_env(true);
|
||||||
|
static jmethodID java_certificate_verifier = env->GetStaticMethodID(ssl_helper_class, "certificateVerifier", "(Ljava/lang/String;Ljava/lang/String;I)Z");
|
||||||
|
jstring jserver_address = env->NewStringUTF(server_address.c_str());
|
||||||
|
// deep copy the pem_data into a string so DeleteLocalRef delete the local reference not the original const char
|
||||||
|
std::string pem(pem_data, pem_size);
|
||||||
|
jstring jpem = env->NewStringUTF(pem.c_str());
|
||||||
|
|
||||||
|
bool isValid = env->CallStaticBooleanMethod(ssl_helper_class, java_certificate_verifier,
|
||||||
|
jserver_address,
|
||||||
|
jpem, depth) == JNI_TRUE;
|
||||||
|
env->DeleteLocalRef(jserver_address);
|
||||||
|
env->DeleteLocalRef(jpem);
|
||||||
|
return isValid;
|
||||||
|
};
|
||||||
|
config.sync_config->ssl_verify_callback = std::move(ssl_verify_functor);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
void SyncClass<T>::populate_sync_config_for_ssl(ContextType ctx, ObjectType config_object, SyncConfig& config)
|
||||||
|
{
|
||||||
|
ValueType validate_ssl = Object::get_property(ctx, config_object, "validate");
|
||||||
|
if (Value::is_boolean(ctx, validate_ssl)) {
|
||||||
|
config.client_validate_ssl = Value::to_boolean(ctx, validate_ssl);
|
||||||
|
}
|
||||||
|
|
||||||
|
ValueType certificate_path = Object::get_property(ctx, config_object, "certificatePath");
|
||||||
|
if (Value::is_string(ctx, certificate_path)) {
|
||||||
|
config.ssl_trust_certificate_path = std::string(Value::to_string(ctx, certificate_path));
|
||||||
|
}
|
||||||
|
|
||||||
|
ValueType validate_callback = Object::get_property(ctx, config_object, "validateCallback");
|
||||||
|
if (Value::is_function(ctx, validate_callback)) {
|
||||||
|
config.ssl_verify_callback = SSLVerifyCallbackSyncThreadFunctor<T> { ctx, Value::to_function(ctx, validate_callback) };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} // js
|
} // js
|
||||||
} // realm
|
} // realm
|
||||||
|
Loading…
x
Reference in New Issue
Block a user