Skip to content

Commit d563c8b

Browse files
authored
Merge pull request #318 from hug-dev/list-clients
Add ListClients and DeleteClient operations
2 parents 1e3a74a + 59a3631 commit d563c8b

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)