Add getRoles method and return ttl for tokens

Signed-off-by: trivernis <trivernis@protonmail.com>
pull/1/head
trivernis 4 years ago
parent 7aded7b002
commit a6119b04bf
Signed by: Trivernis
GPG Key ID: DFFFCC2C7A02DB45

@ -3,15 +3,15 @@ use crate::database::role_permissions::RolePermissions;
use crate::database::roles::Roles;
use crate::database::user_roles::UserRoles;
use crate::database::users::Users;
use crate::utils::error::{
DBError, DatabaseClient, DatabaseResult, PostgresError, RedisClient, RedisConnection,
};
use dotenv;
use postgres::{Client, NoTls};
use redis::{RedisError, RedisResult};
use serde::export::Formatter;
use std::error;
use std::fmt;
use std::fmt::Display;
use redis::RedisResult;
use std::sync::{Arc, Mutex};
pub mod database_error;
pub mod models;
pub mod permissions;
pub mod redis_operations;
@ -26,11 +26,6 @@ const DEFAULT_CONNECTION: &str = "postgres://postgres:postgres@localhost/postgre
const REDIS_CONNECTION_URL: &str = "REDIS_CONNECTION_URL";
const DEFAULT_REDIS_CONNECTION: &str = "redis:://127.0.0.1/";
pub type DatabaseClient = postgres::Client;
pub type RedisClient = redis::Client;
pub type RedisConnection = redis::Connection;
pub type PostgresError = postgres::Error;
pub trait Table {
fn new(
database_connection: Arc<Mutex<DatabaseClient>>,
@ -39,27 +34,6 @@ pub trait Table {
fn init(&self) -> DatabaseResult<()>;
}
#[derive(Debug)]
pub enum Error {
Redis(RedisError),
Postgres(PostgresError),
RecordExists,
ScryptError,
DeserializeError(serde_postgres::DeError),
GenericError(String),
}
impl Display for Error {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.to_string())
}
}
impl error::Error for Error {}
pub type DatabaseError = Error;
pub type DatabaseResult<T> = Result<T, Error>;
#[derive(Clone)]
pub struct Database {
database_connection: Arc<Mutex<Client>>,
@ -74,10 +48,10 @@ pub struct Database {
impl Database {
pub fn new() -> DatabaseResult<Self> {
let database_connection = Arc::new(Mutex::new(
get_database_connection().map_err(|e| Error::Postgres(e))?,
get_database_connection().map_err(|e| DBError::Postgres(e))?,
));
let redis_connection = Arc::new(Mutex::new(
get_redis_connection().map_err(|e| Error::Redis(e))?,
get_redis_connection().map_err(|e| DBError::Redis(e))?,
));
Ok(Self {
users: Users::new(

@ -30,3 +30,10 @@ pub struct Permission {
pub name: String,
pub description: String,
}
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct Role {
pub id: i32,
pub name: String,
pub description: String,
}

@ -1,4 +1,5 @@
use crate::database::{DatabaseClient, DatabaseError, DatabaseResult, RedisConnection, Table};
use crate::database::{DatabaseClient, DatabaseResult, RedisConnection, Table};
use crate::utils::error::DBError;
use postgres::Client;
use std::sync::{Arc, Mutex};
@ -30,6 +31,6 @@ impl Table for Permissions {
description VARCHAR(512)
);",
)
.map_err(|e| DatabaseError::Postgres(e))
.map_err(DBError::from)
}
}

@ -1,5 +1,6 @@
use crate::database::models::Permission;
use crate::database::{DatabaseClient, DatabaseError, DatabaseResult, RedisConnection, Table};
use crate::database::{DatabaseClient, DatabaseResult, RedisConnection, Table};
use crate::utils::error::DBError;
use std::sync::{Arc, Mutex};
#[derive(Clone)]
@ -31,14 +32,17 @@ impl Table for RolePermissions {
PRIMARY KEY (role_id, permission_id)
);",
)
.map_err(|e| DatabaseError::Postgres(e))
.map_err(DBError::from)
}
}
impl RolePermissions {
pub fn by_role(&self, role_id: i32) -> DatabaseResult<Vec<Permission>> {
let mut connection = self.database_connection.lock().unwrap();
let rows = connection.query("SELECT * FROM role_permissions, permissions WHERE role_id = $1 AND role_permissions.permission_id = permissions.id", &[&role_id]).map_err(|e|DatabaseError::Postgres(e))?;
serde_postgres::from_rows(&rows).map_err(|e| DatabaseError::DeserializeError(e))
let rows = connection.query(
"SELECT * FROM role_permissions, permissions WHERE role_id = $1 AND role_permissions.permission_id = permissions.id",
&[&role_id])?;
serde_postgres::from_rows(&rows).map_err(DBError::from)
}
}

@ -1,5 +1,6 @@
use crate::database::role_permissions::RolePermissions;
use crate::database::{DatabaseError, DatabaseResult, RedisConnection, Table};
use crate::database::{DatabaseResult, RedisConnection, Table};
use crate::utils::error::DBError;
use postgres::Client;
use std::sync::{Arc, Mutex};
@ -37,6 +38,6 @@ impl Table for Roles {
description VARCHAR(512)
);",
)
.map_err(|e| DatabaseError::Postgres(e))
.map_err(DBError::from)
}
}

