PSShareTru.psm1

function Get-STConfig {
    <#
    .SYNOPSIS
    Gets the current config for the module.
 
    .DESCRIPTION
    Gets the current config for the module. This is used to set the default values for the module.
 
    .EXAMPLE
    Get-STConfig
 
    #>

    [CmdletBinding()]
    param()
    $script:config
}
function Get-STDirectory {
    <#
    .SYNOPSIS
    Get directories. Generally broken/useless at this point since it can't list directory contents
 
    .DESCRIPTION
    Can do some basic filtering, but nothing very good. Best to ignore this cmdlet until there is better support for this endpoint.
 
    .PARAMETER Path
    The path to the directory or directory ID to retrieve
 
    .PARAMETER Raw
    Return the raw endpoint results rather than extracting the useful data.
 
    #>

    [CmdletBinding()]
    param(
        [string]$Path,
        [Switch]$Raw
    )
    Write-Warning "This is just broken (error 924661345)......"
    if (-not $script:config) {
        Set-STConfig
    }
    $param = $script:config.Irm_splat
    $page_mixin = "page=1,$($script:config.PageSize)"

    if ($Path -as [int]) {
        Invoke-RestMethod @param "$($script:config.BaseUri)/api/folders/$Path/"
    }
    elseif ($Path) {
        Invoke-RestMethod @param "$($script:config.BaseUri)/api/folders/?filter[]=name,begins,$($Path -replace ' ', '%20')&$page_mixin"
    }
    else {
        Invoke-RestMethod @param "$($script:config.BaseUri)/api/folders/?$page_mixin"
    }
}
function Get-STIpRestriction {
    <#
    .SYNOPSIS
    Retrieve the IP address restrictions in place. Can be restricted to specific user(s).
 
    .DESCRIPTION
    Lists all allowed IP address restrictions that are in place for any number of users. Can be any number of users and will return which rules apply to which users.
 
    .PARAMETER UserId
    The UserId [int[]] to be fetched. Defaults to all users if not specified. Is InputObject for pipelining.
 
    .PARAMETER Raw
    Return the raw endpoint results rather than extracting the useful data.
 
    .PARAMETER Page
    Indicate which page should be returned (default is 1)
 
    .PARAMETER PageSize
    Indicate how many items should be on each page.
 
    .EXAMPLE
    Get-STIpRestriction
 
    .EXAMPLE
    Get-STUser user@foo.com | Get-STIpRestriction | ft
 
    .EXAMPLE
    Get-STUser | ogv -PassThru | Get-STIpRestriction | sort username,start_ip | ft
 
    .NOTES
    General notes
    #>

    [CmdletBinding()]
    param(
        [Alias("id")]
        [Parameter(
            ValueFromPipelineByPropertyName = $true,
            ValueFromPipeline = $true
        )]
        [int[]]$UserId,
        [int]$Page = 1,
        [int]$PageSize,
        [switch]$Raw
    )
    begin {
        # Allows lazy loading of the config by waiting until the first time it is needed.
        if (-not $script:config) {
            Set-STConfig
        }
        $param = $script:config.Irm_splat
        $qs = $null
    }

    process {
        $queryString = [System.Web.HttpUtility]::ParseQueryString([string]::Empty)
        if (-not $PageSize) { $PageSize = $script:config.PageSize }
        $queryString.Add("page", "$Page,$PageSize")
        if ($UserId) {
            Write-Verbose "Selecting specific user with id $UserId"
            $queryString.Add("filter[]", "user-id,=,$UserId")
        }
        else {
            Write-Verbose "Selecting all users"
        }
        if ($queryString.ToString()) { $qs = "?" + $queryString.ToString() }
        $res = Invoke-RestMethod @param "$($script:config.BaseUri)/api/network_rules/$qs"
        if ($Raw) { $res }
        else {
            foreach ($item in $res.data) {
                $item.attributes | Select-Object @{n = 'username'; e = { $item.relationships.user.meta.label | Select-Object -First 1 } }, *
            }
        }
    }
}
function Get-STLog {
    <#
    .SYNOPSIS
    Retrieve the file logs from the server. Can be filtered by IP address, user, command, arguments, or filename. Generally used like tail to watch the logs live.
 
    .DESCRIPTION
    Lists all file logs that are in place. Can be any number of users and will return which entries apply to which users. Filtering can be done by IP range, IP address, file, user, or other arguments.
 
    .PARAMETER InputObject
    Filter critera. Can be IP address, IP address range, Get-STUser object, username, file path, or other "sftp arguments" that may be present
 
    .PARAMETER DisableColorize
    By default, a new log entry is compared to the immediate prior log entry. If the prior one was more than 5 seconds before the current one, the current one will have its timestamp colorized to help indicate the start of the scrollup. You can disable this functionality via -DisableColorize.
 
    .PARAMETER DisableWait
    By default, the cmdlet will continue to retrieve events in realtime until a key is pressed. In order to retrieve the latest logs and immediately return, use -DisableWait. It usually is paired with -Raw
 
    .PARAMETER Raw
    Return the raw endpoint results rather than extracting the useful data.
 
    .PARAMETER Page
    Indicate which page should be returned (default is 1)
 
    .PARAMETER PageSize
    Indicate how many items should be on each page.
 
    .PARAMETER Sort
    Indicate how to sort items on the server side.
 
    .EXAMPLE
    Get-STLog | ft
 
    .EXAMPLE
    Get-STLog bob | ft
 
    .EXAMPLE
    Get-STLog 12.34.56.221 | ogv
 
    A really good way to review logs is to use ogv and to sort by time descending. This creates a nice scroll that doesn't require you to move.
 
    .EXAMPLE
    Get-STLog /one
 
    .EXAMPLE
    Get-STLog /one/two/sales.txt
 
    .EXAMPLE
    Get-STLog sales.txt
 
    .EXAMPLE
    Get-STUser bob | Get-STLog | ft
 
    View log realtime log entries for the user(s) matching bob.
 
    .EXAMPLE
    Get-STUser | ogv -passthru | Get-STIpRestriction | Get-STLog
 
    Interactively select user(s) and then open a live log showing traffic for those allowed IP addresses.
    #>

    [CmdletBinding()]
    param(
        [Parameter(
            ValueFromPipelineByPropertyName = $true,
            ValueFromPipeline = $true
        )]
        $InputObject,
        [switch]$DisableColorize,
        [switch]$DisableWait,
        [switch]$Raw,
        $Page = 1,
        $PageSize = 100,
        $Sort = "-id"
    )
    begin {
        # Ensure the global configuration is initialized to avoid redundant checks later.
        if (-not $script:config) {
            Set-STConfig
        }
        $param = $script:config.Irm_splat
        $qs = $null
        $queryString = [System.Web.HttpUtility]::ParseQueryString([string]::Empty)
        if (-not $PageSize) { $PageSize = $script:config.PageSize }
        $queryString.Add("page", "$Page,$PageSize")
        if ($Sort) { $queryString.Add("sort", $Sort) }
        $qs = "?" + $queryString.ToString()

        $psrl = Get-PSReadLineOption
        [int]$id = $null
        $date = Get-Date
        $filter = @()
    }

    process {
        $InputObject | ForEach-Object { $filter += $_ }
    }
    end {
        # Get all the IP addresses that will be used for filtering
        # and split them into a separate array for the IP address filter
        # We want to convert single IP addresses to the IP address range format for simplicity
        $ipaddress_regex = '^(?:\d{1,3}\.){3}\d{1,3}$'
        $ipFilters, $otherFilters = $filter.foreach{
            if ($_ -match $ipaddress_regex) {
                [pscustomobject]@{
                    start_ip = $_
                    end_ip   = $_
                }
            }
            else {
                $_
            }
        }.where({
                $_.start_ip -match $ipaddress_regex -and
                $_.end_ip -match $ipaddress_regex
            }, "split")

        # Allow pipelined users from Get-STUser to get filtered with too
        $otherFilters = $otherFilters.foreach{
            if ($username = $_.attributes.username) { $username }
            else { $_ }
        }

        # use a nice regex match for the other filters
        $otherFilters = $otherFilters -join "|"
        "IP filters in place: $($ipFilters | ConvertTo-Json -Compress)" | Write-Verbose
        "Other filters in place: $otherFilters" | Write-Verbose

        do {
            $res = Invoke-RestMethod @param "$($script:config.BaseUri)/api/file_log/$qs"
            # Get 1 less than the first item to avoid large scroll while also allowing the first item to instantly get returned
            if (-not $id -and $res.data) { $id = [int]$res.data[0].id - 1 }
            if ($filter) {
                $res.data = $res.data.where{
                    $ip = $_.attributes.ip_address -as [version];
                    ($ipFilters | Where-Object {
                        $ip -ge $_.start_ip -and $ip -le $_.end_ip
                    }) -or
                    $(
                        if ($otherFilters) {
                            $_.attributes.user -match $otherFilters -or
                            $_.attributes.arguments -match $otherFilters -or
                            $_.attributes.filename -match $otherFilters
                        }
                    )
                }
            }
            if ($Raw) { $res }
            elseif ($new = $res.data | Where-Object id -GT $id | Sort-Object id) {
                $out = $new | Select-Object -expand attributes | Select-Object @{n = 'date'; e = {
                        [System.TimeZoneInfo]::ConvertTime(
                            [datetime]$_.date_time.date,
    ([System.TimeZoneInfo]::GetSystemTimeZones() | Where-Object id -EQ "Eastern Standard Time"),
    ([System.TimeZoneInfo]::GetSystemTimeZones() | Where-Object id -EQ ([System.TimeZone]::CurrentTimeZone.StandardName))
                        )
                    }
                }, ip_address, user, command, arguments, filename, bytes, status_code, status_message
                if (-not $DisableColorize) {
                    if ($date.AddSeconds(5) -lt $out[0].date) {
                        $date = $out[0].date
                        $out[0].date = $psrl.CommentColor + $out[0].date.Tostring() + $psrl.DefaultTokenColor
                    }
                    else {
                        $date = $out[0].date
                    }
                }
                $out
                $id = $new[-1].id
            }

            if ([console]::KeyAvailable) {
                $null = while ([console]::KeyAvailable) { [console]::ReadKey($true) }
                break
            }
        } while (-not $DisableWait -and -not (Start-Sleep 1))
    }
}
function Get-STUser {
    <#
    .SYNOPSIS
    Retrieves one or more users.
 
    .DESCRIPTION
    Use -Fields to reduce the returned properties per user. Use -ID to retrieve a specific user. -*Operator, -Sort, and -Fields parameters have an argument completer registered to aid. However, they can be ignored if the API supports alternate data.
 
    .PARAMETER Username
    The Username parameters
 
    .PARAMETER UsernameOperator
    The UsernameOperator parameters
 
    .PARAMETER Email
    The Email parameters
 
    .PARAMETER EmailOperator
    The EmailOperator parameters
 
    .PARAMETER Name
    The Name parameters
 
    .PARAMETER NameOperator
    The NameOperator parameters
 
    .PARAMETER Status
    The Status parameters
 
    .PARAMETER StatusOperator
    The StatusOperator parameters
 
    .PARAMETER Comments
    The Comments parameters
 
    .PARAMETER CommentsOperator
    The CommentsOperator parameters
 
    .PARAMETER OtpMethod
    The OtpMethod parameters
 
    .PARAMETER OtpMethodOperator
    The OtpMethodOperator parameters
 
    .PARAMETER IdentityProvider
    The IdentityProvider parameters
 
    .PARAMETER IdentityProviderOperator
    The IdentityProviderOperator parameters
 
    .PARAMETER DateToSuspend
    The DateToSuspend parameters
 
    .PARAMETER DateToSuspendOperator
    The DateToSuspendOperator parameters
 
    .PARAMETER HomeFolder
    The HomeFolder parameters
 
    .PARAMETER HomeFolderOperator
    The HomeFolderOperator parameters
 
    .PARAMETER LastLogin
    The LastLogin parameters
 
    .PARAMETER LastLoginOperator
    The LastLoginOperator parameters
 
    .PARAMETER StorageUsed
    The StorageUsed parameters
 
    .PARAMETER StorageUsedOperator
    The StorageUsedOperator parameters
 
    .PARAMETER LastPwdChange
    The LastPwdChange parameters
 
    .PARAMETER LastPwdChangeOperator
    The LastPwdChangeOperator parameters
 
    .PARAMETER ID
    The user ID to retrieve.
 
    .PARAMETER Fields
    The array of fields to be returned. Argument completer in place with default return values. Does not seem to work well when specifying a specific user by ID number.
 
    .PARAMETER Sort
    Parameter description
 
    .PARAMETER Page
    Indicate which page should be returned (default is 1)
 
    .PARAMETER PageSize
    Indicate how many items should be on each page.
 
    .PARAMETER Raw
    Return the raw endpoint results rather than extracting the useful data.
 
    .EXAMPLE
    Get-STUser | ogv
 
    .EXAMPLE
    Get-STUser John
 
    .EXAMPLE
    Get-STUser -Email @gallerd.com
 
    .EXAMPLE
    Get-STUser -Username @gallerd.com -UsernameOperator ends
 
    .EXAMPLE
    Get-STUser -ID 10001
 
    .EXAMPLE
    Get-STUser -Raw | select -expand meta
 
    Shows total number of users in the system
 
    .EXAMPLE
    Get-STUser -PageSize 5
 
    Gets the first 5 users from the system
 
    .EXAMPLE
    Get-STUser -PageSize 5 -Page 2
 
    Gets the second set of 5 users from the system
    #>

    [CmdletBinding()]
    param(
        [string]$Username,
        [string]$UsernameOperator = "contains",
        [string]$Email,
        [string]$EmailOperator = "contains",
        [string]$Name,
        [string]$NameOperator = "contains",
        [string]$Status,
        [string]$StatusOperator = "contains",
        [string]$Comments,
        [string]$CommentsOperator = "contains",
        [string]$OtpMethod,
        [string]$OtpMethodOperator = "contains",
        [string]$IdentityProvider,
        [string]$IdentityProviderOperator = "contains",
        [string]$DateToSuspend,
        [string]$DateToSuspendOperator = "contains",
        [string]$HomeFolder,
        [string]$HomeFolderOperator = "contains",
        [string]$LastLogin,
        [string]$LastLoginOperator = "contains",
        [string]$StorageUsed,
        [string]$StorageUsedOperator = "contains",
        [string]$LastPwdChange,
        [string]$LastPwdChangeOperator = "contains",
        [int]$ID,
        [string[]]$Fields,
        [string]$Sort,
        [int]$Page = 1,
        [int]$PageSize,
        [Switch]$Raw
    )

    if (-not $script:config) {
        Set-STConfig
    }
    $param = $script:config.Irm_splat
    $qs = $null
    $queryString = [System.Web.HttpUtility]::ParseQueryString([string]::Empty)
    if ($Fields) {
        $queryString.Add("fields[user]", $($Fields -join ","))
    }
    $filter = @(
        "username"
        "email"
        "name"
        "status"
        "comments"
        "otp_method"
        "identity_provider"
        "date_to_suspend"
        "home_folder"
        "last_login"
        "storage_used"
        "last_password_change"
    )
    $filter | ForEach-Object {
        $coded = $_ -replace "_" -replace "password", "pwd"
        if ($PSBoundParameters[$coded]) {
            $queryString.Add("filter[]", "$_,$(Get-Variable "${coded}Operator" -ValueOnly),$($PSBoundParameters[$coded])")
        }
    }

    if ($ID) {
        Write-Verbose "Selecting single user"
        if ($queryString.ToString()) {
            if ($Fields) {
                Write-Warning "This command doesn't work for unknown reasons when it does from the interactive API doc (Error 500961129). Trying it anyway..."
            }
            $qs = "?" + $queryString.ToString()
        }
        $res = Invoke-RestMethod @param "$($script:config.BaseUri)/api/users/$ID/$qs)"
    }
    else {
        Write-Verbose "Selecting from all users"
        if (-not $PageSize) { $PageSize = $script:config.PageSize }
        $queryString.Add("page", "$Page,$PageSize")
        if ($queryString.ToString()) { $qs = "?" + $queryString.ToString() }
        $res = Invoke-RestMethod @param "$($script:config.BaseUri)/api/users/$qs"
    }
    $queryString.ToString() | Write-Verbose
    if ($Raw) { $res }
    else { $res.Data }
}


