Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F3345591
D8911.id30317.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
12 KB
Referenced Files
None
Subscribers
None
D8911.id30317.diff
View Options
diff --git a/services/backup/Cargo.lock b/services/backup/Cargo.lock
--- a/services/backup/Cargo.lock
+++ b/services/backup/Cargo.lock
@@ -45,7 +45,7 @@
"actix-service",
"actix-utils",
"ahash 0.8.3",
- "base64",
+ "base64 0.21.2",
"bitflags 1.3.2",
"brotli",
"bytes",
@@ -236,6 +236,21 @@
"syn 1.0.109",
]
+[[package]]
+name = "actix-web-httpauth"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6dda62cf04bc3a9ad2ea8f314f721951cfdb4cdacec4e984d20e77c7bb170991"
+dependencies = [
+ "actix-utils",
+ "actix-web",
+ "base64 0.13.1",
+ "futures-core",
+ "futures-util",
+ "log",
+ "pin-project-lite",
+]
+
[[package]]
name = "addr2line"
version = "0.21.0"
@@ -754,6 +769,12 @@
"uuid",
]
+[[package]]
+name = "base64"
+version = "0.13.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8"
+
[[package]]
name = "base64"
version = "0.21.2"
@@ -928,10 +949,12 @@
"actix-cors",
"actix-multipart",
"actix-web",
+ "actix-web-httpauth",
"anyhow",
"aws-config",
"aws-sdk-dynamodb",
"aws-types",
+ "base64 0.21.2",
"chrono",
"derive_more",
"futures-core",
@@ -2051,7 +2074,7 @@
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3e9ad3fe7488d7e34558a2033d45a0c90b72d97b4f80705666fea71472e2e6a1"
dependencies = [
- "base64",
+ "base64 0.21.2",
"bytes",
"encoding_rs",
"futures-core",
@@ -2158,7 +2181,7 @@
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2d3987094b1d07b653b7dfdc3f70ce9a1da9c51ac18c1b06b662e4f9a0e9f4b2"
dependencies = [
- "base64",
+ "base64 0.21.2",
]
[[package]]
diff --git a/services/blob/Cargo.lock b/services/blob/Cargo.lock
--- a/services/blob/Cargo.lock
+++ b/services/blob/Cargo.lock
@@ -236,6 +236,21 @@
"syn 1.0.103",
]
+[[package]]
+name = "actix-web-httpauth"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6dda62cf04bc3a9ad2ea8f314f721951cfdb4cdacec4e984d20e77c7bb170991"
+dependencies = [
+ "actix-utils",
+ "actix-web",
+ "base64 0.13.1",
+ "futures-core",
+ "futures-util",
+ "log",
+ "pin-project-lite",
+]
+
[[package]]
name = "adler"
version = "1.0.2"
@@ -978,15 +993,19 @@
"actix-cors",
"actix-multipart",
"actix-web",
+ "actix-web-httpauth",
"anyhow",
"aws-config",
"aws-sdk-dynamodb",
"aws-types",
+ "base64 0.21.0",
"chrono",
"derive_more",
"futures-core",
"futures-util",
"http",
+ "serde",
+ "serde_json",
"tokio-stream",
"tracing",
]
diff --git a/services/comm-services-lib/Cargo.lock b/services/comm-services-lib/Cargo.lock
--- a/services/comm-services-lib/Cargo.lock
+++ b/services/comm-services-lib/Cargo.lock
@@ -45,7 +45,7 @@
"actix-service",
"actix-utils",
"ahash 0.8.3",
- "base64",
+ "base64 0.21.0",
"bitflags",
"brotli",
"bytes",
@@ -236,6 +236,21 @@
"syn 1.0.109",
]
+[[package]]
+name = "actix-web-httpauth"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6dda62cf04bc3a9ad2ea8f314f721951cfdb4cdacec4e984d20e77c7bb170991"
+dependencies = [
+ "actix-utils",
+ "actix-web",
+ "base64 0.13.1",
+ "futures-core",
+ "futures-util",
+ "log",
+ "pin-project-lite",
+]
+
[[package]]
name = "addr2line"
version = "0.21.0"
@@ -652,6 +667,12 @@
"rustc-demangle",
]
+[[package]]
+name = "base64"
+version = "0.13.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8"
+
[[package]]
name = "base64"
version = "0.21.0"
@@ -782,10 +803,12 @@
"actix-cors",
"actix-multipart",
"actix-web",
+ "actix-web-httpauth",
"anyhow",
"aws-config",
"aws-sdk-dynamodb",
"aws-types",
+ "base64 0.21.0",
"chrono",
"derive_more",
"futures-core",
@@ -1758,7 +1781,7 @@
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cde824a14b7c14f85caff81225f411faacc04a2013f41670f41443742b1c1c55"
dependencies = [
- "base64",
+ "base64 0.21.0",
"bytes",
"encoding_rs",
"futures-core",
@@ -1866,7 +1889,7 @@
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d194b56d58803a43635bdc398cd17e383d6f71f9182b9a192c127ca42494a59b"
dependencies = [
- "base64",
+ "base64 0.21.0",
]
[[package]]
diff --git a/services/comm-services-lib/Cargo.toml b/services/comm-services-lib/Cargo.toml
--- a/services/comm-services-lib/Cargo.toml
+++ b/services/comm-services-lib/Cargo.toml
@@ -9,8 +9,6 @@
"dep:reqwest",
"dep:futures-core",
"dep:futures-util",
- "dep:serde",
- "dep:serde_json",
"dep:tokio",
]
http = [
@@ -21,12 +19,16 @@
"dep:futures-util",
"dep:http",
"dep:tokio-stream",
+ "dep:actix-web-httpauth",
]
[dependencies]
+serde = { version = "1.0", features = ["derive"] }
+serde_json = "1.0"
aws-config = "0.55"
aws-sdk-dynamodb = "0.27"
aws-types = "0.55"
+base64 = "0.21"
chrono = "0.4"
derive_more = "0.99"
tracing = "0.1"
@@ -39,12 +41,11 @@
"multipart",
"stream",
], optional = true }
-serde = { version = "1.0", features = ["derive"], optional = true }
-serde_json = { version = "1.0", optional = true }
tokio = { version = "1.32", optional = true }
# http dependencies
actix-cors = { version = "0.6", optional = true }
actix-web = { version = "4.3", optional = true }
http = { version = "0.2.9", optional = true }
+actix-web-httpauth = { version = "0.8.0", optional = true }
actix-multipart = { version = "0.6", optional = true }
tokio-stream = { version = "0.1.14", optional = true }
diff --git a/services/comm-services-lib/src/auth.rs b/services/comm-services-lib/src/auth.rs
new file mode 100644
--- /dev/null
+++ b/services/comm-services-lib/src/auth.rs
@@ -0,0 +1,79 @@
+use base64::{prelude::BASE64_STANDARD, Engine};
+use derive_more::{Display, Error, From};
+use serde::{Deserialize, Serialize};
+use std::{str::FromStr, string::FromUtf8Error};
+
+/// This implements [`actix_web::FromRequest`], so it can be used to extract user
+/// identity information from HTTP requests.
+/// # Example
+/// ```ignore
+/// pub async fn request_handler(
+/// user: UserIdentity,
+/// ) -> Result<HttpResponse> {
+/// Ok(HttpResponse::Ok().finish())
+/// }
+/// ```
+#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
+pub struct UserIdentity {
+ #[serde(rename = "userID")]
+ pub user_id: String,
+ #[serde(rename = "accessToken")]
+ pub access_token: String,
+ #[serde(rename = "deviceID")]
+ pub device_id: String,
+}
+
+impl UserIdentity {
+ /// Gets the access token value, usable in bearer authorization
+ ///
+ /// # Example
+ /// ```ignore
+ /// reqwest::get("url").beaerer_auth(user.as_authorization_token()?).send().await?;
+ /// ```
+ pub fn as_authorization_token(&self) -> Result<String, serde_json::Error> {
+ let json = serde_json::to_string(self)?;
+ let base64_str = BASE64_STANDARD.encode(json);
+ Ok(base64_str)
+ }
+}
+
+#[derive(Debug, Display, Error, From)]
+pub enum UserIdentityParseError {
+ Base64DecodeError(base64::DecodeError),
+ Utf8DecodeError(FromUtf8Error),
+ JsonParseError(serde_json::Error),
+}
+
+/// Parsing of [UserIdentity] from bearer token
+impl FromStr for UserIdentity {
+ type Err = UserIdentityParseError;
+
+ fn from_str(s: &str) -> Result<Self, Self::Err> {
+ let bytes = BASE64_STANDARD.decode(s)?;
+ let text = String::from_utf8(bytes)?;
+ let user = serde_json::from_str(&text)?;
+ Ok(user)
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn test_user_identity_parsing() {
+ let identity = UserIdentity {
+ user_id: "user".to_string(),
+ access_token: "token".to_string(),
+ device_id: "device".to_string(),
+ };
+ let json =
+ r#"{"userID": "user", "accessToken": "token", "deviceID": "device"}"#;
+ let encoded = BASE64_STANDARD.encode(json);
+
+ let parsed_identity = encoded.parse::<UserIdentity>();
+ assert!(parsed_identity.is_ok(), "Parse error: {parsed_identity:?}");
+
+ assert_eq!(parsed_identity.unwrap(), identity);
+ }
+}
diff --git a/services/comm-services-lib/src/http.rs b/services/comm-services-lib/src/http.rs
--- a/services/comm-services-lib/src/http.rs
+++ b/services/comm-services-lib/src/http.rs
@@ -1,3 +1,4 @@
+pub mod auth;
pub mod multipart;
use crate::tools::BoxedError;
diff --git a/services/comm-services-lib/src/http/auth.rs b/services/comm-services-lib/src/http/auth.rs
new file mode 100644
--- /dev/null
+++ b/services/comm-services-lib/src/http/auth.rs
@@ -0,0 +1,108 @@
+use actix_web::{
+ body::{EitherBody, MessageBody},
+ dev::{Service, ServiceRequest, ServiceResponse, Transform},
+ FromRequest, HttpMessage,
+};
+use actix_web_httpauth::{
+ extractors::{bearer::BearerAuth, AuthenticationError},
+ headers::www_authenticate::bearer::Bearer,
+ middleware::HttpAuthentication,
+};
+use std::{
+ future::{ready, Ready},
+ str::FromStr,
+};
+use tracing::debug;
+
+use crate::auth::UserIdentity;
+
+impl FromRequest for UserIdentity {
+ type Error = actix_web::Error;
+ type Future = Ready<Result<Self, Self::Error>>;
+
+ fn from_request(
+ req: &actix_web::HttpRequest,
+ _: &mut actix_web::dev::Payload,
+ ) -> Self::Future {
+ if let Some(user) = req.extensions().get::<UserIdentity>() {
+ return ready(Ok(user.clone()));
+ }
+
+ let f = || {
+ let bearer = BearerAuth::extract(req).into_inner()?;
+ let user = match UserIdentity::from_str(bearer.token()) {
+ Ok(user) => user,
+ Err(err) => {
+ debug!("HTTP authorization error: {err}");
+ return Err(AuthenticationError::new(Bearer::default()).into());
+ }
+ };
+
+ Ok(user)
+ };
+
+ ready(f())
+ }
+}
+
+pub async fn validation_function(
+ req: ServiceRequest,
+ bearer: BearerAuth,
+) -> Result<ServiceRequest, (actix_web::Error, ServiceRequest)> {
+ let user = match UserIdentity::from_str(bearer.token()) {
+ Ok(user) => user,
+ Err(err) => {
+ debug!("HTTP authorization error: {err}");
+ return Err((AuthenticationError::new(Bearer::default()).into(), req));
+ }
+ };
+
+ // TODO: call identity service, for now just allow every request
+ req.extensions_mut().insert(user);
+
+ Ok(req)
+}
+
+/// Use this to add Authentication Middleware. It's going to parse Authorization
+/// header and call the identity service to check if the provided credentials
+/// are correct. If not it's going to reject the request.
+///
+/// # Example
+/// ```ignore
+/// let auth_middleware = get_comm_authentication_middleware();
+/// App::new().wrap(auth_middleware);
+/// ```
+/// If you don't want all of the routes to require authentication you can wrap
+/// individual resources or scopes:
+/// ```ignore
+/// App::new().service(
+/// web::resource("/endpoint").route(web::get().to(handler)).wrap(auth_middleware),
+/// )
+/// ```
+// This type is very complicated, but unfortunately typing this directly
+// requires https://github.com/rust-lang/rust/issues/99697 to be merged.
+// The issue is that we can't specify the second generic argument of
+// HttpAuthentication<T, F>, because it look something like this:
+// ```
+// impl Fn(ServiceRequest, BearerAuth) -> impl Future<
+// Output = Result<ServiceRequest, (actix_web::Error, ServiceRequest)>,
+// >
+// ``
+// which isn't valid (until the linked issue is merged).
+pub fn get_comm_authentication_middleware<B, S>() -> impl Transform<
+ S,
+ ServiceRequest,
+ Response = ServiceResponse<EitherBody<B>>,
+ Error = actix_web::Error,
+ InitError = (),
+> + 'static
+where
+ B: MessageBody + 'static,
+ S: Service<
+ ServiceRequest,
+ Response = ServiceResponse<B>,
+ Error = actix_web::Error,
+ > + 'static,
+{
+ HttpAuthentication::bearer(validation_function)
+}
diff --git a/services/comm-services-lib/src/lib.rs b/services/comm-services-lib/src/lib.rs
--- a/services/comm-services-lib/src/lib.rs
+++ b/services/comm-services-lib/src/lib.rs
@@ -1,3 +1,4 @@
+pub mod auth;
pub mod blob;
pub mod constants;
pub mod database;
diff --git a/services/feature-flags/Cargo.lock b/services/feature-flags/Cargo.lock
--- a/services/feature-flags/Cargo.lock
+++ b/services/feature-flags/Cargo.lock
@@ -731,8 +731,11 @@
"aws-config",
"aws-sdk-dynamodb",
"aws-types",
+ "base64",
"chrono",
"derive_more",
+ "serde",
+ "serde_json",
"tracing",
]
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Sat, Nov 23, 6:28 AM (19 h, 3 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
2561172
Default Alt Text
D8911.id30317.diff (12 KB)
Attached To
Mode
D8911: [services-lib] Add authorization http middleware
Attached
Detach File
Event Timeline
Log In to Comment