Switch to pooled postgres client

Signed-off-by: trivernis <trivernis@protonmail.com>
pull/1/head
trivernis 4 years ago
parent 04de5a4e4f
commit 94ae69ec60
Signed by: Trivernis
GPG Key ID: DFFFCC2C7A02DB45

32
Cargo.lock generated

@ -382,6 +382,8 @@ dependencies = [
"mime 0.3.16", "mime 0.3.16",
"msgrpc", "msgrpc",
"postgres", "postgres",
"r2d2",
"r2d2_postgres",
"rand 0.7.3", "rand 0.7.3",
"rmp", "rmp",
"rmp-serde", "rmp-serde",
@ -1036,6 +1038,27 @@ dependencies = [
"proc-macro2", "proc-macro2",
] ]
[[package]]
name = "r2d2"
version = "0.8.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "545c5bc2b880973c9c10e4067418407a0ccaa3091781d1671d46eb35107cb26f"
dependencies = [
"log 0.4.11",
"parking_lot",
"scheduled-thread-pool",
]
[[package]]
name = "r2d2_postgres"
version = "0.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "707d27f66f43bac1081141f6d9611fffcce7da2841ae97c7ac53619d098efe8f"
dependencies = [
"postgres",
"r2d2",
]
[[package]] [[package]]
name = "rand" name = "rand"
version = "0.4.6" version = "0.4.6"
@ -1332,6 +1355,15 @@ version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ef703b7cb59335eae2eb93ceb664c0eb7ea6bf567079d843e09420219668e072" checksum = "ef703b7cb59335eae2eb93ceb664c0eb7ea6bf567079d843e09420219668e072"
[[package]]
name = "scheduled-thread-pool"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc6f74fd1204073fa02d5d5d68bec8021be4c38690b61264b2fdb48083d0e7d7"
dependencies = [
"parking_lot",
]
[[package]] [[package]]
name = "scopeguard" name = "scopeguard"
version = "1.1.0" version = "1.1.0"

@ -26,4 +26,6 @@ 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" base64 = "0.12.3"
chrono = "0.4.15" chrono = "0.4.15"
r2d2 = "0.8.9"
r2d2_postgres = "0.16.0"

