Skip to content

Commit 59a3631

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 59a3631

File tree

22 files changed

+377
-116
lines changed

22 files changed

+377
-116
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 = "1000" }, { 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_and_delete_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: 12 additions & 8 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.");
@@ -75,6 +75,7 @@ impl Authenticate for DirectAuthenticator {
7575
mod test {
7676
use super::super::{Admin, Authenticate};
7777
use super::DirectAuthenticator;
78+
use crate::authenticators::ApplicationName;
7879
use parsec_interface::requests::request::RequestAuth;
7980
use parsec_interface::requests::ResponseStatus;
8081

@@ -88,12 +89,12 @@ mod test {
8889
let req_auth = RequestAuth::new(app_name.clone().into_bytes());
8990
let conn_metadata = None;
9091

91-
let auth_name = authenticator
92+
let app = authenticator
9293
.authenticate(&req_auth, conn_metadata)
9394
.expect("Failed to authenticate");
9495

95-
assert_eq!(auth_name.get_name(), app_name);
96-
assert_eq!(auth_name.is_admin, false);
96+
assert_eq!(app.get_name(), &ApplicationName::from_name(app_name));
97+
assert_eq!(app.is_admin, false);
9798
}
9899

99100
#[test]
@@ -140,15 +141,18 @@ mod test {
140141
.authenticate(&req_auth, conn_metadata)
141142
.expect("Failed to authenticate");
142143

143-
assert_eq!(auth_name.get_name(), app_name);
144+
assert_eq!(auth_name.get_name(), &ApplicationName::from_name(app_name));
144145
assert_eq!(auth_name.is_admin, false);
145146

146147
let req_auth = RequestAuth::new(admin_name.clone().into_bytes());
147148
let auth_name = authenticator
148149
.authenticate(&req_auth, conn_metadata)
149150
.expect("Failed to authenticate");
150151

151-
assert_eq!(auth_name.get_name(), admin_name);
152+
assert_eq!(
153+
auth_name.get_name(),
154+
&ApplicationName::from_name(admin_name)
155+
);
152156
assert_eq!(auth_name.is_admin, true);
153157
}
154158
}

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: 36 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,20 @@ use zeroize::Zeroize;
2727
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
2828
pub struct ApplicationName {
2929
name: String,
30+
}
31+
32+
impl Deref for ApplicationName {
33+
type Target = String;
34+
35+
fn deref(&self) -> &Self::Target {
36+
&self.name
37+
}
38+
}
39+
40+
/// Wrapper for a Parsec application
41+
#[derive(Debug, Clone)]
42+
pub struct Application {
43+
name: ApplicationName,
3044
is_admin: bool,
3145
}
3246

@@ -40,7 +54,7 @@ pub trait Authenticate {
4054
/// operation.
4155
fn describe(&self) -> Result<list_authenticators::AuthenticatorInfo>;
4256

43-
/// Authenticates a `RequestAuth` payload and returns the `ApplicationName` if successful. A
57+
/// Authenticates a `RequestAuth` payload and returns the `Application` if successful. A
4458
/// optional `ConnectionMetadata` object is passed in too, since it is sometimes possible to
4559
/// perform authentication based on the connection's metadata (i.e. as is the case for UNIX
4660
/// domain sockets with Unix peer credentials).
@@ -52,32 +66,40 @@ pub trait Authenticate {
5266
&self,
5367
auth: &RequestAuth,
5468
meta: Option<ConnectionMetadata>,
55-
) -> Result<ApplicationName>;
69+
) -> Result<Application>;
5670
}
5771

5872
impl ApplicationName {
59-
/// Create a new ApplicationName
60-
fn new(name: String, is_admin: bool) -> ApplicationName {
61-
ApplicationName { name, is_admin }
62-
}
63-
6473
/// Create ApplicationName from name string only
6574
pub fn from_name(name: String) -> ApplicationName {
66-
ApplicationName {
67-
name,
68-
is_admin: false,
69-
}
75+
ApplicationName { name }
7076
}
77+
}
7178

72-
/// Get a reference to the inner string
73-
pub fn get_name(&self) -> &str {
74-
&self.name
79+
impl Application {
80+
/// Create a new Application structure
81+
pub fn new(name: String, is_admin: bool) -> Application {
82+
Application {
83+
name: ApplicationName::from_name(name),
84+
is_admin,
85+
}
7586
}
7687

7788
/// Check whether the application is an admin
7889
pub fn is_admin(&self) -> bool {
7990
self.is_admin
8091
}
92+
93+
/// Get a reference to the inner ApplicationName string
94+
pub fn get_name(&self) -> &ApplicationName {
95+
&self.name
96+
}
97+
}
98+
99+
impl From<Application> for ApplicationName {
100+
fn from(auth: Application) -> Self {
101+
auth.name
102+
}
81103
}
82104

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

0 commit comments

Comments
 (0)