Release v2.2.1: DRY refactoring and housekeeping
Consolidated duplicated helpers into Elysium.Common.ps1: - Settings parsing (Read-KeyValueSettingsFile, Read-ElysiumSettings, Get-SettingsValue) - Azure Blob URI builder (Build-BlobUri) - S3 SigV4 signing helpers and AWS module bootstrap - AD credential validation and replication permission pre-check - Parallel execution helper (Get-FunctionDefinitionText) Test-WeakADPasswords.ps1 and Extract-NTHashes.ps1 now import Elysium.Common.ps1 for the first time. Update-KHDB.ps1 and Prepare-KHDBStorage.ps1 removed their local duplicates. Deleted legacy Settings.ps1 (superseded by ElysiumSettings.txt). Removed stray placeholder comment in Elysium.ps1. All versions bumped to unified v2.2.1.
This commit is contained in:
+1
-155
@@ -7,7 +7,7 @@
|
||||
##################################################
|
||||
## Project: Elysium ##
|
||||
## File: Prepare-KHDBStorage.ps1 ##
|
||||
## Version: 2.2.0 ##
|
||||
## Version: 2.2.1 ##
|
||||
## Support: support@cqre.net ##
|
||||
##################################################
|
||||
|
||||
@@ -100,44 +100,6 @@ function Remove-DirectoryContents {
|
||||
}
|
||||
}
|
||||
|
||||
function Read-KeyValueSettingsFile {
|
||||
param([string]$Path)
|
||||
$result = @{}
|
||||
if (-not (Test-Path -LiteralPath $Path)) { return $result }
|
||||
foreach ($line in (Get-Content -LiteralPath $Path)) {
|
||||
if ($null -eq $line) { continue }
|
||||
$trimmed = $line.Trim()
|
||||
if (-not $trimmed) { continue }
|
||||
if ($trimmed.StartsWith('#')) { continue }
|
||||
$kv = $line -split '=', 2
|
||||
if ($kv.Count -ne 2) { continue }
|
||||
$key = $kv[0].Trim()
|
||||
$value = $kv[1].Trim()
|
||||
if (-not $key) { continue }
|
||||
if ($value.StartsWith("'") -and $value.EndsWith("'") -and $value.Length -ge 2) {
|
||||
$value = $value.Substring(1, $value.Length - 2)
|
||||
}
|
||||
$result[$key] = $value
|
||||
}
|
||||
return $result
|
||||
}
|
||||
|
||||
function Get-SettingsValue {
|
||||
param(
|
||||
[hashtable]$Settings,
|
||||
[string]$Key
|
||||
)
|
||||
if (-not $Settings) { return $null }
|
||||
if ($Settings.ContainsKey($Key)) { return $Settings[$Key] }
|
||||
return $null
|
||||
}
|
||||
|
||||
function Get-FunctionDefinitionText {
|
||||
param([Parameter(Mandatory = $true)][string]$Name)
|
||||
$cmd = Get-Command -Name $Name -CommandType Function -ErrorAction Stop
|
||||
return $cmd.ScriptBlock.Ast.Extent.Text
|
||||
}
|
||||
|
||||
function Merge-ShardsToFile {
|
||||
param(
|
||||
[psobject]$Manifest,
|
||||
@@ -176,27 +138,6 @@ function Get-NormalizedForwardPath {
|
||||
return $PathValue.Replace('\', '/').Trim('/')
|
||||
}
|
||||
|
||||
function Build-BlobUri {
|
||||
param(
|
||||
[string]$Account,
|
||||
[string]$Container,
|
||||
[string]$Sas,
|
||||
[string]$BlobName
|
||||
)
|
||||
|
||||
if ([string]::IsNullOrWhiteSpace($Account)) { throw 'storageAccountName is missing or empty.' }
|
||||
if ([string]::IsNullOrWhiteSpace($Container)) { throw 'containerName is missing or empty.' }
|
||||
if ([string]::IsNullOrWhiteSpace($Sas)) { throw 'sasToken is missing or empty.' }
|
||||
if ([string]::IsNullOrWhiteSpace($BlobName)) { throw 'BlobName cannot be empty.' }
|
||||
|
||||
$sas = $Sas.Trim()
|
||||
if (-not $sas.StartsWith('?')) { $sas = '?' + $sas }
|
||||
$normalizedBlob = $BlobName.Replace('\', '/').TrimStart('/')
|
||||
$builder = [System.UriBuilder]::new("https://$Account.blob.core.windows.net/$Container/$normalizedBlob")
|
||||
$builder.Query = $sas.TrimStart('?')
|
||||
return $builder.Uri.AbsoluteUri
|
||||
}
|
||||
|
||||
function Upload-AzureBlob {
|
||||
param(
|
||||
[string]$Account,
|
||||
@@ -232,88 +173,6 @@ function Upload-AzureBlob {
|
||||
}
|
||||
}
|
||||
|
||||
function Get-Bytes([string]$s) { return [System.Text.Encoding]::UTF8.GetBytes($s) }
|
||||
function Get-HashHex([byte[]]$bytes) {
|
||||
if ($null -eq $bytes) { $bytes = [byte[]]@() }
|
||||
$sha = [System.Security.Cryptography.SHA256]::Create()
|
||||
try {
|
||||
$ms = New-Object System.IO.MemoryStream -ArgumentList (,$bytes)
|
||||
try {
|
||||
$hash = $sha.ComputeHash([System.IO.Stream]$ms)
|
||||
} finally { $ms.Dispose() }
|
||||
return ([BitConverter]::ToString($hash)).Replace('-', '').ToLowerInvariant()
|
||||
} finally { $sha.Dispose() }
|
||||
}
|
||||
function HmacSha256([byte[]]$key, [string]$data) {
|
||||
$h = [System.Security.Cryptography.HMACSHA256]::new($key)
|
||||
try {
|
||||
$b = [System.Text.Encoding]::UTF8.GetBytes($data)
|
||||
$ms = New-Object System.IO.MemoryStream -ArgumentList (,$b)
|
||||
try {
|
||||
return $h.ComputeHash([System.IO.Stream]$ms)
|
||||
} finally { $ms.Dispose() }
|
||||
} finally { $h.Dispose() }
|
||||
}
|
||||
function GetSignatureKey([string]$secret, [string]$dateStamp, [string]$regionName, [string]$serviceName) {
|
||||
$kDate = HmacSha256 (Get-Bytes ('AWS4' + $secret)) $dateStamp
|
||||
$kRegion = HmacSha256 $kDate $regionName
|
||||
$kService = HmacSha256 $kRegion $serviceName
|
||||
HmacSha256 $kService 'aws4_request'
|
||||
}
|
||||
function UriEncode([string]$data, [bool]$encodeSlash) {
|
||||
$enc = [System.Uri]::EscapeDataString($data)
|
||||
if (-not $encodeSlash) { $enc = $enc -replace '%2F', '/' }
|
||||
return $enc
|
||||
}
|
||||
function BuildCanonicalPath([System.Uri]$uri) {
|
||||
$segments = $uri.AbsolutePath.Split('/')
|
||||
$encoded = @()
|
||||
foreach ($s in $segments) { $encoded += (UriEncode $s $false) }
|
||||
$path = ($encoded -join '/')
|
||||
if (-not $path.StartsWith('/')) { $path = '/' + $path }
|
||||
return $path
|
||||
}
|
||||
function ToHex([byte[]]$b) { ([BitConverter]::ToString($b)).Replace('-', '').ToLowerInvariant() }
|
||||
function BuildAuthHeaders($method, [System.Uri]$uri, [string]$region, [string]$accessKey, [string]$secretKey, [string]$payloadHash) {
|
||||
$algorithm = 'AWS4-HMAC-SHA256'
|
||||
$timestamp = (Get-Date).ToUniversalTime()
|
||||
$amzDate = $timestamp.ToString('yyyyMMddTHHmmssZ')
|
||||
$dateStamp = $timestamp.ToString('yyyyMMdd')
|
||||
$hostHeader = $uri.Host
|
||||
if (-not $uri.IsDefaultPort) { $hostHeader = "${hostHeader}:$($uri.Port)" }
|
||||
$canonicalUri = BuildCanonicalPath $uri
|
||||
$canonicalQueryString = ''
|
||||
$canonicalHeaders = "host:$hostHeader`n" + "x-amz-content-sha256:$payloadHash`n" + "x-amz-date:$amzDate`n"
|
||||
$signedHeaders = 'host;x-amz-content-sha256;x-amz-date'
|
||||
$canonicalRequest = "$method`n$canonicalUri`n$canonicalQueryString`n$canonicalHeaders`n$signedHeaders`n$payloadHash"
|
||||
$credentialScope = "$dateStamp/$region/s3/aws4_request"
|
||||
$stringToSign = "$algorithm`n$amzDate`n$credentialScope`n$((Get-HashHex (Get-Bytes $canonicalRequest)))"
|
||||
$signingKey = GetSignatureKey $secretKey $dateStamp $region 's3'
|
||||
$signature = ToHex (HmacSha256 $signingKey $stringToSign)
|
||||
$authHeader = "$algorithm Credential=$accessKey/$credentialScope, SignedHeaders=$signedHeaders, Signature=$signature"
|
||||
@{
|
||||
'x-amz-date' = $amzDate
|
||||
'x-amz-content-sha256' = $payloadHash
|
||||
'Authorization' = $authHeader
|
||||
}
|
||||
}
|
||||
function BuildS3Uri([string]$endpointUrl, [string]$bucket, [string]$key, [bool]$forcePathStyle) {
|
||||
$base = [System.Uri]$endpointUrl
|
||||
$builder = [System.UriBuilder]::new($base)
|
||||
$normalizedKey = $key.Replace('\', '/').TrimStart('/')
|
||||
if ($forcePathStyle) {
|
||||
$path = $builder.Path.TrimEnd('/')
|
||||
if ([string]::IsNullOrEmpty($path)) { $path = '/' }
|
||||
$builder.Path = ($path.TrimEnd('/') + '/' + $bucket + '/' + $normalizedKey)
|
||||
} else {
|
||||
$builder.Host = "$bucket." + $builder.Host
|
||||
$path = $builder.Path.TrimEnd('/')
|
||||
if ([string]::IsNullOrEmpty($path)) { $path = '/' }
|
||||
$builder.Path = ($path.TrimEnd('/') + '/' + $normalizedKey)
|
||||
}
|
||||
return $builder.Uri
|
||||
}
|
||||
|
||||
function Invoke-S3HttpUpload {
|
||||
param(
|
||||
[string]$EndpointUrl,
|
||||
@@ -356,19 +215,6 @@ function Invoke-S3HttpUpload {
|
||||
}
|
||||
}
|
||||
|
||||
function Combine-StoragePath {
|
||||
param(
|
||||
[string]$Prefix,
|
||||
[string]$Name
|
||||
)
|
||||
|
||||
$cleanName = $Name.Replace('\', '/').TrimStart('/')
|
||||
if ([string]::IsNullOrWhiteSpace($Prefix)) { return $cleanName }
|
||||
$normalizedPrefix = $Prefix.Replace('\', '/').Trim('/')
|
||||
if ([string]::IsNullOrEmpty($normalizedPrefix)) { return $cleanName }
|
||||
return "$normalizedPrefix/$cleanName"
|
||||
}
|
||||
|
||||
function Split-KhdbIntoShards {
|
||||
param(
|
||||
[string]$Source,
|
||||
|
||||
Reference in New Issue
Block a user