public inbox for pve-devel@lists.proxmox.com
 help / color / mirror / Atom feed
* [pve-devel] [PATCH pve_flutter_frontend] add changes to support local biometric authentication
@ 2020-09-30 10:32 Tim Marx
  2020-09-30 10:32 ` [pve-devel] [PATCH proxmox_login_manager] add option for " Tim Marx
  2020-11-12 15:39 ` [pve-devel] [PATCH pve_flutter_frontend] add changes to support " Aaron Lauterer
  0 siblings, 2 replies; 3+ messages in thread
From: Tim Marx @ 2020-09-30 10:32 UTC (permalink / raw)
  To: pve-devel

Signed-off-by: Tim Marx <t.marx@proxmox.com>
---
 android/app/src/debug/AndroidManifest.xml     |  2 +
 android/app/src/main/AndroidManifest.xml      |  1 +
 .../app/pve_flutter_frontend/MainActivity.kt  |  4 +-
 pubspec.lock                                  | 52 ++++++++++++-------
 4 files changed, 38 insertions(+), 21 deletions(-)

diff --git a/android/app/src/debug/AndroidManifest.xml b/android/app/src/debug/AndroidManifest.xml
index 617ada1..dd99f60 100644
--- a/android/app/src/debug/AndroidManifest.xml
+++ b/android/app/src/debug/AndroidManifest.xml
@@ -4,4 +4,6 @@
          to allow setting breakpoints, to provide hot reload, etc.
     -->
     <uses-permission android:name="android.permission.INTERNET"/>
+    <uses-permission android:name="android.permission.USE_FINGERPRINT"/>
+
 </manifest>
diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml
index 529671e..22e4a71 100644
--- a/android/app/src/main/AndroidManifest.xml
+++ b/android/app/src/main/AndroidManifest.xml
@@ -2,6 +2,7 @@
     package="com.proxmox.app.pve_flutter_frontend">
 
     <uses-permission android:name="android.permission.INTERNET"/>
+    <uses-permission android:name="android.permission.USE_FINGERPRINT"/>
 
     <application
         android:label="Proxmox Virtual Environment"
diff --git a/android/app/src/main/kotlin/com/proxmox/app/pve_flutter_frontend/MainActivity.kt b/android/app/src/main/kotlin/com/proxmox/app/pve_flutter_frontend/MainActivity.kt
index 8b1e324..1458fa0 100644
--- a/android/app/src/main/kotlin/com/proxmox/app/pve_flutter_frontend/MainActivity.kt
+++ b/android/app/src/main/kotlin/com/proxmox/app/pve_flutter_frontend/MainActivity.kt
@@ -3,7 +3,7 @@ package com.proxmox.app.pve_flutter_frontend
 import android.content.ActivityNotFoundException
 import android.content.Intent
 import androidx.annotation.NonNull
-import io.flutter.embedding.android.FlutterActivity
+import io.flutter.embedding.android.FlutterFragmentActivity
 import io.flutter.embedding.engine.FlutterEngine
 import io.flutter.plugin.common.MethodChannel
 import androidx.core.content.FileProvider
@@ -16,7 +16,7 @@ import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
 
