Private/Reading.ps1
|
# A reading of the Main Part, filled in from the one the Elevated Part took. # # A Check in the Session of the User may not read everything: the processes, the Sessions # and the profile folders of another account answer only with admin rights. Since 0.10 a # Kind that needs them is read a second time in the Elevated Part, by a Kind of its own # that judges nothing, and the Check of the Main Part is handed that reading and fills in # what it could not read itself (see the header of Kinds/Program.ps1 and of # Kinds/Session.ps1). # # The filling in is the same wherever it is done, and is done here, once: # # - the rows of the Main Part are the ones that count: one comes out for each, in their # order, and a row only the Elevated Part has is not added. It was read earlier, and # what it describes may have ended since; # - a row is matched to the row of the Elevated Part with the same key, and only where # that is the same thing still: Windows gives the number of a process or of a Session # to another one once its first owner has ended; # - what the Main Part read itself stays. It was read later. Only what it left absent is # taken from the other row; # - which rows the filling is for, the Kind says. # # The Program and the Session Kind fill in by it. The UserProfile Kind does not: its rule # is another one, field by field - a User Profile only the Elevated Part saw is added, as # it is on the disk whoever saw it; the kind an administrator read replaces one this # account read as local; and of two dates of last use the one from the registry comes # first, whichever reading has it. See ConvertTo-UserProfileOtherRow. # # Why something is still unread afterwards is one of two things, and a Report tells them # apart: nobody read with admin rights in this Run, or Windows did not give it with admin # rights either. Which it is, is a fact about the Run and not about a row: see # Get-UnreadReason. # # Pure, all of it: rows in, rows out. function Test-ReadingAbsent { <# .SYNOPSIS Whether a value of a reading is not there: nothing, or text with nothing in it. Zero and "no" are there. Pure. #> [CmdletBinding()] [OutputType([bool])] param([AllowNull()]$Value) if ($null -eq $Value) { return $true } if ($Value -is [string]) { return -not $Value.Trim() } $false } function Join-ElevatedReading { <# .SYNOPSIS The rows the Main Part read, each with what it left absent filled in from the row of the Elevated Part that has the same key and is the same thing. Pure. .DESCRIPTION One result for each row of the Main Part, in the order they came in: Row the row, with the properties named in Take filled in where they were absent and the other row has them. The very row that came in where nothing was taken; a copy of it with every property it had where something was Elevated the row of the Elevated Part with the same key, whether or not it was taken from, or nothing. For what a Kind has to ask of it beyond the filling Filled whether anything was taken Two rows of the Elevated Part with one key: the later one counts. .PARAMETER Row The rows as the Main Part read them. .PARAMETER Elevated The rows as the Elevated Part read them, or nothing where it did not. .PARAMETER Key The property both are matched by. Compared as text. .PARAMETER Take The properties that are filled in from the row of the Elevated Part where the row of the Main Part has them absent. .PARAMETER Unread Says of a row of the Main Part whether the filling is for it: handed the row, answers yes or no. Without it, the filling is for every row that has one of the properties to take absent. .PARAMETER Same Says whether the row of the Elevated Part is still the same thing as the row of the Main Part, and one to take from: handed the two, in that order, answers yes or no. Without it, the same key is enough. #> [CmdletBinding()] [OutputType([psobject])] param( [AllowNull()][AllowEmptyCollection()]$Row, [AllowNull()][AllowEmptyCollection()]$Elevated, [Parameter(Mandatory)][string]$Key, [Parameter(Mandatory)][string[]]$Take, [scriptblock]$Unread, [scriptblock]$Same ) $byKey = @{} foreach ($other in @($Elevated | Where-Object { $null -ne $_ })) { $byKey["$(Get-DataProperty $other $Key)"] = $other } foreach ($own in @($Row | Where-Object { $null -ne $_ })) { $other = $byKey["$(Get-DataProperty $own $Key)"] $absent = @($Take | Where-Object { Test-ReadingAbsent (Get-DataProperty $own $_) }) $wanted = $(if ($Unread) { [bool](& $Unread $own) } else { [bool]$absent.Count }) $usable = [bool]($wanted -and $null -ne $other -and (-not $Same -or [bool](& $Same $own $other))) $taken = @($absent | Where-Object { $usable -and -not (Test-ReadingAbsent (Get-DataProperty $other $_)) }) $merged = $own if ($taken.Count) { $copy = [ordered]@{} if ($own -is [System.Collections.IDictionary]) { foreach ($name in $own.Keys) { $copy["$name"] = $own[$name] } } else { foreach ($property in $own.PSObject.Properties) { $copy[$property.Name] = $property.Value } } foreach ($name in $taken) { $copy[$name] = Get-DataProperty $other $name } $merged = [pscustomobject]$copy } [pscustomobject]@{ Row = $merged; Elevated = $other; Filled = [bool]$taken.Count } } } function Get-UnreadReason { <# .SYNOPSIS Why something a Check could not read itself is still unread: 'NotAsked' where nobody read with admin rights in this Run, 'Refused' where the Elevated Part read and Windows did not give it even then. Pure. .DESCRIPTION Asked of the data of the Check, which holds as ElevatedRead whether the reading with admin rights was handed to it. That it was, and what is absent all the same, is what "not readable with admin rights either" means. .PARAMETER Data The data of the Check, or the part of it that holds ElevatedRead. #> [CmdletBinding()] [OutputType([string])] param([AllowNull()]$Data) if ([bool](Get-DataProperty $Data 'ElevatedRead')) { return 'Refused' } 'NotAsked' } function Get-UnreadText { <# .SYNOPSIS What a cell says in place of a value that is not there: "nicht gelesen" or "nicht lesbar". Pure. .DESCRIPTION Two wordings, and each means one thing wherever a Report says it: nicht gelesen Gutcheck did not read it in this Run: nobody asked with admin rights, the Check that reads it did not run, or the Run is not in the Session of the User nicht lesbar it was asked for, and Windows did not give it A Technician who reads the first runs Gutcheck again, with admin rights or in the right Session. One who reads the second need not: it will say the same. Where a Kind fills in from the Elevated Part, which of the two it is follows from Get-UnreadReason, and the Kind hands that on. A Kind that asked Windows itself and was given nothing says 'Refused'. .PARAMETER Reason 'NotAsked' or 'Refused': see Get-UnreadReason. #> [CmdletBinding()] [OutputType([string])] param([Parameter(Mandatory)][ValidateSet('NotAsked', 'Refused')][string]$Reason) if ($Reason -eq 'Refused') { return Get-Text 'Value.Cell.Unreadable' } Get-Text 'Value.Cell.NotRead' } |