Add secret service.http.form_method field for debugging
This commit is contained in:
parent
4483185e75
commit
5a4ff4ca65
@ -3,6 +3,8 @@ origin:
|
|||||||
domain:
|
domain:
|
||||||
store_dir_path: /tmp/domani_dev_env/domain
|
store_dir_path: /tmp/domani_dev_env/domain
|
||||||
service:
|
service:
|
||||||
|
http:
|
||||||
|
form_method: GET
|
||||||
passphrase: foobar
|
passphrase: foobar
|
||||||
dns_records:
|
dns_records:
|
||||||
- kind: A
|
- kind: A
|
||||||
|
@ -41,5 +41,6 @@ pub struct Config {
|
|||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub dns: ConfigDNS,
|
pub dns: ConfigDNS,
|
||||||
pub acme: Option<ConfigACME>,
|
pub acme: Option<ConfigACME>,
|
||||||
|
#[serde(default)]
|
||||||
pub builtins: collections::HashMap<domain::Name, BuiltinDomain>,
|
pub builtins: collections::HashMap<domain::Name, BuiltinDomain>,
|
||||||
}
|
}
|
||||||
|
@ -1,38 +1,5 @@
|
|||||||
|
mod config;
|
||||||
pub mod http;
|
pub mod http;
|
||||||
mod util;
|
mod util;
|
||||||
|
|
||||||
use crate::domain;
|
pub use config::*;
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use std::{net, str::FromStr};
|
|
||||||
|
|
||||||
fn default_primary_domain() -> domain::Name {
|
|
||||||
domain::Name::from_str("localhost").unwrap()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone, PartialEq)]
|
|
||||||
#[serde(tag = "kind")]
|
|
||||||
pub enum ConfigDNSRecord {
|
|
||||||
A { addr: net::Ipv4Addr },
|
|
||||||
AAAA { addr: net::Ipv6Addr },
|
|
||||||
CNAME { name: domain::Name },
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<ConfigDNSRecord> for domain::checker::DNSRecord {
|
|
||||||
fn from(r: ConfigDNSRecord) -> Self {
|
|
||||||
match r {
|
|
||||||
ConfigDNSRecord::A { addr } => Self::A(addr),
|
|
||||||
ConfigDNSRecord::AAAA { addr } => Self::AAAA(addr),
|
|
||||||
ConfigDNSRecord::CNAME { name } => Self::CNAME(name),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
|
||||||
pub struct Config {
|
|
||||||
pub passphrase: String,
|
|
||||||
pub dns_records: Vec<ConfigDNSRecord>,
|
|
||||||
#[serde(default = "default_primary_domain")]
|
|
||||||
pub primary_domain: domain::Name,
|
|
||||||
#[serde(default)]
|
|
||||||
pub http: self::http::Config,
|
|
||||||
}
|
|
||||||
|
35
src/service/config.rs
Normal file
35
src/service/config.rs
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
use crate::{domain, service};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use std::{net, str::FromStr};
|
||||||
|
|
||||||
|
fn default_primary_domain() -> domain::Name {
|
||||||
|
domain::Name::from_str("localhost").unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Clone, PartialEq)]
|
||||||
|
#[serde(tag = "kind")]
|
||||||
|
pub enum ConfigDNSRecord {
|
||||||
|
A { addr: net::Ipv4Addr },
|
||||||
|
AAAA { addr: net::Ipv6Addr },
|
||||||
|
CNAME { name: domain::Name },
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<ConfigDNSRecord> for domain::checker::DNSRecord {
|
||||||
|
fn from(r: ConfigDNSRecord) -> Self {
|
||||||
|
match r {
|
||||||
|
ConfigDNSRecord::A { addr } => Self::A(addr),
|
||||||
|
ConfigDNSRecord::AAAA { addr } => Self::AAAA(addr),
|
||||||
|
ConfigDNSRecord::CNAME { name } => Self::CNAME(name),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
pub struct Config {
|
||||||
|
pub passphrase: String,
|
||||||
|
pub dns_records: Vec<ConfigDNSRecord>,
|
||||||
|
#[serde(default = "default_primary_domain")]
|
||||||
|
pub primary_domain: domain::Name,
|
||||||
|
#[serde(default)]
|
||||||
|
pub http: service::http::Config,
|
||||||
|
}
|
@ -4,6 +4,7 @@ mod tpl;
|
|||||||
|
|
||||||
pub use config::*;
|
pub use config::*;
|
||||||
|
|
||||||
|
use http::request::Parts;
|
||||||
use hyper::{Body, Method, Request, Response};
|
use hyper::{Body, Method, Request, Response};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
@ -48,6 +49,7 @@ pub fn new(
|
|||||||
#[derive(Serialize)]
|
#[derive(Serialize)]
|
||||||
struct BasePresenter<'a, T> {
|
struct BasePresenter<'a, T> {
|
||||||
page_name: &'a str,
|
page_name: &'a str,
|
||||||
|
form_method: &'a str,
|
||||||
data: T,
|
data: T,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -59,12 +61,18 @@ struct DomainGetArgs {
|
|||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
struct DomainInitArgs {
|
struct DomainInitArgs {
|
||||||
domain: domain::Name,
|
domain: domain::Name,
|
||||||
|
|
||||||
|
#[serde(flatten)]
|
||||||
|
domain_config: service::util::FlatConfig,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
struct DomainSyncArgs {
|
struct DomainSyncArgs {
|
||||||
domain: domain::Name,
|
domain: domain::Name,
|
||||||
passphrase: String,
|
passphrase: String,
|
||||||
|
|
||||||
|
#[serde(flatten)]
|
||||||
|
domain_config: service::util::FlatConfig,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'svc> Service {
|
impl<'svc> Service {
|
||||||
@ -121,6 +129,7 @@ impl<'svc> Service {
|
|||||||
"/base.html",
|
"/base.html",
|
||||||
BasePresenter {
|
BasePresenter {
|
||||||
page_name: "/error.html",
|
page_name: "/error.html",
|
||||||
|
form_method: self.config.http.form_method.as_str(),
|
||||||
data: &Response { error_msg: e },
|
data: &Response { error_msg: e },
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
@ -143,6 +152,7 @@ impl<'svc> Service {
|
|||||||
"/base.html",
|
"/base.html",
|
||||||
BasePresenter {
|
BasePresenter {
|
||||||
page_name: name,
|
page_name: name,
|
||||||
|
form_method: self.config.http.form_method.as_str(),
|
||||||
data,
|
data,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
@ -174,14 +184,33 @@ impl<'svc> Service {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn with_query_req<'a, F, In, Out>(&self, req: &'a Request<Body>, f: F) -> Response<Body>
|
async fn with_query_req<'a, F, In, Out>(
|
||||||
|
&self,
|
||||||
|
req: &'a Parts,
|
||||||
|
body: Body,
|
||||||
|
f: F,
|
||||||
|
) -> Response<Body>
|
||||||
where
|
where
|
||||||
In: Deserialize<'a>,
|
In: for<'d> Deserialize<'d>,
|
||||||
F: FnOnce(In) -> Out,
|
F: FnOnce(In) -> Out,
|
||||||
Out: future::Future<Output = Response<Body>>,
|
Out: future::Future<Output = Response<Body>>,
|
||||||
{
|
{
|
||||||
let query = req.uri().query().unwrap_or("");
|
let res = match self.config.http.form_method {
|
||||||
match serde_urlencoded::from_str::<In>(query) {
|
ConfigFormMethod::GET => {
|
||||||
|
serde_urlencoded::from_str::<In>(req.uri.query().unwrap_or(""))
|
||||||
|
}
|
||||||
|
ConfigFormMethod::POST => {
|
||||||
|
let body = match hyper::body::to_bytes(body).await {
|
||||||
|
Ok(bytes) => bytes.to_vec(),
|
||||||
|
Err(e) => {
|
||||||
|
return self.internal_error(format!("failed to read body: {e}").as_str())
|
||||||
|
}
|
||||||
|
};
|
||||||
|
serde_urlencoded::from_bytes::<In>(body.as_ref())
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
match res {
|
||||||
Ok(args) => f(args).await,
|
Ok(args) => f(args).await,
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
self.render_error_page(400, format!("failed to parse query args: {}", err).as_str())
|
self.render_error_page(400, format!("failed to parse query args: {}", err).as_str())
|
||||||
@ -219,11 +248,7 @@ impl<'svc> Service {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn domain_init(
|
fn domain_init(&self, args: DomainInitArgs) -> Response<Body> {
|
||||||
&self,
|
|
||||||
args: DomainInitArgs,
|
|
||||||
domain_config: service::util::FlatConfig,
|
|
||||||
) -> Response<Body> {
|
|
||||||
#[derive(Serialize)]
|
#[derive(Serialize)]
|
||||||
struct Response<'a> {
|
struct Response<'a> {
|
||||||
domain: domain::Name,
|
domain: domain::Name,
|
||||||
@ -235,7 +260,7 @@ impl<'svc> Service {
|
|||||||
dns_records_have_cname: bool,
|
dns_records_have_cname: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
let config: domain::Domain = match domain_config.try_into() {
|
let config: domain::Domain = match args.domain_config.try_into() {
|
||||||
Ok(Some(config)) => config,
|
Ok(Some(config)) => config,
|
||||||
Ok(None) => return self.render_error_page(400, "domain config is required"),
|
Ok(None) => return self.render_error_page(400, "domain config is required"),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
@ -272,16 +297,12 @@ impl<'svc> Service {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn domain_sync(
|
async fn domain_sync(&self, args: DomainSyncArgs) -> Response<Body> {
|
||||||
&self,
|
|
||||||
args: DomainSyncArgs,
|
|
||||||
domain_config: service::util::FlatConfig,
|
|
||||||
) -> Response<Body> {
|
|
||||||
if args.passphrase != self.config.passphrase.as_str() {
|
if args.passphrase != self.config.passphrase.as_str() {
|
||||||
return self.render_error_page(401, "Incorrect passphrase");
|
return self.render_error_page(401, "Incorrect passphrase");
|
||||||
}
|
}
|
||||||
|
|
||||||
let config: domain::Domain = match domain_config.try_into() {
|
let config: domain::Domain = match args.domain_config.try_into() {
|
||||||
Ok(Some(config)) => config,
|
Ok(Some(config)) => config,
|
||||||
Ok(None) => return self.render_error_page(400, "domain config is required"),
|
Ok(None) => return self.render_error_page(400, "domain config is required"),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
@ -341,12 +362,14 @@ impl<'svc> Service {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn handle_request(&self, req: Request<Body>) -> Response<Body> {
|
async fn handle_request(&self, req: Request<Body>) -> Response<Body> {
|
||||||
|
let (req, body) = req.into_parts();
|
||||||
|
|
||||||
let maybe_host = match (
|
let maybe_host = match (
|
||||||
req.headers()
|
req.headers
|
||||||
.get("Host")
|
.get("Host")
|
||||||
.and_then(|v| v.to_str().ok())
|
.and_then(|v| v.to_str().ok())
|
||||||
.map(strip_port),
|
.map(strip_port),
|
||||||
req.uri().host().map(strip_port),
|
req.uri.host().map(strip_port),
|
||||||
) {
|
) {
|
||||||
(Some(h), _) if h != self.config.primary_domain.as_str() => Some(h),
|
(Some(h), _) if h != self.config.primary_domain.as_str() => Some(h),
|
||||||
(_, Some(h)) if h != self.config.primary_domain.as_str() => Some(h),
|
(_, Some(h)) if h != self.config.primary_domain.as_str() => Some(h),
|
||||||
@ -354,13 +377,12 @@ impl<'svc> Service {
|
|||||||
}
|
}
|
||||||
.and_then(|h| domain::Name::from_str(h).ok());
|
.and_then(|h| domain::Name::from_str(h).ok());
|
||||||
|
|
||||||
let method = req.method();
|
let path = req.uri.path();
|
||||||
let path = req.uri().path();
|
|
||||||
|
|
||||||
// Serving acme challenges always takes priority. We serve them from the same store no
|
// Serving acme challenges always takes priority. We serve them from the same store no
|
||||||
// matter the domain, presumably they are cryptographically random enough that it doesn't
|
// matter the domain, presumably they are cryptographically random enough that it doesn't
|
||||||
// matter.
|
// matter.
|
||||||
if method == Method::GET && path.starts_with("/.well-known/acme-challenge/") {
|
if req.method == Method::GET && path.starts_with("/.well-known/acme-challenge/") {
|
||||||
let token = path.trim_start_matches("/.well-known/acme-challenge/");
|
let token = path.trim_start_matches("/.well-known/acme-challenge/");
|
||||||
|
|
||||||
if let Ok(key) = self.domain_manager.get_acme_http01_challenge_key(token) {
|
if let Ok(key) = self.domain_manager.get_acme_http01_challenge_key(token) {
|
||||||
@ -369,7 +391,7 @@ impl<'svc> Service {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Serving domani challenges similarly takes priority.
|
// Serving domani challenges similarly takes priority.
|
||||||
if method == Method::GET && path == "/.well-known/domani-challenge" {
|
if req.method == Method::GET && path == "/.well-known/domani-challenge" {
|
||||||
if let Some(ref domain) = maybe_host {
|
if let Some(ref domain) = maybe_host {
|
||||||
match self
|
match self
|
||||||
.domain_manager
|
.domain_manager
|
||||||
@ -388,38 +410,36 @@ impl<'svc> Service {
|
|||||||
|
|
||||||
// If a managed domain was given then serve that from its origin
|
// If a managed domain was given then serve that from its origin
|
||||||
if let Some(domain) = maybe_host {
|
if let Some(domain) = maybe_host {
|
||||||
return self.serve_origin(domain, req.uri().path());
|
return self.serve_origin(domain, req.uri.path());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Serve main domani site
|
// Serve main domani site
|
||||||
|
|
||||||
if method == Method::GET && path.starts_with("/static/") {
|
if req.method == Method::GET && path.starts_with("/static/") {
|
||||||
return self.render(200, path, ());
|
return self.render(200, path, ());
|
||||||
}
|
}
|
||||||
|
|
||||||
match (method, path) {
|
let config_form_method = self.config.http.form_method.as_ref();
|
||||||
|
|
||||||
|
match (&req.method, path) {
|
||||||
(&Method::GET, "/") | (&Method::GET, "/index.html") => {
|
(&Method::GET, "/") | (&Method::GET, "/index.html") => {
|
||||||
self.render_page("/index.html", ())
|
self.render_page("/index.html", ())
|
||||||
}
|
}
|
||||||
(&Method::GET, "/domain.html") => {
|
(form_method, "/domain.html") if form_method == config_form_method => {
|
||||||
self.with_query_req(&req, |args: DomainGetArgs| async { self.domain_get(args) })
|
self.with_query_req(&req, body, |args: DomainGetArgs| async {
|
||||||
.await
|
self.domain_get(args)
|
||||||
}
|
|
||||||
(&Method::GET, "/domain_init.html") => {
|
|
||||||
self.with_query_req(&req, |args: DomainInitArgs| async {
|
|
||||||
self.with_query_req(&req, |config: service::util::FlatConfig| async {
|
|
||||||
self.domain_init(args, config)
|
|
||||||
})
|
|
||||||
.await
|
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
(&Method::GET, "/domain_sync.html") => {
|
(form_method, "/domain_init.html") if form_method == config_form_method => {
|
||||||
self.with_query_req(&req, |args: DomainSyncArgs| async {
|
self.with_query_req(&req, body, |args: DomainInitArgs| async {
|
||||||
self.with_query_req(&req, |config: service::util::FlatConfig| async {
|
self.domain_init(args)
|
||||||
self.domain_sync(args, config).await
|
})
|
||||||
})
|
.await
|
||||||
.await
|
}
|
||||||
|
(form_method, "/domain_sync.html") if form_method == config_form_method => {
|
||||||
|
self.with_query_req(&req, body, |args: DomainSyncArgs| async {
|
||||||
|
self.domain_sync(args).await
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
@ -5,11 +5,44 @@ fn default_http_addr() -> net::SocketAddr {
|
|||||||
net::SocketAddr::from_str("[::]:3030").unwrap()
|
net::SocketAddr::from_str("[::]:3030").unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
pub enum ConfigFormMethod {
|
||||||
|
GET,
|
||||||
|
POST,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ConfigFormMethod {
|
||||||
|
pub fn as_str(&self) -> &str {
|
||||||
|
match self {
|
||||||
|
Self::GET => "GET",
|
||||||
|
Self::POST => "POST",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for ConfigFormMethod {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::POST
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AsRef<hyper::Method> for ConfigFormMethod {
|
||||||
|
fn as_ref(&self) -> &hyper::Method {
|
||||||
|
match self {
|
||||||
|
Self::GET => &hyper::Method::GET,
|
||||||
|
Self::POST => &hyper::Method::POST,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
#[serde(default = "default_http_addr")]
|
#[serde(default = "default_http_addr")]
|
||||||
pub http_addr: net::SocketAddr,
|
pub http_addr: net::SocketAddr,
|
||||||
pub https_addr: Option<net::SocketAddr>,
|
pub https_addr: Option<net::SocketAddr>,
|
||||||
|
|
||||||
|
#[serde(default)]
|
||||||
|
pub form_method: ConfigFormMethod,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Config {
|
impl Default for Config {
|
||||||
@ -17,6 +50,7 @@ impl Default for Config {
|
|||||||
Self {
|
Self {
|
||||||
http_addr: default_http_addr(),
|
http_addr: default_http_addr(),
|
||||||
https_addr: None,
|
https_addr: None,
|
||||||
|
form_method: Default::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,7 +20,7 @@ automatically updated too!</p>
|
|||||||
<p><em>In the future Domani will support more backends than just git
|
<p><em>In the future Domani will support more backends than just git
|
||||||
repos.</em></p>
|
repos.</em></p>
|
||||||
|
|
||||||
<form method="GET" action="/domain_init.html">
|
<form method="{{ form_method }}" action="/domain_init.html">
|
||||||
<input name="domain" type="hidden" value="{{ data.domain }}" />
|
<input name="domain" type="hidden" value="{{ data.domain }}" />
|
||||||
<input name="config_origin_descr_kind" type="hidden" value="git" />
|
<input name="config_origin_descr_kind" type="hidden" value="git" />
|
||||||
|
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
<p>This step requires a passphrase that has been given to you by the
|
<p>This step requires a passphrase that has been given to you by the
|
||||||
administrator of the Domani server:</p>
|
administrator of the Domani server:</p>
|
||||||
|
|
||||||
<form method="GET" action="/domain_sync.html" id="syncForm">
|
<form method="{{ form_method }}" action="/domain_sync.html" id="syncForm">
|
||||||
<input name="domain" type="hidden" value="{{ data.domain }}" />
|
<input name="domain" type="hidden" value="{{ data.domain }}" />
|
||||||
{{ #each data.flat_config }}
|
{{ #each data.flat_config }}
|
||||||
<input name="{{ @key }}" type="hidden" value="{{ this }}" />
|
<input name="{{ @key }}" type="hidden" value="{{ this }}" />
|
||||||
@ -47,7 +47,7 @@ query for your domain name. It can be <strong>one or more of</strong>:</p>
|
|||||||
|
|
||||||
{{ #each data.dns_records }}
|
{{ #each data.dns_records }}
|
||||||
<tr>
|
<tr>
|
||||||
<td>{{ this.type }}</td>
|
<td>{{ this.kind }}</td>
|
||||||
<td>{{ lookup ../data "domain" }}</td>
|
<td>{{ lookup ../data "domain" }}</td>
|
||||||
{{ #if this.name }}
|
{{ #if this.name }}
|
||||||
<td>{{ this.name }}</td>
|
<td>{{ this.name }}</td>
|
||||||
|
@ -13,7 +13,7 @@ server, and you're done!</p>
|
|||||||
<p>Input your domain name below to set it up, or to reconfigure it has already
|
<p>Input your domain name below to set it up, or to reconfigure it has already
|
||||||
been set up.</p>
|
been set up.</p>
|
||||||
|
|
||||||
<form method="get" action="/domain.html">
|
<form method="{{ form_method }}" action="/domain.html">
|
||||||
|
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<label>
|
<label>
|
||||||
|
Loading…
Reference in New Issue
Block a user