Skip to content
This repository was archived by the owner on Jul 28, 2025. It is now read-only.

Commit af2b057

Browse files
committed
Add 'package_keys' to AuthorizedKeyPolicy
This allows signing keys to be granted write access to a log based on the full package name.
1 parent 0880d77 commit af2b057

1 file changed

Lines changed: 74 additions & 11 deletions

File tree

crates/server/src/policy/record/authorization.rs

Lines changed: 74 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,11 @@ use wasmparser::names::KebabStr;
1111
#[derive(Default, Deserialize)]
1212
pub struct AuthorizedKeyPolicy {
1313
#[serde(skip)]
14-
keys: HashSet<KeyID>,
14+
superuser_keys: HashSet<KeyID>,
15+
#[serde(default)]
1516
namespace_keys: HashMap<String, HashSet<KeyID>>,
17+
#[serde(default)]
18+
package_keys: HashMap<PackageId, HashSet<KeyID>>,
1619
}
1720

1821
impl AuthorizedKeyPolicy {
@@ -24,8 +27,8 @@ impl AuthorizedKeyPolicy {
2427
}
2528

2629
/// Sets an authorized key for publishing to any namespace.
27-
pub fn with_key(mut self, key: KeyID) -> Self {
28-
self.keys.insert(key);
30+
pub fn with_superuser_key(mut self, key: KeyID) -> Self {
31+
self.superuser_keys.insert(key);
2932
self
3033
}
3134

@@ -42,6 +45,33 @@ impl AuthorizedKeyPolicy {
4245
.insert(key);
4346
Ok(self)
4447
}
48+
49+
pub fn with_package_key(mut self, package_id: impl Into<String>, key: KeyID) -> Result<Self> {
50+
let package_id = PackageId::new(package_id)?;
51+
self.package_keys.entry(package_id).or_default().insert(key);
52+
Ok(self)
53+
}
54+
55+
pub fn key_authorized_for_package(&self, key: &KeyID, package: &PackageId) -> bool {
56+
if self.superuser_keys.contains(key) {
57+
return true;
58+
}
59+
60+
let namespace_keys = self.namespace_keys.get(package.namespace());
61+
if namespace_keys
62+
.map(|keys| keys.contains(key))
63+
.unwrap_or(false)
64+
{
65+
return true;
66+
}
67+
68+
let package_keys = self.package_keys.get(package);
69+
if package_keys.map(|keys| keys.contains(key)).unwrap_or(false) {
70+
return true;
71+
}
72+
73+
false
74+
}
4575
}
4676

4777
impl RecordPolicy for AuthorizedKeyPolicy {
@@ -50,18 +80,51 @@ impl RecordPolicy for AuthorizedKeyPolicy {
5080
id: &PackageId,
5181
record: &ProtoEnvelope<PackageRecord>,
5282
) -> RecordPolicyResult<()> {
53-
if !self.keys.contains(record.key_id())
54-
&& !self
55-
.namespace_keys
56-
.get(id.namespace())
57-
.map(|keys| keys.contains(record.key_id()))
58-
.unwrap_or(false)
59-
{
83+
let key = record.key_id();
84+
if !self.key_authorized_for_package(key, id) {
6085
return Err(RecordPolicyError::Unauthorized(format!(
6186
"key id `{key}` is not authorized to publish to package `{id}`",
62-
key = record.key_id()
6387
)));
6488
}
89+
Ok(())
90+
}
91+
}
92+
93+
#[cfg(test)]
94+
mod tests {
95+
use super::*;
96+
97+
#[test]
98+
fn test_key_authorized_for_package() -> Result<()> {
99+
let super_key = KeyID::from("super-key".to_string());
100+
let namespace_key = KeyID::from("namespace-key".to_string());
101+
let package_key = KeyID::from("package-key".to_string());
102+
let other_key = KeyID::from("other-key".to_string());
103+
104+
let policy = AuthorizedKeyPolicy::new()
105+
.with_superuser_key(super_key.clone())
106+
.with_namespace_key("my-namespace", namespace_key.clone())?
107+
.with_package_key("my-namespace:my-package", package_key.clone())?;
108+
109+
let my_package: PackageId = "my-namespace:my-package".parse()?;
110+
let my_namespace_other_package: PackageId = "my-namespace:other-package".parse()?;
111+
let other_namespace: PackageId = "other-namespace:any-package".parse()?;
112+
113+
assert!(policy.key_authorized_for_package(&super_key, &my_package));
114+
assert!(policy.key_authorized_for_package(&super_key, &my_namespace_other_package));
115+
assert!(policy.key_authorized_for_package(&super_key, &other_namespace));
116+
117+
assert!(policy.key_authorized_for_package(&namespace_key, &my_package));
118+
assert!(policy.key_authorized_for_package(&namespace_key, &my_namespace_other_package));
119+
assert!(!policy.key_authorized_for_package(&namespace_key, &other_namespace));
120+
121+
assert!(policy.key_authorized_for_package(&package_key, &my_package));
122+
assert!(!policy.key_authorized_for_package(&package_key, &my_namespace_other_package));
123+
assert!(!policy.key_authorized_for_package(&package_key, &other_namespace));
124+
125+
assert!(!policy.key_authorized_for_package(&other_key, &my_package));
126+
assert!(!policy.key_authorized_for_package(&other_key, &my_namespace_other_package));
127+
assert!(!policy.key_authorized_for_package(&other_key, &other_namespace));
65128

66129
Ok(())
67130
}

0 commit comments

Comments
 (0)