-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(security): add support for operator users and operator ephemeral…
… service accounts
- Loading branch information
Showing
13 changed files
with
143 additions
and
33 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -327,6 +327,7 @@ mod tests { | |
security: SecurityConfig { | ||
session_cookie_name: "id2", | ||
jwt_secret: None, | ||
operators: None, | ||
preconfigured_users: Some( | ||
{ | ||
"[email protected]": PreconfiguredUserConfig { | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,6 @@ | ||
use crate::users::SubscriptionTier; | ||
use serde_derive::{Deserialize, Serialize}; | ||
use std::collections::HashMap; | ||
use std::collections::{HashMap, HashSet}; | ||
|
||
/// Describes the preconfigured user configuration. | ||
#[derive(Deserialize, Serialize, Debug, Clone, PartialEq)] | ||
|
@@ -19,6 +19,8 @@ pub struct SecurityConfig { | |
/// Secret key used to sign JWT tokens used for HTTP authentication. If not provided, HTTP | ||
/// authentication will be disabled. | ||
pub jwt_secret: Option<String>, | ||
/// List of user or service account identifiers that should be treated as operators, if specified. | ||
pub operators: Option<HashSet<String>>, | ||
/// List of the preconfigured users, if specified. | ||
pub preconfigured_users: Option<HashMap<String, PreconfiguredUserConfig>>, | ||
} | ||
|
@@ -29,6 +31,7 @@ impl Default for SecurityConfig { | |
session_cookie_name: "id".to_string(), | ||
jwt_secret: None, | ||
preconfigured_users: None, | ||
operators: None, | ||
} | ||
} | ||
} | ||
|
@@ -46,7 +49,9 @@ mod tests { | |
assert_toml_snapshot!(SecurityConfig::default(), @"session_cookie_name = 'id'"); | ||
|
||
let config = SecurityConfig { | ||
session_cookie_name: "id".to_string(), | ||
jwt_secret: Some("3024bf8975b03b84e405f36a7bacd1c1".to_string()), | ||
operators: Some(["[email protected]".to_string()].into_iter().collect()), | ||
preconfigured_users: Some( | ||
[( | ||
"[email protected]".to_string(), | ||
|
@@ -58,12 +63,12 @@ mod tests { | |
.into_iter() | ||
.collect(), | ||
), | ||
..Default::default() | ||
}; | ||
|
||
assert_toml_snapshot!(config, @r###" | ||
session_cookie_name = 'id' | ||
jwt_secret = '3024bf8975b03b84e405f36a7bacd1c1' | ||
operators = ['[email protected]'] | ||
[preconfigured_users."[email protected]"] | ||
handle = 'test-handle' | ||
tier = 'basic' | ||
|
@@ -85,13 +90,15 @@ mod tests { | |
session_cookie_name: "id".to_string(), | ||
jwt_secret: None, | ||
preconfigured_users: None, | ||
operators: None, | ||
} | ||
); | ||
|
||
let config: SecurityConfig = toml::from_str( | ||
r#" | ||
session_cookie_name = 'id' | ||
jwt_secret = '3024bf8975b03b84e405f36a7bacd1c1' | ||
operators = ['[email protected]'] | ||
[preconfigured_users."[email protected]"] | ||
handle = 'test-handle' | ||
|
@@ -115,6 +122,7 @@ mod tests { | |
.into_iter() | ||
.collect(), | ||
), | ||
operators: Some(["[email protected]".to_string()].into_iter().collect()), | ||
..Default::default() | ||
} | ||
); | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
/// Struct to represent an operator account. | ||
#[derive(Debug, Clone, PartialEq, Eq, Hash)] | ||
pub struct Operator(String); | ||
impl Operator { | ||
/// Creates a new operator account with the provided ID. | ||
pub fn new(id: impl Into<String>) -> Self { | ||
Self(id.into()) | ||
} | ||
|
||
/// Returns the ID of the operator account. | ||
pub fn id(&self) -> &str { | ||
&self.0 | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,4 @@ | ||
mod credentials; | ||
mod operator; | ||
mod user; | ||
mod user_share; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
use crate::{security::Credentials, server::app_state::AppState}; | ||
use actix_web::{dev::Payload, error::ErrorUnauthorized, web, Error, FromRequest, HttpRequest}; | ||
use actix_web_httpauth::extractors::bearer::BearerAuth; | ||
use anyhow::anyhow; | ||
use std::{future::Future, pin::Pin}; | ||
|
||
impl FromRequest for Credentials { | ||
type Error = Error; | ||
type Future = Pin<Box<dyn Future<Output = Result<Self, Self::Error>>>>; | ||
|
||
fn from_request(req: &HttpRequest, _: &mut Payload) -> Self::Future { | ||
let req = req.clone(); | ||
Box::pin(async move { | ||
let state = web::Data::<AppState>::extract(&req).await?; | ||
Ok(match Option::<BearerAuth>::extract(&req).await? { | ||
Some(bearer_auth) => Credentials::Jwt(bearer_auth.token().to_string()), | ||
None => Credentials::SessionCookie( | ||
req.cookie(&state.config.security.session_cookie_name) | ||
.ok_or_else(|| ErrorUnauthorized(anyhow!("Unauthorized")))?, | ||
), | ||
}) | ||
}) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
use crate::{ | ||
security::{Credentials, Operator}, | ||
server::app_state::AppState, | ||
}; | ||
use actix_web::{ | ||
dev::Payload, | ||
error::{ErrorInternalServerError, ErrorUnauthorized}, | ||
web, Error, FromRequest, HttpRequest, | ||
}; | ||
use anyhow::anyhow; | ||
use std::{future::Future, pin::Pin}; | ||
|
||
impl FromRequest for Operator { | ||
type Error = Error; | ||
type Future = Pin<Box<dyn Future<Output = Result<Self, Self::Error>>>>; | ||
|
||
fn from_request(req: &HttpRequest, _: &mut Payload) -> Self::Future { | ||
let req = req.clone(); | ||
Box::pin(async move { | ||
let state = web::Data::<AppState>::extract(&req).await?; | ||
let credentials = Credentials::extract(&req).await?; | ||
match state.api.security().get_operator(credentials).await { | ||
Ok(Some(user)) => Ok(user), | ||
Ok(None) => Err(ErrorUnauthorized(anyhow!("Unauthorized"))), | ||
Err(err) => { | ||
log::error!("Failed to extract operator information due to: {err:?}"); | ||
Err(ErrorInternalServerError(anyhow!("Internal server error"))) | ||
} | ||
} | ||
}) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters