Qlik-NPrinting-CLI.psm1

#region Invoke-Get-NPSession_ps1
    <#
        .SYNOPSIS
            Creates a Authenticated Session Token
         
        .DESCRIPTION
            Get-NPSession creates the NPEnv Script Variable used to Authenticate Requests
            $Script:NPEnv
     
        .NOTES
            Additional information about the function.
    #>

    function Get-NPSession
    {
        [CmdletBinding(DefaultParameterSetName = 'Default')]
        param
        (
            [Parameter(ParameterSetName = 'Default')]
            [ValidateSet('http', 'https')]
            [string]$Prefix = 'https',
            [Parameter(ParameterSetName = 'Default',
                       Position = 0)]
            [string]$Computer = $($env:computername),
            [Parameter(ParameterSetName = 'Default',
                       Position = 1)]
            [string]$Port = '4993',
            [switch]$Return,
            [Parameter(ParameterSetName = 'Default')]
            [Parameter(ParameterSetName = 'Creds')]
            [pscredential]$Credentials,
            [Parameter(ParameterSetName = 'Default')]
            [switch]$TrustAllCerts
        )
        
        $APIPath = "api"
        $APIVersion = "v1"
        
        if ($Computer -eq $($env:computername))
        {
            $NPService = Get-Service -Name 'QlikNPrintingWebEngine'
            if ($null -eq $NPService)
            {
                Write-Error -Message "Local Computer Name used and Service in not running locally"
                
                break
            }
        }
        
        if ($Computer -match ":")
        {
            If ($Computer.ToLower().StartsWith("http"))
            {
                $Prefix, $Computer = $Computer -split "://"
            }
            
            if ($Computer -match ":")
            {
                $Computer, $Port = $Computer -split ":"
            }
        }
        $CookieMonster = New-Object System.Net.CookieContainer #[System.Net.CookieContainer]::new()
        $Script:NPEnv = @{
            TrustAllCerts = $TrustAllCerts.IsPresent
            Prefix          = $Prefix
            Computer      = $Computer
            Port          = $Port
            API              = $APIPath
            APIVersion    = $APIVersion
            URLServerAPI  = ""
            WebRequestSession = New-Object Microsoft.PowerShell.Commands.WebRequestSession # [Microsoft.PowerShell.Commands.WebRequestSession]::new()
        }
        if ($null -ne $Credentials)
        {
            $NPEnv.Add("Credentials", $Credentials)
        }
        
        $NPEnv.URLServerAPI = "$($NPEnv.Prefix)://$($NPEnv.Computer):$($NPEnv.Port)/$($NPEnv.API)/$($NPEnv.APIVersion)"
        $WRS = $NPEnv.WebRequestSession
        $WRS.UserAgent = "Windows"
        $WRS.Cookies = $CookieMonster
        
        switch ($PsCmdlet.ParameterSetName)
        {
            'Default' {
                $WRS.UseDefaultCredentials = $true
                $APIAuthScheme = "ntlm"
                break
            }
            'Creds' {
                $WRS.Credentials = $Credentials
                $APIAuthScheme = "ntlm"
                break
            }
            'Certificate' {
                <#
                #Certificate Base Authentication does not currently work as the APIs cannot handle it.
                #Leaving this here in case this is added in the future.
                #Cert
                $NPrintCert = Get-ChildItem Cert:\LocalMachine\My\ | ?{ $_.Issuer -eq "CN=NPrinting-CA" }
                $UserCert = Get-ChildItem Cert:\CurrentUser\My -Eku "Client Authentication"
                $CertificateCollection = [System.Security.Cryptography.X509Certificates.X509Certificate2Collection]::new()
                $CertificateCollection.Add($NPrintCert)
                $CertificateCollection.Add($UserCert)
                $WebRequestSession.Certificates = $CertificateCollection
                #>
            
            }
        }
        $URLServerLogin = "$($NPEnv.URLServerAPI)/login/$($APIAuthScheme)"
        Write-Verbose -Message $URLServerLogin
        $AuthToken = Invoke-NPRequest -path "login/$($APIAuthScheme)" -method get
        $token = $NPenv.WebRequestSession.Cookies.GetCookies($NPenv.URLServerAPI) | Where-Object{ $_.name -eq "NPWEBCONSOLE_XSRF-TOKEN" }
        $Header = New-Object 'System.Collections.Generic.Dictionary[String,String]'
        $Header.Add("X-XSRF-TOKEN", $token.Value)
        $NPEnv.header = $Header
        if ($Return -eq $true)
        {
            $AuthToken
        }
    }
    
