-
Notifications
You must be signed in to change notification settings - Fork 71
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
Add admin configuration #316
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8,8 +8,7 @@ | |
//! This authenticator does not offer any security value and should only be used in environments | ||
//! where all the clients and the service are mutually trustworthy. | ||
|
||
use super::ApplicationName; | ||
use super::Authenticate; | ||
use super::{Admin, AdminList, ApplicationName, Authenticate}; | ||
use crate::front::listener::ConnectionMetadata; | ||
use log::error; | ||
use parsec_interface::operations::list_authenticators; | ||
|
@@ -20,8 +19,19 @@ use parsec_interface::secrecy::ExposeSecret; | |
use std::str; | ||
|
||
/// Direct authentication authenticator implementation | ||
#[derive(Copy, Clone, Debug)] | ||
pub struct DirectAuthenticator; | ||
#[derive(Clone, Debug)] | ||
pub struct DirectAuthenticator { | ||
admins: AdminList, | ||
} | ||
|
||
impl DirectAuthenticator { | ||
/// Create new direct authenticator | ||
pub fn new(admins: Vec<Admin>) -> Self { | ||
DirectAuthenticator { | ||
admins: admins.into(), | ||
} | ||
} | ||
} | ||
|
||
impl Authenticate for DirectAuthenticator { | ||
fn describe(&self) -> Result<list_authenticators::AuthenticatorInfo> { | ||
|
@@ -47,7 +57,11 @@ impl Authenticate for DirectAuthenticator { | |
Err(ResponseStatus::AuthenticationError) | ||
} else { | ||
match str::from_utf8(auth.buffer.expose_secret()) { | ||
Ok(str) => Ok(ApplicationName(String::from(str))), | ||
Ok(str) => { | ||
let app_name = String::from(str); | ||
let is_admin = self.admins.is_admin(&app_name); | ||
Ok(ApplicationName::new(app_name, is_admin)) | ||
} | ||
Err(_) => { | ||
error!("Error parsing the authentication value as a UTF-8 string."); | ||
Err(ResponseStatus::AuthenticationError) | ||
|
@@ -59,14 +73,16 @@ impl Authenticate for DirectAuthenticator { | |
|
||
#[cfg(test)] | ||
mod test { | ||
use super::super::Authenticate; | ||
use super::super::{Admin, Authenticate}; | ||
use super::DirectAuthenticator; | ||
use parsec_interface::requests::request::RequestAuth; | ||
use parsec_interface::requests::ResponseStatus; | ||
|
||
#[test] | ||
fn successful_authentication() { | ||
let authenticator = DirectAuthenticator {}; | ||
let authenticator = DirectAuthenticator { | ||
admins: Default::default(), | ||
}; | ||
|
||
let app_name = "app_name".to_string(); | ||
let req_auth = RequestAuth::new(app_name.clone().into_bytes()); | ||
|
@@ -77,11 +93,14 @@ mod test { | |
.expect("Failed to authenticate"); | ||
|
||
assert_eq!(auth_name.get_name(), app_name); | ||
assert_eq!(auth_name.is_admin, false); | ||
} | ||
|
||
#[test] | ||
fn failed_authentication() { | ||
let authenticator = DirectAuthenticator {}; | ||
let authenticator = DirectAuthenticator { | ||
admins: Default::default(), | ||
}; | ||
let conn_metadata = None; | ||
let status = authenticator | ||
.authenticate(&RequestAuth::new(vec![0xff; 5]), conn_metadata) | ||
|
@@ -92,12 +111,44 @@ mod test { | |
|
||
#[test] | ||
fn empty_auth() { | ||
let authenticator = DirectAuthenticator {}; | ||
let authenticator = DirectAuthenticator { | ||
admins: Default::default(), | ||
}; | ||
let conn_metadata = None; | ||
let status = authenticator | ||
.authenticate(&RequestAuth::new(Vec::new()), conn_metadata) | ||
.expect_err("Empty auth should have failed"); | ||
|
||
assert_eq!(status, ResponseStatus::AuthenticationError); | ||
} | ||
|
||
#[test] | ||
fn admin_check() { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nice tests 👌 |
||
let admin_name = String::from("admin_name"); | ||
let authenticator = DirectAuthenticator { | ||
admins: vec![Admin { | ||
name: admin_name.clone(), | ||
}] | ||
.into(), | ||
}; | ||
|
||
let app_name = "app_name".to_string(); | ||
let req_auth = RequestAuth::new(app_name.clone().into_bytes()); | ||
let conn_metadata = None; | ||
|
||
let auth_name = authenticator | ||
.authenticate(&req_auth, conn_metadata) | ||
.expect("Failed to authenticate"); | ||
|
||
assert_eq!(auth_name.get_name(), app_name); | ||
assert_eq!(auth_name.is_admin, false); | ||
|
||
let req_auth = RequestAuth::new(admin_name.clone().into_bytes()); | ||
let auth_name = authenticator | ||
.authenticate(&req_auth, conn_metadata) | ||
.expect("Failed to authenticate"); | ||
|
||
assert_eq!(auth_name.get_name(), admin_name); | ||
assert_eq!(auth_name.is_admin, true); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -20,11 +20,15 @@ use parsec_interface::operations::list_authenticators; | |
use parsec_interface::requests::request::RequestAuth; | ||
use parsec_interface::requests::Result; | ||
use serde::Deserialize; | ||
use std::ops::Deref; | ||
use zeroize::Zeroize; | ||
|
||
/// String wrapper for app names | ||
#[derive(Debug, Clone, Eq, PartialEq, Hash)] | ||
pub struct ApplicationName(String); | ||
pub struct ApplicationName { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Wasn't sure if I should just change this to There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think for this PR this is fine keeping the name to not trigger too many changes |
||
name: String, | ||
is_admin: bool, | ||
} | ||
|
||
/// Authentication interface | ||
/// | ||
|
@@ -52,20 +56,33 @@ pub trait Authenticate { | |
} | ||
|
||
impl ApplicationName { | ||
/// Create a new ApplicationName from a String | ||
pub fn new(name: String) -> ApplicationName { | ||
ApplicationName(name) | ||
/// Create a new ApplicationName | ||
fn new(name: String, is_admin: bool) -> ApplicationName { | ||
ApplicationName { name, is_admin } | ||
} | ||
|
||
/// Create ApplicationName from name string only | ||
pub fn from_name(name: String) -> ApplicationName { | ||
ApplicationName { | ||
name, | ||
is_admin: false, | ||
} | ||
} | ||
|
||
/// Get a reference to the inner string | ||
pub fn get_name(&self) -> &str { | ||
&self.0 | ||
&self.name | ||
} | ||
|
||
/// Check whether the application is an admin | ||
pub fn is_admin(&self) -> bool { | ||
self.is_admin | ||
} | ||
} | ||
|
||
impl std::fmt::Display for ApplicationName { | ||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||
write!(f, "{}", self.0) | ||
write!(f, "{}", self.name) | ||
} | ||
} | ||
|
||
|
@@ -75,12 +92,56 @@ impl std::fmt::Display for ApplicationName { | |
#[serde(tag = "auth_type")] | ||
pub enum AuthenticatorConfig { | ||
/// Direct authentication | ||
Direct, | ||
/// Unix Peer Credenditals authentication | ||
UnixPeerCredentials, | ||
Direct { | ||
/// List of service admins | ||
admins: Option<Vec<Admin>>, | ||
}, | ||
/// Unix Peer Credentials authentication | ||
UnixPeerCredentials { | ||
/// List of service admins | ||
admins: Option<Vec<Admin>>, | ||
}, | ||
/// JWT-SVID | ||
JwtSvid { | ||
/// Path to the Workload API socket | ||
workload_endpoint: String, | ||
/// List of service admins | ||
admins: Option<Vec<Admin>>, | ||
}, | ||
} | ||
|
||
/// Structure defining the properties of a service admin | ||
#[derive(Deserialize, Debug, Zeroize, Clone)] | ||
#[zeroize(drop)] | ||
pub struct Admin { | ||
name: String, | ||
} | ||
|
||
impl Admin { | ||
fn name(&self) -> &str { | ||
&self.name | ||
} | ||
} | ||
|
||
#[derive(Debug, Clone, Default)] | ||
struct AdminList(Vec<Admin>); | ||
|
||
impl AdminList { | ||
fn is_admin(&self, app_name: &str) -> bool { | ||
self.iter().any(|admin| admin.name() == app_name) | ||
} | ||
} | ||
|
||
impl Deref for AdminList { | ||
type Target = Vec<Admin>; | ||
|
||
fn deref(&self) -> &Self::Target { | ||
&self.0 | ||
} | ||
} | ||
|
||
impl From<Vec<Admin>> for AdminList { | ||
fn from(admin_list: Vec<Admin>) -> Self { | ||
AdminList(admin_list) | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👌