Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make test environment configurable #69

Merged
merged 18 commits into from
Nov 6, 2019
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 0 additions & 3 deletions .cargo/config

This file was deleted.

1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ Cargo.lock
*.code-workspace
.metals/
tests/testkeys/*.pem
tests/testkeys/ironcore-config.json
java/scala/src/test/resources/service-keys.conf
java/scala/src/test/resources/service-keys.conf.stage
java/scala/src/test/resources/service-keys.conf.local
Expand Down
4 changes: 2 additions & 2 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,13 @@ cache:
before_cache:
- rm -rf /home/travis/.cargo/registry
rust:
- 1.36.0
- 1.38.0
branches:
only:
- master
before_install:
# key for integration tests
- openssl aes-256-cbc -K $encrypted_86f168e6939a_key -iv $encrypted_86f168e6939a_iv -in tests/testkeys/rsa_private.pem.enc -out tests/testkeys/rsa_private.pem -d
- openssl aes-256-cbc -K $encrypted_d5683c11b5e1_key -iv $encrypted_d5683c11b5e1_iv -in tests/testkeys/iak-dev.pem.enc -out tests/testkeys/iak-dev.pem -d
before_script:
- rustup component add rustfmt-preview
- cross --version || cargo install cross
Expand Down
2 changes: 1 addition & 1 deletion .travis_scripts/cross-test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ set -x

# If no special target provided, use default cargo arch for building and run all unit tests
if [ -z "$TARGET" ]; then
cargo t --verbose
IRONCORE_ENV=dev cargo t --verbose
cargo fmt -- --check
# Cross doesn't have support for iOS builds, so use cargo to add the target and compile for it
elif [ "$IOS" = 1 ]; then
Expand Down
7 changes: 2 additions & 5 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -37,14 +37,11 @@ log = "~0.4"
protobuf = {version = "~2.8", features = ["with-bytes"]}

[dev-dependencies]
frank_jwt = "~3.1.1"
frank_jwt = {git = "https://github.com/GildedHonour/frank_jwt", rev = "afcb5ba126ab9b1c21fec702045ec3f43e903c12"}
galvanic-assert = "~0.8"
uuid = { version = "~0.7.2", features = ["serde", "v4"] }
double = "~0.2.4"


[build-dependencies]
protobuf-codegen-pure = "~2.8"

[features]
senv = []
protobuf-codegen-pure = "~2.8"
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ If you are not building a Rust application, you might be interested in one of th

- [ironoxide-java](https://github.com/IronCoreLabs/ironoxide-java) - Java bindings for ironoxide. Appropriate for all JVM langauges.
- [ironoxide-scala](https://github.com/IronCoreLabs/ironoxide-scala) - Scala wrappers around `ironoxide-java`.
- [ironode](https://github.com/IronCoreLabs/ironnode) - NodeJS implementation of IronCore's Privacy Platform.
- [ironnode](https://github.com/IronCoreLabs/ironnode) - NodeJS implementation of IronCore's Privacy Platform.
- [ironweb](https://github.com/IronCoreLabs/ironweb) - Javascript implementation of IronCore's Privacy Platform. Appropriate for all modern browsers.

All SDKs are intended to be compatible with one another.
Expand Down
21 changes: 14 additions & 7 deletions src/internal/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,20 @@ pub mod group_api;
mod rest;
pub mod user_api;

#[cfg(feature = "senv")]
pub const OUR_REQUEST: IronCoreRequest =
IronCoreRequest::new("https://api-staging.ironcorelabs.com/api/1/");

#[cfg(not(feature = "senv"))]
pub const OUR_REQUEST: IronCoreRequest =
IronCoreRequest::new("https://api.ironcorelabs.com/api/1/");
lazy_static! {
pub static ref URL_STRING: String = match std::env::var("IRONCORE_ENV") {
Ok(url) => match url.as_ref() {
"dev" => "https://api-dev1.ironcorelabs.com/api/1/",
"stage" => "https://api-staging.ironcorelabs.com/api/1/",
"prod" => "https://api.ironcorelabs.com/api/1/",
url_choice => url_choice,
}
.to_string(),
_ => "https://api.ironcorelabs.com/api/1/".to_string(),
};
pub static ref OUR_REQUEST: IronCoreRequest::<'static> =
IronCoreRequest::new(URL_STRING.as_str());
}

#[derive(Debug, PartialEq, Clone, Copy)]
pub enum RequestErrorCode {
Expand Down
4 changes: 2 additions & 2 deletions src/internal/rest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -300,14 +300,14 @@ impl<'a> HeaderIronCoreRequestSig<'a> {
}

///A struct which holds the basic info that will be needed for making requests to an ironcore service. Currently just the base_url.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[derive(Debug, Clone, Serialize, Deserialize, Copy)]
pub struct IronCoreRequest<'a> {
base_url: &'a str,
}

impl Default for IronCoreRequest<'static> {
fn default() -> Self {
OUR_REQUEST
*OUR_REQUEST
}
}

Expand Down
6 changes: 3 additions & 3 deletions src/user.rs
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ impl UserOps for IronOxide {
jwt.try_into()?,
password.try_into()?,
user_create_opts.needs_rotation,
OUR_REQUEST,
*OUR_REQUEST,
))
}

Expand All @@ -178,7 +178,7 @@ impl UserOps for IronOxide {
password.try_into()?,
device_create_options.device_name,
&std::time::SystemTime::now().into(),
OUR_REQUEST,
*OUR_REQUEST,
))
}

Expand All @@ -189,7 +189,7 @@ impl UserOps for IronOxide {

fn user_verify(jwt: &str) -> Result<Option<UserResult>> {
let mut rt = Runtime::new().unwrap();
rt.block_on(user_api::user_verify(jwt.try_into()?, OUR_REQUEST))
rt.block_on(user_api::user_verify(jwt.try_into()?, *OUR_REQUEST))
}

fn user_get_public_key(&self, users: &[UserId]) -> Result<HashMap<UserId, PublicKey>> {
Expand Down
46 changes: 36 additions & 10 deletions tests/INTEGRATION-TESTS.md
Original file line number Diff line number Diff line change
@@ -1,24 +1,50 @@
Integration Testing
===================
# Integration Testing

Our integration tests require that we point to the IronCore staging environment. In order to swap that out at compile time we use a Rust feature flag to causes the SDK to point to stage. To prevent us from having to always pass `--features` when running our tests, we created an alias so that `cargo t` will automatically apply that feature flag. If you run `cargo test` you'll get failures that will hopefully clue you in that you need to use `cargo t` instead.
Our integration tests default to pointing to the IronCore staging environment, and will therefore need to be set up before use. However, unit tests can be run without prior setup.

Running *only* the unit tests (IronOxide users - this is what you want):
To run _only_ the unit tests (IronOxide users - this is what you want):

`cargo t --lib`

Running *only* the integration tests:
To run _only_ the integration tests:

`cargo t --test group_ops --test user_ops --test document_ops`

Running all the tests:
To run all the tests:

`cargo t`

#### Integration Tests
## Setting Up Integration Tests

Integration tests are run as part of a PR build on Travis. These keys are stored as a Travis secret.
In order to run the integration tests, you must provide IronOxide with an Identity Assertion Key file, an IronCore Config file, and the URL you would like to test against. This will require you to create a project, segment, and Identity Assertion Key using the admin console interface.

The integration test run against IronCore's staging environment and require some tests keys. These can be found in `tests/testkeys/rsa_private.pem.iron`. _Currently only IronCore devs have access to these keys._ The following ironhide command will decrypt the developer test keys.
### Identity Assertion Key File

`$ ironhide file:decrypt rsa_private.pem.iron`
This file must be downloaded from the admin console interface immediately after creating a new Identity Assertion Key. It should be named `rsa_private.pem` and placed in `./tests/testkeys`.
giarc3 marked this conversation as resolved.
Show resolved Hide resolved

### IronCore Config File

This file can be downloaded from the admin console on creation of the very first project. For subsequent projects, it will need to be created manually. The file is of the form:

```json
{
"projectId": { YOUR_PROJECT_ID },
giarc3 marked this conversation as resolved.
Show resolved Hide resolved
"segmentId": "{YOUR_SEGMENT_ID}",
"identityAssertionKeyId": { YOUR_IDENTITY_ASSERION_KEY_ID }
}
```

Note that case is significant for the key names.

This file must be named `ironcore-config.json` and placed in `./tests/testkeys`.

### Environment URL

By default, IronOxide will test against the staging build, but it can also test against dev, prod, or any other environment. This is specified with the environment variable `IRONCORE_ENV`, which can be set before running `cargo t`. There are several built-in environment URLs, or one can be specified. To do this, run the following:
giarc3 marked this conversation as resolved.
Show resolved Hide resolved

Development: `IRONCORE_ENV=dev cargo t`
Staging: `IRONCORE_ENV=stage cargo t`
Production: `IRONCORE_ENV=prod cargo t`
Other: `IRONCORE_ENV={URL} cargo t`

where `{URL}` is the environment you want to test against.
103 changes: 77 additions & 26 deletions tests/common/mod.rs
Original file line number Diff line number Diff line change
@@ -1,26 +1,77 @@
use ironoxide::{
prelude::*,
user::{UserCreateOpts, UserResult},
InitAndRotationCheck, IronOxide,
InitAndRotationCheck,
};
use lazy_static::*;
use std::{convert::TryInto, default::Default};
use uuid::Uuid;

pub const USER_PASSWORD: &str = "foo";

pub fn gen_jwt(
#[derive(serde::Deserialize)]
#[serde(rename_all = "camelCase")]
struct Config {
project_id: usize,
seg_id: &str,
service_key_id: usize,
account_id: Option<&str>,
) -> (String, String) {
use std::env;
segment_id: String,
identity_assertion_key_id: usize,
}

let mut keypath = env::current_dir().unwrap();
keypath.push("tests");
keypath.push("testkeys");
keypath.push("rsa_private.pem");
lazy_static! {
pub static ref ENV: String = match std::env::var("IRONCORE_ENV") {
Ok(url) => match url.as_ref() {
"dev" => "-dev",
"stage" => "-stage",
"prod" => "-prod",
_ => "",
},
_ => "-prod",
}
.to_string();
static ref KEYPATH: (String, std::path::PathBuf) = {
let mut path = std::env::current_dir().unwrap();
let filename = format!("iak{}.pem", *ENV);
path.push("tests");
path.push("testkeys");
path.push(filename.clone());
(filename, path)
};
static ref IRONCORE_CONFIG_PATH: (String, std::path::PathBuf) = {
let mut path = std::env::current_dir().unwrap();
let filename = format!("ironcore-config{}.json", *ENV);
path.push("tests");
path.push("testkeys");
path.push(filename.clone());
(filename, path)
};
static ref CONFIG: Config = {
use std::{error::Error, fs::File, io::Read};
let mut file: File = File::open(IRONCORE_CONFIG_PATH.1.clone()).unwrap_or_else(|err| {
panic!(
"Failed to open config file ({}) with error '{}'",
IRONCORE_CONFIG_PATH.0,
err.description().to_string()
)
});
let mut json_config: String = String::new();
file.read_to_string(&mut json_config).unwrap_or_else(|err| {
panic!(
"Failed to read config file ({}) with error '{}'",
IRONCORE_CONFIG_PATH.0,
err.description().to_string()
)
});
serde_json::from_str(&json_config).unwrap_or_else(|err| {
panic!(
"Failed to deserialize config file ({}) with error '{}'",
IRONCORE_CONFIG_PATH.0,
err.description().to_string()
)
})
};
}

pub fn gen_jwt(account_id: Option<&str>) -> (String, String) {
use std::time::{SystemTime, UNIX_EPOCH};
let start = SystemTime::now();
let iat_seconds = start
Expand All @@ -34,20 +85,22 @@ pub fn gen_jwt(
.or_else(|| Some(&default_account_id))
.expect("Missing expected JWT account ID.");
let jwt_payload = json!({
"pid" : project_id,
"sid" : seg_id,
"kid" : service_key_id,
"pid" : CONFIG.project_id,
"sid" : CONFIG.segment_id,
"kid" : CONFIG.identity_assertion_key_id,
"iat" : iat_seconds,
"exp" : iat_seconds + 120,
"sub" : sub
});
let jwt = frank_jwt::encode(
jwt_header,
&keypath.to_path_buf(),
&KEYPATH.1.to_path_buf(),
&jwt_payload,
frank_jwt::Algorithm::RS256,
frank_jwt::Algorithm::ES256,
)
.expect("You don't appear to have the proper service private key to sign the test JWT.");
.expect(
&format!("Error with {}: You don't appear to have the proper service private key to sign the test JWT.", KEYPATH.0)
);
(jwt, format!("{}", sub))
}

Expand All @@ -63,21 +116,19 @@ pub fn init_sdk_get_user() -> (UserId, IronOxide) {
pub fn init_sdk_get_init_result(user_needs_rotation: bool) -> (UserId, InitAndRotationCheck) {
let account_id: UserId = create_id_all_classes("").try_into().unwrap();
IronOxide::user_create(
&gen_jwt(1012, "test-segment", 551, Some(account_id.id())).0,
&gen_jwt(Some(account_id.id())).0,
USER_PASSWORD,
&UserCreateOpts::new(user_needs_rotation),
)
.unwrap();

let verify_resp =
IronOxide::user_verify(&gen_jwt(1012, "test-segment", 551, Some(account_id.id())).0)
.unwrap()
.unwrap();
let verify_resp = IronOxide::user_verify(&gen_jwt(Some(account_id.id())).0)
.unwrap()
.unwrap();
assert_eq!(&account_id, verify_resp.account_id());
assert_eq!(2012, verify_resp.segment_id());

let device = IronOxide::generate_new_device(
&gen_jwt(1012, "test-segment", 551, Some(account_id.id())).0,
&gen_jwt(Some(account_id.id())).0,
USER_PASSWORD,
&Default::default(),
)
Expand Down Expand Up @@ -106,7 +157,7 @@ pub fn init_sdk_get_init_result(user_needs_rotation: bool) -> (UserId, InitAndRo
}

pub fn create_second_user() -> UserResult {
let (jwt, _) = gen_jwt(1012, "test-segment", 551, Some(&create_id_all_classes("")));
let (jwt, _) = gen_jwt(Some(&create_id_all_classes("")));
let create_result = IronOxide::user_create(&jwt, USER_PASSWORD, &Default::default());
assert!(create_result.is_ok());

Expand All @@ -127,5 +178,5 @@ pub fn create_id_all_classes(prefix: &str) -> String {
#[allow(dead_code)]
// Use this test to print out a JWT and UUID if you need it
fn non_test_print_jwt() {
dbg!(gen_jwt(1012, "test-segment", 551, None));
dbg!(gen_jwt(None));
}
4 changes: 4 additions & 0 deletions tests/testkeys/iak-dev.pem.enc
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
��9C_R���_�/!��t�Ks^`K)SU�\+���\���Ke�n��'.��t_ ���1��ZCBmUk�����Kf�f���2�¤� ��� fWP��Ejc��r���$3��Wj@Jv0�_I:f�+��6cq8V�*�dh�q!d��9F :,Vɐ�k8��Z�&-4�gzi�a��.
�߸�Ee8�oN.��C����/��p��I0M�]�i��6�܇g�:K{���Q�Ó����Q�Q�.��� hP ��-D�@Hd.bt�J�s7��J�ݩ:���V+����|�l�Z;Ⳉ��V���f���[*������T
���[�o�<k����3a��ҟء�rz�L����^>q{��lX��
e;j.��.�����Q�X�G(,�9=*�z!!��B���s�dޒ�mbZ���fA@�7�{�
Binary file added tests/testkeys/iak-dev.pem.iron
Binary file not shown.
Binary file added tests/testkeys/iak-prod.pem.iron
Binary file not shown.
Binary file added tests/testkeys/iak-stage.pem.iron
Binary file not shown.
5 changes: 5 additions & 0 deletions tests/testkeys/ironcore-config-dev.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"projectId": 438,
"segmentId": "ironoxide-dev1",
"identityAssertionKeyId": 593
}
5 changes: 5 additions & 0 deletions tests/testkeys/ironcore-config-prod.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"projectId": 1903,
"segmentId": "ironoxide-tests",
"identityAssertionKeyId": 2173
}
5 changes: 5 additions & 0 deletions tests/testkeys/ironcore-config-stage.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"projectId": 428,
"segmentId": "ironoxide-staging",
"identityAssertionKeyId": 588
}
Loading