-class MainActivity: FlutterActivity() {
+class MainActivity: FlutterFragmentActivity() {
 
     private val CHANNEL = "com.proxmox.app.pve_flutter_frontend/filesharing"
 
diff --git a/pubspec.lock b/pubspec.lock
index 831fd91..94886b3 100644
--- a/pubspec.lock
+++ b/pubspec.lock
@@ -28,14 +28,14 @@ packages:
       name: async
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "2.5.0-nullsafety"
+    version: "2.5.0-nullsafety.1"
   boolean_selector:
     dependency: transitive
     description:
       name: boolean_selector
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "2.1.0-nullsafety"
+    version: "2.1.0-nullsafety.1"
   build:
     dependency: transitive
     description:
@@ -105,14 +105,14 @@ packages:
       name: characters
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "1.1.0-nullsafety.2"
+    version: "1.1.0-nullsafety.3"
   charcode:
     dependency: transitive
     description:
       name: charcode
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "1.2.0-nullsafety"
+    version: "1.2.0-nullsafety.1"
   checked_yaml:
     dependency: transitive
     description:
@@ -133,7 +133,7 @@ packages:
       name: clock
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "1.1.0-nullsafety"
+    version: "1.1.0-nullsafety.1"
   code_builder:
     dependency: transitive
     description:
@@ -147,7 +147,7 @@ packages:
       name: collection
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "1.15.0-nullsafety.2"
+    version: "1.15.0-nullsafety.3"
   convert:
     dependency: transitive
     description:
@@ -189,7 +189,7 @@ packages:
       name: fake_async
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "1.1.0-nullsafety"
+    version: "1.2.0-nullsafety.1"
   ffi:
     dependency: transitive
     description:
@@ -216,6 +216,13 @@ packages:
     description: flutter
     source: sdk
     version: "0.0.0"
+  flutter_plugin_android_lifecycle:
+    dependency: transitive
+    description:
+      name: flutter_plugin_android_lifecycle
+      url: "https://pub.dartlang.org"
+    source: hosted
+    version: "1.0.11"
   flutter_test:
     dependency: "direct dev"
     description: flutter
@@ -303,6 +310,13 @@ packages:
       url: "https://pub.dartlang.org"
     source: hosted
     version: "3.1.0"
+  local_auth:
+    dependency: transitive
+    description:
+      name: local_auth
+      url: "https://pub.dartlang.org"
+    source: hosted
+    version: "0.6.3+2"
   logging:
     dependency: transitive
     description:
@@ -316,14 +330,14 @@ packages:
       name: matcher
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "0.12.10-nullsafety"
+    version: "0.12.10-nullsafety.1"
   meta:
     dependency: "direct main"
     description:
       name: meta
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "1.3.0-nullsafety.2"
+    version: "1.3.0-nullsafety.3"
   mime:
     dependency: transitive
     description:
@@ -365,7 +379,7 @@ packages:
       name: path
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "1.8.0-nullsafety"
+    version: "1.8.0-nullsafety.1"
   path_provider:
     dependency: "direct main"
     description:
@@ -573,21 +587,21 @@ packages:
       name: source_span
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "1.8.0-nullsafety"
+    version: "1.8.0-nullsafety.2"
   stack_trace:
     dependency: transitive
     description:
       name: stack_trace
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "1.10.0-nullsafety"
+    version: "1.10.0-nullsafety.1"
   stream_channel:
     dependency: transitive
     description:
       name: stream_channel
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "2.1.0-nullsafety"
+    version: "2.1.0-nullsafety.1"
   stream_transform:
     dependency: transitive
     description:
@@ -601,21 +615,21 @@ packages:
       name: string_scanner
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "1.1.0-nullsafety"
+    version: "1.1.0-nullsafety.1"
   term_glyph:
     dependency: transitive
     description:
       name: term_glyph
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "1.2.0-nullsafety"
+    version: "1.2.0-nullsafety.1"
   test_api:
     dependency: transitive
     description:
       name: test_api
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "0.2.19-nullsafety"
+    version: "0.2.19-nullsafety.2"
   timing:
     dependency: transitive
     description:
@@ -629,7 +643,7 @@ packages:
       name: typed_data
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "1.3.0-nullsafety.2"
+    version: "1.3.0-nullsafety.3"
   url_launcher:
     dependency: "direct main"
     description:
@@ -678,7 +692,7 @@ packages:
       name: vector_math
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "2.1.0-nullsafety.2"
+    version: "2.1.0-nullsafety.3"
   watcher:
     dependency: transitive
     description:
@@ -715,5 +729,5 @@ packages:
     source: hosted
     version: "2.2.1"
 sdks:
-  dart: ">=2.10.0-0.0.dev <2.10.0"
+  dart: ">=2.10.0-110 <=2.11.0-176.0.dev"
   flutter: ">=1.12.13+hotfix.5 <2.0.0"
-- 
2.20.1




^ permalink raw reply	[flat|nested] 3+ messages in thread

* [pve-devel] [PATCH proxmox_login_manager] add option for local biometric authentication
  2020-09-30 10:32 [pve-devel] [PATCH pve_flutter_frontend] add changes to support local biometric authentication Tim Marx
@ 2020-09-30 10:32 ` Tim Marx
  2020-11-12 15:39 ` [pve-devel] [PATCH pve_flutter_frontend] add changes to support " Aaron Lauterer
  1 sibling, 0 replies; 3+ messages in thread
From: Tim Marx @ 2020-09-30 10:32 UTC (permalink / raw)
  To: pve-devel

Signed-off-by: Tim Marx <t.marx@proxmox.com>
---
 lib/proxmox_general_settings_form.dart  |  39 +++-
 lib/proxmox_general_settings_model.dart |  12 +-
 lib/proxmox_login_form.dart             |   9 +-
 lib/proxmox_login_model.dart            |   4 +-
 lib/proxmox_login_selector.dart         | 234 +++++++++++++++---------
 pubspec.lock                            |  14 ++
 pubspec.yaml                            |   1 +
 7 files changed, 216 insertions(+), 97 deletions(-)

diff --git a/lib/proxmox_general_settings_form.dart b/lib/proxmox_general_settings_form.dart
index f3fdd44..61a7d76 100644
--- a/lib/proxmox_general_settings_form.dart
+++ b/lib/proxmox_general_settings_form.dart
@@ -1,5 +1,9 @@
+import 'dart:io';
+
 import 'package:flutter/material.dart';
+import 'package:flutter/services.dart';
 import 'package:proxmox_login_manager/proxmox_general_settings_model.dart';
+import 'package:local_auth/local_auth.dart';
 
 class ProxmoxGeneralSettingsForm extends StatefulWidget {
   @override
@@ -43,7 +47,40 @@ class _ProxmoxGeneralSettingsFormState
                               ProxmoxGeneralSettingsModel.fromLocalStorage();
                         });
                       },
