private/stigData.psm1

#region Header
using module .\organizationSettings.psm1
using module .\exceptions.psm1
using module .\skipSettings.psm1
#endregion Header
#region Main Functions
function Get-StigDataRootPath 
{
    [cmdletbinding()]
    Param()

    # The path needs to take into account the version folder that changes with each release
    $rootPath = (Resolve-Path -Path $PSScriptRoot\..).Path

    return (Get-ChildItem -Path $rootPath -Filter 'StigData' -Directory -Recurse).FullName
}

function Get-StigParameterSet
{
    [cmdletbinding(DefaultParameterSetName = '1')]
    [outputtype([string[]])]
    param
    (
        [string]
        $Path,

        [parameter(Mandatory = $false, ParameterSetName = '1')]
        [parameter(Mandatory = $true, ParameterSetName = '2')]
        [AllowEmptyString()]
        [AllowNull()]
        [string]
        $TechnologyVersion,

        [parameter(Mandatory = $true, ParameterSetName = '2')]
        [AllowEmptyString()]
        [AllowNull()]
        [string]
        $TechnologyRole 
    )
    
    switch ($PSCmdlet.ParameterSetName)
    {
        1 {$include = "*" }
        2 {$include = "$TechnologyVersion-$TechnologyRole-*" }
    }

    (Get-ChildItem -Path $Path -Include $include -Exclude "*.org.*" -Recurse).BaseName | 
        ForEach-Object {($PSItem -Split "-")[-1]}
}

function Get-CommonStigParameters
{
    [cmdletbinding(DefaultParameterSetName = '1')]
    param
    (
        [Parameter(Mandatory = $true)]
        [string]
        $Technology,

        [parameter(Mandatory = $false, ParameterSetName = '1')]
        [parameter(Mandatory = $true, ParameterSetName = '2')]
        [string]
        $TechnologyVersion,

        [parameter(Mandatory = $false, ParameterSetName = '2')]
        [string]
        $TechnologyRole
    )

    begin 
    {
        $PSBoundParameters.Remove('Technology') | Out-Null
        $PSBoundParameters.Add('Path', "$(Get-StigDataRootPath)\$Technology") | Out-Null
    }

    process
    {
        # Create a dictionary that will contain the parameters
        $runtimeParameterDictionary = New-Object System.Management.Automation.RuntimeDefinedParameterDictionary
         
        ################################### StigVersion ####################################
        # Set the dynamic parameters' name
        $parameterName = 'StigVersion'

        # Create the collection of attributes
        $attributeCollection = New-Object System.Collections.ObjectModel.Collection[System.Attribute]
            
        # Create and set the parameters' attributes
        $parameterAttribute = New-Object System.Management.Automation.ParameterAttribute
        $parameterAttribute.Mandatory = $true

        # Add the attributes to the attributes collection
        $attributeCollection.Add($parameterAttribute)

        # Generate and set the ValidateSet
        $arrSet = Get-StigParameterSet @PSBoundParameters
        $ValidateSetAttribute = New-Object System.Management.Automation.ValidateSetAttribute($arrSet)

        # Add the ValidateSet to the attributes collection
        $attributeCollection.Add($ValidateSetAttribute)

        # Create the dynamic parameter and add it to the dictionary
        $runtimeParameter = New-Object System.Management.Automation.RuntimeDefinedParameter(
            $parameterName, [string], $attributeCollection)
        $runtimeParameterDictionary.Add($parameterName, $runtimeParameter)
        ################################### StigVersion ####################################
        ################################### All Others ####################################
        # List the dynamic parameters with their types
        $parameterList = @{
            'SkippedRuleType'          = 'psobject';
            'SkippedRule'              = 'psobject';
            'StigException'            = 'psobject';
            'StigTitlePrefix'          = 'string';
            'StigTitleSufix'           = 'string';
            'OrganizationSettingsPath' = 'string'
        }
        
        foreach ($parameter in $parameterList.GetEnumerator())
        {
            # Set the dynamic parameters' name
            $parameterName = $parameter.Name
            
            # Create the collection of attributes
            $attributeCollection = New-Object System.Collections.ObjectModel.Collection[System.Attribute]
            
            # Create and set the parameters' attributes
            $parameterAttribute = New-Object System.Management.Automation.ParameterAttribute
            $parameterAttribute.Mandatory = $false
        
            # Add the attributes to the attributes collection
            $attributeCollection.Add($parameterAttribute)
            
            # Create and return the dynamic parameter with the correct type
            switch ($parameter.Value)
            {
                'psobject'
                {
                    $type = [PSobject] ; continue
                }
                'string'
                {
                    $type = [string]   ; continue
                }
            }
            $runtimeParameter = New-Object System.Management.Automation.RuntimeDefinedParameter(
                $parameterName, $type, $attributeCollection)

            $runtimeParameterDictionary.Add($parameterName, $runtimeParameter)
        }
        ################################### All Others ####################################
        return $runtimeParameterDictionary
    }
}

