serviceAccountModule/Tests/Test.ServiceAccount.ps1

#Define global constants
$script:Service_Account_Name = "newServiceAcct"

function New-ServiceAccount
{
    param(
        [Parameter(Mandatory=$true,Position=0)]
        [string]$username,
        
        [Parameter(Mandatory=$true,Position=1)]
        [string]$password
    )
    Process
    {
        ipmo ActiveDirectory
        $oldLocation = Get-Location
        Set-Location AD:
        
        $encryptedPassword = $password | ConvertTo-SecureString -asPlainText -Force

        try
        {
            $userObject = Get-ADUser $userName -ErrorAction SilentlyContinue
        }
        catch
        {
        }
        
        if ($userObject -eq $null)
        {
            New-ADUser -Name  $userName 
        }

        Get-ADUser $userName | Set-ADAccountPassword -Reset -NewPassword $encryptedPassword -PassThru 
        Get-ADUser $userName | Set-ADUser -PasswordNeverExpires $true -PassThru -Description "Password: $password" -Enabled $true
        
        Set-Location $oldLocation
    }
}

#####################################################################
####Helper functions related to rule parsing logic###################
#####################################################################

<#
.SYNOPSIS
    Class to encapsulate parsing of the ADFS Issuances/Auth rules.
#>


class AdfsRules
{
    [System.Collections.ArrayList] hidden $rules

    <#
    .SYNOPSIS
        Constructor
    #>

    AdfsRules([string]$rawRules) 
    {
        $rulesArray = $this.ParseRules($rawRules)
        $this.rules = New-Object "System.Collections.ArrayList"
        $this.rules.AddRange($rulesArray)
    }

    <#
    .SYNOPSIS
        Utility function to parse the rules and return them as a string[].
    #>

    [string[]] hidden ParseRules([string]$rawRules)
    {
        Write-Verbose "$($PSCmdlet.MyInvocation.MyCommand) : BEGIN"

        $allRules = @()
        $singleRule = [string]::Empty

        $rawRules.Split("`n") | %{
            
            $line = $_.ToString().Trim()

            if (-not ([string]::IsNullOrWhiteSpace($line)) ) 
            {
                $singleRule += $_ + "`n"

                if ($line.StartsWith("=>"))
                {
                    Write-Verbose "$($PSCmdlet.MyInvocation.MyCommand) : Parsed rule:`n$singleRule"
                    $allRules += $singleRule
                    $singleRule = [string]::Empty
                }
            }
        }

        Write-Verbose "$($PSCmdlet.MyInvocation.MyCommand) : END"

        return $allRules
    }

    [int] NumberOfRules()
    {
        return $this.rules.Count
    }

    <#
    .SYNOPSIS
        Finds the rule by name in the format: @RuleName = "$ruleName". Returns $null if not found.
    #>

    [string] FindByRuleName([string]$ruleName)
    {
        $ruleNameSearchString = '@RuleName = "' + $ruleName + '"'
        Write-Verbose "$($PSCmdlet.MyInvocation.MyCommand) : Search string: $ruleNameSearchString"

        foreach ($rule in $this.rules)
        {
            if ($rule.Contains($ruleNameSearchString))
            {
                Write-Verbose "$($PSCmdlet.MyInvocation.MyCommand) : Found.`n$rule"
                return $rule
            }
        }

        Write-Verbose "$($PSCmdlet.MyInvocation.MyCommand) : NOT FOUND. Returning $null"
        return $null;
    }

    <#
    .SYNOPSIS
        Replaces the specified old rule with the new one. Returns $true if the old one was found and replaced; $false otherwise.
    #>

