OneTimeSecret.psm1

<#
   ____ _______ _ _____ _
  / __ \ |__ __(_) / ____| | |
 | | | |_ __ ___| | _ _ __ ___ ___| (___ ___ ___ _ __ ___| |_
 | | | | '_ \ / _ \ | | | '_ ` _ \ / _ \\___ \ / _ \/ __| '__/ _ \ __|
 | |__| | | | | __/ | | | | | | | | __/____) | __/ (__| | | __/ |_
  \____/|_| |_|\___|_| |_|_| |_| |_|\___|_____/ \___|\___|_| \___|\__|
 
#>


$ExecutionContext.SessionState.Module.OnRemove = {

}
<#
    - Function: ConvertFrom-UnixTime
#>


function ConvertFrom-UnixTime {
<#
    .SYNOPSIS
    Convert UNIX Time to a readable format
 
    .DESCRIPTION
    Convert UNIX Time to a readable format
 
    .PARAMETER UnixTime
    Unix Time
 
    .INPUTS
    System.String
 
    .OUTPUTS
    System.TimeZone
 
    .EXAMPLE
    ConvertFromUnixTime -UnixTime 777777
 
#>

[CmdletBinding()][OutputType('System.DateTime')]

    Param (

        [Parameter(Mandatory=$true, Position=0)]
        [ValidateNotNullOrEmpty()]
        [String]$UnixTime
    )

    # --- Changed to Get-Date to support Core. This function will die soon.
    #[System.TimeZone]::CurrentTimeZone.ToLocalTime(([datetime]'1/1/1970').` AddSeconds($UnixTime))
    (Get-Date -Date 1/1/1970).AddSeconds($UnixTime)

}

<#
    - Function: Invoke-OTSRestMethod
#>


function Invoke-OTSRestMethod {
    <#
    .SYNOPSIS
    A module specific wrapper for Invoke-ResetMethod
 
    .DESCRIPTION
    A module specific wrapper for Invoke-ResetMethod
 
    .PARAMETER Method
    METHOD: GET, POST, PUT, DELETE
 
    .PARAMETER URI
    Service URI
 
    .PARAMETER QueryStringParameters
    A hashtable of query string parameters
 
    .PARAMETER Body
    Payload for the request, if applicable
 
    .PARAMETER Headers
    Optional Headers to send. This will override the default set provided
 
    .INPUTS
    System.String
 
    .OUTPUTS
    System.Management.Automation.PSObject
 
    .EXAMPLE
    InvokeOTSRestMethod -Method POST -URI /v1/generate?passphrase=1234"
 
    .EXAMPLE
    $QueryStringParameters = @{
        passphrase = "1234"
    }
    InvokeOTSRestMethod -Method POST -URI /v1/generate -QueryStringParameters $QueryStringParameters
 
#>

    [CmdletBinding()][OutputType('System.Management.Automation.PSObject')]

    Param (

        [Parameter(Mandatory = $true)]
        [ValidateSet("GET", "POST", "PUT", "DELETE")]
        [String]$Method,

        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [String]$URI,

        [Parameter(Mandatory = $false)]
        [Hashtable]$QueryStringParameters,

        [Parameter(Mandatory = $false)]
        [ValidateNotNullOrEmpty()]
        [String]$Body,

        [Parameter(Mandatory = $false)]
        [ValidateNotNullOrEmpty()]
        [System.Collections.IDictionary]$Headers

    )

    try {

        # --- Grab the SessionState variable Test for connection variable
        if (!$Script:OTSConnectionInformation) {

            throw "Could not find OTSConnectionInformation. Please run Set-OTSConnectionInformation first"
        }

        # --- Build Uri
        $BaseUrl = $Script:OTSConnectionInformation.BaseUrl
        if (!$BaseUrl) {
            $BaseUrl = "https://onetimesecret.com/"
        }

        $UriBuilder = [System.UriBuilder]::new($BaseUrl)
        $UriBuilder.Path = "api/$URI"

        # --- Add query parameters
        $QueryString = [System.Web.HttpUtility]::ParseQueryString([string]::Empty)
        if ($PSBoundParameters.ContainsKey("QueryStringParameters")) {
            foreach ($Parameter in $QueryStringParameters.GetEnumerator()) {
                $QueryString.Add($Parameter.Key, $Parameter.Value)
            }
        }

        $UriBuilder.Query = $QueryString.ToString()

        # --- Get full Uri
        $FullURI = $UriBuilder.Uri
                
        # --- Build headers
        $Headers = @{

            "Authorization" = "Basic $($Script:OTSConnectionInformation.Authorization)"
        }

        Write-Verbose -Message "Invoking request with headers $($Headers)"
        if ($PSBoundParameters.ContainsKey("Body")) {

            $Response = Invoke-RestMethod -Method $Method -Headers $Headers -Uri $FullURI -Body $Body -Verbose:$VerbosePrefernce

        }
        else {

            $Response = Invoke-RestMethod -Method $Method -Headers $Headers -Uri $FullURI -Verbose:$VerbosePreference

        }

        Write-Output $Response

    }
    catch [Exception] {

        throw $_

    }
    finally {

        if (($PSVersionTable.PSEdition -eq "Desktop") -and $Script:OTSConnectionInformation) {
            $ServicePoint = "$($UriBuilder.Scheme)://$($UriBuilder.Host)"
            $ServicePoint = [System.Net.ServicePointManager]::FindServicePoint($ServicePoint)
            $ServicePoint.CloseConnectionGroup("") | Out-Null

        }

    }

}

