-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathauth-with-state.rs
116 lines (103 loc) · 3.18 KB
/
auth-with-state.rs
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
use davisjr::prelude::*;
// We'll use authstate to (optionally) capture information about the token
// being correct. if it is Some(true), the user was authed, if None, there was no
// authentication performed.
#[derive(Clone)]
struct AuthedState {
authed: Option<bool>,
}
// All transient state structs must have an initial state, which will be
// initialized internally in the router.
impl TransientState for AuthedState {
fn initial() -> Self {
Self { authed: None }
}
}
// our authtoken validator, this queries the app state and the header
// `X-AuthToken` and compares the two. If there are any discrepancies, it
// returns `401 Unauthorized`.
//
// every handler & middleware takes and returns the same params and has the
// same prototype.
//
async fn validate_authtoken(
req: Request<Body>,
resp: Option<Response<Body>>,
_params: Params,
app: App<State, AuthedState>,
mut authstate: AuthedState,
) -> HTTPResult<AuthedState> {
if let (Some(token), Some(state)) = (req.headers().get("X-AuthToken"), app.state().await) {
authstate.authed = Some(state.clone().lock().await.authtoken == token);
Ok((req, resp, authstate))
} else {
Err(Error::StatusCode(
StatusCode::UNAUTHORIZED,
String::default(),
))
}
}
// our `hello` responder; it simply echoes the `name` parameter provided in the
// route.
async fn hello(
req: Request<Body>,
_resp: Option<Response<Body>>,
params: Params,
_app: App<State, AuthedState>,
authstate: AuthedState,
) -> HTTPResult<AuthedState> {
let name = ¶ms["name"];
let bytes = Body::from(format!("hello, {}!\n", name));
if let Some(authed) = authstate.authed {
if authed {
return Ok((
req,
Some(Response::builder().status(200).body(bytes).unwrap()),
authstate,
));
}
} else if authstate.authed.is_none() {
return Ok((
req,
Some(Response::builder().status(200).body(bytes).unwrap()),
authstate,
));
}
Err(Error::StatusCode(
StatusCode::UNAUTHORIZED,
String::default(),
))
}
// our `wildcard` responder, which shows how to use wildcard routes
async fn wildcard(
req: Request<Body>,
_resp: Option<Response<Body>>,
params: Params,
_app: App<State, AuthedState>,
state: AuthedState,
) -> HTTPResult<AuthedState> {
let bytes = Body::from(format!("this route is: {}!\n", params["*"]));
return Ok((
req,
Some(Response::builder().status(200).body(bytes).unwrap()),
state,
));
}
// Our global application state; must be `Clone`.
#[derive(Clone)]
struct State {
authtoken: &'static str,
}
// ServerError is a catch-all for errors returned by serving content through
// davisjr.
#[tokio::main]
async fn main() -> Result<(), ServerError> {
let mut app = App::with_state(State {
authtoken: "867-5309",
});
app.get("/wildcard/*", compose_handler!(wildcard))?;
app.get("/auth/:name", compose_handler!(validate_authtoken, hello))?;
app.get("/:name", compose_handler!(hello))?;
app.serve("127.0.0.1:3000").await?;
Ok(())
}