google_cloud_auth/credentials/
anonymous.rs

1// Copyright 2025 Google LLC
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     https://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! Anonymous credentials.
16//!
17//! These credentials do not provide any authentication information. They are
18//! useful for accessing public resources that do not require authentication.
19
20use crate::credentials::dynamic::CredentialsProvider;
21use crate::credentials::{CacheableResource, Credentials, EntityTag, Result};
22use http::{Extensions, HeaderMap};
23use std::sync::Arc;
24
25#[derive(Debug)]
26struct AnonymousCredentials {
27    entity_tag: EntityTag,
28}
29
30/// A builder for creating anonymous credentials.
31#[derive(Debug, Default)]
32pub struct Builder {}
33
34impl Builder {
35    /// Creates a new builder.
36    pub fn new() -> Self {
37        Self::default()
38    }
39
40    /// Returns a [Credentials] instance.
41    pub fn build(self) -> Credentials {
42        Credentials {
43            inner: Arc::new(AnonymousCredentials {
44                entity_tag: EntityTag::new(),
45            }),
46        }
47    }
48}
49
50#[async_trait::async_trait]
51impl CredentialsProvider for AnonymousCredentials {
52    async fn headers(&self, extensions: Extensions) -> Result<CacheableResource<HeaderMap>> {
53        match extensions.get::<EntityTag>() {
54            Some(tag) if self.entity_tag.eq(tag) => Ok(CacheableResource::NotModified),
55            _ => Ok(CacheableResource::New {
56                data: HeaderMap::new(),
57                entity_tag: self.entity_tag.clone(),
58            }),
59        }
60    }
61}
62
63#[cfg(test)]
64mod tests {
65    use super::*;
66
67    type TestResult = std::result::Result<(), Box<dyn std::error::Error>>;
68
69    #[tokio::test]
70    async fn create_anonymous_credentials() -> TestResult {
71        let creds = Builder::new().build();
72        let mut extensions = Extensions::new();
73        let cached_headers = creds.headers(extensions.clone()).await.unwrap();
74        let (headers, entity_tag) = match cached_headers {
75            CacheableResource::New { entity_tag, data } => (data, entity_tag),
76            CacheableResource::NotModified => unreachable!("expecting new headers"),
77        };
78        assert!(headers.is_empty());
79
80        extensions.insert(entity_tag);
81        let cached_headers = creds.headers(extensions).await.unwrap();
82        match cached_headers {
83            CacheableResource::New { .. } => unreachable!("expecting cached headers"),
84            CacheableResource::NotModified => {}
85        }
86        Ok(())
87    }
88}