diff --git a/Modules/VMware.vSphere.SsoAdmin/MobConnect.ps1 b/Modules/VMware.vSphere.SsoAdmin/MobConnect.ps1 new file mode 100644 index 00000000..84b731c2 --- /dev/null +++ b/Modules/VMware.vSphere.SsoAdmin/MobConnect.ps1 @@ -0,0 +1,524 @@ +<# +Copyright 2024 JetStream Software, Inc. +SPDX-License-Identifier: BSD-2-Clause +#> + +class MobConnection { + <# + .NOTES + =================================================== + MOB3 connection object + + Approach borrowed from https://github.com/lamw/vmware-scripts/blob/master/powershell/GlobalPermissions.ps1 + =================================================== + Created on: 11/13/2024 + Github: https://github.com/jetstreamsoft + =================================================== + Usage sample: + $local_user_name = "user@vsphere.local" + $credential = New-Object System.Management.Automation.PSCredential($adminname, $adminpasswd) + $role_id = (Get-VIRole -Name $role_name).ExtensionData.RoleId + + $mobconn = New-Object MobConnection($vcenter_server, $credential, $True) + $mobconn.SetPermissions($local_user_name, $role_id, $True) + $mobconn.Logout() + #> + + hidden [object] $session + hidden [string] $nonce + hidden [string] $vc_address + hidden [bool] $skipCertCheck + + hidden [string] _getLoginUrl() { + return "https://$($this.vc_address)/invsvc/mob3/?moid=authorizationService&method=AuthorizationService.AddGlobalAccessControlList" + } + + hidden [string] _getAddGlobalPermUrl() { + return "https://$($this.vc_address)/invsvc/mob3/?moid=authorizationService&method=AuthorizationService.AddGlobalAccessControlList" + } + + hidden [string] _getGetGlobalPermUrl() { + return "https://$($this.vc_address)/invsvc/mob3/?moid=authorizationService&method=AuthorizationService.GetUserGlobalPrivileges" + } + + hidden [string] _getRemoveGlobalPermUrl() { + return "https://$($this.vc_address)/invsvc/mob3/?moid=authorizationService&method=AuthorizationService.RemoveGlobalAccess" + } + + hidden [string] _getLogoutUrl() { + return "https://$($this.vc_address)/invsvc/mob3/logout" + } + + hidden [String[]] _executeWebRequest([string] $url, [string] $body) { + $request_params = @{ + "Uri" = $url + "WebSession" = $this.session + "Method" = "POST" + "Body" = $body + } + if ($this.skipCertCheck) { + $request_params["SkipCertificateCheck"] = $True + } + $reply = Invoke-WebRequest @request_params + if($reply.StatusCode -ne 200) { + Write-Error "Failed to execute POST on $url with $($reply.StatusCode) - $($reply.Content)" -ErrorAction Stop + } + if (!($reply.Content -match 'Method Invocation Result: ?([^<]+)<')) { + Write-Error "Unexpected reply from MOB." -ErrorAction Stop + } + return $matches[1], $reply.Content + } + + MobConnection([string] $vcenter, [PSCredential] $credential, [bool] $skipCertCheck) { + if ([string]::IsNullOrEmpty($vcenter)) { + Write-Error "vcenter parameter is required" -ErrorAction Stop + } + if ($null -eq $credential) { + Write-Error "credential parameter is required." -ErrorAction Stop + } + + $this.vc_address = $vcenter + $this.session = $null + $this.skipCertCheck = $skipCertCheck + + $sessionvar = $null + # Initial login to vSphere MOB using GET and store session in class variable + $login_params = @{ + "Uri" = $this._getLoginUrl() + "SessionVariable" = "sessionvar" + "Credential" = $credential + "Method" = "GET" + } + if ($this.skipCertCheck) { + $login_params["SkipCertificateCheck"] = $True + } + $results = Invoke-WebRequest @login_params + + # Extract hidden vmware-session-nonce which must be included in future requests to prevent CSRF error + # Credit to https://blog.netnerds.net/2013/07/use-powershell-to-keep-a-cookiejar-and-post-to-a-web-form/ for parsing vmware-session-nonce via Powershell + if($results.StatusCode -eq 200) { + $null = $results -match 'name="vmware-session-nonce" type="hidden" value="?([^\s^"]+)"' + $this.nonce = $matches[1] + $this.session = $sessionvar + } else { + Write-Error "Failed to login to vSphere MOB with $($results.StatusCode) - $($results.Content)" -ErrorAction Stop + } + } + + [string[]] GetGlobalPermissions([string] $user_domain) { + if ($null -eq $this.session) { + Write-Error "Object not logged in, please relogin" -ErrorAction Stop + } + if ([string]::IsNullOrEmpty($user_domain)){ + Write-Error "user_domain parameter is required" -ErrorAction Stop + } + if ($user_domain.Contains('<') -or $user_domain.Contains('>')) { + Write-Error "Invalid user name provided - $user_domain" -ErrorAction Stop + } + + $body="vmware-session-nonce=$($this.nonce)&userName=$([uri]::EscapeDataString($user_domain))" + $reply = $this._executeWebRequest($this._getGetGlobalPermUrl(), $body) + + # If the account has no permissions, there would be no + # after the returned "val" field. + $perm_list = @() + $reply_start = $reply[1].IndexOf(">val<") + $reply_end = $reply[1].IndexOf("", $reply_start) + + $next_perm = 0 + $perm_data = $reply[1].Substring($reply_start, $reply_end-$reply_start) + while ($next_perm -ge 0) { + $next_li = $perm_data.IndexOf('
  • ', $next_perm) + if ($next_li -lt 0) { + $next_perm = -1 + continue + } + $next_perm = $perm_data.IndexOf('
  • ', $next_li) + $perm_list += @($perm_data.Substring($next_li + 4, $next_perm - $next_li - 4)) + } + return $perm_list + } + + [void] RemoveAllGlobalPermissions([string] $user_domain) { + if ($null -eq $this.session) { + Write-Error "Object not logged in, please relogin" -ErrorAction Stop + } + if ([string]::IsNullOrEmpty($user_domain)){ + Write-Error "user_domain parameter is required" -ErrorAction Stop + } + if ($user_domain.Contains('<') -or $user_domain.Contains('>')) { + Write-Error "Invalid user name provided - $user_domain" -ErrorAction Stop + } + + $request_body=@" + + $user_domain + false + +"@ + # Prepare permissions request + # The POST data payload must include the vmware-session-nonce variable + URL-encoded request body + $body="vmware-session-nonce=$($this.nonce)&principals=$([uri]::EscapeDataString($request_body))" + $this._executeWebRequest($this._getRemoveGlobalPermUrl(), $body) + } + + [void] SetGlobalPermissions([string] $user_domain, [long] $vc_role_id, [bool] $propagate) { + if ($null -eq $this.session) { + Write-Error "Object not logged in, please relogin" -ErrorAction Stop + } + if ([string]::IsNullOrEmpty($user_domain)){ + Write-Error "user_domain parameter is required" -ErrorAction Stop + } + if ($user_domain.Contains('<') -or $user_domain.Contains('>')) { + Write-Error "Invalid user name provided - $user_domain" -ErrorAction Stop + } + + # Prepare permissions request + $request_body = @" + + + $user_domain + false + + $vc_role_id + $propagate + +"@ + # The POST data payload must include the vmware-session-nonce variable + URL-encoded request body + $body="vmware-session-nonce=$($this.nonce)&permissions=$([uri]::EscapeDataString($request_body))" + $reply = $this._executeWebRequest($this._getAddGlobalPermUrl(), $body) + if ($reply[0] -ne 'void') { + Write-Error "Invalid role ID $vc_role_id provided." -ErrorAction Stop + } + } + + [bool] IsConnected() { + if ($null -eq $this.session) { + return $false + } + # Validate the session hasn't expired. + $login_params = @{ + "Uri" = $this._getLoginUrl() + "WebSession" = $this.session + "Method" = "GET" + } + if ($this.skipCertCheck) { + $login_params["SkipCertificateCheck"] = $True + } + $results = Invoke-WebRequest @login_params + + if($results.StatusCode -ne 200) { + $this.session = $null + return $false + } + # Replace nonce for the request. + $null = $results -match 'name="vmware-session-nonce" type="hidden" value="?([^\s^"]+)"' + $this.nonce = $matches[1] + return $true + } + + [void] Logout() { + if ($null -ne $this.session) { + Write-Information "Object not logged in" + return + } + # Logout out of vSphere MOB + $logout_params = @{ + "Uri" = $this._getLogoutUrl() + "WebSession" = $this.session + "Method" = "GET" + } + if ($this.skipCertCheck) { + $logout_params["SkipCertificateCheck"] = $True + } + $null = Invoke-WebRequest @logout_params + $this.session = $null + } +} + +function Connect-VcenterServerMOB { + <# + .NOTES + =========================================================================== + .DESCRIPTION + This function establishes a connection to a vSphere server managed object browser. + + .PARAMETER Server + Specifies the IP address or the DNS name of the vSphere server to which you want to connect. + + .PARAMETER User + Specifies the user name you want to use for authenticating with the server. + + .PARAMETER Password + Specifies the password you want to use for authenticating with the server. + + .PARAMETER Credential + Specifies a PSCredential object to for authenticating with the server. + + .PARAMETER SkipCertificateCheck + Specifies whether server Tls certificate validation will be skipped + + .EXAMPLE + Connect-VcenterServerMOB -Server my.vc.server -User myAdmin@vsphere.local -Password MyStrongPa$$w0rd + + Returns an object with connection of 'myAdmin@vsphere.local' user to MOB of vCenter server 'my.vc.server' +#> + [CmdletBinding()] + param( + [Parameter( + Mandatory = $true, + ValueFromPipeline = $false, + ValueFromPipelineByPropertyName = $false, + HelpMessage = 'IP address or the DNS name of the vSphere server')] + [string] + $Server, + + [Parameter( + Mandatory = $true, + ValueFromPipeline = $false, + ValueFromPipelineByPropertyName = $false, + HelpMessage = 'User name you want to use for authenticating with the server', + ParameterSetName = 'UserPass')] + [string] + $User, + + [Parameter( + Mandatory = $true, + ValueFromPipeline = $false, + ValueFromPipelineByPropertyName = $false, + HelpMessage = 'Password you want to use for authenticating with the server', + ParameterSetName = 'UserPass')] + [VMware.vSphere.SsoAdmin.Utils.StringToSecureStringArgumentTransformationAttribute()] + [SecureString] + $Password, + + [Parameter( + Mandatory = $true, + ValueFromPipeline = $false, + ValueFromPipelineByPropertyName = $false, + HelpMessage = 'PSCredential object to use for authenticating with the server', + ParameterSetName = 'Credential')] + [PSCredential] + $Credential, + + [Parameter( + Mandatory = $false, + HelpMessage = 'Skips server Tls certificate validation')] + [switch] + $SkipCertificateCheck) + + Process { + $vCenterMOB = $null + + try { + if ($PSBoundParameters.ContainsKey('Credential')) { + $vCenterMOB = New-Object MobConnection ` + -ArgumentList @( + $Server, + $Credential, + $SkipCertificateCheck) + } else { + $_credential = New-Object System.Management.Automation.PSCredential($User, $Password) + $vCenterMOB = New-Object MobConnection ` + -ArgumentList @( + $Server, + $_credential, + $SkipCertificateCheck) + } + } catch { + Write-Error (FormatError $_.Exception) -ErrorAction Stop + } + + return $vCenterMOB + } +} + +function Set-VcenterServerGlobalPermission { + <# + .NOTES + =========================================================================== + .DESCRIPTION + This function assigns global permissions associated with role to the specified user. + + .PARAMETER Server + Specifies the vSphere server MOB connection + + .PARAMETER TargetUser + Specifies the full name of the local account to assign permissions to. + + .PARAMETER RoleId + Specifies the vCenter role ID to assign. + Acquire with (Get-VIRole -Name $role_name).ExtensionData.RoleId + + .PARAMETER Propagate + Specifies whether global permission must be propagated to all inventory objects + + .EXAMPLE + $myMobConnection = Connect-VcenterServerMOB -Server my.vc.server -User ssoAdmin@vsphere.local -Password 'MyStrongPa$$w0rd' + Set-VcenterServerGlobalPermission -Server $myMobConnection -TargetUser otheruser@vsphere.local -RoleId -9999 -Propagate + + Assign global permissions associated with role '-9999' to the user 'otheruser@vsphere.local' + propagating the assignment to the whole inventory. +#> + [CmdletBinding()] + param( + [Parameter( + Mandatory = $true, + ValueFromPipeline = $true, + ValueFromPipelineByPropertyName = $false, + HelpMessage = 'MobConnection object')] + [ValidateNotNull()] + [MobConnection] + $Server, + + [Parameter( + Mandatory = $true, + ValueFromPipeline = $false, + ValueFromPipelineByPropertyName = $false, + HelpMessage = 'Full name of local user to assign the permission to')] + [ValidateNotNull()] + [string] + $TargetUser, + + [Parameter( + Mandatory = $true, + ValueFromPipeline = $false, + ValueFromPipelineByPropertyName = $false, + HelpMessage = 'vCenter ID of the role to assign')] + [long] + $RoleId, + + [Parameter( + Mandatory = $false, + HelpMessage = 'Propagate global permission to all objects')] + [switch] + $Propagate + ) + Process { + $Server.SetPermissions($TargetUser, $RoleId, $Propagate) + } +} + +function Get-VcenterServerGlobalPermissions { + <# + .NOTES + =========================================================================== + .DESCRIPTION + This function gets the list of global permissions granted to the specified user. + + .PARAMETER Server + Specifies the vSphere server MOB connection + + .PARAMETER TargetUser + Specifies the full name of the local account to get permissions for. + + .EXAMPLE + $myMobConnection = Connect-VcenterServerMOB -Server my.vc.server -User ssoAdmin@vsphere.local -Password 'MyStrongPa$$w0rd' + $perm_list = Get-VcenterServerGlobalPermission -Server $myMobConnection -TargetUser otheruser@vsphere.local + + Get the list of individual permissions in global context associated with the user 'otheruser@vsphere.local' + into variable $perm_list. + #> + [CmdletBinding()] + param( + [Parameter( + Mandatory = $true, + ValueFromPipeline = $true, + ValueFromPipelineByPropertyName = $false, + HelpMessage = 'MobConnection object')] + [ValidateNotNull()] + [MobConnection] + $Server, + + [Parameter( + Mandatory = $true, + ValueFromPipeline = $false, + ValueFromPipelineByPropertyName = $false, + HelpMessage = 'Full name of local user to get the list of permissions for')] + [ValidateNotNull()] + [string] + $TargetUser + ) + Process { + $Server.GetGlobalPermissions($TargetUser) + } +} + +function Reset-VcenterServerGlobalPermissions { + <# + .NOTES + =========================================================================== + .DESCRIPTION + This function removes all global permissions granted to the specified user. + + .PARAMETER Server + Specifies the vSphere server MOB connection + + .PARAMETER TargetUser + Specifies the full name of the local account to remove permissions from. + + .EXAMPLE + $myMobConnection = Connect-VcenterServerMOB -Server my.vc.server -User ssoAdmin@vsphere.local -Password 'MyStrongPa$$w0rd' + Reset-VcenterServerGlobalPermission -Server $myMobConnection -TargetUser otheruser@vsphere.local + + Remove all global permissions granted to the user 'otheruser@vsphere.local' + #> + [CmdletBinding()] + param( + [Parameter( + Mandatory = $true, + ValueFromPipeline = $true, + ValueFromPipelineByPropertyName = $false, + HelpMessage = 'MobConnection object')] + [ValidateNotNull()] + [MobConnection] + $Server, + + [Parameter( + Mandatory = $true, + ValueFromPipeline = $false, + ValueFromPipelineByPropertyName = $false, + HelpMessage = 'Full name of local user to remove the permissions from')] + [ValidateNotNull()] + [string] + $TargetUser + ) + Process { + $Server.RemoveAllGlobalPermissions($TargetUser) + } +} + +function Disconnect-VcenterServerMOB { + <# + .NOTES + =========================================================================== + .DESCRIPTION + This function closes the connection to a vSphere server managed object browser. + + .PARAMETER Server + Specifies the vSphere server MOB connection you want to terminate + + .EXAMPLE + $myMobConnection = Connect-VcenterServerMOB -Server my.vc.server -User ssoAdmin@vsphere.local -Password 'MyStrongPa$$w0rd' + Disconnect-VcenterServerMOB -Server $myMobConnection + + Disconnect a vSphere server managed object browser stored in 'myMobConnection' varaible +#> + [CmdletBinding()] + param( + [Parameter( + Mandatory = $true, + ValueFromPipeline = $true, + ValueFromPipelineByPropertyName = $false, + HelpMessage = 'MobConnection object')] + [ValidateNotNull()] + [MobConnection] + $Server + ) + + Process { + if ($Server.IsConnected()) { + $Server.Logout() + } + } +} diff --git a/Modules/VMware.vSphere.SsoAdmin/VMware.vSphere.SsoAdmin.psd1 b/Modules/VMware.vSphere.SsoAdmin/VMware.vSphere.SsoAdmin.psd1 index 6b8decf9..38f852db 100644 --- a/Modules/VMware.vSphere.SsoAdmin/VMware.vSphere.SsoAdmin.psd1 +++ b/Modules/VMware.vSphere.SsoAdmin/VMware.vSphere.SsoAdmin.psd1 @@ -12,7 +12,7 @@ RootModule = 'VMware.vSphere.SsoAdmin.psm1' # Version number of this module. -ModuleVersion = '1.3.9' +ModuleVersion = '1.4.0' # Supported PSEditions # CompatiblePSEditions = @() @@ -80,7 +80,10 @@ FunctionsToExport = 'Connect-SsoAdminServer', 'Disconnect-SsoAdminServer', 'Get-SsoTokenLifetime', 'Set-SsoTokenLifetime', 'Get-IdentitySource', 'Set-IdentitySource', 'Remove-IdentitySource', 'Add-ActiveDirectoryIdentitySource', 'Add-LDAPIdentitySource', 'Set-LDAPIdentitySource', - 'Get-SsoAuthenticationPolicy', 'Set-SsoAuthenticationPolicy' + 'Get-SsoAuthenticationPolicy', 'Set-SsoAuthenticationPolicy', + 'Connect-VcenterServerMOB', 'Disconnect-VcenterServerMOB', + 'Set-VcenterServerGlobalPermission', 'Get-VcenterServerGlobalPermissions', + 'Reset-VcenterServerGlobalPermissions' # Cmdlets to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no cmdlets to export. CmdletsToExport = @()