classes/Credentials.ps1


enum AccountType {
    Empty   = 0
    Vault   = 1
    Domain  = 2
    Windows = 4
    SQL     = 8
    Generic = 16
}

 Class Credential {
<#
 # Abstract Class: Credential
 # Create an Abstract credential class to serve as the interface.
 # Constructor restricts this class being instantiated directly.
 #>


    #Properties
    hidden [pscredential]$_Credential

    [string]$Domain
    [string]$Username
    [securestring]$Password
    [bool]$IsValid

    #region Constructor Methods
    hidden Init() {
        $this | Add-Member -Name "Domain" -MemberType ScriptProperty -Value { return $("LDAP://" + ([ADSI]"").distinguishedName) } -Force
        $this | Add-Member -Name "Username" -MemberType ScriptProperty -Value { return [string]$this._Credential.UserName } -Force
        $this | Add-Member -Name "Password" -MemberType ScriptProperty -Value { return [securestring]$this._Credential.Password } -Force
        $this | Add-Member -Name "IsValid" -MemberType ScriptProperty -Value {
            if ([pscredential]::Empty -eq $this._Credential) {
                return $false
            }
            return [bool]((New-Object DirectoryServices.DirectoryEntry($this.Domain,$this._Credential.UserName,$this._Credential.GetNetworkCredential().Password)).distinguishedName)
        } -Force
    }

    Credential() {
        # $type = $this.GetType()
        # if ($type -eq [Credential]) {
        # throw("Class $type must be inherited")
        # }
        $this.Init()
        $this.Clear()
    }
    Credential([pscredential]$Credential) {
        $this.Init()
        $this.Set((if ($null -eq $Credential) { [pscredential]::Empty } else { $Credential }))
    }
    #endregion Constructor Methods

    #region Class Methods

    hidden [string] GetPassword() {
         return $this._Credential.GetNetworkCredential().Password
    }

    [void]Set() {
        $this._Credential = (Get-Credential -Message "Enter Your Credentials")
    }

    [void]Set([string]$Username) {
        $this._Credential = (Get-Credential -Message "Enter Your Credentials" -UserName $Username)
    }

    [void]Set([pscredential]$Credential) {
        $this._Credential = $Credential

        [bool]$tmpIsValid = $false

        if($null -ne $Credential-and [pscredential]::Empty -ne $Credential) {
            $tmpIsValid = $null -ne ([ADSI]::new($this.Domain, $this.UserName, $this.GetPassword()))

            if($false -eq $tmpIsValid) {
                Write-Warning "Credentials Invalid. Please verify your account username and password.`n`n"
                $this._Credential = [pscredential]::Empty
            } else {
                Write-Verbose "Credential Validated. Creating credential object with username and password"
                $this._Credential = $Credential
            }
        } else {
            Write-Verbose "Credential dialog cancelled or empty value provided. Creating pscredential object with empty value"
            $this._Credential = [pscredential]::Empty
        }

        #Implement Credential testing for different credential types.
    }

    [void] Clear() {
        $this._Credential = [pscredential]::Empty
    }

    #endregion Class Methods

}


class Account : Credential {
<# Class Comments
    .SYNOPSIS
    Represents a checked out password vault type of account
 
    .DESCRIPTION
    When checking out accounts from password vaults this
    class will assist with putting the right properties forward.
 
    .INPUTS
    None. You cannot pipe objects to this class.
 
    .OUTPUTS
    Class object with the methods and interface propwerties to represent a checked out vault account eg. VaultAccount
 
    .EXAMPLE
    C:\PS> extension -name "File"
    File.txt
 
    .EXAMPLE
    C:\PS> extension -name "File" -extension "doc"
    File.doc
 
    .EXAMPLE
    C:\PS> extension "File" "doc"
    File.doc
 
    .LINK
    https://github.com/taalmahret/enterprisecommander
#>


#region Property Declarations

    hidden [string]$_ID
    hidden [string]$_AccountName
    hidden [AccountType]$_AccountType

    [string]$ID
    [string]$AccountName
    [AccountType]$AccountType

#endregion Property Declarations

#region Class Constructors
    hidden [void] Init() {

        Add-Member -InputObject $this -MemberType ScriptProperty -Force -Name 'ID' -Value { return $this._ID }
        Add-Member -InputObject $this -MemberType ScriptProperty -Force -Name 'AccountName' -Value { return $this._AccountName }
        Add-Member -InputObject $this -MemberType ScriptProperty -Force -Name 'AccountType' -Value { return $this._AccountType }
        ([Credential]$this).Init()
    }