<#
    - Function: Test-WebAddress
#>


function Test-WebAddress {
    <#
    .SYNOPSIS
    Validate a web address
 
    .DESCRIPTION
    Validate a web address
 
    .PARAMETER Address
    The address to validate
 
    .EXAMPLE
    Test-WebAddress -Address https://google.com
    #>

    [CmdletBinding()]
    Param(
        [Parameter(Mandatory = $true)]
        [String]$Address
    )

    try {
            $URI = $Address -as [System.URI]
            $null -ne $URI.AbsoluteURI -and (($URI.Scheme -eq "http") -or ($URI.Scheme -eq "https"))
    }
    catch {
        Write-Error -Message "$_" -ErrorAction Stop
    }
}

<#
    - Function: Get-OTSAuthorizationToken
#>


function Get-OTSAuthorizationToken {
<#
    .SYNOPSIS
    Retrieve the authorization token variable set by Set-OTSAuthorizationToken
 
    .DESCRIPTION
    Retrieve the authorization token variable set by Set-OTSAuthorizationToken
 
    .INPUTS
    None
 
    .OUTPUTS
    System.Management.Automation.PSObject
 
    .Example
    Get-OTSAuthorizationToken
 
#>

[CmdletBinding()][OutputType('System.Management.Automation.PSObject')]

    Param ()

    try {

        # --- Grab the SessionState variable Test for connection variable
        if (!$Script:OTSConnectionInformation) {

            throw "Could not find OTSConnectionInformation. Please run Set-OTSAuthorizationToken first"
        }

        Write-Output $Script:OTSConnectionInformation
    }
    catch {

        throw $_
    }
}

<#
    - Function: Get-OTSRecentMetadata
#>


function Get-OTSRecentMetadata {
<#
    .SYNOPSIS
    Return recent secret metadata
 
    .DESCRIPTION
    Return recent secret metadata
 
    .OUTPUTS
    System.Management.Automation.PSObject
 
    .Example
    Get-OTSRecentMetadata
 
#>

[CmdletBinding()][OutputType('System.Management.Automation.PSObject')]

    Param ()

    # --- Set URI with mandatory query parameters
    $URI =  "v1/private/recent"

    try {

        $Response = Invoke-OTSRestMethod -Method GET -URI $URI -Verbose:$VerbosePreference

        foreach ($Metadata in $Response) {

            [PSCustomObject]@{

                Custid = $Metadata.custid
                MetadataKey = $Metadata.metadata_key
                SecretKey = $Metadata.secret_key
                Ttl = $Metadata.ttl
                MetadataTtl = $Metadata.metadata_ttl
                SecretTtl = $Metadata.secret_ttl
                State = $Metadata.state
                Updated = (ConvertFrom-UnixTime -UnixTime $Metadata.updated).ToString()
                Created = (ConvertFrom-UnixTime -UnixTime $Metadata.created).ToString()
                Recipient = $Metadata.recipient
                PassphraseRequired = $Metadata.passphrase_required
            }
        }
    }
    catch {

        throw $_
    }
}

