Public/ConfideAdminSharingApi.ps1

function Add-ConfideShareCenterUserImportTask {
    <#
    .SYNOPSIS
    Import sharing users (Privileged User models) either from a CSV file or from an in‑memory list and call Import-SharingUser.
 
    .DESCRIPTION
    Wrapper that builds ApiModelObjectsModernApiPrivilegedUser objects and invokes the existing Import-SharingUser API.
    Supports two parameter sets:
      1. Csv - Provide -CsvPath (and optional -Delimiter) to read rows.
      2. List - Provide -ShareCoordinatorModels as an array of PSCustomObjects (already shaped like the model) OR raw hashtables.
 
    If a connection (AccessToken) is not present and -Url/-ClientId plus credential (ClientSecret or Certificate) are provided, it will call Connect-Confide automatically.
 
    .PARAMETER CsvPath
    Path to CSV file. Only column "Email address" is mandatory. Other optional permission columns:
        Manage and share objects in share center,
        Generate anonymous download link in share center,
        Generate anonymous upload link in share center,
        Generate anonymous download link in project,
        Generate anonymous upload link in project,
        Skip anti-virus scan for files greater than 2 GB,
        Send invitation email.
 
    .PARAMETER ShareCoordinatorModels
    An array of pre-constructed model objects or data rows convertible to the model (property names matching CSV column list).
 
    .PARAMETER Url
    Base API url (only needed if auto-connect is desired).
 
    .PARAMETER ClientId
    Azure AD / Identity client id for auto-connect.
 
    .PARAMETER ClientSecret
    Client secret (for auto-connect, parameter set ClientSecret).
 
    .PARAMETER Certificate
    X509 certificate (for auto-connect, parameter set ClientCertificate).
 
    .PARAMETER IdentityServiceUri
    Override identity service URI.
 
    .PARAMETER Delimiter
    CSV delimiter (default comma).
 
    .PARAMETER PassThru
    Return the array of model objects (always); when omitted still returns import task id (string) from API.
 
    .EXAMPLE
    Add-ConfideShareCenterUserImportTask -CsvPath .\users.csv -Url $url -ClientId $id -ClientSecret $secret
 
    .EXAMPLE
    $models = @(
      Initialize-ConfideApiModelObjectsModernApiPrivilegedUser -Email a@b.com -IsShareCoordinator $true
    )
    Add-ConfideShareCenterUserImportTask -ShareCoordinatorModels $models
    #>

    [CmdletBinding(DefaultParameterSetName = 'Csv', SupportsShouldProcess)]
    Param(
        # CSV parameter set
        [Parameter(Mandatory = $true, ParameterSetName = 'Csv')]
        [ValidateNotNullOrEmpty()]
        [string]$CsvPath,

        [Parameter(ParameterSetName = 'Csv')]
        [char]$Delimiter = ',',

        # List parameter set
        [Parameter(Mandatory = $true, ParameterSetName = 'List')]
        [ValidateNotNullOrEmpty()]
        [object[]]$ShareCoordinatorModels,

        # Connection (optional – only used if not already connected)
        [Parameter(Mandatory = $false)]
        [string]$Url,
        [Parameter(Mandatory = $false)]
        [string]$ClientId,
        [Parameter(Mandatory = $false, ParameterSetName = 'Csv')]
        [Parameter(Mandatory = $false, ParameterSetName = 'List')]
        [Alias('Cert')]
        [System.Security.Cryptography.X509Certificates.X509Certificate2]$Certificate,
        [Parameter(Mandatory = $false, ParameterSetName = 'Csv')]
        [Parameter(Mandatory = $false, ParameterSetName = 'List')]
        [string]$ClientSecret,
        [Parameter(Mandatory = $false)]
        [string]$IdentityServiceUri,
        [switch]$PassThru
    )
    Begin {
        $requiredEmailColumn = 'Email address'
        # Map CSV columns -> internal model property names
        $columnMap = [ordered]@{
            'Email address'                              = 'Email'
            'Manage and share objects in share center'   = 'IsShareCoordinator'
            'Generate anonymous download link in share center' = 'CanGenerateDownloadLinkInShareCenter'
            'Generate anonymous upload link in share center' = 'CanGenerateUploadLinkInShareCenter'
            'Generate anonymous download link in project'    = 'CanShareLink'
            'Generate anonymous upload link in project'      = 'CanGenerateUploadLink'
            'Skip anti-virus scan for files greater than 2 GB' = 'CanConfigureScan'
            'Send invitation email'                      = 'SendInvitationEmail'
        }
        $boolTargetProps = 'IsShareCoordinator', 'CanGenerateDownloadLinkInShareCenter', 'CanGenerateUploadLinkInShareCenter', 'CanShareLink', 'CanGenerateUploadLink', 'CanConfigureScan', 'SendInvitationEmail'
        
        function Convert-ToBool([string]$v) {
            if ($null -eq $v -or $v -eq '') { return $null }
            switch -Regex ($v.ToString()) {
                '^(1|true|yes|y)$' { return $true }
                '^(0|false|no|n)$' { return $false }
                default { return [bool]::Parse($v) }
            }
        }
    }
    Process {
    # Ensure valid access token (auto-connect if needed)
    Ensure-ConfideAccessToken -Url $Url -ClientId $ClientId -ClientSecret $ClientSecret -Certificate $Certificate -IdentityServiceUri $IdentityServiceUri | Out-Null

        $models = @()
        if ($PSCmdlet.ParameterSetName -eq 'Csv') {
            if (-not (Test-Path -LiteralPath $CsvPath)) { throw "CSV file not found: $CsvPath" }
            Write-Verbose "Reading CSV: $CsvPath"
            $rows = Import-Csv -Path $CsvPath -Delimiter $Delimiter
            foreach ($r in $rows) {
                # Skip completely empty lines
                if ($r.PSObject.Properties.ForEach({ $_.Value }) -join '' -eq '') { continue }
                if (-not ($r.PSObject.Properties.Name -contains $requiredEmailColumn) -or [string]::IsNullOrWhiteSpace($r.$requiredEmailColumn)) {
                    throw "Row missing required column: $requiredEmailColumn"
                }
                $email = $r.$requiredEmailColumn

                $modelArgs = @{
                    Email = $email
                }
                foreach ($kv in $columnMap.GetEnumerator()) {
                    $csvCol = $kv.Key; $propName = $kv.Value
                    if ($propName -eq 'Email') { continue } # already handled
                    if ($r.PSObject.Properties.Name -contains $csvCol) {
                        $raw = $r.$csvCol
                        if ($null -ne $raw) { $raw = $raw.ToString().Trim() }
                        # Skip empty / null values entirely
                        if ($raw -eq '' -or $null -eq $raw) { continue }
                        if ($boolTargetProps -contains $propName) { $raw = Convert-ToBool $raw }
                        # Some CSV templates have both download/upload project columns mapped to same generateUploadLink property.
                        # Merge boolean truthy values instead of overwriting with a later blank.
                        if ($modelArgs.ContainsKey($propName)) {
                            if ($boolTargetProps -contains $propName) {
                                $existing = $modelArgs[$propName]
                                if ($existing -is [bool] -and $raw -is [bool]) {
                                    $modelArgs[$propName] = ($existing -or $raw)
                                } elseif ($existing -eq $null) {
                                    $modelArgs[$propName] = $raw
                                } # otherwise keep existing non-null value
                            } else {
                                if ($modelArgs[$propName] -eq $null) { $modelArgs[$propName] = $raw }
                            }
                        } else {
                            $modelArgs[$propName] = $raw
                        }
                    }
                }
                $model = Initialize-ConfideApiModelObjectsModernApiPrivilegedUser @modelArgs
                $models += $model
            }
        } else { # List
            foreach ($item in $ShareCoordinatorModels) {
                if ($item -is [hashtable]) {
                    $itemObj = [pscustomobject]$item
                } else { $itemObj = $item }
                
                # If it's already shaped (has model properties) accept as is; else reconstruct via initializer
                if ($itemObj.PSObject.Properties.Name -contains 'Email') {
                    $models += $itemObj
                } else {
                    # fallback, just ensure Email present
                    $models += Initialize-ConfideApiModelObjectsModernApiPrivilegedUser -Email $itemObj.Email
                }
            }
        }

    if ($models.Count -eq 0) { throw 'No models to import (after processing CSV/List).' }

        if ($PSCmdlet.ShouldProcess("$($models.Count) users", 'Import-ConfideSharingUser')) {
            $result = Import-ConfideSharingUser -ApiModelObjectsModernApiPrivilegedUser $models
        }

        if ($PassThru) {
            if ($result) { return [pscustomobject]@{ ImportTaskId = $result; Models = $models } } else { return $models }
        } else {
            return $result
        }
    }
}