    Account() {
        $this.Init()
        $this.Clear()
    }

#endregion Class Constructor

#region Class Methods
    [void] Set() {
        $this._ID = (Read-Host -Prompt 'Input account ID')
        $this._AccountName = (Read-Host -Prompt 'Input account name')
        $this._AccountType = [AccountType]::Generic

        ([Credential]$this).Set()
    }

    [void] Set([string]$AccountID, [string]$AccountName, [AccountType]$AccountType) {

        $this._ID = $AccountID
        $this._AccountName = $AccountName
        $this._AccountType = $AccountType

        switch ($AccountType) {
            ([AccountType]::Domain) {
                ([Credential]$this).Set((get-aduser -Identity $AccountID).SamAccountName)
                break
            }
            ([AccountType]::Vault) {
                ([Credential]$this).Set([string]::Empty)
                break
            }
            Default {
                ([Credential]$this).Set([string]::Empty)
                break
            }
        }

    }

    [void] Set([string]$AccountID, [string]$AccountName, [pscredential]$Credential, [AccountType]$AccountType) {

        $this._ID = $AccountID
        $this._AccountName = $AccountName
        $this._AccountType = $AccountType

        switch ($AccountType) {
            ([AccountType]::Domain) {
                ([Credential]$this).Set($Credential)
                break
            }
            ([AccountType]::Vault) {
                ([Credential]$this).Set($Credential)
                break
            }
            Default {
                ([Credential]$this).Set([string]::Empty)
                break
            }
        }

    }

    [void] Clear() {
        $this._ID = 0
        $this._AccountName = [string]::Empty
        $this._AccountType = [AccountType]::Generic

        ([Credential]$this).Clear()
    }

#endregion Class Methods

}




class VaultAccount : Account {
<#
# VaultAccount Class: implements Account
# Constructor uses base constructor.
# Methods overrides are declared.
#>


    hidden [int]$_VaultID
    hidden [string]$_AccountName
    hidden [bool]$_CheckedOut
    hidden [string]$_CheckedOutEmail
    hidden [int]$_MinutesLeft
    hidden [datetime]$_CheckOutExpireTime

    [bool]$CheckedOut
    [string]$CheckedOutEmail
    [int]$MinutesLeft
    [datetime]$CheckOutExpireTime

#region Class Constructors
    hidden [void] Init() {
        Add-Member -InputObject $this -MemberType ScriptProperty -Force -Name 'CheckedOut' -Value { return $this._CheckedOut }
        Add-Member -InputObject $this -MemberType ScriptProperty -Force -Name 'CheckedOutEmail' -Value { return $this._CheckedOutEmail }
        Add-Member -InputObject $this -MemberType ScriptProperty -Force -Name 'CheckOutExpireTime' -Value { return $this._CheckOutExpireTime }
        Add-Member -InputObject $this -MemberType ScriptProperty -Force -Name 'MinutesLeft' -Value { return NEW-TIMESPAN -Start (GET-DATE) -End ($this._CheckOutExpireTime) }
        ([Account]$this).Init()
    }

    VaultAccount () {
        $this.Init()
        $this.Clear()
    }

#endregion Class Constructor

#region Class Methods
    [void] Set() {

        $this._CheckedOutEmail = (Read-Host -Prompt 'Input email of account that checked this entry out')
        $this._CheckedOut = (Read-Host -Prompt 'Input $true if checked out or $false')
        $this._CheckOutExpireTime = (GET-DATE).AddMinutes((Read-Host -Prompt 'Input minutes left'))

        ([Account]$this).Set()

    }

    [void] Set([int]$ID, [string]$AccountName, [string]$CheckedOutEmail, [bool]$CheckedOut, [int]$MinutesLeft) {
        $this._VaultID = $ID
        $this._AccountName = $AccountName
        $this._CheckedOut = $CheckedOut
        $this._CheckedOutEmail = $CheckedOutEmail
        $this._CheckOutExpireTime = (GET-DATE).AddMinutes($MinutesLeft)

        ([Account]$this).Set($this._VaultID, $this._AccountName, [pscredential]::Empty, [AccountType]::Vault)
    }

    [void] Set([int]$ID, [string]$AccountName, [string]$CheckedOutEmail, [bool]$CheckedOut, [int]$MinutesLeft, [pscredential]$Credential) {
        $this._VaultID = $ID
        $this._AccountName = $AccountName
        $this._CheckedOut = $CheckedOut
        $this._CheckedOutEmail = $CheckedOutEmail
        $this._CheckOutExpireTime = (GET-DATE).AddMinutes($MinutesLeft)

        ([Account]$this).Set($this._VaultID, $this._AccountName, $Credential, [AccountType]::Vault)
    }

