Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 19 additions & 15 deletions src/IdLE.Core/Private/Assert-IdleConditionPathsResolvable.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -152,22 +152,26 @@ function Assert-IdleConditionPathsResolvable {
}
}

if ($softMissingContextPaths.Count -gt 0 -and $null -ne $WarningSink) {
$warningItem = [ordered]@{
Code = 'PreconditionContextPathUnresolvedAtPlan'
Type = 'Warning'
Step = $StepName
Source = $Source
Paths = @($softMissingContextPaths | Select-Object -Unique)
Message = ("Workflow step '{0}' references Request.Context path(s) in {1} that are not yet available at planning time: [{2}]. Evaluation will continue and paths may be resolved at runtime." -f $StepName, $Source, ([string]::Join(', ', @($softMissingContextPaths | Select-Object -Unique))))
}
if ($softMissingContextPaths.Count -gt 0) {
$uniqueSoftPaths = @($softMissingContextPaths | Select-Object -Unique)
$warningMessage = "Workflow step '{0}' references Request.Context path(s) in {1} that are not yet available at planning time: [{2}]. Evaluation will continue and paths may be resolved at runtime." -f $StepName, $Source, ([string]::Join(', ', $uniqueSoftPaths))

# Emit a visible PowerShell warning for immediate host feedback during planning.
Write-Warning $warningMessage

if ($null -ne $WarningSink) {
$warningItem = [ordered]@{
Code = 'PreconditionContextPathUnresolvedAtPlan'
Type = 'Warning'
Step = $StepName
Source = $Source
Paths = $uniqueSoftPaths
Message = $warningMessage
}

if ($WarningSink -is [System.Collections.IList]) {
$null = $WarningSink.Add($warningItem)
}
elseif ($WarningSink -is [object[]]) {
# Fallback for fixed arrays: cannot mutate by reference safely.
# Caller should pass an IList (plan.Warnings is an ArrayList) for collection.
if ($WarningSink -is [System.Collections.IList]) {
$null = $WarningSink.Add($warningItem)
}
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/IdLE.Core/Private/Get-IdleReadOnlyCapabilities.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ function Get-IdleCapabilityContextPath {

switch ($Capability) {
'IdLE.Entitlement.List' { return 'Identity.Entitlements' }
'IdLE.Identity.Read' { return 'Identity.Profile' }
'IdLE.Identity.Read' { return 'Identity.Profile' }
default {
throw [System.ArgumentException]::new(
"No predefined context path defined for capability '$Capability'.",
Expand Down
6 changes: 3 additions & 3 deletions src/IdLE.Core/Public/Invoke-IdlePlanObject.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ function Invoke-IdlePlanObject {
# Accept both IDictionary (hashtables) and PSCustomObject-shaped provider registries
if ($null -ne $planProviders) {
$isValidProvider = ($planProviders -is [System.Collections.IDictionary]) -or
($planProviders.PSObject -and $planProviders.PSObject.Properties)
($planProviders.PSObject -and $planProviders.PSObject.Properties)
if ($isValidProvider) {
$effectiveProviders = $planProviders
}
Expand Down Expand Up @@ -359,7 +359,7 @@ function Invoke-IdlePlanObject {
$pcEvt = Get-IdlePropertyValue -Object $step -Name 'PreconditionEvent'
if ($null -ne $pcEvt) {
$pcEvtType = [string](Get-IdlePropertyValue -Object $pcEvt -Name 'Type')
$pcEvtMsg = [string](Get-IdlePropertyValue -Object $pcEvt -Name 'Message')
$pcEvtMsg = [string](Get-IdlePropertyValue -Object $pcEvt -Name 'Message')
$pcEvtData = Get-IdlePropertyValue -Object $pcEvt -Name 'Data'
# PreconditionEvent.Data is validated as a hashtable at planning time and
# stored via Copy-IdleDataObject, so it will be a hashtable (IDictionary) here.
Expand Down Expand Up @@ -659,7 +659,7 @@ function Invoke-IdlePlanObject {
$ofPcEvt = Get-IdlePropertyValue -Object $ofStep -Name 'PreconditionEvent'
if ($null -ne $ofPcEvt) {
$ofPcEvtType = [string](Get-IdlePropertyValue -Object $ofPcEvt -Name 'Type')
$ofPcEvtMsg = [string](Get-IdlePropertyValue -Object $ofPcEvt -Name 'Message')
$ofPcEvtMsg = [string](Get-IdlePropertyValue -Object $ofPcEvt -Name 'Message')
$ofPcEvtData = Get-IdlePropertyValue -Object $ofPcEvt -Name 'Data'
$ofPcEvtDataHt = if ($ofPcEvtData -is [System.Collections.IDictionary]) { [hashtable]$ofPcEvtData } else { $null }
$context.EventSink.WriteEvent($ofPcEvtType, $ofPcEvtMsg, $ofName, $ofPcEvtDataHt)
Expand Down
8 changes: 4 additions & 4 deletions src/IdLE.Core/Public/New-IdleAuthSessionBroker.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -370,10 +370,10 @@ function New-IdleAuthSessionBroker {
# If multiple matches, this is ambiguous - fail with clear error
if ($matchingEntries.Count -gt 1) {
$matchDetails = ($matchingEntries | ForEach-Object {
$currentEntry = $_
$keyStr = ($currentEntry.Key.Keys | ForEach-Object { "$_=$($currentEntry.Key[$_])" }) -join ', '
"{ $keyStr }"
}) -join '; '
$currentEntry = $_
$keyStr = ($currentEntry.Key.Keys | ForEach-Object { "$_=$($currentEntry.Key[$_])" }) -join ', '
"{ $keyStr }"
}) -join '; '
throw "Ambiguous auth session match for Name='$Name'. Multiple entries matched: $matchDetails. Provide AuthSessionOptions to disambiguate."
}

Expand Down
4 changes: 2 additions & 2 deletions src/IdLE.Core/Public/New-IdleRequestObject.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -100,8 +100,8 @@ function New-IdleRequestObject {
# Clone hashtables to avoid external mutation after object creation
# shallow clone is sufficient as we have already validated no ScriptBlocks are present
$IdentityKeys = if ($null -eq $IdentityKeys) { @{} } else { $IdentityKeys.Clone() }
$Intent = if ($null -eq $Intent) { @{} } else { $Intent.Clone() }
$Context = if ($null -eq $Context) { @{} } else { $Context.Clone() }
$Intent = if ($null -eq $Intent) { @{} } else { $Intent.Clone() }
$Context = if ($null -eq $Context) { @{} } else { $Context.Clone() }

# Construct and return the core domain object defined in Private/IdleLifecycleRequest.ps1
return [IdleLifecycleRequest]::new(
Expand Down
96 changes: 48 additions & 48 deletions src/IdLE.Provider.AD/Private/Get-IdleADAttributeContract.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -46,88 +46,88 @@ function Get-IdleADAttributeContract {
if ($Operation -eq 'CreateIdentity') {
$contract = @{
# Identity Attributes
SamAccountName = @{ Target = 'Parameter'; Type = 'String'; Required = $false }
UserPrincipalName = @{ Target = 'Parameter'; Type = 'String'; Required = $false }
Path = @{ Target = 'Parameter'; Type = 'String'; Required = $false }
SamAccountName = @{ Target = 'Parameter'; Type = 'String'; Required = $false }
UserPrincipalName = @{ Target = 'Parameter'; Type = 'String'; Required = $false }
Path = @{ Target = 'Parameter'; Type = 'String'; Required = $false }

# Name Attributes
Name = @{ Target = 'Parameter'; Type = 'String'; Required = $false }
GivenName = @{ Target = 'Parameter'; Type = 'String'; Required = $false }
Surname = @{ Target = 'Parameter'; Type = 'String'; Required = $false }
DisplayName = @{ Target = 'Parameter'; Type = 'String'; Required = $false }
Name = @{ Target = 'Parameter'; Type = 'String'; Required = $false }
GivenName = @{ Target = 'Parameter'; Type = 'String'; Required = $false }
Surname = @{ Target = 'Parameter'; Type = 'String'; Required = $false }
DisplayName = @{ Target = 'Parameter'; Type = 'String'; Required = $false }

# Organizational Attributes
Description = @{ Target = 'Parameter'; Type = 'String'; Required = $false }
Department = @{ Target = 'Parameter'; Type = 'String'; Required = $false }
Title = @{ Target = 'Parameter'; Type = 'String'; Required = $false }
Description = @{ Target = 'Parameter'; Type = 'String'; Required = $false }
Department = @{ Target = 'Parameter'; Type = 'String'; Required = $false }
Title = @{ Target = 'Parameter'; Type = 'String'; Required = $false }

# Contact Attributes
EmailAddress = @{ Target = 'Parameter'; Type = 'String'; Required = $false }
EmailAddress = @{ Target = 'Parameter'; Type = 'String'; Required = $false }

# Relationship Attributes
Manager = @{ Target = 'Parameter'; Type = 'String'; Required = $false }
Manager = @{ Target = 'Parameter'; Type = 'String'; Required = $false }

# Password Attributes
AccountPassword = @{ Target = 'Parameter'; Type = 'SecureString|String'; Required = $false }
AccountPasswordAsPlainText = @{ Target = 'Parameter'; Type = 'String'; Required = $false }
ResetOnFirstLogin = @{ Target = 'Parameter'; Type = 'Boolean'; Required = $false }
AllowPlainTextPasswordOutput = @{ Target = 'Parameter'; Type = 'Boolean'; Required = $false }
AccountPasswordAsPlainText = @{ Target = 'Parameter'; Type = 'String'; Required = $false }
ResetOnFirstLogin = @{ Target = 'Parameter'; Type = 'Boolean'; Required = $false }
AllowPlainTextPasswordOutput = @{ Target = 'Parameter'; Type = 'Boolean'; Required = $false }

# State Attributes
Enabled = @{ Target = 'Parameter'; Type = 'Boolean'; Required = $false }
Enabled = @{ Target = 'Parameter'; Type = 'Boolean'; Required = $false }

# Extension Container (keys must be valid LDAP attribute names)
OtherAttributes = @{ Target = 'Container'; Type = 'Hashtable'; Required = $false }
OtherAttributes = @{ Target = 'Container'; Type = 'Hashtable'; Required = $false }
}
}
elseif ($Operation -eq 'EnsureAttributes') {
$contract = @{
# Name Attributes
GivenName = @{ Target = 'Parameter'; Type = 'String'; Required = $false }
Surname = @{ Target = 'Parameter'; Type = 'String'; Required = $false }
DisplayName = @{ Target = 'Parameter'; Type = 'String'; Required = $false }
Initials = @{ Target = 'Parameter'; Type = 'String'; Required = $false }
GivenName = @{ Target = 'Parameter'; Type = 'String'; Required = $false }
Surname = @{ Target = 'Parameter'; Type = 'String'; Required = $false }
DisplayName = @{ Target = 'Parameter'; Type = 'String'; Required = $false }
Initials = @{ Target = 'Parameter'; Type = 'String'; Required = $false }

# Identity Attributes
SamAccountName = @{ Target = 'Parameter'; Type = 'String'; Required = $false }
UserPrincipalName = @{ Target = 'Parameter'; Type = 'String'; Required = $false }
SamAccountName = @{ Target = 'Parameter'; Type = 'String'; Required = $false }
UserPrincipalName = @{ Target = 'Parameter'; Type = 'String'; Required = $false }

# Organizational Attributes
Description = @{ Target = 'Parameter'; Type = 'String'; Required = $false }
Department = @{ Target = 'Parameter'; Type = 'String'; Required = $false }
Title = @{ Target = 'Parameter'; Type = 'String'; Required = $false }
Company = @{ Target = 'Parameter'; Type = 'String'; Required = $false }
Division = @{ Target = 'Parameter'; Type = 'String'; Required = $false }
Office = @{ Target = 'Parameter'; Type = 'String'; Required = $false }
EmployeeID = @{ Target = 'Parameter'; Type = 'String'; Required = $false }
EmployeeNumber = @{ Target = 'Parameter'; Type = 'String'; Required = $false }
Description = @{ Target = 'Parameter'; Type = 'String'; Required = $false }
Department = @{ Target = 'Parameter'; Type = 'String'; Required = $false }
Title = @{ Target = 'Parameter'; Type = 'String'; Required = $false }
Company = @{ Target = 'Parameter'; Type = 'String'; Required = $false }
Division = @{ Target = 'Parameter'; Type = 'String'; Required = $false }
Office = @{ Target = 'Parameter'; Type = 'String'; Required = $false }
EmployeeID = @{ Target = 'Parameter'; Type = 'String'; Required = $false }
EmployeeNumber = @{ Target = 'Parameter'; Type = 'String'; Required = $false }

# Contact Attributes
EmailAddress = @{ Target = 'Parameter'; Type = 'String'; Required = $false }
OfficePhone = @{ Target = 'Parameter'; Type = 'String'; Required = $false }
MobilePhone = @{ Target = 'Parameter'; Type = 'String'; Required = $false }
HomePhone = @{ Target = 'Parameter'; Type = 'String'; Required = $false }
Fax = @{ Target = 'Parameter'; Type = 'String'; Required = $false }
EmailAddress = @{ Target = 'Parameter'; Type = 'String'; Required = $false }
OfficePhone = @{ Target = 'Parameter'; Type = 'String'; Required = $false }
MobilePhone = @{ Target = 'Parameter'; Type = 'String'; Required = $false }
HomePhone = @{ Target = 'Parameter'; Type = 'String'; Required = $false }
Fax = @{ Target = 'Parameter'; Type = 'String'; Required = $false }

# Address Attributes
StreetAddress = @{ Target = 'Parameter'; Type = 'String'; Required = $false }
City = @{ Target = 'Parameter'; Type = 'String'; Required = $false }
State = @{ Target = 'Parameter'; Type = 'String'; Required = $false }
PostalCode = @{ Target = 'Parameter'; Type = 'String'; Required = $false }
Country = @{ Target = 'Parameter'; Type = 'String'; Required = $false }
POBox = @{ Target = 'Parameter'; Type = 'String'; Required = $false }
StreetAddress = @{ Target = 'Parameter'; Type = 'String'; Required = $false }
City = @{ Target = 'Parameter'; Type = 'String'; Required = $false }
State = @{ Target = 'Parameter'; Type = 'String'; Required = $false }
PostalCode = @{ Target = 'Parameter'; Type = 'String'; Required = $false }
Country = @{ Target = 'Parameter'; Type = 'String'; Required = $false }
POBox = @{ Target = 'Parameter'; Type = 'String'; Required = $false }

# Web / Profile Attributes
HomePage = @{ Target = 'Parameter'; Type = 'String'; Required = $false }
HomePage = @{ Target = 'Parameter'; Type = 'String'; Required = $false }

# Relationship Attributes
Manager = @{ Target = 'Parameter'; Type = 'String'; Required = $false }
Manager = @{ Target = 'Parameter'; Type = 'String'; Required = $false }

# Account / Profile Path Attributes
HomeDirectory = @{ Target = 'Parameter'; Type = 'String'; Required = $false }
HomeDrive = @{ Target = 'Parameter'; Type = 'String'; Required = $false }
ProfilePath = @{ Target = 'Parameter'; Type = 'String'; Required = $false }
ScriptPath = @{ Target = 'Parameter'; Type = 'String'; Required = $false }
HomeDirectory = @{ Target = 'Parameter'; Type = 'String'; Required = $false }
HomeDrive = @{ Target = 'Parameter'; Type = 'String'; Required = $false }
ProfilePath = @{ Target = 'Parameter'; Type = 'String'; Required = $false }
ScriptPath = @{ Target = 'Parameter'; Type = 'String'; Required = $false }

# Extension Container (keys must be valid LDAP attribute names, e.g. 'mobile', 'telephoneNumber')
OtherAttributes = @{ Target = 'Container'; Type = 'Hashtable'; Required = $false }
Expand Down
2 changes: 1 addition & 1 deletion src/IdLE.Provider.AD/Private/New-IdleADAdapter.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -331,7 +331,7 @@ function New-IdleADAdapter {
Write-Verbose "AD Provider: Derived CN/RDN Name='$derivedName' from DisplayName"
}
elseif ($effectiveAttributes.ContainsKey('GivenName') -and -not [string]::IsNullOrWhiteSpace($effectiveAttributes['GivenName']) -and
$effectiveAttributes.ContainsKey('Surname') -and -not [string]::IsNullOrWhiteSpace($effectiveAttributes['Surname'])) {
$effectiveAttributes.ContainsKey('Surname') -and -not [string]::IsNullOrWhiteSpace($effectiveAttributes['Surname'])) {
$derivedName = "$($effectiveAttributes['GivenName']) $($effectiveAttributes['Surname'])"
Write-Verbose "AD Provider: Derived CN/RDN Name='$derivedName' from GivenName+Surname"
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
function Normalize-IdleExchangeOnlineAutoReplyMessage {
function Format-IdleExchangeOnlineAutoReplyMessage {
<#
.SYNOPSIS
Normalizes Exchange Online auto-reply messages for stable idempotency comparison.
Formats Exchange Online auto-reply messages for stable idempotency comparison.

.DESCRIPTION
Exchange Online may introduce server-side canonicalization when storing automatic reply messages,
Expand All @@ -26,8 +26,8 @@ function Normalize-IdleExchangeOnlineAutoReplyMessage {
System.String - The normalized message string.

.EXAMPLE
$normalized = Normalize-IdleExchangeOnlineAutoReplyMessage -Message $currentMessage
if ($normalized -eq (Normalize-IdleExchangeOnlineAutoReplyMessage -Message $desiredMessage)) {
$normalized = Format-IdleExchangeOnlineAutoReplyMessage -Message $currentMessage
if ($normalized -eq (Format-IdleExchangeOnlineAutoReplyMessage -Message $desiredMessage)) {
# Messages are functionally equivalent
}

Expand Down
Loading