psPAS icon indicating copy to clipboard operation
psPAS copied to clipboard

Set-PASUser - Unix time error on ExpiryDate

Open anycard94 opened this issue 1 year ago • 10 comments

Set-PASUser in psPAS 6.4.80 using CyberArk PAM 12.6 on accounts without expiryDate shows the following error:

"ConvertTo-UnixTime : The input object cannot be bound to any parameters for the command either because the command does not take pipeline input or the input and its properties do not match any of the parameters that take pipeline input. At line:44 char:65

  • ... ct['ExpiryDate'] = $UserProperties['ExpiryDate'] | ConvertTo-UnixTime
  •                                                    ~~~~~~~~~~~~~~~~~~
    
    • CategoryInfo : InvalidArgument: (-62135578800:Int64) [ConvertTo-UnixTime], ParameterBindingException
    • FullyQualifiedErrorId : InputObjectNotBound,ConvertTo-UnixTime"

This error doesn't prevent the PAS user changes but is thrown for parameter changed using Set-PASUser.

Environment:

  • PowerShell Version: 5.1.22621.2506
  • psPAS Version: 6.4.80
  • CyberArk Version: 12.6.12.217

The accounts in question all have expiryDate of -62135578800 which is a nullable date. If we set an account with a valid expiration date it does not throw this error.

I was able to modify Format-PASUserObject.ps1 to do a check of negative numbers to remove this error:

'ExpiryDate' { #Include date string in required format if ($UserProperties['ExpiryDate'] -le -1){ $UserObject['ExpiryDate'] = $null } else { $UserObject['ExpiryDate'] = UserProperties['ExpiryDate'] | ConvertTo-UnixTime }

		}

This removes the error and updates the account. However not sure if this is a proper fix but should be a good starting point.

I tried other fixes such as changing from piping to ConvertTo-UnixTime to specifying ConvertTo-UnixTime -Date UserProperties['ExpiryDate'] but that throws an error about DateTime.MinValue.Ticks / Max.Ticks and doesn't update PAS user.

anycard94 avatar May 07 '24 18:05 anycard94

Hi @anycard94 - thanks for the report. This is the first time hearing of the value -62135578800 in relation to the expiry date for a user account in CyberArk. To remove an expiry date one would usually provide a UnixTime value of 0 (or a datetime of 1/1/1970).

pspete avatar May 07 '24 20:05 pspete

If you create a local PAS User using New-PASUser without specifying expiry date it creates user and has that has the value -62135578800.

So, without any changes to Format-PASUserObject.ps1 it sends expiry date as pipeline input to ConvertTo-UnixTime.ps1. Which, then just returns value as $null but with error described.

It appears the -62135578800 value comes back from API when sending $null as expiry date??

Tying to specify with -expirydate of 0 gives an error -> Invoke-PASRestMethod : [500] Value was either too large or too small for an Int32.

Results down below:

PS C:\temp> New-PASUser -UserName pas.test -userType epvuser -authenticationMethod AuthTypeRADIUS -ExpiryDate 0
Invoke-PASRestMethod : [500] Value was either too large or too small for an Int32.
At line:474 char:14
+ ...     $result = Invoke-PASRestMethod -Uri $URI -Method POST -Body $Body
+                   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: ({"ErrorCode":"C...for an Int32."}:ErrorRecord) [Invoke-PASRestMethod], Ex
   ception
    + FullyQualifiedErrorId : **CAWS00001E,Invoke-PASRestMethod**
PS C:\temp> New-PASUser -UserName pas.test -userType epvuser -authenticationMethod AuthTypeRADIUS

ID   UserName Source   UserType Suspended enableUser ExpiryDate Location
--   -------- ------   -------- --------- ---------- ---------- --------
201 pas.test CyberArk EPVUser  False     True                  \


PS C:\temp> get-pasuser -id 201 | select *


