objectstore_client/
auth.rs1use std::collections::{BTreeMap, HashSet};
2
3use jsonwebtoken::{Algorithm, EncodingKey, Header, encode, get_current_timestamp};
4use serde::{Deserialize, Serialize};
5
6use crate::ScopeInner;
7
8pub use objectstore_types::Permission;
9
10const DEFAULT_EXPIRY_SECONDS: u64 = 60;
11const DEFAULT_PERMISSIONS: [Permission; 3] = [
12 Permission::ObjectRead,
13 Permission::ObjectWrite,
14 Permission::ObjectDelete,
15];
16
17pub struct SecretKey {
19 pub kid: String,
21
22 pub secret_key: String,
24}
25
26impl std::fmt::Debug for SecretKey {
27 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
28 f.debug_struct("SecretKey")
29 .field("kid", &self.kid)
30 .field("secret_key", &"[redacted]")
31 .finish()
32 }
33}
34
35#[derive(Debug)]
40pub struct TokenGenerator {
41 kid: String,
42 encoding_key: EncodingKey,
43 expiry_seconds: u64,
44 permissions: HashSet<Permission>,
45}
46
47#[derive(Serialize, Deserialize)]
48struct JwtRes {
49 #[serde(rename = "os:usecase")]
50 usecase: String,
51
52 #[serde(flatten)]
53 scopes: BTreeMap<String, String>,
54}
55
56#[derive(Serialize, Deserialize)]
57struct JwtClaims {
58 exp: u64,
59 permissions: HashSet<Permission>,
60 res: JwtRes,
61}
62
63impl TokenGenerator {
64 pub fn new(secret_key: SecretKey) -> crate::Result<TokenGenerator> {
66 let encoding_key = EncodingKey::from_ed_pem(secret_key.secret_key.as_bytes())?;
67 Ok(TokenGenerator {
68 kid: secret_key.kid,
69 encoding_key,
70 expiry_seconds: DEFAULT_EXPIRY_SECONDS,
71 permissions: HashSet::from(DEFAULT_PERMISSIONS),
72 })
73 }
74
75 pub fn expiry_seconds(mut self, expiry_seconds: u64) -> Self {
77 self.expiry_seconds = expiry_seconds;
78 self
79 }
80
81 pub fn permissions(mut self, permissions: &[Permission]) -> Self {
83 self.permissions = HashSet::from_iter(permissions.iter().cloned());
84 self
85 }
86
87 pub(crate) fn sign_for_scope(&self, scope: &ScopeInner) -> crate::Result<String> {
89 let claims = JwtClaims {
90 exp: get_current_timestamp() + self.expiry_seconds,
91 permissions: self.permissions.clone(),
92 res: JwtRes {
93 usecase: scope.usecase().name().into(),
94 scopes: scope
95 .scopes()
96 .iter()
97 .map(|scope| (scope.name().to_string(), scope.value().to_string()))
98 .collect(),
99 },
100 };
101
102 let mut header = Header::new(Algorithm::EdDSA);
103 header.kid = Some(self.kid.clone());
104
105 Ok(encode(&header, &claims, &self.encoding_key)?)
106 }
107}