Merge branch 'rpc-auth-and-meta' into 'main'
rpc: authentication and basic handle manipulation See merge request tpo/core/arti!1200
This commit is contained in:
commit
3d4b9aa1b7
|
@ -1,12 +1,13 @@
|
||||||
//! RPC connection support, mainloop, and protocol implementation.
|
//! RPC connection support, mainloop, and protocol implementation.
|
||||||
|
|
||||||
|
mod auth;
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
collections::HashMap,
|
collections::HashMap,
|
||||||
pin::Pin,
|
pin::Pin,
|
||||||
sync::{Arc, Mutex},
|
sync::{Arc, Mutex},
|
||||||
};
|
};
|
||||||
|
|
||||||
use arti_client::TorClient;
|
|
||||||
use asynchronous_codec::JsonCodecError;
|
use asynchronous_codec::JsonCodecError;
|
||||||
use futures::{
|
use futures::{
|
||||||
channel::mpsc,
|
channel::mpsc,
|
||||||
|
@ -17,7 +18,6 @@ use pin_project::pin_project;
|
||||||
use rpc::dispatch::BoxedUpdateSink;
|
use rpc::dispatch::BoxedUpdateSink;
|
||||||
use serde_json::error::Category as JsonErrorCategory;
|
use serde_json::error::Category as JsonErrorCategory;
|
||||||
use tor_async_utils::SinkExt as _;
|
use tor_async_utils::SinkExt as _;
|
||||||
use tor_rtcompat::PreferredRuntime;
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
cancel::{Cancel, CancelHandle},
|
cancel::{Cancel, CancelHandle},
|
||||||
|
@ -32,7 +32,7 @@ use tor_rpcbase as rpc;
|
||||||
///
|
///
|
||||||
/// Tracks information that persists from one request to another.
|
/// Tracks information that persists from one request to another.
|
||||||
pub struct Connection {
|
pub struct Connection {
|
||||||
/// The mutable state of this connection
|
/// The mutable state of this connection.
|
||||||
inner: Mutex<Inner>,
|
inner: Mutex<Inner>,
|
||||||
|
|
||||||
/// Lookup table to find the implementations for methods
|
/// Lookup table to find the implementations for methods
|
||||||
|
@ -92,7 +92,7 @@ impl Connection {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Look up a given object by its object ID relative to this connection.
|
/// Look up a given object by its object ID relative to this connection.
|
||||||
fn lookup_object(
|
pub(crate) fn lookup_object(
|
||||||
self: &Arc<Self>,
|
self: &Arc<Self>,
|
||||||
id: &rpc::ObjectId,
|
id: &rpc::ObjectId,
|
||||||
) -> Result<Arc<dyn rpc::Object>, rpc::LookupError> {
|
) -> Result<Arc<dyn rpc::Object>, rpc::LookupError> {
|
||||||
|
@ -376,108 +376,29 @@ impl rpc::Context for RequestContext {
|
||||||
.insert_weak(object)
|
.insert_weak(object)
|
||||||
.encode()
|
.encode()
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/// A simple temporary method to echo a reply.
|
fn release_owned(&self, id: &rpc::ObjectId) -> Result<(), rpc::LookupError> {
|
||||||
#[derive(Debug, serde::Deserialize, serde::Serialize)]
|
let idx = crate::objmap::GenIdx::try_decode(id)?;
|
||||||
struct Echo {
|
if !idx.is_strong() {
|
||||||
/// A message to echo.
|
return Err(rpc::LookupError::WrongType(id.clone()));
|
||||||
msg: String,
|
}
|
||||||
}
|
|
||||||
rpc::decl_method! { "arti:x-echo" => Echo}
|
|
||||||
impl rpc::Method for Echo {
|
|
||||||
type Output = Echo;
|
|
||||||
type Update = rpc::NoUpdates;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Implementation for calling "echo" on a TorClient.
|
let removed = self
|
||||||
///
|
.conn
|
||||||
/// TODO RPC: Remove this. It shouldn't exist.
|
.inner
|
||||||
async fn echo_on_session(
|
.lock()
|
||||||
_obj: Arc<TorClient<PreferredRuntime>>,
|
.expect("Lock poisoned")
|
||||||
method: Box<Echo>,
|
.objects
|
||||||
_ctx: Box<dyn rpc::Context>,
|
.remove(idx);
|
||||||
) -> Result<Echo, rpc::RpcError> {
|
|
||||||
Ok(*method)
|
|
||||||
}
|
|
||||||
|
|
||||||
rpc::rpc_invoke_fn! {
|
if removed.is_some() {
|
||||||
echo_on_session(TorClient<PreferredRuntime>,Echo);
|
Ok(())
|
||||||
|
} else {
|
||||||
}
|
Err(rpc::LookupError::NoObject(id.clone()))
|
||||||
|
}
|
||||||
/// The authentication scheme as enumerated in the spec.
|
|
||||||
///
|
|
||||||
/// Conceptually, an authentication scheme answers the question "How can the
|
|
||||||
/// Arti process know you have permissions to use or administer it?"
|
|
||||||
///
|
|
||||||
/// TODO RPC: The only supported one for now is "inherent:unix_path"
|
|
||||||
#[derive(Debug, Copy, Clone, serde::Deserialize)]
|
|
||||||
enum AuthenticationScheme {
|
|
||||||
/// Inherent authority based on the ability to access an AF_UNIX address.
|
|
||||||
#[serde(rename = "inherent:unix_path")]
|
|
||||||
InherentUnixPath,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Method to implement basic authentication. Right now only "I connected to
|
|
||||||
/// you so I must have permission!" is supported.
|
|
||||||
#[derive(Debug, serde::Deserialize)]
|
|
||||||
struct Authenticate {
|
|
||||||
/// The authentication scheme as enumerated in the spec.
|
|
||||||
///
|
|
||||||
/// TODO RPC: The only supported one for now is "inherent:unix_path"
|
|
||||||
scheme: AuthenticationScheme,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A reply from the `Authenticate` method.
|
|
||||||
#[derive(Debug, serde::Serialize)]
|
|
||||||
struct AuthenticateReply {
|
|
||||||
/// An owned reference to a `TorClient` object.
|
|
||||||
client: Option<rpc::ObjectId>,
|
|
||||||
}
|
|
||||||
|
|
||||||
rpc::decl_method! {"auth:authenticate" => Authenticate}
|
|
||||||
impl rpc::Method for Authenticate {
|
|
||||||
type Output = AuthenticateReply;
|
|
||||||
type Update = rpc::NoUpdates;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// An error during authentication.
|
|
||||||
#[derive(Debug, Clone, thiserror::Error, serde::Serialize)]
|
|
||||||
enum AuthenticationFailure {}
|
|
||||||
|
|
||||||
impl tor_error::HasKind for AuthenticationFailure {
|
|
||||||
fn kind(&self) -> tor_error::ErrorKind {
|
|
||||||
// TODO RPC not right.
|
|
||||||
tor_error::ErrorKind::LocalProtocolViolation
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Invoke the "authenticate" method on a connection.
|
|
||||||
///
|
|
||||||
/// TODO RPC: This behavior is wrong; we'll need to fix it to be all
|
|
||||||
/// capabilities-like.
|
|
||||||
async fn authenticate_connection(
|
|
||||||
unauth: Arc<Connection>,
|
|
||||||
method: Box<Authenticate>,
|
|
||||||
ctx: Box<dyn rpc::Context>,
|
|
||||||
) -> Result<AuthenticateReply, rpc::RpcError> {
|
|
||||||
match method.scheme {
|
|
||||||
// For now, we only support AF_UNIX connections, and we assume that if
|
|
||||||
// you have permission to open such a connection to us, you have
|
|
||||||
// permission to use Arti. We will refine this later on!
|
|
||||||
AuthenticationScheme::InherentUnixPath => {}
|
|
||||||
}
|
|
||||||
|
|
||||||
let client = Arc::clone(&unauth.inner.lock().expect("Poisoned lock").client);
|
|
||||||
|
|
||||||
let client = Some(ctx.register_weak(client));
|
|
||||||
Ok(AuthenticateReply { client })
|
|
||||||
}
|
|
||||||
rpc::rpc_invoke_fn! {
|
|
||||||
authenticate_connection(Connection, Authenticate);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// An error given when an RPC request is cancelled.
|
/// An error given when an RPC request is cancelled.
|
||||||
///
|
///
|
||||||
/// This is a separate type from [`crate::cancel::Cancelled`] since eventually
|
/// This is a separate type from [`crate::cancel::Cancelled`] since eventually
|
||||||
|
|
|
@ -0,0 +1,183 @@
|
||||||
|
//! RPC commands and related functionality for authentication.
|
||||||
|
//!
|
||||||
|
//! In Arti's RPC system, authentication is a kind of method that can be invoked
|
||||||
|
//! on the special "connection" object, which gives you an RPC _session_ as a
|
||||||
|
//! result. The RPC session is the root for all other capabilities.
|
||||||
|
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use super::Connection;
|
||||||
|
use tor_rpcbase as rpc;
|
||||||
|
|
||||||
|
/*
|
||||||
|
TODO RPC: This is disabled because the design isn't really useful.
|
||||||
|
If we're going to provide something here, it should probably
|
||||||
|
contain a list of protocol elements/aspects, and it should be designed
|
||||||
|
to enable compatibility, with a clear view of what applications are
|
||||||
|
supposed to do about it.
|
||||||
|
|
||||||
|
/// Declare the get_rpc_protocol method.
|
||||||
|
mod get_rpc_protocol {
|
||||||
|
use super::Connection;
|
||||||
|
use std::sync::Arc;
|
||||||
|
use tor_rpcbase as rpc;
|
||||||
|
|
||||||
|
/// Method to inquire about the RPC protocol.
|
||||||
|
#[derive(Debug, serde::Deserialize)]
|
||||||
|
struct GetRpcProtocol {}
|
||||||
|
|
||||||
|
/// Reply to the [`GetRpcProtocol`] method
|
||||||
|
#[derive(Debug, serde::Serialize)]
|
||||||
|
struct GetProtocolReply {
|
||||||
|
/// The version of the RPC protocol that this server speaks.
|
||||||
|
// TODO RPC: Should this be a list?
|
||||||
|
version: RpcProtocolId,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Identifier for a version of this RPC meta-protocol.
|
||||||
|
#[derive(Debug, Copy, Clone, serde::Serialize)]
|
||||||
|
enum RpcProtocolId {
|
||||||
|
/// Alpha version of the protocol. Things might break between here and the
|
||||||
|
/// stable protocol.
|
||||||
|
///
|
||||||
|
/// TODO RPC: CHange this to v0.
|
||||||
|
#[serde(rename = "alpha")]
|
||||||
|
Alpha,
|
||||||
|
}
|
||||||
|
rpc::decl_method! {"auth:get_rpc_protocol" => GetRpcProtocol}
|
||||||
|
impl rpc::Method for GetRpcProtocol {
|
||||||
|
type Output = GetProtocolReply;
|
||||||
|
type Update = rpc::NoUpdates;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Describe which version of the RPC protocol our connection implements.
|
||||||
|
async fn conn_get_rpc_protocol(
|
||||||
|
_conn: Arc<Connection>,
|
||||||
|
_method: Box<GetRpcProtocol>,
|
||||||
|
_ctx: Box<dyn rpc::Context>,
|
||||||
|
) -> Result<GetProtocolReply, rpc::RpcError> {
|
||||||
|
Ok(GetProtocolReply {
|
||||||
|
version: RpcProtocolId::Alpha,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
rpc::rpc_invoke_fn! {
|
||||||
|
conn_get_rpc_protocol(Connection, GetRpcProtocol);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
/// The authentication scheme as enumerated in the spec.
|
||||||
|
///
|
||||||
|
/// Conceptually, an authentication scheme answers the question "How can the
|
||||||
|
/// Arti process know you have permissions to use or administer it?"
|
||||||
|
///
|
||||||
|
/// TODO RPC: The only supported one for now is "inherent:unix_path"
|
||||||
|
#[derive(Debug, Copy, Clone, serde::Serialize, serde::Deserialize)]
|
||||||
|
enum AuthenticationScheme {
|
||||||
|
/// Inherent authority based on the ability to access an AF_UNIX address.
|
||||||
|
#[serde(rename = "inherent:unix_path")]
|
||||||
|
InherentUnixPath,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Method to ask which authentication methods are supported.
|
||||||
|
#[derive(Debug, serde::Deserialize)]
|
||||||
|
struct AuthQuery {}
|
||||||
|
|
||||||
|
/// A list of supported authentication schemes and their parameters.
|
||||||
|
#[derive(Debug, serde::Serialize)]
|
||||||
|
struct SupportedAuth {
|
||||||
|
/// A list of the supported authentication schemes.
|
||||||
|
///
|
||||||
|
/// TODO RPC: Actually, this should be able to contain strings _or_ maps,
|
||||||
|
/// where the maps are additional information about the parameters needed
|
||||||
|
/// for a particular scheme. But I think that's a change we can make later
|
||||||
|
/// once we have a scheme that takes parameters.
|
||||||
|
///
|
||||||
|
/// TODO RPC: Should we indicate which schemes get you additional privileges?
|
||||||
|
schemes: Vec<AuthenticationScheme>,
|
||||||
|
}
|
||||||
|
|
||||||
|
rpc::decl_method! {"auth:query" => AuthQuery}
|
||||||
|
impl rpc::Method for AuthQuery {
|
||||||
|
type Output = SupportedAuth;
|
||||||
|
type Update = rpc::NoUpdates;
|
||||||
|
}
|
||||||
|
/// Implement `auth:AuthQuery` on a connection.
|
||||||
|
async fn conn_authquery(
|
||||||
|
_conn: Arc<Connection>,
|
||||||
|
_query: Box<AuthQuery>,
|
||||||
|
_ctx: Box<dyn rpc::Context>,
|
||||||
|
) -> Result<SupportedAuth, rpc::RpcError> {
|
||||||
|
// Right now, every connection supports the same scheme.
|
||||||
|
Ok(SupportedAuth {
|
||||||
|
schemes: vec![AuthenticationScheme::InherentUnixPath],
|
||||||
|
})
|
||||||
|
}
|
||||||
|
rpc::rpc_invoke_fn! {
|
||||||
|
conn_authquery(Connection, AuthQuery);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Method to implement basic authentication. Right now only "I connected to
|
||||||
|
/// you so I must have permission!" is supported.
|
||||||
|
#[derive(Debug, serde::Deserialize)]
|
||||||
|
struct Authenticate {
|
||||||
|
/// The authentication scheme as enumerated in the spec.
|
||||||
|
///
|
||||||
|
/// TODO RPC: The only supported one for now is "inherent:unix_path"
|
||||||
|
scheme: AuthenticationScheme,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A reply from the `Authenticate` method.
|
||||||
|
#[derive(Debug, serde::Serialize)]
|
||||||
|
struct AuthenticateReply {
|
||||||
|
/// An owned reference to a `Session` object.
|
||||||
|
session: rpc::ObjectId,
|
||||||
|
}
|
||||||
|
|
||||||
|
rpc::decl_method! {"auth:authenticate" => Authenticate}
|
||||||
|
impl rpc::Method for Authenticate {
|
||||||
|
type Output = AuthenticateReply;
|
||||||
|
type Update = rpc::NoUpdates;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An error during authentication.
|
||||||
|
#[derive(Debug, Clone, thiserror::Error, serde::Serialize)]
|
||||||
|
enum AuthenticationFailure {}
|
||||||
|
|
||||||
|
impl tor_error::HasKind for AuthenticationFailure {
|
||||||
|
fn kind(&self) -> tor_error::ErrorKind {
|
||||||
|
// TODO RPC not right.
|
||||||
|
tor_error::ErrorKind::LocalProtocolViolation
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Invoke the "authenticate" method on a connection.
|
||||||
|
///
|
||||||
|
/// TODO RPC: This behavior is wrong; we'll need to fix it to be all
|
||||||
|
/// capabilities-like.
|
||||||
|
async fn authenticate_connection(
|
||||||
|
unauth: Arc<Connection>,
|
||||||
|
method: Box<Authenticate>,
|
||||||
|
ctx: Box<dyn rpc::Context>,
|
||||||
|
) -> Result<AuthenticateReply, rpc::RpcError> {
|
||||||
|
match method.scheme {
|
||||||
|
// For now, we only support AF_UNIX connections, and we assume that if
|
||||||
|
// you have permission to open such a connection to us, you have
|
||||||
|
// permission to use Arti. We will refine this later on!
|
||||||
|
AuthenticationScheme::InherentUnixPath => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO RPC: I'm actually not totally sure about the semantics of creating a
|
||||||
|
// new session object here, since it will _look_ separate from other
|
||||||
|
// sessions, but in fact they will all share the same object map.
|
||||||
|
//
|
||||||
|
// Perhaps we need to think more about the semantics of authenticating more
|
||||||
|
// then once on the same connection.
|
||||||
|
let client = unauth.inner.lock().expect("lock poisoned").client.clone();
|
||||||
|
let session = crate::session::Session::new(client);
|
||||||
|
let session = ctx.register_owned(session);
|
||||||
|
Ok(AuthenticateReply { session })
|
||||||
|
}
|
||||||
|
rpc::rpc_invoke_fn! {
|
||||||
|
authenticate_connection(Connection, Authenticate);
|
||||||
|
}
|
|
@ -43,6 +43,7 @@ mod err;
|
||||||
mod mgr;
|
mod mgr;
|
||||||
mod msgs;
|
mod msgs;
|
||||||
mod objmap;
|
mod objmap;
|
||||||
|
mod session;
|
||||||
mod streams;
|
mod streams;
|
||||||
|
|
||||||
pub use connection::{Connection, ConnectionError};
|
pub use connection::{Connection, ConnectionError};
|
||||||
|
|
|
@ -181,6 +181,11 @@ impl TaggedAddr {
|
||||||
/// analyze these object IDs, please contact the Arti developers instead and let
|
/// analyze these object IDs, please contact the Arti developers instead and let
|
||||||
/// us give you a better way to do whatever you want.
|
/// us give you a better way to do whatever you want.
|
||||||
impl GenIdx {
|
impl GenIdx {
|
||||||
|
/// Return true if this is a strong (owning) reference.
|
||||||
|
pub(crate) fn is_strong(&self) -> bool {
|
||||||
|
matches!(self, GenIdx::Strong(_))
|
||||||
|
}
|
||||||
|
|
||||||
/// Encode `self` into an rpc::ObjectId that we can give to a client.
|
/// Encode `self` into an rpc::ObjectId that we can give to a client.
|
||||||
pub(crate) fn encode(self) -> rpc::ObjectId {
|
pub(crate) fn encode(self) -> rpc::ObjectId {
|
||||||
self.encode_with_rng(&mut rand::thread_rng())
|
self.encode_with_rng(&mut rand::thread_rng())
|
||||||
|
@ -319,18 +324,19 @@ impl ObjMap {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Remove the entry at `idx`, if any.
|
/// Remove and return the entry at `idx`, if any.
|
||||||
pub(crate) fn remove(&mut self, idx: GenIdx) {
|
pub(crate) fn remove(&mut self, idx: GenIdx) -> Option<Arc<dyn rpc::Object>> {
|
||||||
match idx {
|
match idx {
|
||||||
GenIdx::Weak(idx) => {
|
GenIdx::Weak(idx) => {
|
||||||
if let Some(entry) = self.weak_arena.remove(idx) {
|
if let Some(entry) = self.weak_arena.remove(idx) {
|
||||||
let old_idx = self.reverse_map.remove(&entry.tagged_addr());
|
let old_idx = self.reverse_map.remove(&entry.tagged_addr());
|
||||||
debug_assert_eq!(old_idx, Some(idx));
|
debug_assert_eq!(old_idx, Some(idx));
|
||||||
|
entry.obj.upgrade()
|
||||||
|
} else {
|
||||||
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
GenIdx::Strong(idx) => {
|
GenIdx::Strong(idx) => self.strong_arena.remove(idx),
|
||||||
self.strong_arena.remove(idx);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,92 @@
|
||||||
|
//! High-level APIs for an RPC session
|
||||||
|
//!
|
||||||
|
//! A "session" is created when a user authenticates on an RPC connection. It
|
||||||
|
//! is the root for all other RPC capabilities.
|
||||||
|
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use tor_rpcbase as rpc;
|
||||||
|
|
||||||
|
/// An authenticated RPC session.
|
||||||
|
pub(crate) struct Session {
|
||||||
|
/// An inner TorClient object that we use to implement remaining
|
||||||
|
/// functionality.
|
||||||
|
#[allow(unused)]
|
||||||
|
client: Arc<dyn rpc::Object>,
|
||||||
|
}
|
||||||
|
impl rpc::Object for Session {}
|
||||||
|
rpc::decl_object! {Session}
|
||||||
|
|
||||||
|
impl Session {
|
||||||
|
/// Create a new session object.
|
||||||
|
pub(crate) fn new(client: Arc<dyn rpc::Object>) -> Arc<Self> {
|
||||||
|
Arc::new(Self { client })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// RPC method to release a single strong reference.
|
||||||
|
#[derive(Debug, serde::Deserialize)]
|
||||||
|
struct RpcRelease {
|
||||||
|
/// The object to release. Must be a strong reference.
|
||||||
|
///
|
||||||
|
/// TODO RPC: Releasing a weak reference is perilous and hard-to-define
|
||||||
|
/// based on how we have implemented our object ids. If you tell the objmap
|
||||||
|
/// to "release" a single name for a weak reference, you are releasing every
|
||||||
|
/// name for that weak reference, which may have surprising results.
|
||||||
|
///
|
||||||
|
/// This might be a sign of a design problem.
|
||||||
|
obj: rpc::ObjectId,
|
||||||
|
}
|
||||||
|
/// RPC method to release a single strong reference, creating a weak reference
|
||||||
|
/// in its place.
|
||||||
|
#[derive(Debug, serde::Serialize, serde::Deserialize)]
|
||||||
|
struct RpcDowngrade {
|
||||||
|
/// The object to downgrade
|
||||||
|
obj: rpc::ObjectId,
|
||||||
|
}
|
||||||
|
|
||||||
|
rpc::decl_method! { "rpc:release" => RpcRelease}
|
||||||
|
impl rpc::Method for RpcRelease {
|
||||||
|
type Output = rpc::Nil;
|
||||||
|
type Update = rpc::NoUpdates;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Implementation for calling "release" on a Session.
|
||||||
|
async fn rpc_release(
|
||||||
|
_obj: Arc<Session>,
|
||||||
|
method: Box<RpcRelease>,
|
||||||
|
ctx: Box<dyn rpc::Context>,
|
||||||
|
) -> Result<rpc::Nil, rpc::RpcError> {
|
||||||
|
ctx.release_owned(&method.obj)?;
|
||||||
|
Ok(rpc::Nil::default())
|
||||||
|
}
|
||||||
|
rpc::rpc_invoke_fn! {
|
||||||
|
rpc_release(Session,RpcRelease);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A simple temporary method to echo a reply.
|
||||||
|
#[derive(Debug, serde::Deserialize, serde::Serialize)]
|
||||||
|
struct Echo {
|
||||||
|
/// A message to echo.
|
||||||
|
msg: String,
|
||||||
|
}
|
||||||
|
rpc::decl_method! { "arti:x-echo" => Echo}
|
||||||
|
impl rpc::Method for Echo {
|
||||||
|
type Output = Echo;
|
||||||
|
type Update = rpc::NoUpdates;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Implementation for calling "echo" on a Session.
|
||||||
|
///
|
||||||
|
/// TODO RPC: Remove this. It shouldn't exist.
|
||||||
|
async fn echo_on_session(
|
||||||
|
_obj: Arc<Session>,
|
||||||
|
method: Box<Echo>,
|
||||||
|
_ctx: Box<dyn rpc::Context>,
|
||||||
|
) -> Result<Echo, rpc::RpcError> {
|
||||||
|
Ok(*method)
|
||||||
|
}
|
||||||
|
|
||||||
|
rpc::rpc_invoke_fn! {
|
||||||
|
echo_on_session(Session,Echo);
|
||||||
|
}
|
|
@ -434,6 +434,10 @@ mod test {
|
||||||
fn register_weak(&self, _object: Arc<dyn crate::Object>) -> crate::ObjectId {
|
fn register_weak(&self, _object: Arc<dyn crate::Object>) -> crate::ObjectId {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn release_owned(&self, _object: &crate::ObjectId) -> Result<(), crate::LookupError> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_test]
|
#[async_test]
|
||||||
|
|
|
@ -80,9 +80,7 @@ pub trait Context: Send {
|
||||||
/// Look up an object by identity within this context.
|
/// Look up an object by identity within this context.
|
||||||
fn lookup_object(&self, id: &ObjectId) -> Result<Arc<dyn Object>, LookupError>;
|
fn lookup_object(&self, id: &ObjectId) -> Result<Arc<dyn Object>, LookupError>;
|
||||||
|
|
||||||
/// Find an owning reference to `object` within this context. If none can
|
/// Create an owning reference to `object` within this context.
|
||||||
/// be found, or if only a non-owning reference is found, make sure that
|
|
||||||
/// this context contains an owning reference to `object`.
|
|
||||||
///
|
///
|
||||||
/// Return an ObjectId for this object.
|
/// Return an ObjectId for this object.
|
||||||
///
|
///
|
||||||
|
@ -90,9 +88,9 @@ pub trait Context: Send {
|
||||||
/// function depending on how we decide to name and specify things.
|
/// function depending on how we decide to name and specify things.
|
||||||
fn register_owned(&self, object: Arc<dyn Object>) -> ObjectId;
|
fn register_owned(&self, object: Arc<dyn Object>) -> ObjectId;
|
||||||
|
|
||||||
/// Find any owning reference to `object` within this context. If none can
|
/// Make sure that
|
||||||
/// be found, make sure that
|
/// this context contains a non-owning reference to `object`,
|
||||||
/// this context contains a non-owning reference to `object`.
|
/// creating one if necessary.
|
||||||
///
|
///
|
||||||
/// Return an ObjectId for this object.
|
/// Return an ObjectId for this object.
|
||||||
///
|
///
|
||||||
|
@ -102,6 +100,13 @@ pub trait Context: Send {
|
||||||
/// TODO RPC: We may need to change the above semantics and the name of this
|
/// TODO RPC: We may need to change the above semantics and the name of this
|
||||||
/// function depending on how we decide to name and specify things.
|
/// function depending on how we decide to name and specify things.
|
||||||
fn register_weak(&self, object: Arc<dyn Object>) -> ObjectId;
|
fn register_weak(&self, object: Arc<dyn Object>) -> ObjectId;
|
||||||
|
|
||||||
|
/// Drop an owning reference to the object called `object` within this context.
|
||||||
|
///
|
||||||
|
/// This will return an error if `object` is not an owning reference.
|
||||||
|
///
|
||||||
|
/// TODO RPC should this really return a LookupError?
|
||||||
|
fn release_owned(&self, object: &ObjectId) -> Result<(), LookupError>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An error caused while trying to send an update to a method.
|
/// An error caused while trying to send an update to a method.
|
||||||
|
@ -148,3 +153,13 @@ pub trait ContextExt: Context {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl<T: Context> ContextExt for T {}
|
impl<T: Context> ContextExt for T {}
|
||||||
|
|
||||||
|
/// A serializable empty object.
|
||||||
|
///
|
||||||
|
/// Used when we need to declare that a method returns nothing.
|
||||||
|
///
|
||||||
|
/// TODO RPC: Perhaps we can get () to serialize as {} and make this an alias
|
||||||
|
/// for ().
|
||||||
|
#[derive(Debug, Clone, Copy, serde::Serialize, serde::Deserialize, Default)]
|
||||||
|
#[non_exhaustive]
|
||||||
|
pub struct Nil {}
|
||||||
|
|
|
@ -507,7 +507,7 @@ in order to receive any other object IDs.
|
||||||
|
|
||||||
The pre-authentication methods available on a connection are:
|
The pre-authentication methods available on a connection are:
|
||||||
|
|
||||||
auth:get_proto
|
auth:get_rpc_protocol
|
||||||
: Ask Arti which version of the protocol is in use.
|
: Ask Arti which version of the protocol is in use.
|
||||||
|
|
||||||
auth:query
|
auth:query
|
||||||
|
@ -776,9 +776,13 @@ The echo command will only work post-authentication.
|
||||||
Here is an example session:
|
Here is an example session:
|
||||||
|
|
||||||
```
|
```
|
||||||
|
>>> {"id": "abc", "obj": "connection", "method": "auth:get_rpc_protocol", "params": {}}
|
||||||
|
<<< {"id":"abc","result":{"version":"alpha"}}
|
||||||
|
>>> {"id": "abc", "obj": "connection", "method": "auth:query", "params": {}}
|
||||||
|
<<< {"id":"abc","result":{"schemes":["inherent:unix_path"]}}
|
||||||
>>> {"id": 3, "obj": "connection", "method": "auth:authenticate", "params": {"scheme": "inherent:unix_path"}}
|
>>> {"id": 3, "obj": "connection", "method": "auth:authenticate", "params": {"scheme": "inherent:unix_path"}}
|
||||||
<<< {"id":3,"result":{"client":"dTewFIaZKQV1N7AUhpkpBIrIT-t5Ztb8"}}
|
<<< {"id":3,"result":{"session":"2yFi5qrMD9LbIWLmqswP0iTenRlVM_Au"}}
|
||||||
>>> {"id": 4, "obj": "dTewFIaZKQV1N7AUhpkpBIrIT-t5Ztb8", "method": "arti:x-echo", "params": {"msg": "Hello World"}}
|
>>> {"id": 4, "obj": "2yFi5qrMD9LbIWLmqswP0iTenRlVM_Au", "method": "arti:x-echo", "params": {"msg": "Hello World"}}
|
||||||
<<< {"id":4,"result":{"msg":"Hello World"}}
|
<<< {"id":4,"result":{"msg":"Hello World"}}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue