Skip to content

Commit

Permalink
Merge pull request #66 from hfiguiere/issue53-db-file-path
Browse files Browse the repository at this point in the history
Issue #53 - Implement the manager. r=ferjm
  • Loading branch information
ferjm committed Mar 14, 2016
2 parents 5b5beaa + 37e4ae6 commit 8b82110
Show file tree
Hide file tree
Showing 5 changed files with 135 additions and 69 deletions.
19 changes: 10 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,11 +48,12 @@ $ multirust override nightly-2016-03-07
extern crate foxbox_users;
extern crate iron;

use foxbox_users::users_router::UsersRouter;
use foxbox_users::UsersManager;
use iron::prelude::*;

fn main() {
let router = UsersRouter::init();
let manager = UsersManager::new("sqlite_db.sqlite");
let router = manager.get_router_chain();
Iron::new(router).http("localhost:3000").unwrap();
}
```
Expand All @@ -64,7 +65,7 @@ extern crate foxbox_users;
extern crate iron;
extern crate router;

use foxbox_users::auth_middleware::{AuthEndpoint, AuthMiddleware};
use foxbox_users::AuthEndpoint;
use iron::method::Method;
use iron::prelude::*;
use iron::status;
Expand All @@ -75,16 +76,15 @@ fn dummy_handler(_: &mut Request) -> IronResult<Response> {
}

fn main() {
let manager = UsersManager::new("sqlite_db.sqlite");
let mut router = Router::new();
router.get("/authenticated", dummy_handler);
router.get("/not_authenticated", dummy_handler);

let mut chain = Chain::new(router);
chain.around(AuthMiddleware{
auth_endpoints: vec![
chain.around(manager.get_middleware(vec![
AuthEndpoint(Method::Get, vec!["authenticated".to_string()])
]
});
]);

Iron::new(chain).http("localhost:3000").unwrap();
}
Expand All @@ -95,10 +95,11 @@ fn main() {
```rust
extern crate foxbox_users;

use foxbox_users::users_db::{ReadFilter, UserBuilder, UsersDb};
use foxbox_users::{ReadFilter, UserBuilder};