<#
    .SYNOPSIS
    Used to validate an xml file against a specified schema
 
    .PARAMETER XmlFile
    Path and file name of the XML file to be validated
 
    .PARAMETER Xml
    An already loaded System.Xml.XmlDocument
 
    .PARAMETER SchemaFile
    Path of XML schema used to validate the XML document
 
    .PARAMETER ValidationEventHandler
    Script block that is run when an error occurs while validating XML
 
    .EXAMPLE
    Test-XML -XmlFile C:\source\test.xml -SchemaFile C:\Source\test.xsd
 
    .EXAMPLE
    $xmlobject = Get-StigData -OsVersion 2012R2 -OsRole MemberServer
    Test-XML -Xml $xmlobject -SchemaFile C:\Source\test.xsd
#>

Function Test-XmlSchema
{
    [cmdletbinding()]
    [outputtype([bool])]
    param 
    (     
        [Parameter(ValueFromPipeline = $true, Mandatory = $true, ParameterSetName = 'File')]
        [string] 
        $XmlFile,

        [Parameter(ValueFromPipeline = $true, Mandatory = $true, ParameterSetName = 'Object')]
        [xml] 
        $Xml,

        [Parameter()]
        [string] 
        $SchemaFile = "$(Get-StigDataRootPath)\PowerStig.xsd",

        [scriptblock] 
        $ValidationEventHandler = { Throw $_.Exception }
    )

    If ( -not ( Test-Path -Path $SchemaFile ) )
    {
        Throw "Schema file not found"
    }

    $schemaReader = New-Object System.Xml.XmlTextReader $SchemaFile
    $schema = [System.Xml.Schema.XmlSchema]::Read( $schemaReader, $ValidationEventHandler )

    If ( $PsCmdlet.ParameterSetName -eq "File" )
    {
        $xml = New-Object System.Xml.XmlDocument
        try 
        {
            $xml.Load($XmlFile)
        }
        catch [System.Management.Automation.MethodInvocationException]
        {
            return $false
        }
    }

    $xml.Schemas.Add( $schema ) | Out-Null

    try 
    {
        $xml.Validate( $ValidationEventHandler )
        return $true
    }
    catch
    {
        Write-Error 'Xml Schema Validation failed.'
        return $false
    }
}

