Show-xOTP.ps1

<#
     .SYNOPSIS
        Shows SecureMFA.com OTP code information for the user.
    .DESCRIPTION
        Shows SecureMFA OTP code information.
        Dependencies:
            * System which executes a script must have Microsoft Framework 4.6.1 and above installed.
            * SecureMFA_SupportTools.dll file must be present in script directory.
            * SecureMFA_SupportTools.json configuration file must be present in script directory.
                                     
            Bellow is a sample of valid Json config file with minimal configuration required for script to work:
                {
                "sql_server": "asqlaol1.adatum.labnet",
                "sql_database": "SecureMfaOTP",
                "ui_input_text": "Please enter user's UPN",
                "ui_environment": "MyCompany",
                "data_encryption_passphrase": "d9GhT=7=Ox8-+LaZ"
                }
 
    .PARAMETER DecryptSecret
        Decryption parameter is required for systems which use secret key encryption with AES256.
        ‘encryption_passphrase’ value must match setting which is defined in SecureMFA OTP provider configuration. Otherwise displayed OTP codes will not be valid.
 
    .NOTES
        Version: 1.0.0.2
        Author: SecureMfa.com
        Creation Date: 11/07/2019
        Purpose/Change: Incorporated into module
   
    .EXAMPLE
        C:\PS> Show-xOTP
 
        This command will show OTP codes for the user using local system time.
        It shows OTP values with 10 feature and past time intervals using 30 seconds index. This allows to see if user has any time drift on mobile device.
 
    .EXAMPLE
        C:\PS> Show-xOTP -DecryptSecret
          
        This command will show OTP codes for the user using local system time.
        It shows OTP values with 10 feature and past time intervals using 30 seconds index. This allows to see if user has any time drift on mobile device.
 
    .EXAMPLE
        C:\PS> Show-xOTP -upn test1@adatum.labnet -otpcode 245983 -DecryptSecret
         
        This command will show OTP time drift for a valid user’s OTP code when comparing it against system time.
        Validation is done against 4 hours feature and past time intervals.
        This allows to see if current OTP code displayed on user’s mobile device has time drift and how it is different to system time on which show-otp.ps1 script is executed.
        Time accuracy is up to 30 seconds.
     
#>


#>

