Browse Source

Make login path configurable for security by obscurity

master
Dylan Baker 3 years ago
parent
commit
a105485d81
4 changed files with 47 additions and 25 deletions
  1. 11
    2
      src/main.rs
  2. 7
    6
      src/middleware.rs
  3. 18
    12
      src/routes.rs
  4. 11
    5
      templates/login.html

+ 11
- 2
src/main.rs View File

@@ -9,11 +9,20 @@ mod middleware;
9 9
 mod post;
10 10
 mod routes;
11 11
 
12
+#[derive(Clone)]
13
+pub struct State {
14
+    pub login_path: String,
15
+}
16
+
12 17
 #[async_std::main]
13 18
 async fn main() -> std::io::Result<()> {
14 19
     dotenv::dotenv().ok();
15 20
     tide::log::start();
16
-    let mut app = tide::new();
21
+
22
+    let login_path = std::env::var("LOGIN_PATH").unwrap_or(String::from("/login"));
23
+    let mut app = tide::with_state(State {
24
+        login_path: login_path.clone(),
25
+    });
17 26
 
18 27
     app.with(After(errors));
19 28
     app.with(session());
@@ -29,7 +38,7 @@ async fn main() -> std::io::Result<()> {
29 38
     app.at("/posts/:id/edit")
30 39
         .with(require_auth)
31 40
         .get(routes::edit_post);
32
-    app.at("/login")
41
+    app.at(&login_path)
33 42
         .with(require_guest)
34 43
         .get(routes::login_page)
35 44
         .post(routes::login);

+ 7
- 6
src/middleware.rs View File

@@ -7,7 +7,7 @@ use tide::{
7 7
 use std::{env, future::Future, io::ErrorKind, pin::Pin};
8 8
 use tera::Context;
9 9
 
10
-use crate::routes;
10
+use crate::{routes, State};
11 11
 
12 12
 pub fn session() -> SessionMiddleware<CookieStore> {
13 13
     SessionMiddleware::new(
@@ -22,22 +22,23 @@ pub fn session() -> SessionMiddleware<CookieStore> {
22 22
 }
23 23
 
24 24
 pub fn require_auth<'a>(
25
-    req: Request<()>,
26
-    next: Next<'a, ()>,
25
+    req: Request<State>,
26
+    next: Next<'a, State>,
27 27
 ) -> Pin<Box<dyn Future<Output = Result> + 'a + Send>> {
28 28
     Box::pin(async {
29 29
         let logged_in: bool = req.session().get("logged_in").unwrap_or(false);
30 30
         if logged_in {
31 31
             Ok(next.run(req).await)
32 32
         } else {
33
-            Ok(Redirect::new("/login").into())
33
+            let login_path = &req.state().login_path;
34
+            Ok(Redirect::new(login_path).into())
34 35
         }
35 36
     })
36 37
 }
37 38
 
38 39
 pub fn require_guest<'a>(
39
-    req: Request<()>,
40
-    next: Next<'a, ()>,
40
+    req: Request<State>,
41
+    next: Next<'a, State>,
41 42
 ) -> Pin<Box<dyn Future<Output = Result> + 'a + Send>> {
42 43
     Box::pin(async {
43 44
         let logged_in: bool = req.session().get("logged_in").unwrap_or(false);

+ 18
- 12
src/routes.rs View File

@@ -7,7 +7,7 @@ use uuid::Uuid;
7 7
 
8 8
 use std::env;
9 9
 
10
-use crate::{fs, post::Post};
10
+use crate::{fs, post::Post, State};
11 11
 
12 12
 #[derive(Debug, Serialize, Deserialize)]
13 13
 struct User {
@@ -15,14 +15,14 @@ struct User {
15 15
     password: String,
16 16
 }
17 17
 
18
-pub async fn index(req: Request<()>) -> Result {
18
+pub async fn index(req: Request<State>) -> Result {
19 19
     let posts: Vec<Post> = fs::get_all_posts().await?;
20 20
     let mut context = Context::new();
21 21
     context.insert("posts", &posts);
22 22
     render_response("index.html", &context, &req)
23 23
 }
24 24
 
25
-pub async fn single_post(req: Request<()>) -> Result {
25
+pub async fn single_post(req: Request<State>) -> Result {
26 26
     let mut context = Context::new();
27 27
     let post_id = req.param("id")?;
28 28
     let post = fs::get_one_post(post_id).await?;
@@ -30,7 +30,7 @@ pub async fn single_post(req: Request<()>) -> Result {
30 30
     render_response("single.html", &context, &req)
31 31
 }
32 32
 
33
-pub async fn edit_post(req: Request<()>) -> Result {
33
+pub async fn edit_post(req: Request<State>) -> Result {
34 34
     let mut context = Context::new();
35 35
     let post_id = req.param("id")?;
36 36
     let mut post = fs::get_one_post(post_id).await?;
@@ -39,7 +39,7 @@ pub async fn edit_post(req: Request<()>) -> Result {
39 39
     render_response("edit.html", &context, &req)
40 40
 }
41 41
 
42
-pub async fn create_post(mut req: Request<()>) -> Result {
42
+pub async fn create_post(mut req: Request<State>) -> Result {
43 43
     let mut post: Post = req.body_form().await?;
44 44
     post.id = Uuid::new_v4().to_string();
45 45
     post.date = Local::now().date().naive_local().to_string();
@@ -48,13 +48,13 @@ pub async fn create_post(mut req: Request<()>) -> Result {
48 48
     Ok(Redirect::new("/").into())
49 49
 }
50 50
 
51
-pub async fn update_post(mut req: Request<()>) -> Result {
51
+pub async fn update_post(mut req: Request<State>) -> Result {
52 52
     let mut post: Post = req.body_form().await?;
53 53
     post.save().await?;
54 54
     Ok(Redirect::new(format!("/posts/{}", post.id)).into())
55 55
 }
56 56
 
57
-pub async fn delete_post(req: Request<()>) -> Result {
57
+pub async fn delete_post(req: Request<State>) -> Result {
58 58
     let id: String = req.param("id")?;
59 59
     fs::delete_post(id)?;
60 60
     let mut res = Response::new(StatusCode::Ok);
@@ -63,7 +63,7 @@ pub async fn delete_post(req: Request<()>) -> Result {
63 63
     Ok(res)
64 64
 }
65 65
 
66
-pub async fn login_page(mut req: Request<()>) -> Result {
66
+pub async fn login_page(mut req: Request<State>) -> Result {
67 67
     let mut context = Context::new();
68 68
     match req.session_mut().get::<String>("flash_error") {
69 69
         Some(error) => {
@@ -75,7 +75,7 @@ pub async fn login_page(mut req: Request<()>) -> Result {
75 75
     render_response("login.html", &context, &req)
76 76
 }
77 77
 
78
-pub async fn login(mut req: Request<()>) -> Result {
78
+pub async fn login(mut req: Request<State>) -> Result {
79 79
     let username = env::var("ADMIN_USERNAME")?;
80 80
     let password = env::var("ADMIN_PASSWORD")?;
81 81
     let user: User = req.body_form().await?;
@@ -87,20 +87,26 @@ pub async fn login(mut req: Request<()>) -> Result {
87 87
         req.session_mut().remove("logged_in");
88 88
         req.session_mut()
89 89
             .insert("flash_error", "Invalid credentials")?;
90
-        Ok(Redirect::new("/login").into())
90
+        Ok(Redirect::new(&req.state().login_path).into())
91 91
     }
92 92
 }
93 93
 
94
-pub async fn logout(mut req: Request<()>) -> Result {
94
+pub async fn logout(mut req: Request<State>) -> Result {
95 95
     req.session_mut().remove("logged_in");
96 96
     req.session_mut().insert("logged_in", false)?;
97 97
     Ok(Redirect::new("/").into())
98 98
 }
99 99
 
100
-pub fn render_response(template: &str, context: &Context, req: &Request<()>) -> Result<Response> {
100
+pub fn render_response(
101
+    template: &str,
102
+    context: &Context,
103
+    req: &Request<State>,
104
+) -> Result<Response> {
101 105
     let mut context = context.clone();
102 106
     let logged_in: bool = req.session().get("logged_in").unwrap_or(false);
103 107
     context.insert("logged_in", &logged_in);
108
+    let login_path = &req.state().login_path;
109
+    context.insert("login_path", login_path);
104 110
     let html = render_template(template, &context)?;
105 111
     let mut res = Response::new(StatusCode::Ok);
106 112
     res.set_body(html);

+ 11
- 5
templates/login.html View File

@@ -1,18 +1,24 @@
1 1
 {% extends "layout.html" %} {% block content %}
2 2
 <h1 class="heading">Login</h1>
3
-<form class="form" method="POST" action="/login">
3
+<form class="form" method="POST" action="{{ login_path }}">
4 4
   <div class="form__field">
5 5
     <label for="username" class="form__label">Username</label>
6
-    <input class="form__text-field" type="text" name="username" required autofocus />
6
+    <input
7
+      class="form__text-field"
8
+      type="text"
9
+      name="username"
10
+      required
11
+      autofocus
12
+    />
7 13
   </div>
8 14
   <div class="form__field">
9 15
     <label for="password" class="form__label">Password</label>
10 16
     <input class="form__text-field" type="password" name="password" required />
11 17
   </div>
12 18
   {% if error %}
13
-    <div class="error">
14
-      <p class="error__message">{{ error }}</p>
15
-    </div>
19
+  <div class="error">
20
+    <p class="error__message">{{ error }}</p>
21
+  </div>
16 22
   {% endif %}
17 23
   <div class="form__field">
18 24
     <input class="form__button" type="submit" value="Login" />

Loading…
Cancel
Save