use crate::{
Error, SessionError,
coding::{Decode, Encode, EncodeError},
setup::Token,
};
use super::{ParameterBytes, Parameters, Version};
const DELETE: u64 = 0x0;
const REGISTER: u64 = 0x1;
const USE_ALIAS: u64 = 0x2;
const USE_VALUE: u64 = 0x3;
pub fn from_setup(params: &Parameters, version: Version) -> Result<Option<Token>, Error> {
params
.get_bytes(ParameterBytes::AuthorizationToken)
.map(|value| decode(value, version))
.transpose()
}
#[cfg_attr(not(test), expect(dead_code))]
pub fn into_setup(params: &mut Parameters, token: &Token, version: Version) -> Result<(), EncodeError> {
let mut value = Vec::new();
USE_VALUE.encode(&mut value, version)?;
token.kind.encode(&mut value, version)?;
value.extend_from_slice(&token.value);
params.set_bytes(ParameterBytes::AuthorizationToken, value);
Ok(())
}
fn decode(mut buf: &[u8], version: Version) -> Result<Token, Error> {
let malformed = |_| Error::Session(SessionError::KeyValueFormatting);
match u64::decode(&mut buf, version).map_err(malformed)? {
USE_VALUE => {}
REGISTER => {
u64::decode(&mut buf, version).map_err(malformed)?;
}
DELETE | USE_ALIAS => return Err(Error::ProtocolViolation),
_ => return Err(Error::Session(SessionError::KeyValueFormatting)),
}
let kind = u64::decode(&mut buf, version).map_err(malformed)?;
Ok(Token {
kind,
value: buf.to_vec(),
})
}
#[cfg(test)]
mod tests {
use super::*;
const VERSIONS: [Version; 9] = [
Version::Draft14,
Version::Draft15,
Version::Draft16,
Version::Draft17,
Version::Draft18,
Version::Draft19,
Version::Draft20,
Version::Draft21,
Version::Draft22,
];
fn token() -> Token {
Token {
kind: 300,
value: vec![0x00, 0xff, 0x03, 0x80, b'j'],
}
}
fn received(params: &Parameters, version: Version) -> Parameters {
let mut bytes = params.encode_bytes(version).unwrap();
Parameters::decode(&mut bytes, version).unwrap()
}
fn structure(version: Version, fields: &[u64], value: &[u8]) -> Parameters {
let mut raw = Vec::new();
for field in fields {
field.encode(&mut raw, version).unwrap();
}
raw.extend_from_slice(value);
let mut params = Parameters::default();
params.set_bytes(ParameterBytes::AuthorizationToken, raw);
received(¶ms, version)
}
#[test]
fn use_value_round_trips_on_every_draft() {
for version in VERSIONS {
let mut params = Parameters::default();
into_setup(&mut params, &token(), version).unwrap();
let params = received(¶ms, version);
assert_eq!(from_setup(¶ms, version).unwrap(), Some(token()), "{version:?}");
}
}
#[test]
fn the_encoding_matches_the_cross_language_vector() {
let token = Token {
kind: 300,
value: vec![0x00, 0xff],
};
for (version, expected) in [
(Version::Draft14, [0x03, 0x41, 0x2c, 0x00, 0xff]),
(Version::Draft17, [0x03, 0x81, 0x2c, 0x00, 0xff]),
] {
let mut params = Parameters::default();
into_setup(&mut params, &token, version).unwrap();
assert_eq!(
params.get_bytes(ParameterBytes::AuthorizationToken),
Some(&expected[..]),
"{version:?}"
);
}
}
#[test]
fn an_empty_value_is_a_token() {
for version in VERSIONS {
let params = structure(version, &[USE_VALUE, Token::OUT_OF_BAND], &[]);
let expected = Token {
kind: Token::OUT_OF_BAND,
value: Vec::new(),
};
assert_eq!(from_setup(¶ms, version).unwrap(), Some(expected), "{version:?}");
}
}
#[test]
fn absent_is_none() {
for version in VERSIONS {
assert_eq!(from_setup(&Parameters::default(), version).unwrap(), None);
}
}
#[test]
fn register_is_a_value() {
for version in VERSIONS {
let params = structure(version, &[REGISTER, 7, token().kind], &token().value);
assert_eq!(from_setup(¶ms, version).unwrap(), Some(token()), "{version:?}");
}
}
#[test]
fn two_tokens_are_refused() {
for version in VERSIONS {
let mut value = Vec::new();
USE_VALUE.encode(&mut value, version).unwrap();
Token::OUT_OF_BAND.encode(&mut value, version).unwrap();
let key = u64::from(ParameterBytes::AuthorizationToken);
let (count, keys): (Option<u64>, [u64; 2]) = match version {
Version::Draft14 | Version::Draft15 => (Some(2), [key, key]),
Version::Draft16 => (Some(2), [key, 0]),
_ => (None, [key, 0]),
};
let mut raw = Vec::new();
if let Some(count) = count {
count.encode(&mut raw, version).unwrap();
}
for key in keys {
key.encode(&mut raw, version).unwrap();
value.encode(&mut raw, version).unwrap();
}
let err = Parameters::decode(&mut raw.as_slice(), version).unwrap_err();
assert!(matches!(err, crate::DecodeError::Duplicate), "{version:?}: {err:?}");
}
}
#[test]
fn an_alias_reference_is_a_protocol_violation() {
for version in VERSIONS {
for alias_type in [DELETE, USE_ALIAS] {
let params = structure(version, &[alias_type, 7], &[]);
let err = from_setup(¶ms, version).unwrap_err();
assert!(
matches!(err, Error::ProtocolViolation),
"{version:?} {alias_type}: {err:?}"
);
}
}
}
#[test]
fn an_undecodable_structure_is_a_formatting_error() {
for version in VERSIONS {
for (fields, why) in [
(&[][..], "no alias type"),
(&[USE_VALUE][..], "no token type"),
(&[REGISTER][..], "no alias"),
(&[REGISTER, 7][..], "no token type after the alias"),
(&[0x4, 0][..], "an unknown alias type"),
] {
let params = structure(version, fields, &[]);
let err = from_setup(¶ms, version).unwrap_err();
assert!(
matches!(err, Error::Session(SessionError::KeyValueFormatting)),
"{version:?} {why}: {err:?}"
);
}
}
}
}