2020-11-16 13:37:22 +00:00
|
|
|
use std::fmt;
|
|
|
|
|
|
|
|
use anyhow::{bail, Error};
|
|
|
|
use serde::{Deserialize, Serialize};
|
|
|
|
|
|
|
|
use crate::api2::types::Userid;
|
|
|
|
use crate::config::tfa;
|
|
|
|
|
|
|
|
#[derive(Deserialize, Serialize)]
|
|
|
|
#[serde(deny_unknown_fields)]
|
|
|
|
pub struct PartialTicket {
|
|
|
|
#[serde(rename = "u")]
|
|
|
|
userid: Userid,
|
|
|
|
|
|
|
|
#[serde(rename = "c")]
|
|
|
|
challenge: tfa::TfaChallenge,
|
|
|
|
}
|
|
|
|
|
|
|
|
/// A new ticket struct used in rest.rs's `check_auth` - mostly for better errors than failing to
|
|
|
|
/// parse the userid ticket content.
|
|
|
|
pub enum ApiTicket {
|
|
|
|
Full(Userid),
|
|
|
|
Partial(tfa::TfaChallenge),
|
|
|
|
}
|
|
|
|
|
|
|
|
impl ApiTicket {
|
|
|
|
/// Require the ticket to be a full ticket, otherwise error with a meaningful error message.
|
|
|
|
pub fn require_full(self) -> Result<Userid, Error> {
|
|
|
|
match self {
|
|
|
|
ApiTicket::Full(userid) => Ok(userid),
|
|
|
|
ApiTicket::Partial(_) => bail!("access denied - second login factor required"),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Expect the ticket to contain a tfa challenge, otherwise error with a meaningful error
|
|
|
|
/// message.
|
|
|
|
pub fn require_partial(self) -> Result<tfa::TfaChallenge, Error> {
|
|
|
|
match self {
|
|
|
|
ApiTicket::Full(_) => bail!("invalid tfa challenge"),
|
|
|
|
ApiTicket::Partial(challenge) => Ok(challenge),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Create a new full ticket.
|
|
|
|
pub fn full(userid: Userid) -> Self {
|
|
|
|
ApiTicket::Full(userid)
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Create a new partial ticket.
|
|
|
|
pub fn partial(challenge: tfa::TfaChallenge) -> Self {
|
|
|
|
ApiTicket::Partial(challenge)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl fmt::Display for ApiTicket {
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
|
|
match self {
|
|
|
|
ApiTicket::Full(userid) => fmt::Display::fmt(userid, f),
|
|
|
|
ApiTicket::Partial(partial) => {
|
|
|
|
let data = serde_json::to_string(partial).map_err(|_| fmt::Error)?;
|
|
|
|
write!(f, "!tfa!{}", data)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl std::str::FromStr for ApiTicket {
|
|
|
|
type Err = Error;
|
|
|
|
|
|
|
|
fn from_str(s: &str) -> Result<Self, Error> {
|
2021-01-18 12:50:28 +00:00
|
|
|
if let Some(tfa_ticket) = s.strip_prefix("!tfa!") {
|
|
|
|
Ok(ApiTicket::Partial(serde_json::from_str(tfa_ticket)?))
|
2020-11-16 13:37:22 +00:00
|
|
|
} else {
|
|
|
|
Ok(ApiTicket::Full(s.parse()?))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|