#![feature(proc_macro_hygiene, decl_macro)]
#[macro_use]
extern crate rocket;
extern crate lib;
use lib::{
api,
model::{Group, GroupQueryBuilder, User, UserQueryBuilder},
FileResource,
};
use query::MemberNames;
use rocket::State;
use rocket_contrib::json::Json;
use std::{fs::File, io::Write};
const DEFAULT_USERS_FILE: &str = "/etc/passwd";
const DEFAULT_GROUPS_FILE: &str = "/etc/group";
fn main() {
let mut args = std::env::args().skip(1);
let (users, groups): (FileResource<User>, FileResource<Group>) = match (args.next(), args.next()) {
_ if cfg!(debug_assertions) => _debug_files(),
(Some(users), Some(groups)) => (
FileResource::new(&groups).unwrap_or_else(|_| panic!("could not load groups from {}", &groups)),
FileResource::new(&users).unwrap_or_else(|_| panic!("could not load users from {}", &users)),
),
(None, None) => (
FileResource::new(DEFAULT_USERS_FILE)
.unwrap_or_else(|_| panic!("could not load users from {}", &DEFAULT_USERS_FILE)),
FileResource::new(DEFAULT_GROUPS_FILE)
.unwrap_or_else(|_| panic!("could not load groups from {}", &DEFAULT_GROUPS_FILE)),
),
_ => {
eprintln!("must specify paths to both groups and user files, or none");
std::process::exit(1);
},
};
_main(groups, users)
}
fn _debug_files() -> (FileResource<User>, FileResource<Group>) {
eprintln!("creating debug files");
let dir = tempfile::tempdir().unwrap();
let (mut users, mut groups) = (dir.as_ref().to_owned(), dir.as_ref().to_owned());
users.push("user.json");
groups.push("groups.json");
write!(
File::create(&groups).unwrap(),
r#""
)
.unwrap();
write!(
File::create(&users).unwrap(),
r#"[
{{
"name": "root",
"uid": 0,
"gid": 0,
"comment": "",
"home": "",
"shell": ""
}}
]"#
)
.unwrap();
eprintln!("loading debug files");
let users = FileResource::<User>::new(users).unwrap();
let groups = FileResource::<Group>::new(groups).unwrap();
(users, groups)
}
fn _main(groups: FileResource<Group>, users: FileResource<User>) {
rocket::ignite()
.mount(
"/",
routes![
get_all_groups,
get_all_users,
get_group,
get_user,
get_query_matching_groups,
get_query_matching_users,
get_test
],
)
.manage(users)
.manage(groups)
.launch();
}
mod query {
use rocket::request::{FromQuery, Query};
use std::borrow::Cow;
pub struct MemberNames(pub Vec<String>);
impl<'q> FromQuery<'q> for MemberNames {
type Error = std::str::Utf8Error;
fn from_query(query: Query<'q>) -> std::result::Result<Self, Self::Error> {
let mut members: Vec<String> = Vec::new();
for q in query.filter(|q| q.key == "member") {
members.push(q.value.percent_decode()?.to_string())
}
Ok(Self(members))
}
}
}
type Users<'r> = State<'r, FileResource<User>>;
type Groups<'r> = State<'r, FileResource<Group>>;
type Result<T> = std::io::Result<T>;
#[get("/test")]
pub fn get_test() -> Json<Vec<usize>> {
Json(vec![2, 3, 5])
}
#[get("/users")]
pub fn get_all_users(users: Users) -> Option<Json<Vec<User>>> {
Some(Json(api::get_all_users(users).ok()?))
}
#[get("/users/<uid>")]
pub fn get_user(users: Users, uid: u64) -> Option<Json<User>> {
Some(Json(api::get_user(users, uid)?))
}
#[get("/groups/<gid>")]
pub fn get_group(groups: Groups, gid: u64) -> Option<Json<Group>> {
Some(Json(api::get_group(groups, gid)?))
}
#[get("/users/<uid>/groups")]
pub fn get_groups_for_user(groups: Groups, users: Users, uid: u64) -> Option<Json<Vec<Group>>> {
Some(Json(api::get_groups_for_user(groups, users, uid)?))
}
#[get("/groups")]
pub fn get_all_groups(groups: Groups) -> Option<Json<Vec<Group>>> {
Some(Json(api::get_all_groups(groups).ok()?))
}
#[get("/groups/query?<name>&<gid>&<member..>")]
pub fn get_query_matching_groups(
groups: Groups,
name: Option<String>,
gid: Option<u64>,
member: Option<MemberNames>,
) -> Option<Json<Vec<Group>>> {
let query = GroupQueryBuilder {
name,
gid,
members: member.and_then(|m| Some(m.0)),
}
.build()?;
Some(Json(api::get_matching_groups(query, groups).ok()?))
}
#[get("/users/query?<name>&<uid>&<gid>&<comment>&<home>&<shell>")]
pub fn get_query_matching_users(
users: Users,
name: Option<String>,
uid: Option<u64>,
gid: Option<u64>,
comment: Option<String>,
home: Option<String>,
shell: Option<String>,
) -> Option<Json<Vec<User>>> {
let query = UserQueryBuilder {
name,
uid,
gid,
comment,
home,
shell,
}
.build()?;
Some(Json(api::get_matching_users(query, users).ok()?))
}