2021-03-26 22:30:38 +00:00
|
|
|
use uuid::Uuid;
|
|
|
|
use diesel::prelude::*;
|
2021-03-27 05:45:59 +00:00
|
|
|
use diesel::result::Error as DieselError;
|
2021-03-26 22:30:38 +00:00
|
|
|
use diesel_derive_enum::DbEnum;
|
2021-03-27 17:23:19 +00:00
|
|
|
use rocket::request::{FromRequest, Request, Outcome};
|
|
|
|
use serde::{Serialize, Deserialize};
|
|
|
|
use chrono::serde::ts_seconds;
|
|
|
|
use chrono::prelude::{DateTime, Utc};
|
|
|
|
use chrono::Duration;
|
2021-03-27 05:45:59 +00:00
|
|
|
// TODO: Maybe just use argon2 crate directly
|
|
|
|
use djangohashers::{make_password_with_algorithm, check_password, HasherError, Algorithm};
|
2021-03-27 17:23:19 +00:00
|
|
|
use jsonwebtoken::{encode, Header, EncodingKey, errors::Result as JwtResult};
|
2021-03-26 22:30:38 +00:00
|
|
|
|
|
|
|
use crate::schema::*;
|
2021-03-27 05:45:59 +00:00
|
|
|
use crate::DbConn;
|
2021-03-26 22:30:38 +00:00
|
|
|
|
2021-03-27 17:23:19 +00:00
|
|
|
|
2021-03-27 05:45:59 +00:00
|
|
|
#[derive(Debug, DbEnum, Deserialize)]
|
|
|
|
#[serde(rename_all = "snake_case")]
|
2021-03-26 22:30:38 +00:00
|
|
|
pub enum Role {
|
|
|
|
Admin,
|
|
|
|
ZoneAdmin,
|
|
|
|
}
|
|
|
|
|
2021-03-27 05:45:59 +00:00
|
|
|
// TODO: Store Uuid instead of string??
|
|
|
|
// TODO: Store role as Role and not String.
|
|
|
|
#[derive(Debug, Queryable, Identifiable, Insertable)]
|
2021-03-26 22:30:38 +00:00
|
|
|
#[table_name = "user"]
|
|
|
|
pub struct User {
|
|
|
|
pub id: String,
|
|
|
|
pub role: String,
|
|
|
|
}
|
|
|
|
|
2021-03-27 05:45:59 +00:00
|
|
|
#[derive(Debug, Queryable, Identifiable, Insertable)]
|
2021-03-26 22:30:38 +00:00
|
|
|
#[table_name = "localuser"]
|
|
|
|
#[primary_key(user_id)]
|
|
|
|
pub struct LocalUser {
|
|
|
|
pub user_id: String,
|
|
|
|
pub username: String,
|
|
|
|
pub password: String,
|
|
|
|
}
|
|
|
|
|
2021-03-27 05:45:59 +00:00
|
|
|
#[derive(Debug, Deserialize)]
|
|
|
|
pub struct CreateUserRequest {
|
|
|
|
pub username: String,
|
|
|
|
pub password: String,
|
|
|
|
pub email: String,
|
|
|
|
pub role: Option<Role>
|
|
|
|
}
|
|
|
|
|
2021-03-26 22:30:38 +00:00
|
|
|
// pub struct LdapUserAssociation {
|
|
|
|
// user_id: Uuid,
|
|
|
|
// ldap_id: String
|
|
|
|
// }
|
|
|
|
|
2021-03-27 17:23:19 +00:00
|
|
|
#[derive(Debug, Serialize, Deserialize)]
|
|
|
|
pub struct AuthClaims {
|
|
|
|
pub jti: String,
|
|
|
|
pub sub: String,
|
|
|
|
#[serde(with = "ts_seconds")]
|
|
|
|
pub exp: DateTime<Utc>,
|
|
|
|
#[serde(with = "ts_seconds")]
|
|
|
|
pub iat: DateTime<Utc>,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug, Serialize)]
|
|
|
|
pub struct AuthTokenResponse {
|
|
|
|
pub token: String
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug, Deserialize)]
|
|
|
|
pub struct AuthTokenRequest {
|
|
|
|
pub username: String,
|
|
|
|
pub password: String,
|
|
|
|
}
|
|
|
|
|
2021-03-27 05:45:59 +00:00
|
|
|
#[derive(Debug)]
|
2021-03-26 22:30:38 +00:00
|
|
|
pub struct UserInfo {
|
2021-03-27 05:45:59 +00:00
|
|
|
pub id: String,
|
|
|
|
pub role: String,
|
|
|
|
pub username: String,
|
2021-03-26 22:30:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a, 'r> FromRequest<'a, 'r> for UserInfo {
|
|
|
|
type Error = ();
|
|
|
|
|
|
|
|
fn from_request(request: &'a Request<'r>) -> Outcome<UserInfo, ()> {
|
2021-03-27 05:45:59 +00:00
|
|
|
// LocalUser::get_user_by_uuid()
|
2021-03-26 22:30:38 +00:00
|
|
|
Outcome::Forward(())
|
|
|
|
}
|
|
|
|
}
|
2021-03-27 05:45:59 +00:00
|
|
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
pub enum UserError {
|
|
|
|
NotFound,
|
|
|
|
UserExists,
|
|
|
|
DbError(DieselError),
|
|
|
|
PasswordError(HasherError),
|
|
|
|
}
|
|
|
|
|
|
|
|
impl From<DieselError> for UserError {
|
|
|
|
fn from(e: DieselError) -> Self {
|
|
|
|
match e {
|
|
|
|
DieselError::NotFound => UserError::NotFound,
|
|
|
|
DieselError::DatabaseError(diesel::result::DatabaseErrorKind::UniqueViolation, _) => UserError::UserExists,
|
|
|
|
other => UserError::DbError(other)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl From<HasherError> for UserError {
|
|
|
|
fn from(e: HasherError) -> Self {
|
|
|
|
match e {
|
|
|
|
other => UserError::PasswordError(other)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl LocalUser {
|
|
|
|
pub fn create_user(conn: &DbConn, user_request: CreateUserRequest) -> Result<UserInfo, UserError> {
|
|
|
|
use crate::schema::localuser::dsl::*;
|
|
|
|
use crate::schema::user::dsl::*;
|
|
|
|
|
|
|
|
let new_user_id = Uuid::new_v4().to_simple().to_string();
|
|
|
|
|
|
|
|
let new_user = User {
|
|
|
|
id: new_user_id.clone(),
|
|
|
|
// TODO: Use role from request
|
|
|
|
role: "zoneadmin".into(),
|
|
|
|
};
|
|
|
|
|
|
|
|
let new_localuser = LocalUser {
|
|
|
|
user_id: new_user_id.clone(),
|
|
|
|
username: user_request.username.clone(),
|
|
|
|
password: make_password_with_algorithm(&user_request.password, Algorithm::Argon2),
|
|
|
|
};
|
|
|
|
|
|
|
|
let res = UserInfo {
|
|
|
|
id: new_user.id.clone(),
|
|
|
|
role: new_user.role.clone(),
|
|
|
|
username: new_localuser.username.clone(),
|
|
|
|
};
|
|
|
|
|
|
|
|
conn.immediate_transaction(|| -> diesel::QueryResult<()> {
|
|
|
|
diesel::insert_into(user)
|
|
|
|
.values(new_user)
|
|
|
|
.execute(&**conn)?;
|
|
|
|
|
|
|
|
diesel::insert_into(localuser)
|
|
|
|
.values(new_localuser)
|
|
|
|
.execute(&**conn)?;
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
})?;
|
|
|
|
|
|
|
|
Ok(res)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn get_user_by_creds(
|
|
|
|
conn: &DbConn,
|
|
|
|
request_username: &str,
|
|
|
|
request_password: &str
|
|
|
|
) -> Result<UserInfo, UserError> {
|
|
|
|
|
|
|
|
use crate::schema::localuser::dsl::*;
|
|
|
|
use crate::schema::user::dsl::*;
|
|
|
|
|
|
|
|
let (client_user, client_localuser): (User, LocalUser) = user.inner_join(localuser)
|
|
|
|
.filter(username.eq(request_username))
|
|
|
|
.get_result(&**conn)?;
|
|
|
|
|
|
|
|
if !check_password(&request_password, &client_localuser.password)? {
|
|
|
|
return Err(UserError::NotFound);
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(UserInfo {
|
|
|
|
id: client_user.id,
|
|
|
|
role: client_user.role,
|
|
|
|
username: client_localuser.username,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn get_user_by_uuid(user_id: Uuid) -> Result<UserInfo, ()> {
|
|
|
|
unimplemented!()
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
2021-03-27 17:23:19 +00:00
|
|
|
|
|
|
|
impl AuthClaims {
|
|
|
|
pub fn new(user_info: &UserInfo, token_duration: Duration) -> AuthClaims {
|
|
|
|
let jti = Uuid::new_v4().to_simple().to_string();
|
|
|
|
let iat = Utc::now();
|
|
|
|
let exp = iat + token_duration;
|
|
|
|
|
|
|
|
AuthClaims {
|
|
|
|
jti: jti,
|
|
|
|
sub: user_info.id.clone(),
|
|
|
|
exp: exp,
|
|
|
|
iat: iat,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn encode(self, secret: &[u8]) -> JwtResult<String> {
|
|
|
|
encode(&Header::default(), &self, &EncodingKey::from_secret(&secret))
|
|
|
|
}
|
|
|
|
}
|