Add basic cookie tracking to auth endpoint

This commit is contained in:
William Brown 2019-01-31 14:29:14 +10:00
parent 63e600d49e
commit 0c9a77c39b
5 changed files with 190 additions and 56 deletions

View file

@ -11,10 +11,10 @@ use futures::{future, Future, Stream};
use super::config::Configuration;
// SearchResult
use super::event::{CreateEvent, SearchEvent};
use super::event::{CreateEvent, SearchEvent, AuthEvent};
use super::filter::Filter;
use super::log;
use super::proto_v1::{CreateRequest, SearchRequest};
use super::proto_v1::{CreateRequest, SearchRequest, AuthRequest, AuthResponse};
use super::server;
struct AppState {
@ -85,43 +85,6 @@ macro_rules! json_event_decode {
// Handle the various end points we need to expose
/// simple handle
fn index(req: &HttpRequest<AppState>) -> HttpResponse {
println!("{:?}", req);
HttpResponse::Ok().body("Hello\n")
}
fn class_list((_name, state): (Path<String>, State<AppState>)) -> FutureResponse<HttpResponse> {
// println!("request to class_list");
let filt = Filter::Pres(String::from("objectclass"));
state
.qe
.send(
// This is where we need to parse the request into an event
// LONG TERM
// Make a search REQUEST, and create the audit struct here, then
// pass it to the server
//
// FIXME: Don't use SEARCHEVENT here!!!!
//
SearchEvent::from_request(SearchRequest::new(filt)),
)
// TODO: How to time this part of the code?
// What does this do?
.from_err()
.and_then(|res| match res {
// What type is entry?
Ok(search_result) => Ok(HttpResponse::Ok().json(search_result.response())),
// Ok(_) => Ok(HttpResponse::Ok().into()),
// Can we properly report this?
Err(_) => Ok(HttpResponse::InternalServerError().into()),
})
// What does this do?
.responder()
}
fn create(
(req, state): (HttpRequest<AppState>, State<AppState>),
) -> impl Future<Item = HttpResponse, Error = Error> {
@ -134,6 +97,78 @@ fn search(
json_event_decode!(req, state, SearchEvent, SearchResponse, SearchRequest)
}
// delete, modify
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| {
// limit max size of in-memory payload
if (body.len() + chunk.len()) > max_size {
Err(error::ErrorBadRequest("overflow"))
} else {
body.extend_from_slice(&chunk);
Ok(body)
}
})
.and_then(
move |body| -> Box<Future<Item = HttpResponse, Error = Error>> {
let r_obj = serde_json::from_slice::<AuthRequest>(&body);
// Send to the db for action
match r_obj {
Ok(obj) => {
// First, deal with some state management.
// Do anything here first that's needed like getting the session details
// out of the req cookie.
let mut counter = 1;
// TODO: Make this NOT UNWRAP. From the actix source unwrap here
// seems to be related to the serde_json deserialise of the cookie
// content, and because we control it's get/set it SHOULD be find
// provided we use secure cookies. But we can't always trust that ...
if let Some(count) = req.session().get::<i32>("counter").unwrap() {
println!("SESSION value: {}", count);
counter = count + 1;
req.session().set("counter", counter).unwrap();
} else {
println!("INIT value: {}", counter);
req.session().set("counter", counter).unwrap();
};
// We probably need to know if we allocate the cookie, that this is a
// new session, and in that case, anything *except* authrequest init is
// invalid.
let res = state
.qe
.send(
AuthEvent::from_request(obj),
)
.from_err()
.and_then(|res| match res {
Ok(event_result) => {
Ok(HttpResponse::Ok().json(event_result.response()))
}
Err(e) => Ok(HttpResponse::InternalServerError().json(e)),
});
Box::new(res)
}
Err(e) => Box::new(future::err(error::ErrorBadRequest(format!(
"Json Decode Failed: {:?}",
e
)))),
}
},
)
}
fn whoami(req: &HttpRequest<AppState>) -> Result<&'static str> {
println!("{:?}", req);
@ -190,9 +225,10 @@ pub fn create_server_core(config: Configuration) {
.http_only(true)
.name("rsidm-session")
// This forces https only
// TODO: Make this a config value
.secure(false),
))
.resource("/", |r| r.f(index))
// .resource("/", |r| r.f(index))
// curl --header ...?
.resource("/v1/whoami", |r| r.f(whoami))
// .resource("/v1/login", ...)
@ -209,10 +245,18 @@ pub fn create_server_core(config: Configuration) {
.resource("/v1/search", |r| {
r.method(http::Method::POST).with_async(search)
})
// This is one of the times we need cookies :)
// curl -b /tmp/cookie.jar -c /tmp/cookie.jar --header "Content-Type: application/json" --request POST --data '{ "state" : { "Init": ["Anonymous", []] }}' http://127.0.0.1:8080/v1/auth
.resource("/v1/auth", |r| {
r.method(http::Method::POST).with_async(auth)
})
// Add an ldap compat search function type?
/*
.resource("/v1/list/{class_list}", |r| {
r.method(http::Method::GET).with(class_list)
})
*/
})
.bind(config.address)
.unwrap()

