Skip to content

Commit

Permalink
Make TLS configurable, improve test coverage and add redirect support (
Browse files Browse the repository at this point in the history
  • Loading branch information
saschagrunert committed Aug 1, 2018
1 parent 9c2f9a6 commit 1102ed9
Show file tree
Hide file tree
Showing 39 changed files with 586 additions and 334 deletions.
4 changes: 3 additions & 1 deletion .circleci/config.yml
Expand Up @@ -101,7 +101,9 @@ jobs:
- run:
name: Prepare database
command: |
sleep 10
while true; do
if pg_isready -qh localhost; then break; fi
done
diesel migration run \
--database-url postgres://username:@localhost/database
- run:
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Expand Up @@ -2,3 +2,4 @@ target/
.*/target/
**/*.rs.bk
*.tar
.gdb_history
2 changes: 1 addition & 1 deletion .rustfmt.toml
@@ -1,6 +1,6 @@
condense_wildcard_suffixes = true
format_strings = true
max_width = 120
max_width = 100
merge_imports = true
newline_style = "Unix"
normalize_comments = true
Expand Down
121 changes: 33 additions & 88 deletions Cargo.lock

Large diffs are not rendered by default.

15 changes: 7 additions & 8 deletions Config.toml
@@ -1,7 +1,11 @@
[server]
ip = "127.0.0.1"
port = "30080"
tls = false
url = "http://127.0.0.1:30080"
cert = "backend/tls/cert.pem"
key = "backend/tls/key.pem"
redirect-from = [
"http://127.0.0.1:30081",
"https://127.0.0.1:30082",
]

[log]
actix-web = "debug"
Expand All @@ -12,8 +16,3 @@ host = "127.0.0.1"
username = "username"
password = ""
database = "database"

[api]
login-credentials = "/login/credentials"
login-session = "/login/session"
logout = "/logout"
6 changes: 2 additions & 4 deletions Makefile
Expand Up @@ -46,7 +46,7 @@ build-frontend:
cargo web build $(FRONTEND_ARGS)

coverage:
cd backend && cargo kcov -v
cd backend && cargo kcov

deploy:
# Deploy the frontend
Expand Down Expand Up @@ -93,9 +93,7 @@ run-postgres:
-p 5432:5432 \
-d postgres ;\
while true; do \
if docker logs postgres 2>&1 | grep -q "PostgreSQL init process complete"; then \
break ;\
fi \
if pg_isready -qh $(PG_HOST); then break; fi \
done ;\
sleep 1; \
diesel migration run --database-url \
Expand Down
5 changes: 3 additions & 2 deletions backend/Cargo.toml
Expand Up @@ -20,10 +20,10 @@ path = "src/main.rs"

[dev-dependencies]
lazy_static = "1.0.2"
reqwest = "0.8.6"
reqwest = "0.8.7"

[dependencies]
actix = "0.7.2"
actix = "0.7.3"
actix-web = { version = "=0.7.1", features = ["alpn"] }
bytes = "0.4.9"
diesel = { version = "1.3.2", features = ["r2d2", "postgres"] }
Expand All @@ -40,5 +40,6 @@ serde_cbor = "0.8.2"
serde_derive = "1.0.70"
time = "0.1.40"
toml = "0.4.6"
url = "1.7.1"
uuid = { version = "0.6.5", features = ["v4"] }
webapp = { path = "..", features = ["backend"] }
2 changes: 2 additions & 0 deletions backend/src/cbor.rs → backend/src/cbor/mod.rs
@@ -1,5 +1,7 @@
//! Cbor abstraction for HTTP message handling

mod test;

use actix_web::{
dev::HttpResponseBuilder,
error::{Error as HttpError, PayloadError},
Expand Down
80 changes: 80 additions & 0 deletions backend/src/cbor/test.rs
@@ -0,0 +1,80 @@
//! The cbor tests

#![cfg(test)]

use actix_web::{test::TestRequest, HttpRequest, HttpResponse};
use cbor::{CborRequest, CborResponseBuilder};
use failure::Fail;
use futures::Future;
use serde_cbor::to_vec;
use webapp::protocol::{request, response};

fn build_request() -> TestRequest<()> {
TestRequest::with_header("content-type", "application/cbor")
}

#[test]
fn succeed_to_decode_request() {
let login = request::LoginCredentials {
username: "username".to_owned(),
password: "password".to_owned(),
};
let payload = to_vec(&login).unwrap();
let request: HttpRequest<()> = build_request().set_payload(payload).finish();
let result: request::LoginCredentials = CborRequest::new(&request).wait().unwrap();
assert_eq!(result, login);
}

#[test]
fn fail_to_decode_empty_request() {
let request: HttpRequest<()> = build_request().finish();
let result: Result<(), _> = CborRequest::new(&request).wait();
assert!(
&result
.unwrap_err()
.cause()
.unwrap()
.to_string()
.contains("EOF")
);
}

#[test]
fn fail_to_decode_wrong_request() {
let payload: Vec<u8> = (1..10).collect();
let request: HttpRequest<()> = build_request().set_payload(payload).finish();
let result: Result<(), _> = CborRequest::new(&request).wait();
assert!(
&result
.unwrap_err()
.cause()
.unwrap()
.to_string()
.contains("invalid type")
);
}

#[test]
fn fail_to_decode_wrong_typed_request() {
let login = request::LoginCredentials {
username: "username".to_owned(),
password: "password".to_owned(),
};
let payload = to_vec(&login).unwrap();
let request: HttpRequest<()> = build_request().set_payload(payload).finish();
let result: Result<request::Logout, _> = CborRequest::new(&request).wait();
assert!(
&result
.unwrap_err()
.cause()
.unwrap()
.to_string()
.contains("missing field")
);
}

#[test]
fn succeed_to_encode_response() {
let response = HttpResponse::Ok().cbor(response::Logout);
assert!(response.is_ok());
}
14 changes: 6 additions & 8 deletions backend/src/http/login_credentials/mod.rs
Expand Up @@ -2,26 +2,24 @@

use actix::{dev::ToEnvelope, prelude::*};
use actix_web::{error::ErrorUnauthorized, AsyncResponder, HttpRequest, HttpResponse};
use cbor::{CborRequest, CborResponseBuilder};
use cbor::CborResponseBuilder;
use database::CreateSession;
use futures::Future;
use http::FutureResponse;
use http::{unpack_cbor, FutureResponse};
use server::State;
use token::Token;
use webapp::protocol::{request, response};

mod tests;
mod test;

pub fn login_credentials<T>(http_request: &HttpRequest<State<T>>) -> FutureResponse
where
T: Actor + Handler<CreateSession>,
<T as Actor>::Context: ToEnvelope<T, CreateSession>,
{
let request_clone = http_request.clone();
CborRequest::new(http_request)
.from_err()
// Verify username and password
.and_then(|request::LoginCredentials{username, password}| {
let (request_clone, cbor) = unpack_cbor(http_request);
// Verify username and password
cbor.and_then(|request::LoginCredentials{username, password}| {
debug!("User {} is trying to login", username);
if username.is_empty() || password.is_empty() || username != password {
return Err(ErrorUnauthorized("wrong username or password"));
Expand Down
Expand Up @@ -8,7 +8,7 @@ use database::CreateSession;
use failure::Error;
use http::{
login_credentials::login_credentials,
tests::{execute_request, state, DatabaseExecutorMock},
test::{execute_request, state, DatabaseExecutorMock},
};
use serde_cbor::to_vec;
use token::Token;
Expand Down Expand Up @@ -59,3 +59,18 @@ fn fail_to_login_with_wrong_credentials() {
// Then
assert_eq!(response.status().is_success(), false);
}

#[test]
fn fail_to_login_with_invalid_cbor() {
// Given
#[derive(Serialize)]
struct Invalid;
let mut server = create_testserver();
let body = to_vec(&Invalid).unwrap();

// When
let response = execute_request(&mut server, body);

// Then
assert_eq!(response.status().is_success(), false);
}
14 changes: 6 additions & 8 deletions backend/src/http/login_session/mod.rs
Expand Up @@ -2,26 +2,24 @@

use actix::{dev::ToEnvelope, prelude::*};
use actix_web::{AsyncResponder, HttpRequest, HttpResponse};
use cbor::{CborRequest, CborResponseBuilder};
use cbor::CborResponseBuilder;
use database::UpdateSession;
use futures::Future;
use http::FutureResponse;
use http::{unpack_cbor, FutureResponse};
use server::State;
use token::Token;
use webapp::protocol::{model::Session, request, response};

mod tests;
mod test;

pub fn login_session<T>(http_request: &HttpRequest<State<T>>) -> FutureResponse
where
T: Actor + Handler<UpdateSession>,
<T as Actor>::Context: ToEnvelope<T, UpdateSession>,
{
let request_clone = http_request.clone();
CborRequest::new(http_request)
.from_err()
// Create a new token for the already given one
.and_then(|request::LoginSession(Session{token})| {
let (request_clone, cbor) = unpack_cbor(http_request);
// Create a new token for the already given one
cbor.and_then(|request::LoginSession(Session{token})| {
debug!("Session token {} wants to be renewed", token);
Ok((Token::verify(&token)?, token))
})
Expand Down
Expand Up @@ -8,7 +8,7 @@ use database::UpdateSession;
use failure::Error;
use http::{
login_session::login_session,
tests::{execute_request, state, DatabaseExecutorMock},
test::{execute_request, state, DatabaseExecutorMock},
};
use serde_cbor::to_vec;
use token::Token;
Expand Down Expand Up @@ -56,3 +56,18 @@ fn fail_to_login_with_wrong_session() {
// Then
assert_eq!(response.status().is_success(), false);
}

#[test]
fn fail_to_login_with_invalid_cbor() {
// Given
#[derive(Serialize)]
struct Invalid;
let mut server = create_testserver();
let body = to_vec(&Invalid).unwrap();

// When
let response = execute_request(&mut server, body);

// Then
assert_eq!(response.status().is_success(), false);
}
37 changes: 17 additions & 20 deletions backend/src/http/logout/mod.rs
Expand Up @@ -2,35 +2,32 @@

use actix::{dev::ToEnvelope, prelude::*};
use actix_web::{AsyncResponder, HttpRequest, HttpResponse};
use cbor::{CborRequest, CborResponseBuilder};
use cbor::CborResponseBuilder;
use database::DeleteSession;
use futures::Future;
use http::FutureResponse;
use http::{unpack_cbor, FutureResponse};
use server::State;
use webapp::protocol::{model::Session, request, response};

mod tests;
mod test;

pub fn logout<T: Actor>(http_request: &HttpRequest<State<T>>) -> FutureResponse
where
T: Actor + Handler<DeleteSession>,
<T as Actor>::Context: ToEnvelope<T, DeleteSession>,
{
let request_clone = http_request.clone();
CborRequest::new(http_request)
.from_err()
// Remove the session from the database
.and_then(move |request::Logout(Session{token})| {
debug!("Session token {} wants to be logged out", token);
request_clone
.state()
.database
.send(DeleteSession(token))
.from_err()
.and_then(|result| {
result?;
Ok(HttpResponse::Ok().cbor(response::Logout)?)
})
})
.responder()
let (request_clone, cbor) = unpack_cbor(http_request);
// Remove the session from the database
cbor.and_then(move |request::Logout(Session { token })| {
debug!("Session token {} wants to be logged out", token);
request_clone
.state()
.database
.send(DeleteSession(token))
.from_err()
.and_then(|result| {
result?;
Ok(HttpResponse::Ok().cbor(response::Logout)?)
})
}).responder()
}
Expand Up @@ -8,7 +8,7 @@ use database::DeleteSession;
use failure::Error;
use http::{
logout::logout,
tests::{execute_request, state, DatabaseExecutorMock},
test::{execute_request, state, DatabaseExecutorMock},
};
use serde_cbor::to_vec;
use webapp::protocol::{model::Session, request};
Expand Down Expand Up @@ -39,3 +39,18 @@ fn succeed_to_logout() {
// Then
assert!(response.status().is_success());
}

#[test]
fn fail_to_logout_with_invalid_cbor() {
// Given
#[derive(Serialize)]
struct Invalid;
let mut server = create_testserver();
let body = to_vec(&Invalid).unwrap();

// When
let response = execute_request(&mut server, body);

// Then
assert_eq!(response.status().is_success(), false);
}

0 comments on commit 1102ed9

Please sign in to comment.