Added auth token to server modal and dns statistics request

This commit is contained in:
Juan Gilsanz Polo 2022-09-27 02:38:59 +02:00
parent 360b6865be
commit 97e4fea015
9 changed files with 171 additions and 15 deletions

View file

@ -0,0 +1,7 @@
import 'dart:convert';
String encodeBase64UserPass(String user, String pass) {
String credentials = "$user:$pass";
Codec<String, String> stringToBase64 = utf8.fuse(base64);
return stringToBase64.encode(credentials);
}

View file

@ -0,0 +1,73 @@
import 'dart:convert';
DnsStatistics dnsStatisticsFromJson(String str) => DnsStatistics.fromJson(json.decode(str));
String dnsStatisticsToJson(DnsStatistics data) => json.encode(data.toJson());
class DnsStatistics {
final String timeUnits;
final List<Map<String, int>> topQueriedDomains;
final List<Map<String, int>> topClients;
final List<Map<String, int>> topBlockedDomains;
final List<int> dnsQueries;
final List<int> blockedFiltering;
final List<int> replacedSafebrowsing;
final List<int> replacedParental;
final int numDnsQueries;
final int numBlockedFiltering;
final int numReplacedSafebrowsing;
final int numReplacedSafesearch;
final int numReplacedParental;
final double avgProcessingTime;
DnsStatistics({
required this.timeUnits,
required this.topQueriedDomains,
required this.topClients,
required this.topBlockedDomains,
required this.dnsQueries,
required this.blockedFiltering,
required this.replacedSafebrowsing,
required this.replacedParental,
required this.numDnsQueries,
required this.numBlockedFiltering,
required this.numReplacedSafebrowsing,
required this.numReplacedSafesearch,
required this.numReplacedParental,
required this.avgProcessingTime,
});
factory DnsStatistics.fromJson(Map<String, dynamic> json) => DnsStatistics(
timeUnits: json["time_units"],
topQueriedDomains: List<Map<String, int>>.from(json["top_queried_domains"].map((x) => Map.from(x).map((k, v) => MapEntry<String, int>(k, v)))),
topClients: List<Map<String, int>>.from(json["top_clients"].map((x) => Map.from(x).map((k, v) => MapEntry<String, int>(k, v)))),
topBlockedDomains: List<Map<String, int>>.from(json["top_blocked_domains"].map((x) => Map.from(x).map((k, v) => MapEntry<String, int>(k, v)))),
dnsQueries: List<int>.from(json["dns_queries"].map((x) => x)),
blockedFiltering: List<int>.from(json["blocked_filtering"].map((x) => x)),
replacedSafebrowsing: List<int>.from(json["replaced_safebrowsing"].map((x) => x)),
replacedParental: List<int>.from(json["replaced_parental"].map((x) => x)),
numDnsQueries: json["num_dns_queries"],
numBlockedFiltering: json["num_blocked_filtering"],
numReplacedSafebrowsing: json["num_replaced_safebrowsing"],
numReplacedSafesearch: json["num_replaced_safesearch"],
numReplacedParental: json["num_replaced_parental"],
avgProcessingTime: json["avg_processing_time"].toDouble(),
);
Map<String, dynamic> toJson() => {
"time_units": timeUnits,
"top_queried_domains": List<dynamic>.from(topQueriedDomains.map((x) => Map.from(x).map((k, v) => MapEntry<String, dynamic>(k, v)))),
"top_clients": List<dynamic>.from(topClients.map((x) => Map.from(x).map((k, v) => MapEntry<String, dynamic>(k, v)))),
"top_blocked_domains": List<dynamic>.from(topBlockedDomains.map((x) => Map.from(x).map((k, v) => MapEntry<String, dynamic>(k, v)))),
"dns_queries": List<dynamic>.from(dnsQueries.map((x) => x)),
"blocked_filtering": List<dynamic>.from(blockedFiltering.map((x) => x)),
"replaced_safebrowsing": List<dynamic>.from(replacedSafebrowsing.map((x) => x)),
"replaced_parental": List<dynamic>.from(replacedParental.map((x) => x)),
"num_dns_queries": numDnsQueries,
"num_blocked_filtering": numBlockedFiltering,
"num_replaced_safebrowsing": numReplacedSafebrowsing,
"num_replaced_safesearch": numReplacedSafesearch,
"num_replaced_parental": numReplacedParental,
"avg_processing_time": avgProcessingTime,
};
}

