add timestamp to random code
This commit is contained in:
@@ -2,7 +2,9 @@ use super::{models::*, AppState};
|
|||||||
use crate::auth::Claims;
|
use crate::auth::Claims;
|
||||||
use crate::discord_bot::{send_domain_approval_request, DomainRegistration};
|
use crate::discord_bot::{send_domain_approval_request, DomainRegistration};
|
||||||
use gurt::prelude::*;
|
use gurt::prelude::*;
|
||||||
use std::{env, collections::HashMap};
|
use sha2::{Digest, Sha256};
|
||||||
|
use std::time::{SystemTime, UNIX_EPOCH};
|
||||||
|
use std::{collections::HashMap, env};
|
||||||
|
|
||||||
const VALID_DNS_RECORD_TYPES: &[&str] = &["A", "AAAA", "CNAME", "TXT"];
|
const VALID_DNS_RECORD_TYPES: &[&str] = &["A", "AAAA", "CNAME", "TXT"];
|
||||||
|
|
||||||
@@ -31,17 +33,26 @@ pub(crate) async fn create_logic(domain: Domain, user_id: i32, app: &AppState) -
|
|||||||
|| domain.name.len() > 24
|
|| domain.name.len() > 24
|
||||||
|| domain.name.is_empty()
|
|| domain.name.is_empty()
|
||||||
|| domain.name.starts_with('-')
|
|| domain.name.starts_with('-')
|
||||||
|| domain.name.ends_with('-') {
|
|| domain.name.ends_with('-')
|
||||||
return Err(GurtError::invalid_message("Invalid name, non-existent TLD, or name too long (24 chars)."));
|
{
|
||||||
|
return Err(GurtError::invalid_message(
|
||||||
|
"Invalid name, non-existent TLD, or name too long (24 chars).",
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
if app.config.offen_words().iter().any(|word| domain.name.contains(word)) {
|
if app
|
||||||
return Err(GurtError::invalid_message("The given domain name is offensive."));
|
.config
|
||||||
|
.offen_words()
|
||||||
|
.iter()
|
||||||
|
.any(|word| domain.name.contains(word))
|
||||||
|
{
|
||||||
|
return Err(GurtError::invalid_message(
|
||||||
|
"The given domain name is offensive.",
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
let existing_count: i64 = sqlx::query_scalar(
|
let existing_count: i64 =
|
||||||
"SELECT COUNT(*) FROM domains WHERE name = $1 AND tld = $2"
|
sqlx::query_scalar("SELECT COUNT(*) FROM domains WHERE name = $1 AND tld = $2")
|
||||||
)
|
|
||||||
.bind(&domain.name)
|
.bind(&domain.name)
|
||||||
.bind(&domain.tld)
|
.bind(&domain.tld)
|
||||||
.fetch_one(&app.db)
|
.fetch_one(&app.db)
|
||||||
@@ -73,7 +84,9 @@ pub(crate) async fn create_logic(domain: Domain, user_id: i32, app: &AppState) -
|
|||||||
let domain_id = domain_row.0;
|
let domain_id = domain_row.0;
|
||||||
|
|
||||||
// Decrease user's registrations remaining
|
// Decrease user's registrations remaining
|
||||||
sqlx::query("UPDATE users SET registrations_remaining = registrations_remaining - 1 WHERE id = $1")
|
sqlx::query(
|
||||||
|
"UPDATE users SET registrations_remaining = registrations_remaining - 1 WHERE id = $1",
|
||||||
|
)
|
||||||
.bind(user_id)
|
.bind(user_id)
|
||||||
.execute(&app.db)
|
.execute(&app.db)
|
||||||
.await
|
.await
|
||||||
@@ -92,11 +105,9 @@ pub(crate) async fn create_logic(domain: Domain, user_id: i32, app: &AppState) -
|
|||||||
let bot_token = app.config.discord.bot_token.clone();
|
let bot_token = app.config.discord.bot_token.clone();
|
||||||
|
|
||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
if let Err(e) = send_domain_approval_request(
|
if let Err(e) =
|
||||||
channel_id,
|
send_domain_approval_request(channel_id, domain_registration, &bot_token).await
|
||||||
domain_registration,
|
{
|
||||||
&bot_token,
|
|
||||||
).await {
|
|
||||||
log::error!("Failed to send Discord notification: {}", e);
|
log::error!("Failed to send Discord notification: {}", e);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -105,7 +116,11 @@ pub(crate) async fn create_logic(domain: Domain, user_id: i32, app: &AppState) -
|
|||||||
Ok(domain)
|
Ok(domain)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn create_domain(ctx: &ServerContext, app_state: AppState, claims: Claims) -> Result<GurtResponse> {
|
pub(crate) async fn create_domain(
|
||||||
|
ctx: &ServerContext,
|
||||||
|
app_state: AppState,
|
||||||
|
claims: Claims,
|
||||||
|
) -> Result<GurtResponse> {
|
||||||
// Check if user has registrations remaining
|
// Check if user has registrations remaining
|
||||||
let user: (i32,) = sqlx::query_as("SELECT registrations_remaining FROM users WHERE id = $1")
|
let user: (i32,) = sqlx::query_as("SELECT registrations_remaining FROM users WHERE id = $1")
|
||||||
.bind(claims.user_id)
|
.bind(claims.user_id)
|
||||||
@@ -124,29 +139,31 @@ pub(crate) async fn create_domain(ctx: &ServerContext, app_state: AppState, clai
|
|||||||
.map_err(|_| GurtError::invalid_message("Invalid JSON"))?;
|
.map_err(|_| GurtError::invalid_message("Invalid JSON"))?;
|
||||||
|
|
||||||
match create_logic(domain.clone(), claims.user_id, &app_state).await {
|
match create_logic(domain.clone(), claims.user_id, &app_state).await {
|
||||||
Ok(created_domain) => {
|
Ok(created_domain) => Ok(GurtResponse::ok().with_json_body(&created_domain)?),
|
||||||
Ok(GurtResponse::ok().with_json_body(&created_domain)?)
|
Err(e) => Ok(GurtResponse::bad_request().with_json_body(&Error {
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
Ok(GurtResponse::bad_request().with_json_body(&Error {
|
|
||||||
msg: "Failed to create domain",
|
msg: "Failed to create domain",
|
||||||
error: e.to_string(),
|
error: e.to_string(),
|
||||||
})?)
|
})?),
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn get_domain(ctx: &ServerContext, app_state: AppState, claims: Claims) -> Result<GurtResponse> {
|
pub(crate) async fn get_domain(
|
||||||
|
ctx: &ServerContext,
|
||||||
|
app_state: AppState,
|
||||||
|
claims: Claims,
|
||||||
|
) -> Result<GurtResponse> {
|
||||||
let path_parts: Vec<&str> = ctx.path().split('/').collect();
|
let path_parts: Vec<&str> = ctx.path().split('/').collect();
|
||||||
if path_parts.len() < 3 {
|
if path_parts.len() < 3 {
|
||||||
return Ok(GurtResponse::bad_request().with_string_body("Invalid path format. Expected /domain/{domainName}"));
|
return Ok(GurtResponse::bad_request()
|
||||||
|
.with_string_body("Invalid path format. Expected /domain/{domainName}"));
|
||||||
}
|
}
|
||||||
|
|
||||||
let domain_name = path_parts[2];
|
let domain_name = path_parts[2];
|
||||||
|
|
||||||
let domain_parts: Vec<&str> = domain_name.split('.').collect();
|
let domain_parts: Vec<&str> = domain_name.split('.').collect();
|
||||||
if domain_parts.len() < 2 {
|
if domain_parts.len() < 2 {
|
||||||
return Ok(GurtResponse::bad_request().with_string_body("Invalid domain format. Expected name.tld"));
|
return Ok(GurtResponse::bad_request()
|
||||||
|
.with_string_body("Invalid domain format. Expected name.tld"));
|
||||||
}
|
}
|
||||||
|
|
||||||
let name = domain_parts[0];
|
let name = domain_parts[0];
|
||||||
@@ -171,7 +188,7 @@ pub(crate) async fn get_domain(ctx: &ServerContext, app_state: AppState, claims:
|
|||||||
};
|
};
|
||||||
Ok(GurtResponse::ok().with_json_body(&response_domain)?)
|
Ok(GurtResponse::ok().with_json_body(&response_domain)?)
|
||||||
}
|
}
|
||||||
None => Ok(GurtResponse::not_found().with_string_body("Domain not found"))
|
None => Ok(GurtResponse::not_found().with_string_body("Domain not found")),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -185,12 +202,14 @@ pub(crate) async fn get_domains(ctx: &ServerContext, app_state: AppState) -> Res
|
|||||||
HashMap::new()
|
HashMap::new()
|
||||||
};
|
};
|
||||||
|
|
||||||
let page = query_params.get("page")
|
let page = query_params
|
||||||
|
.get("page")
|
||||||
.and_then(|p| p.parse::<u32>().ok())
|
.and_then(|p| p.parse::<u32>().ok())
|
||||||
.unwrap_or(1)
|
.unwrap_or(1)
|
||||||
.max(1); // Ensure page is at least 1
|
.max(1); // Ensure page is at least 1
|
||||||
|
|
||||||
let page_size = query_params.get("limit")
|
let page_size = query_params
|
||||||
|
.get("limit")
|
||||||
.and_then(|l| l.parse::<u32>().ok())
|
.and_then(|l| l.parse::<u32>().ok())
|
||||||
.unwrap_or(100)
|
.unwrap_or(100)
|
||||||
.clamp(1, 1000); // Limit between 1 and 1000
|
.clamp(1, 1000); // Limit between 1 and 1000
|
||||||
@@ -206,13 +225,14 @@ pub(crate) async fn get_domains(ctx: &ServerContext, app_state: AppState) -> Res
|
|||||||
.await
|
.await
|
||||||
.map_err(|_| GurtError::invalid_message("Database error"))?;
|
.map_err(|_| GurtError::invalid_message("Database error"))?;
|
||||||
|
|
||||||
let response_domains: Vec<ResponseDomain> = domains.into_iter().map(|domain| {
|
let response_domains: Vec<ResponseDomain> = domains
|
||||||
ResponseDomain {
|
.into_iter()
|
||||||
|
.map(|domain| ResponseDomain {
|
||||||
name: domain.name,
|
name: domain.name,
|
||||||
tld: domain.tld,
|
tld: domain.tld,
|
||||||
records: None,
|
records: None,
|
||||||
}
|
})
|
||||||
}).collect();
|
.collect();
|
||||||
|
|
||||||
let response = PaginationResponse {
|
let response = PaginationResponse {
|
||||||
domains: response_domains,
|
domains: response_domains,
|
||||||
@@ -233,12 +253,15 @@ pub(crate) async fn check_domain(ctx: &ServerContext, app_state: AppState) -> Re
|
|||||||
let query_string = &path[query_start + 1..];
|
let query_string = &path[query_start + 1..];
|
||||||
parse_query_string(query_string)
|
parse_query_string(query_string)
|
||||||
} else {
|
} else {
|
||||||
return Ok(GurtResponse::bad_request().with_string_body("Missing query parameters. Expected ?name=<name>&tld=<tld>"));
|
return Ok(GurtResponse::bad_request()
|
||||||
|
.with_string_body("Missing query parameters. Expected ?name=<name>&tld=<tld>"));
|
||||||
};
|
};
|
||||||
|
|
||||||
let name = query_params.get("name")
|
let name = query_params
|
||||||
|
.get("name")
|
||||||
.ok_or_else(|| GurtError::invalid_message("Missing 'name' parameter"))?;
|
.ok_or_else(|| GurtError::invalid_message("Missing 'name' parameter"))?;
|
||||||
let tld = query_params.get("tld")
|
let tld = query_params
|
||||||
|
.get("tld")
|
||||||
.ok_or_else(|| GurtError::invalid_message("Missing 'tld' parameter"))?;
|
.ok_or_else(|| GurtError::invalid_message("Missing 'tld' parameter"))?;
|
||||||
|
|
||||||
let domain: Option<Domain> = sqlx::query_as::<_, Domain>(
|
let domain: Option<Domain> = sqlx::query_as::<_, Domain>(
|
||||||
@@ -258,14 +281,24 @@ pub(crate) async fn check_domain(ctx: &ServerContext, app_state: AppState) -> Re
|
|||||||
Ok(GurtResponse::ok().with_json_body(&domain_list)?)
|
Ok(GurtResponse::ok().with_json_body(&domain_list)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn update_domain(_ctx: &ServerContext, _app_state: AppState, _claims: Claims) -> Result<GurtResponse> {
|
pub(crate) async fn update_domain(
|
||||||
return Ok(GurtResponse::bad_request().with_string_body("Domain updates are no longer supported. Use DNS records instead."));
|
_ctx: &ServerContext,
|
||||||
|
_app_state: AppState,
|
||||||
|
_claims: Claims,
|
||||||
|
) -> Result<GurtResponse> {
|
||||||
|
return Ok(GurtResponse::bad_request()
|
||||||
|
.with_string_body("Domain updates are no longer supported. Use DNS records instead."));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn delete_domain(ctx: &ServerContext, app_state: AppState, claims: Claims) -> Result<GurtResponse> {
|
pub(crate) async fn delete_domain(
|
||||||
|
ctx: &ServerContext,
|
||||||
|
app_state: AppState,
|
||||||
|
claims: Claims,
|
||||||
|
) -> Result<GurtResponse> {
|
||||||
let path_parts: Vec<&str> = ctx.path().split('/').collect();
|
let path_parts: Vec<&str> = ctx.path().split('/').collect();
|
||||||
if path_parts.len() < 4 {
|
if path_parts.len() < 4 {
|
||||||
return Ok(GurtResponse::bad_request().with_string_body("Invalid path format. Expected /domain/{name}/{tld}"));
|
return Ok(GurtResponse::bad_request()
|
||||||
|
.with_string_body("Invalid path format. Expected /domain/{name}/{tld}"));
|
||||||
}
|
}
|
||||||
|
|
||||||
let name = path_parts[2];
|
let name = path_parts[2];
|
||||||
@@ -297,7 +330,11 @@ pub(crate) async fn delete_domain(ctx: &ServerContext, app_state: AppState, clai
|
|||||||
Ok(GurtResponse::ok().with_string_body("Domain deleted successfully"))
|
Ok(GurtResponse::ok().with_string_body("Domain deleted successfully"))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn get_user_domains(ctx: &ServerContext, app_state: AppState, claims: Claims) -> Result<GurtResponse> {
|
pub(crate) async fn get_user_domains(
|
||||||
|
ctx: &ServerContext,
|
||||||
|
app_state: AppState,
|
||||||
|
claims: Claims,
|
||||||
|
) -> Result<GurtResponse> {
|
||||||
// Parse pagination from query parameters
|
// Parse pagination from query parameters
|
||||||
let path = ctx.path();
|
let path = ctx.path();
|
||||||
let query_params = if let Some(query_start) = path.find('?') {
|
let query_params = if let Some(query_start) = path.find('?') {
|
||||||
@@ -307,12 +344,14 @@ pub(crate) async fn get_user_domains(ctx: &ServerContext, app_state: AppState, c
|
|||||||
HashMap::new()
|
HashMap::new()
|
||||||
};
|
};
|
||||||
|
|
||||||
let page = query_params.get("page")
|
let page = query_params
|
||||||
|
.get("page")
|
||||||
.and_then(|p| p.parse::<u32>().ok())
|
.and_then(|p| p.parse::<u32>().ok())
|
||||||
.unwrap_or(1)
|
.unwrap_or(1)
|
||||||
.max(1);
|
.max(1);
|
||||||
|
|
||||||
let page_size = query_params.get("limit")
|
let page_size = query_params
|
||||||
|
.get("limit")
|
||||||
.and_then(|l| l.parse::<u32>().ok())
|
.and_then(|l| l.parse::<u32>().ok())
|
||||||
.unwrap_or(100)
|
.unwrap_or(100)
|
||||||
.clamp(1, 1000);
|
.clamp(1, 1000);
|
||||||
@@ -329,14 +368,15 @@ pub(crate) async fn get_user_domains(ctx: &ServerContext, app_state: AppState, c
|
|||||||
.await
|
.await
|
||||||
.map_err(|_| GurtError::invalid_message("Database error"))?;
|
.map_err(|_| GurtError::invalid_message("Database error"))?;
|
||||||
|
|
||||||
let response_domains: Vec<UserDomain> = domains.into_iter().map(|domain| {
|
let response_domains: Vec<UserDomain> = domains
|
||||||
UserDomain {
|
.into_iter()
|
||||||
|
.map(|domain| UserDomain {
|
||||||
name: domain.name,
|
name: domain.name,
|
||||||
tld: domain.tld,
|
tld: domain.tld,
|
||||||
status: domain.status.unwrap_or_else(|| "pending".to_string()),
|
status: domain.status.unwrap_or_else(|| "pending".to_string()),
|
||||||
denial_reason: domain.denial_reason,
|
denial_reason: domain.denial_reason,
|
||||||
}
|
})
|
||||||
}).collect();
|
.collect();
|
||||||
|
|
||||||
let response = UserDomainResponse {
|
let response = UserDomainResponse {
|
||||||
domains: response_domains,
|
domains: response_domains,
|
||||||
@@ -347,17 +387,23 @@ pub(crate) async fn get_user_domains(ctx: &ServerContext, app_state: AppState, c
|
|||||||
Ok(GurtResponse::ok().with_json_body(&response)?)
|
Ok(GurtResponse::ok().with_json_body(&response)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn get_domain_records(ctx: &ServerContext, app_state: AppState, claims: Claims) -> Result<GurtResponse> {
|
pub(crate) async fn get_domain_records(
|
||||||
|
ctx: &ServerContext,
|
||||||
|
app_state: AppState,
|
||||||
|
claims: Claims,
|
||||||
|
) -> Result<GurtResponse> {
|
||||||
let path_parts: Vec<&str> = ctx.path().split('/').collect();
|
let path_parts: Vec<&str> = ctx.path().split('/').collect();
|
||||||
if path_parts.len() < 4 {
|
if path_parts.len() < 4 {
|
||||||
return Ok(GurtResponse::bad_request().with_string_body("Invalid path format. Expected /domain/{domainName}/records"));
|
return Ok(GurtResponse::bad_request()
|
||||||
|
.with_string_body("Invalid path format. Expected /domain/{domainName}/records"));
|
||||||
}
|
}
|
||||||
|
|
||||||
let domain_name = path_parts[2];
|
let domain_name = path_parts[2];
|
||||||
|
|
||||||
let domain_parts: Vec<&str> = domain_name.split('.').collect();
|
let domain_parts: Vec<&str> = domain_name.split('.').collect();
|
||||||
if domain_parts.len() < 2 {
|
if domain_parts.len() < 2 {
|
||||||
return Ok(GurtResponse::bad_request().with_string_body("Invalid domain format. Expected name.tld"));
|
return Ok(GurtResponse::bad_request()
|
||||||
|
.with_string_body("Invalid domain format. Expected name.tld"));
|
||||||
}
|
}
|
||||||
|
|
||||||
let name = domain_parts[0];
|
let name = domain_parts[0];
|
||||||
@@ -375,7 +421,11 @@ pub(crate) async fn get_domain_records(ctx: &ServerContext, app_state: AppState,
|
|||||||
|
|
||||||
let domain = match domain {
|
let domain = match domain {
|
||||||
Some(d) => d,
|
Some(d) => d,
|
||||||
None => return Ok(GurtResponse::not_found().with_string_body("Domain not found or access denied"))
|
None => {
|
||||||
|
return Ok(
|
||||||
|
GurtResponse::not_found().with_string_body("Domain not found or access denied")
|
||||||
|
)
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let records: Vec<DnsRecord> = sqlx::query_as::<_, DnsRecord>(
|
let records: Vec<DnsRecord> = sqlx::query_as::<_, DnsRecord>(
|
||||||
@@ -386,31 +436,38 @@ pub(crate) async fn get_domain_records(ctx: &ServerContext, app_state: AppState,
|
|||||||
.await
|
.await
|
||||||
.map_err(|_| GurtError::invalid_message("Database error"))?;
|
.map_err(|_| GurtError::invalid_message("Database error"))?;
|
||||||
|
|
||||||
let response_records: Vec<ResponseDnsRecord> = records.into_iter().map(|record| {
|
let response_records: Vec<ResponseDnsRecord> = records
|
||||||
ResponseDnsRecord {
|
.into_iter()
|
||||||
|
.map(|record| ResponseDnsRecord {
|
||||||
id: record.id.unwrap(),
|
id: record.id.unwrap(),
|
||||||
record_type: record.record_type,
|
record_type: record.record_type,
|
||||||
name: record.name,
|
name: record.name,
|
||||||
value: record.value,
|
value: record.value,
|
||||||
ttl: record.ttl,
|
ttl: record.ttl,
|
||||||
priority: record.priority,
|
priority: record.priority,
|
||||||
}
|
})
|
||||||
}).collect();
|
.collect();
|
||||||
|
|
||||||
Ok(GurtResponse::ok().with_json_body(&response_records)?)
|
Ok(GurtResponse::ok().with_json_body(&response_records)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn create_domain_record(ctx: &ServerContext, app_state: AppState, claims: Claims) -> Result<GurtResponse> {
|
pub(crate) async fn create_domain_record(
|
||||||
|
ctx: &ServerContext,
|
||||||
|
app_state: AppState,
|
||||||
|
claims: Claims,
|
||||||
|
) -> Result<GurtResponse> {
|
||||||
let path_parts: Vec<&str> = ctx.path().split('/').collect();
|
let path_parts: Vec<&str> = ctx.path().split('/').collect();
|
||||||
if path_parts.len() < 4 {
|
if path_parts.len() < 4 {
|
||||||
return Ok(GurtResponse::bad_request().with_string_body("Invalid path format. Expected /domain/{domainName}/records"));
|
return Ok(GurtResponse::bad_request()
|
||||||
|
.with_string_body("Invalid path format. Expected /domain/{domainName}/records"));
|
||||||
}
|
}
|
||||||
|
|
||||||
let domain_name = path_parts[2];
|
let domain_name = path_parts[2];
|
||||||
|
|
||||||
let domain_parts: Vec<&str> = domain_name.split('.').collect();
|
let domain_parts: Vec<&str> = domain_name.split('.').collect();
|
||||||
if domain_parts.len() < 2 {
|
if domain_parts.len() < 2 {
|
||||||
return Ok(GurtResponse::bad_request().with_string_body("Invalid domain format. Expected name.tld"));
|
return Ok(GurtResponse::bad_request()
|
||||||
|
.with_string_body("Invalid domain format. Expected name.tld"));
|
||||||
}
|
}
|
||||||
|
|
||||||
let name = domain_parts[0];
|
let name = domain_parts[0];
|
||||||
@@ -428,7 +485,11 @@ pub(crate) async fn create_domain_record(ctx: &ServerContext, app_state: AppStat
|
|||||||
|
|
||||||
let domain = match domain {
|
let domain = match domain {
|
||||||
Some(d) => d,
|
Some(d) => d,
|
||||||
None => return Ok(GurtResponse::not_found().with_string_body("Domain not found or access denied"))
|
None => {
|
||||||
|
return Ok(
|
||||||
|
GurtResponse::not_found().with_string_body("Domain not found or access denied")
|
||||||
|
)
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let record_data: CreateDnsRecord = {
|
let record_data: CreateDnsRecord = {
|
||||||
@@ -436,8 +497,7 @@ pub(crate) async fn create_domain_record(ctx: &ServerContext, app_state: AppStat
|
|||||||
let body_str = std::str::from_utf8(body_bytes).unwrap_or("<invalid utf8>");
|
let body_str = std::str::from_utf8(body_bytes).unwrap_or("<invalid utf8>");
|
||||||
log::info!("Received JSON body: {}", body_str);
|
log::info!("Received JSON body: {}", body_str);
|
||||||
|
|
||||||
serde_json::from_slice(body_bytes)
|
serde_json::from_slice(body_bytes).map_err(|e| {
|
||||||
.map_err(|e| {
|
|
||||||
log::error!("JSON parsing error: {} for body: {}", e, body_str);
|
log::error!("JSON parsing error: {} for body: {}", e, body_str);
|
||||||
GurtError::invalid_message("Invalid JSON")
|
GurtError::invalid_message("Invalid JSON")
|
||||||
})?
|
})?
|
||||||
@@ -448,7 +508,9 @@ pub(crate) async fn create_domain_record(ctx: &ServerContext, app_state: AppStat
|
|||||||
}
|
}
|
||||||
|
|
||||||
if !VALID_DNS_RECORD_TYPES.contains(&record_data.record_type.as_str()) {
|
if !VALID_DNS_RECORD_TYPES.contains(&record_data.record_type.as_str()) {
|
||||||
return Ok(GurtResponse::bad_request().with_string_body("Invalid record type. Only A, AAAA, CNAME, and TXT records are supported."));
|
return Ok(GurtResponse::bad_request().with_string_body(
|
||||||
|
"Invalid record type. Only A, AAAA, CNAME, and TXT records are supported.",
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
let record_name = record_data.name.unwrap_or_else(|| "@".to_string());
|
let record_name = record_data.name.unwrap_or_else(|| "@".to_string());
|
||||||
@@ -457,22 +519,25 @@ pub(crate) async fn create_domain_record(ctx: &ServerContext, app_state: AppStat
|
|||||||
match record_data.record_type.as_str() {
|
match record_data.record_type.as_str() {
|
||||||
"A" => {
|
"A" => {
|
||||||
if !record_data.value.parse::<std::net::Ipv4Addr>().is_ok() {
|
if !record_data.value.parse::<std::net::Ipv4Addr>().is_ok() {
|
||||||
return Ok(GurtResponse::bad_request().with_string_body("Invalid IPv4 address for A record"));
|
return Ok(GurtResponse::bad_request()
|
||||||
|
.with_string_body("Invalid IPv4 address for A record"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
|
||||||
"AAAA" => {
|
"AAAA" => {
|
||||||
if !record_data.value.parse::<std::net::Ipv6Addr>().is_ok() {
|
if !record_data.value.parse::<std::net::Ipv6Addr>().is_ok() {
|
||||||
return Ok(GurtResponse::bad_request().with_string_body("Invalid IPv6 address for AAAA record"));
|
return Ok(GurtResponse::bad_request()
|
||||||
|
.with_string_body("Invalid IPv6 address for AAAA record"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
|
||||||
"CNAME" => {
|
"CNAME" => {
|
||||||
if record_data.value.is_empty() || !record_data.value.contains('.') {
|
if record_data.value.is_empty() || !record_data.value.contains('.') {
|
||||||
return Ok(GurtResponse::bad_request().with_string_body("CNAME records must contain a valid domain name"));
|
return Ok(GurtResponse::bad_request()
|
||||||
|
.with_string_body("CNAME records must contain a valid domain name"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
|
||||||
"TXT" => {
|
"TXT" => {
|
||||||
// TXT records can contain any text
|
// TXT records can contain any text
|
||||||
},
|
}
|
||||||
_ => {
|
_ => {
|
||||||
return Ok(GurtResponse::bad_request().with_string_body("Invalid record type"));
|
return Ok(GurtResponse::bad_request().with_string_body("Invalid record type"));
|
||||||
}
|
}
|
||||||
@@ -506,21 +571,29 @@ pub(crate) async fn create_domain_record(ctx: &ServerContext, app_state: AppStat
|
|||||||
Ok(GurtResponse::ok().with_json_body(&response_record)?)
|
Ok(GurtResponse::ok().with_json_body(&response_record)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn delete_domain_record(ctx: &ServerContext, app_state: AppState, claims: Claims) -> Result<GurtResponse> {
|
pub(crate) async fn delete_domain_record(
|
||||||
|
ctx: &ServerContext,
|
||||||
|
app_state: AppState,
|
||||||
|
claims: Claims,
|
||||||
|
) -> Result<GurtResponse> {
|
||||||
let path_parts: Vec<&str> = ctx.path().split('/').collect();
|
let path_parts: Vec<&str> = ctx.path().split('/').collect();
|
||||||
if path_parts.len() < 5 {
|
if path_parts.len() < 5 {
|
||||||
return Ok(GurtResponse::bad_request().with_string_body("Invalid path format. Expected /domain/{domainName}/records/{recordId}"));
|
return Ok(GurtResponse::bad_request().with_string_body(
|
||||||
|
"Invalid path format. Expected /domain/{domainName}/records/{recordId}",
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
let domain_name = path_parts[2];
|
let domain_name = path_parts[2];
|
||||||
let record_id_str = path_parts[4];
|
let record_id_str = path_parts[4];
|
||||||
|
|
||||||
let record_id: i32 = record_id_str.parse()
|
let record_id: i32 = record_id_str
|
||||||
|
.parse()
|
||||||
.map_err(|_| GurtError::invalid_message("Invalid record ID"))?;
|
.map_err(|_| GurtError::invalid_message("Invalid record ID"))?;
|
||||||
|
|
||||||
let domain_parts: Vec<&str> = domain_name.split('.').collect();
|
let domain_parts: Vec<&str> = domain_name.split('.').collect();
|
||||||
if domain_parts.len() < 2 {
|
if domain_parts.len() < 2 {
|
||||||
return Ok(GurtResponse::bad_request().with_string_body("Invalid domain format. Expected name.tld"));
|
return Ok(GurtResponse::bad_request()
|
||||||
|
.with_string_body("Invalid domain format. Expected name.tld"));
|
||||||
}
|
}
|
||||||
|
|
||||||
let name = domain_parts[0];
|
let name = domain_parts[0];
|
||||||
@@ -538,7 +611,11 @@ pub(crate) async fn delete_domain_record(ctx: &ServerContext, app_state: AppStat
|
|||||||
|
|
||||||
let domain = match domain {
|
let domain = match domain {
|
||||||
Some(d) => d,
|
Some(d) => d,
|
||||||
None => return Ok(GurtResponse::not_found().with_string_body("Domain not found or access denied"))
|
None => {
|
||||||
|
return Ok(
|
||||||
|
GurtResponse::not_found().with_string_body("Domain not found or access denied")
|
||||||
|
)
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let rows_affected = sqlx::query("DELETE FROM dns_records WHERE id = $1 AND domain_id = $2")
|
let rows_affected = sqlx::query("DELETE FROM dns_records WHERE id = $1 AND domain_id = $2")
|
||||||
@@ -556,7 +633,10 @@ pub(crate) async fn delete_domain_record(ctx: &ServerContext, app_state: AppStat
|
|||||||
Ok(GurtResponse::ok().with_string_body("DNS record deleted successfully"))
|
Ok(GurtResponse::ok().with_string_body("DNS record deleted successfully"))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn resolve_domain(ctx: &ServerContext, app_state: AppState) -> Result<GurtResponse> {
|
pub(crate) async fn resolve_domain(
|
||||||
|
ctx: &ServerContext,
|
||||||
|
app_state: AppState,
|
||||||
|
) -> Result<GurtResponse> {
|
||||||
let resolution_request: DnsResolutionRequest = serde_json::from_slice(ctx.body())
|
let resolution_request: DnsResolutionRequest = serde_json::from_slice(ctx.body())
|
||||||
.map_err(|_| GurtError::invalid_message("Invalid JSON"))?;
|
.map_err(|_| GurtError::invalid_message("Invalid JSON"))?;
|
||||||
|
|
||||||
@@ -572,7 +652,10 @@ pub(crate) async fn resolve_domain(ctx: &ServerContext, app_state: AppState) ->
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn resolve_dns_with_delegation(query_name: &str, app_state: &AppState) -> Result<DnsResolutionResponse> {
|
async fn resolve_dns_with_delegation(
|
||||||
|
query_name: &str,
|
||||||
|
app_state: &AppState,
|
||||||
|
) -> Result<DnsResolutionResponse> {
|
||||||
// Parse the query domain
|
// Parse the query domain
|
||||||
let parts: Vec<&str> = query_name.split('.').collect();
|
let parts: Vec<&str> = query_name.split('.').collect();
|
||||||
if parts.len() < 2 {
|
if parts.len() < 2 {
|
||||||
@@ -591,10 +674,16 @@ async fn resolve_dns_with_delegation(query_name: &str, app_state: &AppState) ->
|
|||||||
return Ok(response);
|
return Ok(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
Err(GurtError::invalid_message("No matching records or delegation found"))
|
Err(GurtError::invalid_message(
|
||||||
|
"No matching records or delegation found",
|
||||||
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn try_exact_match(query_name: &str, tld: &str, app_state: &AppState) -> Result<Option<DnsResolutionResponse>> {
|
async fn try_exact_match(
|
||||||
|
query_name: &str,
|
||||||
|
tld: &str,
|
||||||
|
app_state: &AppState,
|
||||||
|
) -> Result<Option<DnsResolutionResponse>> {
|
||||||
let parts: Vec<&str> = query_name.split('.').collect();
|
let parts: Vec<&str> = query_name.split('.').collect();
|
||||||
if parts.len() < 2 {
|
if parts.len() < 2 {
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
@@ -632,16 +721,17 @@ async fn try_exact_match(query_name: &str, tld: &str, app_state: &AppState) -> R
|
|||||||
.map_err(|_| GurtError::invalid_message("Database error"))?;
|
.map_err(|_| GurtError::invalid_message("Database error"))?;
|
||||||
|
|
||||||
if !records.is_empty() {
|
if !records.is_empty() {
|
||||||
let response_records: Vec<ResponseDnsRecord> = records.into_iter().map(|record| {
|
let response_records: Vec<ResponseDnsRecord> = records
|
||||||
ResponseDnsRecord {
|
.into_iter()
|
||||||
|
.map(|record| ResponseDnsRecord {
|
||||||
id: record.id.unwrap(),
|
id: record.id.unwrap(),
|
||||||
record_type: record.record_type,
|
record_type: record.record_type,
|
||||||
name: record.name,
|
name: record.name,
|
||||||
value: record.value,
|
value: record.value,
|
||||||
ttl: record.ttl,
|
ttl: record.ttl,
|
||||||
priority: record.priority,
|
priority: record.priority,
|
||||||
}
|
})
|
||||||
}).collect();
|
.collect();
|
||||||
|
|
||||||
return Ok(Some(DnsResolutionResponse {
|
return Ok(Some(DnsResolutionResponse {
|
||||||
name: query_name.to_string(),
|
name: query_name.to_string(),
|
||||||
@@ -655,7 +745,11 @@ async fn try_exact_match(query_name: &str, tld: &str, app_state: &AppState) -> R
|
|||||||
Ok(None)
|
Ok(None)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn try_delegation_match(query_name: &str, tld: &str, app_state: &AppState) -> Result<Option<DnsResolutionResponse>> {
|
async fn try_delegation_match(
|
||||||
|
query_name: &str,
|
||||||
|
tld: &str,
|
||||||
|
app_state: &AppState,
|
||||||
|
) -> Result<Option<DnsResolutionResponse>> {
|
||||||
let parts: Vec<&str> = query_name.split('.').collect();
|
let parts: Vec<&str> = query_name.split('.').collect();
|
||||||
|
|
||||||
// Try to find NS records for parent domains
|
// Try to find NS records for parent domains
|
||||||
@@ -697,9 +791,9 @@ async fn try_delegation_match(query_name: &str, tld: &str, app_state: &AppState)
|
|||||||
// Get glue records for NS entries that point to subdomains of this zone
|
// Get glue records for NS entries that point to subdomains of this zone
|
||||||
for ns_record in &all_records.clone() {
|
for ns_record in &all_records.clone() {
|
||||||
let ns_host = &ns_record.value;
|
let ns_host = &ns_record.value;
|
||||||
if ns_host.ends_with(&format!(".{}.{}", domain_name, tld)) ||
|
if ns_host.ends_with(&format!(".{}.{}", domain_name, tld))
|
||||||
ns_host == &format!("{}.{}", domain_name, tld) {
|
|| ns_host == &format!("{}.{}", domain_name, tld)
|
||||||
|
{
|
||||||
let glue_records: Vec<DnsRecord> = sqlx::query_as::<_, DnsRecord>(
|
let glue_records: Vec<DnsRecord> = sqlx::query_as::<_, DnsRecord>(
|
||||||
"SELECT id, domain_id, record_type, name, value, ttl, priority, created_at FROM dns_records WHERE domain_id = $1 AND (record_type = 'A' OR record_type = 'AAAA') AND value = $2"
|
"SELECT id, domain_id, record_type, name, value, ttl, priority, created_at FROM dns_records WHERE domain_id = $1 AND (record_type = 'A' OR record_type = 'AAAA') AND value = $2"
|
||||||
)
|
)
|
||||||
@@ -713,16 +807,17 @@ async fn try_delegation_match(query_name: &str, tld: &str, app_state: &AppState)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let response_records: Vec<ResponseDnsRecord> = all_records.into_iter().map(|record| {
|
let response_records: Vec<ResponseDnsRecord> = all_records
|
||||||
ResponseDnsRecord {
|
.into_iter()
|
||||||
|
.map(|record| ResponseDnsRecord {
|
||||||
id: record.id.unwrap(),
|
id: record.id.unwrap(),
|
||||||
record_type: record.record_type,
|
record_type: record.record_type,
|
||||||
name: record.name,
|
name: record.name,
|
||||||
value: record.value,
|
value: record.value,
|
||||||
ttl: record.ttl,
|
ttl: record.ttl,
|
||||||
priority: record.priority,
|
priority: record.priority,
|
||||||
}
|
})
|
||||||
}).collect();
|
.collect();
|
||||||
|
|
||||||
return Ok(Some(DnsResolutionResponse {
|
return Ok(Some(DnsResolutionResponse {
|
||||||
name: query_name.to_string(),
|
name: query_name.to_string(),
|
||||||
@@ -736,7 +831,10 @@ async fn try_delegation_match(query_name: &str, tld: &str, app_state: &AppState)
|
|||||||
Ok(None)
|
Ok(None)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn resolve_full_domain(ctx: &ServerContext, app_state: AppState) -> Result<GurtResponse> {
|
pub(crate) async fn resolve_full_domain(
|
||||||
|
ctx: &ServerContext,
|
||||||
|
app_state: AppState,
|
||||||
|
) -> Result<GurtResponse> {
|
||||||
#[derive(serde::Deserialize)]
|
#[derive(serde::Deserialize)]
|
||||||
struct FullDomainRequest {
|
struct FullDomainRequest {
|
||||||
domain: String,
|
domain: String,
|
||||||
@@ -763,7 +861,10 @@ pub(crate) async fn resolve_full_domain(ctx: &ServerContext, app_state: AppState
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Certificate Authority endpoints
|
// Certificate Authority endpoints
|
||||||
pub(crate) async fn verify_domain_ownership(ctx: &ServerContext, app_state: AppState) -> Result<GurtResponse> {
|
pub(crate) async fn verify_domain_ownership(
|
||||||
|
ctx: &ServerContext,
|
||||||
|
app_state: AppState,
|
||||||
|
) -> Result<GurtResponse> {
|
||||||
let path_parts: Vec<&str> = ctx.path().split('/').collect();
|
let path_parts: Vec<&str> = ctx.path().split('/').collect();
|
||||||
if path_parts.len() < 3 {
|
if path_parts.len() < 3 {
|
||||||
return Ok(GurtResponse::bad_request().with_string_body("Invalid path format"));
|
return Ok(GurtResponse::bad_request().with_string_body("Invalid path format"));
|
||||||
@@ -796,7 +897,10 @@ pub(crate) async fn verify_domain_ownership(ctx: &ServerContext, app_state: AppS
|
|||||||
}))?)
|
}))?)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn request_certificate(ctx: &ServerContext, app_state: AppState) -> Result<GurtResponse> {
|
pub(crate) async fn request_certificate(
|
||||||
|
ctx: &ServerContext,
|
||||||
|
app_state: AppState,
|
||||||
|
) -> Result<GurtResponse> {
|
||||||
#[derive(serde::Deserialize)]
|
#[derive(serde::Deserialize)]
|
||||||
struct CertRequest {
|
struct CertRequest {
|
||||||
domain: String,
|
domain: String,
|
||||||
@@ -824,7 +928,8 @@ pub(crate) async fn request_certificate(ctx: &ServerContext, app_state: AppState
|
|||||||
.map_err(|_| GurtError::invalid_message("Database error"))?;
|
.map_err(|_| GurtError::invalid_message("Database error"))?;
|
||||||
|
|
||||||
if domain_record.is_none() {
|
if domain_record.is_none() {
|
||||||
return Ok(GurtResponse::bad_request().with_string_body("Domain does not exist or is not approved"));
|
return Ok(GurtResponse::bad_request()
|
||||||
|
.with_string_body("Domain does not exist or is not approved"));
|
||||||
}
|
}
|
||||||
|
|
||||||
let token = uuid::Uuid::new_v4().to_string();
|
let token = uuid::Uuid::new_v4().to_string();
|
||||||
@@ -853,7 +958,10 @@ pub(crate) async fn request_certificate(ctx: &ServerContext, app_state: AppState
|
|||||||
Ok(GurtResponse::ok().with_json_body(&challenge)?)
|
Ok(GurtResponse::ok().with_json_body(&challenge)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn get_certificate(ctx: &ServerContext, app_state: AppState) -> Result<GurtResponse> {
|
pub(crate) async fn get_certificate(
|
||||||
|
ctx: &ServerContext,
|
||||||
|
app_state: AppState,
|
||||||
|
) -> Result<GurtResponse> {
|
||||||
let path_parts: Vec<&str> = ctx.path().split('/').collect();
|
let path_parts: Vec<&str> = ctx.path().split('/').collect();
|
||||||
if path_parts.len() < 4 {
|
if path_parts.len() < 4 {
|
||||||
return Ok(GurtResponse::bad_request().with_string_body("Invalid path format"));
|
return Ok(GurtResponse::bad_request().with_string_body("Invalid path format"));
|
||||||
@@ -871,12 +979,16 @@ pub(crate) async fn get_certificate(ctx: &ServerContext, app_state: AppState) ->
|
|||||||
|
|
||||||
let (domain, _challenge_type, verification_data, csr_pem, expires_at) = match challenge {
|
let (domain, _challenge_type, verification_data, csr_pem, expires_at) = match challenge {
|
||||||
Some(c) => c,
|
Some(c) => c,
|
||||||
None => return Ok(GurtResponse::not_found().with_string_body("Challenge not found"))
|
None => return Ok(GurtResponse::not_found().with_string_body("Challenge not found")),
|
||||||
};
|
};
|
||||||
|
|
||||||
let csr_pem = match csr_pem {
|
let csr_pem = match csr_pem {
|
||||||
Some(csr) => csr,
|
Some(csr) => csr,
|
||||||
None => return Ok(GurtResponse::bad_request().with_string_body("CSR not found for this challenge"))
|
None => {
|
||||||
|
return Ok(
|
||||||
|
GurtResponse::bad_request().with_string_body("CSR not found for this challenge")
|
||||||
|
)
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if chrono::Utc::now() > expires_at {
|
if chrono::Utc::now() > expires_at {
|
||||||
@@ -904,7 +1016,11 @@ pub(crate) async fn get_certificate(ctx: &ServerContext, app_state: AppState) ->
|
|||||||
|
|
||||||
let domain_record = match domain_record {
|
let domain_record = match domain_record {
|
||||||
Some(d) => d,
|
Some(d) => d,
|
||||||
None => return Ok(GurtResponse::bad_request().with_string_body("Domain not found or not approved"))
|
None => {
|
||||||
|
return Ok(
|
||||||
|
GurtResponse::bad_request().with_string_body("Domain not found or not approved")
|
||||||
|
)
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let txt_records: Vec<DnsRecord> = sqlx::query_as::<_, DnsRecord>(
|
let txt_records: Vec<DnsRecord> = sqlx::query_as::<_, DnsRecord>(
|
||||||
@@ -918,10 +1034,12 @@ pub(crate) async fn get_certificate(ctx: &ServerContext, app_state: AppState) ->
|
|||||||
.map_err(|_| GurtError::invalid_message("Database error"))?;
|
.map_err(|_| GurtError::invalid_message("Database error"))?;
|
||||||
|
|
||||||
if txt_records.is_empty() {
|
if txt_records.is_empty() {
|
||||||
return Ok(GurtResponse::new(gurt::GurtStatusCode::Accepted).with_string_body("Challenge not completed yet"));
|
return Ok(GurtResponse::new(gurt::GurtStatusCode::Accepted)
|
||||||
|
.with_string_body("Challenge not completed yet"));
|
||||||
}
|
}
|
||||||
|
|
||||||
let ca_cert = super::ca::get_or_create_ca(&app_state.db).await
|
let ca_cert = super::ca::get_or_create_ca(&app_state.db)
|
||||||
|
.await
|
||||||
.map_err(|e| {
|
.map_err(|e| {
|
||||||
log::error!("Failed to get CA certificate: {}", e);
|
log::error!("Failed to get CA certificate: {}", e);
|
||||||
GurtError::invalid_message("CA certificate error")
|
GurtError::invalid_message("CA certificate error")
|
||||||
@@ -931,8 +1049,9 @@ pub(crate) async fn get_certificate(ctx: &ServerContext, app_state: AppState) ->
|
|||||||
&csr_pem,
|
&csr_pem,
|
||||||
&ca_cert.ca_cert_pem,
|
&ca_cert.ca_cert_pem,
|
||||||
&ca_cert.ca_key_pem,
|
&ca_cert.ca_key_pem,
|
||||||
&domain
|
&domain,
|
||||||
).map_err(|e| {
|
)
|
||||||
|
.map_err(|e| {
|
||||||
log::error!("Failed to sign certificate: {}", e);
|
log::error!("Failed to sign certificate: {}", e);
|
||||||
GurtError::invalid_message("Certificate signing failed")
|
GurtError::invalid_message("Certificate signing failed")
|
||||||
})?;
|
})?;
|
||||||
@@ -953,8 +1072,12 @@ pub(crate) async fn get_certificate(ctx: &ServerContext, app_state: AppState) ->
|
|||||||
Ok(GurtResponse::ok().with_json_body(&certificate)?)
|
Ok(GurtResponse::ok().with_json_body(&certificate)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn get_ca_certificate(_ctx: &ServerContext, app_state: AppState) -> Result<GurtResponse> {
|
pub(crate) async fn get_ca_certificate(
|
||||||
let ca_cert = super::ca::get_or_create_ca(&app_state.db).await
|
_ctx: &ServerContext,
|
||||||
|
app_state: AppState,
|
||||||
|
) -> Result<GurtResponse> {
|
||||||
|
let ca_cert = super::ca::get_or_create_ca(&app_state.db)
|
||||||
|
.await
|
||||||
.map_err(|e| {
|
.map_err(|e| {
|
||||||
log::error!("Failed to get CA certificate: {}", e);
|
log::error!("Failed to get CA certificate: {}", e);
|
||||||
GurtError::invalid_message("CA certificate error")
|
GurtError::invalid_message("CA certificate error")
|
||||||
@@ -962,14 +1085,22 @@ pub(crate) async fn get_ca_certificate(_ctx: &ServerContext, app_state: AppState
|
|||||||
|
|
||||||
Ok(GurtResponse::ok()
|
Ok(GurtResponse::ok()
|
||||||
.with_header("Content-Type", "application/x-pem-file")
|
.with_header("Content-Type", "application/x-pem-file")
|
||||||
.with_header("Content-Disposition", "attachment; filename=\"gurted-ca.crt\"")
|
.with_header(
|
||||||
|
"Content-Disposition",
|
||||||
|
"attachment; filename=\"gurted-ca.crt\"",
|
||||||
|
)
|
||||||
.with_string_body(ca_cert.ca_cert_pem))
|
.with_string_body(ca_cert.ca_cert_pem))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn generate_challenge_data(domain: &str, token: &str) -> Result<String> {
|
fn generate_challenge_data(domain: &str, token: &str) -> Result<String> {
|
||||||
use sha2::{Sha256, Digest};
|
let timestamp = SystemTime::now()
|
||||||
|
.duration_since(UNIX_EPOCH)
|
||||||
|
.map_err(|_| GurtError::invalid_message("System time error"))?
|
||||||
|
.as_nanos();
|
||||||
|
|
||||||
let data = format!("{}:{}", domain, token);
|
let entropy = uuid::Uuid::new_v4().to_string();
|
||||||
|
|
||||||
|
let data = format!("{}:{}:{}:{}", domain, token, timestamp, entropy);
|
||||||
let mut hasher = Sha256::new();
|
let mut hasher = Sha256::new();
|
||||||
hasher.update(data.as_bytes());
|
hasher.update(data.as_bytes());
|
||||||
let hash = hasher.finalize();
|
let hash = hasher.finalize();
|
||||||
|
|||||||
Reference in New Issue
Block a user