Authentication
This commit is contained in:
		
							parent
							
								
									f8372f6383
								
							
						
					
					
						commit
						5f4cb6c065
					
				| @ -51,9 +51,13 @@ fn main() { | ||||
|             routes::instance::configure, | ||||
|             routes::instance::post_config, | ||||
| 
 | ||||
|             routes::user::me, | ||||
|             routes::user::details, | ||||
|             routes::user::new, | ||||
|             routes::user::create, | ||||
| 
 | ||||
|             routes::session::new, | ||||
|             routes::session::create | ||||
|         ]) | ||||
|         .manage(init_pool()) | ||||
|         .attach(Template::fairing()) | ||||
|  | ||||
| @ -1,6 +1,13 @@ | ||||
| use rocket::request; | ||||
| use rocket::request::{FromRequest, Request}; | ||||
| use rocket::outcome::IntoOutcome; | ||||
| use diesel; | ||||
| use diesel::{ QueryDsl, RunQueryDsl, ExpressionMethods, PgConnection }; | ||||
| use diesel::{QueryDsl, RunQueryDsl, ExpressionMethods, PgConnection}; | ||||
| use schema::users; | ||||
| use db_conn::DbConn; | ||||
| use bcrypt; | ||||
| 
 | ||||
| pub const AUTH_COOKIE: &'static str = "user_id"; | ||||
| 
 | ||||
