Allow HTTP to be used for configuring the server
This commit is contained in:
@@ -14,7 +14,7 @@ pub mod token;
|
||||
pub const DEVICE_CODE_LEN: usize = 40;
|
||||
pub const USER_CODE_LEN: usize = 8;
|
||||
pub const RANDOM_CODE_LEN: usize = 32;
|
||||
pub const CLIENT_ID_MAX_LEN: usize = 20;
|
||||
pub const CLIENT_ID_MAX_LEN: usize = 60;
|
||||
|
||||
pub const USER_CODE_ALPHABET: &[u8] = b"ABCDEFGHJKLMNPQRSTUVWXYZ23456789"; // No 0, O, I, 1
|
||||
|
||||
|
||||
@@ -81,7 +81,7 @@ impl ManagementApi for Server {
|
||||
if let Some(email) = path.get(1).copied() {
|
||||
self.is_http_anonymous_request_allowed(session.remote_ip)
|
||||
.await?;
|
||||
self.handle_discover_request(decode_path_element(email).as_ref())
|
||||
self.handle_discover_request(session, decode_path_element(email).as_ref())
|
||||
.await
|
||||
} else {
|
||||
Err(trc::ResourceEvent::NotFound.into_err())
|
||||
|
||||
@@ -49,6 +49,7 @@ pub struct OAuthMetadata {
|
||||
pub trait OAuthApiHandler: Sync + Send {
|
||||
fn handle_discover_request(
|
||||
&self,
|
||||
session: &HttpSessionData,
|
||||
account_name: &str,
|
||||
) -> impl Future<Output = trc::Result<HttpResponse>> + Send;
|
||||
|
||||
@@ -114,7 +115,11 @@ pub enum LoginResponse {
|
||||
}
|
||||
|
||||
impl OAuthApiHandler for Server {
|
||||
async fn handle_discover_request(&self, account_name: &str) -> trc::Result<HttpResponse> {
|
||||
async fn handle_discover_request(
|
||||
&self,
|
||||
session: &HttpSessionData,
|
||||
account_name: &str,
|
||||
) -> trc::Result<HttpResponse> {
|
||||
let account_name = account_name.trim().to_lowercase();
|
||||
if let Some(domain_name) = account_name.try_domain_part()
|
||||
&& let Some(endpoint) = self
|
||||
@@ -126,7 +131,7 @@ impl OAuthApiHandler for Server {
|
||||
.no_cache()
|
||||
.into_http_response())
|
||||
} else {
|
||||
self.handle_oidc_metadata().await
|
||||
self.handle_oidc_metadata(!session.is_tls).await
|
||||
}
|
||||
}
|
||||
|
||||
@@ -155,13 +160,13 @@ impl OAuthApiHandler for Server {
|
||||
if client_id.len() > CLIENT_ID_MAX_LEN {
|
||||
return Err(trc::AuthEvent::Error
|
||||
.into_err()
|
||||
.details("Client ID is invalid."));
|
||||
.details("Client ID is too long."));
|
||||
} else if redirect_uri
|
||||
.as_ref()
|
||||
.is_some_and(|uri| uri.starts_with("http://"))
|
||||
{
|
||||
#[cfg(not(feature = "dev_mode"))]
|
||||
if !self.registry().is_recovery_mode() {
|
||||
if !self.registry().is_recovery_mode() && code_challenge.is_none() {
|
||||
return Err(trc::AuthEvent::Error
|
||||
.into_err()
|
||||
.details("Redirect URI must be HTTPS."));
|
||||
|
||||
@@ -33,7 +33,10 @@ pub trait OpenIdHandler: Sync + Send {
|
||||
account_id: u32,
|
||||
) -> impl Future<Output = trc::Result<HttpResponse>> + Send;
|
||||
|
||||
fn handle_oidc_metadata(&self) -> impl Future<Output = trc::Result<HttpResponse>> + Send;
|
||||
fn handle_oidc_metadata(
|
||||
&self,
|
||||
strip_base_url: bool,
|
||||
) -> impl Future<Output = trc::Result<HttpResponse>> + Send;
|
||||
}
|
||||
|
||||
impl OpenIdHandler for Server {
|
||||
@@ -52,8 +55,12 @@ impl OpenIdHandler for Server {
|
||||
.into_http_response())
|
||||
}
|
||||
|
||||
async fn handle_oidc_metadata(&self) -> trc::Result<HttpResponse> {
|
||||
let base_url = &self.core.network.http.url_https;
|
||||
async fn handle_oidc_metadata(&self, strip_base_url: bool) -> trc::Result<HttpResponse> {
|
||||
let base_url = if strip_base_url {
|
||||
""
|
||||
} else {
|
||||
&self.core.network.http.url_https
|
||||
};
|
||||
|
||||
Ok(JsonResponse::new(OpenIdMetadata {
|
||||
authorization_endpoint: format!("{base_url}/login",),
|
||||
|
||||
@@ -278,7 +278,7 @@ impl ParseHttp for Server {
|
||||
self.is_http_anonymous_request_allowed(session.remote_ip)
|
||||
.await?;
|
||||
|
||||
return self.handle_oidc_metadata().await;
|
||||
return self.handle_oidc_metadata(false).await;
|
||||
}
|
||||
("acme-challenge", &Method::GET) if self.has_acme_http_providers() => {
|
||||
if let Some(token) = path.next() {
|
||||
|
||||
@@ -38,6 +38,7 @@ use store::{
|
||||
write::{AnyKey, BatchBuilder},
|
||||
};
|
||||
use types::id::Id;
|
||||
use utils::is_valid_domain;
|
||||
|
||||
pub(crate) async fn bootstrap_get(
|
||||
mut get: RegistryGetResponse<'_>,
|
||||
@@ -505,15 +506,6 @@ pub(crate) async fn bootstrap_set(
|
||||
Ok(set)
|
||||
}
|
||||
|
||||
fn is_valid_domain(hostname: &str) -> bool {
|
||||
const RESERVED_TLDS: &[&str] = &["test", "localhost", "local", "internal"];
|
||||
psl::domain_str(hostname).is_some()
|
||||
|| RESERVED_TLDS.contains(&hostname)
|
||||
|| hostname
|
||||
.rsplit_once('.')
|
||||
.is_some_and(|(_, tld)| RESERVED_TLDS.contains(&tld))
|
||||
}
|
||||
|
||||
async fn write_object(registry: &RegistryStore, object: &Object) -> Result<Id, SetError<Property>> {
|
||||
match registry.write(RegistryWrite::insert(object)).await {
|
||||
Ok(RegistryWriteResult::Success(id)) => Ok(id),
|
||||
|
||||
@@ -340,12 +340,18 @@ pub fn sanitize_domain(domain: &str) -> Option<String> {
|
||||
}
|
||||
}
|
||||
|
||||
if found_dot
|
||||
&& last_ch != '.'
|
||||
&& psl::domain(result.as_bytes()).is_some_and(|d| d.suffix().typ().is_some())
|
||||
{
|
||||
if found_dot && last_ch != '.' && is_valid_domain(&result) {
|
||||
Some(result)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_valid_domain(domain: &str) -> bool {
|
||||
const RESERVED_TLDS: &[&str] = &["test", "localhost", "local", "internal"];
|
||||
psl::domain(domain.as_bytes()).is_some_and(|d| d.suffix().typ().is_some())
|
||||
|| RESERVED_TLDS.contains(&domain)
|
||||
|| domain
|
||||
.rsplit_once('.')
|
||||
.is_some_and(|(_, tld)| RESERVED_TLDS.contains(&tld))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user