diff --git a/PowerFGT/Private/Confirm.ps1 b/PowerFGT/Private/Confirm.ps1 index 40f3f8d1..5acf89af 100644 --- a/PowerFGT/Private/Confirm.ps1 +++ b/PowerFGT/Private/Confirm.ps1 @@ -336,3 +336,51 @@ Function Confirm-FGTInterface { $true } + +Function Confirm-FGTUserLocal { + + Param ( + [Parameter (Mandatory = $true)] + [object]$argument + ) + + if ( -not ( $argument | get-member -name name -Membertype Properties)) { + throw "Element specified does not contain a name property." + } + if ( -not ( $argument | get-member -name status -Membertype Properties)) { + throw "Element specified does not contain a status property." + } + if ( -not ( $argument | get-member -name type -Membertype Properties)) { + throw "Element specified does not contain a type property." + } + if ( -not ( $argument | get-member -name passwd -Membertype Properties)) { + throw "Element specified does not contain a passwd property." + } + if ( -not ( $argument | get-member -name ldap-server -Membertype Properties)) { + throw "Element specified does not contain a ldap-server property." + } + if ( -not ( $argument | get-member -name radius-server -Membertype Properties)) { + throw "Element specified does not contain a radius-server property." + } + if ( -not ( $argument | get-member -name tacacs+-server -Membertype Properties)) { + throw "Element specified does not contain a tacacs+-server property." + } + if ( -not ( $argument | get-member -name two-factor -Membertype Properties)) { + throw "Element specified does not contain a two-factor property." + } + if ( -not ( $argument | get-member -name two-factor-authentication -Membertype Properties)) { + throw "Element specified does not contain a two-factor-authentication property." + } + if ( -not ( $argument | get-member -name fortitoken -Membertype Properties)) { + throw "Element specified does not contain a fortitoken property." + } + if ( -not ( $argument | get-member -name email-to -Membertype Properties)) { + throw "Element specified does not contain a email-to property." + } + if ( -not ( $argument | get-member -name sms-server -Membertype Properties)) { + throw "Element specified does not contain a sms-server property." + } + + $true + +} \ No newline at end of file diff --git a/PowerFGT/Public/cmdb/user/local.ps1 b/PowerFGT/Public/cmdb/user/local.ps1 index d810d7b9..485f4409 100644 --- a/PowerFGT/Public/cmdb/user/local.ps1 +++ b/PowerFGT/Public/cmdb/user/local.ps1 @@ -5,6 +5,129 @@ # SPDX-License-Identifier: Apache-2.0 # +function Add-FGTUserLocal { + + <# + .SYNOPSIS + Add a FortiGate Local User + + .DESCRIPTION + Add a FortiGate Local User (Name, Password, MFA) + + .EXAMPLE + Add-FGTUserLocal -Name FGT -password MyFGT -status + + Add Local User object name FGT, password MyFGT and enable it + + .EXAMPLE + Add-FGTUserLocal -Name FGT -password MyFGT -status -two_factor email -two_factor_authentication email -email_to powerfgt@fgt.power + + Add Local User object name FGT, password MyFGT and enable it, with two factor authentication by email + #> + + Param( + [Parameter (Mandatory = $true)] + [string]$name, + [Parameter (Mandatory = $false)] + [switch]$status, + [Parameter (Mandatory = $false, ParameterSetName = "local")] + [SecureString]$password, + [Parameter (Mandatory = $false, ParameterSetName = "radius")] + [string]$radius_server, + [Parameter (Mandatory = $false, ParameterSetName = "tacacs")] + [string]$tacacs_server, + [Parameter (Mandatory = $false)] + [ValidateSet("fortitoken", "email", "sms", "disable", "fortitoken-cloud")] + [string]$two_factor, + [Parameter (Mandatory = $false)] + [ValidateSet("fortitoken", "email", "sms")] + [string]$two_factor_authentication, + [Parameter (Mandatory = $false)] + [string]$two_factor_notification, + [Parameter (Mandatory = $false)] + [string]$fortitoken, + [Parameter (Mandatory = $false)] + [string]$email_to, + [Parameter (Mandatory = $false)] + [string]$sms_server, + [Parameter(Mandatory = $false)] + [String[]]$vdom, + [Parameter(Mandatory = $false)] + [psobject]$connection = $DefaultFGTConnection + ) + + Begin { + } + + Process { + + $invokeParams = @{ } + if ( $PsBoundParameters.ContainsKey('vdom') ) { + $invokeParams.add( 'vdom', $vdom ) + } + + if ( Get-FGTUserLocal @invokeParams -name $name -connection $connection) { + Throw "Already an Local User object using the same name" + } + + $uri = "api/v2/cmdb/user/local" + + $Local_User = new-Object -TypeName PSObject + + $Local_User | add-member -name "name" -membertype NoteProperty -Value $name + + if ($status) { + $Local_User | add-member -name "status" -membertype NoteProperty -Value "enable" + } + else { + $Local_User | add-member -name "status" -membertype NoteProperty -Value "disable" + } + + switch ( $PSCmdlet.ParameterSetName ) { + "local" { + $Local_User | add-member -name "type" -membertype NoteProperty -Value "password" + $Local_User | add-member -name "passwd" -membertype NoteProperty -Value $password + } + "radius" { + $Local_User | add-member -name "type" -membertype NoteProperty -Value "radius" + $Local_User | add-member -name "radius-server" -membertype NoteProperty -Value $radius_server + } + "tacacs" { + $Local_User | add-member -name "type" -membertype NoteProperty -Value "tacacs" + $Local_User | add-member -name "tacacs+-server" -membertype NoteProperty -Value $tacacs_server + } + default { } + } + + if ( $PsBoundParameters.ContainsKey('two_factor') ) { + $Local_User | add-member -name "two-factor" -membertype NoteProperty -Value $two_factor + } + + if ( $PsBoundParameters.ContainsKey('two_factor_authentication') ) { + $Local_User | add-member -name "two-factor-authentication" -membertype NoteProperty -Value $two_factor_authentication + } + + if ( $PsBoundParameters.ContainsKey('fortitoken') ) { + $Local_User | add-member -name "fortitoken" -membertype NoteProperty -Value $fortitoken + } + + if ( $PsBoundParameters.ContainsKey('email_to') ) { + $Local_User | add-member -name "email-to" -membertype NoteProperty -Value $email_to + } + + if ( $PsBoundParameters.ContainsKey('sms_server') ) { + $Local_User | add-member -name "sms-server" -membertype NoteProperty -Value $sms_server + } + + Invoke-FGTRestMethod -method "POST" -body $Local_User -uri $uri -connection $connection @invokeParams | out-Null + + Get-FGTUserLocal -connection $connection @invokeParams -name $name + } + + End { + } +} + function Get-FGTUserLocal { <# @@ -103,6 +226,200 @@ function Get-FGTUserLocal { $reponse.results } + End { + } +} + +function Set-FGTUserLocal { + + <# + .SYNOPSIS + Configure a FortiGate Local User + + .DESCRIPTION + Change a FortiGate Local User (ip, mask, comment, associated interface... ) + + .EXAMPLE + $MyFGTUserLocal = Get-FGTUserLocal -name MyFGTUserLocal + PS C:\>$MyFGTUserLocal | Set-FGTUserLocal -status $false + + Change MyFGTUserLocal to status disable + + .EXAMPLE + $MyFGTUserLocal = Get-FGTUserLocal -name MyFGTUserLocal + PS C:\>$MyFGTUserLocal | Set-FGTUserLocal -password MyFGTUserLocalPassword + + Change MyFGTUserLocal to value (Password) MyFGTUserLocalPassword + + .EXAMPLE + $MyFGTUserLocal = Get-FGTUserLocal -name MyFGTUserLocal + PS C:\>$MyFGTUserLocal | Set-FGTUserLocal -email_to newpowerfgt@fgt.power + + Change MyFGTUserLocal to set email to newpowerfgt@fgt.power + + #> + + [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'medium', DefaultParameterSetName = 'default')] + Param( + [Parameter (Mandatory = $true, ValueFromPipeline = $true, Position = 1)] + [ValidateScript( { Confirm-FGTAddress $_ })] + [psobject]$userlocal, + [Parameter (Mandatory = $false)] + [string]$name, + [Parameter (Mandatory = $false)] + [switch]$status, + [Parameter (Mandatory = $false, ParameterSetName = "local")] + [SecureString]$password, + [Parameter (Mandatory = $false, ParameterSetName = "radius")] + [string]$radius_server, + [Parameter (Mandatory = $false, ParameterSetName = "tacacs")] + [string]$tacacs_server, + [Parameter (Mandatory = $false)] + [ValidateSet("fortitoken", "email", "sms", "disable", "fortitoken-cloud")] + [string]$two_factor, + [Parameter (Mandatory = $false)] + [ValidateSet("fortitoken", "email", "sms")] + [string]$two_factor_authentication, + [Parameter (Mandatory = $false)] + [string]$two_factor_notification, + [Parameter (Mandatory = $false)] + [string]$fortitoken, + [Parameter (Mandatory = $false)] + [string]$email_to, + [Parameter (Mandatory = $false)] + [string]$sms_server, + [Parameter(Mandatory = $false)] + [String[]]$vdom, + [Parameter(Mandatory = $false)] + [psobject]$connection = $DefaultFGTConnection + ) + + Begin { + } + + Process { + + $invokeParams = @{ } + if ( $PsBoundParameters.ContainsKey('vdom') ) { + $invokeParams.add( 'vdom', $vdom ) + } + + $uri = "api/v2/cmdb/user/local/$($userlocal.name)" + + $_userlocal = new-Object -TypeName PSObject + + if ( $PsBoundParameters.ContainsKey('name') ) { + #TODO check if there is no already a object with this name ? + $_userlocal | add-member -name "name" -membertype NoteProperty -Value $name + $userlocal.name = $name + } + + if ( $PSCmdlet.ParameterSetName -ne "default" -and $userlocal.type -ne $PSCmdlet.ParameterSetName ) { + throw "Address type ($($userlocal.type)) need to be on the same type ($($PSCmdlet.ParameterSetName))" + } + + if ($status) { + $_userlocal | add-member -name "status" -membertype NoteProperty -Value "enable" + } + else { + $_userlocal | add-member -name "status" -membertype NoteProperty -Value "disable" + } + + switch ( $PSCmdlet.ParameterSetName ) { + "local" { + $_userlocal | add-member -name "passwd" -membertype NoteProperty -Value $password + } + "radius" { + $_userlocal | add-member -name "radius-server" -membertype NoteProperty -Value $radius_server + } + "tacacs" { + $_userlocal | add-member -name "tacacs+-server" -membertype NoteProperty -Value $tacacs_server + } + default { } + } + + if ( $PsBoundParameters.ContainsKey('two_factor') ) { + $_userlocal | add-member -name "two-factor" -membertype NoteProperty -Value $two_factor + } + + if ( $PsBoundParameters.ContainsKey('two_factor_authentication') ) { + $_userlocal | add-member -name "two-factor-authentication" -membertype NoteProperty -Value $two_factor_authentication + } + + if ( $PsBoundParameters.ContainsKey('fortitoken') ) { + $_userlocal | add-member -name "fortitoken" -membertype NoteProperty -Value $fortitoken + } + + if ( $PsBoundParameters.ContainsKey('email_to') ) { + $_userlocal | add-member -name "email-to" -membertype NoteProperty -Value $email_to + } + + if ( $PsBoundParameters.ContainsKey('sms_server') ) { + $_userlocal | add-member -name "sms-server" -membertype NoteProperty -Value $sms_server + } + + if ($PSCmdlet.ShouldProcess($userlocal.name, 'Configure User Local')) { + Invoke-FGTRestMethod -method "PUT" -body $_userlocal -uri $uri -connection $connection @invokeParams | out-Null + + Get-FGTUserLocal -connection $connection @invokeParams -name $userlocal.name + } + } + + End { + } +} + +function Remove-FGTUserLocal { + + <# + .SYNOPSIS + Remove a FortiGate Local User + + .DESCRIPTION + Remove a local user object on the FortiGate + + .EXAMPLE + $MyFGTUserLocal = Get-FGTUserLocal -name FGT + PS C:\>$MyFGTUserLocal | Remove-FGTUserLocal + + Remove user object $MyFGTUserLocal + + .EXAMPLE + $MyFGTUserLocal = Get-FGTUserLocal -name MyFGTUserLocal + PS C:\>$MyFGTUserLocal | Remove-FGTUserLocal -confirm:$false + + Remove UserLocal object $MyFGTUserLocal with no confirmation + + #> + + [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'high')] + Param( + [Parameter (Mandatory = $true, ValueFromPipeline = $true, Position = 1)] + [ValidateScript( { Confirm-FGTUserLocal $_ })] + [psobject]$userlocal, + [Parameter(Mandatory = $false)] + [String[]]$vdom, + [Parameter(Mandatory = $false)] + [psobject]$connection = $DefaultFGTConnection + ) + + Begin { + } + + Process { + + $invokeParams = @{ } + if ( $PsBoundParameters.ContainsKey('vdom') ) { + $invokeParams.add( 'vdom', $vdom ) + } + + $uri = "api/v2/cmdb/user/local/$($userlocal.name)" + + if ($PSCmdlet.ShouldProcess($userlocal.name, 'Remove User Local')) { + $null = Invoke-FGTRestMethod -method "DELETE" -uri $uri -connection $connection @invokeParams + } + } + End { } } \ No newline at end of file diff --git a/Tests/common.ps1 b/Tests/common.ps1 index 186e11fc..2f809218 100644 --- a/Tests/common.ps1 +++ b/Tests/common.ps1 @@ -38,6 +38,8 @@ $script:pester_int1 = "int1" $script:pester_vlanid1 = "10" $script:pester_zone1 = "pester_zone1" $script:pester_zone2 = "pester_zone2" +$script:pester_userlocal = "pester_userlocal" +$script:pester_userlocalpassword = "pester_userlocalpassword" . ../credential.ps1 #TODO: Add check if no ipaddress/login/password info... diff --git a/Tests/integration/UserLocal.Tests.ps1 b/Tests/integration/UserLocal.Tests.ps1 new file mode 100644 index 00000000..ca855587 --- /dev/null +++ b/Tests/integration/UserLocal.Tests.ps1 @@ -0,0 +1,187 @@ +# +# Copyright 2022, Cedric Moreau +# +# SPDX-License-Identifier: Apache-2.0 +# + +#include common configuration +. ../common.ps1 + +BeforeAll { + Connect-FGT @invokeParams +} + +Describe "Get User Local" { + + BeforeAll { + Add-FGTUserLocal -name $pester_userlocal -password $pester_userlocalpassword + #$script:uuid = $addr.uuid + } + + It "Get User Local Does not throw an error" { + { + Get-FGTUserLocal + } | Should -Not -Throw + } + + It "Get ALL userlocal" { + $userlocal = Get-FGTUserLocal + $userlocal.count | Should -Not -Be $NULL + } + + It "Get ALL userlocal with -skip" { + $userlocal = Get-FGTUserLocal -skip + $userlocal.count | Should -Not -Be $NULL + } + + It "Get userlocal ($pester_userlocal)" { + $userlocal = Get-FGTUserLocal -name $pester_userlocal + $userlocal.name | Should -Be $pester_userlocal + } + + It "Get userlocal ($pester_userlocal) and confirm (via Confirm-FGTUserLocal)" { + $userlocal = Get-FGTUserLocal -name $pester_userlocal + Confirm-FGTUserLocal ($userlocal) | Should -Be $true + } + + Context "Search" { + + It "Search userlocal by name ($pester_userlocal)" { + $userlocal = Get-FGTUserLocal -name $pester_userlocal + @($userlocal).count | Should -be 1 + $userlocal.name | Should -Be $pester_userlocal + } + + <#It "Search userlocal by uuid ($script:uuid)" { + $userlocal = Get-FGTUserLocal -uuid $script:uuid + @($userlocal).count | Should -be 1 + $userlocal.name | Should -Be $pester_userlocal + }#> + + } + + AfterAll { + Get-FGTUserLocal -name $pester_userlocal | Remove-FGTUserLocal -confirm:$false + } + +} + +Describe "Add User Local" { + + Context "local" { + + AfterEach { + Get-FGTUserLocal -name $pester_userlocal | Remove-FGTUserLocal -confirm:$false + } + + It "Add userlocal $pester_userlocal enable" { + Add-FGTUserLocal -Name $pester_userlocal -status -password $pester_userlocalpassword + $userlocal = Get-FGTUserLocal -name $pester_userlocal + $userlocal.name | Should -Be $pester_userlocal + $userlocal.status | Should -Be "enable" + $userlocal.'email-to' | Should -BeNullOrEmpty + $userlocal.'two-factor' | Should -Be "disable" + } + + It "Add userlocal $pester_userlocal email to" { + Add-FGTUserLocal -Name $pester_userlocal -email_to "powerfgt@power.fgt" -password $pester_userlocalpassword + $userlocal = Get-FGTUserLocal -name $pester_userlocal + $userlocal.name | Should -Be $pester_userlocal + $userlocal.status | Should -Be "disable" + $userlocal.'email-to' | Should -Be "powerfgt@power.fgt" + $userlocal.'two-factor' | Should -Be "disable" + } + + It "Add userlocal $pester_userlocal MFA" { + Add-FGTUserLocal -Name $pester_userlocal -status -two_factor email -email_to "powerfgt@power.fgt" -password $pester_userlocalpassword + $userlocal = Get-FGTUserLocal -name $pester_userlocal + $userlocal.name | Should -Be $pester_userlocal + $userlocal.status | Should -Be "enable" + $userlocal.'email-to' | Should -Be "powerfgt@power.fgt" + $userlocal.'two-factor' | Should -Be "email" + } + + It "Try to Add userlocal $pester_userlocal (but there is already a object with same name)" { + #Add first userlocal + Add-FGTUserLocal -Name $pester_userlocal -status -password $pester_userlocalpassword + #Add Second userlocal with same name + { Add-FGTUserLocal -Name $pester_userlocal -status -password $pester_userlocalpassword } | Should -Throw "Already an Local User object using the same name" + } + + } + +} + +Describe "Configure User Local" { + + Context "local" { + + BeforeAll { + Add-FGTUserLocal -Name $pester_userlocal -password $pester_userlocalpassword + } + + It "Change status userlocal" { + Get-FGTUserLocal -name $pester_userlocal | Set-FGTUserLocal -status + $userlocal = Get-FGTUserLocal -name $pester_userlocal + $userlocal.name | Should -Be $pester_userlocal + $userlocal.status | Should -Be "enable" + $userlocal.'email-to' | Should -BeNullOrEmpty + $userlocal.'two-factor' | Should -Be "disable" + } + + It "Change email to" { + Get-FGTUserLocal -name $pester_userlocal | Set-FGTUserLocal -email_to "powerfgt@power.fgt" + $userlocal = Get-FGTUserLocal -name $pester_userlocal + $userlocal.name | Should -Be $pester_userlocal + $userlocal.status | Should -Be "disable" + $userlocal.'email-to' | Should -Be "powerfgt@power.fgt" + $userlocal.'two-factor' | Should -Be "disable" + } + + It "Change MFA" { + Get-FGTUserLocal -name $pester_userlocal | Set-FGTUserLocal -two_factor email + $userlocal = Get-FGTUserLocal -name $pester_userlocal + $userlocal.name | Should -Be $pester_userlocal + $userlocal.status | Should -Be "disable" + $userlocal.'email-to' | Should -Be "powerfgt@power.fgt" + $userlocal.'two-factor' | Should -Be "email" + } + + It "Change Name" { + Get-FGTUserLocal -name $pester_userlocal | Set-FGTUserLocal -name "pester_userlocal_change" + $userlocal = Get-FGTUserLocal -name "pester_userlocal_change" + $userlocal.name | Should -Be "pester_userlocal_change" + $userlocal.status | Should -Be "disable" + $userlocal.'email-to' | Should -Be "powerfgt@power.fgt" + $userlocal.'two-factor' | Should -Be "email" + } + + AfterAll { + Get-FGTUserLocal -name "pester_userlocal_change" | Remove-FGTUserLocal -confirm:$false + } + + } +} + +Describe "Remove User Local" { + + Context "local" { + + BeforeEach { + Add-FGTUserLocal -Name $pester_userlocal -password $pester_userlocalpassword + } + + It "Remove userlocal $pester_userlocal by pipeline" { + $userlocal = Get-FGTUserLocal -name $pester_userlocal + $userlocal | Remove-FGTUserLocal -confirm:$false + $userlocal = Get-FGTUserLocal -name $pester_userlocal + $userlocal | Should -Be $NULL + } + + } + +} + +AfterAll { + Disconnect-FGT -confirm:$false +} \ No newline at end of file