mirror of
https://github.com/okieselbach/AutopilotMonitor.git
synced 2026-10-25 05:00:07 +00:00
448 lines
17 KiB
C#
448 lines
17 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Text.Json;
|
|
|
|
namespace AutopilotMonitor.Shared.Models
|
|
{
|
|
/// <summary>
|
|
/// Tenant-specific configuration stored in Azure Table Storage
|
|
/// PartitionKey = TenantId
|
|
/// RowKey = "config"
|
|
/// </summary>
|
|
public class TenantConfiguration
|
|
{
|
|
/// <summary>
|
|
/// Tenant ID (PartitionKey in Table Storage)
|
|
/// </summary>
|
|
public string TenantId { get; set; }
|
|
|
|
/// <summary>
|
|
/// Domain name extracted from the first user's UPN
|
|
/// Used for display purposes (e.g., contoso.com)
|
|
/// </summary>
|
|
public string DomainName { get; set; }
|
|
|
|
/// <summary>
|
|
/// When the configuration was last updated
|
|
/// </summary>
|
|
public DateTime LastUpdated { get; set; }
|
|
|
|
/// <summary>
|
|
/// Updated by (user email or system)
|
|
/// </summary>
|
|
public string UpdatedBy { get; set; }
|
|
|
|
// ===== TENANT STATUS =====
|
|
|
|
/// <summary>
|
|
/// Whether this tenant is disabled/suspended
|
|
/// If true, users from this tenant cannot log in
|
|
/// Default: false
|
|
/// </summary>
|
|
public bool Disabled { get; set; } = false;
|
|
|
|
/// <summary>
|
|
/// Optional reason why the tenant was disabled
|
|
/// Displayed to users attempting to log in
|
|
/// </summary>
|
|
public string DisabledReason { get; set; }
|
|
|
|
/// <summary>
|
|
/// Optional date/time until which the tenant is disabled
|
|
/// If set and in the past, the tenant can be automatically re-enabled
|
|
/// If null, the tenant remains disabled until manually re-enabled
|
|
/// </summary>
|
|
public DateTime? DisabledUntil { get; set; }
|
|
|
|
// ===== SECURITY SETTINGS =====
|
|
|
|
/// <summary>
|
|
/// Rate limit: Maximum requests per minute per device
|
|
/// This value is synchronized from the global AdminConfiguration
|
|
/// Default: 100
|
|
/// </summary>
|
|
public int RateLimitRequestsPerMinute { get; set; } = 100;
|
|
|
|
/// <summary>
|
|
/// Optional custom rate limit for this tenant (overrides RateLimitRequestsPerMinute)
|
|
/// If set (not null), this custom value takes precedence over the global default
|
|
/// Note: This is only configurable by Galactic Admins directly in the database
|
|
/// </summary>
|
|
public int? CustomRateLimitRequestsPerMinute { get; set; } = null;
|
|
|
|
/// <summary>
|
|
/// Hardware whitelist: Allowed manufacturers (supports wildcards like "Dell*")
|
|
/// Comma-separated list
|
|
/// </summary>
|
|
public string ManufacturerWhitelist { get; set; } = "Dell*,HP*,Lenovo*,Microsoft Corporation";
|
|
|
|
/// <summary>
|
|
/// Hardware whitelist: Allowed models (supports wildcards like "Latitude*")
|
|
/// Comma-separated list
|
|
/// Default: "*" (all models allowed)
|
|
/// </summary>
|
|
public string ModelWhitelist { get; set; } = "*";
|
|
|
|
/// <summary>
|
|
/// Whether to validate devices against Intune Autopilot device registration
|
|
/// Requires Graph API integration (admin consent for DeviceManagementServiceConfig.Read.All)
|
|
/// </summary>
|
|
public bool ValidateAutopilotDevice { get; set; } = false;
|
|
|
|
/// <summary>
|
|
/// Whether to validate devices against Intune Corporate Device Identifiers
|
|
/// (manufacturer + model + serial number via importedDeviceIdentities/searchExistingIdentities).
|
|
/// Requires Graph API integration (admin consent for DeviceManagementServiceConfig.ReadWrite.All)
|
|
/// </summary>
|
|
public bool ValidateCorporateIdentifier { get; set; } = false;
|
|
|
|
/// <summary>
|
|
/// Emergency bypass for agent security gate (Galactic Admin use only).
|
|
/// If true, agent requests are accepted even when ValidateAutopilotDevice is false.
|
|
/// Default: false
|
|
/// </summary>
|
|
public bool AllowInsecureAgentRequests { get; set; } = false;
|
|
|
|
// ===== DATA MANAGEMENT SETTINGS =====
|
|
|
|
/// <summary>
|
|
/// Data retention period in days
|
|
/// Sessions and events older than this will be deleted by the daily maintenance job
|
|
/// Default: 90 days
|
|
/// </summary>
|
|
public int DataRetentionDays { get; set; } = 90;
|
|
|
|
/// <summary>
|
|
/// Session timeout in hours
|
|
/// Sessions in "InProgress" status longer than this will be marked as "Failed - Timed Out"
|
|
/// This prevents stalled sessions from running indefinitely and skewing statistics
|
|
/// Recommended: Use the same value as your ESP (Enrollment Status Page) timeout
|
|
/// Default: 5 hours
|
|
/// </summary>
|
|
public int SessionTimeoutHours { get; set; } = 5;
|
|
|
|
// ===== PAYLOAD SETTINGS =====
|
|
|
|
/// <summary>
|
|
/// Maximum decompressed NDJSON payload size in MB
|
|
/// Protects against memory exhaustion and denial-of-service attacks
|
|
/// Default: 5 MB
|
|
/// </summary>
|
|
public int MaxNdjsonPayloadSizeMB { get; set; } = 5;
|
|
|
|
// ===== AGENT COLLECTOR SETTINGS =====
|
|
|
|
/// <summary>
|
|
/// Enable Performance Collector (CPU, memory, disk, network monitoring)
|
|
/// Generates ~1 event per interval - can create significant traffic
|
|
/// Default: true
|
|
/// </summary>
|
|
public bool EnablePerformanceCollector { get; set; } = true;
|
|
|
|
/// <summary>
|
|
/// Performance collector interval in seconds
|
|
/// Default: 30 seconds
|
|
/// </summary>
|
|
public int PerformanceCollectorIntervalSeconds { get; set; } = 30;
|
|
|
|
/// <summary>
|
|
/// Seconds to wait for the Windows Hello wizard after ESP exit
|
|
/// Default: 30 seconds
|
|
/// </summary>
|
|
public int HelloWaitTimeoutSeconds { get; set; } = 30;
|
|
|
|
// ===== AGENT AUTH CIRCUIT BREAKER =====
|
|
|
|
/// <summary>
|
|
/// Maximum consecutive authentication failures (401/403) before the agent shuts down.
|
|
/// null = use default (5). 0 = disabled (retry forever).
|
|
/// </summary>
|
|
public int? MaxAuthFailures { get; set; }
|
|
|
|
/// <summary>
|
|
/// Maximum time in minutes the agent keeps retrying after the first auth failure.
|
|
/// null = use default (0 = disabled, only MaxAuthFailures applies).
|
|
/// </summary>
|
|
public int? AuthFailureTimeoutMinutes { get; set; }
|
|
|
|
/// <summary>
|
|
/// Maximum agent lifetime in minutes. Safety net to prevent zombie agents.
|
|
/// null = use default (360 = 6 hours). 0 = disabled (no lifetime limit).
|
|
/// </summary>
|
|
public int? AgentMaxLifetimeMinutes { get; set; }
|
|
|
|
// ===== AGENT BEHAVIOR OVERRIDES =====
|
|
|
|
/// <summary>
|
|
/// Whether to self-destruct after enrollment completion (remove Scheduled Task and all files).
|
|
/// null = use agent default (true).
|
|
/// </summary>
|
|
public bool? SelfDestructOnComplete { get; set; } = true;
|
|
|
|
/// <summary>
|
|
/// Preserve logs during self-destruct.
|
|
/// null = use agent default (false).
|
|
/// </summary>
|
|
public bool? KeepLogFile { get; set; } = false;
|
|
|
|
/// <summary>
|
|
/// Whether to reboot the device after enrollment completes.
|
|
/// null = use agent default (false).
|
|
/// </summary>
|
|
public bool? RebootOnComplete { get; set; }
|
|
|
|
/// <summary>
|
|
/// Delay in seconds before the reboot is initiated (shutdown.exe /r /t X).
|
|
/// null = use agent default (10 seconds).
|
|
/// </summary>
|
|
public int? RebootDelaySeconds { get; set; }
|
|
|
|
/// <summary>
|
|
/// Whether to enable geo-location detection (queries external IP service).
|
|
/// null = use agent default (true).
|
|
/// </summary>
|
|
public bool? EnableGeoLocation { get; set; }
|
|
|
|
/// <summary>
|
|
/// Whether to write a match log for every IME log line matched by a pattern.
|
|
/// When true, the agent writes to the default path Constants.ImeMatchLogPath.
|
|
/// null = use agent default (false).
|
|
/// </summary>
|
|
public bool? EnableImeMatchLog { get; set; }
|
|
|
|
/// <summary>
|
|
/// Log verbosity level override for this tenant's agents.
|
|
/// null = use agent default ("Info"). Values: "Info", "Debug", "Verbose".
|
|
/// </summary>
|
|
public string LogLevel { get; set; }
|
|
|
|
/// <summary>
|
|
/// Maximum events per upload batch.
|
|
/// null = use agent default (100).
|
|
/// </summary>
|
|
public int? MaxBatchSize { get; set; }
|
|
|
|
/// <summary>
|
|
/// Whether to show a visual enrollment summary dialog to the end user
|
|
/// after enrollment completes (success or failure).
|
|
/// null = use agent default (false).
|
|
/// </summary>
|
|
public bool? ShowEnrollmentSummary { get; set; }
|
|
|
|
/// <summary>
|
|
/// Auto-close timeout in seconds for the enrollment summary dialog.
|
|
/// null = use agent default (60). 0 = no auto-close.
|
|
/// </summary>
|
|
public int? EnrollmentSummaryTimeoutSeconds { get; set; }
|
|
|
|
/// <summary>
|
|
/// Optional URL to a branding image displayed as a banner at the top of the enrollment summary dialog.
|
|
/// Expected size: 540 x 80 px. Larger images will be center-cropped.
|
|
/// </summary>
|
|
public string EnrollmentSummaryBrandingImageUrl { get; set; }
|
|
|
|
/// <summary>
|
|
/// Maximum time in seconds the agent retries launching the enrollment summary dialog
|
|
/// when the user's desktop is locked by a credential UI (e.g. Windows Hello).
|
|
/// null = use agent default (120). 0 = no retry (single attempt).
|
|
/// </summary>
|
|
public int? EnrollmentSummaryLaunchRetrySeconds { get; set; }
|
|
|
|
/// <summary>
|
|
/// Whether to show PowerShell script stdout in the web UI.
|
|
/// When false, only stderr (error output) is visible for troubleshooting.
|
|
/// stdout may contain sensitive data (credentials, tokens).
|
|
/// Default true (show stdout). Data is always collected regardless of this setting.
|
|
/// </summary>
|
|
public bool? ShowScriptOutput { get; set; } = true;
|
|
|
|
// ===== AGENT ANALYZER SETTINGS =====
|
|
|
|
/// <summary>
|
|
/// Whether the LocalAdminAnalyzer is enabled for this tenant's devices.
|
|
/// null = use agent default (true).
|
|
/// </summary>
|
|
public bool? EnableLocalAdminAnalyzer { get; set; } = null;
|
|
|
|
/// <summary>
|
|
/// JSON-serialized list of additional local account names that are considered expected
|
|
/// on a newly enrolled device (merged with built-in defaults on the agent).
|
|
/// Example: ["SupportAdmin", "TechDesk"]
|
|
/// </summary>
|
|
public string LocalAdminAllowedAccountsJson { get; set; }
|
|
|
|
/// <summary>
|
|
/// Returns the deserialized list of additional allowed local admin account names.
|
|
/// </summary>
|
|
public List<string> GetLocalAdminAllowedAccounts()
|
|
{
|
|
if (string.IsNullOrEmpty(LocalAdminAllowedAccountsJson))
|
|
return new List<string>();
|
|
try
|
|
{
|
|
return JsonSerializer.Deserialize<List<string>>(LocalAdminAllowedAccountsJson,
|
|
new JsonSerializerOptions { PropertyNameCaseInsensitive = true })
|
|
?? new List<string>();
|
|
}
|
|
catch
|
|
{
|
|
return new List<string>();
|
|
}
|
|
}
|
|
|
|
// ===== BOOTSTRAP TOKEN =====
|
|
|
|
/// <summary>
|
|
/// Whether OOBE Bootstrap Sessions are enabled for this tenant.
|
|
/// When false (default), the Bootstrap Sessions feature is hidden in the UI
|
|
/// and all bootstrap API endpoints reject requests for this tenant.
|
|
/// Only configurable by Galactic Admins.
|
|
/// </summary>
|
|
public bool BootstrapTokenEnabled { get; set; } = false;
|
|
|
|
// ===== DIAGNOSTICS LOG PATHS =====
|
|
|
|
/// <summary>
|
|
/// JSON-serialized list of tenant-specific additional log paths/wildcards
|
|
/// to include in the diagnostics ZIP package (additive to global paths).
|
|
/// Each entry: { "path": "...", "description": "...", "isBuiltIn": false }
|
|
/// </summary>
|
|
public string DiagnosticsLogPathsJson { get; set; }
|
|
|
|
/// <summary>
|
|
/// Returns the deserialized list of tenant-specific diagnostics log paths.
|
|
/// </summary>
|
|
public List<DiagnosticsLogPath> GetDiagnosticsLogPaths()
|
|
{
|
|
if (string.IsNullOrEmpty(DiagnosticsLogPathsJson))
|
|
return new List<DiagnosticsLogPath>();
|
|
try
|
|
{
|
|
return JsonSerializer.Deserialize<List<DiagnosticsLogPath>>(DiagnosticsLogPathsJson,
|
|
new JsonSerializerOptions { PropertyNameCaseInsensitive = true })
|
|
?? new List<DiagnosticsLogPath>();
|
|
}
|
|
catch
|
|
{
|
|
return new List<DiagnosticsLogPath>();
|
|
}
|
|
}
|
|
|
|
// ===== DIAGNOSTICS SETTINGS =====
|
|
|
|
/// <summary>
|
|
/// Azure Blob Storage Container SAS URL for diagnostics package upload.
|
|
/// Each tenant provides their own container — data stays in the customer's storage.
|
|
/// If null or empty, diagnostics upload is disabled.
|
|
/// </summary>
|
|
public string DiagnosticsBlobSasUrl { get; set; }
|
|
|
|
/// <summary>
|
|
/// When to upload diagnostics packages: "Off", "Always", "OnFailure".
|
|
/// Default: "Off"
|
|
/// </summary>
|
|
public string DiagnosticsUploadMode { get; set; } = "Off";
|
|
|
|
// ===== TRACE EVENTS =====
|
|
|
|
/// <summary>
|
|
/// Whether the agent should send Trace-severity events to the backend.
|
|
/// Trace events capture key agent decisions for backend-side troubleshooting.
|
|
/// Default: true (on in preview). Can be disabled per tenant to reduce traffic.
|
|
/// </summary>
|
|
public bool SendTraceEvents { get; set; } = true;
|
|
|
|
// ===== TEAMS NOTIFICATIONS =====
|
|
|
|
/// <summary>
|
|
/// URL of the Teams Incoming Webhook for enrollment notifications.
|
|
/// If null or empty, no notifications are sent.
|
|
/// </summary>
|
|
public string TeamsWebhookUrl { get; set; }
|
|
|
|
/// <summary>
|
|
/// Send a Teams notification when an enrollment completes successfully.
|
|
/// Default: true
|
|
/// </summary>
|
|
public bool TeamsNotifyOnSuccess { get; set; } = true;
|
|
|
|
/// <summary>
|
|
/// Send a Teams notification when an enrollment fails.
|
|
/// Default: true
|
|
/// </summary>
|
|
public bool TeamsNotifyOnFailure { get; set; } = true;
|
|
|
|
// ===== HELPER METHODS =====
|
|
|
|
/// <summary>
|
|
/// Checks if the tenant is currently disabled
|
|
/// Takes into account DisabledUntil if set
|
|
/// </summary>
|
|
public bool IsCurrentlyDisabled()
|
|
{
|
|
if (!Disabled)
|
|
return false;
|
|
|
|
// If DisabledUntil is set and in the past, tenant is no longer disabled
|
|
if (DisabledUntil.HasValue && DisabledUntil.Value <= DateTime.UtcNow)
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets manufacturer whitelist as array
|
|
/// </summary>
|
|
public string[] GetManufacturerWhitelist()
|
|
{
|
|
if (string.IsNullOrEmpty(ManufacturerWhitelist))
|
|
return new[] { "*" };
|
|
|
|
return ManufacturerWhitelist.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets model whitelist as array
|
|
/// </summary>
|
|
public string[] GetModelWhitelist()
|
|
{
|
|
if (string.IsNullOrEmpty(ModelWhitelist))
|
|
return new[] { "*" };
|
|
|
|
return ModelWhitelist.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Creates default configuration for a tenant
|
|
/// </summary>
|
|
public static TenantConfiguration CreateDefault(string tenantId)
|
|
{
|
|
return new TenantConfiguration
|
|
{
|
|
TenantId = tenantId,
|
|
DomainName = "",
|
|
LastUpdated = DateTime.UtcNow,
|
|
UpdatedBy = "System",
|
|
Disabled = false,
|
|
DisabledReason = null,
|
|
DisabledUntil = null,
|
|
RateLimitRequestsPerMinute = 100,
|
|
CustomRateLimitRequestsPerMinute = null,
|
|
ManufacturerWhitelist = "Dell*,HP*,Lenovo*,Microsoft Corporation",
|
|
ModelWhitelist = "*",
|
|
ValidateAutopilotDevice = false,
|
|
AllowInsecureAgentRequests = false,
|
|
DataRetentionDays = 90,
|
|
SessionTimeoutHours = 5,
|
|
MaxNdjsonPayloadSizeMB = 5,
|
|
EnablePerformanceCollector = true,
|
|
PerformanceCollectorIntervalSeconds = 30,
|
|
SelfDestructOnComplete = true,
|
|
KeepLogFile = false,
|
|
ShowScriptOutput = true
|
|
};
|
|
}
|
|
}
|
|
}
|