]>
Commit | Line | Data |
---|---|---|
0e468546 TM |
1 | import 'package:flutter/material.dart'; |
2 | import 'package:proxmox_dart_api_client/proxmox_dart_api_client.dart'; | |
3 | import 'package:proxmox_login_manager/proxmox_login_form.dart'; | |
4 | ||
5 | class ProxmoxTfaForm extends StatefulWidget { | |
a9d1ee22 | 6 | final ProxmoxApiClient? apiClient; |
0e468546 | 7 | |
a9d1ee22 | 8 | const ProxmoxTfaForm({Key? key, this.apiClient}) : super(key: key); |
0e468546 TM |
9 | |
10 | @override | |
11 | _ProxmoxTfaFormState createState() => _ProxmoxTfaFormState(); | |
12 | } | |
13 | ||
14 | class _ProxmoxTfaFormState extends State<ProxmoxTfaForm> { | |
15 | final TextEditingController _codeController = TextEditingController(); | |
16 | bool _isLoading = false; | |
eb675700 TL |
17 | List<String> _tfa_kinds = []; |
18 | String _selected_tfa_kind = ""; | |
19 | ||
20 | @override | |
21 | void initState() { | |
22 | super.initState(); | |
23 | _tfa_kinds = widget.apiClient!.credentials.tfa!.kinds().toList(); | |
24 | _selected_tfa_kind = _tfa_kinds[0]; | |
25 | } | |
26 | ||
0e468546 TM |
27 | @override |
28 | Widget build(BuildContext context) { | |
29 | return Theme( | |
291fc65f DC |
30 | data: ThemeData.dark().copyWith( |
31 | colorScheme: ColorScheme.dark().copyWith( | |
32 | secondary: ProxmoxColors.orange, | |
33 | onSecondary: ProxmoxColors.supportGrey)), | |
79ed04db | 34 | child: Scaffold( |
291fc65f | 35 | backgroundColor: ProxmoxColors.supportBlue, |
79ed04db TM |
36 | extendBodyBehindAppBar: true, |
37 | appBar: AppBar( | |
38 | elevation: 0.0, | |
39 | backgroundColor: Colors.transparent, | |
40 | leading: IconButton( | |
41 | icon: Icon(Icons.close), | |
42 | onPressed: () => Navigator.of(context).pop(), | |
43 | ), | |
44 | ), | |
45 | body: Stack( | |
0e468546 TM |
46 | alignment: Alignment.center, |
47 | children: [ | |
48 | SingleChildScrollView( | |
49 | child: ConstrainedBox( | |
50 | constraints: BoxConstraints.tightFor( | |
51 | height: MediaQuery.of(context).size.height), | |
0de76be7 TL |
52 | child: SafeArea( |
53 | child: Padding( | |
54 | padding: const EdgeInsets.all(8.0), | |
55 | child: Column( | |
56 | mainAxisAlignment: MainAxisAlignment.start, | |
57 | crossAxisAlignment: CrossAxisAlignment.center, | |
58 | children: <Widget>[ | |
59 | Padding( | |
60 | padding: const EdgeInsets.fromLTRB(0, 100.0, 0, 30.0), | |
61 | child: Icon( | |
62 | Icons.lock, | |
63 | size: 48, | |
64 | ), | |
0e468546 | 65 | ), |
0de76be7 TL |
66 | Text( |
67 | 'Verify', | |
68 | style: TextStyle( | |
69 | fontSize: 36, | |
70 | color: Colors.white, | |
71 | fontWeight: FontWeight.bold), | |
72 | ), | |
73 | Text( | |
74 | 'Check your second factor provider', | |
75 | style: TextStyle( | |
76 | color: Colors.white38, | |
77 | fontWeight: FontWeight.bold), | |
78 | ), | |
79 | Padding( | |
80 | padding: const EdgeInsets.fromLTRB(0, 50.0, 0, 8.0), | |
81 | child: Container( | |
82 | width: 175, | |
83 | child: Column( | |
84 | children: <Widget>[ | |
85 | DropdownButtonFormField( | |
eb675700 | 86 | decoration: InputDecoration( |
0de76be7 TL |
87 | labelText: 'Method', |
88 | icon: Icon(Icons.input)), | |
89 | items: _tfa_kinds | |
90 | .map((e) => DropdownMenuItem( | |
91 | child: ListTile(title: Text(e)), | |
92 | value: e, | |
93 | )) | |
94 | .toList(), | |
95 | onChanged: (String? value) { | |
96 | setState(() { | |
97 | _selected_tfa_kind = value!; | |
98 | }); | |
99 | }, | |
100 | selectedItemBuilder: (context) => | |
101 | _tfa_kinds.map((e) => Text(e)).toList(), | |
102 | value: _selected_tfa_kind, | |
103 | ), | |
104 | TextField( | |
105 | controller: _codeController, | |
106 | textAlign: TextAlign.center, | |
107 | decoration: InputDecoration( | |
108 | labelText: 'Code', | |
109 | icon: Icon(Icons.pin)), | |
110 | autofocus: true, | |
111 | onSubmitted: (value) => _submitTfaCode()), | |
112 | ], | |
113 | ), | |
eb675700 | 114 | ), |
0e468546 | 115 | ), |
0de76be7 TL |
116 | Expanded( |
117 | child: Align( | |
118 | alignment: Alignment.bottomCenter, | |
119 | child: Container( | |
120 | width: MediaQuery.of(context).size.width, | |
121 | child: TextButton( | |
122 | style: TextButton.styleFrom( | |
123 | foregroundColor: Colors.white, | |
124 | backgroundColor: Color(0xFFE47225), | |
125 | disabledBackgroundColor: Colors.grey, | |
126 | ), | |
127 | onPressed: () => _submitTfaCode(), | |
128 | child: Text('Continue'), | |
fcdfb148 | 129 | ), |
0e468546 TM |
130 | ), |
131 | ), | |
132 | ), | |
0de76be7 TL |
133 | ], |
134 | ), | |
0e468546 TM |
135 | ), |
136 | ), | |
137 | ), | |
138 | ), | |
139 | if (_isLoading) | |
140 | ProxmoxProgressOverlay( | |
aa8e7870 | 141 | message: 'Verifying second-factor...', |
0e468546 TM |
142 | ) |
143 | ], | |
144 | ), | |
145 | ), | |
146 | ); | |
147 | } | |
148 | ||
149 | Future<void> _submitTfaCode() async { | |
150 | setState(() { | |
151 | _isLoading = true; | |
152 | }); | |
153 | try { | |
eb675700 TL |
154 | final client = await widget.apiClient! |
155 | .finishTfaChallenge(_selected_tfa_kind, _codeController.text); | |
0e468546 TM |
156 | Navigator.of(context).pop(client); |
157 | } on ProxmoxApiException catch (e) { | |
158 | showDialog( | |
159 | context: context, | |
160 | builder: (context) => ProxmoxApiErrorDialog( | |
161 | exception: e, | |
162 | ), | |
163 | ); | |
164 | } catch (e, trace) { | |
165 | print(e); | |
166 | print(trace); | |
167 | showDialog( | |
168 | context: context, | |
90a3b765 DC |
169 | builder: (context) => ConnectionErrorDialog( |
170 | exception: e, | |
0e468546 TM |
171 | ), |
172 | ); | |
173 | } | |
174 | setState(() { | |
175 | _isLoading = false; | |
176 | }); | |
177 | } | |
178 | } |