<#
    - Function: Get-OTSSecret
#>


function Get-OTSSecret {
<#
    .SYNOPSIS
    Retrieve a secret
 
    .DESCRIPTION
    Retrieve a secret
 
    .PARAMETER SecretKey
    The unique key for this secret
 
    .PARAMETER Passphrase
    The passphrase is required only if the secret was create with one
 
    .INPUTS
    System.String
 
    .OUTPUTS
    System.Management.Automation.PSObject
 
    .Example
    Get-OTSSecret -SecretKey qqevnp70b4uoiax4knzhwlhros6ne7x -Passphrase 1234
 
#>

[CmdletBinding()][OutputType('System.Management.Automation.PSObject')]

    Param (

        [Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true, Position=0)]
        [ValidateNotNullOrEmpty()]
        [String]$SecretKey,

        [Parameter(Mandatory=$false, Position=1)]
        [ValidateNotNullOrEmpty()]
        [String]$Passphrase
    )

    # --- Set URI with mandatory query parameters
    $URI = "v1/secret/$($SecretKey)"

    try {

        $QueryStringParameters = @{}
        if ($PSBoundParameters.ContainsKey("Passphrase")) {
            Write-Verbose -Message "Adding Passphrase Query Parameter"
            $QueryStringParameters.Add("passphrase", $Passphrase)
        }

        $Response = Invoke-OTSRestMethod -Method POST -URI $URI -QueryStringParameters $QueryStringParameters -Verbose:$VerbosePreference

        [PSCustomObject]@{

            SecretKey = $Response.secret_key
            Value = $Response.value
        }
    }
    catch {

        throw $_
    }
}

<#
    - Function: Get-OTSSecretMetadata
#>


function Get-OTSSecretMetadata {
<#
    .SYNOPSIS
    Retrieve metadata of a secret
 
    .DESCRIPTION
    Every secret also has associated metadata. The metadata is intended to be used by the creator of the secret (i.e. not the recipient) and should generally be kept private. You can safely use the metadata key to retrieve basic information about the secret itself (e.g. if or when it was viewed) since the metadata key is different from the secret key.
 
    .PARAMETER MetadataKey
    The unique key for the metadata
 
    .INPUTS
    System.String
 
    .OUTPUTS
    System.Management.Automation.PSObject
 
    .Example
 
    Get-OTSSecretMetaData -MetaDataKey allrfe8gf7edstynihtvrblgfuhbbuz
 
#>

[CmdletBinding()][OutputType('System.Management.Automation.PSObject')]

    Param (

        [Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true, Position=0)]
        [ValidateNotNullOrEmpty()]
        [String]$MetadataKey
    )

    # --- Set URI with mandatory query parameters
    $URI = "v1/private/$($MetadataKey)"

    try {

        $Response = Invoke-OTSRestMethod -Method POST -URI $URI -Verbose:$VerbosePreference

        [PSCustomObject]@{

            CustId = $Response.custid
            MetadataKey = $Response.metadata_key
            SecretKey = $Response.secret_key
            Ttl = $Response.ttl
            MetadataTtl = $Response.metadata_ttl
            SecretTtl = $Response.secret_ttl
            State = $Response.state
            Updated = (ConvertFrom-UnixTime -UnixTime $Response.updated).ToString()
            Created = (ConvertFrom-UnixTime -UnixTime $Response.created).ToString()
            Recipient = $Response.recipient
            PassphraseRequired = $Response.passphrase_required
        }
    }
    catch {

        throw $_
    }
}

<#
    - Function: Get-OTSSystemStatus
#>


