use capnp::capability::Promise; use capnp::Error; use capnp_rpc::pry; use rsasl::property::AuthId; use rsasl::session::{Session, Step}; use std::io::Cursor; use crate::capnp::session::APISession; use crate::session::SessionManager; use api::authenticationsystem_capnp::authentication::{ AbortParams, AbortResults, Server as AuthenticationSystem, StepParams, StepResults, }; use api::authenticationsystem_capnp::{response, response::Error as ErrorCode}; pub struct Authentication { state: State, } impl Authentication { pub fn new(session: Session, sessionmanager: SessionManager) -> Self { Self { state: State::Running(session, sessionmanager), } } pub fn invalid_mechanism() -> Self { Self { state: State::InvalidMechanism, } } fn build_error(&self, response: response::Builder) { if let State::Running(_, _) = self.state { return; } let mut builder = response.init_failed(); match self.state { State::InvalidMechanism => builder.set_code(ErrorCode::BadMechanism), State::Finished => builder.set_code(ErrorCode::Aborted), State::Aborted => builder.set_code(ErrorCode::Aborted), _ => unreachable!(), } } } enum State { InvalidMechanism, Finished, Aborted, Running(Session, SessionManager), } impl AuthenticationSystem for Authentication { fn step(&mut self, params: StepParams, mut results: StepResults) -> Promise<(), Error> { let span = tracing::trace_span!("step"); let _guard = span.enter(); let mut builder = results.get(); if let State::Running(mut session, manager) = std::mem::replace(&mut self.state, State::Aborted) { let data: &[u8] = pry!(pry!(params.get()).get_data()); let mut out = Cursor::new(Vec::new()); match session.step(Some(data), &mut out) { Ok(Step::Done(data)) => { self.state = State::Finished; let uid = pry!(session.get_property::().ok_or(capnp::Error::failed( "Authentication didn't provide an authid as required".to_string() ))); let session = pry!(manager.open(uid.as_ref()).ok_or(capnp::Error::failed( "Failed to lookup the given user".to_string() ))); let mut builder = builder.init_successful(); if data.is_some() { builder.set_additional_data(out.into_inner().as_slice()); } APISession::build(session, builder) } Ok(Step::NeedsMore(_)) => { self.state = State::Running(session, manager); builder.set_challenge(out.into_inner().as_slice()); } Err(_) => { self.state = State::Aborted; self.build_error(builder); } } } else { self.build_error(builder); } Promise::ok(()) } fn abort(&mut self, _: AbortParams, _: AbortResults) -> Promise<(), Error> { self.state = State::Aborted; Promise::ok(()) } }