Skip to content

Commit

Permalink
tests: move common tooling to separate file
Browse files Browse the repository at this point in the history
Reorganize the tests a little bit so that we can more easily share
common functions. We could keep all this code in the tests directory but
it seems a little cleaner to move it to src.

Also, rename the test file that we currently have from integration
to get_resource as it will contain tests based around getting resources.

Signed-off-by: Tobin Feldman-Fitzthum <tobin@ibm.com>
  • Loading branch information
fitzthum committed Jan 21, 2025
1 parent cfeb40c commit bde73d8
Show file tree
Hide file tree
Showing 3 changed files with 258 additions and 0 deletions.
174 changes: 174 additions & 0 deletions integration-tests/src/common.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
// Copyright (c) 2025 by IBM.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0

use kbs::admin::config::AdminConfig;
use kbs::attestation::config::{AttestationConfig, AttestationServiceConfig};
use kbs::config::HttpServerConfig;
use kbs::config::KbsConfig;
use kbs::policy_engine::PolicyEngineConfig;
use kbs::token::AttestationTokenVerifierConfig;

use kbs::plugins::{
implementations::{resource::local_fs::LocalFsRepoDesc, RepositoryConfig},
PluginsConfig,
};

use attestation_service::{
config::Config,
rvps::{RvpsConfig, RvpsCrateConfig},
token::{ear_broker, simple, AttestationTokenConfig},
};

use anyhow::{bail, Result};
use log::info;
use openssl::pkey::PKey;
use std::io::Write;
use tempfile::{NamedTempFile, TempDir};

const KBS_URL: &str = "http://127.0.0.1:8080";
const WAIT_TIME: u64 = 3000;

const ALLOW_ALL_POLICY: &str = "
package policy
allow = true
";

const DENY_ALL_POLICY: &str = "
package policy
allow = false
";

pub enum PolicyType {
AllowAll,
DenyAll,
//Custom(String),
}

// Parameters that define test behavior (coming from rstest)
pub struct TestParameters {
pub attestation_token_type: String,
}

// Internal state of tests
pub struct TestHarness {
pub kbs_config: KbsConfig,
auth_privkey: String,

// Future tests will use some parameters at runtime
_test_parameters: TestParameters,
}

impl TestHarness {
pub fn new(test_parameters: TestParameters) -> Result<TestHarness> {
let auth_keypair = PKey::generate_ed25519()?;
let auth_pubkey = String::from_utf8(auth_keypair.public_key_to_pem()?)?;
let auth_privkey = String::from_utf8(auth_keypair.private_key_to_pem_pkcs8()?)?;

let work_dir = TempDir::new()?;
let resource_dir = TempDir::new()?;
let policy_path = NamedTempFile::new()?;

let mut auth_pubkey_path = NamedTempFile::new()?;
auth_pubkey_path.write(auth_pubkey.as_bytes())?;

let attestation_token_config = match &test_parameters.attestation_token_type[..] {
"Ear" => AttestationTokenConfig::Ear(ear_broker::Configuration {
duration_min: 5,
..Default::default()
}),
"Simple" => AttestationTokenConfig::Simple(simple::Configuration::default()),
_ => bail!("Unknown attestation token type. Must be Simple or Ear"),
};

let kbs_config = KbsConfig {
attestation_token: AttestationTokenVerifierConfig {
trusted_certs_paths: vec![],
insecure_key: true,
trusted_jwk_sets: vec![],
extra_teekey_paths: vec![],
},
attestation_service: AttestationConfig {
attestation_service: AttestationServiceConfig::CoCoASBuiltIn(Config {
work_dir: work_dir.path().to_path_buf(),
rvps_config: RvpsConfig::BuiltIn(RvpsCrateConfig::default()),
attestation_token_broker: attestation_token_config,
}),
timeout: 5,
},
http_server: HttpServerConfig {
sockets: vec!["127.0.0.1:8080".parse()?],
private_key: None,
certificate: None,
insecure_http: true,
},
admin: AdminConfig {
auth_public_key: None,
insecure_api: true,
},
policy_engine: PolicyEngineConfig {
policy_path: policy_path.path().to_path_buf(),
},
plugins: vec![PluginsConfig::ResourceStorage(RepositoryConfig::LocalFs(
LocalFsRepoDesc {
dir_path: resource_dir.path().to_str().unwrap().to_string(),
},
))],
};

Ok(TestHarness {
kbs_config,
auth_privkey,
_test_parameters: test_parameters,
})
}

pub async fn set_policy(&self, policy: PolicyType) -> Result<()> {
info!("TEST: Setting Resource Policy");

let policy_bytes = match policy {
PolicyType::AllowAll => ALLOW_ALL_POLICY.as_bytes().to_vec(),
PolicyType::DenyAll => DENY_ALL_POLICY.as_bytes().to_vec(),
//PolicyType::Custom(p) => p.into_bytes(),
};

kbs_client::set_resource_policy(
KBS_URL,
self.auth_privkey.clone(),
policy_bytes,
// Optional HTTPS certs for KBS
vec![],
)
.await?;

Ok(())
}

pub async fn set_secret(&self, secret_path: String, secret_bytes: Vec<u8>) -> Result<()> {
info!("TEST: Setting Secret");
kbs_client::set_resource(
KBS_URL,
self.auth_privkey.clone(),
secret_bytes,
&secret_path,
// Optional HTTPS certs for KBS
vec![],
)
.await?;

Ok(())
}

pub async fn get_secret(&self, secret_path: String) -> Result<Vec<u8>> {
info!("TEST: Getting Secret");
let resource_bytes =
kbs_client::get_resource_with_attestation(KBS_URL, &secret_path, None, vec![]).await?;

Ok(resource_bytes)
}

pub async fn wait(&self) {
let duration = tokio::time::Duration::from_millis(WAIT_TIME);
tokio::time::sleep(duration).await;
}
}
5 changes: 5 additions & 0 deletions integration-tests/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
// Copyright (c) 2025 by IBM.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0

