]> git.proxmox.com Git - flutter/proxmox_login_manager.git/blobdiff - lib/proxmox_login_form.dart
fix issue when 443 port is used
[flutter/proxmox_login_manager.git] / lib / proxmox_login_form.dart
index 15abc6d1217070a71e012100c99d232931988c2d..8ee5c123c277f81b8213faca090f30dfa4710b43 100644 (file)
@@ -8,6 +8,7 @@ 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_model.dart';
 import 'package:proxmox_login_manager/proxmox_tfa_form.dart';
+import 'package:proxmox_login_manager/extension.dart';
 
 class ProxmoxProgressModel {
   bool inProgress;
@@ -26,6 +27,8 @@ class ProxmoxLoginForm extends StatefulWidget {
   final List<PveAccessDomainModel> accessDomains;
   final PveAccessDomainModel selectedDomain;
   final ValueChanged<PveAccessDomainModel> onDomainChanged;
+  final Function onPasswordSubmitted;
+  final Function onOriginSubmitted;
 
   const ProxmoxLoginForm({
     Key key,
@@ -36,6 +39,8 @@ class ProxmoxLoginForm extends StatefulWidget {
     @required this.originValidator,
     this.selectedDomain,
     @required this.onDomainChanged,
+    this.onPasswordSubmitted,
+    this.onOriginSubmitted,
   }) : super(key: key);
 
   @override
@@ -46,16 +51,6 @@ class _ProxmoxLoginFormState extends State<ProxmoxLoginForm> {
   bool _obscure = true;
   FocusNode passwordFocusNode;
 
-  @override
-  void initState() {
-    super.initState();
-
-    if (widget.usernameController.text.isNotEmpty) {
-      passwordFocusNode = FocusNode();
-      passwordFocusNode.requestFocus();
-    }
-  }
-
   @override
   Widget build(BuildContext context) {
     if (widget.accessDomains == null) {
@@ -67,81 +62,88 @@ class _ProxmoxLoginFormState extends State<ProxmoxLoginForm> {
             helperText: 'Protocol (https) and default port (8006) implied'),
         controller: widget.originController,
         validator: widget.originValidator,
+        onFieldSubmitted: (value) => widget.onOriginSubmitted(),
       );
     }
 
-    return Column(
-      mainAxisAlignment: MainAxisAlignment.center,
-      children: [
-        TextFormField(
-          decoration: InputDecoration(
-            icon: Icon(Icons.vpn_lock),
-            labelText: 'Origin',
-          ),
-          controller: widget.originController,
-          enabled: false,
-        ),
-        TextFormField(
-          decoration: InputDecoration(
-            icon: Icon(Icons.person),
-            labelText: 'Username',
+    return AutofillGroup(
+      child: Column(
+        mainAxisAlignment: MainAxisAlignment.center,
+        children: [
+          TextFormField(
+            decoration: InputDecoration(
+              icon: Icon(Icons.vpn_lock),
+              labelText: 'Origin',
+            ),
+            controller: widget.originController,
+            enabled: false,
           ),
-          controller: widget.usernameController,
-          validator: (value) {
-            if (value.isEmpty) {
-              return 'Please enter username';
-            }
-            return null;
-          },
-        ),
-        DropdownButtonFormField(
-          decoration: InputDecoration(icon: Icon(Icons.domain)),
-          items: widget.accessDomains
-              .map((e) => DropdownMenuItem(
-                    child: ListTile(
-                      title: Text(e.realm),
-                      subtitle: Text(e.comment ?? ''),
-                    ),
-                    value: e,
-                  ))
-              .toList(),
-          onChanged: widget.onDomainChanged,
-          selectedItemBuilder: (context) =>
-              widget.accessDomains.map((e) => Text(e.realm)).toList(),
-          value: widget.selectedDomain,
-        ),
-        Stack(
-          children: [
-            TextFormField(
-              decoration: InputDecoration(
-                icon: Icon(Icons.lock),
-                labelText: 'Password',
-              ),
-              controller: widget.passwordController,
-              obscureText: _obscure,
-              autocorrect: false,
-              focusNode: passwordFocusNode,
-              validator: (value) {
-                if (value.isEmpty) {
-                  return 'Please enter password';
-                }
-                return null;
-              },
+          TextFormField(
+            decoration: InputDecoration(
+              icon: Icon(Icons.person),
+              labelText: 'Username',
             ),
-            Align(
-              alignment: Alignment.bottomRight,
-              child: IconButton(
-                constraints: BoxConstraints.tight(Size(58, 58)),
-                iconSize: 24,
-                icon: Icon(_obscure ? Icons.visibility : Icons.visibility_off),
-                onPressed: () => setState(() {
-                  _obscure = !_obscure;
-                }),
+            controller: widget.usernameController,
+            validator: (value) {
+              if (value.isEmpty) {
+                return 'Please enter username';
+              }
+              return null;
+            },
+            autofillHints: [AutofillHints.username],
+          ),
+          DropdownButtonFormField(
+            decoration: InputDecoration(icon: Icon(Icons.domain)),
+            items: widget.accessDomains
+                .map((e) => DropdownMenuItem(
+                      child: ListTile(
+                        title: Text(e.realm),
+                        subtitle: Text(e.comment ?? ''),
+                      ),
+                      value: e,
+                    ))
+                .toList(),
+            onChanged: widget.onDomainChanged,
+            selectedItemBuilder: (context) =>
+                widget.accessDomains.map((e) => Text(e.realm)).toList(),
+            value: widget.selectedDomain,
+          ),
+          Stack(
+            children: [
+              TextFormField(
+                decoration: InputDecoration(
+                  icon: Icon(Icons.lock),
+                  labelText: 'Password',
+                ),
+                controller: widget.passwordController,
+                obscureText: _obscure,
+                autocorrect: false,
+                focusNode: passwordFocusNode,
+                validator: (value) {
+                  if (value.isEmpty) {
+                    return 'Please enter password';
+                  }
+                  return null;
+                },
+                onFieldSubmitted: (value) => widget.onPasswordSubmitted(),
+                autofillHints: [AutofillHints.password],
               ),
-            )
-          ],
-        ),
-      ],
+              Align(
+                alignment: Alignment.bottomRight,
+                child: IconButton(
+                  constraints: BoxConstraints.tight(Size(58, 58)),
+                  iconSize: 24,
+                  icon:
+                      Icon(_obscure ? Icons.visibility : Icons.visibility_off),
+                  onPressed: () => setState(() {
+                    _obscure = !_obscure;
+                  }),
+                ),
+              )
+            ],
+          ),
+        ],
+      ),
     );
   }
 
@@ -155,11 +157,13 @@ class _ProxmoxLoginFormState extends State<ProxmoxLoginForm> {
 class ProxmoxLoginPage extends StatefulWidget {
   final ProxmoxLoginModel userModel;
   final bool isCreate;
+  final String ticket;
 
   const ProxmoxLoginPage({
     Key key,
     this.userModel,
     this.isCreate,
+    this.ticket = '',
   }) : super(key: key);
   @override
   _ProxmoxLoginPageState createState() => _ProxmoxLoginPageState();
@@ -174,6 +178,7 @@ class _ProxmoxLoginPageState extends State<ProxmoxLoginPage> {
   final _formKey = GlobalKey<FormState>();
   ProxmoxProgressModel _progressModel;
   bool _submittButtonEnabled = true;
+
   @override
   void initState() {
     super.initState();
@@ -187,6 +192,9 @@ class _ProxmoxLoginPageState extends State<ProxmoxLoginPage> {
           '${userModel.origin?.host}:${userModel.origin?.port}';
       _accessDomains = _getAccessDomains();
       _usernameController.text = userModel.username;
+      if (widget.ticket.isNotEmpty && userModel.activeSession) {
+        _onLoginButtonPressed(ticket: widget.ticket, mRealm: userModel.realm);
+      }
     }
   }
 
@@ -196,6 +204,15 @@ class _ProxmoxLoginPageState extends State<ProxmoxLoginPage> {
       data: ThemeData.dark().copyWith(accentColor: Color(0xFFE47225)),
       child: Scaffold(
         backgroundColor: Theme.of(context).primaryColor,
+        extendBodyBehindAppBar: true,
+        appBar: AppBar(
+          elevation: 0.0,
+          backgroundColor: Colors.transparent,
+          leading: IconButton(
+            icon: Icon(Icons.close),
+            onPressed: () => Navigator.of(context).pop(),
+          ),
+        ),
         body: Stack(
           children: [
             SingleChildScrollView(
@@ -223,18 +240,9 @@ class _ProxmoxLoginPageState extends State<ProxmoxLoginPage> {
                                   child: Column(
                                     mainAxisAlignment: MainAxisAlignment.center,
                                     children: [
-                                      Text(
-                                        'PROXMOX',
-                                        style: TextStyle(
-                                          fontFamily: 'Proxmox',
-                                          fontSize: 36,
-                                        ),
-                                      ),
-                                      Text(
-                                        'Open Source',
-                                        style: TextStyle(
-                                          fontSize: 18,
-                                        ),
+                                      Image.asset(
+                                        'assets/images/proxmox_logo_symbol_wordmark.png',
+                                        package: 'proxmox_login_manager',
                                       ),
                                     ],
                                   ),
@@ -266,6 +274,33 @@ class _ProxmoxLoginPageState extends State<ProxmoxLoginPage> {
                                     _selectedDomain = value;
                                   });
                                 },
+                                onOriginSubmitted: _submittButtonEnabled
+                                    ? () {
+                                        final isValid =
+                                            _formKey.currentState.validate();
+                                        setState(() {
+                                          _submittButtonEnabled = isValid;
+                                        });
+                                        if (isValid) {
+                                          setState(() {
+                                            _accessDomains =
+                                                _getAccessDomains();
+                                          });
+                                        }
+                                      }
+                                    : null,
+                                onPasswordSubmitted: _submittButtonEnabled
+                                    ? () {
+                                        final isValid =
+                                            _formKey.currentState.validate();
+                                        setState(() {
+                                          _submittButtonEnabled = isValid;
+                                        });
+                                        if (isValid) {
+                                          _onLoginButtonPressed();
+                                        }
+                                      }
+                                    : null,
                               ),
                               if (snapshot.hasData)
                                 Expanded(
@@ -341,7 +376,8 @@ class _ProxmoxLoginPageState extends State<ProxmoxLoginPage> {
     );
   }
 
-  Future<void> _onLoginButtonPressed() async {
+  Future<void> _onLoginButtonPressed(
+      {String ticket = '', String mRealm}) async {
     setState(() {
       _progressModel
         ..inProgress = true
@@ -354,8 +390,9 @@ class _ProxmoxLoginPageState extends State<ProxmoxLoginPage> {
       //cleaned form fields
       final origin = Uri.https(_originController.text.trim(), '');
       final username = _usernameController.text.trim();
-      final password = _passwordController.text.trim();
-      final realm = _selectedDomain.realm;
+      final password =
+          ticket.isNotEmpty ? ticket : _passwordController.text.trim();
+      final realm = _selectedDomain?.realm ?? mRealm;
 
       var client = await proxclient.authenticate(
           '$username@$realm', password, origin, settings.sslValidation);
@@ -366,8 +403,18 @@ class _ProxmoxLoginPageState extends State<ProxmoxLoginPage> {
             apiClient: client,
           ),
         ));
+
+        if (client == null) {
+          setState(() {
+            _progressModel.inProgress = false;
+          });
+          return;
+        }
       }
 
+      final status = await client.getClusterStatus();
+      final hostname =
+          status.singleWhere((element) => element.local ?? false).name;
       var loginStorage = await ProxmoxLoginStorage.fromLocalStorage();
 
       if (widget.isCreate) {
@@ -376,14 +423,17 @@ class _ProxmoxLoginPageState extends State<ProxmoxLoginPage> {
           ..username = username
           ..realm = realm
           ..productType = ProxmoxProductType.pve
-          ..ticket = client.credentials.ticket);
+          ..ticket = client.credentials.ticket
+          ..hostname = hostname);
 
         loginStorage = loginStorage.rebuild((b) => b..logins.add(newLogin));
       } else {
         loginStorage = loginStorage.rebuild((b) => b
-          ..logins.remove(widget.userModel)
-          ..logins.add(widget.userModel
-              .rebuild((b) => b..ticket = client.credentials.ticket)));
+          ..logins.rebuildWhere(
+              (m) => m == widget.userModel,
+              (b) => b
+                ..ticket = client.credentials.ticket
+                ..hostname = hostname));
       }
       await loginStorage.saveToDisk();
 
@@ -417,9 +467,12 @@ class _ProxmoxLoginPageState extends State<ProxmoxLoginPage> {
         ..inProgress = true
         ..message = 'Connection test...';
     });
-    var apiBaseUrl = Uri.https(_originController.text.trim(), '');
+    var host = _originController.text.trim();
+    var apiBaseUrl = Uri.https(host, '');
+
+    RegExp portRE = new RegExp(r":\d{1,5}$");
 
-    if (!apiBaseUrl.hasPort) {
+    if (!portRE.hasMatch(host)) {
       _originController.text += ':8006';
       apiBaseUrl = apiBaseUrl.replace(port: 8006);
     }