objectstore_server/auth/
key_directory.rs

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