🚧 one-click server registration

main
_ 2021-05-02 21:18:07 +00:00
parent dc53aeed30
commit a4ec4878ea
6 changed files with 83 additions and 12 deletions

2
Cargo.lock generated
View File

@ -1545,6 +1545,7 @@ dependencies = [
name = "ptth_relay" name = "ptth_relay"
version = "2.0.0" version = "2.0.0"
dependencies = [ dependencies = [
"anyhow",
"base64", "base64",
"blake3", "blake3",
"chrono", "chrono",
@ -1562,6 +1563,7 @@ dependencies = [
"rusty_ulid", "rusty_ulid",
"serde", "serde",
"serde_json", "serde_json",
"serde_urlencoded",
"thiserror", "thiserror",
"tokio", "tokio",
"tokio-stream", "tokio-stream",

View File

@ -11,6 +11,7 @@ repository = "https://github.com/ReactorScram/ptth"
[dependencies] [dependencies]
anyhow = "1.0.38"
base64 = "0.13.0" base64 = "0.13.0"
blake3 = "0.3.7" blake3 = "0.3.7"
chrono = { version = "0.4.19", features = ["serde"] } chrono = { version = "0.4.19", features = ["serde"] }
@ -27,6 +28,7 @@ rmp-serde = "0.14.4"
rusty_ulid = "0.10.1" rusty_ulid = "0.10.1"
serde = { version = "1.0.117", features = ["derive"] } serde = { version = "1.0.117", features = ["derive"] }
serde_json = "1.0.60" serde_json = "1.0.60"
serde_urlencoded = "0.7.0"
thiserror = "1.0.22" thiserror = "1.0.22"
tokio = { version = "1.4.0", features = [] } tokio = { version = "1.4.0", features = [] }
tokio-stream = "0.1.3" tokio-stream = "0.1.3"

View File

@ -22,7 +22,6 @@
use std::{ use std::{
borrow::Cow, borrow::Cow,
collections::HashSet,
convert::Infallible, convert::Infallible,
net::SocketAddr, net::SocketAddr,
path::{Path, PathBuf}, path::{Path, PathBuf},
@ -30,6 +29,7 @@ use std::{
time::Duration, time::Duration,
}; };
use anyhow::bail;
use chrono::{ use chrono::{
DateTime, DateTime,
SecondsFormat, SecondsFormat,
@ -77,6 +77,10 @@ pub use config::{
}; };
pub use errors::*; pub use errors::*;
pub use relay_state::Relay; pub use relay_state::Relay;
use relay_state::{
AuditData,
AuditEvent,
};
use relay_state::{ use relay_state::{
RejectedServer, RejectedServer,
@ -98,6 +102,12 @@ fn error_reply (status: StatusCode, b: &str)
.body (format! ("{}\n", b).into ()) .body (format! ("{}\n", b).into ())
} }
fn get_user_name (req: &http::request::Parts)
-> Option <String>
{
req.headers.get ("X-Email").map (|x| Some (x.to_str ().ok ()?.to_string ())).flatten ()
}
/// Clients will come here to start requests, and always park for at least /// Clients will come here to start requests, and always park for at least
/// a short amount of time. /// a short amount of time.
@ -109,10 +119,6 @@ async fn handle_http_request (
) )
-> Result <Response <Body>, RequestError> -> Result <Response <Body>, RequestError>
{ {
use crate::relay_state::{
AuditData,
AuditEvent,
};
use RequestError::*; use RequestError::*;
let req_method = req.method.clone (); let req_method = req.method.clone ();
@ -124,7 +130,7 @@ async fn handle_http_request (
} }
} }
let user = req.headers.get ("X-Email").map (|x| Some (x.to_str ().ok ()?.to_string ())).flatten (); let user = get_user_name (&req);
let req = match http_serde::RequestParts::from_hyper (req.method, uri.clone (), req.headers) { let req = match http_serde::RequestParts::from_hyper (req.method, uri.clone (), req.headers) {
Ok (x) => x, Ok (x) => x,
@ -522,6 +528,45 @@ 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>)
-> Result <(), anyhow::Error>
{
let (parts, body) = req.into_parts ();
let user = get_user_name (&parts);
let form_data = read_body_limited (body, 1_024).await?;
let reg: relay_state::Registration = serde_urlencoded::from_bytes (&form_data)?;
state.audit_log.push (AuditEvent::new (AuditData::RegisterServer {
user,
reg: reg.clone (),
})).await;
{
let mut me_config = state.me_config.write ().await;
//me_config.servers.
}
Ok (())
}
async fn read_body_limited (mut body: Body, limit: usize) -> anyhow::Result <Vec <u8>>
{
let mut buffer = vec! [];
while let Some (chunk) = body.next ().await {
let chunk = chunk?;
if buffer.len () + chunk.len () > limit {
bail! ("Body was bigger than limit");
}
buffer.extend_from_slice (&chunk);
}
Ok (buffer)
}
#[instrument (level = "trace", skip (req, state, handlebars))] #[instrument (level = "trace", skip (req, state, handlebars))]
async fn handle_all ( async fn handle_all (
req: Request <Body>, req: Request <Body>,
@ -589,6 +634,15 @@ async fn handle_all (
}, },
ErrorMethodNotAllowed => error_reply (StatusCode::METHOD_NOT_ALLOWED, "Method not allowed. Are you POST-ing to a GET-only url, or vice versa?")?, ErrorMethodNotAllowed => error_reply (StatusCode::METHOD_NOT_ALLOWED, "Method not allowed. Are you POST-ing to a GET-only url, or vice versa?")?,
ErrorRoutingFailed => error_reply (StatusCode::OK, "URL routing failed")?, ErrorRoutingFailed => error_reply (StatusCode::OK, "URL routing failed")?,
RegisterServer => {
match handle_register_server (req, state).await {
Ok (_) => Response::builder ()
.status (StatusCode::TEMPORARY_REDIRECT)
.header ("location", "unregistered_servers")
.body (Body::from ("Success. Redirecting..."))?,
Err (e) => error_reply (StatusCode::BAD_REQUEST, &format! ("{:?}", e))?,
}
}
Root => { Root => {
let s = handlebars.render ("root", &())?; let s = handlebars.render ("root", &())?;
ok_reply (s)? ok_reply (s)?
@ -675,11 +729,6 @@ pub async fn run_relay (
) )
-> Result <(), RelayError> -> Result <(), RelayError>
{ {
use crate::relay_state::{
AuditData,
AuditEvent,
};
let handlebars = Arc::new (load_templates (asset_root)?); let handlebars = Arc::new (load_templates (asset_root)?);
if let Some (x) = git_version::read ().await { if let Some (x) = git_version::read ().await {

View File

@ -7,6 +7,7 @@ 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,
@ -116,6 +117,10 @@ pub struct AuditEvent {
#[derive (Clone, Debug)] #[derive (Clone, Debug)]
pub enum AuditData { pub enum AuditData {
RegisterServer {
user: Option <String>,
reg: Registration,
},
RelayStart, RelayStart,
WebClientGet { WebClientGet {
user: Option <String>, user: Option <String>,
@ -125,6 +130,12 @@ 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

@ -20,6 +20,7 @@ pub enum Route <'a> {
ErrorCantPost, ErrorCantPost,
ErrorMethodNotAllowed, ErrorMethodNotAllowed,
ErrorRoutingFailed, ErrorRoutingFailed,
RegisterServer,
Root, Root,
Scraper { Scraper {
rest: &'a str, rest: &'a str,
@ -45,6 +46,9 @@ pub fn route_url <'a> (method: &Method, path: &'a str) -> Route <'a> {
else if path == "/frontend/debug/toggle" { else if path == "/frontend/debug/toggle" {
Route::DebugToggle Route::DebugToggle
} }
else if path == "/frontend/register" {
Route::RegisterServer
}
else { else {
Route::ErrorCantPost Route::ErrorCantPost
} }

View File

@ -29,6 +29,7 @@ AIABAACAAQAAgAEAAIABAACAAQAAgAEAAIABAACAAQAA" rel="icon" type="image/x-icon" />
border-collapse: collapse; border-collapse: collapse;
} }
.padded { .padded {
display: block;
padding: 20px; padding: 20px;
} }
.submit { .submit {
@ -61,11 +62,13 @@ 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> <!--
<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>