Shared/LDAPEntry.psm1

Set-StrictMode -Version Latest
$ErrorActionPreference = [Management.Automation.ActionPreference]::Stop

# Note: Everything in this file should be considered semi-deprecated. Previous
# versions of this AD lib were more focused on mutating entries than using them
# as immutable record objects.

function Update-LDAPEntryFlag {
    <#
    .SYNOPSIS
        Add a boolean note property to the entry based on a bitfield value.
    .DESCRIPTION
        Editing the direct members of the entries is risky, so instead we can
        set Note properties to expose useful boolean flags that are expressed
        internally to the properties as bit fields. These Note properties will
        shadow the built-in properties that cannot be meaningfully updated and
        don't necessarily match their underlying value.
    #>

    [Diagnostics.CodeAnalysis.SuppressMessage(
        'PSShouldProcess','',Scope='Function',Justification='-WhatIf passed through to Add-Member func'
    )]
    [CmdletBinding(SupportsShouldProcess)]
    param (
        # The entry to set a note flag upon.
        [Parameter(Mandatory, ValueFromPipelineByPropertyName)]
        [PSCustomObject] $Entry,

        # The property name of the entry to read the note flag from.
        [Parameter(Mandatory, ValueFromPipelineByPropertyName)]
        [string] $BitFieldLDAPProperty,

        # The value to bit-test against the entry property.
        [Parameter(Mandatory, ValueFromPipelineByPropertyName)]
        [int] $BitMask,

        # The name to set the resulting Boolean note onto the entry.
        [Parameter(Mandatory, ValueFromPipelineByPropertyName)]
        [string] $NotePropertyName,

        # What value to set on the object it the bit value is true.
        [Parameter(Mandatory, ValueFromPipelineByPropertyName)]
        [object] $TrueValue,

        # What value to set on the object it the bit value is false. If the bit
        # value is found, nothing will be set on the object, which allows
        # multiple bit-values to contribute to a single note property.
        [Parameter(ValueFromPipelineByPropertyName)]
        [object] $FalseValue = $null
    )
    process {
        [bool] $isFlagTrue = Get-LDAPEntryFlag $Entry $BitFieldLDAPProperty $BitMask
        
        $noteValue = if ($isFlagTrue) {
            $TrueValue
        } else {
            $FalseValue
        }
        # TODO: Switch to regular property setting now that properties are all real.

        # $noteValue will only be $null if the bitmask returned false *and*
        # there was no FalseValue provided.
        if ($null -ne $noteValue) {
            $entry | Add-Member -NotePropertyName $NotePropertyName -NotePropertyValue $noteValue -Force
        }
    }
}


function Get-LDAPEntryFlag {
    <#
    .SYNOPSIS
        Gets a bitfield flag within the properties of a given directory entry
        PSCustomObject. This operation happens offline and is not sent to the
        server until Set-ADObject is called with the explicit members to
        replace.
    #>

    [OutputType([bool])]
    [CmdletBinding()]
    param (
        # The entry to set a note flag upon.
        [Parameter(Mandatory, ValueFromPipeline)]
        [PSCustomObject] $Entry,

        # The property name of the entry to read the note flag from.
        [Parameter(Mandatory)]
        [string] $BitFieldLDAPProperty,

        # The value to bit-test against the entry property.
        [Parameter(Mandatory)]
        [int] $BitMask
    )
    process {
        $attribute = $Entry.Attributes[$BitFieldLDAPProperty]
        
        # output
        [bool] ($attribute -band $BitMask)
    }
}


function Set-LDAPEntryFlag {
    <#
    .SYNOPSIS
        Set or clears bitfield flag within the LDAP attributes of a given
        directory entry PSCustomObject. This operation happens offline and is
        not sent to the server until Set-ADObject is called with the explicit
        members to replace.
    #>

    [CmdletBinding(SupportsShouldProcess)]
    param (
        # The entry to set a note flag upon.
        [Parameter(Mandatory, ValueFromPipeline)]
        [PSCustomObject] $Entry,

        # The property name of the entry to read the note flag from.
        [Parameter(Mandatory)]
        [string] $BitFieldLDAPProperty,

        # The value to bit-test against the entry property.
        [Parameter(Mandatory)]
        [int] $BitMask,

        # Whether to set or clear the bitmask from the bitfield.
        [bool] $Value
    )
    process {
        if ($PSCmdlet.ShouldProcess("'$(Get-DistinguishedName $Entry)' property '$BitFieldLDAPProperty' flag '$("0x" + $BitMask.ToString('X'))' to '$value'")) {
            $Entry.Properties[$BitFieldLDAPProperty] = if ($Value) {
                # true
                $Entry.Properties[$BitFieldLDAPProperty] -bor $BitMask
            } else {
                # false
                $Entry.Properties[$BitFieldLDAPProperty] -band (-bnot $BitMask)
            }
        }
    }
}


function Set-LDAPEntryAttributeTable {
    <#
    .SYNOPSIS
        Set properties of a given directory entry PSCustomObject from the given Properties hashtable
    #>

    [CmdletBinding(SupportsShouldProcess)]
    param (
        # The entry to set a note flag upon.
        [Parameter(Mandatory, ValueFromPipeline)]
        [PSCustomObject] $Entry,

        [Parameter(Mandatory)]
        [Hashtable] $AttributeTable
    )
    begin {
        if ($PSCmdlet.ShouldProcess($AttributeTable['distinguishedName'])) {
            # Add raw LDAP attributes as hashtable member of object.
            if ($Entry | Get-Member Attributes) {
                $Entry.PSObject.Properties.Remove('Attributes')
            }
            $Entry | Add-Member -NotePropertyName Attributes -NotePropertyValue $AttributeTable

            # A bit of backwards compatibility to DirectoryEntry, which calls
            # the attribute collection "Properties".
            if ($Entry | Get-Member Properties) {
                $Entry.PSObject.Properties.Remove('Properties')
            }
            $Entry | Add-Member -NotePropertyName Properties -NotePropertyValue $AttributeTable
        }
    }
}