rama_net/user/credentials/
bearer.rs1use rama_core::error::{ErrorContext, OpaqueError};
2use std::borrow::Cow;
3
4#[cfg(feature = "http")]
5use rama_http_types::HeaderValue;
6
7#[derive(Debug, Clone, PartialEq, Eq)]
8pub struct Bearer(Cow<'static, str>);
10
11impl Bearer {
12 pub fn try_from_header_str(value: impl AsRef<str>) -> Result<Self, OpaqueError> {
14 let value = value.as_ref();
15
16 if value.len() <= BEARER_SCHEME.len() + 1 {
17 return Err(OpaqueError::from_display("invalid bearer scheme length"));
18 }
19 if !value.as_bytes()[..BEARER_SCHEME.len()].eq_ignore_ascii_case(BEARER_SCHEME.as_bytes()) {
20 return Err(OpaqueError::from_display("invalid bearer scheme"));
21 }
22
23 let bytes = &value.as_bytes()[BEARER_SCHEME.len() + 1..];
24
25 let non_space_pos = bytes
26 .iter()
27 .position(|b| *b != b' ')
28 .ok_or_else(|| OpaqueError::from_display("missing space separator in bearer str"))?;
29 let bytes = &bytes[non_space_pos..];
30
31 let s =
32 std::str::from_utf8(bytes).context("turn scheme-trimmed bearer back into utf-8 str")?;
33 Self::try_from_clear_str(s.to_owned())
34 }
35
36 pub fn try_from_clear_str(s: impl Into<Cow<'static, str>>) -> Result<Self, OpaqueError> {
38 let s = s.into();
39 if s.is_empty() {
40 return Err(OpaqueError::from_display(
41 "empty str cannot be used as Bearer",
42 ));
43 }
44 if s.as_bytes().iter().any(|b| *b < 32 || *b >= 127) {
45 return Err(OpaqueError::from_display(
46 "string contains non visible ASCII characters",
47 ));
48 }
49
50 Ok(Self(s))
51 }
52
53 pub fn as_header_string(&self) -> String {
55 format!("{BEARER_SCHEME} {}", self.0)
56 }
57
58 #[cfg(feature = "http")]
59 pub fn as_header_value(&self) -> HeaderValue {
61 let encoded = self.as_header_string();
62 HeaderValue::from_str(&encoded).expect("inner value should always be valid")
64 }
65
66 pub fn as_clear_string(&self) -> String {
68 self.0.to_string()
69 }
70
71 pub fn token(&self) -> &str {
73 &self.0
74 }
75}
76
77pub const BEARER_SCHEME: &str = "Bearer";
79
80#[cfg(test)]
81mod tests {
82 use super::*;
83
84 #[test]
85 fn bearer_parse_empty() {
86 let value = Bearer::try_from_header_str("");
87 assert!(value.is_err());
88 }
89
90 #[test]
91 fn bearer_clear_text_empty() {
92 let value = Bearer::try_from_clear_str("");
93 assert!(value.is_err());
94 }
95
96 #[test]
97 fn bearer_header() {
98 let auth = Bearer::try_from_header_str("Bearer 123abc").unwrap();
99 assert_eq!(auth.token(), "123abc");
100 assert_eq!("Bearer 123abc", auth.as_header_string());
101 }
102
103 #[test]
104 fn bearer_clear() {
105 let auth = Bearer::try_from_clear_str("foobar".to_owned()).unwrap();
106 assert_eq!(auth.token(), "foobar");
107 assert_eq!("foobar", auth.as_clear_string());
108 }
109}