v0.3.3, fix #25: better HTTP auth support
This commit is contained in:
parent
3a3f01118e
commit
969afbf96c
@ -1,3 +1,11 @@
|
||||
## `v0.3.3` (2021-10-20)
|
||||
|
||||
* [#25](https://github.com/scottlamb/retina/issues/25): better HTTP
|
||||
authentication support via the new [`http-auth`
|
||||
crate](https://crates.io/crates/http-auth). Before, `retina` would only
|
||||
authenticate properly if the first requested challenge was `Digest`. Now, it
|
||||
will pick out a `Digest` or `Basic` challenge from a list.
|
||||
|
||||
## `v0.3.2` (2021-09-29)
|
||||
|
||||
* better `TEARDOWN` handling, which often avoids the need to wait for session
|
||||
|
40
Cargo.lock
generated
40
Cargo.lock
generated
@ -277,19 +277,6 @@ dependencies = [
|
||||
"generic-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "digest_auth"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fa30657988b2ced88f68fe490889e739bf98d342916c33ed3100af1d6f1cbc9c"
|
||||
dependencies = [
|
||||
"digest",
|
||||
"hex",
|
||||
"md-5",
|
||||
"rand",
|
||||
"sha2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.6.1"
|
||||
@ -481,6 +468,21 @@ version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
|
||||
|
||||
[[package]]
|
||||
name = "http-auth"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "805afa6c41edf02ff4643e6672810974a83c2d866be06e0c377e1789084f6a7e"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"digest",
|
||||
"hex",
|
||||
"md-5",
|
||||
"memchr",
|
||||
"rand",
|
||||
"sha2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "idna"
|
||||
version = "0.2.3"
|
||||
@ -587,9 +589,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.3.4"
|
||||
version = "2.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525"
|
||||
checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a"
|
||||
|
||||
[[package]]
|
||||
name = "memoffset"
|
||||
@ -650,9 +652,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nom"
|
||||
version = "6.2.1"
|
||||
version = "6.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c5c51b9083a3c620fa67a2a635d1ce7d95b897e957d6b28ff9a5da960a103a6"
|
||||
checksum = "e7413f999671bd4745a7b624bd370a569fb6bc574b23c83a3c5ed2e453f3d5e2"
|
||||
dependencies = [
|
||||
"bitvec",
|
||||
"funty",
|
||||
@ -977,17 +979,17 @@ checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b"
|
||||
|
||||
[[package]]
|
||||
name = "retina"
|
||||
version = "0.3.2"
|
||||
version = "0.3.3"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"base64",
|
||||
"bitreader",
|
||||
"bytes",
|
||||
"criterion",
|
||||
"digest_auth",
|
||||
"futures",
|
||||
"h264-reader",
|
||||
"hex",
|
||||
"http-auth",
|
||||
"itertools",
|
||||
"log",
|
||||
"mylog",
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "retina"
|
||||
version = "0.3.2"
|
||||
version = "0.3.3"
|
||||
authors = ["Scott Lamb <slamb@slamb.org>"]
|
||||
license = "MIT/Apache-2.0"
|
||||
edition = "2018"
|
||||
@ -14,10 +14,10 @@ include = ["src/**/*", "benches", "Cargo.toml"]
|
||||
base64 = "0.13.0"
|
||||
bitreader = "0.3.3"
|
||||
bytes = "1.0.1"
|
||||
digest_auth = "0.3.0"
|
||||
futures = "0.3.14"
|
||||
hex = "0.4.3"
|
||||
h264-reader = "0.5.0"
|
||||
hex = "0.4.3"
|
||||
http-auth = "0.1.2"
|
||||
log = "0.4.8"
|
||||
once_cell = "1.7.2"
|
||||
pin-project = "1.0.7"
|
||||
|
@ -1,13 +1,14 @@
|
||||
// Copyright (C) 2021 Scott Lamb <slamb@slamb.org>
|
||||
// SPDX-License-Identifier: MIT OR Apache-2.0
|
||||
|
||||
use std::convert::TryFrom;
|
||||
use std::mem::MaybeUninit;
|
||||
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
|
||||
use std::num::NonZeroU32;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::task::Poll;
|
||||
use std::time::Instant;
|
||||
use std::{borrow::Cow, fmt::Debug, num::NonZeroU16, pin::Pin};
|
||||
use std::{fmt::Debug, num::NonZeroU16, pin::Pin};
|
||||
|
||||
use self::channel_mapping::*;
|
||||
pub use self::timeline::Timeline;
|
||||
@ -656,7 +657,7 @@ struct SessionInner {
|
||||
runtime_handle: Option<tokio::runtime::Handle>,
|
||||
|
||||
options: SessionOptions,
|
||||
requested_auth: Option<digest_auth::WwwAuthenticateHeader>,
|
||||
requested_auth: Option<http_auth::PasswordClient>,
|
||||
presentation: Presentation,
|
||||
|
||||
/// This will be set iff one or more `SETUP` calls have been issued.
|
||||
@ -727,7 +728,7 @@ impl RtspConnection {
|
||||
&mut self,
|
||||
mode: ResponseMode,
|
||||
options: &SessionOptions,
|
||||
requested_auth: &mut Option<digest_auth::WwwAuthenticateHeader>,
|
||||
requested_auth: &mut Option<http_auth::PasswordClient>,
|
||||
req: &mut rtsp_types::Request<Bytes>,
|
||||
) -> Result<(RtspMessageContext, u32, rtsp_types::Response<Bytes>), Error> {
|
||||
loop {
|
||||
@ -828,22 +829,6 @@ impl RtspConnection {
|
||||
}),
|
||||
Some(h) => h,
|
||||
};
|
||||
let www_authenticate = www_authenticate.as_str();
|
||||
if !www_authenticate.starts_with("Digest ") {
|
||||
// TODO: the header(s) might also indicate both Basic and Digest; we shouldn't
|
||||
// error or not based on ordering.
|
||||
bail!(ErrorInt::RtspResponseError {
|
||||
conn_ctx: *self.inner.ctx(),
|
||||
msg_ctx,
|
||||
method: req.method().clone(),
|
||||
cseq,
|
||||
status: resp.status(),
|
||||
description: format!(
|
||||
"Non-digest authentication requested: {}",
|
||||
www_authenticate
|
||||
),
|
||||
})
|
||||
}
|
||||
if options.creds.is_none() {
|
||||
bail!(ErrorInt::RtspResponseError {
|
||||
conn_ctx: *self.inner.ctx(),
|
||||
@ -855,21 +840,18 @@ impl RtspConnection {
|
||||
.to_owned(),
|
||||
})
|
||||
}
|
||||
let www_authenticate = digest_auth::WwwAuthenticateHeader::parse(www_authenticate)
|
||||
.map_err(|e| {
|
||||
wrap!(ErrorInt::RtspResponseError {
|
||||
conn_ctx: *self.inner.ctx(),
|
||||
msg_ctx,
|
||||
method: req.method().clone(),
|
||||
cseq,
|
||||
status: resp.status(),
|
||||
description: format!(
|
||||
"Bad WWW-Authenticate header {:?}: {}",
|
||||
www_authenticate, e
|
||||
),
|
||||
})
|
||||
})?;
|
||||
*requested_auth = Some(www_authenticate);
|
||||
let www_authenticate = www_authenticate.as_str();
|
||||
*requested_auth = match http_auth::PasswordClient::try_from(www_authenticate) {
|
||||
Ok(c) => Some(c),
|
||||
Err(e) => bail!(ErrorInt::RtspResponseError {
|
||||
conn_ctx: *self.inner.ctx(),
|
||||
msg_ctx,
|
||||
method: req.method().clone(),
|
||||
cseq,
|
||||
status: resp.status(),
|
||||
description: format!("Can't understand WWW-Authenticate header: {}", e),
|
||||
}),
|
||||
};
|
||||
continue;
|
||||
} else if !resp.status().is_success() {
|
||||
bail!(ErrorInt::RtspResponseError {
|
||||
@ -889,7 +871,7 @@ impl RtspConnection {
|
||||
fn fill_req(
|
||||
&mut self,
|
||||
options: &SessionOptions,
|
||||
requested_auth: &mut Option<digest_auth::WwwAuthenticateHeader>,
|
||||
requested_auth: &mut Option<http_auth::PasswordClient>,
|
||||
req: &mut rtsp_types::Request<Bytes>,
|
||||
) -> Result<u32, Error> {
|
||||
let cseq = self.next_cseq;
|
||||
@ -899,21 +881,15 @@ impl RtspConnection {
|
||||
.creds
|
||||
.as_ref()
|
||||
.expect("creds were checked when filling request_auth");
|
||||
let uri = req.request_uri().map(|u| u.as_str()).unwrap_or("*");
|
||||
let method = digest_auth::HttpMethod(Cow::Borrowed(req.method().into()));
|
||||
let ctx = digest_auth::AuthContext::new_with_method(
|
||||
&creds.username,
|
||||
&creds.password,
|
||||
uri,
|
||||
Option::<&'static [u8]>::None,
|
||||
method,
|
||||
);
|
||||
|
||||
// digest_auth's comments seem to say 'respond' failing means a parser bug.
|
||||
let authorization = auth
|
||||
.respond(&ctx)
|
||||
.map_err(|e| wrap!(ErrorInt::Internal(e.into())))?
|
||||
.to_string();
|
||||
.respond(&http_auth::PasswordParams {
|
||||
username: &creds.username,
|
||||
password: &creds.password,
|
||||
uri: req.request_uri().map(|u| u.as_str()).unwrap_or("*"),
|
||||
method: req.method().into(),
|
||||
body: Some(&[]),
|
||||
})
|
||||
.map_err(|e| wrap!(ErrorInt::Internal(e.into())))?;
|
||||
req.insert_header(rtsp_types::headers::AUTHORIZATION, authorization);
|
||||
}
|
||||
req.insert_header(rtsp_types::headers::CSEQ, cseq.to_string());
|
||||
|
@ -19,7 +19,7 @@ pub(super) async fn background_teardown(
|
||||
base_url: Url,
|
||||
session_id: Box<str>,
|
||||
options: SessionOptions,
|
||||
requested_auth: Option<digest_auth::WwwAuthenticateHeader>,
|
||||
requested_auth: Option<http_auth::PasswordClient>,
|
||||
conn: Option<RtspConnection>,
|
||||
mut tx: tokio::sync::watch::Sender<Option<Result<(), Error>>>,
|
||||
expires: tokio::time::Instant,
|
||||
@ -63,7 +63,7 @@ pub(super) async fn teardown_loop_forever(
|
||||
url: Url,
|
||||
session_id: &str,
|
||||
options: &SessionOptions,
|
||||
mut requested_auth: Option<digest_auth::WwwAuthenticateHeader>,
|
||||
mut requested_auth: Option<http_auth::PasswordClient>,
|
||||
mut conn: Option<RtspConnection>,
|
||||
tx: &mut tokio::sync::watch::Sender<Option<Result<(), Error>>>,
|
||||
) {
|
||||
@ -155,7 +155,7 @@ pub(super) async fn teardown_loop_forever(
|
||||
async fn attempt(
|
||||
req: &mut Request<Bytes>,
|
||||
options: &SessionOptions,
|
||||
requested_auth: &mut Option<digest_auth::WwwAuthenticateHeader>,
|
||||
requested_auth: &mut Option<http_auth::PasswordClient>,
|
||||
mut conn: RtspConnection,
|
||||
) -> Result<rtsp_types::StatusCode, Error> {
|
||||
let e = match conn
|
||||
|
Loading…
Reference in New Issue
Block a user