    [void] Clear() {

        $this._VaultID = 0
        $this._AccountName = [string]::Empty
        $this._CheckedOut = $false
        $this._CheckedOutEmail = [string]::Empty
        $this._CheckOutExpireTime = [datetime]::FromFileTime(0)

        ([Account]$this).Clear()
    }

    # ToString Method
    [String] ToString() {
        return $this._SamAccountName
    }



#endregion Class Methods




}

class DomainAccount : Account {
<# Class Comments
    # Active Directory Class: implements Account
    # Constructor uses base constructor.
    # Methods overrides are declared.
#>


#region Property Declarations
    hidden [bool]$_Enabled
    hidden [bool]$_LockedOut
    hidden [string[]]$_MemberOf
    hidden [string]$_Description
    hidden [string]$_mail
    hidden [string]$_SamAccountName
    hidden [string]$_EmployeeID
    hidden [long]$_PasswordExpiryTime


    [bool]$Enabled
    [bool]$LockedOut
    [string[]]$MemberOf
    [string]$Description
    [string]$mail
    [string]$SamAccountName
    [int]$EmployeeID
    [datetime]$PasswordExpiryTime
#endregion Property Declarations


#region Class Constructors
    hidden [void] Init() {
        Add-Member -InputObject $this -MemberType ScriptProperty -Force -Name 'Enabled' -Value { return $this._Enabled }
        Add-Member -InputObject $this -MemberType ScriptProperty -Force -Name 'LockedOut' -Value { return $this._LockedOut }
        Add-Member -InputObject $this -MemberType ScriptProperty -Force -Name 'MemberOf' -Value { return $this._MemberOf }
        Add-Member -InputObject $this -MemberType ScriptProperty -Force -Name 'Description' -Value { return $this._Description }
        Add-Member -InputObject $this -MemberType ScriptProperty -Force -Name 'mail' -Value { return $this._mail }
        Add-Member -InputObject $this -MemberType ScriptProperty -Force -Name 'SamAccountName' -Value { return $this._SamAccountName }
        Add-Member -InputObject $this -MemberType ScriptProperty -Force -Name 'EmployeeID' -Value { return $this._EmployeeID }
        Add-Member -InputObject $this -MemberType ScriptProperty -Force -Name 'PasswordExpiryTime' -Value { return [datetime]::FromFileTime($this._PasswordExpiryTime) }
        ([Account]$this).Init()
    }

    DomainAccount () {
        $this.Init()
        $this.Clear()
    }

    DomainAccount ([string]$Username) {
        $this.Init()
        $this.Set($Username)
    }

    DomainAccount ([pscredential]$Credential) {
        $this.Init()
        $this.Set($Credential)
    }
#endregion Class Constructor

#region Class Methods
    [void] Set() {

        $this.Set((Read-Host -Prompt 'Input username of AD Account'))
    }

    [void] Set([string]$Username) {
        #since we are effectively wiping this user entry blank, call base method as well
        $this.Clear()

        $User = $this.GetUserObject($Username)

        $this._Enabled = $User.Enabled
        $this._LockedOut = $User.LockedOut
        $this._MemberOf = $User.MemberOf
        $this._Description = $User.Description
        $this._mail = $User.mail
        $this._SamAccountName = $User.SamAccountName
        $this._EmployeeID = $User.EmployeeID
        $this._PasswordExpiryTime = $($User.'msDS-UserPasswordExpiryTimeComputed')

        #This is frustrating. The account should be a sid but doubles the shortname of what is stored in the objects properties
        #basically im trying to refactor how this should document the "Account Generic" portion of this object
        ([Account]$this).Set($User.SID, $User.name, [AccountType]::Domain)
        #([Credential]$this).Set($User.SamAccountName)
    }

    [void] Set([pscredential]$Credential) {
        #since we are effectively wiping this user entry blank for accounttype domain, call base method as well
        $this.Clear()

        $User = $this.GetUserObject($Credential.Username)

        $this._Enabled = $User.Enabled
        $this._LockedOut = $User.LockedOut
        $this._MemberOf = $User.MemberOf
        $this._Description = $User.Description
        $this._mail = $User.mail
        $this._SamAccountName = $User.SamAccountName
        $this._EmployeeID = $User.EmployeeID
        $this._PasswordExpiryTime = $($User.'msDS-UserPasswordExpiryTimeComputed')

        #This is frustrating. The account ID may be more complicated than other accounttypes
        #I'll research and reapproach
        ([Account]$this).Set($User.SID, $User.name, $Credential, [AccountType]::Domain)
        #([Credential]$this).Set($User.SamAccountName)
    }

