From 6357c4e2aed699f2eae7530b2d7a22d1432dfce9 Mon Sep 17 00:00:00 2001 From: lemoer Date: Sun, 29 Jun 2025 12:51:54 +0200 Subject: [PATCH] make initial push ssh keys working --- app/lib/main.dart | 198 ++++++++++++++++++ .../flutter/generated_plugin_registrant.cc | 4 + app/linux/flutter/generated_plugins.cmake | 1 + .../Flutter/GeneratedPluginRegistrant.swift | 4 + app/pubspec.lock | 128 +++++++++++ app/pubspec.yaml | 3 + .../flutter/generated_plugin_registrant.cc | 3 + app/windows/flutter/generated_plugins.cmake | 1 + 8 files changed, 342 insertions(+) diff --git a/app/lib/main.dart b/app/lib/main.dart index 019a644..f6cc843 100644 --- a/app/lib/main.dart +++ b/app/lib/main.dart @@ -1,4 +1,5 @@ import 'dart:convert'; +import 'dart:io'; import 'package:dartssh2/dartssh2.dart'; import 'package:flutter/material.dart'; @@ -9,11 +10,91 @@ import 'package:flutter/services.dart'; // secret storage import 'package:flutter_secure_storage/flutter_secure_storage.dart'; +import 'package:oauth2/oauth2.dart' as oauth2; + +import 'package:url_launcher/url_launcher.dart'; void main() { runApp(const MyApp()); } +Future getOAuth2Client() async { + // This is a placeholder for OAuth2 client initialization. + // Replace with your actual OAuth2 client setup. + + final authorizationEndpoint = Uri.parse( + 'https://auth.leinelab.org/application/o/authorize/', + ); + + final tokenEndpoint = Uri.parse( + 'https://auth.leinelab.org/application/o/token/', + ); + + final identifier = 'UwSMm8gTwBTUURSaxp5uPpuwX1OkGO4FRHeO9v3i'; + final secret = null; // = 'my client secret'; + + final redirectUrl = Uri.parse('http://localhost:30165/'); + + final credentialsFile = File('~/.myapp/credentials.json'); + + //.... + + var exists = await credentialsFile.exists(); + + if (exists) { + var credentials = oauth2.Credentials.fromJson( + await credentialsFile.readAsString(), + ); + return oauth2.Client(credentials, identifier: identifier, secret: secret); + } + + var grant = oauth2.AuthorizationCodeGrant( + identifier, + authorizationEndpoint, + tokenEndpoint, + secret: secret, + ); + + var authorizationUrl = grant.getAuthorizationUrl( + redirectUrl, + scopes: ["profile", "email", "goauthentik.io/api", "openid"], + ); + + // TODO: clicking the button twice might try to bind the server twice + var server = await HttpServer.bind("127.0.0.1", 30165); + + await launchUrl(authorizationUrl); + + var queryParameters; + + await server.forEach((HttpRequest request) { + request.response.write( + 'Success! You can close this window now and go back to the app.', + ); + queryParameters = request.uri.queryParameters; + request.response.close(); + server.close(); + }); + + return await grant.handleAuthorizationResponse(queryParameters); +} + +void makeAlert(BuildContext context, String title, String message) { + showDialog( + context: context, + builder: (BuildContext context) => AlertDialog( + title: Text(title), + content: Text(message), + actions: [ + TextButton( + onPressed: () => Navigator.pop(context, 'OK'), + child: const Text('OK'), + ), + ], + ), + ); +} + Future generateKeyPair() async { final algorithm = Ed25519(); @@ -66,6 +147,122 @@ class _MyHomePageState extends State { OpenSSHEd25519KeyPair? keyPair; String _output = ''; String key = ''; + + Future doOAuth() async { + final client = await getOAuth2Client(); + + // TODO: Handle errors better + try { + final jsonMe = await client.read( + Uri.parse('https://auth.leinelab.org/api/v3/core/users/me/'), + ); + + // final me = jsonDecode(jsonMe); + + // final user = me['user']; + // final transformed = { + // "username": user['username'], + // "name": user['name'], + // "email": user['email'], + // "attributes.settings.locale": user['settings']['locale'], + // "attributes.sshPublicKeys": + // "foooooooobar!", // fix oder aus anderer Quelle + // "component": "ak-stage-prompt", + // }; + + // final push = jsonEncode(transformed); + + final push = + '{"username":"lemoer","name":"Leonardo Mörlein","email":"me@irrelefant.net","attributes.settings.locale":"","attributes.sshPublicKeys":"BLA!","component":"ak-stage-prompt"}'; + + print(push); + + final response0 = await client.get( + Uri.parse( + 'https://auth.leinelab.org/api/v3/flows/instances/default-user-settings-flow/execute/', + ), + ); + + var sessionCookieHeader = response0.headers['set-cookie']; + if (sessionCookieHeader == null) { + throw Exception('No session cookie found in response headers.'); + } + + String? sessionCookie; + int index = sessionCookieHeader.indexOf(';'); + sessionCookie = (index == -1) + ? sessionCookieHeader + : sessionCookieHeader.substring(0, index); + + final responsea = await client.get( + Uri.parse( + 'https://auth.leinelab.org/api/v3/flows/executor/default-user-settings-flow/?query=', + ), + headers: {'Cookie': sessionCookie}, + ); + + print("Response A status code: ${responsea.statusCode}"); + print("Response body:"); + print(responsea.body); + + print("Session cookie: $sessionCookie"); + + final response = await client.post( + Uri.parse( + 'https://auth.leinelab.org/api/v3/flows/executor/default-user-settings-flow/?query=', + ), + body: push, + headers: {'Content-Type': 'application/json', 'Cookie': sessionCookie}, + ); + + if (response.statusCode != 302) { + throw Exception( + "Expected a redirect (302) response, but got ${response.statusCode}", + ); + } + + // if (response.statusCode == 200) { + // print("User data updated successfully."); + // print("Response body:"); + // print(response.body); + // response.headers.toString().split('\n').forEach(print); + // } else { + // print("Error updating user data: ${response.statusCode}"); + // print("Response body:"); + // print(response.body); + // print(response.headers.toString()); + // } + + final newLocation = response.headers['location']; + if (newLocation != null) { + print("Redirecting to: $newLocation"); + await launchUrl(Uri.parse(newLocation)); + } else { + throw Exception("No redirect location found in response headers."); + } + + final responseFinal = await client.get( + Uri.parse('https://auth.leinelab.org/' + newLocation), + headers: {'Cookie': sessionCookie}, + ); + + if (responseFinal.statusCode == 200) { + print("User data updated successfully."); + print("responseFinal body:"); + print(responseFinal.body); + responseFinal.headers.toString().split('\n').forEach(print); + } else { + print("Error updating user data: ${responseFinal.statusCode}"); + print("responseFinal body:"); + print(responseFinal.body); + print(responseFinal.headers.toString()); + } + } catch (e) { + print(e.toString()); + } + ; + } + Future doSSH() async { SSHSocket? socket; @@ -151,6 +348,7 @@ class _MyHomePageState extends State { child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ + TextButton(onPressed: doOAuth, child: Text("Oauth2 Login")), Text('Current output:'), Text(outputText, style: Theme.of(context).textTheme.headlineMedium), ], diff --git a/app/linux/flutter/generated_plugin_registrant.cc b/app/linux/flutter/generated_plugin_registrant.cc index d0e7f79..38dd0bc 100644 --- a/app/linux/flutter/generated_plugin_registrant.cc +++ b/app/linux/flutter/generated_plugin_registrant.cc @@ -7,9 +7,13 @@ #include "generated_plugin_registrant.h" #include +#include void fl_register_plugins(FlPluginRegistry* registry) { g_autoptr(FlPluginRegistrar) flutter_secure_storage_linux_registrar = fl_plugin_registry_get_registrar_for_plugin(registry, "FlutterSecureStorageLinuxPlugin"); flutter_secure_storage_linux_plugin_register_with_registrar(flutter_secure_storage_linux_registrar); + g_autoptr(FlPluginRegistrar) url_launcher_linux_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "UrlLauncherPlugin"); + url_launcher_plugin_register_with_registrar(url_launcher_linux_registrar); } diff --git a/app/linux/flutter/generated_plugins.cmake b/app/linux/flutter/generated_plugins.cmake index b29e9ba..65240e9 100644 --- a/app/linux/flutter/generated_plugins.cmake +++ b/app/linux/flutter/generated_plugins.cmake @@ -4,6 +4,7 @@ list(APPEND FLUTTER_PLUGIN_LIST flutter_secure_storage_linux + url_launcher_linux ) list(APPEND FLUTTER_FFI_PLUGIN_LIST diff --git a/app/macos/Flutter/GeneratedPluginRegistrant.swift b/app/macos/Flutter/GeneratedPluginRegistrant.swift index 15a1671..4277d2e 100644 --- a/app/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/app/macos/Flutter/GeneratedPluginRegistrant.swift @@ -7,8 +7,12 @@ import Foundation import flutter_secure_storage_macos import path_provider_foundation +import url_launcher_macos +import webview_flutter_wkwebview func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { FlutterSecureStoragePlugin.register(with: registry.registrar(forPlugin: "FlutterSecureStoragePlugin")) PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) + UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin")) + WebViewFlutterPlugin.register(with: registry.registrar(forPlugin: "WebViewFlutterPlugin")) } diff --git a/app/pubspec.lock b/app/pubspec.lock index 9aca0be..20c902b 100644 --- a/app/pubspec.lock +++ b/app/pubspec.lock @@ -176,6 +176,22 @@ packages: description: flutter source: sdk version: "0.0.0" + http: + dependency: transitive + description: + name: http + sha256: "2c11f3f94c687ee9bad77c171151672986360b2b001d109814ee7140b2cf261b" + url: "https://pub.dev" + source: hosted + version: "1.4.0" + http_parser: + dependency: transitive + description: + name: http_parser + sha256: "178d74305e7866013777bab2c3d8726205dc5a4dd935297175b19a23a2e66571" + url: "https://pub.dev" + source: hosted + version: "4.1.2" js: dependency: transitive description: @@ -240,6 +256,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.16.0" + oauth2: + dependency: "direct main" + description: + name: oauth2 + sha256: c84470642cbb2bec450ccab2f8520c079cd1ca546a76ffd5c40589e07f4e8bf4 + url: "https://pub.dev" + source: hosted + version: "2.0.3" path: dependency: transitive description: @@ -389,6 +413,70 @@ packages: url: "https://pub.dev" source: hosted version: "1.4.0" + url_launcher: + dependency: "direct main" + description: + name: url_launcher + sha256: "9d06212b1362abc2f0f0d78e6f09f726608c74e3b9462e8368bb03314aa8d603" + url: "https://pub.dev" + source: hosted + version: "6.3.1" + url_launcher_android: + dependency: transitive + description: + name: url_launcher_android + sha256: "8582d7f6fe14d2652b4c45c9b6c14c0b678c2af2d083a11b604caeba51930d79" + url: "https://pub.dev" + source: hosted + version: "6.3.16" + url_launcher_ios: + dependency: transitive + description: + name: url_launcher_ios + sha256: "7f2022359d4c099eea7df3fdf739f7d3d3b9faf3166fb1dd390775176e0b76cb" + url: "https://pub.dev" + source: hosted + version: "6.3.3" + url_launcher_linux: + dependency: transitive + description: + name: url_launcher_linux + sha256: "4e9ba368772369e3e08f231d2301b4ef72b9ff87c31192ef471b380ef29a4935" + url: "https://pub.dev" + source: hosted + version: "3.2.1" + url_launcher_macos: + dependency: transitive + description: + name: url_launcher_macos + sha256: "17ba2000b847f334f16626a574c702b196723af2a289e7a93ffcb79acff855c2" + url: "https://pub.dev" + source: hosted + version: "3.2.2" + url_launcher_platform_interface: + dependency: transitive + description: + name: url_launcher_platform_interface + sha256: "552f8a1e663569be95a8190206a38187b531910283c3e982193e4f2733f01029" + url: "https://pub.dev" + source: hosted + version: "2.3.2" + url_launcher_web: + dependency: transitive + description: + name: url_launcher_web + sha256: "4bd2b7b4dc4d4d0b94e5babfffbca8eac1a126c7f3d6ecbc1a11013faa3abba2" + url: "https://pub.dev" + source: hosted + version: "2.4.1" + url_launcher_windows: + dependency: transitive + description: + name: url_launcher_windows + sha256: "3284b6d2ac454cf34f114e1d3319866fdd1e19cdc329999057e44ffe936cfa77" + url: "https://pub.dev" + source: hosted + version: "3.1.4" vector_math: dependency: transitive description: @@ -405,6 +493,46 @@ packages: url: "https://pub.dev" source: hosted version: "15.0.0" + web: + dependency: transitive + description: + name: web + sha256: "868d88a33d8a87b18ffc05f9f030ba328ffefba92d6c127917a2ba740f9cfe4a" + url: "https://pub.dev" + source: hosted + version: "1.1.1" + webview_flutter: + dependency: "direct main" + description: + name: webview_flutter + sha256: c3e4fe614b1c814950ad07186007eff2f2e5dd2935eba7b9a9a1af8e5885f1ba + url: "https://pub.dev" + source: hosted + version: "4.13.0" + webview_flutter_android: + dependency: transitive + description: + name: webview_flutter_android + sha256: f6e6afef6e234801da77170f7a1847ded8450778caf2fe13979d140484be3678 + url: "https://pub.dev" + source: hosted + version: "4.7.0" + webview_flutter_platform_interface: + dependency: transitive + description: + name: webview_flutter_platform_interface + sha256: f0dc2dc3a2b1e3a6abdd6801b9355ebfeb3b8f6cde6b9dc7c9235909c4a1f147 + url: "https://pub.dev" + source: hosted + version: "2.13.1" + webview_flutter_wkwebview: + dependency: transitive + description: + name: webview_flutter_wkwebview + sha256: a3d461fe3467014e05f3ac4962e5fdde2a4bf44c561cb53e9ae5c586600fdbc3 + url: "https://pub.dev" + source: hosted + version: "3.22.0" win32: dependency: transitive description: diff --git a/app/pubspec.yaml b/app/pubspec.yaml index 60dce69..c3bf81e 100644 --- a/app/pubspec.yaml +++ b/app/pubspec.yaml @@ -37,6 +37,9 @@ dependencies: dartssh2: ^2.12.0 cryptography: ^2.7.0 flutter_secure_storage: ^9.2.4 + oauth2: ^2.0.3 + webview_flutter: ^4.13.0 + url_launcher: ^6.3.1 dev_dependencies: flutter_test: diff --git a/app/windows/flutter/generated_plugin_registrant.cc b/app/windows/flutter/generated_plugin_registrant.cc index 0c50753..2048c45 100644 --- a/app/windows/flutter/generated_plugin_registrant.cc +++ b/app/windows/flutter/generated_plugin_registrant.cc @@ -7,8 +7,11 @@ #include "generated_plugin_registrant.h" #include +#include void RegisterPlugins(flutter::PluginRegistry* registry) { FlutterSecureStorageWindowsPluginRegisterWithRegistrar( registry->GetRegistrarForPlugin("FlutterSecureStorageWindowsPlugin")); + UrlLauncherWindowsRegisterWithRegistrar( + registry->GetRegistrarForPlugin("UrlLauncherWindows")); } diff --git a/app/windows/flutter/generated_plugins.cmake b/app/windows/flutter/generated_plugins.cmake index 4fc759c..de626cc 100644 --- a/app/windows/flutter/generated_plugins.cmake +++ b/app/windows/flutter/generated_plugins.cmake @@ -4,6 +4,7 @@ list(APPEND FLUTTER_PLUGIN_LIST flutter_secure_storage_windows + url_launcher_windows ) list(APPEND FLUTTER_FFI_PLUGIN_LIST