enableUser              : True
changePassOnNextLogon   : True
expiryDate              : -62135578800
suspended               : False
lastSuccessfulLoginDate : 1715173918
unAuthorizedInterfaces  : {}
authenticationMethod    : {AuthTypeRadius}
passwordNeverExpires    : False
distinguishedName       :
description             :
businessAddress         : @{workStreet=; workCity=; workState=; workZip=; workCountry=}
internet                : @{homePage=; homeEmail=; businessEmail=; otherEmail=}
phones                  : @{homeNumber=; businessNumber=; cellularNumber=; faxNumber=; pagerNumber=}
personalDetails         : @{street=; city=; state=; zip=; country=; title=; organization=; department=; profession=;
                          firstName=; middleName=; lastName=}
id                      : 201
username                : pas.test
source                  : CyberArk
userType                : EPVUser
componentUser           : False
groupsMembership        : {}
vaultAuthorization      : {}
location                : \

anycard94 avatar May 08 '24 13:05 anycard94

its not something we see when creating a user nor are able to replicate image Expiry date is not set on creation when no value is specified.

ExpiryDate parameter of New-PASUser accepts a DateTime object type, and sets the expected value when specified image

Providing a DateTime of 1/1/1970 clears the expirydate when specified for Set-PASUser image psPAS converts the date to unix time and provides a value of 0 in the request

pspete avatar May 08 '24 22:05 pspete

I must have something different going on with date time on my end. If I set expiry date to 1/1/1970 it returns value 18000 instead of 0.

Creating user with expire date of tomorrow image

Changing expiry date to 1/1/1970 image image

anycard94 avatar May 09 '24 13:05 anycard94

The time I'm getting is UTC - I wrote a function long ago to convert:

image

anycard94 avatar May 09 '24 13:05 anycard94

Maybe something to do with timezone offset then - presume you are UTC-5?

pspete avatar May 09 '24 17:05 pspete

Potentially due to the ToUniversalTime() conversion here.... https://github.com/pspete/psPAS/blob/b8fba0fbf3c1acab2e958f275f39a73647a4b129/psPAS/Private/ConvertTo-UnixTime.ps1#L37

but - removing this would impact times used/returned by other functions in the module.

does (Get-Date 1/1/1970).AddHours(-5) land you at 0 (when updating expirydate)?

pspete avatar May 09 '24 17:05 pspete

I am UTC -5 and came across the following KB "PVWA - Requests REST API converts the request's timeframe value to LocalTime instead of UTC time". This article is marked as a known issue and no workaround.

If I do .AddHours(-5) it does get the expiry date to be 0 and no error. I could use this as workaround when I update other properties on the PAS User accounts.

image

But then when I get the user the value is converted back to -62135578800.

image

anycard94 avatar May 09 '24 18:05 anycard94

@pspete - I tried some modifications but probably not in the preferred way. It appears to give the correct results. Please feel free to copy / modify as needed.

Created a new file ConvertTo-UtcTime.ps1

Function ConvertTo-UtcTime {
	[CmdletBinding()]
	[OutputType('System.Integer')]
	Param(
		[Parameter(
			Mandatory = $true,
			ValueFromPipeline = $true
		)]
		[int64]$time
	)
	begin {
		$currentCulture = [System.Threading.Thread]::CurrentThread.CurrentCulture
	}
	process {
		[System.Threading.Thread]::CurrentThread.CurrentCulture = 'en-US'
		
		#Convert EPOCH Time from local
		$UTCTime = (Get-Date 01/01/1970)+([System.TimeSpan]::fromseconds($time))
		$strCurrentTimeZone = (Get-WmiObject win32_timezone).StandardName
		$TZ = [System.TimeZoneInfo]::FindSystemTimeZoneById($strCurrentTimeZone)
		$UTCTimeFinal = [System.TimeZoneInfo]::ConvertTimeFromUtc($UTCTime, $TZ)
		#If the time is before 1/1/1970 set final UTC to 1/1/1970
		if($UTCTimeFinal -le (Get-Date 1/1/1970)){
			$UTCTimeFinal = (Get-Date 1/1/1970)
		}
		
		# Do UTC Offset
		$UtcOffSetHours = $TZ.BaseUtcOffset.Hours
		$UtcOffSetMinutes = $TZ.BaseUtcOffset.Minutes
                # Are we currently in Daylight Savings??
		If($tz.IsDaylightSavingTime((get-date))){$UtcOffSetHours += 1}
		
		# Return Value
		Return $UTCTimeFinal.AddHours($UtcOffSetHours).AddMinutes($UtcOffSetMinutes)
	}
	end {
		[System.Threading.Thread]::CurrentThread.CurrentCulture = $currentCulture
	}
}

