mirror of
https://github.com/pchuan98/codex.git
synced 2026-07-01 00:31:56 +08:00
device-key: clean up unused crate (#21487)
This commit is contained in:
@@ -64,7 +64,6 @@ use crate::outgoing_message::OutgoingMessage;
|
||||
use crate::outgoing_message::OutgoingMessageSender;
|
||||
use crate::outgoing_message::QueuedOutgoingMessage;
|
||||
use crate::transport::CHANNEL_CAPACITY;
|
||||
use crate::transport::ConnectionOrigin;
|
||||
use crate::transport::OutboundConnectionState;
|
||||
use crate::transport::route_outgoing_envelope;
|
||||
use codex_analytics::AppServerRpcTransport;
|
||||
@@ -435,7 +434,7 @@ async fn start_uninitialized(args: InProcessStartArgs) -> IoResult<InProcessClie
|
||||
plugin_startup_tasks: crate::PluginStartupTasks::Start,
|
||||
}));
|
||||
let mut thread_created_rx = processor.thread_created_receiver();
|
||||
let session = Arc::new(ConnectionSessionState::new(ConnectionOrigin::InProcess));
|
||||
let session = Arc::new(ConnectionSessionState::new());
|
||||
let mut listen_for_threads = true;
|
||||
|
||||
loop {
|
||||
@@ -722,14 +721,8 @@ async fn start_uninitialized(args: InProcessStartArgs) -> IoResult<InProcessClie
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::error_code::INVALID_REQUEST_ERROR_CODE;
|
||||
use codex_app_server_protocol::ClientInfo;
|
||||
use codex_app_server_protocol::ConfigRequirementsReadResponse;
|
||||
use codex_app_server_protocol::DeviceKeyPublicParams;
|
||||
use codex_app_server_protocol::DeviceKeySignParams;
|
||||
use codex_app_server_protocol::DeviceKeySignPayload;
|
||||
use codex_app_server_protocol::RemoteControlClientConnectionAudience;
|
||||
use codex_app_server_protocol::RemoteControlClientEnrollmentAudience;
|
||||
use codex_app_server_protocol::SessionSource as ApiSessionSource;
|
||||
use codex_app_server_protocol::ThreadStartParams;
|
||||
use codex_app_server_protocol::ThreadStartResponse;
|
||||
@@ -821,87 +814,6 @@ mod tests {
|
||||
.expect("in-process runtime should shutdown cleanly");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn in_process_allows_device_key_requests_to_reach_device_key_processor() {
|
||||
let client = start_test_client(SessionSource::Cli).await;
|
||||
const MALFORMED_KEY_ID_MESSAGE: &str = concat!(
|
||||
"invalid device key payload: keyId must be dk_hse_, dk_tpm_, or dk_osn_ ",
|
||||
"followed by unpadded base64url-encoded 32 bytes"
|
||||
);
|
||||
let requests = [
|
||||
(
|
||||
ClientRequest::DeviceKeyPublic {
|
||||
request_id: RequestId::Integer(11),
|
||||
params: DeviceKeyPublicParams {
|
||||
key_id: String::new(),
|
||||
},
|
||||
},
|
||||
MALFORMED_KEY_ID_MESSAGE,
|
||||
),
|
||||
(
|
||||
ClientRequest::DeviceKeySign {
|
||||
request_id: RequestId::Integer(12),
|
||||
params: DeviceKeySignParams {
|
||||
key_id: String::new(),
|
||||
payload: DeviceKeySignPayload::RemoteControlClientConnection {
|
||||
nonce: "nonce-123".to_string(),
|
||||
audience:
|
||||
RemoteControlClientConnectionAudience::RemoteControlClientWebsocket,
|
||||
session_id: "wssess_123".to_string(),
|
||||
target_origin: "https://chatgpt.com".to_string(),
|
||||
target_path: "/api/codex/remote/control/client".to_string(),
|
||||
account_user_id: "acct_123".to_string(),
|
||||
client_id: "cli_123".to_string(),
|
||||
token_expires_at: 4_102_444_800,
|
||||
token_sha256_base64url: "47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU"
|
||||
.to_string(),
|
||||
scopes: vec!["remote_control_controller_websocket".to_string()],
|
||||
},
|
||||
},
|
||||
},
|
||||
MALFORMED_KEY_ID_MESSAGE,
|
||||
),
|
||||
(
|
||||
ClientRequest::DeviceKeySign {
|
||||
request_id: RequestId::Integer(13),
|
||||
params: DeviceKeySignParams {
|
||||
key_id: String::new(),
|
||||
payload: DeviceKeySignPayload::RemoteControlClientEnrollment {
|
||||
nonce: "nonce-123".to_string(),
|
||||
audience:
|
||||
RemoteControlClientEnrollmentAudience::RemoteControlClientEnrollment,
|
||||
challenge_id: "rch_123".to_string(),
|
||||
target_origin: "https://chatgpt.com".to_string(),
|
||||
target_path: "/wham/remote/control/client/enroll".to_string(),
|
||||
account_user_id: "acct_123".to_string(),
|
||||
client_id: "cli_123".to_string(),
|
||||
device_identity_sha256_base64url:
|
||||
"47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU".to_string(),
|
||||
challenge_expires_at: 4_102_444_800,
|
||||
},
|
||||
},
|
||||
},
|
||||
MALFORMED_KEY_ID_MESSAGE,
|
||||
),
|
||||
];
|
||||
|
||||
for (request, expected_message) in requests {
|
||||
let error = client
|
||||
.request(request)
|
||||
.await
|
||||
.expect("request transport should work")
|
||||
.expect_err("request should be rejected");
|
||||
|
||||
assert_eq!(error.code, INVALID_REQUEST_ERROR_CODE);
|
||||
assert_eq!(error.message, expected_message);
|
||||
}
|
||||
|
||||
client
|
||||
.shutdown()
|
||||
.await
|
||||
.expect("in-process runtime should shutdown cleanly");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn in_process_start_uses_requested_session_source_for_thread_start() {
|
||||
for (requested_source, expected_source) in [
|
||||
|
||||
@@ -17,7 +17,6 @@ use crate::request_processors::AppsRequestProcessor;
|
||||
use crate::request_processors::CatalogRequestProcessor;
|
||||
use crate::request_processors::CommandExecRequestProcessor;
|
||||
use crate::request_processors::ConfigRequestProcessor;
|
||||
use crate::request_processors::DeviceKeyRequestProcessor;
|
||||
use crate::request_processors::ExternalAgentConfigRequestProcessor;
|
||||
use crate::request_processors::FeedbackRequestProcessor;
|
||||
use crate::request_processors::FsRequestProcessor;
|
||||
@@ -37,7 +36,6 @@ use crate::request_serialization::RequestSerializationQueueKey;
|
||||
use crate::request_serialization::RequestSerializationQueues;
|
||||
use crate::thread_state::ThreadStateManager;
|
||||
use crate::transport::AppServerTransport;
|
||||
use crate::transport::ConnectionOrigin;
|
||||
use crate::transport::RemoteControlHandle;
|
||||
use async_trait::async_trait;
|
||||
use codex_analytics::AnalyticsEventsClient;
|
||||
@@ -160,7 +158,6 @@ pub(crate) struct MessageProcessor {
|
||||
command_exec_processor: CommandExecRequestProcessor,
|
||||
process_exec_processor: ProcessExecRequestProcessor,
|
||||
config_processor: ConfigRequestProcessor,
|
||||
device_key_processor: DeviceKeyRequestProcessor,
|
||||
external_agent_config_processor: ExternalAgentConfigRequestProcessor,
|
||||
feedback_processor: FeedbackRequestProcessor,
|
||||
fs_processor: FsRequestProcessor,
|
||||
@@ -179,7 +176,6 @@ pub(crate) struct MessageProcessor {
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct ConnectionSessionState {
|
||||
origin: ConnectionOrigin,
|
||||
pub(crate) rpc_gate: Arc<ConnectionRpcGate>,
|
||||
initialized: OnceLock<InitializedConnectionSessionState>,
|
||||
}
|
||||
@@ -194,14 +190,13 @@ pub(crate) struct InitializedConnectionSessionState {
|
||||
|
||||
impl Default for ConnectionSessionState {
|
||||
fn default() -> Self {
|
||||
Self::new(ConnectionOrigin::WebSocket)
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl ConnectionSessionState {
|
||||
pub(crate) fn new(origin: ConnectionOrigin) -> Self {
|
||||
pub(crate) fn new() -> Self {
|
||||
Self {
|
||||
origin,
|
||||
rpc_gate: Arc::new(ConnectionRpcGate::new()),
|
||||
initialized: OnceLock::new(),
|
||||
}
|
||||
@@ -211,10 +206,6 @@ impl ConnectionSessionState {
|
||||
self.initialized.get().is_some()
|
||||
}
|
||||
|
||||
fn allows_device_key_requests(&self) -> bool {
|
||||
self.origin.allows_device_key_requests()
|
||||
}
|
||||
|
||||
pub(crate) fn experimental_api_enabled(&self) -> bool {
|
||||
self.initialized
|
||||
.get()
|
||||
@@ -397,7 +388,7 @@ impl MessageProcessor {
|
||||
thread_watch_manager.clone(),
|
||||
Arc::clone(&thread_list_state_permit),
|
||||
thread_goal_processor.clone(),
|
||||
state_db.clone(),
|
||||
state_db,
|
||||
);
|
||||
let turn_processor = TurnRequestProcessor::new(
|
||||
auth_manager.clone(),
|
||||
@@ -441,7 +432,6 @@ impl MessageProcessor {
|
||||
arg0_paths,
|
||||
config.codex_home.to_path_buf(),
|
||||
);
|
||||
let device_key_processor = DeviceKeyRequestProcessor::new(outgoing.clone(), state_db);
|
||||
let fs_processor = FsRequestProcessor::new(
|
||||
thread_manager
|
||||
.environment_manager()
|
||||
@@ -463,7 +453,6 @@ impl MessageProcessor {
|
||||
command_exec_processor,
|
||||
process_exec_processor,
|
||||
config_processor,
|
||||
device_key_processor,
|
||||
external_agent_config_processor,
|
||||
feedback_processor,
|
||||
fs_processor,
|
||||
@@ -770,7 +759,6 @@ impl MessageProcessor {
|
||||
let serialization_scope = codex_request.serialization_scope();
|
||||
let app_server_client_name = session.app_server_client_name().map(str::to_string);
|
||||
let client_version = session.client_version().map(str::to_string);
|
||||
let device_key_requests_allowed = session.allows_device_key_requests();
|
||||
let error_request_id = connection_request_id.clone();
|
||||
let rpc_gate = Arc::clone(&session.rpc_gate);
|
||||
let processor = Arc::clone(self);
|
||||
@@ -786,7 +774,6 @@ impl MessageProcessor {
|
||||
request_context,
|
||||
app_server_client_name,
|
||||
client_version,
|
||||
device_key_requests_allowed,
|
||||
)
|
||||
.await;
|
||||
if let Err(error) = result {
|
||||
@@ -816,7 +803,6 @@ impl MessageProcessor {
|
||||
request_context: RequestContext,
|
||||
app_server_client_name: Option<String>,
|
||||
client_version: Option<String>,
|
||||
device_key_requests_allowed: bool,
|
||||
) -> Result<(), JSONRPCErrorError> {
|
||||
let connection_id = connection_request_id.connection_id;
|
||||
let request_id = ConnectionRequestId {
|
||||
@@ -864,30 +850,6 @@ impl MessageProcessor {
|
||||
.config_requirements_read()
|
||||
.await
|
||||
.map(|response| Some(response.into())),
|
||||
ClientRequest::DeviceKeyCreate { params, .. } => {
|
||||
self.device_key_processor.create(
|
||||
request_id.clone(),
|
||||
params,
|
||||
device_key_requests_allowed,
|
||||
);
|
||||
Ok(None)
|
||||
}
|
||||
ClientRequest::DeviceKeyPublic { params, .. } => {
|
||||
self.device_key_processor.public(
|
||||
request_id.clone(),
|
||||
params,
|
||||
device_key_requests_allowed,
|
||||
);
|
||||
Ok(None)
|
||||
}
|
||||
ClientRequest::DeviceKeySign { params, .. } => {
|
||||
self.device_key_processor.sign(
|
||||
request_id.clone(),
|
||||
params,
|
||||
device_key_requests_allowed,
|
||||
);
|
||||
Ok(None)
|
||||
}
|
||||
ClientRequest::FsReadFile { params, .. } => self
|
||||
.fs_processor
|
||||
.read_file(params)
|
||||
|
||||
@@ -6,21 +6,16 @@ use crate::config_manager::ConfigManager;
|
||||
use crate::outgoing_message::ConnectionId;
|
||||
use crate::outgoing_message::OutgoingMessageSender;
|
||||
use crate::transport::AppServerTransport;
|
||||
use crate::transport::ConnectionOrigin;
|
||||
use anyhow::Result;
|
||||
use app_test_support::create_mock_responses_server_repeating_assistant;
|
||||
use app_test_support::write_mock_responses_config_toml;
|
||||
use codex_analytics::AppServerRpcTransport;
|
||||
use codex_app_server_protocol::ClientInfo;
|
||||
use codex_app_server_protocol::ClientRequest;
|
||||
use codex_app_server_protocol::DeviceKeySignParams;
|
||||
use codex_app_server_protocol::DeviceKeySignPayload;
|
||||
use codex_app_server_protocol::InitializeCapabilities;
|
||||
use codex_app_server_protocol::InitializeParams;
|
||||
use codex_app_server_protocol::InitializeResponse;
|
||||
use codex_app_server_protocol::JSONRPCErrorError;
|
||||
use codex_app_server_protocol::JSONRPCRequest;
|
||||
use codex_app_server_protocol::RemoteControlClientConnectionAudience;
|
||||
use codex_app_server_protocol::RequestId;
|
||||
use codex_app_server_protocol::ThreadStartParams;
|
||||
use codex_app_server_protocol::ThreadStartResponse;
|
||||
@@ -121,10 +116,6 @@ struct TracingHarness {
|
||||
|
||||
impl TracingHarness {
|
||||
async fn new() -> Result<Self> {
|
||||
Self::new_with_origin(ConnectionOrigin::WebSocket).await
|
||||
}
|
||||
|
||||
async fn new_with_origin(origin: ConnectionOrigin) -> Result<Self> {
|
||||
let server = create_mock_responses_server_repeating_assistant("Done").await;
|
||||
let codex_home = TempDir::new()?;
|
||||
let config = Arc::new(build_test_config(codex_home.path(), &server.uri()).await?);
|
||||
@@ -137,7 +128,7 @@ impl TracingHarness {
|
||||
_codex_home: codex_home,
|
||||
processor,
|
||||
outgoing_rx,
|
||||
session: Arc::new(ConnectionSessionState::new(origin)),
|
||||
session: Arc::new(ConnectionSessionState::new()),
|
||||
tracing,
|
||||
};
|
||||
|
||||
@@ -196,29 +187,6 @@ impl TracingHarness {
|
||||
read_response(&mut self.outgoing_rx, request_id).await
|
||||
}
|
||||
|
||||
async fn request_error(
|
||||
&mut self,
|
||||
request: ClientRequest,
|
||||
trace: Option<W3cTraceContext>,
|
||||
) -> JSONRPCErrorError {
|
||||
let request_id = match request.id() {
|
||||
RequestId::Integer(request_id) => *request_id,
|
||||
request_id => panic!("expected integer request id in test harness, got {request_id:?}"),
|
||||
};
|
||||
let mut request = request_from_client_request(request);
|
||||
request.trace = trace;
|
||||
|
||||
self.processor
|
||||
.process_request(
|
||||
TEST_CONNECTION_ID,
|
||||
request,
|
||||
&AppServerTransport::Stdio,
|
||||
Arc::clone(&self.session),
|
||||
)
|
||||
.await;
|
||||
read_error(&mut self.outgoing_rx, request_id).await
|
||||
}
|
||||
|
||||
async fn start_thread(
|
||||
&mut self,
|
||||
request_id: i64,
|
||||
@@ -485,36 +453,6 @@ async fn read_response<T: serde::de::DeserializeOwned>(
|
||||
}
|
||||
}
|
||||
|
||||
async fn read_error(
|
||||
outgoing_rx: &mut mpsc::Receiver<crate::outgoing_message::OutgoingEnvelope>,
|
||||
request_id: i64,
|
||||
) -> JSONRPCErrorError {
|
||||
loop {
|
||||
let envelope = tokio::time::timeout(std::time::Duration::from_secs(5), outgoing_rx.recv())
|
||||
.await
|
||||
.expect("timed out waiting for error")
|
||||
.expect("outgoing channel closed");
|
||||
let crate::outgoing_message::OutgoingEnvelope::ToConnection {
|
||||
connection_id,
|
||||
message,
|
||||
..
|
||||
} = envelope
|
||||
else {
|
||||
continue;
|
||||
};
|
||||
if connection_id != TEST_CONNECTION_ID {
|
||||
continue;
|
||||
}
|
||||
let crate::outgoing_message::OutgoingMessage::Error(error) = message else {
|
||||
continue;
|
||||
};
|
||||
if error.id != RequestId::Integer(request_id) {
|
||||
continue;
|
||||
}
|
||||
return error.error;
|
||||
}
|
||||
}
|
||||
|
||||
async fn read_thread_started_notification(
|
||||
outgoing_rx: &mut mpsc::Receiver<crate::outgoing_message::OutgoingEnvelope>,
|
||||
) {
|
||||
@@ -693,47 +631,6 @@ fn thread_start_jsonrpc_span_exports_server_span_and_parents_children() -> Resul
|
||||
)
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "current_thread")]
|
||||
#[serial(app_server_tracing)]
|
||||
async fn remote_control_origin_rejects_device_key_requests() -> Result<()> {
|
||||
let mut harness = TracingHarness::new_with_origin(ConnectionOrigin::RemoteControl).await?;
|
||||
|
||||
let error = harness
|
||||
.request_error(
|
||||
ClientRequest::DeviceKeySign {
|
||||
request_id: RequestId::Integer(20_004),
|
||||
params: DeviceKeySignParams {
|
||||
key_id: "dk_123".to_string(),
|
||||
payload: DeviceKeySignPayload::RemoteControlClientConnection {
|
||||
nonce: "nonce-123".to_string(),
|
||||
audience:
|
||||
RemoteControlClientConnectionAudience::RemoteControlClientWebsocket,
|
||||
session_id: "wssess_123".to_string(),
|
||||
target_origin: "https://chatgpt.com".to_string(),
|
||||
target_path: "/api/codex/remote/control/client".to_string(),
|
||||
account_user_id: "acct_123".to_string(),
|
||||
client_id: "cli_123".to_string(),
|
||||
token_expires_at: 4_102_444_800,
|
||||
token_sha256_base64url: "47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU"
|
||||
.to_string(),
|
||||
scopes: vec!["remote_control_controller_websocket".to_string()],
|
||||
},
|
||||
},
|
||||
},
|
||||
/*trace*/ None,
|
||||
)
|
||||
.await;
|
||||
|
||||
assert_eq!(error.code, crate::error_code::INVALID_REQUEST_ERROR_CODE);
|
||||
assert_eq!(
|
||||
error.message,
|
||||
"device/key/sign is not available over remote transports"
|
||||
);
|
||||
|
||||
harness.shutdown().await;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "current_thread")]
|
||||
#[serial(app_server_tracing)]
|
||||
async fn turn_start_jsonrpc_span_parents_core_turn_spans() -> Result<()> {
|
||||
|
||||
@@ -435,7 +435,6 @@ mod apps_processor;
|
||||
mod catalog_processor;
|
||||
mod command_exec_processor;
|
||||
mod config_processor;
|
||||
mod device_key_processor;
|
||||
mod external_agent_config_processor;
|
||||
mod feedback_processor;
|
||||
mod fs_processor;
|
||||
@@ -456,7 +455,6 @@ pub(crate) use apps_processor::AppsRequestProcessor;
|
||||
pub(crate) use catalog_processor::CatalogRequestProcessor;
|
||||
pub(crate) use command_exec_processor::CommandExecRequestProcessor;
|
||||
pub(crate) use config_processor::ConfigRequestProcessor;
|
||||
pub(crate) use device_key_processor::DeviceKeyRequestProcessor;
|
||||
pub(crate) use external_agent_config_processor::ExternalAgentConfigRequestProcessor;
|
||||
pub(crate) use feedback_processor::FeedbackRequestProcessor;
|
||||
pub(crate) use fs_processor::FsRequestProcessor;
|
||||
|
||||
@@ -1,369 +0,0 @@
|
||||
use std::fmt;
|
||||
use std::future::Future;
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::error_code::internal_error;
|
||||
use crate::error_code::invalid_request;
|
||||
use crate::outgoing_message::ConnectionRequestId;
|
||||
use crate::outgoing_message::OutgoingMessageSender;
|
||||
use async_trait::async_trait;
|
||||
use base64::Engine;
|
||||
use base64::engine::general_purpose::STANDARD;
|
||||
use codex_app_server_protocol::ClientResponsePayload;
|
||||
use codex_app_server_protocol::DeviceKeyAlgorithm;
|
||||
use codex_app_server_protocol::DeviceKeyCreateParams;
|
||||
use codex_app_server_protocol::DeviceKeyCreateResponse;
|
||||
use codex_app_server_protocol::DeviceKeyProtectionClass;
|
||||
use codex_app_server_protocol::DeviceKeyPublicParams;
|
||||
use codex_app_server_protocol::DeviceKeyPublicResponse;
|
||||
use codex_app_server_protocol::DeviceKeySignParams;
|
||||
use codex_app_server_protocol::DeviceKeySignPayload;
|
||||
use codex_app_server_protocol::DeviceKeySignResponse;
|
||||
use codex_app_server_protocol::JSONRPCErrorError;
|
||||
use codex_device_key::DeviceKeyBinding;
|
||||
use codex_device_key::DeviceKeyBindingStore;
|
||||
use codex_device_key::DeviceKeyCreateRequest;
|
||||
use codex_device_key::DeviceKeyError;
|
||||
use codex_device_key::DeviceKeyGetPublicRequest;
|
||||
use codex_device_key::DeviceKeyInfo;
|
||||
use codex_device_key::DeviceKeyProtectionPolicy;
|
||||
use codex_device_key::DeviceKeySignRequest;
|
||||
use codex_device_key::DeviceKeyStore;
|
||||
use codex_device_key::RemoteControlClientConnectionAudience;
|
||||
use codex_device_key::RemoteControlClientConnectionSignPayload;
|
||||
use codex_device_key::RemoteControlClientEnrollmentAudience;
|
||||
use codex_device_key::RemoteControlClientEnrollmentSignPayload;
|
||||
use codex_state::DeviceKeyBindingRecord;
|
||||
use codex_state::StateRuntime;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub(crate) struct DeviceKeyRequestProcessor {
|
||||
outgoing: Arc<OutgoingMessageSender>,
|
||||
store: DeviceKeyStore,
|
||||
}
|
||||
|
||||
impl DeviceKeyRequestProcessor {
|
||||
pub(crate) fn new(
|
||||
outgoing: Arc<OutgoingMessageSender>,
|
||||
state_db: Option<Arc<StateRuntime>>,
|
||||
) -> Self {
|
||||
Self {
|
||||
outgoing,
|
||||
store: DeviceKeyStore::new(Arc::new(StateDeviceKeyBindingStore::new(state_db))),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn create(
|
||||
&self,
|
||||
request_id: ConnectionRequestId,
|
||||
params: DeviceKeyCreateParams,
|
||||
device_key_requests_allowed: bool,
|
||||
) {
|
||||
self.spawn_request(
|
||||
request_id,
|
||||
"device/key/create",
|
||||
device_key_requests_allowed,
|
||||
move |store| async move { create_device_key(store, params).await },
|
||||
);
|
||||
}
|
||||
|
||||
pub(crate) fn public(
|
||||
&self,
|
||||
request_id: ConnectionRequestId,
|
||||
params: DeviceKeyPublicParams,
|
||||
device_key_requests_allowed: bool,
|
||||
) {
|
||||
self.spawn_request(
|
||||
request_id,
|
||||
"device/key/public",
|
||||
device_key_requests_allowed,
|
||||
move |store| async move { public_device_key(store, params).await },
|
||||
);
|
||||
}
|
||||
|
||||
pub(crate) fn sign(
|
||||
&self,
|
||||
request_id: ConnectionRequestId,
|
||||
params: DeviceKeySignParams,
|
||||
device_key_requests_allowed: bool,
|
||||
) {
|
||||
self.spawn_request(
|
||||
request_id,
|
||||
"device/key/sign",
|
||||
device_key_requests_allowed,
|
||||
move |store| async move { sign_device_key(store, params).await },
|
||||
);
|
||||
}
|
||||
|
||||
fn spawn_request<R, F, Fut>(
|
||||
&self,
|
||||
request_id: ConnectionRequestId,
|
||||
method: &'static str,
|
||||
device_key_requests_allowed: bool,
|
||||
run_request: F,
|
||||
) where
|
||||
R: Into<ClientResponsePayload> + Send + 'static,
|
||||
F: FnOnce(DeviceKeyStore) -> Fut + Send + 'static,
|
||||
Fut: Future<Output = Result<R, JSONRPCErrorError>> + Send + 'static,
|
||||
{
|
||||
let store = self.store.clone();
|
||||
let outgoing = Arc::clone(&self.outgoing);
|
||||
tokio::spawn(async move {
|
||||
let result = if !device_key_requests_allowed {
|
||||
Err(invalid_request(format!(
|
||||
"{method} is not available over remote transports"
|
||||
)))
|
||||
} else {
|
||||
run_request(store).await
|
||||
};
|
||||
outgoing.send_result(request_id, result).await;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async fn create_device_key(
|
||||
store: DeviceKeyStore,
|
||||
params: DeviceKeyCreateParams,
|
||||
) -> Result<DeviceKeyCreateResponse, JSONRPCErrorError> {
|
||||
let info = store
|
||||
.create(DeviceKeyCreateRequest {
|
||||
protection_policy: protection_policy_from_params(params.protection_policy),
|
||||
binding: DeviceKeyBinding {
|
||||
account_user_id: params.account_user_id,
|
||||
client_id: params.client_id,
|
||||
},
|
||||
})
|
||||
.await
|
||||
.map_err(map_device_key_error)?;
|
||||
Ok(create_response_from_info(info))
|
||||
}
|
||||
|
||||
async fn public_device_key(
|
||||
store: DeviceKeyStore,
|
||||
params: DeviceKeyPublicParams,
|
||||
) -> Result<DeviceKeyPublicResponse, JSONRPCErrorError> {
|
||||
let info = store
|
||||
.get_public(DeviceKeyGetPublicRequest {
|
||||
key_id: params.key_id,
|
||||
})
|
||||
.await
|
||||
.map_err(map_device_key_error)?;
|
||||
Ok(public_response_from_info(info))
|
||||
}
|
||||
|
||||
async fn sign_device_key(
|
||||
store: DeviceKeyStore,
|
||||
params: DeviceKeySignParams,
|
||||
) -> Result<DeviceKeySignResponse, JSONRPCErrorError> {
|
||||
let signature = store
|
||||
.sign(DeviceKeySignRequest {
|
||||
key_id: params.key_id,
|
||||
payload: payload_from_params(params.payload),
|
||||
})
|
||||
.await
|
||||
.map_err(map_device_key_error)?;
|
||||
Ok(DeviceKeySignResponse {
|
||||
signature_der_base64: STANDARD.encode(signature.signature_der),
|
||||
signed_payload_base64: STANDARD.encode(signature.signed_payload),
|
||||
algorithm: algorithm_from_store(signature.algorithm),
|
||||
})
|
||||
}
|
||||
|
||||
struct StateDeviceKeyBindingStore {
|
||||
state_db: Option<Arc<StateRuntime>>,
|
||||
}
|
||||
|
||||
impl StateDeviceKeyBindingStore {
|
||||
fn new(state_db: Option<Arc<StateRuntime>>) -> Self {
|
||||
Self { state_db }
|
||||
}
|
||||
|
||||
async fn state_db(&self) -> Result<Arc<StateRuntime>, DeviceKeyError> {
|
||||
self.state_db
|
||||
.clone()
|
||||
.ok_or_else(|| DeviceKeyError::Platform("sqlite state db unavailable".to_string()))
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for StateDeviceKeyBindingStore {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("StateDeviceKeyBindingStore")
|
||||
.field("has_state_db", &self.state_db.is_some())
|
||||
.finish_non_exhaustive()
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl DeviceKeyBindingStore for StateDeviceKeyBindingStore {
|
||||
async fn get_binding(&self, key_id: &str) -> Result<Option<DeviceKeyBinding>, DeviceKeyError> {
|
||||
let state_db = self.state_db().await?;
|
||||
state_db
|
||||
.get_device_key_binding(key_id)
|
||||
.await
|
||||
.map(|record| {
|
||||
record.map(|record| DeviceKeyBinding {
|
||||
account_user_id: record.account_user_id,
|
||||
client_id: record.client_id,
|
||||
})
|
||||
})
|
||||
.map_err(|err| DeviceKeyError::Platform(err.to_string()))
|
||||
}
|
||||
|
||||
async fn put_binding(
|
||||
&self,
|
||||
key_id: &str,
|
||||
binding: &DeviceKeyBinding,
|
||||
) -> Result<(), DeviceKeyError> {
|
||||
let state_db = self.state_db().await?;
|
||||
state_db
|
||||
.upsert_device_key_binding(&DeviceKeyBindingRecord {
|
||||
key_id: key_id.to_string(),
|
||||
account_user_id: binding.account_user_id.clone(),
|
||||
client_id: binding.client_id.clone(),
|
||||
})
|
||||
.await
|
||||
.map_err(|err| DeviceKeyError::Platform(err.to_string()))
|
||||
}
|
||||
}
|
||||
|
||||
fn create_response_from_info(info: DeviceKeyInfo) -> DeviceKeyCreateResponse {
|
||||
DeviceKeyCreateResponse {
|
||||
key_id: info.key_id,
|
||||
public_key_spki_der_base64: STANDARD.encode(info.public_key_spki_der),
|
||||
algorithm: algorithm_from_store(info.algorithm),
|
||||
protection_class: protection_class_from_store(info.protection_class),
|
||||
}
|
||||
}
|
||||
|
||||
fn public_response_from_info(info: DeviceKeyInfo) -> DeviceKeyPublicResponse {
|
||||
DeviceKeyPublicResponse {
|
||||
key_id: info.key_id,
|
||||
public_key_spki_der_base64: STANDARD.encode(info.public_key_spki_der),
|
||||
algorithm: algorithm_from_store(info.algorithm),
|
||||
protection_class: protection_class_from_store(info.protection_class),
|
||||
}
|
||||
}
|
||||
|
||||
fn protection_policy_from_params(
|
||||
protection_policy: Option<codex_app_server_protocol::DeviceKeyProtectionPolicy>,
|
||||
) -> DeviceKeyProtectionPolicy {
|
||||
match protection_policy
|
||||
.unwrap_or(codex_app_server_protocol::DeviceKeyProtectionPolicy::HardwareOnly)
|
||||
{
|
||||
codex_app_server_protocol::DeviceKeyProtectionPolicy::HardwareOnly => {
|
||||
DeviceKeyProtectionPolicy::HardwareOnly
|
||||
}
|
||||
codex_app_server_protocol::DeviceKeyProtectionPolicy::AllowOsProtectedNonextractable => {
|
||||
DeviceKeyProtectionPolicy::AllowOsProtectedNonextractable
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn payload_from_params(payload: DeviceKeySignPayload) -> codex_device_key::DeviceKeySignPayload {
|
||||
match payload {
|
||||
DeviceKeySignPayload::RemoteControlClientConnection {
|
||||
nonce,
|
||||
audience,
|
||||
session_id,
|
||||
target_origin,
|
||||
target_path,
|
||||
account_user_id,
|
||||
client_id,
|
||||
token_sha256_base64url,
|
||||
token_expires_at,
|
||||
scopes,
|
||||
} => codex_device_key::DeviceKeySignPayload::RemoteControlClientConnection(
|
||||
RemoteControlClientConnectionSignPayload {
|
||||
nonce,
|
||||
audience: remote_control_client_connection_audience_from_protocol(audience),
|
||||
session_id,
|
||||
target_origin,
|
||||
target_path,
|
||||
account_user_id,
|
||||
client_id,
|
||||
token_sha256_base64url,
|
||||
token_expires_at,
|
||||
scopes,
|
||||
},
|
||||
),
|
||||
DeviceKeySignPayload::RemoteControlClientEnrollment {
|
||||
nonce,
|
||||
audience,
|
||||
challenge_id,
|
||||
target_origin,
|
||||
target_path,
|
||||
account_user_id,
|
||||
client_id,
|
||||
device_identity_sha256_base64url,
|
||||
challenge_expires_at,
|
||||
} => codex_device_key::DeviceKeySignPayload::RemoteControlClientEnrollment(
|
||||
RemoteControlClientEnrollmentSignPayload {
|
||||
nonce,
|
||||
audience: remote_control_client_enrollment_audience_from_protocol(audience),
|
||||
challenge_id,
|
||||
target_origin,
|
||||
target_path,
|
||||
account_user_id,
|
||||
client_id,
|
||||
device_identity_sha256_base64url,
|
||||
challenge_expires_at,
|
||||
},
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
fn remote_control_client_connection_audience_from_protocol(
|
||||
audience: codex_app_server_protocol::RemoteControlClientConnectionAudience,
|
||||
) -> RemoteControlClientConnectionAudience {
|
||||
match audience {
|
||||
codex_app_server_protocol::RemoteControlClientConnectionAudience::RemoteControlClientWebsocket => {
|
||||
RemoteControlClientConnectionAudience::RemoteControlClientWebsocket
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn remote_control_client_enrollment_audience_from_protocol(
|
||||
audience: codex_app_server_protocol::RemoteControlClientEnrollmentAudience,
|
||||
) -> RemoteControlClientEnrollmentAudience {
|
||||
match audience {
|
||||
codex_app_server_protocol::RemoteControlClientEnrollmentAudience::RemoteControlClientEnrollment => {
|
||||
RemoteControlClientEnrollmentAudience::RemoteControlClientEnrollment
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn algorithm_from_store(algorithm: codex_device_key::DeviceKeyAlgorithm) -> DeviceKeyAlgorithm {
|
||||
match algorithm {
|
||||
codex_device_key::DeviceKeyAlgorithm::EcdsaP256Sha256 => {
|
||||
DeviceKeyAlgorithm::EcdsaP256Sha256
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn protection_class_from_store(
|
||||
protection_class: codex_device_key::DeviceKeyProtectionClass,
|
||||
) -> DeviceKeyProtectionClass {
|
||||
match protection_class {
|
||||
codex_device_key::DeviceKeyProtectionClass::HardwareSecureEnclave => {
|
||||
DeviceKeyProtectionClass::HardwareSecureEnclave
|
||||
}
|
||||
codex_device_key::DeviceKeyProtectionClass::HardwareTpm => {
|
||||
DeviceKeyProtectionClass::HardwareTpm
|
||||
}
|
||||
codex_device_key::DeviceKeyProtectionClass::OsProtectedNonextractable => {
|
||||
DeviceKeyProtectionClass::OsProtectedNonextractable
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn map_device_key_error(error: DeviceKeyError) -> JSONRPCErrorError {
|
||||
match &error {
|
||||
DeviceKeyError::DegradedProtectionNotAllowed { .. }
|
||||
| DeviceKeyError::HardwareBackedKeysUnavailable
|
||||
| DeviceKeyError::KeyNotFound
|
||||
| DeviceKeyError::InvalidPayload(_) => invalid_request(error.to_string()),
|
||||
DeviceKeyError::Platform(_) | DeviceKeyError::Crypto(_) => {
|
||||
internal_error(error.to_string())
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -36,7 +36,7 @@ pub(crate) struct ConnectionState {
|
||||
|
||||
impl ConnectionState {
|
||||
pub(crate) fn new(
|
||||
origin: ConnectionOrigin,
|
||||
_origin: ConnectionOrigin,
|
||||
outbound_initialized: Arc<AtomicBool>,
|
||||
outbound_experimental_api_enabled: Arc<AtomicBool>,
|
||||
outbound_opted_out_notification_methods: Arc<RwLock<HashSet<String>>>,
|
||||
@@ -45,7 +45,7 @@ impl ConnectionState {
|
||||
outbound_initialized,
|
||||
outbound_experimental_api_enabled,
|
||||
outbound_opted_out_notification_methods,
|
||||
session: Arc::new(ConnectionSessionState::new(origin)),
|
||||
session: Arc::new(ConnectionSessionState::new()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user