forked from core/mobile_nebula
Support replacing certs (#33)
This commit is contained in:
parent
a283bf8010
commit
0bb2a30829
|
@ -263,9 +263,11 @@ class IncomingSite(
|
|||
}
|
||||
|
||||
if (key != null) {
|
||||
val f = EncFile(context).openWrite(siteDir.resolve("key"))
|
||||
f.use { it.write(key) }
|
||||
f.close()
|
||||
val keyFile = siteDir.resolve("key")
|
||||
keyFile.delete()
|
||||
val encFile = EncFile(context).openWrite(keyFile)
|
||||
encFile.use { it.write(key) }
|
||||
encFile.close()
|
||||
}
|
||||
|
||||
key = null
|
||||
|
|
|
@ -8,12 +8,13 @@ import 'package:mobile_nebula/services/utils.dart';
|
|||
/// SimplePage with a form and built in validation and confirmation to discard changes if any are made
|
||||
class FormPage extends StatefulWidget {
|
||||
const FormPage(
|
||||
{Key key, this.title, @required this.child, @required this.onSave, @required this.changed, this.hideSave = false})
|
||||
{Key key, this.title, @required this.child, @required this.onSave, @required this.changed, this.hideSave = false, this.scrollController})
|
||||
: super(key: key);
|
||||
|
||||
final String title;
|
||||
final Function onSave;
|
||||
final Widget child;
|
||||
final ScrollController scrollController;
|
||||
|
||||
/// If you need the page to progress to a certain point before saving, control it here
|
||||
final bool hideSave;
|
||||
|
@ -50,6 +51,7 @@ class _FormPageState extends State<FormPage> {
|
|||
child: SimplePage(
|
||||
leadingAction: _buildLeader(context),
|
||||
trailingActions: _buildTrailer(context),
|
||||
scrollController: widget.scrollController,
|
||||
title: widget.title,
|
||||
child: Form(
|
||||
key: _formKey,
|
||||
|
|
|
@ -28,8 +28,8 @@ class SiteItem extends StatelessWidget {
|
|||
Widget _buildContent(BuildContext context) {
|
||||
final border = BorderSide(color: Utils.configSectionBorder(context));
|
||||
var ip = "Error";
|
||||
if (site.cert != null && site.cert.cert.details.ips.length > 0) {
|
||||
ip = site.cert.cert.details.ips[0];
|
||||
if (site.certInfo != null && site.certInfo.cert.details.ips.length > 0) {
|
||||
ip = site.certInfo.cert.details.ips[0];
|
||||
}
|
||||
|
||||
return SpecialButton(
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
import 'dart:io';
|
||||
|
||||
class IPAndPort {
|
||||
String ip;
|
||||
int port;
|
||||
|
|
|
@ -27,7 +27,7 @@ class Site {
|
|||
|
||||
// pki fields
|
||||
List<CertificateInfo> ca;
|
||||
CertificateInfo cert;
|
||||
CertificateInfo certInfo;
|
||||
String key;
|
||||
|
||||
// lighthouse options
|
||||
|
@ -52,7 +52,7 @@ class Site {
|
|||
id,
|
||||
staticHostmap,
|
||||
ca,
|
||||
this.cert,
|
||||
this.certInfo,
|
||||
this.lhDuration = 0,
|
||||
this.port = 0,
|
||||
this.cipher = "aes",
|
||||
|
@ -95,7 +95,7 @@ class Site {
|
|||
});
|
||||
|
||||
if (json['cert'] != null) {
|
||||
cert = CertificateInfo.fromJson(json['cert']);
|
||||
certInfo = CertificateInfo.fromJson(json['cert']);
|
||||
}
|
||||
|
||||
lhDuration = json['lhDuration'];
|
||||
|
@ -146,7 +146,7 @@ class Site {
|
|||
return cert.rawCert;
|
||||
})?.join('\n') ??
|
||||
"",
|
||||
'cert': cert?.rawCert,
|
||||
'cert': certInfo?.rawCert,
|
||||
'key': key,
|
||||
'lhDuration': lhDuration,
|
||||
'port': port,
|
||||
|
|
|
@ -68,7 +68,7 @@ class _HostInfoScreenState extends State<HostInfoScreen> {
|
|||
labelWidth: 150,
|
||||
content: Text(hostInfo.cert.details.name),
|
||||
onPressed: () => Utils.openPage(
|
||||
context, (context) => CertificateDetailsScreen(certificate: CertificateInfo(cert: hostInfo.cert))))
|
||||
context, (context) => CertificateDetailsScreen(certInfo: CertificateInfo(cert: hostInfo.cert))))
|
||||
: Container(),
|
||||
]);
|
||||
}
|
||||
|
|
|
@ -179,7 +179,7 @@ mUOcsdFcCZiXrj7ryQIG1+WfqA46w71A/lV4nAc=
|
|||
"10.1.0.1": StaticHost(lighthouse: true, destinations: [IPAndPort(ip: '10.1.1.53', port: 4242), IPAndPort(ip: '1::1', port: 4242)])
|
||||
},
|
||||
ca: [CertificateInfo.debug(rawCert: ca)],
|
||||
cert: CertificateInfo.debug(rawCert: cert),
|
||||
certInfo: CertificateInfo.debug(rawCert: cert),
|
||||
unsafeRoutes: [UnsafeRoute(route: '10.3.3.3/32', via: '10.1.0.1')]
|
||||
);
|
||||
|
||||
|
|
|
@ -1,56 +1,54 @@
|
|||
import 'dart:convert';
|
||||
|
||||
import 'package:barcode_scan/barcode_scan.dart';
|
||||
import 'package:file_picker/file_picker.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:mobile_nebula/components/FormPage.dart';
|
||||
import 'package:flutter_platform_widgets/flutter_platform_widgets.dart';
|
||||
import 'package:mobile_nebula/components/SimplePage.dart';
|
||||
import 'package:mobile_nebula/components/SpecialSelectableText.dart';
|
||||
import 'package:mobile_nebula/components/config/ConfigButtonItem.dart';
|
||||
import 'package:mobile_nebula/components/config/ConfigItem.dart';
|
||||
import 'package:mobile_nebula/components/config/ConfigPageItem.dart';
|
||||
import 'package:mobile_nebula/components/config/ConfigSection.dart';
|
||||
import 'package:mobile_nebula/components/config/ConfigTextItem.dart';
|
||||
import 'package:mobile_nebula/models/Certificate.dart';
|
||||
import 'package:mobile_nebula/screens/siteConfig/CertificateDetailsScreen.dart';
|
||||
import 'package:mobile_nebula/services/share.dart';
|
||||
import 'package:mobile_nebula/services/utils.dart';
|
||||
|
||||
import 'CertificateDetailsScreen.dart';
|
||||
|
||||
class CertificateResult {
|
||||
CertificateInfo cert;
|
||||
CertificateInfo certInfo;
|
||||
String key;
|
||||
|
||||
CertificateResult({this.cert, this.key});
|
||||
CertificateResult({this.certInfo, this.key});
|
||||
}
|
||||
|
||||
class CertificateScreen extends StatefulWidget {
|
||||
const CertificateScreen({Key key, this.cert, this.onSave}) : super(key: key);
|
||||
class AddCertificateScreen extends StatefulWidget {
|
||||
const AddCertificateScreen({Key key, this.onSave, this.onReplace}) : super(key: key);
|
||||
|
||||
final CertificateInfo cert;
|
||||
// onSave will pop a new CertificateDetailsScreen
|
||||
final ValueChanged<CertificateResult> onSave;
|
||||
// onReplace will return the CertificateResult, assuming the previous screen is a CertificateDetailsScreen
|
||||
final ValueChanged<CertificateResult> onReplace;
|
||||
|
||||
@override
|
||||
_CertificateScreenState createState() => _CertificateScreenState();
|
||||
_AddCertificateScreenState createState() => _AddCertificateScreenState();
|
||||
}
|
||||
|
||||
class _CertificateScreenState extends State<CertificateScreen> {
|
||||
class _AddCertificateScreenState extends State<AddCertificateScreen> {
|
||||
String pubKey;
|
||||
String privKey;
|
||||
bool changed = false;
|
||||
|
||||
CertificateInfo cert;
|
||||
|
||||
String inputType = 'paste';
|
||||
bool shared = false;
|
||||
|
||||
final pasteController = TextEditingController();
|
||||
static const platform = MethodChannel('net.defined.mobileNebula/NebulaVpnService');
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
cert = widget.cert;
|
||||
_generateKeys();
|
||||
super.initState();
|
||||
}
|
||||
|
||||
|
@ -62,71 +60,29 @@ class _CertificateScreenState extends State<CertificateScreen> {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
List<Widget> items = [];
|
||||
bool hideSave = true;
|
||||
|
||||
if (cert == null) {
|
||||
if (pubKey == null) {
|
||||
items = _buildGenerate();
|
||||
} else {
|
||||
items.addAll(_buildShare());
|
||||
items.addAll(_buildLoadCert());
|
||||
}
|
||||
} else {
|
||||
items.addAll(_buildCertList());
|
||||
hideSave = false;
|
||||
if (pubKey == null) {
|
||||
return Center(
|
||||
child: PlatformCircularProgressIndicator(cupertino: (_, __) {
|
||||
return CupertinoProgressIndicatorData(radius: 500);
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
return FormPage(
|
||||
List<Widget> items = [];
|
||||
items.addAll(_buildShare());
|
||||
items.addAll(_buildLoadCert());
|
||||
|
||||
return SimplePage(
|
||||
title: 'Certificate',
|
||||
changed: changed,
|
||||
hideSave: hideSave,
|
||||
onSave: () {
|
||||
Navigator.pop(context);
|
||||
if (widget.onSave != null) {
|
||||
widget.onSave(CertificateResult(cert: cert, key: privKey));
|
||||
}
|
||||
},
|
||||
child: Column(children: items));
|
||||
}
|
||||
|
||||
_buildCertList() {
|
||||
//TODO: generate a full list
|
||||
return [
|
||||
ConfigSection(
|
||||
children: [
|
||||
ConfigPageItem(
|
||||
content: Text(cert.cert.details.name),
|
||||
onPressed: () {
|
||||
Utils.openPage(context, (context) {
|
||||
//TODO: wire on delete
|
||||
return CertificateDetailsScreen(certificate: cert);
|
||||
});
|
||||
},
|
||||
)
|
||||
],
|
||||
)
|
||||
];
|
||||
}
|
||||
|
||||
List<Widget> _buildGenerate() {
|
||||
return [
|
||||
ConfigSection(label: 'Please generate a new public and private key', children: [
|
||||
ConfigButtonItem(
|
||||
content: Text('Generate Keys'),
|
||||
onPressed: () => _generateKeys(),
|
||||
)
|
||||
])
|
||||
];
|
||||
}
|
||||
|
||||
_generateKeys() async {
|
||||
try {
|
||||
var kp = await platform.invokeMethod("nebula.generateKeyPair");
|
||||
Map<String, dynamic> keyPair = jsonDecode(kp);
|
||||
|
||||
setState(() {
|
||||
changed = true;
|
||||
pubKey = keyPair['PublicKey'];
|
||||
privKey = keyPair['PrivateKey'];
|
||||
});
|
||||
|
@ -148,9 +104,6 @@ class _CertificateScreenState extends State<CertificateScreen> {
|
|||
content: Text('Share Public Key'),
|
||||
onPressed: () async {
|
||||
await Share.share(title: 'Please sign and return a certificate', text: pubKey, filename: 'device.pub');
|
||||
setState(() {
|
||||
shared = true;
|
||||
});
|
||||
},
|
||||
),
|
||||
])
|
||||
|
@ -198,14 +151,7 @@ class _CertificateScreenState extends State<CertificateScreen> {
|
|||
ConfigButtonItem(
|
||||
content: Center(child: Text('Load Certificate')),
|
||||
onPressed: () {
|
||||
_addCertEntry(pasteController.text, (err) {
|
||||
if (err != null) {
|
||||
return Utils.popError(context, 'Failed to parse certificate content', err);
|
||||
}
|
||||
|
||||
pasteController.text = '';
|
||||
setState(() {});
|
||||
});
|
||||
_addCertEntry(pasteController.text);
|
||||
}),
|
||||
],
|
||||
)
|
||||
|
@ -225,13 +171,7 @@ class _CertificateScreenState extends State<CertificateScreen> {
|
|||
return;
|
||||
}
|
||||
|
||||
_addCertEntry(content, (err) {
|
||||
if (err != null) {
|
||||
Utils.popError(context, 'Error loading certificate file', err);
|
||||
} else {
|
||||
setState(() {});
|
||||
}
|
||||
});
|
||||
_addCertEntry(content);
|
||||
} catch (err) {
|
||||
return Utils.popError(context, 'Failed to load certificate file', err.toString());
|
||||
}
|
||||
|
@ -254,13 +194,7 @@ class _CertificateScreenState extends State<CertificateScreen> {
|
|||
|
||||
var result = await BarcodeScanner.scan(options: options);
|
||||
if (result.rawContent != "") {
|
||||
_addCertEntry(result.rawContent, (err) {
|
||||
if (err != null) {
|
||||
Utils.popError(context, 'Error loading certificate content', err);
|
||||
} else {
|
||||
setState(() {});
|
||||
}
|
||||
});
|
||||
_addCertEntry(result.rawContent);
|
||||
}
|
||||
}),
|
||||
],
|
||||
|
@ -268,9 +202,7 @@ class _CertificateScreenState extends State<CertificateScreen> {
|
|||
];
|
||||
}
|
||||
|
||||
_addCertEntry(String rawCert, ValueChanged<String> callback) async {
|
||||
String error;
|
||||
|
||||
_addCertEntry(String rawCert) async {
|
||||
// Allow for app store review testing cert to override the generated key
|
||||
if (rawCert.trim() == _testCert) {
|
||||
privKey = _testKey;
|
||||
|
@ -278,21 +210,36 @@ class _CertificateScreenState extends State<CertificateScreen> {
|
|||
|
||||
try {
|
||||
var rawCerts = await platform.invokeMethod("nebula.parseCerts", <String, String>{"certs": rawCert});
|
||||
|
||||
List<dynamic> certs = jsonDecode(rawCerts);
|
||||
if (certs.length > 0) {
|
||||
var tryCert = CertificateInfo.fromJson(certs.first);
|
||||
if (tryCert.cert.details.isCa) {
|
||||
return callback('A certificate authority is not appropriate for a client certificate.');
|
||||
var tryCertInfo = CertificateInfo.fromJson(certs.first);
|
||||
if (tryCertInfo.cert.details.isCa) {
|
||||
return Utils.popError(context, 'Error loading certificate content', 'A certificate authority is not appropriate for a client certificate.');
|
||||
|
||||
} else if (!tryCertInfo.validity.valid) {
|
||||
return Utils.popError(context, 'Certificate was invalid', tryCertInfo.validity.reason);
|
||||
}
|
||||
//TODO: test that the pubkey matches the privkey
|
||||
cert = tryCert;
|
||||
|
||||
//TODO: test that the pubkey we generated equals the pub key in the cert
|
||||
|
||||
// If we are replacing we just return the results now
|
||||
if (widget.onReplace != null) {
|
||||
Navigator.pop(context);
|
||||
widget.onReplace(CertificateResult(certInfo: tryCertInfo, key: privKey));
|
||||
return;
|
||||
}
|
||||
|
||||
// We have a cert, pop the details screen where they can hit save
|
||||
Utils.openPage(context, (context) {
|
||||
return CertificateDetailsScreen(certInfo: tryCertInfo, onSave: () {
|
||||
Navigator.pop(context);
|
||||
widget.onSave(CertificateResult(certInfo: tryCertInfo, key: privKey));
|
||||
});
|
||||
});
|
||||
}
|
||||
} on PlatformException catch (err) {
|
||||
error = err.details ?? err.message;
|
||||
}
|
||||
|
||||
if (callback != null) {
|
||||
callback(error);
|
||||
return Utils.popError(context, 'Error loading certificate content', err.details ?? err.message);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -77,7 +77,7 @@ class _CAListScreenState extends State<CAListScreen> {
|
|||
onPressed: () {
|
||||
Utils.openPage(context, (context) {
|
||||
return CertificateDetailsScreen(
|
||||
certificate: ca,
|
||||
certInfo: ca,
|
||||
onDelete: () {
|
||||
setState(() {
|
||||
changed = true;
|
||||
|
|
|
@ -2,34 +2,73 @@ import 'package:flutter/cupertino.dart';
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:flutter_platform_widgets/flutter_platform_widgets.dart';
|
||||
import 'package:mobile_nebula/components/SimplePage.dart';
|
||||
import 'package:mobile_nebula/components/FormPage.dart';
|
||||
import 'package:mobile_nebula/components/SpecialSelectableText.dart';
|
||||
import 'package:mobile_nebula/components/config/ConfigItem.dart';
|
||||
import 'package:mobile_nebula/components/config/ConfigSection.dart';
|
||||
import 'package:mobile_nebula/models/Certificate.dart';
|
||||
import 'package:mobile_nebula/screens/siteConfig/AddCertificateScreen.dart';
|
||||
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, this.certificate, this.onDelete}) : super(key: key);
|
||||
const CertificateDetailsScreen({Key key, this.certInfo, this.onDelete, this.onSave, this.onReplace}) : super(key: key);
|
||||
|
||||
final CertificateInfo certificate;
|
||||
final CertificateInfo certInfo;
|
||||
|
||||
// onDelete is used to remove a CA cert
|
||||
final Function onDelete;
|
||||
|
||||
// onSave is used to install a new certificate
|
||||
final Function onSave;
|
||||
|
||||
// onReplace is used to install a new certificate over top of the old one
|
||||
final ValueChanged<CertificateResult> onReplace;
|
||||
|
||||
@override
|
||||
_CertificateDetailsScreenState createState() => _CertificateDetailsScreenState();
|
||||
}
|
||||
|
||||
class _CertificateDetailsScreenState extends State<CertificateDetailsScreen> {
|
||||
bool changed = false;
|
||||
CertificateResult certResult;
|
||||
CertificateInfo certInfo;
|
||||
ScrollController controller = ScrollController();
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
certInfo = widget.certInfo;
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
controller.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SimplePage(
|
||||
return FormPage(
|
||||
title: 'Certificate Details',
|
||||
scrollController: controller,
|
||||
changed: widget.onSave != null || changed,
|
||||
onSave: () {
|
||||
if (widget.onSave != null) {
|
||||
Navigator.pop(context);
|
||||
widget.onSave();
|
||||
} else if (widget.onReplace != null) {
|
||||
Navigator.pop(context);
|
||||
widget.onReplace(certResult);
|
||||
}
|
||||
},
|
||||
hideSave: widget.onSave == null && widget.onReplace == null,
|
||||
child: Column(children: [
|
||||
_buildID(),
|
||||
_buildFilters(),
|
||||
_buildValid(),
|
||||
_buildAdvanced(),
|
||||
_buildReplace(),
|
||||
_buildDelete(),
|
||||
]),
|
||||
);
|
||||
|
@ -37,17 +76,17 @@ class _CertificateDetailsScreenState extends State<CertificateDetailsScreen> {
|
|||
|
||||
Widget _buildID() {
|
||||
return ConfigSection(children: <Widget>[
|
||||
ConfigItem(label: Text('Name'), content: SpecialSelectableText(widget.certificate.cert.details.name)),
|
||||
ConfigItem(label: Text('Name'), content: SpecialSelectableText(certInfo.cert.details.name)),
|
||||
ConfigItem(
|
||||
label: Text('Type'),
|
||||
content: Text(widget.certificate.cert.details.isCa ? 'CA certificate' : 'Client certificate')),
|
||||
content: Text(certInfo.cert.details.isCa ? 'CA certificate' : 'Client certificate')),
|
||||
]);
|
||||
}
|
||||
|
||||
Widget _buildValid() {
|
||||
var valid = Text('yes');
|
||||
if (widget.certificate.validity != null && !widget.certificate.validity.valid) {
|
||||
valid = Text(widget.certificate.validity.valid ? 'yes' : widget.certificate.validity.reason,
|
||||
if (certInfo.validity != null && !certInfo.validity.valid) {
|
||||
valid = Text(certInfo.validity.valid ? 'yes' : certInfo.validity.reason,
|
||||
style: TextStyle(color: CupertinoColors.systemRed.resolveFrom(context)));
|
||||
}
|
||||
return ConfigSection(
|
||||
|
@ -56,33 +95,33 @@ class _CertificateDetailsScreenState extends State<CertificateDetailsScreen> {
|
|||
ConfigItem(label: Text('Valid?'), content: valid),
|
||||
ConfigItem(
|
||||
label: Text('Created'),
|
||||
content: SpecialSelectableText(widget.certificate.cert.details.notBefore.toLocal().toString())),
|
||||
content: SpecialSelectableText(certInfo.cert.details.notBefore.toLocal().toString())),
|
||||
ConfigItem(
|
||||
label: Text('Expires'),
|
||||
content: SpecialSelectableText(widget.certificate.cert.details.notAfter.toLocal().toString())),
|
||||
content: SpecialSelectableText(certInfo.cert.details.notAfter.toLocal().toString())),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildFilters() {
|
||||
List<Widget> items = [];
|
||||
if (widget.certificate.cert.details.groups.length > 0) {
|
||||
if (certInfo.cert.details.groups.length > 0) {
|
||||
items.add(ConfigItem(
|
||||
label: Text('Groups'), content: SpecialSelectableText(widget.certificate.cert.details.groups.join(', '))));
|
||||
label: Text('Groups'), content: SpecialSelectableText(certInfo.cert.details.groups.join(', '))));
|
||||
}
|
||||
|
||||
if (widget.certificate.cert.details.ips.length > 0) {
|
||||
if (certInfo.cert.details.ips.length > 0) {
|
||||
items
|
||||
.add(ConfigItem(label: Text('IPs'), content: SpecialSelectableText(widget.certificate.cert.details.ips.join(', '))));
|
||||
.add(ConfigItem(label: Text('IPs'), content: SpecialSelectableText(certInfo.cert.details.ips.join(', '))));
|
||||
}
|
||||
|
||||
if (widget.certificate.cert.details.subnets.length > 0) {
|
||||
if (certInfo.cert.details.subnets.length > 0) {
|
||||
items.add(ConfigItem(
|
||||
label: Text('Subnets'), content: SpecialSelectableText(widget.certificate.cert.details.subnets.join(', '))));
|
||||
label: Text('Subnets'), content: SpecialSelectableText(certInfo.cert.details.subnets.join(', '))));
|
||||
}
|
||||
|
||||
return items.length > 0
|
||||
? ConfigSection(label: widget.certificate.cert.details.isCa ? 'FILTERS' : 'DETAILS', children: items)
|
||||
? ConfigSection(label: certInfo.cert.details.isCa ? 'FILTERS' : 'DETAILS', children: items)
|
||||
: Container();
|
||||
}
|
||||
|
||||
|
@ -91,18 +130,18 @@ class _CertificateDetailsScreenState extends State<CertificateDetailsScreen> {
|
|||
children: <Widget>[
|
||||
ConfigItem(
|
||||
label: Text('Fingerprint'),
|
||||
content: SpecialSelectableText(widget.certificate.cert.fingerprint,
|
||||
content: SpecialSelectableText(certInfo.cert.fingerprint,
|
||||
style: TextStyle(fontFamily: 'RobotoMono', fontSize: 14)),
|
||||
crossAxisAlignment: CrossAxisAlignment.start),
|
||||
ConfigItem(
|
||||
label: Text('Public Key'),
|
||||
content: SpecialSelectableText(widget.certificate.cert.details.publicKey,
|
||||
content: SpecialSelectableText(certInfo.cert.details.publicKey,
|
||||
style: TextStyle(fontFamily: 'RobotoMono', fontSize: 14)),
|
||||
crossAxisAlignment: CrossAxisAlignment.start),
|
||||
widget.certificate.rawCert != null
|
||||
certInfo.rawCert != null
|
||||
? ConfigItem(
|
||||
label: Text('PEM Format'),
|
||||
content: SpecialSelectableText(widget.certificate.rawCert,
|
||||
content: SpecialSelectableText(certInfo.rawCert,
|
||||
style: TextStyle(fontFamily: 'RobotoMono', fontSize: 14)),
|
||||
crossAxisAlignment: CrossAxisAlignment.start)
|
||||
: Container(),
|
||||
|
@ -110,12 +149,40 @@ class _CertificateDetailsScreenState extends State<CertificateDetailsScreen> {
|
|||
);
|
||||
}
|
||||
|
||||
Widget _buildReplace() {
|
||||
if (widget.onReplace == null) {
|
||||
return Container();
|
||||
}
|
||||
|
||||
return Padding(
|
||||
padding: EdgeInsets.only(top: 50, bottom: 10, left: 10, right: 10),
|
||||
child: SizedBox(
|
||||
width: double.infinity,
|
||||
child: PlatformButton(
|
||||
child: Text('Replace certificate'),
|
||||
color: CupertinoColors.systemRed.resolveFrom(context),
|
||||
onPressed: () {
|
||||
Utils.openPage(context, (context) {
|
||||
return AddCertificateScreen(
|
||||
onReplace: (result) {
|
||||
setState(() {
|
||||
changed = true;
|
||||
certResult = result;
|
||||
certInfo = certResult.certInfo;
|
||||
});
|
||||
// Slam the page back to the top
|
||||
controller.animateTo(0, duration: const Duration(milliseconds: 10), curve: Curves.linearToEaseOut);
|
||||
});
|
||||
});
|
||||
})));
|
||||
}
|
||||
|
||||
Widget _buildDelete() {
|
||||
if (widget.onDelete == null) {
|
||||
return Container();
|
||||
}
|
||||
|
||||
var title = widget.certificate.cert.details.isCa ? 'Delete CA?' : 'Delete cert?';
|
||||
var title = certInfo.cert.details.isCa ? 'Delete CA?' : 'Delete cert?';
|
||||
|
||||
return Padding(
|
||||
padding: EdgeInsets.only(top: 50, bottom: 10, left: 10, right: 10),
|
||||
|
|
|
@ -13,11 +13,13 @@ import 'package:mobile_nebula/components/config/ConfigSection.dart';
|
|||
import 'package:mobile_nebula/models/Site.dart';
|
||||
import 'package:mobile_nebula/screens/siteConfig/AdvancedScreen.dart';
|
||||
import 'package:mobile_nebula/screens/siteConfig/CAListScreen.dart';
|
||||
import 'package:mobile_nebula/screens/siteConfig/CertificateScreen.dart';
|
||||
import 'package:mobile_nebula/screens/siteConfig/AddCertificateScreen.dart';
|
||||
import 'package:mobile_nebula/screens/siteConfig/CertificateDetailsScreen.dart';
|
||||
import 'package:mobile_nebula/screens/siteConfig/StaticHostsScreen.dart';
|
||||
import 'package:mobile_nebula/services/utils.dart';
|
||||
|
||||
//TODO: Add a config test mechanism
|
||||
//TODO: Enforce a name
|
||||
|
||||
class SiteConfigScreen extends StatefulWidget {
|
||||
const SiteConfigScreen({Key key, this.site, this.onSave}) : super(key: key);
|
||||
|
@ -100,12 +102,18 @@ class _SiteConfigScreenState extends State<SiteConfigScreen> {
|
|||
content: PlatformTextFormField(
|
||||
placeholder: 'Required',
|
||||
controller: nameController,
|
||||
validator: (name) {
|
||||
if (name == null || name == "") {
|
||||
return "A name is required";
|
||||
}
|
||||
return null;
|
||||
},
|
||||
))
|
||||
]);
|
||||
}
|
||||
|
||||
Widget _keys() {
|
||||
final certError = site.cert == null || !site.cert.validity.valid;
|
||||
final certError = site.certInfo == null || !site.certInfo.validity.valid;
|
||||
var caError = site.ca.length == 0;
|
||||
if (!caError) {
|
||||
site.ca.forEach((ca) {
|
||||
|
@ -126,16 +134,28 @@ class _SiteConfigScreenState extends State<SiteConfigScreen> {
|
|||
child: Icon(Icons.error, color: CupertinoColors.systemRed.resolveFrom(context), size: 20),
|
||||
padding: EdgeInsets.only(right: 5))
|
||||
: Container(),
|
||||
certError ? Text('Needs attention') : Text(site.cert.cert.details.name)
|
||||
certError ? Text('Needs attention') : Text(site.certInfo.cert.details.name)
|
||||
]),
|
||||
onPressed: () {
|
||||
Utils.openPage(context, (context) {
|
||||
return CertificateScreen(
|
||||
cert: site.cert,
|
||||
if (site.certInfo != null) {
|
||||
return CertificateDetailsScreen(
|
||||
certInfo: site.certInfo,
|
||||
onReplace: (result) {
|
||||
setState(() {
|
||||
changed = true;
|
||||
site.certInfo = result.certInfo;
|
||||
site.key = result.key;
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
return AddCertificateScreen(
|
||||
onSave: (result) {
|
||||
setState(() {
|
||||
changed = true;
|
||||
site.cert = result.cert;
|
||||
site.certInfo = result.certInfo;
|
||||
site.key = result.key;
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue