Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support Postgres as a persistence backend #8202

Merged
merged 1 commit into from
Jun 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
429 changes: 239 additions & 190 deletions .github/workflows/rust.yml

Large diffs are not rendered by default.

23 changes: 8 additions & 15 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 11 additions & 5 deletions NOTICE.md
Original file line number Diff line number Diff line change
Expand Up @@ -192,13 +192,15 @@ This file contains attributions for any 3rd-party open source code used in this
| equivalent | Apache-2.0, MIT | https://crates.io/crates/equivalent |
| errno | MIT, Apache-2.0 | https://crates.io/crates/errno |
| error-code | BSL-1.0 | https://crates.io/crates/error-code |
| etcetera | MIT, Apache-2.0 | https://crates.io/crates/etcetera |
| event-listener | Apache-2.0, MIT | https://crates.io/crates/event-listener |
| event-listener-strategy | Apache-2.0, MIT | https://crates.io/crates/event-listener-strategy |
| fastrand | Apache-2.0, MIT | https://crates.io/crates/fastrand |
| fd-lock | MIT, Apache-2.0 | https://crates.io/crates/fd-lock |
| fdeflate | MIT, Apache-2.0 | https://crates.io/crates/fdeflate |
| ff | MIT, Apache-2.0 | https://crates.io/crates/ff |
| fiat-crypto | MIT, Apache-2.0, BSD-1-Clause | https://crates.io/crates/fiat-crypto |
| finl_unicode | MIT, Apache-2.0 | https://crates.io/crates/finl_unicode |
| flate2 | MIT, Apache-2.0 | https://crates.io/crates/flate2 |
| flexi_logger | MIT, Apache-2.0 | https://crates.io/crates/flexi_logger |
| flume | Apache-2.0, MIT | https://crates.io/crates/flume |
Expand Down Expand Up @@ -487,11 +489,12 @@ This file contains attributions for any 3rd-party open source code used in this
| spin | MIT | https://crates.io/crates/spin |
| spki | Apache-2.0, MIT | https://crates.io/crates/spki |
| sqlformat | MIT, Apache-2.0 | https://crates.io/crates/sqlformat |
| sqlx | MIT, Apache-2.0 | https://crates.io/crates/sqlx |
| sqlx-core | MIT, Apache-2.0 | https://crates.io/crates/sqlx-core |
| sqlx-macros | MIT, Apache-2.0 | https://crates.io/crates/sqlx-macros |
| sqlx-macros-core | MIT, Apache-2.0 | https://crates.io/crates/sqlx-macros-core |
| sqlx-sqlite | MIT, Apache-2.0 | https://crates.io/crates/sqlx-sqlite |
| sqlx | MIT, Apache-2.0 | https://github.com/etorreborre/sqlx?rev=5fec648d2de0cbeed738dcf1c6f5bc9194fc439b |
| sqlx-core | MIT, Apache-2.0 | https://github.com/etorreborre/sqlx?rev=5fec648d2de0cbeed738dcf1c6f5bc9194fc439b |
| sqlx-macros | MIT, Apache-2.0 | https://github.com/etorreborre/sqlx?rev=5fec648d2de0cbeed738dcf1c6f5bc9194fc439b |
| sqlx-macros-core | MIT, Apache-2.0 | https://github.com/etorreborre/sqlx?rev=5fec648d2de0cbeed738dcf1c6f5bc9194fc439b |
| sqlx-postgres | MIT, Apache-2.0 | https://github.com/etorreborre/sqlx?rev=5fec648d2de0cbeed738dcf1c6f5bc9194fc439b |
| sqlx-sqlite | MIT, Apache-2.0 | https://github.com/etorreborre/sqlx?rev=5fec648d2de0cbeed738dcf1c6f5bc9194fc439b |
| stable_deref_trait | MIT, Apache-2.0 | https://crates.io/crates/stable_deref_trait |
| static_assertions | MIT, Apache-2.0 | https://crates.io/crates/static_assertions |
| stm32-device-signature | MIT, Apache-2.0 | https://crates.io/crates/stm32-device-signature |
Expand All @@ -500,6 +503,7 @@ This file contains attributions for any 3rd-party open source code used in this
| stm32h7 | MIT, Apache-2.0 | https://crates.io/crates/stm32h7 |
| stm32h7xx-hal | 0BSD | https://crates.io/crates/stm32h7xx-hal |
| str-buf | BSL-1.0 | https://crates.io/crates/str-buf |
| stringprep | MIT, Apache-2.0 | https://crates.io/crates/stringprep |
| strip-ansi-escapes | Apache-2.0, MIT | https://crates.io/crates/strip-ansi-escapes |
| strsim | MIT | https://crates.io/crates/strsim |
| strum | MIT | https://crates.io/crates/strum |
Expand Down Expand Up @@ -586,6 +590,7 @@ This file contains attributions for any 3rd-party open source code used in this
| waker-fn | Apache-2.0, MIT | https://crates.io/crates/waker-fn |
| walkdir | Unlicense, MIT | https://crates.io/crates/walkdir |
| want | MIT | https://crates.io/crates/want |
| wasite | Apache-2.0, BSL-1.0, MIT | https://crates.io/crates/wasite |
| wasm-bindgen | MIT, Apache-2.0 | https://crates.io/crates/wasm-bindgen |
| wasm-bindgen-backend | MIT, Apache-2.0 | https://crates.io/crates/wasm-bindgen-backend |
| wasm-bindgen-futures | MIT, Apache-2.0 | https://crates.io/crates/wasm-bindgen-futures |
Expand All @@ -604,6 +609,7 @@ This file contains attributions for any 3rd-party open source code used in this
| web-time | MIT, Apache-2.0 | https://crates.io/crates/web-time |
| weezl | MIT, Apache-2.0 | https://crates.io/crates/weezl |
| which | MIT | https://crates.io/crates/which |
| whoami | Apache-2.0, BSL-1.0, MIT | https://crates.io/crates/whoami |
| winapi | MIT, Apache-2.0 | https://crates.io/crates/winapi |
| winapi-i686-pc-windows-gnu | MIT, Apache-2.0 | https://crates.io/crates/winapi-i686-pc-windows-gnu |
| winapi-util | Unlicense, MIT | https://crates.io/crates/winapi-util |
Expand Down
7 changes: 7 additions & 0 deletions implementations/rust/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,13 @@ nextest:
nextest_%:
cargo --locked nextest --config-file $(ROOT_DIR)/tools/nextest/.config/nextest.toml run -E 'package($*)' --no-fail-fast
cargo --locked test --doc
test_postgres:
export OCKAM_POSTGRES_HOST=localhost
export OCKAM_POSTGRES_PORT=5433
export OCKAM_POSTGRES_DATABASE_NAME=test
export OCKAM_POSTGRES_USER=postgres
export OCKAM_POSTGRES_PASSWORD=password
cargo --locked nextest --config-file $(ROOT_DIR)/tools/nextest/.config/nextest.toml run -E 'test(sql) or test(cli_state)' --no-fail-fast --test-threads 1

