mirror of
https://github.com/status-im/realm-js.git
synced 2025-01-11 23:04:29 +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
|
||||
* Sync protocol: 24
|
||||
@ -10,10 +10,12 @@ X.Y.Z Release notes ()
|
||||
* None.
|
||||
|
||||
### 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.
|
||||
|
||||
### Bug fixes
|
||||
* None
|
||||
* None.
|
||||
|
||||
### Internal
|
||||
* None.
|
||||
|
@ -347,52 +347,7 @@ Realm.defaultPath;
|
||||
* If omitted, the schema will be read from the existing Realm file.
|
||||
* @property {number} [schemaVersion] - **Required** (and must be incremented) after
|
||||
* changing the `schema`.
|
||||
* @property {Object} [sync] - Sync configuration parameters with the following
|
||||
* 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.**
|
||||
* @property {Realm.Sync~SyncConfiguration} [sync] - Sync configuration parameters.
|
||||
*/
|
||||
|
||||
/**
|
||||
|
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
|
||||
* migrated to the v2.x format. In case this migration
|
||||
@ -658,8 +720,9 @@ class Adapter {
|
||||
* use `.*` to match all all Realms
|
||||
* @param {function(realmPath)} changeCallback - called when a new transaction is available
|
||||
* 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.
|
||||
|
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 SSLVerifyCallback = (sslVerifyObject: SSLVerifyObject) => boolean;
|
||||
|
||||
interface SSLConfiguration {
|
||||
validate?: boolean;
|
||||
certificatePath?: string;
|
||||
validateCallback?: SSLVerifyCallback;
|
||||
}
|
||||
|
||||
interface SyncConfiguration {
|
||||
user: User;
|
||||
url: string;
|
||||
/** @deprecated use `ssl` instead */
|
||||
validate_ssl?: boolean;
|
||||
/** @deprecated use `ssl` instead */
|
||||
ssl_trust_certificate_path?: string;
|
||||
/** @deprecated use `ssl` instead */
|
||||
open_ssl_verify_callback?: SSLVerifyCallback;
|
||||
ssl?: SSLConfiguration;
|
||||
error?: ErrorCallback;
|
||||
partial?: boolean;
|
||||
_disablePartialSyncUrlChecks?:boolean;
|
||||
@ -496,7 +506,8 @@ declare namespace Realm.Sync {
|
||||
server_url: string,
|
||||
admin_user: User,
|
||||
regex: string,
|
||||
change_callback: Function
|
||||
change_callback: Function,
|
||||
ssl?: SSLConfiguration
|
||||
)
|
||||
|
||||
/**
|
||||
|
@ -716,6 +716,7 @@ public:
|
||||
// private
|
||||
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_for_ssl(ContextType, ObjectType config_object, SyncConfig&);
|
||||
|
||||
// static properties
|
||||
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)};
|
||||
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;
|
||||
ValueType partial_value = Object::get_property(ctx, sync_config_object, "partial");
|
||||
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->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->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;
|
||||
|
||||
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.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>();
|
||||
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
|
||||
} // realm
|
||||
|
Loading…
x
Reference in New Issue
Block a user