CBAM, SUMA, RelayHub
This commit is contained in:
@@ -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
|
||||
Reference in New Issue
Block a user