Publish-Password.psm1
<## # Copyright 2021 David Hollings. All rights reserved. # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. #> . $PSScriptRoot/Private/HelperFunctions.ps1 Function Publish-Password { <# .SYNOPSIS Generate PwPush link for a given password. .DESCRIPTION Generate a PwPush link with a cusomisable lifetime and copies it to the clipboard. When multiple passwords are provided via pipeline input only the last is available on the clipboard. .PARAMETER Password The password to be sent (Mandatory). .PARAMETER Days How long to make the password available for. Default: 7. Maximum: 90. .PARAMETER Views How many times the password can be viewed before the link is removed. Default: 5. Maximum 90. .PARAMETER URI URI for PwPush API. Default: https://pwpush.com/p.json .PARAMETER DisableEarlyDeletion Prevent anyone accessing the link from deleting the password before the scheduled expiration. .EXAMPLE PS> Publish-Password -Password password123 -Days 7 -Views 5 -DisableEarlyDeletion Password : password123 Days : 7 Views : 5 Deletable : False Link : https://pwpush.com/p/f3vg6lry95gj6n9n This example creates a link for the password "password123", makes it available for 7 days or 5 views and prevents the user from deleting it early. .EXAMPLE PS> New-Password -Character -Length 32 | Publish-Password -Views 10 Password : \t&#P$c`u~5Xgb9c`!V]w0,r>9O~nsA3 Days : 7 Views : 10 Deletable : True Link : https://pwpush.com/p/dd59wor2ipsnm7az This example generates a random string and pipes it to PwPush with a view count of 10. .INPUTS System.String .OUTPUTS System.Management.Automation.PSCustomObject .LINK https://github.com/davidshomelab/PS-PwUtils #> [CmdletBinding()] Param( # Password to be sent. [Parameter(mandatory, ValueFromPipeline)] $Password, # Days to retain link. [ValidateRange(1, 90)] [int16] $Days = 7, # Views to retain link. [ValidateRange(1, 100)] [int16] $Views = 5, # URI for PwPush API. [ValidateScript ({ $UriStructure = [uri]$_ $UriStructure.Scheme -eq "HTTPS" })] [string] $URI = 'https://pwpush.com/p.json', # Is user allowed to delete link early. [Parameter()] [switch] $DisableEarlyDeletion, # Use 1-click retrieval step. Avoids URL scanners consuming a view. [Parameter()] [switch] $OneClickRetrieval, # Do not show password in output. If set, SecureString object will be returned instead of cleartext password. [Parameter()] [switch] $HidePassword, # Copy link to clipboard. If called as part of a pipeline, only the last value will be copied [Parameter()] [switch] $CopyToClipboard, # Number of retries if connection fails. Default = 5 [Parameter()] [int] $MaxRetries = 5 ) begin { # Response URI ends in /p instead of /p.json so we prepare this in advance to avoid recalculating for every pipeline object # We convert to a [uri] object to ensure URI is in simplest form (i.e. implicit port numbers are removed) $ResponseUri = (([uri]$URI).AbsoluteUri -replace '.json$', '/') } process { # Check if password was recieved from New-Password and extract password property if it was Write-Verbose "Checking if pipeline input is pscustomobject" Write-Verbose "Password type is $($Password.GetType().Name)" if ($Password.GetType().Name -eq "PSCustomObject") { $Password = $Password.Password Write-Verbose "Extracted password $Password from pipeline object" } # Ideally password should be received as a secure string. If a String is received, print a warning and convert to secure string if ($Password -isnot [SecureString]) { Write-Warning -Message "It is recommended to input the password as a secure string." $SecurePassword = ConvertTo-SecureString -String $Password -AsPlainText -Force } else { $SecurePassword = $Password } $attempts = 0 $Response = New-Object -TypeName pscustomobject do { $attempts ++ try { Write-Verbose "Attempt $attempts of $MaxRetries" $Response = Invoke-RestMethod -Method "Post" -Uri $URI -ContentType "application/json" -TimeoutSec 5 -Body ([PSCustomObject]@{ password = [PSCustomObject]@{ payload = SecureStringToPlainText -Password $SecurePassword expire_after_days = $Days expire_after_views = $Views deletable_by_viewer = -not $DisableEarlyDeletion } } | ConvertTo-Json ) } catch { Write-Verbose -Message "Connection failed." Start-Sleep -Seconds 0.5 } } while ($attempts -lt $MaxRetries -and !$Response.url_token) # If the request fails for any reason we won't get a response. In this case we shouldn't write out a summary if ($Response.url_token) { $Link = $ResponseUri + $Response.url_token if ($OneClickRetrieval) { $Link += "/r" } return [PSCustomObject]@{ Password = if ($HidePassword) { $SecurePassword } else { SecureStringToPlainText -Password $SecurePassword } Days = $Days Views = $Views Deletable = -not $DisableEarlyDeletion Link = $Link } } else { Write-Error -Message "Failed to connect to host after $MaxRetries attempts. Skipping" } } end { if ($CopyToClipboard) { Set-Clipboard -Value $Link } } } New-Alias PwPush -Value Publish-Password |