Skip to content
Open
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
32 changes: 32 additions & 0 deletions services/oracle/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,38 @@ The environment provider reads `OCI_USER`, `OCI_TENANCY`, `OCI_KEY_FILE`, and
`OCI_FINGERPRINT`. Use `StaticCredentialProvider` or
`DefaultCredentialProvider::builder()` for explicit configuration.

## Configuration Profiles

Select a named profile with `OCI_PROFILE`. Missing credential fields in an existing
profile inherit from `[DEFAULT]`, following the OCI
[profiles and inheritance](https://docs.oracle.com/en-us/iaas/Content/API/Concepts/sdkconfig.htm)
convention:

```ini
[DEFAULT]
tenancy=ocid1.tenancy.oc1..example
user=ocid1.user.oc1..default
fingerprint=default-fingerprint
key_file=~/.oci/default.pem

[ADMIN]
user=ocid1.user.oc1..admin
fingerprint=admin-fingerprint
key_file=~/.oci/admin.pem
```

With `OCI_PROFILE=ADMIN`, the provider uses the admin user and key and inherits
the default tenancy. Explicit values, including empty values, override defaults.
A missing profile or a profile still missing required fields yields no credential;
the provider does not substitute another named profile.

Configuration values are literal. Write Windows paths such as
`C:\Users\example\.oci\api_key.pem` directly, without doubling backslashes or
adding surrounding quotes. If a config previously relied on INI escape processing,
replace the escaped value with the literal path. Home expansion of `~/` happens
after resolving the selected profile. Config files are reread when the provider
is invoked, so inherited values participate in credential refresh as well.

## Private Key Formats

The file selected by `OCI_KEY_FILE` or the OCI configuration's `key_file` entry
Expand Down
36 changes: 29 additions & 7 deletions services/oracle/src/provide_credential/config_file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,14 @@ use std::time::Duration;
/// The config file path and profile name can be overridden using environment variables:
/// - `OCI_CONFIG_FILE`: Override the config file path
/// - `OCI_PROFILE`: Override the profile name (default is "DEFAULT")
///
/// An existing named profile inherits missing credential fields from `[DEFAULT]`.
/// Explicit values, including empty strings, take precedence over defaults. A missing
/// profile is not replaced by `[DEFAULT]`, and other named profiles are never consulted.
/// Values are literal: backslashes and quotes in key paths are not INI escapes.
/// Home expansion is applied to the resolved `key_file` value after inheritance.
///
/// See [OCI profiles and inheritance](https://docs.oracle.com/en-us/iaas/Content/API/Concepts/sdkconfig.htm).
#[derive(Debug, Default, Clone)]
pub struct ConfigFileCredentialProvider {}

Expand Down Expand Up @@ -71,8 +79,16 @@ impl ProvideCredential for ConfigFileCredentialProvider {
.map(|s| s.as_str())
.unwrap_or(ORACLE_DEFAULT_PROFILE);

// Parse INI content
let ini = ini::Ini::read_from(&mut content.as_bytes()).map_err(|e| {
// OCI paths are literal, including Windows backslashes and embedded quotes.
let ini = ini::Ini::load_from_str_opt(
&content,
ini::ParseOption {
enabled_escape: false,
enabled_quote: false,
..Default::default()
},
)
.map_err(|e| {
reqsign_core::Error::config_invalid(format!("Failed to parse config file: {e}"))
})?;
let section = match ini.section(Some(profile)) {
Expand All @@ -83,12 +99,18 @@ impl ProvideCredential for ConfigFileCredentialProvider {
}
};

// Extract values
// Only absent fields inherit; a missing selected profile must not load defaults.
let defaults = ini.section(Some(ORACLE_DEFAULT_PROFILE));
let value = |key: &str| {
section
.get(key)
.or_else(|| defaults.and_then(|defaults| defaults.get(key)))
};
match (
section.get("tenancy"),
section.get("user"),
section.get("key_file"),
section.get("fingerprint"),
value("tenancy"),
value("user"),
value("key_file"),
value("fingerprint"),
) {
(Some(tenancy), Some(user), Some(key_file), Some(fingerprint)) => {
debug!("loading credential from config file");
Expand Down
Loading
Loading