Classes/LS2AdcsObject.ps1

class LS2AdcsObject {
    # Common properties for all AD CS objects
    [string]$distinguishedName
    [string[]]$objectClass
    [string]$name
    [string]$displayName
    [string]$cn
    [System.DirectoryServices.ActiveDirectorySecurity]$ObjectSecurity
    [string]$Path
    [string]$Owner
    [Nullable[bool]]$HasNonStandardOwner
    
    # Certificate Template properties (pKICertificateTemplate)
    [Nullable[int]]$flags
    [Nullable[int]]$pKIDefaultKeySpec
    [Nullable[int]]$pKIMaxIssuingDepth
    [string[]]$pKICriticalExtensions
    [string[]]$pKIExtendedKeyUsage
    [Nullable[int]]$CertificateNameFlag      # msPKI-Certificate-Name-Flag
    [Nullable[int]]$EnrollmentFlag            # msPKI-Enrollment-Flag
    [Nullable[int]]$PrivateKeyFlag            # msPKI-Private-Key-Flag
    [Nullable[int]]$RASignature               # msPKI-RA-Signature
    [Nullable[int]]$TemplateSchemaVersion     # msPKI-Template-Schema-Version
    [Nullable[int]]$TemplateMinorRevision     # msPKI-Template-Minor-Revision
    
    # CA properties (pKIEnrollmentService)
    [string[]]$certificateTemplates
    [string]$dNSHostName
    [object[]]$CAAdministrators
    [object[]]$CertificateManagers
    [string[]]$DangerousCAAdministrator
    [string[]]$DangerousCAAdministratorNames
    [string[]]$LowPrivilegeCAAdministrator
    [string[]]$LowPrivilegeCAAdministratorNames
    [string[]]$DangerousCACertificateManager
    [string[]]$DangerousCACertificateManagerNames
    [string[]]$LowPrivilegeCACertificateManager
    [string[]]$LowPrivilegeCACertificateManagerNames
    
    # Computed properties (added by Set-* functions)
    [Nullable[bool]]$SANAllowed
    [Nullable[bool]]$AuthenticationEKUExist
    [Nullable[bool]]$AnyPurposeEKUExist
    [Nullable[bool]]$DangerousEnrollee
    [Nullable[bool]]$LowPrivilegeEnrollee
    [string[]]$DangerousEditor
    [string[]]$DangerousEditorNames
    [string[]]$LowPrivilegeEditor
    [string[]]$LowPrivilegeEditorNames
    [Nullable[bool]]$ManagerApprovalNotRequired
    [Nullable[bool]]$AuthorizedSignatureNotRequired
    [Nullable[bool]]$Enabled
    [string[]]$EnabledOn
    [string]$ComputerPrincipal
    [Nullable[bool]]$RPCEncryptionNotRequired
    [object[]]$EditFlags
    [Nullable[bool]]$SANFlagEnabled
    [object[]]$InterfaceFlags
    [Nullable[int]]$AuditFilter
    [object[]]$DisableExtensionList
    [Nullable[bool]]$SecurityExtensionDisabled
    
    # Schema class name for easy type checking
    [string]$SchemaClassName
    