View file

@ -1,6 +1,6 @@
use super::filter::Filter;
use super::proto_v1::Entry as ProtoEntry;
use super::proto_v1::{CreateRequest, Response, SearchRequest, SearchResponse};
use super::proto_v1::{CreateRequest, Response, SearchRequest, SearchResponse, AuthRequest, AuthResponse, AuthStatus};
use actix::prelude::*;
use entry::{Entry, EntryCommitted, EntryInvalid, EntryNew, EntryValid};
use error::OperationError;
@ -185,3 +185,29 @@ impl ModifyEvent {
}
}
}
#[derive(Debug)]
pub struct AuthEvent {
}
impl Message for AuthEvent {
type Result = Result<AuthResult, OperationError>;
}
impl AuthEvent {
pub fn from_request(request: AuthRequest) -> Self {
AuthEvent {}
}
}
pub struct AuthResult {
}
impl AuthResult {
pub fn response(self) -> AuthResponse {
AuthResponse {
status: AuthStatus::Begin(String::from("hello"))
}
}
}

29
src/lib/modify.rs Normal file
View file

@ -0,0 +1,29 @@
#[derive(Serialize, Deserialize, Debug)]
pub enum Modify {
// This value *should* exist.
Present(String, String),
// This value *should not* exist.
Removed(String, String),
// This attr *should not* exist.
Purged(String),
}
#[derive(Serialize, Deserialize, Debug)]
pub struct ModifyList {
// And ordered list of changes to apply. Should this be state based?
pub mods: Vec<Modify>,
}
impl ModifyList {
pub fn new() -> Self {
ModifyList { mods: Vec::new() }
}
pub fn new_list(mods: Vec<Modify>) -> Self {
ModifyList { mods: mods }
}
pub fn push_mod(&mut self, modify: Modify) {
self.mods.push(modify)
}
}

View file

@ -76,22 +76,41 @@ impl CreateRequest {
// On loginSuccess, we send a cookie, and that allows the token to be
// generated. The cookie can be shared between servers.
// Request auth for identity X
pub struct AuthRequest {}
#[derive(Debug, Serialize, Deserialize)]
pub enum AuthState {
Init(String, Vec<String>),
/*
Step(
Type(params ....)
),
*/
}
// Request auth for identity X with roles Y?
#[derive(Debug, Serialize, Deserialize)]
pub struct AuthRequest {
pub state: AuthState
}
// Respond with the list of auth types and nonce, etc.
pub struct AuthResponse {}
// It can also contain a denied, or success.
#[derive(Debug, Serialize, Deserialize)]
pub enum AuthStatus {
Begin(String), // uuid of this session.
// Continue, // Keep going, here are the things you could still provide ...
// Go away, you made a mistake somewhere.
// Provide reason?
// Denied(String),
// Welcome friend.
// On success provide entry "self", for group assertions?
// We also provide the "cookie"/token?
// Success(String, Entry),
}
// Provide responses
pub struct AuthProvide {}
#[derive(Debug, Serialize, Deserialize)]
pub struct AuthResponse {
pub status: AuthStatus,
}
// After authprovide, we can move to AuthResponse (for more)
// or below ...
// Go away.
// Provide reason?
pub struct AuthDenied {}
// Welcome friend.
// On success provide entry "self", for group assertions?
pub struct AuthSuccess {}

View file

@ -14,6 +14,7 @@ use entry::{Entry, EntryCommitted, EntryInvalid, EntryNew, EntryValid};
use error::{OperationError, SchemaError};
use event::{
CreateEvent, DeleteEvent, ExistsEvent, ModifyEvent, OpResult, SearchEvent, SearchResult,
AuthEvent, AuthResult,
};
use filter::Filter;
use log::EventLog;
@ -695,6 +696,21 @@ impl Handler<CreateEvent> for QueryServer {
}
}
impl Handler<AuthEvent> for QueryServer {
type Result = Result<AuthResult, OperationError>;
fn handle(&mut self, msg: AuthEvent, _: &mut Self::Context) -> Self::Result {
let mut audit = AuditScope::new("auth");
let res = audit_segment!(&mut audit, || {
audit_log!(audit, "Begin auth event {:?}", msg);
Err(OperationError::InvalidState)
});
// At the end of the event we send it for logging.
self.log.do_send(audit);
res
}
}
// Auth requests? How do we structure these ...
#[cfg(test)]