Register-ArgumentCompleter -CommandName Get-STUser -ParameterName Fields -ScriptBlock {
    param(
        $commandName,
        $parameterName,
        $wordToComplete,
        $commandAst,
        $fakeBoundParameters
    )

    $fields = @(
        "username"
        "email"
        "name"
        "comments"
        "status"
        "date_suspended"
        "date_to_suspend"
        "password_state"
        "password_expiration_date"
        "password_expiration_inherit"
        "password_expiration_days"
        "last_password_change"
        "otp_method"
        "otp_sms_number"
        "otp_sms_carrier"
        "otp_sms_gateway"
        "otp_totp_secret"
        "ssh_key_authentication_allowed"
        "storage_used"
        "identity_provider_uid"
        "last_login"
        "home_folder"
    )

    $match = $fields -match "^$wordToComplete"

    $match | ForEach-Object {
        New-Object -Type System.Management.Automation.CompletionResult -ArgumentList @(
            $_          # completionText
            $_          # listItemText
            'ParameterValue' # resultType
            $_          # toolTip
        )
    }
}



$completer = {
    param(
        $commandName,
        $parameterName,
        $wordToComplete,
        $commandAst,
        $fakeBoundParameters
    )

    $fields = @(
        "="
        "<>"
        ">"
        "<="
        ">="
        "begins"
        "ends"
        "contains"
    )

    $match = $fields -match "^$wordToComplete"

    $match | ForEach-Object {
        New-Object -Type System.Management.Automation.CompletionResult -ArgumentList @(
            $_          # completionText
            $_          # listItemText
            'ParameterValue' # resultType
            $_          # toolTip
        )
    }
}
Register-ArgumentCompleter -CommandName Get-STUser -ParameterName UsernameOperator -ScriptBlock $completer

