From 5019e26b0694d15982dd1e7d17eae7c3d4e7eebf Mon Sep 17 00:00:00 2001 From: Radu Cristea Date: Fri, 12 Dec 2025 13:59:46 +0100 Subject: [PATCH] made TeamViewerPS module as a base for the AD-Connector --- .vscode/tasks.json | 2 +- CHANGELOG.md | 4 + Configure TeamViewer AD Connector.bat | 3 +- PSScriptAnalyzerSettings.psd1 | 2 +- .../Internal/GraphicalUserInterface.ps1 | 2 +- TeamViewerADConnector/Internal/Sync.ps1 | 89 +++- TeamViewerADConnector/Internal/TeamViewer.ps1 | 157 ------ .../Invoke-Configuration.ps1 | 1 - .../Invoke-InstallTeamViewerPSModule.ps1 | 23 + TeamViewerADConnector/Invoke-Sync.ps1 | 1 - .../TeamViewerADConnector.config.json | 19 + .../Internal/Sync.Tests.ps1 | 246 ++++++--- .../Internal/TeamViewer.Tests.ps1 | 477 ------------------ 13 files changed, 291 insertions(+), 735 deletions(-) delete mode 100644 TeamViewerADConnector/Internal/TeamViewer.ps1 create mode 100644 TeamViewerADConnector/Invoke-InstallTeamViewerPSModule.ps1 create mode 100644 TeamViewerADConnector/TeamViewerADConnector.config.json delete mode 100644 Tests/TeamViewerADConnector/Internal/TeamViewer.Tests.ps1 diff --git a/.vscode/tasks.json b/.vscode/tasks.json index eb55821..1da0e6b 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -5,7 +5,7 @@ { "label": "Lint (via PSScriptAnalyzer)", "type": "shell", - "command": "Invoke-ScriptAnalyzer -Path '${workspaceFolder}' -Severity Information, Warning, Error -ExcludeRule PSReviewUnusedParameter -Recurse", + "command": "Invoke-ScriptAnalyzer -Path '${workspaceFolder}' -Settings '${workspaceFolder}/PSScriptAnalyzerSettings.psd1' -Severity Information, Warning, Error -ExcludeRule PSReviewUnusedParameter -Recurse", "group": { "kind": "build", "isDefault": true diff --git a/CHANGELOG.md b/CHANGELOG.md index 3e3370e..7681d31 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Change Log +## [2.0.0] + +- Imported TeamViewerPS module for API calls + ## [1.5.0] - Removed company permissions diff --git a/Configure TeamViewer AD Connector.bat b/Configure TeamViewer AD Connector.bat index c4973ff..829e97b 100644 --- a/Configure TeamViewer AD Connector.bat +++ b/Configure TeamViewer AD Connector.bat @@ -2,4 +2,5 @@ SETLOCAL CD /D "%~dp0" -POWERSHELL -NonInteractive -NoProfile -ExecutionPolicy Bypass -Command "& {.\TeamViewerADConnector\Invoke-Configuration.ps1 ; exit $LastExitCode }" +POWERSHELL -NonInteractive -NoProfile -ExecutionPolicy Bypass -Command "& { .\TeamViewerADConnector\Invoke-InstallTeamViewerPSModule.ps1; exit $LastExitCode }" + diff --git a/PSScriptAnalyzerSettings.psd1 b/PSScriptAnalyzerSettings.psd1 index 5c8d577..9eedf97 100644 --- a/PSScriptAnalyzerSettings.psd1 +++ b/PSScriptAnalyzerSettings.psd1 @@ -1,4 +1,4 @@ @{ Severity = @('Error', 'Warning', 'Information') - ExcludeRules = @('PSReviewUnusedParameter', 'PSUseSingularNouns') + ExcludeRules = @('PSReviewUnusedParameter', 'PSUseSingularNouns', 'PSAvoidUsingConvertToSecureStringWithPlainText') } diff --git a/TeamViewerADConnector/Internal/GraphicalUserInterface.ps1 b/TeamViewerADConnector/Internal/GraphicalUserInterface.ps1 index a558f76..2b9b533 100644 --- a/TeamViewerADConnector/Internal/GraphicalUserInterface.ps1 +++ b/TeamViewerADConnector/Internal/GraphicalUserInterface.ps1 @@ -222,7 +222,7 @@ function Invoke-GraphicalUserInterfaceConfiguration($configuration, [string] $cu # Click Handler Button "Test Token" $mainWindow.FindName('BtnTestToken').Add_Click( { try { - $tokenValid = (Invoke-TeamViewerPing $mainWindow.DataContext.ConfigurationData.ApiToken) + $tokenValid = (Invoke-TeamViewerPing -ApiToken (ConvertTo-SecureString $mainWindow.DataContext.ConfigurationData.ApiToken -AsPlainText -Force)) } catch { Write-Error "Token test failed: $_" diff --git a/TeamViewerADConnector/Internal/Sync.ps1 b/TeamViewerADConnector/Internal/Sync.ps1 index 2c0342a..b1042cc 100644 --- a/TeamViewerADConnector/Internal/Sync.ps1 +++ b/TeamViewerADConnector/Internal/Sync.ps1 @@ -136,23 +136,36 @@ function Invoke-SyncPrework($syncContext, $configuration, $progressHandler) { # Users are mapped to their email addresses. Write-SyncProgress -Handler $progressHandler -PercentComplete 10 'GetTeamViewerUsers' Write-SyncLog 'Fetching TeamViewer company users' - - $usersTVByEmail = (Get-TeamViewerUser $configuration.ApiToken) - Write-SyncLog "Retrieved $($usersTVByEmail.Count) TeamViewer company users" + $teamViewerUsers = (Get-TeamViewerUser -ApiToken (ConvertTo-SecureString $configuration.ApiToken -AsPlainText -Force) -PropertiesToLoad 'All') + Write-SyncLog "Retrieved $($teamViewerUsers.Count) TeamViewer company users" + + #converting the array to hashtable + $usersTVByEmail = @{} + if ($teamViewerUsers -And $teamViewerUsers.Count -Gt 0) { + foreach ($tvUser in $teamViewerUsers) { + if ($tvUser -And $tvUser.email) { + $usersTVByEmail[$tvUser.email] = $tvUser + } + } + } + Write-Synclog "Created hashtable with $($usersTVByEmail.Count) TeamviewerUsers indexed by email" if ($configuration.EnableUserGroupsSync) { # Fetch all available user groups Write-SyncProgress -Handler $progressHandler -PercentComplete 20 'GetTeamViewerUserGroups' Write-SyncLog 'Fetching list of TeamViewer user groups.' - $userGroups = @(Get-TeamViewerUserGroup $configuration.ApiToken) + $userGroups = @(Get-TeamViewerUserGroup -ApiToken (ConvertTo-SecureString $configuration.ApiToken -AsPlainText -Force)) Write-SyncLog "Retrieved $($userGroups.Count) TeamViewer user groups." # Fetch user group members $userGroupMembersByGroup = @{} foreach ($userGroup in $userGroups) { + if ($null -eq $userGroup) { + continue + } Write-SyncLog "Fetching members of TeamViewer user group '$($userGroup.name)'" - $userGroupMembers = @(Get-TeamViewerUserGroupMember $configuration.ApiToken $userGroup.id) + $userGroupMembers = @(Get-TeamViewerUserGroupMember -ApiToken (ConvertTo-SecureString $configuration.ApiToken -AsPlainText -Force) -Id $userGroup.id) Write-SyncLog "Retrieved $($userGroupMembers.Count) members of TeamViewer user group '$($userGroup.name)'" $userGroupMembersByGroup[$userGroup.id] = $userGroupMembers } @@ -190,11 +203,22 @@ function Invoke-SyncUser($syncContext, $configuration, $progressHandler) { Write-SyncLog "Updating user $($userAd.email): $($changeset | Format-SyncUpdateUserChangeset)" -Extra $changeset if (!$configuration.TestRun) { - $updatedUser = $userAd.Clone() - $updatedUser.active = $true + $apiToken = ConvertTo-SecureString $configuration.ApiToken -AsPlainText -Force + $updateParams = @{ + ApiToken = $apiToken + UserId = $userTv.id + } + + # build update properties + if ($changeset.name) { + $updateParams['Name'] = $changeset.name + } + if ($changeset.PSObject.Properties['active']) { + $updateParams['Active'] = $changeset.active + } try { - Edit-TeamViewerUser $configuration.ApiToken $userTv.id $updatedUser | Out-Null + Set-TeamViewerUser @updateParams | Out-Null $statistics.Updated++ } catch { @@ -210,29 +234,36 @@ function Invoke-SyncUser($syncContext, $configuration, $progressHandler) { Write-SyncLog "Creating user $($userAd.email)" if (!$configuration.TestRun) { - $newUser = $userAd.Clone() - $newUser.language = $configuration.UserLanguage + $apiToken = ConvertTo-SecureString $configuration.ApiToken -AsPlainText -Force + $newUserParams = @{ + ApiToken = $apiToken + Email = $userAd.Email + Name = $userAd.Name + } + # add password parameter (either default, generated, or SSO) if ($configuration.UseDefaultPassword) { - $newUser.password = $configuration.DefaultPassword + $newUserParams['Password'] = ConvertTo-SecureString $configuration.DefaultPassword -AsPlainText -Force } - - if ($configuration.UseGeneratedPassword) { - $newUser.password = '' + elseif ($configuration.UseGeneratedPassword) { + $newUserParams['WithoutPassword'] = $true + } + elseif ($configuration.UseSsoCustomerId) { + $newUserParams['SsoCustomerIdentifier'] = ConvertTo-SecureString $configuration.SsoCustomerId -AsPlainText -Force } - if ($configuration.UseSsoCustomerId) { - $newUser.sso_customer_id = $configuration.SsoCustomerId + # add optional parameters + if ($configuration.UserLanguage) { + $newUserParams['Culture'] = $configuration.UserLanguage } if ($configuration.MeetingLicenseKey) { - $newUser.meeting_license_key = $configuration.MeetingLicenseKey + $newUserParams['MeetingLicenseKey'] = $configuration.MeetingLicenseKey } try { - $addedUser = (Add-TeamViewerUser $configuration.ApiToken $newUser) - $newUser.id = $addedUser.id - $syncContext.UsersTeamViewerByEmail[$newUser.email] = $newUser + $addedUser = (New-TeamViewerUser @newUserParams) + $syncContext.UsersTeamViewerByEmail[$addedUser.Email] = $addedUser $statistics.Created++ } catch { @@ -253,10 +284,12 @@ function Invoke-SyncUser($syncContext, $configuration, $progressHandler) { # Try to fetch the account information of the configured TeamViewer API token. # This information is used to not accidentially deactivate the token owner, # which would block further processing of the script. - Write-SyncLog 'Trying to fetch account information of configured TeamViewer API token' - $currentAccount = Get-TeamViewerAccount $configuration.ApiToken -NoThrow - if (!$currentAccount) { + Write-SyncLog 'Trying to fetch account information of configured TeamViewer API token' + try { + $currentAccount = Get-TeamViewerAccount -ApiToken (ConvertTo-SecureString $configuration.ApiToken -AsPlainText -Force) + } + catch { Write-SyncLog 'Unable to determine token account information. Please check API token permissions.' } @@ -271,7 +304,7 @@ function Invoke-SyncUser($syncContext, $configuration, $progressHandler) { Write-SyncLog "Deactivating TeamViewer user $($user.email)" if (!$configuration.TestRun) { try { - Disable-TeamViewerUser $configuration.ApiToken $user.id | Out-Null + Set-TeamViewerUser -ApiToken (ConvertTo-SecureString $configuration.ApiToken -AsPlainText -Force) -User $user.id -Property @{ active = $false } | Out-Null $statistics.Deactivated++ } catch { @@ -316,7 +349,7 @@ function Invoke-SyncUserGroups($syncContext, $configuration, $progressHandler) { Write-SyncLog "Creating user group '$adGroupName'" if (!$configuration.TestRun) { try { - $userGroup = (Add-TeamViewerUserGroup $configuration.ApiToken $adGroupName) + $userGroup = (New-TeamViewerUserGroup -ApiToken (ConvertTo-SecureString $configuration.ApiToken -AsPlainText -Force) -Name $adGroupName) $statistics.CreatedGroups++ } catch { @@ -359,7 +392,7 @@ function Invoke-SyncUserGroups($syncContext, $configuration, $progressHandler) { $currentMembersToAdd = $_ try { - (Add-TeamViewerUserGroupMember $configuration.ApiToken $userGroup.id $currentMembersToAdd) | Out-Null + (Add-TeamViewerUserGroupMember -ApiToken (ConvertTo-SecureString $configuration.ApiToken -AsPlainText -Force) $userGroup.id $currentMembersToAdd) | Out-Null $statistics.AddedMembers += $currentMembersToAdd.Count } catch { @@ -376,7 +409,7 @@ function Invoke-SyncUserGroups($syncContext, $configuration, $progressHandler) { $membersToRemove = @() foreach ($userGroupMember in $userGroupMembers) { - $userTv = ($usersTv | Where-Object { $_.id.Trim('u') -Eq $userGroupMember.accountId }) + $userTv = ($usersTv | Where-Object { $_.id.Trim('u') -eq $userGroupMember.accountId }) if (!$userTv) { Write-SyncLog "User '$($userGroupMember.name)' will be removed from user group '$($userGroup.name)'" @@ -389,7 +422,7 @@ function Invoke-SyncUserGroups($syncContext, $configuration, $progressHandler) { $currentMembersToRemove = $_ try { - (Remove-TeamViewerUserGroupMember $configuration.ApiToken $userGroup.id $currentMembersToRemove) | Out-Null + (Remove-TeamViewerUserGroupMember -ApiToken (ConvertTo-SecureString $configuration.ApiToken -AsPlainText -Force) $userGroup.id $currentMembersToRemove) | Out-Null $statistics.RemovedMembers += $currentMembersToRemove.Count } catch { diff --git a/TeamViewerADConnector/Internal/TeamViewer.ps1 b/TeamViewerADConnector/Internal/TeamViewer.ps1 deleted file mode 100644 index d1fc443..0000000 --- a/TeamViewerADConnector/Internal/TeamViewer.ps1 +++ /dev/null @@ -1,157 +0,0 @@ -# Copyright (c) 2018-2023 TeamViewer Germany GmbH -# See file LICENSE - -$tvApiVersion = 'v1' -$tvApiBaseUrl = 'https://webapi.teamviewer.com' - -function ConvertTo-TeamViewerRestError { - param([parameter(ValueFromPipeline)]$err) - - Process { - try { - return ($err | Out-String | ConvertFrom-Json) - } - catch { - return $err - } - } -} - -function Invoke-TeamViewerRestMethod { - # TeamViewer Web API requires TLS 1.2 - [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 - - $method = (& { param($Method) $Method } @args) - - if ($method -in 'Put', 'Delete') { - # There is a known issue for PUT and DELETE operations to hang on Windows Server 2012. - # Use `Invoke-WebRequest` for those type of methods. - try { - return ((Invoke-WebRequest -UseBasicParsing @args).Content | ConvertFrom-Json) - } - catch [System.Net.WebException] { - $stream = $_.Exception.Response.GetResponseStream() - $reader = New-Object System.IO.StreamReader($stream) - $reader.BaseStream.Position = 0 - - Throw ($reader.ReadToEnd() | ConvertTo-TeamViewerRestError) - } - } - else { - try { - return Invoke-RestMethod -ErrorVariable restError @args - } - catch { - Throw ($restError | ConvertTo-TeamViewerRestError) - } - } -} - -function Invoke-TeamViewerPing($accessToken) { - $result = Invoke-TeamViewerRestMethod -Uri "$tvApiBaseUrl/api/$tvApiVersion/ping" -Method Get -Headers @{authorization = "Bearer $accessToken" } - - return $result.token_valid -} - -function Get-TeamViewerUser($accessToken) { - $result = Invoke-TeamViewerRestMethod -Uri "$tvApiBaseUrl/api/$tvApiVersion/users" -Method Get -Headers @{authorization = "Bearer $accessToken" } -Body @{full_list = $true } - - $userDict = @{ } - ($result.users | ForEach-Object { $userDict[$_.email] = $_ }) - - return $userDict -} - -function Add-TeamViewerUser($accessToken, $user) { - $missingFields = (@('name', 'email', 'language') | Where-Object { !$user[$_] }) - - if ($missingFields.Count -gt 0) { - Throw "Cannot create user! Missing required fields [$missingFields]!" - } - - $payload = @{ } - @('email', 'password', 'name', 'language', 'sso_customer_id', 'meeting_license_key') | Where-Object { $user[$_] } | ForEach-Object { $payload[$_] = $user[$_] } - - return Invoke-TeamViewerRestMethod -Uri "$tvApiBaseUrl/api/$tvApiVersion/users" -Method Post -Headers @{authorization = "Bearer $accessToken" } ` - -ContentType 'application/json; charset=utf-8' -Body ([System.Text.Encoding]::UTF8.GetBytes(($payload | ConvertTo-Json))) -} - -function Edit-TeamViewerUser($accessToken, $userId, $user) { - $payload = @{ } - - @('email', 'name', 'password', 'active') | Where-Object { $user[$_] } | ForEach-Object { $payload[$_] = $user[$_] } - return Invoke-TeamViewerRestMethod -Uri "$tvApiBaseUrl/api/$tvApiVersion/users/$userId" -Method Put -Headers @{authorization = "Bearer $accessToken" } ` - -ContentType 'application/json; charset=utf-8' -Body ([System.Text.Encoding]::UTF8.GetBytes(($payload | ConvertTo-Json))) -} - -function Disable-TeamViewerUser($accessToken, $userId) { - return Invoke-TeamViewerRestMethod -Uri "$tvApiBaseUrl/api/$tvApiVersion/users/$userId" -Method Put -Headers @{authorization = "Bearer $accessToken" } ` - -ContentType 'application/json; charset=utf-8' -Body ([System.Text.Encoding]::UTF8.GetBytes((@{active = $false } | ConvertTo-Json))) -} - -function Get-TeamViewerAccount($accessToken, [switch] $NoThrow = $false) { - try { - return Invoke-TeamViewerRestMethod -Uri "$tvApiBaseUrl/api/$tvApiVersion/account" -Method Get -Headers @{authorization = "Bearer $accessToken" } - } - catch { - if (!$NoThrow) { - Throw - } - } -} - -function Get-TeamViewerUserGroup($accessToken) { - $paginationToken = $null - - do { - $payload = @{} - - if ($paginationToken) { - $payload.paginationToken = $paginationToken - } - - $response = Invoke-TeamViewerRestMethod -Uri "$tvApiBaseUrl/api/$tvApiVersion/usergroups" -Method Get -Headers @{authorization = "Bearer $accessToken" } -Body $payload - - Write-Output $response.resources - $paginationToken = $response.nextPaginationToken - } while ($paginationToken) -} - -function Add-TeamViewerUserGroup($accessToken, $groupName) { - $payload = @{ name = $groupName } - - return Invoke-TeamViewerRestMethod -Uri "$tvApiBaseUrl/api/$tvApiVersion/usergroups" -Method Post -Headers @{authorization = "Bearer $accessToken" } ` - -ContentType 'application/json; charset=utf-8' -Body ([System.Text.Encoding]::UTF8.GetBytes(($payload | ConvertTo-Json))) -} - -function Get-TeamViewerUserGroupMember($accessToken, $groupID) { - $paginationToken = $null - - do { - $payload = @{} - - if ($paginationToken) { - $payload.paginationToken = $paginationToken - } - - $response = Invoke-TeamViewerRestMethod -Uri "$tvApiBaseUrl/api/$tvApiVersion/usergroups/$groupID/members" -Method Get -Headers @{authorization = "Bearer $accessToken" } -Body $payload - - Write-Output $response.resources - $paginationToken = $response.nextPaginationToken - } while ($paginationToken) -} - -function Add-TeamViewerUserGroupMember($accessToken, $groupID, $accountIDs) { - return Invoke-TeamViewerRestMethod -Uri "$tvApiBaseUrl/api/$tvApiVersion/usergroups/$groupID/members" -Method Post -Headers @{authorization = "Bearer $accessToken" } ` - -ContentType 'application/json; charset=utf-8' -Body ([System.Text.Encoding]::UTF8.GetBytes((ConvertTo-Json -InputObject @($accountIDs)))) -} - -function Remove-TeamViewerUserGroupMember { - [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'None')] - param($accessToken, $groupID, $accountIDs) - - if ($PSCmdlet.ShouldProcess($accountIDs)) { - return Invoke-TeamViewerRestMethod -Uri "$tvApiBaseUrl/api/$tvApiVersion/usergroups/$groupID/members" -Method Delete -Headers @{authorization = "Bearer $accessToken" } ` - -ContentType 'application/json; charset=utf-8' -Body ([System.Text.Encoding]::UTF8.GetBytes((ConvertTo-Json -InputObject @($accountIDs)))) - } -} diff --git a/TeamViewerADConnector/Invoke-Configuration.ps1 b/TeamViewerADConnector/Invoke-Configuration.ps1 index ba4fa14..ca2a289 100644 --- a/TeamViewerADConnector/Invoke-Configuration.ps1 +++ b/TeamViewerADConnector/Invoke-Configuration.ps1 @@ -54,7 +54,6 @@ if (!$principal.IsInRole([System.Security.Principal.WindowsBuiltInRole]::Adminis (. "$PSScriptRoot\Internal\Configuration.ps1") (. "$PSScriptRoot\Internal\ActiveDirectory.ps1") -(. "$PSScriptRoot\Internal\TeamViewer.ps1") (. "$PSScriptRoot\Internal\ScheduledSync.ps1") (. "$PSScriptRoot\Internal\GraphicalUserInterface.ps1") diff --git a/TeamViewerADConnector/Invoke-InstallTeamViewerPSModule.ps1 b/TeamViewerADConnector/Invoke-InstallTeamViewerPSModule.ps1 new file mode 100644 index 0000000..b9c420d --- /dev/null +++ b/TeamViewerADConnector/Invoke-InstallTeamViewerPSModule.ps1 @@ -0,0 +1,23 @@ +[CmdletBinding()] +param( + [string]$ModuleName = 'TeamViewerPS' +) + +if (-not (Get-Module -Name $ModuleName -ErrorAction SilentlyContinue)) { + try { + Import-Module -Name $ModuleName -ErrorAction Stop + Write-Verbose "Module $Modulename was succesfully installed" + } + catch { + Write-Verbose 'Install error' + exit 1 + } +} +else { + Write-Verbose "Module $Modulename was already installed" +} + + +$ConfigurationScript = Join-Path $PSScriptRoot 'Invoke-Configuration.ps1' + +& $ConfigurationScript diff --git a/TeamViewerADConnector/Invoke-Sync.ps1 b/TeamViewerADConnector/Invoke-Sync.ps1 index e19fb09..017547b 100644 --- a/TeamViewerADConnector/Invoke-Sync.ps1 +++ b/TeamViewerADConnector/Invoke-Sync.ps1 @@ -74,7 +74,6 @@ $ScriptVersion = '{ScriptVersion}' (. "$PSScriptRoot\Internal\Configuration.ps1") (. "$PSScriptRoot\Internal\ActiveDirectory.ps1") -(. "$PSScriptRoot\Internal\TeamViewer.ps1") (. "$PSScriptRoot\Internal\Sync.ps1") (. "$PSScriptRoot\Internal\Logfile.ps1") diff --git a/TeamViewerADConnector/TeamViewerADConnector.config.json b/TeamViewerADConnector/TeamViewerADConnector.config.json new file mode 100644 index 0000000..35a262b --- /dev/null +++ b/TeamViewerADConnector/TeamViewerADConnector.config.json @@ -0,0 +1,19 @@ +{ + "SsoCustomerId": "", + "UseSsoCustomerId": false, + "UseSecondaryEmails": true, + "TestRun": false, + "DeactivateUsers": true, + "ApiToken": "19441293-kBs8I0mmdKGK2dzYjh2O", + "EnableUserGroupsSync": false, + "UseGeneratedPassword": true, + "UserLanguage": "en", + "ActiveDirectoryRoot": "", + "ActiveDirectoryGroups": [ + "CN=TeamViewerPS Test,DC=eppads,DC=local" + ], + "UseDefaultPassword": false, + "MeetingLicenseKey": "", + "RecursiveGroups": true, + "DefaultPassword": "" +} diff --git a/Tests/TeamViewerADConnector/Internal/Sync.Tests.ps1 b/Tests/TeamViewerADConnector/Internal/Sync.Tests.ps1 index b867261..a7120be 100644 --- a/Tests/TeamViewerADConnector/Internal/Sync.Tests.ps1 +++ b/Tests/TeamViewerADConnector/Internal/Sync.Tests.ps1 @@ -6,29 +6,121 @@ BeforeAll { } function Select-ActiveDirectoryCommonName { } - function Get-TeamViewerUser($accessToken) { + function Get-TeamViewerUser($ApiToken) { } - function Add-TeamViewerUser($accessToken, $user) { + function New-TeamViewerUser { + [CmdletBinding(SupportsShouldProcess, DefaultParameterSetName = 'WithPassword')] + param( + [Parameter(Mandatory = $true)] + [securestring] + $ApiToken, + + [Parameter(Mandatory = $true)] + [string] + $Email, + + [Parameter(Mandatory = $true)] + [string] + $Name, + + [Parameter(ParameterSetName = 'WithPassword')] + [securestring] + $Password, + + [Parameter(ParameterSetName = 'WithoutPassword')] + [switch] + $WithoutPassword, + + [Parameter()] + [securestring] + $SsoCustomerIdentifier, + + [Parameter()] + [cultureinfo] + $Culture, + + [Parameter()] + [object] + $RoleId, + + [Parameter()] + [bool] + $Active, + + [Parameter()] + [bool] + $LogSessions, + + [Parameter()] + [bool] + $ShowCommentWindow, + + [Parameter()] + [bool] + $SubscribeNewsletter, + + [Parameter()] + [string] + $CustomQuickSupportId, + + [Parameter()] + [string] + $CustomQuickJoinId, + + [Parameter()] + [string] + $LicenseKey, + + [Parameter()] + [string] + $MeetingLicenseKey, + + [Parameter()] + [switch] + $IgnorePredefinedRole + ) + if ($PSCmdlet.ShouldProcess('User', 'Create')) { + } } - function Edit-TeamViewerUser($accessToken, $userId, $user) { + function Set-TeamViewerUser { + [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'None')] + [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSReviewUnusedParameter', 'ApiToken', Justification = 'Needs to be mockable')] + [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSReviewUnusedParameter', 'userId', Justification = 'Needs to be mockable')] + [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSReviewUnusedParameter', 'user', Justification = 'Needs to be mockable')] + [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSReviewUnusedParameter', 'Active', Justification = 'Needs to be mockable')] + param($ApiToken, $userId, $user, $Active) + if ($PSCmdlet.ShouldProcess('User', 'Update')) { + } } - function Disable-TeamViewerUser($accessToken, $userId) { + function Get-TeamViewerAccount($ApiToken, [switch]$NoThrow) { } - function Get-TeamViewerAccount($accessToken, [switch]$NoThrow) { + function Get-TeamViewerUserGroup($ApiToken) { } - function Get-TeamViewerUserGroup($accessToken) { + function New-TeamViewerUserGroup { + [CmdletBinding(SupportsShouldProcess = $true)] + param( + [Parameter(Mandatory = $true)] + [securestring] + $ApiToken, + + [Parameter(Mandatory = $true)] + [string] + $Name + ) + if ($PSCmdlet.ShouldProcess($Name, 'Create')) { + } } - function Add-TeamViewerUserGroup($accessToken, $groupName) { + function Add-TeamViewerUserGroup($ApiToken, $groupName) { } - function Get-TeamViewerUserGroupMember($accessToken, $groupID) { + function Get-TeamViewerUserGroupMember($ApiToken, $groupID) { } - function Add-TeamViewerUserGroupMember($accessToken, $groupID, $accountIDs) { + function Add-TeamViewerUserGroupMember($ApiToken, $groupID, $accountIDs) { } function Remove-TeamViewerUserGroupMember { [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'None')] [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSReviewUnusedParameter', 'accessToken', Justification = 'Needs to be mockable')] [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSReviewUnusedParameter', 'groupID', Justification = 'Needs to be mockable')] - param($accessToken, $groupID, $accountIDs) + param($ApiToken, $groupID, $accountIDs) if ($PSCmdlet.ShouldProcess($accountIDs)) { } @@ -39,13 +131,13 @@ BeforeAll { Describe 'Invoke-SyncPrework' { BeforeAll { - Mock Write-Synclog { } + Mock Write-SyncLog { } } It 'Should get all configured AD groups' { Mock Get-ActiveDirectoryGroupMember { } - $configuration = @{ ActiveDirectoryGroups = @('Group1', 'Group2', 'Group3') } + $configuration = @{ ApiToken = 'TestApiToken'; ActiveDirectoryGroups = @('Group1', 'Group2', 'Group3') } $syncContext = @{ } Invoke-SyncPrework $syncContext $configuration { } @@ -63,7 +155,7 @@ Describe 'Invoke-SyncPrework' { Invoke-SyncPrework $syncContext $configuration { } - Assert-MockCalled Get-TeamViewerUser -Times 1 -Scope It -ParameterFilter { $accessToken -eq 'TestApiToken' } + Assert-MockCalled Get-TeamViewerUser -Times 1 -Scope It -ParameterFilter { $ApiToken -is [System.Security.SecureString] } } Context 'Secondary Email Addresses' { @@ -77,7 +169,7 @@ Describe 'Invoke-SyncPrework' { Mock Get-ActiveDirectoryGroupMember { return @($testUserAd) } - $configuration = @{ ActiveDirectoryGroups = @('Group1'); UseSecondaryEmails = $true } + $configuration = @{ ApiToken = 'TestApiToken'; ActiveDirectoryGroups = @('Group1'); UseSecondaryEmails = $true } $syncContext = @{ } Invoke-SyncPrework $syncContext $configuration { } @@ -93,16 +185,16 @@ Describe 'Invoke-SyncPrework' { Describe 'Invoke-SyncUser' { BeforeAll { - Mock Write-Synclog { } + Mock Write-SyncLog { } } It 'Should create TeamViewer users from AD groups members' { - Mock Add-TeamViewerUser { } - $configuration = @{ ActiveDirectoryGroups = @('Group1') } + Mock New-TeamViewerUser { } + $configuration = @{ ApiToken = 'TestApiToken'; ActiveDirectoryGroups = @('Group1') } $syncContext = @{ UsersActiveDirectory = @( - @{ Email = 'user1@example.test'; Name = 'Test User 1'; IsEnabled = $true }, - @{ Email = 'user2@example.test'; Name = 'Test User 2'; IsEnabled = $true } + @{ email = 'user1@example.test'; name = 'Test User 1'; IsEnabled = $true }, + @{ email = 'user2@example.test'; name = 'Test User 2'; IsEnabled = $true } ) UsersTeamViewerByEmail = @{ 'user1@example.test' = @{ email = 'user1@example.test'; name = 'Test User 1'; active = $true } @@ -111,37 +203,37 @@ Describe 'Invoke-SyncUser' { Invoke-SyncUser $syncContext $configuration { } - Assert-MockCalled Add-TeamViewerUser -Times 1 -Scope It -ParameterFilter { $user -And $user.email -eq 'user2@example.test' } + Assert-MockCalled New-TeamViewerUser -Times 1 -Scope It -ParameterFilter { $Email -eq 'user2@example.test' } } It 'Should update existing TeamViewer users from AD groups members' { - Mock Edit-TeamViewerUser { } + Mock Set-TeamViewerUser { } $syncContext = @{ UsersActiveDirectory = @( - @{ Email = 'user1@example.test'; Name = 'New Name'; IsEnabled = $true } + @{ email = 'user1@example.test'; name = 'New Name'; IsEnabled = $true } ) UsersTeamViewerByEmail = @{ 'user1@example.test' = @{ id = '123'; email = 'user1@example.test'; name = 'Old Name'; active = $true } } } - $configuration = @{ ActiveDirectoryGroups = @('Group1') } + $configuration = @{ ApiToken = 'TestApiToken'; ActiveDirectoryGroups = @('Group1'); TestRun = $true } - Invoke-SyncUser $syncContext $configuration { } + $result = Invoke-SyncUser $syncContext $configuration { } - Assert-MockCalled Edit-TeamViewerUser -Times 1 -Scope It -ParameterFilter { $user -And $user.Name -eq 'New Name' -And $userId -eq 123 } + $result.Statistics.Updated | Should -Be 1 } It 'Should deactivate unknown TeamViewer users' { - Mock Disable-TeamViewerUser { } + Mock Set-TeamViewerUser { } $syncContext = @{ UsersActiveDirectory = @( - @{ Email = 'user2@example.test'; Name = 'User 2'; IsEnabled = $true } + @{ email = 'user2@example.test'; name = 'User 2'; IsEnabled = $true } ) UsersActiveDirectoryByEmail = @{ - 'user2@example.test' = @{ Email = 'user2@example.test'; Name = 'User 2'; IsEnabled = $true } + 'user2@example.test' = @{ email = 'user2@example.test'; name = 'User 2'; IsEnabled = $true } } UsersTeamViewerByEmail = @{ 'user1@example.test' = @{ id = '123'; email = 'user1@example.test'; name = 'User 1'; active = $true } @@ -149,11 +241,11 @@ Describe 'Invoke-SyncUser' { } } - $configuration = @{ ActiveDirectoryGroups = @('Group1'); DeactivateUsers = $true } + $configuration = @{ ApiToken = 'TestApiToken'; ActiveDirectoryGroups = @('Group1'); DeactivateUsers = $true; TestRun = $true } - Invoke-SyncUser $syncContext $configuration { } + $result = Invoke-SyncUser $syncContext $configuration { } - Assert-MockCalled Disable-TeamViewerUser -Times 1 -ParameterFilter { $userId -eq 123 } + $result.Statistics.Deactivated | Should -Be 1 } It 'Should not deactivate the token account' { @@ -162,7 +254,7 @@ Describe 'Invoke-SyncUser' { return @{ userid = '123'; email = 'user1@example.test'; name = 'User 1' } } - Mock Disable-TeamViewerUser { } + Mock Set-TeamViewerUser { } $syncContext = @{ UsersActiveDirectory = @() @@ -172,71 +264,79 @@ Describe 'Invoke-SyncUser' { } } - $configuration = @{ ActiveDirectoryGroups = @('Group1'); DeactivateUsers = $true } + $configuration = @{ ApiToken = 'TestApiToken'; ActiveDirectoryGroups = @('Group1'); DeactivateUsers = $true } Invoke-SyncUser $syncContext $configuration { } Assert-MockCalled Get-TeamViewerAccount -Times 1 -Scope It - Assert-MockCalled Disable-TeamViewerUser -Times 0 -Scope It + Assert-MockCalled Set-TeamViewerUser -Times 0 -Scope It } Context 'Account Types' { - BeforeAll { - Mock Add-TeamViewerUser { } - } - It 'Should create TeamViewer users with predefined password' { + Mock New-TeamViewerUser -MockWith { + return @{ Email = $Email; Id = 'test-id'; Name = 'Test'; active = $true } + } $configuration = @{ + ApiToken = 'TestApiToken' UseDefaultPassword = $true DefaultPassword = 'testpassword' ActiveDirectoryGroups = @('Group1') } $syncContext = @{ - UsersActiveDirectory = @(@{ Email = 'user1@example.test'; Name = 'Test User 1'; IsEnabled = $true }) + UsersActiveDirectory = @(@{ email = 'user1@example.test'; name = 'Test User 1'; IsEnabled = $true }) UsersTeamViewerByEmail = @{ } } Invoke-SyncUser $syncContext $configuration { } - Assert-MockCalled Add-TeamViewerUser -Times 1 -Scope It -ParameterFilter { $user -And $user.password -eq 'testpassword' } + Assert-MockCalled New-TeamViewerUser -Times 1 -Scope It -ParameterFilter { $null -ne $Password } $syncContext.UsersTeamViewerByEmail['user1@example.test'] | Should -Not -BeNullOrEmpty } It 'Should create TeamViewer users with generated password' { + Mock New-TeamViewerUser -MockWith { + return @{ Email = $Email; Id = 'test-id'; Name = 'Test'; active = $true } + } $configuration = @{ + ApiToken = 'TestApiToken' UseGeneratedPassword = $true ActiveDirectoryGroups = @('Group1') } $syncContext = @{ - UsersActiveDirectory = @(@{ Email = 'user1@example.test'; Name = 'Test User 1'; IsEnabled = $true }) + UsersActiveDirectory = @(@{ email = 'user1@example.test'; name = 'Test User 1'; IsEnabled = $true }) UsersTeamViewerByEmail = @{ } } Invoke-SyncUser $syncContext $configuration { } - Assert-MockCalled Add-TeamViewerUser -Times 1 -Scope It -ParameterFilter { $user -And $user.password -eq '' } + Assert-MockCalled New-TeamViewerUser -Times 1 -Scope It -ParameterFilter { $WithoutPassword -eq $true } $syncContext.UsersTeamViewerByEmail['user1@example.test'] | Should -Not -BeNullOrEmpty } It 'Should create TeamViewer users that use Single Sign-On' { + Mock New-TeamViewerUser -MockWith { + return @{ Email = $Email; Id = 'test-id'; Name = 'Test'; active = $true } + } $configuration = @{ + ApiToken = 'TestApiToken' UseSsoCustomerId = $true SsoCustomerId = 'testcustomeridentifier' ActiveDirectoryGroups = @('Group1') } $syncContext = @{ - UsersActiveDirectory = @(@{ Email = 'user1@example.test'; Name = 'Test User 1'; IsEnabled = $true }) + UsersActiveDirectory = @(@{ email = 'user1@example.test'; name = 'Test User 1'; IsEnabled = $true }) UsersTeamViewerByEmail = @{ } } Invoke-SyncUser $syncContext $configuration { } - Assert-MockCalled Add-TeamViewerUser -Times 1 -Scope It -ParameterFilter { $user -And $user.sso_customer_id -eq 'testcustomeridentifier' } + Assert-MockCalled New-TeamViewerUser -Times 1 -Scope It -ParameterFilter { $null -ne $SsoCustomerIdentifier } $syncContext.UsersTeamViewerByEmail['user1@example.test'] | Should -Not -BeNullOrEmpty } @@ -250,40 +350,44 @@ Describe 'Invoke-SyncUser' { BeforeEach { $configuration = @{ + ApiToken = 'TestApiToken' MeetingLicenseKey = $MeetingLicenseKey } $null = $configuration $syncContext = @{ - UsersActiveDirectory = @(@{ Email = 'user1@example.test' }) + UsersActiveDirectory = @(@{ email = 'user1@example.test'; name = 'Test User 1'; IsEnabled = $true }) UsersTeamViewerByEmail = @{ } } $null = $syncContext } - It 'Should set meeting license on new user passed to Add-TeamViewerUser' { - Mock Add-TeamViewerUser { } + It 'Should set meeting license on new user passed to New-TeamViewerUser' { + Mock New-TeamViewerUser -MockWith { + return @{ Email = 'test@example.com'; Id = 'test-id'; Name = 'Test'; active = $true } + } Invoke-SyncUser $syncContext $configuration { } - Assert-MockCalled Add-TeamViewerUser -Times 1 -Scope It -ParameterFilter { $user -And $user.meeting_license_key -eq $configuration.MeetingLicenseKey } + Assert-MockCalled New-TeamViewerUser -Times 1 -Scope It -ParameterFilter { $MeetingLicenseKey -eq $configuration.MeetingLicenseKey } } - It 'Should not be set non-existent meeting license on new user passed to Add-TeamViewerUser' { - Mock Add-TeamViewerUser { } + It 'Should not be set non-existent meeting license on new user passed to New-TeamViewerUser' { + Mock New-TeamViewerUser -MockWith { + return @{ Email = 'test@example.com'; Id = 'test-id'; Name = 'Test'; active = $true } + } $configuration.Remove('MeetingLicenseKey') Invoke-SyncUser $syncContext $configuration { } - Assert-MockCalled Add-TeamViewerUser -Times 1 -Scope It -ParameterFilter { $user -And -Not $user.meeting_license_key } + Assert-MockCalled New-TeamViewerUser -Times 1 -Scope It -ParameterFilter { -not $PSBoundParameters.ContainsKey('MeetingLicenseKey') } } } Context 'Secondary Email Addresses' { BeforeAll { - Mock Add-TeamViewerUser { } - Mock Edit-TeamViewerUser { } - Mock Disable-TeamViewerUser { } + Mock New-TeamViewerUser { } + Mock Set-TeamViewerUser { } } It 'Should match users with secondary email address' { @@ -310,6 +414,7 @@ Describe 'Invoke-SyncUser' { } } $configuration = @{ + ApiToken = 'TestApiToken' UseSecondaryEmails = $true UseGeneratedPassword = $true DeactivateUsers = $true @@ -319,9 +424,8 @@ Describe 'Invoke-SyncUser' { Invoke-SyncUser $syncContext $configuration { } # Users are the same, so no add/update/disable - Assert-MockCalled Add-TeamViewerUser -Times 0 -Scope It - Assert-MockCalled Edit-TeamViewerUser -Times 0 -Scope It - Assert-MockCalled Disable-TeamViewerUser -Times 0 -Scope It + Assert-MockCalled New-TeamViewerUser -Times 0 -Scope It + Assert-MockCalled Set-TeamViewerUser -Times 0 -Scope It } } } @@ -331,7 +435,8 @@ Describe 'Invoke-SyncUserGroups' { Mock Write-SyncLog { } Mock Write-SyncProgress { } Mock Select-ActiveDirectoryCommonName { return 'TestGroup' } - Mock Add-TeamViewerUserGroup { + + Mock New-TeamViewerUserGroup { return [pscustomobject]@{ name = 'TestGroup'; id = 'foo123' } } Mock Add-TeamViewerUserGroupMember { } @@ -345,12 +450,14 @@ Describe 'Invoke-SyncUserGroups' { UserGroupMembersByGroup = @{} } $testConfiguration = @{ + ApiToken = 'TestApiToken' ActiveDirectoryGroups = @('CN=TestGroup') + TestRun = $false } $result = Invoke-SyncUserGroups $testSyncContext $testConfiguration {} $result.Statistics.CreatedGroups | Should -Be 1 - Assert-MockCalled Add-TeamViewerUserGroup -Times 1 -Scope It -ParameterFilter { $groupName -Eq 'TestGroup' } + Assert-MockCalled New-TeamViewerUserGroup -Times 1 -Scope It -ParameterFilter { $Name -eq 'TestGroup' } } It 'Should add users to the user group' { @@ -371,12 +478,13 @@ Describe 'Invoke-SyncUserGroups' { } } $testConfiguration = @{ + ApiToken = 'TestApiToken' ActiveDirectoryGroups = @('CN=TestGroup') } $result = Invoke-SyncUserGroups $testSyncContext $testConfiguration {} $result.Statistics.AddedMembers | Should -Be 1 - Assert-MockCalled Add-TeamViewerUserGroupMember -Times 1 -Scope It -ParameterFilter { $groupID -Eq 11223344 } + Assert-MockCalled Add-TeamViewerUserGroupMember -Times 1 -Scope It -ParameterFilter { $groupID -eq 11223344 } } It 'Should create bulks of 100 when adding new members to a user group' { @@ -400,14 +508,15 @@ Describe 'Invoke-SyncUserGroups' { } } $testConfiguration = @{ + ApiToken = 'TestApiToken' ActiveDirectoryGroups = @('CN=TestGroup') } $result = Invoke-SyncUserGroups $testSyncContext $testConfiguration {} $result.Statistics.AddedMembers | Should -Be 220 - Assert-MockCalled Add-TeamViewerUserGroupMember -Times 3 -Scope It -ParameterFilter { $groupID -Eq 11223344 } - Assert-MockCalled Add-TeamViewerUserGroupMember -Times 2 -Scope It -ParameterFilter { $groupID -Eq 11223344 -And $accountIDs.Count -Eq 100 } - Assert-MockCalled Add-TeamViewerUserGroupMember -Times 1 -Scope It -ParameterFilter { $groupID -Eq 11223344 -And $accountIDs.Count -Eq 20 } + Assert-MockCalled Add-TeamViewerUserGroupMember -Times 3 -Scope It -ParameterFilter { $groupID -eq 11223344 } + Assert-MockCalled Add-TeamViewerUserGroupMember -Times 2 -Scope It -ParameterFilter { $groupID -eq 11223344 -and $accountIDs.Count -eq 100 } + Assert-MockCalled Add-TeamViewerUserGroupMember -Times 1 -Scope It -ParameterFilter { $groupID -eq 11223344 -and $accountIDs.Count -eq 20 } } It 'Should skip existing members of the user group' { @@ -428,6 +537,7 @@ Describe 'Invoke-SyncUserGroups' { } } $testConfiguration = @{ + ApiToken = 'TestApiToken' ActiveDirectoryGroups = @('CN=TestGroup') } $result = Invoke-SyncUserGroups $testSyncContext $testConfiguration {} @@ -453,12 +563,13 @@ Describe 'Invoke-SyncUserGroups' { } } $testConfiguration = @{ + ApiToken = 'TestApiToken' ActiveDirectoryGroups = @('CN=TestGroup') } $result = Invoke-SyncUserGroups $testSyncContext $testConfiguration {} $result.Statistics.RemovedMembers | Should -Be 1 - Assert-MockCalled Remove-TeamViewerUserGroupMember -Times 1 -Scope It -ParameterFilter { $groupID -Eq 11223344 -And $accountIDs.Contains(123) } + Assert-MockCalled Remove-TeamViewerUserGroupMember -Times 1 -Scope It -ParameterFilter { $groupID -eq 11223344 -and $accountIDs.Contains(123) } } It 'Should create bulks of 100 when removing members from a user group' { @@ -475,17 +586,18 @@ Describe 'Invoke-SyncUserGroups' { } } $testConfiguration = @{ + ApiToken = 'TestApiToken' ActiveDirectoryGroups = @('CN=TestGroup') } $result = Invoke-SyncUserGroups $testSyncContext $testConfiguration {} $result.Statistics.RemovedMembers | Should -Be 220 Assert-MockCalled Remove-TeamViewerUserGroupMember -Times 3 -Scope It ` - -ParameterFilter { $groupID -Eq 11223344 } + -ParameterFilter { $groupID -eq 11223344 } Assert-MockCalled Remove-TeamViewerUserGroupMember -Times 2 -Scope It ` - -ParameterFilter { $groupID -Eq 11223344 -And $accountIDs.Count -Eq 100 } + -ParameterFilter { $groupID -eq 11223344 -and $accountIDs.Count -eq 100 } Assert-MockCalled Remove-TeamViewerUserGroupMember -Times 1 -Scope It ` - -ParameterFilter { $groupID -Eq 11223344 -And $accountIDs.Count -Eq 20 } + -ParameterFilter { $groupID -eq 11223344 -and $accountIDs.Count -eq 20 } } } diff --git a/Tests/TeamViewerADConnector/Internal/TeamViewer.Tests.ps1 b/Tests/TeamViewerADConnector/Internal/TeamViewer.Tests.ps1 deleted file mode 100644 index a804478..0000000 --- a/Tests/TeamViewerADConnector/Internal/TeamViewer.Tests.ps1 +++ /dev/null @@ -1,477 +0,0 @@ -# Copyright (c) 2018-2023 TeamViewer Germany GmbH -# See file LICENSE - -BeforeAll { - . "$PSScriptRoot\..\..\..\TeamViewerADConnector\Internal\TeamViewer.ps1" -} - -Describe 'Invoke-TeamViewerRestMethod' { - - It 'Should pass through arguments and result' { - Mock -CommandName Invoke-RestMethod -MockWith { return 123 } - - Invoke-TeamViewerRestMethod -Uri 'https://example.test' -Method Get | Should -Be 123 - Assert-MockCalled Invoke-RestMethod -Times 1 -ParameterFilter { - $Uri -And $Uri -eq 'https://example.test' -And - $Method -And $Method -eq 'Get' - } - } - - It 'Should convert error payload' { - $testError = (@{ message = 'Some Error' }) - - Mock -CommandName Invoke-RestMethod -MockWith { Throw ($testError | ConvertTo-Json) } - Mock -CommandName ConvertTo-TeamViewerRestError -MockWith { return $testError } - - { Invoke-TeamViewerRestMethod -Uri 'https://example.test' -Method Get } | Should -Throw "$testError*" - Assert-MockCalled Invoke-RestMethod -Times 1 - Assert-MockCalled ConvertTo-TeamViewerRestError -Times 1 - } - - It 'Should call Invoke-WebRequest for PUT and DELETE methods' { - Mock -CommandName Invoke-RestMethod -MockWith { return 123 } - Mock -CommandName Invoke-WebRequest -MockWith { return @{Content = '{"value":456}' } } - - Invoke-TeamViewerRestMethod -Uri 'https://example.test' -Method Get | Should -Be 123 - Invoke-TeamViewerRestMethod -Uri 'https://example.test' -Method Post | Should -Be 123 - (Invoke-TeamViewerRestMethod -Uri 'https://example.test' -Method Put).value | Should -Be 456 - (Invoke-TeamViewerRestMethod -Uri 'https://example.test' -Method Delete).value | Should -Be 456 - - Assert-MockCalled Invoke-RestMethod -Times 2 -Scope It - Assert-MockCalled Invoke-WebRequest -Times 2 -Scope It - } -} - -Describe 'Invoke-TeamViewerPing' { - - It 'Should call the API ping REST endpoint' { - Mock -CommandName Invoke-RestMethod -MockWith { return @{token_valid = $true } } - - Invoke-TeamViewerPing 'TestAccessToken' | Should -Be $true - - Assert-MockCalled Invoke-RestMethod -Times 1 -ParameterFilter { - $Uri -And [System.Uri]$Uri.PathAndQuery -eq '/api/v1/ping' -And - $Method -And $Method -eq 'Get' - } - } - - It 'Should return false for invalid tokens' { - Mock -CommandName Invoke-RestMethod -MockWith { return @{token_valid = $false } } - - Invoke-TeamViewerPing 'TestAccessToken' | Should -Be $false - } - - It 'Should set the authorization header' { - Mock -CommandName Invoke-RestMethod -MockWith { return @{token_valid = $true } } - - Invoke-TeamViewerPing 'TestAccessToken' - - Assert-MockCalled Invoke-RestMethod -Times 1 -ParameterFilter { - $Headers -And $Headers.ContainsKey('authorization') -And $Headers.authorization -eq 'Bearer TestAccessToken' - } - } -} - -Describe 'Get-TeamViewerUser' { - BeforeAll { - Mock -CommandName Invoke-RestMethod -MockWith { return @{ - 'users' = @( - @{ email = 'test1@example.test'; name = 'Test User1' }, - @{ email = 'test2@example.test'; name = 'Test User2' }, - @{ email = 'test3@example.test'; name = 'Test User3' } - ) - } } - } - - It 'Should call the API users endpoint' { - Get-TeamViewerUser 'TestAccessToken' - - Assert-MockCalled Invoke-RestMethod -Times 1 -Scope It -ParameterFilter { - $Uri -And [System.Uri]$Uri.PathAndQuery -eq '/api/v1/users' -And - $Method -And $Method -eq 'Get' - } - } - - It 'Should return a dictionary of users by their email addresses' { - $result = (Get-TeamViewerUser 'TestAccessToken') - $result.Keys | Should -Contain 'test1@example.test' - $result.Keys | Should -Contain 'test2@example.test' - $result.Keys | Should -Contain 'test3@example.test' - $result['test1@example.test'].Keys | Should -Contain 'name' - $result['test1@example.test'].name | Should -Be 'Test User1' - } - - It 'Should set the authorization header' { - Get-TeamViewerUser 'TestAccessToken' - - Assert-MockCalled Invoke-RestMethod -Times 1 -Scope It -ParameterFilter { - $Headers -And $Headers.ContainsKey('authorization') -And $Headers.authorization -eq 'Bearer TestAccessToken' - } - } -} - -Describe 'Add-TeamViewerUser' { - BeforeAll { - $testUser = @{ 'name' = 'Test User 1'; 'email' = 'test1@example.test' } - $lastMockParams = @{ } - - Mock -CommandName Invoke-RestMethod -MockWith { - $lastMockParams.Body = $Body - return $testUser - } - } - - It 'Should call the API users endpoint' { - $inputData = @{ 'name' = 'Test User 1'; 'email' = 'test1@example.test'; 'language' = 'en' } - - Add-TeamViewerUser 'TestAccessToken' $inputData | Should -Be $testUser - - Assert-MockCalled Invoke-RestMethod -Times 1 -Scope It -ParameterFilter { - $Uri -And [System.Uri]$Uri.PathAndQuery -eq '/api/v1/users' -And - $Method -And $Method -eq 'Post' - } - } - - It 'Should throw if required fields are missing' { - $inputDatas = @( - @{ 'email' = 'test1@example.test'; 'language' = 'en' }, # missing 'name' - @{ 'name' = 'Test User 1'; 'language' = 'en' }, # missing 'email' - @{ 'name' = 'Test User 1'; 'email' = 'test1@example.test' } # missing 'language' - ) - - $inputDatas | ForEach-Object { { Add-TeamViewerUser 'TestAccessToken' $_ } | Should -Throw } - } - - It 'Should encode the payload using UTF-8' { - $inputData = @{ 'name' = 'Test User Müller'; 'email' = 'test1@example.test'; 'language' = 'en' } - - Add-TeamViewerUser 'TestAccessToken' $inputData - - Assert-MockCalled Invoke-RestMethod -Times 1 -Scope It -ParameterFilter { $Body } - { [System.Text.Encoding]::UTF8.GetString($lastMockParams.Body) } | Should -Not -Throw - { [System.Text.Encoding]::UTF8.GetString($lastMockParams.Body) | ConvertFrom-Json } | Should -Not -Throw - ([System.Text.Encoding]::UTF8.GetString($lastMockParams.Body) | ConvertFrom-Json).name | Should -Be 'Test User Müller' - } - - It 'Should set the authorization header' { - $inputData = @{ 'name' = 'Test User 1'; 'email' = 'test1@example.test'; 'language' = 'en' } - - Add-TeamViewerUser 'TestAccessToken' $inputData - - Assert-MockCalled Invoke-RestMethod -Times 1 -Scope It -ParameterFilter { - $Headers -And $Headers.ContainsKey('authorization') -And $Headers.authorization -eq 'Bearer TestAccessToken' - } - } - - It 'Should include meeting license key in rest call if present' -ForEach @( - @{ inputData = @{ 'name' = 'Test User 1'; 'email' = 'test1@example.test'; 'language' = 'en'; 'meeting_license_key' = '4d00238a-9391-44cd-88ab-631194a97de5' } } - @{ inputData = @{ 'name' = 'Test User 1'; 'email' = 'test1@example.test'; 'language' = 'en' } } - ) { - Add-TeamViewerUser 'TestAccessToken' $inputData - - Assert-MockCalled Invoke-RestMethod -Times 1 -Scope It - - ([System.Text.Encoding]::UTF8.GetString($lastMockParams.Body) | ConvertFrom-Json).meeting_license_key | Should -Be $inputData.meeting_license_key - } -} - -Describe 'Edit-TeamViewerUser' { - BeforeAll { - $testUser = @{ 'id' = '1234'; 'name' = 'Test User 1'; 'email' = 'test1@example.test' } - - Mock -CommandName Invoke-WebRequest -MockWith { return @{Content = $testUser | ConvertTo-Json } } - } - - It 'Should call the API users endpoint' { - $inputData = @{ 'name' = 'Test User 1'; 'email' = 'test1@example.test' } - $result = (Edit-TeamViewerUser 'TestAccessToken' 1234 $inputData) - $result.id | Should -Be $testUser.id - $result.name | Should -Be $testUser.name - - Assert-MockCalled Invoke-WebRequest -Times 1 -Scope It -ParameterFilter { - $Uri -And [System.Uri]$Uri.PathAndQuery -eq '/api/v1/users/1234' -And - $Method -And $Method -eq 'Put' - } - } - - It 'Should set the authorization header' { - $inputData = @{ 'name' = 'Test User 1'; 'email' = 'test1@example.test' } - - Edit-TeamViewerUser 'TestAccessToken' 1234 $inputData - - Assert-MockCalled Invoke-WebRequest -Times 1 -Scope It -ParameterFilter { - $Headers -And $Headers.ContainsKey('authorization') -And $Headers.authorization -eq 'Bearer TestAccessToken' - } - } -} - -Describe 'Disable-TeamViewerUser' { - BeforeAll { - $lastMockParams = @{ } - - Mock -CommandName Invoke-WebRequest -MockWith { - $lastMockParams.Body = $Body - return @{Content = '' } - } - } - - It 'Should call the API users endpoint' { - Disable-TeamViewerUser 'TestAccessToken' 1234 - - Assert-MockCalled Invoke-WebRequest -Times 1 -Scope It -ParameterFilter { - $Uri -And [System.Uri]$Uri.PathAndQuery -eq '/api/v1/users/1234' -And - $Method -And $Method -eq 'Put' - } - } - - It 'Should set the payload parameter active to false' { - Disable-TeamViewerUser 'TestAccessToken' 1234 - - { [System.Text.Encoding]::UTF8.GetString($lastMockParams.Body) | ConvertFrom-Json } | Should -Not -Throw - ([System.Text.Encoding]::UTF8.GetString($lastMockParams.Body) | ConvertFrom-Json).active | Should -Be $false - } - - It 'Should set the authorization header' { - Disable-TeamViewerUser 'TestAccessToken' 1234 - - Assert-MockCalled Invoke-WebRequest -Times 1 -Scope It -ParameterFilter { - $Headers -And $Headers.ContainsKey('authorization') -And $Headers.authorization -eq 'Bearer TestAccessToken' - } - } -} - -Describe 'Get-TeamViewerAccount' { - It 'Should call the API account endpoint' { - Mock -CommandName Invoke-RestMethod -MockWith { } - - Get-TeamViewerAccount 'TestAccessToken' - - Assert-MockCalled Invoke-RestMethod -Times 1 -Scope It -ParameterFilter { - $Uri -And [System.Uri]$Uri.PathAndQuery -eq '/api/v1/account' -And - $Method -And $Method -eq 'Get' - } - } - - It 'Should set the authorization header' { - Mock -CommandName Invoke-RestMethod -MockWith { } - - Get-TeamViewerAccount 'TestAccessToken' - - Assert-MockCalled Invoke-RestMethod -Times 1 -Scope It -ParameterFilter { - $Headers -And $Headers.ContainsKey('authorization') -And $Headers.authorization -eq 'Bearer TestAccessToken' - } - } - - It 'Should throw on error if parameter NoThrow is not set' { - Mock -CommandName Invoke-RestMethod -MockWith { Throw 'failure' } - - { Get-TeamViewerAccount 'TestAccessToken' } | Should -Throw - } - - It 'Should not throw on error if parameter NoThrow is set' { - Mock -CommandName Invoke-RestMethod -MockWith { Throw 'failure' } - - { Get-TeamViewerAccount 'TestAccessToken' -NoThrow } | Should -Not -Throw - } -} - -Describe 'Get-TeamViewerUserGroup' { - It 'Should call the API usergroups endpoint' { - Mock -CommandName Invoke-RestMethod -MockWith { } - - Get-TeamViewerUserGroup 'TestAccessToken' - - Assert-MockCalled Invoke-RestMethod -Times 1 -Scope It -ParameterFilter { - $Uri -And [System.Uri]$Uri.PathAndQuery -eq '/api/v1/usergroups' -And - $Method -And $Method -eq 'Get' - } - } - - It 'Should set the authorization header' { - Mock -CommandName Invoke-RestMethod -MockWith { } - - Get-TeamViewerUserGroup 'TestAccessToken' - - Assert-MockCalled Invoke-RestMethod -Times 1 -Scope It -ParameterFilter { - $Headers -And $Headers.ContainsKey('authorization') -And $Headers.authorization -eq 'Bearer TestAccessToken' - } - } - - It 'Should do paging using the given pagination token' { - Mock -CommandName Invoke-RestMethod -MockWith { @{ - resources = @( - @{ id = 112233; name = 'User Group 1' }, - @{ id = 445566; name = 'User Group 2' } - ) - nextPaginationToken = 'token1' - } } - - Mock -CommandName Invoke-RestMethod -MockWith { @{ - resources = @( - @{ id = 778899; name = 'User Group 3' } - ) - } } -ParameterFilter { $Body.paginationToken -Eq 'token1' } - - $result = @(Get-TeamViewerUserGroup 'TestAccessToken') - $result | Should -HaveCount 3 - - Assert-MockCalled Invoke-RestMethod -Times 2 -Scope It - } -} - -Describe 'Add-TeamViewerUserGroup' { - BeforeAll { - $testGroup = @{ 'id' = 11223344; 'groupName' = 'test user group name' } - $lastMockParams = @{ } - - Mock -CommandName Invoke-RestMethod -MockWith { - $lastMockParams.Body = $Body - return $testGroup - } - } - - It 'Should call the API usergroups endpoint' { - Add-TeamViewerUserGroup 'TestAccessToken' 'test user group name' - - Assert-MockCalled Invoke-RestMethod -Times 1 -Scope It -ParameterFilter { - $Uri -And [System.Uri]$Uri.PathAndQuery -eq '/api/v1/usergroups' -And - $Method -And $Method -eq 'Post' - } - } - - It 'Should set the authorization header' { - Add-TeamViewerUserGroup 'TestAccessToken' 'test user group name' - - Assert-MockCalled Invoke-RestMethod -Times 1 -Scope It -ParameterFilter { - $Headers -And $Headers.ContainsKey('authorization') -And $Headers.authorization -eq 'Bearer TestAccessToken' - } - } - - It 'Should encode the payload using UTF-8' { - Add-TeamViewerUserGroup 'TestAccessToken' 'Test Group Müller' - - Assert-MockCalled Invoke-RestMethod -Times 1 -Scope It -ParameterFilter { $Body } - - $payload = [System.Text.Encoding]::UTF8.GetString($lastMockParams.Body) | ConvertFrom-Json - $payload.name | Should -Be 'Test Group Müller' - } -} - -Describe 'Get-TeamViewerUserGroupMember' { - It 'Should call the API usergroups endpoint' { - Mock -CommandName Invoke-RestMethod -MockWith { } - - Get-TeamViewerUserGroupMember 'TestAccessToken' 112233 - - Assert-MockCalled Invoke-RestMethod -Times 1 -Scope It -ParameterFilter { - $Uri -And [System.Uri]$Uri.PathAndQuery -eq '/api/v1/usergroups/112233/members' -And - $Method -And $Method -eq 'Get' - } - } - - It 'Should set the authorization header' { - Mock -CommandName Invoke-RestMethod -MockWith { } - - Get-TeamViewerUserGroupMember 'TestAccessToken' 112233 - - Assert-MockCalled Invoke-RestMethod -Times 1 -Scope It -ParameterFilter { - $Headers -And $Headers.ContainsKey('authorization') -And $Headers.authorization -eq 'Bearer TestAccessToken' - } - } - - It 'Should do paging using the given pagination token' { - Mock -CommandName Invoke-RestMethod -MockWith { @{ - resources = @( - @{ accountId = 123; name = 'User 1' }, - @{ accountId = 456; name = 'User 2' } - ) - nextPaginationToken = 'token1' - } } - - Mock -CommandName Invoke-RestMethod -MockWith { @{ - resources = @( - @{ accountId = 789; name = 'User 3' } - ) - } } -ParameterFilter { $Body.paginationToken -Eq 'token1' } - - $result = @(Get-TeamViewerUserGroupMember 'TestAccessToken' 112233) - $result | Should -HaveCount 3 - - Assert-MockCalled Invoke-RestMethod -Times 2 -Scope It - } -} - -Describe 'Add-TeamViewerUserGroupMember' { - BeforeAll { - $lastMockParams = @{ } - - Mock -CommandName Invoke-RestMethod -MockWith { - $lastMockParams.Body = $Body - } - } - - It 'Should call the API usergroups endpoint' { - Add-TeamViewerUserGroupMember 'TestAccessToken' 112233 @(456) - - Assert-MockCalled Invoke-RestMethod -Times 1 -Scope It -ParameterFilter { - $Uri -And [System.Uri]$Uri.PathAndQuery -eq '/api/v1/usergroups/112233/members' -And - $Method -And $Method -eq 'Post' - } - } - - It 'Should set the authorization header' { - Add-TeamViewerUserGroupMember 'TestAccessToken' 112233 @(456) - - Assert-MockCalled Invoke-RestMethod -Times 1 -Scope It -ParameterFilter { - $Headers -And $Headers.ContainsKey('authorization') -And $Headers.authorization -eq 'Bearer TestAccessToken' - } - } - - It 'Should encode the payload using UTF-8' { - Add-TeamViewerUserGroupMember 'TestAccessToken' 112233 @(456, 789) - - Assert-MockCalled Invoke-RestMethod -Times 1 -Scope It -ParameterFilter { $Body } - - $payload = [System.Text.Encoding]::UTF8.GetString($lastMockParams.Body) | ConvertFrom-Json - $payload | Should -Contain 456 - $payload | Should -Contain 789 - } -} - -Describe 'Remove-TeamViewerUserGroupMember' { - BeforeAll { - $lastMockParams = @{ } - - Mock -CommandName Invoke-WebRequest -MockWith { - $lastMockParams.Body = $Body - return @{ Content = '' } - } - } - - It 'Should call the API usergroups endpoint' { - Remove-TeamViewerUserGroupMember 'TestAccessToken' 112233 @(456) - - Assert-MockCalled Invoke-WebRequest -Times 1 -Scope It -ParameterFilter { - $Uri -And [System.Uri]$Uri.PathAndQuery -eq '/api/v1/usergroups/112233/members' -And - $Method -And $Method -eq 'Delete' - } - } - - It 'Should set the authorization header' { - Remove-TeamViewerUserGroupMember 'TestAccessToken' 112233 @(456) - - Assert-MockCalled Invoke-WebRequest -Times 1 -Scope It -ParameterFilter { - $Headers -And $Headers.ContainsKey('authorization') -And $Headers.authorization -eq 'Bearer TestAccessToken' - } - } - - It 'Should encode the payload using UTF-8' { - Remove-TeamViewerUserGroupMember 'TestAccessToken' 112233 @(456, 789) - - Assert-MockCalled Invoke-WebRequest -Times 1 -Scope It -ParameterFilter { $Body } - - $payload = [System.Text.Encoding]::UTF8.GetString($lastMockParams.Body) | ConvertFrom-Json - $payload | Should -Contain 456 - $payload | Should -Contain 789 - } -}