2020-07-27 20:43:58 +00:00
|
|
|
import 'dart:convert';
|
|
|
|
|
|
|
|
import 'package:flutter/cupertino.dart';
|
|
|
|
import 'package:flutter/foundation.dart';
|
|
|
|
import 'package:flutter/material.dart';
|
|
|
|
import 'package:flutter/widgets.dart';
|
|
|
|
import 'package:mobile_nebula/components/FormPage.dart';
|
|
|
|
import 'package:mobile_nebula/components/PlatformTextFormField.dart';
|
2020-08-31 23:32:40 +00:00
|
|
|
import 'package:mobile_nebula/components/SpecialSelectableText.dart';
|
2020-07-27 20:43:58 +00:00
|
|
|
import 'package:mobile_nebula/components/config/ConfigPageItem.dart';
|
|
|
|
import 'package:mobile_nebula/components/config/ConfigItem.dart';
|
|
|
|
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';
|
2021-05-03 20:16:00 +00:00
|
|
|
import 'package:mobile_nebula/screens/siteConfig/AddCertificateScreen.dart';
|
|
|
|
import 'package:mobile_nebula/screens/siteConfig/CertificateDetailsScreen.dart';
|
2020-07-27 20:43:58 +00:00
|
|
|
import 'package:mobile_nebula/screens/siteConfig/StaticHostsScreen.dart';
|
|
|
|
import 'package:mobile_nebula/services/utils.dart';
|
|
|
|
|
|
|
|
//TODO: Add a config test mechanism
|
2021-05-03 20:16:00 +00:00
|
|
|
//TODO: Enforce a name
|
2020-07-27 20:43:58 +00:00
|
|
|
|
|
|
|
class SiteConfigScreen extends StatefulWidget {
|
|
|
|
const SiteConfigScreen({Key key, this.site, this.onSave}) : super(key: key);
|
|
|
|
|
|
|
|
final Site site;
|
|
|
|
|
|
|
|
// This is called after the target OS has saved the configuration
|
|
|
|
final ValueChanged<Site> onSave;
|
|
|
|
|
|
|
|
@override
|
|
|
|
_SiteConfigScreenState createState() => _SiteConfigScreenState();
|
|
|
|
}
|
|
|
|
|
|
|
|
class _SiteConfigScreenState extends State<SiteConfigScreen> {
|
|
|
|
bool changed = false;
|
|
|
|
bool newSite = false;
|
|
|
|
bool debug = false;
|
|
|
|
Site site;
|
|
|
|
|
|
|
|
final nameController = TextEditingController();
|
|
|
|
|
|
|
|
@override
|
|
|
|
void initState() {
|
|
|
|
if (widget.site == null) {
|
|
|
|
newSite = true;
|
|
|
|
site = Site();
|
|
|
|
} else {
|
|
|
|
site = widget.site;
|
|
|
|
nameController.text = site.name;
|
|
|
|
}
|
|
|
|
|
|
|
|
super.initState();
|
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
|
|
|
Widget build(BuildContext context) {
|
|
|
|
return FormPage(
|
|
|
|
title: newSite ? 'New Site' : 'Edit Site',
|
|
|
|
changed: changed,
|
|
|
|
onSave: () async {
|
|
|
|
site.name = nameController.text;
|
|
|
|
try {
|
|
|
|
await site.save();
|
|
|
|
} catch (error) {
|
|
|
|
return Utils.popError(context, 'Failed to save the site configuration', error.toString());
|
|
|
|
}
|
|
|
|
|
|
|
|
Navigator.pop(context);
|
|
|
|
if (widget.onSave != null) {
|
|
|
|
widget.onSave(site);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
child: Column(
|
|
|
|
children: <Widget>[
|
|
|
|
_main(),
|
|
|
|
_keys(),
|
|
|
|
_hosts(),
|
|
|
|
_advanced(),
|
|
|
|
kDebugMode ? _debugConfig() : Container(height: 0),
|
|
|
|
],
|
|
|
|
));
|
|
|
|
}
|
|
|
|
|
|
|
|
Widget _debugConfig() {
|
|
|
|
var data = "";
|
|
|
|
try {
|
|
|
|
final encoder = new JsonEncoder.withIndent(' ');
|
|
|
|
data = encoder.convert(site);
|
|
|
|
} catch (err) {
|
|
|
|
data = err.toString();
|
|
|
|
}
|
|
|
|
|
2020-08-31 23:32:40 +00:00
|
|
|
return ConfigSection(label: 'DEBUG', children: [ConfigItem(labelWidth: 0, content: SpecialSelectableText(data))]);
|
2020-07-27 20:43:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Widget _main() {
|
|
|
|
return ConfigSection(children: <Widget>[
|
|
|
|
ConfigItem(
|
|
|
|
label: Text("Name"),
|
|
|
|
content: PlatformTextFormField(
|
|
|
|
placeholder: 'Required',
|
|
|
|
controller: nameController,
|
2021-05-03 20:16:00 +00:00
|
|
|
validator: (name) {
|
|
|
|
if (name == null || name == "") {
|
|
|
|
return "A name is required";
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
},
|
2020-07-27 20:43:58 +00:00
|
|
|
))
|
|
|
|
]);
|
|
|
|
}
|
|
|
|
|
|
|
|
Widget _keys() {
|
2021-05-03 20:16:00 +00:00
|
|
|
final certError = site.certInfo == null || !site.certInfo.validity.valid;
|
2020-07-27 20:43:58 +00:00
|
|
|
var caError = site.ca.length == 0;
|
|
|
|
if (!caError) {
|
|
|
|
site.ca.forEach((ca) {
|
|
|
|
if (!ca.validity.valid) {
|
|
|
|
caError = true;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
return ConfigSection(
|
|
|
|
label: "IDENTITY",
|
|
|
|
children: [
|
|
|
|
ConfigPageItem(
|
|
|
|
label: Text('Certificate'),
|
|
|
|
content: Wrap(alignment: WrapAlignment.end, crossAxisAlignment: WrapCrossAlignment.center, children: <Widget>[
|
|
|
|
certError
|
|
|
|
? Padding(
|
|
|
|
child: Icon(Icons.error, color: CupertinoColors.systemRed.resolveFrom(context), size: 20),
|
|
|
|
padding: EdgeInsets.only(right: 5))
|
|
|
|
: Container(),
|
2021-05-03 20:16:00 +00:00
|
|
|
certError ? Text('Needs attention') : Text(site.certInfo.cert.details.name)
|
2020-07-27 20:43:58 +00:00
|
|
|
]),
|
|
|
|
onPressed: () {
|
|
|
|
Utils.openPage(context, (context) {
|
2021-05-03 20:16:00 +00:00
|
|
|
if (site.certInfo != null) {
|
|
|
|
return CertificateDetailsScreen(
|
|
|
|
certInfo: site.certInfo,
|
|
|
|
onReplace: (result) {
|
|
|
|
setState(() {
|
|
|
|
changed = true;
|
|
|
|
site.certInfo = result.certInfo;
|
|
|
|
site.key = result.key;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
return AddCertificateScreen(
|
2020-07-27 20:43:58 +00:00
|
|
|
onSave: (result) {
|
|
|
|
setState(() {
|
|
|
|
changed = true;
|
2021-05-03 20:16:00 +00:00
|
|
|
site.certInfo = result.certInfo;
|
2020-07-27 20:43:58 +00:00
|
|
|
site.key = result.key;
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
},
|
|
|
|
),
|
|
|
|
ConfigPageItem(
|
|
|
|
label: Text("CA"),
|
|
|
|
content:
|
|
|
|
Wrap(alignment: WrapAlignment.end, crossAxisAlignment: WrapCrossAlignment.center, children: <Widget>[
|
|
|
|
caError
|
|
|
|
? Padding(
|
|
|
|
child: Icon(Icons.error, color: CupertinoColors.systemRed.resolveFrom(context), size: 20),
|
|
|
|
padding: EdgeInsets.only(right: 5))
|
|
|
|
: Container(),
|
|
|
|
caError ? Text('Needs attention') : Text(Utils.itemCountFormat(site.ca.length))
|
|
|
|
]),
|
|
|
|
onPressed: () {
|
|
|
|
Utils.openPage(context, (context) {
|
|
|
|
return CAListScreen(
|
|
|
|
cas: site.ca,
|
|
|
|
onSave: (ca) {
|
|
|
|
setState(() {
|
|
|
|
changed = true;
|
|
|
|
site.ca = ca;
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
})
|
|
|
|
],
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
Widget _hosts() {
|
|
|
|
return ConfigSection(
|
|
|
|
label: "Set up static hosts and lighthouses",
|
|
|
|
children: <Widget>[
|
|
|
|
ConfigPageItem(
|
|
|
|
label: Text('Hosts'),
|
|
|
|
content: Wrap(alignment: WrapAlignment.end, crossAxisAlignment: WrapCrossAlignment.center, children: <Widget>[
|
|
|
|
site.staticHostmap.length == 0
|
|
|
|
? Padding(
|
|
|
|
child: Icon(Icons.error, color: CupertinoColors.systemRed.resolveFrom(context), size: 20),
|
|
|
|
padding: EdgeInsets.only(right: 5))
|
|
|
|
: Container(),
|
|
|
|
site.staticHostmap.length == 0
|
|
|
|
? Text('Needs attention')
|
|
|
|
: Text(Utils.itemCountFormat(site.staticHostmap.length))
|
|
|
|
]),
|
|
|
|
onPressed: () {
|
|
|
|
Utils.openPage(context, (context) {
|
|
|
|
return StaticHostsScreen(
|
|
|
|
hostmap: site.staticHostmap,
|
|
|
|
onSave: (map) {
|
|
|
|
setState(() {
|
|
|
|
changed = true;
|
|
|
|
site.staticHostmap = map;
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
},
|
|
|
|
),
|
|
|
|
],
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
Widget _advanced() {
|
|
|
|
return ConfigSection(
|
|
|
|
children: <Widget>[
|
|
|
|
ConfigPageItem(
|
|
|
|
label: Text('Advanced'),
|
|
|
|
onPressed: () {
|
|
|
|
Utils.openPage(context, (context) {
|
|
|
|
return AdvancedScreen(
|
|
|
|
site: site,
|
|
|
|
onSave: (settings) {
|
|
|
|
setState(() {
|
|
|
|
changed = true;
|
|
|
|
site.cipher = settings.cipher;
|
|
|
|
site.lhDuration = settings.lhDuration;
|
|
|
|
site.port = settings.port;
|
|
|
|
site.logVerbosity = settings.verbosity;
|
|
|
|
site.unsafeRoutes = settings.unsafeRoutes;
|
|
|
|
site.mtu = settings.mtu;
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
})
|
|
|
|
],
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|