summaryrefslogtreecommitdiff
path: root/src/web/extract.rs
diff options
context:
space:
mode:
authorTyler Murphy <tylermurphy534@gmail.com>2023-03-06 18:50:08 -0500
committerTyler Murphy <tylermurphy534@gmail.com>2023-03-06 18:50:08 -0500
commitb1fb410affb7bcd2e714abac01d22c4a5332c344 (patch)
tree7ebb621ab9b73e3e1fbaeb0ef8c19abef95b7c9f /src/web/extract.rs
parentfinialize initial dns + caching (diff)
downloadwrapper-b1fb410affb7bcd2e714abac01d22c4a5332c344.tar.gz
wrapper-b1fb410affb7bcd2e714abac01d22c4a5332c344.tar.bz2
wrapper-b1fb410affb7bcd2e714abac01d22c4a5332c344.zip
finish dns and start webserver
Diffstat (limited to 'src/web/extract.rs')
-rw-r--r--src/web/extract.rs139
1 files changed, 139 insertions, 0 deletions
diff --git a/src/web/extract.rs b/src/web/extract.rs
new file mode 100644
index 0000000..4b6cd7c
--- /dev/null
+++ b/src/web/extract.rs
@@ -0,0 +1,139 @@
+use std::{
+ io::Read,
+ net::{IpAddr, SocketAddr},
+};
+
+use axum::{
+ async_trait,
+ body::HttpBody,
+ extract::{ConnectInfo, FromRequest, FromRequestParts},
+ http::{request::Parts, Request},
+ response::Response,
+ BoxError,
+};
+use bytes::Bytes;
+use moka::future::Cache;
+use tower_cookies::Cookies;
+
+use super::http::text;
+
+pub struct Authorized;
+
+#[async_trait]
+impl<S> FromRequestParts<S> for Authorized
+where
+ S: Send + Sync,
+{
+ type Rejection = Response;
+
+ async fn from_request_parts(parts: &mut Parts, state: &S) -> Result<Self, Self::Rejection> {
+ let Ok(Some(cookies)) = Option::<Cookies>::from_request_parts(parts, state).await else {
+ return Err(text(403, "No cookies provided"))
+ };
+
+ let Some(token) = cookies.get("auth") else {
+ return Err(text(403, "No auth token provided"))
+ };
+
+ let auth_ip: IpAddr;
+ {
+ let Some(cache) = parts.extensions.get::<Cache<String, IpAddr>>() else {
+ return Err(text(500, "Failed to load auth store"))
+ };
+
+ let Some(ip) = cache.get(token.value()) else {
+ return Err(text(401, "Unauthorized"))
+ };
+
+ auth_ip = ip
+ }
+
+ let Ok(Some(RequestIp(ip))) = Option::<RequestIp>::from_request_parts(parts, state).await else {
+ return Err(text(403, "You have no ip"))
+ };
+
+ if auth_ip != ip {
+ return Err(text(403, "Auth token does not match current ip"));
+ }
+
+ Ok(Self)
+ }
+}
+
+pub struct RequestIp(pub IpAddr);
+
+#[async_trait]
+impl<S> FromRequestParts<S> for RequestIp
+where
+ S: Send + Sync,
+{
+ type Rejection = Response;
+
+ async fn from_request_parts(parts: &mut Parts, _state: &S) -> Result<Self, Self::Rejection> {
+ let headers = &parts.headers;
+
+ let forwardedfor = headers
+ .get("x-forwarded-for")
+ .and_then(|h| h.to_str().ok())
+ .and_then(|h| {
+ h.split(',')
+ .rev()
+ .find_map(|s| s.trim().parse::<IpAddr>().ok())
+ });
+
+ if let Some(forwardedfor) = forwardedfor {
+ return Ok(Self(forwardedfor));
+ }
+
+ let realip = headers
+ .get("x-real-ip")
+ .and_then(|hv| hv.to_str().ok())
+ .and_then(|s| s.parse::<IpAddr>().ok());
+
+ if let Some(realip) = realip {
+ return Ok(Self(realip));
+ }
+
+ let realip = headers
+ .get("x-real-ip")
+ .and_then(|hv| hv.to_str().ok())
+ .and_then(|s| s.parse::<IpAddr>().ok());
+
+ if let Some(realip) = realip {
+ return Ok(Self(realip));
+ }
+
+ let info = parts.extensions.get::<ConnectInfo<SocketAddr>>();
+
+ if let Some(info) = info {
+ return Ok(Self(info.0.ip()));
+ }
+
+ Err(text(403, "You have no ip"))
+ }
+}
+
+pub struct Body(pub String);
+
+#[async_trait]
+impl<S, B> FromRequest<S, B> for Body
+where
+ B: HttpBody + Sync + Send + 'static,
+ B::Data: Send,
+ B::Error: Into<BoxError>,
+ S: Send + Sync,
+{
+ type Rejection = Response;
+
+ async fn from_request(req: Request<B>, state: &S) -> Result<Self, Self::Rejection> {
+ let Ok(bytes) = Bytes::from_request(req, state).await else {
+ return Err(text(413, "Payload too large"));
+ };
+
+ let Ok(body) = String::from_utf8(bytes.bytes().flatten().collect()) else {
+ return Err(text(400, "Invalid utf8 body"))
+ };
+
+ Ok(Self(body))
+ }
+}