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" name = "flotte-user-management"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"base64 0.12.3",
"bcrypt", "bcrypt",
"byteorder", "byteorder",
"colored", "colored",

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

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

@ -1,5 +1,5 @@
use crate::database::Database; use crate::database::Database;
use crate::server::messages::LoginMessage; use crate::server::messages::{LoginMessage, RefreshMessage};
use crate::utils::error::DBError; use crate::utils::error::DBError;
use rouille::{Request, Response, Server}; use rouille::{Request, Response, Server};
use serde::export::Formatter; use serde::export::Formatter;
@ -60,6 +60,9 @@ impl UserHttpServer {
(POST) (/login) => { (POST) (/login) => {
Self::login(&database, request).unwrap_or_else(|e|Response::text(e.to_string())) 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() _ => Response::empty_404()
) )
}) })
@ -76,7 +79,23 @@ impl UserHttpServer {
.map_err(|e| HTTPError::new(e.to_string(), 400))?; .map_err(|e| HTTPError::new(e.to_string(), 400))?;
let tokens = database let tokens = database
.users .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)) Ok(Response::json(&tokens))
} else { } else {

@ -9,7 +9,7 @@ use zeroize::Zeroize;
#[derive(Deserialize)] #[derive(Deserialize)]
pub struct TokenRequest { pub struct TokenRequest {
pub token: [u8; 32], pub token: String,
} }
#[derive(Debug, Serialize)] #[derive(Debug, Serialize)]
@ -77,7 +77,14 @@ pub struct CreatePermissionsRequest {
} }
#[derive(Deserialize, Zeroize)] #[derive(Deserialize, Zeroize)]
#[zeroize(drop)]
pub struct LoginMessage { pub struct LoginMessage {
pub email: String, pub email: String,
pub password: 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",
VALIDATE_TOKEN, VALIDATE_TOKEN,
"Validates a request token", "Validates a request token",
"{token: [u8; 32]}", "{token: String}",
), ),
InfoEntry::new( InfoEntry::new(
"get roles", "get roles",
GET_ROLES, GET_ROLES,
"Returns the roles the user is assigned to", "Returns the roles the user is assigned to",
"{token: [u8; 32]}", "{token: String}",
), ),
InfoEntry::new( InfoEntry::new(
"get permissions", "get permissions",

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

Loading…
Cancel
Save