Skip to content

Commit 0c5159e

Browse files
committed
Add ListClients and DeleteClient operations
Also adds multitenancy tests for those operations and for the admin feature. Splits the ApplicationName structure into Application and ApplicationName to only deal with key names in the Key Info Managers and not the admin status which is not relevent there. Signed-off-by: Hugues de Valon <[email protected]>
1 parent 1e3a74a commit 0c5159e

File tree

21 files changed

+356
-109
lines changed

21 files changed

+356
-109
lines changed

Cargo.lock

Lines changed: 1 addition & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ name = "parsec"
1818
path = "src/bin/main.rs"
1919

2020
[dependencies]
21-
parsec-interface = "0.22.0"
21+
parsec-interface = { git = "https://github.com/parallaxsecond/parsec-interface-rs", rev = "c8a59544fac04df347f51d19323f4a0b5bb9580d" }
2222
rand = { version = "0.7.3", features = ["small_rng"], optional = true }
2323
base64 = "0.12.3"
2424
uuid = "0.8.1"

e2e_tests/provider_cfg/all/config.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ socket_path = "/tmp/parsec.sock"
1414

1515
[authenticator]
1616
auth_type = "Direct"
17+
admins = [ { name = "list_clients test" }, { name = "parsec-client-1" }, { name = "client1" }, { name = "spiffe://example.org/parsec-client-1" } ]
1718
#workload_endpoint="unix:///tmp/agent.sock"
1819

1920
[[key_manager]]

