1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
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))
}
}
|