Private/Config/Get-PukModuleConfig.ps1
|
function Get-PukModuleConfig { [CmdletBinding()] param( [string]$Path, [switch]$Force ) if (-not $Path) { if ($script:ModuleRoot) { $Path = Join-Path -Path $script:ModuleRoot -ChildPath 'Config/config.json' } else { $Path = Join-Path -Path (Split-Path -Path (Split-Path -Path $PSScriptRoot -Parent) -Parent) -ChildPath 'Config/config.json' } } if (-not $Force -and $script:PukModuleConfig -and $script:PukModuleConfigPath -eq $Path) { return $script:PukModuleConfig } if (-not (Test-Path -LiteralPath $Path)) { throw "PWSHPUKMGT configuration file not found at '$Path'." } $raw = Get-Content -LiteralPath $Path -Raw -ErrorAction Stop try { $config = $raw | ConvertFrom-Json -ErrorAction Stop } catch { throw "PWSHPUKMGT configuration file at '$Path' is not valid JSON: $($_.Exception.Message)" } function Test-PukPositiveInteger { param($Value) if ($null -eq $Value) { return $false } if ($Value -isnot [int] -and $Value -isnot [long] -and $Value -isnot [double]) { return $false } return ($Value -eq [Math]::Truncate($Value)) -and $Value -gt 0 } $validationErrors = [System.Collections.Generic.List[string]]::new() if ([string]::IsNullOrWhiteSpace($config.schemaVersion)) { $validationErrors.Add("'schemaVersion' is required.") } if ([string]::IsNullOrWhiteSpace($config.certificate.thumbprint)) { $validationErrors.Add("'certificate.thumbprint' is required.") } elseif ($config.certificate.thumbprint -notmatch '^[0-9A-Fa-f]{40}$') { $validationErrors.Add("'certificate.thumbprint' must be a 40-character hexadecimal SHA-1 thumbprint.") } if ($config.certificate.storeLocation -notin @('CurrentUser', 'LocalMachine')) { $validationErrors.Add("'certificate.storeLocation' must be 'CurrentUser' or 'LocalMachine'.") } $requiredEkuOids = @($config.certificate.requiredEkuOids) if ($requiredEkuOids.Count -eq 0) { $validationErrors.Add("'certificate.requiredEkuOids' must contain at least one OID.") } else { foreach ($oid in $requiredEkuOids) { if ($oid -notmatch '^\d+(\.\d+)+$') { $validationErrors.Add("'certificate.requiredEkuOids' contains an invalid OID: '$oid'.") } } } if ($config.hosting.model -notin @('FlatFile', 'ActiveDirectory')) { $validationErrors.Add("'hosting.model' must be 'FlatFile' or 'ActiveDirectory'.") } if ([string]::IsNullOrWhiteSpace($config.hosting.flatFile.path)) { $validationErrors.Add("'hosting.flatFile.path' is required.") } if ([string]::IsNullOrWhiteSpace($config.hosting.activeDirectory.attributeName)) { $validationErrors.Add("'hosting.activeDirectory.attributeName' is required.") } if (-not (Test-PukPositiveInteger -Value $config.hosting.activeDirectory.maxAttributeValueLength)) { $validationErrors.Add("'hosting.activeDirectory.maxAttributeValueLength' must be a positive integer.") } if ($config.puk.characterSet -ne 'Digits') { $validationErrors.Add("'puk.characterSet' must be 'Digits' (no other character set is currently implemented).") } foreach ($lengthField in @('minLength', 'maxLength', 'defaultLength')) { if (-not (Test-PukPositiveInteger -Value $config.puk.$lengthField)) { $validationErrors.Add("'puk.$lengthField' must be a positive integer.") } } if ($validationErrors.Count -eq 0) { if ($config.puk.minLength -gt $config.puk.defaultLength -or $config.puk.defaultLength -gt $config.puk.maxLength) { $validationErrors.Add("'puk.minLength' <= 'puk.defaultLength' <= 'puk.maxLength' must hold.") } } if (-not (Test-PukPositiveInteger -Value $config.puk.maxGenerationAttempts)) { $validationErrors.Add("'puk.maxGenerationAttempts' must be a positive integer.") } $weaknessRules = $config.puk.weaknessRules if (-not $weaknessRules) { $validationErrors.Add("'puk.weaknessRules' is required.") } else { foreach ($ruleName in @('rejectSequentialRun', 'rejectRepeatedDigitRun', 'rejectLowUniqueDigitCount', 'rejectRepeatingPattern', 'rejectPalindrome', 'rejectDatePattern')) { $rule = $weaknessRules.$ruleName if (-not $rule -or -not ($rule.enabled -is [bool])) { $validationErrors.Add("'puk.weaknessRules.$ruleName.enabled' must be a boolean.") } } if ($weaknessRules.rejectSequentialRun.enabled -and (-not (Test-PukPositiveInteger -Value $weaknessRules.rejectSequentialRun.runLength) -or $weaknessRules.rejectSequentialRun.runLength -le 1)) { $validationErrors.Add("'puk.weaknessRules.rejectSequentialRun.runLength' must be an integer greater than 1 when enabled.") } if ($weaknessRules.rejectRepeatedDigitRun.enabled -and (-not (Test-PukPositiveInteger -Value $weaknessRules.rejectRepeatedDigitRun.runLength) -or $weaknessRules.rejectRepeatedDigitRun.runLength -le 1)) { $validationErrors.Add("'puk.weaknessRules.rejectRepeatedDigitRun.runLength' must be an integer greater than 1 when enabled.") } if ($weaknessRules.rejectLowUniqueDigitCount.enabled -and -not (Test-PukPositiveInteger -Value $weaknessRules.rejectLowUniqueDigitCount.minUniqueDigits)) { $validationErrors.Add("'puk.weaknessRules.rejectLowUniqueDigitCount.minUniqueDigits' must be a positive integer when enabled.") } if ($null -ne $weaknessRules.customDenyList) { foreach ($entry in @($weaknessRules.customDenyList)) { if ($entry -isnot [string]) { $validationErrors.Add("'puk.weaknessRules.customDenyList' must contain only strings.") break } } } } if ($config.logging.target -notin @('File', 'EventLog', 'Both')) { $validationErrors.Add("'logging.target' must be one of: File, EventLog, Both.") } if ($config.logging.level -notin @('Debug', 'Info', 'Warning', 'Error')) { $validationErrors.Add("'logging.level' must be one of: Debug, Info, Warning, Error.") } if ($config.logging.target -in @('File', 'Both') -and [string]::IsNullOrWhiteSpace($config.logging.file.path)) { $validationErrors.Add("'logging.file.path' is required when 'logging.target' is 'File' or 'Both'.") } if ($config.logging.target -in @('EventLog', 'Both')) { if ([string]::IsNullOrWhiteSpace($config.logging.eventLog.logName)) { $validationErrors.Add("'logging.eventLog.logName' is required when 'logging.target' is 'EventLog' or 'Both'.") } if ([string]::IsNullOrWhiteSpace($config.logging.eventLog.source)) { $validationErrors.Add("'logging.eventLog.source' is required when 'logging.target' is 'EventLog' or 'Both'.") } if (-not (Test-PukPositiveInteger -Value $config.logging.eventLog.eventId)) { $validationErrors.Add("'logging.eventLog.eventId' must be a positive integer when 'logging.target' is 'EventLog' or 'Both'.") } } if ($validationErrors.Count -gt 0) { throw "PWSHPUKMGT configuration at '$Path' is invalid:`n - $($validationErrors -join "`n - ")" } $config.hosting.flatFile.path = [System.Environment]::ExpandEnvironmentVariables($config.hosting.flatFile.path) if ($config.logging.file.path) { $config.logging.file.path = [System.Environment]::ExpandEnvironmentVariables($config.logging.file.path) } $script:PukModuleConfig = $config $script:PukModuleConfigPath = $Path return $config } |