You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

86 lines
2.4 KiB

use hex::FromHexError;
use rocket::http::Status;
use rocket::outcome::{try_outcome, IntoOutcome};
use rocket::request::{FromRequest, Outcome};
use rocket::{Request, State};
use rocket_basicauth::{BasicAuth, BasicAuthError};
use std::fmt;
use std::str::FromStr;
/// A request guard that can be included in handler definitions to enforce authentication.
pub struct Authenticated {}
pub const MAKER_USERNAME: &str = "maker";
#[derive(Debug)]
pub enum Error {
UnknownUser(String),
BadPassword,
InvalidEncoding(FromHexError),
BadBasicAuthHeader(BasicAuthError),
/// The auth password was not configured in Rocket's state.
MissingPassword,
NoAuthHeader,
}
#[derive(PartialEq)]
pub struct Password([u8; 32]);
impl From<[u8; 32]> for Password {
fn from(bytes: [u8; 32]) -> Self {
Self(bytes)
}
}
impl fmt::Display for Password {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", hex::encode(self.0))
}
}
impl FromStr for Password {
type Err = FromHexError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut bytes = [0u8; 32];
hex::decode_to_slice(s, &mut bytes)?;
Ok(Self(bytes))
}
}
#[rocket::async_trait]
impl<'r> FromRequest<'r> for Authenticated {
type Error = Error;
async fn from_request(req: &'r Request<'_>) -> Outcome<Self, Self::Error> {
let basic_auth = try_outcome!(req
.guard::<BasicAuth>()
.await
.map_failure(|(status, error)| (status, Error::BadBasicAuthHeader(error)))
.forward_then(|()| Outcome::Failure((Status::Unauthorized, Error::NoAuthHeader))));
let password = try_outcome!(req
.guard::<&'r State<Password>>()
.await
.map_failure(|(status, _)| (status, Error::MissingPassword)));
if basic_auth.username != MAKER_USERNAME {
return Outcome::Failure((
Status::Unauthorized,
Error::UnknownUser(basic_auth.username),
));
}
if &try_outcome!(basic_auth
.password
.parse::<Password>()
.map_err(Error::InvalidEncoding)
.into_outcome(Status::BadRequest))
!= password.inner()
{
return Outcome::Failure((Status::Unauthorized, Error::BadPassword));
}
Outcome::Success(Authenticated {})
}
}