@ -3,10 +3,11 @@ use crate::database::role_permissions::RolePermissions;
use crate::database::roles::Roles; use crate::database::roles::Roles;
use crate::database::user_roles::UserRoles; use crate::database::user_roles::UserRoles;
use crate::database::users::Users; use crate::database::users::Users;
use crate::utils::error::{DBError, DatabaseClient, DatabaseResult, PostgresError}; use crate::utils::error::DatabaseResult;
use dotenv; use dotenv;
use postgres::{Client, NoTls}; use postgres::NoTls;
use std::sync::{Arc, Mutex}; use r2d2::Pool;
use r2d2_postgres::PostgresConnectionManager;
pub mod database_error; pub mod database_error;
pub mod models; pub mod models;
@ -21,13 +22,13 @@ const DB_CONNECTION_URL: &str = "POSTGRES_CONNECTION_URL";
const DEFAULT_CONNECTION: &str = "postgres://postgres:postgres@localhost/postgres"; const DEFAULT_CONNECTION: &str = "postgres://postgres:postgres@localhost/postgres";
pub trait Table { pub trait Table {
fn new(database_connection: Arc<Mutex<DatabaseClient>>) -> Self; fn new(pool: PostgresPool) -> Self;
fn init(&self) -> DatabaseResult<()>; fn init(&self) -> DatabaseResult<()>;
} }
#[derive(Clone)] #[derive(Clone)]
pub struct Database { pub struct Database {
database_connection: Arc<Mutex<Client>>, pool: PostgresPool,
pub users: Users, pub users: Users,
pub roles: Roles, pub roles: Roles,
pub permissions: Permissions, pub permissions: Permissions,
@ -37,16 +38,14 @@ pub struct Database {
impl Database { impl Database {
pub fn new() -> DatabaseResult<Self> { pub fn new() -> DatabaseResult<Self> {
let database_connection = Arc::new(Mutex::new( let pool = get_database_connection()?;
get_database_connection().map_err(|e| DBError::Postgres(e))?,
));
Ok(Self { Ok(Self {
users: Users::new(Arc::clone(&database_connection)), users: Users::new(PostgresPool::clone(&pool)),
roles: Roles::new(Arc::clone(&database_connection)), roles: Roles::new(PostgresPool::clone(&pool)),
permissions: Permissions::new(Arc::clone(&database_connection)), permissions: Permissions::new(PostgresPool::clone(&pool)),
user_roles: UserRoles::new(Arc::clone(&database_connection)), user_roles: UserRoles::new(PostgresPool::clone(&pool)),
role_permission: RolePermissions::new(Arc::clone(&database_connection)), role_permission: RolePermissions::new(PostgresPool::clone(&pool)),
database_connection, pool,
}) })
} }
@ -67,8 +66,15 @@ impl Database {
Ok(()) Ok(())
} }
} }
pub type PostgresPool = Pool<PostgresConnectionManager<NoTls>>;
/// Returns a database connection /// Returns a database connection
fn get_database_connection() -> Result<DatabaseClient, PostgresError> { fn get_database_connection() -> Result<PostgresPool, r2d2::Error> {
let conn_url = dotenv::var(DB_CONNECTION_URL).unwrap_or(DEFAULT_CONNECTION.to_string()); let conn_url = dotenv::var(DB_CONNECTION_URL).unwrap_or(DEFAULT_CONNECTION.to_string());
Client::connect(conn_url.as_str(), NoTls)
Pool::new(PostgresConnectionManager::new(
conn_url.parse().unwrap(),
NoTls,
))
} }

@ -1,25 +1,20 @@
use crate::database::models::{CreatePermissionsEntry, Permission}; use crate::database::models::{CreatePermissionsEntry, Permission};
use crate::database::{DatabaseClient, DatabaseResult, Table}; use crate::database::{DatabaseResult, PostgresPool, Table};
use crate::utils::error::DBError; use crate::utils::error::DBError;
use postgres::Client;
use std::sync::{Arc, Mutex};
#[derive(Clone)] #[derive(Clone)]
pub struct Permissions { pub struct Permissions {
database_connection: Arc<Mutex<DatabaseClient>>, pool: PostgresPool,
} }
impl Table for Permissions { impl Table for Permissions {
fn new(database_connection: Arc<Mutex<Client>>) -> Self { fn new(pool: PostgresPool) -> Self {
Self { Self { pool }
database_connection,
}
} }
fn init(&self) -> DatabaseResult<()> { fn init(&self) -> DatabaseResult<()> {
self.database_connection self.pool
.lock() .get()?
.unwrap()
.batch_execute( .batch_execute(
"CREATE TABLE IF NOT EXISTS permissions ( "CREATE TABLE IF NOT EXISTS permissions (
id SERIAL PRIMARY KEY, id SERIAL PRIMARY KEY,
@ -36,7 +31,7 @@ impl Permissions {
&self, &self,
permissions: Vec<CreatePermissionsEntry>, permissions: Vec<CreatePermissionsEntry>,
) -> DatabaseResult<Vec<Permission>> { ) -> DatabaseResult<Vec<Permission>> {
let mut connection = self.database_connection.lock().unwrap(); let mut connection = self.pool.get()?;
let mut transaction = connection.transaction()?; let mut transaction = connection.transaction()?;
let mut created_permissions = Vec::new(); let mut created_permissions = Vec::new();
let _: Vec<DatabaseResult<()>> = permissions let _: Vec<DatabaseResult<()>> = permissions

@ -1,24 +1,20 @@
use crate::database::models::Permission; use crate::database::models::Permission;
use crate::database::{DatabaseClient, DatabaseResult, Table}; use crate::database::{DatabaseResult, PostgresPool, Table};
use crate::utils::error::DBError; use crate::utils::error::DBError;
use std::sync::{Arc, Mutex};
#[derive(Clone)] #[derive(Clone)]
pub struct RolePermissions { pub struct RolePermissions {
database_connection: Arc<Mutex<DatabaseClient>>, pool: PostgresPool,
} }
impl Table for RolePermissions { impl Table for RolePermissions {
fn new(database_connection: Arc<Mutex<DatabaseClient>>) -> Self { fn new(pool: PostgresPool) -> Self {
Self { Self { pool }
database_connection,
}
} }
fn init(&self) -> DatabaseResult<()> { fn init(&self) -> DatabaseResult<()> {
self.database_connection self.pool
.lock() .get()?
.unwrap()
.batch_execute( .batch_execute(
" "
CREATE TABLE IF NOT EXISTS role_permissions ( CREATE TABLE IF NOT EXISTS role_permissions (
@ -33,7 +29,7 @@ impl Table for RolePermissions {
impl RolePermissions { impl RolePermissions {
pub fn by_role(&self, role_id: i32) -> DatabaseResult<Vec<Permission>> { pub fn by_role(&self, role_id: i32) -> DatabaseResult<Vec<Permission>> {
let mut connection = self.database_connection.lock().unwrap(); let mut connection = self.pool.get()?;
let rows = connection.query( let rows = connection.query(
"SELECT * FROM role_permissions, permissions WHERE role_id = $1 AND role_permissions.permission_id = permissions.id", "SELECT * FROM role_permissions, permissions WHERE role_id = $1 AND role_permissions.permission_id = permissions.id",
&[&role_id])?; &[&role_id])?;

@ -1,28 +1,25 @@
use crate::database::models::Role; use crate::database::models::Role;
use crate::database::role_permissions::RolePermissions; use crate::database::role_permissions::RolePermissions;
use crate::database::{DatabaseResult, Table}; use crate::database::{DatabaseResult, PostgresPool, Table};
use crate::utils::error::DBError; use crate::utils::error::DBError;
use postgres::Client;
use std::sync::{Arc, Mutex};
#[derive(Clone)] #[derive(Clone)]
pub struct Roles { pub struct Roles {
database_connection: Arc<Mutex<Client>>, pool: PostgresPool,
role_permission: RolePermissions, role_permission: RolePermissions,
} }
impl Table for Roles { impl Table for Roles {
fn new(database_connection: Arc<Mutex<Client>>) -> Self { fn new(pool: PostgresPool) -> Self {
Self { Self {
role_permission: RolePermissions::new(Arc::clone(&database_connection)), role_permission: RolePermissions::new(PostgresPool::clone(&pool)),
database_connection, pool,
} }
} }
fn init(&self) -> DatabaseResult<()> { fn init(&self) -> DatabaseResult<()> {
self.database_connection self.pool
.lock() .get()?
.unwrap()
.batch_execute( .batch_execute(
" "
CREATE TABLE IF NOT EXISTS roles ( CREATE TABLE IF NOT EXISTS roles (
@ -42,7 +39,7 @@ impl Roles {
description: Option<String>, description: Option<String>,
permissions: Vec<i32>, permissions: Vec<i32>,
) -> DatabaseResult<Role> { ) -> DatabaseResult<Role> {
let mut connection = self.database_connection.lock().unwrap(); let mut connection = self.pool.get()?;
let exists = connection.query_opt("SELECT id FROM roles WHERE name = $1", &[&name])?; let exists = connection.query_opt("SELECT id FROM roles WHERE name = $1", &[&name])?;
if exists.is_some() { if exists.is_some() {

@ -1,25 +1,20 @@
use crate::database::models::Role; use crate::database::models::Role;
use crate::database::{DatabaseResult, Table}; use crate::database::{DatabaseResult, PostgresPool, Table};
use crate::utils::error::DBError; use crate::utils::error::DBError;
use postgres::Client;
use std::sync::{Arc, Mutex};
#[derive(Clone)] #[derive(Clone)]
pub struct UserRoles { pub struct UserRoles {
database_connection: Arc<Mutex<Client>>, pool: PostgresPool,
} }
impl Table for UserRoles { impl Table for UserRoles {
fn new(database_connection: Arc<Mutex<Client>>) -> Self { fn new(pool: PostgresPool) -> Self {
Self { Self { pool }
database_connection,
}
} }
fn init(&self) -> DatabaseResult<()> { fn init(&self) -> DatabaseResult<()> {
self.database_connection self.pool
.lock() .get()?
.unwrap()
.batch_execute( .batch_execute(
" "
CREATE TABLE IF NOT EXISTS user_roles ( CREATE TABLE IF NOT EXISTS user_roles (
@ -34,7 +29,7 @@ impl Table for UserRoles {
impl UserRoles { impl UserRoles {
pub fn by_user(&self, user_id: i32) -> DatabaseResult<Vec<Role>> { pub fn by_user(&self, user_id: i32) -> DatabaseResult<Vec<Role>> {
let mut connection = self.database_connection.lock().unwrap(); let mut connection = self.pool.get()?;
let rows = connection.query( let rows = connection.query(
"SELECT * FROM user_roles, roles WHERE user_id = $1 AND roles.id = user_roles.role_id", "SELECT * FROM user_roles, roles WHERE user_id = $1 AND roles.id = user_roles.role_id",
&[&user_id], &[&user_id],

@ -1,11 +1,10 @@
use crate::database::models::UserRecord; use crate::database::models::UserRecord;
use crate::database::tokens::{SessionTokens, TokenStore}; use crate::database::tokens::{SessionTokens, TokenStore};
use crate::database::user_roles::UserRoles; use crate::database::user_roles::UserRoles;
use crate::database::{DatabaseResult, Table}; use crate::database::{DatabaseResult, PostgresPool, Table};
use crate::utils::error::DBError; use crate::utils::error::DBError;
use crate::utils::{create_salt, hash_password}; use crate::utils::{create_salt, hash_password};
use postgres::Client;
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
use zeroize::{Zeroize, Zeroizing}; use zeroize::{Zeroize, Zeroizing};
@ -16,22 +15,22 @@ const ENV_ADMIN_EMAIL: &str = "ADMIN_EMAIL";
#[derive(Clone)] #[derive(Clone)]
pub struct Users { pub struct Users {
database_connection: Arc<Mutex<Client>>, pool: PostgresPool,
user_roles: UserRoles, user_roles: UserRoles,
token_store: Arc<Mutex<TokenStore>>, token_store: Arc<Mutex<TokenStore>>,
} }
impl Table for Users { impl Table for Users {
fn new(database_connection: Arc<Mutex<Client>>) -> Self { fn new(pool: PostgresPool) -> Self {
Self { Self {
user_roles: UserRoles::new(Arc::clone(&database_connection)), user_roles: UserRoles::new(PostgresPool::clone(&pool)),
database_connection, pool,
token_store: Arc::new(Mutex::new(TokenStore::new())), token_store: Arc::new(Mutex::new(TokenStore::new())),
} }
} }
fn init(&self) -> DatabaseResult<()> { fn init(&self) -> DatabaseResult<()> {
self.database_connection.lock().unwrap().batch_execute( self.pool.get()?.batch_execute(
"CREATE TABLE IF NOT EXISTS users ( "CREATE TABLE IF NOT EXISTS users (
id SERIAL PRIMARY KEY, id SERIAL PRIMARY KEY,
name VARCHAR(255) NOT NULL, name VARCHAR(255) NOT NULL,
@ -62,7 +61,7 @@ impl Users {
email: String, email: String,
password: String, password: String,
) -> DatabaseResult<UserRecord> { ) -> DatabaseResult<UserRecord> {
let mut connection = self.database_connection.lock().unwrap(); let mut connection = self.pool.get()?;
let mut password = Zeroizing::new(password); let mut password = Zeroizing::new(password);
log::trace!("Creating user {} with email {}", name, email); log::trace!("Creating user {} with email {}", name, email);
@ -90,7 +89,7 @@ impl Users {
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.pool.get()?;
let row = connection.query_one("SELECT id FROM users WHERE email = $1", &[&email])?; let row = connection.query_one("SELECT id FROM users WHERE email = $1", &[&email])?;
let id: i32 = row.get(0); let id: i32 = row.get(0);
@ -139,7 +138,7 @@ impl Users {
} }
fn validate_login(&self, email: &String, password: &String) -> DatabaseResult<bool> { fn validate_login(&self, email: &String, password: &String) -> DatabaseResult<bool> {
let mut connection = self.database_connection.lock().unwrap(); let mut connection = self.pool.get()?;
let row = connection let row = connection
.query_opt( .query_opt(
"SELECT password_hash, salt FROM users WHERE email = $1", "SELECT password_hash, salt FROM users WHERE email = $1",

@ -61,7 +61,7 @@ impl InfoEntry {
#[derive(Deserialize)] #[derive(Deserialize)]
pub struct GetPermissionsRequest { pub struct GetPermissionsRequest {
pub role_ids: Vec<i32>, pub roles: Vec<i32>,
} }
#[derive(Deserialize)] #[derive(Deserialize)]

@ -97,7 +97,7 @@ impl UserRpcServer {
"get permissions", "get permissions",
GET_ROLE_PERMISSIONS, GET_ROLE_PERMISSIONS,
"Returns all permissions the given roles are assigned to", "Returns all permissions the given roles are assigned to",
"{role_ids: [i32]}", "{roles: [i32]}",
), ),
InfoEntry::new( InfoEntry::new(
"create role", "create role",
@ -121,7 +121,7 @@ impl UserRpcServer {
GetPermissionsRequest::deserialize(&mut Deserializer::new(&mut data.as_slice())) GetPermissionsRequest::deserialize(&mut Deserializer::new(&mut data.as_slice()))
.map_err(|e| ErrorMessage::new(e.to_string()))?; .map_err(|e| ErrorMessage::new(e.to_string()))?;
let mut response_data = HashMap::new(); let mut response_data = HashMap::new();
for role_id in message.role_ids { for role_id in message.roles {
let permissions = self.database.role_permission.by_role(role_id)?; let permissions = self.database.role_permission.by_role(role_id)?;
response_data.insert(role_id.to_string(), permissions); response_data.insert(role_id.to_string(), permissions);
} }

@ -1,3 +1,4 @@
use r2d2::Error;
use serde_postgres::DeError; use serde_postgres::DeError;
use std::error; use std::error;
use std::fmt::{self, Display, Formatter}; use std::fmt::{self, Display, Formatter};
@ -5,6 +6,7 @@ use std::fmt::{self, Display, Formatter};
#[derive(Debug)] #[derive(Debug)]
pub enum DBError { pub enum DBError {
Postgres(PostgresError), Postgres(PostgresError),
Pool(r2d2::Error),
RecordExists, RecordExists,
BCryptError, BCryptError,
DeserializeError(serde_postgres::DeError), DeserializeError(serde_postgres::DeError),
@ -27,6 +29,7 @@ impl DBError {
DBError::Postgres(p) => p.to_string(), DBError::Postgres(p) => p.to_string(),
DBError::DeserializeError(de) => de.to_string(), DBError::DeserializeError(de) => de.to_string(),
DBError::BCryptError => "BCrypt Hash creation error".to_string(), DBError::BCryptError => "BCrypt Hash creation error".to_string(),
DBError::Pool(p) => p.to_string(),
} }
} }
} }
@ -39,6 +42,12 @@ impl From<PostgresError> for DBError {
} }
} }
impl From<r2d2::Error> for DBError {
fn from(other: Error) -> Self {
Self::Pool(other)
}
}
impl From<serde_postgres::DeError> for DBError { impl From<serde_postgres::DeError> for DBError {
fn from(other: DeError) -> Self { fn from(other: DeError) -> Self {
Self::DeserializeError(other) Self::DeserializeError(other)

Loading…
Cancel
Save