#![crate_type = "lib"]
#![crate_name = "websession"]
extern crate time;
extern crate uuid;
#[cfg(feature = "hyper")]
extern crate hyper;
extern crate crypto;
extern crate pwhash;
extern crate fs2;
#[macro_use] extern crate log;
pub mod sessions;
pub mod backingstore;
pub mod connectionsignature;
pub mod token;
pub mod sessionpolicy;
pub use self::connectionsignature::ConnectionSignature;
use std::collections::HashMap;
use time::Duration;
use std::error::Error;
use std::{fmt, io};
use std::sync::Mutex;
use std::vec::IntoIter;
use self::backingstore::{BackingStore, BackingStoreError};
use self::sessions::SessionManager;
pub use self::sessionpolicy::SessionPolicy;
#[derive(Debug)]
pub enum AuthError {
Expired,
Unauthorized,
Mutex,
IO(io::Error),
Hash(pwhash::error::Error),
MissingData,
InternalConsistency,
}
impl From<BackingStoreError> for AuthError {
fn from(err: BackingStoreError) -> AuthError {
match err {
BackingStoreError::NoSuchUser => AuthError::Unauthorized,
BackingStoreError::Locked => AuthError::Unauthorized,
BackingStoreError::UserExists => AuthError::Unauthorized,
BackingStoreError::Mutex => AuthError::Mutex,
BackingStoreError::IO(i) => AuthError::IO(i),
BackingStoreError::Hash(h) => AuthError::Hash(h),
BackingStoreError::MissingData => AuthError::MissingData,
}
}
}
impl Error for AuthError {
fn description(&self) -> &str {
match *self {
AuthError::Unauthorized => "User not authorized",
AuthError::Expired => "Session Expired",
AuthError::IO(ref err) => err.description(),
AuthError::Hash(ref err) => err.description(),
_ => "Internal Error",
}
}
fn cause(&self) -> Option<&Error> {
match *self {
AuthError::Hash(ref e) => Some(e),
AuthError::IO(ref e) => Some(e),
_ => None
}
}
}
impl fmt::Display for AuthError {
fn fmt(&self, out: &mut fmt::Formatter) -> fmt::Result {
write!(out, "{}", self.description())
}
}
#[derive(Debug)]
pub struct Authenticator {
sess_mgr: SessionManager,
backing_store: Box<BackingStore + Send + Sync>,
mapping: Mutex<HashMap<String, String>>,
}
impl Authenticator {
pub fn new(backing_store: Box<BackingStore + Send + Sync>, expiration: Duration, policy: SessionPolicy) -> Authenticator {
Authenticator {
sess_mgr: SessionManager::new(expiration, policy),
backing_store: backing_store,
mapping: Mutex::new(HashMap::new()),
}
}
pub fn verify(&self, user: &str, credentials: &str) -> Result<bool, AuthError> {
self.backing_store.verify(user, credentials).map_err(|e| AuthError::from(e))
}
pub fn login(&self, user: &str, credentials: &str, signature: &ConnectionSignature) -> Result<(), AuthError> {
match self.sess_mgr.is_expired(signature) {
Ok(true) => {
self.sess_mgr.stop(signature);
Err(AuthError::Expired)
},
Ok(false) => match self.verify(user, credentials) {
Ok(true) => {
try!(self.mapping.lock().map_err(|_| AuthError::Mutex))
.insert(signature.token.to_string(), user.to_string());
Ok(())
},
Ok(false) => Err(AuthError::Unauthorized),
Err(e) => Err(e),
},
Err(e) => Err(From::from(e)),
}
}
pub fn logout(&self, signature: &ConnectionSignature) {
let id = signature.token.to_string();
match self.mapping.lock() {
Ok(mut hashmap) => hashmap.remove(&id),
Err(poisoned) => poisoned.into_inner().remove(&id),
};
self.sess_mgr.stop(signature);
}
pub fn get_user(&self, signature: &ConnectionSignature) -> Result<Option<String>, AuthError> {
match self.sess_mgr.is_expired(signature) {
Ok(true) => Err(AuthError::Expired),
Ok(false) => match self.mapping.lock() {
Ok(hashmap) => Ok(hashmap.get(&signature.token.to_string())
.map(|s| s.clone())),
Err(_) => Err(AuthError::Mutex),
},
Err(e) => Err(e),
}
}
pub fn encrypt_credentials(&self, plain_cred: &str) -> Result<String, AuthError> {
Ok(self.backing_store.encrypt_credentials(plain_cred)?)
}
pub fn update_credentials(&self, user: &str, enc_creds: &str) -> Result<(), AuthError> {
Ok(self.backing_store.update_credentials(user, enc_creds)?)
}
pub fn update_credentials_plain(&self, user: &str, plain_creds: &str) -> Result<(), AuthError> {
Ok(self.backing_store.update_credentials_plain(user, plain_creds)?)
}
pub fn lock_user(&self, user: &str) -> Result<(), AuthError> {
match self.mapping.lock() {
Ok(mut hashmap) => hashmap.retain(|_, ref v| user != *v),
Err(mut poisoned) => poisoned.get_mut().retain(|_, ref v| user != *v),
};
self.backing_store.lock(user).map_err(|e| AuthError::from(e))
}
pub fn is_locked(&self, user: &str) -> Result<bool, AuthError> {
self.backing_store.is_locked(user).map_err(|e| AuthError::from(e))
}
pub fn unlock(&self, user: &str) -> Result<(), AuthError> {
self.backing_store.unlock(user).map_err(|e| AuthError::from(e))
}
pub fn create_preencrypted(&self, user: &str, enc_creds: &str) -> Result<(), AuthError> {
Ok(self.backing_store.create_preencrypted(user, enc_creds)?)
}
pub fn create_plain(&self, user: &str, plain_creds: &str) -> Result<(), AuthError> {
Ok(self.backing_store.create_plain(user, plain_creds)?)
}
pub fn delete(&self, user: &str) -> Result<(), AuthError> {
self.backing_store.delete(user).map_err(|e| AuthError::from(e))
}
pub fn run(&self, signature: ConnectionSignature) -> Result<ConnectionSignature, AuthError> {
self.sess_mgr.start(signature).map_err(|err| AuthError::from(err))
}
pub fn users(&self) -> Result<Vec<String>, AuthError> {
self.backing_store.users().map_err(|e| AuthError::from(e))
}
pub fn users_iter(&self) -> Result<IntoIter<String>, AuthError> {
self.backing_store.users_iter().map_err(|e| AuthError::from(e))
}
pub fn check_user(&self, user: &str) -> Result<bool, AuthError> {
self.backing_store.check_user(user).map_err(|e| AuthError::from(e))
}
}