View file

@ -8,6 +8,7 @@ class Server {
String user; String user;
String password; String password;
bool defaultServer; bool defaultServer;
String authToken;
Server({ Server({
required this.id, required this.id,
@ -18,6 +19,7 @@ class Server {
this.port, this.port,
required this.user, required this.user,
required this.password, required this.password,
required this.defaultServer required this.defaultServer,
required this.authToken
}); });
} }

View file

@ -1,3 +1,5 @@
import 'package:adguard_home_manager/models/dns_statistics.dart';
import 'package:adguard_home_manager/services/http_requests.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:sqflite/sqflite.dart'; import 'package:sqflite/sqflite.dart';
@ -10,6 +12,7 @@ class ServersProvider with ChangeNotifier {
List<Server> _serversList = []; List<Server> _serversList = [];
Server? _selectedServer; Server? _selectedServer;
bool? _isServerConnected; bool? _isServerConnected;
DnsStatistics? _dnsStatistics;
List<Server> get serversList { List<Server> get serversList {
return _serversList; return _serversList;
@ -23,6 +26,10 @@ class ServersProvider with ChangeNotifier {
return _isServerConnected; return _isServerConnected;
} }
DnsStatistics? get dnsStatistics {
return _dnsStatistics;
}
void setDbInstance(Database db) { void setDbInstance(Database db) {
_dbInstance = db; _dbInstance = db;
} }
@ -42,6 +49,11 @@ class ServersProvider with ChangeNotifier {
notifyListeners(); notifyListeners();
} }
void setDnsStatistics(DnsStatistics data) {
_dnsStatistics = data;
notifyListeners();
}
Future<bool> createServer(Server server) async { Future<bool> createServer(Server server) async {
final saved = await saveServerIntoDb(server); final saved = await saveServerIntoDb(server);
if (saved == true) { if (saved == true) {
@ -127,7 +139,7 @@ class ServersProvider with ChangeNotifier {
try { try {
return await _dbInstance!.transaction((txn) async { return await _dbInstance!.transaction((txn) async {
await txn.rawInsert( await txn.rawInsert(
'INSERT INTO servers (id, name, connectionMethod, domain, path, port, user, password, defaultServer) VALUES ("${server.id}", "${server.name}", "${server.connectionMethod}", "${server.domain}", ${server.path != null ? "${server.path}" : null}, ${server.port}, "${server.user}", "${server.password}", 0)', 'INSERT INTO servers (id, name, connectionMethod, domain, path, port, user, password, defaultServer, authToken) VALUES ("${server.id}", "${server.name}", "${server.connectionMethod}", "${server.domain}", ${server.path != null ? "${server.path}" : null}, ${server.port}, "${server.user}", "${server.password}", 0, "${server.authToken}")',
); );
return true; return true;
}); });
@ -140,7 +152,7 @@ class ServersProvider with ChangeNotifier {
try { try {
return await _dbInstance!.transaction((txn) async { return await _dbInstance!.transaction((txn) async {
await txn.rawUpdate( await txn.rawUpdate(
'UPDATE servers SET name = "${server.name}", connectionMethod = "${server.connectionMethod}", domain = "${server.domain}", path = ${server.path != null ? "${server.path}" : null}, port = ${server.port}, user = "${server.user}", password = "${server.password}" WHERE id = "${server.id}"', 'UPDATE servers SET name = "${server.name}", connectionMethod = "${server.connectionMethod}", domain = "${server.domain}", path = ${server.path != null ? "${server.path}" : null}, port = ${server.port}, user = "${server.user}", password = "${server.password}", authToken = "${server.authToken}" WHERE id = "${server.id}"',
); );
return true; return true;
}); });
@ -191,10 +203,19 @@ class ServersProvider with ChangeNotifier {
user: server['user'], user: server['user'],
password: server['password'], password: server['password'],
defaultServer: convertFromIntToBool(server['defaultServer'])!, defaultServer: convertFromIntToBool(server['defaultServer'])!,
authToken: server['authToken']
); );
_serversList.add(serverObj); _serversList.add(serverObj);
if (convertFromIntToBool(server['defaultServer']) == true) { if (convertFromIntToBool(server['defaultServer']) == true) {
_selectedServer = serverObj; _selectedServer = serverObj;
final dnsStatistics = await getDnsStatistics(serverObj);
if (dnsStatistics['result'] == 'success') {
_dnsStatistics = dnsStatistics['data'];
_isServerConnected = true;
}
else {
_isServerConnected = false;
}
} }
} }
} }

View file

@ -1,10 +1,15 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:adguard_home_manager/providers/servers_provider.dart';
class Home extends StatelessWidget { class Home extends StatelessWidget {
const Home({Key? key}) : super(key: key); const Home({Key? key}) : super(key: key);
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final serversProvider = Provider.of<ServersProvider>(context);
return Container(); return Container();
} }
} }

View file

@ -8,7 +8,7 @@ Future<Map<String, dynamic>> loadDb() async {
'adguard_home_manager.db', 'adguard_home_manager.db',
version: 1, version: 1,
onCreate: (Database db, int version) async { onCreate: (Database db, int version) async {
await db.execute("CREATE TABLE servers (id TEXT PRIMARY KEY, name TEXT, connectionMethod TEXT, domain TEXT, path TEXT, port INTEGER, user TEXT, password TEXT, defaultServer INTEGER)"); await db.execute("CREATE TABLE servers (id TEXT PRIMARY KEY, name TEXT, connectionMethod TEXT, domain TEXT, path TEXT, port INTEGER, user TEXT, password TEXT, defaultServer INTEGER, authToken TEXT)");
}, },
onUpgrade: (Database db, int oldVersion, int newVersion) async { onUpgrade: (Database db, int oldVersion, int newVersion) async {

View file

@ -6,6 +6,7 @@ import 'dart:io';
import 'package:http/http.dart' as http; import 'package:http/http.dart' as http;
import 'package:adguard_home_manager/models/dns_statistics.dart';
import 'package:adguard_home_manager/models/server.dart'; import 'package:adguard_home_manager/models/server.dart';
Future login(Server server) async { Future login(Server server) async {
@ -45,3 +46,31 @@ Future login(Server server) async {
return {'result': 'error'}; return {'result': 'error'};
} }
} }
Future getDnsStatistics(Server server) async {
try {
final result = await http.get(
Uri.parse("${server.connectionMethod}://${server.domain}${server.path ?? ""}${server.port != null ? ':${server.port}' : ""}/control/stats"),
headers: {
'Authorization': 'Basic ${server.authToken}'
}
);
if (result.statusCode == 200) {
return {
'result': 'success',
'data': DnsStatistics.fromJson(jsonDecode(result.body))
};
}
else {
return {'result': 'error'};
}
} on SocketException {
return {'result': 'no_connection'};
} on TimeoutException {
return {'result': 'no_connection'};
} on HandshakeException {
return {'result': 'ssl_error'};
} catch (e) {
return {'result': 'error'};
}
}

View file

@ -1,5 +1,6 @@
// ignore_for_file: use_build_context_synchronously // ignore_for_file: use_build_context_synchronously
import 'package:adguard_home_manager/functions/encode_base64.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:uuid/uuid.dart'; import 'package:uuid/uuid.dart';
@ -206,7 +207,7 @@ class _AddServerModalState extends State<AddServerModal> {
final mediaQuery = MediaQuery.of(context); final mediaQuery = MediaQuery.of(context);
void connect() async { void connect() async {
final Server serverObj = Server( Server serverObj = Server(
id: uuid.v4(), id: uuid.v4(),
name: nameController.text, name: nameController.text,
connectionMethod: connectionType, connectionMethod: connectionType,
@ -214,14 +215,24 @@ class _AddServerModalState extends State<AddServerModal> {
port: int.parse(portController.text), port: int.parse(portController.text),
user: userController.text, user: userController.text,
password: passwordController.text, password: passwordController.text,
defaultServer: defaultServer defaultServer: defaultServer,
authToken: ''
); );
setState(() => isConnecting = true); setState(() => isConnecting = true);
final result = await login(serverObj); final result = await login(serverObj);
setState(() => isConnecting = false); setState(() => isConnecting = false);
if (result['result'] == 'success') { if (result['result'] == 'success') {
serverObj.authToken = encodeBase64UserPass(serverObj.user, serverObj.password);
final serverCreated = await serversProvider.createServer(serverObj); final serverCreated = await serversProvider.createServer(serverObj);
if (serverCreated == true) { if (serverCreated == true) {
final dnsStatistics = await getDnsStatistics(serverObj);
if (dnsStatistics['result'] == 'success') {
serversProvider.setDnsStatistics(dnsStatistics['data']);
serversProvider.setIsServerConnected(true);
}
else {
serversProvider.setIsServerConnected(false);
}
Navigator.pop(context); Navigator.pop(context);
} }
else { else {
@ -284,10 +295,12 @@ class _AddServerModalState extends State<AddServerModal> {
port: int.parse(portController.text), port: int.parse(portController.text),
user: userController.text, user: userController.text,
password: passwordController.text, password: passwordController.text,
defaultServer: defaultServer defaultServer: defaultServer,
authToken: ''
); );
final result = await login(serverObj); final result = await login(serverObj);
if (result['result'] == 'success') { if (result['result'] == 'success') {
serverObj.authToken = encodeBase64UserPass(serverObj.user, serverObj.password);
final serverSaved = await serversProvider.editServer(serverObj); final serverSaved = await serversProvider.editServer(serverObj);
if (serverSaved == true) { if (serverSaved == true) {
Navigator.pop(context); Navigator.pop(context);

View file

@ -1,11 +1,11 @@
// ignore_for_file: use_build_context_synchronously // ignore_for_file: use_build_context_synchronously
import 'package:adguard_home_manager/widgets/add_server_modal.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:expandable/expandable.dart'; import 'package:expandable/expandable.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:adguard_home_manager/widgets/servers_list/delete_modal.dart'; import 'package:adguard_home_manager/widgets/servers_list/delete_modal.dart';
import 'package:adguard_home_manager/widgets/add_server_modal.dart';
import 'package:adguard_home_manager/classes/process_modal.dart'; import 'package:adguard_home_manager/classes/process_modal.dart';
import 'package:adguard_home_manager/models/server.dart'; import 'package:adguard_home_manager/models/server.dart';
@ -54,21 +54,27 @@ class ServersList extends StatelessWidget {
} }
void connectToServer(Server server) async { void connectToServer(Server server) async {
Future connectSuccess(result) async {
serversProvider.setSelectedServer(server);
}
final ProcessModal process = ProcessModal(context: context); final ProcessModal process = ProcessModal(context: context);
process.open(AppLocalizations.of(context)!.connecting); process.open(AppLocalizations.of(context)!.connecting);
final result = await login(server); final result = await login(server);
process.close();
if (result['result'] == 'success') { if (result['result'] == 'success') {
await connectSuccess(result); serversProvider.setSelectedServer(server);
final dnsStatistics = await getDnsStatistics(server);
if (dnsStatistics['result'] == 'success') {
serversProvider.setDnsStatistics(dnsStatistics['data']);
serversProvider.setIsServerConnected(true);
} }
else { else {
serversProvider.setIsServerConnected(false);
}
process.close();
}
else {
process.close();
ScaffoldMessenger.of(context).showSnackBar( ScaffoldMessenger.of(context).showSnackBar(
SnackBar( SnackBar(
content: Text(AppLocalizations.of(context)!.cannotConnect), content: Text(AppLocalizations.of(context)!.cannotConnect),