2020-11-13 19:20:59 +00:00
|
|
|
use anyhow::*;
|
2020-11-19 07:54:45 +00:00
|
|
|
use futures_core::future::BoxFuture;
|
|
|
|
use futures_util::FutureExt;
|
2020-11-14 02:56:50 +00:00
|
|
|
use log::LevelFilter;
|
2020-11-13 19:20:59 +00:00
|
|
|
use tokio::sync::RwLock;
|
2020-11-13 23:48:50 +00:00
|
|
|
use northstar::{Certificate, GEMINI_MIME, GEMINI_PORT, Request, Response, Server};
|
2020-11-13 19:20:59 +00:00
|
|
|
use std::collections::HashMap;
|
|
|
|
use std::sync::Arc;
|
|
|
|
|
|
|
|
// Workaround for Certificates not being hashable
|
|
|
|
type CertBytes = Vec<u8>;
|
|
|
|
|
|
|
|
#[tokio::main]
|
|
|
|
async fn main() -> Result<()> {
|
2020-11-14 02:56:50 +00:00
|
|
|
env_logger::builder()
|
|
|
|
.filter_module("northstar", LevelFilter::Debug)
|
|
|
|
.init();
|
|
|
|
|
2020-11-13 19:20:59 +00:00
|
|
|
let users = Arc::<RwLock::<HashMap<CertBytes, String>>>::default();
|
|
|
|
|
|
|
|
Server::bind(("0.0.0.0", GEMINI_PORT))
|
2020-11-13 20:04:25 +00:00
|
|
|
.serve(move|req| handle_request(users.clone(), req))
|
2020-11-13 19:20:59 +00:00
|
|
|
.await
|
|
|
|
}
|
|
|
|
|
|
|
|
/// An ultra-simple demonstration of simple authentication.
|
|
|
|
///
|
|
|
|
/// If the user attempts to connect, they will be prompted to create a client certificate.
|
|
|
|
/// Once they've made one, they'll be given the opportunity to create an account by
|
|
|
|
/// selecting a username. They'll then get a message confirming their account creation.
|
|
|
|
/// Any time this user visits the site in the future, they'll get a personalized welcome
|
|
|
|
/// message.
|
2020-11-13 20:04:25 +00:00
|
|
|
fn handle_request(users: Arc<RwLock<HashMap<CertBytes, String>>>, request: Request) -> BoxFuture<'static, Result<Response>> {
|
2020-11-13 19:20:59 +00:00
|
|
|
async move {
|
2020-11-13 20:04:25 +00:00
|
|
|
if let Some(Certificate(cert_bytes)) = request.certificate() {
|
2020-11-13 19:20:59 +00:00
|
|
|
// The user provided a certificate
|
|
|
|
let users_read = users.read().await;
|
2020-11-13 20:04:25 +00:00
|
|
|
if let Some(user) = users_read.get(cert_bytes) {
|
2020-11-13 19:20:59 +00:00
|
|
|
// The user has already registered
|
|
|
|
Ok(
|
2020-11-18 02:41:18 +00:00
|
|
|
Response::success_with_body(
|
|
|
|
&GEMINI_MIME,
|
|
|
|
format!("Welcome {}!", user)
|
|
|
|
)
|
2020-11-13 19:20:59 +00:00
|
|
|
)
|
|
|
|
} else {
|
|
|
|
// The user still needs to register
|
|
|
|
drop(users_read);
|
|
|
|
if let Some(query_part) = request.uri().query() {
|
|
|
|
// The user provided some input (a username request)
|
|
|
|
let username = query_part.as_str();
|
|
|
|
let mut users_write = users.write().await;
|
2020-11-13 20:04:25 +00:00
|
|
|
users_write.insert(cert_bytes.clone(), username.to_owned());
|
2020-11-13 19:20:59 +00:00
|
|
|
Ok(
|
2020-11-18 02:41:18 +00:00
|
|
|
Response::success_with_body(
|
|
|
|
&GEMINI_MIME,
|
|
|
|
format!(
|
2020-11-13 19:20:59 +00:00
|
|
|
"Your account has been created {}! Welcome!",
|
|
|
|
username
|
2020-11-18 02:41:18 +00:00
|
|
|
)
|
|
|
|
)
|
2020-11-13 19:20:59 +00:00
|
|
|
)
|
|
|
|
} else {
|
|
|
|
// The user didn't provide input, and should be prompted
|
|
|
|
Response::input("What username would you like?")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// The user didn't provide a certificate
|
2020-11-14 00:55:47 +00:00
|
|
|
Ok(Response::client_certificate_required())
|
2020-11-13 19:20:59 +00:00
|
|
|
}
|
|
|
|
}.boxed()
|
|
|
|
}
|