Modified Format-PASUserObject.ps1 - I added the switch as my expire date of -62135578800 comes back as Int64 type instead of DateTime.

switch ($UserProperties.keys) {

			'ExpiryDate' {
				#Include date string in required format
				switch ($UserProperties['ExpiryDate'].GetType().Name){
					'DateTime'{$UserObject['ExpiryDate'] = $UserProperties['ExpiryDate'] | ConvertTo-UnixTime}
					'Int64' {$UserObject['ExpiryDate'] = ($UserProperties['ExpiryDate'] | ConvertTo-UtcTime) | ConvertTo-UnixTime}
				}
			}

Modified ConvertTo-UnixTime.ps1 to modify date on offset.

process {
		[System.Threading.Thread]::CurrentThread.CurrentCulture = 'en-US'
		
		$strCurrentTimeZone = (Get-WmiObject win32_timezone).StandardName
		$TZ = [System.TimeZoneInfo]::FindSystemTimeZoneById($strCurrentTimeZone)
		# Do UTC Offset
		$UtcOffSetHours = $TZ.BaseUtcOffset.Hours
		$UtcOffSetMinutes = $TZ.BaseUtcOffset.Minutes
		# Are we currently in Daylight Savings??
		If($tz.IsDaylightSavingTime((get-date))){$UtcOffSetHours += 1}

		# Return Value
		$Date = $Date.AddHours($UtcOffSetHours).AddMinutes($UtcOffSetMinutes)
		
		
		$UnixTime = [math]::Round($(Get-Date $Date.ToUniversalTime() -UFormat %s))

		If ($Milliseconds) {
			$UnixTime = $UnixTime * 1000
		}

		$UnixTime

Results

Before ConvertTo-UnixTime.ps1 fix for TZ

PS C:\> date

Tuesday, May 14, 2024 2:45:24 PM


PS C:\> Set-PASUser -id 144 -username cybtest -ExpiryDate (get-date).addhours(4)

ID  UserName Source   UserType Suspended enableUser ExpiryDate            Location
--  -------- ------   -------- --------- ---------- ----------            --------
144 CybTest  CyberArk EPVUser  False     True       5/14/2024 10:45:29 PM \


PS C:\> date

Tuesday, May 14, 2024 2:46:17 PM

After modifications ConvertTo-UnixTime.ps1 for TZ

PS C:\> date

Tuesday, May 14, 2024 3:10:08 PM


PS C:\> Set-PASUser -id 144 -username cybtest -description 'test' -ExpiryDate (Get-Date).AddHours(4)

ID  UserName Source   UserType Suspended enableUser ExpiryDate           Location
--  -------- ------   -------- --------- ---------- ----------           --------
144 CybTest  CyberArk EPVUser  False     True       5/14/2024 7:10:11 PM \


PS C:\> date

Tuesday, May 14, 2024 3:10:14 PM

anycard94 avatar May 14 '24 19:05 anycard94

Nice - will take a look next time I have some dev time 👍

pspete avatar May 14 '24 19:05 pspete

Should now be resolved in 6.4.85. Set-PASUser now does not attempt to convert in-situ property values. Unix epoch time is also not converted to universal time if specified as an expirydate.

pspete avatar Jun 04 '24 21:06 pspete