blob: 7f660234bfebf000e4d9cf0948b35c31a515a0df [file]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
use maka_agent_facts::configuration::*;
use maka_config::ConfigurationStore;
use maka_event_log::root::{RootNamespaces, RootOwner};
use serde_json::json;
use std::sync::Arc;
#[tokio::test]
async fn plugin_vault_isolates_namespaces_and_retains_cas_through_deletion_and_reopen() {
use maka_capabilities::credentials::Write;
use maka_capabilities::credentials::WriteResult;
use maka_capabilities::storage::Namespace;
use maka_plugin_core::scope::Scope;
let temp = tempfile::tempdir().unwrap();
let owner = Arc::new(
RootOwner::create(
&temp.path().join("root"),
&RootNamespaces {
ownership: temp.path().join("owners"),
control: temp.path().join("control"),
},
)
.unwrap(),
);
let namespace = Namespace::new("example", Scope::Profile).unwrap();
let store = ConfigurationStore::for_root(owner.clone()).await.unwrap();
let write = |expected_revision, secret: Option<&str>| Write {
key: "token".into(),
expected_revision,
secret: secret.map(str::to_owned),
};
assert_eq!(
store
.write_plugin_credential(namespace.clone(), write(None, Some("first")))
.await
.unwrap(),
WriteResult::Written { revision: 1 }
);
for other in [
Namespace::new("other", Scope::Profile).unwrap(),
Namespace::new("example", Scope::Session("session".into())).unwrap(),
] {
assert!(
store
.plugin_credential(other, "token".into())
.await
.unwrap()
.is_none()
);
}
let (left, right) = tokio::join!(
store.write_plugin_credential(namespace.clone(), write(Some(1), Some("left"))),
store.write_plugin_credential(namespace.clone(), write(Some(1), Some("right"))),
);
let results = [left.unwrap(), right.unwrap()];
assert_eq!(
results
.iter()
.filter(|value| **value == WriteResult::Written { revision: 2 })
.count(),
1
);
assert_eq!(
results
.iter()
.filter(|value| **value == WriteResult::Conflict { actual: Some(2) })
.count(),
1
);
assert_eq!(
store
.write_plugin_credential(namespace.clone(), write(Some(2), None))
.await
.unwrap(),
WriteResult::Written { revision: 3 }
);
store.close().await.unwrap();
let reopened = ConfigurationStore::for_root(owner).await.unwrap();
let tombstone = reopened
.plugin_credential(namespace.clone(), "token".into())
.await
.unwrap()
.unwrap();
assert_eq!(tombstone.revision, 3);
assert!(tombstone.secret.is_none());
for stale in [None, Some(2)] {
assert_eq!(
reopened
.write_plugin_credential(namespace.clone(), write(stale, Some("stale")))
.await
.unwrap(),
WriteResult::Conflict { actual: Some(3) }
);
}
assert_eq!(
reopened
.write_plugin_credential(namespace, write(Some(3), Some("renewed")))
.await
.unwrap(),
WriteResult::Written { revision: 4 }
);
reopened.close().await.unwrap();
}
#[tokio::test]
async fn catalog_and_vault_keep_independent_cas_and_private_material_across_reopen() {
let temp = tempfile::tempdir().unwrap();
let root = temp.path().join("root");
let namespaces = RootNamespaces {
ownership: temp.path().join("owners"),
control: temp.path().join("control"),
};
let owner = Arc::new(RootOwner::create(&root, &namespaces).unwrap());
let store = ConfigurationStore::for_root(owner.clone()).await.unwrap();
let create: CreateCatalogConnectionInput = serde_json::from_value(json!({
"expectedCatalogRevision":0,
"connection":{"slug":"local-fixture","name":"Local fixture","provider":{"packageId":"external","entryId":"api","scope":"profile","name":"api"},
"configuration":{"baseUrl":"http://127.0.0.1:18080/v1"},"enabled":true,"enabledModelIds":["fixture-model"]}
})).unwrap();
let created = store.create_connection(create.clone()).await.unwrap();
let CatalogMutationResult::Committed {
catalog_revision: 1,
connection: Some(basis),
} = created
else {
panic!("expected first connection commit");
};
assert!(matches!(
store.create_connection(create).await.unwrap(),
CatalogMutationResult::RevisionConflict {
expected_revision: 0,
actual_revision: 1
}
));
let locator = CredentialLocator::Connection {
connection_id: basis.connection_id.clone(),
kind: ConnectionCredentialKind::RequestHeaders,
};
let expected_connection = ConnectionCredentialTarget {
connection_id: basis.connection_id.clone(),
revision: 1,
slug: "local-fixture".into(),
provider: serde_json::from_value(
json!({"packageId":"external","entryId":"api","scope":"profile","name":"api"}),
)
.unwrap(),
configuration: json!({"baseUrl":"http://127.0.0.1:18080/v1"}),
};
let input = SetCredentialInput {
locator: locator.clone(),
expected: None,
expected_connection: Some(expected_connection.clone()),
secret: r#"{"X-Fixture":"dummy-private-fixture"}"#.into(),
};
let saved = store.set_credential(input.clone(), 42).await.unwrap();
let CredentialMutationResult::Committed {
vault_revision: 1,
status,
} = saved
else {
panic!("expected first credential commit");
};
let CredentialState::Configured {
credential_id,
revision: 1,
updated_at: 42,
} = &status.state
else {
panic!("expected configured credential metadata");
};
assert!(matches!(
store.set_credential(input.clone(), 43).await.unwrap(),
CredentialMutationResult::CredentialStale {
expected: None,
actual: Some(_)
}
));
let mut stale_target = input;
stale_target
.expected_connection
.as_mut()
.unwrap()
.configuration = json!({"baseUrl":"http://127.0.0.1:18081/v1"});
assert!(matches!(
store.set_credential(stale_target, 44).await.unwrap(),
CredentialMutationResult::ConnectionStale { .. }
));
let target = ConnectionTarget {
connection_id: basis.connection_id.clone(),
model_id: "fixture-model".into(),
};
assert!(matches!(
store
.set_default_target(SetDefaultConnectionTargetInput {
expected_catalog_revision: 1,
target: Some(target.clone()),
})
.await
.unwrap(),
CatalogMutationResult::Committed {
catalog_revision: 2,
connection: None
}
));
let catalog = store.catalog().await.unwrap();
assert_eq!(catalog.default_target, Some(target.clone()));
assert!(
catalog.connections[0].models.is_empty(),
"manual enabled model needs no fabricated discovery"
);
assert!(
!serde_json::to_string(&catalog)
.unwrap()
.contains("dummy-private-fixture")
);
assert!(
!serde_json::to_string(&status)
.unwrap()
.contains("dummy-private-fixture")
);
assert_eq!(
store
.credential_secret(&locator, Some(&expected_connection))
.await
.unwrap()
.as_deref(),
Some(r#"{"X-Fixture":"dummy-private-fixture"}"#)
);
#[cfg(unix)]
{
use std::os::unix::fs::PermissionsExt;
assert_eq!(
std::fs::metadata(root.join("configuration-rust.sqlite"))
.unwrap()
.permissions()
.mode()
& 0o077,
0
);
}
drop(owner);
assert!(
RootOwner::open(&root, &namespaces).is_err(),
"configuration store retains root authority"
);
store.close().await.unwrap();
let owner = Arc::new(RootOwner::open(&root, &namespaces).unwrap());
let store = ConfigurationStore::for_root(owner).await.unwrap();
assert_eq!(store.catalog().await.unwrap(), catalog);
let CredentialVaultQueryResult::Status { status: reopened } =
store.credential_status(locator.clone()).await.unwrap()
else {
panic!("expected existing connection");
};
assert_eq!(reopened, status);
assert_eq!(
store
.credential_secret(&locator, Some(&expected_connection))
.await
.unwrap()
.as_deref(),
Some(r#"{"X-Fixture":"dummy-private-fixture"}"#)
);
let deleted = store
.delete_credential(DeleteCredentialInput {
expected_connection: Some(expected_connection.clone()),
expected: CredentialVersionBasis {
locator: locator.clone(),
credential_id: credential_id.clone(),
revision: 1,
},
})
.await
.unwrap();
assert!(matches!(
deleted,
CredentialMutationResult::Committed {
vault_revision: 2,
status: CredentialStatus {
state: CredentialState::Absent,
..
}
}
));
assert!(
store
.credential_secret(&locator, Some(&expected_connection))
.await
.unwrap()
.is_none()
);
assert_eq!(
store.catalog().await.unwrap().revision,
2,
"vault writes do not invent catalog changes"
);
}