|
| 1 | +############################################################################## |
| 2 | +# Part of PowerShell module : GenXdev.Helpers |
| 3 | +# Cmdlet filename : Confirm-InstallationConsent.ps1 |
| 4 | +# Author : René Vaessen / GenXdev (with AI assistance) |
| 5 | +# Version : 1.0.0 |
| 6 | +################################################################################ |
| 7 | +# MIT License |
| 8 | +# |
| 9 | +# Copyright 2021-2025 GenXdev |
| 10 | +# |
| 11 | +# Permission is hereby granted, free of charge, to any person obtaining a copy |
| 12 | +# of this software and associated documentation files (the "Software"), to deal |
| 13 | +# in the Software without restriction, including without limitation the rights |
| 14 | +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
| 15 | +# copies of the Software, and to permit persons to whom the Software is |
| 16 | +# furnished to do so, subject to the following conditions: |
| 17 | +# |
| 18 | +# The above copyright notice and this permission notice shall be included in all |
| 19 | +# copies or substantial portions of the Software. |
| 20 | +# |
| 21 | +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| 22 | +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| 23 | +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
| 24 | +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
| 25 | +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
| 26 | +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
| 27 | +# SOFTWARE. |
| 28 | +################################################################################ |
| 29 | +<# |
| 30 | +.SYNOPSIS |
| 31 | +Confirms user consent for installing third-party software, using preferences for persistent choices. |
| 32 | +
|
| 33 | +.DESCRIPTION |
| 34 | +This function acts as a custom ShouldProcess mechanism specifically for software installations. |
| 35 | +It checks a user preference (via Get-GenXdevPreference) to determine if automatic installation |
| 36 | +is allowed for the specified application. If no preference is set, it prompts the user with |
| 37 | +a clear explanation of what will be installed, the source, potential risks, and options to |
| 38 | +allow or deny the installation (with options for one-time or persistent choices). |
| 39 | +
|
| 40 | +This ensures explicit user consent before proceeding with any installation, helping to |
| 41 | +mitigate potential legal risks by requiring affirmative action from the user. The prompt |
| 42 | +clearly states that the module author (GenXdev) is not responsible for third-party software, |
| 43 | +and that the user is consenting to the installation at their own risk. |
| 44 | +
|
| 45 | +Preferences are stored using Set-GenXdevPreference, allowing users to set "always allow" |
| 46 | +or "always deny" for specific applications, making it convenient while remaining legally sound. |
| 47 | +
|
| 48 | +If consent is denied (or preference is set to deny), the function returns $false and does not |
| 49 | +proceed with installation. If allowed, it returns $true. |
| 50 | +
|
| 51 | +.PARAMETER ApplicationName |
| 52 | +The name of the application or software being installed (e.g., "Docker Desktop", "Sysinternals Suite"). |
| 53 | +This is used to create a unique preference key like "AllowInstall_DockerDesktop". |
| 54 | +
|
| 55 | +.PARAMETER Source |
| 56 | +The source of the installation (e.g., "Winget", "PowerShell Gallery", "apt-get in WSL", "dotnet CLI"). |
| 57 | +This is included in the explanation prompt for transparency. |
| 58 | +
|
| 59 | +.PARAMETER Description |
| 60 | +Optional detailed description of what the software does and why it's being installed. |
| 61 | +If not provided, a generic message is used. |
| 62 | +
|
| 63 | +.PARAMETER Publisher |
| 64 | +Optional publisher or vendor of the software (e.g., "Microsoft", "Docker Inc."). |
| 65 | +Included in the prompt for clarity. |
| 66 | +
|
| 67 | +.PARAMETER ForcePrompt |
| 68 | +Forces a prompt even if a preference is already set (useful for re-confirmation). |
| 69 | +
|
| 70 | +.EXAMPLE |
| 71 | +if (Confirm-InstallationConsent -ApplicationName "Docker Desktop" -Source "Winget") { |
| 72 | + # Proceed with installation |
| 73 | + Microsoft.WinGet.Client\Install-WinGetPackage -Id "Docker.DockerDesktop" |
| 74 | +} |
| 75 | +
|
| 76 | +This checks consent before installing Docker Desktop via Winget. |
| 77 | +
|
| 78 | +.EXAMPLE |
| 79 | +Confirm-InstallationConsent -ApplicationName "Pester" -Source "PowerShell Gallery" -Publisher "Pester Team" -Description "Required for unit testing in PowerShell modules." |
| 80 | +
|
| 81 | +Prompts with detailed information before installing the Pester module. |
| 82 | +
|
| 83 | +.NOTES |
| 84 | +- Preference keys are formatted as "AllowInstall_<ApplicationName>" (spaces removed for simplicity). |
| 85 | +- Preferences are stored in JSON format at: $Env:LOCALAPPDATA\GenXdev.PowerShell\SoftwareConsent.json |
| 86 | +- If denied, no installation occurs, and the function returns $false. |
| 87 | +- 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). |
| 88 | +- Integrate this into your Ensure* functions by replacing automatic installations with a check like: if (-not (Confirm-InstallationConsent ...)) { throw "Installation consent denied." } |
| 89 | +- For non-Winget installs (e.g., apt-get in EnsureYtdlp, dotnet in EnsureNuGetAssembly), still use this function for consistency. |
| 90 | +- This function does not perform the installation itself—it's purely for consent checking. |
| 91 | +#> |
| 92 | +function Confirm-InstallationConsent { |
| 93 | + |
| 94 | + [CmdletBinding()] |
| 95 | + [OutputType([System.Boolean])] |
| 96 | + |
| 97 | + param( |
| 98 | + [Parameter(Mandatory = $true, Position = 0, |
| 99 | + HelpMessage = "The name of the application or software being installed.")] |
| 100 | + [ValidateNotNullOrEmpty()] |
| 101 | + [string] $ApplicationName, |
| 102 | + |
| 103 | + [Parameter(Mandatory = $true, Position = 1, |
| 104 | + HelpMessage = "The source of the installation (e.g., Winget, PowerShell Gallery).")] |
| 105 | + [ValidateNotNullOrEmpty()] |
| 106 | + [string] $Source, |
| 107 | + |
| 108 | + [Parameter(Mandatory = $false, |
| 109 | + HelpMessage = "Optional description of the software and its purpose.")] |
| 110 | + [string] $Description = "This software is required for certain features in the GenXdev modules.", |
| 111 | + |
| 112 | + [Parameter(Mandatory = $false, |
| 113 | + HelpMessage = "Optional publisher or vendor of the software.")] |
| 114 | + [string] $Publisher = "Third-party vendor", |
| 115 | + |
| 116 | + [Parameter(Mandatory = $false, |
| 117 | + HelpMessage = "Force a prompt even if preference is set.")] |
| 118 | + [switch] $ForcePrompt |
| 119 | + ) |
| 120 | + |
| 121 | + begin { |
| 122 | + # Normalize ApplicationName for preference key (remove spaces, make it safe) |
| 123 | + $safeAppName = $ApplicationName -replace '\s+', '' |
| 124 | + $preferenceKey = "AllowInstall_$safeAppName" |
| 125 | + |
| 126 | + # Setup JSON file path for storing consent preferences |
| 127 | + $consentDir = Microsoft.PowerShell.Management\Join-Path $Env:LOCALAPPDATA "GenXdev.PowerShell" |
| 128 | + $consentFile = Microsoft.PowerShell.Management\Join-Path $consentDir "SoftwareConsent.json" |
| 129 | + |
| 130 | + Microsoft.PowerShell.Utility\Write-Verbose "Checking consent for installing '$ApplicationName' from '$Source'." |
| 131 | + } |
| 132 | + |
| 133 | + process { |
| 134 | + # Check existing preference from JSON file |
| 135 | + $existingPref = $null |
| 136 | + try { |
| 137 | + if (Microsoft.PowerShell.Management\Test-Path $consentFile) { |
| 138 | + $consentData = Microsoft.PowerShell.Management\Get-Content $consentFile -Raw | Microsoft.PowerShell.Utility\ConvertFrom-Json -ErrorAction SilentlyContinue |
| 139 | + if ($consentData -and $consentData.PSObject.Properties.Name -contains $preferenceKey) { |
| 140 | + $existingPref = $consentData.$preferenceKey |
| 141 | + } |
| 142 | + } |
| 143 | + } catch { |
| 144 | + Microsoft.PowerShell.Utility\Write-Verbose "Could not read consent file: $_" |
| 145 | + } |
| 146 | + |
| 147 | + if ($existingPref -and -not $ForcePrompt) { |
| 148 | + if ($existingPref -eq 'true') { |
| 149 | + Microsoft.PowerShell.Utility\Write-Verbose "Existing preference allows installation of '$ApplicationName'." |
| 150 | + return $true |
| 151 | + } elseif ($existingPref -eq 'false') { |
| 152 | + Microsoft.PowerShell.Utility\Write-Warning "Installation of '$ApplicationName' denied by user preference." |
| 153 | + return $false |
| 154 | + } |
| 155 | + } |
| 156 | + |
| 157 | + # If no preference or ForcePrompt, explain and prompt |
| 158 | + $promptMessage = @" |
| 159 | +`n### Installation Consent Required for '$ApplicationName' ### |
| 160 | +
|
| 161 | +This PowerShell module (GenXdev) needs to install third-party software: '$ApplicationName'. |
| 162 | +- Publisher: $Publisher |
| 163 | +- Source: $Source |
| 164 | +- Purpose: $Description |
| 165 | +
|
| 166 | +Important Legal and Safety Notes: |
| 167 | +- This software is provided by a third party ($Publisher), not by GenXdev or its author (René Vaessen). |
| 168 | +- By consenting, you agree to download and install this software at your own risk. |
| 169 | +- GenXdev makes no warranties about the software's safety, functionality, or compliance with laws. |
| 170 | +- Third-party software may have its own license terms, privacy policies, and potential risks (e.g., security vulnerabilities, data collection). |
| 171 | +- You are responsible for reviewing the software's terms and ensuring it meets your needs and legal requirements. |
| 172 | +- No liability: The author of GenXdev (René Vaessen) assumes no responsibility for any issues arising from this installation. |
| 173 | +
|
| 174 | +Do you consent to install '$ApplicationName'? |
| 175 | +- [Y] Yes (this time only) |
| 176 | +- [A] Always allow (set persistent preference) |
| 177 | +- [N] No (this time only) |
| 178 | +- [D] Always deny (set persistent preference) |
| 179 | +- [?] Help (more info) |
| 180 | +
|
| 181 | +Your choice: |
| 182 | +"@ |
| 183 | + |
| 184 | + # Display the prompt |
| 185 | + Microsoft.PowerShell.Utility\Write-Host $promptMessage -ForegroundColor Yellow |
| 186 | + |
| 187 | + # Get user input |
| 188 | + $choice = Microsoft.PowerShell.Utility\Read-Host |
| 189 | + $choice = $choice.ToUpper() |
| 190 | + |
| 191 | + switch ($choice) { |
| 192 | + 'Y' { |
| 193 | + Microsoft.PowerShell.Utility\Write-Verbose "User consented for this installation only." |
| 194 | + return $true |
| 195 | + } |
| 196 | + 'A' { |
| 197 | + try { |
| 198 | + # Ensure directory exists |
| 199 | + if (-not (Microsoft.PowerShell.Management\Test-Path $consentDir)) { |
| 200 | + Microsoft.PowerShell.Management\New-Item -Path $consentDir -ItemType Directory -Force | Microsoft.PowerShell.Core\Out-Null |
| 201 | + } |
| 202 | + |
| 203 | + # Load existing data or create new |
| 204 | + $consentData = @{} |
| 205 | + if (Microsoft.PowerShell.Management\Test-Path $consentFile) { |
| 206 | + $consentData = Microsoft.PowerShell.Management\Get-Content $consentFile -Raw | Microsoft.PowerShell.Utility\ConvertFrom-Json -AsHashtable -ErrorAction SilentlyContinue |
| 207 | + if (-not $consentData) { $consentData = @{} } |
| 208 | + } |
| 209 | + |
| 210 | + # Update and save |
| 211 | + $consentData[$preferenceKey] = 'true' |
| 212 | + $consentData | Microsoft.PowerShell.Utility\ConvertTo-Json -Depth 10 | Microsoft.PowerShell.Management\Set-Content $consentFile -Encoding UTF8 |
| 213 | + Microsoft.PowerShell.Utility\Write-Verbose "User set persistent allowance for '$ApplicationName'." |
| 214 | + } catch { |
| 215 | + Microsoft.PowerShell.Utility\Write-Warning "Could not save consent preference: $_" |
| 216 | + } |
| 217 | + return $true |
| 218 | + } |
| 219 | + 'N' { |
| 220 | + Microsoft.PowerShell.Utility\Write-Warning "User denied installation for this time." |
| 221 | + return $false |
| 222 | + } |
| 223 | + 'D' { |
| 224 | + try { |
| 225 | + # Ensure directory exists |
| 226 | + if (-not (Microsoft.PowerShell.Management\Test-Path $consentDir)) { |
| 227 | + Microsoft.PowerShell.Management\New-Item -Path $consentDir -ItemType Directory -Force | Microsoft.PowerShell.Core\Out-Null |
| 228 | + } |
| 229 | + |
| 230 | + # Load existing data or create new |
| 231 | + $consentData = @{} |
| 232 | + if (Microsoft.PowerShell.Management\Test-Path $consentFile) { |
| 233 | + $consentData = Microsoft.PowerShell.Management\Get-Content $consentFile -Raw | Microsoft.PowerShell.Utility\ConvertFrom-Json -AsHashtable -ErrorAction SilentlyContinue |
| 234 | + if (-not $consentData) { $consentData = @{} } |
| 235 | + } |
| 236 | + |
| 237 | + # Update and save |
| 238 | + $consentData[$preferenceKey] = 'false' |
| 239 | + $consentData | Microsoft.PowerShell.Utility\ConvertTo-Json -Depth 10 | Microsoft.PowerShell.Management\Set-Content $consentFile -Encoding UTF8 |
| 240 | + Microsoft.PowerShell.Utility\Write-Warning "User set persistent denial for '$ApplicationName'." |
| 241 | + } catch { |
| 242 | + Microsoft.PowerShell.Utility\Write-Warning "Could not save consent preference: $_" |
| 243 | + } |
| 244 | + return $false |
| 245 | + } |
| 246 | + '?' { |
| 247 | + Microsoft.PowerShell.Utility\Write-Host @" |
| 248 | +Additional Help: |
| 249 | +- Choosing 'Always' options saves your preference to: $consentFile |
| 250 | +- You can manually edit the JSON file or delete it to reset all preferences. |
| 251 | +- This prompt ensures your explicit consent to avoid any automatic installations. |
| 252 | +- The preference key for this application is: $preferenceKey |
| 253 | +"@ -ForegroundColor Cyan |
| 254 | + |
| 255 | + # Re-prompt after help |
| 256 | + return GenXdev.FileSystem\Confirm-InstallationConsent @PSBoundParameters |
| 257 | + } |
| 258 | + default { |
| 259 | + Microsoft.PowerShell.Utility\Write-Warning "Invalid choice. Treating as denial." |
| 260 | + return $false |
| 261 | + } |
| 262 | + } |
| 263 | + } |
| 264 | +} |
0 commit comments