Providers/Authentik/Public/Test-AuthentikEnvironment.ps1
|
function Test-AuthentikEnvironment { <# .SYNOPSIS Verifies that the seeded Authentik instance matches the seed data .DESCRIPTION Reads the users, groups and applications the module owns in the connected instance, the same way teardown finds them, and compares them with the seed files: every seeded username, group name and application name should be present, nothing the module owns should be there that the data does not describe, every user's name should match the data by codepoint, and every group a user row lists should hold that user. Names are compared ordinally, not with -eq, because a decomposed and a precomposed name are equal to -eq and different on the wire; a name that came back mangled is the fault this exists to catch. Every other seeded type - providers, entitlements, scope mappings, roles, outposts, certificates, flows, stages, policies, notification rules and transports, tokens and invitations - is counted and reported without a verdict, because its rows do not map one to one onto objects. .PARAMETER SkipMembership Do not compare group memberships .PARAMETER Quiet Return the result without writing to the console .OUTPUTS PSCustomObject typed TestEnvironmentVerification. Passed is $true when every check passed. .EXAMPLE PS> Test-AuthentikEnvironment Prints one line per check and returns the result. .EXAMPLE PS> (Test-AuthentikEnvironment -Quiet).Checks | Where-Object { $_.Passed -eq $false } Lists only the checks that failed, with their missing and unexpected names. #> [CmdletBinding()] [OutputType([PSCustomObject])] param( [Parameter()] [switch]$SkipMembership, [Parameter()] [switch]$Quiet ) $connection = Get-AuthentikConnection $marker = Get-AuthentikSeedMarker -Connection $connection $dataPath = Get-AuthentikDataPath $checks = New-Object System.Collections.Generic.List[object] $read = { param($file) @(Import-Csv -Path (Join-Path -Path $dataPath -ChildPath $file) -Encoding UTF8) } $prefixed = { param($name) '{0}{1}' -f $marker.Prefix, $name } # --- Users ----------------------------------------------------------------------------- $userRows = & $read 'AuthentikUsers.csv' $users = @(Get-AuthentikSeededObject -Type Users -Connection $connection) $checks.Add((New-TestEnvironmentCheck -Name 'Users' ` -Expected @($userRows | ForEach-Object { $_.Username }) ` -Found @($users | ForEach-Object { [string]$_.username }))) $userByName = @{} foreach ($user in $users) { if ($user.username) { $userByName[[string]$user.username] = $user } } $compared = 0 $mismatch = foreach ($row in $userRows) { $user = $userByName[$row.Username] if (-not $user) { continue } $compared++ if (-not [string]::Equals([string]$user.name, [string]$row.Name, [StringComparison]::Ordinal)) { "{0}: '{1}' should be '{2}'" -f $row.Username, $user.name, $row.Name } } $checks.Add((New-TestEnvironmentCheck -Name 'User names' -Compared $compared -Mismatch @($mismatch))) # --- Groups and applications ----------------------------------------------------------- $groupRows = & $read 'AuthentikGroups.csv' $groups = @(Get-AuthentikSeededObject -Type Groups -Connection $connection) $checks.Add((New-TestEnvironmentCheck -Name 'Groups' ` -Expected @($groupRows | ForEach-Object { & $prefixed $_.DisplayName }) ` -Found @($groups | ForEach-Object { [string]$_.name }))) $applicationRows = & $read 'AuthentikApplications.csv' $applications = @(Get-AuthentikSeededObject -Type Applications -Connection $connection) $checks.Add((New-TestEnvironmentCheck -Name 'Applications' ` -Expected @($applicationRows | ForEach-Object { & $prefixed $_.Name }) ` -Found @($applications | ForEach-Object { [string]$_.name }))) # --- Memberships ----------------------------------------------------------------------- if (-not $SkipMembership) { # A user row names its groups by their row key; the instance names them by their prefixed # display name and holds them on the user as primary keys. $groupNameByKey = @{} foreach ($row in $groupRows) { $groupNameByKey[$row.Name] = & $prefixed $row.DisplayName } $groupNameByPk = @{} foreach ($group in $groups) { $groupNameByPk[[string]$group.pk] = [string]$group.name } $expectedPairs = New-Object System.Collections.Generic.List[string] $foundPairs = New-Object System.Collections.Generic.List[string] foreach ($row in $userRows) { foreach ($key in @($row.Groups -split ';' | Where-Object { $_ })) { $groupName = if ($groupNameByKey.ContainsKey($key)) { $groupNameByKey[$key] } else { & $prefixed $key } $expectedPairs.Add(('{0} <- {1}' -f $groupName, $row.Username)) } } foreach ($user in $users) { foreach ($pk in @($user.groups)) { $groupName = $groupNameByPk[[string]$pk] if ($groupName) { $foundPairs.Add(('{0} <- {1}' -f $groupName, [string]$user.username)) } } } $checks.Add((New-TestEnvironmentCheck -Name 'Group memberships' -Expected $expectedPairs -Found $foundPairs -MissingOnly)) } # --- Everything else, counted ---------------------------------------------------------- foreach ($type in 'Providers', 'Entitlements', 'ScopeMappings', 'Roles', 'Outposts', 'Certificates', 'Flows', 'Stages', 'Policies', 'NotificationRules', 'NotificationTransports', 'Tokens', 'Invitations') { try { $count = @(Get-AuthentikSeededObject -Type $type -Connection $connection).Count $checks.Add((New-TestEnvironmentCheck -Name $type -FoundCount $count)) } catch { Write-Warning "Could not count the seeded $type`: $($_.Exception.Message)" } } return New-TestEnvironmentVerification -Provider 'Authentik' -Target $connection.BaseUrl -Check $checks.ToArray() -Quiet:$Quiet } |