CBAM, SUMA, RelayHub

This commit is contained in:
2026-10-06 09:24:20 +02:00
parent 7be9708ede
commit 9eb8a1d800
13 changed files with 1786 additions and 382 deletions

View File

@@ -2,208 +2,254 @@
Imports System.IO
Imports System.Net.Http
Imports System.Text
Imports System.Web
Imports System.Windows.Forms.VisualStyles.VisualStyleElement.StartPanel
Imports Newtonsoft.Json
' NuGet: Newtonsoft.Json (>= 13.x)
Imports sun.security.util
Imports VERAG_PROG_ALLGEMEIN.cRelayHub
Public Class cRelayHubToken
' ======= KONFIG =======
Private Shared ReadOnly TOKEN_ENDPOINT As String = "https://login.singlewindow.io/auth/realms/agsw/protocol/openid-connect/token"
' Private Shared ReadOnly TOKEN_ENDPOINT As String = "https://dev-kc.singlewindow.io/auth/realms/agsw/protocol/openid-connect/token"
' curl --location 'https://login.singlewindow.io/auth/realms/agsw/protocol/openid-connect/token' \
'--header 'Content-Type: application/x-www-form-urlencoded' \
'--data-urlencode 'grant_type=password' \
'--data-urlencode 'password=Dhub1234*' \
'--data-urlencode 'username=dhub@verag.ag' \
'--data-urlencode 'client_id=agsw-admin'
Private Shared ReadOnly TOKEN_ENDPOINT_PROD As String = "https://login.singlewindow.io/auth/realms/agsw/protocol/openid-connect/token"
Private Shared ReadOnly TOKEN_ENDPOINT_TEST As String = "https://dev-kc.singlewindow.io/auth/realms/agsw/protocol/openid-connect/token"
Private Shared ReadOnly CLIENT_ID As String = "agsw-admin"
' Gewünscht: Zugangsdaten in der Klasse definieren
Private Shared ReadOnly USERNAME As String = "dhub@verag.ag" '"andreas.test@test.com"
Private Shared ReadOnly PASSWORD As String = "Dhub1234*" '"Password.123"
Private Shared ReadOnly USERNAME_PROD As String = "dhub@verag.ag"
Private Shared ReadOnly PASSWORD_PROD As String = "Dhub1234*"
' Token-File pro Benutzer unter %AppData%
Private Shared ReadOnly StorePath As String =
Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
"RelayHub", "token.json")
Private Shared ReadOnly USERNAME_TEST As String = "andreas.test@test.com"
Private Shared ReadOnly PASSWORD_TEST As String = "Atez123."
' Sicherheitspuffer, bevor wir erneuern (Sekunden)
Private Const ExpirySkewSeconds As Integer = 60
' ======= ÖFFENTLICHE API =======
''' <summary>
''' Liefert einen gültigen Access Token (nie Leerstring).
''' </summary>
Public Shared Function GetAccessToken() As String
Dim store = LoadStore()
'======================================================================
' KONFIGURATION
'======================================================================
Private Shared Function GetTokenEndpoint(environment As cRelayHubEnvironment) As String
Select Case environment
Case cRelayHubEnvironment.TEST
Return TOKEN_ENDPOINT_TEST
Case cRelayHubEnvironment.PROD
Return TOKEN_ENDPOINT_PROD
Case Else
Throw New ApplicationException("Unbekannte RelayHub-Umgebung.")
End Select
End Function
Private Shared Function GetUsername(environment As cRelayHubEnvironment) As String
Select Case environment
Case cRelayHubEnvironment.TEST
Return USERNAME_TEST
Case cRelayHubEnvironment.PROD
Return USERNAME_PROD
Case Else
Throw New ApplicationException("Unbekannte RelayHub-Umgebung.")
End Select
End Function
Private Shared Function GetPassword(environment As cRelayHubEnvironment) As String
Select Case environment
Case cRelayHubEnvironment.TEST
Return PASSWORD_TEST
Case cRelayHubEnvironment.PROD
Return PASSWORD_PROD
Case Else
Throw New ApplicationException("Unbekannte RelayHub-Umgebung.")
End Select
End Function
Private Shared Function GetStorePath(environment As cRelayHubEnvironment) As String
Dim fileName As String = If(environment = cRelayHubEnvironment.TEST, "token_test.json", "token_prod.json")
Return Path.Combine(System.Environment.GetFolderPath(System.Environment.SpecialFolder.ApplicationData), "RelayHub", fileName)
End Function
'======================================================================
' ACCESS TOKEN
'======================================================================
Public Shared Function GetAccessToken(environment As cRelayHubEnvironment) As String
Dim store = LoadStore(environment)
' 1) Wenn wir einen (noch) gültigen Token haben
If store IsNot Nothing AndAlso Not String.IsNullOrWhiteSpace(store.AccessToken) Then
If store.ExpiresAtUtc > DateTimeOffset.UtcNow.AddSeconds(ExpirySkewSeconds) Then
Return store.AccessToken
End If
If store.ExpiresAtUtc > DateTimeOffset.UtcNow.AddSeconds(ExpirySkewSeconds) Then Return store.AccessToken
End If
' 2) Versuche Refresh, falls vorhanden
If store IsNot Nothing AndAlso Not String.IsNullOrWhiteSpace(store.RefreshToken) Then
store = TryRefresh(store.RefreshToken)
End If
If store IsNot Nothing AndAlso Not String.IsNullOrWhiteSpace(store.RefreshToken) Then store = TryRefresh(store.RefreshToken, environment)
' 3) Fallback: Password-Grant Login
If store Is Nothing OrElse String.IsNullOrWhiteSpace(store.AccessToken) Then
store = PasswordLogin()
End If
If store Is Nothing OrElse String.IsNullOrWhiteSpace(store.AccessToken) Then store = PasswordLogin(environment)
' Validierung
If store Is Nothing OrElse String.IsNullOrWhiteSpace(store.AccessToken) Then
Throw New ApplicationException("Konnte keinen gültigen Access Token erhalten (leer).")
End If
If store Is Nothing OrElse String.IsNullOrWhiteSpace(store.AccessToken) Then Throw New ApplicationException("Konnte keinen gültigen Access Token erhalten (" & environment.ToString() & ").")
' Persistieren & zurück
SaveStore(store)
SaveStore(store, environment)
Return store.AccessToken
End Function
' ======= INTERNES =======
Private Shared Function PasswordLogin() As TokenStore
'======================================================================
' LOGIN
'======================================================================
Private Shared Function PasswordLogin(environment As cRelayHubEnvironment) As TokenStore
Dim form = New Dictionary(Of String, String) From {
{"grant_type", "password"},
{"username", USERNAME},
{"password", PASSWORD},
{"username", GetUsername(environment)},
{"password", GetPassword(environment)},
{"client_id", CLIENT_ID}
}
Dim resp = PostForm(form)
Dim token = ParseTokenResponse(resp)
Return token
Dim resp = PostForm(form, environment)
Return ParseTokenResponse(resp, environment)
End Function
Private Shared Function TryRefresh(refreshToken As String) As TokenStore
'======================================================================
' REFRESH
'======================================================================
Private Shared Function TryRefresh(refreshToken As String, environment As cRelayHubEnvironment) As TokenStore
Try
Dim form = New Dictionary(Of String, String) From {
{"grant_type", "refresh_token"},
{"refresh_token", refreshToken},
{"client_id", CLIENT_ID}
}
Dim resp = PostForm(form)
Dim token = ParseTokenResponse(resp)
' Nur speichern, wenn ein Access Token vorhanden ist
Dim resp = PostForm(form, environment)
Dim token = ParseTokenResponse(resp, environment)
If Not String.IsNullOrWhiteSpace(token.AccessToken) Then
SaveStore(token)
SaveStore(token, environment)
Return token
End If
Catch ex As Exception
' Ignorieren -> fällt auf PasswordLogin zurück
Console.WriteLine("Token Refresh fehlgeschlagen (" & environment.ToString() & "): " & ex.Message)
End Try
Return Nothing
End Function
Private Shared Function PostForm(formFields As Dictionary(Of String, String)) As String
'======================================================================
' HTTP
'======================================================================
Private Shared Function PostForm(formFields As Dictionary(Of String, String), environment As cRelayHubEnvironment) As String
Using client As New HttpClient()
Using content As New FormUrlEncodedContent(formFields)
Dim response = client.PostAsync(TOKEN_ENDPOINT, content).Result
Dim tokenEndpoint As String = GetTokenEndpoint(environment)
Dim username As String = GetUsername(environment)
Dim password As String = GetPassword(environment)
Console.WriteLine("")
Console.WriteLine("==================================================")
Console.WriteLine(" RELAYHUB TOKEN REQUEST")
Console.WriteLine("==================================================")
Console.WriteLine("Environment : " & environment.ToString())
Console.WriteLine("Token Endpoint : " & tokenEndpoint)
Console.WriteLine("Client ID : " & CLIENT_ID)
'Console.WriteLine("Username : " & username)
'Console.WriteLine("Password : " & password)
'Console.WriteLine("Password Länge : " & If(password Is Nothing, 0, password.Length))
'Console.WriteLine("Grant Type : " & If(formFields.ContainsKey("grant_type"), formFields("grant_type"), "<nicht gesetzt>"))
'Console.WriteLine("Token Store : " & GetStorePath(environment))
'Console.WriteLine("==================================================")
Dim response = client.PostAsync(tokenEndpoint, content).Result
Dim body = response.Content.ReadAsStringAsync().Result
If Not response.IsSuccessStatusCode Then
Throw New ApplicationException(
$"Token-Endpoint Fehler ({CInt(response.StatusCode)}): {body}")
End If
If Not response.IsSuccessStatusCode Then Throw New ApplicationException("Token-Endpoint Fehler (" & environment.ToString() & " / " & CInt(response.StatusCode) & "): " & body)
Return body
End Using
End Using
End Function
Private Shared Function ParseTokenResponse(json As String) As TokenStore
'======================================================================
' TOKEN RESPONSE
'======================================================================
Private Shared Function ParseTokenResponse(json As String, environment As cRelayHubEnvironment) As TokenStore
Dim r = JsonConvert.DeserializeObject(Of TokenResponse)(json)
If r Is Nothing OrElse String.IsNullOrWhiteSpace(r.access_token) Then
Throw New ApplicationException("Token-Antwort ungültig oder ohne access_token.")
End If
If r Is Nothing OrElse String.IsNullOrWhiteSpace(r.access_token) Then Throw New ApplicationException("Token-Antwort ungültig oder ohne access_token.")
Dim now = DateTimeOffset.UtcNow
Dim expiresIn = If(r.expires_in <= 0, 3600, r.expires_in) ' Fallback 1h
Dim expiresIn = If(r.expires_in <= 0, 3600, r.expires_in)
Dim store = New TokenStore With {
.AccessToken = r.access_token.Trim(),
.RefreshToken = If(r.refresh_token, String.Empty),
.ExpiresAtUtc = now.AddSeconds(expiresIn)
}
.AccessToken = r.access_token.Trim(),
.RefreshToken = If(r.refresh_token, String.Empty),
.ExpiresAtUtc = now.AddSeconds(expiresIn)
}
' === Konsolen-Ausgabe ===
Console.WriteLine("== Neuer Token erhalten ==")
Console.WriteLine("Access Token: " & store.AccessToken)
Console.WriteLine("Refresh Token: " & store.RefreshToken)
Console.WriteLine("== Neuer RelayHub Token ==")
Console.WriteLine("Environment: " & environment.ToString())
Console.WriteLine("Gültig bis UTC: " & store.ExpiresAtUtc.ToString("yyyy-MM-dd HH:mm:ss"))
Return store
End Function
' ======= PERSISTENZ =======
Private Shared Function LoadStore() As TokenStore
'======================================================================
' TOKEN LADEN
'======================================================================
Private Shared Function LoadStore(environment As cRelayHubEnvironment) As TokenStore
Try
If File.Exists(StorePath) Then
Dim json = File.ReadAllText(StorePath, Encoding.UTF8)
Dim storePath As String = GetStorePath(environment)
If File.Exists(storePath) Then
Dim json = File.ReadAllText(storePath, Encoding.UTF8)
Dim s = JsonConvert.DeserializeObject(Of TokenStore)(json)
' Ausgabe in Konsole
If s IsNot Nothing Then
Console.WriteLine("== Token aus Datei geladen ==")
Console.WriteLine("Access Token: " & (If(String.IsNullOrWhiteSpace(s.AccessToken), "<leer>", s.AccessToken)))
Console.WriteLine("Refresh Token: " & (If(String.IsNullOrWhiteSpace(s.RefreshToken), "<leer>", s.RefreshToken)))
Console.WriteLine("Environment: " & environment.ToString())
Console.WriteLine("Gültig bis UTC: " & s.ExpiresAtUtc.ToString("yyyy-MM-dd HH:mm:ss"))
End If
Return s
Else
' Datei existiert nicht -> Info ausgeben
Console.WriteLine("Keine Token-Datei vorhanden, neuer Login erforderlich.")
Console.WriteLine("Keine Token-Datei für " & environment.ToString() & " vorhanden.")
End If
Catch ex As Exception
Console.WriteLine("Fehler beim Laden der Token-Datei: " & ex.Message)
' Datei defekt? -> Ignorieren, neu holen
End Try
Return Nothing
End Function
Private Shared Sub SaveStore(store As TokenStore)
'======================================================================
' TOKEN SPEICHERN
'======================================================================
Private Shared Sub SaveStore(store As TokenStore, environment As cRelayHubEnvironment)
Try
Dim dir = Path.GetDirectoryName(StorePath)
Dim storePath As String = GetStorePath(environment)
Dim dir = Path.GetDirectoryName(storePath)
If Not Directory.Exists(dir) Then Directory.CreateDirectory(dir)
' Datei immer neu schreiben/überschreiben
Dim json = JsonConvert.SerializeObject(store, Formatting.Indented)
File.WriteAllText(StorePath, json, Encoding.UTF8)
File.WriteAllText(storePath, json, Encoding.UTF8)
Console.WriteLine("Token-Datei gespeichert: " & StorePath)
Console.WriteLine("Token-Datei gespeichert: " & storePath)
Catch ex As Exception
Console.WriteLine("Fehler beim Speichern der Token-Datei: " & ex.Message)
End Try
End Sub
Public Shared Sub DeleteStore()
'======================================================================
' TOKEN LÖSCHEN
'======================================================================
Private Shared Sub Delete_Store(Optional environment As cRelayHubEnvironment = cRelayHubEnvironment.PROD)
Try
If System.IO.File.Exists(StorePath) Then System.IO.File.Delete(StorePath)
Console.WriteLine("Token-Datei gelöscht: " & StorePath)
Dim storePath As String = GetStorePath(environment)
If File.Exists(storePath) Then File.Delete(storePath)
Console.WriteLine("Token-Datei gelöscht: " & storePath)
Catch ex As Exception
Console.WriteLine("Fehler beim Löschen der Token-Datei: " & ex.Message)
End Try
End Sub
Public Shared Sub DeleteAllStores()
Delete_Store(cRelayHubEnvironment.TEST)
Delete_Store(cRelayHubEnvironment.PROD)
End Sub
' ======= DTOs =======
'======================================================================
' DTOs
'======================================================================
Private Class TokenResponse
Public Property access_token As String
Public Property expires_in As Integer
@@ -211,7 +257,6 @@ Public Class cRelayHubToken
Public Property refresh_token As String
Public Property token_type As String
Public Property scope As String
' … weitere Felder bei Bedarf
End Class
Private Class TokenStore
@@ -220,4 +265,4 @@ Public Class cRelayHubToken
Public Property ExpiresAtUtc As DateTimeOffset
End Class
End Class
End Class