mobile_nebula/lib/screens/siteConfig/CertificateDetailsScreen.dart

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

209 lines
7 KiB
Dart
Raw Normal View History

2020-07-27 20:43:58 +00:00
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
Add DangerButton component (#219) This pulls out a component that we can use for "dangerous" operations like deleting, and styles it in one place. It also starts to move us slowly towards Material 3, with the rounded corners on these buttons in Android. Android: |Before Light|Before Dark|After Light|After Dark| |---|---|---|---| |<img width="425" alt="Android Studio 2025-01-15 14 16 36" src="https://github.com/user-attachments/assets/4823e551-6a40-48dd-9bc1-3004699b90ea" />|<img width="417" alt="Android Studio 2025-01-15 14 16 47" src="https://github.com/user-attachments/assets/df5461fd-586e-47bb-99b9-0212e63f0454" />|<img width="413" alt="Android Studio 2025-01-15 14 15 59" src="https://github.com/user-attachments/assets/d88a6225-b71a-4886-8387-e35811a3a6ec" />|<img width="418" alt="Android Studio 2025-01-15 14 16 15" src="https://github.com/user-attachments/assets/d4f23b1c-7003-4a00-b865-4a123d8fe3e9" />| iOS: |Before Light|Before Dark|After Light|After Dark| |---|---|---|---| |<img width="437" alt="Simulator 2025-01-15 15 56 26" src="https://github.com/user-attachments/assets/87c4eed3-6d07-4858-8ad8-d8c011538154" />|<img width="445" alt="Simulator 2025-01-15 15 56 36" src="https://github.com/user-attachments/assets/9dc5b174-7bc7-48ec-a3c0-61633168c31a" />|<img width="439" alt="Simulator 2025-01-15 16 05 23" src="https://github.com/user-attachments/assets/31dc9ab6-8a3c-49c7-892d-627f16e2a8cd" />|<img width="444" alt="Simulator 2025-01-15 16 05 37" src="https://github.com/user-attachments/assets/979280d6-e1f4-4d57-a86a-10bb4def729a" />|
2025-01-16 13:16:23 +00:00
import 'package:mobile_nebula/components/DangerButton.dart';
2021-05-03 20:16:00 +00:00
import 'package:mobile_nebula/components/FormPage.dart';
2020-07-27 20:43:58 +00:00
import 'package:mobile_nebula/components/config/ConfigItem.dart';
import 'package:mobile_nebula/components/config/ConfigSection.dart';
import 'package:mobile_nebula/models/Certificate.dart';
2021-05-03 20:16:00 +00:00
import 'package:mobile_nebula/screens/siteConfig/AddCertificateScreen.dart';
2020-07-27 20:43:58 +00:00
import 'package:mobile_nebula/services/utils.dart';
/// Displays the details of a CertificateInfo object. Respects incomplete objects (missing validity or rawCert)
class CertificateDetailsScreen extends StatefulWidget {
const CertificateDetailsScreen({
Key? key,
required this.certInfo,
this.onDelete,
this.onSave,
this.onReplace,
this.pubKey,
this.privKey,
required this.supportsQRScanning,
}) : super(key: key);
2020-07-27 20:43:58 +00:00
2021-05-03 20:16:00 +00:00
final CertificateInfo certInfo;
// onDelete is used to remove a CA cert
final Function? onDelete;
2020-07-27 20:43:58 +00:00
2021-05-03 20:16:00 +00:00
// onSave is used to install a new certificate
final Function? onSave;
2021-05-03 20:16:00 +00:00
// onReplace is used to install a new certificate over top of the old one
final ValueChanged<CertificateResult>? onReplace;
2021-05-03 20:16:00 +00:00
// pubKey and privKey should be set if onReplace is not null.
final String? pubKey;
final String? privKey;
final bool supportsQRScanning;
2020-07-27 20:43:58 +00:00
@override
_CertificateDetailsScreenState createState() => _CertificateDetailsScreenState();
}
class _CertificateDetailsScreenState extends State<CertificateDetailsScreen> {
2021-05-03 20:16:00 +00:00
bool changed = false;
CertificateResult? certResult;
late CertificateInfo certInfo;
2021-05-03 20:16:00 +00:00
ScrollController controller = ScrollController();
@override
void initState() {
certInfo = widget.certInfo;
super.initState();
}
@override
void dispose() {
controller.dispose();
super.dispose();
}
2020-07-27 20:43:58 +00:00
@override
Widget build(BuildContext context) {
2021-05-03 20:16:00 +00:00
return FormPage(
2020-07-27 20:43:58 +00:00
title: 'Certificate Details',
2021-05-03 20:16:00 +00:00
scrollController: controller,
changed: widget.onSave != null || changed,
onSave: () {
if (widget.onSave != null) {
Navigator.pop(context);
widget.onSave!();
2021-05-03 20:16:00 +00:00
} else if (widget.onReplace != null) {
Navigator.pop(context);
widget.onReplace!(certResult!);
2021-05-03 20:16:00 +00:00
}
},
hideSave: widget.onSave == null && widget.onReplace == null,
2020-07-27 20:43:58 +00:00
child: Column(children: [
_buildID(),
_buildFilters(),
_buildValid(),
_buildAdvanced(),
2021-05-03 20:16:00 +00:00
_buildReplace(),
2020-07-27 20:43:58 +00:00
_buildDelete(),
]),
);
}
Widget _buildID() {
return ConfigSection(children: <Widget>[
ConfigItem(label: Text('Name'), content: SelectableText(certInfo.cert.details.name)),
2020-07-27 20:43:58 +00:00
ConfigItem(
2021-05-03 20:16:00 +00:00
label: Text('Type'), content: Text(certInfo.cert.details.isCa ? 'CA certificate' : 'Client certificate')),
2020-07-27 20:43:58 +00:00
]);
}
Widget _buildValid() {
var valid = Text('yes');
if (certInfo.validity != null && !certInfo.validity!.valid) {
valid = Text(certInfo.validity!.valid ? 'yes' : certInfo.validity!.reason,
2020-07-27 20:43:58 +00:00
style: TextStyle(color: CupertinoColors.systemRed.resolveFrom(context)));
}
return ConfigSection(
label: 'VALIDITY',
children: <Widget>[
ConfigItem(label: Text('Valid?'), content: valid),
ConfigItem(
2022-07-27 16:38:02 +00:00
label: Text('Created'), content: SelectableText(certInfo.cert.details.notBefore.toLocal().toString())),
2020-07-27 20:43:58 +00:00
ConfigItem(
2022-07-27 16:38:02 +00:00
label: Text('Expires'), content: SelectableText(certInfo.cert.details.notAfter.toLocal().toString())),
2020-07-27 20:43:58 +00:00
],
);
}
Widget _buildFilters() {
List<Widget> items = [];
2021-05-03 20:16:00 +00:00
if (certInfo.cert.details.groups.length > 0) {
2022-07-27 16:38:02 +00:00
items.add(ConfigItem(label: Text('Groups'), content: SelectableText(certInfo.cert.details.groups.join(', '))));
2020-07-27 20:43:58 +00:00
}
2021-05-03 20:16:00 +00:00
if (certInfo.cert.details.ips.length > 0) {
items.add(ConfigItem(label: Text('IPs'), content: SelectableText(certInfo.cert.details.ips.join(', '))));
2020-07-27 20:43:58 +00:00
}
2021-05-03 20:16:00 +00:00
if (certInfo.cert.details.subnets.length > 0) {
2022-07-27 16:38:02 +00:00
items.add(ConfigItem(label: Text('Subnets'), content: SelectableText(certInfo.cert.details.subnets.join(', '))));
2020-07-27 20:43:58 +00:00
}
return items.length > 0
2021-05-03 20:16:00 +00:00
? ConfigSection(label: certInfo.cert.details.isCa ? 'FILTERS' : 'DETAILS', children: items)
2020-07-27 20:43:58 +00:00
: Container();
}
Widget _buildAdvanced() {
return ConfigSection(
children: <Widget>[
ConfigItem(
label: Text('Fingerprint'),
2022-07-27 16:38:02 +00:00
content:
SelectableText(certInfo.cert.fingerprint, style: TextStyle(fontFamily: 'RobotoMono', fontSize: 14)),
2020-07-27 20:43:58 +00:00
crossAxisAlignment: CrossAxisAlignment.start),
ConfigItem(
label: Text('Public Key'),
content: SelectableText(certInfo.cert.details.publicKey,
2020-07-27 20:43:58 +00:00
style: TextStyle(fontFamily: 'RobotoMono', fontSize: 14)),
crossAxisAlignment: CrossAxisAlignment.start),
2021-05-03 20:16:00 +00:00
certInfo.rawCert != null
2020-07-27 20:43:58 +00:00
? ConfigItem(
label: Text('PEM Format'),
content: SelectableText(certInfo.rawCert!, style: TextStyle(fontFamily: 'RobotoMono', fontSize: 14)),
2020-07-27 20:43:58 +00:00
crossAxisAlignment: CrossAxisAlignment.start)
: Container(),
],
);
}
2021-05-03 20:16:00 +00:00
Widget _buildReplace() {
if (widget.onReplace == null || widget.pubKey == null || widget.privKey == null) {
2021-05-03 20:16:00 +00:00
return Container();
}
return Padding(
padding: EdgeInsets.only(top: 50, bottom: 10, left: 10, right: 10),
child: SizedBox(
width: double.infinity,
Add DangerButton component (#219) This pulls out a component that we can use for "dangerous" operations like deleting, and styles it in one place. It also starts to move us slowly towards Material 3, with the rounded corners on these buttons in Android. Android: |Before Light|Before Dark|After Light|After Dark| |---|---|---|---| |<img width="425" alt="Android Studio 2025-01-15 14 16 36" src="https://github.com/user-attachments/assets/4823e551-6a40-48dd-9bc1-3004699b90ea" />|<img width="417" alt="Android Studio 2025-01-15 14 16 47" src="https://github.com/user-attachments/assets/df5461fd-586e-47bb-99b9-0212e63f0454" />|<img width="413" alt="Android Studio 2025-01-15 14 15 59" src="https://github.com/user-attachments/assets/d88a6225-b71a-4886-8387-e35811a3a6ec" />|<img width="418" alt="Android Studio 2025-01-15 14 16 15" src="https://github.com/user-attachments/assets/d4f23b1c-7003-4a00-b865-4a123d8fe3e9" />| iOS: |Before Light|Before Dark|After Light|After Dark| |---|---|---|---| |<img width="437" alt="Simulator 2025-01-15 15 56 26" src="https://github.com/user-attachments/assets/87c4eed3-6d07-4858-8ad8-d8c011538154" />|<img width="445" alt="Simulator 2025-01-15 15 56 36" src="https://github.com/user-attachments/assets/9dc5b174-7bc7-48ec-a3c0-61633168c31a" />|<img width="439" alt="Simulator 2025-01-15 16 05 23" src="https://github.com/user-attachments/assets/31dc9ab6-8a3c-49c7-892d-627f16e2a8cd" />|<img width="444" alt="Simulator 2025-01-15 16 05 37" src="https://github.com/user-attachments/assets/979280d6-e1f4-4d57-a86a-10bb4def729a" />|
2025-01-16 13:16:23 +00:00
child: DangerButton(
2021-05-03 20:16:00 +00:00
child: Text('Replace certificate'),
onPressed: () {
Utils.openPage(context, (context) {
return AddCertificateScreen(
onReplace: (result) {
setState(() {
changed = true;
certResult = result;
certInfo = result.certInfo;
});
// Slam the page back to the top
controller.animateTo(0,
duration: const Duration(milliseconds: 10), curve: Curves.linearToEaseOut);
},
pubKey: widget.pubKey!,
privKey: widget.privKey!,
supportsQRScanning: widget.supportsQRScanning,
);
2021-05-03 20:16:00 +00:00
});
})));
}
2020-07-27 20:43:58 +00:00
Widget _buildDelete() {
if (widget.onDelete == null) {
return Container();
}
2021-05-03 20:16:00 +00:00
var title = certInfo.cert.details.isCa ? 'Delete CA?' : 'Delete cert?';
2020-07-27 20:43:58 +00:00
return Padding(
padding: EdgeInsets.only(top: 50, bottom: 10, left: 10, right: 10),
child: SizedBox(
width: double.infinity,
Add DangerButton component (#219) This pulls out a component that we can use for "dangerous" operations like deleting, and styles it in one place. It also starts to move us slowly towards Material 3, with the rounded corners on these buttons in Android. Android: |Before Light|Before Dark|After Light|After Dark| |---|---|---|---| |<img width="425" alt="Android Studio 2025-01-15 14 16 36" src="https://github.com/user-attachments/assets/4823e551-6a40-48dd-9bc1-3004699b90ea" />|<img width="417" alt="Android Studio 2025-01-15 14 16 47" src="https://github.com/user-attachments/assets/df5461fd-586e-47bb-99b9-0212e63f0454" />|<img width="413" alt="Android Studio 2025-01-15 14 15 59" src="https://github.com/user-attachments/assets/d88a6225-b71a-4886-8387-e35811a3a6ec" />|<img width="418" alt="Android Studio 2025-01-15 14 16 15" src="https://github.com/user-attachments/assets/d4f23b1c-7003-4a00-b865-4a123d8fe3e9" />| iOS: |Before Light|Before Dark|After Light|After Dark| |---|---|---|---| |<img width="437" alt="Simulator 2025-01-15 15 56 26" src="https://github.com/user-attachments/assets/87c4eed3-6d07-4858-8ad8-d8c011538154" />|<img width="445" alt="Simulator 2025-01-15 15 56 36" src="https://github.com/user-attachments/assets/9dc5b174-7bc7-48ec-a3c0-61633168c31a" />|<img width="439" alt="Simulator 2025-01-15 16 05 23" src="https://github.com/user-attachments/assets/31dc9ab6-8a3c-49c7-892d-627f16e2a8cd" />|<img width="444" alt="Simulator 2025-01-15 16 05 37" src="https://github.com/user-attachments/assets/979280d6-e1f4-4d57-a86a-10bb4def729a" />|
2025-01-16 13:16:23 +00:00
child: DangerButton(
2020-07-27 20:43:58 +00:00
child: Text('Delete'),
onPressed: () => Utils.confirmDelete(context, title, () async {
Navigator.pop(context);
widget.onDelete!();
2020-07-27 20:43:58 +00:00
}))));
}
}