use actix::Actor;
use actix_web::middleware::session::{self, RequestSession};
use actix_web::Path;
use actix_web::{
error, http, middleware, App, Error, HttpMessage, HttpRequest, HttpResponse, Result, State,
};
use bytes::BytesMut;
use futures::{future, Future, Stream};
use std::sync::Arc;
use time::Duration;
use crate::config::Configuration;
use crate::actors::v1_read::QueryServerReadV1;
use crate::actors::v1_read::{
AuthMessage, InternalRadiusReadMessage, InternalRadiusTokenReadMessage, InternalSearchMessage,
InternalSshKeyReadMessage, InternalSshKeyTagReadMessage, InternalUnixGroupTokenReadMessage,
InternalUnixUserTokenReadMessage, SearchMessage, WhoamiMessage,
};
use crate::actors::v1_write::QueryServerWriteV1;
use crate::actors::v1_write::{
AppendAttributeMessage, CreateMessage, DeleteMessage, IdmAccountSetPasswordMessage,
IdmAccountUnixExtendMessage, IdmGroupUnixExtendMessage, InternalCredentialSetMessage,
InternalDeleteMessage, InternalRegenerateRadiusMessage, InternalSshKeyCreateMessage,
ModifyMessage, PurgeAttributeMessage, RemoveAttributeValueMessage, SetAttributeMessage,
};
use crate::async_log;
use crate::audit::AuditScope;
use crate::be::{Backend, BackendTransaction};
use crate::crypto::setup_tls;
use crate::filter::{Filter, FilterInvalid};
use crate::idm::server::IdmServer;
use crate::interval::IntervalActor;
use crate::schema::Schema;
use crate::schema::SchemaTransaction;
use crate::server::QueryServer;
use crate::utils::SID;
use crate::value::PartialValue;
use kanidm_proto::v1::Entry as ProtoEntry;
use kanidm_proto::v1::OperationError;
use kanidm_proto::v1::{
AccountUnixExtend, AuthRequest, AuthState, CreateRequest, DeleteRequest, GroupUnixExtend,
ModifyRequest, SearchRequest, SetAuthCredential, SingleStringRequest, UserAuthToken,
};
use uuid::Uuid;
struct AppState {
qe_r: actix::Addr<QueryServerReadV1>,
qe_w: actix::Addr<QueryServerWriteV1>,
max_size: usize,
}
fn get_current_user(req: &HttpRequest<AppState>) -> Option<UserAuthToken> {
match req.session().get::<UserAuthToken>("uat") {
Ok(maybe_uat) => maybe_uat,
Err(_) => {
None
}
}
}
fn operation_error_to_response(e: OperationError) -> HttpResponse {
match e {
OperationError::NotAuthenticated => HttpResponse::Unauthorized().json(e),
OperationError::AccessDenied | OperationError::SystemProtectedObject => {
HttpResponse::Forbidden().json(e)
}
OperationError::EmptyRequest
| OperationError::NoMatchingEntries
| OperationError::SchemaViolation(_) => HttpResponse::BadRequest().json(e),
_ => HttpResponse::InternalServerError().json(e),
}
}
macro_rules! json_event_post {
($req:expr, $state:expr, $message_type:ty, $request_type:ty, $dest:expr) => {{
let max_size = $state.max_size;
let uat = get_current_user(&$req);
$req.payload()
.from_err()
.fold(BytesMut::new(), move |mut body, chunk| {
if (body.len() + chunk.len()) > max_size {
Err(error::ErrorBadRequest("overflow"))
} else {
body.extend_from_slice(&chunk);
Ok(body)
}
})
.and_then(
move |body| -> Box<dyn Future<Item = HttpResponse, Error = Error>> {
let r_obj = serde_json::from_slice::<$request_type>(&body);
match r_obj {
Ok(obj) => {
let m_obj = <$message_type>::new(uat, obj);
let res = $dest
.send(m_obj)
.from_err()
.and_then(|res| match res {
Ok(event_result) => Ok(HttpResponse::Ok().json(event_result)),
Err(e) => Ok(operation_error_to_response(e)),
});
Box::new(res)
}
Err(e) => Box::new(future::err(error::ErrorBadRequest(format!(
"Json Decode Failed: {:?}",
e
)))),
}
},
)
}};
}
macro_rules! json_event_get {
($req:expr, $state:expr, $message_type:ty) => {{
let uat = get_current_user(&$req);
let obj = <$message_type>::new(uat);
let res = $state.qe_r.send(obj).from_err().and_then(|res| match res {
Ok(event_result) => Ok(HttpResponse::Ok().json(event_result)),
Err(e) => Ok(operation_error_to_response(e)),
});
Box::new(res)
}};
}
fn create(
(req, state): (HttpRequest<AppState>, State<AppState>),
) -> impl Future<Item = HttpResponse, Error = Error> {
json_event_post!(req, state, CreateMessage, CreateRequest, state.qe_w)
}
fn modify(
(req, state): (HttpRequest<AppState>, State<AppState>),
) -> impl Future<Item = HttpResponse, Error = Error> {
json_event_post!(req, state, ModifyMessage, ModifyRequest, state.qe_w)
}
fn delete(
(req, state): (HttpRequest<AppState>, State<AppState>),
) -> impl Future<Item = HttpResponse, Error = Error> {
json_event_post!(req, state, DeleteMessage, DeleteRequest, state.qe_w)
}
fn search(
(req, state): (HttpRequest<AppState>, State<AppState>),
) -> impl Future<Item = HttpResponse, Error = Error> {
json_event_post!(req, state, SearchMessage, SearchRequest, state.qe_r)
}
fn whoami(
(req, state): (HttpRequest<AppState>, State<AppState>),
) -> impl Future<Item = HttpResponse, Error = Error> {
json_event_get!(req, state, WhoamiMessage)
}
fn json_rest_event_get(
req: HttpRequest<AppState>,
state: State<AppState>,
filter: Filter<FilterInvalid>,
attrs: Option<Vec<String>>,
) -> impl Future<Item = HttpResponse, Error = Error> {
let uat = get_current_user(&req);
let obj = InternalSearchMessage { uat, filter, attrs };
let res = state.qe_r.send(obj).from_err().and_then(|res| match res {
Ok(event_result) => Ok(HttpResponse::Ok().json(event_result)),
Err(e) => Ok(operation_error_to_response(e)),
});
Box::new(res)
}
fn json_rest_event_get_id(
path: Path<String>,
req: HttpRequest<AppState>,
state: State<AppState>,
filter: Filter<FilterInvalid>,
attrs: Option<Vec<String>>,
) -> impl Future<Item = HttpResponse, Error = Error> {
let uat = get_current_user(&req);
let filter = Filter::join_parts_and(filter, filter_all!(f_id(path.as_str())));
let obj = InternalSearchMessage { uat, filter, attrs };
let res = state.qe_r.send(obj).from_err().and_then(|res| match res {
Ok(mut event_result) => {
Ok(HttpResponse::Ok().json(event_result.pop()))
}
Err(e) => Ok(operation_error_to_response(e)),
});
Box::new(res)
}
fn json_rest_event_delete_id(
path: Path<String>,
req: HttpRequest<AppState>,
state: State<AppState>,
filter: Filter<FilterInvalid>,
) -> impl Future<Item = HttpResponse, Error = Error> {
let uat = get_current_user(&req);
let filter = Filter::join_parts_and(filter, filter_all!(f_id(path.as_str())));
let obj = InternalDeleteMessage { uat, filter };
let res = state.qe_w.send(obj).from_err().and_then(|res| match res {
Ok(_) => {
Ok(HttpResponse::Ok().json(()))
}
Err(e) => Ok(operation_error_to_response(e)),
});
Box::new(res)
}
fn json_rest_event_get_id_attr(
path: Path<(String, String)>,
req: HttpRequest<AppState>,
state: State<AppState>,
filter: Filter<FilterInvalid>,
) -> impl Future<Item = HttpResponse, Error = Error> {
let (id, attr) = path.into_inner();
let uat = get_current_user(&req);
let filter = Filter::join_parts_and(filter, filter_all!(f_id(id.as_str())));
let obj = InternalSearchMessage {
uat,
filter,
attrs: Some(vec![attr.clone()]),
};
let res = state
.qe_r
.send(obj)
.from_err()
.and_then(move |res| match res {
Ok(mut event_result) => {
let r = event_result.pop().and_then(|mut e| {
e.attrs.remove(&attr)
});
debug!("final json result {:?}", r);
Ok(HttpResponse::Ok().json(r))
}
Err(e) => Ok(operation_error_to_response(e)),
});
Box::new(res)
}
fn json_rest_event_post(
req: HttpRequest<AppState>,
state: State<AppState>,
classes: Vec<String>,
) -> impl Future<Item = HttpResponse, Error = Error> {
let max_size = state.max_size;
let uat = get_current_user(&req);
req.payload()
.from_err()
.fold(BytesMut::new(), move |mut body, chunk| {
if (body.len() + chunk.len()) > max_size {
Err(error::ErrorBadRequest("overflow"))
} else {
body.extend_from_slice(&chunk);
Ok(body)
}
})
.and_then(
move |body| -> Box<dyn Future<Item = HttpResponse, Error = Error>> {
let r_obj = serde_json::from_slice::<ProtoEntry>(&body);
match r_obj {
Ok(mut obj) => {
obj.attrs.insert("class".to_string(), classes);
let m_obj = CreateMessage::new_entry(uat, obj);
let res = state.qe_w.send(m_obj).from_err().and_then(|res| match res {
Ok(_) => Ok(HttpResponse::Ok().json(())),
Err(e) => Ok(operation_error_to_response(e)),
});
Box::new(res)
}
Err(e) => Box::new(future::err(error::ErrorBadRequest(format!(
"Json Decode Failed: {:?}",
e
)))),
}
},
)
}
fn json_rest_event_post_id_attr(
path: Path<(String, String)>,
req: HttpRequest<AppState>,
state: State<AppState>,
filter: Filter<FilterInvalid>,
) -> impl Future<Item = HttpResponse, Error = Error> {
let max_size = state.max_size;
let uat = get_current_user(&req);
let (id, attr) = path.into_inner();
req.payload()
.from_err()
.fold(BytesMut::new(), move |mut body, chunk| {
if (body.len() + chunk.len()) > max_size {
Err(error::ErrorBadRequest("overflow"))
} else {
body.extend_from_slice(&chunk);
Ok(body)
}
})
.and_then(
move |body| -> Box<dyn Future<Item = HttpResponse, Error = Error>> {
let r_obj = serde_json::from_slice::<Vec<String>>(&body);
match r_obj {
Ok(obj) => {
let m_obj = AppendAttributeMessage {
uat,
uuid_or_name: id,
attr,
values: obj,
filter,
};
let res = state.qe_w.send(m_obj).from_err().and_then(|res| match res {
Ok(_) => Ok(HttpResponse::Ok().json(())),
Err(e) => Ok(operation_error_to_response(e)),
});
Box::new(res)
}
Err(e) => Box::new(future::err(error::ErrorBadRequest(format!(
"Json Decode Failed: {:?}",
e
)))),
}
},
)
}
fn json_rest_event_put_id_attr(
path: Path<(String, String)>,
req: HttpRequest<AppState>,
state: State<AppState>,
filter: Filter<FilterInvalid>,
) -> impl Future<Item = HttpResponse, Error = Error> {
let max_size = state.max_size;
let uat = get_current_user(&req);
let (id, attr) = path.into_inner();
req.payload()
.from_err()
.fold(BytesMut::new(), move |mut body, chunk| {
if (body.len() + chunk.len()) > max_size {
Err(error::ErrorBadRequest("overflow"))
} else {
body.extend_from_slice(&chunk);
Ok(body)
}
})
.and_then(
move |body| -> Box<dyn Future<Item = HttpResponse, Error = Error>> {
let r_obj = serde_json::from_slice::<Vec<String>>(&body);
match r_obj {
Ok(obj) => {
let m_obj = SetAttributeMessage {
uat,
uuid_or_name: id,
attr,
values: obj,
filter,
};
let res = state.qe_w.send(m_obj).from_err().and_then(|res| match res {
Ok(_) => Ok(HttpResponse::Ok().json(())),
Err(e) => Ok(operation_error_to_response(e)),
});
Box::new(res)
}
Err(e) => Box::new(future::err(error::ErrorBadRequest(format!(
"Json Decode Failed: {:?}",
e
)))),
}
},
)
}
fn json_rest_event_delete_id_attr(
path: Path<(String, String)>,
req: HttpRequest<AppState>,
state: State<AppState>,
filter: Filter<FilterInvalid>,
) -> impl Future<Item = HttpResponse, Error = Error> {
let uat = get_current_user(&req);
let (id, attr) = path.into_inner();
let obj = PurgeAttributeMessage {
uat,
uuid_or_name: id,
attr,
filter,
};
let res = state.qe_w.send(obj).from_err().and_then(|res| match res {
Ok(()) => Ok(HttpResponse::Ok().json(())),
Err(e) => Ok(operation_error_to_response(e)),
});
Box::new(res)
}
fn json_rest_event_credential_put(
id: String,
cred_id: Option<String>,
req: HttpRequest<AppState>,
state: State<AppState>,
) -> impl Future<Item = HttpResponse, Error = Error> {
let max_size = state.max_size;
let uat = get_current_user(&req);
req.payload()
.from_err()
.fold(BytesMut::new(), move |mut body, chunk| {
if (body.len() + chunk.len()) > max_size {
Err(error::ErrorBadRequest("overflow"))
} else {
body.extend_from_slice(&chunk);
Ok(body)
}
})
.and_then(
move |body| -> Box<dyn Future<Item = HttpResponse, Error = Error>> {
let r_obj = serde_json::from_slice::<SetAuthCredential>(&body);
match r_obj {
Ok(obj) => {
let m_obj = InternalCredentialSetMessage::new(uat, id, cred_id, obj);
let res = state.qe_w.send(m_obj).from_err().and_then(|res| match res {
Ok(event_result) => Ok(HttpResponse::Ok().json(event_result)),
Err(e) => Ok(operation_error_to_response(e)),
});
Box::new(res)
}
Err(e) => Box::new(future::err(error::ErrorBadRequest(format!(
"Json Decode Failed: {:?}",
e
)))),
}
},
)
}
fn schema_get(
(req, state): (HttpRequest<AppState>, State<AppState>),
) -> impl Future<Item = HttpResponse, Error = Error> {
let filter = filter_all!(f_or!([
f_eq("class", PartialValue::new_class("attributetype")),
f_eq("class", PartialValue::new_class("classtype"))
]));
json_rest_event_get(req, state, filter, None)
}
fn schema_attributetype_get(
(req, state): (HttpRequest<AppState>, State<AppState>),
) -> impl Future<Item = HttpResponse, Error = Error> {
let filter = filter_all!(f_eq("class", PartialValue::new_class("attributetype")));
json_rest_event_get(req, state, filter, None)
}
fn schema_attributetype_get_id(
(path, req, state): (Path<String>, HttpRequest<AppState>, State<AppState>),
) -> impl Future<Item = HttpResponse, Error = Error> {
let uat = get_current_user(&req);
let filter = filter_all!(f_and!([
f_eq("class", PartialValue::new_class("attributetype")),
f_eq("attributename", PartialValue::new_iutf8s(path.as_str()))
]));
let obj = InternalSearchMessage {
uat,
filter,
attrs: None,
};
let res = state.qe_r.send(obj).from_err().and_then(|res| match res {
Ok(mut event_result) => {
Ok(HttpResponse::Ok().json(event_result.pop()))
}
Err(e) => Ok(operation_error_to_response(e)),
});
Box::new(res)
}
fn schema_classtype_get(
(req, state): (HttpRequest<AppState>, State<AppState>),
) -> impl Future<Item = HttpResponse, Error = Error> {
let filter = filter_all!(f_eq("class", PartialValue::new_class("classtype")));
json_rest_event_get(req, state, filter, None)
}
fn schema_classtype_get_id(
(path, req, state): (Path<String>, HttpRequest<AppState>, State<AppState>),
) -> impl Future<Item = HttpResponse, Error = Error> {
let uat = get_current_user(&req);
let filter = filter_all!(f_and!([
f_eq("class", PartialValue::new_class("classtype")),
f_eq("classname", PartialValue::new_iutf8s(path.as_str()))
]));
let obj = InternalSearchMessage {
uat,
filter,
attrs: None,
};
let res = state.qe_r.send(obj).from_err().and_then(|res| match res {
Ok(mut event_result) => {
Ok(HttpResponse::Ok().json(event_result.pop()))
}
Err(e) => Ok(operation_error_to_response(e)),
});
Box::new(res)
}
fn account_get(
(req, state): (HttpRequest<AppState>, State<AppState>),
) -> impl Future<Item = HttpResponse, Error = Error> {
let filter = filter_all!(f_eq("class", PartialValue::new_class("account")));
json_rest_event_get(req, state, filter, None)
}
fn account_post(
(req, state): (HttpRequest<AppState>, State<AppState>),
) -> impl Future<Item = HttpResponse, Error = Error> {
let classes = vec!["account".to_string(), "object".to_string()];
json_rest_event_post(req, state, classes)
}
fn account_id_get(
(path, req, state): (Path<String>, HttpRequest<AppState>, State<AppState>),
) -> impl Future<Item = HttpResponse, Error = Error> {
let filter = filter_all!(f_eq("class", PartialValue::new_class("account")));
json_rest_event_get_id(path, req, state, filter, None)
}
fn account_id_get_attr(
(path, req, state): (
Path<(String, String)>,
HttpRequest<AppState>,
State<AppState>,
),
) -> impl Future<Item = HttpResponse, Error = Error> {
let filter = filter_all!(f_eq("class", PartialValue::new_class("account")));
json_rest_event_get_id_attr(path, req, state, filter)
}
fn account_id_post_attr(
(path, req, state): (
Path<(String, String)>,
HttpRequest<AppState>,
State<AppState>,
),
) -> impl Future<Item = HttpResponse, Error = Error> {
let filter = filter_all!(f_eq("class", PartialValue::new_class("account")));
json_rest_event_post_id_attr(path, req, state, filter)
}
fn account_id_delete_attr(
(path, req, state): (
Path<(String, String)>,
HttpRequest<AppState>,
State<AppState>,
),
) -> impl Future<Item = HttpResponse, Error = Error> {
let filter = filter_all!(f_eq("class", PartialValue::new_class("account")));
json_rest_event_delete_id_attr(path, req, state, filter)
}
fn account_id_put_attr(
(path, req, state): (
Path<(String, String)>,
HttpRequest<AppState>,
State<AppState>,
),
) -> impl Future<Item = HttpResponse, Error = Error> {
let filter = filter_all!(f_eq("class", PartialValue::new_class("account")));
json_rest_event_put_id_attr(path, req, state, filter)
}
fn account_id_delete(
(path, req, state): (Path<String>, HttpRequest<AppState>, State<AppState>),
) -> impl Future<Item = HttpResponse, Error = Error> {
let filter = filter_all!(f_eq("class", PartialValue::new_class("account")));
json_rest_event_delete_id(path, req, state, filter)
}
fn account_put_id_credential_primary(
(path, req, state): (Path<String>, HttpRequest<AppState>, State<AppState>),
) -> impl Future<Item = HttpResponse, Error = Error> {
let id = path.into_inner();
json_rest_event_credential_put(id, None, req, state)
}
fn account_get_id_ssh_pubkeys(
(path, req, state): (Path<String>, HttpRequest<AppState>, State<AppState>),
) -> impl Future<Item = HttpResponse, Error = Error> {
let uat = get_current_user(&req);
let id = path.into_inner();
let obj = InternalSshKeyReadMessage {
uat,
uuid_or_name: id,
};
let res = state.qe_r.send(obj).from_err().and_then(|res| match res {
Ok(event_result) => {
Ok(HttpResponse::Ok().json(event_result))
}
Err(e) => Ok(operation_error_to_response(e)),
});
Box::new(res)
}
fn account_post_id_ssh_pubkey(
(path, req, state): (Path<String>, HttpRequest<AppState>, State<AppState>),
) -> impl Future<Item = HttpResponse, Error = Error> {
let max_size = state.max_size;
let uat = get_current_user(&req);
let id = path.into_inner();
req.payload()
.from_err()
.fold(BytesMut::new(), move |mut body, chunk| {
if (body.len() + chunk.len()) > max_size {
Err(error::ErrorBadRequest("overflow"))
} else {
body.extend_from_slice(&chunk);
Ok(body)
}
})
.and_then(
move |body| -> Box<dyn Future<Item = HttpResponse, Error = Error>> {
let r_obj = serde_json::from_slice::<(String, String)>(&body);
match r_obj {
Ok((tag, key)) => {
let m_obj = InternalSshKeyCreateMessage {
uat,
uuid_or_name: id,
tag,
key,
filter: filter_all!(f_eq("class", PartialValue::new_class("account"))),
};
let res = state.qe_w.send(m_obj).from_err().and_then(|res| match res {
Ok(_) => Ok(HttpResponse::Ok().json(())),
Err(e) => Ok(operation_error_to_response(e)),
});
Box::new(res)
}
Err(e) => Box::new(future::err(error::ErrorBadRequest(format!(
"Json Decode Failed: {:?}",
e
)))),
}
},
)
}
fn account_get_id_ssh_pubkey_tag(
(path, req, state): (
Path<(String, String)>,
HttpRequest<AppState>,
State<AppState>,
),
) -> impl Future<Item = HttpResponse, Error = Error> {
let uat = get_current_user(&req);
let (id, tag) = path.into_inner();
let obj = InternalSshKeyTagReadMessage {
uat,
uuid_or_name: id,
tag,
};
let res = state.qe_r.send(obj).from_err().and_then(|res| match res {
Ok(event_result) => {
Ok(HttpResponse::Ok().json(event_result))
}
Err(e) => Ok(operation_error_to_response(e)),
});
Box::new(res)
}
fn account_delete_id_ssh_pubkey_tag(
(path, req, state): (
Path<(String, String)>,
HttpRequest<AppState>,
State<AppState>,
),
) -> impl Future<Item = HttpResponse, Error = Error> {
let uat = get_current_user(&req);
let (id, tag) = path.into_inner();
let obj = RemoveAttributeValueMessage {
uat,
uuid_or_name: id,
attr: "ssh_publickey".to_string(),
value: tag,
filter: filter_all!(f_eq("class", PartialValue::new_class("account"))),
};
let res = state.qe_w.send(obj).from_err().and_then(|res| match res {
Ok(()) => Ok(HttpResponse::Ok().json(())),
Err(e) => Ok(operation_error_to_response(e)),
});
Box::new(res)
}
fn account_get_id_radius(
(path, req, state): (Path<String>, HttpRequest<AppState>, State<AppState>),
) -> impl Future<Item = HttpResponse, Error = Error> {
let uat = get_current_user(&req);
let id = path.into_inner();
let obj = InternalRadiusReadMessage {
uat,
uuid_or_name: id,
};
let res = state.qe_r.send(obj).from_err().and_then(|res| match res {
Ok(event_result) => {
Ok(HttpResponse::Ok().json(event_result))
}
Err(e) => Ok(operation_error_to_response(e)),
});
Box::new(res)
}
fn account_post_id_radius_regenerate(
(path, req, state): (Path<String>, HttpRequest<AppState>, State<AppState>),
) -> impl Future<Item = HttpResponse, Error = Error> {
let uat = get_current_user(&req);
let id = path.into_inner();
let obj = InternalRegenerateRadiusMessage::new(uat, id);
let res = state.qe_w.send(obj).from_err().and_then(|res| match res {
Ok(event_result) => {
Ok(HttpResponse::Ok().json(event_result))
}
Err(e) => Ok(operation_error_to_response(e)),
});
Box::new(res)
}
fn account_delete_id_radius(
(path, req, state): (Path<String>, HttpRequest<AppState>, State<AppState>),
) -> impl Future<Item = HttpResponse, Error = Error> {
let p = Path::from((path.into_inner(), "radius_secret".to_string()));
let filter = filter_all!(f_eq("class", PartialValue::new_class("account")));
json_rest_event_delete_id_attr(p, req, state, filter)
}
fn account_get_id_radius_token(
(path, req, state): (Path<String>, HttpRequest<AppState>, State<AppState>),
) -> impl Future<Item = HttpResponse, Error = Error> {
let uat = get_current_user(&req);
let id = path.into_inner();
let obj = InternalRadiusTokenReadMessage {
uat,
uuid_or_name: id,
};
let res = state.qe_r.send(obj).from_err().and_then(|res| match res {
Ok(event_result) => {
Ok(HttpResponse::Ok().json(event_result))
}
Err(e) => Ok(operation_error_to_response(e)),
});
Box::new(res)
}
fn account_post_id_unix(
(path, req, state): (Path<String>, HttpRequest<AppState>, State<AppState>),
) -> impl Future<Item = HttpResponse, Error = Error> {
let max_size = state.max_size;
let uat = get_current_user(&req);
let id = path.into_inner();
req.payload()
.from_err()
.fold(BytesMut::new(), move |mut body, chunk| {
if (body.len() + chunk.len()) > max_size {
Err(error::ErrorBadRequest("overflow"))
} else {
body.extend_from_slice(&chunk);
Ok(body)
}
})
.and_then(
move |body| -> Box<dyn Future<Item = HttpResponse, Error = Error>> {
let r_obj = serde_json::from_slice::<AccountUnixExtend>(&body);
match r_obj {
Ok(obj) => {
let m_obj = IdmAccountUnixExtendMessage::new(uat, id, obj);
let res = state.qe_w.send(m_obj).from_err().and_then(|res| match res {
Ok(event_result) => Ok(HttpResponse::Ok().json(event_result)),
Err(e) => Ok(operation_error_to_response(e)),
});
Box::new(res)
}
Err(e) => Box::new(future::err(error::ErrorBadRequest(format!(
"Json Decode Failed: {:?}",
e
)))),
}
},
)
}
fn account_get_id_unix_token(
(path, req, state): (Path<String>, HttpRequest<AppState>, State<AppState>),
) -> impl Future<Item = HttpResponse, Error = Error> {
let uat = get_current_user(&req);
let id = path.into_inner();
let obj = InternalUnixUserTokenReadMessage {
uat,
uuid_or_name: id,
};
let res = state.qe_r.send(obj).from_err().and_then(|res| match res {
Ok(event_result) => {
Ok(HttpResponse::Ok().json(event_result))
}
Err(e) => Ok(operation_error_to_response(e)),
});
Box::new(res)
}
fn group_get(
(req, state): (HttpRequest<AppState>, State<AppState>),
) -> impl Future<Item = HttpResponse, Error = Error> {
let filter = filter_all!(f_eq("class", PartialValue::new_class("group")));
json_rest_event_get(req, state, filter, None)
}
fn group_post(
(req, state): (HttpRequest<AppState>, State<AppState>),
) -> impl Future<Item = HttpResponse, Error = Error> {
let classes = vec!["group".to_string(), "object".to_string()];
json_rest_event_post(req, state, classes)
}
fn group_id_get(
(path, req, state): (Path<String>, HttpRequest<AppState>, State<AppState>),
) -> impl Future<Item = HttpResponse, Error = Error> {
let filter = filter_all!(f_eq("class", PartialValue::new_class("group")));
json_rest_event_get_id(path, req, state, filter, None)
}
fn group_id_get_attr(
(path, req, state): (
Path<(String, String)>,
HttpRequest<AppState>,
State<AppState>,
),
) -> impl Future<Item = HttpResponse, Error = Error> {
let filter = filter_all!(f_eq("class", PartialValue::new_class("group")));
json_rest_event_get_id_attr(path, req, state, filter)
}
fn group_id_post_attr(
(path, req, state): (
Path<(String, String)>,
HttpRequest<AppState>,
State<AppState>,
),
) -> impl Future<Item = HttpResponse, Error = Error> {
let filter = filter_all!(f_eq("class", PartialValue::new_class("group")));
json_rest_event_post_id_attr(path, req, state, filter)
}
fn group_id_delete_attr(
(path, req, state): (
Path<(String, String)>,
HttpRequest<AppState>,
State<AppState>,
),
) -> impl Future<Item = HttpResponse, Error = Error> {
let filter = filter_all!(f_eq("class", PartialValue::new_class("group")));
json_rest_event_delete_id_attr(path, req, state, filter)
}
fn group_id_put_attr(
(path, req, state): (
Path<(String, String)>,
HttpRequest<AppState>,
State<AppState>,
),
) -> impl Future<Item = HttpResponse, Error = Error> {
let filter = filter_all!(f_eq("class", PartialValue::new_class("group")));
json_rest_event_put_id_attr(path, req, state, filter)
}
fn group_id_delete(
(path, req, state): (Path<String>, HttpRequest<AppState>, State<AppState>),
) -> impl Future<Item = HttpResponse, Error = Error> {
let filter = filter_all!(f_eq("class", PartialValue::new_class("group")));
json_rest_event_delete_id(path, req, state, filter)
}
fn group_post_id_unix(
(path, req, state): (Path<String>, HttpRequest<AppState>, State<AppState>),
) -> impl Future<Item = HttpResponse, Error = Error> {
let max_size = state.max_size;
let uat = get_current_user(&req);
let id = path.into_inner();
req.payload()
.from_err()
.fold(BytesMut::new(), move |mut body, chunk| {
if (body.len() + chunk.len()) > max_size {
Err(error::ErrorBadRequest("overflow"))
} else {
body.extend_from_slice(&chunk);
Ok(body)
}
})
.and_then(
move |body| -> Box<dyn Future<Item = HttpResponse, Error = Error>> {
let r_obj = serde_json::from_slice::<GroupUnixExtend>(&body);
match r_obj {
Ok(obj) => {
let m_obj = IdmGroupUnixExtendMessage::new(uat, id, obj);
let res = state.qe_w.send(m_obj).from_err().and_then(|res| match res {
Ok(event_result) => Ok(HttpResponse::Ok().json(event_result)),
Err(e) => Ok(operation_error_to_response(e)),
});
Box::new(res)
}
Err(e) => Box::new(future::err(error::ErrorBadRequest(format!(
"Json Decode Failed: {:?}",
e
)))),
}
},
)
}
fn group_get_id_unix_token(
(path, req, state): (Path<String>, HttpRequest<AppState>, State<AppState>),
) -> impl Future<Item = HttpResponse, Error = Error> {
let uat = get_current_user(&req);
let id = path.into_inner();
let obj = InternalUnixGroupTokenReadMessage {
uat,
uuid_or_name: id,
};
let res = state.qe_r.send(obj).from_err().and_then(|res| match res {
Ok(event_result) => {
Ok(HttpResponse::Ok().json(event_result))
}
Err(e) => Ok(operation_error_to_response(e)),
});
Box::new(res)
}
fn domain_get(
(req, state): (HttpRequest<AppState>, State<AppState>),
) -> impl Future<Item = HttpResponse, Error = Error> {
let filter = filter_all!(f_eq("class", PartialValue::new_class("domain_info")));
json_rest_event_get(req, state, filter, None)
}
fn domain_id_get(
(path, req, state): (Path<String>, HttpRequest<AppState>, State<AppState>),
) -> impl Future<Item = HttpResponse, Error = Error> {
let filter = filter_all!(f_eq("class", PartialValue::new_class("domain_info")));
json_rest_event_get_id(path, req, state, filter, None)
}
fn domain_id_get_attr(
(path, req, state): (
Path<(String, String)>,
HttpRequest<AppState>,
State<AppState>,
),
) -> impl Future<Item = HttpResponse, Error = Error> {
let filter = filter_all!(f_eq("class", PartialValue::new_class("domain_info")));
json_rest_event_get_id_attr(path, req, state, filter)
}
fn domain_id_put_attr(
(path, req, state): (
Path<(String, String)>,
HttpRequest<AppState>,
State<AppState>,
),
) -> impl Future<Item = HttpResponse, Error = Error> {
let filter = filter_all!(f_eq("class", PartialValue::new_class("domain_info")));
json_rest_event_put_id_attr(path, req, state, filter)
}
fn do_nothing((_req, _state): (HttpRequest<AppState>, State<AppState>)) -> String {
"did nothing".to_string()
}
fn auth(
(req, state): (HttpRequest<AppState>, State<AppState>),
) -> impl Future<Item = HttpResponse, Error = Error> {
let max_size = state.max_size;
req.payload()
.from_err()
.fold(BytesMut::new(), move |mut body, chunk| {
if (body.len() + chunk.len()) > max_size {
Err(error::ErrorBadRequest("overflow"))
} else {
body.extend_from_slice(&chunk);
Ok(body)
}
})
.and_then(
move |body| -> Box<dyn Future<Item = HttpResponse, Error = Error>> {
let r_obj = serde_json::from_slice::<AuthRequest>(&body);
match r_obj {
Ok(obj) => {
let maybe_sessionid = match req.session().get::<Uuid>("auth-session-id") {
Ok(c) => c,
Err(e) => {
return Box::new(future::err(e));
}
};
let auth_msg = AuthMessage::new(obj, maybe_sessionid);
let res = state
.qe_r
.send(auth_msg)
.from_err()
.and_then(move |res| match res {
Ok(ar) => {
match &ar.state {
AuthState::Success(uat) => {
req.session().remove("auth-session-id");
match req.session().set("uat", uat) {
Ok(_) => Ok(HttpResponse::Ok().json(ar)),
Err(_) => {
Ok(HttpResponse::InternalServerError().json(()))
}
}
}
AuthState::Denied(_) => {
req.session().remove("auth-session-id");
Ok(HttpResponse::Unauthorized().json(ar))
}
AuthState::Continue(_) => {
match req.session().set("auth-session-id", ar.sessionid)
{
Ok(_) => Ok(HttpResponse::Ok().json(ar)),
Err(_) => {
Ok(HttpResponse::InternalServerError().json(()))
}
}
}
}
}
Err(e) => Ok(operation_error_to_response(e)),
});
Box::new(res)
}
Err(e) => Box::new(future::err(error::ErrorBadRequest(format!(
"Json Decode Failed: {:?}",
e
)))),
}
},
)
}
fn idm_account_set_password(
(req, state): (HttpRequest<AppState>, State<AppState>),
) -> impl Future<Item = HttpResponse, Error = Error> {
json_event_post!(
req,
state,
IdmAccountSetPasswordMessage,
SingleStringRequest,
state.qe_w
)
}
fn setup_backend(config: &Configuration) -> Result<Backend, OperationError> {
let mut audit_be = AuditScope::new("backend_setup");
let pool_size: u32 = config.threads as u32;
let be = Backend::new(&mut audit_be, config.db_path.as_str(), pool_size);
debug!("{}", audit_be);
be
}
fn setup_qs_idms(
audit: &mut AuditScope,
be: Backend,
sid: SID,
) -> Result<(QueryServer, IdmServer), OperationError> {
let schema = match Schema::new(audit) {
Ok(s) => s,
Err(e) => {
error!("Failed to setup in memory schema: {:?}", e);
return Err(e);
}
};
let query_server = QueryServer::new(be, schema);
query_server.initialise_helper(audit)?;
let idms = IdmServer::new(query_server.clone(), sid);
Ok((query_server, idms))
}
pub fn backup_server_core(config: Configuration, dst_path: &str) {
let be = match setup_backend(&config) {
Ok(be) => be,
Err(e) => {
error!("Failed to setup BE: {:?}", e);
return;
}
};
let mut audit = AuditScope::new("backend_backup");
let be_ro_txn = be.read();
let r = be_ro_txn.backup(&mut audit, dst_path);
debug!("{}", audit);
match r {
Ok(_) => info!("Backup success!"),
Err(e) => {
error!("Backup failed: {:?}", e);
std::process::exit(1);
}
};
}
pub fn restore_server_core(config: Configuration, dst_path: &str) {
let be = match setup_backend(&config) {
Ok(be) => be,
Err(e) => {
error!("Failed to setup BE: {:?}", e);
return;
}
};
let mut audit = AuditScope::new("backend_restore");
let schema = match Schema::new(&mut audit) {
Ok(s) => s,
Err(e) => {
error!("Failed to setup in memory schema: {:?}", e);
std::process::exit(1);
}
};
let idxmeta = { schema.write().get_idxmeta_set() };
let mut be_wr_txn = be.write(idxmeta);
let r = be_wr_txn
.restore(&mut audit, dst_path)
.and_then(|_| be_wr_txn.commit(&mut audit));
if r.is_err() {
debug!("{}", audit);
error!("Failed to restore database: {:?}", r);
std::process::exit(1);
}
info!("Restore Success!");
info!("Attempting to init query server ...");
let server_id = be.get_db_sid();
let (qs, _idms) = match setup_qs_idms(&mut audit, be, server_id) {
Ok(t) => t,
Err(e) => {
debug!("{}", audit);
error!("Unable to setup query server or idm server -> {:?}", e);
return;
}
};
info!("Success!");
info!("Start reindex phase ...");
let qs_write = qs.write();
let r = qs_write
.reindex(&mut audit)
.and_then(|_| qs_write.commit(&mut audit));
match r {
Ok(_) => info!("Reindex Success!"),
Err(e) => {
error!("Restore failed: {:?}", e);
std::process::exit(1);
}
};
}
pub fn reindex_server_core(config: Configuration) {
let be = match setup_backend(&config) {
Ok(be) => be,
Err(e) => {
error!("Failed to setup BE: {:?}", e);
return;
}
};
let mut audit = AuditScope::new("server_reindex");
let schema = match Schema::new(&mut audit) {
Ok(s) => s,
Err(e) => {
error!("Failed to setup in memory schema: {:?}", e);
std::process::exit(1);
}
};
info!("Start Index Phase 1 ...");
let idxmeta = { schema.write().get_idxmeta_set() };
let be_wr_txn = be.write(idxmeta);
let r = be_wr_txn
.reindex(&mut audit)
.and_then(|_| be_wr_txn.commit(&mut audit));
if r.is_err() {
debug!("{}", audit);
error!("Failed to reindex database: {:?}", r);
std::process::exit(1);
}
info!("Index Phase 1 Success!");
info!("Attempting to init query server ...");
let server_id = be.get_db_sid();
let (qs, _idms) = match setup_qs_idms(&mut audit, be, server_id) {
Ok(t) => t,
Err(e) => {
debug!("{}", audit);
error!("Unable to setup query server or idm server -> {:?}", e);
return;
}
};
info!("Init Query Server Success!");
info!("Start Index Phase 2 ...");
let qs_write = qs.write();
let r = qs_write
.reindex(&mut audit)
.and_then(|_| qs_write.commit(&mut audit));
match r {
Ok(_) => info!("Index Phase 2 Success!"),
Err(e) => {
error!("Reindex failed: {:?}", e);
std::process::exit(1);
}
};
}
pub fn domain_rename_core(config: Configuration, new_domain_name: String) {
let mut audit = AuditScope::new("domain_rename");
let be = match setup_backend(&config) {
Ok(be) => be,
Err(e) => {
error!("Failed to setup BE: {:?}", e);
return;
}
};
let server_id = be.get_db_sid();
let (qs, _idms) = match setup_qs_idms(&mut audit, be, server_id) {
Ok(t) => t,
Err(e) => {
debug!("{}", audit);
error!("Unable to setup query server or idm server -> {:?}", e);
return;
}
};
let mut qs_write = qs.write();
let r = qs_write
.domain_rename(&mut audit, new_domain_name.as_str())
.and_then(|_| qs_write.commit(&mut audit));
match r {
Ok(_) => info!("Domain Rename Success!"),
Err(e) => {
error!("Domain Rename Failed - Rollback has occured: {:?}", e);
std::process::exit(1);
}
};
}
pub fn reset_sid_core(config: Configuration) {
let mut audit = AuditScope::new("reset_sid_core");
let be = match setup_backend(&config) {
Ok(be) => be,
Err(e) => {
error!("Failed to setup BE: {:?}", e);
return;
}
};
let nsid = be.reset_db_sid(&mut audit);
debug!("{}", audit);
info!("New Server ID: {:?}", nsid);
}
pub fn verify_server_core(config: Configuration) {
let mut audit = AuditScope::new("server_verify");
let be = match setup_backend(&config) {
Ok(be) => be,
Err(e) => {
error!("Failed to setup BE: {:?}", e);
return;
}
};
let schema_mem = match Schema::new(&mut audit) {
Ok(sc) => sc,
Err(e) => {
error!("Failed to setup in memory schema: {:?}", e);
return;
}
};
let server = QueryServer::new(be, schema_mem);
let r = server.verify(&mut audit);
debug!("{}", audit);
if r.is_empty() {
info!("Verification passed!");
std::process::exit(0);
} else {
for er in r {
error!("{:?}", er);
}
std::process::exit(1);
}
}
pub fn recover_account_core(config: Configuration, name: String, password: String) {
let mut audit = AuditScope::new("recover_account");
let be = match setup_backend(&config) {
Ok(be) => be,
Err(e) => {
error!("Failed to setup BE: {:?}", e);
return;
}
};
let server_id = be.get_db_sid();
let (_qs, idms) = match setup_qs_idms(&mut audit, be, server_id) {
Ok(t) => t,
Err(e) => {
debug!("{}", audit);
error!("Unable to setup query server or idm server -> {:?}", e);
return;
}
};
let mut idms_prox_write = idms.proxy_write();
match idms_prox_write.recover_account(&mut audit, name, password) {
Ok(_) => {
idms_prox_write
.commit(&mut audit)
.expect("A critical error during commit occured.");
debug!("{}", audit);
info!("Password reset!");
}
Err(e) => {
error!("Error during password reset -> {:?}", e);
debug!("{}", audit);
std::mem::drop(idms_prox_write);
std::process::exit(1);
}
};
}
pub fn create_server_core(config: Configuration) {
if config.integration_test_config.is_some() {
warn!("RUNNING IN INTEGRATION TEST MODE.");
warn!("IF YOU SEE THIS IN PRODUCTION YOU MUST CONTACT SUPPORT IMMEDIATELY.");
}
info!("Starting kanidm with configuration: {}", config);
let log_addr = async_log::start();
let opt_tls_params = match setup_tls(&config) {
Ok(opt_tls_params) => opt_tls_params,
Err(e) => {
error!("Failed to configure TLS parameters -> {:?}", e);
return;
}
};
let be = match setup_backend(&config) {
Ok(be) => be,
Err(e) => {
error!("Failed to setup BE -> {:?}", e);
return;
}
};
let server_id = be.get_db_sid();
info!("Server ID -> {:?}", server_id);
let mut audit = AuditScope::new("setup_qs_idms");
let (qs, idms) = match setup_qs_idms(&mut audit, be, server_id) {
Ok(t) => t,
Err(e) => {
debug!("{}", audit);
error!("Unable to setup query server or idm server -> {:?}", e);
return;
}
};
match &config.integration_test_config {
Some(itc) => {
let mut idms_prox_write = idms.proxy_write();
match idms_prox_write.recover_account(
&mut audit,
"admin".to_string(),
itc.admin_password.clone(),
) {
Ok(_) => {}
Err(e) => {
debug!("{}", audit);
error!(
"Unable to configure INTERGATION TEST admin account -> {:?}",
e
);
return;
}
};
match idms_prox_write.commit(&mut audit) {
Ok(_) => {}
Err(e) => {
debug!("{}", audit);
error!("Unable to commit INTERGATION TEST setup -> {:?}", e);
return;
}
}
}
None => {}
}
log_addr.do_send(audit);
let idms_arc = Arc::new(idms);
let server_read_addr = QueryServerReadV1::start(
log_addr.clone(),
qs.clone(),
idms_arc.clone(),
config.threads,
);
let server_write_addr = QueryServerWriteV1::start(log_addr, qs, idms_arc);
let _int_addr = IntervalActor::new(server_write_addr.clone()).start();
let max_size = config.maximum_request;
let secure_cookies = config.secure_cookies;
let cookie_key: [u8; 32] = config.cookie_key;
let aws_builder = actix_web::server::new(move || {
App::with_state(AppState {
qe_r: server_read_addr.clone(),
qe_w: server_write_addr.clone(),
max_size,
})
.middleware(middleware::Logger::default())
.middleware(session::SessionStorage::new(
session::CookieSessionBackend::signed(&cookie_key)
.max_age(Duration::hours(1))
.same_site(cookie::SameSite::Strict)
.http_only(false)
.name("kanidm-session")
.secure(secure_cookies),
))
.resource("/v1/raw/create", |r| {
r.method(http::Method::POST).with_async(create)
})
.resource("/v1/raw/modify", |r| {
r.method(http::Method::POST).with_async(modify)
})
.resource("/v1/raw/delete", |r| {
r.method(http::Method::POST).with_async(delete)
})
.resource("/v1/raw/search", |r| {
r.method(http::Method::POST).with_async(search)
})
.resource("/v1/auth", |r| {
r.method(http::Method::POST).with_async(auth)
})
.resource("/v1/schema", |r| {
r.method(http::Method::GET).with_async(schema_get)
})
.resource("/v1/schema/attributetype", |r| {
r.method(http::Method::GET)
.with_async(schema_attributetype_get)
})
.resource("/v1/schema/attributetype", |r| {
r.method(http::Method::POST).with(do_nothing)
})
.resource("/v1/schema/attributetype/{id}", |r| {
r.method(http::Method::GET)
.with_async(schema_attributetype_get_id)
})
.resource("/v1/schema/attributetype/{id}", |r| {
r.method(http::Method::PUT).with(do_nothing)
})
.resource("/v1/schema/attributetype/{id}", |r| {
r.method(http::Method::PATCH).with(do_nothing)
})
.resource("/v1/schema/classtype", |r| {
r.method(http::Method::GET).with_async(schema_classtype_get)
})
.resource("/v1/schema/classtype", |r| {
r.method(http::Method::POST).with(do_nothing)
})
.resource("/v1/schema/classtype/{id}", |r| {
r.method(http::Method::GET)
.with_async(schema_classtype_get_id)
})
.resource("/v1/schema/classtype/{id}", |r| {
r.method(http::Method::PUT).with(do_nothing)
})
.resource("/v1/schema/classtype/{id}", |r| {
r.method(http::Method::PATCH).with(do_nothing)
})
.resource("/v1/self", |r| {
r.method(http::Method::GET).with_async(whoami)
})
.resource("/v1/self/_attr/{attr}", |r| {
r.method(http::Method::GET).with(do_nothing)
})
.resource("/v1/self/_credential", |r| {
r.method(http::Method::GET).with(do_nothing)
})
.resource("/v1/self/_credential/primary/set_password", |r| {
r.method(http::Method::POST)
.with_async(idm_account_set_password)
})
.resource("/v1/self/_credential/{cid}/_lock", |r| {
r.method(http::Method::GET).with(do_nothing)
})
.resource("/v1/self/_radius", |r| {
r.method(http::Method::GET).with(do_nothing)
})
.resource("/v1/self/_radius", |r| {
r.method(http::Method::DELETE).with(do_nothing)
})
.resource("/v1/self/_radius", |r| {
r.method(http::Method::POST).with(do_nothing)
})
.resource("/v1/self/_radius/_config", |r| {
r.method(http::Method::POST).with(do_nothing)
})
.resource("/v1/self/_radius/_config/{secret_otp}", |r| {
r.method(http::Method::GET).with(do_nothing)
})
.resource("/v1/self/_radius/_config/{secret_otp}/apple", |r| {
r.method(http::Method::GET).with(do_nothing)
})
.resource("/v1/account", |r| {
r.method(http::Method::GET).with_async(account_get);
r.method(http::Method::POST).with_async(account_post);
})
.resource("/v1/account/{id}", |r| {
r.method(http::Method::GET).with_async(account_id_get);
r.method(http::Method::DELETE).with_async(account_id_delete);
})
.resource("/v1/account/{id}/_attr/{attr}", |r| {
r.method(http::Method::GET).with_async(account_id_get_attr);
r.method(http::Method::POST)
.with_async(account_id_post_attr);
r.method(http::Method::PUT).with_async(account_id_put_attr);
r.method(http::Method::DELETE)
.with_async(account_id_delete_attr);
})
.resource("/v1/account/{id}/_lock", |r| {
r.method(http::Method::GET).with(do_nothing)
})
.resource("/v1/account/{id}/_credential", |r| {
r.method(http::Method::GET).with(do_nothing)
})
.resource("/v1/account/{id}/_credential/primary", |r| {
r.method(http::Method::PUT)
.with_async(account_put_id_credential_primary)
})
.resource("/v1/account/{id}/_credential/{cid}/_lock", |r| {
r.method(http::Method::GET).with(do_nothing)
})
.resource("/v1/account/{id}/_ssh_pubkeys", |r| {
r.method(http::Method::GET)
.with_async(account_get_id_ssh_pubkeys);
r.method(http::Method::POST)
.with_async(account_post_id_ssh_pubkey);
})
.resource("/v1/account/{id}/_ssh_pubkeys/{tag}", |r| {
r.method(http::Method::GET)
.with_async(account_get_id_ssh_pubkey_tag);
r.method(http::Method::DELETE)
.with_async(account_delete_id_ssh_pubkey_tag);
})
.resource("/v1/account/{id}/_radius", |r| {
r.method(http::Method::GET)
.with_async(account_get_id_radius);
r.method(http::Method::POST)
.with_async(account_post_id_radius_regenerate);
r.method(http::Method::DELETE)
.with_async(account_delete_id_radius);
})
.resource("/v1/account/{id}/_radius/_token", |r| {
r.method(http::Method::GET)
.with_async(account_get_id_radius_token)
})
.resource("/v1/account/{id}/_unix", |r| {
r.method(http::Method::POST)
.with_async(account_post_id_unix);
})
.resource("/v1/account/{id}/_unix/_token", |r| {
r.method(http::Method::GET)
.with_async(account_get_id_unix_token)
})
.resource("/v1/group", |r| {
r.method(http::Method::GET).with_async(group_get);
r.method(http::Method::POST).with_async(group_post);
})
.resource("/v1/group/{id}", |r| {
r.method(http::Method::GET).with_async(group_id_get);
r.method(http::Method::DELETE).with_async(group_id_delete);
})
.resource("/v1/group/{id}/_attr/{attr}", |r| {
r.method(http::Method::GET).with_async(group_id_get_attr);
r.method(http::Method::POST).with_async(group_id_post_attr);
r.method(http::Method::PUT).with_async(group_id_put_attr);
r.method(http::Method::DELETE)
.with_async(group_id_delete_attr);
})
.resource("/v1/group/{id}/_unix", |r| {
r.method(http::Method::POST).with_async(group_post_id_unix);
})
.resource("/v1/group/{id}/_unix/_token", |r| {
r.method(http::Method::GET)
.with_async(group_get_id_unix_token)
})
.resource("/v1/domain", |r| {
r.method(http::Method::GET).with_async(domain_get);
})
.resource("/v1/domain/{id}", |r| {
r.method(http::Method::GET).with_async(domain_id_get);
})
.resource("/v1/domain/{id}/_attr/{attr}", |r| {
r.method(http::Method::GET).with_async(domain_id_get_attr);
r.method(http::Method::PUT).with_async(domain_id_put_attr);
})
.resource("/v1/recycle_bin", |r| {
r.method(http::Method::GET).with(do_nothing)
})
.resource("/v1/recycle_bin/{id}", |r| {
r.method(http::Method::GET).with(do_nothing)
})
.resource("/v1/recycle_bin/{id}/_restore", |r| {
r.method(http::Method::POST).with(do_nothing)
})
.resource("/v1/access_profile", |r| {
r.method(http::Method::GET).with(do_nothing)
})
.resource("/v1/access_profile/{id}", |r| {
r.method(http::Method::GET).with(do_nothing)
})
.resource("/v1/access_profile/{id}/_attr/{attr}", |r| {
r.method(http::Method::GET).with(do_nothing)
})
});
let tls_aws_builder = match opt_tls_params {
Some(tls_params) => aws_builder.bind_ssl(config.address, tls_params),
None => {
warn!("Starting WITHOUT TLS parameters. This may cause authentication to fail!");
aws_builder.bind(config.address)
}
};
tls_aws_builder
.expect("Failed to initialise server!")
.start();
}