diff --git a/Cargo.lock b/Cargo.lock index f0830b5..a9b1734 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -114,6 +114,11 @@ dependencies = [ "which 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "bitflags" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "bitflags" version = "1.0.4" @@ -928,6 +933,18 @@ dependencies = [ "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "openssl" +version = "0.9.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bitflags 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", + "foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", + "openssl-sys 0.9.39 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "openssl" version = "0.10.15" @@ -1025,6 +1042,7 @@ dependencies = [ "bytes 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)", "fallible-iterator 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", + "openssl 0.9.24 (registry+https://github.com/rust-lang/crates.io-index)", "postgres-protocol 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "postgres-shared 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "socket2 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2209,6 +2227,7 @@ dependencies = [ "checksum base64 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "96434f987501f0ed4eb336a411e0631ecd1afa11574fe148587adc4ff96143c9" "checksum base64 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)" = "489d6c0ed21b11d038c31b6ceccca973e65d73ba3bd8ecb9a2babf5546164643" "checksum bindgen 0.40.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8f4c4ffe91e0f26bdcc5a8dd58cbf0358ad772b8ec1ae274a11a0ba54ec175f4" +"checksum bitflags 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4efd02e230a02e18f92fc2735f44597385ed02ad8f831e7c1c1156ee5e1ab3a5" "checksum bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "228047a76f468627ca71776ecdebd732a3423081fcf5125585bcd7c49886ce12" "checksum block-buffer 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a076c298b9ecdb530ed9d967e74a6027d6a7478924520acddcddc24c1c8ab3ab" "checksum byte-tools 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "560c32574a12a89ecd91f5e742165893f86e3ab98d21f8ea548658eb9eef5f40" @@ -2301,6 +2320,7 @@ dependencies = [ "checksum num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0b3a5d7cc97d6d30d8b9bc8fa19bf45349ffe46241e8816f50f62f6d6aaabee1" "checksum num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c51a3322e4bca9d212ad9a158a02abc6934d005490c054a2778df73a70aa0a30" "checksum openssl 0.10.15 (registry+https://github.com/rust-lang/crates.io-index)" = "5e1309181cdcbdb51bc3b6bedb33dfac2a83b3d585033d3f6d9e22e8c1928613" +"checksum openssl 0.9.24 (registry+https://github.com/rust-lang/crates.io-index)" = "a3605c298474a3aa69de92d21139fb5e2a81688d308262359d85cdd0d12a7985" "checksum openssl-probe 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "77af24da69f9d9341038eba93a073b1fdaaa1b788221b00a69bce9e762cb32de" "checksum openssl-sys 0.9.39 (registry+https://github.com/rust-lang/crates.io-index)" = "278c1ad40a89aa1e741a1eed089a2f60b18fab8089c3139b542140fc7d674106" "checksum owning_ref 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "cdf84f41639e037b484f93433aa3897863b561ed65c6e59c7073d7c561710f37" diff --git a/Cargo.toml b/Cargo.toml index f5900d4..607082b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,7 +21,7 @@ r2d2 = "0.8" r2d2_sqlite = "0.7" r2d2_redis = "0.8" r2d2_postgres = "0.14" -postgres = {version="0.15", features=["with-serde_json"]} +postgres = {version="0.15", features=["with-serde_json","with-openssl"]} sha2 = "0.7" sha-1 = "0.7" url = "1.7" diff --git a/src/postgres_data.rs b/src/postgres_data.rs index 04e032e..f91026e 100644 --- a/src/postgres_data.rs +++ b/src/postgres_data.rs @@ -4,11 +4,15 @@ use std::sync::Arc; extern crate postgres; extern crate r2d2; extern crate r2d2_postgres; -use self::r2d2_postgres::PostgresConnectionManager; +use self::r2d2_postgres::{PostgresConnectionManager, TlsMode}; use self::postgres::params::{Builder, ConnectParams, IntoConnectParams}; +use self::postgres::tls::openssl::openssl::ssl::{SslConnectorBuilder, SslMethod}; +use self::postgres::tls::openssl::openssl::x509::{X509_FILETYPE_DEFAULT, X509_FILETYPE_PEM}; use self::postgres::types::ToSql; -use self::postgres::{Connection, TlsMode}; +use self::postgres::Connection; + +use settings::PostgresStoreConfig; extern crate serde_json; @@ -19,7 +23,9 @@ pub struct PostgresDataStore { } impl PostgresDataStore { - pub fn new(url: String, maybe_dbname: Option) -> Self { + pub fn new(conf: &PostgresStoreConfig) -> Self { + let url = conf.url.clone(); + let maybe_dbname = conf.database.as_ref().cloned(); let params: ConnectParams = url.into_connect_params().unwrap(); let mut builder = Builder::new(); builder.port(params.port()); @@ -33,8 +39,32 @@ impl PostgresDataStore { let params = builder.build(params.host().clone()); + let maybe_tls = if conf.tls_client_crt.is_some() { + let mut connbuilder = SslConnectorBuilder::new(SslMethod::tls()).unwrap(); + if let Some(ref ca) = conf.tls_ca_crt { + connbuilder.set_ca_file(ca).unwrap(); + } + connbuilder + .set_certificate_file(conf.tls_client_crt.as_ref().unwrap(), X509_FILETYPE_DEFAULT) + .unwrap(); + connbuilder + .set_private_key_file(conf.tls_client_key.as_ref().unwrap(), X509_FILETYPE_PEM) + .unwrap(); + // connbuilder. + // connbuilder.set_verify(postgres::tls::openssl::openssl::ssl::); + Some(postgres::tls::openssl::OpenSsl::from(connbuilder.build())) + } else { + None + }; + let pool = if let Some(dbname) = &maybe_dbname { - let conn = Connection::connect(params.clone(), TlsMode::None).unwrap(); + let conn = Connection::connect( + params.clone(), + match maybe_tls.as_ref() { + Some(tls) => postgres::TlsMode::Require(tls), + None => postgres::TlsMode::None, + }, + ).unwrap(); match conn.execute(&format!("CREATE DATABASE \"{}\"", dbname), NO_PARAMS) { Ok(_) => debug!("database created with success"), Err(e) => warn!( @@ -50,11 +80,22 @@ impl PostgresDataStore { } let pool_params = builder.build(params.host().clone()); - let manager = - PostgresConnectionManager::new(pool_params, r2d2_postgres::TlsMode::None).unwrap(); + let manager = PostgresConnectionManager::new( + pool_params, + match maybe_tls { + Some(tls) => TlsMode::Require(Box::new(tls)), + None => TlsMode::None, + }, + ).unwrap(); r2d2::Pool::builder().build(manager).unwrap() } else { - let manager = PostgresConnectionManager::new(params, r2d2_postgres::TlsMode::None).unwrap(); + let manager = PostgresConnectionManager::new( + params, + match maybe_tls { + Some(tls) => TlsMode::Require(Box::new(tls)), + None => TlsMode::None, + }, + ).unwrap(); r2d2::Pool::builder().build(manager).unwrap() }; PostgresDataStore { @@ -182,11 +223,18 @@ mod tests { } fn setup(dbname: Option) -> PostgresDataStore { - PostgresDataStore::new((*PG_URL).clone(), dbname) + let conf = PostgresStoreConfig { + url: (*PG_URL).clone(), + database: dbname, + tls_client_crt: None, + tls_client_key: None, + tls_ca_crt: None, + }; + PostgresDataStore::new(&conf) } fn teardown(dbname: &str) { - let conn = Connection::connect((*PG_URL).as_str(), TlsMode::None).unwrap(); + let conn = Connection::connect((*PG_URL).as_str(), postgres::TlsMode::None).unwrap(); conn .execute(&format!("DROP DATABASE {}", dbname), NO_PARAMS) diff --git a/src/redis_cache.rs b/src/redis_cache.rs index 2917abe..e5a074a 100644 --- a/src/redis_cache.rs +++ b/src/redis_cache.rs @@ -8,18 +8,29 @@ use cache::*; extern crate r2d2_redis; use self::r2d2_redis::{r2d2, redis, RedisConnectionManager}; +use settings::RedisStoreConfig; + #[derive(Debug)] pub struct RedisCacheStore { pool: Arc>, + ns: Option, } impl RedisCacheStore { - pub fn new(url: String) -> Self { - let manager = RedisConnectionManager::new(url.as_str()).unwrap(); + pub fn new(conf: &RedisStoreConfig) -> Self { + let manager = RedisConnectionManager::new(conf.url.as_str()).unwrap(); let pool = r2d2::Pool::builder().build(manager).unwrap(); RedisCacheStore { pool: Arc::new(pool), + ns: conf.namespace.as_ref().cloned(), + } + } + + fn key(&self, k: String) -> String { + if self.ns.is_none() { + return k; } + format!("{}:{}", self.ns.as_ref().unwrap(), k) } } @@ -28,10 +39,11 @@ impl CacheStore for RedisCacheStore { &self, key: String, ) -> CacheResult, Error = CacheError> + Send>>> { - debug!("redis cache get with key: {}", key); let pool = Arc::clone(&self.pool); let conn = pool.get().unwrap(); // TODO: no unwrap let size = 256 * 1024; + let fullkey = self.key(key); + debug!("redis cache get with key: {}", fullkey); Ok(Some(Box::new(stream::unfold(0, move |pos| { // End early given some rules! // not a multiple of size, means we're done. @@ -39,7 +51,7 @@ impl CacheStore for RedisCacheStore { return None; } match redis::cmd("GETRANGE") - .arg(key.clone()) + .arg(fullkey.clone()) .arg(pos) .arg(pos + size - 1) // end arg is inclusive .query::>(conn.deref()) @@ -62,8 +74,12 @@ impl CacheStore for RedisCacheStore { data_stream: Box, Error = ()> + Send>, maybe_ttl: Option, ) -> Box + Send> { - debug!("redis cache set with key: {} and ttl: {:?}", key, maybe_ttl); let pool = Arc::clone(&self.pool); + let fullkey = self.key(key); + debug!( + "redis cache set with key: {} and ttl: {:?}", + fullkey, maybe_ttl + ); Box::new( data_stream .concat2() @@ -73,7 +89,7 @@ impl CacheStore for RedisCacheStore { }).and_then(move |b| { let conn = pool.get().unwrap(); // TODO: no unwrap let mut cmd = redis::cmd("SET"); - cmd.arg(key).arg(b); + cmd.arg(fullkey).arg(b); if let Some(ttl) = maybe_ttl { cmd.arg("EX").arg(ttl); } @@ -86,12 +102,12 @@ impl CacheStore for RedisCacheStore { } fn del(&self, key: String) -> Box + Send> { - debug!("redis cache del key: {}", key); - let pool = Arc::clone(&self.pool); + let fullkey = self.key(key); + debug!("redis cache del key: {}", fullkey); Box::new(future::lazy(move || -> Result<(), CacheError> { let conn = pool.get().unwrap(); // TODO: no unwrap - match redis::cmd("DEL").arg(key).query::(conn.deref()) { + match redis::cmd("DEL").arg(fullkey).query::(conn.deref()) { Ok(_) => Ok(()), Err(e) => Err(CacheError::Failure(format!("{}", e))), } @@ -99,13 +115,14 @@ impl CacheStore for RedisCacheStore { } fn expire(&self, key: String, ttl: u32) -> Box + Send> { - debug!("redis cache expire key: {} w/ ttl: {}", key, ttl); - let pool = Arc::clone(&self.pool); + let fullkey = self.key(key); + debug!("redis cache expire key: {} w/ ttl: {}", fullkey, ttl); + Box::new(future::lazy(move || -> CacheResult<()> { let conn = pool.get().unwrap(); // TODO: no unwrap match redis::cmd("EXPIRE") - .arg(key) + .arg(fullkey) .arg(ttl) .query::(conn.deref()) { diff --git a/src/runtime.rs b/src/runtime.rs index 037b956..8da963d 100644 --- a/src/runtime.rs +++ b/src/runtime.rs @@ -216,7 +216,7 @@ impl Runtime { CacheStore::Sqlite(conf) => { Box::new(sqlite_cache::SqliteCacheStore::new(conf.filename.clone())) } - CacheStore::Redis(conf) => Box::new(redis_cache::RedisCacheStore::new(conf.url.clone())), + CacheStore::Redis(conf) => Box::new(redis_cache::RedisCacheStore::new(&conf)), }, None => Box::new(sqlite_cache::SqliteCacheStore::new("cache.db".to_string())), }, @@ -225,10 +225,7 @@ impl Runtime { DataStore::Sqlite(conf) => { Box::new(sqlite_data::SqliteDataStore::new(conf.filename.clone())) } - DataStore::Postgres(conf) => Box::new(postgres_data::PostgresDataStore::new( - conf.url.clone(), - conf.dbname.as_ref().cloned(), - )), + DataStore::Postgres(conf) => Box::new(postgres_data::PostgresDataStore::new(&conf)), }, None => Box::new(sqlite_data::SqliteDataStore::new("data.db".to_string())), }, diff --git a/src/settings.rs b/src/settings.rs index aaadc7f..5948f2e 100644 --- a/src/settings.rs +++ b/src/settings.rs @@ -6,35 +6,39 @@ lazy_static! { pub static ref SETTINGS: RwLock = RwLock::new(Settings::new().unwrap()); } -#[derive(Debug, Deserialize)] +#[derive(Debug, Deserialize, Clone)] pub struct SqliteStoreConfig { pub filename: String, } -#[derive(Debug, Deserialize)] +#[derive(Debug, Deserialize, Clone)] pub struct PostgresStoreConfig { pub url: String, - pub dbname: Option, + pub database: Option, + pub tls_client_crt: Option, + pub tls_client_key: Option, + pub tls_ca_crt: Option, } -#[derive(Debug, Deserialize)] +#[derive(Debug, Deserialize, Clone)] pub struct RedisStoreConfig { pub url: String, + pub namespace: Option, } -#[derive(Debug, Deserialize)] +#[derive(Debug, Deserialize, Clone)] pub enum DataStore { Sqlite(SqliteStoreConfig), Postgres(PostgresStoreConfig), } -#[derive(Debug, Deserialize)] +#[derive(Debug, Deserialize, Clone)] pub enum CacheStore { Sqlite(SqliteStoreConfig), Redis(RedisStoreConfig), } -#[derive(Debug, Deserialize)] +#[derive(Debug, Deserialize, Clone)] pub struct Settings { pub data_store: Option, pub cache_store: Option,