Register-ArgumentCompleter -CommandName Get-STUser -ParameterName Sort -ScriptBlock {
    param(
        $commandName,
        $parameterName,
        $wordToComplete,
        $commandAst,
        $fakeBoundParameters
    )

    $fields = @(
        "id"
        "-id"
        "username"
        "-username"
        "name"
        "-name"
        "status"
        "-status"
        "date_to_suspend"
        "-date_to_suspend"
        "home_folder"
        "-home_folder"
        "last_login"
        "-last_login"
        "storage_used"
        "-storage_used"
        "last_password_change"
        "-last_password_change"
    )

    $match = $fields -match "^$wordToComplete"

    $match | ForEach-Object {
        New-Object -Type System.Management.Automation.CompletionResult -ArgumentList @(
            $_          # completionText
            $_          # listItemText
            'ParameterValue' # resultType
            $_          # toolTip
        )
    }
}
function Set-STConfig {
    <#
    .SYNOPSIS
    Sets the configuration parameters for this module to use.
 
    .DESCRIPTION
    This command does not reach out to the API server. It only sets the values needed in order for other cmdlets in the module to reach out to the API host. If the BaseURI and ApiKey are stored in ENV variables ($ENV:ST_BASE_URI and $ENV:ST_APIKEY), this cmdlet can be run without any parameters to fully configure the module (or skipped entirely and invoked automatically on first cmdlet invocation).
 
    .PARAMETER BaseUri
    The base URI of the API host. e.g. "https://mycompany.sharetru.com". If the BaseURI and ApiKey are stored in ENV variables ($ENV:ST_BASE_URI and $ENV:ST_APIKEY), this cmdlet can be run without any parameters to fully configure the module (or skipped entirely and invoked automatically on first cmdlet invocation).
 
    .PARAMETER ApiKey
    The API key needed to interact with the HTTPS API. If the BaseURI and ApiKey are stored in ENV variables ($ENV:ST_BASE_URI and $ENV:ST_APIKEY), this cmdlet can be run without any parameters to fully configure the module (or skipped entirely and invoked automatically on first cmdlet invocation).
 
    .PARAMETER PageSize
    Sets the default PageSize for cmdlets in this module to use.
 
    .PARAMETER Passthru
    Return the config object for further review or manipulation.
 
    .EXAMPLE
    Set-STConfig
 
    .NOTES
    General notes
    #>

    [CmdletBinding(SupportsShouldProcess)]
    param(
        $BaseUri,
        $ApiKey,
        $PageSize = 20,
        [switch]$Passthru
    )
    if ($PSCmdlet.ShouldProcess("PSShareTru", "Nothing outside of module scope")) {}
    $script:config = @{
        BaseUri  = $ENV:ST_BASE_URI
        ApiKey   = $ENV:ST_APIKEY
        PageSize = $PageSize
    }
    if ($BaseUri) { $script:config.BaseUri = $BaseUri }
    if ($ApiKey) { $script:config.ApiKey = $ApiKey }
    $script:config.Irm_splat = @{
        Headers     = @{
            "X-API-KEY" = $script:config.ApiKey
            Accept      = "application/vnd.api+json"
        }
        ContentType = "application/json"
    }
    if ($Passthru) { $script:config }
}
# Hack needed for old powershell.
if ($PSVersionTable.PSVersion.Major -le 5) {
    Add-Type -AssemblyName System.Web
}