From 3b962c5f5ae0da5c7572360b66cc9726d1cecb2a Mon Sep 17 00:00:00 2001 From: NaiJi Date: Wed, 11 Jan 2023 22:01:46 +0400 Subject: [PATCH] feat: Adapt Desired DNS Records checking for Digital Ocean --- .../dns_providers/cloudflare/cloudflare.dart | 82 ++++++++++++ .../dns_providers/desired_dns_record.dart | 44 +++++++ .../digital_ocean_dns/digital_ocean_dns.dart | 90 +++++++++++++ .../rest_maps/dns_providers/dns_provider.dart | 7 + .../cubit/dns_records/dns_records_cubit.dart | 50 ++++--- lib/ui/pages/dns_details/dns_details.dart | 13 +- lib/utils/network_utils.dart | 122 ------------------ 7 files changed, 261 insertions(+), 147 deletions(-) create mode 100644 lib/logic/api_maps/rest_maps/dns_providers/desired_dns_record.dart diff --git a/lib/logic/api_maps/rest_maps/dns_providers/cloudflare/cloudflare.dart b/lib/logic/api_maps/rest_maps/dns_providers/cloudflare/cloudflare.dart index 433ee792..1da08c40 100644 --- a/lib/logic/api_maps/rest_maps/dns_providers/cloudflare/cloudflare.dart +++ b/lib/logic/api_maps/rest_maps/dns_providers/cloudflare/cloudflare.dart @@ -189,6 +189,88 @@ class CloudflareApi extends DnsProviderApi { return allRecords; } + @override + List getDesiredDnsRecords({ + final String? domainName, + final String? ipAddress, + final String? dkimPublicKey, + }) { + if (domainName == null || ipAddress == null) { + return []; + } + return [ + DesiredDnsRecord( + name: domainName, + content: ipAddress, + description: 'record.root', + ), + DesiredDnsRecord( + name: 'api.$domainName', + content: ipAddress, + description: 'record.api', + ), + DesiredDnsRecord( + name: 'cloud.$domainName', + content: ipAddress, + description: 'record.cloud', + ), + DesiredDnsRecord( + name: 'git.$domainName', + content: ipAddress, + description: 'record.git', + ), + DesiredDnsRecord( + name: 'meet.$domainName', + content: ipAddress, + description: 'record.meet', + ), + DesiredDnsRecord( + name: 'social.$domainName', + content: ipAddress, + description: 'record.social', + ), + DesiredDnsRecord( + name: 'password.$domainName', + content: ipAddress, + description: 'record.password', + ), + DesiredDnsRecord( + name: 'vpn.$domainName', + content: ipAddress, + description: 'record.vpn', + ), + DesiredDnsRecord( + name: domainName, + content: domainName, + description: 'record.mx', + type: 'MX', + category: DnsRecordsCategory.email, + ), + DesiredDnsRecord( + name: '_dmarc.$domainName', + content: 'v=DMARC1; p=none', + description: 'record.dmarc', + type: 'TXT', + category: DnsRecordsCategory.email, + ), + DesiredDnsRecord( + name: domainName, + content: 'v=spf1 a mx ip4:$ipAddress -all', + description: 'record.spf', + type: 'TXT', + category: DnsRecordsCategory.email, + ), + if (dkimPublicKey != null) + DesiredDnsRecord( + name: 'selector._domainkey.$domainName', + content: dkimPublicKey, + description: 'record.dkim', + type: 'TXT', + category: DnsRecordsCategory.email, + ), + ]; + } + @override Future> createMultipleDnsRecords({ required final ServerDomain domain, diff --git a/lib/logic/api_maps/rest_maps/dns_providers/desired_dns_record.dart b/lib/logic/api_maps/rest_maps/dns_providers/desired_dns_record.dart new file mode 100644 index 00000000..4a64d49e --- /dev/null +++ b/lib/logic/api_maps/rest_maps/dns_providers/desired_dns_record.dart @@ -0,0 +1,44 @@ +enum DnsRecordsCategory { + services, + email, + other, +} + +class DesiredDnsRecord { + const DesiredDnsRecord({ + required this.name, + required this.content, + this.type = 'A', + this.description = '', + this.category = DnsRecordsCategory.services, + this.isSatisfied = false, + this.displayName, + }); + + final String name; + final String type; + final String content; + final String description; + final String? displayName; + final DnsRecordsCategory category; + final bool isSatisfied; + + DesiredDnsRecord copyWith({ + final String? name, + final String? type, + final String? content, + final String? description, + final String? displayName, + final DnsRecordsCategory? category, + final bool? isSatisfied, + }) => + DesiredDnsRecord( + name: name ?? this.name, + type: type ?? this.type, + content: content ?? this.content, + description: description ?? this.description, + category: category ?? this.category, + isSatisfied: isSatisfied ?? this.isSatisfied, + displayName: displayName ?? this.displayName, + ); +} diff --git a/lib/logic/api_maps/rest_maps/dns_providers/digital_ocean_dns/digital_ocean_dns.dart b/lib/logic/api_maps/rest_maps/dns_providers/digital_ocean_dns/digital_ocean_dns.dart index 309c26a8..b1485312 100644 --- a/lib/logic/api_maps/rest_maps/dns_providers/digital_ocean_dns/digital_ocean_dns.dart +++ b/lib/logic/api_maps/rest_maps/dns_providers/digital_ocean_dns/digital_ocean_dns.dart @@ -171,6 +171,96 @@ class DigitalOceanDnsApi extends DnsProviderApi { return allRecords; } + @override + List getDesiredDnsRecords({ + final String? domainName, + final String? ipAddress, + final String? dkimPublicKey, + }) { + if (domainName == null || ipAddress == null) { + return []; + } + return [ + DesiredDnsRecord( + name: '@', + content: ipAddress, + description: 'record.root', + displayName: domainName, + ), + DesiredDnsRecord( + name: 'api', + content: ipAddress, + description: 'record.api', + displayName: 'api.$domainName', + ), + DesiredDnsRecord( + name: 'cloud', + content: ipAddress, + description: 'record.cloud', + displayName: 'cloud.$domainName', + ), + DesiredDnsRecord( + name: 'git', + content: ipAddress, + description: 'record.git', + displayName: 'git.$domainName', + ), + DesiredDnsRecord( + name: 'meet', + content: ipAddress, + description: 'record.meet', + displayName: 'meet.$domainName', + ), + DesiredDnsRecord( + name: 'social', + content: ipAddress, + description: 'record.social', + displayName: 'social.$domainName', + ), + DesiredDnsRecord( + name: 'password', + content: ipAddress, + description: 'record.password', + displayName: 'password.$domainName', + ), + DesiredDnsRecord( + name: 'vpn', + content: ipAddress, + description: 'record.vpn', + displayName: 'vpn.$domainName', + ), + DesiredDnsRecord( + name: domainName, + content: domainName, + description: 'record.mx', + type: 'MX', + category: DnsRecordsCategory.email, + ), + DesiredDnsRecord( + name: '_dmarc.$domainName', + content: 'v=DMARC1; p=none', + description: 'record.dmarc', + type: 'TXT', + category: DnsRecordsCategory.email, + ), + DesiredDnsRecord( + name: domainName, + content: 'v=spf1 a mx ip4:$ipAddress -all', + description: 'record.spf', + type: 'TXT', + category: DnsRecordsCategory.email, + ), + if (dkimPublicKey != null) + DesiredDnsRecord( + name: 'selector._domainkey.$domainName', + content: dkimPublicKey, + description: 'record.dkim', + type: 'TXT', + category: DnsRecordsCategory.email, + ), + ]; + } + @override Future> createMultipleDnsRecords({ required final ServerDomain domain, diff --git a/lib/logic/api_maps/rest_maps/dns_providers/dns_provider.dart b/lib/logic/api_maps/rest_maps/dns_providers/dns_provider.dart index ce308121..0d010242 100644 --- a/lib/logic/api_maps/rest_maps/dns_providers/dns_provider.dart +++ b/lib/logic/api_maps/rest_maps/dns_providers/dns_provider.dart @@ -1,9 +1,11 @@ import 'package:selfprivacy/logic/api_maps/api_generic_result.dart'; import 'package:selfprivacy/logic/api_maps/rest_maps/api_map.dart'; +import 'package:selfprivacy/logic/api_maps/rest_maps/dns_providers/desired_dns_record.dart'; import 'package:selfprivacy/logic/models/hive/server_domain.dart'; import 'package:selfprivacy/logic/models/json/dns_records.dart'; export 'package:selfprivacy/logic/api_maps/api_generic_result.dart'; +export 'package:selfprivacy/logic/api_maps/rest_maps/dns_providers/desired_dns_record.dart'; class DomainNotFoundException implements Exception { DomainNotFoundException(this.message); @@ -14,6 +16,11 @@ abstract class DnsProviderApi extends ApiMap { Future> getDnsRecords({ required final ServerDomain domain, }); + List getDesiredDnsRecords({ + final String? domainName, + final String? ipAddress, + final String? dkimPublicKey, + }); Future> removeSimilarRecords({ required final ServerDomain domain, final String? ip4, diff --git a/lib/logic/cubit/dns_records/dns_records_cubit.dart b/lib/logic/cubit/dns_records/dns_records_cubit.dart index 3403dc68..589fb1ff 100644 --- a/lib/logic/cubit/dns_records/dns_records_cubit.dart +++ b/lib/logic/cubit/dns_records/dns_records_cubit.dart @@ -25,11 +25,14 @@ class DnsRecordsCubit emit( DnsRecordsState( dnsState: DnsRecordsStatus.refreshing, - dnsRecords: getDesiredDnsRecords( - serverInstallationCubit.state.serverDomain?.domainName, - '', - '', - ), + dnsRecords: ApiController.currentDnsProviderApiFactory! + .getDnsProvider() + .getDesiredDnsRecords( + domainName: + serverInstallationCubit.state.serverDomain?.domainName, + dkimPublicKey: '', + ipAddress: '', + ), ), ); @@ -44,16 +47,23 @@ class DnsRecordsCubit .getDnsRecords(domain: domain); final String? dkimPublicKey = extractDkimRecord(await api.getDnsRecords())?.content; - final List desiredRecords = - getDesiredDnsRecords(domain.domainName, ipAddress, dkimPublicKey); + final List desiredRecords = ApiController + .currentDnsProviderApiFactory! + .getDnsProvider() + .getDesiredDnsRecords( + domainName: domain.domainName, + ipAddress: ipAddress, + dkimPublicKey: dkimPublicKey, + ); final List foundRecords = []; - for (final DesiredDnsRecord record in desiredRecords) { - if (record.description == 'record.dkim') { + for (final DesiredDnsRecord desiredRecord in desiredRecords) { + if (desiredRecord.description == 'record.dkim') { final DnsRecord foundRecord = records.firstWhere( - (final r) => r.name == record.name && r.type == record.type, + (final r) => + r.name == desiredRecord.name && r.type == desiredRecord.type, orElse: () => DnsRecord( - name: record.name, - type: record.type, + name: desiredRecord.name, + type: desiredRecord.type, content: '', ttl: 800, proxied: false, @@ -65,22 +75,22 @@ class DnsRecordsCubit final String? foundContent = foundRecord.content?.replaceAll(RegExp(r'\s+'), ''); final String content = - record.content.replaceAll(RegExp(r'\s+'), ''); + desiredRecord.content.replaceAll(RegExp(r'\s+'), ''); if (foundContent == content) { - foundRecords.add(record.copyWith(isSatisfied: true)); + foundRecords.add(desiredRecord.copyWith(isSatisfied: true)); } else { - foundRecords.add(record.copyWith(isSatisfied: false)); + foundRecords.add(desiredRecord.copyWith(isSatisfied: false)); } } else { if (records.any( (final r) => - r.name == record.name && - r.type == record.type && - r.content == record.content, + r.name == desiredRecord.name && + r.type == desiredRecord.type && + r.content == desiredRecord.content, )) { - foundRecords.add(record.copyWith(isSatisfied: true)); + foundRecords.add(desiredRecord.copyWith(isSatisfied: true)); } else { - foundRecords.add(record.copyWith(isSatisfied: false)); + foundRecords.add(desiredRecord.copyWith(isSatisfied: false)); } } } diff --git a/lib/ui/pages/dns_details/dns_details.dart b/lib/ui/pages/dns_details/dns_details.dart index 692921eb..98f23979 100644 --- a/lib/ui/pages/dns_details/dns_details.dart +++ b/lib/ui/pages/dns_details/dns_details.dart @@ -1,12 +1,12 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:selfprivacy/config/get_it_config.dart'; +import 'package:selfprivacy/logic/api_maps/rest_maps/dns_providers/desired_dns_record.dart'; import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart'; import 'package:selfprivacy/logic/cubit/dns_records/dns_records_cubit.dart'; import 'package:selfprivacy/ui/components/brand_cards/filled_card.dart'; import 'package:selfprivacy/ui/components/brand_hero_screen/brand_hero_screen.dart'; import 'package:selfprivacy/ui/components/brand_icons/brand_icons.dart'; -import 'package:selfprivacy/utils/network_utils.dart'; class DnsDetailsPage extends StatefulWidget { const DnsDetailsPage({super.key}); @@ -109,9 +109,12 @@ class _DnsDetailsPageState extends State { heroIcon: BrandIcons.globe, heroTitle: 'domain.screen_title'.tr(), children: [ - _getStateCard(dnsCubit.dnsState, () { - context.read().fix(); - }), + _getStateCard( + dnsCubit.dnsState, + () { + context.read().fix(); + }, + ), const SizedBox(height: 16.0), ListTile( title: Text( @@ -150,7 +153,7 @@ class _DnsDetailsPageState extends State { dnsRecord.description.tr(), ), subtitle: Text( - dnsRecord.name, + dnsRecord.displayName ?? dnsRecord.name, ), ), ], diff --git a/lib/utils/network_utils.dart b/lib/utils/network_utils.dart index 1a8f0df2..a94ecb35 100644 --- a/lib/utils/network_utils.dart +++ b/lib/utils/network_utils.dart @@ -1,128 +1,6 @@ import 'package:selfprivacy/logic/models/json/dns_records.dart'; import 'package:url_launcher/url_launcher.dart'; -enum DnsRecordsCategory { - services, - email, - other, -} - -class DesiredDnsRecord { - const DesiredDnsRecord({ - required this.name, - required this.content, - this.type = 'A', - this.description = '', - this.category = DnsRecordsCategory.services, - this.isSatisfied = false, - }); - - final String name; - final String type; - final String content; - final String description; - final DnsRecordsCategory category; - final bool isSatisfied; - - DesiredDnsRecord copyWith({ - final String? name, - final String? type, - final String? content, - final String? description, - final DnsRecordsCategory? category, - final bool? isSatisfied, - }) => - DesiredDnsRecord( - name: name ?? this.name, - type: type ?? this.type, - content: content ?? this.content, - description: description ?? this.description, - category: category ?? this.category, - isSatisfied: isSatisfied ?? this.isSatisfied, - ); -} - -List getDesiredDnsRecords( - final String? domainName, - final String? ipAddress, - final String? dkimPublicKey, -) { - if (domainName == null || ipAddress == null) { - return []; - } - return [ - DesiredDnsRecord( - name: domainName, - content: ipAddress, - description: 'record.root', - ), - DesiredDnsRecord( - name: 'api.$domainName', - content: ipAddress, - description: 'record.api', - ), - DesiredDnsRecord( - name: 'cloud.$domainName', - content: ipAddress, - description: 'record.cloud', - ), - DesiredDnsRecord( - name: 'git.$domainName', - content: ipAddress, - description: 'record.git', - ), - DesiredDnsRecord( - name: 'meet.$domainName', - content: ipAddress, - description: 'record.meet', - ), - DesiredDnsRecord( - name: 'social.$domainName', - content: ipAddress, - description: 'record.social', - ), - DesiredDnsRecord( - name: 'password.$domainName', - content: ipAddress, - description: 'record.password', - ), - DesiredDnsRecord( - name: 'vpn.$domainName', - content: ipAddress, - description: 'record.vpn', - ), - DesiredDnsRecord( - name: domainName, - content: domainName, - description: 'record.mx', - type: 'MX', - category: DnsRecordsCategory.email, - ), - DesiredDnsRecord( - name: '_dmarc.$domainName', - content: 'v=DMARC1; p=none', - description: 'record.dmarc', - type: 'TXT', - category: DnsRecordsCategory.email, - ), - DesiredDnsRecord( - name: domainName, - content: 'v=spf1 a mx ip4:$ipAddress -all', - description: 'record.spf', - type: 'TXT', - category: DnsRecordsCategory.email, - ), - if (dkimPublicKey != null) - DesiredDnsRecord( - name: 'selector._domainkey.$domainName', - content: dkimPublicKey, - description: 'record.dkim', - type: 'TXT', - category: DnsRecordsCategory.email, - ), - ]; -} - DnsRecord? extractDkimRecord(final List records) { DnsRecord? dkimRecord;