diff --git a/CHANGELOG.md b/CHANGELOG.md
index b04281cc..ff9119c8 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,10 @@
+NEXT RELEASE
+=============================================================
+
+### Enhancements
+* Add a callback function used to verify SSL certificates in the sync config.
+
+
2.0.0-rc10 Release notes (2017-9-19)
=============================================================
### Breaking changes
diff --git a/docs/realm.js b/docs/realm.js
index 63c14ad5..e379d1c7 100644
--- a/docs/realm.js
+++ b/docs/realm.js
@@ -92,17 +92,17 @@ class Realm {
constructor(config) {}
/**
- * Open a realm asynchronously with a promise. If the realm is synced, it will be fully
+ * Open a Realm asynchronously with a promise. If the Realm is synced, it will be fully
* synchronized before it is available.
- * @param {Realm~Configuration} config
+ * @param {Realm~Configuration} config
* @returns {ProgressPromise} - a promise that will be resolved with the realm instance when it's available.
*/
static open(config) {}
/**
- * Open a realm asynchronously with a callback. If the realm is synced, it will be fully
+ * Open a Realm asynchronously with a callback. If the Realm is synced, it will be fully
* synchronized before it is available.
- * @param {Realm~Configuration} config
+ * @param {Realm~Configuration} config
* @param {callback(error, realm)} - will be called when the realm is ready.
* @param {callback(transferred, transferable)} [progressCallback] - an optional callback for download progress notifications
* @throws {Error} If anything in the provided `config` is invalid.
@@ -217,7 +217,7 @@ class Realm {
/*
* Replaces all string columns in this Realm with a string enumeration column and compacts the
* database file.
- *
+ *
* Cannot be called from a write transaction.
*
* Compaction will not occur if other `Realm` instances exist.
@@ -269,12 +269,12 @@ Realm.defaultPath;
* This function takes two arguments:
* - `oldRealm` - The Realm before migration is performed.
* - `newRealm` - The Realm that uses the latest `schema`, which should be modified as necessary.
- * @property {callback(number, number)} [shouldCompactOnLaunch] - The function called when opening
- * a Realm for the first time during the life of a process to determine if it should be compacted
+ * @property {callback(number, number)} [shouldCompactOnLaunch] - The function called when opening
+ * a Realm for the first time during the life of a process to determine if it should be compacted
* before being returned to the user. The function takes two arguments:
- * - `totalSize` - The total file size (data + free space)
+ * - `totalSize` - The total file size (data + free space)
* - `unusedSize` - The total bytes used by data in the file.
- * It returns `true` to indicate that an attempt to compact the file should be made. The compaction
+ * It returns `true` to indicate that an attempt to compact the file should be made. The compaction
* will be skipped if another process is accessing it.
* @property {string} [path={@link Realm.defaultPath}] - The path to the file where the
* Realm database should be stored.
@@ -288,14 +288,49 @@ Realm.defaultPath;
* object types in this Realm. **Required** when first creating a Realm at this `path`.
* @property {number} [schemaVersion] - **Required** (and must be incremented) after
* changing the `schema`.
- * @property {Object} [sync] - Sync configuration parameters with the following
+ * @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
+ * - `error` - A callback function which is called in error situations.
+ * The `error` callback can take up to four optional arguments: `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
- * The `error` callback can take up to four optional arguments: `message`, `isFatal`, `category`, and `code`.
+ * - `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
+ *
+ * {
+ * serverAddress: String,
+ * serverPort: Number,
+ * pemCertificate: String,
+ * acceptedByOpenSSL: Boolean,
+ * depth: Number
+ * }
+ *
+ * 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.
+ *
*/
/**
@@ -329,7 +364,7 @@ Realm.defaultPath;
* otherwise specified.
* @property {boolean} [optional] - Signals if this property may be assigned `null` or `undefined`.
* @property {boolean} [indexed] - Signals if this property should be indexed. Only supported for
- * `"string"`, `"int"`, and `"bool"` properties.
+ * `"string"`, `"int"`, and `"bool"` properties.
*/
/**
diff --git a/lib/index.d.ts b/lib/index.d.ts
index 941fe647..76106fe4 100644
--- a/lib/index.d.ts
+++ b/lib/index.d.ts
@@ -265,25 +265,25 @@ declare namespace Realm.Sync {
readonly server: string;
readonly token: string;
static adminUser(adminToken: string, server?: string): User;
-
+
/**
* @deprecated, to be removed in future versions
*/
static login(server: string, username: string, password: string, callback: (error: any, user: User) => void): void;
static login(server: string, username: string, password: string): Promise;
-
+
/**
* @deprecated, to be removed in future versions
- */
+ */
static register(server: string, username: string, password: string, callback: (error: any, user: User) => void): void;
static register(server: string, username: string, password: string): Promise;
-
+
/**
* @deprecated, to be removed in versions
- */
+ */
static registerWithProvider(server: string, options: { provider: string, providerToken: string, userInfo: any }, callback: (error: Error | null, user: User | null) => void): void;
static registerWithProvider(server: string, options: { provider: string, providerToken: string, userInfo: any }): Promise;
-
+
logout(): void;
openManagementRealm(): Realm;
retrieveAccount(provider: string, username: string): Promise;
@@ -299,7 +299,7 @@ declare namespace Realm.Sync {
userId: string |
{ metadataKey: string, metadataValue: string }
};
-
+
type AccessLevel = 'none' | 'read' | 'write' | 'admin';
class Permission {
@@ -310,7 +310,7 @@ declare namespace Realm.Sync {
readonly mayRead?: boolean;
readonly mayWrite?: boolean;
readonly mayManage?: boolean;
- }
+ }
class PermissionChange {
id: string;
@@ -342,19 +342,21 @@ declare namespace Realm.Sync {
}
type ErrorCallback = (message?: string, isFatal?: boolean, category?: string, code?: number) => void;
+ type SSLVerifyCallback = (serverAddress: string, serverPort: number, pemCertificate: string, preverifyOk: number, depth: number) => boolean;
interface SyncConfiguration {
user: User;
url: string;
validate_ssl?: boolean;
ssl_trust_certificate_path?: string;
+ ssl_verify_callback?: SSLVerifyCallback;
error?: ErrorCallback;
}
type ProgressNotificationCallback = (transferred: number, transferable: number) => void;
type ProgressDirection = 'download' | 'upload';
type ProgressMode = 'reportIndefinitely' | 'forCurrentlyOutstandingWork';
-
+
/**
* Session
* @see { @link https://realm.io/docs/javascript/latest/api/Realm.Sync.Session.html }
@@ -394,7 +396,7 @@ declare namespace Realm.Sync {
function removeListener(regex: string, name: string, changeCallback: (changeEvent: ChangeEvent) => void): void;
function setLogLevel(logLevel: 'all' | 'trace' | 'debug' | 'detail' | 'info' | 'warn' | 'error' | 'fatal' | 'off'): void;
function setFeatureToken(token: string): void;
-
+
/**
* @deprecated, to be removed in 2.0
*/
@@ -458,19 +460,19 @@ declare class Realm {
*/
static schemaVersion(path: string, encryptionKey?: ArrayBuffer | ArrayBufferView): number;
-
+
/**
* Open a realm asynchronously with a promise. If the realm is synced, it will be fully synchronized before it is available.
- * @param {Configuration} config
+ * @param {Configuration} config
*/
static open(config: Realm.Configuration): ProgressPromise;
/**
* @deprecated in favor of `Realm.open`
* Open a realm asynchronously with a callback. If the realm is synced, it will be fully synchronized before it is available.
- * @param {Configuration} config
+ * @param {Configuration} config
* @param {Function} callback will be called when the realm is ready.
- * @param {ProgressNotificationCallback} progressCallback? a progress notification callback for 'download' direction and 'forCurrentlyOutstandingWork' mode
+ * @param {ProgressNotificationCallback} progressCallback? a progress notification callback for 'download' direction and 'forCurrentlyOutstandingWork' mode
*/
static openAsync(config: Realm.Configuration, callback: (error: any, realm: Realm) => void, progressCallback?: Realm.Sync.ProgressNotificationCallback): void
diff --git a/src/js_sync.hpp b/src/js_sync.hpp
index 0ff96000..b364b81b 100644
--- a/src/js_sync.hpp
+++ b/src/js_sync.hpp
@@ -22,6 +22,8 @@
#include