Add authentication #1
File diff suppressed because it is too large
Load Diff
|
@ -11,8 +11,8 @@ trust-dns-client = "0.20.1"
|
||||||
trust-dns-proto = "0.20.1"
|
trust-dns-proto = "0.20.1"
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
rocket = "0.4"
|
rocket = { git = "https://github.com/SergioBenitez/Rocket", rev = "0654890", version = "0.5.0-dev" }
|
||||||
rocket_contrib = { version = "0.4", default-features = false, features = ["json", "diesel_sqlite_pool"]}
|
rocket_contrib = { git = "https://github.com/SergioBenitez/Rocket", rev = "0654890", default-features = false, features = ["json", "diesel_sqlite_pool"], version = "0.5.0-dev"}
|
||||||
toml = "0.5"
|
toml = "0.5"
|
||||||
base64 = "0.13.0"
|
base64 = "0.13.0"
|
||||||
uuid = { version = "0.8.2", features = ["v4", "serde"] }
|
uuid = { version = "0.8.2", features = ["v4", "serde"] }
|
||||||
|
|
|
@ -9,13 +9,16 @@ use crate::models::users::{LocalUser, CreateUserRequest, AuthClaims, AuthTokenRe
|
||||||
|
|
||||||
|
|
||||||
#[post("/users/me/token", data = "<auth_request>")]
|
#[post("/users/me/token", data = "<auth_request>")]
|
||||||
pub fn create_auth_token(
|
pub async fn create_auth_token(
|
||||||
conn: DbConn,
|
conn: DbConn,
|
||||||
config: State<Config>,
|
config: State<'_, Config>,
|
||||||
auth_request: Json<AuthTokenRequest>
|
auth_request: Json<AuthTokenRequest>
|
||||||
) -> Result<Json<AuthTokenResponse>, ErrorResponse<()>> {
|
) -> Result<Json<AuthTokenResponse>, ErrorResponse<()>> {
|
||||||
|
|
||||||
let user_info = LocalUser::get_user_by_creds(&conn, &auth_request.username, &auth_request.password)?;
|
let user_info = conn.run(move |c| {
|
||||||
|
LocalUser::get_user_by_creds(c, &auth_request.username, &auth_request.password)
|
||||||
|
}).await?;
|
||||||
|
|
||||||
let token = AuthClaims::new(&user_info, config.web_app.token_duration)
|
let token = AuthClaims::new(&user_info, config.web_app.token_duration)
|
||||||
.encode(&config.web_app.secret)
|
.encode(&config.web_app.secret)
|
||||||
.map_err(|e| make_500(e))?;
|
.map_err(|e| make_500(e))?;
|
||||||
|
@ -24,9 +27,12 @@ pub fn create_auth_token(
|
||||||
}
|
}
|
||||||
|
|
||||||
#[post("/users", data = "<user_request>")]
|
#[post("/users", data = "<user_request>")]
|
||||||
pub fn create_user<'r>(conn: DbConn, user_request: Json<CreateUserRequest>) -> Result<Response<'r>, ErrorResponse<()>>{
|
pub async fn create_user<'r>(conn: DbConn, user_request: Json<CreateUserRequest>) -> Result<Response<'r>, ErrorResponse<()>>{
|
||||||
// TODO: Check current user if any to check if user has permission to create users (with or without role)
|
// TODO: Check current user if any to check if user has permission to create users (with or without role)
|
||||||
let _user_info = LocalUser::create_user(&conn, user_request.into_inner())?;
|
let _user_info = conn.run(|c| {
|
||||||
|
LocalUser::create_user(&c, user_request.into_inner())
|
||||||
|
}).await?;
|
||||||
|
|
||||||
Response::build()
|
Response::build()
|
||||||
.status(Status::Created)
|
.status(Status::Created)
|
||||||
.ok()
|
.ok()
|
||||||
|
|
|
@ -27,6 +27,7 @@ use auth::routes::*;
|
||||||
#[database("db")]
|
#[database("db")]
|
||||||
pub struct DbConn(diesel::SqliteConnection);
|
pub struct DbConn(diesel::SqliteConnection);
|
||||||
|
|
||||||
|
|
||||||
#[get("/zones/<zone>/records")]
|
#[get("/zones/<zone>/records")]
|
||||||
fn get_zone_records(
|
fn get_zone_records(
|
||||||
client: State<SyncClient<TcpClientConnection>>,
|
client: State<SyncClient<TcpClientConnection>>,
|
||||||
|
@ -60,7 +61,8 @@ fn get_zone_records(
|
||||||
Ok(Json(records))
|
Ok(Json(records))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
#[launch]
|
||||||
|
fn rocket() -> rocket::Rocket {
|
||||||
let app_config = config::load("config.toml".into());
|
let app_config = config::load("config.toml".into());
|
||||||
println!("{:#?}", app_config);
|
println!("{:#?}", app_config);
|
||||||
|
|
||||||
|
@ -71,5 +73,5 @@ fn main() {
|
||||||
.manage(client)
|
.manage(client)
|
||||||
.manage(app_config)
|
.manage(app_config)
|
||||||
.attach(DbConn::fairing())
|
.attach(DbConn::fairing())
|
||||||
.mount("/api/v1", routes![get_zone_records, create_auth_token, create_user]).launch();
|
.mount("/api/v1", routes![get_zone_records, create_auth_token, create_user])
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,8 +44,8 @@ impl<T> ErrorResponse<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'r, T: Serialize> Responder<'r> for ErrorResponse<T> {
|
impl<'r, T: Serialize> Responder<'r, 'static> for ErrorResponse<T> {
|
||||||
fn respond_to(self, req: &Request) -> response::Result<'r> {
|
fn respond_to(self, req: &'r Request<'_>) -> response::Result<'static> {
|
||||||
let status = self.status;
|
let status = self.status;
|
||||||
Response::build_from(Json(self).respond_to(req)?).status(status).ok()
|
Response::build_from(Json(self).respond_to(req)?).status(status).ok()
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@ use uuid::Uuid;
|
||||||
use diesel::prelude::*;
|
use diesel::prelude::*;
|
||||||
use diesel::result::Error as DieselError;
|
use diesel::result::Error as DieselError;
|
||||||
use diesel_derive_enum::DbEnum;
|
use diesel_derive_enum::DbEnum;
|
||||||
use rocket::request::{FromRequest, Request, Outcome, State};
|
use rocket::{State, request::{FromRequest, Request, Outcome}};
|
||||||
use serde::{Serialize, Deserialize};
|
use serde::{Serialize, Deserialize};
|
||||||
use rocket::http::Status;
|
use rocket::http::Status;
|
||||||
use chrono::serde::ts_seconds;
|
use chrono::serde::ts_seconds;
|
||||||
|
@ -93,10 +93,11 @@ pub struct UserInfo {
|
||||||
pub username: String,
|
pub username: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'r> FromRequest<'a, 'r> for UserInfo {
|
#[rocket::async_trait]
|
||||||
|
impl<'r> FromRequest<'r> for UserInfo {
|
||||||
type Error = UserError;
|
type Error = UserError;
|
||||||
|
|
||||||
fn from_request(request: &'a Request<'r>) -> Outcome<UserInfo, UserError> {
|
async fn from_request(request: &'r Request<'_>) -> Outcome<Self, Self::Error> {
|
||||||
let auth_header = match request.headers().get_one(AUTH_HEADER) {
|
let auth_header = match request.headers().get_one(AUTH_HEADER) {
|
||||||
None => return Outcome::Forward(()),
|
None => return Outcome::Forward(()),
|
||||||
Some(auth_header) => auth_header,
|
Some(auth_header) => auth_header,
|
||||||
|
@ -109,8 +110,8 @@ impl<'a, 'r> FromRequest<'a, 'r> for UserInfo {
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO: Better error handling
|
// TODO: Better error handling
|
||||||
let config = request.guard::<State<Config>>().unwrap();
|
let config = request.guard::<State<Config>>().await.unwrap();
|
||||||
let conn = request.guard::<DbConn>().unwrap();
|
let conn = request.guard::<DbConn>().await.unwrap();
|
||||||
|
|
||||||
let token_data = AuthClaims::decode(
|
let token_data = AuthClaims::decode(
|
||||||
token, &config.web_app.secret
|
token, &config.web_app.secret
|
||||||
|
@ -125,13 +126,14 @@ impl<'a, 'r> FromRequest<'a, 'r> for UserInfo {
|
||||||
};
|
};
|
||||||
|
|
||||||
let user_id = token_data.sub;
|
let user_id = token_data.sub;
|
||||||
let user_info = match LocalUser::get_user_by_uuid(&conn, user_id) {
|
|
||||||
Err(UserError::NotFound) => return Outcome::Failure((Status::NotFound, UserError::NotFound)),
|
|
||||||
Err(e) => return Outcome::Failure((Status::InternalServerError, e)),
|
|
||||||
Ok(d) => d,
|
|
||||||
};
|
|
||||||
|
|
||||||
return Outcome::Success(user_info)
|
conn.run(|c| {
|
||||||
|
match LocalUser::get_user_by_uuid(c, user_id) {
|
||||||
|
Err(UserError::NotFound) => Outcome::Failure((Status::NotFound, UserError::NotFound)),
|
||||||
|
Err(e) => Outcome::Failure((Status::InternalServerError, e)),
|
||||||
|
Ok(d) => Outcome::Success(d),
|
||||||
|
}
|
||||||
|
}).await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -166,7 +168,7 @@ impl From<HasherError> for UserError {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LocalUser {
|
impl LocalUser {
|
||||||
pub fn create_user(conn: &DbConn, user_request: CreateUserRequest) -> Result<UserInfo, UserError> {
|
pub fn create_user(conn: &diesel::SqliteConnection, user_request: CreateUserRequest) -> Result<UserInfo, UserError> {
|
||||||
use crate::schema::localuser::dsl::*;
|
use crate::schema::localuser::dsl::*;
|
||||||
use crate::schema::user::dsl::*;
|
use crate::schema::user::dsl::*;
|
||||||
|
|
||||||
|
@ -193,11 +195,11 @@ impl LocalUser {
|
||||||
conn.immediate_transaction(|| -> diesel::QueryResult<()> {
|
conn.immediate_transaction(|| -> diesel::QueryResult<()> {
|
||||||
diesel::insert_into(user)
|
diesel::insert_into(user)
|
||||||
.values(new_user)
|
.values(new_user)
|
||||||
.execute(&**conn)?;
|
.execute(conn)?;
|
||||||
|
|
||||||
diesel::insert_into(localuser)
|
diesel::insert_into(localuser)
|
||||||
.values(new_localuser)
|
.values(new_localuser)
|
||||||
.execute(&**conn)?;
|
.execute(conn)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
})?;
|
})?;
|
||||||
|
@ -206,7 +208,7 @@ impl LocalUser {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_user_by_creds(
|
pub fn get_user_by_creds(
|
||||||
conn: &DbConn,
|
conn: &diesel::SqliteConnection,
|
||||||
request_username: &str,
|
request_username: &str,
|
||||||
request_password: &str
|
request_password: &str
|
||||||
) -> Result<UserInfo, UserError> {
|
) -> Result<UserInfo, UserError> {
|
||||||
|
@ -216,7 +218,7 @@ impl LocalUser {
|
||||||
|
|
||||||
let (client_user, client_localuser): (User, LocalUser) = user.inner_join(localuser)
|
let (client_user, client_localuser): (User, LocalUser) = user.inner_join(localuser)
|
||||||
.filter(username.eq(request_username))
|
.filter(username.eq(request_username))
|
||||||
.get_result(&**conn)?;
|
.get_result(conn)?;
|
||||||
|
|
||||||
if !check_password(&request_password, &client_localuser.password)? {
|
if !check_password(&request_password, &client_localuser.password)? {
|
||||||
return Err(UserError::NotFound);
|
return Err(UserError::NotFound);
|
||||||
|
@ -229,13 +231,13 @@ impl LocalUser {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_user_by_uuid(conn: &DbConn, request_user_id: String) -> Result<UserInfo, UserError> {
|
pub fn get_user_by_uuid(conn: &diesel::SqliteConnection, request_user_id: String) -> Result<UserInfo, UserError> {
|
||||||
use crate::schema::localuser::dsl::*;
|
use crate::schema::localuser::dsl::*;
|
||||||
use crate::schema::user::dsl::*;
|
use crate::schema::user::dsl::*;
|
||||||
|
|
||||||
let (client_user, client_localuser): (User, LocalUser) = user.inner_join(localuser)
|
let (client_user, client_localuser): (User, LocalUser) = user.inner_join(localuser)
|
||||||
.filter(id.eq(request_user_id))
|
.filter(id.eq(request_user_id))
|
||||||
.get_result(&**conn)?;
|
.get_result(conn)?;
|
||||||
|
|
||||||
Ok(UserInfo {
|
Ok(UserInfo {
|
||||||
id: client_user.id,
|
id: client_user.id,
|
||||||
|
|
Loading…
Reference in New Issue