function Get-OTSSystemStatus {
<#
    .SYNOPSIS
    Get the current status of OneTimeSecret.com
 
    .DESCRIPTION
    Get the current status of OneTimeSecret.com
 
    .INPUTS
    None
 
    .OUTPUTS
    System.Management.Automation.PSObject
 
    .Example
    Get-OTSSystemStatus
 
#>

[CmdletBinding()][OutputType('System.Management.Automation.PSObject')]

    Param ()

    # --- Set URI with mandatory query parameters
    $URI = "v1/status"

    try {

        $Response = Invoke-OTSRestMethod -Method GET -URI $URI -Verbose:$VerbosePreference

        [PSCustomObject]@{

            Status = $Response.status
        }
    }
    catch {

        throw $_
    }
}

<#
    - Function: New-OTSSecret
#>


function New-OTSSecret {
    <#
    .SYNOPSIS
    Generate a short, unique secret. This is useful for temporary passwords, one-time pads, salts, etc.
 
    .DESCRIPTION
    Generate a short, unique secret. This is useful for temporary passwords, one-time pads, salts, etc.
 
    .PARAMETER Passphrase
    A string that the recipient must know to view the secret. This value is also used to encrypt the secret and is bcrypted before being stored so we only have this value in transit.
 
    .PARAMETER Ttl
    The maximum amount of time, in seconds, that the secret should survive (i.e. time-to-live). Once this time expires, the secret will be deleted and not recoverable.
 
    .PARAMETER Recipient
    An email address. We will send a friendly email containing the secret link (NOT the secret itself).
 
    .INPUTS
    System.String
 
    .OUTPUTS
    System.Management.Automation.PSObject
 
    .EXAMPLE
    New-OTSSecret -Passphrase 1234 -Recipient user@mail.com
 
    .EXAMPLE
    New-OTSSecret -Passphrase 1234 -Ttl 90000 -MetadataTtl 90000 -SecretTtil 90000 -Recipient user@mail.com
 
#>

    [CmdletBinding(SupportsShouldProcess, ConfirmImpact = "Low")][OutputType('System.Management.Automation.PSObject')]

    Param (

        [Parameter(Mandatory = $false, Position = 0)]
        [ValidateNotNullOrEmpty()]
        [String]$Passphrase,

        [Parameter(Mandatory = $false, Position = 1)]
        [ValidateNotNullOrEmpty()]
        [String]$Ttl,

        [Parameter(Mandatory = $false, Position = 4)]
        [ValidateNotNullOrEmpty()]
        [String]$Recipient
    )

    # --- Set URI with mandatory query parameters
    $URI = "v1/generate"

    try {

        $QueryStringParameters = @{}
        if ($PSBoundParameters.ContainsKey("Passphrase")) {
            Write-Verbose -Message "Adding Passphrase Query Parameter"
            $QueryStringParameters.Add("passphrase", $Passphrase)
        }

        if ($PSBoundParameters.ContainsKey("Ttl")) {
            Write-Verbose -Message "Adding Ttl Query Parameter"
            $QueryStringParameters.Add("ttl", $Ttl)
        }

        if ($PSBoundParameters.ContainsKey("Recipient")) {

            Write-Verbose -Message "Adding Recipient Parameter"
            $QueryStringParameters.Add("recipient", $Recipient)
        }

        if ($PSCmdlet.ShouldProcess("onetimesecret.com")) {

            $Response = Invoke-OTSRestMethod -Method POST -URI $URI -QueryStringParameters $QueryStringParameters -Verbose:$VerbosePreference

            [PSCustomObject]@{

                CustId             = $Response.custid
                MetadataKey        = $Response.metadata_key
                SecretKey          = $Response.secret_key
                Ttl                = $Response.ttl
                MetadataTtl        = $Response.metadata_ttl
                SecretTtl          = $Response.secret_ttl
                State              = $Response.state
                Updated            = (ConvertFrom-UnixTime -UnixTime $Response.updated).ToString()
                Created            = (ConvertFrom-UnixTime -UnixTime $Response.created).ToString()
                Recipient          = $Response.recipient
                Value              = $Response.value
                PassphraseRequired = $Response.passphrase_required
            }
        }
    }
    catch {

        throw $_
    }
}