e2e_tests/src/lib.rs

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,10 @@ impl TestClient {
5353
/// Creates a TestClient instance.
5454
pub fn new() -> TestClient {
5555
// As this method is called in test, it will be called more than once per application.
56-
if let Err(_) = env_logger::try_init() {};
56+
#[allow(unused_must_use)]
57+
{
58+
env_logger::try_init();
59+
}
5760

5861
let mut basic_client = BasicClient::new_naked();
5962

@@ -892,6 +895,18 @@ impl TestClient {
892895
self.basic_client.list_keys().map_err(convert_error)
893896
}
894897

898+
/// Lists the clients.
899+
pub fn list_clients(&mut self) -> Result<Vec<String>> {
900+
self.basic_client.list_clients().map_err(convert_error)
901+
}
902+
903+
/// Delete a client.
904+
pub fn delete_client(&mut self, client: String) -> Result<()> {
905+
self.basic_client
906+
.delete_client(client)
907+
.map_err(convert_error)
908+
}
909+
895910
/// Executes a ping operation.
896911
pub fn ping(&mut self) -> Result<(u8, u8)> {
897912
self.basic_client.ping().map_err(convert_error)

e2e_tests/src/stress.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -330,7 +330,7 @@ impl ServiceChecker {
330330
.expect("Verification failed");
331331

332332
client
333-
.destroy_key(sign_key_name.clone())
333+
.destroy_key(sign_key_name)
334334
.expect("Failed to destroy key");
335335
}
336336

@@ -352,7 +352,7 @@ impl ServiceChecker {
352352
assert_eq!(plaintext, vec![0xa5; 16]);
353353

354354
client
355-
.destroy_key(encr_key_name.clone())
355+
.destroy_key(encr_key_name)
356356
.expect("Failed to destroy key");
357357
}
358358
}

e2e_tests/tests/all_providers/multitenancy.rs

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ use parsec_client::core::interface::requests::{ProviderID, ResponseStatus};
99
// 3. client1_after is executed as parsec-client-1
1010
//
1111
// They are executed against all possible authenticators in Parsec.
12+
//
13+
// client1 will be configured as an admin.
1214

1315
#[test]
1416
fn client1_before() {
@@ -23,11 +25,15 @@ fn client1_before() {
2325
client.set_provider(*provider);
2426
client.generate_rsa_sign_key(key.clone()).unwrap();
2527
}
28+
29+
let clients = client.list_clients().unwrap();
30+
assert_eq!(clients.len(), 1);
2631
}
2732

2833
#[test]
2934
fn client2() {
3035
let mut client = TestClient::new();
36+
client.do_not_destroy_keys();
3137
client.set_default_auth(Some("client2".to_string()));
3238

3339
let key = String::from("multitenant");
@@ -49,11 +55,24 @@ fn client2() {
4955
client.generate_rsa_sign_key(key.clone()).unwrap();
5056
client.destroy_key(key.clone()).unwrap();
5157
}
58+
59+
assert_eq!(
60+
client.list_clients().unwrap_err(),
61+
ResponseStatus::AdminOperation
62+
);
63+
assert_eq!(
64+
client.delete_client("toto".to_string()).unwrap_err(),
65+
ResponseStatus::AdminOperation
66+
);
67+
client
68+
.generate_rsa_sign_key("client2-key".to_string())
69+
.unwrap();
5270
}
5371

5472
#[test]
5573
fn client1_after() {
5674
let mut client = TestClient::new();
75+
client.do_not_destroy_keys();
5776
client.set_default_auth(Some("client1".to_string()));
5877

5978
// Verify all keys are still there and can be used
@@ -66,4 +85,18 @@ fn client1_after() {
6685
client.set_provider(*provider);
6786
client.destroy_key(key.clone()).unwrap();
6887
}
88+
89+
client
90+
.generate_rsa_sign_key("client1-key".to_string())
91+
.unwrap();
92+
let mut clients = client.list_clients().unwrap();
93+
assert_eq!(clients.len(), 2);
94+
client.delete_client(clients.remove(0)).unwrap();
95+
let mut clients = client.list_clients().unwrap();
96+
assert_eq!(clients.len(), 1);
97+
client.delete_client(clients.remove(0)).unwrap();
98+
let clients = client.list_clients().unwrap();
99+
assert_eq!(clients.len(), 0);
100+
let keys = client.list_keys().unwrap();
101+
assert_eq!(keys.len(), 0);
69102
}

e2e_tests/tests/all_providers/normal.rs

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,3 +167,49 @@ fn invalid_provider_list_keys() {
167167
.expect("Failed to read Response");
168168
assert_eq!(resp.header.status, ResponseStatus::PsaErrorNotSupported);
169169
}
170+
171+
#[test]
172+
fn invalid_provider_list_clients() {
173+
let mut client = RawRequestClient {};
174+
let mut req_hdr = RawHeader::new();
175+
176+
// Always targeting the Mbed Crypto provider
177+
req_hdr.provider = 0x1;
178+
req_hdr.opcode = Opcode::ListClients as u32;
179+
180+
let resp = client
181+
.send_raw_request(req_hdr, Vec::new())
182+
.expect("Failed to read Response");
183+
assert_eq!(resp.header.status, ResponseStatus::PsaErrorNotSupported);
184+
}
185+
186+
#[test]
187+
fn list_clients() {
188+
let mut client = TestClient::new();
189+
client.do_not_destroy_keys();
190+
client.set_default_auth(Some("list_clients test".to_string()));
191+
192+
let clients = client.list_clients().expect("list_clients failed");
193+
assert!(!clients.contains(&"list_clients test".to_string()));
194+
195+
let key1 = String::from("list_clients1");
196+
let key2 = String::from("list_keys2");
197+
let key3 = String::from("list_keys3");
198+
199+
client.set_provider(ProviderID::MbedCrypto);
200+
client.generate_rsa_sign_key(key1.clone()).unwrap();
201+
client.set_provider(ProviderID::Pkcs11);
202+
client.generate_rsa_sign_key(key2.clone()).unwrap();
203+
client.set_provider(ProviderID::Tpm);
204+
client.generate_rsa_sign_key(key3.clone()).unwrap();
205+
206+
let clients = client.list_clients().expect("list_clients failed");
207+
assert!(clients.contains(&"list_clients test".to_string()));
208+
client
209+
.delete_client("list_clients test".to_string())
210+
.unwrap();
211+
212+
let keys = client.list_keys().expect("list_keys failed");
213+
214+
assert!(keys.is_empty());
215+
}

src/authenticators/direct_authenticator/mod.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
//! This authenticator does not offer any security value and should only be used in environments
99
//! where all the clients and the service are mutually trustworthy.
1010
11-
use super::{Admin, AdminList, ApplicationName, Authenticate};
11+
use super::{Admin, AdminList, Application, Authenticate};
1212
use crate::front::listener::ConnectionMetadata;
1313
use log::error;
1414
use parsec_interface::operations::list_authenticators;
@@ -51,7 +51,7 @@ impl Authenticate for DirectAuthenticator {
5151
&self,
5252
auth: &RequestAuth,
5353
_: Option<ConnectionMetadata>,
54-
) -> Result<ApplicationName> {
54+
) -> Result<Application> {
5555
if auth.buffer.expose_secret().is_empty() {
5656
error!("The direct authenticator does not expect empty authentication values.");
5757
Err(ResponseStatus::AuthenticationError)
@@ -60,7 +60,7 @@ impl Authenticate for DirectAuthenticator {
6060
Ok(str) => {
6161
let app_name = String::from(str);
6262
let is_admin = self.admins.is_admin(&app_name);
63-
Ok(ApplicationName::new(app_name, is_admin))
63+
Ok(Application::new(app_name, is_admin))
6464
}
6565
Err(_) => {
6666
error!("Error parsing the authentication value as a UTF-8 string.");
@@ -88,12 +88,12 @@ mod test {
8888
let req_auth = RequestAuth::new(app_name.clone().into_bytes());
8989
let conn_metadata = None;
9090

91-
let auth_name = authenticator
91+
let app = authenticator
9292
.authenticate(&req_auth, conn_metadata)
9393
.expect("Failed to authenticate");
9494

95-
assert_eq!(auth_name.get_name(), app_name);
96-
assert_eq!(auth_name.is_admin, false);
95+
assert_eq!(app.get_name(), app_name);
96+
assert_eq!(app.is_admin, false);
9797
}
9898

9999
#[test]

src/authenticators/jwt_svid_authenticator/mod.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
// SPDX-License-Identifier: Apache-2.0
33
//! JWT SVID authenticator
44
5-
use super::{Admin, AdminList, ApplicationName, Authenticate};
5+
use super::{Admin, AdminList, Application, Authenticate};
66
use crate::front::listener::ConnectionMetadata;
77
use log::error;
88
use parsec_interface::operations::list_authenticators;
@@ -48,7 +48,7 @@ impl Authenticate for JwtSvidAuthenticator {
4848
&self,
4949
auth: &RequestAuth,
5050
_: Option<ConnectionMetadata>,
51-
) -> Result<ApplicationName> {
51+
) -> Result<Application> {
5252
let svid = Jwt::new(
5353
str::from_utf8(auth.buffer.expose_secret())
5454
.map_err(|e| {
@@ -68,6 +68,6 @@ impl Authenticate for JwtSvidAuthenticator {
6868
})?;
6969
let app_name = validate_response.spiffe_id().to_string();
7070
let is_admin = self.admins.is_admin(&app_name);
71-
Ok(ApplicationName::new(app_name, is_admin))
71+
Ok(Application::new(app_name, is_admin))
7272
}
7373
}

src/authenticators/mod.rs

Lines changed: 30 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,12 @@ use zeroize::Zeroize;
2727
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
2828
pub struct ApplicationName {
2929
name: String,
30+
}
31+
32+
/// Wrapper for a Parsec application
33+
#[derive(Debug, Clone)]
34+
pub struct Application {
35+
name: ApplicationName,
3036
is_admin: bool,
3137
}
3238

@@ -40,7 +46,7 @@ pub trait Authenticate {
4046
/// operation.
4147
fn describe(&self) -> Result<list_authenticators::AuthenticatorInfo>;
4248

43-
/// Authenticates a `RequestAuth` payload and returns the `ApplicationName` if successful. A
49+
/// Authenticates a `RequestAuth` payload and returns the `Application` if successful. A
4450
/// optional `ConnectionMetadata` object is passed in too, since it is sometimes possible to
4551
/// perform authentication based on the connection's metadata (i.e. as is the case for UNIX
4652
/// domain sockets with Unix peer credentials).
@@ -52,32 +58,45 @@ pub trait Authenticate {
5258
&self,
5359
auth: &RequestAuth,
5460
meta: Option<ConnectionMetadata>,
55-
) -> Result<ApplicationName>;
61+
) -> Result<Application>;
5662
}
5763

5864
impl ApplicationName {
59-
/// Create a new ApplicationName
60-
fn new(name: String, is_admin: bool) -> ApplicationName {
61-
ApplicationName { name, is_admin }
62-
}
63-
6465
/// Create ApplicationName from name string only
6566
pub fn from_name(name: String) -> ApplicationName {
66-
ApplicationName {
67-
name,
68-
is_admin: false,
69-
}
67+
ApplicationName { name }
7068
}
7169

7270
/// Get a reference to the inner string
7371
pub fn get_name(&self) -> &str {
7472
&self.name
7573
}
74+
}
75+
76+
impl Application {
77+
/// Create a new Application structure
78+
pub fn new(name: String, is_admin: bool) -> Application {
79+
Application {
80+
name: ApplicationName::from_name(name),
81+
is_admin,
82+
}
83+
}
7684

7785
/// Check whether the application is an admin
7886
pub fn is_admin(&self) -> bool {
7987
self.is_admin
8088
}
89+
90+
/// Get a reference to the inner ApplicationName string
91+
pub fn get_name(&self) -> &str {
92+
&self.name.name
93+
}
94+
}
95+
96+
impl From<Application> for ApplicationName {
97+
fn from(auth: Application) -> Self {
98+
auth.name
99+
}
81100
}
82101

83102
impl std::fmt::Display for ApplicationName {

0 commit comments

Comments
 (0)