1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
#[doc(hidden)]
pub use super::tokenizers::AuthTokenizer as Tokenizer;
use crate::{
common::Uri,
headers::auth::{self, Algorithm, AuthQop},
Error,
};
use rsip_derives::TypedHeader;
use std::convert::{TryFrom, TryInto};
#[derive(TypedHeader, Eq, PartialEq, Clone, Debug)]
pub struct Authorization {
pub scheme: auth::Scheme,
pub username: String,
pub realm: String,
pub nonce: String,
pub uri: Uri,
pub response: String,
pub algorithm: Option<Algorithm>,
pub opaque: Option<String>,
pub qop: Option<AuthQop>,
}
impl<'a> TryFrom<Tokenizer<'a>> for Authorization {
type Error = crate::Error;
fn try_from(tokenizer: Tokenizer) -> Result<Self, Self::Error> {
Ok(Authorization {
scheme: tokenizer.scheme.try_into()?,
username: find_param(&tokenizer.params, "username")
.ok_or_else(|| Error::InvalidParam("missing username".into()))?
.into(),
realm: find_param(&tokenizer.params, "realm")
.ok_or_else(|| Error::InvalidParam("missing realm".into()))?
.into(),
nonce: find_param(&tokenizer.params, "nonce")
.ok_or_else(|| Error::InvalidParam("missing nonce".into()))?
.into(),
uri: find_param(&tokenizer.params, "uri")
.ok_or_else(|| Error::InvalidParam("missing uri".into()))?
.try_into()?,
response: find_param(&tokenizer.params, "response")
.ok_or_else(|| Error::InvalidParam("missing response".into()))?
.into(),
algorithm: find_param(&tokenizer.params, "algorithm")
.map(TryInto::try_into)
.transpose()?,
opaque: find_param(&tokenizer.params, "opaque").map(Into::into),
qop: find_qop(&tokenizer.params)?,
})
}
}
impl std::fmt::Display for Authorization {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"{} username=\"{}\", realm=\"{}\", nonce=\"{}\", uri=\"{}\", response=\"{}\"",
self.scheme, self.username, self.realm, self.nonce, self.uri, self.response
)?;
if let Some(algorithm) = &self.algorithm {
write!(f, ", algorithm={}", algorithm)?;
}
if let Some(opaque) = &self.opaque {
write!(f, ", opaque=\"{}\"", opaque)?;
}
if let Some(qop) = &self.qop {
write!(f, ", {}", qop)?;
}
Ok(())
}
}
fn find_param<'a>(params: &[(&'a str, &'a str)], name: &str) -> Option<&'a str> {
params.iter().find_map(|(key, value)| {
if key.eq_ignore_ascii_case(name) {
Some(*value)
} else {
None
}
})
}
fn find_qop<'a>(params: &[(&'a str, &'a str)]) -> Result<Option<AuthQop>, Error> {
Ok(match find_param(params, "qop") {
Some(qop) if qop.eq_ignore_ascii_case("auth") => Some(AuthQop::Auth {
cnonce: find_param(params, "cnonce")
.ok_or_else(|| Error::InvalidParam("Found qop, but missing cnonce".into()))?
.into(),
nc: find_param(params, "nc")
.ok_or_else(|| Error::InvalidParam("Found qop, but missing nc".into()))?
.parse::<u8>()?,
}),
Some(qop) if qop.eq_ignore_ascii_case("auth-int") => Some(AuthQop::AuthInt {
cnonce: find_param(params, "cnonce")
.ok_or_else(|| Error::InvalidParam("Found qop, but missing cnonce".into()))?
.into(),
nc: find_param(params, "nc")
.ok_or_else(|| Error::InvalidParam("Found qop, but missing nc".into()))?
.parse::<u8>()?,
}),
Some(qop) => return Err(Error::InvalidParam(format!("Found unknown qop: {}", qop))),
None => None,
})
}