@ -1,8 +1,8 @@
use crate::database::redis_operations::{EX, GET, SET};
use crate::database::RedisConnection;
use crate::database::redis_operations::{EX, GET, SET, TTL};
use crate::utils::create_user_token;
use crate::utils::error::RedisConnection;
use byteorder::{BigEndian, ByteOrder};
use redis::RedisResult;
use redis::{ErrorKind, RedisError, RedisResult};
use zeroize::Zeroize;
const REQUEST_TOKEN_EXPIRE_SECONDS: usize = 60 * 10;
@ -13,6 +13,8 @@ const REFRESH_TOKEN_EXPIRE_SECONDS: usize = 60 * 60 * 24;
pub struct SessionTokens {
pub request_token: [u8; 32],
pub refresh_token: [u8; 32],
pub request_ttl: i32,
pub refresh_ttl: i32,
}
impl SessionTokens {
@ -20,6 +22,8 @@ impl SessionTokens {
Self {
request_token: create_user_token(user_id),
refresh_token: create_user_token(user_id),
request_ttl: -1,
refresh_ttl: -1,
}
}
@ -27,31 +31,51 @@ impl SessionTokens {
Self {
request_token,
refresh_token,
request_ttl: -1,
refresh_ttl: -1,
}
}
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)
.arg(redis_request_key)
.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)
.arg(redis_refresh_key)
.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 {
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 {
return Err(RedisError::from((
ErrorKind::ResponseError,
"No refresh token available",
)));
}
let request_ttl: i32 = redis::cmd(TTL)
.arg(&redis_request_key)
.query(redis_connection)?;
let refresh_ttl: i32 = redis::cmd(TTL)
.arg(&redis_refresh_key)
.query(redis_connection)?;
Ok(Self {
request_token,
refresh_token,
request_ttl,
refresh_ttl,
})
}

@ -1,4 +1,6 @@
use crate::database::{DatabaseError, DatabaseResult, RedisConnection, Table};
use crate::database::models::Role;
use crate::database::{DatabaseResult, RedisConnection, Table};
use crate::utils::error::DBError;
use postgres::Client;
use std::sync::{Arc, Mutex};
@ -31,6 +33,18 @@ impl Table for UserRoles {
PRIMARY KEY (user_id, role_id)
);",
)
.map_err(|e| DatabaseError::Postgres(e))
.map_err(DBError::from)
}
}
impl UserRoles {
pub fn by_user(&self, user_id: i32) -> DatabaseResult<Vec<Role>> {
let mut connection = self.database_connection.lock().unwrap();
let rows = connection.query(
"SELECT * FROM user_roles, roles WHERE user_id = $1 AND roles.id = user_roles.role_id",
&[&user_id],
)?;
serde_postgres::from_rows(&rows).map_err(DBError::from)
}
}

