Public/New-IdleMockIdentityProvider.ps1
|
function New-IdleMockIdentityProvider { <# .SYNOPSIS Creates an in-memory identity provider for tests and demos. .DESCRIPTION This provider is deterministic and has no external dependencies. It is designed to be used in unit tests, contract tests, and example workflows. The provider keeps all state in a private in-memory store that is scoped to the returned provider object instance (no global state). This makes tests predictable. .PARAMETER InitialStore Optional initial store content. This is useful when a test wants to start with pre-seeded identities. The input is shallow-copied to avoid unintended mutations from the outside. .EXAMPLE $provider = New-IdleMockIdentityProvider $provider.EnsureAttribute('user1', 'Department', 'IT') | Out-Null $provider.GetIdentity('user1') | Format-List .EXAMPLE $provider = New-IdleMockIdentityProvider -InitialStore @{ 'user1' = @{ IdentityKey = 'user1' Enabled = $true Attributes = @{ Department = 'IT' } Entitlements = @( @{ Kind = 'Group'; Id = 'demo-group'; DisplayName = 'Demo Group' } ) } } #> [CmdletBinding()] param( [Parameter()] [hashtable] $InitialStore ) $store = @{} if ($null -ne $InitialStore) { foreach ($key in $InitialStore.Keys) { $store[$key] = $InitialStore[$key] } } $convertToEntitlement = { [CmdletBinding()] param( [Parameter(Mandatory)] [ValidateNotNull()] [object] $Value ) $kind = $null $id = $null $displayName = $null if ($Value -is [System.Collections.IDictionary]) { $kind = $Value['Kind'] $id = $Value['Id'] if ($Value.Contains('DisplayName')) { $displayName = $Value['DisplayName'] } } else { $props = $Value.PSObject.Properties if ($props.Name -contains 'Kind') { $kind = $Value.Kind } if ($props.Name -contains 'Id') { $id = $Value.Id } if ($props.Name -contains 'DisplayName') { $displayName = $Value.DisplayName } } if ([string]::IsNullOrWhiteSpace([string]$kind)) { throw "Entitlement.Kind must not be empty." } if ([string]::IsNullOrWhiteSpace([string]$id)) { throw "Entitlement.Id must not be empty." } return [pscustomobject]@{ PSTypeName = 'IdLE.Entitlement' Kind = [string]$kind Id = [string]$id DisplayName = if ($null -eq $displayName -or [string]::IsNullOrWhiteSpace([string]$displayName)) { $null } else { [string]$displayName } } } $testEntitlementEquals = { [CmdletBinding()] param( [Parameter(Mandatory)] [ValidateNotNull()] [object] $A, [Parameter(Mandatory)] [ValidateNotNull()] [object] $B ) $aEnt = $this.ConvertToEntitlement($A) $bEnt = $this.ConvertToEntitlement($B) if ($aEnt.Kind -ne $bEnt.Kind) { return $false } return [string]::Equals($aEnt.Id, $bEnt.Id, [System.StringComparison]::OrdinalIgnoreCase) } $provider = [pscustomobject]@{ PSTypeName = 'IdLE.Provider.MockIdentityProvider' Name = 'MockIdentityProvider' Store = $store } $provider | Add-Member -MemberType ScriptMethod -Name ConvertToEntitlement -Value $convertToEntitlement -Force $provider | Add-Member -MemberType ScriptMethod -Name TestEntitlementEquals -Value $testEntitlementEquals -Force $provider | Add-Member -MemberType ScriptMethod -Name GetCapabilities -Value { <# .SYNOPSIS Advertises the capabilities provided by this provider instance. .DESCRIPTION Capabilities are stable string identifiers used by IdLE to validate that a workflow plan can be executed with the available providers. This mock provider intentionally advertises only the capabilities that it implements to keep tests deterministic. #> return @( 'IdLE.Identity.Read' 'IdLE.Identity.Attribute.Ensure' 'IdLE.Identity.Disable' 'IdLE.Entitlement.List' 'IdLE.Entitlement.Grant' 'IdLE.Entitlement.Revoke' ) } -Force $provider | Add-Member -MemberType ScriptMethod -Name GetIdentity -Value { param( [Parameter(Mandatory)] [ValidateNotNullOrEmpty()] [string] $IdentityKey ) # Create missing identities on demand to keep tests and demos frictionless. if (-not $this.Store.ContainsKey($IdentityKey)) { $this.Store[$IdentityKey] = @{ IdentityKey = $IdentityKey Enabled = $true Attributes = @{} Entitlements = @() } } $raw = $this.Store[$IdentityKey] if ($null -eq $raw.Entitlements) { $raw.Entitlements = @() } return [pscustomobject]@{ PSTypeName = 'IdLE.Identity' IdentityKey = $raw.IdentityKey Enabled = [bool]$raw.Enabled Attributes = [hashtable]$raw.Attributes } } -Force $provider | Add-Member -MemberType ScriptMethod -Name EnsureAttribute -Value { param( [Parameter(Mandatory)] [ValidateNotNullOrEmpty()] [string] $IdentityKey, [Parameter(Mandatory)] [ValidateNotNullOrEmpty()] [string] $Name, [Parameter()] [AllowNull()] [object] $Value ) if (-not $this.Store.ContainsKey($IdentityKey)) { $this.Store[$IdentityKey] = @{ IdentityKey = $IdentityKey Enabled = $true Attributes = @{} Entitlements = @() } } $identity = $this.Store[$IdentityKey] if ($null -eq $identity.Attributes) { $identity.Attributes = @{} } if ($null -eq $identity.Entitlements) { $identity.Entitlements = @() } $changed = $false if (-not $identity.Attributes.ContainsKey($Name)) { $changed = $true $identity.Attributes[$Name] = $Value } else { $existing = $identity.Attributes[$Name] # Compare loosely because values may come in as different but equivalent types in tests. if ($existing -ne $Value) { $changed = $true $identity.Attributes[$Name] = $Value } } return [pscustomobject]@{ PSTypeName = 'IdLE.ProviderResult' Operation = 'EnsureAttribute' IdentityKey = $IdentityKey Changed = [bool]$changed Name = $Name Value = $Value } } -Force $provider | Add-Member -MemberType ScriptMethod -Name DisableIdentity -Value { param( [Parameter(Mandatory)] [ValidateNotNullOrEmpty()] [string] $IdentityKey ) if (-not $this.Store.ContainsKey($IdentityKey)) { $this.Store[$IdentityKey] = @{ IdentityKey = $IdentityKey Enabled = $true Attributes = @{} Entitlements = @() } } $identity = $this.Store[$IdentityKey] if ($null -eq $identity.Entitlements) { $identity.Entitlements = @() } $changed = $false if ($identity.Enabled -ne $false) { $changed = $true } if ($changed) { $identity.Enabled = $false } return [pscustomobject]@{ PSTypeName = 'IdLE.ProviderResult' Operation = 'DisableIdentity' IdentityKey = $IdentityKey Changed = [bool]$changed } } -Force $provider | Add-Member -MemberType ScriptMethod -Name ListEntitlements -Value { param( [Parameter(Mandatory)] [ValidateNotNullOrEmpty()] [string] $IdentityKey ) if (-not $this.Store.ContainsKey($IdentityKey)) { throw "Identity '$IdentityKey' does not exist in the mock provider store." } $identity = $this.Store[$IdentityKey] if ($null -eq $identity.Entitlements) { $identity.Entitlements = @() } $result = @() foreach ($e in @($identity.Entitlements)) { $normalized = $this.ConvertToEntitlement($e) $result += $normalized } return $result } -Force $provider | Add-Member -MemberType ScriptMethod -Name GrantEntitlement -Value { param( [Parameter(Mandatory)] [ValidateNotNullOrEmpty()] [string] $IdentityKey, [Parameter(Mandatory)] [ValidateNotNull()] [object] $Entitlement ) if (-not $this.Store.ContainsKey($IdentityKey)) { throw "Identity '$IdentityKey' does not exist in the mock provider store." } $normalized = $this.ConvertToEntitlement($Entitlement) $identity = $this.Store[$IdentityKey] if ($null -eq $identity.Entitlements) { $identity.Entitlements = @() } $existing = $identity.Entitlements | Where-Object { $this.TestEntitlementEquals($_, $normalized) } $changed = $false if (@($existing).Count -eq 0) { $identity.Entitlements += $normalized $changed = $true } return [pscustomobject]@{ PSTypeName = 'IdLE.ProviderResult' Operation = 'GrantEntitlement' IdentityKey = $IdentityKey Changed = [bool]$changed Entitlement = $normalized } } -Force $provider | Add-Member -MemberType ScriptMethod -Name RevokeEntitlement -Value { param( [Parameter(Mandatory)] [ValidateNotNullOrEmpty()] [string] $IdentityKey, [Parameter(Mandatory)] [ValidateNotNull()] [object] $Entitlement ) if (-not $this.Store.ContainsKey($IdentityKey)) { throw "Identity '$IdentityKey' does not exist in the mock provider store." } $normalized = $this.ConvertToEntitlement($Entitlement) $identity = $this.Store[$IdentityKey] if ($null -eq $identity.Entitlements) { $identity.Entitlements = @() } $remaining = @() $removed = $false foreach ($item in @($identity.Entitlements)) { if ($this.TestEntitlementEquals($item, $normalized)) { $removed = $true continue } $remaining += $item } $identity.Entitlements = $remaining return [pscustomobject]@{ PSTypeName = 'IdLE.ProviderResult' Operation = 'RevokeEntitlement' IdentityKey = $IdentityKey Changed = [bool]$removed Entitlement = $normalized } } -Force return $provider } |