<#
    - Function: New-OTSSharedSecret
#>


function New-OTSSharedSecret {
    <#
    .SYNOPSIS
    Store and share a secret value
 
    .DESCRIPTION
    Store and share a secret value
 
    .PARAMETER Secret
    The secret value which is encrypted before being stored. There is a maximum length based on your plan that is enforced (1k-10k
 
    .PARAMETER Passphrase
    A string that the recipient must know to view the secret. This value is also used to encrypt the secret and is bcrypted before being stored so we only have this value in transit.
 
    .PARAMETER Ttl
    The maximum amount of time, in seconds, that the secret should survive (i.e. time-to-live). Once this time expires, the secret will be deleted and not recoverable.
 
    .PARAMETER Recipient
    An email address. We will send a friendly email containing the secret link (NOT the secret itself).
 
    .INPUTS
    System.String
 
    .OUTPUTS
    System.Management.Automation.PSObject
 
    .EXAMPLE
    New-OTSSharedSecret -Secret "Very Secret" -Passphrase 12334
 
    .EXAMPLE
    New-OTSSharedSecret -Secret "Very Secret" -Passphrase 1234 -Ttl 90000 -Recipient user@mail.com
 
 
#>

    [CmdletBinding(SupportsShouldProcess, ConfirmImpact = "Low")][OutputType('System.Management.Automation.PSObject')]

    Param (

        [Parameter(Mandatory = $true, Position = 0)]
        [ValidateNotNullOrEmpty()]
        [String]$Secret,

        [Parameter(Mandatory = $false, Position = 1)]
        [ValidateNotNullOrEmpty()]
        [String]$Passphrase,

        [Parameter(Mandatory = $false, Position = 2)]
        [ValidateNotNullOrEmpty()]
        [String]$Ttl,

        [Parameter(Mandatory = $false, Position = 3)]
        [ValidateNotNullOrEmpty()]
        [String]$Recipient
    )

    # --- Set URI with mandatory query parameters
    $URI = "v1/share"

    try {

        $QueryStringParameters = @{}
        Write-Verbose -Message "Adding Secret Query Parameter"
        $QueryStringParameters.Add("secret", $Secret)

        if ($PSBoundParameters.ContainsKey("Passphrase")) {
            Write-Verbose -Message "Adding Passphrase Query Parameter"
            $QueryStringParameters.Add("passphrase", $Passphrase)
        }

        if ($PSBoundParameters.ContainsKey("Ttl")) {
            Write-Verbose -Message "Adding Ttl Query Parameter"
            $QueryStringParameters.Add("ttl", $Ttl)
        }

        if ($PSBoundParameters.ContainsKey("Recipient")) {

            Write-Verbose -Message "Adding Recipient Parameter"
            $QueryStringParameters.Add("recipient", $Recipient)
        }

        if ($PSCmdlet.ShouldProcess("onetimesecret.com")) {

            $Response = Invoke-OTSRestMethod -Method POST -URI $URI -QueryStringParameters $QueryStringParameters -Verbose:$VerbosePreference

            [PSCustomObject]@{

                CustId             = $Response.custid
                MetadataKey        = $Response.metadata_key
                SecretKey          = $Response.secret_key
                Ttl                = $Response.ttl
                MetadataTtl        = $Response.metadata_ttl
                SecretTtl          = $Response.secret_ttl
                State              = $Response.state
                Updated            = (ConvertFrom-UnixTime -UnixTime $Response.updated).ToString()
                Created            = (ConvertFrom-UnixTime -UnixTime $Response.created).ToString()
                Recipient          = $Response.recipient
                PassphraseRequired = $Response.passphrase_required

            }
        }
    }
    catch {

        throw $_
    }
}

<#
    - Function: Remove-OTSSecret
#>


function Remove-OTSSecret {
<#
    .SYNOPSIS
    Burn a secren that has not been read yet.
 
    .DESCRIPTION
    Burn a secren that has not been read yet.
 
    Secrets with passphrases are currently not supported by this function
 
    .PARAMETER MetadataKey
    The unique key for the metadata
 
    .INPUTS
    System.String
 
    .OUTPUTS
    System.Management.Automation.PSObject
 
    .Example
    Remove-OTSSecret -MetaDataKey allrfe8gf7edstynihtvrblgfuhbbuz
 
#>

[CmdletBinding(SupportsShouldProcess,ConfirmImpact="High")][OutputType('System.Management.Automation.PSObject')]

    Param (

        [Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true, Position=0)]
        [ValidateNotNullOrEmpty()]
        [String]$MetadataKey
    )

    # --- Set URI with mandatory query parameters
    $URI = "v1/private/$($MetadataKey)/burn"

    try {

        if ($PSCmdlet.ShouldProcess("onetimesecret.com")){

            $Response = (Invoke-OTSRestMethod -Method POST -URI $URI -Verbose:$VerbosePreference).state

            [PSCustomObject]@{

                CustId = $Response.custid
                MetadataKey = $Response.metadata_key
                SecretKey = $Response.secret_key
                Ttl = $Response.ttl
                MetadataTtl = $Response.metadata_ttl
                SecretTtl = $Response.secret_ttl
                State = $Response.state
                Updated = (ConvertFrom-UnixTime -UnixTime $Response.updated).ToString()
                Created = (ConvertFrom-UnixTime -UnixTime $Response.created).ToString()
                Recipient = $Response.recipient
                PassphraseRequired = $Response.passphrase_required
            }
        }
    }
    catch {

        throw "$_ - This command does not support secrets with passphrases!"
    }
}

