mirror of
https://github.com/status-im/realm-js.git
synced 2025-01-11 14:54:33 +00:00
Merge branch 'master' of github.com:realm/realm-js into kneth/fix-ts-for-open_ssl_verify_callback
This commit is contained in:
commit
dad5b3f521
109
CHANGELOG.md
109
CHANGELOG.md
@ -1,5 +1,11 @@
|
||||
X.Y.Z Release notes
|
||||
=============================================================
|
||||
### Compatibility
|
||||
* Sync protocol: 24
|
||||
* Server-side history format: 4
|
||||
* File format: 7
|
||||
* Realm Object Server: 3.0.0 or later
|
||||
|
||||
### Breaking changes
|
||||
* None.
|
||||
|
||||
@ -7,12 +13,37 @@ X.Y.Z Release notes
|
||||
* None.
|
||||
|
||||
### Bug fixes
|
||||
* Fixed logout error due to fetch body not being stringified (#1731).
|
||||
* Fixed from TypeScript defintion for `open_ssl_verify_callback` configuration option (#1652).
|
||||
|
||||
### Internal
|
||||
* Updated to Relm Sync 3.3.0.
|
||||
* Updated to Realm Core 5.6.0.
|
||||
|
||||
|
||||
2.4.0 Release notes (2018-4-26)
|
||||
=============================================================
|
||||
### Compatibility
|
||||
* Sync protocol: 24
|
||||
* Server-side history format: 4
|
||||
* File format: 7
|
||||
* Realm Object Server: 3.0.0 or later
|
||||
|
||||
### Breaking changes
|
||||
* None.
|
||||
|
||||
### Enhancements
|
||||
* Added password reset wrappers (#1699).
|
||||
* Added a certificate validation using Android Keystore for RN (#1761).
|
||||
|
||||
### Bug fixes
|
||||
* Fixed logout error due to fetch body not being stringified (#1731).
|
||||
* Added `Subscription` import to `browser/index.js` and register type converter (#1711).
|
||||
* Fixed call to `logout()` when debugging React Native apps (#1744).
|
||||
|
||||
### Internal
|
||||
* Updated `scripts/test.sh` so it doesn't hang forever when the React tests fail to start (#1764).
|
||||
|
||||
|
||||
2.3.4 Release notes (2018-4-12)
|
||||
=============================================================
|
||||
### Compatibility
|
||||
@ -132,6 +163,82 @@ X.Y.Z Release notes
|
||||
* Tested against Realm Object Server 3.0.0-alpha.8.
|
||||
* Added `_disablePartialSyncUrlChecks` to `Realm.Configuration`.
|
||||
|
||||
|
||||
2.2.20 Release notes (2018-4-13)
|
||||
=============================================================
|
||||
### Breaking changes
|
||||
* None.
|
||||
|
||||
### Enhancements
|
||||
* None.
|
||||
|
||||
### Bug fixes
|
||||
* None.
|
||||
|
||||
### Internal
|
||||
* Updated to Realm Sync 2.2.17
|
||||
|
||||
|
||||
2.2.19 Release notes (2018-4-10)
|
||||
=============================================================
|
||||
### Breaking changes
|
||||
* None.
|
||||
|
||||
### Enhancements
|
||||
* None.
|
||||
|
||||
### Bug fixes
|
||||
* Avoid crashing if partial Realms find their way into the admin Realm when using Realm Object Server v3.0.0 or later (realm-js-private #430).
|
||||
|
||||
### Internal
|
||||
* None.
|
||||
|
||||
|
||||
2.2.18 Release notes (2018-3-23)
|
||||
=============================================================
|
||||
### Breaking changes
|
||||
* None.
|
||||
|
||||
### Enhancements
|
||||
* None.
|
||||
|
||||
### Bug fixes
|
||||
* Fixed a bug where leaking Realms when an error occurs within an event handler (#1725).
|
||||
|
||||
### Internal
|
||||
* Added trace logging to the global notifier (realm-js-private #426).
|
||||
|
||||
|
||||
2.2.17 Release notes (2018-3-21)
|
||||
=============================================================
|
||||
### Breaking changes
|
||||
* None.
|
||||
|
||||
### Enhancements
|
||||
* None.
|
||||
|
||||
### Bug fixes
|
||||
* [Sync] Do a better job of not opening the notifier pipe file for global notifier realms.
|
||||
|
||||
### Internal
|
||||
* None.
|
||||
|
||||
|
||||
2.2.16 Release notes (2018-3-16)
|
||||
=============================================================
|
||||
### Breaking changes
|
||||
* None.
|
||||
|
||||
### Enhancements
|
||||
* None.
|
||||
|
||||
### Bug fixes
|
||||
* [Sync] Avoid hammering the ROS authentication service when large numbers of Realms are opened at once.
|
||||
|
||||
### Internal
|
||||
* None.
|
||||
|
||||
|
||||
2.2.15 Release notes (2018-3-9)
|
||||
=============================================================
|
||||
### Breaking changes
|
||||
|
@ -1,5 +1,6 @@
|
||||
PACKAGE_NAME=realm-js
|
||||
VERSION=2.3.4
|
||||
REALM_CORE_VERSION=5.4.0
|
||||
REALM_SYNC_VERSION=3.0.0
|
||||
VERSION=2.4.0
|
||||
REALM_CORE_VERSION=5.6.0
|
||||
REALM_SYNC_VERSION=3.3.0
|
||||
REALM_OBJECT_SERVER_VERSION=3.0.0
|
||||
|
||||
|
51
docs/sync.js
51
docs/sync.js
@ -270,6 +270,55 @@ class User {
|
||||
*/
|
||||
static register(server, username, password, callback) {}
|
||||
|
||||
/**
|
||||
* Request a password reset email to be sent to a user's email.
|
||||
* This will not throw an exception, even if the email doesn't belong to a Realm Object Server user.
|
||||
*
|
||||
* This can only be used for users who authenticated with the 'password' provider, and passed a valid email address as a username.
|
||||
*
|
||||
* @param {string} server - authentication server
|
||||
* @param {string} email - The email that corresponds to the user's username.
|
||||
* @return {Promise<void>} A promise which is resolved when the request has been sent.
|
||||
*/
|
||||
static requestPasswordReset(server, email) {}
|
||||
|
||||
/**
|
||||
* Complete the password reset flow by using the reset token sent to the user's email as a one-time authorization token to change the password.
|
||||
*
|
||||
* By default, Realm Object Server will send a link to the user's email that will redirect to a webpage where they can enter their new password.
|
||||
* If you wish to provide a native UX, you may wish to modify the password authentication provider to use a custom URL with deep linking, so you can
|
||||
* open the app, extract the token, and navigate to a view that allows to change the password within the app.
|
||||
*
|
||||
* @param {string} server - authentication server
|
||||
* @param {string} reset_token - The token that was sent to the user's email address.
|
||||
* @param {string} new_password - The user's new password.
|
||||
* @return {Promise<void>} A promise which is resolved when the request has been sent.
|
||||
*/
|
||||
static completePasswordReset(server, reset_token, new_password) {}
|
||||
|
||||
/**
|
||||
* Request an email confirmation email to be sent to a user's email.
|
||||
* This will not throw an exception, even if the email doesn't belong to a Realm Object Server user.
|
||||
*
|
||||
* @param {string} server - authentication server
|
||||
* @param {string} email - The email that corresponds to the user's username.
|
||||
* @return {Promise<void>} A promise which is resolved when the request has been sent.
|
||||
*/
|
||||
static requestEmailConfirmation(server, email) {}
|
||||
|
||||
/**
|
||||
* Complete the email confirmation flow by using the confirmation token sent to the user's email as a one-time authorization token to confirm their email.
|
||||
*
|
||||
* By default, Realm Object Server will send a link to the user's email that will redirect to a webpage where they can enter their new password.
|
||||
* If you wish to provide a native UX, you may wish to modify the password authentication provider to use a custom URL with deep linking, so you can
|
||||
* open the app, extract the token, and navigate to a view that allows to confirm the email within the app.
|
||||
*
|
||||
* @param {string} server - authentication server
|
||||
* @param {string} confirmation_token - The token that was sent to the user's email address.
|
||||
* @return {Promise<void>} A promise which is resolved when the request has been sent.
|
||||
*/
|
||||
static confirmEmail(server, confirmation_token) {}
|
||||
|
||||
/**
|
||||
* Create an admin user for the given authentication server with an existing token
|
||||
* @param {string} adminToken - existing admin token
|
||||
@ -591,7 +640,7 @@ class Worker {
|
||||
*/
|
||||
class Adapter {
|
||||
/**
|
||||
* Create a new Adapter to moitor and process changes made across multiple Realms
|
||||
* Create a new Adapter to monitor and process changes made across multiple Realms
|
||||
* @param {string} localPath - the local path where realm files are stored
|
||||
* @param {string} serverUrl - the sync server to listen to
|
||||
* @param {SyncUser} adminUser - an admin user obtained by calling `new Realm.Sync.User.adminUser`
|
||||
|
@ -42,7 +42,7 @@ export default class User {
|
||||
}
|
||||
|
||||
createMethods(User.prototype, objectTypes.USER, [
|
||||
'logout',
|
||||
'_logout',
|
||||
'_sessionForOnDiskPath'
|
||||
]);
|
||||
|
||||
|
8
lib/index.d.ts
vendored
8
lib/index.d.ts
vendored
@ -296,6 +296,14 @@ declare namespace Realm.Sync {
|
||||
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<Realm.Sync.User>;
|
||||
|
||||
static requestPasswordReset(server: string, email: string): Promise<void>;
|
||||
|
||||
static completePasswordReset(server:string, reset_token:string, new_password:string): Promise<void>;
|
||||
|
||||
static requestEmailConfirmation(server:string, email:string): Promise<void>;
|
||||
|
||||
static confirmEmail(server:string, confirmation_token:string): Promise<void>;
|
||||
|
||||
authenticate(server: string, provider: string, options: any): Promise<Realm.Sync.User>;
|
||||
logout(): void;
|
||||
openManagementRealm(): Realm;
|
||||
|
@ -25,7 +25,7 @@ function nodeRequire(module) {
|
||||
return require_method(module);
|
||||
}
|
||||
|
||||
const Realm = require('.');
|
||||
const Realm = nodeRequire('.');
|
||||
|
||||
let impl;
|
||||
process.on('message', (m) => {
|
||||
|
@ -85,7 +85,7 @@ class FunctionListener {
|
||||
throw e;
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
class OutOfProcListener {
|
||||
constructor(regex, regexStr, worker) {
|
||||
@ -120,7 +120,7 @@ class OutOfProcListener {
|
||||
}
|
||||
this.worker.onchange(changes);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
class Listener {
|
||||
constructor(Sync, server, user) {
|
||||
@ -231,7 +231,7 @@ class Listener {
|
||||
}
|
||||
this.initPromises = [];
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
let listener;
|
||||
function addListener(server, user, regex, event, callback) {
|
||||
|
@ -275,6 +275,32 @@ function _authenticate(userConstructor, server, json, callback) {
|
||||
}
|
||||
}
|
||||
|
||||
function _updateAccount(userConstructor, server, json) {
|
||||
const url = append_url(server, 'auth/password/updateAccount');
|
||||
const options = {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(json),
|
||||
headers: postHeaders,
|
||||
open_timeout: 5000
|
||||
};
|
||||
|
||||
return performFetch(url, options)
|
||||
.then((response) => {
|
||||
const contentType = response.headers.get('Content-Type');
|
||||
if (contentType.indexOf('application/json') === -1) {
|
||||
return response.text().then((body) => {
|
||||
throw new AuthError({
|
||||
title: `Could not update user account: Realm Object Server didn't respond with valid JSON`,
|
||||
body,
|
||||
});
|
||||
});
|
||||
}
|
||||
if (!response.ok) {
|
||||
return response.json().then((body) => Promise.reject(new AuthError(body)));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const staticMethods = {
|
||||
get current() {
|
||||
const allUsers = this.all;
|
||||
@ -379,6 +405,51 @@ const staticMethods = {
|
||||
return _authenticate(this, server, json)
|
||||
},
|
||||
|
||||
requestPasswordReset(server, email) {
|
||||
checkTypes(arguments, ['string', 'string']);
|
||||
const json = {
|
||||
provider_id: email,
|
||||
data: { action: 'reset_password' }
|
||||
};
|
||||
|
||||
return _updateAccount(this, server, json);
|
||||
},
|
||||
|
||||
completePasswordReset(server, reset_token, new_password) {
|
||||
checkTypes(arguments, ['string', 'string']);
|
||||
const json = {
|
||||
data: {
|
||||
action: 'complete_reset',
|
||||
token: reset_token,
|
||||
new_password: new_password
|
||||
}
|
||||
};
|
||||
|
||||
return _updateAccount(this, server, json);
|
||||
},
|
||||
|
||||
requestEmailConfirmation(server, email) {
|
||||
checkTypes(arguments, ['string', 'string']);
|
||||
const json = {
|
||||
provider_id: email,
|
||||
data: { action: 'request_email_confirmation' }
|
||||
};
|
||||
|
||||
return _updateAccount(this, server, json);
|
||||
},
|
||||
|
||||
confirmEmail(server, confirmation_token) {
|
||||
checkTypes(arguments, ['string', 'string']);
|
||||
const json = {
|
||||
data: {
|
||||
action: 'confirm_email',
|
||||
token: confirmation_token
|
||||
}
|
||||
};
|
||||
|
||||
return _updateAccount(this, server, json);
|
||||
},
|
||||
|
||||
_refreshAccessToken: refreshAccessToken,
|
||||
};
|
||||
|
||||
|
@ -123,6 +123,6 @@ class Worker {
|
||||
const message = this._workQueue.shift();
|
||||
worker.send(message);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = Worker;
|
||||
|
@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "realm",
|
||||
"description": "Realm is a mobile database: an alternative to SQLite and key-value stores",
|
||||
"version": "2.3.4",
|
||||
"version": "2.4.0",
|
||||
"license": "Apache-2.0",
|
||||
"homepage": "https://realm.io",
|
||||
"keywords": [
|
||||
|
@ -1,3 +1,19 @@
|
||||
/*
|
||||
* Copyright 2018 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.
|
||||
*/
|
||||
|
||||
package io.realm.react;
|
||||
|
||||
import android.content.res.AssetManager;
|
||||
|
@ -1,3 +1,19 @@
|
||||
/*
|
||||
* Copyright 2018 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.
|
||||
*/
|
||||
|
||||
package io.realm.react;
|
||||
|
||||
import com.facebook.react.ReactPackage;
|
||||
|
@ -0,0 +1,152 @@
|
||||
/*
|
||||
* Copyright 2018 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.
|
||||
*/
|
||||
|
||||
package io.realm.react.util;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.KeyStore;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.security.cert.CertificateFactory;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
||||
import javax.net.ssl.TrustManager;
|
||||
import javax.net.ssl.TrustManagerFactory;
|
||||
import javax.net.ssl.X509TrustManager;
|
||||
|
||||
import okhttp3.internal.tls.OkHostnameVerifier;
|
||||
|
||||
public class SSLHelper {
|
||||
private final static String TAG = "REALM SSLHelper";
|
||||
// Holds the certificate chain (per hostname). We need to keep the order of each certificate
|
||||
// according to it's depth in the chain. The depth of the last
|
||||
// certificate is 0. The depth of the first certificate is chain
|
||||
// length - 1.
|
||||
private static HashMap<String, List<String>> ROS_CERTIFICATES_CHAIN;
|
||||
|
||||
// The default Android Trust Manager which uses the default KeyStore to
|
||||
// validate the certificate chain.
|
||||
private static X509TrustManager TRUST_MANAGER;
|
||||
|
||||
// Help transform a String PEM representation of the certificate, into
|
||||
// X509Certificate format.
|
||||
private static CertificateFactory CERTIFICATE_FACTORY;
|
||||
|
||||
// From Sync implementation:
|
||||
// A recommended way of using the callback function is to return true
|
||||
// if preverify_ok = 1 and depth > 0,
|
||||
// always check the host name if depth = 0,
|
||||
// and use an independent verification step if preverify_ok = 0.
|
||||
//
|
||||
// Another possible way of using the callback is to collect all the
|
||||
// ROS_CERTIFICATES_CHAIN until depth = 0, and present the entire chain for
|
||||
// independent verification.
|
||||
//
|
||||
// In this implementation we use the second method, since it's more suitable for
|
||||
// the underlying Java API we need to call to validate the certificate chain.
|
||||
|
||||
public synchronized static boolean certificateVerifier(String serverAddress, String pemData, int depth) {
|
||||
try {
|
||||
if (ROS_CERTIFICATES_CHAIN == null) {
|
||||
ROS_CERTIFICATES_CHAIN = new HashMap<>();
|
||||
TRUST_MANAGER = systemDefaultTrustManager();
|
||||
CERTIFICATE_FACTORY = CertificateFactory.getInstance("X.509");
|
||||
}
|
||||
|
||||
if (!ROS_CERTIFICATES_CHAIN.containsKey(serverAddress)) {
|
||||
ROS_CERTIFICATES_CHAIN.put(serverAddress, new ArrayList<String>());
|
||||
}
|
||||
|
||||
ROS_CERTIFICATES_CHAIN.get(serverAddress).add(pemData);
|
||||
|
||||
if (depth == 0) {
|
||||
// transform all PEM ROS_CERTIFICATES_CHAIN into Java X509
|
||||
// with respecting the order/depth provided from Sync.
|
||||
List<String> pemChain = ROS_CERTIFICATES_CHAIN.get(serverAddress);
|
||||
int n = pemChain.size();
|
||||
X509Certificate[] chain = new X509Certificate[n];
|
||||
for (String pem : pemChain) {
|
||||
// The depth of the last certificate is 0.
|
||||
// The depth of the first certificate is chain length - 1.
|
||||
chain[--n] = buildCertificateFromPEM(pem);
|
||||
}
|
||||
|
||||
// verify the entire chain
|
||||
try {
|
||||
TRUST_MANAGER.checkServerTrusted(chain, "RSA");
|
||||
// verify the hostname
|
||||
boolean isValid = OkHostnameVerifier.INSTANCE.verify(serverAddress, chain[0]);
|
||||
if (isValid) {
|
||||
return true;
|
||||
} else {
|
||||
Log.e(TAG, "Can not verify the hostname for the host: " + serverAddress);
|
||||
return false;
|
||||
}
|
||||
} catch (CertificateException e) {
|
||||
Log.e(TAG, "Can not validate SSL chain certificate for the host: " + serverAddress, e);
|
||||
return false;
|
||||
} finally {
|
||||
// don't keep the certificate chain in memory
|
||||
ROS_CERTIFICATES_CHAIN.remove(serverAddress);
|
||||
}
|
||||
} else {
|
||||
// return true, since the verification will happen for the entire chain
|
||||
// when receiving the depth == 0 (host certificate)
|
||||
return true;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Error during certificate validation for host: " + serverAddress, e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Credit OkHttp https://github.com/square/okhttp/blob/e5c84e1aef9572adb493197c1b6c4e882aca085b/okhttp/src/main/java/okhttp3/OkHttpClient.java#L270
|
||||
private static X509TrustManager systemDefaultTrustManager() {
|
||||
try {
|
||||
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(
|
||||
TrustManagerFactory.getDefaultAlgorithm());
|
||||
trustManagerFactory.init((KeyStore) null);
|
||||
TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();
|
||||
if (trustManagers.length != 1 || !(trustManagers[0] instanceof X509TrustManager)) {
|
||||
throw new IllegalStateException("Unexpected default trust managers:"
|
||||
+ Arrays.toString(trustManagers));
|
||||
}
|
||||
return (X509TrustManager) trustManagers[0];
|
||||
} catch (GeneralSecurityException e) {
|
||||
throw new AssertionError(); // The system has no TLS. Just give up.
|
||||
}
|
||||
}
|
||||
|
||||
private static X509Certificate buildCertificateFromPEM(String pem) throws IOException, CertificateException {
|
||||
InputStream stream = null;
|
||||
try {
|
||||
stream = new ByteArrayInputStream(pem.getBytes("UTF-8"));
|
||||
return (X509Certificate) CERTIFICATE_FACTORY.generateCertificate(stream);
|
||||
} finally {
|
||||
if (stream != null) {
|
||||
stream.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -45,6 +45,7 @@ LOCAL_SRC_FILES += src/rpc.cpp
|
||||
LOCAL_SRC_FILES += src/jsc/jsc_init.cpp
|
||||
LOCAL_SRC_FILES += src/jsc/jsc_value.cpp
|
||||
LOCAL_SRC_FILES += src/android/io_realm_react_RealmReactModule.cpp
|
||||
LOCAL_SRC_FILES += src/android/jni_utils.cpp
|
||||
LOCAL_SRC_FILES += src/android/jsc_override.cpp
|
||||
LOCAL_SRC_FILES += src/android/platform.cpp
|
||||
LOCAL_SRC_FILES += src/object-store/src/impl/collection_change_builder.cpp
|
||||
|
@ -200,13 +200,7 @@
|
||||
"type": "none",
|
||||
"dependencies": [ "realm-core" ],
|
||||
"link_settings": {
|
||||
"conditions": [
|
||||
["OS=='win'", {
|
||||
"libraries": [ "-lrealm-sync<(debug_library_suffix)" ]
|
||||
}, {
|
||||
"libraries": [ "-lrealm-sync-node<(debug_library_suffix)" ]
|
||||
}]
|
||||
]
|
||||
"libraries": [ "-lrealm-sync<(debug_library_suffix)" ],
|
||||
},
|
||||
"all_dependent_settings": {
|
||||
"defines": [ "REALM_ENABLE_SYNC=1" ]
|
||||
@ -259,6 +253,7 @@
|
||||
"include_dirs": [ "<(vendor_dir)/include" ],
|
||||
"library_dirs": [
|
||||
"<(vendor_dir)/lib",
|
||||
"<(vendor_dir)/lib64",
|
||||
"<(vendor_dir)/osx"
|
||||
]
|
||||
},
|
||||
|
@ -147,7 +147,17 @@ xctest() {
|
||||
|
||||
|
||||
echo "Launching application. (output is in $(pwd)/build/out.txt)"
|
||||
xcrun simctl launch --console ${SIM_DEVICE_NAME} io.realm.$1 | tee $(pwd)/build/out.txt
|
||||
testpid=$(xcrun simctl launch --stdout=$(pwd)/build/out.txt --stderr=$(pwd)/build/err.txt ${SIM_DEVICE_NAME} io.realm.$1 | grep -m1 -o '\d\+$')
|
||||
tail -n +0 -f $(pwd)/build/out.txt &
|
||||
stdoutpid=$!
|
||||
tail -n +0 -f $(pwd)/build/err.txt &
|
||||
stderrpid=$!
|
||||
|
||||
# `kill -0` checks if a signal can be sent to the pid without actually doing so
|
||||
while kill -0 $testpid 2> /dev/null; do sleep 1; done
|
||||
|
||||
kill $stdoutpid
|
||||
kill $stderrpid
|
||||
|
||||
echo "Shuttting down ${SIM_DEVICE_NAME} simulator. (device is not deleted. you can use it to debug the app)"
|
||||
shutdown_ios_simulator
|
||||
@ -171,7 +181,7 @@ setup_ios_simulator() {
|
||||
delete_ios_simulator >/dev/null 2>&1
|
||||
|
||||
#parse devices
|
||||
IOS_RUNTIME=$(xcrun simctl list runtimes | grep -m1 -o 'com.apple.CoreSimulator.SimRuntime.iOS.*' | sed 's/[()]//g')
|
||||
IOS_RUNTIME=$(xcrun simctl list runtimes | grep -v unavailable | grep -m1 -o 'com.apple.CoreSimulator.SimRuntime.iOS.*' | sed 's/[()]//g')
|
||||
echo using iOS Runtime ${IOS_RUNTIME} to create new simulator ${SIM_DEVICE_NAME}
|
||||
|
||||
#create new test simulator
|
||||
|
@ -969,7 +969,7 @@
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||
COPY_PHASE_STRIP = NO;
|
||||
CURRENT_PROJECT_VERSION = 2.3.4;
|
||||
CURRENT_PROJECT_VERSION = 2.4.0;
|
||||
CXX = "$(SRCROOT)/../scripts/ccache-clang++.sh";
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
@ -1033,7 +1033,7 @@
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||
COPY_PHASE_STRIP = NO;
|
||||
CURRENT_PROJECT_VERSION = 2.3.4;
|
||||
CURRENT_PROJECT_VERSION = 2.4.0;
|
||||
CXX = "$(SRCROOT)/../scripts/ccache-clang++.sh";
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
ENABLE_NS_ASSERTIONS = NO;
|
||||
|
@ -23,17 +23,49 @@
|
||||
#include "io_realm_react_RealmReactModule.h"
|
||||
#include "rpc.hpp"
|
||||
#include "platform.hpp"
|
||||
#include "jni_utils.hpp"
|
||||
|
||||
using namespace realm::rpc;
|
||||
using namespace realm::jni_util;
|
||||
|
||||
static RPCServer *s_rpc_server;
|
||||
extern bool realmContextInjected;
|
||||
jclass ssl_helper_class;
|
||||
|
||||
namespace realm {
|
||||
// set the AssetManager used to access bundled files within the APK
|
||||
void set_asset_manager(AAssetManager* assetManager);
|
||||
}
|
||||
|
||||
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void*)
|
||||
{
|
||||
JNIEnv* env;
|
||||
if (vm->GetEnv((void**)&env, JNI_VERSION_1_6) != JNI_OK) {
|
||||
return JNI_ERR;
|
||||
}
|
||||
else {
|
||||
JniUtils::initialize(vm, JNI_VERSION_1_6);
|
||||
}
|
||||
|
||||
// We do lookup the class in this Thread, since FindClass sometimes fails
|
||||
// when issued from the sync client thread
|
||||
ssl_helper_class = reinterpret_cast<jclass>(env->NewGlobalRef(env->FindClass("io/realm/react/util/SSLHelper")));
|
||||
|
||||
return JNI_VERSION_1_6;
|
||||
}
|
||||
|
||||
JNIEXPORT void JNI_OnUnload(JavaVM* vm, void*)
|
||||
{
|
||||
JNIEnv* env;
|
||||
if (vm->GetEnv((void**)&env, JNI_VERSION_1_6) != JNI_OK) {
|
||||
return;
|
||||
}
|
||||
else {
|
||||
env->DeleteLocalRef(ssl_helper_class);
|
||||
JniUtils::release();
|
||||
}
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL Java_io_realm_react_RealmReactModule_setDefaultRealmFileDirectory
|
||||
(JNIEnv *env, jclass, jstring fileDir, jobject javaAssetManager)
|
||||
{
|
||||
|
50
src/android/jni_utils.cpp
Normal file
50
src/android/jni_utils.cpp
Normal file
@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright 2018 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.
|
||||
*/
|
||||
|
||||
#include "jni_utils.hpp"
|
||||
|
||||
#include <memory>
|
||||
|
||||
using namespace realm::jni_util;
|
||||
|
||||
static std::unique_ptr<JniUtils> s_instance;
|
||||
|
||||
void JniUtils::initialize(JavaVM* vm, jint vm_version) noexcept
|
||||
{
|
||||
s_instance = std::unique_ptr<JniUtils>(new JniUtils(vm, vm_version));
|
||||
}
|
||||
|
||||
void JniUtils::release()
|
||||
{
|
||||
s_instance.release();
|
||||
}
|
||||
|
||||
JNIEnv* JniUtils::get_env(bool attach_if_needed)
|
||||
{
|
||||
JNIEnv* env;
|
||||
if (s_instance->m_vm->GetEnv(reinterpret_cast<void**>(&env), s_instance->m_vm_version) != JNI_OK) {
|
||||
if (attach_if_needed) {
|
||||
jint ret = s_instance->m_vm->AttachCurrentThread(reinterpret_cast<void**>(&env), nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
return env;
|
||||
}
|
||||
|
||||
void JniUtils::detach_current_thread()
|
||||
{
|
||||
s_instance->m_vm->DetachCurrentThread();
|
||||
}
|
59
src/android/jni_utils.hpp
Normal file
59
src/android/jni_utils.hpp
Normal file
@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright 2018 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.
|
||||
*/
|
||||
|
||||
#ifndef REALM_JNI_UTIL_JNI_UTILS_HPP
|
||||
#define REALM_JNI_UTIL_JNI_UTILS_HPP
|
||||
|
||||
#include <jni.h>
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace realm {
|
||||
namespace jni_util {
|
||||
|
||||
// Util functions for JNI.
|
||||
class JniUtils {
|
||||
public:
|
||||
~JniUtils()
|
||||
{
|
||||
}
|
||||
|
||||
// Call this only once in JNI_OnLoad.
|
||||
static void initialize(JavaVM* vm, jint vm_version) noexcept;
|
||||
// Call this in JNI_OnUnload.
|
||||
static void release();
|
||||
// When attach_if_needed is false, returns the JNIEnv if there is one attached to this thread. Assert if there is
|
||||
// none. When attach_if_needed is true, try to attach and return a JNIEnv if necessary.
|
||||
static JNIEnv* get_env(bool attach_if_needed = false);
|
||||
// Detach the current thread from the JVM. Only required for C++ threads that where attached in the first place.
|
||||
// Failing to do so is a resource leak.
|
||||
static void detach_current_thread();
|
||||
|
||||
private:
|
||||
JniUtils(JavaVM* vm, jint vm_version) noexcept
|
||||
: m_vm(vm)
|
||||
, m_vm_version(vm_version)
|
||||
{
|
||||
}
|
||||
|
||||
JavaVM* m_vm;
|
||||
jint m_vm_version;
|
||||
};
|
||||
|
||||
} // namespace realm
|
||||
} // namespace jni_util
|
||||
|
||||
#endif // REALM_JNI_UTIL_JNI_UTILS_HPP
|
@ -33,6 +33,14 @@
|
||||
#include "realm/util/logger.hpp"
|
||||
#include "realm/util/uri.hpp"
|
||||
|
||||
#if REALM_ANDROID
|
||||
#include <jni.h>
|
||||
#include "./android/io_realm_react_RealmReactModule.h"
|
||||
#include "./android/jni_utils.hpp"
|
||||
|
||||
extern jclass ssl_helper_class;
|
||||
#endif
|
||||
|
||||
namespace realm {
|
||||
namespace js {
|
||||
|
||||
@ -303,7 +311,6 @@ public:
|
||||
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;
|
||||
@ -824,7 +831,33 @@ 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)) {
|
||||
|
@ -751,7 +751,7 @@ module.exports = {
|
||||
const realm = new Realm(config1);
|
||||
TestCase.assertFalse(realm.isClosed);
|
||||
realm.close();
|
||||
};
|
||||
}
|
||||
|
||||
function __partialIsNotAllowed() {
|
||||
let config2 = {
|
||||
@ -762,7 +762,7 @@ module.exports = {
|
||||
}
|
||||
};
|
||||
TestCase.assertThrows(() => new Realm(config2));
|
||||
};
|
||||
}
|
||||
|
||||
function shouldFail() {
|
||||
let config = {
|
||||
@ -780,11 +780,11 @@ module.exports = {
|
||||
TestCase.assertEqual(realm.objects('Dog').length, 0);
|
||||
TestCase.assertThrows(function () { var subscription = realm.objects('Dog').filtered("name == 'Lassy 1'").subscribe(); } );
|
||||
realm.close();
|
||||
};
|
||||
}
|
||||
|
||||
function defaultRealmInvalidArguments() {
|
||||
TestCase.assertThrows(() => Realm.automaticSyncConfiguration('foo', 'bar')); // too many arguments
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
return runOutOfProcess(__dirname + '/partial-sync-api-helper.js', username, REALM_MODULE_PATH)
|
||||
|
Loading…
x
Reference in New Issue
Block a user