function Get-ConfideShareCenterUserImportTask {
    <#
    .SYNOPSIS
    Get the processing status of an import sharing users task (wrapper for Get-ImportUsersStatus).
 
    .DESCRIPTION
    Calls the underlying Get-ImportUsersStatus API for the provided import task id and returns the task status object.
    If the current access token is missing or expired and connection parameters (Url/ClientId plus ClientSecret or Certificate) are provided the cmdlet will auto-connect.
    Specify -Wait to poll at the provided interval until the task completes (CompletedAt has a value or status indicates completion).
 
    .PARAMETER Id
    The import task id returned by Import-ConfideSharingUser.
 
    .PARAMETER Url
    Base API endpoint (only used for auto-connect when token refresh is required).
 
    .PARAMETER ClientId
    Identity / Client id used for auto-connect.
 
    .PARAMETER ClientSecret
    Client secret (used for auto-connect; mutually exclusive with Certificate).
 
    .PARAMETER Certificate
    X509 certificate (used for auto-connect; mutually exclusive with ClientSecret).
 
    .PARAMETER IdentityServiceUri
    Override the identity service URI.
 
    .PARAMETER WithHttpInfo
    When present returns a hashtable including Response, StatusCode and Headers.
 
    .PARAMETER Wait
    Poll until the task completes.
 
    .PARAMETER PollSeconds
    Polling interval in seconds (default 5).
 
    .EXAMPLE
    Get-ConfideShareCenterUserImportTask -ImportTaskId $id
 
    .EXAMPLE
    Get-ConfideShareCenterUserImportTask -ImportTaskId $id -Wait -PollSeconds 3 -Url $Url -ClientId $ClientId -ClientSecret $Secret
    #>

    [CmdletBinding(SupportsShouldProcess)]
    Param(
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [string]$Id,
        [Parameter(Mandatory = $false)]
        [string]$Url,
        [Parameter(Mandatory = $false)]
        [string]$ClientId,
        [Parameter(Mandatory = $false)]
        [Alias('Cert')]
        [System.Security.Cryptography.X509Certificates.X509Certificate2]$Certificate,
        [Parameter(Mandatory = $false)]
        [string]$ClientSecret,
        [Parameter(Mandatory = $false)]
        [string]$IdentityServiceUri,
        [switch]$WithHttpInfo,
        [switch]$Wait,
        [int]$PollSeconds = 5
    )
    Begin {}
    Process {
    # Ensure valid access token
    Ensure-ConfideAccessToken -Url $Url -ClientId $ClientId -ClientSecret $ClientSecret -Certificate $Certificate -IdentityServiceUri $IdentityServiceUri | Out-Null

        if ($Wait -and $PollSeconds -le 0) { throw '-PollSeconds must be a positive integer.' }

        function Invoke-StatusQuery {
            if ($PSCmdlet.ShouldProcess($Id,'Get-ConfideImportUsersStatus')) {
                if ($WithHttpInfo) { return Get-ConfideImportUsersStatus -Id $Id -WithHttpInfo } else { return Get-ConfideImportUsersStatus -Id $Id }
            }
        }

        $result = Invoke-StatusQuery
        if (-not $Wait) { return $result }

        function Test-Completed($r) {
            if ($null -eq $r) { return $false }
            $obj = $r
            if ($WithHttpInfo) { $obj = $r['Response'] }
            if ($obj.completedAt) { return $true }
            $statusText = ($obj.status | Out-String).Trim()
            if ($statusText -match '(?i)completed|complete|failed|success|succeeded|done') { return $true }
            return $false
        }

        $attempt = 0
        while (-not (Test-Completed $result)) {
            $attempt++
            Write-Verbose "Polling import status (attempt $attempt)..."
            Start-Sleep -Seconds $PollSeconds
            $result = Invoke-StatusQuery
        }
        Write-Verbose 'Import task completed.'
        return $result
    }
}