Skip to content

Commit

Permalink
refactor(iroh): remove request token (#1828)
Browse files Browse the repository at this point in the history
## Description

Removes the request token from iroh-bytes, and everywhere else

## Notes & open questions

Ticket format changes. Do we care?

## Change checklist

- [ ] Self-review.
- [ ] Documentation updates if relevant.
- [ ] Tests if relevant.

---------

Co-authored-by: Diva M <divma@protonmail.com>
  • Loading branch information
rklaehn and divagant-martian authored Nov 24, 2023
1 parent e03de38 commit 1dfb7ac
Show file tree
Hide file tree
Showing 9 changed files with 47 additions and 504 deletions.
121 changes: 3 additions & 118 deletions iroh-bytes/src/protocol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -322,12 +322,6 @@
//! In this case the provider will close just the stream used to send the response.
//! The exact location of the missing data can be retrieved from the error.
//!
//! # Request tokens
//!
//! Request tokens are an optional feature of the protocol. They are opaque byte
//! sequences that are associated with a single request. Applications can use
//! request tokens to implement request level authorization.
//!
//! # Requesting multiple unrelated blobs
//!
//! Currently, the protocol does not support requesting multiple unrelated blobs
Expand All @@ -344,14 +338,8 @@
//!
//! In case nodes are permanently exchanging data, it is probably valuable to
//! keep a connection open and reuse it for multiple requests.
use std::fmt::{self, Display};
use std::str::FromStr;

use anyhow::{ensure, Result};
use bao_tree::{ChunkNum, ChunkRanges};
use bytes::Bytes;
use derive_more::From;
use iroh_base::base32;
use quinn::VarInt;
use serde::{Deserialize, Serialize};
mod range_spec;
Expand All @@ -363,58 +351,7 @@ use crate::Hash;
pub const MAX_MESSAGE_SIZE: usize = 1024 * 1024 * 100;

/// The ALPN used with quic for the iroh bytes protocol.
pub const ALPN: [u8; 13] = *b"/iroh-bytes/2";

/// Maximum size of a request token, matches a browser cookie max size:
/// <https://datatracker.ietf.org/doc/html/rfc2109#section-6.3>.
const MAX_REQUEST_TOKEN_SIZE: usize = 4096;

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, From)]
/// A Request token is an opaque byte sequence associated with a single request.
/// Applications can use request tokens to implement request authorization,
/// user association, etc.
pub struct RequestToken {
bytes: Bytes,
}

impl RequestToken {
/// Creates a new request token from bytes.
pub fn new(bytes: impl Into<Bytes>) -> Result<Self> {
let bytes: Bytes = bytes.into();
ensure!(
bytes.len() < MAX_REQUEST_TOKEN_SIZE,
"request token is too large"
);
Ok(Self { bytes })
}

/// Generate a random 32 byte request token.
pub fn generate() -> Self {
Self {
bytes: rand::random::<[u8; 32]>().to_vec().into(),
}
}

/// Returns a reference the token bytes.
pub fn as_bytes(&self) -> &Bytes {
&self.bytes
}
}

impl FromStr for RequestToken {
type Err = anyhow::Error;

fn from_str(s: &str) -> Result<Self, Self::Err> {
RequestToken::new(base32::parse_vec(s)?)
}
}

/// Serializes to base32.
impl Display for RequestToken {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", base32::fmt(&self.bytes))
}
}
pub const ALPN: [u8; 13] = *b"/iroh-bytes/3";

#[derive(Deserialize, Serialize, Debug, PartialEq, Eq, Clone, From)]
/// A request to the provider
Expand All @@ -423,28 +360,9 @@ pub enum Request {
Get(GetRequest),
}

impl Request {
/// Gets the request token.
pub fn token(&self) -> Option<&RequestToken> {
match self {
Request::Get(get) => get.token(),
}
}

/// Sets the request token and returns a new request.
pub fn with_token(mut self, value: Option<RequestToken>) -> Self {
match &mut self {
Request::Get(get) => get.token = value,
}
self
}
}

