To receive notifications about scheduled maintenance, please subscribe to the mailing-list gitlab-operations@sympa.ethz.ch. You can subscribe to the mailing-list at https://sympa.ethz.ch

...
 
Commits (3)
......@@ -43,4 +43,72 @@ function Convert-CnToName {
PROCESS {
return ($Cn -split ",")[0] -replace "CN="
}
}
function ParseHttpException {
[CmdLetBinding()]
param(
[Parameter(Position = 0, Mandatory = $true)]
[System.Management.Automation.ErrorRecord]$InputObject
)
# if response is a string, is contains a json object
if ($null -ne $InputObject.Exception.Response) {
# read answer from stream
# > use .ToString() and string comparision instead of -is operator
# > as the -is operator requires a vaild type, which is not the case in PowerShell 6 (type does not exist)
if ($InputObject.Exception.Response.GetType().FullName -eq "System.Net.HttpWebResponse") {
# PowerShell 5
$responseStream = $InputObject.Exception.Response.GetResponseStream()
# create streamreader to read from stream
$streamReader = New-Object System.IO.StreamReader -ArgumentList $responseStream
# read
$responseStream.Seek(0, [System.IO.SeekOrigin]::Begin)
$errResponse = $streamReader.ReadToEnd()
# clean up reader and stream
$streamReader.Dispose()
$responseStream.Dispose()
}
else {
# PowerShell 6+
$errResponse = $InputObject.ErrorDetails.Message
}
# try to parse Response message
if (-not [string]::IsNullOrEmpty($errResponse)) {
try {
$errObject = ConvertFrom-Json $errResponse
$errMessage = ($errObject.level + " -> " + $errObject.message)
}
catch {
$errMessage = $errResponse
}
$newException = New-Object System.Exception -ArgumentList $errMessage, $InputObject.Exception
# response did not contain valid JSON, return original error message ( see below )
return $newException
}
# we did not get any additional info from the error message, just throw the original message
# throw original error
return $InputObject
}
}
function IsPsWindows {
<#
.SYNOPSIS
Used as fallback for the $isWindows variable on PSv5 and before
#>
if (Get-Variable -Name "isWindows" -ErrorAction SilentlyContinue) {
return $isWindows
}
return ($PSVersionTable.PSVersion.Major -le 5)
}
\ No newline at end of file
function Test-IsIAMClientInitialized {
if ($null -eq $script:IAMCreds) {
Initialize-IAMClient
# remove all "normal" output from this cmdlet -> do not display hints
Initialize-IAMClient 6>$null
return $true
}
......@@ -8,6 +9,12 @@ function Test-IsIAMClientInitialized {
}
function SaveCredToCredMan {
<#
.SYNOPSIS
Stores a pscredential in the PwManager
#>
[CmdLetBinding()]
param(
[Parameter(Mandatory = $true)]
......@@ -17,12 +24,8 @@ function SaveCredToCredMan {
[string]$PwIdentifier
)
try {
$PwVault = [Windows.Security.Credentials.PasswordVault, Windows.Security.Credentials, ContentType = WindowsRuntime]::new()
}
catch {
throw "Credential manager is only supported on Windows"
}
$PwVault = InitCredMan -ErrorAction Stop
$PwCred = New-Object Windows.Security.Credentials.PasswordCredential -ArgumentList ($PwIdentifier, $Credential.UserName, $Credential.GetNetworkCredential().Password)
$PwVault.Add($PwCred)
......@@ -31,18 +34,19 @@ function SaveCredToCredMan {
}
function RetreiveCredFromCredMan {
<#
.SYNOPSIS
Retrieves a password stored in the windows credential manager
#>
[CmdLetBinding()]
param(
[Parameter(Mandatory = $true)]
[string]$PwIdentifier
)
try {
$PwVault = [Windows.Security.Credentials.PasswordVault, Windows.Security.Credentials, ContentType = WindowsRuntime]::new()
}
catch {
throw "Credential manager is only supported on Windows"
}
$PwVault = InitCredMan -ErrorAction Stop
$PwCreds = $PwVault.FindAllByResource($PwIdentifier)
if ($PwCreds.Count -ne 1) {
......@@ -54,4 +58,47 @@ function RetreiveCredFromCredMan {
return [pscredential]::new($PwCreds[0].UserName, (ConvertTo-SecureString $PwCreds[0].Password -AsPlainText -Force))
}
function IsPwStoredInCredMan {
<#
.SYNOPSIS
Checks if a password is stored in the windows credential manager
#>
[CmdLetBinding()]
param(
[Parameter(Mandatory = $true)]
[string]$PwIdentifier
)
$PwVault = InitCredMan -ErrorAction Stop
try {
return @($PwVault.FindAllByResource($PwIdentifier)).Count -ge 1
}
catch {
return $false
}
}
function InitCredMan {
<#
.SYNOPSIS
Initializes the Credential Manager and throws an error when is does not exist
#>
[CmdLetBinding()]
param ()
try {
return [Windows.Security.Credentials.PasswordVault, Windows.Security.Credentials, ContentType = WindowsRuntime]::new()
}
catch {
throw [System.NotSupportedException]::new("Credential manager is only supported on Windows", $_.Exception)
}
}
\ No newline at end of file
......@@ -77,50 +77,9 @@ Function Invoke-IAMMethod {
}
}
catch {
# if response is a string, is contains a json object
if ($null -ne $_.Exception.Response) {
# read answer from stream
if ($_.Exception.Response -is [System.Net.HttpWebResponse]) {
# PowerShell 5
$responseStream = $_.Exception.Response.GetResponseStream()
}
else {
# PowerShell 6+
$responseStream = $_.Exception.Response.Content.ReadAsStreamAsync().Result
}
# create streamreader to read from stream
$streamReader = new-object System.IO.StreamReader -ArgumentList $responseStream
# read
$responseStream.Seek(0, [System.IO.SeekOrigin]::Begin)
$errResponse = $streamReader.ReadToEnd()
# parse the error message and throw the output
throw (ParseHttpException -InputObject $_)
# clean up reader and stream
$streamReader.Dispose()
$responseStream.Dispose()
# try to parse Response message
if ($errResponse -ne "") {
$Exception = $_.Exception
try {
$errObject = ConvertFrom-Json $errResponse
Write-Error -Exception $Exception -Message ($errObject.level + " -> " + $errObject.message)
return
}
catch {
Write-Error -Exception $Exception -Message $errResponse
# response did not contain valid JSON, return original error message ( see below )
throw;
}
}
}
# we did not get any additional info from the error message, just throw the original message
# throw original error
throw $_
}
}
}
......
$PWIDENTIFIER = "ETH.PS.IAMClient.APIUSER"
function Initialize-IAMClient {
<#
.SYNOPSIS
......@@ -53,15 +51,24 @@ function Initialize-IAMClient {
# for debug purposes
[switch]$EnableDebugOutput,
[Parameter(Mandatory = $false)]
[ValidateScript({[Uri]::IsWellFormedUriString($_, [UriKind]::Absolute)})]
[string]$ApiHost = "https://iam.passwort.ethz.ch/iam-ws-legacy/"
)
if ($null -eq $Credential) {
$Credential = RetreiveCredFromCredMan -PwIdentifier $PWIDENTIFIER
if ((IsPsWindows) -and (IsPwStoredInCredMan -PwIdentifier $ApiHost)){
$Credential = RetreiveCredFromCredMan -PwIdentifier $ApiHost
} else {
$Credential = Get-Credential -Message "Enter your credentials for IAM"
}
}
# store the API host for the module to use
$script:ApiHost = $ApiHost
# message about force not being used.
# TODO Remove in Version 2.0
if ($Force -eq $true) {
Write-Warning "The -Force switch is included for backwards compatibility only, it has no functionality"
}
......@@ -75,17 +82,27 @@ function Initialize-IAMClient {
$VerbosePreference = "SilentlyContinue"
}
# test credentials and fail if they were entered wrong
if (-not (Test-ETHCredentials $Credential)) {
$script:IAMCreds = $null
throw "Could not validate your credentials"
}
if ($SaveCred) {
SaveCredToCredMan -Credential $Credential -PwIdentifier $PWIDENTIFIER
if ($SaveCredential) {
# save credential to credman
SaveCredToCredMan -Credential $Credential -PwIdentifier $ApiHost
}
elseif ((IsPsWindows) -and -not (IsPwStoredInCredMan -PwIdentifier $ApiHost)) {
# hints about new functionality
Write-Host -f Cyan "HINT: Use the -SaveCredential switch to save your password to the Windows Credential Manager!"
Write-Host -f Cyan "HINT: The password will then be stored secure and permanent!"
Write-Host -f Cyan "Example: PS> Initialize-IAMClient -SaveCredential"
}
# save credentials in module
$script:IAMCreds = $Credential
Set-StrictMode -Version latest
}