Skip to content

Commit

Permalink
[FEAT] Added Postgres timestamp table columns (#75)
Browse files Browse the repository at this point in the history
* FEAT: [gw] Add timestamp columns to Postgres tables

* TASK: [doc] Various (/simple) documentation updates
  • Loading branch information
chewyfish authored Feb 28, 2024
1 parent 0182a14 commit dcf9fd0
Show file tree
Hide file tree
Showing 11 changed files with 100 additions and 9 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ jobs:
uses: taiki-e/install-action@cargo-llvm-cov
- name: Setup Cache
uses: Swatinem/rust-cache@v2
- name: Install postgres client (Linux)
- name: Install Postgres Client (Linux)
if: runner.os == 'Linux'
run: |
sudo apt-get update
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
ALTER TABLE roles DROP COLUMN created_at;
ALTER TABLE roles DROP COLUMN updated_at;
ALTER TABLE services DROP COLUMN created_at;
ALTER TABLE services DROP COLUMN updated_at;
ALTER TABLE service_accesses DROP COLUMN created_at;
ALTER TABLE service_accesses DROP COLUMN updated_at;
ALTER TABLE users DROP COLUMN created_at;
ALTER TABLE users DROP COLUMN updated_at;
ALTER TABLE user_roles DROP COLUMN created_at;
ALTER TABLE user_roles DROP COLUMN updated_at;
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
ALTER TABLE roles ADD COLUMN created_at TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP;
ALTER TABLE roles ADD COLUMN updated_at TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP;
ALTER TABLE services ADD COLUMN created_at TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP;
ALTER TABLE services ADD COLUMN updated_at TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP;
ALTER TABLE service_accesses ADD COLUMN created_at TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP;
ALTER TABLE service_accesses ADD COLUMN updated_at TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP;
ALTER TABLE users ADD COLUMN created_at TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP;
ALTER TABLE users ADD COLUMN updated_at TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP;
ALTER TABLE user_roles ADD COLUMN created_at TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP;
ALTER TABLE user_roles ADD COLUMN updated_at TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP;

SELECT diesel_manage_updated_at('roles');
SELECT diesel_manage_updated_at('services');
SELECT diesel_manage_updated_at('service_accesses');
SELECT diesel_manage_updated_at('users');
SELECT diesel_manage_updated_at('user_roles');
6 changes: 3 additions & 3 deletions crates/gateway/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ pub enum DataSource {
/// In-memory DB, with a simple backing persistence store. Entity store connect string is file path to directory holding JSON record files.
InMemoryDb,

/// Postgres DB accessed via repository layer using diesel ORM.
/// Postgres DB
#[cfg(feature = "postgres_db")]
PostgresDb,

Expand Down Expand Up @@ -292,8 +292,8 @@ pub struct AppConfigArgs {
pub datasource: DataSource,

/// DB entity store connect specifier string. Specification format is dependent on <DATASOURCE> type.
/// Format: 'in-memory-db': Directory holding JSON files named 'trust0-db-access.json', 'trust0-db-role.json', 'trust0-db-service.json', 'trust0-db-user.json'
/// 'postgres-db': Standard Postgres connect string specification (https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-CONNSTRING)
/// For 'in-memory-db' datasource: Directory holding JSON files named 'trust0-db-access.json', 'trust0-db-role.json', 'trust0-db-service.json', 'trust0-db-user.json'
/// For 'postgres-db' datasource: Standard Postgres connect string specification (https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-CONNSTRING)
#[arg(required = false, long = "db-connect", env, verbatim_doc_comment)]
pub db_connect: Option<String>,

Expand Down
15 changes: 15 additions & 0 deletions crates/gateway/src/repository/postgres_db/access_repo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use diesel::prelude::*;
use diesel::sql_types;
use std::ops::DerefMut;
use std::sync::{Arc, Mutex};
use std::time::SystemTime;

use crate::repository::access_repo::AccessRepository;
use crate::repository::postgres_db::db_conn;
Expand All @@ -20,6 +21,10 @@ pub struct ServiceAccess {
pub entity_type: String,
/// Entity ID (either role ID or user ID)
pub entity_id: i64,
/// Datetime record was created
pub created_at: Option<SystemTime>,
/// Datetime record was last updated
pub updated_at: Option<SystemTime>,
}

fn entity_type_to_string(model_entity_type: &model::access::EntityType) -> String {
Expand All @@ -36,6 +41,8 @@ impl From<model::access::ServiceAccess> for ServiceAccess {
service_id: access.service_id,
entity_type: entity_type_to_string(&access.entity_type),
entity_id: access.entity_id,
created_at: None,
updated_at: None,
}
}
}
Expand Down Expand Up @@ -308,11 +315,15 @@ mod tests {
service_id: 200,
entity_type: "Role".to_string(),
entity_id: 50,
created_at: None,
updated_at: None,
};
let expected_access2 = ServiceAccess {
service_id: 201,
entity_type: "User".to_string(),
entity_id: 100,
created_at: None,
updated_at: None,
};
assert_eq!(ServiceAccess::from(model_access1), expected_access1);
assert_eq!(ServiceAccess::from(model_access2), expected_access2);
Expand All @@ -324,11 +335,15 @@ mod tests {
service_id: 200,
entity_type: "Role".to_string(),
entity_id: 50,
created_at: None,
updated_at: None,
};
let access2 = ServiceAccess {
service_id: 201,
entity_type: "User".to_string(),
entity_id: 100,
created_at: None,
updated_at: None,
};
let expected_model_access1 = model::access::ServiceAccess {
service_id: 200,
Expand Down
10 changes: 10 additions & 0 deletions crates/gateway/src/repository/postgres_db/db_schema.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ diesel::table! {
id -> Int8,
#[max_length = 50]
name -> Varchar,
created_at -> Nullable<Timestamp>,
updated_at -> Nullable<Timestamp>,
}
}

Expand All @@ -14,6 +16,8 @@ diesel::table! {
entity_type -> Varchar,
entity_id -> Int8,
service_id -> Int8,
created_at -> Nullable<Timestamp>,
updated_at -> Nullable<Timestamp>,
}
}

Expand All @@ -27,13 +31,17 @@ diesel::table! {
#[max_length = 255]
host -> Varchar,
port -> Int4,
created_at -> Nullable<Timestamp>,
updated_at -> Nullable<Timestamp>,
}
}

