Skip to content

Commit 79e2ba6

Browse files
authored
feat: add GCP Credential Access Boundary downscoping (#805)
## Summary Add a typed Google Cloud Credential Access Boundary grant and server-side STS granter to `reqsign-google`. It reuses the `GrantCredential` / `Granter<Credential>` capability introduced in #803: a token-only Google source credential authorizes the exchange, and the result is an expiration-aware token-only `Credential` that the existing Google signing path can consume. The service API constructs Cloud Storage bucket and object-prefix rules from validated types, emits the required list-prefix condition without accepting raw CEL or STS JSON, enforces the documented CAB and token-lifetime limits, and performs the exchange through `Context::http_send`. Source and output deadlines are checked around I/O, granted outputs are never cached, and credential material is redacted from errors and `Debug`. This is the second service implementation of scoped credential granting after Azure while keeping authorization semantics within the Google crate. The client-issued intermediary-token and session-key flow is intentionally tracked separately in #804. Live GCP STS and Cloud Storage acceptance remains pending because no test credential and bucket were available for this implementation. Closes #749.
1 parent 6392bc4 commit 79e2ba6

7 files changed

Lines changed: 1780 additions & 7 deletions

File tree

reqsign/src/google.rs

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,43 @@
1919
//!
2020
//! This module provides Google Cloud signing functionality along with convenience
2121
//! functions for common use cases.
22+
//!
23+
//! ## Credential Access Boundary downscoping
24+
//!
25+
//! A control plane can exchange a source OAuth access token for a token restricted
26+
//! to selected Cloud Storage buckets or object prefixes:
27+
//!
28+
//! ```no_run
29+
//! use std::time::Duration;
30+
//!
31+
//! use reqsign::{Context, Granter, time::Timestamp};
32+
//! use reqsign::google::{
33+
//! CredentialAccessBoundaryGrant, CredentialAccessBoundaryGranter,
34+
//! CredentialAccessBoundaryPermissions, TokenCredentialProvider,
35+
//! };
36+
//!
37+
//! # async fn example() -> reqsign_core::Result<()> {
38+
//! let source = TokenCredentialProvider::new("source-oauth-token")
39+
//! .with_expires_at(Timestamp::now() + Duration::from_secs(3600));
40+
//! let grant = CredentialAccessBoundaryGrant::for_object_prefix(
41+
//! "example-bucket",
42+
//! "customer-a/",
43+
//! CredentialAccessBoundaryPermissions::OBJECT_VIEWER,
44+
//! );
45+
//! // The context must be configured with an HttpSend implementation. When the
46+
//! // `default-context` feature is enabled, use `reqsign::default_context()`.
47+
//! let context = Context::new();
48+
//! let downscoped = Granter::new(
49+
//! context,
50+
//! source,
51+
//! CredentialAccessBoundaryGranter::new(grant),
52+
//! )
53+
//! .grant(None)
54+
//! .await?;
55+
//! # let _ = downscoped;
56+
//! # Ok(())
57+
//! # }
58+
//! ```
2259
2360
// Re-export all Google Cloud signing types
2461
pub use reqsign_google::*;

services/google/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ repository.workspace = true
2727
rust-version.workspace = true
2828

2929
[dependencies]
30+
bytes = { workspace = true }
3031
form_urlencoded = { workspace = true }
3132
http = { workspace = true }
3233
log = { workspace = true }
@@ -42,7 +43,6 @@ tokio = { workspace = true, features = ["time"] }
4243
reqsign-core = { workspace = true, features = ["jwt"] }
4344

4445
[dev-dependencies]
45-
bytes = { workspace = true }
4646
dotenvy = { workspace = true }
4747
env_logger = { workspace = true }
4848
reqsign-file-read-tokio = { workspace = true }

services/google/src/constants.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
// specific language governing permissions and limitations
1616
// under the License.
1717

18+
use std::time::Duration;
19+
1820
use percent_encoding::AsciiSet;
1921
use percent_encoding::NON_ALPHANUMERIC;
2022

@@ -25,6 +27,8 @@ pub const GOOGLE_SCOPE: &str = "GOOGLE_SCOPE";
2527
// Default OAuth2 scope for Google Cloud services
2628
pub const DEFAULT_SCOPE: &str = "https://www.googleapis.com/auth/cloud-platform";
2729

30+
pub(crate) const TOKEN_OPERATION_HEADROOM: Duration = Duration::from_secs(10);
31+
2832
/// AsciiSet for [Google UriEncode](https://cloud.google.com/storage/docs/authentication/canonical-requests)
2933
///
3034
/// - URI encode every byte except the unreserved characters: 'A'-'Z', 'a'-'z', '0'-'9', '-', '.', '_', and '~'.

0 commit comments

Comments
 (0)