cloud_sdk_reqwest/shared/
auth.rs1use core::fmt;
2
3use bytes::Bytes;
4use cloud_sdk_sanitization::{SecretBuffer, sanitize_bytes};
5use reqwest::header::HeaderValue;
6#[cfg(test)]
7use std::sync::{
8 Arc,
9 atomic::{AtomicUsize, Ordering},
10};
11use std::vec::Vec;
12
13pub const MAX_BEARER_TOKEN_BYTES: usize = 4096;
15
16const BEARER_PREFIX: &[u8] = b"Bearer ";
17
18#[derive(Clone, Copy, Debug, Eq, PartialEq)]
20pub enum BearerTokenError {
21 Empty,
23 TooLong,
25 InvalidByte,
28 AllocationFailed,
30}
31
32impl_static_error!(BearerTokenError,
33 Self::Empty => "bearer token is empty",
34 Self::TooLong => "bearer token exceeds the length limit",
35 Self::InvalidByte => "bearer token contains an invalid byte",
36 Self::AllocationFailed => "bearer-token allocation failed",
37);
38
39pub struct BearerToken {
42 authorization: Vec<u8>,
43 #[cfg(test)]
44 drop_probe: Option<Arc<AtomicUsize>>,
45 #[cfg(test)]
46 header_drop_probe: Option<Arc<AtomicUsize>>,
47}
48
49impl BearerToken {
50 pub fn new(token: &str) -> Result<Self, BearerTokenError> {
52 Self::from_bytes(token.as_bytes())
53 }
54
55 pub fn from_mut_bytes(token: &mut [u8]) -> Result<Self, BearerTokenError> {
58 let result = Self::from_bytes(token);
59 sanitize_bytes(token);
60 result
61 }
62
63 pub fn from_secret_buffer(token: SecretBuffer<'_>) -> Result<Self, BearerTokenError> {
66 Self::from_bytes(token.as_slice())
67 }
68
69 fn from_bytes(token: &[u8]) -> Result<Self, BearerTokenError> {
70 if token.is_empty() {
71 return Err(BearerTokenError::Empty);
72 }
73 if token.len() > MAX_BEARER_TOKEN_BYTES {
74 return Err(BearerTokenError::TooLong);
75 }
76 let mut saw_token_byte = false;
77 let mut padding = false;
78 for byte in token.iter().copied() {
79 match byte {
80 b'=' if saw_token_byte => padding = true,
81 b'=' => return Err(BearerTokenError::InvalidByte),
82 _ if padding || !is_bearer_byte(byte) => {
83 return Err(BearerTokenError::InvalidByte);
84 }
85 _ => saw_token_byte = true,
86 }
87 }
88
89 let capacity = BEARER_PREFIX
90 .len()
91 .checked_add(token.len())
92 .ok_or(BearerTokenError::TooLong)?;
93 let mut authorization = Vec::new();
94 authorization
95 .try_reserve_exact(capacity)
96 .map_err(|_| BearerTokenError::AllocationFailed)?;
97 authorization.extend_from_slice(BEARER_PREFIX);
98 authorization.extend_from_slice(token);
99 Ok(Self {
100 authorization,
101 #[cfg(test)]
102 drop_probe: None,
103 #[cfg(test)]
104 header_drop_probe: None,
105 })
106 }
107
108 pub(crate) fn header_value(&self) -> Result<HeaderValue, ()> {
109 let mut bytes = Vec::new();
110 bytes
111 .try_reserve_exact(self.authorization.len())
112 .map_err(|_| ())?;
113 bytes.extend_from_slice(&self.authorization);
114 let owner = SanitizedHeaderValue {
115 bytes,
116 #[cfg(test)]
117 drop_probe: self.header_drop_probe.clone(),
118 };
119 let mut value = HeaderValue::from_maybe_shared(Bytes::from_owner(owner)).map_err(|_| ())?;
120 value.set_sensitive(true);
121 Ok(value)
122 }
123
124 #[cfg(test)]
125 pub(crate) fn owned_bytes(&self) -> &[u8] {
126 &self.authorization
127 }
128
129 #[cfg(test)]
130 pub(crate) fn with_drop_probe(
131 token: &str,
132 probe: Arc<AtomicUsize>,
133 ) -> Result<Self, BearerTokenError> {
134 let mut value = Self::new(token)?;
135 value.drop_probe = Some(probe);
136 Ok(value)
137 }
138
139 #[cfg(test)]
140 pub(crate) fn with_header_drop_probe(
141 token: &str,
142 probe: Arc<AtomicUsize>,
143 ) -> Result<Self, BearerTokenError> {
144 let mut value = Self::new(token)?;
145 value.header_drop_probe = Some(probe);
146 Ok(value)
147 }
148}
149
150impl Drop for BearerToken {
151 fn drop(&mut self) {
152 sanitize_bytes(&mut self.authorization);
153 #[cfg(test)]
154 if let Some(probe) = &self.drop_probe {
155 probe.fetch_add(1, Ordering::SeqCst);
156 }
157 }
158}
159
160impl fmt::Debug for BearerToken {
161 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
162 formatter.write_str("BearerToken([redacted])")
163 }
164}
165
166const fn is_bearer_byte(byte: u8) -> bool {
167 byte.is_ascii_alphanumeric() || matches!(byte, b'-' | b'.' | b'_' | b'~' | b'+' | b'/')
168}
169
170struct SanitizedHeaderValue {
171 bytes: Vec<u8>,
172 #[cfg(test)]
173 drop_probe: Option<Arc<AtomicUsize>>,
174}
175
176impl AsRef<[u8]> for SanitizedHeaderValue {
177 fn as_ref(&self) -> &[u8] {
178 &self.bytes
179 }
180}
181
182impl Drop for SanitizedHeaderValue {
183 fn drop(&mut self) {
184 sanitize_bytes(&mut self.bytes);
185 #[cfg(test)]
186 if let Some(probe) = &self.drop_probe {
187 probe.fetch_add(1, Ordering::SeqCst);
188 }
189 }
190}