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
 mod post;
9
 mod post;
10
 mod routes;
10
 mod routes;
11
 
11
 
12
+#[derive(Clone)]
13
+pub struct State {
14
+    pub login_path: String,
15
+}
16
+
12
 #[async_std::main]
17
 #[async_std::main]
13
 async fn main() -> std::io::Result<()> {
18
 async fn main() -> std::io::Result<()> {
14
     dotenv::dotenv().ok();
19
     dotenv::dotenv().ok();
15
     tide::log::start();
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
     app.with(After(errors));
27
     app.with(After(errors));
19
     app.with(session());
28
     app.with(session());
29
     app.at("/posts/:id/edit")
38
     app.at("/posts/:id/edit")
30
         .with(require_auth)
39
         .with(require_auth)
31
         .get(routes::edit_post);
40
         .get(routes::edit_post);
32
-    app.at("/login")
41
+    app.at(&login_path)
33
         .with(require_guest)
42
         .with(require_guest)
34
         .get(routes::login_page)
43
         .get(routes::login_page)
35
         .post(routes::login);
44
         .post(routes::login);

+ 7
- 6
src/middleware.rs View File

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

+ 18
- 12
src/routes.rs View File

7
 
7
 
8
 use std::env;
8
 use std::env;
9
 
9
 
10
-use crate::{fs, post::Post};
10
+use crate::{fs, post::Post, State};
11
 
11
 
12
 #[derive(Debug, Serialize, Deserialize)]
12
 #[derive(Debug, Serialize, Deserialize)]
13
 struct User {
13
 struct User {
15
     password: String,
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
     let posts: Vec<Post> = fs::get_all_posts().await?;
19
     let posts: Vec<Post> = fs::get_all_posts().await?;
20
     let mut context = Context::new();
20
     let mut context = Context::new();
21
     context.insert("posts", &posts);
21
     context.insert("posts", &posts);
22
     render_response("index.html", &context, &req)
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
     let mut context = Context::new();
26
     let mut context = Context::new();
27
     let post_id = req.param("id")?;
27
     let post_id = req.param("id")?;
28
     let post = fs::get_one_post(post_id).await?;
28
     let post = fs::get_one_post(post_id).await?;
30
     render_response("single.html", &context, &req)
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
     let mut context = Context::new();
34
     let mut context = Context::new();
35
     let post_id = req.param("id")?;
35
     let post_id = req.param("id")?;
36
     let mut post = fs::get_one_post(post_id).await?;
36
     let mut post = fs::get_one_post(post_id).await?;
39
     render_response("edit.html", &context, &req)
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
     let mut post: Post = req.body_form().await?;
43
     let mut post: Post = req.body_form().await?;
44
     post.id = Uuid::new_v4().to_string();
44
     post.id = Uuid::new_v4().to_string();
45
     post.date = Local::now().date().naive_local().to_string();
45
     post.date = Local::now().date().naive_local().to_string();
48
     Ok(Redirect::new("/").into())
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
     let mut post: Post = req.body_form().await?;
52
     let mut post: Post = req.body_form().await?;
53
     post.save().await?;
53
     post.save().await?;
54
     Ok(Redirect::new(format!("/posts/{}", post.id)).into())
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
     let id: String = req.param("id")?;
58
     let id: String = req.param("id")?;
59
     fs::delete_post(id)?;
59
     fs::delete_post(id)?;
60
     let mut res = Response::new(StatusCode::Ok);
60
     let mut res = Response::new(StatusCode::Ok);
63
     Ok(res)
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
     let mut context = Context::new();
67
     let mut context = Context::new();
68
     match req.session_mut().get::<String>("flash_error") {
68
     match req.session_mut().get::<String>("flash_error") {
69
         Some(error) => {
69
         Some(error) => {
75
     render_response("login.html", &context, &req)
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
     let username = env::var("ADMIN_USERNAME")?;
79
     let username = env::var("ADMIN_USERNAME")?;
80
     let password = env::var("ADMIN_PASSWORD")?;
80
     let password = env::var("ADMIN_PASSWORD")?;
81
     let user: User = req.body_form().await?;
81
     let user: User = req.body_form().await?;
87
         req.session_mut().remove("logged_in");
87
         req.session_mut().remove("logged_in");
88
         req.session_mut()
88
         req.session_mut()
89
             .insert("flash_error", "Invalid credentials")?;
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
     req.session_mut().remove("logged_in");
95
     req.session_mut().remove("logged_in");
96
     req.session_mut().insert("logged_in", false)?;
96
     req.session_mut().insert("logged_in", false)?;
97
     Ok(Redirect::new("/").into())
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
     let mut context = context.clone();
105
     let mut context = context.clone();
102
     let logged_in: bool = req.session().get("logged_in").unwrap_or(false);
106
     let logged_in: bool = req.session().get("logged_in").unwrap_or(false);
103
     context.insert("logged_in", &logged_in);
107
     context.insert("logged_in", &logged_in);
108
+    let login_path = &req.state().login_path;
109
+    context.insert("login_path", login_path);
104
     let html = render_template(template, &context)?;
110
     let html = render_template(template, &context)?;
105
     let mut res = Response::new(StatusCode::Ok);
111
     let mut res = Response::new(StatusCode::Ok);
106
     res.set_body(html);
112
     res.set_body(html);

+ 11
- 5
templates/login.html View File

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

Loading…
Cancel
Save