pub mod common;
79 changes: 79 additions & 0 deletions integration-tests/tests/get_resource.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
// Copyright (c) 2024 by IBM.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0

use kbs::ApiServer;

use anyhow::Result;
use log::info;
use rstest::rstest;
use serial_test::serial;

extern crate integration_tests;
use crate::integration_tests::common::{PolicyType, TestHarness, TestParameters};

const SECRET_BYTES: &[u8; 8] = b"shhhhhhh";
const SECRET_PATH: &str = "default/test/secret";

#[rstest]
#[case::ear_allow_all(TestParameters{attestation_token_type: "Ear".to_string() })]
#[case::simple_allow_all(TestParameters{attestation_token_type: "Simple".to_string() })]
#[serial]
#[actix_rt::test]
async fn get_secret_allow_all(#[case] test_parameters: TestParameters) -> Result<()> {
let _ = env_logger::try_init_from_env(env_logger::Env::new().default_filter_or("debug"));
let harness = TestHarness::new(test_parameters)?;

let api_server = ApiServer::new(harness.kbs_config.clone()).await?;

let kbs_server = api_server.server()?;
let kbs_handle = kbs_server.handle();

actix_web::rt::spawn(kbs_server);

harness.wait().await;
harness.set_secret(SECRET_PATH.to_string(), SECRET_BYTES.as_ref().to_vec())
.await?;
harness.set_policy(PolicyType::AllowAll).await?;

let secret = harness.get_secret(SECRET_PATH.to_string()).await?;

assert_eq!(secret, SECRET_BYTES);
info!("TEST: test completed succesfully");

kbs_handle.stop(true).await;

Ok(())
}

#[rstest]
#[case::ear_deny_all(TestParameters{attestation_token_type: "Ear".to_string() })]
#[case::simple_deny_all(TestParameters{attestation_token_type: "Simple".to_string() })]
#[serial]
#[actix_rt::test]
async fn get_secret_deny_all(#[case] test_parameters: TestParameters) -> Result<()> {
let _ = env_logger::try_init_from_env(env_logger::Env::new().default_filter_or("debug"));
let harness = TestHarness::new(test_parameters)?;

let api_server = ApiServer::new(harness.kbs_config.clone()).await?;

let kbs_server = api_server.server()?;
let kbs_handle = kbs_server.handle();

actix_web::rt::spawn(kbs_server);

harness.wait().await;
harness.set_secret(SECRET_PATH.to_string(), SECRET_BYTES.as_ref().to_vec())
.await?;
harness.set_policy(PolicyType::DenyAll).await?;

let secret = harness.get_secret(SECRET_PATH.to_string()).await;

assert!(secret.is_err());
assert_eq!(secret.unwrap_err().to_string(), "request unauthorized".to_string());
info!("TEST: test completed succesfully");

kbs_handle.stop(true).await;

Ok(())
}

0 comments on commit bde73d8

Please sign in to comment.