Functions/GenXdev.FileSystem/Confirm-InstallationConsent.ps1

##############################################################################
# Part of PowerShell module : GenXdev.FileSystem
# Cmdlet filename : Confirm-InstallationConsent.ps1
# Author : René Vaessen / GenXdev (with AI assistance)
# Version : 1.0.0
################################################################################
# MIT License
#
# Copyright 2021-2025 GenXdev
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
################################################################################
<#
.SYNOPSIS
Confirms user consent for installing third-party software, using
preferences for persistent choices.
 
.DESCRIPTION
This function acts as a custom ShouldProcess mechanism specifically for
software installations. It checks a user preference to determine if
automatic installation is allowed for the specified application. If no
preference is set, it prompts the user with a clear explanation of what
will be installed, the source, potential risks, and options to allow or
deny the installation (with options for one-time or persistent choices).
 
This ensures explicit user consent before proceeding with any installation,
helping to mitigate potential legal risks by requiring affirmative action
from the user. The prompt clearly states that the module author (GenXdev)
is not responsible for third-party software, and that the user is
consenting to the installation at their own risk.
 
Preferences are stored in a JSON file, allowing users to set "always
allow" for specific applications or enable global consent for all GenXdev
third-party installations, making it convenient while remaining legally
sound.
 
If consent is denied (or preference is set to deny), the function returns $false and does not
proceed with installation. If allowed, it returns $true.
 
