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;
final List<PveAccessDomainModel> accessDomains;
final PveAccessDomainModel selectedDomain;
final ValueChanged<PveAccessDomainModel> onDomainChanged;
+ final Function onPasswordSubmitted;
+ final Function onOriginSubmitted;
const ProxmoxLoginForm({
Key key,
@required this.originValidator,
this.selectedDomain,
@required this.onDomainChanged,
+ this.onPasswordSubmitted,
+ this.onOriginSubmitted,
}) : super(key: key);
@override
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) {
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;
+ }),
+ ),
+ )
+ ],
+ ),
+ ],
+ ),
);
}
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();
final _formKey = GlobalKey<FormState>();
ProxmoxProgressModel _progressModel;
bool _submittButtonEnabled = true;
+
@override
void initState() {
super.initState();
'${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);
+ }
}
}
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(
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',
),
],
),
_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(
);
}
- Future<void> _onLoginButtonPressed() async {
+ Future<void> _onLoginButtonPressed(
+ {String ticket = '', String mRealm}) async {
setState(() {
_progressModel
..inProgress = true
//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);
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) {
..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();
..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);
}