@ -1,7 +1,8 @@
use crate::database::models::UserRecord;
use crate::database::tokens::SessionTokens;
use crate::database::user_roles::UserRoles;
use crate::database::{DatabaseError, DatabaseResult, RedisConnection, Table};
use crate::database::{DatabaseResult, RedisConnection, Table};
use crate::utils::error::DBError;
use crate::utils::{create_salt, get_user_id_from_token, TOKEN_LENGTH};
use postgres::Client;
use scrypt::ScryptParams;
@ -43,7 +44,7 @@ impl Table for Users {
salt BYTEA NOT NULL
);",
)
.map_err(|e| DatabaseError::Postgres(e))
.map_err(DBError::from)
}
}
@ -58,11 +59,10 @@ impl Users {
let mut password = Zeroizing::new(password);
if !connection
.query("SELECT email FROM users WHERE email = $1", &[&email])
.map_err(|e| DatabaseError::Postgres(e))?
.query("SELECT email FROM users WHERE email = $1", &[&email])?
.is_empty()
{
return Err(DatabaseError::RecordExists);
return Err(DBError::RecordExists);
}
let salt = Zeroizing::new(create_salt());
let mut pw_hash = Zeroizing::new([0u8; 32]);
@ -72,11 +72,11 @@ impl Users {
&ScryptParams::recommended(),
&mut *pw_hash,
)
.map_err(|_| DatabaseError::ScryptError)?;
.map_err(|_| DBError::ScryptError)?;
password.zeroize();
let row = connection.query_one("
INSERT INTO users (name, email, password_hash, salt) VALUES ($1, $2, $3, $4) RETURNING *;
", &[&name, &email, &pw_hash.to_vec(), &salt.to_vec()]).map_err(|e|DatabaseError::Postgres(e))?;
", &[&name, &email, &pw_hash.to_vec(), &salt.to_vec()])?;
Ok(UserRecord::from_ordered_row(&row))
}
@ -84,39 +84,39 @@ impl Users {
pub fn create_token(&self, email: String, password: String) -> DatabaseResult<SessionTokens> {
if self.validate_login(&email, password)? {
let mut connection = self.database_connection.lock().unwrap();
let row = connection
.query_one("SELECT id FROM users WHERE email = $1", &[&email])
.map_err(|e| DatabaseError::Postgres(e))?;
let row = connection.query_one("SELECT id FROM users WHERE email = $1", &[&email])?;
let id: i32 = row.get(0);
let mut redis_connection = self.redis_connection.lock().unwrap();
let tokens = SessionTokens::new(id);
tokens
.store(&mut redis_connection)
.map_err(|e| DatabaseError::Redis(e))?;
tokens.store(&mut redis_connection)?;
Ok(tokens)
} else {
Err(DatabaseError::GenericError("Invalid password".to_string()))
Err(DBError::GenericError("Invalid password".to_string()))
}
}
pub fn validate_request_token(&self, token: &[u8; TOKEN_LENGTH]) -> DatabaseResult<bool> {
pub fn validate_request_token(
&self,
token: &[u8; TOKEN_LENGTH],
) -> 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)
.map_err(|e| DatabaseError::Redis(e))?;
let tokens = SessionTokens::retrieve(id, &mut redis_connection)?;
Ok(tokens.request_token == *token)
Ok((tokens.request_token == *token, tokens.request_ttl))
}
pub fn validate_refresh_token(&self, token: &[u8; TOKEN_LENGTH]) -> DatabaseResult<bool> {
pub fn validate_refresh_token(
&self,
token: &[u8; TOKEN_LENGTH],
) -> 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)
.map_err(|e| DatabaseError::Redis(e))?;
let tokens = SessionTokens::retrieve(id, &mut redis_connection)?;
Ok(tokens.refresh_token == *token)
Ok((tokens.refresh_token == *token, tokens.refresh_ttl))
}
pub fn refresh_tokens(
@ -125,32 +125,25 @@ impl Users {
) -> 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)
.map_err(|e| DatabaseError::Redis(e))?;
let mut tokens = SessionTokens::retrieve(id, &mut redis_connection)?;
if tokens.refresh_token == *refresh_token {
tokens.refresh();
tokens
.store(&mut redis_connection)
.map_err(|e| DatabaseError::Redis(e))?;
tokens.store(&mut redis_connection)?;
Ok(tokens)
} else {
Err(DatabaseError::GenericError(
"Invalid refresh token!".to_string(),
))
Err(DBError::GenericError("Invalid refresh token!".to_string()))
}
}
fn validate_login(&self, email: &String, password: String) -> DatabaseResult<bool> {
let password = Zeroizing::new(password);
let mut connection = self.database_connection.lock().unwrap();
let row = connection
.query_one(
"SELECT password_hash, salt FROM users WHERE email = $1",
&[&email],
)
.map_err(|e| DatabaseError::Postgres(e))?;
let row = connection.query_one(
"SELECT password_hash, salt FROM users WHERE email = $1",
&[&email],
)?;
let original_pw_hash: Zeroizing<Vec<u8>> = Zeroizing::new(row.get(0));
let salt: Zeroizing<Vec<u8>> = Zeroizing::new(row.get(1));
let mut pw_hash = Zeroizing::new([0u8; 32]);
@ -161,7 +154,7 @@ impl Users {
&ScryptParams::recommended(),
&mut *pw_hash,
)
.map_err(|_| DatabaseError::ScryptError)?;
.map_err(|_| DBError::ScryptError)?;
Ok(*pw_hash == *original_pw_hash.as_slice())
}