fn main() {
let db = UsersDb::new();
let manager = UsersManager::new("sqlite_db.sqlite");
let db = manager.get_db();
let user = UserBuilder::new()
.name("MrFox")
.email("fox@foxlink.org")
Expand Down
46 changes: 25 additions & 21 deletions src/auth_middleware.rs
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ impl PartialEq for AuthEndpoint {

struct AuthHandler<H: Handler> {
handler: H,
auth_db_file: String,
auth_endpoints: Vec<AuthEndpoint>
}

Expand All @@ -124,7 +125,8 @@ impl<H: Handler> Handler for AuthHandler<H> {
// Otherwise, we need to verify the authorization header.
match req.headers.get::<headers::Authorization<headers::Bearer>>() {
Some(&headers::Authorization(headers::Bearer { ref token })) => {
if let Err(_) = AuthMiddleware::verify(token) {
if let Err(_) = AuthMiddleware::verify(token,
&self.auth_db_file) {
return EndpointError::with(status::Unauthorized, 401)
}
},
Expand All @@ -146,36 +148,36 @@ impl<H: Handler> Handler for AuthHandler<H> {
/// extern crate foxbox_users;
///
/// fn main() {
/// use foxbox_users::users_router::UsersRouter;
/// use foxbox_users::auth_middleware::AuthMiddleware;
/// use foxbox_users::UsersManager;
/// use iron::prelude::{Chain, Iron};
///
/// let router = UsersRouter::init();
/// let manager = UsersManager::new("AuthMiddleware_0.sqlite");
/// let router = manager.get_router_chain();
/// let mut chain = Chain::new(router);
/// chain.around(AuthMiddleware{
/// auth_endpoints: vec![]
/// });
/// chain.around(manager.get_middleware(vec![]));
/// # if false {
/// Iron::new(chain).http("localhost:3000").unwrap();
/// # }
/// }
/// ```
pub struct AuthMiddleware {
/// `Vec<AuthEndpoint>` containing the set of endpoints to be authenticated.
pub auth_endpoints: Vec<AuthEndpoint>
pub auth_endpoints: Vec<AuthEndpoint>,
pub auth_db_file: String,
}

impl AroundMiddleware for AuthMiddleware {
fn around(self, handler: Box<Handler>) -> Box<Handler> {
Box::new(AuthHandler {
handler: handler,
auth_endpoints: self.auth_endpoints
auth_endpoints: self.auth_endpoints,
auth_db_file: self.auth_db_file.clone(),
}) as Box<Handler>
}
}

impl AuthMiddleware {
pub fn verify(token: &str) -> Result<(), ()> {
pub fn verify(token: &str, auth_db_file: &str) -> Result<(), ()> {
if token.is_empty() {
return Err(());
}
Expand All @@ -187,7 +189,7 @@ impl AuthMiddleware {

// To verify the token we need to get the secret associated to
// user id contained in the token claim.
let db = UsersDb::new();
let db = UsersDb::new(auth_db_file);
match db.read(ReadFilter::Id(token.claims.id)) {
Ok(users) => {
if users.len() != 1 {
Expand Down Expand Up @@ -216,7 +218,10 @@ describe! auth_middleware_tests {
use iron::status::Status;
use iron_test::request;
use router::Router;
use super::super::users_db::get_db_environment;
use super::super::UsersManager;

let manager = UsersManager::new(&get_db_environment());
fn not_implemented(_: &mut Request) -> IronResult<Response> {
Ok(Response::with(Status::NotImplemented))
}
Expand All @@ -228,14 +233,12 @@ describe! auth_middleware_tests {
router.get("/not_authenticated", not_implemented);

let mut chain = Chain::new(router);
chain.around(AuthMiddleware {
auth_endpoints: vec![
AuthEndpoint(vec![Method::Get, Method::Delete],
"/authenticated".to_owned()),
AuthEndpoint(vec![Method::Get],
"/authenticated/:foo/bar/:baz".to_owned())
]
});
chain.around(manager.get_middleware(
vec![AuthEndpoint(vec![Method::Get, Method::Delete],
"/authenticated".to_owned()),
AuthEndpoint(vec![Method::Get],
"/authenticated/:foo/bar/:baz".to_owned())]
));
}

it "should allow request to not authenticated endpoint" {
Expand Down Expand Up @@ -273,13 +276,14 @@ describe! auth_middleware_tests {
}

it "should allow request to authenticated endpoint" {
use super::super::users_db::{UserBuilder, UsersDb, remove_test_db};
use super::super::UserBuilder;
use super::super::users_db::remove_test_db;

use iron::headers::{Authorization, Bearer};
use crypto::sha2::Sha256;
use jwt;

let db = UsersDb::new();
let db = manager.get_db();
db.clear().ok();
let user = UserBuilder::new()
.id(1).name(String::from("username"))
Expand Down
48 changes: 45 additions & 3 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,49 @@ extern crate unicase;
extern crate jwt;
extern crate rand;

pub mod users_db;
pub mod users_router;
pub mod auth_middleware;
mod users_db;
mod users_router;
mod auth_middleware;
mod errors;

pub use users_db::UsersDb as UsersDb;
pub use users_db::UserBuilder as UserBuilder;
pub use users_db::UserBuilderError as UserBuilderError;
pub use users_db::ReadFilter as ReadFilter;
pub use users_db::User as User;
pub use users_router::UsersRouter as UsersRouter;
pub use auth_middleware::AuthMiddleware as AuthMiddleware;
pub use auth_middleware::AuthEndpoint as AuthEndpoint;

pub struct UsersManager {
db_file_path: String,
}

impl UsersManager {

/// Create the UsersManager. The database will be stored at
/// `db_file_path`.
pub fn new(db_file_path: &str) -> Self {
UsersManager { db_file_path: String::from(db_file_path) }
}

/// Get a new database connection.
pub fn get_db(&self) -> UsersDb {
UsersDb::new(&self.db_file_path)
}

/// Get a new router chain
pub fn get_router_chain(&self) -> iron::middleware::Chain {
UsersRouter::init(&self.db_file_path)
}

pub fn get_middleware(&self, auth_endpoints: Vec<AuthEndpoint>)
-> AuthMiddleware {
AuthMiddleware { auth_endpoints: auth_endpoints,
auth_db_file: self.db_file_path.to_owned() }
}

pub fn verify_token(&self, token: &str) -> Result<(), ()> {
AuthMiddleware::verify(token, &self.db_file_path)
}
}
42 changes: 22 additions & 20 deletions src/users_db.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
//! methods this way:
//!
//! ```
//! use foxbox_users::users_db::UserBuilder;
//! use foxbox_users::UserBuilder;
//!
//! let new_user =
//! UserBuilder::new()
Expand Down Expand Up @@ -52,7 +52,7 @@ pub struct User {
/// Starting with `UserBuilder::new()` and chaining methods you can create users:
///
/// ```
/// # use foxbox_users::users_db::UserBuilder;
/// # use foxbox_users::UserBuilder;
/// let new_user =
/// UserBuilder::new()
/// .name(String::from("Miles")) // mandatory, not empty
Expand All @@ -68,7 +68,7 @@ pub struct User {
/// can inspect `UserWithError#error` field to see what failed during initialization:
///
/// ```
/// # use foxbox_users::users_db::{UserBuilder, UserBuilderError};
/// # use foxbox_users::{UserBuilder, UserBuilderError};
/// let failing_user = UserBuilder::new()
/// .name(String::from("Miles"))
/// .password(String::from("short"))
Expand All @@ -82,7 +82,7 @@ pub struct User {
/// although it will be automatically initialized to random if not provided.
///
/// ```
/// # use foxbox_users::users_db::UserBuilder;
/// # use foxbox_users::UserBuilder;
/// let new_user =
/// UserBuilder::new()
/// .name(String::from("Miles")) // mandatory, not empty
Expand Down Expand Up @@ -250,18 +250,14 @@ pub fn get_db_environment() -> String {
tid.replace("/", "42"))
}

#[cfg(not(test))]
fn get_db_environment() -> String {
"./users_db.sqlite".to_owned()
}

impl UsersDb {
/// Opens the database and create it if not available yet.
/// path: the file path to the database.
///
/// When the database instance exits the scope where it was created, it is
/// automatically closed.
pub fn new() -> UsersDb {
let connection = Connection::open(get_db_environment()).unwrap();
pub fn new(path: &str) -> UsersDb {
let connection = Connection::open(path).unwrap();
connection.execute("CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL UNIQUE,
Expand All @@ -281,8 +277,9 @@ impl UsersDb {
/// # Examples
///
/// ```no_run
/// # use foxbox_users::users_db::{UserBuilder, UsersDb, ReadFilter};
/// let db = UsersDb::new();
/// # use foxbox_users::{UsersManager, UserBuilder, ReadFilter};
/// let manager = UsersManager::new("UsersDb_clear_0.sqlite");
/// let db = manager.get_db();
/// # db.create(&UserBuilder::new().name(String::from("John Doe")).finalize().unwrap());
/// db.clear();
/// let users = db.read(ReadFilter::All).unwrap();
Expand All @@ -301,9 +298,10 @@ impl UsersDb {
/// # Examples
///
/// ```no_run
/// # use foxbox_users::users_db::{User, UsersDb, ReadFilter, UserBuilder};
/// # use foxbox_users::{UsersManager, UserBuilder};
/// let admin = UserBuilder::new().name(String::from("admin")).set_admin(true).finalize().unwrap();
/// let db = UsersDb::new();
/// let manager = UsersManager::new("UsersDb_create_0.sqlite");
/// let db = manager.get_db();
/// assert!(db.create(&admin).is_ok());
/// ```
pub fn create(&self, user: &User) -> rusqlite::Result<User> {
Expand All @@ -329,15 +327,19 @@ impl UsersDb {
/// For instance, to get all users:
///
/// ```no_run
/// # use foxbox_users::users_db::{User, UsersDb, ReadFilter};
/// let all_users: Vec<User> = UsersDb::new().read(ReadFilter::All).unwrap();
/// # use foxbox_users::{UsersManager, User, ReadFilter};
/// let manager = UsersManager::new("UsersDb_read_0.sqlite");
/// let db = manager.get_db();
/// let all_users: Vec<User> = db.read(ReadFilter::All).unwrap();
/// ```
///
/// And to quickly find administrators:
///
/// ```no_run
/// # use foxbox_users::users_db::{User, UsersDb, ReadFilter};
/// let admins: Vec<User> = UsersDb::new().read(ReadFilter::IsAdmin(true)).unwrap();
/// # use foxbox_users::{UsersManager, User, ReadFilter};
/// let manager = UsersManager::new("UsersDb_read_1.sqlite");
/// let db = manager.get_db();
/// let admins: Vec<User> = db.read(ReadFilter::IsAdmin(true)).unwrap();
/// ```
pub fn read(&self, filter: ReadFilter) -> rusqlite::Result<Vec<User>> {
let mut stmt = try!(
Expand Down Expand Up @@ -479,7 +481,7 @@ pub fn remove_test_db() {
#[cfg(test)]
describe! user_db_tests {
before_each {
let usersDb = UsersDb::new();
let usersDb = UsersDb::new(&get_db_environment());
usersDb.clear().ok();

let defaultUsers = vec![
Expand Down
Loading

0 comments on commit 8b82110

Please sign in to comment.