diesel::table! {
user_roles (user_id, role_id) {
user_id -> Int8,
role_id -> Int8,
created_at -> Nullable<Timestamp>,
updated_at -> Nullable<Timestamp>,
}
}

Expand All @@ -48,6 +56,8 @@ diesel::table! {
user_name -> Nullable<Varchar>,
#[max_length = 255]
password -> Nullable<Varchar>,
created_at -> Nullable<Timestamp>,
updated_at -> Nullable<Timestamp>,
}
}

Expand Down
11 changes: 11 additions & 0 deletions crates/gateway/src/repository/postgres_db/role_repo.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use diesel::prelude::*;
use std::ops::DerefMut;
use std::sync::{Arc, Mutex};
use std::time::SystemTime;

use crate::repository::postgres_db::db_conn;
use crate::repository::postgres_db::db_schema::roles::dsl::*;
Expand All @@ -17,13 +18,19 @@ pub struct Role {
pub id: i64,
/// Friendly name for role
pub name: String,
/// Datetime record was created
pub created_at: Option<SystemTime>,
/// Datetime record was last updated
pub updated_at: Option<SystemTime>,
}

impl From<model::role::Role> for Role {
fn from(role: model::role::Role) -> Self {
Self {
id: role.role_id,
name: role.name,
created_at: None,
updated_at: None,
}
}
}
Expand Down Expand Up @@ -225,6 +232,8 @@ mod tests {
let expected_role = Role {
id: 50,
name: "Role50.1".to_string(),
created_at: None,
updated_at: None,
};
assert_eq!(Role::from(model_role), expected_role);
}
Expand All @@ -234,6 +243,8 @@ mod tests {
let role = Role {
id: 50,
name: "Role50.1".to_string(),
created_at: None,
updated_at: None,
};
let expected_model_role = model::role::Role {
role_id: 50,
Expand Down
15 changes: 15 additions & 0 deletions crates/gateway/src/repository/postgres_db/service_repo.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use diesel::prelude::*;
use std::ops::DerefMut;
use std::sync::{Arc, Mutex};
use std::time::SystemTime;

use crate::repository::postgres_db::db_conn;
use crate::repository::postgres_db::db_schema::services::dsl::*;
Expand All @@ -23,6 +24,10 @@ pub struct Service {
pub host: String,
/// Service address port (used in gateway proxy connections)
pub port: i32,
/// Datetime record was created
pub created_at: Option<SystemTime>,
/// Datetime record was last updated
pub updated_at: Option<SystemTime>,
}

impl From<model::service::Service> for Service {
Expand All @@ -36,6 +41,8 @@ impl From<model::service::Service> for Service {
},
host: service.host,
port: service.port as i32,
created_at: None,
updated_at: None,
}
}
}
Expand Down Expand Up @@ -278,13 +285,17 @@ mod tests {
transport: "TCP".to_string(),
host: "host200.com".to_string(),
port: 8200,
created_at: None,
updated_at: None,
};
let expected_udp_service = Service {
id: 201,
name: "Service201".to_string(),
transport: "UDP".to_string(),
host: "host201.com".to_string(),
port: 8201,
created_at: None,
updated_at: None,
};
assert_eq!(Service::from(model_tcp_service), expected_tcp_service);
assert_eq!(Service::from(model_udp_service), expected_udp_service);
Expand All @@ -298,13 +309,17 @@ mod tests {
transport: "TCP".to_string(),
host: "host200.com".to_string(),
port: 8200,
created_at: None,
updated_at: None,
};
let udp_service = Service {
id: 201,
name: "Service201".to_string(),
transport: "UDP".to_string(),
host: "host201.com".to_string(),
port: 8201,
created_at: None,
updated_at: None,
};
let expected_model_tcp_service = model::service::Service {
service_id: 200,
Expand Down
15 changes: 15 additions & 0 deletions crates/gateway/src/repository/postgres_db/user_repo.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use diesel::prelude::*;
use std::ops::DerefMut;
use std::sync::{Arc, Mutex};
use std::time::SystemTime;

use crate::repository::postgres_db::db_conn;
use crate::repository::postgres_db::db_schema::user_roles;
Expand All @@ -24,6 +25,10 @@ pub struct User {
pub user_name: Option<String>,
/// (optional) Password used in secondary authentication
pub password: Option<String>,
/// Datetime record was created
pub created_at: Option<SystemTime>,
/// Datetime record was last updated
pub updated_at: Option<SystemTime>,
}

impl From<model::user::User> for User {
Expand All @@ -37,6 +42,8 @@ impl From<model::user::User> for User {
},
user_name: user.user_name.clone(),
password: user.password.clone(),
created_at: None,
updated_at: None,
}
}
}
Expand Down Expand Up @@ -366,13 +373,17 @@ mod tests {
status: "Active".to_string(),
user_name: Some("uname100".to_string()),
password: Some("pass100".to_string()),
created_at: None,
updated_at: None,
};
let expected_user101 = User {
id: 101,
name: "User101".to_string(),
status: "Inactive".to_string(),
user_name: None,
password: None,
created_at: None,
updated_at: None,
};
assert_eq!(User::from(model_user100), expected_user100);
assert_eq!(User::from(model_user101), expected_user101);
Expand All @@ -386,13 +397,17 @@ mod tests {
status: "Active".to_string(),
user_name: Some("uname100".to_string()),
password: Some("pass100".to_string()),
created_at: None,
updated_at: None,
};
let user101 = User {
id: 101,
name: "User101".to_string(),
status: "Inactive".to_string(),
user_name: None,
password: None,
created_at: None,
updated_at: None,
};
let expected_model_user100 = model::user::User {
user_id: 100,
Expand Down
3 changes: 1 addition & 2 deletions docs/Architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,6 @@ Here is the current list of signaling events in use:
| Proxy Connections | `T0C` <--> `T0G` | <p>This event is sent bidirectionally every `6 secs`. It contains the current list of service<br>proxy connections' bind address pairs (as known by the gateway (1)) for the<br>respective user session.</p><p>Each side will keep track of missing binds as a consecutive count. When that reaches `5`,<br>those corresponding missing connection(s) will be shut down.</p><p>Additionally, each side will also keep track of consecutive missing `Proxy Connections`<br>signal events. when that reaches `5`, the entire user session(control plane, service<br>proxy connections) will be shut down.</p><p>(1) - Upon TLS connection establishment an initial message, detailing the connection bind<br>addresses, is sent from the gateway to the client. This address pair will be used in<br>coordinating the active state for the respective connection. |
| Certificate Reissue | `T0C` <--- `T0G` | <p>If the client certificate is expiring in the near future, the gateway (if CA is enabled) will<br>send a new certificate and public/private key pair via this event. The client will backup<br>the current PKI resources and save these new files (which will be used on client restart).</p><p>Refer to [Certificate Authority](#certificate-authority) for more information about its design and implementation.</p> |


The Trust0 Gateway can enable a certificate authority (CA) to send clients new certificates, when their existing certificates are expiring.
### Service Proxy

Expand Down Expand Up @@ -216,7 +215,7 @@ Currently, there are two supported DB implementations:
| DB Type | Description |
|-------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `in-memory` | DB based on JSON record files. At runtime, the system will periodically scan for file<br>changes and reload the corresponding DB with latest records from the changed file. |
| `postgres` | Repositories using an ORM, which connects to a Postgres DB. |
| `postgres` | Repositories using diesel ORM, which connects/executes queries against a Postgres DB. |

The following sections represent a pseudo-layout for the core respository tables. Actual table layout may differ.

Expand Down
6 changes: 3 additions & 3 deletions docs/Invocation.md
Original file line number Diff line number Diff line change
Expand Up @@ -123,13 +123,13 @@ Options:
Possible values:
- in-memory-db: In-memory DB, with a simple backing persistence store. Entity store connect string is file path to directory holding JSON record files
- postgres-db: Postgres DB accessed via repository layer using diesel ORM
- postgres-db: Postgres DB
- no-db: No DB configured, used in testing (internally empty in-memory DB structures are used)
--db-connect <DB_CONNECT>
DB entity store connect specifier string. Specification format is dependent on <DATASOURCE> type.
Format: 'in-memory-db': Directory holding JSON files named 'trust0-db-access.json', 'trust0-db-role.json', 'trust0-db-service.json', 'trust0-db-user.json'
'postgres-db': Standard Postgres connect string specification (https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-CONNSTRING)
For 'in-memory-db' datasource: Directory holding JSON files named 'trust0-db-access.json', 'trust0-db-role.json', 'trust0-db-service.json', 'trust0-db-user.json'
For 'postgres-db' datasource: Standard Postgres connect string specification (https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-CONNSTRING)
[env: DB_CONNECT=]
Expand Down

0 comments on commit dcf9fd0

Please sign in to comment.