Skip to content

Commit

Permalink
feat: add get_vault_secret_by_name() function (#335)
Browse files Browse the repository at this point in the history
* feat: add get_vault_secret_by_name() function

* update docs

* pump stripe fdw version number

* add error report for missing options
  • Loading branch information
burmecia authored Aug 29, 2024
1 parent fe91635 commit dc687ea
Show file tree
Hide file tree
Showing 4 changed files with 59 additions and 23 deletions.
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

0 comments on commit dc687ea

Please sign in to comment.