Run cargo fmt
This commit is contained in:
parent
e16eb40fd9
commit
f3ec5bd6dc
6 changed files with 324 additions and 201 deletions
|
@ -1,5 +1,4 @@
|
|||
use csrf::{AesGcmCsrfProtection, CsrfProtection,
|
||||
CSRF_COOKIE_NAME, CSRF_FORM_FIELD};
|
||||
use csrf::{AesGcmCsrfProtection, CsrfProtection, CSRF_COOKIE_NAME, CSRF_FORM_FIELD};
|
||||
use data_encoding::{BASE64, BASE64URL_NOPAD};
|
||||
use rand::prelude::thread_rng;
|
||||
use rand::Rng;
|
||||
|
@ -19,7 +18,8 @@ use csrf_token::CsrfToken;
|
|||
use path::Path;
|
||||
use utils::parse_args;
|
||||
|
||||
const CSRF_FORM_FIELD_MULTIPART: &[u8] = "Content-Disposition: form-data; name=\"csrf-token\"".as_bytes();
|
||||
const CSRF_FORM_FIELD_MULTIPART: &[u8] =
|
||||
"Content-Disposition: form-data; name=\"csrf-token\"".as_bytes();
|
||||
|
||||
/// Builder for [CsrfFairing](struct.CsrfFairing.html)
|
||||
///
|
||||
|
@ -321,10 +321,12 @@ impl Fairing for CsrfFairing {
|
|||
|
||||
let _ = request.guard::<CsrfToken>(); //force regeneration of csrf cookies
|
||||
|
||||
let token = if request.content_type()
|
||||
.map(|c| c.media_type())
|
||||
.filter(|m| m.top()=="multipart" && m.sub()=="form-data" )
|
||||
.is_some() {
|
||||
let token = if request
|
||||
.content_type()
|
||||
.map(|c| c.media_type())
|
||||
.filter(|m| m.top() == "multipart" && m.sub() == "form-data")
|
||||
.is_some()
|
||||
{
|
||||
data.peek().split(|&c| c==0x0A || c==0x0D)//0x0A=='\n', 0x0D=='\r'
|
||||
.filter(|l| l.len() > 0)
|
||||
.skip_while(|&l| l != CSRF_FORM_FIELD_MULTIPART && l != &CSRF_FORM_FIELD_MULTIPART[..CSRF_FORM_FIELD_MULTIPART.len()-2])
|
||||
|
@ -333,7 +335,13 @@ impl Fairing for CsrfFairing {
|
|||
.next().unwrap_or(None)
|
||||
} else {
|
||||
parse_args(from_utf8(data.peek()).unwrap_or(""))
|
||||
.filter_map(|(key, token)| if key == CSRF_FORM_FIELD {Some(token.as_bytes())} else {None})
|
||||
.filter_map(|(key, token)| {
|
||||
if key == CSRF_FORM_FIELD {
|
||||
Some(token.as_bytes())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.next()
|
||||
}.and_then(|token| BASE64URL_NOPAD.decode(&token).ok())
|
||||
.and_then(|token| csrf_engine.parse_token(&token).ok());
|
||||
|
@ -379,7 +387,8 @@ impl Fairing for CsrfFairing {
|
|||
if self
|
||||
.auto_insert_disable_prefix
|
||||
.iter()
|
||||
.any(|prefix| uri.starts_with(prefix)) {
|
||||
.any(|prefix| uri.starts_with(prefix))
|
||||
{
|
||||
return;
|
||||
} //if request is on an ignored prefix, ignore it
|
||||
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
use std::io::{Read, Error};
|
||||
use csrf_proxy::ParseState::*;
|
||||
use std::cmp;
|
||||
use std::collections::VecDeque;
|
||||
use csrf_proxy::ParseState::*;
|
||||
|
||||
use std::io::{Error, Read};
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Buffer {
|
||||
|
@ -30,20 +29,21 @@ impl Buffer {
|
|||
let buf_len = buf.len() - read;
|
||||
let to_copy = cmp::min(part_len, buf_len);
|
||||
buf[read..read + to_copy]
|
||||
.copy_from_slice(&self.buf[0][self.pos[0]..self.pos[0]+to_copy]);
|
||||
.copy_from_slice(&self.buf[0][self.pos[0]..self.pos[0] + to_copy]);
|
||||
read += to_copy;
|
||||
if part_len == to_copy {
|
||||
self.buf.pop_front();
|
||||
self.pos.pop_front();
|
||||
} else {
|
||||
self.pos[0]+=to_copy;
|
||||
self.pos[0] += to_copy;
|
||||
}
|
||||
}
|
||||
read
|
||||
}
|
||||
|
||||
fn len(&self) -> usize {
|
||||
self.buf.iter().fold(0, |size, buf| size+buf.len()) - self.pos.iter().fold(0, |size, pos| size + pos)
|
||||
self.buf.iter().fold(0, |size, buf| size + buf.len())
|
||||
- self.pos.iter().fold(0, |size, pos| size + pos)
|
||||
}
|
||||
|
||||
fn is_empty(&self) -> bool {
|
||||
|
@ -57,16 +57,15 @@ impl Default for Buffer {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
#[derive(Debug)]
|
||||
enum ParseState {
|
||||
Init, //default state
|
||||
PartialFormMatch, //when parsing "<form"
|
||||
SearchFormElem, //like default state, but inside a form
|
||||
PartialFormElemMatch, //when parsing "<input", "<textarea" or other form elems, and "</form"
|
||||
SearchMethod(usize), //when inside the first <input>, search for begining of a param
|
||||
PartialNameMatch(usize), //when parsing "name="_method""
|
||||
CloseInputTag, //only if insert after, search for '>' of a "<input name=\"_method\">"
|
||||
Init, //default state
|
||||
PartialFormMatch, //when parsing "<form"
|
||||
SearchFormElem, //like default state, but inside a form
|
||||
PartialFormElemMatch, //when parsing "<input", "<textarea" or other form elems, and "</form"
|
||||
SearchMethod(usize), //when inside the first <input>, search for begining of a param
|
||||
PartialNameMatch(usize), //when parsing "name="_method""
|
||||
CloseInputTag, //only if insert after, search for '>' of a "<input name=\"_method\">"
|
||||
}
|
||||
|
||||
pub struct CsrfProxy<'a> {
|
||||
|
@ -74,7 +73,7 @@ pub struct CsrfProxy<'a> {
|
|||
token: Vec<u8>, //a full input tag loaded with a valid token
|
||||
buf: Buffer,
|
||||
unparsed: Vec<u8>,
|
||||
state: ParseState, //state of the parser
|
||||
state: ParseState, //state of the parser
|
||||
eof: bool,
|
||||
}
|
||||
|
||||
|
@ -100,46 +99,46 @@ impl<'a> CsrfProxy<'a> {
|
|||
|
||||
impl<'a> Read for CsrfProxy<'a> {
|
||||
fn read(&mut self, buf: &mut [u8]) -> Result<usize, Error> {
|
||||
|
||||
while self.buf.len() < buf.len() && !(self.eof && self.unparsed.is_empty()) {
|
||||
let len = if !self.eof {
|
||||
let unparsed_len = self.unparsed.len();
|
||||
self.unparsed.resize(4096,0);
|
||||
unparsed_len +
|
||||
match self.underlying.read(&mut self.unparsed[unparsed_len..]) {
|
||||
Ok(0) => {
|
||||
self.eof = true;
|
||||
0
|
||||
},
|
||||
Ok(len) => len,
|
||||
Err(e) => return Err(e),
|
||||
self.unparsed.resize(4096, 0);
|
||||
unparsed_len + match self.underlying.read(&mut self.unparsed[unparsed_len..]) {
|
||||
Ok(0) => {
|
||||
self.eof = true;
|
||||
0
|
||||
}
|
||||
Ok(len) => len,
|
||||
Err(e) => return Err(e),
|
||||
}
|
||||
} else {
|
||||
self.unparsed.len()
|
||||
};
|
||||
|
||||
self.unparsed.resize(len, 0);//we growed unparsed buffer to 4k before, so shrink it to it's needed size
|
||||
self.unparsed.resize(len, 0); //we growed unparsed buffer to 4k before, so shrink it to it's needed size
|
||||
|
||||
let (consumed, insert_token) = {
|
||||
let mut buf = &self.unparsed[..len];//work only on the initialized part
|
||||
let mut buf = &self.unparsed[..len]; //work only on the initialized part
|
||||
let mut consumed = 0;
|
||||
let mut leave = false;
|
||||
let mut insert_token = false;
|
||||
while !leave {
|
||||
self.state = match self.state {
|
||||
Init => {
|
||||
if let Some(tag_pos) = buf.iter().position(|&c| c as char=='<') {
|
||||
if let Some(tag_pos) = buf.iter().position(|&c| c as char == '<') {
|
||||
buf = &buf[tag_pos..];
|
||||
consumed+=tag_pos;
|
||||
consumed += tag_pos;
|
||||
PartialFormMatch
|
||||
} else {
|
||||
leave = true;
|
||||
consumed += buf.len();
|
||||
Init
|
||||
}
|
||||
},
|
||||
}
|
||||
PartialFormMatch => {
|
||||
if let Some(lower_begin) = buf.get(1..5).map(|slice| slice.to_ascii_lowercase()) {
|
||||
if let Some(lower_begin) =
|
||||
buf.get(1..5).map(|slice| slice.to_ascii_lowercase())
|
||||
{
|
||||
buf = &buf[5..];
|
||||
consumed += 5;
|
||||
if lower_begin == "form".as_bytes() {
|
||||
|
@ -148,30 +147,34 @@ impl<'a> Read for CsrfProxy<'a> {
|
|||
Init
|
||||
}
|
||||
} else {
|
||||
leave = true;
|
||||
PartialFormMatch
|
||||
leave = true;
|
||||
PartialFormMatch
|
||||
}
|
||||
},
|
||||
}
|
||||
SearchFormElem => {
|
||||
if let Some(tag_pos) = buf.iter().position(|&c| c as char=='<') {
|
||||
if let Some(tag_pos) = buf.iter().position(|&c| c as char == '<') {
|
||||
buf = &buf[tag_pos..];
|
||||
consumed+=tag_pos;
|
||||
consumed += tag_pos;
|
||||
PartialFormElemMatch
|
||||
} else {
|
||||
leave = true;
|
||||
consumed += buf.len();
|
||||
SearchFormElem
|
||||
}},
|
||||
}
|
||||
}
|
||||
PartialFormElemMatch => {
|
||||
if let Some(lower_begin) = buf.get(1..9).map(|slice| slice.to_ascii_lowercase()) {
|
||||
if let Some(lower_begin) =
|
||||
buf.get(1..9).map(|slice| slice.to_ascii_lowercase())
|
||||
{
|
||||
if lower_begin.starts_with("/form".as_bytes())
|
||||
|| lower_begin.starts_with("textarea".as_bytes())
|
||||
|| lower_begin.starts_with("button".as_bytes())
|
||||
|| lower_begin.starts_with("select".as_bytes()) {
|
||||
|| lower_begin.starts_with("select".as_bytes())
|
||||
{
|
||||
insert_token = true;
|
||||
leave = true;
|
||||
Init
|
||||
} else if lower_begin.starts_with("input".as_bytes()){
|
||||
} else if lower_begin.starts_with("input".as_bytes()) {
|
||||
SearchMethod(6)
|
||||
} else {
|
||||
buf = &buf[9..];
|
||||
|
@ -179,15 +182,19 @@ impl<'a> Read for CsrfProxy<'a> {
|
|||
SearchFormElem
|
||||
}
|
||||
} else {
|
||||
leave = true;
|
||||
SearchFormElem
|
||||
leave = true;
|
||||
SearchFormElem
|
||||
}
|
||||
},
|
||||
}
|
||||
SearchMethod(pos) => {
|
||||
if let Some(meth_pos) = buf[pos..].iter().position(|&c| c as char == ' ' || c as char == '>') {
|
||||
if let Some(meth_pos) = buf[pos..]
|
||||
.iter()
|
||||
.position(|&c| c as char == ' ' || c as char == '>')
|
||||
{
|
||||
if buf[meth_pos + pos] as char == ' ' {
|
||||
PartialNameMatch(meth_pos + pos + 1)
|
||||
} else { //reached '>'
|
||||
} else {
|
||||
//reached '>'
|
||||
insert_token = true;
|
||||
leave = true;
|
||||
Init
|
||||
|
@ -196,38 +203,42 @@ impl<'a> Read for CsrfProxy<'a> {
|
|||
leave = true;
|
||||
SearchMethod(buf.len())
|
||||
}
|
||||
},
|
||||
}
|
||||
PartialNameMatch(pos) => {
|
||||
if let Some(lower_begin) = buf.get(pos..pos+14).map(|slice| slice.to_ascii_lowercase()) {
|
||||
if let Some(lower_begin) = buf
|
||||
.get(pos..pos + 14)
|
||||
.map(|slice| slice.to_ascii_lowercase())
|
||||
{
|
||||
if lower_begin.starts_with("name=\"_method\"".as_bytes())
|
||||
|| lower_begin.starts_with("name='_method'".as_bytes()) {
|
||||
buf = &buf[pos+14..];
|
||||
consumed += pos+14;
|
||||
|| lower_begin.starts_with("name='_method'".as_bytes())
|
||||
{
|
||||
buf = &buf[pos + 14..];
|
||||
consumed += pos + 14;
|
||||
CloseInputTag
|
||||
} else if lower_begin.starts_with("name=_method".as_bytes()) {
|
||||
buf = &buf[pos+12..];
|
||||
consumed += pos+12;
|
||||
buf = &buf[pos + 12..];
|
||||
consumed += pos + 12;
|
||||
CloseInputTag
|
||||
} else {
|
||||
SearchMethod(pos)
|
||||
}
|
||||
} else {
|
||||
leave = true;
|
||||
PartialNameMatch(pos)
|
||||
leave = true;
|
||||
PartialNameMatch(pos)
|
||||
}
|
||||
},
|
||||
}
|
||||
CloseInputTag => {
|
||||
leave = true;
|
||||
if let Some(tag_pos) = buf.iter().position(|&c| c as char=='>') {
|
||||
buf = &buf[tag_pos+1..];
|
||||
consumed+=tag_pos+1;
|
||||
if let Some(tag_pos) = buf.iter().position(|&c| c as char == '>') {
|
||||
buf = &buf[tag_pos + 1..];
|
||||
consumed += tag_pos + 1;
|
||||
insert_token = true;
|
||||
Init
|
||||
} else {
|
||||
consumed += buf.len();
|
||||
CloseInputTag
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
(consumed, insert_token)
|
||||
|
@ -243,9 +254,9 @@ impl<'a> Read for CsrfProxy<'a> {
|
|||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests{
|
||||
mod tests {
|
||||
use csrf_proxy::{Buffer, CsrfProxy};
|
||||
use std::io::{Cursor,Read};
|
||||
use std::io::{Cursor, Read};
|
||||
|
||||
#[test]
|
||||
fn test_buffer_size() {
|
||||
|
@ -270,26 +281,26 @@ mod tests{
|
|||
let mut buffer = Buffer::new();
|
||||
let mut buf = [0; 8];
|
||||
|
||||
buffer.push_back(vec![0,1,2,3,4,5,6,7,8,9]);
|
||||
buffer.push_back(vec![10,11,12,13,14,15,16,17,18,19]);
|
||||
buffer.push_back(vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
|
||||
buffer.push_back(vec![10, 11, 12, 13, 14, 15, 16, 17, 18, 19]);
|
||||
|
||||
let size = buffer.read(&mut buf);
|
||||
assert_eq!(size, 8);
|
||||
assert_eq!(buf,[0,1,2,3,4,5,6,7]);
|
||||
assert_eq!(buf, [0, 1, 2, 3, 4, 5, 6, 7]);
|
||||
|
||||
buffer.push_back(vec![20,21,22,23,24,25,26,27,28,29]);
|
||||
buffer.push_back(vec![20, 21, 22, 23, 24, 25, 26, 27, 28, 29]);
|
||||
|
||||
let size = buffer.read(&mut buf);
|
||||
assert_eq!(size, 8);
|
||||
assert_eq!(buf,[8,9,10,11,12,13,14,15]);
|
||||
assert_eq!(buf, [8, 9, 10, 11, 12, 13, 14, 15]);
|
||||
|
||||
let size = buffer.read(&mut buf);
|
||||
assert_eq!(size, 8);
|
||||
assert_eq!(buf,[16,17,18,19,20,21,22,23]);
|
||||
assert_eq!(buf, [16, 17, 18, 19, 20, 21, 22, 23]);
|
||||
|
||||
let size = buffer.read(&mut buf);
|
||||
assert_eq!(size, 6);
|
||||
assert_eq!(buf[..6], [24,25,26,27,28,29]);
|
||||
assert_eq!(buf[..6], [24, 25, 26, 27, 28, 29]);
|
||||
|
||||
let size = buffer.read(&mut buf);
|
||||
assert_eq!(size, 0);
|
||||
|
@ -305,12 +316,13 @@ mod tests{
|
|||
<body>
|
||||
Body of this simple doc
|
||||
</body>
|
||||
</html>".as_bytes();
|
||||
</html>"
|
||||
.as_bytes();
|
||||
let mut proxy = CsrfProxy::from(Box::new(Cursor::new(data)), "abcd".as_bytes());
|
||||
let mut pr_data = Vec::new();
|
||||
let read = proxy.read_to_end(&mut pr_data);
|
||||
assert_eq!(read.unwrap(), data.len());
|
||||
assert_eq!(&pr_data,&data)
|
||||
assert_eq!(&pr_data, &data)
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -327,7 +339,8 @@ mod tests{
|
|||
</p>
|
||||
</form>
|
||||
</body>
|
||||
</html>".as_bytes();
|
||||
</html>"
|
||||
.as_bytes();
|
||||
let expected = "<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
|
@ -340,12 +353,16 @@ mod tests{
|
|||
</p>
|
||||
<input type=\"hidden\" name=\"csrf-token\" value=\"abcd\"/></form>
|
||||
</body>
|
||||
</html>".as_bytes();
|
||||
</html>"
|
||||
.as_bytes();
|
||||
let mut proxy = CsrfProxy::from(Box::new(Cursor::new(data)), "abcd".as_bytes());
|
||||
let mut pr_data = Vec::new();
|
||||
let read = proxy.read_to_end(&mut pr_data);
|
||||
assert_eq!(read.unwrap(), data.len() + "<input type=\"hidden\" name=\"csrf-token\" value=\"abcd\"/>".len());
|
||||
assert_eq!(&pr_data,&expected)
|
||||
assert_eq!(
|
||||
read.unwrap(),
|
||||
data.len() + "<input type=\"hidden\" name=\"csrf-token\" value=\"abcd\"/>".len()
|
||||
);
|
||||
assert_eq!(&pr_data, &expected)
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -360,7 +377,8 @@ mod tests{
|
|||
<input name=\"name\"/>
|
||||
</form>
|
||||
</body>
|
||||
</html>".as_bytes();
|
||||
</html>"
|
||||
.as_bytes();
|
||||
let expected = "<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
|
@ -371,12 +389,16 @@ mod tests{
|
|||
<input type=\"hidden\" name=\"csrf-token\" value=\"abcd\"/><input name=\"name\"/>
|
||||
</form>
|
||||
</body>
|
||||
</html>".as_bytes();
|
||||
</html>"
|
||||
.as_bytes();
|
||||
let mut proxy = CsrfProxy::from(Box::new(Cursor::new(data)), "abcd".as_bytes());
|
||||
let mut pr_data = Vec::new();
|
||||
let read = proxy.read_to_end(&mut pr_data);
|
||||
assert_eq!(read.unwrap(), data.len() + "<input type=\"hidden\" name=\"csrf-token\" value=\"abcd\"/>".len());
|
||||
assert_eq!(&pr_data,&expected)
|
||||
assert_eq!(
|
||||
read.unwrap(),
|
||||
data.len() + "<input type=\"hidden\" name=\"csrf-token\" value=\"abcd\"/>".len()
|
||||
);
|
||||
assert_eq!(&pr_data, &expected)
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -391,7 +413,8 @@ mod tests{
|
|||
<input name=\"_method\"/>
|
||||
</form>
|
||||
</body>
|
||||
</html>".as_bytes();
|
||||
</html>"
|
||||
.as_bytes();
|
||||
let expected = "<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
|
@ -402,15 +425,19 @@ mod tests{
|
|||
<input name=\"_method\"/><input type=\"hidden\" name=\"csrf-token\" value=\"abcd\"/>
|
||||
</form>
|
||||
</body>
|
||||
</html>".as_bytes();
|
||||
</html>"
|
||||
.as_bytes();
|
||||
let mut proxy = CsrfProxy::from(Box::new(Cursor::new(data)), "abcd".as_bytes());
|
||||
let mut pr_data = Vec::new();
|
||||
let read = proxy.read_to_end(&mut pr_data);
|
||||
assert_eq!(read.unwrap(), data.len() + "<input type=\"hidden\" name=\"csrf-token\" value=\"abcd\"/>".len());
|
||||
assert_eq!(&pr_data,&expected)
|
||||
assert_eq!(
|
||||
read.unwrap(),
|
||||
data.len() + "<input type=\"hidden\" name=\"csrf-token\" value=\"abcd\"/>".len()
|
||||
);
|
||||
assert_eq!(&pr_data, &expected)
|
||||
}
|
||||
|
||||
struct ErrorReader{}
|
||||
struct ErrorReader {}
|
||||
|
||||
impl Read for ErrorReader {
|
||||
fn read(&mut self, _buf: &mut [u8]) -> Result<usize, ::std::io::Error> {
|
||||
|
@ -420,15 +447,18 @@ mod tests{
|
|||
|
||||
#[test]
|
||||
fn test_relay_error() {
|
||||
let buf = &mut[0; 1];
|
||||
let err = ErrorReader{};
|
||||
let buf = &mut [0; 1];
|
||||
let err = ErrorReader {};
|
||||
let mut proxy_err = CsrfProxy::from(Box::new(err), &[0]);
|
||||
let read = proxy_err.read(buf).unwrap_err();
|
||||
assert_eq!(read.kind(), ::std::io::Error::new(::std::io::ErrorKind::Other, "").kind());
|
||||
assert_eq!(
|
||||
read.kind(),
|
||||
::std::io::Error::new(::std::io::ErrorKind::Other, "").kind()
|
||||
);
|
||||
}
|
||||
|
||||
struct SlowReader<'a>{
|
||||
content: &'a[u8],
|
||||
struct SlowReader<'a> {
|
||||
content: &'a [u8],
|
||||
}
|
||||
|
||||
impl<'a> Read for SlowReader<'a> {
|
||||
|
@ -444,7 +474,8 @@ mod tests{
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn test_difficult_cut() {//this basically re-test the parser, using short reads so it encounter rare code paths
|
||||
fn test_difficult_cut() {
|
||||
//this basically re-test the parser, using short reads so it encounter rare code paths
|
||||
let data = "<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
|
@ -457,7 +488,8 @@ mod tests{
|
|||
</p>
|
||||
</form>
|
||||
</body>
|
||||
</html>".as_bytes();
|
||||
</html>"
|
||||
.as_bytes();
|
||||
let expected = "<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
|
@ -470,12 +502,16 @@ mod tests{
|
|||
</p>
|
||||
<input type=\"hidden\" name=\"csrf-token\" value=\"abcd\"/></form>
|
||||
</body>
|
||||
</html>".as_bytes();
|
||||
let mut proxy = CsrfProxy::from(Box::new(SlowReader{content: data}), "abcd".as_bytes());
|
||||
</html>"
|
||||
.as_bytes();
|
||||
let mut proxy = CsrfProxy::from(Box::new(SlowReader { content: data }), "abcd".as_bytes());
|
||||
let mut pr_data = Vec::new();
|
||||
let read = proxy.read_to_end(&mut pr_data);
|
||||
assert_eq!(read.unwrap(), data.len() + "<input type=\"hidden\" name=\"csrf-token\" value=\"abcd\"/>".len());
|
||||
assert_eq!(&pr_data,&expected);
|
||||
assert_eq!(
|
||||
read.unwrap(),
|
||||
data.len() + "<input type=\"hidden\" name=\"csrf-token\" value=\"abcd\"/>".len()
|
||||
);
|
||||
assert_eq!(&pr_data, &expected);
|
||||
|
||||
let data = "<!DOCTYPE html>
|
||||
<html>
|
||||
|
@ -487,7 +523,8 @@ mod tests{
|
|||
<input name=\"name\"/>
|
||||
</form>
|
||||
</body>
|
||||
</html>".as_bytes();
|
||||
</html>"
|
||||
.as_bytes();
|
||||
let expected = "<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
|
@ -498,12 +535,16 @@ mod tests{
|
|||
<input type=\"hidden\" name=\"csrf-token\" value=\"abcd\"/><input name=\"name\"/>
|
||||
</form>
|
||||
</body>
|
||||
</html>".as_bytes();
|
||||
let mut proxy = CsrfProxy::from(Box::new(SlowReader{content: data}), "abcd".as_bytes());
|
||||
</html>"
|
||||
.as_bytes();
|
||||
let mut proxy = CsrfProxy::from(Box::new(SlowReader { content: data }), "abcd".as_bytes());
|
||||
let mut pr_data = Vec::new();
|
||||
let read = proxy.read_to_end(&mut pr_data);
|
||||
assert_eq!(read.unwrap(), data.len() + "<input type=\"hidden\" name=\"csrf-token\" value=\"abcd\"/>".len());
|
||||
assert_eq!(&pr_data,&expected);
|
||||
assert_eq!(
|
||||
read.unwrap(),
|
||||
data.len() + "<input type=\"hidden\" name=\"csrf-token\" value=\"abcd\"/>".len()
|
||||
);
|
||||
assert_eq!(&pr_data, &expected);
|
||||
|
||||
let data = "<!DOCTYPE html>
|
||||
<html>
|
||||
|
@ -515,7 +556,8 @@ mod tests{
|
|||
<input name=\"_method\"/>
|
||||
</form>
|
||||
</body>
|
||||
</html>".as_bytes();
|
||||
</html>"
|
||||
.as_bytes();
|
||||
let expected = "<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
|
@ -526,11 +568,15 @@ mod tests{
|
|||
<input name=\"_method\"/><input type=\"hidden\" name=\"csrf-token\" value=\"abcd\"/>
|
||||
</form>
|
||||
</body>
|
||||
</html>".as_bytes();
|
||||
let mut proxy = CsrfProxy::from(Box::new(SlowReader{content: data}), "abcd".as_bytes());
|
||||
</html>"
|
||||
.as_bytes();
|
||||
let mut proxy = CsrfProxy::from(Box::new(SlowReader { content: data }), "abcd".as_bytes());
|
||||
let mut pr_data = Vec::new();
|
||||
let read = proxy.read_to_end(&mut pr_data);
|
||||
assert_eq!(read.unwrap(), data.len() + "<input type=\"hidden\" name=\"csrf-token\" value=\"abcd\"/>".len());
|
||||
assert_eq!(&pr_data,&expected)
|
||||
assert_eq!(
|
||||
read.unwrap(),
|
||||
data.len() + "<input type=\"hidden\" name=\"csrf-token\" value=\"abcd\"/>".len()
|
||||
);
|
||||
assert_eq!(&pr_data, &expected)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
use csrf::{AesGcmCsrfProtection, CsrfProtection, CSRF_COOKIE_NAME};
|
||||
use data_encoding::{BASE64, BASE64URL_NOPAD};
|
||||
use rocket::{Request, State};
|
||||
use rocket::http::{Cookie, Status};
|
||||
use rocket::outcome::Outcome;
|
||||
use rocket::request::{self, FromRequest};
|
||||
use rocket::{Request, State};
|
||||
use serde::{Serialize, Serializer};
|
||||
use time::Duration;
|
||||
|
||||
|
|
|
@ -49,11 +49,11 @@ extern crate rocket;
|
|||
extern crate serde;
|
||||
extern crate time;
|
||||
|
||||
mod csrf_proxy;
|
||||
mod csrf_fairing;
|
||||
mod csrf_proxy;
|
||||
mod csrf_token;
|
||||
mod path;
|
||||
mod utils;
|
||||
|
||||
pub use self::csrf_fairing::{CsrfFairingBuilder, CsrfFairing};
|
||||
pub use self::csrf_fairing::{CsrfFairing, CsrfFairingBuilder};
|
||||
pub use self::csrf_token::CsrfToken;
|
||||
|
|
220
src/path.rs
220
src/path.rs
|
@ -30,32 +30,34 @@ impl Path {
|
|||
}
|
||||
})
|
||||
.collect();
|
||||
if path[0..path.len()-1].iter().any(|a|
|
||||
if let PathPart::MultiDynamic(_) = a {true} else {false}
|
||||
) {
|
||||
if path[0..path.len() - 1].iter().any(|a| {
|
||||
if let PathPart::MultiDynamic(_) = a {
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}) {
|
||||
panic!("PathPart::MultiDynamic can only be found at end of path"); //TODO return error instead of panic
|
||||
}
|
||||
|
||||
let param = query.map(|query| {
|
||||
let param = query.map(|query| {
|
||||
parse_args(query)
|
||||
.map(|(k, v)| {(
|
||||
k.to_owned(),
|
||||
if v.starts_with('<') && v.ends_with("..>") {
|
||||
panic!("PathPart::MultiDynamic is invalid in query part");
|
||||
} else if v.starts_with('<') && v.ends_with('>') {
|
||||
//do the same kind of parsing as above, but on query params
|
||||
PathPart::Dynamic(v[1..v.len() - 1].to_owned())
|
||||
} else {
|
||||
PathPart::Static(v.to_owned())
|
||||
},
|
||||
)
|
||||
})
|
||||
.collect()
|
||||
.map(|(k, v)| {
|
||||
(
|
||||
k.to_owned(),
|
||||
if v.starts_with('<') && v.ends_with("..>") {
|
||||
panic!("PathPart::MultiDynamic is invalid in query part");
|
||||
} else if v.starts_with('<') && v.ends_with('>') {
|
||||
//do the same kind of parsing as above, but on query params
|
||||
PathPart::Dynamic(v[1..v.len() - 1].to_owned())
|
||||
} else {
|
||||
PathPart::Static(v.to_owned())
|
||||
},
|
||||
)
|
||||
})
|
||||
.collect()
|
||||
});
|
||||
Path {
|
||||
path,
|
||||
param,
|
||||
}
|
||||
Path { path, param }
|
||||
}
|
||||
|
||||
pub fn extract<'a>(&self, uri: &'a str) -> Option<HashMap<&str, String>> {
|
||||
|
@ -73,32 +75,32 @@ impl Path {
|
|||
loop {
|
||||
match reference.next() {
|
||||
Some(reference) => {
|
||||
match reference {
|
||||
PathPart::Static(reference) => {
|
||||
//static, but not the same, fail to parse
|
||||
if let Some(val) = path.next() {
|
||||
if val!=reference{
|
||||
return None
|
||||
}
|
||||
} else {
|
||||
return None
|
||||
}
|
||||
},
|
||||
PathPart::Dynamic(key) => {
|
||||
//dynamic, store to hashmap
|
||||
if let Some(val) = path.next() {
|
||||
res.insert(key, val.to_owned());
|
||||
} else {
|
||||
return None
|
||||
match reference {
|
||||
PathPart::Static(reference) => {
|
||||
//static, but not the same, fail to parse
|
||||
if let Some(val) = path.next() {
|
||||
if val != reference {
|
||||
return None;
|
||||
}
|
||||
} else {
|
||||
return None;
|
||||
}
|
||||
PathPart::MultiDynamic(key) => {
|
||||
let val = path.collect::<Vec<_>>().join("/");
|
||||
res.insert(key, val);
|
||||
break;
|
||||
}
|
||||
PathPart::Dynamic(key) => {
|
||||
//dynamic, store to hashmap
|
||||
if let Some(val) = path.next() {
|
||||
res.insert(key, val.to_owned());
|
||||
} else {
|
||||
return None;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
PathPart::MultiDynamic(key) => {
|
||||
let val = path.collect::<Vec<_>>().join("/");
|
||||
res.insert(key, val);
|
||||
break;
|
||||
}
|
||||
};
|
||||
}
|
||||
None => if path.next().is_some() {
|
||||
//not the same lenght, fail to parse
|
||||
return None;
|
||||
|
@ -120,7 +122,9 @@ impl Path {
|
|||
//dynamic, store to hashmap
|
||||
res.insert(key, hm.get::<&str>(&(k as &str))?.to_string());
|
||||
}
|
||||
PathPart::MultiDynamic(_) => panic!("PathPart::MultiDynamic is invalid in query part"),
|
||||
PathPart::MultiDynamic(_) => {
|
||||
panic!("PathPart::MultiDynamic is invalid in query part")
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
@ -143,7 +147,9 @@ impl Path {
|
|||
res.push('/');
|
||||
match seg {
|
||||
PathPart::Static(val) => res.push_str(val),
|
||||
PathPart::Dynamic(val) | PathPart::MultiDynamic(val) => res.push_str(param.get::<str>(val)?),
|
||||
PathPart::Dynamic(val) | PathPart::MultiDynamic(val) => {
|
||||
res.push_str(param.get::<str>(val)?)
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some(ref keymap) = self.param {
|
||||
|
@ -155,7 +161,9 @@ impl Path {
|
|||
match v {
|
||||
PathPart::Static(val) => res.push_str(val),
|
||||
PathPart::Dynamic(val) => res.push_str(param.get::<str>(val)?),
|
||||
PathPart::MultiDynamic(_) => panic!("PathPart::MultiDynamic is invalid in query part"),
|
||||
PathPart::MultiDynamic(_) => {
|
||||
panic!("PathPart::MultiDynamic is invalid in query part")
|
||||
}
|
||||
}
|
||||
res.push('&');
|
||||
}
|
||||
|
@ -172,7 +180,7 @@ enum PathPart {
|
|||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests{
|
||||
mod tests {
|
||||
use path::Path;
|
||||
use std::collections::HashMap;
|
||||
#[test]
|
||||
|
@ -189,41 +197,66 @@ mod tests{
|
|||
assert_eq!(no_query.map(&HashMap::new()).unwrap(), "/path/no_query");
|
||||
|
||||
let mut hashmap = HashMap::new();
|
||||
hashmap.insert("key","value".to_owned());
|
||||
hashmap.insert("key", "value".to_owned());
|
||||
assert_eq!(no_query.map(&hashmap).unwrap(), "/path/no_query");
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn test_static_path_with_query() {
|
||||
let query = Path::from("/path/query?param=value¶m2=value2");
|
||||
assert!(query.extract("/path/query").is_none());
|
||||
assert!(query.extract("/path/other?param=value¶m2=value2").is_none());
|
||||
assert!(query.extract("/path/query/longer?param=value¶m2=value2").is_none());
|
||||
assert!(
|
||||
query
|
||||
.extract("/path/other?param=value¶m2=value2")
|
||||
.is_none()
|
||||
);
|
||||
assert!(
|
||||
query
|
||||
.extract("/path/query/longer?param=value¶m2=value2")
|
||||
.is_none()
|
||||
);
|
||||
assert!(query.extract("/path?param=value¶m2=value2").is_none());
|
||||
|
||||
let hashmap = query.extract("/path/query?param=value¶m2=value2").unwrap();
|
||||
let hashmap = query
|
||||
.extract("/path/query?param=value¶m2=value2")
|
||||
.unwrap();
|
||||
assert_eq!(hashmap.len(), 0);
|
||||
|
||||
let hashmap = query.extract("/path/query?param2=value2¶m=value").unwrap();
|
||||
let hashmap = query
|
||||
.extract("/path/query?param2=value2¶m=value")
|
||||
.unwrap();
|
||||
assert_eq!(hashmap.len(), 0);
|
||||
|
||||
let uri = query.map(&HashMap::new()).unwrap();
|
||||
assert!(uri=="/path/query?param=value¶m2=value2" || uri=="/path/query?param2=value2¶m=value");
|
||||
|
||||
assert!(
|
||||
uri == "/path/query?param=value¶m2=value2"
|
||||
|| uri == "/path/query?param2=value2¶m=value"
|
||||
);
|
||||
|
||||
let mut hashmap = HashMap::new();
|
||||
hashmap.insert("key","value".to_owned());
|
||||
hashmap.insert("key", "value".to_owned());
|
||||
let uri = query.map(&hashmap).unwrap();
|
||||
assert!(uri=="/path/query?param=value¶m2=value2" || uri=="/path/query?param2=value2¶m=value");
|
||||
assert!(
|
||||
uri == "/path/query?param=value¶m2=value2"
|
||||
|| uri == "/path/query?param2=value2¶m=value"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_dynamic_path_without_query() {
|
||||
let no_query = Path::from("/path/<with>/<dynamic>/values");
|
||||
assert!(no_query.extract("/path/with/dynamic/values/longer").is_none());
|
||||
assert!(
|
||||
no_query
|
||||
.extract("/path/with/dynamic/values/longer")
|
||||
.is_none()
|
||||
);
|
||||
assert!(no_query.extract("/path/with/dynamic").is_none());
|
||||
assert!(no_query.extract("/path/with/dynamic/non_value").is_none());
|
||||
assert!(no_query.extract("/path/with/dynamic/values?and=query").is_none());
|
||||
assert!(
|
||||
no_query
|
||||
.extract("/path/with/dynamic/values?and=query")
|
||||
.is_none()
|
||||
);
|
||||
|
||||
let hashmap = no_query.extract("/path/containing/moving/values").unwrap();
|
||||
assert_eq!(hashmap.len(), 2);
|
||||
|
@ -233,25 +266,39 @@ mod tests{
|
|||
assert!(no_query.map(&HashMap::new()).is_none());
|
||||
|
||||
let mut hashmap = HashMap::new();
|
||||
hashmap.insert("with","with".to_owned());
|
||||
hashmap.insert("dynamic","non_static".to_owned());
|
||||
assert_eq!(no_query.map(&hashmap).unwrap(), "/path/with/non_static/values");
|
||||
hashmap.insert("random","value".to_owned());
|
||||
assert_eq!(no_query.map(&hashmap).unwrap(), "/path/with/non_static/values");
|
||||
hashmap.insert("with", "with".to_owned());
|
||||
hashmap.insert("dynamic", "non_static".to_owned());
|
||||
assert_eq!(
|
||||
no_query.map(&hashmap).unwrap(),
|
||||
"/path/with/non_static/values"
|
||||
);
|
||||
hashmap.insert("random", "value".to_owned());
|
||||
assert_eq!(
|
||||
no_query.map(&hashmap).unwrap(),
|
||||
"/path/with/non_static/values"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_dynamic_path_with_query() {
|
||||
let query = Path::from("/path/<with>/<dynamic>/values?key=<value>&static=static");
|
||||
assert!(query.extract("/path/with/dynamic/values?key=something&static=error").is_none());
|
||||
assert!(
|
||||
query
|
||||
.extract("/path/with/dynamic/values?key=something&static=error")
|
||||
.is_none()
|
||||
);
|
||||
|
||||
let hashmap = query.extract("/path/containing/moving/values?key=val&static=static").unwrap();
|
||||
let hashmap = query
|
||||
.extract("/path/containing/moving/values?key=val&static=static")
|
||||
.unwrap();
|
||||
assert_eq!(hashmap.len(), 3);
|
||||
assert_eq!(hashmap.get("with").unwrap(), "containing");
|
||||
assert_eq!(hashmap.get("dynamic").unwrap(), "moving");
|
||||
assert_eq!(hashmap.get("value").unwrap(), "val");
|
||||
|
||||
let hashmap = query.extract("/path/containing/moving/values?static=static&key=val").unwrap();
|
||||
let hashmap = query
|
||||
.extract("/path/containing/moving/values?static=static&key=val")
|
||||
.unwrap();
|
||||
assert_eq!(hashmap.len(), 3);
|
||||
assert_eq!(hashmap.get("with").unwrap(), "containing");
|
||||
assert_eq!(hashmap.get("dynamic").unwrap(), "moving");
|
||||
|
@ -260,12 +307,22 @@ mod tests{
|
|||
assert!(query.map(&HashMap::new()).is_none());
|
||||
|
||||
let mut hashmap = HashMap::new();
|
||||
hashmap.insert("with","with".to_owned());
|
||||
hashmap.insert("dynamic","non_static".to_owned());
|
||||
hashmap.insert("with", "with".to_owned());
|
||||
hashmap.insert("dynamic", "non_static".to_owned());
|
||||
hashmap.insert("value", "something".to_owned());
|
||||
assert!(query.map(&hashmap).unwrap()=="/path/with/non_static/values?key=something&static=static" || query.map(&hashmap).unwrap()=="/path/with/non_static/values?static=static&key=something");
|
||||
hashmap.insert("random","value".to_owned());
|
||||
assert!(query.map(&hashmap).unwrap()=="/path/with/non_static/values?key=something&static=static" || query.map(&hashmap).unwrap()=="/path/with/non_static/values?static=static&key=something");
|
||||
assert!(
|
||||
query.map(&hashmap).unwrap()
|
||||
== "/path/with/non_static/values?key=something&static=static"
|
||||
|| query.map(&hashmap).unwrap()
|
||||
== "/path/with/non_static/values?static=static&key=something"
|
||||
);
|
||||
hashmap.insert("random", "value".to_owned());
|
||||
assert!(
|
||||
query.map(&hashmap).unwrap()
|
||||
== "/path/with/non_static/values?key=something&static=static"
|
||||
|| query.map(&hashmap).unwrap()
|
||||
== "/path/with/non_static/values?static=static&key=something"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -283,17 +340,22 @@ mod tests{
|
|||
#[test]
|
||||
fn test_multidynamic() {
|
||||
let query = Path::from("/path/<multidyn..>?static=static");
|
||||
|
||||
|
||||
let hashmap = query.extract("/path?static=static").unwrap();
|
||||
assert_eq!(hashmap.len(), 1);
|
||||
assert_eq!(hashmap.get("multidyn").unwrap(), "");
|
||||
|
||||
let hashmap = query.extract("/path/longer/than/before?static=static").unwrap();
|
||||
|
||||
let hashmap = query
|
||||
.extract("/path/longer/than/before?static=static")
|
||||
.unwrap();
|
||||
assert_eq!(hashmap.len(), 1);
|
||||
assert_eq!(hashmap.get("multidyn").unwrap(), "longer/than/before");
|
||||
|
||||
|
||||
let mut hashmap = HashMap::new();
|
||||
hashmap.insert("multidyn","something".to_owned());
|
||||
assert_eq!(query.map(&hashmap).unwrap(), "/path/something?static=static");
|
||||
hashmap.insert("multidyn", "something".to_owned());
|
||||
assert_eq!(
|
||||
query.map(&hashmap).unwrap(),
|
||||
"/path/something?static=static"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
18
src/utils.rs
18
src/utils.rs
|
@ -15,18 +15,24 @@ fn parse_keyvalue(kv: &str) -> Option<(&str, &str)> {
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use utils::{parse_keyvalue, parse_args};
|
||||
use utils::{parse_args, parse_keyvalue};
|
||||
#[test]
|
||||
fn test_parse_keyvalue() {
|
||||
assert_eq!(parse_keyvalue("a_key=a_value").unwrap(),("a_key", "a_value"));
|
||||
assert_eq!(
|
||||
parse_keyvalue("a_key=a_value").unwrap(),
|
||||
("a_key", "a_value")
|
||||
);
|
||||
|
||||
assert_eq!(parse_keyvalue("=a_value").unwrap(),("", "a_value"));
|
||||
assert_eq!(parse_keyvalue("=a_value").unwrap(), ("", "a_value"));
|
||||
|
||||
assert_eq!(parse_keyvalue("a_key=").unwrap(),("a_key", ""));
|
||||
assert_eq!(parse_keyvalue("a_key=").unwrap(), ("a_key", ""));
|
||||
|
||||
assert_eq!(parse_keyvalue("a_key=a=value").unwrap(),("a_key", "a=value"));
|
||||
assert_eq!(
|
||||
parse_keyvalue("a_key=a=value").unwrap(),
|
||||
("a_key", "a=value")
|
||||
);
|
||||
|
||||
assert_eq!(parse_keyvalue("=").unwrap(),("", ""));
|
||||
assert_eq!(parse_keyvalue("=").unwrap(), ("", ""));
|
||||
|
||||
assert!(parse_keyvalue("a_key_a_value").is_none());
|
||||
|
||||
|
|
Loading…
Reference in a new issue