From e1fcb8a13f1590c90ce9834bcb40d38b2320dd6f Mon Sep 17 00:00:00 2001 From: Jonas Martinez <36544012+jonas-martinez@users.noreply.github.com> Date: Mon, 21 Aug 2023 12:03:12 +0200 Subject: [PATCH 01/15] feat: Oauth implementation (#95) * debug oauth * Remove useless code * Preparing oauth * Trying some things * much better * More error handling * This is working nicely * feat: Handle OAuth client ID with an env var * fix: Scopes * fix review * feat: upgrade common * change catcher dep and update flutter requirements * update flutter requirement * fix logout * fix analyze * use client-common alpha * fix analyze * fix --------- Co-authored-by: Thomas DA ROCHA --- .github/workflows/flutter.yml | 2 +- .github/workflows/semantic_release.yml | 2 +- README.md | 7 +- lib/main.dart | 150 ++++++-- lib/navigation/backoffice_navigator.dart | 57 +-- lib/navigation/guard.dart | 18 +- lib/views/backoffice_drawer.dart | 13 +- lib/views/create_project_form.dart | 12 +- lib/views/dev_validation_page.dart | 4 +- linux/flutter/generated_plugin_registrant.cc | 8 + linux/flutter/generated_plugins.cmake | 2 + macos/Flutter/GeneratedPluginRegistrant.swift | 8 + pubspec.lock | 357 +++++++++++------- pubspec.yaml | 18 +- web/redirect.html | 16 + .../flutter/generated_plugin_registrant.cc | 6 + windows/flutter/generated_plugins.cmake | 2 + 17 files changed, 457 insertions(+), 225 deletions(-) create mode 100644 web/redirect.html diff --git a/.github/workflows/flutter.yml b/.github/workflows/flutter.yml index 1179d9c..1ea6ee9 100644 --- a/.github/workflows/flutter.yml +++ b/.github/workflows/flutter.yml @@ -7,7 +7,7 @@ on: - synchronize env: - flutter_version: "3.3.9" + flutter_version: "3.10.x" jobs: prIsWip: name: PR is WIP? diff --git a/.github/workflows/semantic_release.yml b/.github/workflows/semantic_release.yml index 2266849..ede9a6e 100644 --- a/.github/workflows/semantic_release.yml +++ b/.github/workflows/semantic_release.yml @@ -8,7 +8,7 @@ on: - "*.x" env: - flutter_version: "3.3.9" + flutter_version: "3.10.x" jobs: release: diff --git a/README.md b/README.md index 375d0e4..a1cec35 100644 --- a/README.md +++ b/README.md @@ -44,9 +44,14 @@ This repository gives you the possibility to create, manage and deploy your appl ## Usage +On the server project, you need to have a running instance of the [Lenra Server](https://github.com/lenra-io/server) and have created an OAuth client for the backoffice with the next command: +```sh +mix create_oauth2_client backoffice +``` + Run flutter app with chrome ```sh -flutter run -d chrome --web-port 10000 --dart-define=LENRA_SERVER_URL=http://localhost:4000 +flutter run -d chrome --web-port 10000 --dart-define=LENRA_SERVER_URL=http://localhost:4000 --dart-define=OAUTH_CLIENT_ID=6c434f76-9917-4856-901b-f723fe45446c ``` Run flutter test diff --git a/lib/main.dart b/lib/main.dart index 1b4b3f9..7013fe3 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,12 +1,18 @@ +import 'dart:async'; + +import 'package:catcher/catcher.dart'; import 'package:client_backoffice/navigation/backoffice_navigator.dart'; import 'package:client_backoffice/navigation/url_strategy/url_strategy.dart' show setUrlStrategyTo; +import 'package:client_common/api/response_models/api_error.dart'; import 'package:client_common/config/config.dart'; import 'package:client_common/models/auth_model.dart'; import 'package:client_common/models/build_model.dart'; -import 'package:client_common/models/cgu_model.dart'; import 'package:client_common/models/deployment_model.dart'; import 'package:client_common/models/store_model.dart'; import 'package:client_common/models/user_application_model.dart'; +import 'package:client_common/oauth/oauth_model.dart'; +import 'package:client_common/views/lenra_report_mode.dart'; +import 'package:client_common/views/simple_page.dart'; import 'package:flutter/material.dart'; import 'package:lenra_components/lenra_components.dart'; import 'package:logging/logging.dart'; @@ -22,47 +28,129 @@ void main() async { }); debugPrint("Starting main app[debugPrint]: ${Config.instance.application}"); - // TODO: Récupération de variables d'environnement ne doit pas marcher + const environment = String.fromEnvironment('ENVIRONMENT'); + var reportMode = LenraReportMode(); + CatcherOptions debugOptions = CatcherOptions( + reportMode, + environment == "production" || environment == "staging" + ? [ + SentryHandler( + SentryClient(SentryOptions(dsn: Config.instance.sentryDsn)..environment = environment), + ), + ] + : [], + reportOccurrenceTimeout: 100, + ); + + Catcher( + debugConfig: debugOptions, + rootWidget: ErrorHandler(streamController: reportMode.streamController, child: Backoffice()), + ); +} - if (environment == "production" || environment == "staging") { - String sentryDsn = Config.instance.sentryDsn; - await SentryFlutter.init( - (options) => options - ..dsn = sentryDsn - ..environment = environment, - appRunner: () => runApp(Backoffice()), +class ErrorHandler extends StatelessWidget { + final Widget child; + final StreamController streamController; + + const ErrorHandler({Key? key, required this.child, required this.streamController}) : super(key: key); + + @override + Widget build(BuildContext context) { + return StreamBuilder( + builder: (context, snapshot) { + if (snapshot.hasError) { + return Center( + child: Text(snapshot.error.toString()), + ); + } + if (snapshot.hasData) { + var themeData = LenraThemeData(); + return LenraTheme( + themeData: themeData, + child: MaterialApp( + theme: ThemeData( + visualDensity: VisualDensity.standard, + textTheme: TextTheme(bodyMedium: themeData.lenraTextThemeData.bodyText), + ), + home: SimplePage( + title: getErrorTitle(snapshot.data), + message: getErrorMessage(snapshot.data), + child: Flex( + direction: Axis.horizontal, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + TextButton( + onPressed: () { + streamController.add(null); + }, + child: Text("Retry"), + ), + ], + ), + ), + ), + ); + } + return child; + }, + stream: streamController.stream, ); - } else { - runApp(Backoffice()); + } + + String getErrorTitle(dynamic error) { + if (error is ApiError) { + return "Connection lost!"; + } else { + return "Unknown error"; + } + } + + String getErrorMessage(dynamic error) { + if (error is ApiError) { + return "It looks like you lost connection to the server. Please check your internet connection and try again."; + } else { + return "An unknown error occured. If the error persists, please contact us at contact@lenra.io."; + } } } class Backoffice extends StatelessWidget { Backoffice({Key? key}) : super(key: key); + @override Widget build(BuildContext context) { var themeData = LenraThemeData(); - return MultiProvider( - providers: [ - ChangeNotifierProvider(create: (context) => AuthModel()), - ChangeNotifierProvider(create: (context) => BuildModel()), - ChangeNotifierProvider(create: (context) => DeploymentModel()), - ChangeNotifierProvider(create: (context) => UserApplicationModel()), - ChangeNotifierProvider(create: (context) => StoreModel()), - ChangeNotifierProvider(create: (context) => CguModel()), - ], - builder: (BuildContext context, _) => LenraTheme( - themeData: themeData, - child: MaterialApp.router( - routerConfig: BackofficeNavigator.router, - title: 'Lenra', - theme: ThemeData( - visualDensity: VisualDensity.standard, - textTheme: TextTheme(bodyText2: themeData.lenraTextThemeData.bodyText), - ), - ), - ), + return Container( + color: Colors.white, + child: MultiProvider( + providers: [ + ChangeNotifierProvider( + create: (context) => OAuthModel( + const String.fromEnvironment('OAUTH_CLIENT_ID'), + 'http://localhost:10000/redirect.html', + scopes: ['profile', 'store', 'manage:account', 'manage:apps'], + ), + ), + ChangeNotifierProvider(create: (context) => AuthModel()), + ChangeNotifierProvider(create: (context) => BuildModel()), + ChangeNotifierProvider(create: (context) => DeploymentModel()), + ChangeNotifierProvider(create: (context) => UserApplicationModel()), + ChangeNotifierProvider(create: (context) => StoreModel()), + ], + builder: (BuildContext context, _) { + return LenraTheme( + themeData: themeData, + child: MaterialApp.router( + routerConfig: BackofficeNavigator.router, + title: 'Lenra', + theme: ThemeData( + visualDensity: VisualDensity.standard, + textTheme: TextTheme(bodyMedium: themeData.lenraTextThemeData.bodyText), + ), + ), + ); + }), ); } } diff --git a/lib/navigation/backoffice_navigator.dart b/lib/navigation/backoffice_navigator.dart index ffa938a..f31462e 100644 --- a/lib/navigation/backoffice_navigator.dart +++ b/lib/navigation/backoffice_navigator.dart @@ -1,3 +1,4 @@ +import 'package:catcher/catcher.dart'; import 'package:client_backoffice/navigation/guard.dart'; import 'package:client_backoffice/views/create_project_page.dart'; import 'package:client_backoffice/views/dev_validation_page.dart'; @@ -16,11 +17,14 @@ class BackofficeNavigator extends CommonNavigator { static GoRoute validationDev = GoRoute( name: "validation-dev", path: "/validation-dev", - redirect: (context, state) => Guard.guards(context, [ - Guard.checkAuthenticated, - Guard.checkCguAccepted, - Guard.checkIsNotDev, - ]), + redirect: (context, state) => Guard.guards( + context, + [ + Guard.checkIsAuthenticated, + Guard.checkIsNotDev, + ], + metadata: {"initialRoute": state.location}, + ), pageBuilder: (context, state) => NoTransitionPage( child: DevValidationPage(), ), @@ -29,13 +33,15 @@ class BackofficeNavigator extends CommonNavigator { static GoRoute welcome = GoRoute( name: "welcome", path: "/welcome", - redirect: (context, state) => Guard.guards(context, [ - Guard.checkAuthenticated, - Guard.checkCguAccepted, - Guard.checkIsUser, - Guard.checkIsDev, - Guard.checkNotHaveApp, - ]), + redirect: (context, state) => Guard.guards( + context, + [ + Guard.checkIsAuthenticated, + Guard.checkIsDev, + Guard.checkNotHaveApp, + ], + metadata: {"initialRoute": state.location}, + ), pageBuilder: (context, state) => NoTransitionPage( child: WelcomeDevPage(), ), @@ -44,12 +50,14 @@ class BackofficeNavigator extends CommonNavigator { static GoRoute createProject = GoRoute( name: "create-project", path: "/create-project", - redirect: (context, state) => Guard.guards(context, [ - Guard.checkAuthenticated, - Guard.checkCguAccepted, - Guard.checkIsUser, - Guard.checkIsDev, - ]), + redirect: (context, state) => Guard.guards( + context, + [ + Guard.checkIsAuthenticated, + Guard.checkIsDev, + ], + metadata: {"initialRoute": state.location}, + ), pageBuilder: (context, state) => NoTransitionPage( child: CreateProjectPage(), ), @@ -103,12 +111,11 @@ class BackofficeNavigator extends CommonNavigator { redirect: (context, state) => Guard.guards( context, [ - Guard.checkAuthenticated, - Guard.checkCguAccepted, - Guard.checkIsUser, + Guard.checkIsAuthenticated, Guard.checkIsDev, BackofficeGuard.checkHaveApp, ], + metadata: {"initialRoute": state.location}, ), pageBuilder: (context, state) { return NoTransitionPage( @@ -127,12 +134,11 @@ class BackofficeNavigator extends CommonNavigator { redirect: (context, state) => Guard.guards( context, [ - Guard.checkAuthenticated, - Guard.checkCguAccepted, - Guard.checkIsUser, + Guard.checkIsAuthenticated, Guard.checkIsDev, BackofficeGuard.checkHaveApp, ], + metadata: {"initialRoute": state.location}, ), pageBuilder: (context, state) { return NoTransitionPage( @@ -142,7 +148,8 @@ class BackofficeNavigator extends CommonNavigator { }, ); - static final GoRouter router = GoRouter( + static GoRouter router = GoRouter( + navigatorKey: Catcher.navigatorKey, routes: [ CommonNavigator.authRoutes, validationDev, diff --git a/lib/navigation/guard.dart b/lib/navigation/guard.dart index 8e1e215..7a311f6 100644 --- a/lib/navigation/guard.dart +++ b/lib/navigation/guard.dart @@ -1,27 +1,15 @@ import 'dart:async'; import 'package:client_backoffice/navigation/backoffice_navigator.dart'; -import 'package:client_common/api/response_models/app_response.dart'; -import 'package:client_common/models/user_application_model.dart'; import 'package:client_common/navigator/guard.dart'; import 'package:flutter/material.dart'; -import 'package:provider/provider.dart'; + +typedef IsValid = Future Function(BuildContext, Map?); class BackofficeGuard extends Guard { BackofficeGuard({required super.isValid, required super.onInvalid}); - static final BackofficeGuard checkHaveApp = BackofficeGuard(isValid: _haveApp(true), onInvalid: _toWelcome); - - static Future Function(BuildContext) _haveApp(bool mustHaveApp) { - return (BuildContext context) async { - try { - List userApps = await context.read().fetchUserApplications(); - return userApps.isNotEmpty == mustHaveApp; - } catch (e) { - return false; - } - }; - } + static final BackofficeGuard checkHaveApp = BackofficeGuard(isValid: Guard.haveApp(true), onInvalid: _toWelcome); static String _toWelcome(context) { return BackofficeNavigator.welcome.path; diff --git a/lib/views/backoffice_drawer.dart b/lib/views/backoffice_drawer.dart index 7152d52..b603110 100644 --- a/lib/views/backoffice_drawer.dart +++ b/lib/views/backoffice_drawer.dart @@ -1,13 +1,12 @@ import 'package:client_backoffice/navigation/backoffice_navigator.dart'; import 'package:client_common/api/response_models/app_response.dart'; import 'package:client_common/api/response_models/environment_response.dart'; -import 'package:client_common/models/auth_model.dart'; import 'package:client_common/models/user_application_model.dart'; import 'package:client_common/navigator/common_navigator.dart'; +import 'package:client_common/oauth/oauth_model.dart'; import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; import 'package:lenra_components/lenra_components.dart'; -import 'package:logging/logging.dart'; import 'package:provider/provider.dart'; import 'package:url_launcher/url_launcher.dart'; @@ -18,7 +17,6 @@ class BackofficeDrawer extends StatelessWidget { @override Widget build(BuildContext context) { - final logger = Logger('BackofficeSideMenu'); var theme = LenraTheme.of(context); return Drawer( width: 196, @@ -69,12 +67,9 @@ class BackofficeDrawer extends StatelessWidget { BackofficeSideMenuItem( "Logout", icon: Icons.logout, - onPressed: () { - context.read().logout().then((value) { - CommonNavigator.go(context, CommonNavigator.sign, extra: {"register": false}); - }).catchError((error) { - logger.warning(error); - }); + onPressed: () async { + await context.read().helper.disconnect(); + context.go("/"); }, ), BackofficeSideMenuItem( diff --git a/lib/views/create_project_form.dart b/lib/views/create_project_form.dart index 06deb05..b507a32 100644 --- a/lib/views/create_project_form.dart +++ b/lib/views/create_project_form.dart @@ -1,9 +1,10 @@ import 'package:client_backoffice/navigation/backoffice_navigator.dart'; -import 'package:client_common/models/auth_model.dart'; import 'package:client_common/models/user_application_model.dart'; import 'package:client_common/navigator/common_navigator.dart'; +import 'package:client_common/oauth/oauth_model.dart'; import 'package:client_common/views/loading_button.dart'; import 'package:flutter/material.dart'; +import 'package:go_router/go_router.dart'; import 'package:lenra_components/lenra_components.dart'; import 'package:logging/logging.dart'; import 'package:provider/provider.dart'; @@ -111,12 +112,9 @@ class _CreateProjectFormState extends State { } else { return LenraButton( text: "Logout", - onPressed: () { - context.read().logout().then((value) { - CommonNavigator.go(context, CommonNavigator.sign, extra: {"register": false}); - }).catchError((error) { - logger.warning(error); - }); + onPressed: () async { + await context.read().helper.disconnect(); + context.go("/"); }, type: LenraComponentType.secondary, ); diff --git a/lib/views/dev_validation_page.dart b/lib/views/dev_validation_page.dart index bf794d4..611a613 100644 --- a/lib/views/dev_validation_page.dart +++ b/lib/views/dev_validation_page.dart @@ -30,7 +30,7 @@ class _DevValidationPageState extends State { return SimplePage( title: "Thank you for your registration", - message: "Great things are about to happen! Do you confirm that want to access our developer platform ?", + message: "Great things are about to happen! Do you confirm that you want to access our developer platform ?", child: LenraFlex( direction: Axis.vertical, fillParent: true, @@ -77,8 +77,6 @@ class _DevValidationPageState extends State { void validateDev() { context.read().validateDev().then((_) { CommonNavigator.go(context, BackofficeNavigator.welcome); - }).catchError((error) { - logger.warning(error); }); } } diff --git a/linux/flutter/generated_plugin_registrant.cc b/linux/flutter/generated_plugin_registrant.cc index 28722f2..2beacf1 100644 --- a/linux/flutter/generated_plugin_registrant.cc +++ b/linux/flutter/generated_plugin_registrant.cc @@ -6,11 +6,16 @@ #include "generated_plugin_registrant.h" +#include #include #include #include +#include void fl_register_plugins(FlPluginRegistry* registry) { + g_autoptr(FlPluginRegistrar) catcher_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "CatcherPlugin"); + catcher_plugin_register_with_registrar(catcher_registrar); 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); @@ -20,4 +25,7 @@ void fl_register_plugins(FlPluginRegistry* registry) { 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); + g_autoptr(FlPluginRegistrar) window_to_front_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "WindowToFrontPlugin"); + window_to_front_plugin_register_with_registrar(window_to_front_registrar); } diff --git a/linux/flutter/generated_plugins.cmake b/linux/flutter/generated_plugins.cmake index d4e32b2..f26fdca 100644 --- a/linux/flutter/generated_plugins.cmake +++ b/linux/flutter/generated_plugins.cmake @@ -3,9 +3,11 @@ # list(APPEND FLUTTER_PLUGIN_LIST + catcher flutter_secure_storage_linux sentry_flutter url_launcher_linux + window_to_front ) list(APPEND FLUTTER_FFI_PLUGIN_LIST diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index 39f909f..26e6e86 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -5,14 +5,22 @@ import FlutterMacOS import Foundation +import catcher +import device_info_plus import flutter_secure_storage_macos +import flutter_web_auth_2 import package_info_plus import sentry_flutter import url_launcher_macos +import window_to_front func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { + CatcherPlugin.register(with: registry.registrar(forPlugin: "CatcherPlugin")) + DeviceInfoPlusMacosPlugin.register(with: registry.registrar(forPlugin: "DeviceInfoPlusMacosPlugin")) FlutterSecureStoragePlugin.register(with: registry.registrar(forPlugin: "FlutterSecureStoragePlugin")) + FlutterWebAuth2Plugin.register(with: registry.registrar(forPlugin: "FlutterWebAuth2Plugin")) FLTPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FLTPackageInfoPlusPlugin")) SentryFlutterPlugin.register(with: registry.registrar(forPlugin: "SentryFlutterPlugin")) UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin")) + WindowToFrontPlugin.register(with: registry.registrar(forPlugin: "WindowToFrontPlugin")) } diff --git a/pubspec.lock b/pubspec.lock index 5403127..6d3e65e 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -5,42 +5,42 @@ packages: dependency: transitive description: name: _fe_analyzer_shared - sha256: "4897882604d919befd350648c7f91926a9d5de99e67b455bf0917cc2362f4bb8" + sha256: ae92f5d747aee634b87f89d9946000c2de774be1d6ac3e58268224348cd0101a url: "https://pub.dev" source: hosted - version: "47.0.0" + version: "61.0.0" analyzer: dependency: transitive description: name: analyzer - sha256: "690e335554a8385bc9d787117d9eb52c0c03ee207a607e593de3c9d71b1cfe80" + sha256: ea3d8652bda62982addfd92fdc2d0214e5f82e43325104990d4f4c4a2a313562 url: "https://pub.dev" source: hosted - version: "4.7.0" + version: "5.13.0" archive: dependency: transitive description: name: archive - sha256: d6347d54a2d8028e0437e3c099f66fdb8ae02c4720c1e7534c9f24c10351f85d + sha256: "0c8368c9b3f0abbc193b9d6133649a614204b528982bebc7026372d61677ce3a" url: "https://pub.dev" source: hosted - version: "3.3.6" + version: "3.3.7" args: dependency: transitive description: name: args - sha256: "139d809800a412ebb26a3892da228b2d0ba36f0ef5d9a82166e5e52ec8d61611" + sha256: eef6c46b622e0494a36c5a12d10d77fb4e855501a91c1b9ef9339326e58f0596 url: "https://pub.dev" source: hosted - version: "2.3.2" + version: "2.4.2" async: dependency: transitive description: name: async - sha256: bfe67ef28df125b7dddcea62755991f807aa39a2492a23e1550161692950bbe0 + sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" url: "https://pub.dev" source: hosted - version: "2.10.0" + version: "2.11.0" boolean_selector: dependency: transitive description: @@ -49,14 +49,23 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.1" + catcher: + dependency: "direct main" + description: + path: "." + ref: ae44d0bbce00c3801629b65f60783d15184a7f8f + resolved-ref: ae44d0bbce00c3801629b65f60783d15184a7f8f + url: "https://github.com/ThexXTURBOXx/catcher.git" + source: git + version: "0.8.0" characters: dependency: transitive description: name: characters - sha256: e6a326c8af69605aec75ed6c187d06b349707a27fbff8222ca9cc2cff167975c + sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" url: "https://pub.dev" source: hosted - version: "1.2.1" + version: "1.3.0" charcode: dependency: transitive description: @@ -69,10 +78,10 @@ packages: dependency: transitive description: name: checked_yaml - sha256: "3d1505d91afa809d177efd4eed5bb0eb65805097a1463abdd2add076effae311" + sha256: feb6bed21949061731a7a75fc5d2aa727cf160b91af9a3e464c5e3a32e28b5ff url: "https://pub.dev" source: hosted - version: "2.0.2" + version: "2.0.3" cli_util: dependency: transitive description: @@ -85,9 +94,9 @@ packages: dependency: "direct main" description: path: "." - ref: "v1.0.0-beta.41" - resolved-ref: b697fc3315b53cb35b52abb5e0f985bbcfd4eab9 - url: "git@github.com:lenra-io/client-common.git" + ref: "v1.0.0-alpha.1" + resolved-ref: e861c16762a32f835cc568a02678f1269ecf25ec + url: "https://github.com/lenra-io/client-common.git" source: git version: "1.0.0" clock: @@ -102,10 +111,10 @@ packages: dependency: transitive description: name: collection - sha256: cfc915e6923fe5ce6e153b0723c753045de46de1b4d63771530504004a45fae0 + sha256: "4a07be6cb69c84d677a6c3096fcf960cc3285a8330b4603e0d463d15d9bd934c" url: "https://pub.dev" source: hosted - version: "1.17.0" + version: "1.17.1" convert: dependency: transitive description: @@ -126,18 +135,42 @@ packages: dependency: transitive description: name: crypto - sha256: aa274aa7774f8964e4f4f38cc994db7b6158dd36e9187aaceaddc994b35c6c67 + sha256: ff625774173754681d66daaf4a448684fb04b78f902da9cb3d308c19cc5e8bab url: "https://pub.dev" source: hosted - version: "3.0.2" + version: "3.0.3" csslib: dependency: transitive description: name: csslib - sha256: b36c7f7e24c0bdf1bf9a3da461c837d1de64b9f8beb190c9011d8c72a3dfd745 + sha256: "831883fb353c8bdc1d71979e5b342c7d88acfbc643113c14ae51e2442ea0f20f" + url: "https://pub.dev" + source: hosted + version: "0.17.3" + device_info_plus: + dependency: transitive + description: + name: device_info_plus + sha256: "86add5ef97215562d2e090535b0a16f197902b10c369c558a100e74ea06e8659" url: "https://pub.dev" source: hosted - version: "0.17.2" + version: "9.0.3" + device_info_plus_platform_interface: + dependency: transitive + description: + name: device_info_plus_platform_interface + sha256: d3b01d5868b50ae571cd1dc6e502fc94d956b665756180f7b16ead09e836fd64 + url: "https://pub.dev" + source: hosted + version: "7.0.0" + dio: + dependency: transitive + description: + name: dio + sha256: ce75a1b40947fea0a0e16ce73337122a86762e38b982e1ccb909daa3b9bc4197 + url: "https://pub.dev" + source: hosted + version: "5.3.2" fake_async: dependency: transitive description: @@ -150,10 +183,10 @@ packages: dependency: transitive description: name: ffi - sha256: a38574032c5f1dd06c4aee541789906c12ccaab8ba01446e800d9c5b79c4a978 + sha256: ed5337a5660c506388a9f012be0288fb38b49020ce2b45fe1f8b8323fe429f99 url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "2.0.2" file: dependency: transitive description: @@ -175,14 +208,22 @@ packages: url: "https://pub.dev" source: hosted version: "0.11.0" + flutter_mailer: + dependency: transitive + description: + name: flutter_mailer + sha256: a935e9caa842877e8ed56109afb75b86e6488edbcd4696a5ac02b327a48fcd8a + url: "https://pub.dev" + source: hosted + version: "2.1.1" flutter_markdown: dependency: transitive description: name: flutter_markdown - sha256: "818cf6c28377ba2c91ed283c96fd712e9c175dd2d2488eb7fc93b6afb9ad2e08" + sha256: "4b1bfbb802d76320a1a46d9ce984106135093efd9d969765d07c2125af107bdf" url: "https://pub.dev" source: hosted - version: "0.6.13+1" + version: "0.6.17" flutter_secure_storage: dependency: transitive description: @@ -195,10 +236,10 @@ packages: dependency: transitive description: name: flutter_secure_storage_linux - sha256: "736436adaf91552433823f51ce22e098c2f0551db06b6596f58597a25b8ea797" + sha256: "0912ae29a572230ad52d8a4697e5518d7f0f429052fd51df7e5a7952c7efe2a3" url: "https://pub.dev" source: hosted - version: "1.1.2" + version: "1.1.3" flutter_secure_storage_macos: dependency: transitive description: @@ -236,51 +277,75 @@ packages: description: flutter source: sdk version: "0.0.0" + flutter_web_auth_2: + dependency: transitive + description: + name: flutter_web_auth_2 + sha256: "70e4df72940183b8e269c4163f78dd5bf9102ba3329bfe00c0f2373f30fb32d0" + url: "https://pub.dev" + source: hosted + version: "2.1.5" + flutter_web_auth_2_platform_interface: + dependency: transitive + description: + name: flutter_web_auth_2_platform_interface + sha256: f6fa7059ff3428c19cd756c02fef8eb0147131c7e64591f9060c90b5ab84f094 + url: "https://pub.dev" + source: hosted + version: "2.1.4" flutter_web_plugins: dependency: "direct main" description: flutter source: sdk version: "0.0.0" + fluttertoast: + dependency: transitive + description: + name: fluttertoast + sha256: "474f7d506230897a3cd28c965ec21c5328ae5605fc9c400cd330e9e9d6ac175c" + url: "https://pub.dev" + source: hosted + version: "8.2.2" frontend_server_client: dependency: transitive description: name: frontend_server_client - sha256: "4f4a162323c86ffc1245765cfe138872b8f069deb42f7dbb36115fa27f31469b" + sha256: "408e3ca148b31c20282ad6f37ebfa6f4bdc8fede5b74bc2f08d9d92b55db3612" url: "https://pub.dev" source: hosted - version: "2.1.3" + version: "3.2.0" glob: dependency: transitive description: name: glob - sha256: "4515b5b6ddb505ebdd242a5f2cc5d22d3d6a80013789debfbda7777f47ea308c" + sha256: "0e7014b3b7d4dac1ca4d6114f82bf1782ee86745b9b42a92c9289c23d8a0ab63" url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "2.1.2" go_router: dependency: "direct main" description: name: go_router - sha256: "54ecc1cd90c6948539c8cae3c11d7cb5010ad9b14533e8f49d060839d742be12" + sha256: bd7e671d26fd39c78cba82070fa34ef1f830b0e7ed1aeebccabc6561302a7ee5 url: "https://pub.dev" source: hosted - version: "6.0.4" + version: "6.5.9" html: dependency: transitive description: name: html - sha256: d9793e10dbe0e6c364f4c59bf3e01fb33a9b2a674bc7a1081693dba0614b6269 + sha256: "3a7812d5bcd2894edf53dfaf8cd640876cf6cef50a8f238745c8b8120ea74d3a" url: "https://pub.dev" source: hosted - version: "0.15.1" + version: "0.15.4" http: dependency: transitive description: name: http - sha256: "6aa2946395183537c8b880962d935877325d6a09a2867c3970c05c0fed6ac482" + sha256: "5895291c13fa8a3bd82e76d5627f69e0d85ca6a30dcac95c4ea19a5d555879c2" url: "https://pub.dev" source: hosted - version: "0.13.5" + version: "0.13.6" http_multi_server: dependency: transitive description: @@ -309,10 +374,10 @@ packages: dependency: "direct main" description: name: intl - sha256: a3715e3bc90294e971cb7dc063fbf3cd9ee0ebf8604ffeafabd9e6f16abbdbe6 + sha256: "3bc132a9dbce73a7e4a21a17d06e1878839ffbf975568bc875c60537824b0c4d" url: "https://pub.dev" source: hosted - version: "0.18.0" + version: "0.18.1" io: dependency: transitive description: @@ -325,59 +390,67 @@ packages: dependency: transitive description: name: js - sha256: "5528c2f391ededb7775ec1daa69e65a2d61276f7552de2b5f7b8d34ee9fd4ab7" + sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3 url: "https://pub.dev" source: hosted - version: "0.6.5" + version: "0.6.7" json_annotation: dependency: transitive description: name: json_annotation - sha256: c33da08e136c3df0190bd5bbe51ae1df4a7d96e7954d1d7249fea2968a72d317 + sha256: b10a7b2ff83d83c777edba3c6a0f97045ddadd56c944e1a23a3fdf43a1bf4467 url: "https://pub.dev" source: hosted - version: "4.8.0" + version: "4.8.1" lenra_components: dependency: "direct main" description: path: "." ref: "v1.0.0-beta.52" resolved-ref: "26cc6d810bea235f59d0890f99691d065de74c98" - url: "git@github.com:lenra-io/lenra_components.git" + url: "https://github.com/lenra-io/lenra_components.git" source: git version: "0.0.0" lints: dependency: "direct dev" description: name: lints - sha256: "5e4a9cd06d447758280a8ac2405101e0e2094d2a1dbdd3756aec3fe7775ba593" + sha256: "0a217c6c989d21039f1498c3ed9f3ed71b354e69873f13a8dfc3c9fe76f1b452" url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "2.1.1" logging: dependency: "direct main" description: name: logging - sha256: "04094f2eb032cbb06c6f6e8d3607edcfcb0455e2bb6cbc010cb01171dcb64e6d" + sha256: "623a88c9594aa774443aa3eb2d41807a48486b5613e67599fb4c41c0ad47c340" url: "https://pub.dev" source: hosted - version: "1.1.1" + version: "1.2.0" + mailer: + dependency: transitive + description: + name: mailer + sha256: "57f6dd1496699999a7bfd0aa6be0645384f477f4823e16d4321c40a434346382" + url: "https://pub.dev" + source: hosted + version: "6.0.1" markdown: dependency: transitive description: name: markdown - sha256: c2b81e184067b41d0264d514f7cdaa2c02d38511e39d6521a1ccc238f6d7b3f2 + sha256: acf35edccc0463a9d7384e437c015a3535772e09714cf60e07eeef3a15870dcd url: "https://pub.dev" source: hosted - version: "6.0.1" + version: "7.1.1" matcher: dependency: transitive description: name: matcher - sha256: "16db949ceee371e9b99d22f88fa3a73c4e59fd0afed0bd25fc336eb76c198b72" + sha256: "6501fbd55da300384b768785b83e5ce66991266cec21af89ab9ae7f5ce1c4cbb" url: "https://pub.dev" source: hosted - version: "0.12.13" + version: "0.12.15" material_color_utilities: dependency: transitive description: @@ -390,10 +463,10 @@ packages: dependency: transitive description: name: meta - sha256: "6c268b42ed578a53088d834796959e4a1814b5e9e164f147f580a386e5decf42" + sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3" url: "https://pub.dev" source: hosted - version: "1.8.0" + version: "1.9.1" mime: dependency: transitive description: @@ -414,10 +487,18 @@ packages: dependency: transitive description: name: node_preamble - sha256: "8ebdbaa3b96d5285d068f80772390d27c21e1fa10fb2df6627b1b9415043608d" + sha256: "6e7eac89047ab8a8d26cf16127b5ed26de65209847630400f9aefd7cd5c730db" url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "2.0.2" + oauth2_client: + dependency: transitive + description: + name: oauth2_client + sha256: "7d53b2a9eec4c1cbfddfd0a69dd0b6e8db73e6f75e4fb910c8070ac6513ae544" + url: "https://pub.dev" + source: hosted + version: "3.2.0" package_config: dependency: transitive description: @@ -430,10 +511,10 @@ packages: dependency: transitive description: name: package_info_plus - sha256: f619162573096d428ccde2e33f92e05b5a179cd6f0e3120c1005f181bee8ed16 + sha256: "6ff267fcd9d48cb61c8df74a82680e8b82e940231bb5f68356672fde0397334a" url: "https://pub.dev" source: hosted - version: "3.0.2" + version: "4.1.0" package_info_plus_platform_interface: dependency: transitive description: @@ -446,18 +527,18 @@ packages: dependency: transitive description: name: path - sha256: db9d4f58c908a4ba5953fcee2ae317c94889433e5024c27ce74a37f94267945b + sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917" url: "https://pub.dev" source: hosted - version: "1.8.2" + version: "1.8.3" petitparser: dependency: transitive description: name: petitparser - sha256: "49392a45ced973e8d94a85fdb21293fbb40ba805fc49f2965101ae748a3683b4" + sha256: cb3798bef7fc021ac45b308f4b51208a152792445cce0448c9a4ba5879dd8750 url: "https://pub.dev" source: hosted - version: "5.1.0" + version: "5.4.0" phoenix_wings: dependency: transitive description: @@ -470,18 +551,18 @@ packages: dependency: transitive description: name: plugin_platform_interface - sha256: dbf0f707c78beedc9200146ad3cb0ab4d5da13c246336987be6940f026500d3a + sha256: "43798d895c929056255600343db8f049921cbec94d31ec87f1dc5c16c01935dd" url: "https://pub.dev" source: hosted - version: "2.1.3" + version: "2.1.5" pointycastle: dependency: transitive description: name: pointycastle - sha256: db7306cf0249f838d1a24af52b5a5887c5bf7f31d8bb4e827d071dc0939ad346 + sha256: "7c1e5f0d23c9016c5bbd8b1473d0d3fb3fc851b876046039509e18e0c7485f2c" url: "https://pub.dev" source: hosted - version: "3.6.2" + version: "3.7.3" pool: dependency: transitive description: @@ -502,58 +583,66 @@ packages: dependency: transitive description: name: pub_semver - sha256: "307de764d305289ff24ad257ad5c5793ce56d04947599ad68b3baa124105fc17" + sha256: "40d3ab1bbd474c4c2328c91e3a7df8c6dd629b79ece4c4bd04bee496a224fb0c" url: "https://pub.dev" source: hosted - version: "2.1.3" + version: "2.1.4" + random_string: + dependency: transitive + description: + name: random_string + sha256: "03b52435aae8cbdd1056cf91bfc5bf845e9706724dd35ae2e99fa14a1ef79d02" + url: "https://pub.dev" + source: hosted + version: "2.3.1" sentry: dependency: transitive description: name: sentry - sha256: c64db3444237ff747c5a68f5214897bcb078de248785d0d816e3c55ab94dd71d + sha256: "39c23342fc96105da449914f7774139a17a0ca8a4e70d9ad5200171f7e47d6ba" url: "https://pub.dev" source: hosted - version: "6.19.0" + version: "7.9.0" sentry_flutter: dependency: "direct main" description: name: sentry_flutter - sha256: a76cf5180d571535fb8fc3bf10358ab385d78134fcf652d0e03ba7741525ab09 + sha256: ff68ab31918690da004a42e20204242a3ad9ad57da7e2712da8487060ac9767f url: "https://pub.dev" source: hosted - version: "6.19.0" + version: "7.9.0" shelf: dependency: transitive description: name: shelf - sha256: c24a96135a2ccd62c64b69315a14adc5c3419df63b4d7c05832a346fdb73682c + sha256: ad29c505aee705f41a4d8963641f91ac4cee3c8fad5947e033390a7bd8180fa4 url: "https://pub.dev" source: hosted - version: "1.4.0" + version: "1.4.1" shelf_packages_handler: dependency: transitive description: name: shelf_packages_handler - sha256: aef74dc9195746a384843102142ab65b6a4735bb3beea791e63527b88cc83306 + sha256: "89f967eca29607c933ba9571d838be31d67f53f6e4ee15147d5dc2934fee1b1e" url: "https://pub.dev" source: hosted - version: "3.0.1" + version: "3.0.2" shelf_static: dependency: transitive description: name: shelf_static - sha256: e792b76b96a36d4a41b819da593aff4bdd413576b3ba6150df5d8d9996d2e74c + sha256: a41d3f53c4adf0f57480578c1d61d90342cd617de7fc8077b1304643c2d85c1e url: "https://pub.dev" source: hosted - version: "1.1.1" + version: "1.1.2" shelf_web_socket: dependency: transitive description: name: shelf_web_socket - sha256: a988c0e8d8ffbdb8a28aa7ec8e449c260f3deb808781fe1284d22c5bba7156e8 + sha256: "9ca081be41c60190ebcb4766b2486a7d50261db7bd0f5d9615f2d653637a84c1" url: "https://pub.dev" source: hosted - version: "1.0.3" + version: "1.0.4" sky_engine: dependency: transitive description: flutter @@ -571,10 +660,10 @@ packages: dependency: transitive description: name: source_maps - sha256: "490098075234dcedb83c5d949b4c93dad5e6b7702748de000be2b57b8e6b2427" + sha256: "708b3f6b97248e5781f493b765c3337db11c5d2c81c3094f10904bfa8004c703" url: "https://pub.dev" source: hosted - version: "0.10.11" + version: "0.10.12" source_span: dependency: transitive description: @@ -619,114 +708,114 @@ packages: dependency: "direct dev" description: name: test - sha256: a5fcd2d25eeadbb6589e80198a47d6a464ba3e2049da473943b8af9797900c2d + sha256: "3dac9aecf2c3991d09b9cdde4f98ded7b30804a88a0d7e4e7e1678e78d6b97f4" url: "https://pub.dev" source: hosted - version: "1.22.0" + version: "1.24.1" test_api: dependency: transitive description: name: test_api - sha256: ad540f65f92caa91bf21dfc8ffb8c589d6e4dc0c2267818b4cc2792857706206 + sha256: eb6ac1540b26de412b3403a163d919ba86f6a973fe6cc50ae3541b80092fdcfb url: "https://pub.dev" source: hosted - version: "0.4.16" + version: "0.5.1" test_core: dependency: transitive description: name: test_core - sha256: "0ef9755ec6d746951ba0aabe62f874b707690b5ede0fecc818b138fcc9b14888" + sha256: "5138dbffb77b2289ecb12b81c11ba46036590b72a64a7a90d6ffb880f1a29e93" url: "https://pub.dev" source: hosted - version: "0.4.20" + version: "0.5.1" typed_data: dependency: transitive description: name: typed_data - sha256: "26f87ade979c47a150c9eaab93ccd2bebe70a27dc0b4b29517f2904f04eb11a5" + sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c url: "https://pub.dev" source: hosted - version: "1.3.1" + version: "1.3.2" universal_html: dependency: transitive description: name: universal_html - sha256: "5ff50b7c14d201421cf5230ec389a0591c4deb5c817c9d7ccca3b26fe5f31e34" + sha256: a5cc5a84188e5d3e58f3ed77fe3dd4575dc1f68aa7c89e51b5b4105b9aab3b9d url: "https://pub.dev" source: hosted - version: "2.0.8" + version: "2.2.3" universal_io: dependency: transitive description: name: universal_io - sha256: "79f78ddad839ee3aae3ec7c01eb4575faf0d5c860f8e5223bc9f9c17f7f03cef" + sha256: "1722b2dcc462b4b2f3ee7d188dad008b6eb4c40bbd03a3de451d82c78bba9aad" url: "https://pub.dev" source: hosted - version: "2.0.4" + version: "2.2.2" url_launcher: dependency: "direct main" description: name: url_launcher - sha256: eb1e00ab44303d50dd487aab67ebc575456c146c6af44422f9c13889984c00f3 + sha256: "781bd58a1eb16069412365c98597726cd8810ae27435f04b3b4d3a470bacd61e" url: "https://pub.dev" source: hosted - version: "6.1.11" + version: "6.1.12" url_launcher_android: dependency: transitive description: name: url_launcher_android - sha256: "3e2f6dfd2c7d9cd123296cab8ef66cfc2c1a13f5845f42c7a0f365690a8a7dd1" + sha256: "78cb6dea3e93148615109e58e42c35d1ffbf5ef66c44add673d0ab75f12ff3af" url: "https://pub.dev" source: hosted - version: "6.0.23" + version: "6.0.37" url_launcher_ios: dependency: transitive description: name: url_launcher_ios - sha256: "0a5af0aefdd8cf820dd739886efb1637f1f24489900204f50984634c07a54815" + sha256: "9af7ea73259886b92199f9e42c116072f05ff9bea2dcb339ab935dfc957392c2" url: "https://pub.dev" source: hosted - version: "6.1.0" + version: "6.1.4" url_launcher_linux: dependency: transitive description: name: url_launcher_linux - sha256: "318c42cba924e18180c029be69caf0a1a710191b9ec49bb42b5998fdcccee3cc" + sha256: "207f4ddda99b95b4d4868320a352d374b0b7e05eefad95a4a26f57da413443f5" url: "https://pub.dev" source: hosted - version: "3.0.2" + version: "3.0.5" url_launcher_macos: dependency: transitive description: name: url_launcher_macos - sha256: "41988b55570df53b3dd2a7fc90c76756a963de6a8c5f8e113330cb35992e2094" + sha256: "1c4fdc0bfea61a70792ce97157e5cc17260f61abbe4f39354513f39ec6fd73b1" url: "https://pub.dev" source: hosted - version: "3.0.2" + version: "3.0.6" url_launcher_platform_interface: dependency: transitive description: name: url_launcher_platform_interface - sha256: "4eae912628763eb48fc214522e58e942fd16ce195407dbf45638239523c759a6" + sha256: bfdfa402f1f3298637d71ca8ecfe840b4696698213d5346e9d12d4ab647ee2ea url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "2.1.3" url_launcher_web: dependency: transitive description: name: url_launcher_web - sha256: "44d79408ce9f07052095ef1f9a693c258d6373dc3944249374e30eff7219ccb0" + sha256: cc26720eefe98c1b71d85f9dc7ef0cada5132617046369d9dc296b3ecaa5cbb4 url: "https://pub.dev" source: hosted - version: "2.0.14" + version: "2.0.18" url_launcher_windows: dependency: transitive description: name: url_launcher_windows - sha256: b6217370f8eb1fd85c8890c539f5a639a01ab209a36db82c921ebeacefc7a615 + sha256: "7967065dd2b5fccc18c653b97958fdf839c5478c28e767c61ee879f4e7882422" url: "https://pub.dev" source: hosted - version: "3.0.3" + version: "3.0.7" uuid: dependency: transitive description: @@ -747,26 +836,26 @@ packages: dependency: transitive description: name: vm_service - sha256: e7fb6c2282f7631712b69c19d1bff82f3767eea33a2321c14fa59ad67ea391c7 + sha256: "0fae432c85c4ea880b33b497d32824b97795b04cdaa74d270219572a1f50268d" url: "https://pub.dev" source: hosted - version: "9.4.0" + version: "11.9.0" watcher: dependency: transitive description: name: watcher - sha256: "6a7f46926b01ce81bfc339da6a7f20afbe7733eff9846f6d6a5466aa4c6667c0" + sha256: "3d2ad6751b3c16cf07c7fca317a1413b3f26530319181b37e3b9039b84fc01d8" url: "https://pub.dev" source: hosted - version: "1.0.2" + version: "1.1.0" web_socket_channel: dependency: transitive description: name: web_socket_channel - sha256: ca49c0bc209c687b887f30527fb6a9d80040b072cc2990f34b9bec3e7663101b + sha256: d88238e5eac9a42bb43ca4e721edba3c08c6354d4a53063afaa568516217621b url: "https://pub.dev" source: hosted - version: "2.3.0" + version: "2.4.0" webkit_inspection_protocol: dependency: transitive description: @@ -779,26 +868,42 @@ packages: dependency: transitive description: name: win32 - sha256: c9ebe7ee4ab0c2194e65d3a07d8c54c5d00bb001b76081c4a04cdb8448b59e46 + sha256: f2add6fa510d3ae152903412227bda57d0d5a8da61d2c39c1fb022c9429a41c0 + url: "https://pub.dev" + source: hosted + version: "5.0.6" + win32_registry: + dependency: transitive + description: + name: win32_registry + sha256: e4506d60b7244251bc59df15656a3093501c37fb5af02105a944d73eb95be4c9 url: "https://pub.dev" source: hosted - version: "3.1.3" + version: "1.1.1" + window_to_front: + dependency: transitive + description: + name: window_to_front + sha256: "7aef379752b7190c10479e12b5fd7c0b9d92adc96817d9e96c59937929512aee" + url: "https://pub.dev" + source: hosted + version: "0.0.3" xml: dependency: transitive description: name: xml - sha256: ac0e3f4bf00ba2708c33fbabbbe766300e509f8c82dbd4ab6525039813f7e2fb + sha256: "5bc72e1e45e941d825fd7468b9b4cc3b9327942649aeb6fc5cdbf135f0a86e84" url: "https://pub.dev" source: hosted - version: "6.1.0" + version: "6.3.0" yaml: dependency: transitive description: name: yaml - sha256: "23812a9b125b48d4007117254bca50abb6c712352927eece9e155207b1db2370" + sha256: "75769501ea3489fca56601ff33454fe45507ea3bfb014161abc3b43ae25989d5" url: "https://pub.dev" source: hosted - version: "3.1.1" + version: "3.1.2" sdks: - dart: ">=2.18.0 <3.0.0" - flutter: ">=3.3.0" + dart: ">=3.0.0 <4.0.0" + flutter: ">=3.10.0" diff --git a/pubspec.yaml b/pubspec.yaml index f160757..e043d56 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -5,7 +5,8 @@ version: 1.0.0 # homepage: https://www.example.com environment: - sdk: ">=2.17.1 <3.3.9" + sdk: ">=3.0.0 <4.0.0" + flutter: ^3.10.0 # dependencies: # path: ^1.8.0 dev_dependencies: @@ -17,22 +18,27 @@ dev_dependencies: dependencies: flutter: sdk: flutter + flutter_web_plugins: sdk: flutter lenra_components: git: - url: git@github.com:lenra-io/lenra_components.git + url: https://github.com/lenra-io/lenra_components.git ref: v1.0.0-beta.52 client_common: git: - url: git@github.com:lenra-io/client-common.git - ref: v1.0.0-beta.41 + url: https://github.com/lenra-io/client-common.git + ref: v1.0.0-alpha.1 url_launcher: ^6.1.2 - intl: ^0.18.0 logging: ^1.0.2 provider: ^6.0.1 - sentry_flutter: ^6.19.0 + sentry_flutter: ^7.9.0 go_router: ^6.0.1 + catcher: + git: + url: https://github.com/ThexXTURBOXx/catcher.git + ref: ae44d0bbce00c3801629b65f60783d15184a7f8f + intl: ^0.18.1 flutter: uses-material-design: true diff --git a/web/redirect.html b/web/redirect.html new file mode 100644 index 0000000..b43ade8 --- /dev/null +++ b/web/redirect.html @@ -0,0 +1,16 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/windows/flutter/generated_plugin_registrant.cc b/windows/flutter/generated_plugin_registrant.cc index b857556..27766f7 100644 --- a/windows/flutter/generated_plugin_registrant.cc +++ b/windows/flutter/generated_plugin_registrant.cc @@ -6,15 +6,21 @@ #include "generated_plugin_registrant.h" +#include #include #include #include +#include void RegisterPlugins(flutter::PluginRegistry* registry) { + CatcherPluginRegisterWithRegistrar( + registry->GetRegistrarForPlugin("CatcherPlugin")); FlutterSecureStorageWindowsPluginRegisterWithRegistrar( registry->GetRegistrarForPlugin("FlutterSecureStorageWindowsPlugin")); SentryFlutterPluginRegisterWithRegistrar( registry->GetRegistrarForPlugin("SentryFlutterPlugin")); UrlLauncherWindowsRegisterWithRegistrar( registry->GetRegistrarForPlugin("UrlLauncherWindows")); + WindowToFrontPluginRegisterWithRegistrar( + registry->GetRegistrarForPlugin("WindowToFrontPlugin")); } diff --git a/windows/flutter/generated_plugins.cmake b/windows/flutter/generated_plugins.cmake index 5f74a95..4c0e0d4 100644 --- a/windows/flutter/generated_plugins.cmake +++ b/windows/flutter/generated_plugins.cmake @@ -3,9 +3,11 @@ # list(APPEND FLUTTER_PLUGIN_LIST + catcher flutter_secure_storage_windows sentry_flutter url_launcher_windows + window_to_front ) list(APPEND FLUTTER_FFI_PLUGIN_LIST From 8e0594fcdc52d301e07de1d6a13a86e9c41f0036 Mon Sep 17 00:00:00 2001 From: Jonas Martinez <36544012+jonas-martinez@users.noreply.github.com> Date: Mon, 21 Aug 2023 16:43:18 +0200 Subject: [PATCH 02/15] feat: External clients (#96) * debug oauth * Remove useless code * Preparing oauth * Trying some things * much better * More error handling * This is working nicely * feat: Handle OAuth client ID with an env var * fix: Scopes * fix review * feat: upgrade common * change catcher dep and update flutter requirements * update flutter requirement * fix logout * fix analyze * use client-common alpha * fix analyze * fix * feat: External clients * Preparing client response * fix * This is working * fix analyze * Update README.md Co-authored-by: Thomas DA ROCHA --------- Co-authored-by: Thomas DA ROCHA --- README.md | 2 +- .../oauth_client_response.dart | 30 ++ .../oauth_clients_response.dart | 9 + lib/navigation/backoffice_navigator.dart | 15 + lib/views/settings/external_clients_page.dart | 322 ++++++++++++++++++ lib/views/settings_page.dart | 5 + 6 files changed, 382 insertions(+), 1 deletion(-) create mode 100644 lib/api/response_models/oauth_client_response.dart create mode 100644 lib/api/response_models/oauth_clients_response.dart create mode 100644 lib/views/settings/external_clients_page.dart diff --git a/README.md b/README.md index a1cec35..e2824cb 100644 --- a/README.md +++ b/README.md @@ -51,7 +51,7 @@ mix create_oauth2_client backoffice Run flutter app with chrome ```sh -flutter run -d chrome --web-port 10000 --dart-define=LENRA_SERVER_URL=http://localhost:4000 --dart-define=OAUTH_CLIENT_ID=6c434f76-9917-4856-901b-f723fe45446c +flutter run -d chrome --web-port 10000 --dart-define=LENRA_SERVER_URL=http://localhost:4000 --dart-define=OAUTH_CLIENT_ID= ``` Run flutter test diff --git a/lib/api/response_models/oauth_client_response.dart b/lib/api/response_models/oauth_client_response.dart new file mode 100644 index 0000000..8cd4997 --- /dev/null +++ b/lib/api/response_models/oauth_client_response.dart @@ -0,0 +1,30 @@ +import 'package:client_common/api/response_models/api_response.dart'; + +class OAuthClientResponse extends ApiResponse { + String clientId; + int environmentId; + String name; + List scopes; + List redirectUris; + List allowedOrigins; + + OAuthClientResponse.fromJson(Map json) + : clientId = json["oauth2_client_id"], + environmentId = json["environment_id"], + name = json['name'], + scopes = json['scopes'], + redirectUris = json['redirect_uris'], + allowedOrigins = json['allowed_origins']; + + @override + bool operator ==(Object other) { + return other is OAuthClientResponse && + other.clientId == clientId && + other.environmentId == environmentId && + other.name == name; + } + + @override + // ignore: unnecessary_overrides + int get hashCode => super.hashCode; +} diff --git a/lib/api/response_models/oauth_clients_response.dart b/lib/api/response_models/oauth_clients_response.dart new file mode 100644 index 0000000..d4ca45a --- /dev/null +++ b/lib/api/response_models/oauth_clients_response.dart @@ -0,0 +1,9 @@ +import 'package:client_backoffice/api/response_models/oauth_client_response.dart'; +import 'package:client_common/api/response_models/api_response.dart'; + +class OAuthClientsResponse extends ApiResponse { + List clients; + + OAuthClientsResponse.fromJson(List json) + : clients = json.map((client) => OAuthClientResponse.fromJson(client)).toList(); +} diff --git a/lib/navigation/backoffice_navigator.dart b/lib/navigation/backoffice_navigator.dart index f31462e..5b38a7e 100644 --- a/lib/navigation/backoffice_navigator.dart +++ b/lib/navigation/backoffice_navigator.dart @@ -4,6 +4,7 @@ import 'package:client_backoffice/views/create_project_page.dart'; import 'package:client_backoffice/views/dev_validation_page.dart'; import 'package:client_backoffice/views/overview_page.dart'; import 'package:client_backoffice/views/select_project_page.dart'; +import 'package:client_backoffice/views/settings/external_clients_page.dart'; import 'package:client_backoffice/views/settings/git_integration_page.dart'; import 'package:client_backoffice/views/settings/manage_access_page.dart'; import 'package:client_backoffice/views/settings_page.dart'; @@ -89,6 +90,19 @@ class BackofficeNavigator extends CommonNavigator { }, ); + static GoRoute oauthSettings = GoRoute( + name: "oauth-settings", + path: "settings/oauth", + pageBuilder: (context, state) { + return NoTransitionPage( + key: state.pageKey, + child: ExternalClientsPage( + appId: int.tryParse(state.params["appId"]!)!, + ), + ); + }, + ); + static ShellRoute settings = ShellRoute( pageBuilder: (context, state, child) { return NoTransitionPage( @@ -102,6 +116,7 @@ class BackofficeNavigator extends CommonNavigator { routes: [ gitSettings, accessSettings, + oauthSettings, ], ); diff --git a/lib/views/settings/external_clients_page.dart b/lib/views/settings/external_clients_page.dart new file mode 100644 index 0000000..b477740 --- /dev/null +++ b/lib/views/settings/external_clients_page.dart @@ -0,0 +1,322 @@ +import 'package:client_backoffice/api/response_models/oauth_client_response.dart'; +import 'package:client_backoffice/api/response_models/oauth_clients_response.dart'; +import 'package:client_common/api/lenra_http_client.dart'; +import 'package:client_common/api/response_models/get_main_env_response.dart'; +import 'package:client_common/models/user_application_model.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:lenra_components/lenra_components.dart'; +import 'package:logging/logging.dart'; +import 'package:provider/provider.dart'; + +class ExternalClientsPage extends StatefulWidget { + final int appId; + ExternalClientsPage({required this.appId}); + + @override + State createState() { + return _ExternalClientsPageState(); + } +} + +class _ExternalClientsPageState extends State { + final logger = Logger("ExternalClientsPage"); + final GlobalKey tooltipKey = GlobalKey(); + + @override + Widget build(BuildContext context) { + final lenraTextThemeData = LenraTheme.of(context).lenraTextThemeData; + + return FutureBuilder( + future: getOauthClients(), + builder: (context, snapshot) { + if (!snapshot.hasError && snapshot.hasData) { + return LenraFlex( + spacing: 8, + direction: Axis.vertical, + children: [ + Text("OAuth2 external clients", style: lenraTextThemeData.headline3), + Text( + "Manage your OAuth2 external clients. It is recommended to create one OAuth client for each platform (web, windows, linux, android, ...)."), + SizedBox(height: 8), + Flex( + direction: Axis.horizontal, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Text("Clients", style: lenraTextThemeData.headline3), + LenraButton( + onPressed: () { + showCreateDialog(); + }, + text: "Add new client", + ) + ], + ), + Divider(), + DataTable( + showCheckboxColumn: false, + horizontalMargin: 4, + columnSpacing: 16, + columns: [ + DataColumn( + label: Expanded( + child: Text('Name', style: lenraTextThemeData.headlineBody), + ), + ), + DataColumn( + label: Expanded( + child: Text('Client ID', style: lenraTextThemeData.headlineBody), + ), + ), + DataColumn( + label: Expanded( + child: Text(''), + ), + ), + ], + rows: List.generate(snapshot.data!.clients.length, (index) { + OAuthClientResponse client = snapshot.data!.clients[index]; + return DataRow( + cells: [ + DataCell( + Text(client.name), + ), + DataCell( + Row( + children: [ + Text(client.clientId), + Tooltip( + // We need to set this waitDuration because the triggerMode manual does not remove the hover + waitDuration: Duration(days: 200), + key: tooltipKey, + triggerMode: TooltipTriggerMode.manual, + showDuration: const Duration(seconds: 1), + message: 'Copied to clipboard.', + child: IconButton( + onPressed: () { + Clipboard.setData(ClipboardData(text: client.clientId)); + tooltipKey.currentState?.ensureTooltipVisible(); + }, + icon: Icon(Icons.copy), + ), + ), + ], + ), + ), + DataCell(Flex( + direction: Axis.horizontal, + children: [ + IconButton( + onPressed: () async { + await deleteOauthClient(client.clientId); + setState(() {}); + }, + icon: Icon(Icons.delete), + ), + ], + )), + ], + onSelectChanged: (bool? selected) { + if (selected ?? false) { + showEditDialog(client); + } + }); + })), + ], + ); + } + + print(snapshot.error); + + return CircularProgressIndicator(); + }, + ); + } + + Future getOauthClients() async { + GetMainEnvResponse res = await context.read().getMainEnv(widget.appId); + + var getResponse = await LenraApi.instance.get('/environments/${res.mainEnv.id}/oauth2'); + return OAuthClientsResponse.fromJson(getResponse); + } + + Future deleteOauthClient(String clientId) async { + GetMainEnvResponse res = await context.read().getMainEnv(widget.appId); + + await LenraApi.instance.delete('/environments/${res.mainEnv.id}/oauth2/$clientId'); + } + + void showCreateDialog() { + showDialog( + context: context, + useRootNavigator: true, + builder: (context) { + return buildDialog(); + }, + ); + } + + void showEditDialog(OAuthClientResponse client) { + showDialog( + context: context, + useRootNavigator: true, + builder: (context) { + return buildDialog(client: client); + }, + ); + } + + Widget buildDialog({OAuthClientResponse? client}) { + final GlobalKey tooltipKey = GlobalKey(); + final formKey = GlobalKey(); + + final nameController = TextEditingController(); + final redirectUrisController = TextEditingController(); + final allowedOriginsController = TextEditingController(); + + if (client != null) { + nameController.text = client.name; + redirectUrisController.text = client.redirectUris.join('\n'); + allowedOriginsController.text = client.allowedOrigins.join('\n'); + } + + final lenraTextThemeData = LenraTheme.of(context).lenraTextThemeData; + + return Dialog( + child: Container( + padding: EdgeInsets.all(24), + child: SizedBox( + width: 400, + child: SingleChildScrollView( + child: Form( + key: formKey, + child: IntrinsicHeight( + child: Column( + children: [ + Row( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text("${client == null ? 'Create' : 'Edit'} client", style: lenraTextThemeData.headline3), + IconButton( + onPressed: () { + Navigator.of(context, rootNavigator: true).pop(); + }, + icon: Icon(Icons.close), + ), + ], + ), + SizedBox(height: 16), + TextFormField( + controller: nameController, + decoration: InputDecoration( + label: Text('Name'), + ), + ), + SizedBox(height: 16), + client != null + ? Flex( + direction: Axis.horizontal, + children: [ + Text('Client ID: ', style: lenraTextThemeData.headlineBody), + Text(client.clientId), + Tooltip( + // We need to set this waitDuration because the triggerMode manual does not remove the hover + waitDuration: Duration(days: 200), + key: tooltipKey, + triggerMode: TooltipTriggerMode.manual, + showDuration: const Duration(seconds: 1), + message: 'Copied to clipboard.', + child: IconButton( + onPressed: () { + Clipboard.setData(ClipboardData(text: client.clientId)); + tooltipKey.currentState?.ensureTooltipVisible(); + }, + icon: Icon(Icons.copy), + ), + ), + ], + ) + : SizedBox(), + SizedBox(height: 16), + TextFormField( + controller: redirectUrisController, + decoration: InputDecoration( + label: Text('Redirect URIs'), + helperText: 'Use one line per URI', + ), + keyboardType: TextInputType.multiline, + maxLines: null, + ), + SizedBox(height: 16), + TextFormField( + controller: allowedOriginsController, + maxLines: null, + decoration: InputDecoration( + label: Text('Allowed origins'), + helperText: 'Use one line per origin', + ), + ), + SizedBox(height: 16), + Divider(), + SizedBox(height: 24), + Flex( + direction: Axis.horizontal, + mainAxisAlignment: MainAxisAlignment.end, + children: [ + LenraButton( + type: LenraComponentType.secondary, + onPressed: () { + Navigator.of(context, rootNavigator: true).pop(); + }, + text: "Cancel", + ), + SizedBox(width: 16), + LenraButton( + onPressed: () async { + if (formKey.currentState!.validate()) { + GetMainEnvResponse res = + await context.read().getMainEnv(widget.appId); + + if (client == null) { + await LenraApi.instance.post( + '/environments/${res.mainEnv.id}/oauth2', + body: { + 'name': nameController.text, + 'scopes': ['manage:apps'], + 'redirect_uris': redirectUrisController.text.split('\n').map((e) => e.trim()), + 'allowed_origins': allowedOriginsController.text.split('\n').map((e) => e.trim()), + }, + ); + } else { + await LenraApi.instance.put( + '/environments/${res.mainEnv.id}/oauth2/${client.clientId}', + body: { + 'name': nameController.text, + 'scopes': ['manage:apps'], + 'redirect_uris': redirectUrisController.text.split('\n'), + 'allowed_origins': allowedOriginsController.text.split('\n') + }, + ); + } + + Navigator.pop(context); + + setState(() {}); + } + }, + text: "Save", + ), + ], + ), + ], + ), + ), + ), + ), + ), + ), + ); + } +} diff --git a/lib/views/settings_page.dart b/lib/views/settings_page.dart index 45d6a0d..5ef130a 100644 --- a/lib/views/settings_page.dart +++ b/lib/views/settings_page.dart @@ -44,6 +44,11 @@ class SettingsPage extends StatelessWidget { "Manage access", BackofficeNavigator.accessSettings, ), + createSubMenuItem( + context, + "External clients", + BackofficeNavigator.oauthSettings, + ) ], ), ), From 01046d26192f701af3cc1321b32a43b6637ecf2a Mon Sep 17 00:00:00 2001 From: Thomas DA ROCHA Date: Mon, 21 Aug 2023 16:54:24 +0200 Subject: [PATCH 03/15] ci: Fix build Flutter --- .github/workflows/semantic_release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/semantic_release.yml b/.github/workflows/semantic_release.yml index ede9a6e..d1b7d95 100644 --- a/.github/workflows/semantic_release.yml +++ b/.github/workflows/semantic_release.yml @@ -35,7 +35,7 @@ jobs: flutter-version: ${{ env.flutter_version }} - name: Build Flutter Web run: | - flutter build web + flutter build web --no-tree-shake-icons cd .. - name: Setup Node.js uses: actions/setup-node@v2 From 4a0d3f3fdc98a87d0311b6988c79b5339a38c4a6 Mon Sep 17 00:00:00 2001 From: Thomas DA ROCHA Date: Tue, 22 Aug 2023 14:25:08 +0200 Subject: [PATCH 04/15] fix: update Content-Security-Policy --- nginx.conf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nginx.conf b/nginx.conf index e28a2cc..50a0df3 100644 --- a/nginx.conf +++ b/nginx.conf @@ -10,7 +10,7 @@ server { charset utf-8; charset_types text/css application/javascript; add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always; - add_header Content-Security-Policy "default-src 'self' 'unsafe-inline' 'unsafe-eval' lenra.io *.lenra.io https://unpkg.com/ https://fonts.gstatic.com/ wss://*.lenra.io; object-src 'none'; base-uri 'self';"; + add_header Content-Security-Policy "default-src 'self' 'unsafe-inline' 'unsafe-eval' lenra.io *.lenra.io https://www.gstatic.com/flutter-canvaskit/ https://fonts.gstatic.com/ wss://*.lenra.io; object-src 'none'; base-uri 'self';"; add_header Vary "Accept-Encoding"; add_header X-Content-Type-Options "nosniff"; add_header X-Frame-Options "DENY"; From 6bb2077a5ade01a306a91fb62c563a3d1e4702cf Mon Sep 17 00:00:00 2001 From: Jonas Martinez <36544012+jonas-martinez@users.noreply.github.com> Date: Tue, 22 Aug 2023 15:36:22 +0200 Subject: [PATCH 05/15] feat: Dynamic oauth client (#98) * feat: dynamic OAuth client id * fix * Update dependencies * Remove unused test file --- entrypoint.sh | 1 + lib/main.dart | 2 +- pubspec.lock | 4 +-- pubspec.yaml | 2 +- test/views/activation_code_page_test.dart | 31 ----------------------- web/index.html | 2 ++ 6 files changed, 7 insertions(+), 35 deletions(-) delete mode 100644 test/views/activation_code_page_test.dart diff --git a/entrypoint.sh b/entrypoint.sh index 253d57b..2085190 100755 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -2,5 +2,6 @@ sed -i "s|\${LENRA_SERVER_URL}|${LENRA_SERVER_URL}|g" index.html sed -i "s|\${SENTRY_CLIENT_DSN}|${SENTRY_CLIENT_DSN}|g" index.html +sed -i "s|\${OAUTH_CLIENT_ID}|${OAUTH_CLIENT_ID}|g" index.html /bin/bash -c "/opt/bitnami/scripts/nginx/entrypoint.sh $@" diff --git a/lib/main.dart b/lib/main.dart index 7013fe3..1d06a13 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -127,7 +127,7 @@ class Backoffice extends StatelessWidget { providers: [ ChangeNotifierProvider( create: (context) => OAuthModel( - const String.fromEnvironment('OAUTH_CLIENT_ID'), + Config.instance.oauthClientId, 'http://localhost:10000/redirect.html', scopes: ['profile', 'store', 'manage:account', 'manage:apps'], ), diff --git a/pubspec.lock b/pubspec.lock index 6d3e65e..24b249a 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -94,8 +94,8 @@ packages: dependency: "direct main" description: path: "." - ref: "v1.0.0-alpha.1" - resolved-ref: e861c16762a32f835cc568a02678f1269ecf25ec + ref: "v1.0.0-alpha.2" + resolved-ref: "139a65af5a37056b299b90ddff49f31d885d4dab" url: "https://github.com/lenra-io/client-common.git" source: git version: "1.0.0" diff --git a/pubspec.yaml b/pubspec.yaml index e043d56..0cfbcff 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -28,7 +28,7 @@ dependencies: client_common: git: url: https://github.com/lenra-io/client-common.git - ref: v1.0.0-alpha.1 + ref: v1.0.0-alpha.2 url_launcher: ^6.1.2 logging: ^1.0.2 provider: ^6.0.1 diff --git a/test/views/activation_code_page_test.dart b/test/views/activation_code_page_test.dart deleted file mode 100644 index 8465485..0000000 --- a/test/views/activation_code_page_test.dart +++ /dev/null @@ -1,31 +0,0 @@ -import 'package:client_backoffice/views/dev_validation_page.dart'; -import 'package:client_common/models/auth_model.dart'; -import 'package:client_common/test/lenra_page_test_help.dart'; -import 'package:client_common/views/simple_page.dart'; -import 'package:flutter/cupertino.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:provider/provider.dart'; - -void main() { - testWidgets('ActivationCodePage check SimplePage', (WidgetTester tester) async { - await tester.pumpWidget(createAppTestWidgets(ChangeNotifierProvider( - create: (_) => AuthModel(), - child: DevValidationPage(), - ))); - - final widgetFinder = find.byType(SimplePage); - - expect(widgetFinder, findsOneWidget); - }); - - testWidgets('ActivationCodePage check texts', (WidgetTester tester) async { - await tester.pumpWidget(createAppTestWidgets(ChangeNotifierProvider( - create: (_) => AuthModel(), - child: DevValidationPage(), - ))); - - final textFinder = find.byType(Text); - - expect(textFinder, findsNWidgets(4)); - }); -} diff --git a/web/index.html b/web/index.html index 90557bd..082bc21 100644 --- a/web/index.html +++ b/web/index.html @@ -35,6 +35,8 @@ + + From b33110959f2bae92eeea8d46f903c7ef76ad5acf Mon Sep 17 00:00:00 2001 From: "jonas.martinez" Date: Tue, 22 Aug 2023 19:35:49 +0200 Subject: [PATCH 11/15] fix: OAuth client iterable to list --- lib/views/settings/external_clients_page.dart | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/views/settings/external_clients_page.dart b/lib/views/settings/external_clients_page.dart index b477740..6743dea 100644 --- a/lib/views/settings/external_clients_page.dart +++ b/lib/views/settings/external_clients_page.dart @@ -285,8 +285,10 @@ class _ExternalClientsPageState extends State { body: { 'name': nameController.text, 'scopes': ['manage:apps'], - 'redirect_uris': redirectUrisController.text.split('\n').map((e) => e.trim()), - 'allowed_origins': allowedOriginsController.text.split('\n').map((e) => e.trim()), + 'redirect_uris': + redirectUrisController.text.split('\n').map((e) => e.trim()).toList(), + 'allowed_origins': + allowedOriginsController.text.split('\n').map((e) => e.trim()).toList(), }, ); } else { From 88487af48d688d7f93c840b6232d93053b2f1f32 Mon Sep 17 00:00:00 2001 From: Thomas DA ROCHA Date: Wed, 23 Aug 2023 11:12:09 +0200 Subject: [PATCH 12/15] fix: Socket scope --- lib/views/settings/external_clients_page.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/views/settings/external_clients_page.dart b/lib/views/settings/external_clients_page.dart index 6743dea..ff6f909 100644 --- a/lib/views/settings/external_clients_page.dart +++ b/lib/views/settings/external_clients_page.dart @@ -284,7 +284,7 @@ class _ExternalClientsPageState extends State { '/environments/${res.mainEnv.id}/oauth2', body: { 'name': nameController.text, - 'scopes': ['manage:apps'], + 'scopes': ['app:websocket'], 'redirect_uris': redirectUrisController.text.split('\n').map((e) => e.trim()).toList(), 'allowed_origins': @@ -296,7 +296,7 @@ class _ExternalClientsPageState extends State { '/environments/${res.mainEnv.id}/oauth2/${client.clientId}', body: { 'name': nameController.text, - 'scopes': ['manage:apps'], + 'scopes': ['app:websocket'], 'redirect_uris': redirectUrisController.text.split('\n'), 'allowed_origins': allowedOriginsController.text.split('\n') }, From e2c2e4e9760f27b2840bd024c9845ab37404d546 Mon Sep 17 00:00:00 2001 From: Jonas Martinez <36544012+jonas-martinez@users.noreply.github.com> Date: Mon, 28 Aug 2023 18:00:29 +0200 Subject: [PATCH 13/15] fix: Oauth client creation (#99) --- lib/views/settings/external_clients_page.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/views/settings/external_clients_page.dart b/lib/views/settings/external_clients_page.dart index ff6f909..52e44c6 100644 --- a/lib/views/settings/external_clients_page.dart +++ b/lib/views/settings/external_clients_page.dart @@ -303,7 +303,7 @@ class _ExternalClientsPageState extends State { ); } - Navigator.pop(context); + Navigator.of(context, rootNavigator: true).pop(); setState(() {}); } From 86851f7e6ca620b02f0e5bdf67d41302404b22f1 Mon Sep 17 00:00:00 2001 From: Jonas Martinez <36544012+jonas-martinez@users.noreply.github.com> Date: Wed, 30 Aug 2023 12:57:12 +0200 Subject: [PATCH 14/15] feat: Oauth page design (#100) * feat: OAuth Page Design * fix * Update deps * fix padding * Add asset * Use asset * Add method for right side * Add go to app page * Add space before arrow * Use config base url --- assets/texts/oauth-page-code.js | 25 +++ lib/navigation/backoffice_navigator.dart | 19 ++- lib/navigation/guard.dart | 11 ++ lib/views/oauth_page.dart | 204 +++++++++++++++++++++++ pubspec.lock | 6 +- pubspec.yaml | 4 +- 6 files changed, 259 insertions(+), 10 deletions(-) create mode 100644 assets/texts/oauth-page-code.js create mode 100644 lib/views/oauth_page.dart diff --git a/assets/texts/oauth-page-code.js b/assets/texts/oauth-page-code.js new file mode 100644 index 0000000..9d505c5 --- /dev/null +++ b/assets/texts/oauth-page-code.js @@ -0,0 +1,25 @@ +module.exports = (data, counter) => { + return { + "type": "flex", + "spacing": 2, + "mainAxisAlignment": "spaceEvenly", + "crossAxisAlignment": "center", + "children": [ + { + "type": "text", + "value": `${counter.text}: ${data[0].count}` + }, + { + "type": "button", + "text": "+", + "onPressed": { + "action": "increment", + "props": { + "id": data[0]._id, + "datastore": data[0].datastore + } + } + } + ] + } +} \ No newline at end of file diff --git a/lib/navigation/backoffice_navigator.dart b/lib/navigation/backoffice_navigator.dart index 5b38a7e..b3b727f 100644 --- a/lib/navigation/backoffice_navigator.dart +++ b/lib/navigation/backoffice_navigator.dart @@ -2,6 +2,7 @@ import 'package:catcher/catcher.dart'; import 'package:client_backoffice/navigation/guard.dart'; import 'package:client_backoffice/views/create_project_page.dart'; import 'package:client_backoffice/views/dev_validation_page.dart'; +import 'package:client_backoffice/views/oauth_page.dart'; import 'package:client_backoffice/views/overview_page.dart'; import 'package:client_backoffice/views/select_project_page.dart'; import 'package:client_backoffice/views/settings/external_clients_page.dart'; @@ -15,13 +16,19 @@ import 'package:flutter/widgets.dart'; import 'package:go_router/go_router.dart'; class BackofficeNavigator extends CommonNavigator { + static GoRoute oauth = GoRoute( + name: "oauth", + path: "/oauth", + builder: (ctx, state) => SafeArea(child: OAuthPage()), + ); + static GoRoute validationDev = GoRoute( name: "validation-dev", path: "/validation-dev", redirect: (context, state) => Guard.guards( context, [ - Guard.checkIsAuthenticated, + BackofficeGuard.checkIsAuthenticated, Guard.checkIsNotDev, ], metadata: {"initialRoute": state.location}, @@ -37,7 +44,7 @@ class BackofficeNavigator extends CommonNavigator { redirect: (context, state) => Guard.guards( context, [ - Guard.checkIsAuthenticated, + BackofficeGuard.checkIsAuthenticated, Guard.checkIsDev, Guard.checkNotHaveApp, ], @@ -54,7 +61,7 @@ class BackofficeNavigator extends CommonNavigator { redirect: (context, state) => Guard.guards( context, [ - Guard.checkIsAuthenticated, + BackofficeGuard.checkIsAuthenticated, Guard.checkIsDev, ], metadata: {"initialRoute": state.location}, @@ -126,7 +133,7 @@ class BackofficeNavigator extends CommonNavigator { redirect: (context, state) => Guard.guards( context, [ - Guard.checkIsAuthenticated, + BackofficeGuard.checkIsAuthenticated, Guard.checkIsDev, BackofficeGuard.checkHaveApp, ], @@ -149,7 +156,7 @@ class BackofficeNavigator extends CommonNavigator { redirect: (context, state) => Guard.guards( context, [ - Guard.checkIsAuthenticated, + BackofficeGuard.checkIsAuthenticated, Guard.checkIsDev, BackofficeGuard.checkHaveApp, ], @@ -166,12 +173,12 @@ class BackofficeNavigator extends CommonNavigator { static GoRouter router = GoRouter( navigatorKey: Catcher.navigatorKey, routes: [ - CommonNavigator.authRoutes, validationDev, welcome, createProject, overview, selectProject, + oauth, ], ); } diff --git a/lib/navigation/guard.dart b/lib/navigation/guard.dart index 7a311f6..9c722c6 100644 --- a/lib/navigation/guard.dart +++ b/lib/navigation/guard.dart @@ -1,6 +1,7 @@ import 'dart:async'; import 'package:client_backoffice/navigation/backoffice_navigator.dart'; +import 'package:client_backoffice/views/oauth_page.dart'; import 'package:client_common/navigator/guard.dart'; import 'package:flutter/material.dart'; @@ -11,7 +12,17 @@ class BackofficeGuard extends Guard { static final BackofficeGuard checkHaveApp = BackofficeGuard(isValid: Guard.haveApp(true), onInvalid: _toWelcome); + static final Guard checkIsAuthenticated = Guard(isValid: isAuthenticated, onInvalid: toOauth); + + static Future isAuthenticated(BuildContext context) async { + return OAuthPageState.isAuthenticated(context); + } + static String _toWelcome(context) { return BackofficeNavigator.welcome.path; } + + static String toOauth(context) { + return BackofficeNavigator.oauth.path; + } } diff --git a/lib/views/oauth_page.dart b/lib/views/oauth_page.dart new file mode 100644 index 0000000..d63e3fc --- /dev/null +++ b/lib/views/oauth_page.dart @@ -0,0 +1,204 @@ +import 'package:client_common/api/lenra_http_client.dart'; +import 'package:client_common/api/response_models/user_response.dart'; +import 'package:client_common/api/user_api.dart'; +import 'package:client_common/config/config.dart'; +import 'package:client_common/models/auth_model.dart'; +import 'package:client_common/oauth/oauth_model.dart'; +import 'package:flutter/gestures.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:go_router/go_router.dart'; +import 'package:lenra_components/lenra_components.dart'; +import 'package:oauth2_client/access_token_response.dart'; +import 'package:provider/provider.dart'; +import 'package:url_launcher/url_launcher.dart'; + +class OAuthPage extends StatefulWidget { + const OAuthPage({Key? key}) : super(key: key); + + @override + OAuthPageState createState() => OAuthPageState(); +} + +class OAuthPageState extends State { + @override + Widget build(BuildContext context) { + return FutureBuilder( + future: Future.wait( + [ + isAuthenticated(context), + rootBundle.loadString('texts/oauth-page-code.js'), + ], + ), + builder: ((context, snapshot) { + if (snapshot.hasData && !(snapshot.data![0] as bool)) { + var theme = LenraTheme.of(context); + bool isMobileDevice = MediaQuery.of(context).size.width <= 875; + + return Scaffold( + body: Row( + children: [ + Flexible( + child: Column( + children: [ + Align( + alignment: Alignment.centerLeft, + child: Padding( + padding: EdgeInsets.only(left: 40, top: 40), + child: Image.asset( + 'assets/images/logo-vertical.png', + height: theme.baseSize * 8, + ), + ), + ), + Padding( + padding: EdgeInsets.all(100), + child: Column( + children: [ + RichText( + text: TextSpan( + style: theme.lenraTextThemeData.headline1, + text: "Welcome to the ", + children: [ + TextSpan( + text: "technical platform", + style: TextStyle(color: LenraColorThemeData.lenraBlue)), + TextSpan(text: " !"), + ], + ), + ), + SizedBox(height: 32), + LenraButton( + onPressed: () async { + bool authenticated = await authenticate(context); + if (authenticated) { + context.go(context.read().beforeRedirectPath); + } + }, + text: 'Sign in to Lenra', + ), + SizedBox( + height: 32, + ), + Align( + alignment: Alignment.centerLeft, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text("You're not a dev?"), + SizedBox(height: 14), + RichText( + text: TextSpan( + text: "Go to the app platform", + style: TextStyle( + fontWeight: FontWeight.w600, + color: LenraColorThemeData.lenraBlue, + ), + children: [ + WidgetSpan( + alignment: PlaceholderAlignment.middle, + child: Padding( + padding: EdgeInsets.only(left: 8), + child: Icon(Icons.arrow_forward, color: LenraColorThemeData.lenraBlue), + ), + ) + ], + recognizer: TapGestureRecognizer() + ..onTap = () async { + String url = Config.instance.appBaseUrl; + if (await canLaunchUrl(Uri.parse(url))) { + await launchUrl(Uri.parse(url)); + } else { + throw "Could not launch $url"; + } + }), + ), + ], + ), + ), + ], + ), + ), + ], + ), + ), + rightSide(isMobileDevice, snapshot.data![1] as String) + ], + ), + ); + } else { + return Center(child: CircularProgressIndicator()); + } + }), + ); + } + + static Future isAuthenticated(BuildContext context) async { + OAuthModel oauthModel = context.read(); + + AccessTokenResponse? token = await oauthModel.helper.getTokenFromStorage(); + if (token?.accessToken != null) { + return await authenticate(context); + } + + return oauthModel.accessToken != null; + } + + static Future authenticate(BuildContext context) async { + AccessTokenResponse? response = await context.read().authenticate(); + if (response != null) { + context.read().accessToken = response; + + // Set the token for the global API instance + LenraApi.instance.token = response.accessToken; + + if (context.read().user == null) { + UserResponse user = await UserApi.me(); + context.read().user = user.user; + } + + return true; + } + + return false; + } + + Widget rightSide(bool isMobileDevice, String code) { + return isMobileDevice + ? SizedBox() + : Flexible( + child: Container( + constraints: BoxConstraints.expand(), + padding: EdgeInsets.all(32), + decoration: BoxDecoration(color: Color(0xFF1E232C)), + child: SingleChildScrollView( + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Column( + children: List.generate( + 37, + (index) => Text( + (index + 1).toString(), + style: TextStyle(color: Color(0xFF475367), fontSize: 14), + ), + ), + ), + SizedBox( + width: 16, + ), + Column( + children: [ + Text( + code, + style: TextStyle(color: Color(0xFF70CBF2), fontSize: 14), + ), + ], + ), + ], + ), + ), + ), + ); + } +} diff --git a/pubspec.lock b/pubspec.lock index bef4c63..cc020b4 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -94,8 +94,8 @@ packages: dependency: "direct main" description: path: "." - ref: "v1.0.0-alpha.4" - resolved-ref: "808df16da1180e973eeb4de358cfa83cbccd6fe3" + ref: "v1.0.0-alpha.5" + resolved-ref: e2808046f3c7ebb2f798497bd87f4cdeb3014a0b url: "https://github.com/lenra-io/client-common.git" source: git version: "1.0.0" @@ -492,7 +492,7 @@ packages: source: hosted version: "2.0.2" oauth2_client: - dependency: transitive + dependency: "direct main" description: name: oauth2_client sha256: "7d53b2a9eec4c1cbfddfd0a69dd0b6e8db73e6f75e4fb910c8070ac6513ae544" diff --git a/pubspec.yaml b/pubspec.yaml index 3f83893..fbe134d 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -28,7 +28,7 @@ dependencies: client_common: git: url: https://github.com/lenra-io/client-common.git - ref: v1.0.0-alpha.4 + ref: v1.0.0-alpha.5 url_launcher: ^6.1.2 logging: ^1.0.2 provider: ^6.0.1 @@ -39,10 +39,12 @@ dependencies: url: https://github.com/ThexXTURBOXx/catcher.git ref: ae44d0bbce00c3801629b65f60783d15184a7f8f intl: ^0.18.1 + oauth2_client: ^3.2.0 flutter: uses-material-design: true assets: + - assets/texts/ - assets/images/ fonts: - family: Source Sans Pro From 78f7d682ea9eea09508a437985995a05a72b3fe6 Mon Sep 17 00:00:00 2001 From: "jonas.martinez" Date: Wed, 30 Aug 2023 15:55:46 +0200 Subject: [PATCH 15/15] feat: Update client-common to beta.45 --- pubspec.lock | 4 ++-- pubspec.yaml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pubspec.lock b/pubspec.lock index cc020b4..51ab147 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -94,8 +94,8 @@ packages: dependency: "direct main" description: path: "." - ref: "v1.0.0-alpha.5" - resolved-ref: e2808046f3c7ebb2f798497bd87f4cdeb3014a0b + ref: "v1.0.0-beta.45" + resolved-ref: "0b7891871299c854445b30c0dac78553ca6a3005" url: "https://github.com/lenra-io/client-common.git" source: git version: "1.0.0" diff --git a/pubspec.yaml b/pubspec.yaml index fbe134d..1568156 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -28,7 +28,7 @@ dependencies: client_common: git: url: https://github.com/lenra-io/client-common.git - ref: v1.0.0-alpha.5 + ref: v1.0.0-beta.45 url_launcher: ^6.1.2 logging: ^1.0.2 provider: ^6.0.1