refactor(credentials): centralize grant policy (#2517)

* refactor(credentials): centralize grant policy

Represent ACL groups, relay permission, proxy CIDRs, and reuse as one
internal credential grant shared by generated, imported, managed, and
attached credentials. Normalize proxy CIDRs at construction while
preserving the flat credential storage schema.

Reuse one managed credential adapter for protobuf/TOML projection and
patching so defaults and future fields have a single mapping authority.

* fix(credentials): normalize grants loaded from storage

Run persisted grants through the same CIDR normalization used by new and
managed credentials. Reject invalid stored CIDRs through the existing
storage-unavailable path and include the credential ID in the error.

Cover whitespace migration and invalid legacy data with regression tests.
This commit is contained in:
KKRainbow
2026-08-22 23:55:29 +08:00
committed by GitHub
parent 3fe427bc99
commit 9f231ee76c
8 changed files with 313 additions and 175 deletions
+8
View File
@@ -22,6 +22,14 @@ operation transition.
Host capability operations use a separate seam. They turn Host readiness into
Rust task wakeups and do not share the caller-to-core broker state machine.
## Credential grant
A credential grant contains the authorization constraints shared by generated,
imported, managed, and attached-peer credentials: ACL groups, relay permission,
allowed proxy CIDRs, and whether concurrent reuse is allowed. It does not own
credential identity, key material, lifetime, persistence, or runtime ownership.
Each credential intake path normalizes the grant before installing it.
## Attached peer
An attached peer is an ordinary `PeerManagerCore` connected to another
+5 -10
View File
@@ -4,7 +4,10 @@ use easytier_proto::api::manage::{
self, NetworkConfig, NetworkingMethod, PortForwardConfig as ApiPortForwardConfig,
};
use super::toml::{ConfigLoader as _, TomlConfig};
use super::{
api_input::managed_credential_to_proto,
toml::{ConfigLoader as _, TomlConfig},
};
pub fn network_config_from_toml(config: &TomlConfig) -> NetworkConfig {
let default_config = TomlConfig::default();
@@ -121,15 +124,7 @@ pub fn network_config_from_toml(config: &TomlConfig) -> NetworkConfig {
result.managed_credentials = config
.get_managed_credentials()
.into_iter()
.map(|credential| manage::ManagedCredentialConfig {
credential_id: credential.credential_id,
credential_secret: credential.credential_secret,
groups: credential.groups,
allow_relay: credential.allow_relay,
allowed_proxy_cidrs: credential.allowed_proxy_cidrs,
expiry_unix: credential.expiry_unix,
reusable: Some(credential.reusable),
})
.map(managed_credential_to_proto)
.collect();
let flags = config.get_flags();
+30 -18
View File
@@ -51,6 +51,34 @@ pub fn add_proxy_network_to_config(
pub type NetworkingMethod = easytier_proto::api::manage::NetworkingMethod;
pub type NetworkConfig = easytier_proto::api::manage::NetworkConfig;
pub(crate) fn managed_credential_from_proto(
credential: &manage::ManagedCredentialConfig,
) -> ManagedCredentialConfig {
ManagedCredentialConfig {
credential_id: credential.credential_id.clone(),
credential_secret: credential.credential_secret.clone(),
groups: credential.groups.clone(),
allow_relay: credential.allow_relay,
allowed_proxy_cidrs: credential.allowed_proxy_cidrs.clone(),
expiry_unix: credential.expiry_unix,
reusable: credential.reusable.unwrap_or(true),
}
}
pub(crate) fn managed_credential_to_proto(
credential: ManagedCredentialConfig,
) -> manage::ManagedCredentialConfig {
manage::ManagedCredentialConfig {
credential_id: credential.credential_id,
credential_secret: credential.credential_secret,
groups: credential.groups,
allow_relay: credential.allow_relay,
allowed_proxy_cidrs: credential.allowed_proxy_cidrs,
expiry_unix: credential.expiry_unix,
reusable: Some(credential.reusable),
}
}
pub trait NetworkConfigExt {
fn gen_config(&self) -> Result<TomlConfigLoader, anyhow::Error>;
fn new_from_config(config: impl ConfigLoader) -> Result<NetworkConfig, anyhow::Error>;
@@ -301,15 +329,7 @@ impl NetworkConfigExt for NetworkConfig {
cfg.set_managed_credentials(
self.managed_credentials
.iter()
.map(|credential| ManagedCredentialConfig {
credential_id: credential.credential_id.clone(),
credential_secret: credential.credential_secret.clone(),
groups: credential.groups.clone(),
allow_relay: credential.allow_relay,
allowed_proxy_cidrs: credential.allowed_proxy_cidrs.clone(),
expiry_unix: credential.expiry_unix,
reusable: credential.reusable.unwrap_or(true),
})
.map(managed_credential_from_proto)
.collect(),
);
@@ -624,15 +644,7 @@ impl NetworkConfigExt for NetworkConfig {
result.managed_credentials = config
.get_managed_credentials()
.into_iter()
.map(|credential| manage::ManagedCredentialConfig {
credential_id: credential.credential_id,
credential_secret: credential.credential_secret,
groups: credential.groups,
allow_relay: credential.allow_relay,
allowed_proxy_cidrs: credential.allowed_proxy_cidrs,
expiry_unix: credential.expiry_unix,
reusable: Some(credential.reusable),
})
.map(managed_credential_to_proto)
.collect();
let flags = config.get_flags();
let default_flags = default_config.get_flags();
+1 -8
View File
@@ -157,14 +157,7 @@ where
let generated = self
.peer_manager
.credential_manager()
.generate_credential_with_options(
options.groups,
options.allow_relay,
options.allowed_proxy_cidrs,
options.ttl,
options.credential_id,
options.reusable,
)
.generate_credential_with_options(options)
.map_err(anyhow::Error::msg)?;
self.peer_manager.notify_credential_changed();
Ok(generated)
@@ -8,9 +8,10 @@ use easytier_proto::api::config::{
use crate::{
config::{
api_input::managed_credential_from_proto,
peers::AclRuleConfig,
runtime::CoreInstanceRuntimeConfig,
toml::{ConfigLoader as _, ManagedCredentialConfig, TomlConfig},
toml::{ConfigLoader as _, TomlConfig},
},
instance::{CoreInstance, CoreInstanceConfig, CoreInstanceHost, CoreInstanceState},
peers::credential_manager::CredentialManager,
@@ -164,15 +165,7 @@ where
let entries = managed
.entries
.iter()
.map(|credential| ManagedCredentialConfig {
credential_id: credential.credential_id.clone(),
credential_secret: credential.credential_secret.clone(),
groups: credential.groups.clone(),
allow_relay: credential.allow_relay,
allowed_proxy_cidrs: credential.allowed_proxy_cidrs.clone(),
expiry_unix: credential.expiry_unix,
reusable: credential.reusable.unwrap_or(true),
})
.map(managed_credential_from_proto)
.collect::<Vec<_>>();
let replacement = credential_manager
.validate_managed_credentials(&entries)
+2 -7
View File
@@ -75,13 +75,8 @@ impl AttachedCredentialRegistration {
network_runtime_config.snapshot().as_ref(),
&configured_groups,
);
let credential_id = network_peer_manager.register_ephemeral_credential(
public_key,
groups,
false,
Vec::new(),
false,
)?;
let credential_id =
network_peer_manager.register_ephemeral_credential(public_key, groups)?;
let task_peer_manager = network_peer_manager.clone();
let policy_task = tokio::spawn(async move {
while peer_changes.changed().await.is_ok() {
+263 -112
View File
@@ -47,15 +47,68 @@ pub struct CredentialUpsertOptions {
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub(crate) struct CredentialEntry {
pubkey: String,
#[serde(default)]
secret: String,
struct CredentialGrant {
groups: Vec<String>,
allow_relay: bool,
allowed_proxy_cidrs: Vec<String>,
#[serde(default = "default_true")]
reusable: bool,
}
#[derive(Debug)]
struct InvalidAllowedProxyCidr(String);
impl std::fmt::Display for InvalidAllowedProxyCidr {
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(formatter, "invalid allowed_proxy_cidr: {}", self.0)
}
}
impl CredentialGrant {
fn new(
groups: Vec<String>,
allow_relay: bool,
allowed_proxy_cidrs: Vec<String>,
reusable: bool,
) -> Result<Self, InvalidAllowedProxyCidr> {
let mut grant = Self {
groups,
allow_relay,
allowed_proxy_cidrs,
reusable,
};
grant.normalize()?;
Ok(grant)
}
fn normalize(&mut self) -> Result<(), InvalidAllowedProxyCidr> {
for cidr in &mut self.allowed_proxy_cidrs {
let normalized = cidr.trim().to_owned();
normalized
.parse::<cidr::IpCidr>()
.map_err(|_| InvalidAllowedProxyCidr(normalized.clone()))?;
*cidr = normalized;
}
Ok(())
}
fn for_attached_peer(groups: Vec<String>) -> Self {
Self {
groups,
allow_relay: false,
allowed_proxy_cidrs: Vec::new(),
reusable: false,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub(crate) struct CredentialEntry {
pubkey: String,
#[serde(default)]
secret: String,
#[serde(flatten)]
grant: CredentialGrant,
expiry_unix: i64,
created_at_unix: i64,
}
@@ -68,22 +121,22 @@ impl CredentialEntry {
fn to_trusted_credential(&self) -> Option<TrustedCredentialPubkey> {
Some(TrustedCredentialPubkey {
pubkey: CredentialManager::decode_pubkey_b64(&self.pubkey)?,
groups: self.groups.clone(),
allow_relay: self.allow_relay,
groups: self.grant.groups.clone(),
allow_relay: self.grant.allow_relay,
expiry_unix: self.expiry_unix,
allowed_proxy_cidrs: self.allowed_proxy_cidrs.clone(),
reusable: Some(self.reusable),
allowed_proxy_cidrs: self.grant.allowed_proxy_cidrs.clone(),
reusable: Some(self.grant.reusable),
})
}
fn to_credential_info(&self, credential_id: &str) -> CredentialInfo {
CredentialInfo {
credential_id: credential_id.to_string(),
groups: self.groups.clone(),
allow_relay: self.allow_relay,
groups: self.grant.groups.clone(),
allow_relay: self.grant.allow_relay,
expiry_unix: self.expiry_unix,
allowed_proxy_cidrs: self.allowed_proxy_cidrs.clone(),
reusable: Some(self.reusable),
allowed_proxy_cidrs: self.grant.allowed_proxy_cidrs.clone(),
reusable: Some(self.grant.reusable),
public_key_fingerprint: CredentialManager::public_key_fingerprint(&self.pubkey)
.unwrap_or_default(),
}
@@ -97,20 +150,22 @@ impl CredentialEntry {
.try_into()
.map_err(|_| format!("credential_secret for {credential_id} must contain 32 bytes"))?;
let private = StaticSecret::from(private_bytes);
let mut allowed_proxy_cidrs = Vec::with_capacity(entry.allowed_proxy_cidrs.len());
for cidr in &entry.allowed_proxy_cidrs {
let cidr = cidr.trim();
cidr.parse::<cidr::IpCidr>()
.map_err(|_| format!("invalid allowed_proxy_cidr for {credential_id}: {cidr}"))?;
allowed_proxy_cidrs.push(cidr.to_owned());
}
let grant = CredentialGrant::new(
entry.groups.clone(),
entry.allow_relay,
entry.allowed_proxy_cidrs.clone(),
entry.reusable,
)
.map_err(|error| {
format!(
"invalid allowed_proxy_cidr for {credential_id}: {}",
error.0
)
})?;
Ok(Self {
pubkey: BASE64_STANDARD.encode(PublicKey::from(&private).as_bytes()),
secret: BASE64_STANDARD.encode(private.as_bytes()),
groups: entry.groups.clone(),
allow_relay: entry.allow_relay,
allowed_proxy_cidrs,
reusable: entry.reusable,
grant,
expiry_unix: entry.expiry_unix,
created_at_unix: 0,
})
@@ -192,7 +247,9 @@ impl CredentialManager {
pub fn from_storage(storage: Arc<dyn CredentialStorage>) -> Self {
let loaded = match storage.load() {
Ok(Some(serialized)) => serde_json::from_str(&serialized).map_err(anyhow::Error::from),
Ok(Some(serialized)) => serde_json::from_str(&serialized)
.map_err(anyhow::Error::from)
.and_then(Self::normalize_loaded_entries),
Ok(None) => Ok(HashMap::new()),
Err(error) => Err(error),
};
@@ -213,15 +270,29 @@ impl CredentialManager {
}
}
fn normalize_loaded_entries(
mut entries: HashMap<String, CredentialEntry>,
) -> anyhow::Result<HashMap<String, CredentialEntry>> {
for (credential_id, entry) in &mut entries {
entry.grant.normalize().map_err(|error| {
anyhow::anyhow!("invalid stored credential {credential_id}: {error}")
})?;
}
Ok(entries)
}
pub fn generate_credential_with_options(
&self,
groups: Vec<String>,
allow_relay: bool,
allowed_proxy_cidrs: Vec<String>,
ttl: Duration,
credential_id: Option<String>,
reusable: bool,
options: CredentialCreateOptions,
) -> Result<GeneratedCredential, String> {
let CredentialCreateOptions {
groups,
allow_relay,
allowed_proxy_cidrs,
ttl,
credential_id,
reusable,
} = options;
self.ensure_storage_available()
.map_err(|error| error.to_string())?;
let mut state = self.state.lock().unwrap();
@@ -254,15 +325,11 @@ impl CredentialManager {
}
}
};
let grant = CredentialGrant::new(groups, allow_relay, allowed_proxy_cidrs, reusable)
.map_err(|error| error.to_string())?;
let (entry, secret) = loop {
let generated = Self::build_entry(
groups.clone(),
allow_relay,
allowed_proxy_cidrs.clone(),
reusable,
ttl,
);
let generated = Self::build_entry(grant.clone(), ttl);
let public_key_in_use = updated
.values()
.chain(Self::managed_values(&state))
@@ -285,13 +352,7 @@ impl CredentialManager {
})
}
fn build_entry(
groups: Vec<String>,
allow_relay: bool,
allowed_proxy_cidrs: Vec<String>,
reusable: bool,
ttl: Duration,
) -> (CredentialEntry, String) {
fn build_entry(grant: CredentialGrant, ttl: Duration) -> (CredentialEntry, String) {
let private = StaticSecret::random_from_rng(rand::rngs::OsRng);
let public = PublicKey::from(&private);
let pubkey = BASE64_STANDARD.encode(public.as_bytes());
@@ -306,10 +367,7 @@ impl CredentialManager {
let entry = CredentialEntry {
pubkey,
secret: secret.clone(),
groups,
allow_relay,
allowed_proxy_cidrs,
reusable,
grant,
expiry_unix,
created_at_unix: now,
};
@@ -335,17 +393,11 @@ impl CredentialManager {
&self,
public_key: [u8; 32],
groups: Vec<String>,
allow_relay: bool,
allowed_proxy_cidrs: Vec<String>,
reusable: bool,
) -> Result<uuid::Uuid, String> {
let entry = CredentialEntry {
pubkey: BASE64_STANDARD.encode(public_key),
secret: String::new(),
groups,
allow_relay,
allowed_proxy_cidrs,
reusable,
grant: CredentialGrant::for_attached_peer(groups),
expiry_unix: i64::MAX,
created_at_unix: current_unix_timestamp(),
};
@@ -376,10 +428,10 @@ impl CredentialManager {
) -> Option<bool> {
let mut state = self.state.lock().unwrap();
let credential = state.ephemeral.get_mut(&credential_id)?;
if credential.groups == groups {
if credential.grant.groups == groups {
return Some(false);
}
credential.groups = groups;
credential.grant.groups = groups;
Some(true)
}
@@ -416,13 +468,12 @@ impl CredentialManager {
.try_into()
.map_err(|_| "credential_secret must contain 32 bytes".to_string())?;
let private = StaticSecret::from(private_bytes);
let grant = CredentialGrant::new(groups, allow_relay, allowed_proxy_cidrs, reusable)
.map_err(|error| error.to_string())?;
let entry = CredentialEntry {
pubkey: BASE64_STANDARD.encode(PublicKey::from(&private).as_bytes()),
secret: BASE64_STANDARD.encode(private.as_bytes()),
groups,
allow_relay,
allowed_proxy_cidrs,
reusable,
grant,
expiry_unix,
created_at_unix: current_unix_timestamp(),
};
@@ -455,10 +506,7 @@ impl CredentialManager {
let changed = state.base.get(&credential_id).is_none_or(|existing| {
existing.secret != entry.secret
|| existing.pubkey != entry.pubkey
|| existing.groups != entry.groups
|| existing.allow_relay != entry.allow_relay
|| existing.allowed_proxy_cidrs != entry.allowed_proxy_cidrs
|| existing.reusable != entry.reusable
|| existing.grant != entry.grant
|| existing.expiry_unix != entry.expiry_unix
});
if !changed {
@@ -722,7 +770,43 @@ mod tests {
let entry = CredentialEntry::from_managed(&credential).unwrap();
assert_eq!(entry.allowed_proxy_cidrs, ["10.0.0.0/24"]);
assert_eq!(entry.grant.allowed_proxy_cidrs, ["10.0.0.0/24"]);
}
#[test]
fn generated_and_imported_credentials_normalize_allowed_proxy_cidrs() {
let source = CredentialManager::new();
let generated = source
.generate_credential_with_options(CredentialCreateOptions {
groups: Vec::new(),
allow_relay: false,
allowed_proxy_cidrs: vec![" 10.0.0.0/24 ".to_owned()],
ttl: Duration::from_secs(3600),
credential_id: None,
reusable: true,
})
.unwrap();
assert_eq!(
source.list_credentials()[0].allowed_proxy_cidrs,
["10.0.0.0/24"]
);
let target = CredentialManager::new();
target
.upsert_credential(CredentialUpsertOptions {
credential_id: "imported".to_owned(),
credential_secret: generated.secret,
groups: Vec::new(),
allow_relay: false,
allowed_proxy_cidrs: vec![" 192.168.0.0/16 ".to_owned()],
expiry_unix: generated.expiry_unix,
reusable: true,
})
.unwrap();
assert_eq!(
target.list_credentials()[0].allowed_proxy_cidrs,
["192.168.0.0/16"]
);
}
impl CredentialManager {
@@ -733,14 +817,14 @@ mod tests {
allowed_proxy_cidrs: Vec<String>,
ttl: Duration,
) -> GeneratedCredential {
self.generate_credential_with_options(
self.generate_credential_with_options(CredentialCreateOptions {
groups,
allow_relay,
allowed_proxy_cidrs,
ttl,
None,
true,
)
credential_id: None,
reusable: true,
})
.unwrap()
}
@@ -752,14 +836,14 @@ mod tests {
ttl: Duration,
credential_id: Option<String>,
) -> GeneratedCredential {
self.generate_credential_with_options(
self.generate_credential_with_options(CredentialCreateOptions {
groups,
allow_relay,
allowed_proxy_cidrs,
ttl,
credential_id,
true,
)
reusable: true,
})
.unwrap()
}
}
@@ -780,6 +864,24 @@ mod tests {
}
}
fn credential_storage_with_proxy_cidr(
allowed_proxy_cidr: &str,
) -> (Arc<MemoryCredentialStorage>, String) {
let storage = Arc::new(MemoryCredentialStorage::default());
let manager = CredentialManager::from_storage(storage.clone());
let generated = manager.generate_credential(
Vec::new(),
false,
vec!["10.0.0.0/24".to_owned()],
Duration::from_secs(3600),
);
let serialized = storage.serialized.lock().unwrap().clone().unwrap();
let mut snapshot: serde_json::Value = serde_json::from_str(&serialized).unwrap();
snapshot[&generated.credential_id]["allowed_proxy_cidrs"][0] = allowed_proxy_cidr.into();
*storage.serialized.lock().unwrap() = Some(serde_json::to_string(&snapshot).unwrap());
(storage, generated.credential_id)
}
struct FailOnceCredentialStorage {
serialized: Mutex<Option<String>>,
fail_next_store: Mutex<bool>,
@@ -887,14 +989,14 @@ mod tests {
fn upsert_credential_preserves_key_attributes_and_storage() {
let source = CredentialManager::new();
let generated = source
.generate_credential_with_options(
vec!["users".to_string()],
false,
vec!["10.0.0.0/8".to_string()],
Duration::from_secs(3600),
Some("shared-id".to_string()),
false,
)
.generate_credential_with_options(CredentialCreateOptions {
groups: vec!["users".to_string()],
allow_relay: false,
allowed_proxy_cidrs: vec!["10.0.0.0/8".to_string()],
ttl: Duration::from_secs(3600),
credential_id: Some("shared-id".to_string()),
reusable: false,
})
.unwrap();
let source_info = source.list_credentials().remove(0);
let options = CredentialUpsertOptions {
@@ -1011,6 +1113,67 @@ mod tests {
assert!(reloaded.list_credentials().is_empty());
}
#[test]
fn stored_credentials_normalize_allowed_proxy_cidrs_on_load() {
let (storage, credential_id) = credential_storage_with_proxy_cidr(" 10.0.0.0/24 ");
let manager = CredentialManager::from_storage(storage);
assert_eq!(manager.list_credentials()[0].credential_id, credential_id);
assert_eq!(
manager.list_credentials()[0].allowed_proxy_cidrs,
["10.0.0.0/24"]
);
}
#[test]
fn stored_credentials_with_invalid_proxy_cidr_fail_closed() {
let (storage, credential_id) = credential_storage_with_proxy_cidr("not-a-cidr");
let manager = CredentialManager::from_storage(storage);
assert!(manager.list_credentials().is_empty());
let error = manager
.install_initial_managed_credentials(&[])
.unwrap_err();
assert!(error.contains(&credential_id), "{error}");
assert!(error.contains("invalid allowed_proxy_cidr"), "{error}");
}
#[test]
fn credential_storage_schema_remains_flat() {
let storage = Arc::new(MemoryCredentialStorage::default());
let manager = CredentialManager::from_storage(storage.clone());
manager.generate_credential(
vec!["ops".to_owned()],
true,
vec!["10.0.0.0/24".to_owned()],
Duration::from_secs(3600),
);
let serialized = storage.serialized.lock().unwrap().clone().unwrap();
let mut snapshot: serde_json::Value = serde_json::from_str(&serialized).unwrap();
let entry = snapshot.as_object().unwrap().values().next().unwrap();
assert!(entry.get("grant").is_none());
assert_eq!(entry["groups"][0], "ops");
assert_eq!(entry["allowed_proxy_cidrs"][0], "10.0.0.0/24");
assert_eq!(entry["allow_relay"], true);
assert_eq!(entry["reusable"], true);
snapshot
.as_object_mut()
.unwrap()
.values_mut()
.next()
.unwrap()
.as_object_mut()
.unwrap()
.remove("reusable");
let legacy: HashMap<String, CredentialEntry> = serde_json::from_value(snapshot).unwrap();
assert!(legacy.values().next().unwrap().grant.reusable);
}
#[test]
fn malformed_storage_is_fail_closed() {
let storage = Arc::new(MemoryCredentialStorage {
@@ -1031,7 +1194,7 @@ mod tests {
let public = *PublicKey::from(&private).as_bytes();
let credential_id = manager
.register_ephemeral_credential(public, vec!["ops".to_owned()], false, Vec::new(), false)
.register_ephemeral_credential(public, vec!["ops".to_owned()])
.unwrap();
assert!(manager.is_pubkey_trusted(&public));
@@ -1096,25 +1259,19 @@ mod tests {
.unwrap();
let error = manager
.generate_credential_with_options(
Vec::new(),
false,
Vec::new(),
Duration::from_secs(60),
Some("pending".to_owned()),
true,
)
.generate_credential_with_options(CredentialCreateOptions {
groups: Vec::new(),
allow_relay: false,
allowed_proxy_cidrs: Vec::new(),
ttl: Duration::from_secs(60),
credential_id: Some("pending".to_owned()),
reusable: true,
})
.unwrap_err();
assert!(error.contains("managed by configuration"));
assert!(
manager
.register_ephemeral_credential(
*public.as_bytes(),
Vec::new(),
false,
Vec::new(),
false,
)
.register_ephemeral_credential(*public.as_bytes(), Vec::new())
.is_err()
);
assert!(!manager.is_pubkey_trusted(public.as_bytes()));
@@ -1122,13 +1279,7 @@ mod tests {
drop(replacement);
assert!(
manager
.register_ephemeral_credential(
*public.as_bytes(),
Vec::new(),
false,
Vec::new(),
false,
)
.register_ephemeral_credential(*public.as_bytes(), Vec::new())
.is_ok()
);
}
@@ -1146,14 +1297,14 @@ mod tests {
.unwrap();
let error = manager
.generate_credential_with_options(
Vec::new(),
false,
Vec::new(),
Duration::from_secs(60),
Some("managed".to_owned()),
true,
)
.generate_credential_with_options(CredentialCreateOptions {
groups: Vec::new(),
allow_relay: false,
allowed_proxy_cidrs: Vec::new(),
ttl: Duration::from_secs(60),
credential_id: Some("managed".to_owned()),
reusable: true,
})
.unwrap_err();
assert!(error.contains("managed by configuration"));
+1 -10
View File
@@ -1166,19 +1166,10 @@ impl PeerManagerCore {
&self,
public_key: [u8; 32],
groups: Vec<String>,
allow_relay: bool,
allowed_proxy_cidrs: Vec<String>,
reusable: bool,
) -> anyhow::Result<uuid::Uuid> {
let credential_id = self
.credential_manager()
.register_ephemeral_credential(
public_key,
groups,
allow_relay,
allowed_proxy_cidrs,
reusable,
)
.register_ephemeral_credential(public_key, groups)
.map_err(anyhow::Error::msg)?;
self.notify_credential_changed();
Ok(credential_id)