Private
Public Access
1
0

feat: add host self-enrollment workflow v0.1.7
All checks were successful
CI Pipeline / Rust Format Check (push) Successful in 5s
CI Pipeline / Clippy Lints (push) Successful in 53s
CI Pipeline / Rust Unit Tests (push) Successful in 1m11s
CI Pipeline / Security Audit (push) Successful in 4s
CI Pipeline / Frontend Lint & Type Check (push) Successful in 14s
CI Pipeline / Build .deb & Release (push) Has been skipped

This commit is contained in:
2026-05-16 16:58:00 +00:00
parent f183c8edf8
commit da3dffd81f
17 changed files with 841 additions and 55 deletions

View File

@ -1,6 +1,8 @@
use crate::config::DatabaseConfig;
use crate::models::{CreateEnrollmentRequest, EnrollmentRequest};
use sqlx::postgres::{PgPool, PgPoolOptions};
use std::time::Duration;
use uuid::Uuid;
/// Initialize and return a PostgreSQL connection pool.
pub async fn init_pool(cfg: &DatabaseConfig) -> Result<PgPool, sqlx::Error> {
@ -56,6 +58,53 @@ pub async fn run_migrations(pool: &PgPool) -> Result<(), sqlx::migrate::MigrateE
result
}
// ============================================================
// Enrollment Requests
// ============================================================
pub async fn create_enrollment_request(
pool: &PgPool,
req: CreateEnrollmentRequest,
token_hash: String,
) -> Result<EnrollmentRequest, sqlx::Error> {
sqlx::query_as::<
_,
EnrollmentRequest,
>(
r#"
INSERT INTO enrollment_requests (machine_id, fqdn, ip_address, os_details, polling_token)
VALUES ($1, $2, $3, $4, $5)
RETURNING id, machine_id, fqdn, ip_address, os_details, polling_token, created_at, expires_at
"#,
)
.bind(req.machine_id)
.bind(req.fqdn)
.bind(req.ip_address)
.bind(req.os_details)
.bind(token_hash)
.fetch_one(pool)
.await
}
pub async fn list_enrollment_requests(
pool: &PgPool,
) -> Result<Vec<EnrollmentRequest>, sqlx::Error> {
sqlx::query_as::<_, EnrollmentRequest>(
"SELECT id, machine_id, fqdn, ip_address, os_details, polling_token, created_at, expires_at FROM enrollment_requests ORDER BY created_at DESC",
)
.fetch_all(pool)
.await
}
pub async fn delete_enrollment_request(pool: &PgPool, id: Uuid) -> Result<u64, sqlx::Error> {
let result = sqlx::query("DELETE FROM enrollment_requests WHERE id = $1")
.bind(id)
.execute(pool)
.await?;
Ok(result.rows_affected())
}
/// Check that the database schema is at the expected version.
/// Used by the worker to wait until migrations have been applied.
pub async fn check_schema_version(pool: &PgPool) -> Result<i64, sqlx::Error> {

View File

@ -123,6 +123,51 @@ pub struct HostSummary {
pub registered_at: DateTime<Utc>,
}
// ============================================================
// Host Enrollment
// ============================================================
#[derive(Debug, Clone, Serialize, Deserialize, FromRow)]
pub struct EnrollmentRequest {
pub id: Uuid,
pub machine_id: String,
pub fqdn: String,
pub ip_address: String,
pub os_details: serde_json::Value,
pub polling_token: String,
pub created_at: DateTime<Utc>,
pub expires_at: DateTime<Utc>,
}
/// Payload for initial host enrollment request.
#[derive(Debug, Deserialize, Serialize)]
pub struct CreateEnrollmentRequest {
pub machine_id: String,
pub fqdn: String,
pub ip_address: String,
pub os_details: serde_json::Value,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "status", rename_all = "lowercase")]
pub enum EnrollmentStatusResponse {
Pending,
Approved {
ca_crt: String,
server_crt: String,
server_key: String,
},
Denied,
NotFound,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PkiBundle {
pub ca_crt: String,
pub server_crt: String,
pub server_key: String,
}
// ============================================================
// Health Checks
// ============================================================