using System;
using System.Net;
using AutopilotMonitor.Functions.Functions.Sessions;
using AutopilotMonitor.Functions.Security;
using AutopilotMonitor.Functions.Services;
using AutopilotMonitor.Shared.Models;
using Microsoft.Azure.Functions.Worker;
using Microsoft.Azure.Functions.Worker.Http;
using Microsoft.Azure.Functions.Worker.Extensions.SignalRService;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
namespace AutopilotMonitor.Functions.Functions.Bootstrap
{
///
/// POST /api/bootstrap/register-session — cert-free session registration for pre-enrollment agents.
/// Requires X-Bootstrap-Token header. Delegates to RegisterSessionFunction.ProcessRegisterAsync.
///
public class BootstrapRegisterSessionFunction
{
private readonly ILogger _logger;
private readonly RegisterSessionFunction _inner;
private readonly TenantConfigurationService _configService;
private readonly AdminConfigurationService _adminConfigService;
private readonly RateLimitService _rateLimitService;
private readonly AutopilotDeviceValidator _autopilotDeviceValidator;
private readonly CorporateIdentifierValidator _corporateIdentifierValidator;
private readonly DeviceAssociationValidator _deviceAssociationValidator;
private readonly BootstrapSessionService _bootstrapSessionService;
public BootstrapRegisterSessionFunction(
ILogger logger,
RegisterSessionFunction inner,
TenantConfigurationService configService,
AdminConfigurationService adminConfigService,
RateLimitService rateLimitService,
AutopilotDeviceValidator autopilotDeviceValidator,
CorporateIdentifierValidator corporateIdentifierValidator,
DeviceAssociationValidator deviceAssociationValidator,
BootstrapSessionService bootstrapSessionService)
{
_logger = logger;
_inner = inner;
_configService = configService;
_adminConfigService = adminConfigService;
_rateLimitService = rateLimitService;
_autopilotDeviceValidator = autopilotDeviceValidator;
_corporateIdentifierValidator = corporateIdentifierValidator;
_deviceAssociationValidator = deviceAssociationValidator;
_bootstrapSessionService = bootstrapSessionService;
}
[Function("BootstrapRegisterSession")]
public async Task Run(
[HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = "bootstrap/register-session")] HttpRequestData req)
{
try
{
// Bootstrap-only: reject requests without a (non-empty) X-Bootstrap-Token before any
// parsing. SecurityValidator enforces the same rule fail-closed for every
// /api/bootstrap/* route; this is only the cheap early exit.
if (SecurityValidator.GetBootstrapToken(req) == null)
{
var noToken = req.CreateResponse(HttpStatusCode.Unauthorized);
await noToken.WriteAsJsonAsync(new { success = false, message = "X-Bootstrap-Token header is required" });
return new RegisterSessionOutput { HttpResponse = noToken };
}
// Parse request body (same as original RegisterSessionFunction)
if (req.Headers.TryGetValues("Content-Length", out var clValues)
&& long.TryParse(clValues.FirstOrDefault(), out var contentLength)
&& contentLength > 1_048_576)
{
var tooLarge = req.CreateResponse(HttpStatusCode.BadRequest);
await tooLarge.WriteAsJsonAsync(new { success = false, message = "Request body too large" });
return new RegisterSessionOutput { HttpResponse = tooLarge };
}
var requestBody = await new StreamReader(req.Body).ReadToEndAsync();
var request = JsonConvert.DeserializeObject(requestBody);
if (request?.Registration == null)
{
var badReq = req.CreateResponse(HttpStatusCode.BadRequest);
await badReq.WriteAsJsonAsync(new { success = false, message = "Invalid request payload" });
return new RegisterSessionOutput { HttpResponse = badReq };
}
var registration = request.Registration;
// Feature gate: bootstrap endpoints only available when explicitly enabled for this tenant
var (config, tenantExists) = await _configService.TryGetConfigurationAsync(registration.TenantId);
if (!tenantExists || !TenantEntitlementService.IsBootstrapEnabled(config, DateTime.UtcNow))
{
return new RegisterSessionOutput { HttpResponse = req.CreateResponse(HttpStatusCode.NotFound) };
}
var (validation, errorResponse) = await req.ValidateSecurityAsync(
registration.TenantId, _configService, _adminConfigService, _rateLimitService,
_autopilotDeviceValidator, _corporateIdentifierValidator,
_logger, registration.SessionId,
bootstrapSessionService: _bootstrapSessionService,
deviceAssociationValidator: _deviceAssociationValidator);
if (errorResponse != null)
{
return new RegisterSessionOutput { HttpResponse = errorResponse };
}
return await _inner.ProcessRegisterAsync(req, registration, validation);
}
catch (Exception ex)
{
_logger.LogError(ex, "Error in bootstrap register-session");
var error = req.CreateResponse(HttpStatusCode.InternalServerError);
await error.WriteAsJsonAsync(new { success = false, message = "Internal server error" });
return new RegisterSessionOutput { HttpResponse = error };
}
}
}
}