| #[derive(Queryable, Identifiable)] | ||||
| pub struct User { | ||||
| @ -37,7 +44,7 @@ impl User { | ||||
|         diesel::insert_into(users::table) | ||||
|             .values(new) | ||||
|             .get_result(conn) | ||||
|             .expect("Error saving new instance") | ||||
|             .expect("Error saving new user") | ||||
|     } | ||||
| 
 | ||||
|     pub fn compute_outbox(user: String, hostname: String) -> String { | ||||
| @ -48,5 +55,48 @@ impl User { | ||||
|         format!("https://{}/@/{}/inbox", hostname, user) | ||||
|     } | ||||
| 
 | ||||
|     fn get () {} | ||||
|     pub fn get(conn: &PgConnection, id: i32) -> Option<User> { | ||||
|         users::table.filter(users::id.eq(id)) | ||||
|             .limit(1) | ||||
|             .load::<User>(conn) | ||||
|             .expect("Error loading user by id") | ||||
|             .into_iter().nth(0) | ||||
|     } | ||||
| 
 | ||||
|     pub fn find_by_email(conn: &PgConnection, email: String) -> Option<User> { | ||||
|         users::table.filter(users::email.eq(email)) | ||||
|             .limit(1) | ||||
|             .load::<User>(conn) | ||||
|             .expect("Error loading user by email") | ||||
|             .into_iter().nth(0) | ||||
|     } | ||||
| 
 | ||||
|     pub fn find_by_name(conn: &PgConnection, username: String) -> Option<User> { | ||||
|         users::table.filter(users::username.eq(username)) | ||||
|             .limit(1) | ||||
|             .load::<User>(conn) | ||||
|             .expect("Error loading user by email") | ||||
|             .into_iter().nth(0) | ||||
|     } | ||||
| 
 | ||||
|     pub fn hash_pass(pass: String) -> String { | ||||
|         bcrypt::hash(pass.as_str(), bcrypt::DEFAULT_COST).unwrap() | ||||
|     } | ||||
| 
 | ||||
|     pub fn auth(&self, pass: String) -> bool { | ||||
|         bcrypt::verify(pass.as_str(), self.hashed_password.clone().unwrap().as_str()).is_ok() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<'a, 'r> FromRequest<'a, 'r> for User { | ||||
|     type Error = (); | ||||
| 
 | ||||
|     fn from_request(request: &'a Request<'r>) -> request::Outcome<User, ()> { | ||||
|         let conn = request.guard::<DbConn>()?; | ||||
|         request.cookies() | ||||
|             .get_private(AUTH_COOKIE) | ||||
|             .and_then(|cookie| cookie.value().parse().ok()) | ||||
|             .map(|id| User::get(&*conn, id).unwrap()) | ||||
|             .or_forward(()) | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -1,2 +1,3 @@ | ||||
| pub mod instance; | ||||
| pub mod session; | ||||
| pub mod user; | ||||
|  | ||||
							
								
								
									
										45
									
								
								src/routes/session.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								src/routes/session.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,45 @@ | ||||
| use std::collections::HashMap; | ||||
| use rocket_contrib::Template; | ||||
| use rocket::response::Redirect; | ||||
| use rocket::request::Form; | ||||
| use models::user::User; | ||||
| use rocket::response::status::NotFound; | ||||
| use rocket::http::Cookies; | ||||
| use db_conn::DbConn; | ||||
| use rocket::http::Cookie; | ||||
| use models::user::AUTH_COOKIE; | ||||
| 
 | ||||
| #[get("/login")] | ||||
| fn new() -> Template { | ||||
|     Template::render("session/login", HashMap::<String, String>::new()) | ||||
| } | ||||
| 
 | ||||
| #[derive(FromForm)] | ||||
| struct LoginForm { | ||||
|     email_or_name: String, | ||||
|     password: String | ||||
| } | ||||
| 
 | ||||
| #[post("/login", data = "<data>")] | ||||
| fn create(conn: DbConn, data: Form<LoginForm>, mut cookies: Cookies) -> Result<Redirect, NotFound<String>> { | ||||
|     let form = data.get(); | ||||
|     let user = match User::find_by_email(&*conn, form.email_or_name.to_string()) { | ||||
|         Some(usr) => Ok(usr), | ||||
|         None => match User::find_by_name(&*conn, form.email_or_name.to_string()) { | ||||
|             Some(usr) => Ok(usr), | ||||
|             None => Err("Invalid username or password") | ||||
|         } | ||||
|     }; | ||||
| 
 | ||||
|     match user { | ||||
|         Ok(usr) => { | ||||
|             if usr.auth(form.password.to_string()) { | ||||
|                 cookies.add_private(Cookie::new(AUTH_COOKIE, usr.id.to_string())); | ||||
|                 Ok(Redirect::to("/")) | ||||
|             } else { | ||||
|                 Err(NotFound(String::from("Invalid username or password"))) | ||||
|             } | ||||
|         }, | ||||
|         Err(e) => Err(NotFound(String::from(e))) | ||||
|     } | ||||
| } | ||||
| @ -2,16 +2,19 @@ use rocket::request::Form; | ||||
| use rocket::response::Redirect; | ||||
| use rocket_contrib::Template; | ||||
| use std::collections::HashMap; | ||||
| use bcrypt::{hash, DEFAULT_COST}; | ||||
| 
 | ||||
| use db_conn::DbConn; | ||||
| use models::user::*; | ||||
| use models::instance::Instance; | ||||
| 
 | ||||
| #[get("/me")] | ||||
| fn me(user: User) -> String { | ||||
|     format!("Logged in as {}", user.username.to_string()) | ||||
| } | ||||
| 
 | ||||
| #[get("/@/<name>")] | ||||
| fn details(name: String) { | ||||
| 
 | ||||
| fn details(name: String) -> String { | ||||
|     format!("Hello, @{}", name) | ||||
| } | ||||
| 
 | ||||
| #[get("/users/new")] | ||||
| @ -41,7 +44,7 @@ fn create(conn: DbConn, data: Form<NewUserForm>) -> Redirect { | ||||
|             is_admin: !inst.has_admin(&*conn), | ||||
|             summary: String::from(""), | ||||
|             email: Some(form.email.to_string()), | ||||
|             hashed_password: Some(hash(form.password.as_str(), DEFAULT_COST).unwrap()), | ||||
|             hashed_password: Some(User::hash_pass(form.password.to_string())), | ||||
|             instance_id: inst.id | ||||
|         }); | ||||
|     } | ||||
|  | ||||
							
								
								
									
										19
									
								
								templates/session/login.tera
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								templates/session/login.tera
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,19 @@ | ||||
| <!DOCTYPE html> | ||||
| <html> | ||||
|     <head> | ||||
|         <meta charset="utf-8"> | ||||
|         <title>Login</title> | ||||
|     </head> | ||||
|     <body> | ||||
|         <h1>Login</h1> | ||||
|         <form method="post"> | ||||
|             <label for="email_or_name">Username or email</label> | ||||
|             <input name="email_or_name"> | ||||
| 
 | ||||
|             <label for="password">Password</label> | ||||
|             <input type="password" name="password"> | ||||
| 
 | ||||
|             <input type="submit" value="Login"/>             | ||||
|         </form> | ||||
|     </body> | ||||
| </html> | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user