@ -1,3 +1,4 @@
use crate::utils::error::DBError;
use serde::export::Formatter;
use serde::{Deserialize, Serialize};
use std::error::Error;
@ -5,7 +6,7 @@ use std::fmt;
use std::fmt::Display;
#[derive(Deserialize)]
pub struct ValidateTokenRequest {
pub struct TokenRequest {
pub token: [u8; 32],
}
@ -27,16 +28,26 @@ impl Display for ErrorMessage {
}
impl Error for ErrorMessage {}
impl From<DBError> for ErrorMessage {
fn from(other: DBError) -> Self {
Self::new(other.to_string())
}
}
#[derive(Serialize)]
pub struct InfoEntry {
name: String,
method: [u8; 4],
method: String,
description: String,
data: String,
}
impl InfoEntry {
pub fn new(name: &str, method: [u8; 4], description: &str, data: &str) -> Self {
let method = format!(
"0x{:x} 0x{:x} 0x{:x} 0x{:x}",
method[0], method[1], method[2], method[3]
);
Self {
method,
name: name.to_string(),

@ -1,8 +1,7 @@
use super::rpc_methods::*;
use crate::database::Database;
use crate::server::messages::{
ErrorMessage, GetPermissionsRequest, InfoEntry, ValidateTokenRequest,
};
use crate::server::messages::{ErrorMessage, GetPermissionsRequest, InfoEntry, TokenRequest};
use crate::utils::get_user_id_from_token;
use msgrpc::message::Message;
use msgrpc::server::RpcServer;
use rmp_serde::Deserializer;
@ -39,7 +38,7 @@ impl UserRpcServer {
let mut handler = h.lock().unwrap();
let response = match handler.message.method {
INFO => self.handle_info(),
GET_ROLES => unimplemented!(),
GET_ROLES => self.handle_get_roles(&handler.message.data),
VALIDATE_TOKEN => self.handle_validate_token(&handler.message.data),
GET_ROLE_PERMISSIONS => self.handle_get_permissions(&handler.message.data),
_ => Err(ErrorMessage::new("Invalid Method".to_string())),
@ -52,14 +51,13 @@ impl UserRpcServer {
fn handle_validate_token(&self, data: &Vec<u8>) -> RpcResult<Message> {
log::trace!("Validating token.");
let message =
ValidateTokenRequest::deserialize(&mut Deserializer::new(&mut data.as_slice()))
.map_err(|e| ErrorMessage::new(e.to_string()))?;
let message = TokenRequest::deserialize(&mut Deserializer::new(&mut data.as_slice()))
.map_err(|e| ErrorMessage::new(e.to_string()))?;
let valid = self
.database
.users
.validate_request_token(&message.token)
.unwrap_or(false);
.unwrap_or((false, -1));
log::trace!("Serializing...");
let data = rmp_serde::to_vec(&valid).map_err(|e| ErrorMessage::new(e.to_string()))?;
@ -67,6 +65,7 @@ impl UserRpcServer {
}
fn handle_info(&self) -> RpcResult<Message> {
log::trace!("Get Info");
Ok(Message::new_with_serialize(
INFO,
vec![
@ -86,7 +85,7 @@ impl UserRpcServer {
InfoEntry::new(
"get permissions",
GET_ROLE_PERMISSIONS,
"Returns all permissions the givenroles are assigned to",
"Returns all permissions the given roles are assigned to",
"{role_ids: [i32]}",
),
],
@ -94,16 +93,13 @@ impl UserRpcServer {
}
fn handle_get_permissions(&self, data: &Vec<u8>) -> RpcResult<Message> {
log::trace!("Get Permissions");
let message =
GetPermissionsRequest::deserialize(&mut Deserializer::new(&mut data.as_slice()))
.map_err(|e| ErrorMessage::new(e.to_string()))?;
let mut response_data = HashMap::new();
for role_id in message.role_ids {
let permissions = self
.database
.role_permission
.by_role(role_id)
.map_err(|e| ErrorMessage::new(e.to_string()))?;
let permissions = self.database.role_permission.by_role(role_id)?;
response_data.insert(role_id.to_string(), permissions);
}
@ -112,4 +108,23 @@ impl UserRpcServer {
response_data,
))
}
fn handle_get_roles(&self, data: &Vec<u8>) -> RpcResult<Message> {
log::trace!("Get Roles");
let message = TokenRequest::deserialize(&mut Deserializer::new(&mut data.as_slice()))
.map_err(|e| ErrorMessage::new(e.to_string()))?;
if !self
.database
.users
.validate_request_token(&message.token)
.unwrap_or((false, -1))
.0
{
return Err(ErrorMessage::new("Invalid request token".to_string()));
}
let user_id = get_user_id_from_token(&message.token);
let response_data = self.database.user_roles.by_user(user_id)?;
Ok(Message::new_with_serialize(GET_ROLES, response_data))
}
}

@ -0,0 +1,47 @@
use redis::RedisError;
use serde_postgres::DeError;
use std::error;
use std::fmt::{self, Display, Formatter};
#[derive(Debug)]
pub enum DBError {
Redis(RedisError),
Postgres(PostgresError),
RecordExists,
ScryptError,
DeserializeError(serde_postgres::DeError),
GenericError(String),
}
impl Display for DBError {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.to_string())
}
}
impl error::Error for DBError {}
pub type DatabaseResult<T> = Result<T, DBError>;
impl From<PostgresError> for DBError {
fn from(other: PostgresError) -> Self {
Self::Postgres(other)
}
}
impl From<RedisError> for DBError {
fn from(other: RedisError) -> Self {
Self::Redis(other)
}
}
impl From<serde_postgres::DeError> for DBError {
fn from(other: DeError) -> Self {
Self::DeserializeError(other)
}
}
pub type DatabaseClient = postgres::Client;
pub type RedisClient = redis::Client;
pub type RedisConnection = redis::Connection;
pub type PostgresError = postgres::Error;

@ -1,6 +1,8 @@
use byteorder::{BigEndian, ByteOrder};
use rand::Rng;
pub mod error;
pub const TOKEN_LENGTH: usize = 32;
const SALT_LENGTH: usize = 16;
Loading…
Cancel
Save