62 lines
3.5 KiB
Rust
62 lines
3.5 KiB
Rust
//! Authorization tokens and their alias cache (`draft-ietf-moq-transport-22` §8.9, §9.1.3,
|
|
//! §9.1.4), over the WebSocket fallback: a token registered in SETUP authorizes the
|
|
//! session; one registered in a request is used by alias in the next; a request without a
|
|
//! token is refused; a reference to an alias never registered closes the session.
|
|
|
|
mod common;
|
|
|
|
use common::*;
|
|
use joule_moq::draft22::{Parameter, Parameters, RequestErrorCode, SessionError, Token};
|
|
use joule_moq_relay::client::ClientOptions;
|
|
use joule_moq_relay::config::Grant;
|
|
use joule_moq_relay::transport::TransportError;
|
|
use std::time::Duration;
|
|
use tokio::time::timeout;
|
|
|
|
#[tokio::test(flavor = "multi_thread", worker_threads = 4)]
|
|
async fn tokens_by_value_and_by_alias() {
|
|
timeout(Duration::from_secs(120), run()).await.expect("the test finishes in time");
|
|
}
|
|
|
|
async fn run() {
|
|
// The token is made here; only its hash is in the relay's configuration.
|
|
let token: Vec<u8> = (0..24u8).map(|i| i.wrapping_mul(37).wrapping_add(11)).collect();
|
|
let hash = hex::encode(joule_moq_relay::auth::sha256(&token));
|
|
let server = relay(|c| {
|
|
c.auth.required = true;
|
|
c.auth.grants = vec![Grant { prefix: vec!["live".into()], token_sha256: hash, publish: true, subscribe: true, proof: false, unlimited: false, seek: false }];
|
|
c.limits.max_auth_token_cache_size = 1024;
|
|
})
|
|
.await;
|
|
let t = Tracks::new(ns(&["live", "t"]), "cam", 10, 5, 128);
|
|
|
|
// A publisher whose SETUP registers the token (§9.1.4).
|
|
let setup_token = Token::Register { alias: 1, token_type: 0, value: token.clone() };
|
|
let (p, _) = connect(&server, Via::WebSocket, ClientOptions { tokens: vec![setup_token], ..ClientOptions::default() }).await;
|
|
let _pub = publish_tracks(&p, &t, None).await;
|
|
|
|
// A subscriber with no token is refused.
|
|
let (s, _) = connect(&server, Via::WebSocket, ClientOptions::default()).await;
|
|
let refused = s.subscribe(t.ns.clone(), t.media.clone(), Parameters::new()).await.expect_err("no token");
|
|
assert!(refused.contains(&RequestErrorCode::UNAUTHORIZED.to_string()), "{refused}");
|
|
// It registers the token in one request, then uses it by alias in the next (§8.9).
|
|
let reg = Parameters::new().with(Parameter::AuthorizationToken(Token::Register { alias: 7, token_type: 0, value: token.clone() }));
|
|
s.subscribe(t.ns.clone(), t.media.clone(), reg).await.expect("authorized by the token it registers");
|
|
let by_alias = Parameters::new().with(Parameter::AuthorizationToken(Token::UseAlias { alias: 7 }));
|
|
s.subscribe(t.ns.clone(), t.receipts.clone(), by_alias).await.expect("authorized by alias");
|
|
// A wrong token is refused even when it is well formed.
|
|
let wrong = Parameters::new().with(Parameter::AuthorizationToken(Token::UseValue { token_type: 0, value: b"not-it".to_vec() }));
|
|
assert!(s.subscribe(t.ns.clone(), t.media.clone(), wrong).await.is_err());
|
|
|
|
// A reference to an alias never registered closes the session (§8.9).
|
|
let (x, _) = connect(&server, Via::WebSocket, ClientOptions::default()).await;
|
|
let unknown = Parameters::new().with(Parameter::AuthorizationToken(Token::UseAlias { alias: 99 }));
|
|
assert!(x.subscribe(t.ns.clone(), t.media.clone(), unknown).await.is_err());
|
|
match timeout(STEP, x.conn.closed()).await.expect("closed in time") {
|
|
TransportError::Closed { code, .. } => assert_eq!(code, SessionError::UNKNOWN_AUTH_TOKEN_ALIAS.0),
|
|
other => panic!("{other:?}"),
|
|
}
|
|
s.close(0, "done");
|
|
p.close(0, "done");
|
|
server.shutdown().await;
|
|
}
|