🚧 working on one-click registration.

also removed many un-needed `Arc` wrappers
main
_ 2021-05-02 21:59:04 +00:00
parent a4ec4878ea
commit ebd1026903
7 changed files with 113 additions and 75 deletions

View File

@ -2,7 +2,9 @@
#![allow (clippy::redundant_closure)] #![allow (clippy::redundant_closure)]
use std::{ use std::{
collections::HashMap, collections::{
HashMap,
},
convert::{TryFrom}, convert::{TryFrom},
net::IpAddr, net::IpAddr,
path::Path, path::Path,
@ -23,6 +25,7 @@ use crate::{
pub mod machine_editable { pub mod machine_editable {
use std::{ use std::{
collections::BTreeMap,
path::Path, path::Path,
}; };
@ -30,22 +33,25 @@ pub mod machine_editable {
use super::file::Server; use super::file::Server;
#[derive (Default, Deserialize, Serialize)] #[derive (Deserialize, Serialize)]
pub struct Config { pub struct ConfigFile {
pub servers: Vec <Server>, pub servers: Vec <Server>,
pub debug_toggle: bool,
} }
impl Config { #[derive (Default)]
pub struct Config {
pub servers: BTreeMap <String, Server>,
}
impl ConfigFile {
pub fn from_file (path: &Path) -> Result <Self, crate::ConfigError> pub fn from_file (path: &Path) -> Result <Self, crate::ConfigError>
{ {
let config_s = std::fs::read_to_string (path)?; let config_s = std::fs::read_to_string (path)?;
let new_config: Config = toml::from_str (&config_s)?; Ok (toml::from_str (&config_s)?)
Ok (new_config)
} }
pub async fn save (&self, path: &Path) -> Result <(), crate::ConfigError> { pub async fn save (&self, path: &Path) -> Result <(), crate::ConfigError>
{
let s = toml::to_string (self)?; let s = toml::to_string (self)?;
// This is way easier in C++ but also not safe // This is way easier in C++ but also not safe
let mut temp_path = path.file_name ().unwrap ().to_os_string (); let mut temp_path = path.file_name ().unwrap ().to_os_string ();
@ -56,6 +62,33 @@ pub mod machine_editable {
Ok (()) Ok (())
} }
} }
impl Config {
pub fn from_file (path: &Path) -> Result <Self, crate::ConfigError>
{
let c = ConfigFile::from_file (path)?;
let servers = c.servers.into_iter ()
.map (|s| (s.name.clone (), s))
.collect ();
Ok (Self {
servers,
})
}
pub async fn save (&self, path: &Path) -> Result <(), crate::ConfigError>
{
let servers = self.servers.values ()
.cloned ().into_iter ().collect ();
let c = ConfigFile {
servers,
};
c.save (path).await?;
Ok (())
}
}
} }
/// Config fields as they are loaded from the config file /// Config fields as they are loaded from the config file
@ -69,7 +102,7 @@ pub mod file {
Valid30Days, Valid30Days,
}; };
#[derive (Deserialize, Serialize)] #[derive (Clone, Debug, Deserialize, Serialize)]
pub struct Server { pub struct Server {
/// This is duplicated in the hashmap, but it's not a problem /// This is duplicated in the hashmap, but it's not a problem
pub name: String, pub name: String,

View File

@ -114,7 +114,7 @@ fn get_user_name (req: &http::request::Parts)
async fn handle_http_request ( async fn handle_http_request (
req: http::request::Parts, req: http::request::Parts,
uri: String, uri: String,
state: Arc <Relay>, state: &Relay,
server_name: &str server_name: &str
) )
-> Result <Response <Body>, RequestError> -> Result <Response <Body>, RequestError>
@ -300,7 +300,7 @@ struct AuditEntryPretty {
data_pretty: String, data_pretty: String,
} }
async fn handle_server_list_internal (state: &Arc <Relay>) async fn handle_server_list_internal (state: &Relay)
-> ServerListPage <'static> -> ServerListPage <'static>
{ {
use LastSeen::*; use LastSeen::*;
@ -357,7 +357,7 @@ async fn handle_server_list_internal (state: &Arc <Relay>)
} }
} }
async fn handle_unregistered_servers_internal (state: &Arc <Relay>) async fn handle_unregistered_servers_internal (state: &Relay)
-> UnregisteredServerListPage -> UnregisteredServerListPage
{ {
use LastSeen::*; use LastSeen::*;
@ -366,6 +366,13 @@ async fn handle_unregistered_servers_internal (state: &Arc <Relay>)
let mut server_list = state.unregistered_servers.to_vec ().await; let mut server_list = state.unregistered_servers.to_vec ().await;
{
let me_config = state.me_config.read ().await;
server_list = server_list.into_iter ()
.filter (|s| ! me_config.servers.contains_key (&s.name))
.collect ();
}
server_list.sort_by_key (|s| { server_list.sort_by_key (|s| {
(s.name.clone (), s.tripcode.as_bytes ().clone (), now - s.seen) (s.name.clone (), s.tripcode.as_bytes ().clone (), now - s.seen)
}); });
@ -395,7 +402,7 @@ async fn handle_unregistered_servers_internal (state: &Arc <Relay>)
} }
} }
async fn handle_audit_log_internal (state: &Arc <Relay>) async fn handle_audit_log_internal (state: &Relay)
-> AuditLogPage -> AuditLogPage
{ {
let utc_now = Utc::now (); let utc_now = Utc::now ();
@ -413,7 +420,7 @@ async fn handle_audit_log_internal (state: &Arc <Relay>)
} }
async fn handle_server_list ( async fn handle_server_list (
state: Arc <Relay>, state: &Relay,
handlebars: Arc <Handlebars <'static>> handlebars: Arc <Handlebars <'static>>
) -> Result <Response <Body>, RequestError> ) -> Result <Response <Body>, RequestError>
{ {
@ -424,7 +431,7 @@ async fn handle_server_list (
} }
async fn handle_unregistered_servers ( async fn handle_unregistered_servers (
state: Arc <Relay>, state: &Relay,
handlebars: Arc <Handlebars <'static>> handlebars: Arc <Handlebars <'static>>
) -> Result <Response <Body>, RequestError> ) -> Result <Response <Body>, RequestError>
{ {
@ -435,11 +442,11 @@ async fn handle_unregistered_servers (
} }
async fn handle_audit_log ( async fn handle_audit_log (
state: Arc <Relay>, state: &Relay,
handlebars: Arc <Handlebars <'static>> handlebars: Arc <Handlebars <'static>>
) -> Result <Response <Body>, RequestError> ) -> Result <Response <Body>, RequestError>
{ {
let page = handle_audit_log_internal (&state).await; let page = handle_audit_log_internal (state).await;
let s = handlebars.render ("audit_log", &page)?; let s = handlebars.render ("audit_log", &page)?;
Ok (ok_reply (s)?) Ok (ok_reply (s)?)
@ -515,7 +522,7 @@ async fn handle_endless_source (gib: usize, throttle: Option <usize>)
.body (Body::wrap_stream (ReceiverStream::new (rx))) .body (Body::wrap_stream (ReceiverStream::new (rx)))
} }
async fn handle_gen_scraper_key (_state: Arc <Relay>) async fn handle_gen_scraper_key (_state: &Relay)
-> Result <Response <Body>, http::Error> -> Result <Response <Body>, http::Error>
{ {
let key = ptth_core::gen_key (); let key = ptth_core::gen_key ();
@ -528,24 +535,25 @@ async fn handle_gen_scraper_key (_state: Arc <Relay>)
.body (Body::from (body)) .body (Body::from (body))
} }
async fn handle_register_server (req: Request <Body>, state: Arc <Relay>) async fn handle_register_server (req: Request <Body>, state: &Relay)
-> Result <(), anyhow::Error> -> Result <(), anyhow::Error>
{ {
let (parts, body) = req.into_parts (); let (parts, body) = req.into_parts ();
let user = get_user_name (&parts); let user = get_user_name (&parts);
let form_data = read_body_limited (body, 1_024).await?; let form_data = read_body_limited (body, 1_024).await?;
let reg: relay_state::Registration = serde_urlencoded::from_bytes (&form_data)?; let server: crate::config::file::Server = serde_urlencoded::from_bytes (&form_data)?;
state.audit_log.push (AuditEvent::new (AuditData::RegisterServer { state.audit_log.push (AuditEvent::new (AuditData::RegisterServer {
user, user,
reg: reg.clone (), server: server.clone (),
})).await; })).await;
{ {
let mut me_config = state.me_config.write ().await; let mut me_config = state.me_config.write ().await;
//me_config.servers. me_config.servers.insert (server.name.clone (), server);
me_config.save (Path::new ("data/ptth_relay_me_config.toml")).await?;
} }
Ok (()) Ok (())
@ -577,6 +585,8 @@ async fn handle_all (
{ {
use routing::Route::*; use routing::Route::*;
let state = &*state;
// The path is cloned here, so it's okay to consume the request // The path is cloned here, so it's okay to consume the request
// later. // later.
let path = req.uri ().path ().to_string (); let path = req.uri ().path ().to_string ();
@ -592,41 +602,18 @@ async fn handle_all (
} => { } => {
let (parts, _) = req.into_parts (); let (parts, _) = req.into_parts ();
handle_http_request (parts, path.to_string (), state, listen_code).await? handle_http_request (parts, path.to_string (), &state, listen_code).await?
}, },
ClientServerList => handle_server_list (state, handlebars).await?, ClientServerList => handle_server_list (state, handlebars).await?,
ClientUnregisteredServers => handle_unregistered_servers (state, handlebars).await?, ClientUnregisteredServers => handle_unregistered_servers (state, handlebars).await?,
Debug => { Debug => {
#[derive (Serialize)] let s = handlebars.render ("debug", &())?;
struct DebugPage {
persistent_toggle: bool,
}
let page;
{
let guard = state.me_config.read ().await;
page = DebugPage {
persistent_toggle: guard.debug_toggle,
};
}
let s = handlebars.render ("debug", &page)?;
ok_reply (s)? ok_reply (s)?
}, },
DebugEndlessSink => handle_endless_sink (req).await?, DebugEndlessSink => handle_endless_sink (req).await?,
DebugEndlessSource (throttle) => handle_endless_source (1, throttle).await?, DebugEndlessSource (throttle) => handle_endless_source (1, throttle).await?,
DebugGenKey => handle_gen_scraper_key (state).await?, DebugGenKey => handle_gen_scraper_key (state).await?,
DebugMysteriousError => return Err (RequestError::Mysterious), DebugMysteriousError => return Err (RequestError::Mysterious),
DebugToggle => {
trace! ("Toggling debug toggle");
{
let mut guard = state.me_config.write ().await;
guard.debug_toggle = ! guard.debug_toggle;
guard.save (Path::new ("data/ptth_relay_me_config.toml")).await.unwrap ();
}
error_reply (StatusCode::OK, "Toggled.")?
},
ErrorBadUriFormat => error_reply (StatusCode::BAD_REQUEST, "Bad URI format")?, ErrorBadUriFormat => error_reply (StatusCode::BAD_REQUEST, "Bad URI format")?,
ErrorCantPost => { ErrorCantPost => {
error! ("Can't POST {}", path); error! ("Can't POST {}", path);
@ -637,7 +624,7 @@ async fn handle_all (
RegisterServer => { RegisterServer => {
match handle_register_server (req, state).await { match handle_register_server (req, state).await {
Ok (_) => Response::builder () Ok (_) => Response::builder ()
.status (StatusCode::TEMPORARY_REDIRECT) .status (StatusCode::SEE_OTHER)
.header ("location", "unregistered_servers") .header ("location", "unregistered_servers")
.body (Body::from ("Success. Redirecting..."))?, .body (Body::from ("Success. Redirecting..."))?,
Err (e) => error_reply (StatusCode::BAD_REQUEST, &format! ("{:?}", e))?, Err (e) => error_reply (StatusCode::BAD_REQUEST, &format! ("{:?}", e))?,
@ -671,6 +658,41 @@ async fn handle_all (
Ok (response) Ok (response)
} }
async fn check_server_api_key (state: &Relay, name: &str, req: &http::request::Parts)
-> Result <(), anyhow::Error>
{
let api_key = req.headers.get ("X-ApiKey");
let api_key = match api_key {
None => bail! ("Can't run server without an API key"),
Some (x) => x,
};
let actual_tripcode = blake3::hash (api_key.as_bytes ());
let expected_tripcode = {
let config = state.config.read ().await;
match config.servers.get (name) {
None => {
state.unregistered_servers.push (crate::RejectedServer {
name: name.to_string (),
tripcode: actual_tripcode,
seen: Utc::now (),
}).await;
bail! ("Denied API request for non-existent server name {}", name);
},
Some (x) => *(*x).tripcode,
}
};
if expected_tripcode != actual_tripcode {
bail! ("Denied API request for bad tripcode {}", base64::encode (actual_tripcode.as_bytes ()));
}
Ok (())
}
fn load_templates (asset_root: &Path) fn load_templates (asset_root: &Path)
-> Result <Handlebars <'static>, RelayError> -> Result <Handlebars <'static>, RelayError>
{ {
@ -761,6 +783,7 @@ pub async fn run_relay (
async { async {
Ok::<_, Infallible> (handle_all (req, state, handlebars).await.unwrap_or_else (|e| { Ok::<_, Infallible> (handle_all (req, state, handlebars).await.unwrap_or_else (|e| {
use RequestError::*;
error! ("{}", e); error! ("{}", e);
let status_code = match &e { let status_code = match &e {

View File

@ -7,7 +7,6 @@ use std::{
use chrono::{DateTime, Utc}; use chrono::{DateTime, Utc};
use dashmap::DashMap; use dashmap::DashMap;
use serde::Deserialize;
use tokio::sync::{ use tokio::sync::{
Mutex, Mutex,
RwLock, RwLock,
@ -119,7 +118,7 @@ pub struct AuditEvent {
pub enum AuditData { pub enum AuditData {
RegisterServer { RegisterServer {
user: Option <String>, user: Option <String>,
reg: Registration, server: crate::config::file::Server,
}, },
RelayStart, RelayStart,
WebClientGet { WebClientGet {
@ -130,12 +129,6 @@ pub enum AuditData {
}, },
} }
#[derive (Clone, Debug, Deserialize)]
pub struct Registration {
name: String,
tripcode: String,
}
impl AuditEvent { impl AuditEvent {
pub fn new (data: AuditData) -> Self { pub fn new (data: AuditData) -> Self {
Self { Self {

View File

@ -15,7 +15,6 @@ pub enum Route <'a> {
DebugEndlessSource (Option <usize>), DebugEndlessSource (Option <usize>),
DebugGenKey, DebugGenKey,
DebugMysteriousError, DebugMysteriousError,
DebugToggle,
ErrorBadUriFormat, ErrorBadUriFormat,
ErrorCantPost, ErrorCantPost,
ErrorMethodNotAllowed, ErrorMethodNotAllowed,
@ -43,9 +42,6 @@ pub fn route_url <'a> (method: &Method, path: &'a str) -> Route <'a> {
else if path == "/frontend/debug/endless_sink" { else if path == "/frontend/debug/endless_sink" {
Route::DebugEndlessSink Route::DebugEndlessSink
} }
else if path == "/frontend/debug/toggle" {
Route::DebugToggle
}
else if path == "/frontend/register" { else if path == "/frontend/register" {
Route::RegisterServer Route::RegisterServer
} }

View File

@ -1,6 +1,5 @@
use std::{ use std::{
collections::HashMap, collections::HashMap,
sync::Arc,
}; };
use chrono::{DateTime, Utc}; use chrono::{DateTime, Utc};
@ -65,7 +64,7 @@ pub struct ServerList {
pub servers: Vec <Server>, pub servers: Vec <Server>,
} }
pub async fn v1_server_list (state: &Arc <Relay>) pub async fn v1_server_list (state: &Relay)
-> ServerList -> ServerList
{ {
// name --> display_name // name --> display_name
@ -112,7 +111,7 @@ pub async fn v1_server_list (state: &Arc <Relay>)
#[instrument (level = "trace", skip (req, state))] #[instrument (level = "trace", skip (req, state))]
async fn api_v1 ( async fn api_v1 (
req: Request <Body>, req: Request <Body>,
state: Arc <Relay>, state: &Relay,
path_rest: &str path_rest: &str
) )
-> Result <Response <Body>, RequestError> -> Result <Response <Body>, RequestError>
@ -170,7 +169,7 @@ async fn api_v1 (
// This is ugly. I don't like having scraper_api know about the // This is ugly. I don't like having scraper_api know about the
// crate root. // crate root.
Ok (crate::handle_http_request (parts, path, state, &listen_code).await?) Ok (crate::handle_http_request (parts, path, &state, &listen_code).await?)
} }
else { else {
Ok (error_reply (StatusCode::BAD_REQUEST, "Bad URI format")?) Ok (error_reply (StatusCode::BAD_REQUEST, "Bad URI format")?)
@ -184,7 +183,7 @@ async fn api_v1 (
#[instrument (level = "trace", skip (req, state))] #[instrument (level = "trace", skip (req, state))]
pub async fn handle ( pub async fn handle (
req: Request <Body>, req: Request <Body>,
state: Arc <Relay>, state: &Relay,
path_rest: &str path_rest: &str
) )
-> Result <Response <Body>, RequestError> -> Result <Response <Body>, RequestError>
@ -298,9 +297,7 @@ mod tests {
let relay_state = builder.build ().expect ("Can't create relay state"); let relay_state = builder.build ().expect ("Can't create relay state");
let relay_state = Arc::new (relay_state); let actual = super::handle (input, &relay_state, self.path_rest).await;
let actual = super::handle (input, relay_state, self.path_rest).await;
let actual = actual.expect ("Relay didn't respond"); let actual = actual.expect ("Relay didn't respond");
let (actual_head, actual_body) = actual.into_parts (); let (actual_head, actual_body) = actual.into_parts ();

View File

@ -1,5 +1,4 @@
use std::{ use std::{
sync::Arc,
time::Duration, time::Duration,
}; };
@ -44,7 +43,7 @@ use super::{
// Step 1 // Step 1
pub async fn handle_listen ( pub async fn handle_listen (
state: Arc <Relay>, state: &Relay,
watcher_code: String, watcher_code: String,
api_key: &[u8], api_key: &[u8],
) )
@ -140,7 +139,7 @@ pub async fn handle_listen (
pub async fn handle_response ( pub async fn handle_response (
req: Request <Body>, req: Request <Body>,
state: Arc <Relay>, state: &Relay,
req_id: String, req_id: String,
) )
-> Result <Response <Body>, HandleHttpResponseError> -> Result <Response <Body>, HandleHttpResponseError>

View File

@ -29,7 +29,6 @@ AIABAACAAQAAgAEAAIABAACAAQAAgAEAAIABAACAAQAA" rel="icon" type="image/x-icon" />
border-collapse: collapse; border-collapse: collapse;
} }
.padded { .padded {
display: block;
padding: 20px; padding: 20px;
} }
.submit { .submit {
@ -62,13 +61,11 @@ AIABAACAAQAAgAEAAIABAACAAQAAgAEAAIABAACAAQAA" rel="icon" type="image/x-icon" />
<td><span class="padded">{{this.name}}</span></td> <td><span class="padded">{{this.name}}</span></td>
<td> <td>
<span class="padded">{{this.tripcode}}</span> <span class="padded">{{this.tripcode}}</span>
<!--
<form action="register" method="post"> <form action="register" method="post">
<input type="hidden" name="name" value="{{this.name}}"> <input type="hidden" name="name" value="{{this.name}}">
<input type="hidden" name="tripcode" value="{{this.tripcode}}"> <input type="hidden" name="tripcode" value="{{this.tripcode}}">
<input class="submit" type="submit" value="Register"> <input class="submit" type="submit" value="Register">
</form> </form>
-->
</td> </td>
<td><span class="padded">{{this.last_seen}}</span></td> <td><span class="padded">{{this.last_seen}}</span></td>
</tr> </tr>