#endregion

#region Invoke-Invoke-NPRequest_ps1
    function Invoke-NPRequest
    {
        param
        (
            [Parameter(Mandatory = $true,
                       Position = 0)]
            [string]$Path,
            [ValidateSet('Get', 'Post', 'Patch', 'Delete', 'Put')]
            [string]$method = 'Get',
            $Data
        )
        if ($null -eq $NPEnv) { Get-NPSession }
        $URI = "$($NPEnv.URLServerAPI)/$($path)"
        
        $Script:SplatRest = @{
            URI           = $URI
            WebSession = $($NPEnv.WebRequestSession)
            Method       = $method
            ContentType = "application/json"
            Headers    = $NPenv.header
        }
        
        if ($PSVersionTable.PSVersion.Major -gt 5 -and $NPEnv.TrustAllCerts)
        {
            $Script:SplatRest.Add("SkipCertificateCheck", $NPEnv.TrustAllCerts)
        }
        else
        {
            if ($NPEnv.TrustAllCerts)
            {
                if (-not ("CTrustAllCerts" -as [type]))
                {
                    add-type -TypeDefinition @"
using System;
using System.Net;
using System.Net.Security;
using System.Security.Cryptography.X509Certificates;
 
public static class CTrustAllCerts {
    public static bool ReturnTrue(object sender,
        X509Certificate certificate,
        X509Chain chain,
        SslPolicyErrors sslPolicyErrors) { return true; }
 
    public static RemoteCertificateValidationCallback GetDelegate() {
        return new RemoteCertificateValidationCallback(CTrustAllCerts.ReturnTrue);
    }
}
"@

                    Write-Verbose -Message "Added Cert Ignore Type"
                }
                
                [System.Net.ServicePointManager]::ServerCertificateValidationCallback = [CTrustAllCerts]::GetDelegate()
                [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
                Write-Verbose -Message "Server Certificate Validation Bypass"
            }
        }
        
        if ("" -eq $NPEnv.WebRequestSession.Cookies.GetCookies($NPEnv.URLServerAPI) -and ($null -ne $NPEnv.Credentials))
        {
            $SplatRest.Add("Credential", $NPEnv.Credentials)
        }
        
        #Convert Data to Json and add to body of request
        if ($null -ne $data)
        {
            if ($Data.GetType().name -like "Array*")
            {
                $jsondata = Convertto-Json @($Data)
            }
            elseif ($Data.GetType().name -ne "string")
            {
                $jsondata = Convertto-Json $Data
            }
            else { $jsondata = $Data }
            
            #Catch All
            if (!(($jsondata.StartsWith('{') -and $jsondata.EndsWith('}')) -or ($jsondata.StartsWith('[') -and $jsondata.EndsWith(']'))))
            {
                $jsondata = $Data | Convertto-Json
            }
            
            $SplatRest.Add("Body", $jsondata)
        }
        
        if ($PSBoundParameters.Debug.IsPresent) { $Global:NPSplat = $SplatRest }
        
        try { $Result = Invoke-RestMethod @SplatRest  }
        catch [System.Net.WebException]{
            $EXCEPTION = $_.Exception
            $EXCEPTION
            Write-Warning -Message "From: $($Exception.Response.ResponseUri.AbsoluteUri) `nResponse: $($Exception.Response.StatusDescription)"
            break
        }
        
        if ($Null -ne $Result)
        {
            if ((($Result | Get-Member -MemberType Properties).count -eq 1 -and ($null -ne $Result.data)))
            {
                
                if ($null -ne $Result.data.items) { $Result.data.items }
                else { $Result.data }
                
            }
            else
            {
                $Result
            }
        }
        else { Write-Error -Message "no Results received" }
        
    }
#endregion

#region Invoke-GetNPFilter_ps1
    Function GetNPFilter ($Property, $Value, $Filter)
    {
        if ($null -ne $Property)
        {
            $Value = $Value.replace('*', '%')
            if ($Filter.StartsWith("?")) { $qt = "&" }
            else { $qt = "?" }
            $Filter = "$($Filter)$($qt)$($Property)=$($Value)"
        }
        $Filter
    }
#endregion

#region Invoke-AddNPProperty_ps1
    
    Function AddNPProperty ($Property, $NPObject, $path)
    {
        $PropertyValues = Get-Variable -Name "NP$($Property)" -ValueOnly -ErrorAction SilentlyContinue
        $NPObject | ForEach-Object{
            $Object = $_
            $ObjPath = "$($path)/$($Object.ID)/$Property"
            $NPObjProperties = $(Invoke-NPRequest -Path $ObjPath -method Get)
            $LookupProperties = $NPObjProperties | ForEach-Object{
                $ObjProperty = $_;
                $ObjectProperty = $PropertyValues | Where-Object{ $_.id -eq $ObjProperty }
                if ($Null -eq $ObjectProperty)
                {
                    Write-Verbose "$($ObjProperty) Missing from Internal $($Property) List: Updating"
                    & "Get-NP$($Property)" -update
                    $PropertyValues = Get-Variable -Name "NP$($Property)" -ValueOnly
                    $ObjectProperty = $PropertyValues | Where-Object{ $_.id -eq $ObjProperty }
                }
                $ObjectProperty
            }
            Add-Member -InputObject $Object -MemberType NoteProperty -Name $Property -Value $LookupProperties
        }
    }
    
#endregion

#region Invoke-Get-NPFilters_ps1
    function Get-NPFilters
    {
        param
        (
            [parameter(DontShow)]
            [switch]$Update
        )
        $Script:NPFilters = Invoke-NPRequest -Path "Filters" -method Get
        #The Update Switch is used to refresh the Internal List only
        #It is used when Called from Get-NPUsers and a Property is missing from the Internal List
        #The Internal List is used to speed up operations, by minimizing requests for data we have already received
        if ($Update.IsPresent -eq $false)
        {
            $Script:NPFilters
        }
    }
#endregion

#region Invoke-Get-NPGroups_ps1
    Function Get-NPGroups
    {
        param
        (
            [int32]$limit,
            [parameter(DontShow)]
            [switch]$Update
        )
        $filter = ""
        if ("limit" -in $PSBoundParameters.Keys){ $Filter = GetNPFilter -Filter $Filter -Property "limit" -Value $limit.ToString() } 
        
        $Script:NPGroups = Invoke-NPRequest -Path "groups$Filter" -method Get
        
        #The Update Switch is used to refresh the Internal List only
        #It is used when Called from Get-NPUsers and a Property is missing from the Internal List
        #The Internal List is used to speed up operations, by minimizing requests for data we have already received
        if ($Update.IsPresent -eq $false)
        {
            $Script:NPGroups
        }
    }
    
#endregion

#region Invoke-Get-NPRoles_ps1
    function Get-NPRoles
    {
        param
        (
            [parameter(DontShow)]
            [switch]$Update
        )
        
        $Script:NPRoles = Invoke-NPRequest -Path "roles" -method Get
        
        #The Update Switch is used to refresh the Internal List only
        #It is used when Called from Get-NPUsers and a Property is missing from the Internal List
        #The Internal List is used to speed up operations, by minimizing requests for data we have already received
        if ($Update.IsPresent -eq $false)
        {
            $Script:NPRoles
        }
    }
#endregion

#region Invoke-Get-NPTasks_ps1
    function Get-NPTasks
    {
        param
        (
            $ID,
            [string]$Name,
            [switch]$Executions,
            [parameter(DontShow)]
            [switch]$Update
        )
        $BasePath = "tasks"
        
        if ($Null -ne $ID)
        {
            $Path = "$BasePath/$($ID)"
        }
        else
        {
            $Path = "$BasePath"
        }
        
        $Path = "$($Path)$($Filter)"
        Write-Verbose $Path
        
        #The Update Switch is used to refresh the Internal List only
        #It is used when Called from Get-NPUsers and a Property is missing from the Internal List
        #The Internal List is used to speed up operations, by minimizing requests for data we have already received
        $Script:NPTasks = Invoke-NPRequest -Path $Path -method Get
        
        if ($Executions.IsPresent)
        {
            $NPTasks | ForEach-Object{
                $ExecutionPath = "tasks/$($_.id)/Executions"
                $NPTaskExecutions = Invoke-NPRequest -Path $ExecutionPath -method Get
                Add-Member -InputObject $_ -MemberType NoteProperty -Name "Executions" -Value $NPTaskExecutions
            }
        }
        
        if ($Update.IsPresent -eq $false)
        {
            $Script:NPTasks
        }
        
    }
#endregion

#region Invoke-NPUsers_ps1
    
    <#
    #Avaliable APIs
        Get-NPUsers
        get /users
        get /users/{id}
        get /users/{id}/filters
        get /users/{id}/groups
        get /users/{id}/roles
     
        Update-NPUser
        put /users/{id}/filters
        put /users/{id}/groups
        put /users/{id}
        put /users/{id}/roles
     
        New-NPUser
        post /users
     
        Remove-NPUser
        delete /users/{id}
     
    #>

    
    <#
    #Implemented APIS
    Get-NPUsers
    get /users
    get /users/{id}
    get /users/{id}/filters
    get /users/{id}/groups
    get /users/{id}/roles
    #>

    
    <#
        .SYNOPSIS
            Gets details of the Users in NPrinting
         
        .DESCRIPTION
            A detailed description of the Get-NPUsers function.
         
        .PARAMETER ID
            ID of object to get.
         
        .PARAMETER UserName
            Username of object to get.
         
        .PARAMETER Email
            Email address of object to get.
         
        .PARAMETER roles
            Include Role.
         
        .PARAMETER groups
            Inlcude Groups.
         
        .PARAMETER filters
            Include Filters.
         
        .PARAMETER limit
            number of objects to return (default is 50).
     
        .EXAMPLE
            Get-NPUsers -roles -groups -filters
            Get-NPUsers -UserName Marc -roles -groups -filters
         
        .NOTES
            Additional information about the function.
    #>

    function Get-NPUsers
    {
        [CmdletBinding()]
        param
        (
            [Parameter(ValueFromPipeline = $true)]
            [string]$ID,
            [string]$UserName,
            [string]$Email,
            [switch]$roles,
            [switch]$groups,
            [switch]$filters,
            [int32]$limit
        )
        $BasePath = "Users"
        $Filter = ""
        if ("limit" -in $PSBoundParameters.Keys) { $Filter = GetNPFilter -Filter $Filter -Property "limit" -Value $limit.ToString() }
        if ("UserName" -in $PSBoundParameters.Keys) { $Filter = GetNPFilter -Filter $Filter -Property "UserName" -Value $UserName }
        if ("EMail" -in $PSBoundParameters.Keys) { $Filter = GetNPFilter -Filter $Filter -Property "EMail" -Value $EMail }
        
        if ("ID" -in $PSBoundParameters.Keys) { $Path = "$BasePath/$($ID)" }
        else { $Path = "$BasePath" }
        
        $Path = "$($Path)$($Filter)"
        $NPUsers = Invoke-NPRequest -Path $Path -method Get
        
        if ($roles.IsPresent)
        {
            AddNPProperty -Property "Roles" -NPObject $NPUsers -path $BasePath
        }
        if ($groups.IsPresent)
        {
            AddNPProperty -Property "Groups" -NPObject $NPUsers -path $BasePath
        }
        if ($filters.IsPresent)
        {
            AddNPProperty -Property "Filters" -NPObject $NPUsers -path $BasePath
        }
        $NPUsers
    }
    
#endregion

#region Invoke-NPReports_ps1
    
    #This Function is a mess, it kinda works, but there will be filter scenarios where it is broken.
    #WIP
    function Get-NPReports{
        param
        (
            $ID,
            [string]$Name,
            [parameter(DontShow)]
            [switch]$Update
        )
        $BasePath = "Reports"
        
        if ($Null -ne $ID)
        {
            $Path = "$BasePath/$($ID)"
        }
        else
        {
            $Path = "$BasePath"
        }
        
        $Path = "$($Path)$($Filter)"
        Write-Verbose $Path
        
        #The Update Switch is used to refresh the Internal List only
        #It is used when Called from Get-NPUsers and a Property is missing from the Internal List
        #The Internal List is used to speed up operations, by minimizing requests for data we have already received
        $Script:NPReports = Invoke-NPRequest -Path $Path -method Get
        if ($Update.IsPresent -eq $false)
        {
            $Script:NPReports
        }
        
    }
    
#endregion

#region Invoke-NPApps_ps1
    
    #This Function is a mess, it kinda works, but there will be filter scenarios where it is broken.
    #WIP
    function Get-NPApps
    {
        param
        (
            $ID,
            [string]$Name,
            [parameter(DontShow)]
            [switch]$Update
        )
        $BasePath = "Apps"
        
        if ($Null -ne $ID)
        {
            $Path = "$BasePath/$($ID)"
        }
        else
        {
            $Path = "$BasePath"
        }
        
        $FilterApps = $Script:NPapps
        
        switch ($PSBoundParameters.Keys)
        {
            name{
                if ($Name -match '\*')
                {
                    $FilterApps = $FilterApps | Where-Object { $_.name -like $Name }
                }
                else
                {
                    $FilterApps = $FilterApps | Where-Object { $_.name -eq $Name }
                }
            }
            ID{ $Path = "$BasePath/$($ID)" }
            Update{ $Path = "$BasePath" }
            Default { $Path = "$BasePath" }
        }
        
        $Path = "$($Path)$($Filter)"
        Write-Verbose $Path
        
        #The Update Switch is used to refresh the Internal List only
        #It is used when Called from Get-NPUsers and a Property is missing from the Internal List
        #The Internal List is used to speed up operations, by minimizing requests for data we have already received
        
        if ($Null -eq $FilterApps)
        {
            $Script:NPapps = Invoke-NPRequest -Path $Path -method Get
            if ($Update.IsPresent -eq $false)
            {
                $Script:NPapps
            }
        }
        else
        {
            if ($Update.IsPresent -eq $false)
            {
                $FilterApps
            }
            
        }
        
    }
    
#endregion

#region Invoke-Add-NPFilterField_ps1
    function Add-NPFilterField
    {
        [CmdletBinding()]
        param
        (
            $NPFilter,
            [Parameter(Mandatory = $true)]
            [string]$ConnectionID,
            [Parameter(Mandatory = $true)]
            [string]$FieldName,
            [ValidateSet('text', 'number', 'expression')]
            [string]$FieldType = "text",
            [Parameter(Mandatory = $true)]
            [string[]]$FieldValue,
            [switch]$Overridevalues
        )
        if ($NPFilter.GetType().name -eq "String")
        {
        $NPFilter = Invoke-NPRequest -Path "filters/$($NPFilter)" -method Get
        }
        
        if ($NPFilter.fields.name -contains "$FieldName")
        {
            $Field = $NPFilter.fields | ?{ $_.name -eq "$FieldName" }
            [System.Collections.ArrayList]$Field.values = $Field.values
        }
        else
        {
            $Field = [PSCustomObject]@{
                connectionId   = $ConnectionID
                name           = $FieldName
                overrideValues = $Overridevalues.IsPresent
                values           = New-Object System.Collections.ArrayList
            }
            $NPFilter.fields += $Field
        }
        foreach ($Value in $FieldValue)
        {
            $Field.values.Add(
                $([PSCustomObject]@{
                        value = $Value
                        type  = $FieldType
                    })
            ) | out-null
        }
        
        $json = $NPFilter | ConvertTo-Json -Depth 10
        $NPut = Invoke-NPRequest -Path "filters/$($NPFilter.id)" -method Put -Data $json
        Invoke-NPRequest -Path "filters/$($NPFilter.id)" -method Get
    }
    
#endregion

#region Invoke-New-NPFilter_ps1
    function New-NPFilter
    {
        [CmdletBinding()]
        param
        (
            [Parameter(Mandatory = $true)]
            [String]$FilterName,
            [string]$FilterDescription,
            [Parameter(Mandatory = $true)]
            [string]$AppID,
            [bool]$enabled = $true
        )
        
        $NewNPFilter = @{
            appid        = $AppID
            enabled        = $enabled
            name        = $FilterName
            description = $FilterDescription
            fields        = New-Object System.Collections.ArrayList
        }
        $json = $NewNPFilter | ConvertTo-Json
        $Create = Invoke-NPRequest -Path "filters" -method Post -data $json
        $Filter = ""
        $Filter = GetNPFilter -Property appid -Value $AppID -Filter $Filter
        $Results = Invoke-NPRequest -Path "filters/$Filter" -method get
        $Result = $Results[0]
        return $Result
    }
    
#endregion

#region Invoke-Get-NPConnections_ps1
    function Get-NPConnections
    {
        param
        (
            [parameter(DontShow)]
            [switch]$Update
        )
        
        $Script:NPConnections = Invoke-NPRequest -Path "connections" -method Get
        if ($Update.IsPresent -eq $false)
        {
            $Script:NPConnections
        }
    }
#endregion

    <#
        ===========================================================================
         Created with: SAPIEN Technologies, Inc., PowerShell Studio 2018 v5.5.155
         Created on: 2018-12-03 10:21 AM
         Created by: Marc Collins
         Organization: Qlik - Consulting
         Filename: Qlik-NPrinting-CLI.psm1
        -------------------------------------------------------------------------
         Module Name: Qlik-NPrinting-CLI
        ===========================================================================
        Qlik NPrinting CLI - PowerShell Module to work with NPrinting
        The Function "Invoke-NPRequest" can be used to access all the NPrinting API's
    #>

    
    Export-ModuleMember -Function Get-*, Add-*, Invoke-*