From 9f3fd659d2d104d877c434f5a8e75657642d5c2d Mon Sep 17 00:00:00 2001
From: Awiteb
Date: Sat, 27 Jul 2024 01:53:17 +0300
Subject: [PATCH 1/9] chore: Move `serde_json` to be shared dependencie
Signed-off-by: Awiteb
---
Cargo.toml | 3 ++-
crates/oxidetalis/Cargo.toml | 2 +-
2 files changed, 3 insertions(+), 2 deletions(-)
diff --git a/Cargo.toml b/Cargo.toml
index 6417005..8686106 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -22,8 +22,9 @@ thiserror = "1.0.61"
log = "0.4.21"
logcall = "0.1.9"
chrono = "0.4.38"
+serde_json = "1.0.117"
sea-orm = { version = "0.12.15", features = ["with-chrono", "macros"] }
-salvo_core = { version = "0.68.3", default-features = false, features = ["rustls"] }
+salvo_core = { version = "0.68.3", default-features = false }
salvo-oapi = { version = "0.68.3", default-features = false, features = ["rapidoc","redoc","scalar","swagger-ui"] }
[profile.release]
diff --git a/crates/oxidetalis/Cargo.toml b/crates/oxidetalis/Cargo.toml
index 64881f7..f405995 100644
--- a/crates/oxidetalis/Cargo.toml
+++ b/crates/oxidetalis/Cargo.toml
@@ -21,12 +21,12 @@ sea-orm = { workspace = true }
serde = { workspace = true }
thiserror = { workspace = true }
chrono = { workspace = true }
+serde_json = { workspace = true }
salvo = { version = "0.68.2", features = ["rustls", "affix", "logging", "oapi", "rate-limiter", "websocket"] }
tokio = { version = "1.38.0", features = ["macros", "rt-multi-thread"] }
uuid = { version = "1.9.1", default-features = false, features = ["v4"] }
derive-new = "0.6.0"
pretty_env_logger = "0.5.0"
-serde_json = "1.0.117"
once_cell = "1.19.0"
futures = "0.3.30"
rayon = "1.10.0"
--
2.45.2
From 6fc14d380bbfe36565692fb829b5c2336456d6b6 Mon Sep 17 00:00:00 2001
From: Awiteb
Date: Sat, 27 Jul 2024 01:54:35 +0300
Subject: [PATCH 2/9] chore: Add `serde_json` & `salvo_core` to `core`
dependencies
Signed-off-by: Awiteb
---
Cargo.lock | 2 ++
crates/oxidetalis_core/Cargo.toml | 4 +++-
2 files changed, 5 insertions(+), 1 deletion(-)
diff --git a/Cargo.lock b/Cargo.lock
index 8e6aea0..7a844d1 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1954,8 +1954,10 @@ dependencies = [
"k256",
"rand",
"salvo-oapi",
+ "salvo_core",
"sea-orm",
"serde",
+ "serde_json",
"sha2",
"thiserror",
]
diff --git a/crates/oxidetalis_core/Cargo.toml b/crates/oxidetalis_core/Cargo.toml
index d112c81..dc11a40 100644
--- a/crates/oxidetalis_core/Cargo.toml
+++ b/crates/oxidetalis_core/Cargo.toml
@@ -16,6 +16,8 @@ thiserror = { workspace = true }
salvo-oapi = { workspace = true, optional = true }
serde = { workspace = true, optional = true }
sea-orm = { workspace = true, optional = true }
+salvo_core = { workspace = true, optional = true }
+serde_json = { workspace = true, optional = true }
cbc = { version = "0.1.2", features = ["alloc", "std"] }
k256 = { version = "0.13.3", default-features = false, features = ["ecdh"] }
rand = { version = "0.8.5", default-features = false, features = ["std_rng", "std"] }
@@ -25,7 +27,7 @@ hmac = "0.12.1"
sha2 = "0.10.8"
[features]
-openapi = ["dep:salvo-oapi"]
+openapi = ["dep:salvo-oapi", "dep:salvo_core", "dep:serde_json"]
serde = ["dep:serde"]
sea-orm = ["dep:sea-orm"]
--
2.45.2
From 2b282cbec664d7c37a48e52d7208c15dda11a51d Mon Sep 17 00:00:00 2001
From: Awiteb
Date: Sat, 27 Jul 2024 02:06:12 +0300
Subject: [PATCH 3/9] remove: Remove Public key middleware `public_key_check`
Signed-off-by: Awiteb
---
crates/oxidetalis/src/middlewares/mod.rs | 2 --
.../oxidetalis/src/middlewares/public_key.rs | 32 -------------------
crates/oxidetalis/src/routes/user.rs | 1 -
crates/oxidetalis/src/websocket/mod.rs | 1 -
4 files changed, 36 deletions(-)
delete mode 100644 crates/oxidetalis/src/middlewares/public_key.rs
diff --git a/crates/oxidetalis/src/middlewares/mod.rs b/crates/oxidetalis/src/middlewares/mod.rs
index 8ec3c79..cdd26ae 100644
--- a/crates/oxidetalis/src/middlewares/mod.rs
+++ b/crates/oxidetalis/src/middlewares/mod.rs
@@ -24,10 +24,8 @@ use salvo::{
Response,
};
-mod public_key;
mod signature;
-pub use public_key::*;
pub use signature::*;
use crate::{routes::write_json_body, schemas::MessageSchema};
diff --git a/crates/oxidetalis/src/middlewares/public_key.rs b/crates/oxidetalis/src/middlewares/public_key.rs
deleted file mode 100644
index a811db6..0000000
--- a/crates/oxidetalis/src/middlewares/public_key.rs
+++ /dev/null
@@ -1,32 +0,0 @@
-// OxideTalis Messaging Protocol homeserver implementation
-// Copyright (C) 2024 Awiteb , OxideTalis Contributors
-//
-// This program is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Affero General Public License as
-// published by the Free Software Foundation, either version 3 of the
-// License, or (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Affero General Public License for more details.
-//
-// You should have received a copy of the GNU Affero General Public License
-// along with this program. If not, see .
-
-//! Request sender public key middleware.
-
-use salvo::{handler, http::StatusCode, FlowCtrl, Request, Response};
-
-use crate::utils;
-
-/// Middleware to check the public key of the request sender.
-///
-/// If the public key is valid, the request will be passed to the next handler.
-/// Otherwise, a 401 Unauthorized response will be returned.
-#[handler]
-pub async fn public_key_check(req: &mut Request, res: &mut Response, ctrl: &mut FlowCtrl) {
- if let Err(err) = utils::extract_public_key(req) {
- super::write_error(res, ctrl, err.to_string(), StatusCode::UNAUTHORIZED)
- }
-}
diff --git a/crates/oxidetalis/src/routes/user.rs b/crates/oxidetalis/src/routes/user.rs
index ec57da2..cca02f7 100644
--- a/crates/oxidetalis/src/routes/user.rs
+++ b/crates/oxidetalis/src/routes/user.rs
@@ -150,6 +150,5 @@ pub fn route() -> Router {
.push(Router::with_path("register").post(register))
.push(Router::with_path("whitelist").get(user_whitelist))
.push(Router::with_path("blacklist").get(user_blacklist))
- .hoop(middlewares::public_key_check)
.hoop(middlewares::signature_check)
}
diff --git a/crates/oxidetalis/src/websocket/mod.rs b/crates/oxidetalis/src/websocket/mod.rs
index 9f93b2a..c1887cc 100644
--- a/crates/oxidetalis/src/websocket/mod.rs
+++ b/crates/oxidetalis/src/websocket/mod.rs
@@ -264,5 +264,4 @@ pub fn route() -> Router {
Router::new()
.push(Router::with_path("chat").get(user_connected))
.hoop(middlewares::signature_check)
- .hoop(middlewares::public_key_check)
}
--
2.45.2
From 1d0268c501c7e2c571a472b0f171f9abab38560a Mon Sep 17 00:00:00 2001
From: Awiteb
Date: Sat, 27 Jul 2024 02:09:21 +0300
Subject: [PATCH 4/9] chore: Imple openAPI traits for `PublicKey` and
`Signature`
Signed-off-by: Awiteb
---
crates/oxidetalis_core/src/types/cipher.rs | 29 ----
.../oxidetalis_core/src/types/impl_openapi.rs | 162 ++++++++++++++++++
crates/oxidetalis_core/src/types/mod.rs | 2 +
3 files changed, 164 insertions(+), 29 deletions(-)
create mode 100644 crates/oxidetalis_core/src/types/impl_openapi.rs
diff --git a/crates/oxidetalis_core/src/types/cipher.rs b/crates/oxidetalis_core/src/types/cipher.rs
index 8aa8aa5..b2f50d8 100644
--- a/crates/oxidetalis_core/src/types/cipher.rs
+++ b/crates/oxidetalis_core/src/types/cipher.rs
@@ -24,15 +24,6 @@
use std::{fmt, str::FromStr};
use base58::{FromBase58, ToBase58};
-#[cfg(feature = "openapi")]
-use salvo_oapi::{
- schema::{
- Schema as OapiSchema,
- SchemaFormat as OapiSchemaFormat,
- SchemaType as OapiSchemaType,
- },
- ToSchema,
-};
use crate::cipher::{hmac_sha256, CipherError};
@@ -206,23 +197,3 @@ impl From<[u8; 56]> for Signature {
}
}
}
-
-#[cfg(feature = "openapi")]
-impl ToSchema for PublicKey {
- fn to_schema(_components: &mut salvo_oapi::Components) -> salvo_oapi::RefOr {
- salvo_oapi::Object::new()
- .schema_type(OapiSchemaType::String)
- .format(OapiSchemaFormat::Custom("base58".to_owned()))
- .into()
- }
-}
-
-#[cfg(feature = "openapi")]
-impl ToSchema for Signature {
- fn to_schema(_components: &mut salvo_oapi::Components) -> salvo_oapi::RefOr {
- salvo_oapi::Object::new()
- .schema_type(OapiSchemaType::String)
- .format(OapiSchemaFormat::Custom("hex".to_owned()))
- .into()
- }
-}
diff --git a/crates/oxidetalis_core/src/types/impl_openapi.rs b/crates/oxidetalis_core/src/types/impl_openapi.rs
new file mode 100644
index 0000000..b22fbfe
--- /dev/null
+++ b/crates/oxidetalis_core/src/types/impl_openapi.rs
@@ -0,0 +1,162 @@
+// OxideTalis Messaging Protocol homeserver core implementation
+// Copyright (C) 2024 Awiteb , OxideTalis Contributors
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+
+//! OpenAPI schema for some core types.
+
+use std::str::FromStr;
+
+use salvo_core::{extract::Metadata as ExtractMetadata, http::StatusError, Extractible, Request};
+use salvo_oapi::{
+ schema::{
+ Schema as OapiSchema,
+ SchemaFormat as OapiSchemaFormat,
+ SchemaType as OapiSchemaType,
+ },
+ Components as OapiComponents,
+ EndpointArgRegister,
+ Parameter,
+ ParameterIn,
+ Parameters,
+ ToParameters,
+ ToSchema,
+};
+
+use super::{PublicKey as CorePublicKey, Signature};
+
+impl ToSchema for CorePublicKey {
+ fn to_schema(_components: &mut salvo_oapi::Components) -> salvo_oapi::RefOr {
+ salvo_oapi::Object::new()
+ .name(crate::PUBLIC_KEY_HEADER)
+ .description("User's public key")
+ .schema_type(OapiSchemaType::String)
+ .format(OapiSchemaFormat::Custom("base58".to_owned()))
+ .required(crate::PUBLIC_KEY_HEADER)
+ // A 33-byte base58 string can be either 44 or 45 characters long
+ .example("rW8FMG5D75NVNJV3Wd498dEh65BgUuhwY1Yk5zYJPpRe".into())
+ .max_length(45)
+ .min_length(44)
+ .into()
+ }
+}
+
+impl ToSchema for Signature {
+ fn to_schema(_components: &mut salvo_oapi::Components) -> salvo_oapi::RefOr {
+ salvo_oapi::Object::new()
+ .name(crate::SIGNATURE_HEADER)
+ .description("Signature of the request")
+ .schema_type(OapiSchemaType::String)
+ .format(OapiSchemaFormat::Custom("hex".to_owned()))
+ .required(crate::SIGNATURE_HEADER)
+ // 56 bytes in hex (valid signature)
+ .example("0".repeat(112).into())
+ .max_length(112)
+ .min_length(112)
+ .into()
+ }
+}
+
+impl<'ex> Extractible<'ex> for CorePublicKey {
+ fn metadata() -> &'ex ExtractMetadata {
+ static METADATA: ExtractMetadata = ExtractMetadata::new("");
+ &METADATA
+ }
+
+ #[allow(refining_impl_trait)]
+ async fn extract(req: &'ex mut Request) -> Result {
+ extract_header(req, crate::PUBLIC_KEY_HEADER).and_then(|public_key| {
+ CorePublicKey::from_str(public_key).map_err(|err| {
+ StatusError::bad_request()
+ .brief("Invalid public key")
+ .cause(err.to_string())
+ })
+ })
+ }
+
+ #[allow(refining_impl_trait)]
+ async fn extract_with_arg(req: &'ex mut Request, _: &str) -> Result {
+ Self::extract(req).await
+ }
+}
+
+impl EndpointArgRegister for CorePublicKey {
+ fn register(components: &mut OapiComponents, operation: &mut salvo_oapi::Operation, _: &str) {
+ operation.parameters.insert(
+ Parameter::new(crate::PUBLIC_KEY_HEADER)
+ .parameter_in(ParameterIn::Header)
+ .required(true)
+ .description("User's public key")
+ .example("2BiUSWkJUy5bcdJB8qszq9K6a5EXVHvK41vQWZVkUBUM8".into())
+ .schema(CorePublicKey::to_schema(components)),
+ )
+ }
+}
+
+impl<'ex> Extractible<'ex> for Signature {
+ fn metadata() -> &'ex ExtractMetadata {
+ unreachable!(
+ "
+ `Extractible` is required to implement `ToParameters` for `Signature`, but \
+ Salvo does not need it actually, see https://github.com/salvo-rs/salvo/issues/838"
+ )
+ }
+
+ #[allow(refining_impl_trait)]
+ async fn extract(_: &'ex mut Request) -> Result {
+ unreachable!(
+ "
+ `Extractible` is required to implement `ToParameters` for `Signature`, but \
+ Salvo does not need it actually, see https://github.com/salvo-rs/salvo/issues/838"
+ )
+ }
+}
+
+impl ToParameters<'_> for Signature {
+ fn to_parameters(components: &mut OapiComponents) -> Parameters {
+ Parameters::new().parameter(
+ Parameter::new(crate::SIGNATURE_HEADER)
+ .parameter_in(ParameterIn::Header)
+ .required(true)
+ .description("Signature of the request")
+ .example("0".repeat(112).into())
+ .schema(Self::to_schema(components)),
+ )
+ }
+}
+
+fn extract_header<'req>(req: &'req Request, name: &str) -> Result<&'req str, StatusError> {
+ req.headers()
+ .get(name)
+ .map(|v| {
+ v.to_str().map_err(|_| {
+ StatusError::bad_request()
+ .brief("Invalid header value")
+ .cause("Header value must be a valid ascii string")
+ })
+ })
+ .transpose()?
+ .ok_or_else(|| {
+ StatusError::bad_request()
+ .brief(format!("Could not find {name} in headers"))
+ .cause(format!(
+ "{name} is required to authenication and authorization"
+ ))
+ })
+}
diff --git a/crates/oxidetalis_core/src/types/mod.rs b/crates/oxidetalis_core/src/types/mod.rs
index 7635eb3..a2257f1 100644
--- a/crates/oxidetalis_core/src/types/mod.rs
+++ b/crates/oxidetalis_core/src/types/mod.rs
@@ -22,6 +22,8 @@
//! Oxidetalis server types
mod cipher;
+#[cfg(feature = "openapi")]
+mod impl_openapi;
#[cfg(feature = "sea-orm")]
mod impl_sea_orm;
#[cfg(feature = "serde")]
--
2.45.2
From 531e27ab2c67e0996b153f68a16fefde38e4f2cb Mon Sep 17 00:00:00 2001
From: Awiteb
Date: Sat, 27 Jul 2024 02:46:03 +0300
Subject: [PATCH 5/9] chore: Use `Signature` as a openAPI parameter
Signed-off-by: Awiteb
---
crates/oxidetalis/src/routes/user.rs | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/crates/oxidetalis/src/routes/user.rs b/crates/oxidetalis/src/routes/user.rs
index cca02f7..252aa76 100644
--- a/crates/oxidetalis/src/routes/user.rs
+++ b/crates/oxidetalis/src/routes/user.rs
@@ -45,7 +45,7 @@ use crate::{
(status_code = 500, description = "Internal server error", content_type = "application/json", body = MessageSchema),
),
parameters(
- ("X-OTMP-SIGNATURE" = Signature, Header, description = "Signature of the request"),
+ Signature,
("X-OTMP-PUBLIC" = PublicKey, Header, description = "Public key of the sender"),
),
)]
@@ -80,7 +80,7 @@ pub async fn register(req: &Request, depot: &mut Depot) -> ApiResult
Date: Sat, 27 Jul 2024 02:47:17 +0300
Subject: [PATCH 6/9] chore: Use `PublicKey` as an argument and openapi doc
Signed-off-by: Awiteb
---
crates/oxidetalis/src/routes/user.rs | 49 +++++++++-----------------
crates/oxidetalis/src/websocket/mod.rs | 5 ++-
2 files changed, 18 insertions(+), 36 deletions(-)
diff --git a/crates/oxidetalis/src/routes/user.rs b/crates/oxidetalis/src/routes/user.rs
index 252aa76..bace030 100644
--- a/crates/oxidetalis/src/routes/user.rs
+++ b/crates/oxidetalis/src/routes/user.rs
@@ -17,7 +17,7 @@
//! REST API endpoints for user management
use oxidetalis_core::types::{PublicKey, Signature};
-use salvo::{http::StatusCode, oapi::endpoint, writing::Json, Depot, Request, Router, Writer};
+use salvo::{http::StatusCode, oapi::endpoint, writing::Json, Depot, Router, Writer};
use super::{ApiError, ApiResult};
use crate::{
@@ -26,7 +26,6 @@ use crate::{
middlewares,
parameters::Pagination,
schemas::{BlackListedUser, EmptySchema, MessageSchema, WhiteListedUser},
- utils,
};
/// (🔓) Register a user
@@ -38,22 +37,18 @@ use crate::{
tags("User"),
responses(
(status_code = 201, description = "User registered"),
- (status_code = 400, description = "The entered public key is already registered", content_type = "application/json", body = MessageSchema),
- (status_code = 401, description = "The entered signature or public key is invalid", content_type = "application/json", body = MessageSchema),
+ (status_code = 400, description = "Invalid public key", content_type = "application/json", body = MessageSchema),
+ (status_code = 401, description = "Invalid signature", content_type = "application/json", body = MessageSchema),
(status_code = 403, description = "Server registration is closed", content_type = "application/json", body = MessageSchema),
+ (status_code = 409, description = "The entered public key is already registered", content_type = "application/json", body = MessageSchema),
(status_code = 429, description = "Too many requests", content_type = "application/json", body = MessageSchema),
(status_code = 500, description = "Internal server error", content_type = "application/json", body = MessageSchema),
),
- parameters(
- Signature,
- ("X-OTMP-PUBLIC" = PublicKey, Header, description = "Public key of the sender"),
- ),
+ parameters(Signature),
)]
-pub async fn register(req: &Request, depot: &mut Depot) -> ApiResult {
+pub async fn register(public_key: PublicKey, depot: &mut Depot) -> ApiResult {
let db = depot.db_conn();
let config = depot.config();
- let public_key =
- utils::extract_public_key(req).expect("Public key should be checked in the middleware");
if !db.users_exists_in_database().await? {
db.register_user(&public_key, true).await?;
@@ -72,28 +67,22 @@ pub async fn register(req: &Request, depot: &mut Depot) -> ApiResult),
- (status_code = 400, description = "Wrong query parameter", content_type = "application/json", body = MessageSchema),
- (status_code = 401, description = "The entered signature or public key is invalid", content_type = "application/json", body = MessageSchema),
+ (status_code = 400, description = "Invalid parameters or public key", content_type = "application/json", body = MessageSchema),
+ (status_code = 401, description = "Invalid signature", content_type = "application/json", body = MessageSchema),
(status_code = 403, description = "Not registered user, must register first", content_type = "application/json", body = MessageSchema),
(status_code = 429, description = "Too many requests", content_type = "application/json", body = MessageSchema),
(status_code = 500, description = "Internal server error", content_type = "application/json", body = MessageSchema),
),
- parameters(
- ("X-OTMP-PUBLIC" = PublicKey, Header, description = "Public key of the sender"),
- Signature,
- ),
+ parameters(Signature),
)]
async fn user_whitelist(
- req: &mut Request,
depot: &mut Depot,
pagination: Pagination,
+ public_key: PublicKey,
) -> ApiResult>> {
let conn = depot.db_conn();
let user = conn
- .get_user_by_pubk(
- &utils::extract_public_key(req)
- .expect("Public key should be checked in the middleware"),
- )
+ .get_user_by_pubk(&public_key)
.await?
.ok_or(ApiError::NotRegisteredUser)?;
Ok(Json(
@@ -111,28 +100,22 @@ async fn user_whitelist(
tags("User"),
responses(
(status_code = 200, description = "Returns blacklisted users", content_type = "application/json", body = Vec),
- (status_code = 400, description = "Wrong query parameter", content_type = "application/json", body = MessageSchema),
- (status_code = 401, description = "The entered signature or public key is invalid", content_type = "application/json", body = MessageSchema),
+ (status_code = 400, description = "Invalid parameters or public key", content_type = "application/json", body = MessageSchema),
+ (status_code = 401, description = "Invalid signature", content_type = "application/json", body = MessageSchema),
(status_code = 403, description = "Not registered user, must register first", content_type = "application/json", body = MessageSchema),
(status_code = 429, description = "Too many requests", content_type = "application/json", body = MessageSchema),
(status_code = 500, description = "Internal server error", content_type = "application/json", body = MessageSchema),
),
- parameters(
- ("X-OTMP-PUBLIC" = PublicKey, Header, description = "Public key of the sender"),
- Signature,
- ),
+ parameters(Signature),
)]
async fn user_blacklist(
- req: &mut Request,
depot: &mut Depot,
pagination: Pagination,
+ public_key: PublicKey,
) -> ApiResult>> {
let conn = depot.db_conn();
let user = conn
- .get_user_by_pubk(
- &utils::extract_public_key(req)
- .expect("Public key should be checked in the middleware"),
- )
+ .get_user_by_pubk(&public_key)
.await?
.ok_or(ApiError::NotRegisteredUser)?;
Ok(Json(
diff --git a/crates/oxidetalis/src/websocket/mod.rs b/crates/oxidetalis/src/websocket/mod.rs
index c1887cc..5284172 100644
--- a/crates/oxidetalis/src/websocket/mod.rs
+++ b/crates/oxidetalis/src/websocket/mod.rs
@@ -33,6 +33,7 @@ use salvo::{
Request,
Response,
Router,
+ Writer,
};
use sea_orm::DatabaseConnection;
use tokio::{sync::RwLock, task::spawn as tokio_spawn, time::sleep as tokio_sleep};
@@ -49,7 +50,6 @@ use crate::{
extensions::{DepotExt, OnlineUsersExt},
middlewares,
nonce::NonceCache,
- utils,
};
/// Online users type
@@ -96,12 +96,11 @@ impl SocketUserData {
pub async fn user_connected(
req: &mut Request,
res: &mut Response,
+ public_key: PublicKey,
depot: &Depot,
) -> Result<(), StatusError> {
let nonce_cache = depot.nonce_cache();
let db_conn = depot.db_conn();
- let public_key =
- utils::extract_public_key(req).expect("The public key was checked in the middleware");
let shared_secret = depot.config().server.private_key.shared_secret(&public_key);
WebSocketUpgrade::new()
--
2.45.2
From 5ca23c8af5eb3a23767b91cca3c59a5f82ec0d15 Mon Sep 17 00:00:00 2001
From: Awiteb
Date: Sun, 28 Jul 2024 09:46:14 +0300
Subject: [PATCH 7/9] chore: Use core implementation to extract the signature
and public key
Signed-off-by: Awiteb
---
.../oxidetalis/src/middlewares/signature.rs | 6 ++--
.../oxidetalis_core/src/types/impl_openapi.rs | 28 +++++++++++--------
2 files changed, 21 insertions(+), 13 deletions(-)
diff --git a/crates/oxidetalis/src/middlewares/signature.rs b/crates/oxidetalis/src/middlewares/signature.rs
index f108f4a..e5d019b 100644
--- a/crates/oxidetalis/src/middlewares/signature.rs
+++ b/crates/oxidetalis/src/middlewares/signature.rs
@@ -16,10 +16,12 @@
//! Request signature middleware.
+use oxidetalis_core::types::{PublicKey, Signature};
use salvo::{
handler,
http::{Body, StatusCode},
Depot,
+ Extractible,
FlowCtrl,
Request,
Response,
@@ -54,7 +56,7 @@ pub async fn signature_check(
}
};
- let signature = match utils::extract_signature(req) {
+ let signature = match Signature::extract(req).await {
Ok(s) => s,
Err(err) => {
write_err(&err.to_string(), UNAUTHORIZED);
@@ -62,7 +64,7 @@ pub async fn signature_check(
}
};
- let sender_public_key = match utils::extract_public_key(req) {
+ let sender_public_key = match PublicKey::extract(req).await {
Ok(k) => k,
Err(err) => {
write_err(&err.to_string(), UNAUTHORIZED);
diff --git a/crates/oxidetalis_core/src/types/impl_openapi.rs b/crates/oxidetalis_core/src/types/impl_openapi.rs
index b22fbfe..eea4d05 100644
--- a/crates/oxidetalis_core/src/types/impl_openapi.rs
+++ b/crates/oxidetalis_core/src/types/impl_openapi.rs
@@ -111,20 +111,26 @@ impl EndpointArgRegister for CorePublicKey {
impl<'ex> Extractible<'ex> for Signature {
fn metadata() -> &'ex ExtractMetadata {
- unreachable!(
- "
- `Extractible` is required to implement `ToParameters` for `Signature`, but \
- Salvo does not need it actually, see https://github.com/salvo-rs/salvo/issues/838"
- )
+ static METADATA: ExtractMetadata = ExtractMetadata::new("");
+ &METADATA
}
#[allow(refining_impl_trait)]
- async fn extract(_: &'ex mut Request) -> Result {
- unreachable!(
- "
- `Extractible` is required to implement `ToParameters` for `Signature`, but \
- Salvo does not need it actually, see https://github.com/salvo-rs/salvo/issues/838"
- )
+ async fn extract(req: &'ex mut Request) -> Result {
+ extract_header(req, crate::SIGNATURE_HEADER)
+ .and_then(|sig| {
+ Signature::from_str(sig).map_err(|err| {
+ StatusError::unauthorized()
+ .brief("Invalid signature")
+ .cause(err.to_string())
+ })
+ })
+ .map_err(|err| {
+ StatusError::unauthorized().brief(err.brief).cause(
+ err.cause
+ .expect("The cause was set when we extract the header"),
+ )
+ })
}
}
--
2.45.2
From c1371294884ef8048247412ca8602a5cc8ee4b80 Mon Sep 17 00:00:00 2001
From: Awiteb
Date: Sun, 28 Jul 2024 09:47:18 +0300
Subject: [PATCH 8/9] remove: Remove `extract_public_key` and
`extract_signature` from utils
Signed-off-by: Awiteb
---
crates/oxidetalis/src/utils.rs | 37 +---------------------------------
1 file changed, 1 insertion(+), 36 deletions(-)
diff --git a/crates/oxidetalis/src/utils.rs b/crates/oxidetalis/src/utils.rs
index 2bd316b..a197eee 100644
--- a/crates/oxidetalis/src/utils.rs
+++ b/crates/oxidetalis/src/utils.rs
@@ -16,17 +16,10 @@
//! Oxidetalis server utilities, utilities shared across the crate.
-use std::str::FromStr;
-
use chrono::Utc;
use logcall::logcall;
use oxidetalis_config::Postgres;
-use oxidetalis_core::{
- types::{PublicKey, Signature},
- PUBLIC_KEY_HEADER,
- SIGNATURE_HEADER,
-};
-use salvo::Request;
+use oxidetalis_core::types::Signature;
use crate::nonce::NonceCache;
@@ -48,31 +41,3 @@ pub(crate) async fn is_valid_nonce(signature: &Signature, nonce_cache: &NonceCac
let unused_nonce = new_timestamp && nonce_cache.add_nonce(signature.nonce()).await;
new_timestamp && unused_nonce
}
-
-/// Extract the sender public key from the request
-///
-/// Returns the public key of the sender extracted from the request, or the
-/// reason why it failed.
-pub(crate) fn extract_public_key(req: &Request) -> Result {
- req.headers()
- .get(PUBLIC_KEY_HEADER)
- .map(|v| {
- PublicKey::from_str(v.to_str().map_err(|err| err.to_string())?)
- .map_err(|err| err.to_string())
- })
- .ok_or_else(|| "The public key is missing".to_owned())?
-}
-
-/// Extract the signature from the request
-///
-/// Returns the signature extracted from the request, or the reason why it
-/// failed.
-pub(crate) fn extract_signature(req: &Request) -> Result {
- req.headers()
- .get(SIGNATURE_HEADER)
- .map(|v| {
- Signature::from_str(v.to_str().map_err(|err| err.to_string())?)
- .map_err(|err| err.to_string())
- })
- .ok_or_else(|| "The signature is missing".to_owned())?
-}
--
2.45.2
From 71d63580bfbe7720b2212e94d72fd0c5e707db44 Mon Sep 17 00:00:00 2001
From: Awiteb
Date: Sun, 28 Jul 2024 12:27:03 +0300
Subject: [PATCH 9/9] chore: Let salvo extract the public key and the signature
Signed-off-by: Awiteb
---
.../oxidetalis/src/middlewares/signature.rs | 20 +++----------------
1 file changed, 3 insertions(+), 17 deletions(-)
diff --git a/crates/oxidetalis/src/middlewares/signature.rs b/crates/oxidetalis/src/middlewares/signature.rs
index e5d019b..2729873 100644
--- a/crates/oxidetalis/src/middlewares/signature.rs
+++ b/crates/oxidetalis/src/middlewares/signature.rs
@@ -21,10 +21,10 @@ use salvo::{
handler,
http::{Body, StatusCode},
Depot,
- Extractible,
FlowCtrl,
Request,
Response,
+ Writer,
};
use crate::{extensions::DepotExt, utils};
@@ -39,6 +39,8 @@ pub async fn signature_check(
res: &mut Response,
depot: &mut Depot,
ctrl: &mut FlowCtrl,
+ sender_public_key: PublicKey,
+ signature: Signature,
) {
const UNAUTHORIZED: StatusCode = StatusCode::UNAUTHORIZED;
let mut write_err =
@@ -56,22 +58,6 @@ pub async fn signature_check(
}
};
- let signature = match Signature::extract(req).await {
- Ok(s) => s,
- Err(err) => {
- write_err(&err.to_string(), UNAUTHORIZED);
- return;
- }
- };
-
- let sender_public_key = match PublicKey::extract(req).await {
- Ok(k) => k,
- Err(err) => {
- write_err(&err.to_string(), UNAUTHORIZED);
- return;
- }
- };
-
if !utils::is_valid_nonce(&signature, &depot.nonce_cache()).await
|| !depot.config().server.private_key.verify(
data.as_bytes(),
--
2.45.2