objectstore_server/auth/
key_directory.rs

1use std::collections::{BTreeMap, HashSet};
2use std::path::Path;
3
4use jsonwebtoken::DecodingKey;
5use objectstore_types::Permission;
6
7use crate::auth::AuthError;
8use crate::config::{AuthZ, AuthZVerificationKey};
9
10fn read_key_from_file(filename: &Path) -> Result<DecodingKey, AuthError> {
11    let key_content = std::fs::read_to_string(filename).map_err(|_| {
12        AuthError::InitFailure(format!("key could not be read from '{filename:?}'"))
13    })?;
14    DecodingKey::from_ed_pem(key_content.as_bytes())
15        .map_err(|_| AuthError::InitFailure("key could not be parsed".into()))
16}
17
18/// Configures the EdDSA public key(s) and permissions used to verify tokens from a single `kid`.
19///
20/// Note: [`jsonwebtoken::DecodingKey`] redacts key content in its `Debug` implementation.
21#[derive(Debug)]
22pub struct PublicKeyConfig {
23    /// Versions of this key's key material which may be used to verify signatures.
24    ///
25    /// If a key is being rotated, the old and new versions of that key should both be
26    /// configured so objectstore can verify signatures while the updated key is still
27    /// rolling out. Otherwise, this should only contain the most recent version of a key.
28    pub key_versions: Vec<DecodingKey>,
29
30    /// The maximum set of permissions that this key's signer is authorized to grant.
31    ///
32    /// If a request's `Authorization` header grants full permission but it was signed by
33    /// a key that is only allowed to grant read permission, then the request only has
34    /// read permission.
35    pub max_permissions: HashSet<Permission>,
36}
37
38impl TryFrom<&AuthZVerificationKey> for PublicKeyConfig {
39    type Error = AuthError;
40    fn try_from(key_config: &AuthZVerificationKey) -> Result<Self, Self::Error> {
41        Ok(Self {
42            max_permissions: key_config.max_permissions.clone(),
43            key_versions: key_config
44                .key_files
45                .iter()
46                .map(|filename| read_key_from_file(filename))
47                .collect::<Result<Vec<DecodingKey>, AuthError>>()?,
48        })
49    }
50}
51
52/// Directory of keys that may be used to verify a request's `Authorization` header.
53///
54/// This directory contains a map that is keyed on a key's ID. When verifying a JWT
55/// from the `Authorization` header, the `kid` field should be read from the JWT
56/// header and used to index into this directory to select the appropriate key.
57#[derive(Debug)]
58pub struct PublicKeyDirectory {
59    /// Mapping from key ID to key configuration.
60    pub keys: BTreeMap<String, PublicKeyConfig>,
61}
62
63impl TryFrom<&AuthZ> for PublicKeyDirectory {
64    type Error = AuthError;
65
66    fn try_from(auth_config: &AuthZ) -> Result<Self, Self::Error> {
67        Ok(Self {
68            keys: auth_config
69                .keys
70                .iter()
71                .map(|(kid, key)| Ok((kid.clone(), key.try_into()?)))
72                .collect::<Result<BTreeMap<String, PublicKeyConfig>, AuthError>>()?,
73        })
74    }
75}