    # Constructor from DirectoryEntry
    LS2AdcsObject([System.DirectoryServices.DirectoryEntry]$DirectoryEntry) {
        # Common properties - set these FIRST
        $this.distinguishedName = if ($DirectoryEntry.distinguishedName) { $DirectoryEntry.distinguishedName.Value } else { $null }
        $this.objectClass = if ($DirectoryEntry.objectClass) { @($DirectoryEntry.objectClass) } else { @() }
        $this.name = if ($DirectoryEntry.name) { $DirectoryEntry.name.Value } else { $null }
        $this.displayName = if ($DirectoryEntry.displayName) { $DirectoryEntry.displayName.Value } else { $null }
        $this.cn = if ($DirectoryEntry.cn) { $DirectoryEntry.cn.Value } else { $null }
        $this.Path = $DirectoryEntry.Path
        
        # CA properties - set dNSHostName early for CAFullName ScriptProperty
        $this.certificateTemplates = if ($DirectoryEntry.Properties.Contains('certificateTemplates')) { @($DirectoryEntry.certificateTemplates) } else { @() }
        $this.dNSHostName = if ($DirectoryEntry.Properties.Contains('dNSHostName')) { $DirectoryEntry.Properties['dNSHostName'][0] } else { $null }
        
        # Determine schema class name (most specific objectClass)
        if ($this.objectClass.Count -gt 0) {
            $this.SchemaClassName = $this.objectClass[$this.objectClass.Count - 1]
        }
        
        # Certificate Template properties
        $this.flags = if ($DirectoryEntry.Properties.Contains('flags')) { $DirectoryEntry.flags.Value } else { $null }
        $this.pKIDefaultKeySpec = if ($DirectoryEntry.Properties.Contains('pKIDefaultKeySpec')) { $DirectoryEntry.pKIDefaultKeySpec.Value } else { $null }
        $this.pKIMaxIssuingDepth = if ($DirectoryEntry.Properties.Contains('pKIMaxIssuingDepth')) { $DirectoryEntry.pKIMaxIssuingDepth.Value } else { $null }
        $this.pKICriticalExtensions = if ($DirectoryEntry.Properties.Contains('pKICriticalExtensions')) { @($DirectoryEntry.pKICriticalExtensions) } else { @() }
        $this.pKIExtendedKeyUsage = if ($DirectoryEntry.Properties.Contains('pKIExtendedKeyUsage')) { @($DirectoryEntry.pKIExtendedKeyUsage) } else { @() }
        
        # msPKI-* properties with proper type conversion
        $this.CertificateNameFlag = if ($DirectoryEntry.Properties.Contains('msPKI-Certificate-Name-Flag')) { [int]$DirectoryEntry.Properties['msPKI-Certificate-Name-Flag'][0] } else { $null }
        $this.EnrollmentFlag = if ($DirectoryEntry.Properties.Contains('msPKI-Enrollment-Flag')) { [int]$DirectoryEntry.Properties['msPKI-Enrollment-Flag'][0] } else { $null }
        $this.PrivateKeyFlag = if ($DirectoryEntry.Properties.Contains('msPKI-Private-Key-Flag')) { [int]$DirectoryEntry.Properties['msPKI-Private-Key-Flag'][0] } else { $null }
        $this.RASignature = if ($DirectoryEntry.Properties.Contains('msPKI-RA-Signature')) { [int]$DirectoryEntry.Properties['msPKI-RA-Signature'][0] } else { $null }
        $this.TemplateSchemaVersion = if ($DirectoryEntry.Properties.Contains('msPKI-Template-Schema-Version')) { [int]$DirectoryEntry.Properties['msPKI-Template-Schema-Version'][0] } else { $null }
        $this.TemplateMinorRevision = if ($DirectoryEntry.Properties.Contains('msPKI-Template-Minor-Revision')) { [int]$DirectoryEntry.Properties['msPKI-Template-Minor-Revision'][0] } else { $null }
        
        # Security descriptor and ownership
        try {
            $this.ObjectSecurity = $DirectoryEntry.ObjectSecurity
            $this.Owner = $this.ObjectSecurity.Owner
        } catch {
            Write-Verbose "Could not retrieve ObjectSecurity for '$($this.distinguishedName)': $_"
            $this.ObjectSecurity = $null
            $this.Owner = $null
        }
        
        # Initialize HasNonStandardOwner (preserve value if already set by Set-HasNonStandardOwner)
        if ($DirectoryEntry.PSObject.Properties['HasNonStandardOwner'] -and $null -ne $DirectoryEntry.HasNonStandardOwner) {
            $this.HasNonStandardOwner = $DirectoryEntry.HasNonStandardOwner
        } else {
            $this.HasNonStandardOwner = $null
        }
        
        # Initialize computed properties to defaults
        $this.SANAllowed = $null
        $this.AuthenticationEKUExist = $null
        $this.AnyPurposeEKUExist = $null
        $this.DangerousEnrollee = $null
        $this.LowPrivilegeEnrollee = $null
        $this.DangerousEditor = @()
        $this.DangerousEditorNames = @()
        $this.LowPrivilegeEditor = @()
        $this.LowPrivilegeEditorNames = @()
        $this.ManagerApprovalNotRequired = $null
        $this.AuthorizedSignatureNotRequired = $null
        $this.Enabled = $null
        $this.EnabledOn = @()
        $this.RPCEncryptionNotRequired = $null
        $this.SANFlagEnabled = $null
        $this.AuditFilter = $null
        $this.DisableExtensionList = @()
        
        # Initialize CA-specific properties
        $this.CAAdministrators = @()
        $this.CertificateManagers = @()
        $this.DangerousCAAdministrator = @()
        $this.DangerousCAAdministratorNames = @()
        $this.LowPrivilegeCAAdministrator = @()
        $this.LowPrivilegeCAAdministratorNames = @()
        $this.DangerousCACertificateManager = @()
        $this.DangerousCACertificateManagerNames = @()
        $this.LowPrivilegeCACertificateManager = @()
        $this.LowPrivilegeCACertificateManagerNames = @()
        
        # Add CAFullName as a ScriptProperty for CA objects
        if ($this.IsCertificationAuthority()) {
            $this | Add-Member -MemberType ScriptProperty -Name CAFullName -Value {
                if ($this.dNSHostName -and $this.cn) {
                    return "$($this.dNSHostName)\$($this.cn)"
                } elseif ($this.cn) {
                    return $this.cn
                } else {
                    return $null
                }
            }
        }
        
        # Add nTSecurityDescriptor as an alias for ObjectSecurity
        $this | Add-Member -MemberType ScriptProperty -Name nTSecurityDescriptor -Value {
            return $this.ObjectSecurity
        }
    }
    
    # Method to check if this is a Certificate Template
    [bool] IsCertificateTemplate() {
        return $this.SchemaClassName -eq 'pKICertificateTemplate'
    }
    
    # Method to check if this is a CA
    [bool] IsCertificationAuthority() {
        return $this.objectClass -contains 'pKIEnrollmentService'
    }
    
    # Method to get a friendly name for logging
    [string] GetFriendlyName() {
        if ($this.displayName) {
            return $this.displayName
        } elseif ($this.name) {
            return $this.name
        } elseif ($this.cn) {
            return $this.cn
        } else {
            return $this.distinguishedName
        }
    }
}