<#
    - Function: Set-OTSAuthorizationToken
#>


function Set-OTSAuthorizationToken {
<#
    .SYNOPSIS
    Create the Authorization information required to interact with the OneTimeSecret.com API
 
    .DESCRIPTION
    Create the Authorization information required to interact with the OneTimeSecret.com API
 
    .PARAMETER Username
    The Username of the account
 
    .PARAMETER APIKey
    The API key for the account
 
    .PARAMETER BaseUrl
    Use a custom instance of onetimesecret. Defaults to https://onetimesecret.com/
 
    .INPUTS
    System.String
 
    .OUTPUTS
    System.Management.Automation.PSObject
 
    .EXAMPLE
    Set-OTSAuthorizationToken -Username user@mail.com -APIKey 52302308erf2e799affd33cbc7c85896b4c6a6997
 
    .EXAMPLE
    Set-OTSAuthorizationToken -Username user@mail.com -APIKey 52302308erf2e799affd33cbc7c85896b4c6a6997 -BaseUrl https://mycustomhost.com/
 
#>

[CmdletBinding(SupportsShouldProcess,ConfirmImpact="Low")][OutputType('System.Management.Automation.PSObject')]

    Param (

        [Parameter(Mandatory=$true, Position=0)]
        [ValidateNotNullOrEmpty()]
        [String]$Username,

        [Parameter(Mandatory=$true, Position=1)]
        [ValidateNotNullOrEmpty()]
        [String]$APIKey,

        [Parameter(Mandatory=$false, Position=2)]
        [ValidateNotNullOrEmpty()]
        [String]$BaseUrl = "https://onetimesecret.com/"
    )

    if ($PSCmdlet.ShouldProcess("onetimesecret")){

        try {

            $EncodedAuth = [System.Text.Encoding]::UTF8.GetBytes("$($Username):$($APIKey)")
            $Script:OTSConnectionInformation = [PSCustomObject]@{
                Authorization = [System.Convert]::ToBase64String($EncodedAuth)
            }

            if ($PSBoundParameters.ContainsKey("BaseUrl")){
                if (!(Test-WebAddress -Address $BaseUrl)) {
                    Write-Error -Message "The url provided was not valid: $BaseUrl" -ErrorAction Stop
                }

                if (!$BaseUrl.EndsWith("/")) {
                    $BaseUrl = $BaseUrl + "/"
                }

                $Script:OTSConnectionInformation | Add-Member -MemberType NoteProperty -Name BaseUrl -Value $BaseUrl
            }

            Write-Output $Script:OTSConnectionInformation

        } catch {

            throw $_
        }
    }
}