feat: rustc and clippy linting
This commit is contained in:
@@ -13,8 +13,8 @@ const TOKEN_LENGTH: usize = 64;
|
||||
pub async fn generate_token() -> Result<String, std::io::Error> {
|
||||
let rng: StdRng = make_rng();
|
||||
|
||||
let token: String = rng.sample_iter(Alphanumeric).take(TOKEN_LENGTH).fold(
|
||||
Default::default(),
|
||||
let token = rng.sample_iter(Alphanumeric).take(TOKEN_LENGTH).fold(
|
||||
String::default(),
|
||||
|mut accum, char| {
|
||||
accum += char.to_string().as_str();
|
||||
accum
|
||||
@@ -27,15 +27,15 @@ pub async fn generate_token() -> Result<String, std::io::Error> {
|
||||
}
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum Error {
|
||||
pub enum BootstrappError {
|
||||
#[error("Database error: {0}")]
|
||||
Database(#[from] db::PoolError),
|
||||
|
||||
#[error("Database query error: {0}")]
|
||||
Query(#[from] diesel::result::Error),
|
||||
|
||||
#[error("I/O error: {0}")]
|
||||
Io(#[from] std::io::Error),
|
||||
|
||||
#[error("Database query error: {0}")]
|
||||
Query(#[from] diesel::result::Error),
|
||||
}
|
||||
|
||||
#[derive(Actor)]
|
||||
@@ -44,7 +44,7 @@ pub struct Bootstrapper {
|
||||
}
|
||||
|
||||
impl Bootstrapper {
|
||||
pub async fn new(db: &DatabasePool) -> Result<Self, Error> {
|
||||
pub async fn new(db: &DatabasePool) -> Result<Self, BootstrappError> {
|
||||
let row_count: i64 = {
|
||||
let mut conn = db.get().await?;
|
||||
|
||||
@@ -69,16 +69,13 @@ impl Bootstrapper {
|
||||
impl Bootstrapper {
|
||||
#[message]
|
||||
pub fn is_correct_token(&self, token: String) -> bool {
|
||||
match &self.token {
|
||||
Some(expected) => {
|
||||
let expected_bytes = expected.as_bytes();
|
||||
let token_bytes = token.as_bytes();
|
||||
self.token.as_ref().is_some_and(|expected| {
|
||||
let expected_bytes = expected.as_bytes();
|
||||
let token_bytes = token.as_bytes();
|
||||
|
||||
let choice = expected_bytes.ct_eq(token_bytes);
|
||||
bool::from(choice)
|
||||
}
|
||||
None => false,
|
||||
}
|
||||
let choice = expected_bytes.ct_eq(token_bytes);
|
||||
bool::from(choice)
|
||||
})
|
||||
}
|
||||
|
||||
#[message]
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
use arbiter_crypto::authn::{self, CLIENT_CONTEXT};
|
||||
use arbiter_proto::{
|
||||
ClientMetadata,
|
||||
proto::client::auth::{AuthChallenge as ProtoAuthChallenge, AuthResult as ProtoAuthResult},
|
||||
transport::{Bi, expect_message},
|
||||
};
|
||||
use chrono::Utc;
|
||||
@@ -27,34 +28,60 @@ use crate::{
|
||||
};
|
||||
|
||||
#[derive(thiserror::Error, Debug, Clone, PartialEq, Eq)]
|
||||
pub enum Error {
|
||||
#[error("Database pool unavailable")]
|
||||
DatabasePoolUnavailable,
|
||||
#[error("Database operation failed")]
|
||||
DatabaseOperationFailed,
|
||||
#[error("Integrity check failed")]
|
||||
IntegrityCheckFailed,
|
||||
#[error("Invalid challenge solution")]
|
||||
InvalidChallengeSolution,
|
||||
pub enum ClientAuthError {
|
||||
#[error("Client approval request failed")]
|
||||
ApproveError(#[from] ApproveError),
|
||||
|
||||
#[error("Database operation failed")]
|
||||
DatabaseOperationFailed,
|
||||
|
||||
#[error("Database pool unavailable")]
|
||||
DatabasePoolUnavailable,
|
||||
|
||||
#[error("Integrity check failed")]
|
||||
IntegrityCheckFailed,
|
||||
|
||||
#[error("Invalid challenge solution")]
|
||||
InvalidChallengeSolution,
|
||||
|
||||
#[error("Transport error")]
|
||||
Transport,
|
||||
}
|
||||
|
||||
impl From<diesel::result::Error> for Error {
|
||||
impl From<diesel::result::Error> for ClientAuthError {
|
||||
fn from(e: diesel::result::Error) -> Self {
|
||||
error!(?e, "Database error");
|
||||
Self::DatabaseOperationFailed
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ClientAuthError> for arbiter_proto::proto::client::auth::AuthResult {
|
||||
fn from(value: ClientAuthError) -> Self {
|
||||
match value {
|
||||
ClientAuthError::ApproveError(e) => match e {
|
||||
ApproveError::Denied => Self::ApprovalDenied,
|
||||
ApproveError::Internal => Self::Internal,
|
||||
ApproveError::Upstream(flow_coordinator::ApprovalError::NoUserAgentsConnected) => {
|
||||
Self::NoUserAgentsOnline
|
||||
} // ApproveError::Upstream(_) => Self::Internal,
|
||||
},
|
||||
ClientAuthError::DatabaseOperationFailed
|
||||
| ClientAuthError::DatabasePoolUnavailable
|
||||
| ClientAuthError::IntegrityCheckFailed
|
||||
| ClientAuthError::Transport => Self::Internal,
|
||||
ClientAuthError::InvalidChallengeSolution => Self::InvalidSignature,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(thiserror::Error, Debug, Clone, PartialEq, Eq)]
|
||||
pub enum ApproveError {
|
||||
#[error("Internal error")]
|
||||
Internal,
|
||||
#[error("Client connection denied by user agents")]
|
||||
Denied,
|
||||
|
||||
#[error("Internal error")]
|
||||
Internal,
|
||||
|
||||
#[error("Upstream error: {0}")]
|
||||
Upstream(flow_coordinator::ApprovalError),
|
||||
}
|
||||
@@ -79,16 +106,28 @@ pub enum Outbound {
|
||||
AuthSuccess,
|
||||
}
|
||||
|
||||
impl From<Outbound> for arbiter_proto::proto::client::auth::response::Payload {
|
||||
fn from(value: Outbound) -> Self {
|
||||
match value {
|
||||
Outbound::AuthChallenge { pubkey, nonce } => Self::Challenge(ProtoAuthChallenge {
|
||||
pubkey: pubkey.to_bytes(),
|
||||
nonce,
|
||||
}),
|
||||
Outbound::AuthSuccess => Self::Result(ProtoAuthResult::Success.into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the current nonce and client ID for a registered client.
|
||||
/// Returns `None` if the pubkey is not registered.
|
||||
async fn get_current_nonce_and_id(
|
||||
db: &db::DatabasePool,
|
||||
pubkey: &authn::PublicKey,
|
||||
) -> Result<Option<(i32, i32)>, Error> {
|
||||
) -> Result<Option<(i32, i32)>, ClientAuthError> {
|
||||
let pubkey_bytes = pubkey.to_bytes();
|
||||
let mut conn = db.get().await.map_err(|e| {
|
||||
error!(error = ?e, "Database pool error");
|
||||
Error::DatabasePoolUnavailable
|
||||
ClientAuthError::DatabasePoolUnavailable
|
||||
})?;
|
||||
program_client::table
|
||||
.filter(program_client::public_key.eq(&pubkey_bytes))
|
||||
@@ -98,7 +137,7 @@ async fn get_current_nonce_and_id(
|
||||
.optional()
|
||||
.map_err(|e| {
|
||||
error!(error = ?e, "Database error");
|
||||
Error::DatabaseOperationFailed
|
||||
ClientAuthError::DatabaseOperationFailed
|
||||
})
|
||||
}
|
||||
|
||||
@@ -106,15 +145,15 @@ async fn verify_integrity(
|
||||
db: &db::DatabasePool,
|
||||
keyholder: &ActorRef<KeyHolder>,
|
||||
pubkey: &authn::PublicKey,
|
||||
) -> Result<(), Error> {
|
||||
) -> Result<(), ClientAuthError> {
|
||||
let mut db_conn = db.get().await.map_err(|e| {
|
||||
error!(error = ?e, "Database pool error");
|
||||
Error::DatabasePoolUnavailable
|
||||
ClientAuthError::DatabasePoolUnavailable
|
||||
})?;
|
||||
|
||||
let (id, nonce) = get_current_nonce_and_id(db, pubkey).await?.ok_or_else(|| {
|
||||
error!("Client not found during integrity verification");
|
||||
Error::DatabaseOperationFailed
|
||||
ClientAuthError::DatabaseOperationFailed
|
||||
})?;
|
||||
|
||||
let attestation = integrity::verify_entity(
|
||||
@@ -129,12 +168,12 @@ async fn verify_integrity(
|
||||
.await
|
||||
.map_err(|e| {
|
||||
error!(?e, "Integrity verification failed");
|
||||
Error::IntegrityCheckFailed
|
||||
ClientAuthError::IntegrityCheckFailed
|
||||
})?;
|
||||
|
||||
if attestation != AttestationStatus::Attested {
|
||||
error!("Integrity attestation unavailable for client {id}");
|
||||
return Err(Error::IntegrityCheckFailed);
|
||||
return Err(ClientAuthError::IntegrityCheckFailed);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@@ -146,13 +185,13 @@ async fn create_nonce(
|
||||
db: &db::DatabasePool,
|
||||
keyholder: &ActorRef<KeyHolder>,
|
||||
pubkey: &authn::PublicKey,
|
||||
) -> Result<i32, Error> {
|
||||
) -> Result<i32, ClientAuthError> {
|
||||
let pubkey_bytes = pubkey.to_bytes();
|
||||
let pubkey = pubkey.clone();
|
||||
|
||||
let mut conn = db.get().await.map_err(|e| {
|
||||
error!(error = ?e, "Database pool error");
|
||||
Error::DatabasePoolUnavailable
|
||||
ClientAuthError::DatabasePoolUnavailable
|
||||
})?;
|
||||
|
||||
conn.exclusive_transaction(|conn| {
|
||||
@@ -178,7 +217,7 @@ async fn create_nonce(
|
||||
.await
|
||||
.map_err(|e| {
|
||||
error!(?e, "Integrity sign failed after nonce update");
|
||||
Error::DatabaseOperationFailed
|
||||
ClientAuthError::DatabaseOperationFailed
|
||||
})?;
|
||||
|
||||
Ok(new_nonce)
|
||||
@@ -190,7 +229,7 @@ async fn create_nonce(
|
||||
async fn approve_new_client(
|
||||
actors: &crate::actors::GlobalActors,
|
||||
profile: ClientProfile,
|
||||
) -> Result<(), Error> {
|
||||
) -> Result<(), ClientAuthError> {
|
||||
let result = actors
|
||||
.flow_coordinator
|
||||
.ask(RequestClientApproval { client: profile })
|
||||
@@ -198,14 +237,14 @@ async fn approve_new_client(
|
||||
|
||||
match result {
|
||||
Ok(true) => Ok(()),
|
||||
Ok(false) => Err(Error::ApproveError(ApproveError::Denied)),
|
||||
Ok(false) => Err(ClientAuthError::ApproveError(ApproveError::Denied)),
|
||||
Err(SendError::HandlerError(e)) => {
|
||||
error!(error = ?e, "Approval upstream error");
|
||||
Err(Error::ApproveError(ApproveError::Upstream(e)))
|
||||
Err(ClientAuthError::ApproveError(ApproveError::Upstream(e)))
|
||||
}
|
||||
Err(e) => {
|
||||
error!(error = ?e, "Approval request to flow coordinator failed");
|
||||
Err(Error::ApproveError(ApproveError::Internal))
|
||||
Err(ClientAuthError::ApproveError(ApproveError::Internal))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -215,14 +254,14 @@ async fn insert_client(
|
||||
keyholder: &ActorRef<KeyHolder>,
|
||||
pubkey: &authn::PublicKey,
|
||||
metadata: &ClientMetadata,
|
||||
) -> Result<i32, Error> {
|
||||
use crate::db::schema::{client_metadata, program_client};
|
||||
) -> Result<i32, ClientAuthError> {
|
||||
use crate::db::schema::client_metadata;
|
||||
let pubkey = pubkey.clone();
|
||||
let metadata = metadata.clone();
|
||||
|
||||
let mut conn = db.get().await.map_err(|e| {
|
||||
error!(error = ?e, "Database pool error");
|
||||
Error::DatabasePoolUnavailable
|
||||
ClientAuthError::DatabasePoolUnavailable
|
||||
})?;
|
||||
|
||||
conn.exclusive_transaction(|conn| {
|
||||
@@ -264,7 +303,7 @@ async fn insert_client(
|
||||
.await
|
||||
.map_err(|e| {
|
||||
error!(error = ?e, "Failed to sign integrity tag for new client key");
|
||||
Error::DatabaseOperationFailed
|
||||
ClientAuthError::DatabaseOperationFailed
|
||||
})?;
|
||||
|
||||
Ok(client_id)
|
||||
@@ -277,14 +316,14 @@ async fn sync_client_metadata(
|
||||
db: &db::DatabasePool,
|
||||
client_id: i32,
|
||||
metadata: &ClientMetadata,
|
||||
) -> Result<(), Error> {
|
||||
) -> Result<(), ClientAuthError> {
|
||||
use crate::db::schema::{client_metadata, client_metadata_history};
|
||||
|
||||
let now = SqliteTimestamp(Utc::now());
|
||||
|
||||
let mut conn = db.get().await.map_err(|e| {
|
||||
error!(error = ?e, "Database pool error");
|
||||
Error::DatabasePoolUnavailable
|
||||
ClientAuthError::DatabasePoolUnavailable
|
||||
})?;
|
||||
|
||||
conn.exclusive_transaction(|conn| {
|
||||
@@ -340,7 +379,7 @@ async fn sync_client_metadata(
|
||||
.await
|
||||
.map_err(|e| {
|
||||
error!(error = ?e, "Database error");
|
||||
Error::DatabaseOperationFailed
|
||||
ClientAuthError::DatabaseOperationFailed
|
||||
})
|
||||
}
|
||||
|
||||
@@ -348,9 +387,9 @@ async fn challenge_client<T>(
|
||||
transport: &mut T,
|
||||
pubkey: authn::PublicKey,
|
||||
nonce: i32,
|
||||
) -> Result<(), Error>
|
||||
) -> Result<(), ClientAuthError>
|
||||
where
|
||||
T: Bi<Inbound, Result<Outbound, Error>> + ?Sized,
|
||||
T: Bi<Inbound, Result<Outbound, ClientAuthError>> + ?Sized,
|
||||
{
|
||||
transport
|
||||
.send(Ok(Outbound::AuthChallenge {
|
||||
@@ -360,51 +399,51 @@ where
|
||||
.await
|
||||
.map_err(|e| {
|
||||
error!(error = ?e, "Failed to send auth challenge");
|
||||
Error::Transport
|
||||
ClientAuthError::Transport
|
||||
})?;
|
||||
|
||||
let signature = expect_message(transport, |req: Inbound| match req {
|
||||
Inbound::AuthChallengeSolution { signature } => Some(signature),
|
||||
_ => None,
|
||||
Inbound::AuthChallengeRequest { .. } => None,
|
||||
})
|
||||
.await
|
||||
.map_err(|e| {
|
||||
error!(error = ?e, "Failed to receive challenge solution");
|
||||
Error::Transport
|
||||
ClientAuthError::Transport
|
||||
})?;
|
||||
|
||||
if !pubkey.verify(nonce, CLIENT_CONTEXT, &signature) {
|
||||
error!("Challenge solution verification failed");
|
||||
return Err(Error::InvalidChallengeSolution);
|
||||
return Err(ClientAuthError::InvalidChallengeSolution);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn authenticate<T>(props: &mut ClientConnection, transport: &mut T) -> Result<i32, Error>
|
||||
pub async fn authenticate<T>(
|
||||
props: &mut ClientConnection,
|
||||
transport: &mut T,
|
||||
) -> Result<i32, ClientAuthError>
|
||||
where
|
||||
T: Bi<Inbound, Result<Outbound, Error>> + Send + ?Sized,
|
||||
T: Bi<Inbound, Result<Outbound, ClientAuthError>> + Send + ?Sized,
|
||||
{
|
||||
let Some(Inbound::AuthChallengeRequest { pubkey, metadata }) = transport.recv().await else {
|
||||
return Err(Error::Transport);
|
||||
return Err(ClientAuthError::Transport);
|
||||
};
|
||||
|
||||
let client_id = match get_current_nonce_and_id(&props.db, &pubkey).await? {
|
||||
Some((id, _)) => {
|
||||
verify_integrity(&props.db, &props.actors.key_holder, &pubkey).await?;
|
||||
id
|
||||
}
|
||||
None => {
|
||||
approve_new_client(
|
||||
&props.actors,
|
||||
ClientProfile {
|
||||
pubkey: pubkey.clone(),
|
||||
metadata: metadata.clone(),
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
insert_client(&props.db, &props.actors.key_holder, &pubkey, &metadata).await?
|
||||
}
|
||||
let client_id = if let Some((id, _)) = get_current_nonce_and_id(&props.db, &pubkey).await? {
|
||||
verify_integrity(&props.db, &props.actors.key_holder, &pubkey).await?;
|
||||
id
|
||||
} else {
|
||||
approve_new_client(
|
||||
&props.actors,
|
||||
ClientProfile {
|
||||
pubkey: pubkey.clone(),
|
||||
metadata: metadata.clone(),
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
insert_client(&props.db, &props.actors.key_holder, &pubkey, &metadata).await?
|
||||
};
|
||||
|
||||
sync_client_metadata(&props.db, client_id, &metadata).await?;
|
||||
@@ -416,7 +455,7 @@ where
|
||||
.await
|
||||
.map_err(|e| {
|
||||
error!(error = ?e, "Failed to send auth success");
|
||||
Error::Transport
|
||||
ClientAuthError::Transport
|
||||
})?;
|
||||
|
||||
Ok(client_id)
|
||||
|
||||
@@ -31,7 +31,7 @@ pub struct ClientConnection {
|
||||
}
|
||||
|
||||
impl ClientConnection {
|
||||
pub fn new(db: db::DatabasePool, actors: GlobalActors) -> Self {
|
||||
pub const fn new(db: db::DatabasePool, actors: GlobalActors) -> Self {
|
||||
Self { db, actors }
|
||||
}
|
||||
}
|
||||
@@ -41,10 +41,10 @@ pub mod session;
|
||||
|
||||
pub async fn connect_client<T>(mut props: ClientConnection, transport: &mut T)
|
||||
where
|
||||
T: Bi<auth::Inbound, Result<auth::Outbound, auth::Error>> + Send + ?Sized,
|
||||
T: Bi<auth::Inbound, Result<auth::Outbound, auth::ClientAuthError>> + Send + ?Sized,
|
||||
{
|
||||
let fut = auth::authenticate(&mut props, transport);
|
||||
println!("authenticate future size: {}", std::mem::size_of_val(&fut));
|
||||
println!("authenticate future size: {}", size_of_val(&fut));
|
||||
match fut.await {
|
||||
Ok(client_id) => {
|
||||
ClientSession::spawn(ClientSession::new(props, client_id));
|
||||
|
||||
@@ -21,7 +21,7 @@ pub struct ClientSession {
|
||||
}
|
||||
|
||||
impl ClientSession {
|
||||
pub(crate) fn new(props: ClientConnection, client_id: i32) -> Self {
|
||||
pub(crate) const fn new(props: ClientConnection, client_id: i32) -> Self {
|
||||
Self { props, client_id }
|
||||
}
|
||||
}
|
||||
@@ -29,14 +29,16 @@ impl ClientSession {
|
||||
#[messages]
|
||||
impl ClientSession {
|
||||
#[message]
|
||||
pub(crate) async fn handle_query_vault_state(&mut self) -> Result<KeyHolderState, Error> {
|
||||
pub(crate) async fn handle_query_vault_state(
|
||||
&mut self,
|
||||
) -> Result<KeyHolderState, ClientSessionError> {
|
||||
use crate::actors::keyholder::GetState;
|
||||
|
||||
let vault_state = match self.props.actors.key_holder.ask(GetState {}).await {
|
||||
Ok(state) => state,
|
||||
Err(err) => {
|
||||
error!(?err, actor = "client", "keyholder.query.failed");
|
||||
return Err(Error::Internal);
|
||||
return Err(ClientSessionError::Internal);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -75,7 +77,7 @@ impl ClientSession {
|
||||
impl Actor for ClientSession {
|
||||
type Args = Self;
|
||||
|
||||
type Error = Error;
|
||||
type Error = ClientSessionError;
|
||||
|
||||
async fn on_start(
|
||||
args: Self::Args,
|
||||
@@ -86,13 +88,13 @@ impl Actor for ClientSession {
|
||||
.flow_coordinator
|
||||
.ask(RegisterClient { actor: this })
|
||||
.await
|
||||
.map_err(|_| Error::ConnectionRegistrationFailed)?;
|
||||
.map_err(|_| ClientSessionError::ConnectionRegistrationFailed)?;
|
||||
Ok(args)
|
||||
}
|
||||
}
|
||||
|
||||
impl ClientSession {
|
||||
pub fn new_test(db: db::DatabasePool, actors: GlobalActors) -> Self {
|
||||
pub const fn new_test(db: db::DatabasePool, actors: GlobalActors) -> Self {
|
||||
let props = ClientConnection::new(db, actors);
|
||||
Self {
|
||||
props,
|
||||
@@ -102,7 +104,7 @@ impl ClientSession {
|
||||
}
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum Error {
|
||||
pub enum ClientSessionError {
|
||||
#[error("Connection registration failed")]
|
||||
ConnectionRegistrationFailed,
|
||||
#[error("Internal error")]
|
||||
@@ -111,9 +113,9 @@ pub enum Error {
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum SignTransactionRpcError {
|
||||
#[error("Policy evaluation failed")]
|
||||
Vet(#[from] VetError),
|
||||
|
||||
#[error("Internal error")]
|
||||
Internal,
|
||||
|
||||
#[error("Policy evaluation failed")]
|
||||
Vet(#[from] VetError),
|
||||
}
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
use alloy::{consensus::TxEip1559, primitives::Address, signers::Signature};
|
||||
use alloy::{
|
||||
consensus::TxEip1559, network::TxSignerSync as _, primitives::Address, signers::Signature,
|
||||
};
|
||||
use diesel::{
|
||||
ExpressionMethods, OptionalExtension as _, QueryDsl, SelectableHelper as _, dsl::insert_into,
|
||||
};
|
||||
@@ -35,7 +37,7 @@ pub enum SignTransactionError {
|
||||
Database(#[from] DatabaseError),
|
||||
|
||||
#[error("Keyholder error: {0}")]
|
||||
Keyholder(#[from] crate::actors::keyholder::Error),
|
||||
Keyholder(#[from] crate::actors::keyholder::KeyHolderError),
|
||||
|
||||
#[error("Keyholder mailbox error")]
|
||||
KeyholderSend,
|
||||
@@ -48,9 +50,9 @@ pub enum SignTransactionError {
|
||||
}
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum Error {
|
||||
pub enum EvmActorError {
|
||||
#[error("Keyholder error: {0}")]
|
||||
Keyholder(#[from] crate::actors::keyholder::Error),
|
||||
Keyholder(#[from] crate::actors::keyholder::KeyHolderError),
|
||||
|
||||
#[error("Keyholder mailbox error")]
|
||||
KeyholderSend,
|
||||
@@ -59,7 +61,7 @@ pub enum Error {
|
||||
Database(#[from] DatabaseError),
|
||||
|
||||
#[error("Integrity violation: {0}")]
|
||||
Integrity(#[from] integrity::Error),
|
||||
Integrity(#[from] integrity::IntegrityError),
|
||||
}
|
||||
|
||||
#[derive(Actor)]
|
||||
@@ -88,7 +90,7 @@ impl EvmActor {
|
||||
#[messages]
|
||||
impl EvmActor {
|
||||
#[message]
|
||||
pub async fn generate(&mut self) -> Result<(i32, Address), Error> {
|
||||
pub async fn generate(&mut self) -> Result<(i32, Address), EvmActorError> {
|
||||
let (mut key_cell, address) = safe_signer::generate(&mut self.rng);
|
||||
|
||||
let plaintext = key_cell.read_inline(|reader| SafeCell::new(reader.to_vec()));
|
||||
@@ -97,7 +99,7 @@ impl EvmActor {
|
||||
.keyholder
|
||||
.ask(CreateNew { plaintext })
|
||||
.await
|
||||
.map_err(|_| Error::KeyholderSend)?;
|
||||
.map_err(|_| EvmActorError::KeyholderSend)?;
|
||||
|
||||
let mut conn = self.db.get().await.map_err(DatabaseError::from)?;
|
||||
let wallet_id = insert_into(schema::evm_wallet::table)
|
||||
@@ -114,7 +116,7 @@ impl EvmActor {
|
||||
}
|
||||
|
||||
#[message]
|
||||
pub async fn list_wallets(&self) -> Result<Vec<(i32, Address)>, Error> {
|
||||
pub async fn list_wallets(&self) -> Result<Vec<(i32, Address)>, EvmActorError> {
|
||||
let mut conn = self.db.get().await.map_err(DatabaseError::from)?;
|
||||
let rows: Vec<models::EvmWallet> = schema::evm_wallet::table
|
||||
.select(models::EvmWallet::as_select())
|
||||
@@ -136,7 +138,7 @@ impl EvmActor {
|
||||
&mut self,
|
||||
basic: SharedGrantSettings,
|
||||
grant: SpecificGrant,
|
||||
) -> Result<i32, Error> {
|
||||
) -> Result<i32, EvmActorError> {
|
||||
match grant {
|
||||
SpecificGrant::EtherTransfer(settings) => self
|
||||
.engine
|
||||
@@ -145,7 +147,7 @@ impl EvmActor {
|
||||
specific: settings,
|
||||
})
|
||||
.await
|
||||
.map_err(Error::from),
|
||||
.map_err(EvmActorError::from),
|
||||
SpecificGrant::TokenTransfer(settings) => self
|
||||
.engine
|
||||
.create_grant::<TokenTransfer>(CombinedSettings {
|
||||
@@ -153,12 +155,13 @@ impl EvmActor {
|
||||
specific: settings,
|
||||
})
|
||||
.await
|
||||
.map_err(Error::from),
|
||||
.map_err(EvmActorError::from),
|
||||
}
|
||||
}
|
||||
|
||||
#[message]
|
||||
pub async fn useragent_delete_grant(&mut self, _grant_id: i32) -> Result<(), Error> {
|
||||
#[expect(clippy::unused_async, reason = "reserved for impl")]
|
||||
pub async fn useragent_delete_grant(&mut self, _grant_id: i32) -> Result<(), EvmActorError> {
|
||||
// let mut conn = self.db.get().await.map_err(DatabaseError::from)?;
|
||||
// let keyholder = self.keyholder.clone();
|
||||
|
||||
@@ -183,11 +186,15 @@ impl EvmActor {
|
||||
}
|
||||
|
||||
#[message]
|
||||
pub async fn useragent_list_grants(&mut self) -> Result<Vec<Grant<SpecificGrant>>, Error> {
|
||||
pub async fn useragent_list_grants(
|
||||
&mut self,
|
||||
) -> Result<Vec<Grant<SpecificGrant>>, EvmActorError> {
|
||||
match self.engine.list_all_grants().await {
|
||||
Ok(grants) => Ok(grants),
|
||||
Err(ListError::Database(db_err)) => Err(Error::Database(db_err)),
|
||||
Err(ListError::Integrity(integrity_err)) => Err(Error::Integrity(integrity_err)),
|
||||
Err(ListError::Database(db_err)) => Err(EvmActorError::Database(db_err)),
|
||||
Err(ListError::Integrity(integrity_err)) => {
|
||||
Err(EvmActorError::Integrity(integrity_err))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -267,7 +274,6 @@ impl EvmActor {
|
||||
.evaluate_transaction(wallet_access, transaction.clone(), RunKind::Execution)
|
||||
.await?;
|
||||
|
||||
use alloy::network::TxSignerSync as _;
|
||||
Ok(signer.sign_transaction_sync(&mut transaction)?)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@ impl Actor for ClientApprovalController {
|
||||
async fn on_start(
|
||||
Args {
|
||||
client,
|
||||
mut user_agents,
|
||||
user_agents,
|
||||
reply,
|
||||
}: Self::Args,
|
||||
actor_ref: ActorRef<Self>,
|
||||
@@ -52,8 +52,9 @@ impl Actor for ClientApprovalController {
|
||||
reply: Some(reply),
|
||||
};
|
||||
|
||||
for user_agent in user_agents.drain(..) {
|
||||
for user_agent in user_agents {
|
||||
actor_ref.link(&user_agent).await;
|
||||
|
||||
let _ = user_agent
|
||||
.tell(BeginNewClientApproval {
|
||||
client: client.clone(),
|
||||
@@ -85,7 +86,7 @@ impl Actor for ClientApprovalController {
|
||||
#[messages]
|
||||
impl ClientApprovalController {
|
||||
#[message(ctx)]
|
||||
pub async fn client_approval_answer(&mut self, approved: bool, ctx: &mut Context<Self, ()>) {
|
||||
pub fn client_approval_answer(&mut self, approved: bool, ctx: &mut Context<Self, ()>) {
|
||||
if !approved {
|
||||
// Denial wins immediately regardless of other pending responses.
|
||||
self.send_reply(Ok(false));
|
||||
|
||||
@@ -92,7 +92,7 @@ impl FlowCoordinator {
|
||||
}
|
||||
|
||||
#[message(ctx)]
|
||||
pub async fn request_client_approval(
|
||||
pub fn request_client_approval(
|
||||
&mut self,
|
||||
client: ClientProfile,
|
||||
ctx: &mut Context<Self, DelegatedReply<Result<bool, ApprovalError>>>,
|
||||
|
||||
@@ -36,19 +36,12 @@ enum State {
|
||||
}
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum Error {
|
||||
pub enum KeyHolderError {
|
||||
#[error("Keyholder is already bootstrapped")]
|
||||
AlreadyBootstrapped,
|
||||
#[error("Keyholder is not bootstrapped")]
|
||||
NotBootstrapped,
|
||||
#[error("Invalid key provided")]
|
||||
InvalidKey,
|
||||
|
||||
#[error("Requested aead entry not found")]
|
||||
NotFound,
|
||||
|
||||
#[error("Encryption error: {0}")]
|
||||
Encryption(#[from] chacha20poly1305::aead::Error),
|
||||
#[error("Broken database")]
|
||||
BrokenDatabase,
|
||||
|
||||
#[error("Database error: {0}")]
|
||||
DatabaseConnection(#[from] db::PoolError),
|
||||
@@ -56,11 +49,21 @@ pub enum Error {
|
||||
#[error("Database transaction error: {0}")]
|
||||
DatabaseTransaction(#[from] diesel::result::Error),
|
||||
|
||||
#[error("Broken database")]
|
||||
BrokenDatabase,
|
||||
#[error("Encryption error: {0}")]
|
||||
Encryption(#[from] chacha20poly1305::aead::Error),
|
||||
|
||||
#[error("Invalid key provided")]
|
||||
InvalidKey,
|
||||
|
||||
#[error("Keyholder is not bootstrapped")]
|
||||
NotBootstrapped,
|
||||
|
||||
#[error("Requested aead entry not found")]
|
||||
NotFound,
|
||||
}
|
||||
|
||||
/// Manages vault root key and tracks current state of the vault (bootstrapped/unbootstrapped, sealed/unsealed).
|
||||
///
|
||||
/// Provides API for encrypting and decrypting data using the vault root key.
|
||||
/// Abstraction over database to make sure nonces are never reused and encryption keys are never exposed in plaintext outside of this actor.
|
||||
#[derive(Actor)]
|
||||
@@ -71,7 +74,7 @@ pub struct KeyHolder {
|
||||
|
||||
#[messages]
|
||||
impl KeyHolder {
|
||||
pub async fn new(db: db::DatabasePool) -> Result<Self, Error> {
|
||||
pub async fn new(db: db::DatabasePool) -> Result<Self, KeyHolderError> {
|
||||
let state = {
|
||||
let mut conn = db.get().await?;
|
||||
|
||||
@@ -94,7 +97,10 @@ impl KeyHolder {
|
||||
|
||||
// Exclusive transaction to avoid race condtions if multiple keyholders write
|
||||
// additional layer of protection against nonce-reuse
|
||||
async fn get_new_nonce(pool: &db::DatabasePool, root_key_id: i32) -> Result<Nonce, Error> {
|
||||
async fn get_new_nonce(
|
||||
pool: &db::DatabasePool,
|
||||
root_key_id: i32,
|
||||
) -> Result<Nonce, KeyHolderError> {
|
||||
let mut conn = pool.get().await?;
|
||||
|
||||
let nonce = conn
|
||||
@@ -106,12 +112,12 @@ impl KeyHolder {
|
||||
.first(conn)
|
||||
.await?;
|
||||
|
||||
let mut nonce = Nonce::try_from(current_nonce.as_slice()).map_err(|_| {
|
||||
let mut nonce = Nonce::try_from(current_nonce.as_slice()).map_err(|()| {
|
||||
error!(
|
||||
"Broken database: invalid nonce for root key history id={}",
|
||||
root_key_id
|
||||
);
|
||||
Error::BrokenDatabase
|
||||
KeyHolderError::BrokenDatabase
|
||||
})?;
|
||||
nonce.increment();
|
||||
|
||||
@@ -121,7 +127,7 @@ impl KeyHolder {
|
||||
.execute(conn)
|
||||
.await?;
|
||||
|
||||
Result::<_, Error>::Ok(nonce)
|
||||
Result::<_, KeyHolderError>::Ok(nonce)
|
||||
})
|
||||
})
|
||||
.await?;
|
||||
@@ -130,9 +136,12 @@ impl KeyHolder {
|
||||
}
|
||||
|
||||
#[message]
|
||||
pub async fn bootstrap(&mut self, seal_key_raw: SafeCell<Vec<u8>>) -> Result<(), Error> {
|
||||
pub async fn bootstrap(
|
||||
&mut self,
|
||||
seal_key_raw: SafeCell<Vec<u8>>,
|
||||
) -> Result<(), KeyHolderError> {
|
||||
if !matches!(self.state, State::Unbootstrapped) {
|
||||
return Err(Error::AlreadyBootstrapped);
|
||||
return Err(KeyHolderError::AlreadyBootstrapped);
|
||||
}
|
||||
let salt = v1::generate_salt();
|
||||
let mut seal_key = derive_key(seal_key_raw, &salt);
|
||||
@@ -148,7 +157,7 @@ impl KeyHolder {
|
||||
.encrypt(&root_key_nonce, v1::ROOT_KEY_TAG, root_key_reader)
|
||||
.map_err(|err| {
|
||||
error!(?err, "Fatal bootstrap error");
|
||||
Error::Encryption(err)
|
||||
KeyHolderError::Encryption(err)
|
||||
})
|
||||
})?;
|
||||
|
||||
@@ -192,12 +201,15 @@ impl KeyHolder {
|
||||
}
|
||||
|
||||
#[message]
|
||||
pub async fn try_unseal(&mut self, seal_key_raw: SafeCell<Vec<u8>>) -> Result<(), Error> {
|
||||
pub async fn try_unseal(
|
||||
&mut self,
|
||||
seal_key_raw: SafeCell<Vec<u8>>,
|
||||
) -> Result<(), KeyHolderError> {
|
||||
let State::Sealed {
|
||||
root_key_history_id,
|
||||
} = &self.state
|
||||
else {
|
||||
return Err(Error::NotBootstrapped);
|
||||
return Err(KeyHolderError::NotBootstrapped);
|
||||
};
|
||||
|
||||
// We don't want to hold connection while doing expensive KDF work
|
||||
@@ -213,16 +225,16 @@ impl KeyHolder {
|
||||
let salt = ¤t_key.salt;
|
||||
let salt = v1::Salt::try_from(salt.as_slice()).map_err(|_| {
|
||||
error!("Broken database: invalid salt for root key");
|
||||
Error::BrokenDatabase
|
||||
KeyHolderError::BrokenDatabase
|
||||
})?;
|
||||
let mut seal_key = derive_key(seal_key_raw, &salt);
|
||||
|
||||
let mut root_key = SafeCell::new(current_key.ciphertext.clone());
|
||||
|
||||
let nonce = v1::Nonce::try_from(current_key.root_key_encryption_nonce.as_slice()).map_err(
|
||||
|_| {
|
||||
let nonce = Nonce::try_from(current_key.root_key_encryption_nonce.as_slice()).map_err(
|
||||
|()| {
|
||||
error!("Broken database: invalid nonce for root key");
|
||||
Error::BrokenDatabase
|
||||
KeyHolderError::BrokenDatabase
|
||||
},
|
||||
)?;
|
||||
|
||||
@@ -230,14 +242,14 @@ impl KeyHolder {
|
||||
.decrypt_in_place(&nonce, v1::ROOT_KEY_TAG, &mut root_key)
|
||||
.map_err(|err| {
|
||||
error!(?err, "Failed to unseal root key: invalid seal key");
|
||||
Error::InvalidKey
|
||||
KeyHolderError::InvalidKey
|
||||
})?;
|
||||
|
||||
self.state = State::Unsealed {
|
||||
root_key_history_id: current_key.id,
|
||||
root_key: KeyCell::try_from(root_key).map_err(|err| {
|
||||
error!(?err, "Broken database: invalid encryption key size");
|
||||
Error::BrokenDatabase
|
||||
KeyHolderError::BrokenDatabase
|
||||
})?,
|
||||
};
|
||||
|
||||
@@ -247,9 +259,9 @@ impl KeyHolder {
|
||||
}
|
||||
|
||||
#[message]
|
||||
pub async fn decrypt(&mut self, aead_id: i32) -> Result<SafeCell<Vec<u8>>, Error> {
|
||||
pub async fn decrypt(&mut self, aead_id: i32) -> Result<SafeCell<Vec<u8>>, KeyHolderError> {
|
||||
let State::Unsealed { root_key, .. } = &mut self.state else {
|
||||
return Err(Error::NotBootstrapped);
|
||||
return Err(KeyHolderError::NotBootstrapped);
|
||||
};
|
||||
|
||||
let row: models::AeadEncrypted = {
|
||||
@@ -260,15 +272,15 @@ impl KeyHolder {
|
||||
.first(&mut conn)
|
||||
.await
|
||||
.optional()?
|
||||
.ok_or(Error::NotFound)?
|
||||
.ok_or(KeyHolderError::NotFound)?
|
||||
};
|
||||
|
||||
let nonce = v1::Nonce::try_from(row.current_nonce.as_slice()).map_err(|_| {
|
||||
let nonce = Nonce::try_from(row.current_nonce.as_slice()).map_err(|()| {
|
||||
error!(
|
||||
"Broken database: invalid nonce for aead_encrypted id={}",
|
||||
aead_id
|
||||
);
|
||||
Error::BrokenDatabase
|
||||
KeyHolderError::BrokenDatabase
|
||||
})?;
|
||||
let mut output = SafeCell::new(row.ciphertext);
|
||||
root_key.decrypt_in_place(&nonce, v1::TAG, &mut output)?;
|
||||
@@ -277,14 +289,17 @@ impl KeyHolder {
|
||||
|
||||
// Creates new `aead_encrypted` entry in the database and returns it's ID
|
||||
#[message]
|
||||
pub async fn create_new(&mut self, mut plaintext: SafeCell<Vec<u8>>) -> Result<i32, Error> {
|
||||
pub async fn create_new(
|
||||
&mut self,
|
||||
mut plaintext: SafeCell<Vec<u8>>,
|
||||
) -> Result<i32, KeyHolderError> {
|
||||
let State::Unsealed {
|
||||
root_key,
|
||||
root_key_history_id,
|
||||
..
|
||||
} = &mut self.state
|
||||
else {
|
||||
return Err(Error::NotBootstrapped);
|
||||
return Err(KeyHolderError::NotBootstrapped);
|
||||
};
|
||||
|
||||
// Order matters here - `get_new_nonce` acquires connection, so we need to call it before next acquire
|
||||
@@ -320,21 +335,19 @@ impl KeyHolder {
|
||||
}
|
||||
|
||||
#[message]
|
||||
pub fn sign_integrity(&mut self, mac_input: Vec<u8>) -> Result<(i32, Vec<u8>), Error> {
|
||||
pub fn sign_integrity(&mut self, mac_input: Vec<u8>) -> Result<(i32, Vec<u8>), KeyHolderError> {
|
||||
let State::Unsealed {
|
||||
root_key,
|
||||
root_key_history_id,
|
||||
} = &mut self.state
|
||||
else {
|
||||
return Err(Error::NotBootstrapped);
|
||||
return Err(KeyHolderError::NotBootstrapped);
|
||||
};
|
||||
|
||||
let mut hmac = root_key
|
||||
.0
|
||||
.read_inline(|k| match HmacSha256::new_from_slice(k) {
|
||||
Ok(v) => v,
|
||||
Err(_) => unreachable!("HMAC accepts keys of any size"),
|
||||
});
|
||||
let mut hmac = root_key.0.read_inline(|k| {
|
||||
HmacSha256::new_from_slice(k)
|
||||
.unwrap_or_else(|_| unreachable!("HMAC accepts keys of any size"))
|
||||
});
|
||||
hmac.update(&root_key_history_id.to_be_bytes());
|
||||
hmac.update(&mac_input);
|
||||
|
||||
@@ -348,25 +361,23 @@ impl KeyHolder {
|
||||
mac_input: Vec<u8>,
|
||||
expected_mac: Vec<u8>,
|
||||
key_version: i32,
|
||||
) -> Result<bool, Error> {
|
||||
) -> Result<bool, KeyHolderError> {
|
||||
let State::Unsealed {
|
||||
root_key,
|
||||
root_key_history_id,
|
||||
} = &mut self.state
|
||||
else {
|
||||
return Err(Error::NotBootstrapped);
|
||||
return Err(KeyHolderError::NotBootstrapped);
|
||||
};
|
||||
|
||||
if *root_key_history_id != key_version {
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
let mut hmac = root_key
|
||||
.0
|
||||
.read_inline(|k| match HmacSha256::new_from_slice(k) {
|
||||
Ok(v) => v,
|
||||
Err(_) => unreachable!("HMAC accepts keys of any size"),
|
||||
});
|
||||
let mut hmac = root_key.0.read_inline(|k| {
|
||||
HmacSha256::new_from_slice(k)
|
||||
.unwrap_or_else(|_| unreachable!("HMAC accepts keys of any size"))
|
||||
});
|
||||
hmac.update(&key_version.to_be_bytes());
|
||||
hmac.update(&mac_input);
|
||||
|
||||
@@ -374,13 +385,13 @@ impl KeyHolder {
|
||||
}
|
||||
|
||||
#[message]
|
||||
pub fn seal(&mut self) -> Result<(), Error> {
|
||||
pub fn seal(&mut self) -> Result<(), KeyHolderError> {
|
||||
let State::Unsealed {
|
||||
root_key_history_id,
|
||||
..
|
||||
} = &self.state
|
||||
else {
|
||||
return Err(Error::NotBootstrapped);
|
||||
return Err(KeyHolderError::NotBootstrapped);
|
||||
};
|
||||
self.state = State::Sealed {
|
||||
root_key_history_id: *root_key_history_id,
|
||||
@@ -391,12 +402,7 @@ impl KeyHolder {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use diesel::SelectableHelper;
|
||||
|
||||
use diesel_async::RunQueryDsl;
|
||||
|
||||
use crate::db::{self};
|
||||
use arbiter_crypto::safecell::{SafeCell, SafeCellHandle as _};
|
||||
use arbiter_crypto::safecell::SafeCellHandle as _;
|
||||
|
||||
use super::*;
|
||||
|
||||
@@ -412,12 +418,12 @@ mod tests {
|
||||
async fn nonce_monotonic_even_when_nonce_allocation_interleaves() {
|
||||
let db = db::create_test_pool().await;
|
||||
let mut actor = bootstrapped_actor(&db).await;
|
||||
let root_key_history_id = match actor.state {
|
||||
State::Unsealed {
|
||||
root_key_history_id,
|
||||
..
|
||||
} => root_key_history_id,
|
||||
_ => panic!("expected unsealed state"),
|
||||
let State::Unsealed {
|
||||
root_key_history_id,
|
||||
..
|
||||
} = actor.state
|
||||
else {
|
||||
panic!("expected unsealed state");
|
||||
};
|
||||
|
||||
let n1 = KeyHolder::get_new_nonce(&db, root_key_history_id)
|
||||
@@ -429,8 +435,8 @@ mod tests {
|
||||
assert!(n2.to_vec() > n1.to_vec(), "nonce must increase");
|
||||
|
||||
let mut conn = db.get().await.unwrap();
|
||||
let root_row: models::RootKeyHistory = schema::root_key_history::table
|
||||
.select(models::RootKeyHistory::as_select())
|
||||
let root_row = schema::root_key_history::table
|
||||
.select(RootKeyHistory::as_select())
|
||||
.first(&mut conn)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
@@ -11,18 +11,18 @@ use crate::{
|
||||
|
||||
pub mod bootstrap;
|
||||
pub mod client;
|
||||
mod evm;
|
||||
pub mod evm;
|
||||
pub mod flow_coordinator;
|
||||
pub mod keyholder;
|
||||
pub mod user_agent;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum SpawnError {
|
||||
pub enum GlobalActorsSpawnError {
|
||||
#[error("Failed to spawn Bootstrapper actor")]
|
||||
Bootstrapper(#[from] bootstrap::Error),
|
||||
Bootstrapper(#[from] bootstrap::BootstrappError),
|
||||
|
||||
#[error("Failed to spawn KeyHolder actor")]
|
||||
KeyHolder(#[from] keyholder::Error),
|
||||
KeyHolder(#[from] keyholder::KeyHolderError),
|
||||
}
|
||||
|
||||
/// Long-lived actors that are shared across all connections and handle global state and operations
|
||||
@@ -35,7 +35,7 @@ pub struct GlobalActors {
|
||||
}
|
||||
|
||||
impl GlobalActors {
|
||||
pub async fn spawn(db: db::DatabasePool) -> Result<Self, SpawnError> {
|
||||
pub async fn spawn(db: db::DatabasePool) -> Result<Self, GlobalActorsSpawnError> {
|
||||
let key_holder = KeyHolder::spawn(KeyHolder::new(db.clone()).await?);
|
||||
Ok(Self {
|
||||
bootstrapper: Bootstrapper::spawn(Bootstrapper::new(&db).await?),
|
||||
|
||||
@@ -7,7 +7,9 @@ use crate::actors::user_agent::{
|
||||
auth::state::{AuthContext, AuthStateMachine},
|
||||
};
|
||||
mod state;
|
||||
use state::*;
|
||||
use state::{
|
||||
AuthError, AuthEvents, AuthStates, BootstrapAuthRequest, ChallengeRequest, ChallengeSolution,
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Inbound {
|
||||
|
||||
@@ -204,14 +204,14 @@ pub struct AuthContext<'a, T> {
|
||||
}
|
||||
|
||||
impl<'a, T> AuthContext<'a, T> {
|
||||
pub fn new(conn: &'a mut UserAgentConnection, transport: T) -> Self {
|
||||
pub const fn new(conn: &'a mut UserAgentConnection, transport: T) -> Self {
|
||||
Self { conn, transport }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> AuthStateMachineContext for AuthContext<'_, T>
|
||||
where
|
||||
T: Bi<super::Inbound, Result<super::Outbound, Error>> + Send,
|
||||
T: Bi<super::Inbound, Result<Outbound, Error>> + Send,
|
||||
{
|
||||
type Error = Error;
|
||||
|
||||
@@ -237,8 +237,6 @@ where
|
||||
})
|
||||
}
|
||||
|
||||
#[allow(missing_docs)]
|
||||
#[allow(clippy::result_unit_err)]
|
||||
async fn verify_bootstrap_token(
|
||||
&mut self,
|
||||
BootstrapAuthRequest { pubkey, token }: BootstrapAuthRequest,
|
||||
@@ -261,28 +259,23 @@ where
|
||||
return Err(Error::InvalidBootstrapToken);
|
||||
}
|
||||
|
||||
match token_ok {
|
||||
true => {
|
||||
register_key(&self.conn.db, &self.conn.actors.key_holder, &pubkey).await?;
|
||||
self.transport
|
||||
.send(Ok(Outbound::AuthSuccess))
|
||||
.await
|
||||
.map_err(|_| Error::Transport)?;
|
||||
Ok(pubkey)
|
||||
}
|
||||
false => {
|
||||
error!("Invalid bootstrap token provided");
|
||||
self.transport
|
||||
.send(Err(Error::InvalidBootstrapToken))
|
||||
.await
|
||||
.map_err(|_| Error::Transport)?;
|
||||
Err(Error::InvalidBootstrapToken)
|
||||
}
|
||||
if token_ok {
|
||||
register_key(&self.conn.db, &self.conn.actors.key_holder, &pubkey).await?;
|
||||
self.transport
|
||||
.send(Ok(Outbound::AuthSuccess))
|
||||
.await
|
||||
.map_err(|_| Error::Transport)?;
|
||||
Ok(pubkey)
|
||||
} else {
|
||||
error!("Invalid bootstrap token provided");
|
||||
self.transport
|
||||
.send(Err(Error::InvalidBootstrapToken))
|
||||
.await
|
||||
.map_err(|_| Error::Transport)?;
|
||||
Err(Error::InvalidBootstrapToken)
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(missing_docs)]
|
||||
#[allow(clippy::unused_unit)]
|
||||
async fn verify_solution(
|
||||
&mut self,
|
||||
ChallengeContext {
|
||||
@@ -291,28 +284,25 @@ where
|
||||
}: &ChallengeContext,
|
||||
ChallengeSolution { solution }: ChallengeSolution,
|
||||
) -> Result<authn::PublicKey, Self::Error> {
|
||||
let signature = authn::Signature::try_from(solution.as_slice()).map_err(|_| {
|
||||
let signature = authn::Signature::try_from(solution.as_slice()).map_err(|()| {
|
||||
error!("Failed to decode signature in challenge solution");
|
||||
Error::InvalidChallengeSolution
|
||||
})?;
|
||||
|
||||
let valid = key.verify(*challenge_nonce, USERAGENT_CONTEXT, &signature);
|
||||
|
||||
match valid {
|
||||
true => {
|
||||
self.transport
|
||||
.send(Ok(Outbound::AuthSuccess))
|
||||
.await
|
||||
.map_err(|_| Error::Transport)?;
|
||||
Ok(key.clone())
|
||||
}
|
||||
false => {
|
||||
self.transport
|
||||
.send(Err(Error::InvalidChallengeSolution))
|
||||
.await
|
||||
.map_err(|_| Error::Transport)?;
|
||||
Err(Error::InvalidChallengeSolution)
|
||||
}
|
||||
if valid {
|
||||
self.transport
|
||||
.send(Ok(Outbound::AuthSuccess))
|
||||
.await
|
||||
.map_err(|_| Error::Transport)?;
|
||||
Ok(key.clone())
|
||||
} else {
|
||||
self.transport
|
||||
.send(Err(Error::InvalidChallengeSolution))
|
||||
.await
|
||||
.map_err(|_| Error::Transport)?;
|
||||
Err(Error::InvalidChallengeSolution)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ pub struct UserAgentConnection {
|
||||
}
|
||||
|
||||
impl UserAgentConnection {
|
||||
pub fn new(db: db::DatabasePool, actors: GlobalActors) -> Self {
|
||||
pub const fn new(db: db::DatabasePool, actors: GlobalActors) -> Self {
|
||||
Self { db, actors }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,28 +17,28 @@ mod state;
|
||||
use state::{DummyContext, UserAgentEvents, UserAgentStateMachine};
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum Error {
|
||||
#[error("State transition failed")]
|
||||
State,
|
||||
|
||||
pub enum UserAgentSessionError {
|
||||
#[error("Internal error: {message}")]
|
||||
Internal { message: Cow<'static, str> },
|
||||
|
||||
#[error("State transition failed")]
|
||||
State,
|
||||
}
|
||||
|
||||
impl From<crate::db::PoolError> for Error {
|
||||
impl From<crate::db::PoolError> for UserAgentSessionError {
|
||||
fn from(err: crate::db::PoolError) -> Self {
|
||||
error!(?err, "Database pool error");
|
||||
Self::internal("Database pool error")
|
||||
}
|
||||
}
|
||||
impl From<diesel::result::Error> for Error {
|
||||
impl From<diesel::result::Error> for UserAgentSessionError {
|
||||
fn from(err: diesel::result::Error) -> Self {
|
||||
error!(?err, "Database error");
|
||||
Self::internal("Database error")
|
||||
}
|
||||
}
|
||||
|
||||
impl Error {
|
||||
impl UserAgentSessionError {
|
||||
pub fn internal(message: impl Into<Cow<'static, str>>) -> Self {
|
||||
Self::Internal {
|
||||
message: message.into(),
|
||||
@@ -67,7 +67,7 @@ impl UserAgentSession {
|
||||
props,
|
||||
state: UserAgentStateMachine::new(DummyContext),
|
||||
sender,
|
||||
pending_client_approvals: Default::default(),
|
||||
pending_client_approvals: HashMap::default(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -87,10 +87,10 @@ impl UserAgentSession {
|
||||
Self::new(UserAgentConnection::new(db, actors), Box::new(DummySender))
|
||||
}
|
||||
|
||||
fn transition(&mut self, event: UserAgentEvents) -> Result<(), Error> {
|
||||
fn transition(&mut self, event: UserAgentEvents) -> Result<(), UserAgentSessionError> {
|
||||
self.state.process_event(event).map_err(|e| {
|
||||
error!(?e, "State transition failed");
|
||||
Error::State
|
||||
UserAgentSessionError::State
|
||||
})?;
|
||||
Ok(())
|
||||
}
|
||||
@@ -132,11 +132,11 @@ impl UserAgentSession {
|
||||
impl Actor for UserAgentSession {
|
||||
type Args = Self;
|
||||
|
||||
type Error = Error;
|
||||
type Error = UserAgentSessionError;
|
||||
|
||||
async fn on_start(
|
||||
args: Self::Args,
|
||||
this: kameo::prelude::ActorRef<Self>,
|
||||
this: ActorRef<Self>,
|
||||
) -> Result<Self, Self::Error> {
|
||||
args.props
|
||||
.actors
|
||||
@@ -150,7 +150,9 @@ impl Actor for UserAgentSession {
|
||||
?err,
|
||||
"Failed to register user agent connection with flow coordinator"
|
||||
);
|
||||
Error::internal("Failed to register user agent connection with flow coordinator")
|
||||
UserAgentSessionError::internal(
|
||||
"Failed to register user agent connection with flow coordinator",
|
||||
)
|
||||
})?;
|
||||
Ok(args)
|
||||
}
|
||||
|
||||
@@ -11,12 +11,13 @@ use diesel_async::{AsyncConnection, RunQueryDsl};
|
||||
use kameo::error::SendError;
|
||||
use kameo::messages;
|
||||
use kameo::prelude::Context;
|
||||
use thiserror::Error;
|
||||
use tracing::{error, info};
|
||||
use x25519_dalek::{EphemeralSecret, PublicKey};
|
||||
|
||||
use crate::actors::flow_coordinator::client_connect_approval::ClientApprovalAnswer;
|
||||
use crate::{actors::flow_coordinator::client_connect_approval::ClientApprovalAnswer, evm::policies::SharedGrantSettings};
|
||||
use crate::actors::keyholder::KeyHolderState;
|
||||
use crate::actors::user_agent::session::Error;
|
||||
use crate::actors::user_agent::session::UserAgentSessionError;
|
||||
use crate::actors::{
|
||||
evm::{
|
||||
ClientSignTransaction, Generate, ListWallets, SignTransactionError as EvmSignError,
|
||||
@@ -34,10 +35,12 @@ use crate::db::models::{
|
||||
use crate::evm::policies::{Grant, SpecificGrant};
|
||||
|
||||
impl UserAgentSession {
|
||||
fn take_unseal_secret(&mut self) -> Result<(EphemeralSecret, PublicKey), Error> {
|
||||
fn take_unseal_secret(&self) -> Result<(EphemeralSecret, PublicKey), UserAgentSessionError> {
|
||||
let UserAgentStates::WaitingForUnsealKey(unseal_context) = self.state.state() else {
|
||||
error!("Received encrypted key in invalid state");
|
||||
return Err(Error::internal("Invalid state for unseal encrypted key"));
|
||||
return Err(UserAgentSessionError::internal(
|
||||
"Invalid state for unseal encrypted key",
|
||||
));
|
||||
};
|
||||
|
||||
let ephemeral_secret = {
|
||||
@@ -47,13 +50,14 @@ impl UserAgentSession {
|
||||
)]
|
||||
let mut secret_lock = unseal_context.secret.lock().unwrap();
|
||||
let secret = secret_lock.take();
|
||||
match secret {
|
||||
Some(secret) => secret,
|
||||
None => {
|
||||
drop(secret_lock);
|
||||
error!("Ephemeral secret already taken");
|
||||
return Err(Error::internal("Ephemeral secret already taken"));
|
||||
}
|
||||
if let Some(secret) = secret {
|
||||
secret
|
||||
} else {
|
||||
drop(secret_lock);
|
||||
error!("Ephemeral secret already taken");
|
||||
return Err(UserAgentSessionError::internal(
|
||||
"Ephemeral secret already taken",
|
||||
));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -79,7 +83,7 @@ impl UserAgentSession {
|
||||
});
|
||||
|
||||
match decryption_result {
|
||||
Ok(_) => Ok(key_buffer),
|
||||
Ok(()) => Ok(key_buffer),
|
||||
Err(err) => {
|
||||
error!(?err, "Failed to decrypt encrypted key material");
|
||||
Err(())
|
||||
@@ -97,7 +101,7 @@ pub enum UnsealError {
|
||||
#[error("Invalid key provided for unsealing")]
|
||||
InvalidKey,
|
||||
#[error("Internal error during unsealing process")]
|
||||
General(#[from] super::Error),
|
||||
General(#[from] UserAgentSessionError),
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
@@ -108,7 +112,7 @@ pub enum BootstrapError {
|
||||
AlreadyBootstrapped,
|
||||
|
||||
#[error("Internal error during bootstrapping process")]
|
||||
General(#[from] super::Error),
|
||||
General(#[from] UserAgentSessionError),
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
@@ -132,16 +136,16 @@ pub enum GrantMutationError {
|
||||
#[messages]
|
||||
impl UserAgentSession {
|
||||
#[message]
|
||||
pub async fn handle_unseal_request(
|
||||
pub fn handle_unseal_request(
|
||||
&mut self,
|
||||
client_pubkey: x25519_dalek::PublicKey,
|
||||
) -> Result<UnsealStartResponse, Error> {
|
||||
client_pubkey: PublicKey,
|
||||
) -> Result<UnsealStartResponse, UserAgentSessionError> {
|
||||
let secret = EphemeralSecret::random();
|
||||
let public_key = PublicKey::from(&secret);
|
||||
|
||||
self.transition(UserAgentEvents::UnsealRequest(UnsealContext {
|
||||
secret: Mutex::new(Some(secret)),
|
||||
client_public_key: client_pubkey,
|
||||
secret: Mutex::new(Some(secret)),
|
||||
}))?;
|
||||
|
||||
Ok(UnsealStartResponse {
|
||||
@@ -158,27 +162,24 @@ impl UserAgentSession {
|
||||
) -> Result<(), UnsealError> {
|
||||
let (ephemeral_secret, client_public_key) = match self.take_unseal_secret() {
|
||||
Ok(values) => values,
|
||||
Err(Error::State) => {
|
||||
Err(UserAgentSessionError::State) => {
|
||||
self.transition(UserAgentEvents::ReceivedInvalidKey)?;
|
||||
return Err(UnsealError::InvalidKey);
|
||||
}
|
||||
Err(_err) => {
|
||||
return Err(Error::internal("Failed to take unseal secret").into());
|
||||
return Err(UserAgentSessionError::internal("Failed to take unseal secret").into());
|
||||
}
|
||||
};
|
||||
|
||||
let seal_key_buffer = match Self::decrypt_client_key_material(
|
||||
let Ok(seal_key_buffer) = Self::decrypt_client_key_material(
|
||||
ephemeral_secret,
|
||||
client_public_key,
|
||||
&nonce,
|
||||
&ciphertext,
|
||||
&associated_data,
|
||||
) {
|
||||
Ok(buffer) => buffer,
|
||||
Err(()) => {
|
||||
self.transition(UserAgentEvents::ReceivedInvalidKey)?;
|
||||
return Err(UnsealError::InvalidKey);
|
||||
}
|
||||
) else {
|
||||
self.transition(UserAgentEvents::ReceivedInvalidKey)?;
|
||||
return Err(UnsealError::InvalidKey);
|
||||
};
|
||||
|
||||
match self
|
||||
@@ -190,12 +191,12 @@ impl UserAgentSession {
|
||||
})
|
||||
.await
|
||||
{
|
||||
Ok(_) => {
|
||||
Ok(()) => {
|
||||
info!("Successfully unsealed key with client-provided key");
|
||||
self.transition(UserAgentEvents::ReceivedValidKey)?;
|
||||
Ok(())
|
||||
}
|
||||
Err(SendError::HandlerError(keyholder::Error::InvalidKey)) => {
|
||||
Err(SendError::HandlerError(keyholder::KeyHolderError::InvalidKey)) => {
|
||||
self.transition(UserAgentEvents::ReceivedInvalidKey)?;
|
||||
Err(UnsealError::InvalidKey)
|
||||
}
|
||||
@@ -207,7 +208,7 @@ impl UserAgentSession {
|
||||
Err(err) => {
|
||||
error!(?err, "Failed to send unseal request to keyholder");
|
||||
self.transition(UserAgentEvents::ReceivedInvalidKey)?;
|
||||
Err(Error::internal("Vault actor error").into())
|
||||
Err(UserAgentSessionError::internal("Vault actor error").into())
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -221,25 +222,22 @@ impl UserAgentSession {
|
||||
) -> Result<(), BootstrapError> {
|
||||
let (ephemeral_secret, client_public_key) = match self.take_unseal_secret() {
|
||||
Ok(values) => values,
|
||||
Err(Error::State) => {
|
||||
Err(UserAgentSessionError::State) => {
|
||||
self.transition(UserAgentEvents::ReceivedInvalidKey)?;
|
||||
return Err(BootstrapError::InvalidKey);
|
||||
}
|
||||
Err(err) => return Err(err.into()),
|
||||
};
|
||||
|
||||
let seal_key_buffer = match Self::decrypt_client_key_material(
|
||||
let Ok(seal_key_buffer) = Self::decrypt_client_key_material(
|
||||
ephemeral_secret,
|
||||
client_public_key,
|
||||
&nonce,
|
||||
&ciphertext,
|
||||
&associated_data,
|
||||
) {
|
||||
Ok(buffer) => buffer,
|
||||
Err(()) => {
|
||||
self.transition(UserAgentEvents::ReceivedInvalidKey)?;
|
||||
return Err(BootstrapError::InvalidKey);
|
||||
}
|
||||
) else {
|
||||
self.transition(UserAgentEvents::ReceivedInvalidKey)?;
|
||||
return Err(BootstrapError::InvalidKey);
|
||||
};
|
||||
|
||||
match self
|
||||
@@ -251,12 +249,12 @@ impl UserAgentSession {
|
||||
})
|
||||
.await
|
||||
{
|
||||
Ok(_) => {
|
||||
Ok(()) => {
|
||||
info!("Successfully bootstrapped vault with client-provided key");
|
||||
self.transition(UserAgentEvents::ReceivedValidKey)?;
|
||||
Ok(())
|
||||
}
|
||||
Err(SendError::HandlerError(keyholder::Error::AlreadyBootstrapped)) => {
|
||||
Err(SendError::HandlerError(keyholder::KeyHolderError::AlreadyBootstrapped)) => {
|
||||
self.transition(UserAgentEvents::ReceivedInvalidKey)?;
|
||||
Err(BootstrapError::AlreadyBootstrapped)
|
||||
}
|
||||
@@ -268,7 +266,7 @@ impl UserAgentSession {
|
||||
Err(err) => {
|
||||
error!(?err, "Failed to send bootstrap request to keyholder");
|
||||
self.transition(UserAgentEvents::ReceivedInvalidKey)?;
|
||||
Err(BootstrapError::General(Error::internal(
|
||||
Err(BootstrapError::General(UserAgentSessionError::internal(
|
||||
"Vault actor error",
|
||||
)))
|
||||
}
|
||||
@@ -279,14 +277,16 @@ impl UserAgentSession {
|
||||
#[messages]
|
||||
impl UserAgentSession {
|
||||
#[message]
|
||||
pub(crate) async fn handle_query_vault_state(&mut self) -> Result<KeyHolderState, Error> {
|
||||
pub(crate) async fn handle_query_vault_state(
|
||||
&mut self,
|
||||
) -> Result<KeyHolderState, UserAgentSessionError> {
|
||||
use crate::actors::keyholder::GetState;
|
||||
|
||||
let vault_state = match self.props.actors.key_holder.ask(GetState {}).await {
|
||||
Ok(state) => state,
|
||||
Err(err) => {
|
||||
error!(?err, actor = "useragent", "keyholder.query.failed");
|
||||
return Err(Error::internal("Vault is in broken state"));
|
||||
return Err(UserAgentSessionError::internal("Vault is in broken state"));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -297,26 +297,32 @@ impl UserAgentSession {
|
||||
#[messages]
|
||||
impl UserAgentSession {
|
||||
#[message]
|
||||
pub(crate) async fn handle_evm_wallet_create(&mut self) -> Result<(i32, Address), Error> {
|
||||
pub(crate) async fn handle_evm_wallet_create(
|
||||
&mut self,
|
||||
) -> Result<(i32, Address), UserAgentSessionError> {
|
||||
match self.props.actors.evm.ask(Generate {}).await {
|
||||
Ok(address) => Ok(address),
|
||||
Err(SendError::HandlerError(err)) => Err(Error::internal(format!(
|
||||
Err(SendError::HandlerError(err)) => Err(UserAgentSessionError::internal(format!(
|
||||
"EVM wallet generation failed: {err}"
|
||||
))),
|
||||
Err(err) => {
|
||||
error!(?err, "EVM actor unreachable during wallet create");
|
||||
Err(Error::internal("EVM actor unreachable"))
|
||||
Err(UserAgentSessionError::internal("EVM actor unreachable"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[message]
|
||||
pub(crate) async fn handle_evm_wallet_list(&mut self) -> Result<Vec<(i32, Address)>, Error> {
|
||||
pub(crate) async fn handle_evm_wallet_list(
|
||||
&mut self,
|
||||
) -> Result<Vec<(i32, Address)>, UserAgentSessionError> {
|
||||
match self.props.actors.evm.ask(ListWallets {}).await {
|
||||
Ok(wallets) => Ok(wallets),
|
||||
Err(err) => {
|
||||
error!(?err, "EVM wallet list failed");
|
||||
Err(Error::internal("Failed to list EVM wallets"))
|
||||
Err(UserAgentSessionError::internal(
|
||||
"Failed to list EVM wallets",
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -325,12 +331,14 @@ impl UserAgentSession {
|
||||
#[messages]
|
||||
impl UserAgentSession {
|
||||
#[message]
|
||||
pub(crate) async fn handle_grant_list(&mut self) -> Result<Vec<Grant<SpecificGrant>>, Error> {
|
||||
pub(crate) async fn handle_grant_list(
|
||||
&mut self,
|
||||
) -> Result<Vec<Grant<SpecificGrant>>, UserAgentSessionError> {
|
||||
match self.props.actors.evm.ask(UseragentListGrants {}).await {
|
||||
Ok(grants) => Ok(grants),
|
||||
Err(err) => {
|
||||
error!(?err, "EVM grant list failed");
|
||||
Err(Error::internal("Failed to list EVM grants"))
|
||||
Err(UserAgentSessionError::internal("Failed to list EVM grants"))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -338,8 +346,8 @@ impl UserAgentSession {
|
||||
#[message]
|
||||
pub(crate) async fn handle_grant_create(
|
||||
&mut self,
|
||||
basic: crate::evm::policies::SharedGrantSettings,
|
||||
grant: crate::evm::policies::SpecificGrant,
|
||||
basic: SharedGrantSettings,
|
||||
grant: SpecificGrant,
|
||||
) -> Result<i32, GrantMutationError> {
|
||||
match self
|
||||
.props
|
||||
@@ -357,6 +365,7 @@ impl UserAgentSession {
|
||||
}
|
||||
|
||||
#[message]
|
||||
#[expect(clippy::unused_async, reason = "false positive")]
|
||||
pub(crate) async fn handle_grant_delete(
|
||||
&mut self,
|
||||
grant_id: i32,
|
||||
@@ -374,7 +383,7 @@ impl UserAgentSession {
|
||||
// Err(GrantMutationError::Internal)
|
||||
// }
|
||||
// }
|
||||
let _ = grant_id;
|
||||
let _ = grant_id;
|
||||
todo!()
|
||||
}
|
||||
|
||||
@@ -411,7 +420,7 @@ impl UserAgentSession {
|
||||
pub(crate) async fn handle_grant_evm_wallet_access(
|
||||
&mut self,
|
||||
entries: Vec<NewEvmWalletAccess>,
|
||||
) -> Result<(), Error> {
|
||||
) -> Result<(), UserAgentSessionError> {
|
||||
let mut conn = self.props.db.get().await?;
|
||||
conn.transaction(|conn| {
|
||||
Box::pin(async move {
|
||||
@@ -425,7 +434,7 @@ impl UserAgentSession {
|
||||
.await?;
|
||||
}
|
||||
|
||||
Result::<_, Error>::Ok(())
|
||||
Result::<_, UserAgentSessionError>::Ok(())
|
||||
})
|
||||
})
|
||||
.await?;
|
||||
@@ -436,7 +445,7 @@ impl UserAgentSession {
|
||||
pub(crate) async fn handle_revoke_evm_wallet_access(
|
||||
&mut self,
|
||||
entries: Vec<i32>,
|
||||
) -> Result<(), Error> {
|
||||
) -> Result<(), UserAgentSessionError> {
|
||||
let mut conn = self.props.db.get().await?;
|
||||
conn.transaction(|conn| {
|
||||
Box::pin(async move {
|
||||
@@ -448,7 +457,7 @@ impl UserAgentSession {
|
||||
.await?;
|
||||
}
|
||||
|
||||
Result::<_, Error>::Ok(())
|
||||
Result::<_, UserAgentSessionError>::Ok(())
|
||||
})
|
||||
})
|
||||
.await?;
|
||||
@@ -458,10 +467,9 @@ impl UserAgentSession {
|
||||
#[message]
|
||||
pub(crate) async fn handle_list_wallet_access(
|
||||
&mut self,
|
||||
) -> Result<Vec<EvmWalletAccess>, Error> {
|
||||
) -> Result<Vec<EvmWalletAccess>, UserAgentSessionError> {
|
||||
let mut conn = self.props.db.get().await?;
|
||||
use crate::db::schema::evm_wallet_access;
|
||||
let access_entries = evm_wallet_access::table
|
||||
let access_entries = crate::db::schema::evm_wallet_access::table
|
||||
.select(EvmWalletAccess::as_select())
|
||||
.load::<_>(&mut conn)
|
||||
.await?;
|
||||
@@ -476,14 +484,14 @@ impl UserAgentSession {
|
||||
&mut self,
|
||||
approved: bool,
|
||||
pubkey: authn::PublicKey,
|
||||
ctx: &mut Context<Self, Result<(), Error>>,
|
||||
) -> Result<(), Error> {
|
||||
let pending_approval = match self.pending_client_approvals.remove(&pubkey.to_bytes()) {
|
||||
Some(approval) => approval,
|
||||
None => {
|
||||
error!("Received client connection response for unknown client");
|
||||
return Err(Error::internal("Unknown client in connection response"));
|
||||
}
|
||||
ctx: &Context<Self, Result<(), UserAgentSessionError>>,
|
||||
) -> Result<(), UserAgentSessionError> {
|
||||
let Some(pending_approval) = self.pending_client_approvals.remove(&pubkey.to_bytes())
|
||||
else {
|
||||
error!("Received client connection response for unknown client");
|
||||
return Err(UserAgentSessionError::internal(
|
||||
"Unknown client in connection response",
|
||||
));
|
||||
};
|
||||
|
||||
pending_approval
|
||||
@@ -495,7 +503,9 @@ impl UserAgentSession {
|
||||
?err,
|
||||
"Failed to send client approval response to controller"
|
||||
);
|
||||
Error::internal("Failed to send client approval response to controller")
|
||||
UserAgentSessionError::internal(
|
||||
"Failed to send client approval response to controller",
|
||||
)
|
||||
})?;
|
||||
|
||||
ctx.actor_ref().unlink(&pending_approval.controller).await;
|
||||
@@ -506,7 +516,7 @@ impl UserAgentSession {
|
||||
#[message]
|
||||
pub(crate) async fn handle_sdk_client_list(
|
||||
&mut self,
|
||||
) -> Result<Vec<(ProgramClient, ProgramClientMetadata)>, Error> {
|
||||
) -> Result<Vec<(ProgramClient, ProgramClientMetadata)>, UserAgentSessionError> {
|
||||
use crate::db::schema::{client_metadata, program_client};
|
||||
let mut conn = self.props.db.get().await?;
|
||||
|
||||
|
||||
@@ -19,8 +19,6 @@ smlang::statemachine!(
|
||||
|
||||
pub struct DummyContext;
|
||||
impl UserAgentStateMachineContext for DummyContext {
|
||||
#[allow(missing_docs)]
|
||||
#[allow(clippy::unused_unit)]
|
||||
fn generate_temp_keypair(&mut self, event_data: UnsealContext) -> Result<UnsealContext, ()> {
|
||||
Ok(event_data)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user