-                    )
+                    ),
+                    if (Platform.isAndroid)
+                      SwitchListTile(
+                        title: Text('Biometric lock'),
+                        subtitle: Text('Lock app with fingerprint'),
+                        value: settings.useBiometrics,
+                        onChanged: (value) async {
+                          try {
+                            bool didAuthenticate = await LocalAuthentication()
+                                .authenticateWithBiometrics(
+                                    localizedReason:
+                                        'Please authenticate to enable app lock');
+                            if (didAuthenticate) {
+                              await settings
+                                  .rebuild((b) => b.useBiometrics = value)
+                                  .toLocalStorage();
+                              setState(() {
+                                _settings = ProxmoxGeneralSettingsModel
+                                    .fromLocalStorage();
+                              });
+                            }
+                          } on PlatformException catch (e) {
+                            print(e);
+                            showDialog(
+                              context: context,
+                              builder: (context) => AlertDialog(
+                                title: Text('Sensor Error'),
+                                content:
+                                    Text('Accessing biometric sensor failed'),
+                              ),
+                            );
+                          }
+                        },
+                      )
                   ],
                 ),
               );
diff --git a/lib/proxmox_general_settings_model.dart b/lib/proxmox_general_settings_model.dart
index 72fc0f7..3f3b9a3 100644
--- a/lib/proxmox_general_settings_model.dart
+++ b/lib/proxmox_general_settings_model.dart
@@ -11,6 +11,7 @@ abstract class ProxmoxGeneralSettingsModel
     implements
         Built<ProxmoxGeneralSettingsModel, ProxmoxGeneralSettingsModelBuilder> {
   bool get sslValidation;
+  bool get useBiometrics;
 
   ProxmoxGeneralSettingsModel._();
   factory ProxmoxGeneralSettingsModel(
@@ -18,8 +19,15 @@ abstract class ProxmoxGeneralSettingsModel
       _$ProxmoxGeneralSettingsModel;
 
   factory ProxmoxGeneralSettingsModel.defaultValues() =>
-      ProxmoxGeneralSettingsModel((b) => b..sslValidation = true);
-
+      ProxmoxGeneralSettingsModel(
+        (b) => b
+          ..sslValidation = true
+          ..useBiometrics = false,
+      );
+  static void _initializeBuilder(ProxmoxGeneralSettingsModelBuilder builder) =>
+      builder
+        ..sslValidation = true
+        ..useBiometrics = false;
   Object toJson() {
     return serializers.serializeWith(
         ProxmoxGeneralSettingsModel.serializer, this);
diff --git a/lib/proxmox_login_form.dart b/lib/proxmox_login_form.dart
index 3115ffd..50afb3a 100644
--- a/lib/proxmox_login_form.dart
+++ b/lib/proxmox_login_form.dart
@@ -556,8 +556,10 @@ class _ProxmoxLoginPageState extends State<ProxmoxLoginPage> {
 }
 
 class ProxmoxProgressOverlay extends StatelessWidget {
+  final Color foregroundColor;
   const ProxmoxProgressOverlay({
     Key key,
+    Color this.foregroundColor,
     @required this.message,
   }) : super(key: key);
 
@@ -574,13 +576,16 @@ class ProxmoxProgressOverlay extends StatelessWidget {
             Text(
               message,
               style: TextStyle(
-                color: Theme.of(context).accentColor,
+                color: foregroundColor ?? Theme.of(context).accentColor,
                 fontSize: 20,
               ),
             ),
             Padding(
               padding: const EdgeInsets.only(top: 20.0),
-              child: CircularProgressIndicator(),
+              child: CircularProgressIndicator(
+                valueColor: AlwaysStoppedAnimation<Color>(
+                    foregroundColor ?? Theme.of(context).accentColor),
+              ),
             )
           ],
         ),
diff --git a/lib/proxmox_login_model.dart b/lib/proxmox_login_model.dart
index af7c4fd..067e92f 100644
--- a/lib/proxmox_login_model.dart
+++ b/lib/proxmox_login_model.dart
@@ -44,8 +44,10 @@ abstract class ProxmoxLoginStorage
   }
 
   Future<proxclient.ProxmoxApiClient> recoverLatestSession() async {
-    final latestSession = logins.singleWhere((e) => e.ticket.isNotEmpty);
     final settings = await ProxmoxGeneralSettingsModel.fromLocalStorage();
+    if (settings.useBiometrics)
+      throw Exception('Biometric authentication required');
+    final latestSession = logins.singleWhere((e) => e.ticket.isNotEmpty);
     final apiClient = await proxclient.authenticate(latestSession.fullUsername,
         latestSession.ticket, latestSession.origin, settings.sslValidation);
     return apiClient;
diff --git a/lib/proxmox_login_selector.dart b/lib/proxmox_login_selector.dart
index f6ca2fa..fc8dda9 100644
--- a/lib/proxmox_login_selector.dart
+++ b/lib/proxmox_login_selector.dart
@@ -1,5 +1,8 @@
 import 'package:flutter/material.dart';
+import 'package:flutter/services.dart';
+import 'package:local_auth/local_auth.dart';
 import 'package:proxmox_login_manager/proxmox_general_settings_form.dart';
+import 'package:proxmox_login_manager/proxmox_general_settings_model.dart';
 import 'package:proxmox_login_manager/proxmox_login_form.dart';
 import 'package:proxmox_login_manager/proxmox_login_model.dart';
 import 'package:proxmox_dart_api_client/proxmox_dart_api_client.dart'
@@ -19,12 +22,44 @@ class ProxmoxLoginSelector extends StatefulWidget {
 
 class _ProxmoxLoginSelectorState extends State<ProxmoxLoginSelector> {
   Future<ProxmoxLoginStorage> loginStorage;
+  Future<bool> authenticated;
+  final LocalAuthentication auth = LocalAuthentication();
+  bool _isAuthenticating = false;
+
   @override
   void initState() {
     super.initState();
+    authenticated = _authenticate();
     loginStorage = ProxmoxLoginStorage.fromLocalStorage();
   }
 
+  Future<bool> _authenticate() async {
+    final settings = await ProxmoxGeneralSettingsModel.fromLocalStorage();
+
+    if (!settings.useBiometrics) return true;
+
+    var authenticated = false;
+
+    try {
+      setState(() {
+        _isAuthenticating = true;
+      });
+      while (!authenticated) {
+        authenticated = await auth.authenticateWithBiometrics(
+            localizedReason: 'Scan your fingerprint to authenticate',
+            useErrorDialogs: true,
+            stickyAuth: true);
+      }
+      setState(() {
+        _isAuthenticating = false;
+      });
+    } on PlatformException catch (e) {
+      print(e);
+    }
+    if (!mounted) return authenticated;
+    return authenticated;
+  }
+
   @override
   Widget build(BuildContext context) {
     return SafeArea(
@@ -57,103 +92,120 @@ class _ProxmoxLoginSelectorState extends State<ProxmoxLoginSelector> {
                 })
           ],
         ),
-        body: FutureBuilder<ProxmoxLoginStorage>(
-            future: loginStorage,
+        body: FutureBuilder(
+            future: authenticated,
             builder: (context, snapshot) {
-              if (!snapshot.hasData) {
-                return Center(
-                  child: CircularProgressIndicator(),
-                );
-              }
-              if (snapshot.hasData && (snapshot.data.logins?.isEmpty ?? true)) {
-                return Center(
-                  child: Text('Add an account'),
+              if (_isAuthenticating || !snapshot.hasData || !snapshot.data) {
+                return ProxmoxProgressOverlay(
+                  message: 'Waiting for authentication',
+                  foregroundColor: Colors.white,
                 );
               }
-              var items = <Widget>[];
-              final logins = snapshot.data?.logins;
+              return FutureBuilder<ProxmoxLoginStorage>(
+                  future: loginStorage,
+                  builder: (context, snapshot) {
+                    if (!snapshot.hasData) {
+                      return Center(
+                        child: CircularProgressIndicator(),
+                      );
+                    }
+                    if (snapshot.hasData &&
+                        (snapshot.data.logins?.isEmpty ?? true)) {
+                      return Center(
+                        child: Text('Add an account'),
+                      );
+                    }
+                    var items = <Widget>[];
+                    final logins = snapshot.data?.logins;
 
-              final activeSessions =
-                  logins.rebuild((b) => b.where((b) => b.activeSession));
+                    final activeSessions =
+                        logins.rebuild((b) => b.where((b) => b.activeSession));
 
-              if (activeSessions.isNotEmpty) {
-                items.addAll([
-                  Padding(
-                    padding: const EdgeInsets.all(12.0),
-                    child: Text(
-                      'Active Sessions',
-                      style: TextStyle(
-                        fontSize: 18,
-                        fontWeight: FontWeight.bold,
+                    if (activeSessions.isNotEmpty) {
+                      items.addAll([
+                        Padding(
+                          padding: const EdgeInsets.all(12.0),
+                          child: Text(
+                            'Active Sessions',
+                            style: TextStyle(
+                              fontSize: 18,
+                              fontWeight: FontWeight.bold,
+                            ),
+                          ),
+                        ),
+                        ...activeSessions.map((s) => ListTile(
+                              title: Text(s.fullHostname),
+                              subtitle: Text(s.fullUsername),
+                              trailing: Icon(Icons.navigate_next),
+                              leading: PopupMenuButton(
+                                  icon: Icon(Icons.more_vert,
+                                      color: Colors.green),
+                                  itemBuilder: (context) => [
+                                        PopupMenuItem(
+                                          child: ListTile(
+                                            dense: true,
+                                            leading: Icon(Icons.logout),
+                                            title: Text('Logout'),
+                                            onTap: () async {
+                                              await snapshot.data
+                                                  .rebuild((b) => b.logins
+                                                      .rebuildWhere(
+                                                          (m) => s == m,
+                                                          (b) =>
+                                                              b..ticket = ''))
+                                                  .saveToDisk();
+                                              refreshFromStorage();
+                                              Navigator.of(context).pop();
+                                            },
+                                          ),
+                                        ),
+                                      ]),
+                              onTap: () => _login(user: s),
+                            )),
+                      ]);
+                    }
+                    items.addAll([
+                      Padding(
+                        padding: const EdgeInsets.all(12.0),
+                        child: Text(
+                          'Available Sites',
+                          style: TextStyle(
+                            fontSize: 18,
+                            fontWeight: FontWeight.bold,
+                          ),
+                        ),
                       ),
-                    ),
-                  ),
-                  ...activeSessions.map((s) => ListTile(
-                        title: Text(s.fullHostname),
-                        subtitle: Text(s.fullUsername),
-                        trailing: Icon(Icons.navigate_next),
-                        leading: PopupMenuButton(
-                            icon: Icon(Icons.more_vert, color: Colors.green),
-                            itemBuilder: (context) => [
-                                  PopupMenuItem(
-                                    child: ListTile(
-                                      dense: true,
-                                      leading: Icon(Icons.logout),
-                                      title: Text('Logout'),
-                                      onTap: () async {
-                                        await snapshot.data
-                                            .rebuild((b) => b.logins
-                                                .rebuildWhere((m) => s == m,
-                                                    (b) => b..ticket = ''))
-                                            .saveToDisk();
-                                        refreshFromStorage();
-                                        Navigator.of(context).pop();
-                                      },
-                                    ),
-                                  ),
-                                ]),
-                        onTap: () => _login(user: s),
-                      )),
-                ]);
-              }
-              items.addAll([
-                Padding(
-                  padding: const EdgeInsets.all(12.0),
-                  child: Text(
-                    'Available Sites',
-                    style: TextStyle(
-                      fontSize: 18,
-                      fontWeight: FontWeight.bold,
-                    ),
-                  ),
-                ),
-                ...logins.where((b) => !b.activeSession)?.map((l) => ListTile(
-                      title: Text(l.fullHostname),
-                      subtitle: Text(l.fullUsername),
-                      trailing: Icon(Icons.navigate_next),
-                      leading: PopupMenuButton(
-                          itemBuilder: (context) => [
-                                PopupMenuItem(
-                                  child: ListTile(
-                                    dense: true,
-                                    leading: Icon(Icons.delete),
-                                    title: Text('Delete'),
-                                    onTap: () async {
-                                      await snapshot.data
-                                          .rebuild((b) => b.logins.remove(l))
-                                          .saveToDisk();
-                                      refreshFromStorage();
-                                      Navigator.of(context).pop();
-                                    },
-                                  ),
-                                ),
-                              ]),
-                      onTap: () => _login(user: l),
-                    ))
-              ]);
-              return ListView(
-                children: items,
-              );
+                      ...logins
+                          .where((b) => !b.activeSession)
+                          ?.map((l) => ListTile(
+                                title: Text(l.fullHostname),
+                                subtitle: Text(l.fullUsername),
+                                trailing: Icon(Icons.navigate_next),
+                                leading: PopupMenuButton(
+                                    itemBuilder: (context) => [
+                                          PopupMenuItem(
+                                            child: ListTile(
+                                              dense: true,
+                                              leading: Icon(Icons.delete),
+                                              title: Text('Delete'),
+                                              onTap: () async {
+                                                await snapshot.data
+                                                    .rebuild((b) =>
+                                                        b.logins.remove(l))
+                                                    .saveToDisk();
+                                                refreshFromStorage();
+                                                Navigator.of(context).pop();
+                                              },
+                                            ),
+                                          ),
+                                        ]),
+                                onTap: () => _login(user: l),
+                              ))
+                    ]);
+                    return ListView(
+                      children: items,
+                    );
+                  });
             }),
         floatingActionButton: FloatingActionButton.extended(
           onPressed: () => _login(isCreate: true),
diff --git a/pubspec.lock b/pubspec.lock
index b0bf674..58794ad 100644
--- a/pubspec.lock
+++ b/pubspec.lock
@@ -209,6 +209,13 @@ packages:
     description: flutter
     source: sdk
     version: "0.0.0"
+  flutter_plugin_android_lifecycle:
+    dependency: transitive
+    description:
+      name: flutter_plugin_android_lifecycle
+      url: "https://pub.dartlang.org"
+    source: hosted
+    version: "1.0.11"
   flutter_test:
     dependency: "direct dev"
     description: flutter
@@ -289,6 +296,13 @@ packages:
       url: "https://pub.dartlang.org"
     source: hosted
     version: "3.1.0"
+  local_auth:
+    dependency: "direct main"
+    description:
+      name: local_auth
+      url: "https://pub.dartlang.org"
+    source: hosted
+    version: "0.6.3+2"
   logging:
     dependency: transitive
     description:
diff --git a/pubspec.yaml b/pubspec.yaml
index dc11ec8..2b5f5a5 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -12,6 +12,7 @@ dependencies:
   shared_preferences: '>=0.5.10 <2.0.0'
   built_value: ^7.1.0
   built_collection: ^4.3.2
+  local_auth: ^0.6.3+2
   proxmox_dart_api_client:
     path: ../proxmox_dart_api_client
 
-- 
2.20.1




^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: [pve-devel] [PATCH pve_flutter_frontend] add changes to support local biometric authentication
  2020-09-30 10:32 [pve-devel] [PATCH pve_flutter_frontend] add changes to support local biometric authentication Tim Marx
  2020-09-30 10:32 ` [pve-devel] [PATCH proxmox_login_manager] add option for " Tim Marx
@ 2020-11-12 15:39 ` Aaron Lauterer
  1 sibling, 0 replies; 3+ messages in thread
From: Aaron Lauterer @ 2020-11-12 15:39 UTC (permalink / raw)
  To: pve-devel

Maybe a bit more explanation is needed for what this patch actually does.

Once enabled, a fingerprint scan is needed in the following situations:
* open the app
* navigate to the "Sites" panel


for the series:
Tested-By: Aaron Lauterer <a.lauterer@proxmox.com>

On 9/30/20 12:32 PM, Tim Marx wrote:
> Signed-off-by: Tim Marx <t.marx@proxmox.com>
> ---
>   android/app/src/debug/AndroidManifest.xml     |  2 +
>   android/app/src/main/AndroidManifest.xml      |  1 +
>   .../app/pve_flutter_frontend/MainActivity.kt  |  4 +-
>   pubspec.lock                                  | 52 ++++++++++++-------
>   4 files changed, 38 insertions(+), 21 deletions(-)
> 
> diff --git a/android/app/src/debug/AndroidManifest.xml b/android/app/src/debug/AndroidManifest.xml
> index 617ada1..dd99f60 100644
> --- a/android/app/src/debug/AndroidManifest.xml
> +++ b/android/app/src/debug/AndroidManifest.xml
> @@ -4,4 +4,6 @@
>            to allow setting breakpoints, to provide hot reload, etc.
>       -->
>       <uses-permission android:name="android.permission.INTERNET"/>
> +    <uses-permission android:name="android.permission.USE_FINGERPRINT"/>
> +
>   </manifest>
> diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml
> index 529671e..22e4a71 100644
> --- a/android/app/src/main/AndroidManifest.xml
> +++ b/android/app/src/main/AndroidManifest.xml
> @@ -2,6 +2,7 @@
>       package="com.proxmox.app.pve_flutter_frontend">
>   
>       <uses-permission android:name="android.permission.INTERNET"/>
> +    <uses-permission android:name="android.permission.USE_FINGERPRINT"/>
>   
>       <application
>           android:label="Proxmox Virtual Environment"
> diff --git a/android/app/src/main/kotlin/com/proxmox/app/pve_flutter_frontend/MainActivity.kt b/android/app/src/main/kotlin/com/proxmox/app/pve_flutter_frontend/MainActivity.kt
> index 8b1e324..1458fa0 100644
> --- a/android/app/src/main/kotlin/com/proxmox/app/pve_flutter_frontend/MainActivity.kt
> +++ b/android/app/src/main/kotlin/com/proxmox/app/pve_flutter_frontend/MainActivity.kt
> @@ -3,7 +3,7 @@ package com.proxmox.app.pve_flutter_frontend
>   import android.content.ActivityNotFoundException
>   import android.content.Intent
>   import androidx.annotation.NonNull
> -import io.flutter.embedding.android.FlutterActivity
> +import io.flutter.embedding.android.FlutterFragmentActivity
>   import io.flutter.embedding.engine.FlutterEngine
>   import io.flutter.plugin.common.MethodChannel
>   import androidx.core.content.FileProvider
> @@ -16,7 +16,7 @@ import java.io.IOException;
>   import java.io.InputStream;
>   import java.io.OutputStream;
>   
> -class MainActivity: FlutterActivity() {
> +class MainActivity: FlutterFragmentActivity() {
>   
>       private val CHANNEL = "com.proxmox.app.pve_flutter_frontend/filesharing"
>   
> diff --git a/pubspec.lock b/pubspec.lock
> index 831fd91..94886b3 100644
> --- a/pubspec.lock
> +++ b/pubspec.lock
> @@ -28,14 +28,14 @@ packages:
>         name: async
>         url: "https://pub.dartlang.org"
>       source: hosted
> -    version: "2.5.0-nullsafety"
> +    version: "2.5.0-nullsafety.1"
>     boolean_selector:
>       dependency: transitive
>       description:
>         name: boolean_selector
>         url: "https://pub.dartlang.org"
>       source: hosted
> -    version: "2.1.0-nullsafety"
> +    version: "2.1.0-nullsafety.1"
>     build:
>       dependency: transitive
>       description:
> @@ -105,14 +105,14 @@ packages:
>         name: characters
>         url: "https://pub.dartlang.org"
>       source: hosted
> -    version: "1.1.0-nullsafety.2"
> +    version: "1.1.0-nullsafety.3"
>     charcode:
>       dependency: transitive
>       description:
>         name: charcode
>         url: "https://pub.dartlang.org"
>       source: hosted
> -    version: "1.2.0-nullsafety"
> +    version: "1.2.0-nullsafety.1"
>     checked_yaml:
>       dependency: transitive
>       description:
> @@ -133,7 +133,7 @@ packages:
>         name: clock
>         url: "https://pub.dartlang.org"
>       source: hosted
> -    version: "1.1.0-nullsafety"
> +    version: "1.1.0-nullsafety.1"
>     code_builder:
>       dependency: transitive
>       description:
> @@ -147,7 +147,7 @@ packages:
>         name: collection
>         url: "https://pub.dartlang.org"
>       source: hosted
> -    version: "1.15.0-nullsafety.2"
> +    version: "1.15.0-nullsafety.3"
>     convert:
>       dependency: transitive
>       description:
> @@ -189,7 +189,7 @@ packages:
>         name: fake_async
>         url: "https://pub.dartlang.org"
>       source: hosted
> -    version: "1.1.0-nullsafety"
> +    version: "1.2.0-nullsafety.1"
>     ffi:
>       dependency: transitive
>       description:
> @@ -216,6 +216,13 @@ packages:
>       description: flutter
>       source: sdk
>       version: "0.0.0"
> +  flutter_plugin_android_lifecycle:
> +    dependency: transitive
> +    description:
> +      name: flutter_plugin_android_lifecycle
> +      url: "https://pub.dartlang.org"
> +    source: hosted
> +    version: "1.0.11"
>     flutter_test:
>       dependency: "direct dev"
>       description: flutter
> @@ -303,6 +310,13 @@ packages:
>         url: "https://pub.dartlang.org"
>       source: hosted
>       version: "3.1.0"
> +  local_auth:
> +    dependency: transitive
> +    description:
> +      name: local_auth
> +      url: "https://pub.dartlang.org"
> +    source: hosted
> +    version: "0.6.3+2"
>     logging:
>       dependency: transitive
>       description:
> @@ -316,14 +330,14 @@ packages:
>         name: matcher
>         url: "https://pub.dartlang.org"
>       source: hosted
> -    version: "0.12.10-nullsafety"
> +    version: "0.12.10-nullsafety.1"
>     meta:
>       dependency: "direct main"
>       description:
>         name: meta
>         url: "https://pub.dartlang.org"
>       source: hosted
> -    version: "1.3.0-nullsafety.2"
> +    version: "1.3.0-nullsafety.3"
>     mime:
>       dependency: transitive
>       description:
> @@ -365,7 +379,7 @@ packages:
>         name: path
>         url: "https://pub.dartlang.org"
>       source: hosted
> -    version: "1.8.0-nullsafety"
> +    version: "1.8.0-nullsafety.1"
>     path_provider:
>       dependency: "direct main"
>       description:
> @@ -573,21 +587,21 @@ packages:
>         name: source_span
>         url: "https://pub.dartlang.org"
>       source: hosted
> -    version: "1.8.0-nullsafety"
> +    version: "1.8.0-nullsafety.2"
>     stack_trace:
>       dependency: transitive
>       description:
>         name: stack_trace
>         url: "https://pub.dartlang.org"
>       source: hosted
> -    version: "1.10.0-nullsafety"
> +    version: "1.10.0-nullsafety.1"
>     stream_channel:
>       dependency: transitive
>       description:
>         name: stream_channel
>         url: "https://pub.dartlang.org"
>       source: hosted
> -    version: "2.1.0-nullsafety"
> +    version: "2.1.0-nullsafety.1"
>     stream_transform:
>       dependency: transitive
>       description:
> @@ -601,21 +615,21 @@ packages:
>         name: string_scanner
>         url: "https://pub.dartlang.org"
>       source: hosted
> -    version: "1.1.0-nullsafety"
> +    version: "1.1.0-nullsafety.1"
>     term_glyph:
>       dependency: transitive
>       description:
>         name: term_glyph
>         url: "https://pub.dartlang.org"
>       source: hosted
> -    version: "1.2.0-nullsafety"
> +    version: "1.2.0-nullsafety.1"
>     test_api:
>       dependency: transitive
>       description:
>         name: test_api
>         url: "https://pub.dartlang.org"
>       source: hosted
> -    version: "0.2.19-nullsafety"
> +    version: "0.2.19-nullsafety.2"
>     timing:
>       dependency: transitive
>       description:
> @@ -629,7 +643,7 @@ packages:
>         name: typed_data
>         url: "https://pub.dartlang.org"
>       source: hosted
> -    version: "1.3.0-nullsafety.2"
> +    version: "1.3.0-nullsafety.3"
>     url_launcher:
>       dependency: "direct main"
>       description:
> @@ -678,7 +692,7 @@ packages:
>         name: vector_math
>         url: "https://pub.dartlang.org"
>       source: hosted
> -    version: "2.1.0-nullsafety.2"
> +    version: "2.1.0-nullsafety.3"
>     watcher:
>       dependency: transitive
>       description:
> @@ -715,5 +729,5 @@ packages:
>       source: hosted
>       version: "2.2.1"
>   sdks:
> -  dart: ">=2.10.0-0.0.dev <2.10.0"
> +  dart: ">=2.10.0-110 <=2.11.0-176.0.dev"
>     flutter: ">=1.12.13+hotfix.5 <2.0.0"
> 




^ permalink raw reply	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2020-11-12 15:39 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-09-30 10:32 [pve-devel] [PATCH pve_flutter_frontend] add changes to support local biometric authentication Tim Marx
2020-09-30 10:32 ` [pve-devel] [PATCH proxmox_login_manager] add option for " Tim Marx
2020-11-12 15:39 ` [pve-devel] [PATCH pve_flutter_frontend] add changes to support " Aaron Lauterer

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
Service provided by Proxmox Server Solutions GmbH | Privacy | Legal