Усилить безопасность и надёжность выполнения операций
This commit is contained in:
@@ -21,6 +21,9 @@ pub async fn confirm_operation(
|
||||
if !safety.class.requires_confirmation() {
|
||||
return Ok(());
|
||||
}
|
||||
if request_context.is_some_and(RuntimeRequestContext::approval_granted) {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let Some(store) = store else {
|
||||
return Err(RuntimeError::ConfirmationStoreUnavailable {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use crank_adapter_rest::RestAdapter;
|
||||
use crank_adapter_rest::{OutboundHttpPolicy, RestAdapter, RestAdapterError};
|
||||
use crank_core::{
|
||||
AdapterRegistry, CoordinationStateStore, MeteringSink, NoopMeteringSink, ResponseCacheStore,
|
||||
SharedMeteringSink, SharedProtocolAdapter,
|
||||
@@ -73,3 +73,13 @@ pub fn community_default() -> RuntimeExecutorBuilder {
|
||||
RuntimeExecutorBuilder::new()
|
||||
.register_adapter(Arc::new(RestAdapter::new()) as SharedProtocolAdapter)
|
||||
}
|
||||
|
||||
pub fn community_from_env() -> Result<RuntimeExecutorBuilder, RestAdapterError> {
|
||||
Ok(RuntimeExecutorBuilder::new()
|
||||
.register_adapter(Arc::new(RestAdapter::from_env()?) as SharedProtocolAdapter))
|
||||
}
|
||||
|
||||
pub fn community_with_outbound_policy(policy: OutboundHttpPolicy) -> RuntimeExecutorBuilder {
|
||||
RuntimeExecutorBuilder::new()
|
||||
.register_adapter(Arc::new(RestAdapter::with_policy(policy)) as SharedProtocolAdapter)
|
||||
}
|
||||
|
||||
@@ -23,9 +23,12 @@ pub use cache::{
|
||||
pub use cache_factory::{
|
||||
BuiltinCacheBackendFactory, CacheBackendFactory, SharedCacheBackendFactory,
|
||||
};
|
||||
pub use crank_adapter_rest::OutboundHttpPolicy;
|
||||
pub use error::RuntimeError;
|
||||
pub use executor::{RuntimeExecutionRequest, RuntimeExecutor};
|
||||
pub use executor_builder::{RuntimeExecutorBuilder, community_default};
|
||||
pub use executor_builder::{
|
||||
RuntimeExecutorBuilder, community_default, community_from_env, community_with_outbound_policy,
|
||||
};
|
||||
pub use limits::{RuntimeLimits, RuntimeLimitsConfigError};
|
||||
pub use model::{AdapterResponse, PreparedRequest, RuntimeOperation};
|
||||
pub use rate_limit::{
|
||||
|
||||
@@ -15,6 +15,7 @@ pub struct RuntimeRequestContext {
|
||||
pub response_cache_scope: Option<ResponseCacheScope>,
|
||||
pub metering_context: Option<MeteringContext>,
|
||||
pub confirmation_token: Option<String>,
|
||||
pub approval_granted: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
@@ -32,6 +33,7 @@ impl RuntimeRequestContext {
|
||||
response_cache_scope: None,
|
||||
metering_context: None,
|
||||
confirmation_token: None,
|
||||
approval_granted: false,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -89,6 +91,15 @@ impl RuntimeRequestContext {
|
||||
pub fn confirmation_token(&self) -> Option<&str> {
|
||||
self.confirmation_token.as_deref()
|
||||
}
|
||||
|
||||
pub fn with_approval_granted(mut self) -> Self {
|
||||
self.approval_granted = true;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn approval_granted(&self) -> bool {
|
||||
self.approval_granted
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ResponseCacheScope> for crank_core::ResponseCacheScope {
|
||||
@@ -154,6 +165,7 @@ mod tests {
|
||||
assert_eq!(context.correlation_id, "req_123");
|
||||
assert!(context.response_cache_scope.is_none());
|
||||
assert!(context.confirmation_token.is_none());
|
||||
assert!(!context.approval_granted());
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -187,4 +199,11 @@ mod tests {
|
||||
|
||||
assert_eq!(context.confirmation_token(), Some("ct_123"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn records_prior_human_approval() {
|
||||
let context = RuntimeRequestContext::from_request_id("req_123").with_approval_granted();
|
||||
|
||||
assert!(context.approval_granted());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,6 +75,18 @@ async fn destructive_operation_requires_single_use_confirmation() {
|
||||
RuntimeError::InvalidConfirmationToken { .. }
|
||||
));
|
||||
assert_eq!(call_count.load(Ordering::SeqCst), 1);
|
||||
|
||||
let approved_context = context.with_approval_granted();
|
||||
let approved = executor
|
||||
.execute_with_context(
|
||||
&operation,
|
||||
&json!({ "order_id": "ord_123" }),
|
||||
Some(&approved_context),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(approved, json!({ "deleted": true }));
|
||||
assert_eq!(call_count.load(Ordering::SeqCst), 2);
|
||||
}
|
||||
|
||||
struct CountingAdapter {
|
||||
|
||||
Reference in New Issue
Block a user