Function Show-xOTP {
Param
(
    [Parameter(Mandatory=$false,ParameterSetName="Default")]
    [String]$upn = $null,
    [Parameter(Mandatory=$false,ParameterSetName="Default")]
    [String]$otpcode = $null,
    [Parameter(Mandatory=$false, ParameterSetName="Default")]
    [Switch]$DecryptSecret
)

DynamicParam
{
    # create a dictionary to return, and collection of parameters
    $paramDictionary = New-Object -Type System.Management.Automation.RuntimeDefinedParameterDictionary
    $attributeCollection = New-Object -Type System.Collections.ObjectModel.Collection[System.Attribute]
 
    # create a new [string] parameter for all parameter sets, and decorate with a [ValidateSet]
    $dynParam = New-Object -Type System.Management.Automation.RuntimeDefinedParameter("Profile", [String], $attributeCollection)
    $attributes = New-Object System.Management.Automation.ParameterAttribute
    $fname = "Profiles.json"
    $paramOptions = New-Object System.Management.Automation.ValidateSetAttribute -ArgumentList (ConvertFrom-Json (Get-Content (join-path $PSScriptRoot $fname) -Raw))
 
    $attributeCollection.Add($attributes)
    $attributeCollection.Add($paramOptions)
    $paramDictionary.Add("Profile", $dynParam)
 
    return $paramDictionary
}

Process
{
    #Static Parameters
    $Event_Source = "SecureMFA_SupportTools"
    [Int16]$UserStatus = 0;
    $ConfirmPreference="high"
    $decision_Validation = $null

    $message  = "Please confirm if you want to extract OTP information for user [$upn]"            
    $question = 'Please confirm?'
    $choices = New-Object Collections.ObjectModel.Collection[Management.Automation.Host.ChoiceDescription]
    $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&Yes'))
    $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&No'))
    $decision_Validation = $Host.UI.PromptForChoice($message, $question, $choices, 0)
    if ($decision_Validation -eq 1 ) {break}

    #Checking Dependencies
    #EventLog source dependency
    $ErrMsg = "ResetOTP EventLog source is missing. Please execute following PS command 'New-EventLog -Source SecureMFA_SupportTools -LogName Application' on the system before using the app."
    if (((Get-ChildItem HKLM:\SYSTEM\CurrentControlSet\Services\EventLog\Application).pschildname | where { $_ -eq $Event_Source} | measure).Count -eq 0) 
    {write-host $ErrMsg -ForegroundColor red; pause; break}
    #Config file dependency
    if ( $PSBoundParameters.Keys.Contains("Profile") )
    {
        $configfile = (Join-Path -Path $PSScriptRoot -ChildPath ($PSBoundParameters.Profile + '_SecureMFA_SupportTools.json'))
    }
    else {$configfile = (Join-Path -Path $PSScriptRoot -ChildPath SecureMFA_SupportTools.json)}
    #Test config file path.
    $ErrMsg = "$configfile file is missing. Please copy a file to script directory and try again."
    if (!(Test-Path $configfile)) { write-host $ErrMsg -ForegroundColor red; pause; break }
    #DLL file dependency
    $dllpath = (Join-Path -Path $PSScriptRoot -ChildPath SecureMFA_SupportTools.dll)
    $ErrMsg = "$configfile file is missing. Please copy a file to script directory and try again."
    if (!(Test-Path $dllpath)) { write-host $ErrMsg -ForegroundColor red; pause; break }

    #Read JSON file Configuration
    $json = Get-Content -Raw $configfile | ConvertFrom-Json
    $sqlinstance = $json.sqlserver
    $sqldbname = $json.sqldbname
    $sqlintegratedsecurity = $json.sqlintegratedsecurity
    $sqluseraccount = $json.sqluseraccount
    $sqluserpassword = $json.sqluserpassword
    $input_text = $json.ui_input_text
    $environment = $json.ui_environment
    $encryption_passphrase = $json.data_encryption_passphrase
    $sqlConnectString = "server=" + $sqlinstance + ";initial catalog=" + $sqldbname + ";integrated security=" + $sqlintegratedsecurity + ";User ID=" + $sqluseraccount + ";Password=" + $sqluserpassword;

    #Get user's input if required
    write-host " -- Show OTP utility for $environment --" -ForegroundColor Green
    if ($upn.Length -le 1) {Do { $upn = read-host $input_text} while ($upn -eq "")}

    Try {
        [System.Reflection.Assembly]::LoadFile($dllpath) | Out-Null 
    
        if ([SecureMFA_SupportTools.OTP]::isUserExist($upn, $sqlConnectString, [ref] $UserStatus)) 
            {
        
            #Show user OTP time drift
            if (($otpcode) -and ($otpcode -ne $null)) 
                {
                [string] $export_code = $null
                [DateTime]$time_dynamic = Get-Date -Date "1970-01-01 00:00:00Z";
                [TimeSpan]$time_drift = new-timespan -hour 0 -minute 0 ;
                $isSuccess = [SecureMFA_SupportTools.OTP]::GetOTPTime($upn, $otpcode, $sqlConnectString,$DecryptSecret, $encryption_passphrase, $env:username, $env:computername, [ref]$export_code,[ref]$time_dynamic ,[ref]$time_drift)
                
                If ($isSuccess) 
                    {
                    write-host "OTP code is validated against 4 hours past / feature intervals." -ForegroundColor yellow
                    write-host ".......User: $upn" -ForegroundColor Cyan
                    write-host "...OTP Code: $otpcode" -ForegroundColor Cyan
                    write-host "...OTP Time: $time_dynamic" -ForegroundColor Cyan
                    write-host "System Time:" (Get-Date) -ForegroundColor Cyan
                    write-host ".Time Drift: $time_drift" -ForegroundColor Cyan
                    }
                else
                    {
                    write-host "OTP code is validated against 4 hours past / feature intervals." -ForegroundColor yellow
                    write-host "Cannot find OTP code [$otpcode] time value for the user: [$upn] "
                    }
                }
            #Show use OTP values with 10 feature and past time intervals using 30 seconds index.
            else
                {
                $CodeList = [SecureMFA_SupportTools.OTP]::ListOTPCodes($upn, $sqlConnectString,$DecryptSecret,$encryption_passphrase,$env:username,$env:computername)
            
                $CodeList | %     {
                if ($_.item3 -eq 0){write-host ("{0:D2}" -f $_.item1) $_.item2 "PAST____OTP_CODE:" $_.item4 -ForegroundColor Cyan}
                elseif ($_.item3 -eq 1){write-host ("{0:D2}" -f $_.item1) $_.item2 "CURRENT_OTP_CODE:" $_.item4 -ForegroundColor Green}
                else {write-host ("{0:D2}" -f $_.item1) $_.item2 "FUTURE__OTP_CODE:" $_.item4 -ForegroundColor Yellow}}  
                } 
             
            } 
        
        else 
            {
             write-host "User: [$upn] doesn’t exist in OTP database. "
            }
        }

    #On error acction
    catch [System.Exception] { 
            $completed = get-date
            $line = $_.InvocationInfo.ScriptLineNumber
            $msg = $_.Exception.Message 

            Write-Host -ForegroundColor Red "Error: $msg"
            Write-EventLog –LogName Application –Source $Event_Source –EntryType Error –EventID 5559 –Message “$msg Executed by: $env:username Computer: $env:computername Line: $line”                 
            }  
    }
}