    hidden [object] GetUserObject([string]$Username) {
        #Create an empty scaffold so that no matter whether a valid
        #or empty username is given the method gracefully handles this
        $User = {
            Enabled = $false
            LockedOut = $false
            MemberOf = @()
            Description = [string]::Empty
            mail = [string]::Empty
            SamAccountName = [string]::Empty
            EmpoyeeID = 0
            msDS-UserPasswordExpiryTimeComputed = 0
            SID = 0
            Name = [string]::Empty
        }

# if ([pscredential]::Empty -ne $Credential) {
        if ([string]::IsNullOrEmpty($Username) -eq $false) {
           $User = Get-ADUser $UserName -Properties 'msDS-UserPasswordExpiryTimeComputed', MemberOf, mail, Description, EmployeeID
        }
        return $User
    }


    [void] Clear() {

        $this._Enabled = $false
        $this._LockedOut = $false
        $this._MemberOf = [string]::Empty
        $this._Description = [string]::Empty
        $this._mail = [string]::Empty
        $this._SamAccountName = [string]::Empty
        $this._EmployeeID = 0
        $this._PasswordExpiryTime = 0

        ([Account]$this).Clear()
    }

    # ToString Method
    [String] ToString() {
        return $this._SamAccountName
    }


#endregion Class Methods


}

#region Factory Classes

 class AccountFactory {
 <#
  # Factory Class: AccountFactory
  # Instance Methods generate new Accounts.
  # Static Properties/Methods demonstrate Storage/Fetch Concepts.
  #>


  #Store and Fetch
     static [Account[]] $Accounts

     static [Object] getByType([Object] $O)
     {
         return [AccountFactory]::Accounts.Where({$_ -is $O})
     }

     static [Object] getByName([String] $Name)
     {
         return [AccountFactory]::Accounts.Where({$_.Name -eq $Name})
     }

     #Create an instance
     [Account] makeAccount([String] $Name, [String] $Caffeine, [String] $Type)
     {
         return (New-Object -TypeName "$Type" -ArgumentList $Name, $Caffeine)
     }
 }

class License {
    #this is an attempt to combine Credential with Account


    #region Property Declarations
    #endregion Property Declarations

    #region Class Constructors
    hidden Init([pscredential]$Credential, [Account]$Account) {
        $this.Init($Credential, [AccountType]::Empty)
    }
    [Diagnostics.CodeAnalysis.SuppressMessage("PSAvoidUsingPlainTextForPassword",'')]
    hidden Init([Credential]$Credential, [Account]$Account) {
        $this._Credential = $Credential
        $this._AccountType = $Credential.AccountType

        $this | Add-Member -Name "Domain" -MemberType ScriptProperty -Value { return $("LDAP://" + ([ADSI]"").distinguishedName) } -Force
        $this | Add-Member -Name "Username" -MemberType ScriptProperty -Value { return [string]$this._Credential.UserName } -Force
        $this | Add-Member -Name "Password" -MemberType ScriptProperty -Value { return [securestring]$this._Credential.Password } -Force
        $this | Add-Member -Name "AccountType" -MemberType ScriptProperty -Value { return $this._AccountType } -Force
        $this | Add-Member -Name "IsValid" -MemberType ScriptProperty -Value { if ([pscredential]::Empty -eq $this._Credential) { return $false}  return $null -ne (New-Object System.DirectoryServices.DirectoryEntry($this.Domain,$this._Credential.UserName,$this._Credential.GetNetworkCredential().Password)) } -Force
    }
    License() {
        $this.Init([pscredential]::Empty, [AccountType]::Empty)
    }
    License([pscredential]$Credential) {
        $this.Init((if ($null -eq $Credential) { [pscredential]::Empty } else { $Credential }), [AccountType]::Generic )
    }
    License([pscredential]$Credential, [AccountType]$AccountType) {
        $this.Init(
            (if ($null -eq $Credential) { [pscredential]::Empty } else { $Credential }),
            (if ($null -eq $AccountType) { [AccountType]::Empty } else { $AccountType })
        )
    }
    #endregion Class Constructors


    #region Class Methods
    #endregion Class Methods
}

#endregion Factory Classes