Add method to refresh a request token

Signed-off-by: trivernis <trivernis@protonmail.com>
pull/1/head
trivernis 4 years ago
parent 5e77c77559
commit 88d4c3966b
Signed by: Trivernis
GPG Key ID: DFFFCC2C7A02DB45

1
Cargo.lock generated

@ -527,6 +527,7 @@ dependencies = [
name = "flotte-user-management"
version = "0.1.0"
dependencies = [
"base64 0.12.3",
"bcrypt",
"byteorder",
"colored",

@ -25,4 +25,5 @@ colored = "2.0.0"
crossbeam-utils = "0.7.2"
mime = "0.3.16"
serde_json = "1.0.57"
rouille = "3.0.0"
rouille = "3.0.0"
base64 = "0.12.3"

@ -1,6 +1,6 @@
use crate::database::redis_operations::{EX, GET, SET, TTL};
use crate::utils::create_user_token;
use crate::utils::error::RedisConnection;
use crate::utils::{create_user_token, get_user_id_from_token};
use byteorder::{BigEndian, ByteOrder};
use redis::{ErrorKind, RedisError, RedisResult};
use serde::Serialize;
@ -12,8 +12,8 @@ const REFRESH_TOKEN_EXPIRE_SECONDS: i32 = 60 * 60 * 24;
#[derive(Clone, Debug, Zeroize, Serialize)]
#[zeroize(drop)]
pub struct SessionTokens {
pub request_token: [u8; 32],
pub refresh_token: [u8; 32],
pub request_token: String,
pub refresh_token: String,
pub request_ttl: i32,
pub refresh_ttl: i32,
}
@ -21,14 +21,14 @@ pub struct SessionTokens {
impl SessionTokens {
pub fn new(user_id: i32) -> Self {
Self {
request_token: create_user_token(user_id),
refresh_token: create_user_token(user_id),
request_token: base64::encode(create_user_token(user_id)),
refresh_token: base64::encode(create_user_token(user_id)),
request_ttl: REQUEST_TOKEN_EXPIRE_SECONDS,
refresh_ttl: REFRESH_TOKEN_EXPIRE_SECONDS,
}
}
pub fn from_tokens(request_token: [u8; 32], refresh_token: [u8; 32]) -> Self {
pub fn from_tokens(request_token: String, refresh_token: String) -> Self {
Self {
request_token,
refresh_token,
@ -39,27 +39,21 @@ impl SessionTokens {
pub fn retrieve(user_id: i32, redis_connection: &mut RedisConnection) -> RedisResult<Self> {
let redis_request_key = format!("user-{}_request", user_id);
let request_token_vec: Vec<u8> = redis::cmd(GET)
let request_token: String = redis::cmd(GET)
.arg(&redis_request_key)
.query(redis_connection)?;
let redis_refresh_key = format!("user-{}_refresh", user_id);
let refresh_token_vec: Vec<u8> = redis::cmd(GET)
let refresh_token: String = redis::cmd(GET)
.arg(&redis_refresh_key)
.query(redis_connection)?;
let mut request_token = [0u8; 32];
let mut refresh_token = [0u8; 32];
if request_token_vec.len() == 32 {
request_token.copy_from_slice(&request_token_vec);
} else {
if request_token.len() == 0 {
return Err(RedisError::from((
ErrorKind::ResponseError,
"No refresh token available",
)));
}
if refresh_token_vec.len() == 32 {
refresh_token.copy_from_slice(&refresh_token_vec);
} else {
if refresh_token.len() == 0 {
return Err(RedisError::from((
ErrorKind::ResponseError,
"No refresh token available",
@ -81,13 +75,12 @@ impl SessionTokens {
}
pub fn refresh(&mut self) {
self.request_token = create_user_token(self.get_user_id());
self.refresh_token = create_user_token(self.get_user_id());
self.request_token = base64::encode(create_user_token(self.get_user_id()));
}
/// Returns the user id that is stored in the first four bytes of the refresh token
pub fn get_user_id(&self) -> i32 {
BigEndian::read_i32(&self.refresh_token[0..4])
get_user_id_from_token(&self.refresh_token)
}
/// Saves the tokens into the database

@ -77,8 +77,8 @@ impl Users {
pub fn create_get_tokens(
&self,
email: String,
password: String,
email: &String,
password: &String,
) -> DatabaseResult<SessionTokens> {
if self.validate_login(&email, password)? {
let mut connection = self.database_connection.lock().unwrap();
@ -99,10 +99,7 @@ impl Users {
}
}
pub fn validate_request_token(
&self,
token: &[u8; TOKEN_LENGTH],
) -> DatabaseResult<(bool, i32)> {
pub fn validate_request_token(&self, token: &String) -> DatabaseResult<(bool, i32)> {
let id = get_user_id_from_token(token);
let mut redis_connection = self.redis_connection.lock().unwrap();
let tokens = SessionTokens::retrieve(id, &mut redis_connection)?;
@ -110,10 +107,7 @@ impl Users {
Ok((tokens.request_token == *token, tokens.request_ttl))
}
pub fn validate_refresh_token(
&self,
token: &[u8; TOKEN_LENGTH],
) -> DatabaseResult<(bool, i32)> {
pub fn validate_refresh_token(&self, token: &String) -> DatabaseResult<(bool, i32)> {
let id = get_user_id_from_token(token);
let mut redis_connection = self.redis_connection.lock().unwrap();
let tokens = SessionTokens::retrieve(id, &mut redis_connection)?;
@ -121,11 +115,8 @@ impl Users {
Ok((tokens.refresh_token == *token, tokens.refresh_ttl))
}
pub fn refresh_tokens(
&self,
refresh_token: &[u8; TOKEN_LENGTH],
) -> DatabaseResult<SessionTokens> {
let id = get_user_id_from_token(refresh_token);
pub fn refresh_tokens(&self, refresh_token: &String) -> DatabaseResult<SessionTokens> {
let id = get_user_id_from_token(&refresh_token);
let mut redis_connection = self.redis_connection.lock().unwrap();
let mut tokens = SessionTokens::retrieve(id, &mut redis_connection)?;
@ -139,8 +130,7 @@ impl Users {
}
}
fn validate_login(&self, email: &String, password: String) -> DatabaseResult<bool> {
let password = Zeroizing::new(password);
fn validate_login(&self, email: &String, password: &String) -> DatabaseResult<bool> {
let mut connection = self.database_connection.lock().unwrap();
let row = connection
.query_opt(

@ -1,5 +1,5 @@
use crate::database::Database;
use crate::server::messages::LoginMessage;
use crate::server::messages::{LoginMessage, RefreshMessage};
use crate::utils::error::DBError;
use rouille::{Request, Response, Server};
use serde::export::Formatter;
@ -60,6 +60,9 @@ impl UserHttpServer {
(POST) (/login) => {
Self::login(&database, request).unwrap_or_else(|e|Response::text(e.to_string()))
},
(POST) (/new-token) => {
Self::new_token(&database, request).unwrap_or_else(|e|Response::text(e.to_string()))
},
_ => Response::empty_404()
)
})
@ -76,7 +79,23 @@ impl UserHttpServer {
.map_err(|e| HTTPError::new(e.to_string(), 400))?;
let tokens = database
.users
.create_get_tokens(login_request.email, login_request.password)?;
.create_get_tokens(&login_request.email, &login_request.password)?;
Ok(Response::json(&tokens))
} else {
Err(HTTPError::new("Missing Request Data".to_string(), 400))
}
}
fn new_token(database: &Database, request: &Request) -> HTTPResult<Response> {
if let Some(mut data) = request.data() {
let mut data_string = String::new();
data.read_to_string(&mut data_string)
.map_err(|_| HTTPError::new("Failed to read request data".to_string(), 500))?;
let message: RefreshMessage = serde_json::from_str(data_string.as_str())
.map_err(|e| HTTPError::new(e.to_string(), 400))?;
let tokens = database.users.refresh_tokens(&message.refresh_token)?;
Ok(Response::json(&tokens))
} else {

@ -9,7 +9,7 @@ use zeroize::Zeroize;
#[derive(Deserialize)]
pub struct TokenRequest {
pub token: [u8; 32],
pub token: String,
}
#[derive(Debug, Serialize)]
@ -77,7 +77,14 @@ pub struct CreatePermissionsRequest {
}
#[derive(Deserialize, Zeroize)]
#[zeroize(drop)]
pub struct LoginMessage {
pub email: String,
pub password: String,
}
#[derive(Deserialize, Zeroize)]
#[zeroize(drop)]
pub struct RefreshMessage {
pub refresh_token: String,
}

@ -79,13 +79,13 @@ impl UserRpcServer {
"validate token",
VALIDATE_TOKEN,
"Validates a request token",
"{token: [u8; 32]}",
"{token: String}",
),
InfoEntry::new(
"get roles",
GET_ROLES,
"Returns the roles the user is assigned to",
"{token: [u8; 32]}",
"{token: String}",
),
InfoEntry::new(
"get permissions",

@ -25,8 +25,9 @@ pub fn create_user_token(user_id: i32) -> [u8; TOKEN_LENGTH] {
value
}
pub fn get_user_id_from_token(token: &[u8]) -> i32 {
BigEndian::read_i32(token)
pub fn get_user_id_from_token(token: &String) -> i32 {
let token = base64::decode(&token).unwrap();
BigEndian::read_i32(token.as_slice())
}
pub fn hash_password(password: &[u8], salt: &[u8]) -> Result<[u8; 24], String> {

Loading…
Cancel
Save