Add anyhow, Improve Auth token code
Moved test helper code to its own module.
This commit is contained in:
@@ -32,6 +32,7 @@ pub async fn login(
|
||||
user_dao: web::Data<Box<dyn UserDao>>,
|
||||
) -> HttpResponse {
|
||||
debug!("Logging in: {}", creds.username);
|
||||
|
||||
if let Some(user) = user_dao.get_user(&creds.username, &creds.password) {
|
||||
let claims = Claims {
|
||||
sub: user.id.to_string(),
|
||||
@@ -43,6 +44,7 @@ pub async fn login(
|
||||
&EncodingKey::from_secret(secret_key().as_bytes()),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
HttpResponse::Ok().json(Token { token: &token })
|
||||
} else {
|
||||
error!(
|
||||
@@ -56,7 +58,7 @@ pub async fn login(
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::database::testhelpers::{BodyReader, TestUserDao};
|
||||
use crate::testhelpers::{BodyReader, TestUserDao};
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn test_login_reports_200_when_user_exists() {
|
||||
|
||||
@@ -35,7 +35,7 @@ impl FromStr for Claims {
|
||||
let token = *(s.split("Bearer ").collect::<Vec<_>>().last().unwrap_or(&""));
|
||||
|
||||
match decode::<Claims>(
|
||||
&token,
|
||||
token,
|
||||
&DecodingKey::from_secret(secret_key().as_bytes()),
|
||||
&Validation::new(Algorithm::HS256),
|
||||
) {
|
||||
@@ -54,18 +54,27 @@ impl FromRequest for Claims {
|
||||
type Config = ();
|
||||
|
||||
fn from_request(req: &HttpRequest, _payload: &mut dev::Payload) -> Self::Future {
|
||||
let claims = match req.headers().get(header::AUTHORIZATION) {
|
||||
Some(header) => Claims::from_str(header.to_str().unwrap_or("")),
|
||||
None => Err(jsonwebtoken::errors::Error::from(
|
||||
jsonwebtoken::errors::ErrorKind::InvalidToken,
|
||||
)),
|
||||
};
|
||||
|
||||
if let Ok(claims) = claims {
|
||||
ok(claims)
|
||||
} else {
|
||||
err(ErrorUnauthorized("Bad token"))
|
||||
}
|
||||
req.headers()
|
||||
.get(header::AUTHORIZATION)
|
||||
.map_or_else(
|
||||
|| Err(anyhow!("No authorization header")),
|
||||
|header| {
|
||||
header
|
||||
.to_str()
|
||||
.context("Unable to read Authorization header to string")
|
||||
},
|
||||
)
|
||||
.and_then(|header| {
|
||||
Claims::from_str(header)
|
||||
.with_context(|| format!("Unable to decode token from: {}", header))
|
||||
})
|
||||
.map_or_else(
|
||||
|e| {
|
||||
error!("{}", e);
|
||||
err(ErrorUnauthorized("Bad token"))
|
||||
},
|
||||
ok,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -156,4 +165,17 @@ mod tests {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_junk_token_is_invalid() {
|
||||
let err = Claims::from_str("uni-֍ՓՓՓՓՓՓՓՓՓՓՓՓՓՓՓ");
|
||||
|
||||
match err.unwrap_err().into_kind() {
|
||||
ErrorKind::InvalidToken => assert!(true),
|
||||
kind => {
|
||||
println!("Unexpected error: {:?}", kind);
|
||||
assert!(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ use std::{
|
||||
|
||||
use crate::database::models::{Favorite, InsertFavorite, InsertUser, User};
|
||||
|
||||
mod models;
|
||||
pub mod models;
|
||||
mod schema;
|
||||
|
||||
pub trait UserDao {
|
||||
@@ -141,7 +141,7 @@ impl FavoriteDao for SqliteFavoriteDao {
|
||||
diesel::insert_into(favorites)
|
||||
.values(InsertFavorite {
|
||||
userid: &user_id,
|
||||
path: &favorite_path,
|
||||
path: favorite_path,
|
||||
})
|
||||
.execute(connection)
|
||||
.map_err(|_| DbError::new(DbErrorKind::InsertError))
|
||||
@@ -168,74 +168,3 @@ impl FavoriteDao for SqliteFavoriteDao {
|
||||
.map_err(|_| DbError::new(DbErrorKind::QueryError))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub mod testhelpers {
|
||||
use actix_web::dev::{Body, ResponseBody};
|
||||
|
||||
use super::{models::User, UserDao};
|
||||
use std::cell::RefCell;
|
||||
use std::option::Option;
|
||||
|
||||
pub struct TestUserDao {
|
||||
pub user_map: RefCell<Vec<User>>,
|
||||
}
|
||||
|
||||
impl TestUserDao {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
user_map: RefCell::new(Vec::new()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl UserDao for TestUserDao {
|
||||
fn create_user(&self, username: &str, password: &str) -> Option<User> {
|
||||
let u = User {
|
||||
id: (self.user_map.borrow().len() + 1) as i32,
|
||||
username: username.to_string(),
|
||||
password: password.to_string(),
|
||||
};
|
||||
|
||||
self.user_map.borrow_mut().push(u.clone());
|
||||
|
||||
Some(u)
|
||||
}
|
||||
|
||||
fn get_user(&self, user: &str, pass: &str) -> Option<User> {
|
||||
match self
|
||||
.user_map
|
||||
.borrow()
|
||||
.iter()
|
||||
.find(|&u| u.username == user && u.password == pass)
|
||||
{
|
||||
Some(u) => {
|
||||
let copy = (*u).clone();
|
||||
Some(copy)
|
||||
}
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn user_exists(&self, user: &str) -> bool {
|
||||
self.user_map
|
||||
.borrow()
|
||||
.iter()
|
||||
.find(|&u| u.username == user)
|
||||
.is_some()
|
||||
}
|
||||
}
|
||||
|
||||
pub trait BodyReader {
|
||||
fn read_to_str(&self) -> &str;
|
||||
}
|
||||
|
||||
impl BodyReader for ResponseBody<Body> {
|
||||
fn read_to_str(&self) -> &str {
|
||||
match self {
|
||||
ResponseBody::Body(Body::Bytes(ref b)) => std::str::from_utf8(b).unwrap(),
|
||||
_ => panic!("Unknown response body"),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,6 +42,9 @@ mod database;
|
||||
mod files;
|
||||
mod video;
|
||||
|
||||
#[cfg(test)]
|
||||
mod testhelpers;
|
||||
|
||||
lazy_static! {
|
||||
static ref IMAGE_GAUGE: IntGauge = IntGauge::new(
|
||||
"imageserver_image_total",
|
||||
@@ -190,7 +193,7 @@ async fn generate_video(
|
||||
let filename = name.to_str().expect("Filename should convert to string");
|
||||
let playlist = format!("tmp/{}.m3u8", filename);
|
||||
if let Some(path) = is_valid_path(&body.path) {
|
||||
if let Ok(child) = create_playlist(&path.to_str().unwrap(), &playlist).await {
|
||||
if let Ok(child) = create_playlist(path.to_str().unwrap(), &playlist).await {
|
||||
data.stream_manager
|
||||
.do_send(ProcessMessage(playlist.clone(), child));
|
||||
}
|
||||
|
||||
86
src/testhelpers.rs
Normal file
86
src/testhelpers.rs
Normal file
@@ -0,0 +1,86 @@
|
||||
use actix_web::dev::{Body, ResponseBody};
|
||||
use serde::Deserialize;
|
||||
|
||||
use crate::database::{models::User, UserDao};
|
||||
use std::cell::RefCell;
|
||||
use std::option::Option;
|
||||
|
||||
pub struct TestUserDao {
|
||||
pub user_map: RefCell<Vec<User>>,
|
||||
}
|
||||
|
||||
impl TestUserDao {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
user_map: RefCell::new(Vec::new()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl UserDao for TestUserDao {
|
||||
fn create_user(&self, username: &str, password: &str) -> Option<User> {
|
||||
let u = User {
|
||||
id: (self.user_map.borrow().len() + 1) as i32,
|
||||
username: username.to_string(),
|
||||
password: password.to_string(),
|
||||
};
|
||||
|
||||
self.user_map.borrow_mut().push(u.clone());
|
||||
|
||||
Some(u)
|
||||
}
|
||||
|
||||
fn get_user(&self, user: &str, pass: &str) -> Option<User> {
|
||||
match self
|
||||
.user_map
|
||||
.borrow()
|
||||
.iter()
|
||||
.find(|&u| u.username == user && u.password == pass)
|
||||
{
|
||||
Some(u) => {
|
||||
let copy = (*u).clone();
|
||||
Some(copy)
|
||||
}
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn user_exists(&self, user: &str) -> bool {
|
||||
self.user_map
|
||||
.borrow()
|
||||
.iter()
|
||||
.find(|&u| u.username == user)
|
||||
.is_some()
|
||||
}
|
||||
}
|
||||
|
||||
pub trait BodyReader {
|
||||
fn read_to_str(&self) -> &str;
|
||||
}
|
||||
|
||||
impl BodyReader for ResponseBody<Body> {
|
||||
fn read_to_str(&self) -> &str {
|
||||
match self {
|
||||
ResponseBody::Body(Body::Bytes(ref b)) => std::str::from_utf8(b).unwrap(),
|
||||
_ => panic!("Unknown response body"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait TypedBodyReader<'a, T>
|
||||
where
|
||||
T: Deserialize<'a>,
|
||||
{
|
||||
fn read_body(&'a self) -> T;
|
||||
}
|
||||
|
||||
impl<'a, T: Deserialize<'a>> TypedBodyReader<'a, T> for ResponseBody<Body> {
|
||||
fn read_body(&'a self) -> T {
|
||||
match self {
|
||||
ResponseBody::Body(Body::Bytes(ref b)) => {
|
||||
serde_json::from_str(std::str::from_utf8(b).unwrap()).unwrap()
|
||||
}
|
||||
_ => panic!("Unknown response body"),
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user