Updated
This commit is contained in:
@@ -0,0 +1,219 @@
|
||||
{{-- resources/views/filament/pages/dmarc-records.blade.php --}}
|
||||
<x-filament-panels::page>
|
||||
<div class="space-y-6">
|
||||
{{-- Header --}}
|
||||
<div class="bg-white dark:bg-gray-800 p-6 rounded-lg shadow-sm">
|
||||
<h2 class="text-xl font-semibold text-gray-900 dark:text-white mb-2">
|
||||
DMARC Record Configuration
|
||||
</h2>
|
||||
<p class="text-gray-600 dark:text-gray-400">
|
||||
Configure Domain-based Message Authentication, Reporting, and Conformance (DMARC) settings.
|
||||
This page shows how your current settings would appear as a DNS TXT record.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{{-- Current Settings --}}
|
||||
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6">
|
||||
{{-- Policy Settings --}}
|
||||
<div class="bg-white dark:bg-gray-800 rounded-lg shadow-sm overflow-hidden">
|
||||
<div class="px-6 py-4 border-b border-gray-200 dark:border-gray-700">
|
||||
<h3 class="text-lg font-medium text-gray-900 dark:text-white">
|
||||
Policy Settings
|
||||
</h3>
|
||||
</div>
|
||||
<div class="p-6 space-y-4">
|
||||
<div class="flex justify-between items-center">
|
||||
<span class="text-sm font-medium text-gray-500 dark:text-gray-400">Policy</span>
|
||||
<span class="text-sm font-mono px-2 py-1 bg-blue-100 dark:bg-blue-900 text-blue-800 dark:text-blue-200 rounded">
|
||||
{{ $this->settings['dmarc_policy'] ?? 'none' }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="flex justify-between items-center">
|
||||
<span class="text-sm font-medium text-gray-500 dark:text-gray-400">Subdomain Policy</span>
|
||||
<span class="text-sm font-mono px-2 py-1 bg-gray-100 dark:bg-gray-700 text-gray-800 dark:text-gray-200 rounded">
|
||||
{{ $this->settings['dmarc_subdomain_policy'] ?? 'none' }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="flex justify-between items-center">
|
||||
<span class="text-sm font-medium text-gray-500 dark:text-gray-400">DKIM Alignment</span>
|
||||
<span class="text-sm font-mono px-2 py-1 bg-gray-100 dark:bg-gray-700 text-gray-800 dark:text-gray-200 rounded">
|
||||
{{ $this->settings['dmarc_adkim'] ?? 'relaxed' }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="flex justify-between items-center">
|
||||
<span class="text-sm font-medium text-gray-500 dark:text-gray-400">SPF Alignment</span>
|
||||
<span class="text-sm font-mono px-2 py-1 bg-gray-100 dark:bg-gray-700 text-gray-800 dark:text-gray-200 rounded">
|
||||
{{ $this->settings['dmarc_aspf'] ?? 'relaxed' }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="flex justify-between items-center">
|
||||
<span class="text-sm font-medium text-gray-500 dark:text-gray-400">Percentage</span>
|
||||
<span class="text-sm font-mono px-2 py-1 bg-gray-100 dark:bg-gray-700 text-gray-800 dark:text-gray-200 rounded">
|
||||
{{ $this->settings['dmarc_percentage'] ?? 100 }}%
|
||||
</span>
|
||||
</div>
|
||||
<div class="flex justify-between items-center">
|
||||
<span class="text-sm font-medium text-gray-500 dark:text-gray-400">Testing Mode</span>
|
||||
<span class="text-sm font-mono px-2 py-1 bg-gray-100 dark:bg-gray-700 text-gray-800 dark:text-gray-200 rounded">
|
||||
{{ isset($this->settings['dmarc_t']) && $this->settings['dmarc_t'] === 'y' ? 'Enabled' : 'Disabled' }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="flex justify-between items-center">
|
||||
<span class="text-sm font-medium text-gray-500 dark:text-gray-400">Report Interval</span>
|
||||
<span class="text-sm font-mono px-2 py-1 bg-gray-100 dark:bg-gray-700 text-gray-800 dark:text-gray-200 rounded">
|
||||
{{ $this->settings['dmarc_report_interval'] ?? 86400 }} seconds
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- Reporting Settings --}}
|
||||
<div class="bg-white dark:bg-gray-800 rounded-lg shadow-sm overflow-hidden">
|
||||
<div class="px-6 py-4 border-b border-gray-200 dark:border-gray-700">
|
||||
<h3 class="text-lg font-medium text-gray-900 dark:text-white">
|
||||
Reporting Settings
|
||||
</h3>
|
||||
</div>
|
||||
<div class="p-6 space-y-4">
|
||||
<div>
|
||||
<span class="text-sm font-medium text-gray-500 dark:text-gray-400 block mb-2">Aggregate Reports (RUA)</span>
|
||||
<div class="flex flex-wrap gap-2">
|
||||
@forelse($this->getRuaDestinations() as $destination)
|
||||
<span class="text-sm font-mono px-2 py-1 bg-green-100 dark:bg-green-900 text-green-800 dark:text-green-200 rounded">
|
||||
{{ $destination }}
|
||||
</span>
|
||||
@empty
|
||||
<span class="text-sm text-gray-500 dark:text-gray-400">No destinations configured</span>
|
||||
@endforelse
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<span class="text-sm font-medium text-gray-500 dark:text-gray-400 block mb-2">Forensic Reports (RUF)</span>
|
||||
<div class="flex flex-wrap gap-2">
|
||||
@forelse($this->getRufDestinations() as $destination)
|
||||
<span class="text-sm font-mono px-2 py-1 bg-red-100 dark:bg-red-900 text-red-800 dark:text-red-200 rounded">
|
||||
{{ $destination }}
|
||||
</span>
|
||||
@empty
|
||||
<span class="text-sm text-gray-500 dark:text-gray-400">No destinations configured</span>
|
||||
@endforelse
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex justify-between items-center">
|
||||
<span class="text-sm font-medium text-gray-500 dark:text-gray-400">Reporting Options (FO)</span>
|
||||
<span class="text-sm font-mono px-2 py-1 bg-gray-100 dark:bg-gray-700 text-gray-800 dark:text-gray-200 rounded">
|
||||
{{ implode(', ', $this->settings['dmarc_reporting'] ?? ['all']) }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="flex justify-between items-center">
|
||||
<span class="text-sm font-medium text-gray-500 dark:text-gray-400">RUA Local Part</span>
|
||||
<span class="text-sm font-mono px-2 py-1 bg-gray-100 dark:bg-gray-700 text-gray-800 dark:text-gray-200 rounded">
|
||||
{{ $this->settings['dmarc_rua_localpart'] ?? 'dmarc' }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="flex justify-between items-center">
|
||||
<span class="text-sm font-medium text-gray-500 dark:text-gray-400">RUF Local Part</span>
|
||||
<span class="text-sm font-mono px-2 py-1 bg-gray-100 dark:bg-gray-700 text-gray-800 dark:text-gray-200 rounded">
|
||||
{{ $this->settings['dmarc_ruf_localpart'] ?? 'dmarc' }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- Advanced Settings --}}
|
||||
<div class="bg-white dark:bg-gray-800 rounded-lg shadow-sm overflow-hidden">
|
||||
<div class="px-6 py-4 border-b border-gray-200 dark:border-gray-700">
|
||||
<h3 class="text-lg font-medium text-gray-900 dark:text-white">
|
||||
Advanced Settings
|
||||
</h3>
|
||||
</div>
|
||||
<div class="p-6">
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<div class="flex justify-between items-center">
|
||||
<span class="text-sm font-medium text-gray-500 dark:text-gray-400">Non-existent Subdomain Policy (NP)</span>
|
||||
<span class="text-sm font-mono px-2 py-1 bg-gray-100 dark:bg-gray-700 text-gray-800 dark:text-gray-200 rounded">
|
||||
{{ $this->settings['dmarc_np'] ?? 'Not set' }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="flex justify-between items-center">
|
||||
<span class="text-sm font-medium text-gray-500 dark:text-gray-400">Public Suffix Domain Policy (PSD)</span>
|
||||
<span class="text-sm font-mono px-2 py-1 bg-gray-100 dark:bg-gray-700 text-gray-800 dark:text-gray-200 rounded">
|
||||
{{ $this->settings['dmarc_psd'] ?? 'Not set' }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- DMARC Record Preview --}}
|
||||
<div class="bg-white dark:bg-gray-800 rounded-lg shadow-sm overflow-hidden border-2 border-blue-500 dark:border-blue-600">
|
||||
<div class="px-6 py-4 border-b border-gray-200 dark:border-gray-700 bg-blue-50 dark:bg-blue-900/20">
|
||||
<h3 class="text-lg font-medium text-blue-900 dark:text-blue-200 flex items-center">
|
||||
<svg class="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"/>
|
||||
</svg>
|
||||
DMARC DNS Record Preview
|
||||
</h3>
|
||||
</div>
|
||||
<div class="p-6">
|
||||
<div class="bg-gray-50 dark:bg-gray-900 rounded-lg p-4 border border-gray-200 dark:border-gray-700">
|
||||
<div class="space-y-2">
|
||||
<div class="flex flex-col sm:flex-row sm:justify-between sm:items-center gap-2">
|
||||
<span class="text-sm font-medium text-gray-500 dark:text-gray-400">Record Type:</span>
|
||||
<span class="text-sm font-mono px-3 py-1 bg-blue-100 dark:bg-blue-900 text-blue-800 dark:text-blue-200 rounded">TXT</span>
|
||||
</div>
|
||||
<div class="flex flex-col sm:flex-row sm:justify-between sm:items-start gap-2">
|
||||
<span class="text-sm font-medium text-gray-500 dark:text-gray-400">Name:</span>
|
||||
<span class="text-sm font-mono px-3 py-1 bg-gray-100 dark:bg-gray-700 text-gray-800 dark:text-gray-200 rounded break-all">_dmarc.yourdomain.com</span>
|
||||
</div>
|
||||
<div class="flex flex-col gap-2">
|
||||
<span class="text-sm font-medium text-gray-500 dark:text-gray-400">Value:</span>
|
||||
<div class="text-sm font-mono bg-gray-100 dark:bg-gray-700 p-4 rounded-lg border border-gray-200 dark:border-gray-600 overflow-x-auto">
|
||||
<code class="text-gray-800 dark:text-gray-200 break-all">
|
||||
{{ $this->getDmarcRecord($this->settings, 'yourdomain.com') }}
|
||||
</code>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- Explanation --}}
|
||||
<div class="mt-6 bg-blue-50 dark:bg-blue-900/20 border border-blue-200 dark:border-blue-800 rounded-lg p-4">
|
||||
<h4 class="font-medium text-blue-900 dark:text-blue-200 mb-2">Record Components:</h4>
|
||||
<ul class="text-sm text-blue-800 dark:text-blue-300 space-y-1 list-disc list-inside">
|
||||
<li><strong>v=DMARC1</strong> - Version identifier</li>
|
||||
<li><strong>p=</strong> - Policy ({{ $this->settings['dmarc_policy'] ?? 'none' }})</li>
|
||||
@if(!empty($this->settings['dmarc_rua'] ?? []))
|
||||
<li><strong>rua=</strong> - Aggregate report destinations</li>
|
||||
@endif
|
||||
@if(!empty($this->settings['dmarc_ruf'] ?? []))
|
||||
<li><strong>ruf=</strong> - Forensic report destinations</li>
|
||||
@endif
|
||||
@if(isset($this->settings['dmarc_percentage']) && $this->settings['dmarc_percentage'] != 100)
|
||||
<li><strong>pct=</strong> - Percentage of messages to apply policy</li>
|
||||
@endif
|
||||
<li><strong>adkim=</strong> - DKIM alignment ({{ $this->settings['dmarc_adkim'] ?? 'relaxed' }})</li>
|
||||
<li><strong>aspf=</strong> - SPF alignment ({{ $this->settings['dmarc_aspf'] ?? 'relaxed' }})</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- Action Buttons --}}
|
||||
<div class="flex flex-wrap gap-3 justify-end">
|
||||
<a href="{{ route('filament.admin.resources.settings.index') }}"
|
||||
class="inline-flex items-center px-4 py-2 bg-gray-200 dark:bg-gray-700 hover:bg-gray-300 dark:hover:bg-gray-600 text-gray-800 dark:text-gray-200 text-sm font-medium rounded-lg transition-colors">
|
||||
Manage Settings
|
||||
</a>
|
||||
<button type="button"
|
||||
onclick="navigator.clipboard.writeText('{{ $this->getDmarcRecord($this->settings, 'yourdomain.com') }}')"
|
||||
class="inline-flex items-center px-4 py-2 bg-blue-600 hover:bg-blue-700 text-white text-sm font-medium rounded-lg transition-colors">
|
||||
<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 5H6a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2v-1M8 5a2 2 0 002 2h2a2 2 0 002-2M8 5a2 2 0 012-2h2a2 2 0 012 2m0 0h2a2 2 0 012 2v3m2 4H10m0 0l3-3m-3 3l3 3"/>
|
||||
</svg>
|
||||
Copy DNS Record
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</x-filament-panels::page>
|
||||
@@ -0,0 +1,177 @@
|
||||
<?php
|
||||
namespace VEximweb\Plugin\DnsTools\Filament\Resources\Dmarc\Modals;
|
||||
|
||||
use Filament\Forms;
|
||||
use VEximweb\Core\Data\Repositories\Interfaces\SettingRepositoryInterface;
|
||||
use Filament\Forms\Components\CheckboxList;
|
||||
use Filament\Schemas\Components\Section;
|
||||
use Filament\Forms\Components\Select;
|
||||
use Filament\Forms\Components\Textarea;
|
||||
use Filament\Forms\Components\TextInput;
|
||||
use Filament\Forms\Components\Hidden;
|
||||
|
||||
class GenerateDmarcForm
|
||||
{
|
||||
protected static ?string $domain = null;
|
||||
|
||||
/**
|
||||
* Set the domain for the form.
|
||||
*/
|
||||
public static function setDomain(string $domain): void
|
||||
{
|
||||
static::$domain = $domain;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current domain.
|
||||
*/
|
||||
protected static function getDomain(): string
|
||||
{
|
||||
return static::$domain ?? 'example.com';
|
||||
}
|
||||
|
||||
/**
|
||||
* The Filament form schema.
|
||||
*/
|
||||
public static function schema(string $domain): array
|
||||
{
|
||||
return [
|
||||
Section::make('Policy')
|
||||
->columns(2)
|
||||
->schema([
|
||||
Select::make('dmarc_policy')
|
||||
->label('Policy')
|
||||
->options([
|
||||
'none' => 'None',
|
||||
'quarantine' => 'Quarantine',
|
||||
'reject' => 'Reject',
|
||||
])
|
||||
->required(),
|
||||
|
||||
Select::make('dmarc_subdomain_policy')
|
||||
->label('Subdomain Policy')
|
||||
->options([
|
||||
'' => 'Inherit Policy',
|
||||
'none' => 'None',
|
||||
'quarantine' => 'Quarantine',
|
||||
'reject' => 'Reject',
|
||||
]),
|
||||
|
||||
Hidden::make('dmarc_np')
|
||||
->default(''),
|
||||
|
||||
Hidden::make('dmarc_psd')
|
||||
->default('n'),
|
||||
|
||||
Hidden::make('dmarc_t')
|
||||
->default('n'),
|
||||
|
||||
Hidden::make('dmarc_percentage')
|
||||
->default(100),
|
||||
|
||||
Hidden::make('dmarc_report_interval')
|
||||
->default(86400),
|
||||
]),
|
||||
|
||||
Section::make('Alignment')
|
||||
->columns(2)
|
||||
->schema([
|
||||
Select::make('dmarc_adkim')
|
||||
->label('DKIM Alignment')
|
||||
->options([
|
||||
'relaxed' => 'Relaxed',
|
||||
'strict' => 'Strict',
|
||||
]),
|
||||
|
||||
Select::make('dmarc_aspf')
|
||||
->label('SPF Alignment')
|
||||
->options([
|
||||
'relaxed' => 'Relaxed',
|
||||
'strict' => 'Strict',
|
||||
]),
|
||||
]),
|
||||
|
||||
Section::make('Reporting')
|
||||
->schema([
|
||||
CheckboxList::make('dmarc_reporting')
|
||||
->label('Failure Reporting Options')
|
||||
->columns(5)
|
||||
->options([
|
||||
'0' => 'No reports',
|
||||
'1' => 'Summary only',
|
||||
'd' => 'DKIM failures',
|
||||
's' => 'SPF failures',
|
||||
'all' => 'All failures',
|
||||
]),
|
||||
|
||||
TextInput::make('dmarc_rua_localpart')
|
||||
->label('Aggregate Reports Email')
|
||||
->helperText('Email address for aggregate reports (auto-appended with @' . $domain . ')')
|
||||
->default('dmarc-reports')
|
||||
->prefix('📊')
|
||||
->suffix('@' . $domain),
|
||||
|
||||
TextInput::make('dmarc_ruf_localpart')
|
||||
->label('Forensic Reports Email')
|
||||
->helperText('Email address for forensic reports (auto-appended with @' . $domain . ')')
|
||||
->default('dmarc-forensic')
|
||||
->prefix('🔍')
|
||||
->suffix('@' . $domain),
|
||||
]),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Populate the form from the repository.
|
||||
*/
|
||||
public static function values(SettingRepositoryInterface $settings): array
|
||||
{
|
||||
// Get settings for this domain (you might need to adjust this)
|
||||
$rua_localpart = $settings->get('dmarc_rua_localpart', 'dmarc-reports');
|
||||
$ruf_localpart = $settings->get('dmarc_ruf_localpart', 'dmarc-forensic');
|
||||
|
||||
return [
|
||||
'dmarc_policy' => $settings->get('dmarc_policy'),
|
||||
'dmarc_subdomain_policy' => $settings->get('dmarc_subdomain_policy'),
|
||||
'dmarc_np' => $settings->get('dmarc_np', ''),
|
||||
'dmarc_psd' => $settings->get('dmarc_psd', 'n'),
|
||||
'dmarc_t' => $settings->get('dmarc_t', 'n'),
|
||||
'dmarc_percentage' => $settings->get('dmarc_percentage', 100),
|
||||
'dmarc_report_interval' => $settings->get('dmarc_report_interval', 86400),
|
||||
'dmarc_adkim' => $settings->get('dmarc_adkim'),
|
||||
'dmarc_aspf' => $settings->get('dmarc_aspf'),
|
||||
'dmarc_reporting' => $settings->get('dmarc_reporting', []),
|
||||
'dmarc_rua_localpart' => $rua_localpart,
|
||||
'dmarc_ruf_localpart' => $ruf_localpart,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Save the form back to the repository.
|
||||
*/
|
||||
public static function save(
|
||||
SettingRepositoryInterface $settings,
|
||||
array $data,
|
||||
string $domain,
|
||||
): void {
|
||||
foreach ([
|
||||
'dmarc_policy',
|
||||
'dmarc_subdomain_policy',
|
||||
'dmarc_np',
|
||||
'dmarc_psd',
|
||||
'dmarc_t',
|
||||
'dmarc_percentage',
|
||||
'dmarc_report_interval',
|
||||
'dmarc_adkim',
|
||||
'dmarc_aspf',
|
||||
'dmarc_rua_localpart',
|
||||
'dmarc_ruf_localpart',
|
||||
] as $key) {
|
||||
$settings->set($key, $data[$key]);
|
||||
}
|
||||
|
||||
$settings->set('dmarc_rua', [$data['dmarc_rua_localpart'] . '@' . $domain]);
|
||||
$settings->set('dmarc_ruf', [$data['dmarc_ruf_localpart'] . '@' . $domain]);
|
||||
$settings->set('dmarc_reporting', $data['dmarc_reporting'] ?? []);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,207 @@
|
||||
<?php
|
||||
|
||||
namespace VEximweb\Plugin\DnsTools\Filament\Resources\Dmarc\Pages;
|
||||
|
||||
use App\Settings\DmarcSettings;
|
||||
use Filament\Pages\Page;
|
||||
use Illuminate\Support\Collection;
|
||||
use VEximweb\Core\Data\Repositories\SettingRepository;
|
||||
|
||||
class DmarcRecords extends Page
|
||||
{
|
||||
protected static ?string $navigationIcon = 'heroicon-o-document-text';
|
||||
|
||||
protected static string $view = 'filament.pages.dmarc-records';
|
||||
|
||||
protected static ?string $navigationGroup = 'Email';
|
||||
|
||||
protected static ?int $navigationSort = 10;
|
||||
|
||||
protected static ?string $title = 'DMARC Records';
|
||||
|
||||
protected SettingRepository $settingRepository;
|
||||
|
||||
public array $settings = [];
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->settingRepository = app(SettingRepository::class);
|
||||
$this->settings = $this->getDmarcSettings();
|
||||
}
|
||||
|
||||
public function getDmarcSettings(): array
|
||||
{
|
||||
$keys = [
|
||||
'dmarc_policy',
|
||||
'dmarc_subdomain_policy',
|
||||
'dmarc_adkim',
|
||||
'dmarc_aspf',
|
||||
'dmarc_percentage',
|
||||
'dmarc_report_interval',
|
||||
'dmarc_t',
|
||||
'dmarc_rua',
|
||||
'dmarc_ruf',
|
||||
'dmarc_reporting',
|
||||
'dmarc_np',
|
||||
'dmarc_psd',
|
||||
'dmarc_rua_localpart',
|
||||
'dmarc_ruf_localpart',
|
||||
];
|
||||
|
||||
$settings = [];
|
||||
foreach ($keys as $key) {
|
||||
$value = $this->settingRepository->get($key);
|
||||
$settings[$key] = $value;
|
||||
}
|
||||
|
||||
return $settings;
|
||||
}
|
||||
|
||||
public function getDefaultSettings(): array
|
||||
{
|
||||
return [
|
||||
'dmarc_policy' => 'none',
|
||||
'dmarc_subdomain_policy' => 'none',
|
||||
'dmarc_adkim' => 'relaxed',
|
||||
'dmarc_aspf' => 'relaxed',
|
||||
'dmarc_percentage' => 100,
|
||||
'dmarc_report_interval' => 86400,
|
||||
'dmarc_t' => 'n',
|
||||
'dmarc_rua' => [],
|
||||
'dmarc_ruf' => [],
|
||||
'dmarc_reporting' => ['all'],
|
||||
'dmarc_np' => '',
|
||||
'dmarc_psd' => '',
|
||||
'dmarc_rua_localpart' => 'dmarc',
|
||||
'dmarc_ruf_localpart' => 'dmarc',
|
||||
];
|
||||
}
|
||||
|
||||
public function getDmarcRecord(array $settings, string $domain): string
|
||||
{
|
||||
$parts = [];
|
||||
|
||||
// Required: v (version)
|
||||
$parts[] = 'v=DMARC1';
|
||||
|
||||
// Required: p (policy)
|
||||
$policy = $settings['dmarc_policy'] ?? 'none';
|
||||
$parts[] = "p=$policy";
|
||||
|
||||
// pct (percentage)
|
||||
$percentage = $settings['dmarc_percentage'] ?? 100;
|
||||
if ($percentage !== 100) {
|
||||
$parts[] = "pct=$percentage";
|
||||
}
|
||||
|
||||
// adkim (DKIM alignment)
|
||||
$adkim = $settings['dmarc_adkim'] ?? 'relaxed';
|
||||
if ($adkim !== 'relaxed') {
|
||||
$parts[] = "adkim=$adkim";
|
||||
}
|
||||
|
||||
// aspf (SPF alignment)
|
||||
$aspf = $settings['dmarc_aspf'] ?? 'relaxed';
|
||||
if ($aspf !== 'relaxed') {
|
||||
$parts[] = "aspf=$aspf";
|
||||
}
|
||||
|
||||
// rua (aggregate reports)
|
||||
$rua = $settings['dmarc_rua'] ?? [];
|
||||
if (!empty($rua)) {
|
||||
$ruaStr = implode(',', array_map(function ($email) {
|
||||
return "mailto:$email";
|
||||
}, $rua));
|
||||
$parts[] = "rua=$ruaStr";
|
||||
}
|
||||
|
||||
// ruf (forensic reports)
|
||||
$ruf = $settings['dmarc_ruf'] ?? [];
|
||||
if (!empty($ruf)) {
|
||||
$rufStr = implode(',', array_map(function ($email) {
|
||||
return "mailto:$email";
|
||||
}, $ruf));
|
||||
$parts[] = "ruf=$rufStr";
|
||||
}
|
||||
|
||||
// fo (failure reporting options)
|
||||
$reporting = $settings['dmarc_reporting'] ?? ['all'];
|
||||
if (!empty($reporting)) {
|
||||
$fo = implode('', array_map(function ($opt) {
|
||||
return $this->getFoValue($opt);
|
||||
}, $reporting));
|
||||
if (!empty($fo)) {
|
||||
$parts[] = "fo=$fo";
|
||||
}
|
||||
}
|
||||
|
||||
// np (non-existent subdomain policy)
|
||||
$np = $settings['dmarc_np'] ?? '';
|
||||
if (!empty($np)) {
|
||||
$parts[] = "np=$np";
|
||||
}
|
||||
|
||||
// sp (subdomain policy)
|
||||
$sp = $settings['dmarc_subdomain_policy'] ?? '';
|
||||
if (!empty($sp)) {
|
||||
$parts[] = "sp=$sp";
|
||||
}
|
||||
|
||||
// psd (public suffix domain policy)
|
||||
$psd = $settings['dmarc_psd'] ?? '';
|
||||
if (!empty($psd)) {
|
||||
$parts[] = "psd=$psd";
|
||||
}
|
||||
|
||||
// t (testing mode)
|
||||
$testing = $settings['dmarc_t'] ?? 'n';
|
||||
if ($testing === 'y') {
|
||||
$parts[] = 't=y';
|
||||
}
|
||||
|
||||
// ri (report interval)
|
||||
$ri = $settings['dmarc_report_interval'] ?? 86400;
|
||||
if ($ri !== 86400) {
|
||||
$parts[] = "ri=$ri";
|
||||
}
|
||||
|
||||
return implode('; ', $parts);
|
||||
}
|
||||
|
||||
private function getFoValue(string $option): string
|
||||
{
|
||||
return match ($option) {
|
||||
'all' => '1',
|
||||
'any' => '1',
|
||||
'dkim' => 'd',
|
||||
'spf' => 's',
|
||||
'both' => '1',
|
||||
default => '1'
|
||||
};
|
||||
}
|
||||
|
||||
public function getRuaDestinations(): array
|
||||
{
|
||||
$rua = $this->settings['dmarc_rua'] ?? [];
|
||||
$localpart = $this->settings['dmarc_rua_localpart'] ?? 'dmarc';
|
||||
|
||||
return array_map(function ($email) use ($localpart) {
|
||||
return "mailto:$email";
|
||||
}, $rua);
|
||||
}
|
||||
|
||||
public function getRufDestinations(): array
|
||||
{
|
||||
$ruf = $this->settings['dmarc_ruf'] ?? [];
|
||||
$localpart = $this->settings['dmarc_ruf_localpart'] ?? 'dmarc';
|
||||
|
||||
return array_map(function ($email) use ($localpart) {
|
||||
return "mailto:$email";
|
||||
}, $ruf);
|
||||
}
|
||||
|
||||
public static function getNavigationBadge(): ?string
|
||||
{
|
||||
return 'DMARC';
|
||||
}
|
||||
}
|
||||
@@ -20,6 +20,8 @@ use Filament\Support\Icons\Heroicon;
|
||||
use VEximweb\Plugin\DnsTools\Models\DmarcCheck;
|
||||
use Filament\Notifications\Notification;
|
||||
use Filament\Actions\ActionGroup;
|
||||
use VEximweb\Plugin\DnsTools\Filament\Resources\Dmarc\Modals\GenerateDmarcForm;
|
||||
use VEximweb\Core\Data\Repositories\Interfaces\SettingRepositoryInterface;
|
||||
|
||||
class DomainsTable
|
||||
{
|
||||
@@ -209,6 +211,27 @@ class DomainsTable
|
||||
EditAction::make(),
|
||||
EditAction::make(),
|
||||
EditAction::make(),
|
||||
Action::make('dmarc')
|
||||
->fillForm(fn (SettingRepositoryInterface $settings) =>
|
||||
GenerateDmarcForm::values($settings)
|
||||
)
|
||||
->form(fn ($record) =>
|
||||
GenerateDmarcForm::schema($record->domain)
|
||||
)
|
||||
->action(fn (array $data, SettingRepositoryInterface $settings, $record) =>
|
||||
GenerateDmarcForm::save($settings, $data, $record->domain)
|
||||
),
|
||||
|
||||
/*
|
||||
Action::make('dmarc')
|
||||
->fillForm(fn (SettingRepositoryInterface $settings) =>
|
||||
GenerateDmarcForm::values($settings)
|
||||
)
|
||||
->form(GenerateDmarcForm::schema())
|
||||
->action(fn (array $data, SettingRepositoryInterface $settings) =>
|
||||
GenerateDmarcForm::save($settings, $data)
|
||||
),
|
||||
*/
|
||||
Action::make('viewDmarc')
|
||||
->label('DMARC Details')
|
||||
->modalHeading('DMARC Record Details')
|
||||
|
||||
Reference in New Issue
Block a user