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

feat: add get_vault_secret_by_name() function #335

Merged
merged 4 commits into from
Aug 29, 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
29 changes: 15 additions & 14 deletions docs/catalog/stripe.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,18 +41,6 @@ create foreign data wrapper stripe_wrapper

We need to provide Postgres with the credentials to connect to Stripe, and any additional options. We can do this using the `create server` command:

=== "Without Vault"

```sql
create server stripe_server
foreign data wrapper stripe_wrapper
options (
api_key '<Stripe API Key>', -- Stripe API key, required
api_url 'https://api.stripe.com/v1/', -- Stripe API base URL, optional. Default is 'https://api.stripe.com/v1/'
api_version '2024-06-20' -- Stripe API version, optional. Default is your Stripe account’s default API version.
);
```

=== "With Vault"

By default, Postgres stores FDW credentials inside `pg_catalog.pg_foreign_server` in plain text. Anyone with access to this table will be able to view these credentials.
Expand All @@ -68,13 +56,26 @@ We need to provide Postgres with the credentials to connect to Stripe, and any a
)
returning key_id;
```
Reference the credentials using the Key ID:
Reference the credentials using the Key ID or Key Name:

```sql
create server stripe_server
foreign data wrapper stripe_wrapper
options (
api_key_id '<key_ID>', -- The Key ID from above, required.
api_key_id '<key_ID>', -- The Key ID from above, required if api_key_name is not specified.
api_key_name '<key_Name>', -- The Key Name from above, required if api_key_id is not specified.
api_url 'https://api.stripe.com/v1/', -- Stripe API base URL, optional. Default is 'https://api.stripe.com/v1/'
api_version '2024-06-20' -- Stripe API version, optional. Default is your Stripe account’s default API version.
);
```

=== "Without Vault"

```sql
create server stripe_server
foreign data wrapper stripe_wrapper
options (
api_key '<Stripe API Key>', -- Stripe API key, required
api_url 'https://api.stripe.com/v1/', -- Stripe API base URL, optional. Default is 'https://api.stripe.com/v1/'
api_version '2024-06-20' -- Stripe API version, optional. Default is your Stripe account’s default API version.
);
Expand Down
28 changes: 24 additions & 4 deletions supabase-wrappers/src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -154,9 +154,9 @@ pub fn create_async_runtime() -> Result<Runtime, CreateRuntimeError> {
Ok(Builder::new_current_thread().enable_all().build()?)
}

/// Get decrypted secret from Vault
/// Get decrypted secret from Vault by secret ID
///
/// Get decrypted secret as string from Vault. Vault is an extension for storing
/// Get decrypted secret as string from Vault by secret ID. Vault is an extension for storing
/// encrypted secrets, [see more details](https://github.com/supabase/vault).
pub fn get_vault_secret(secret_id: &str) -> Option<String> {
match Uuid::try_parse(secret_id) {
Expand All @@ -169,11 +169,11 @@ pub fn get_vault_secret(secret_id: &str) -> Option<String> {
pgrx::Uuid::from_bytes(sid).into_datum(),
)],
) {
Ok(sid) => sid,
Ok(decrypted) => decrypted,
Err(err) => {
report_error(
PgSqlErrorCode::ERRCODE_FDW_ERROR,
&format!("invalid secret id \"{}\": {}", secret_id, err),
&format!("query vault failed \"{}\": {}", secret_id, err),
);
None
}
Expand All @@ -189,6 +189,26 @@ pub fn get_vault_secret(secret_id: &str) -> Option<String> {
}
}

/// Get decrypted secret from Vault by secret name
///
/// Get decrypted secret as string from Vault by secret name. Vault is an extension for storing
/// encrypted secrets, [see more details](https://github.com/supabase/vault).
pub fn get_vault_secret_by_name(secret_name: &str) -> Option<String> {
match Spi::get_one_with_args::<String>(
"select decrypted_secret from vault.decrypted_secrets where name = $1",
vec![(PgBuiltInOids::TEXTOID.oid(), secret_name.into_datum())],
) {
Ok(decrypted) => decrypted,
Err(err) => {
report_error(
PgSqlErrorCode::ERRCODE_FDW_ERROR,
&format!("query vault failed \"{}\": {}", secret_name, err),
);
None
}
}
}

pub(super) unsafe fn tuple_table_slot_to_row(slot: *mut pg_sys::TupleTableSlot) -> Row {
let tup_desc = PgTupleDesc::from_pg_copy((*slot).tts_tupleDescriptor);

Expand Down
1 change: 1 addition & 0 deletions wrappers/src/fdw/stripe_fdw/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ This is a foreign data wrapper for [Stripe](https://stripe.com/) developed using

| Version | Date | Notes |
| ------- | ---------- | ---------------------------------------------------- |
| 0.1.10 | 2024-08-26 | Added 'api_key_name' server option |
| 0.1.9 | 2024-07-01 | Added 'api_version' server option |
| 0.1.7 | 2023-07-13 | Added fdw stats collection |
| 0.1.6 | 2023-05-30 | Added Checkout Session object |
Expand Down
24 changes: 19 additions & 5 deletions wrappers/src/fdw/stripe_fdw/stripe_fdw.rs
Original file line number Diff line number Diff line change
Expand Up @@ -255,7 +255,7 @@ fn inc_stats_request_cnt(stats_metadata: &mut JsonB) -> StripeFdwResult<()> {
}

#[wrappers_fdw(
version = "0.1.9",
version = "0.1.10",
author = "Supabase",
website = "https://github.com/supabase/wrappers/tree/main/wrappers/src/fdw/stripe_fdw",
error_type = "StripeFdwError"
Expand Down Expand Up @@ -638,10 +638,24 @@ impl ForeignDataWrapper<StripeFdwError> for StripeFdw {
let api_version = server.options.get("api_version").map(|t| t.as_str());
let client = match server.options.get("api_key") {
Some(api_key) => Some(create_client(api_key, api_version)),
None => {
let key_id = require_option("api_key_id", &server.options)?;
get_vault_secret(key_id).map(|api_key| create_client(&api_key, api_version))
}
None => server
.options
.get("api_key_id")
.and_then(|key_id| get_vault_secret(key_id))
.or_else(|| {
server
.options
.get("api_key_name")
.and_then(|key_name| get_vault_secret_by_name(key_name))
})
.map(|api_key| create_client(&api_key, api_version))
.or_else(|| {
report_error(
pgrx::PgSqlErrorCode::ERRCODE_FDW_ERROR,
"either api_key_id or api_key_name option is required",
);
None
}),
}
.transpose()?;

Expand Down
Loading