// Each entry in the ConfigStore has the base64-encoded value of username:password as its key.
// To generate a key from username:password pairs, on a bash shell you should be able to do:
// echo -n "alice:secret" | base64
use base64::engine::{general_purpose::STANDARD, Engine as _};
use fastly::http::{header, StatusCode};
use fastly::{mime, ConfigStore, Error, Request, Response};
use lazy_static::lazy_static;
use regex::Regex;

#[fastly::main]
fn main(mut req: Request) -> Result<Response, Error> {
    let username_password = ConfigStore::open("username_password");

    if let Some(credential) = req
        .get_header(header::AUTHORIZATION)
        .and_then(|header| header.to_str().ok())
        .and_then(get_credential)
        .filter(|credential| username_password.contains(credential))
    {
        // Decode the credential so we can pull the user name out.
        let bytes = STANDARD.decode(credential)?;
        let decoded = std::str::from_utf8(&bytes)?;
        let username = get_username(decoded).unwrap();
        req.remove_header(header::AUTHORIZATION);
        req.set_header("authorized-user", &username);
        println!("Access granted for user {}", username);
        return Ok(req.send("origin_0")?);
    }

    let body = r#"
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
    <head>
        <title>Error</title>
        <meta HTTP-EQUIV='Content-Type' CONTENT='text/html;'>
    </head>
    <body><h1>401 Unauthorized (Fastly)</h1></body>
</html>"#;

    Ok(Response::from_body(body)
        .with_status(StatusCode::UNAUTHORIZED)
        .with_content_type(mime::TEXT_HTML_UTF_8)
        .with_header(header::WWW_AUTHENTICATE, "Basic realm=MYREALM"))
}

fn get_credential(input: &str) -> Option<String> {
    lazy_static! {
        static ref RE: Regex = Regex::new(r"Basic (?P<credential>.*)$").unwrap();
    }
    RE.captures(input).and_then(|cap| {
        cap.name("credential")
            .map(|credential| credential.as_str().to_owned())
    })
}

fn get_username(input: &str) -> Option<String> {
    lazy_static! {
        static ref RE: Regex = Regex::new(r"^(?P<username>.+?):.*$").unwrap();
    }
    RE.captures(input).and_then(|cap| {
        cap.name("username")
            .map(|username| username.as_str().to_owned())
    })
}