use diesel;
use diesel::mysql::types::Unsigned;
use diesel::mysql::Mysql;
use diesel::mysql::MysqlConnection;
use diesel::sql_types;
use diesel::ExpressionMethods;
use diesel::QueryDsl;
use diesel::RunQueryDsl;
use google_signin;
use log::debug;
use log::trace;
use log::warn;
use crate::errors::{Error, ErrorKind};
use crate::search::Search;
use super::models::{
JoinedUserPermission, JoinedUserPermissionList, NewPermission,
NewUserPermission, PartialPermission, PartialUserPermission, Permission,
PermissionList, PermissionRequest, PermissionResponse,
SearchUserPermission, UserPermission, UserPermissionRequest,
UserPermissionResponse
};
use crate::users::models::{NewUser, SearchUser};
use crate::users::requests::create_user;
use crate::users::requests::search_users;
use super::schema::permissions as permissions_schema;
use super::schema::user_permissions as user_permissions_schema;
use crate::users::schema::users as users_schema;
pub fn validate_token(id_token: &str, database_connection: &MysqlConnection) -> Result<u64, Error> {
let mut client = google_signin::Client::new();
client.audiences.push(String::from(
"918184954544-jm1aufr31fi6sdjs1140p7p3rouaka14.apps.googleusercontent.com",
));
let id_info = client.verify(id_token)?;
trace!("Validated token: {:?}", id_info);
if let Some(email) = id_info.email {
let mut found_users = search_users(
SearchUser {
first_name: Search::NoSearch,
last_name: Search::NoSearch,
banner_id: Search::NoSearch,
email: Search::Exact(email),
},
database_connection,
)?;
if let Some(user) = found_users.users.pop() {
Ok(user.id)
} else {
Err(Error::new(ErrorKind::GoogleUserNotFound))
}
} else {
Err(Error::new(ErrorKind::GoogleUserNoEmail))
}
}
pub fn check_to_run(
requesting_user_id: Option<u64>,
permission_name: &str,
database_connection: &MysqlConnection,
) -> Result<(), Error> {
trace!(
"Checking if user {:?} has {}",
requesting_user_id,
permission_name
);
match requesting_user_id {
Some(user_id) => {
match check_user_permission(user_id, String::from(permission_name), database_connection)
{
Ok(permission) => {
if permission {
debug!("Permission granted!");
Ok(())
} else {
Err(Error::new(ErrorKind::PermissionDenied))
}
}
Err(e) => Err(e),
}
}
None => Err(Error::new(ErrorKind::PermissionDenied)),
}
}
pub fn handle_permission(
request: PermissionRequest,
requesting_user: Option<u64>,
database_connection: &MysqlConnection,
) -> Result<PermissionResponse, Error> {
match request {
PermissionRequest::FirstPermission(id_token) => {
first_permission(requesting_user, &id_token, database_connection)
.map(|_| PermissionResponse::NoResponse)
}
PermissionRequest::GetPermission(id) => {
match check_to_run(requesting_user, "GetPermission", database_connection) {
Ok(()) => get_permission(id, database_connection)
.map(|a| PermissionResponse::OnePermission(a)),
Err(e) => Err(e),
}
}
PermissionRequest::CreatePermission(permission) => {
match check_to_run(requesting_user, "CreatePermission", database_connection) {
Ok(()) => create_permission(permission, database_connection)
.map(|a| PermissionResponse::OnePermission(a)),
Err(e) => Err(e),
}
}
PermissionRequest::UpdatePermission(id, permission) => {
match check_to_run(requesting_user, "UpdatePermission", database_connection) {
Ok(()) => update_permission(id, permission, database_connection)
.map(|_| PermissionResponse::NoResponse),
Err(e) => Err(e),
}
}
PermissionRequest::DeletePermission(id) => {
match check_to_run(requesting_user, "DeletePermission", database_connection) {
Ok(()) => delete_permission(id, database_connection)
.map(|_| PermissionResponse::NoResponse),
Err(e) => Err(e),
}
}
PermissionRequest::GetPermissions => {
get_permissions(database_connection)
.map(|u| PermissionResponse::ManyPermissions(u))
}
}
}
pub(crate) fn first_permission(
requesting_user: Option<u64>,
id_token: &str,
database_connection: &MysqlConnection,
) -> Result<(), Error> {
trace!(
"First permission requested for user: {:?}, id_token: {}",
requesting_user,
id_token
);
let search = SearchUserPermission {
permission_id: Search::NoSearch,
user_id: Search::NoSearch,
};
let non_root_permissions = search_user_permission(search, &database_connection)?
.entries
.into_iter()
.filter(|permission| permission.permission_id != 1)
.count();
trace!("Found {} non-root permissions", non_root_permissions);
if non_root_permissions == 0 {
let user_id = if let Some(user_id) = requesting_user {
user_id
} else {
let mut client = google_signin::Client::new();
client.audiences.push(String::from(
"918184954544-jm1aufr31fi6sdjs1140p7p3rouaka14.apps.googleusercontent.com",
));
let info = client.verify(id_token)?;
trace!("Token verified: {:?}", info);
let email = match info.email {
Some(email) => email,
None => {
return Err(Error::new(ErrorKind::PermissionDenied));
}
};
let new_user = NewUser {
first_name: info
.given_name
.unwrap_or("Not supplied by Google".to_owned()),
last_name: info
.family_name
.unwrap_or("Not supplied by Google".to_owned()),
email: email,
banner_id: 0,
permissions: Vec::new(),
};
trace!("New user: {:#?}", new_user);
create_user(new_user, database_connection)?.id
};
let permissions = permissions_schema::table
.filter(permissions_schema::permission_name.ne("RootPermission"))
.load::<Permission>(database_connection)?;
let new_user_permissions: Vec<_> = permissions
.into_iter()
.map(|permission| NewUserPermission {
permission_id: permission.id,
user_id: user_id,
})
.collect();
diesel::insert_into(user_permissions_schema::table)
.values(new_user_permissions)
.execute(database_connection)?;
Ok(())
} else {
warn!("First permission request attempted, but permission has already been setup.");
Err(Error::new(ErrorKind::PermissionDenied))
}
}
pub(crate) fn get_permission(
id: u64,
database_connection: &MysqlConnection,
) -> Result<Permission, Error> {
let mut found_permission = permissions_schema::table
.filter(permissions_schema::id.eq(id))
.load::<Permission>(database_connection)?;
match found_permission.pop() {
Some(permission) => Ok(permission),
None => Err(Error::new(ErrorKind::NotFound)),
}
}
pub(crate) fn create_permission(
permission: NewPermission,
database_connection: &MysqlConnection,
) -> Result<Permission, Error> {
diesel::insert_into(permissions_schema::table)
.values(permission)
.execute(database_connection)?;
no_arg_sql_function!(last_insert_id, Unsigned<sql_types::Bigint>);
let mut inserted_permissions = permissions_schema::table
.filter(permissions_schema::id.eq(last_insert_id))
.load::<Permission>(database_connection)?;
if let Some(inserted_permission) = inserted_permissions.pop() {
Ok(inserted_permission)
} else {
Err(Error::new(ErrorKind::Database))
}
}
pub(crate) fn update_permission(
id: u64,
permission: PartialPermission,
database_connection: &MysqlConnection,
) -> Result<(), Error> {
diesel::update(permissions_schema::table)
.filter(permissions_schema::id.eq(id))
.set(&permission)
.execute(database_connection)?;
Ok(())
}
pub(crate) fn delete_permission(
id: u64,
database_connection: &MysqlConnection,
) -> Result<(), Error> {
diesel::delete(permissions_schema::table.filter(permissions_schema::id.eq(id)))
.execute(database_connection)?;
Ok(())
}
pub fn handle_user_permission(
request: UserPermissionRequest,
requesting_user: Option<u64>,
database_connection: &MysqlConnection,
) -> Result<UserPermissionResponse, Error> {
match request {
UserPermissionRequest::SearchPermission(user_permission) => {
match check_to_run(requesting_user, "GetUserPermission", database_connection) {
Ok(()) => search_user_permission(user_permission, database_connection)
.map(|u| UserPermissionResponse::ManyUserPermission(u)),
Err(e) => Err(e),
}
}
UserPermissionRequest::GetCurrentUserPermission => {
get_current_user_permission(requesting_user, database_connection)
.map(|u| UserPermissionResponse::ManyPermission(u))
}
UserPermissionRequest::GetPermission(permission_id) => {
match check_to_run(requesting_user, "GetUserPermission", database_connection) {
Ok(()) => get_user_permission(permission_id, database_connection)
.map(|a| UserPermissionResponse::OneUserPermission(a)),
Err(e) => Err(e),
}
}
UserPermissionRequest::CheckPermission(user_id, permission_name) => {
check_user_permission(user_id, permission_name, database_connection)
.map(|s| UserPermissionResponse::PermissionState(s))
}
UserPermissionRequest::CreatePermission(user_permission) => {
match check_to_run(requesting_user, "CreateUserPermission", database_connection) {
Ok(()) => create_user_permission(user_permission, database_connection)
.map(|a| UserPermissionResponse::OneUserPermission(a)),
Err(e) => Err(e),
}
}
UserPermissionRequest::UpdatePermission(id, user_permission) => {
match check_to_run(requesting_user, "UpdateUserPermission", database_connection) {
Ok(()) => update_user_permission(id, user_permission, database_connection)
.map(|_| UserPermissionResponse::NoResponse),
Err(e) => Err(e),
}
}
UserPermissionRequest::DeletePermission(id) => {
match check_to_run(requesting_user, "DeleteUserPermission", database_connection) {
Ok(()) => delete_user_permission(id, database_connection)
.map(|_| UserPermissionResponse::NoResponse),
Err(e) => Err(e),
}
}
}
}
pub(crate) fn search_user_permission(
user_permission_search: SearchUserPermission,
database_connection: &MysqlConnection,
) -> Result<JoinedUserPermissionList, Error> {
let mut user_permission_query = user_permissions_schema::table
.inner_join(permissions_schema::table)
.inner_join(users_schema::table)
.select((
user_permissions_schema::permission_id,
users_schema::id,
permissions_schema::id,
users_schema::first_name,
users_schema::last_name,
users_schema::banner_id,
))
.into_boxed::<Mysql>();
match user_permission_search.permission_id {
Search::Partial(s) => {
user_permission_query =
user_permission_query.filter(user_permissions_schema::permission_id.eq(s))
}
Search::Exact(s) => {
user_permission_query =
user_permission_query.filter(user_permissions_schema::permission_id.eq(s))
}
Search::NoSearch => {}
}
match user_permission_search.user_id {
Search::Partial(s) => {
user_permission_query =
user_permission_query.filter(user_permissions_schema::user_id.eq(s))
}
Search::Exact(s) => {
user_permission_query =
user_permission_query.filter(user_permissions_schema::user_id.eq(s))
}
Search::NoSearch => {}
}
let found_permission_entries =
user_permission_query.load::<JoinedUserPermission>(database_connection)?;
let joined_list = JoinedUserPermissionList {
entries: found_permission_entries,
};
Ok(joined_list)
}
pub(crate) fn get_current_user_permission(
requesting_user: Option<u64>,
database_connection: &MysqlConnection,
) -> Result<PermissionList, Error> {
if let Some(user_id) = requesting_user {
let permissions = permissions_schema::table
.inner_join(user_permissions_schema::table)
.select((permissions_schema::id, permissions_schema::permission_name))
.filter(user_permissions_schema::user_id.eq(user_id))
.load::<Permission>(database_connection)?;
Ok(PermissionList { permissions })
} else {
Err(Error::new(ErrorKind::PermissionDenied))
}
}
pub(crate) fn get_user_permission(
permission_id: u64,
database_connection: &MysqlConnection,
) -> Result<UserPermission, Error> {
let mut found_user_permissions = user_permissions_schema::table
.filter(user_permissions_schema::permission_id.eq(permission_id))
.load::<UserPermission>(database_connection)?;
match found_user_permissions.pop() {
Some(found_user_permission) => Ok(found_user_permission),
None => Err(Error::new(ErrorKind::NotFound)),
}
}
pub(crate) fn check_user_permission(
user_id: u64,
permission_name: String,
database_connection: &MysqlConnection,
) -> Result<bool, Error> {
let found_user_permissions = user_permissions_schema::table
.inner_join(permissions_schema::table)
.select((
user_permissions_schema::user_id,
permissions_schema::permission_name,
))
.filter(user_permissions_schema::user_id.eq(user_id))
.filter(permissions_schema::permission_name.eq(permission_name))
.execute(database_connection)?;
if found_user_permissions != 0 {
Ok(true)
} else {
Ok(false)
}
}
pub(crate) fn create_user_permission(
user_permission: NewUserPermission,
database_connection: &MysqlConnection,
) -> Result<UserPermission, Error> {
let found_user_permissions = user_permissions_schema::table
.filter(user_permissions_schema::user_id.eq(user_permission.user_id))
.filter(user_permissions_schema::permission_id.eq(user_permission.permission_id))
.execute(database_connection)?;
if found_user_permissions != 0 {
return Err(Error::new(ErrorKind::Database));
}
diesel::insert_into(user_permissions_schema::table)
.values(user_permission)
.execute(database_connection)?;
no_arg_sql_function!(last_insert_id, Unsigned<sql_types::Bigint>);
let mut inserted_permissions = user_permissions_schema::table
.filter(user_permissions_schema::user_permission_id.eq(last_insert_id))
.load::<UserPermission>(database_connection)?;
if let Some(inserted_permission) = inserted_permissions.pop() {
Ok(inserted_permission)
} else {
Err(Error::new(ErrorKind::Database))
}
}
pub(crate) fn update_user_permission(
id: u64,
user_permission: PartialUserPermission,
database_connection: &MysqlConnection,
) -> Result<(), Error> {
diesel::update(user_permissions_schema::table)
.filter(user_permissions_schema::permission_id.eq(id))
.set(&user_permission)
.execute(database_connection)?;
Ok(())
}
pub(crate) fn delete_user_permission(
id: u64,
database_connection: &MysqlConnection,
) -> Result<(), Error> {
diesel::delete(
user_permissions_schema::table.filter(user_permissions_schema::permission_id.eq(id)),
)
.execute(database_connection)?;
Ok(())
}
pub(crate) fn get_permissions(
database_connection: &MysqlConnection
) -> Result<PermissionList, Error> {
let permissions = permissions_schema::table
.select((
permissions_schema::id,
permissions_schema::permission_name,
))
.load::<Permission>(database_connection)?;
Ok(PermissionList { permissions })
}