lint: lint_cargo_fmt_check lint_cargo_deny lint_cargo_clippy
lint_cargo_fmt_check:
Expand Down
2 changes: 1 addition & 1 deletion implementations/rust/ockam/ockam_abac/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ ockam_executor = { version = "0.80.0", path = "../ockam_executor", default-featu
regex = { version = "1.10.5", default-features = false, optional = true }
rustyline = { version = "14.0.0", optional = true }
rustyline-derive = { version = "0.10.0", optional = true }
sqlx = { version = "0.7.4", optional = true }
sqlx = { git = "https://github.com/etorreborre/sqlx", rev = "5fec648d2de0cbeed738dcf1c6f5bc9194fc439b", optional = true }
str-buf = "3.0.3"
tokio = { version = "1.38", default-features = false, optional = true, features = ["sync", "time", "rt", "rt-multi-thread", "macros"] }
tracing = { version = "0.1", default-features = false, features = ["attributes"] }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use tracing::debug;
use ockam_core::async_trait;
use ockam_core::compat::vec::Vec;
use ockam_core::Result;
use ockam_node::database::{FromSqlxError, SqlxDatabase, SqlxType, ToSqlxType, ToVoid};
use ockam_node::database::{FromSqlxError, SqlxDatabase, ToVoid};

use crate::{Action, Expr, ResourceName, ResourcePoliciesRepository, ResourcePolicy};

Expand Down Expand Up @@ -43,13 +43,15 @@ impl ResourcePoliciesRepository for ResourcePolicySqlxDatabase {
expression: &Expr,
) -> Result<()> {
let query = query(
r#"INSERT OR REPLACE INTO resource_policy
VALUES (?, ?, ?, ?)"#,
r#"INSERT INTO resource_policy (resource_name, action, expression, node_name)
VALUES ($1, $2, $3, $4)
ON CONFLICT (resource_name, action, node_name)
DO UPDATE SET expression = $3"#,
)
.bind(resource_name.to_sql())
.bind(action.to_sql())
.bind(expression.to_string().to_sql())
.bind(self.node_name.to_sql());
.bind(resource_name)
.bind(action)
.bind(expression)
.bind(&self.node_name);
query.execute(&*self.database.pool).await.void()
}

Expand All @@ -61,11 +63,11 @@ impl ResourcePoliciesRepository for ResourcePolicySqlxDatabase {
let query = query_as(
r#"SELECT resource_name, action, expression
FROM resource_policy
WHERE node_name=$1 and resource_name=$2 and action=$3"#,
WHERE node_name = $1 and resource_name = $2 and action = $3"#,
)
.bind(self.node_name.to_sql())
.bind(resource_name.to_sql())
.bind(action.to_sql());
.bind(&self.node_name)
.bind(resource_name)
.bind(action);
let row: Option<PolicyRow> = query
.fetch_optional(&*self.database.pool)
.await
Expand All @@ -77,9 +79,9 @@ impl ResourcePoliciesRepository for ResourcePolicySqlxDatabase {
let query = query_as(
r#"SELECT resource_name, action, expression
FROM resource_policy
WHERE node_name=$1"#,
WHERE node_name = $1"#,
)
.bind(self.node_name.to_sql());
.bind(&self.node_name);
let row: Vec<PolicyRow> = query.fetch_all(&*self.database.pool).await.into_core()?;
row.into_iter()
.map(|r| r.try_into())
Expand All @@ -93,10 +95,10 @@ impl ResourcePoliciesRepository for ResourcePolicySqlxDatabase {
let query = query_as(
r#"SELECT resource_name, action, expression
FROM resource_policy
WHERE node_name=$1 and resource_name=$2"#,
WHERE node_name = $1 and resource_name = $2"#,
)
.bind(self.node_name.to_sql())
.bind(resource_name.to_sql());
.bind(&self.node_name)
.bind(resource_name);
let row: Vec<PolicyRow> = query.fetch_all(&*self.database.pool).await.into_core()?;
row.into_iter()
.map(|r| r.try_into())
Expand All @@ -106,29 +108,15 @@ impl ResourcePoliciesRepository for ResourcePolicySqlxDatabase {
async fn delete_policy(&self, resource_name: &ResourceName, action: &Action) -> Result<()> {
let query = query(
r#"DELETE FROM resource_policy
WHERE node_name=? and resource_name=? and action=?"#,
WHERE node_name = $1 and resource_name = $2 and action = $3"#,
)
.bind(self.node_name.to_sql())
.bind(resource_name.to_sql())
.bind(action.to_sql());
.bind(&self.node_name)
.bind(resource_name)
.bind(action);
query.execute(&*self.database.pool).await.void()
}
}

// Database serialization / deserialization

impl ToSqlxType for ResourceName {
fn to_sql(&self) -> SqlxType {
SqlxType::Text(self.as_str().to_string())
}
}

impl ToSqlxType for Action {
fn to_sql(&self) -> SqlxType {
SqlxType::Text(self.to_string())
}
}

/// Low-level representation of a row in the resource_policy table
#[derive(FromRow)]
struct PolicyRow {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
use core::str::FromStr;
use sqlx::database::HasArguments;
use sqlx::encode::IsNull;
use sqlx::*;
use tracing::debug;

use ockam_core::async_trait;
use ockam_core::Result;
use ockam_node::database::{FromSqlxError, SqlxDatabase, ToSqlxType, ToVoid};
use ockam_node::database::{FromSqlxError, SqlxDatabase, ToVoid};

use crate::{Resource, ResourceName, ResourceType, ResourcesRepository};

Expand Down Expand Up @@ -37,23 +39,25 @@ impl ResourcesSqlxDatabase {
impl ResourcesRepository for ResourcesSqlxDatabase {
async fn store_resource(&self, resource: &Resource) -> Result<()> {
let query = query(
r#"INSERT OR REPLACE INTO resource
VALUES (?, ?, ?)"#,
r#"
INSERT INTO resource (resource_name, resource_type, node_name)
VALUES ($1, $2, $3)
ON CONFLICT DO NOTHING"#,
)
.bind(resource.resource_name.to_sql())
.bind(resource.resource_type.to_sql())
.bind(self.node_name.to_sql());
.bind(&resource.resource_name)
.bind(&resource.resource_type)
.bind(&self.node_name);
query.execute(&*self.database.pool).await.void()
}

async fn get_resource(&self, resource_name: &ResourceName) -> Result<Option<Resource>> {
let query = query_as(
r#"SELECT resource_name, resource_type
FROM resource
WHERE node_name=$1 and resource_name=$2"#,
WHERE node_name = $1 and resource_name = $2"#,
)
.bind(self.node_name.to_sql())
.bind(resource_name.to_sql());
.bind(&self.node_name)
.bind(resource_name);
let row: Option<ResourceRow> = query
.fetch_optional(&*self.database.pool)
.await
Expand All @@ -66,24 +70,38 @@ impl ResourcesRepository for ResourcesSqlxDatabase {

let query = query(
r#"DELETE FROM resource
WHERE node_name=? and resource_name=?"#,
WHERE node_name = $1 and resource_name = $2"#,
)
.bind(self.node_name.to_sql())
.bind(resource_name.to_sql());
.bind(&self.node_name)
.bind(resource_name);
query.execute(&mut *transaction).await.void()?;

let query = sqlx::query(
r#"DELETE FROM resource_policy
WHERE node_name=? and resource_name=?"#,
WHERE node_name = $1 and resource_name = $2"#,
)
.bind(self.node_name.to_sql())
.bind(resource_name.to_sql());
.bind(&self.node_name)
.bind(resource_name);
query.execute(&mut *transaction).await.void()?;

transaction.commit().await.void()
}
}

// Database serialization / deserialization

impl Type<Any> for ResourceName {
fn type_info() -> <Any as Database>::TypeInfo {
<String as Type<Any>>::type_info()
}
}

impl sqlx::Encode<'_, Any> for ResourceName {
fn encode_by_ref(&self, buf: &mut <Any as HasArguments>::ArgumentBuffer) -> IsNull {
<String as sqlx::Encode<'_, Any>>::encode_by_ref(&self.to_string(), buf)
}
}

/// Low-level representation of a row in the resource_type_policy table
#[derive(FromRow)]
#[allow(dead_code)]
Expand Down
Loading
Loading