#TODO: The help file for this needs to be updated for this function
<#
    .SYNOPSIS
       Get-StigData retrieves the DscStigXml file from the path provided along with the OS and STIG
       version to return.
 
    .DESCRIPTION
       This function is the heart of the data retrieval process of the composite resource. It will pull
       the stig file using the provided parameters, merge in the data from the local settings file,
       and overwrite any settings that are provided in the exception list.
 
    .PARAMETER StigDataPath
       The root directory to look for the DscStigXml files. This parameter us used to build the path
       to the translated DscStigXml file
 
    .PARAMETER OsVersion
       The version of the operating system STIG to lookup. This parameter us used to build the path
       to the translated DscStigXml file
 
     .PARAMETER StigVersion
       The version number to look up and return. This parameter us used to build the path
       to the translated DscStigXml file
 
    .PARAMETER StigException
       A hashtable os exceptions to overwrite STIG settings.
 
    .PARAMETER SkippedRule
        An array of one or more rules to be added to the SkipRule Node.
 
    .PARAMETER SkippedRuleType
           An array of one or more rule types to be added to the SkipRule Node.
 
    .EXAMPLE
       Get the 2.8 version of the Windows Server 2012 R2 STIG from the C:\Stigs directory.
 
       Get-StigData -OsVersion 2012R2 -StigVersion 2.8 -StigDataPath c:\Stigs
 
    .EXAMPLE
       Get the 2.8 version of the Windows Server 2012 R2 STIG from the C:\Stigs directory and overwrites
       the STIG setting for V-1079 with 0 before returnign the data.
        
       Get-StigData -OsVersion 2012R2 -StigVersion 2.8 -StigDataPath c:\Stigs -StigException @{id=V-1079,value=0}
 
    .NOTES
       General notes
#>

function Get-StigData {
    [outputtype([xml])]
    [cmdletbinding(DefaultParameterSetName = 'Default')]
    param
    (
        [Parameter(Mandatory = $true)]
        [string]
        $Path,

        [Parameter()]
        [psobject] 
        $StigException,
        
        [Parameter()]
        [psobject] 
        $SkippedRule,

        [Parameter()]
        [psobject] 
        $SkippedRuleType,

        [Parameter()]
        [string] 
        $OrganizationSettingsPath,

        [Parameter()]
        [string] 
        $StigTitlePrefix,

        [Parameter()]
        [string] 
        $StigTitleSuffix = '[Exception]'
    )

    [xml] $stigContent = Get-Content -Path $Path

    ################################### OrganizationSettings ###################################
    $OrganizationSettingsParameters = @{
        stigContent     = ([ref] $stigContent) 
        StigContentPath = $Path 
    }
    
    if ( $OrganizationSettingsPath ) {
        if ( Test-XmlSchema -XmlFile $OrganizationSettingsPath ) {
            $OrganizationSettingsParameters.add(
                'OrganizationSettingsPath', $OrganizationSettingsPath)
        }
        else {
            throw "The file $OrganizationSettingsPath failed schema validation.
            "
 +
            "Please run New-OrganizationSettingsFile to obtain a template file."
        }
    }

    Merge-OrganizationSettingsFile @OrganizationSettingsParameters
    ################################### OrganizationSettings ###################################
    ################################### StigExceptions ###################################
    if ( $StigException ) {
        $StigExceptionsParameters = @{
            stigContent    = ([ref] $stigContent) 
            StigExceptions = $StigException 
        }

        Merge-StigExceptions @StigExceptionsParameters
    }
    ################################### StigExceptions ###################################
    ################################### SkippedRule ###################################
    if ( $SkippedRule ) {
        $SkippedRuleParameters = @{
            stigContent    = ([ref] $stigContent) 
            SkippedRule = $SkippedRule 
        }

        Merge-SkippedRule @SkippedRuleParameters
    }
        ################################### SkippedRuleType ###################################
    if ( $SkippedRuleType ) {
        $SkippedRuleTypeParameters = @{
            stigContent    = ([ref] $stigContent) 
            SkippedRuleType = $SkippedRuleType 
        }

        Merge-SkippedRuleType @SkippedRuleTypeParameters
    }

    return $stigContent
}

function Get-HigestStigVersion
{
    [CmdletBinding()]
    [outputtype([version])]
    Param
    (
        [Parameter(Mandatory = $true)]
        [string]
        $StigDataRootPath
    )

    $HigestStigVersionInTarget = ((Get-ChildItem -Path $stigDataRootPath -Exclude "*org*").BaseName |
        Foreach-Object {($PsItem -split "-")[2]} | 
            Select-Object -unique | 
                Sort-Object -descending)[0]

    return $HigestStigVersionInTarget
}
#endregion Main Functions