.PARAMETER ApplicationName
The name of the application or software being installed (e.g., "Docker
Desktop", "Sysinternals Suite"). This is used to create a unique
preference key like "AllowInstall_DockerDesktop".
 
.PARAMETER Source
The source of the installation (e.g., "Winget", "PowerShell Gallery",
"apt-get in WSL", "dotnet CLI"). This is included in the explanation
prompt for transparency.
 
.PARAMETER Description
Optional detailed description of what the software does and why it's
being installed. If not provided, a generic message is used.
 
.PARAMETER Publisher
Optional publisher or vendor of the software (e.g., "Microsoft", "Docker
Inc."). Included in the prompt for clarity.
 
.PARAMETER ForceConsent
Forces a prompt even if a preference is already set (useful for
re-confirmation).
 
.EXAMPLE
if (Confirm-InstallationConsent -ApplicationName "Docker Desktop" `
        -Source "Winget") {
    # Proceed with installation
    Microsoft.WinGet.Client\Install-WinGetPackage `
        -Id "Docker.DockerDesktop"
}
 
This checks consent before installing Docker Desktop via Winget.
 
.EXAMPLE
Confirm-InstallationConsent -ApplicationName "Pester" `
    -Source "PowerShell Gallery" -Publisher "Pester Team" `
    -Description "Required for unit testing in PowerShell modules."
 
Prompts with detailed information before installing the Pester module.
 
.NOTES
- Preference keys are formatted as "AllowInstall_<ApplicationName>" (spaces
  removed for simplicity).
- Global consent key is "AllowInstall_GenXdevGlobal" which applies to all
  third-party installations.
- Preferences are stored in JSON format at:
  $Env:LOCALAPPDATA\GenXdev.PowerShell\SoftwareConsent.json
- If denied, no installation occurs, and the function returns $false.
- For legal soundness: The prompt explicitly requires user consent,
  disclaims liability, and explains risks (e.g., third-party software may
  have its own terms, potential security implications).
- Integrate this into your Ensure* functions by replacing automatic
  installations with a check like: if (-not (Confirm-InstallationConsent
  ...)) { throw "Installation consent denied." }
- For non-Winget installs (e.g., apt-get in EnsureYtdlp, dotnet in
  EnsureNuGetAssembly), still use this function for consistency.
- This function does not perform the installation itself—it's purely for
  consent checking.
#>

function Confirm-InstallationConsent {

    [CmdletBinding()]
    [OutputType([System.Boolean])]

    param(
        [Parameter(Mandatory = $true, Position = 0,
            HelpMessage = "The name of the application or software being installed.")]
        [ValidateNotNullOrEmpty()]
        [string] $ApplicationName,

        [Parameter(Mandatory = $true, Position = 1,
            HelpMessage = "The source of the installation (e.g., Winget, PowerShell Gallery).")]
        [ValidateNotNullOrEmpty()]
        [string] $Source,

        [Parameter(Mandatory = $false,
            HelpMessage = "Optional description of the software and its purpose.")]
        [ValidateNotNullOrEmpty()]
        [string] $Description = "This software is required for certain features in the GenXdev modules.",

        ###############################################################################
        [Parameter(Mandatory = $false,
            HelpMessage = "Optional publisher or vendor of the software.")]
        [ValidateNotNullOrEmpty()]
        [string] $Publisher = "Third-party vendor",

        ###############################################################################
        [Parameter(Mandatory = $false,
            HelpMessage = "Force a prompt even if preference is set.")]
        [switch] $ForceConsent,

        ###############################################################################
        [Parameter(Mandatory = $false,
            HelpMessage = "Automatically consent to third-party software installation and set persistent flag.")]
        [switch] $ConsentToThirdPartySoftwareInstallation
    )

    begin {
        # Normalize ApplicationName for preference key (remove spaces, make it safe)
        $safeAppName = $ApplicationName -replace '\s+', ''
        $preferenceKey = "AllowInstall_${safeAppName}"

        # Setup JSON file path for storing consent preferences
        $consentDir = Microsoft.PowerShell.Management\Join-Path $Env:LOCALAPPDATA "GenXdev.PowerShell"
        $consentFile = Microsoft.PowerShell.Management\Join-Path $consentDir "SoftwareConsent.json"

        Microsoft.PowerShell.Utility\Write-Verbose "Checking consent for installing '${ApplicationName}' from '${Source}'."

        # Helper function to read consent data
        function GetConsentData {
            try {
                if (Microsoft.PowerShell.Management\Test-Path $consentFile) {
                    $data = Microsoft.PowerShell.Management\Get-Content $consentFile -Raw -ErrorAction Stop |
                        Microsoft.PowerShell.Utility\ConvertFrom-Json -AsHashtable -ErrorAction Stop
                    if ($data) { return $data }
                }
            } catch {
                Microsoft.PowerShell.Utility\Write-Verbose "Could not read consent file: ${_}"
            }
            return @{}
        }

        # Helper function to save consent data
        function SaveConsentData {
            param([hashtable]$Data, [string]$Key, [string]$Value)
            try {
                if (-not (Microsoft.PowerShell.Management\Test-Path $consentDir)) {
                    Microsoft.PowerShell.Management\New-Item -Path $consentDir -ItemType Directory -Force |
                        Microsoft.PowerShell.Core\Out-Null
                }
                $Data[$Key] = $Value
                $Data | Microsoft.PowerShell.Utility\ConvertTo-Json -Depth 10 |
                    Microsoft.PowerShell.Management\Set-Content $consentFile -Encoding UTF8 -ErrorAction Stop
                return $true
            } catch {
                Microsoft.PowerShell.Utility\Write-Warning "Could not save consent preference: ${_}"
                return $false
            }
        }
    }

    process {
        # Check for global GenXdev consent first
        $globalConsentKey = "AllowInstall_GenXdevGlobal"
        $consentData = GetConsentData

        if ($consentData[$globalConsentKey] -eq 'true' -and -not $ForceConsent) {
            Microsoft.PowerShell.Utility\Write-Verbose "Global GenXdev consent allows installation of '${ApplicationName}'."
            return $true
        }

        # Check existing preference from JSON file
        $existingPref = $consentData[$preferenceKey]

        if ($existingPref -and -not $ForceConsent) {
            if ($existingPref -eq 'true') {
                Microsoft.PowerShell.Utility\Write-Verbose "Existing preference allows installation of '${ApplicationName}'."
                return $true
            } elseif ($existingPref -eq 'false') {
                Microsoft.PowerShell.Utility\Write-Warning "Installation of '${ApplicationName}' denied by user preference."
                return $false
            }
        }

        # Handle automatic consent parameter
        if ($ConsentToThirdPartySoftwareInstallation) {
            if (SaveConsentData -Data $consentData -Key $preferenceKey -Value 'true') {
                Microsoft.PowerShell.Utility\Write-Verbose "Automatic consent granted and persistent allowance set for '${ApplicationName}'."
            }
            return $true
        }

        # If no preference or ForceConsent, explain and prompt

        # Display colorized header
        Microsoft.PowerShell.Utility\Write-Host "`n" -NoNewline
        Microsoft.PowerShell.Utility\Write-Host "### " -ForegroundColor Cyan -NoNewline
        Microsoft.PowerShell.Utility\Write-Host "Installation Consent Required" -ForegroundColor Cyan -NoNewline
        Microsoft.PowerShell.Utility\Write-Host " ###`n" -ForegroundColor Cyan

        # Display software details with colors
        Microsoft.PowerShell.Utility\Write-Host "This PowerShell module (GenXdev) needs to install third-party software:" -ForegroundColor White
        Microsoft.PowerShell.Utility\Write-Host " Software: " -ForegroundColor White -NoNewline
        Microsoft.PowerShell.Utility\Write-Host "${ApplicationName}" -ForegroundColor Green
        Microsoft.PowerShell.Utility\Write-Host " Publisher: " -ForegroundColor White -NoNewline
        Microsoft.PowerShell.Utility\Write-Host "${Publisher}" -ForegroundColor Green
        Microsoft.PowerShell.Utility\Write-Host " Source: " -ForegroundColor White -NoNewline
        Microsoft.PowerShell.Utility\Write-Host "${Source}" -ForegroundColor Green
        Microsoft.PowerShell.Utility\Write-Host " Purpose: " -ForegroundColor White -NoNewline
        Microsoft.PowerShell.Utility\Write-Host "${Description}" -ForegroundColor Cyan

        # Display legal notes with warning colors
        Microsoft.PowerShell.Utility\Write-Host "`nImportant Legal and Safety Notes:" -ForegroundColor Yellow
        Microsoft.PowerShell.Utility\Write-Host "• This software is provided by a third party (" -ForegroundColor White -NoNewline
        Microsoft.PowerShell.Utility\Write-Host "${Publisher}" -ForegroundColor Yellow -NoNewline
        Microsoft.PowerShell.Utility\Write-Host "), not by GenXdev or its author." -ForegroundColor White
        Microsoft.PowerShell.Utility\Write-Host "• By consenting, you agree to download and install this software " -ForegroundColor White -NoNewline
        Microsoft.PowerShell.Utility\Write-Host "at your own risk" -ForegroundColor Yellow -NoNewline
        Microsoft.PowerShell.Utility\Write-Host "." -ForegroundColor White
        Microsoft.PowerShell.Utility\Write-Host "• GenXdev makes " -ForegroundColor White -NoNewline
        Microsoft.PowerShell.Utility\Write-Host "no warranties" -ForegroundColor Yellow -NoNewline
        Microsoft.PowerShell.Utility\Write-Host " about the software's safety, functionality, or compliance." -ForegroundColor White
        Microsoft.PowerShell.Utility\Write-Host "• Third-party software may have its own license terms, privacy policies, and potential risks." -ForegroundColor White
        Microsoft.PowerShell.Utility\Write-Host "• You are responsible for reviewing the software's terms and ensuring compliance." -ForegroundColor White
        Microsoft.PowerShell.Utility\Write-Host "• " -ForegroundColor White -NoNewline
        Microsoft.PowerShell.Utility\Write-Host "No liability:" -ForegroundColor Yellow -NoNewline
        Microsoft.PowerShell.Utility\Write-Host " The author of GenXdev assumes no responsibility for any issues." -ForegroundColor White
        Microsoft.PowerShell.Utility\Write-Host ""

        # Use $Host.UI.PromptForChoice for professional prompt
        $title = "Installation Consent for '${ApplicationName}'"
        $message = "Do you consent to install this third-party software?"

        $choices = @(
            Microsoft.PowerShell.Utility\New-Object System.Management.Automation.Host.ChoiceDescription "&Yes", "Yes (this time only)"
            Microsoft.PowerShell.Utility\New-Object System.Management.Automation.Host.ChoiceDescription "&Always", "Always allow (set persistent preference)"
            Microsoft.PowerShell.Utility\New-Object System.Management.Automation.Host.ChoiceDescription "&Global", "Always consent with GenXdev (allow all GenXdev third-party installations)"
            Microsoft.PowerShell.Utility\New-Object System.Management.Automation.Host.ChoiceDescription "&No", "No (this time only)"
            Microsoft.PowerShell.Utility\New-Object System.Management.Automation.Host.ChoiceDescription "&Help", "Show additional help information"
        )

        $defaultChoice = 3  # Default to "No"
        $choice = $Host.UI.PromptForChoice($title, $message, $choices, $defaultChoice)

        switch ($choice) {
            0 {  # Yes
                Microsoft.PowerShell.Utility\Write-Verbose "User consented for this installation only."
                return $true
            }
            1 {  # Always
                if (SaveConsentData -Data $consentData -Key $preferenceKey -Value 'true') {
                    Microsoft.PowerShell.Utility\Write-Host "Persistent allowance set for '${ApplicationName}'." -ForegroundColor Green
                    Microsoft.PowerShell.Utility\Write-Verbose "User set persistent allowance for '${ApplicationName}'."
                }
                return $true
            }
            2 {  # Global
                if (SaveConsentData -Data $consentData -Key $globalConsentKey -Value 'true') {
                    Microsoft.PowerShell.Utility\Write-Host "Global GenXdev consent enabled for all third-party software installations." -ForegroundColor Green
                    Microsoft.PowerShell.Utility\Write-Verbose "User enabled global GenXdev consent for all third-party installations."
                }
                return $true
            }
            3 {  # No
                Microsoft.PowerShell.Utility\Write-Host "Installation denied for this time." -ForegroundColor Yellow
                return $false
            }
            4 {  # Help
                Microsoft.PowerShell.Utility\Write-Host "`nAdditional Help:" -ForegroundColor Cyan
                Microsoft.PowerShell.Utility\Write-Host "• Choosing 'Always' options saves your preference to: " -ForegroundColor White -NoNewline
                Microsoft.PowerShell.Utility\Write-Host "${consentFile}" -ForegroundColor Green
                Microsoft.PowerShell.Utility\Write-Host "• 'Global' option enables automatic consent for ALL GenXdev third-party installations." -ForegroundColor White
                Microsoft.PowerShell.Utility\Write-Host "• You can manually edit the JSON file or delete it to reset all preferences." -ForegroundColor White
                Microsoft.PowerShell.Utility\Write-Host "• This prompt ensures your explicit consent to avoid any automatic installations." -ForegroundColor White
                Microsoft.PowerShell.Utility\Write-Host "• The preference key for this application is: " -ForegroundColor White -NoNewline
                Microsoft.PowerShell.Utility\Write-Host "${preferenceKey}" -ForegroundColor Green
                Microsoft.PowerShell.Utility\Write-Host "• The global preference key is: " -ForegroundColor White -NoNewline
                Microsoft.PowerShell.Utility\Write-Host "${globalConsentKey}" -ForegroundColor Green
                Microsoft.PowerShell.Utility\Write-Host ""

                # Re-prompt after help
                return GenXdev.FileSystem\Confirm-InstallationConsent @PSBoundParameters
            }
            default {
                Microsoft.PowerShell.Utility\Write-Warning "Invalid choice. Treating as denial."
                return $false
            }
        }
    }
}