    [bool] ReplaceRule([string]$oldRule, [string]$newRule)
    {
        Write-Verbose "$($PSCmdlet.MyInvocation.MyCommand) : Trying to replace old rule with new.`n Old Rule:`n$oldRule`n New Rule:`n$newRule"
        $idx = $this.FindIndexForRule($oldRule)

        if ($idx -ge 0)
        {
            Write-Verbose "$($PSCmdlet.MyInvocation.MyCommand) : Replacing old rule with new."
            $this.rules[$idx] = $newRule
            return $true
        }

        Write-Verbose "$($PSCmdlet.MyInvocation.MyCommand) : Old rule is not found so NOT replacing it."
        return $false
    }

    <#
    .SYNOPSIS
        Removes the specified if found. Returns $true if found; $false otherwise.
    #>

    [bool] RemoveRule([string]$ruleToRemove)
    {
        Write-Verbose "$($PSCmdlet.MyInvocation.MyCommand) : Trying to remove rule.`n Rule:`n$ruleToRemove"

        $idx = $this.FindIndexForRule($ruleToRemove)

        if ($idx -ge 0)
        {
            Write-Verbose "$($PSCmdlet.MyInvocation.MyCommand) : Removing rule at index: $idx."
            $this.rules.RemoveAt($idx)
            return $true
        }

        Write-Verbose "$($PSCmdlet.MyInvocation.MyCommand) : Rule is not found so NOT removing it."
        return $false
    }

    <#
    .SYNOPSIS
        Helper function to find the index of the rule. Returns index if found; -1 otherwise.
    #>

    [int] FindIndexForRule([string]$ruleToFind)
    {
        Write-Verbose "$($PSCmdlet.MyInvocation.MyCommand) : Trying to find rule.`n Rule:`n$ruleToFind"

        for ($i = 0; $i -lt $this.rules.Count; $i++)
        {
            $rule = $this.rules[$i]

            if ($rule.trim().Equals($ruleToFind.trim()))
            {
                Write-Verbose "$($PSCmdlet.MyInvocation.MyCommand) : Found at index: $i."
                return $i
            }
        }

        Write-Verbose "$($PSCmdlet.MyInvocation.MyCommand) : NOT FOUND. Returning -1"
        return -1
    }
    
    <#
    .SYNOPSIS
        Returns all the rules as string.
    #>

    [string] ToString()
    {
        return [string]::Join("`n", $this.rules.ToArray())
    }
}

# Gets internal ADFS settings by extracting them Get-AdfsProperties
function Get-AdfsInternalSettings()
{
    $settings = Get-AdfsProperties
    $settingsType = $settings.GetType()
    $propInfo = $settingsType.GetProperty("ServiceSettingsData", [System.Reflection.BindingFlags]::Instance -bor [System.Reflection.BindingFlags]::NonPublic)
    $internalSettings = $propInfo.GetValue($settings, $null)
    
    return $internalSettings
}