/// A request
#[derive(Deserialize, Serialize, Debug, PartialEq, Eq, Clone)]
pub struct GetRequest {
/// Optional Request token
token: Option<RequestToken>,
/// blake3 hash
pub hash: Hash,
/// The range of data to request
Expand All @@ -456,18 +374,13 @@ pub struct GetRequest {
impl GetRequest {
/// Request a blob or collection with specified ranges
pub fn new(hash: Hash, ranges: RangeSpecSeq) -> Self {
Self {
hash,
ranges,
token: None,
}
Self { hash, ranges }
}

/// Request a collection and all its children
pub fn all(hash: Hash) -> Self {
Self {
hash,
token: None,
ranges: RangeSpecSeq::all(),
}
}
Expand All @@ -476,7 +389,6 @@ impl GetRequest {
pub fn single(hash: Hash) -> Self {
Self {
hash,
token: None,
ranges: RangeSpecSeq::from_ranges([ChunkRanges::all()]),
}
}
Expand All @@ -487,7 +399,6 @@ impl GetRequest {
pub fn last_chunk(hash: Hash) -> Self {
Self {
hash,
token: None,
ranges: RangeSpecSeq::from_ranges([ChunkRanges::from(ChunkNum(u64::MAX)..)]),
}
}
Expand All @@ -498,23 +409,12 @@ impl GetRequest {
pub fn last_chunks(hash: Hash) -> Self {
Self {
hash,
token: None,
ranges: RangeSpecSeq::from_ranges_infinite([
ChunkRanges::all(),
ChunkRanges::from(ChunkNum(u64::MAX)..),
]),
}
}

/// Set the request token
pub fn with_token(self, token: Option<RequestToken>) -> Self {
Self { token, ..self }
}

/// Get the request token
pub fn token(&self) -> Option<&RequestToken> {
self.token.as_ref()
}
}

/// Reasons to close connections or stop streams.
Expand Down Expand Up @@ -581,21 +481,18 @@ impl TryFrom<VarInt> for Closed {

#[cfg(test)]
mod tests {
use bytes::Bytes;
use iroh_test::{assert_eq_hex, hexdump::parse_hexdump};

use super::{GetRequest, Request, RequestToken};
use super::{GetRequest, Request};

#[test]
fn request_wire_format() {
let hash = [0xda; 32].into();
let token = RequestToken::from(Bytes::from(b"TOKEN".as_slice()));
let cases = [
(
Request::from(GetRequest::single(hash)),
r"
00 # enum variant for GetRequest
00 # no token
dadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadada # the hash
020001000100 # the RangeSpecSeq
",
Expand All @@ -604,18 +501,6 @@ mod tests {
Request::from(GetRequest::all(hash)),
r"
00 # enum variant for GetRequest
00 # no token
dadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadada # the hash
01000100 # the RangeSpecSeq
",
),
(
Request::from(GetRequest::all(hash).with_token(Some(token.clone()))),
r"
00 # enum variant for GetRequest
01 # a token
05 # length 5
54 4f 4b 45 4e # token content
dadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadada # the hash
01000100 # the RangeSpecSeq
",
Expand Down
36 changes: 2 additions & 34 deletions iroh-bytes/src/provider.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
//! The server side API
use std::fmt::Debug;
use std::path::PathBuf;
use std::sync::Arc;
use std::time::Duration;

use anyhow::{Context, Result};
Expand All @@ -17,7 +16,7 @@ use tracing::{debug, debug_span, info, trace, warn};
use tracing_futures::Instrument;

use crate::hashseq::parse_hash_seq;
use crate::protocol::{GetRequest, RangeSpec, Request, RequestToken};
use crate::protocol::{GetRequest, RangeSpec, Request};
use crate::store::*;
use crate::util::Tag;
use crate::{BlobFormat, Hash};
Expand Down Expand Up @@ -45,8 +44,6 @@ pub enum Event {
connection_id: u64,
/// An identifier uniquely identifying this transfer request.
request_id: u64,
/// Token requester gve for this request, if any
token: Option<RequestToken>,
/// The hash for which the client wants to receive data.
hash: Hash,
},
Expand All @@ -56,8 +53,6 @@ pub enum Event {
connection_id: u64,
/// An identifier uniquely identifying this transfer request.
request_id: u64,
/// Token requester gve for this request, if any
token: Option<RequestToken>,
/// The size of the custom get request.
len: usize,
},
Expand Down Expand Up @@ -227,18 +222,6 @@ pub enum DownloadProgress {
AllDone,
}

/// hook into the request handling to process authorization by examining
/// the request and any given token. Any error returned will abort the request,
/// and the error will be sent to the requester.
pub trait RequestAuthorizationHandler: Send + Sync + Debug + 'static {
/// Handle the authorization request, given an opaque data blob from the requester.
fn authorize(
&self,
token: Option<RequestToken>,
request: &Request,
) -> BoxFuture<'static, anyhow::Result<()>>;
}

/// Read the request from the getter.
///
/// Will fail if there is an error while reading, if the reader
Expand Down Expand Up @@ -368,7 +351,6 @@ pub async fn handle_connection<D: Map, E: EventSender>(
connecting: quinn::Connecting,
db: D,
events: E,
authorization_handler: Arc<dyn RequestAuthorizationHandler>,
rt: crate::util::runtime::Handle,
) {
let remote_addr = connecting.remote_address();
Expand All @@ -394,11 +376,9 @@ pub async fn handle_connection<D: Map, E: EventSender>(
};
events.send(Event::ClientConnected { connection_id }).await;
let db = db.clone();
let authorization_handler = authorization_handler.clone();
rt.local_pool().spawn_pinned(|| {
async move {
if let Err(err) = handle_stream(db, reader, writer, authorization_handler).await
{
if let Err(err) = handle_stream(db, reader, writer).await {
warn!("error: {err:#?}",);
}
}
Expand All @@ -414,7 +394,6 @@ async fn handle_stream<D: Map, E: EventSender>(
db: D,
reader: quinn::RecvStream,
writer: ResponseWriter<E>,
authorization_handler: Arc<dyn RequestAuthorizationHandler>,
) -> Result<()> {
// 1. Decode the request.
debug!("reading request");
Expand All @@ -426,16 +405,6 @@ async fn handle_stream<D: Map, E: EventSender>(
}
};

// 2. Authorize the request (may be a no-op)
debug!("authorizing request");
if let Err(e) = authorization_handler
.authorize(request.token().cloned(), &request)
.await
{
writer.notify_transfer_aborted(None).await;
return Err(e);
}

match request {
Request::Get(request) => handle_get(db, request, writer).await,
}
Expand All @@ -455,7 +424,6 @@ pub async fn handle_get<D: Map, E: EventSender>(
hash,
connection_id: writer.connection_id(),
request_id: writer.request_id(),
token: request.token().cloned(),
})
.await;

Expand Down
21 changes: 1 addition & 20 deletions iroh/src/commands.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
use std::path::PathBuf;
use std::str::FromStr;

use anyhow::{bail, ensure, Context, Result};
use clap::Parser;
use iroh_bytes::{protocol::RequestToken, util::runtime};
use iroh_bytes::util::runtime;

use crate::config::{iroh_data_root, ConsoleEnv, NodeConfig};

Expand Down Expand Up @@ -161,21 +160,3 @@ async fn iroh_quic_connect(rt: runtime::Handle) -> Result<iroh::client::quic::Ir
}
}
}

#[derive(Debug, Clone)]
pub enum RequestTokenOptions {
Random,
Token(RequestToken),
}

impl FromStr for RequestTokenOptions {
type Err = anyhow::Error;

fn from_str(s: &str) -> Result<Self, Self::Err> {
if s.to_lowercase().trim() == "random" {
return Ok(Self::Random);
}
let token = RequestToken::from_str(s)?;
Ok(Self::Token(token))
}
}
Loading

0 comments on commit 1dfb7ac

Please sign in to comment.