function ValidateRules
{
    param
    (
        [parameter()]
        [switch]$CheckNotPresent
    )

    $Properties = Get-AdfsInternalSettings
    $AuthorizationPolicyRules = [AdfsRules]::new($Properties.PolicyStore.AuthorizationPolicy) 
    $AuthorizationPolicyReadOnlyRules = [AdfsRules]::new($Properties.PolicyStore.AuthorizationPolicyReadOnly)

    $SID = (New-Object system.security.principal.NtAccount($Service_Account_Name )).translate([system.security.principal.securityidentifier])
    $ServiceAccountRule = "@RuleName = `"Permit Service Account`"`nexists([Type == `"http://schemas.microsoft.com/ws/2008/06/identity/claims/primarysid`", Value == `"$SID`"])`n=> issue(Type = `"http://schemas.microsoft.com/authorization/claims/permit`", value = `"true`");`n`n"

    $AuthPolicyIndex = $AuthorizationPolicyRules.FindIndexForRule($ServiceAccountRule)
    $ReadOnlyIndex = $AuthorizationPolicyReadOnlyRules.FindIndexForRule($ServiceAccountRule)

    if($CheckNotPresent)
    {
        return ($AuthPolicyIndex -eq -1 -and $ReadOnlyIndex -eq -1)
    }
    return ($AuthPolicyIndex -ne -1 -and $ReadOnlyIndex -ne -1)

}


function Initialize()
{
    ipmo .\ServiceAccount.psm1
    New-ServiceAccount -username $script:Service_Account_Name -password "Password"
}

Describe 'Basic functionality of adding and removing service account rule'{
    BeforeAll {
        Initialize
    }

    AfterAll {
        Remove-ADUser -Identity $script:Service_Account_Name
    }

    It "[00000]: Add-AdfsServiceAccountRule adds permit rule to ruleset"{
        Add-AdfsServiceAccountRule -ServiceAccount $script:Service_Account_Name
        ValidateRules | Should Be $true
    }

    It "[00000]: Add-AdfsServiceAccountRule fails if rule already exists"{
        $BeforeProperties = Get-AdfsInternalSettings
        $BeforeAuthorizationPolicyRules = [AdfsRules]::new($Properties.PolicyStore.AuthorizationPolicy) 
        $BeforeAuthorizationPolicyReadOnlyRules = [AdfsRules]::new($Properties.PolicyStore.AuthorizationPolicyReadOnly)

        Add-AdfsServiceAccountRule -ServiceAccount $script:Service_Account_Name

        $AfterProperties = Get-AdfsInternalSettings
        $AfterAuthorizationPolicyRules = [AdfsRules]::new($Properties.PolicyStore.AuthorizationPolicy) 
        $AfterAuthorizationPolicyReadOnlyRules = [AdfsRules]::new($Properties.PolicyStore.AuthorizationPolicyReadOnly)

        $AuthPolicyMatches = $BeforeAuthorizationPolicyRules.NumberOfRules() -eq $AfterAuthorizationPolicyRules.NumberOfRules()
        $ReadOnlyMatches = $BeforeAuthorizationPolicyReadOnlyRules.NumberOfRules() -eq $AfterAuthorizationPolicyReadOnlyRules.NumberOfRules()



        ($AuthPolicyMatches -eq $ReadOnlyMatches) | Should Be $true
    }

    It "[00000]: Remove-AdfsServiceAccountRule removes permit rule to ruleset"{
        Remove-AdfsServiceAccountRule -ServiceAccount $script:Service_Account_Name 
        ValidateRules -CheckNotPresent | Should Be $true
    }

    It "[00000]: Remove-AdfsServiceAccountRule does nothing if rule isn't present"{
        $BeforeProperties = Get-AdfsInternalSettings
        $BeforeAuthorizationPolicyRules = [AdfsRules]::new($Properties.PolicyStore.AuthorizationPolicy) 
        $BeforeAuthorizationPolicyReadOnlyRules = [AdfsRules]::new($Properties.PolicyStore.AuthorizationPolicyReadOnly)

        Remove-AdfsServiceAccountRule -ServiceAccount $script:Service_Account_Name

        $AfterProperties = Get-AdfsInternalSettings
        $AfterAuthorizationPolicyRules = [AdfsRules]::new($Properties.PolicyStore.AuthorizationPolicy) 
        $AfterAuthorizationPolicyReadOnlyRules = [AdfsRules]::new($Properties.PolicyStore.AuthorizationPolicyReadOnly)

        $AuthPolicyMatches = $BeforeAuthorizationPolicyRules.NumberOfRules() -eq $AfterAuthorizationPolicyRules.NumberOfRules()
        $ReadOnlyMatches = $BeforeAuthorizationPolicyReadOnlyRules.NumberOfRules() -eq $AfterAuthorizationPolicyReadOnlyRules.NumberOfRules()



        ($AuthPolicyMatches -eq $ReadOnlyMatches) | Should Be $true
    }

    It "[00000]: Add-AdfsServiceAccountRule adds permit rule to ruleset"{
        $ErrorThrown = $false
        try
        {
            Add-AdfsServiceAccountRule -ServiceAccount "fakeAccount"
        }
        catch
        {
            $ErrorThrown = $true
        }
        $ErrorThrown | Should Be $true
    }

}