Updated to Laravel 10

This commit is contained in:
Will Browning
2023-04-26 16:53:39 +01:00
parent 2822e87f36
commit 9125741f4f
94 changed files with 1323 additions and 1572 deletions

View File

@@ -40,20 +40,20 @@ class CheckDomainsMxValidation extends Command
public function handle()
{
Domain::all()
->each(function ($domain) {
try {
if (! $domain->checkMxRecords()) {
// Notify user via email only if domain's MX previously were valid
if (! is_null($domain->domain_mx_validated_at)) {
$domain->user->notify(new DomainMxRecordsInvalid($domain->domain));
}
->each(function ($domain) {
try {
if (! $domain->checkMxRecords()) {
// Notify user via email only if domain's MX previously were valid
if (! is_null($domain->domain_mx_validated_at)) {
$domain->user->notify(new DomainMxRecordsInvalid($domain->domain));
}
$domain->domain_mx_validated_at = null;
$domain->save();
$domain->domain_mx_validated_at = null;
$domain->save();
}
} catch (\Exception $e) {
//
}
} catch (\Exception $e) {
//
}
});
});
}
}

View File

@@ -99,10 +99,10 @@ class ReceiveEmail extends Command
} else {
// Does not exist, must be a standard, username or custom domain alias
$parentDomain = collect(config('anonaddy.all_domains'))
->filter(function ($name) use ($recipient) {
return Str::endsWith($recipient['domain'], $name);
})
->first();
->filter(function ($name) use ($recipient) {
return Str::endsWith($recipient['domain'], $name);
})
->first();
if (! empty($parentDomain)) {
// It is standard or username alias

View File

@@ -29,6 +29,7 @@ class Kernel extends ConsoleKernel
$schedule->command('anonaddy:clear-failed-deliveries')->daily();
$schedule->command('anonaddy:clear-postfix-queue-ids')->hourly();
$schedule->command('auth:clear-resets')->daily();
$schedule->command('cache:prune-stale-tags')->hourly();
}
/**

View File

@@ -7,30 +7,20 @@ use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
class Handler extends ExceptionHandler
{
/**
* A list of the exception types that are not reported.
* The list of the inputs that are never flashed to the session on validation exceptions.
*
* @var array
*/
protected $dontReport = [
//
];
/**
* A list of the inputs that are never flashed for validation exceptions.
*
* @var array
* @var array<int, string>
*/
protected $dontFlash = [
'current_password',
'password',
'password_confirmation',
];
/**
* Register the exception handling callbacks for the application.
*
* @return void
*/
public function register()
public function register(): void
{
//
}

View File

@@ -2,9 +2,10 @@
namespace App\Http\Controllers;
use App\Http\Requests\ImportAliasesRequest;
use App\Imports\AliasesImport;
use Illuminate\Http\Request;
use Illuminate\Validation\Rules\File;
use Illuminate\Support\Facades\App;
use Maatwebsite\Excel\HeadingRowImport;
class AliasImportController extends Controller
{
@@ -18,18 +19,18 @@ class AliasImportController extends Controller
$this->middleware('throttle:1,1'); // Limit to 1 upload per minute
}
public function import(Request $request)
public function import(ImportAliasesRequest $request)
{
// Validate
$request->validate([
'aliases_import' => [
'required',
File::types(['csv'])->max(5 * 1024) // 5MB
],
]);
try {
$import = new AliasesImport(user());
$headings = (new HeadingRowImport)->toCollection($request->file('aliases_import'))->flatten();
// Validate the heading row
if (($headings->diff(['alias', 'description', 'recipients'])->count() || $headings->count() !== 3) && ! App::environment('testing')) {
return back()->withErrors(['aliases_import' => 'The aliases import file has invalid headers, please use the template provided above.']);
}
$import->queue($request->file('aliases_import'));
} catch (\Exception $e) {
report($e);

View File

@@ -114,10 +114,10 @@ class AliasController extends Controller
// Check if domain is for username or custom domain
$parentDomain = collect(config('anonaddy.all_domains'))
->filter(function ($name) use ($request) {
return Str::endsWith($request->domain, $name);
})
->first();
->filter(function ($name) use ($request) {
return Str::endsWith($request->domain, $name);
})
->first();
$aliasable = null;

View File

@@ -78,7 +78,7 @@ class ForgotPasswordController extends Controller
protected function sendResetLinkFailedResponse(Request $request, $response)
{
return back()
->withInput($request->only('username'))
->withErrors(['username' => trans($response)]);
->withInput($request->only('username'))
->withErrors(['username' => trans($response)]);
}
}

View File

@@ -11,7 +11,7 @@ class PersonalAccessTokenController extends Controller
{
public function index()
{
return PersonalAccessTokenResource::collection(user()->tokens()->select(['id', 'tokenable_id', 'name', 'created_at', 'last_used_at', 'expires_at'])->get());
return PersonalAccessTokenResource::collection(user()->tokens()->select(['id', 'tokenable_id', 'name', 'created_at', 'last_used_at', 'expires_at', 'updated_at', 'created_at'])->get());
}
public function store(StorePersonalAccessTokenRequest $request)

View File

@@ -76,9 +76,9 @@ class RegisterController extends Controller
], [
'captcha.captcha' => 'The text entered was incorrect, please try again.',
])
->sometimes('captcha', 'required|captcha', function () {
return ! App::environment('testing');
});
->sometimes('captcha', 'required|captcha', function () {
return ! App::environment('testing');
});
}
/**

View File

@@ -98,7 +98,7 @@ class ResetPasswordController extends Controller
protected function sendResetFailedResponse(Request $request, $response)
{
return back()
->withInput($request->only('username'))
->withErrors(['username' => trans($response)]);
->withInput($request->only('username'))
->withErrors(['username' => trans($response)]);
}
}

View File

@@ -3,13 +3,11 @@
namespace App\Http\Controllers;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
use Illuminate\Foundation\Bus\DispatchesJobs;
use Illuminate\Foundation\Validation\ValidatesRequests;
use Illuminate\Routing\Controller as BaseController;
class Controller extends BaseController
{
use AuthorizesRequests;
use DispatchesJobs;
use ValidatesRequests;
}

View File

@@ -38,6 +38,6 @@ class SettingController extends Controller
$request->session()->invalidate();
return redirect()->route('login')
->with(['status' => 'Account deleted successfully!']);
->with(['status' => 'Account deleted successfully!']);
}
}

View File

@@ -41,19 +41,19 @@ class Kernel extends HttpKernel
'api' => [
\Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class,
'throttle:api',
\Illuminate\Routing\Middleware\ThrottleRequests::class.':api',
\Illuminate\Routing\Middleware\SubstituteBindings::class,
],
];
/**
* The application's route middleware.
* The application's middleware aliases.
*
* These middleware may be assigned to groups or used individually.
* Aliases may be used instead of class names to conveniently assign middleware to routes and groups.
*
* @var array
*/
protected $routeMiddleware = [
protected $middlewareAliases = [
'auth' => \App\Http\Middleware\Authenticate::class,
'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
'auth.session' => \Illuminate\Session\Middleware\AuthenticateSession::class,

View File

@@ -3,19 +3,15 @@
namespace App\Http\Middleware;
use Illuminate\Auth\Middleware\Authenticate as Middleware;
use Illuminate\Http\Request;
class Authenticate extends Middleware
{
/**
* Get the path the user should be redirected to when they are not authenticated.
*
* @param \Illuminate\Http\Request $request
* @return string
*/
protected function redirectTo($request)
protected function redirectTo(Request $request): ?string
{
if (! $request->expectsJson()) {
return route('login');
}
return $request->expectsJson() ? null : route('login');
}
}

View File

@@ -4,18 +4,18 @@ namespace App\Http\Middleware;
use App\Providers\RouteServiceProvider;
use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Symfony\Component\HttpFoundation\Response;
class RedirectIfAuthenticated
{
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param string|null ...$guards
* @return mixed
* @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next
*/
public function handle($request, Closure $next, ...$guards)
public function handle(Request $request, Closure $next, string ...$guards): Response
{
$guards = empty($guards) ? [null] : $guards;

View File

@@ -0,0 +1,34 @@
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\Rules\File;
class ImportAliasesRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array<string, mixed>
*/
public function rules()
{
return [
'aliases_import' => [
'required',
File::types('csv')->min(0.1)->max(1024), // 1MB
],
];
}
}

View File

@@ -50,7 +50,7 @@ class StoreAliasRequest extends FormRequest
'max:50',
Rule::unique('aliases')->where(function ($query) {
return $query->where('local_part', $this->validationData()['local_part'])
->where('domain', $this->validationData()['domain']);
->where('domain', $this->validationData()['domain']);
}),
new ValidAliasLocalPart(),
], function () {

View File

@@ -6,7 +6,7 @@ use App\Models\Alias;
use App\Models\User;
use App\Notifications\AliasesImportedNotification;
use App\Rules\ValidAliasLocalPart;
use App\Rules\VerifiedRecipientId;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\Middleware\WithoutOverlapping;
use Illuminate\Support\Facades\Log;
@@ -32,23 +32,26 @@ use Ramsey\Uuid\Uuid;
class AliasesImport implements ToModel, WithHeadingRow, WithValidation, WithChunkReading, ShouldQueue, SkipsOnFailure, SkipsEmptyRows, SkipsOnError, WithLimit, WithColumnLimit, WithEvents
{
use Importable, SkipsFailures, SkipsErrors, RemembersRowNumber;
use Queueable, Importable, SkipsFailures, SkipsErrors, RemembersRowNumber;
protected $user;
protected $domains;
protected $verifiedRecipientIds;
protected $verfiedRecipientEmailAndIds;
public function __construct(User $user)
{
$this->user = $user;
$this->domains = $user->domains()->select(['id', 'domain'])->get();
$this->verifiedRecipientIds = $user
$this->verfiedRecipientEmailAndIds = $user
->verifiedRecipients()
->pluck('id')
->toArray();
->select(['email', 'id'])
->get()
->mapWithKeys(function ($recipient) {
return [$recipient->email => $recipient->id];
});
}
/**
@@ -89,12 +92,13 @@ class AliasesImport implements ToModel, WithHeadingRow, WithValidation, WithChun
public function prepareForValidation($data)
{
// Ensure the alias is all lowercase
$data['alias'] = strtolower($data['alias']);
// Ensure the alias is all lowercase and whitespace has been trimmed
$data['alias'] = trim(strtolower($data['alias']));
// Add for validation
$data['domain'] = Str::afterLast($data['alias'], '@');
$data['local_part'] = Str::beforeLast($data['alias'], '@');
$data['extension'] = null;
$data['recipient_ids'] = null;
if (! is_null($data['description'])) {
// Make sure it is a string
@@ -108,12 +112,29 @@ class AliasesImport implements ToModel, WithHeadingRow, WithValidation, WithChun
$data['email'] = $data['local_part'].'@'.$data['domain'];
}
// Convert recipient IDs to an array
if ($data['recipient_ids']) {
$data['recipient_ids'] = explode(',', $data['recipient_ids']);
// Map emails to an array of corresponding recipient IDs
if ($data['recipients']) {
$recipients = explode(',', $data['recipients']);
foreach ($recipients as $recipient) {
if (isset($this->verfiedRecipientEmailAndIds[$recipient])) {
$data['recipient_ids'][] = $this->verfiedRecipientEmailAndIds[$recipient];
}
}
}
return $data;
// Return only required array keys
return collect($data)
->only([
'alias',
'email',
'local_part',
'extension',
'domain',
'description',
'recipient_ids',
])
->all();
}
public function rules(): array
@@ -156,7 +177,9 @@ class AliasesImport implements ToModel, WithHeadingRow, WithValidation, WithChun
'nullable',
'array',
'max:10',
new VerifiedRecipientId($this->verifiedRecipientIds), // Pass through so it doesn't get called for every row!
],
'recipient_ids.*' => [
'uuid',
],
];
}
@@ -195,7 +218,7 @@ class AliasesImport implements ToModel, WithHeadingRow, WithValidation, WithChun
$totalImported = $totalRows - $totalNotImported;
// Notify user with email.
$import->user->notify(new AliasesImportedNotification($totalRows, $totalImported, $totalNotImported, $totalFailures, $totalErrors));
$import->getUser()->notify(new AliasesImportedNotification($totalRows, $totalImported, $totalNotImported, $totalFailures, $totalErrors));
}
public static function importFailed(ImportFailed $event)
@@ -224,6 +247,11 @@ class AliasesImport implements ToModel, WithHeadingRow, WithValidation, WithChun
return [(new WithoutOverlapping($this->user->id))->releaseAfter(180)->expireAfter(600)]; // release after 3 minutes and expire after 10
}
public function getUser()
{
return $this->user;
}
// For testing
public function getDomains()
{
@@ -233,6 +261,8 @@ class AliasesImport implements ToModel, WithHeadingRow, WithValidation, WithChun
// For testing
public function getRecipientIds()
{
return $this->verifiedRecipientIds;
return $this->verfiedRecipientEmailAndIds
->values()
->all();
}
}

View File

@@ -160,73 +160,73 @@ class ForwardEmail extends Mailable implements ShouldQueue, ShouldBeEncrypted
$message->returnPath($returnPath);
$message->getHeaders()
->addTextHeader('Feedback-ID', 'F:'.$this->alias->id.':anonaddy');
->addTextHeader('Feedback-ID', 'F:'.$this->alias->id.':anonaddy');
// This header is used to set the To: header as the alias just before sending.
$message->getHeaders()
->addTextHeader('Alias-To', $this->alias->email);
->addTextHeader('Alias-To', $this->alias->email);
$message->getHeaders()->remove('Message-ID');
if ($this->messageId) {
$message->getHeaders()
->addIdHeader('Message-ID', base64_decode($this->messageId));
->addIdHeader('Message-ID', base64_decode($this->messageId));
} else {
$message->getHeaders()
->addIdHeader('Message-ID', bin2hex(random_bytes(16)).'@'.$this->alias->domain);
->addIdHeader('Message-ID', bin2hex(random_bytes(16)).'@'.$this->alias->domain);
}
if ($this->listUnsubscribe) {
$message->getHeaders()
->addTextHeader('List-Unsubscribe', base64_decode($this->listUnsubscribe));
->addTextHeader('List-Unsubscribe', base64_decode($this->listUnsubscribe));
}
if ($this->inReplyTo) {
$message->getHeaders()
->addTextHeader('In-Reply-To', base64_decode($this->inReplyTo));
->addTextHeader('In-Reply-To', base64_decode($this->inReplyTo));
}
if ($this->references) {
$message->getHeaders()
->addTextHeader('References', base64_decode($this->references));
->addTextHeader('References', base64_decode($this->references));
}
if ($this->receivedHeaders) {
if (is_array($this->receivedHeaders)) {
foreach ($this->receivedHeaders as $receivedHeader) {
$message->getHeaders()
->addTextHeader('Received', $receivedHeader);
->addTextHeader('Received', $receivedHeader);
}
} else {
$message->getHeaders()
->addTextHeader('Received', $this->receivedHeaders);
->addTextHeader('Received', $this->receivedHeaders);
}
}
if ($this->authenticationResults) {
$message->getHeaders()
->addTextHeader('X-AnonAddy-Authentication-Results', $this->authenticationResults);
->addTextHeader('X-AnonAddy-Authentication-Results', $this->authenticationResults);
}
$message->getHeaders()
->addTextHeader('X-AnonAddy-Original-Sender', $this->sender);
->addTextHeader('X-AnonAddy-Original-Sender', $this->sender);
$message->getHeaders()
->addTextHeader('X-AnonAddy-Original-Envelope-From', $this->originalEnvelopeFrom);
->addTextHeader('X-AnonAddy-Original-Envelope-From', $this->originalEnvelopeFrom);
if ($this->originalFromHeader) {
$message->getHeaders()
->addTextHeader('X-AnonAddy-Original-From-Header', base64_decode($this->originalFromHeader));
->addTextHeader('X-AnonAddy-Original-From-Header', base64_decode($this->originalFromHeader));
}
if ($this->originalReplyToHeader) {
$message->getHeaders()
->addTextHeader('X-AnonAddy-Original-Reply-To-Header', base64_decode($this->originalReplyToHeader));
->addTextHeader('X-AnonAddy-Original-Reply-To-Header', base64_decode($this->originalReplyToHeader));
}
if ($this->originalSenderHeader) {
$message->getHeaders()
->addTextHeader('Original-Sender', base64_decode($this->originalSenderHeader));
->addTextHeader('Original-Sender', base64_decode($this->originalSenderHeader));
}
if ($this->emailInlineAttachments) {
@@ -244,12 +244,12 @@ class ForwardEmail extends Mailable implements ShouldQueue, ShouldBeEncrypted
if ($this->originalCc) {
$message->getHeaders()
->addTextHeader('X-AnonAddy-Original-Cc', $this->originalCc);
->addTextHeader('X-AnonAddy-Original-Cc', $this->originalCc);
}
if ($this->originalTo) {
$message->getHeaders()
->addTextHeader('X-AnonAddy-Original-To', $this->originalTo);
->addTextHeader('X-AnonAddy-Original-To', $this->originalTo);
}
});

View File

@@ -98,21 +98,21 @@ class ReplyToEmail extends Mailable implements ShouldQueue, ShouldBeEncrypted
$message->returnPath($returnPath);
$message->getHeaders()
->addTextHeader('Feedback-ID', 'R:'.$this->alias->id.':anonaddy');
->addTextHeader('Feedback-ID', 'R:'.$this->alias->id.':anonaddy');
// Message-ID is replaced on replies as it can leak parts of the real email
$message->getHeaders()->remove('Message-ID');
$message->getHeaders()
->addIdHeader('Message-ID', bin2hex(random_bytes(16)).'@'.$this->alias->domain);
->addIdHeader('Message-ID', bin2hex(random_bytes(16)).'@'.$this->alias->domain);
if ($this->inReplyTo) {
$message->getHeaders()
->addTextHeader('In-Reply-To', base64_decode($this->inReplyTo));
->addTextHeader('In-Reply-To', base64_decode($this->inReplyTo));
}
if ($this->references) {
$message->getHeaders()
->addTextHeader('References', base64_decode($this->references));
->addTextHeader('References', base64_decode($this->references));
}
if ($this->emailInlineAttachments) {

View File

@@ -92,12 +92,12 @@ class SendFromEmail extends Mailable implements ShouldQueue, ShouldBeEncrypted
$message->returnPath($returnPath);
$message->getHeaders()
->addTextHeader('Feedback-ID', 'S:'.$this->alias->id.':anonaddy');
->addTextHeader('Feedback-ID', 'S:'.$this->alias->id.':anonaddy');
// Message-ID is replaced on send from as it can leak parts of the real email
$message->getHeaders()->remove('Message-ID');
$message->getHeaders()
->addIdHeader('Message-ID', bin2hex(random_bytes(16)).'@'.$this->alias->domain);
->addIdHeader('Message-ID', bin2hex(random_bytes(16)).'@'.$this->alias->domain);
if ($this->emailInlineAttachments) {
foreach ($this->emailInlineAttachments as $attachment) {

View File

@@ -46,7 +46,7 @@ class TokenExpiringSoon extends Mailable implements ShouldQueue, ShouldBeEncrypt
])
->withSymfonyMessage(function (Email $message) {
$message->getHeaders()
->addTextHeader('Feedback-ID', 'TES:anonaddy');
->addTextHeader('Feedback-ID', 'TES:anonaddy');
});
}
}

View File

@@ -4,6 +4,7 @@ namespace App\Models;
use App\Traits\HasEncryptedAttributes;
use App\Traits\HasUuid;
use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
@@ -41,18 +42,15 @@ class Alias extends Model
'emails_sent',
];
protected $dates = [
'created_at',
'updated_at',
'deleted_at',
];
protected $casts = [
'id' => 'string',
'user_id' => 'string',
'aliasable_id' => 'string',
'aliasable_type' => 'string',
'active' => 'boolean',
'created_at' => 'datetime',
'updated_at' => 'datetime',
'deleted_at' => 'datetime',
];
public static function boot()
@@ -72,19 +70,25 @@ class Alias extends Model
});
}
public function setLocalPartAttribute($value)
protected function localPart(): Attribute
{
$this->attributes['local_part'] = strtolower($value);
return Attribute::make(
set: fn (string $value) => strtolower($value),
);
}
public function setDomainAttribute($value)
protected function domain(): Attribute
{
$this->attributes['domain'] = strtolower($value);
return Attribute::make(
set: fn (string $value) => strtolower($value),
);
}
public function setEmailAttribute($value)
protected function email(): Attribute
{
$this->attributes['email'] = strtolower($value);
return Attribute::make(
set: fn (string $value) => strtolower($value),
);
}
/**

View File

@@ -6,6 +6,7 @@ use App\Http\Resources\DomainResource;
use App\Traits\HasEncryptedAttributes;
use App\Traits\HasUuid;
use Exception;
use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\App;
@@ -32,20 +33,17 @@ class Domain extends Model
'catch_all',
];
protected $dates = [
'created_at',
'updated_at',
'domain_verified_at',
'domain_mx_validated_at',
'domain_sending_verified_at',
];
protected $casts = [
'id' => 'string',
'user_id' => 'string',
'active' => 'boolean',
'catch_all' => 'boolean',
'default_recipient_id' => 'string',
'created_at' => 'datetime',
'updated_at' => 'datetime',
'domain_verified_at' => 'datetime',
'domain_mx_validated_at' => 'datetime',
'domain_sending_verified_at' => 'datetime',
];
public static function boot()
@@ -60,9 +58,11 @@ class Domain extends Model
/**
* Set the domain's name.
*/
public function setDomainAttribute($value)
protected function domain(): Attribute
{
$this->attributes['domain'] = strtolower($value);
return Attribute::make(
set: fn (string $value) => strtolower($value),
);
}
/**
@@ -197,9 +197,9 @@ class Domain extends Model
try {
return collect(dns_get_record($this->domain.'.', DNS_TXT))
->contains(function ($r) {
return trim($r['txt']) === 'aa-verify='.sha1(config('anonaddy.secret').user()->id.user()->domains->count());
});
->contains(function ($r) {
return trim($r['txt']) === 'aa-verify='.sha1(config('anonaddy.secret').user()->id.user()->domains->count());
});
} catch (Exception $e) {
Log::info('DNS Get TXT Error:', ['domain' => $this->domain, 'user' => $this->user?->username, 'error' => $e->getMessage()]);

View File

@@ -35,17 +35,14 @@ class FailedDelivery extends Model
'attempted_at',
];
protected $dates = [
'attempted_at',
'created_at',
'updated_at',
];
protected $casts = [
'id' => 'string',
'user_id' => 'string',
'recipient_id' => 'string',
'alias_id' => 'string',
'attempted_at' => 'datetime',
'created_at' => 'datetime',
'updated_at' => 'datetime',
];
/**

View File

@@ -37,12 +37,6 @@ class Recipient extends Model
'email_verified_at',
];
protected $dates = [
'created_at',
'updated_at',
'email_verified_at',
];
protected $casts = [
'id' => 'string',
'user_id' => 'string',
@@ -50,6 +44,9 @@ class Recipient extends Model
'should_encrypt' => 'boolean',
'inline_encryption' => 'boolean',
'protected_headers' => 'boolean',
'created_at' => 'datetime',
'updated_at' => 'datetime',
'email_verified_at' => 'datetime',
];
public static function boot()

View File

@@ -27,11 +27,6 @@ class Rule extends Model
'order',
];
protected $dates = [
'created_at',
'updated_at',
];
protected $casts = [
'id' => 'string',
'user_id' => 'string',
@@ -41,6 +36,8 @@ class Rule extends Model
'sends' => 'boolean',
'conditions' => 'array',
'actions' => 'array',
'created_at' => 'datetime',
'updated_at' => 'datetime',
];
/**

View File

@@ -7,6 +7,7 @@ use App\Traits\HasEncryptedAttributes;
use App\Traits\HasUuid;
use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
@@ -79,28 +80,29 @@ class User extends Authenticatable implements MustVerifyEmail
'catch_all' => 'boolean',
'two_factor_enabled' => 'boolean',
'use_reply_to' => 'boolean',
];
protected $dates = [
'created_at',
'updated_at',
'email_verified_at',
'created_at' => 'datetime',
'updated_at' => 'datetime',
'email_verified_at' => 'datetime',
];
/**
* Get the user's default email.
*/
public function getEmailAttribute()
protected function email(): Attribute
{
return $this->defaultRecipient->email;
return Attribute::make(
get: fn () => $this->defaultRecipient->email,
);
}
/**
* Get the user's default email verified_at.
*/
public function getEmailVerifiedAtAttribute()
protected function emailVerifiedAt(): Attribute
{
return $this->defaultRecipient->email_verified_at;
return Attribute::make(
get: fn () => $this->defaultRecipient->email_verified_at,
);
}
/**
@@ -114,9 +116,11 @@ class User extends Authenticatable implements MustVerifyEmail
/**
* Get the user's default username.
*/
public function getUsernameAttribute()
protected function username(): Attribute
{
return $this->defaultUsername->username;
return Attribute::make(
get: fn () => $this->defaultUsername->username,
);
}
/**
@@ -140,9 +144,11 @@ class User extends Authenticatable implements MustVerifyEmail
/**
* Get the user's bandwidth in MB.
*/
public function getBandwidthMbAttribute()
protected function bandwidthMb(): Attribute
{
return round($this->bandwidth / 1024 / 1024, 2);
return Attribute::make(
get: fn () => round($this->bandwidth / 1024 / 1024, 2),
);
}
/**

View File

@@ -29,17 +29,14 @@ class Username extends Model
'catch_all',
];
protected $dates = [
'created_at',
'updated_at',
];
protected $casts = [
'id' => 'string',
'user_id' => 'string',
'active' => 'boolean',
'catch_all' => 'boolean',
'default_recipient_id' => 'string',
'created_at' => 'datetime',
'updated_at' => 'datetime',
];
public static function boot()

View File

@@ -75,7 +75,7 @@ class AliasesImportedNotification extends Notification implements ShouldQueue, S
])
->withSymfonyMessage(function (Email $message) {
$message->getHeaders()
->addTextHeader('Feedback-ID', 'AIF:anonaddy');
->addTextHeader('Feedback-ID', 'AIF:anonaddy');
});
}

View File

@@ -44,7 +44,7 @@ class CustomVerifyEmail extends VerifyEmail implements ShouldQueue, ShouldBeEncr
])
->withSymfonyMessage(function (Email $message) use ($feedbackId) {
$message->getHeaders()
->addTextHeader('Feedback-ID', $feedbackId);
->addTextHeader('Feedback-ID', $feedbackId);
});
}

View File

@@ -54,7 +54,7 @@ class DefaultRecipientUpdated extends Notification implements ShouldQueue, Shoul
])
->withSymfonyMessage(function (Email $message) {
$message->getHeaders()
->addTextHeader('Feedback-ID', 'DRU:anonaddy');
->addTextHeader('Feedback-ID', 'DRU:anonaddy');
});
}

View File

@@ -68,7 +68,7 @@ class DisallowedReplySendAttempt extends Notification implements ShouldQueue, Sh
])
->withSymfonyMessage(function (Email $message) {
$message->getHeaders()
->addTextHeader('Feedback-ID', 'DRSA:anonaddy');
->addTextHeader('Feedback-ID', 'DRSA:anonaddy');
});
}

View File

@@ -56,7 +56,7 @@ class DomainMxRecordsInvalid extends Notification implements ShouldQueue, Should
])
->withSymfonyMessage(function (Email $message) {
$message->getHeaders()
->addTextHeader('Feedback-ID', 'DMI:anonaddy');
->addTextHeader('Feedback-ID', 'DMI:anonaddy');
});
}

View File

@@ -60,7 +60,7 @@ class DomainUnverifiedForSending extends Notification implements ShouldQueue, Sh
])
->withSymfonyMessage(function (Email $message) {
$message->getHeaders()
->addTextHeader('Feedback-ID', 'DUS:anonaddy');
->addTextHeader('Feedback-ID', 'DUS:anonaddy');
});
}

View File

@@ -50,18 +50,18 @@ class FailedDeliveryNotification extends Notification implements ShouldQueue, Sh
public function toMail($notifiable)
{
return (new MailMessage())
->subject('New failed delivery on AnonAddy')
->markdown('mail.failed_delivery_notification', [
'aliasEmail' => $this->aliasEmail,
'originalSender' => $this->originalSender,
'originalSubject' => $this->originalSubject,
'recipientId' => $notifiable->id,
'fingerprint' => $notifiable->should_encrypt ? $notifiable->fingerprint : null,
])
->withSymfonyMessage(function ($message) {
$message->getHeaders()
->addTextHeader('Feedback-ID', 'FDN:anonaddy');
});
->subject('New failed delivery on AnonAddy')
->markdown('mail.failed_delivery_notification', [
'aliasEmail' => $this->aliasEmail,
'originalSender' => $this->originalSender,
'originalSubject' => $this->originalSubject,
'recipientId' => $notifiable->id,
'fingerprint' => $notifiable->should_encrypt ? $notifiable->fingerprint : null,
])
->withSymfonyMessage(function ($message) {
$message->getHeaders()
->addTextHeader('Feedback-ID', 'FDN:anonaddy');
});
}
/**

View File

@@ -39,7 +39,7 @@ class GpgKeyExpired extends Notification implements ShouldQueue, ShouldBeEncrypt
])
->withSymfonyMessage(function (Email $message) {
$message->getHeaders()
->addTextHeader('Feedback-ID', 'GKE:anonaddy');
->addTextHeader('Feedback-ID', 'GKE:anonaddy');
});
}

View File

@@ -45,7 +45,7 @@ class IncorrectOtpNotification extends Notification implements ShouldQueue, Shou
])
->withSymfonyMessage(function (Email $message) {
$message->getHeaders()
->addTextHeader('Feedback-ID', 'FLA:anonaddy');
->addTextHeader('Feedback-ID', 'FLA:anonaddy');
});
}

View File

@@ -51,19 +51,19 @@ class NearBandwidthLimit extends Notification implements ShouldQueue, ShouldBeEn
$fingerprint = $recipient->should_encrypt ? $recipient->fingerprint : null;
return (new MailMessage())
->subject("You're close to your bandwidth limit for ".$this->month)
->markdown('mail.near_bandwidth_limit', [
'bandwidthUsage' => $notifiable->bandwidth_mb,
'bandwidthLimit' => $notifiable->getBandwidthLimitMb(),
'month' => $this->month,
'reset' => $this->reset,
'recipientId' => $recipient->id,
'fingerprint' => $fingerprint,
])
->withSymfonyMessage(function (Email $message) {
$message->getHeaders()
->subject("You're close to your bandwidth limit for ".$this->month)
->markdown('mail.near_bandwidth_limit', [
'bandwidthUsage' => $notifiable->bandwidth_mb,
'bandwidthLimit' => $notifiable->getBandwidthLimitMb(),
'month' => $this->month,
'reset' => $this->reset,
'recipientId' => $recipient->id,
'fingerprint' => $fingerprint,
])
->withSymfonyMessage(function (Email $message) {
$message->getHeaders()
->addTextHeader('Feedback-ID', 'NBL:anonaddy');
});
});
}
/**

View File

@@ -66,7 +66,7 @@ class SpamReplySendAttempt extends Notification implements ShouldQueue, ShouldBe
])
->withSymfonyMessage(function (Email $message) {
$message->getHeaders()
->addTextHeader('Feedback-ID', 'SRSA:anonaddy');
->addTextHeader('Feedback-ID', 'SRSA:anonaddy');
});
}

View File

@@ -41,7 +41,7 @@ class UsernameReminder extends Notification implements ShouldQueue, ShouldBeEncr
])
->withSymfonyMessage(function (Email $message) {
$message->getHeaders()
->addTextHeader('Feedback-ID', 'UR:anonaddy');
->addTextHeader('Feedback-ID', 'UR:anonaddy');
});
}

View File

@@ -5,6 +5,7 @@ namespace App\Providers;
use App\Models\PersonalAccessToken;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Support\Arr;
use Illuminate\Support\ServiceProvider;
@@ -14,21 +15,21 @@ class AppServiceProvider extends ServiceProvider
{
/**
* Register any application services.
*
* @return void
*/
public function register()
public function register(): void
{
Sanctum::ignoreMigrations();
}
/**
* Bootstrap any application services.
*
* @return void
*/
public function boot()
public function boot(): void
{
Model::preventAccessingMissingAttributes();
Model::preventSilentlyDiscardingAttributes();
Model::preventLazyLoading();
Sanctum::usePersonalAccessTokenModel(PersonalAccessToken::class);
Builder::macro('jsonPaginate', function (int $maxResults = null, int $defaultSize = null) {

View File

@@ -12,16 +12,14 @@ class AuthServiceProvider extends ServiceProvider
* @var array
*/
protected $policies = [
'App\Models\Model' => 'App\Policies\ModelPolicy',
//
];
/**
* Register any authentication / authorization services.
*
* @return void
*/
public function boot()
public function boot(): void
{
$this->registerPolicies();
//
}
}

View File

@@ -9,10 +9,8 @@ class BroadcastServiceProvider extends ServiceProvider
{
/**
* Bootstrap any application services.
*
* @return void
*/
public function boot()
public function boot(): void
{
Broadcast::routes();

View File

@@ -31,20 +31,16 @@ class EventServiceProvider extends ServiceProvider
/**
* Register any events for your application.
*
* @return void
*/
public function boot()
public function boot(): void
{
//
}
/**
* Determine if events and listeners should be automatically discovered.
*
* @return bool
*/
public function shouldDiscoverEvents()
public function shouldDiscoverEvents(): bool
{
return false;
}

View File

@@ -35,7 +35,9 @@ class RouteServiceProvider extends ServiceProvider
*/
public function boot()
{
$this->configureRateLimiting();
RateLimiter::for('api', function (Request $request) {
return Limit::perMinute(60)->by($request->user()?->id ?: $request->ip());
});
$this->routes(function () {
Route::middleware('api')
@@ -48,16 +50,4 @@ class RouteServiceProvider extends ServiceProvider
->group(base_path('routes/web.php'));
});
}
/**
* Configure the rate limiters for the application.
*
* @return void
*/
protected function configureRateLimiting()
{
RateLimiter::for('api', function (Request $request) {
return Limit::perMinute(60)->by($request->user()?->id ?: $request->ip());
});
}
}

View File

@@ -2,39 +2,25 @@
namespace App\Rules;
use Illuminate\Contracts\Validation\Rule;
use Closure;
use Illuminate\Contracts\Validation\ValidationRule;
class NotBlacklisted implements Rule
class NotBlacklisted implements ValidationRule
{
/**
* Create a new rule instance.
* Indicates whether the rule should be implicit.
*
* @return void
* @var bool
*/
public function __construct()
{
//
}
public $implicit = true;
/**
* Determine if the validation rule passes.
*
* @param string $attribute
* @param mixed $value
* @return bool
* Run the validation rule.
*/
public function passes($attribute, $value)
public function validate(string $attribute, mixed $value, Closure $fail): void
{
return ! in_array(strtolower($value), config('anonaddy.blacklist'));
}
/**
* Get the validation error message.
*
* @return string
*/
public function message()
{
return 'The :attribute has already been taken.';
if (in_array(strtolower($value), config('anonaddy.blacklist'))) {
$fail('The :attribute has already been taken.');
}
}
}

View File

@@ -3,28 +3,22 @@
namespace App\Rules;
use App\Models\DeletedUsername;
use Illuminate\Contracts\Validation\Rule;
use Closure;
use Illuminate\Contracts\Validation\ValidationRule;
class NotDeletedUsername implements Rule
class NotDeletedUsername implements ValidationRule
{
/**
* Create a new rule instance.
* Indicates whether the rule should be implicit.
*
* @return void
* @var bool
*/
public function __construct()
{
//
}
public $implicit = true;
/**
* Determine if the validation rule passes.
*
* @param string $attribute
* @param mixed $value
* @return bool
* Run the validation rule.
*/
public function passes($attribute, $value)
public function validate(string $attribute, mixed $value, Closure $fail): void
{
$deletedUsernames = DeletedUsername::select('username')
->get()
@@ -33,16 +27,8 @@ class NotDeletedUsername implements Rule
})
->toArray();
return ! in_array(strtolower($value), $deletedUsernames);
}
/**
* Get the validation error message.
*
* @return string
*/
public function message()
{
return 'The :attribute has already been taken.';
if (in_array(strtolower($value), $deletedUsernames)) {
$fail('The :attribute has already been taken.');
}
}
}

View File

@@ -2,46 +2,32 @@
namespace App\Rules;
use Illuminate\Contracts\Validation\Rule;
use Closure;
use Illuminate\Contracts\Validation\ValidationRule;
use Illuminate\Support\Str;
class NotLocalDomain implements Rule
class NotLocalDomain implements ValidationRule
{
/**
* Create a new rule instance.
* Indicates whether the rule should be implicit.
*
* @return void
* @var bool
*/
public function __construct()
{
//
}
public $implicit = true;
/**
* Determine if the validation rule passes.
*
* @param string $attribute
* @param mixed $value
* @return bool
* Run the validation rule.
*/
public function passes($attribute, $value)
public function validate(string $attribute, mixed $value, Closure $fail): void
{
$count = collect(config('anonaddy.all_domains'))
->filter(function ($name) use ($value) {
return Str::endsWith(strtolower($value), $name);
})
->count();
->filter(function ($name) use ($value) {
return Str::endsWith(strtolower($value), $name);
})
->count();
return $count === 0;
}
/**
* Get the validation error message.
*
* @return string
*/
public function message()
{
return 'The domain cannot be a local one.';
if ($count !== 0) {
$fail('The domain cannot be a local one.');
}
}
}

View File

@@ -3,35 +3,29 @@
namespace App\Rules;
use App\Models\Domain;
use Illuminate\Contracts\Validation\Rule;
use Closure;
use Illuminate\Contracts\Validation\ValidationRule;
use Illuminate\Support\Str;
class NotLocalRecipient implements Rule
class NotLocalRecipient implements ValidationRule
{
/**
* Create a new rule instance.
* Indicates whether the rule should be implicit.
*
* @return void
* @var bool
*/
public function __construct()
{
//
}
public $implicit = true;
/**
* Determine if the validation rule passes.
*
* @param string $attribute
* @param mixed $value
* @return bool
* Run the validation rule.
*/
public function passes($attribute, $value)
public function validate(string $attribute, mixed $value, Closure $fail): void
{
$emailDomain = strtolower(Str::afterLast($value, '@'));
// Make sure the recipient domain is not added as a verified custom domain
if (Domain::whereNotNull('domain_verified_at')->pluck('domain')->contains($emailDomain)) {
return false;
$fail('The recipient cannot use a domain that is already used by a custom domain.');
}
$count = collect(config('anonaddy.all_domains'))
@@ -40,16 +34,8 @@ class NotLocalRecipient implements Rule
})
->count();
return $count === 0;
}
/**
* Get the validation error message.
*
* @return string
*/
public function message()
{
return 'The recipient cannot use a local domain or be an alias.';
if ($count !== 0) {
$fail('The recipient cannot use a local domain or be an alias.');
}
}
}

View File

@@ -3,47 +3,34 @@
namespace App\Rules;
use App\Models\Recipient;
use Illuminate\Contracts\Validation\Rule;
use Closure;
use Illuminate\Contracts\Validation\ValidationRule;
use Illuminate\Support\Str;
class NotUsedAsRecipientDomain implements Rule
class NotUsedAsRecipientDomain implements ValidationRule
{
/**
* Create a new rule instance.
* Indicates whether the rule should be implicit.
*
* @return void
* @var bool
*/
public function __construct()
{
//
}
public $implicit = true;
/**
* Determine if the validation rule passes.
*
* @param string $attribute
* @param mixed $value
* @return bool
* Run the validation rule.
*/
public function passes($attribute, $value)
public function validate(string $attribute, mixed $value, Closure $fail): void
{
return ! Recipient::whereNotNull('email_verified_at')
$recipientDomains = Recipient::whereNotNull('email_verified_at')
->select('email')
->get()
->map(function ($recipient) {
return Str::afterLast($recipient->email, '@');
})
->unique()
->contains($value);
}
->unique();
/**
* Get the validation error message.
*
* @return string
*/
public function message()
{
return 'The domain must not already be used by a verified recipient.';
if ($recipientDomains->contains($value)) {
$fail('The domain must not already be used by a verified recipient.');
}
}
}

View File

@@ -3,28 +3,22 @@
namespace App\Rules;
use App\Models\Recipient;
use Illuminate\Contracts\Validation\Rule;
use Closure;
use Illuminate\Contracts\Validation\ValidationRule;
class RegisterUniqueRecipient implements Rule
class RegisterUniqueRecipient implements ValidationRule
{
/**
* Create a new rule instance.
* Indicates whether the rule should be implicit.
*
* @return void
* @var bool
*/
public function __construct()
{
//
}
public $implicit = true;
/**
* Determine if the validation rule passes.
*
* @param string $attribute
* @param mixed $value
* @return bool
* Run the validation rule.
*/
public function passes($attribute, $value)
public function validate(string $attribute, mixed $value, Closure $fail): void
{
$items = Recipient::whereNotNull('email_verified_at')
->select('email')
@@ -35,16 +29,8 @@ class RegisterUniqueRecipient implements Rule
}
});
return count($items) === 0;
}
/**
* Get the validation error message.
*
* @return string
*/
public function message()
{
return 'A user with that email already exists.';
if (count($items) !== 0) {
$fail('A user with that email already exists.');
}
}
}

View File

@@ -3,32 +3,24 @@
namespace App\Rules;
use App\Models\Recipient;
use Illuminate\Contracts\Validation\Rule;
use Closure;
use Illuminate\Contracts\Validation\ValidationRule;
class UniqueRecipient implements Rule
class UniqueRecipient implements ValidationRule
{
protected $user;
/**
* Indicates whether the rule should be implicit.
*
* @var bool
*/
public $implicit = true;
/**
* Create a new rule instance.
*
* @return void
* Run the validation rule.
*/
public function __construct()
public function validate(string $attribute, mixed $value, Closure $fail): void
{
$this->user = user();
}
/**
* Determine if the validation rule passes.
*
* @param string $attribute
* @param mixed $value
* @return bool
*/
public function passes($attribute, $value)
{
$items = Recipient::where('user_id', $this->user->id)
$items = Recipient::where('user_id', user()->id)
->orWhere('email_verified_at', '!=', null)
->select('email')
->get()
@@ -38,16 +30,8 @@ class UniqueRecipient implements Rule
}
});
return count($items) === 0;
}
/**
* Get the validation error message.
*
* @return string
*/
public function message()
{
return 'A recipient with that email already exists.';
if (count($items) !== 0) {
$fail('A recipient with that email already exists.');
}
}
}

View File

@@ -2,39 +2,25 @@
namespace App\Rules;
use Illuminate\Contracts\Validation\Rule;
use Closure;
use Illuminate\Contracts\Validation\ValidationRule;
class ValidAliasLocalPart implements Rule
class ValidAliasLocalPart implements ValidationRule
{
/**
* Create a new rule instance.
* Indicates whether the rule should be implicit.
*
* @return void
* @var bool
*/
public function __construct()
{
//
}
public $implicit = true;
/**
* Determine if the validation rule passes.
*
* @param string $attribute
* @param mixed $value
* @return bool
* Run the validation rule.
*/
public function passes($attribute, $value)
public function validate(string $attribute, mixed $value, Closure $fail): void
{
return preg_match('/^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))$/', $value);
}
/**
* Get the validation error message.
*
* @return string
*/
public function message()
{
return 'Invalid alias local part.';
if (! preg_match('/^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))$/', $value)) {
$fail('Invalid alias local part.');
}
}
}

View File

@@ -2,39 +2,25 @@
namespace App\Rules;
use Illuminate\Contracts\Validation\Rule;
use Closure;
use Illuminate\Contracts\Validation\ValidationRule;
class ValidDomain implements Rule
class ValidDomain implements ValidationRule
{
/**
* Create a new rule instance.
* Indicates whether the rule should be implicit.
*
* @return void
* @var bool
*/
public function __construct()
{
//
}
public $implicit = true;
/**
* Determine if the validation rule passes.
*
* @param string $attribute
* @param mixed $value
* @return bool
* Run the validation rule.
*/
public function passes($attribute, $value)
public function validate(string $attribute, mixed $value, Closure $fail): void
{
return preg_match('/(?=^.{4,253}$)(^((?!-)[a-zA-Z0-9-]{0,62}[a-zA-Z0-9]\.)+[a-zA-Z]{2,63}$)/', $value);
}
/**
* Get the validation error message.
*
* @return string
*/
public function message()
{
return 'Invalid domain name.';
if (! preg_match('/(?=^.{4,253}$)(^((?!-)[a-zA-Z0-9-]{0,62}[a-zA-Z0-9]\.)+[a-zA-Z]{2,63}$)/', $value)) {
$fail('Invalid domain name.');
}
}
}

View File

@@ -2,52 +2,32 @@
namespace App\Rules;
use Illuminate\Contracts\Validation\Rule;
use Closure;
use Illuminate\Contracts\Validation\ValidationRule;
class ValidRuleId implements Rule
class ValidRuleId implements ValidationRule
{
protected $user;
/**
* Indicates whether the rule should be implicit.
*
* @var bool
*/
public $implicit = true;
/**
* Create a new rule instance.
*
* @return void
* Run the validation rule.
*/
public function __construct()
public function validate(string $attribute, mixed $ids, Closure $fail): void
{
$this->user = user();
}
/**
* Determine if the validation rule passes.
*
* @param string $attribute
* @param mixed $ids
* @return bool
*/
public function passes($attribute, $ids)
{
$validRuleIds = $this->user
$validRuleIds = user()
->rules()
->pluck('id')
->toArray();
foreach ($ids as $id) {
if (! in_array($id, $validRuleIds)) {
return false;
$fail('Invalid Rule ID.');
}
}
return true;
}
/**
* Get the validation error message.
*
* @return string
*/
public function message()
{
return 'Invalid Rule ID.';
}
}

View File

@@ -2,12 +2,11 @@
namespace App\Rules;
use Illuminate\Contracts\Validation\Rule;
use Closure;
use Illuminate\Contracts\Validation\ValidationRule;
class VerifiedRecipientId implements Rule
class VerifiedRecipientId implements ValidationRule
{
protected $user;
protected $verifiedRecipientIds;
/**
@@ -17,47 +16,30 @@ class VerifiedRecipientId implements Rule
*/
public function __construct(array $verifiedRecipientIds = null)
{
$this->user = user();
if (! is_null($verifiedRecipientIds)) {
$this->verifiedRecipientIds = $verifiedRecipientIds;
} else {
$this->verifiedRecipientIds = $this->user
->verifiedRecipients()
->pluck('id')
->toArray();
$this->verifiedRecipientIds = user()
->verifiedRecipients()
->pluck('id')
->toArray();
}
}
/**
* Determine if the validation rule passes.
*
* @param string $attribute
* @param mixed $ids
* @return bool
* Run the validation rule.
*/
public function passes($attribute, $ids)
public function validate(string $attribute, mixed $ids, Closure $fail): void
{
// Multiple calls to $fail simply add more validation errors, they don't stop processing.
if (! is_array($ids)) {
return false;
}
foreach ($ids as $id) {
if (!in_array($id, $this->verifiedRecipientIds)) {
return false;
$fail('Invalid Recipient');
} else {
foreach ($ids as $id) {
if (! in_array($id, $this->verifiedRecipientIds)) {
$fail('Invalid Recipient');
}
}
}
return true;
}
/**
* Get the validation error message.
*
* @return string
*/
public function message()
{
return 'Invalid Recipient.';
}
}

View File

@@ -7,14 +7,14 @@
],
"license": "AGPL-3.0-or-later",
"require": {
"php": "^8.0.2",
"php": "^8.1",
"asbiin/laravel-webauthn": "^3.0.0",
"bacon/bacon-qr-code": "^2.0",
"chillerlan/php-qrcode": "^4.3",
"doctrine/dbal": "^3.0",
"guzzlehttp/guzzle": "^7.2",
"laravel/framework": "^9.11",
"laravel/sanctum": "^3.0",
"laravel/framework": "^10.0",
"laravel/sanctum": "^3.2",
"laravel/tinker": "^2.7",
"laravel/ui": "^4.0",
"maatwebsite/excel": "^3.1",
@@ -27,9 +27,9 @@
"fakerphp/faker": "^1.9.1",
"laravel/pint": "^1.2",
"mockery/mockery": "^1.4.4",
"nunomaduro/collision": "^6.1",
"phpunit/phpunit": "^9.5.10",
"spatie/laravel-ignition": "^1.0",
"nunomaduro/collision": "^7.0",
"phpunit/phpunit": "^10.0",
"spatie/laravel-ignition": "^2.0",
"spatie/laravel-ray": "^1.29"
},
"config": {
@@ -56,7 +56,7 @@
"Tests\\": "tests/"
}
},
"minimum-stability": "dev",
"minimum-stability": "stable",
"prefer-stable": true,
"scripts": {
"post-autoload-dump": [

1048
composer.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,7 @@
<?php
use Illuminate\Support\Facades\Facade;
use Illuminate\Support\ServiceProvider;
return [
@@ -154,33 +155,11 @@ return [
|
*/
'providers' => [
'providers' => ServiceProvider::defaultProviders()->merge([
/*
* Laravel Framework Service Providers...
*/
Illuminate\Auth\AuthServiceProvider::class,
Illuminate\Broadcasting\BroadcastServiceProvider::class,
Illuminate\Bus\BusServiceProvider::class,
Illuminate\Cache\CacheServiceProvider::class,
Illuminate\Foundation\Providers\ConsoleSupportServiceProvider::class,
Illuminate\Cookie\CookieServiceProvider::class,
Illuminate\Database\DatabaseServiceProvider::class,
Illuminate\Encryption\EncryptionServiceProvider::class,
Illuminate\Filesystem\FilesystemServiceProvider::class,
Illuminate\Foundation\Providers\FoundationServiceProvider::class,
Illuminate\Hashing\HashServiceProvider::class,
Illuminate\Mail\MailServiceProvider::class,
Illuminate\Notifications\NotificationServiceProvider::class,
Illuminate\Pagination\PaginationServiceProvider::class,
Illuminate\Pipeline\PipelineServiceProvider::class,
Illuminate\Queue\QueueServiceProvider::class,
Illuminate\Redis\RedisServiceProvider::class,
Illuminate\Auth\Passwords\PasswordResetServiceProvider::class,
Illuminate\Session\SessionServiceProvider::class,
Illuminate\Translation\TranslationServiceProvider::class,
Illuminate\Validation\ValidationServiceProvider::class,
Illuminate\View\ViewServiceProvider::class,
/*
* Package Service Providers...
@@ -197,7 +176,7 @@ return [
App\Providers\HelperServiceProvider::class,
App\Providers\CustomMailServiceProvider::class,
],
])->toArray(),
/*
|--------------------------------------------------------------------------
@@ -211,7 +190,7 @@ return [
*/
'aliases' => Facade::defaultAliases()->merge([
// 'ExampleClass' => App\Example\ExampleClass::class,
// 'Example' => App\Facades\Example::class,
])->toArray(),
];

View File

@@ -3,6 +3,7 @@
use Monolog\Handler\NullHandler;
use Monolog\Handler\StreamHandler;
use Monolog\Handler\SyslogUdpHandler;
use Monolog\Processor\PsrLogMessageProcessor;
return [
@@ -61,6 +62,7 @@ return [
'driver' => 'single',
'path' => storage_path('logs/laravel.log'),
'level' => 'debug',
'replace_placeholders' => true,
],
'daily' => [
@@ -68,6 +70,7 @@ return [
'path' => storage_path('logs/laravel.log'),
'level' => 'debug',
'days' => 14,
'replace_placeholders' => true,
],
'slack' => [
@@ -76,6 +79,7 @@ return [
'username' => 'Laravel Log',
'emoji' => ':boom:',
'level' => 'critical',
'replace_placeholders' => true,
],
'papertrail' => [
@@ -87,6 +91,7 @@ return [
'port' => env('PAPERTRAIL_PORT'),
'connectionString' => 'tls://'.env('PAPERTRAIL_URL').':'.env('PAPERTRAIL_PORT'),
],
'processors' => [PsrLogMessageProcessor::class],
],
'stderr' => [
@@ -96,16 +101,19 @@ return [
'with' => [
'stream' => 'php://stderr',
],
'processors' => [PsrLogMessageProcessor::class],
],
'syslog' => [
'driver' => 'syslog',
'level' => 'debug',
'replace_placeholders' => true,
],
'errorlog' => [
'driver' => 'errorlog',
'level' => 'debug',
'replace_placeholders' => true,
],
'null' => [

View File

@@ -13,12 +13,12 @@ class UpdateAliasableTypeInAliasesTable extends Migration
public function up()
{
Alias::withTrashed()
->where('aliasable_type', 'App\AdditionalUsername')
->update(['aliasable_type' => 'App\Models\AdditionalUsername']);
->where('aliasable_type', 'App\AdditionalUsername')
->update(['aliasable_type' => 'App\Models\AdditionalUsername']);
Alias::withTrashed()
->where('aliasable_type', 'App\Domain')
->update(['aliasable_type' => 'App\Models\Domain']);
->where('aliasable_type', 'App\Domain')
->update(['aliasable_type' => 'App\Models\Domain']);
}
/**
@@ -29,11 +29,11 @@ class UpdateAliasableTypeInAliasesTable extends Migration
public function down()
{
Alias::withTrashed()
->where('aliasable_type', 'App\Models\AdditionalUsername')
->update(['aliasable_type' => 'App\AdditionalUsername']);
->where('aliasable_type', 'App\Models\AdditionalUsername')
->update(['aliasable_type' => 'App\AdditionalUsername']);
Alias::withTrashed()
->where('aliasable_type', 'App\Models\Domain')
->update(['aliasable_type' => 'App\Domain']);
->where('aliasable_type', 'App\Models\Domain')
->update(['aliasable_type' => 'App\Domain']);
}
}

View File

@@ -65,8 +65,8 @@ class MoveAccountUsernameToUsernamesTable extends Migration
// Update all additional username aliases aliasable_type
Alias::withTrashed()
->where('aliasable_type', 'App\Models\AdditionalUsername')
->update(['aliasable_type' => 'App\Models\Username']);
->where('aliasable_type', 'App\Models\AdditionalUsername')
->update(['aliasable_type' => 'App\Models\Username']);
// Drop the username and catch_all column from the users table
Schema::table('users', function (Blueprint $table) {
@@ -93,8 +93,8 @@ class MoveAccountUsernameToUsernamesTable extends Migration
// Update all usernames back to additional username
Alias::withTrashed()
->where('aliasable_type', 'App\Models\Username')
->update(['aliasable_type' => 'App\Models\AdditionalUsername']);
->where('aliasable_type', 'App\Models\Username')
->update(['aliasable_type' => 'App\Models\AdditionalUsername']);
// Repopulate the username column and delete the defaultUsername from the usernames table
User::select(['id', 'username', 'default_username_id', 'catch_all'])->chunk(200, function ($users) {

629
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,27 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit backupGlobals="false"
backupStaticAttributes="false"
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="./vendor/phpunit/phpunit/phpunit.xsd"
bootstrap="vendor/autoload.php"
colors="true"
convertErrorsToExceptions="true"
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
processIsolation="false"
stopOnFailure="false">
>
<testsuites>
<testsuite name="Unit">
<directory suffix="Test.php">./tests/Unit</directory>
</testsuite>
<testsuite name="Feature">
<directory suffix="Test.php">./tests/Feature</directory>
</testsuite>
</testsuites>
<coverage processUncoveredFiles="true">
<source>
<include>
<directory suffix=".php">./app</directory>
</include>
</coverage>
</source>
<php>
<env name="APP_ENV" value="testing"/>
<env name="BCRYPT_ROUNDS" value="4"/>
@@ -32,4 +27,4 @@
<env name="DB_CONNECTION" value="sqlite"/>
<env name="DB_DATABASE" value=":memory:"/>
</php>
</phpunit>
</phpunit>

View File

@@ -1,4 +1,4 @@
alias,description,recipient_ids
abc@example.com,My awesome alias!,477b1a7f-280e-4153-9178-76862b2e0404
alias,description,recipients
abc@example.com,My awesome alias!,recipient1@example.com
no-description@example.com,,
xyz@example.com,"Alias with 2 recipients, use a comma separated list of IDs.","477b1a7f-280e-4153-9178-76862b2e0404,9040f2c0-e330-4650-9e7c-96d51b6c5d17"
xyz@example.com,"Alias with 2 recipients, use a comma separated list of recipient emails.","recipient1@example.com,recipient2@example.com"
1 alias description recipient_ids recipients
2 abc@example.com My awesome alias! 477b1a7f-280e-4153-9178-76862b2e0404 recipient1@example.com
3 no-description@example.com
4 xyz@example.com Alias with 2 recipients, use a comma separated list of IDs. Alias with 2 recipients, use a comma separated list of recipient emails. 477b1a7f-280e-4153-9178-76862b2e0404,9040f2c0-e330-4650-9e7c-96d51b6c5d17 recipient1@example.com,recipient2@example.com

View File

@@ -214,7 +214,7 @@
</Modal>
<Modal :open="revokeTokenModalOpen" @close="closeRevokeTokenModal">
<template v-slot:title> ARevoke API Access Token </template>
<template v-slot:title> Revoke API Access Token </template>
<template v-slot:content>
<p class="my-4 text-grey-700">
Any browser extension, application or script using this API access token will no longer be

View File

@@ -2,12 +2,12 @@
namespace Tests\Feature\Api;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Foundation\Testing\LazilyRefreshDatabase;
use Tests\TestCase;
class AccountDetailsTest extends TestCase
{
use RefreshDatabase;
use LazilyRefreshDatabase;
protected function setUp(): void
{

View File

@@ -5,12 +5,12 @@ namespace Tests\Feature\Api;
use App\Models\Alias;
use App\Models\AliasRecipient;
use App\Models\Recipient;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Foundation\Testing\LazilyRefreshDatabase;
use Tests\TestCase;
class AliasRecipientsTest extends TestCase
{
use RefreshDatabase;
use LazilyRefreshDatabase;
protected function setUp(): void
{

View File

@@ -6,12 +6,12 @@ use App\Models\Alias;
use App\Models\Domain;
use App\Models\Recipient;
use App\Models\Username;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Foundation\Testing\LazilyRefreshDatabase;
use Tests\TestCase;
class AliasesTest extends TestCase
{
use RefreshDatabase;
use LazilyRefreshDatabase;
protected function setUp(): void
{

View File

@@ -3,12 +3,12 @@
namespace Tests\Feature\Api;
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Foundation\Testing\LazilyRefreshDatabase;
use Tests\TestCase;
class ApiTokenDetailsTest extends TestCase
{
use RefreshDatabase;
use LazilyRefreshDatabase;
protected function setUp(): void
{
@@ -18,7 +18,7 @@ class ApiTokenDetailsTest extends TestCase
/** @test */
public function user_can_get_account_details()
{
$user = User::factory()->create();
$user = User::factory()->create()->fresh();
$token = $user->createToken('New');
$response = $this->withHeaders([

View File

@@ -3,13 +3,13 @@
namespace Tests\Feature\Api;
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Foundation\Testing\LazilyRefreshDatabase;
use Laravel\Sanctum\Sanctum;
use Tests\TestCase;
class ApiTokensTest extends TestCase
{
use RefreshDatabase;
use LazilyRefreshDatabase;
protected $user;
@@ -17,7 +17,7 @@ class ApiTokensTest extends TestCase
{
parent::setUp();
$this->user = User::factory()->create();
$this->user = User::factory()->create()->fresh();
Sanctum::actingAs($this->user, [], 'web');
$this->user->recipients()->save($this->user->defaultRecipient);
}

View File

@@ -3,12 +3,12 @@
namespace Tests\Feature\Api;
use App\Helpers\GitVersionHelper as Version;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Foundation\Testing\LazilyRefreshDatabase;
use Tests\TestCase;
class AppVersionTest extends TestCase
{
use RefreshDatabase;
use LazilyRefreshDatabase;
protected function setUp(): void
{

View File

@@ -4,12 +4,12 @@ namespace Tests\Feature\Api;
use App\Models\Domain;
use App\Models\Recipient;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Foundation\Testing\LazilyRefreshDatabase;
use Tests\TestCase;
class DomainsTest extends TestCase
{
use RefreshDatabase;
use LazilyRefreshDatabase;
protected function setUp(): void
{

View File

@@ -3,12 +3,12 @@
namespace Tests\Feature\Api;
use App\Models\FailedDelivery;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Foundation\Testing\LazilyRefreshDatabase;
use Tests\TestCase;
class FailedDeliveriesTest extends TestCase
{
use RefreshDatabase;
use LazilyRefreshDatabase;
protected function setUp(): void
{
@@ -61,6 +61,6 @@ class FailedDeliveriesTest extends TestCase
$response = $this->json('DELETE', '/api/v1/failed-deliveries/'.$failedDelivery->id);
$response->assertStatus(204);
$this->assertEmpty($this->user->failedDelivery);
$this->assertEmpty($this->user->failedDeliveries);
}
}

View File

@@ -4,13 +4,13 @@ namespace Tests\Feature\Api;
use App\Models\Domain;
use App\Models\Recipient;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Foundation\Testing\LazilyRefreshDatabase;
use Illuminate\Support\Facades\Notification;
use Tests\TestCase;
class RecipientsTest extends TestCase
{
use RefreshDatabase;
use LazilyRefreshDatabase;
protected function setUp(): void
{

View File

@@ -6,14 +6,14 @@ use App\Mail\ForwardEmail;
use App\Models\Alias;
use App\Models\EmailData;
use App\Models\Rule;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Foundation\Testing\LazilyRefreshDatabase;
use Illuminate\Support\Str;
use PhpMimeMailParser\Parser;
use Tests\TestCase;
class RulesTest extends TestCase
{
use RefreshDatabase;
use LazilyRefreshDatabase;
protected function setUp(): void
{

View File

@@ -6,12 +6,12 @@ use App\Models\DeletedUsername;
use App\Models\Recipient;
use App\Models\User;
use App\Models\Username;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Foundation\Testing\LazilyRefreshDatabase;
use Tests\TestCase;
class UsernamesTest extends TestCase
{
use RefreshDatabase;
use LazilyRefreshDatabase;
protected function setUp(): void
{
@@ -129,7 +129,7 @@ class UsernamesTest extends TestCase
/** @test */
public function must_be_unique_across_users_and_usernames_tables()
{
$user = User::factory()->create();
$user = User::factory()->create()->fresh();
$response = $this->json('POST', '/api/v1/usernames', [
'username' => $user->username,

View File

@@ -3,14 +3,14 @@
namespace Tests\Feature;
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Foundation\Testing\LazilyRefreshDatabase;
use Illuminate\Routing\Middleware\ThrottleRequestsWithRedis;
use Illuminate\Support\Facades\Hash;
use Tests\TestCase;
class ApiAuthenticationTest extends TestCase
{
use RefreshDatabase;
use LazilyRefreshDatabase;
protected $user;
@@ -20,7 +20,7 @@ class ApiAuthenticationTest extends TestCase
$this->user = User::factory()->create([
'password' => Hash::make('mypassword'),
]);
])->fresh();
$this->user->usernames()->save($this->user->defaultUsername);
$this->user->defaultUsername->username = 'johndoe';

View File

@@ -5,7 +5,7 @@ namespace Tests\Feature;
use App\Models\User;
use App\Models\Username;
use App\Notifications\UsernameReminder;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Foundation\Testing\LazilyRefreshDatabase;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Notification;
use Illuminate\Support\Str;
@@ -13,7 +13,7 @@ use Tests\TestCase;
class LoginTest extends TestCase
{
use RefreshDatabase;
use LazilyRefreshDatabase;
protected $user;
@@ -23,7 +23,7 @@ class LoginTest extends TestCase
$this->user = User::factory()->create([
'password' => Hash::make('mypassword'),
]);
])->fresh();
$this->user->recipients()->save($this->user->defaultRecipient);
$this->user->usernames()->save($this->user->defaultUsername);

View File

@@ -10,7 +10,7 @@ use App\Models\Recipient;
use App\Models\User;
use App\Models\Username;
use App\Notifications\NearBandwidthLimit;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Foundation\Testing\LazilyRefreshDatabase;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Mail;
use Illuminate\Support\Facades\Notification;
@@ -18,7 +18,7 @@ use Tests\TestCase;
class ReceiveEmailTest extends TestCase
{
use RefreshDatabase;
use LazilyRefreshDatabase;
protected $user;
@@ -26,7 +26,7 @@ class ReceiveEmailTest extends TestCase
{
parent::setUp();
$this->user = User::factory()->create();
$this->user = User::factory()->create()->fresh();
$this->user->recipients()->save($this->user->defaultRecipient);
$this->user->usernames()->save($this->user->defaultUsername);

View File

@@ -7,7 +7,7 @@ use App\Models\Recipient;
use App\Models\User;
use App\Models\Username;
use App\Notifications\CustomVerifyEmail;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Foundation\Testing\LazilyRefreshDatabase;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\Config;
use Illuminate\Support\Facades\Hash;
@@ -17,7 +17,7 @@ use Tests\TestCase;
class RegistrationTest extends TestCase
{
use RefreshDatabase;
use LazilyRefreshDatabase;
/** @test */
public function user_can_register_successfully()
@@ -69,7 +69,7 @@ class RegistrationTest extends TestCase
/** @test */
public function user_can_verify_email_successfully()
{
$user = User::factory()->create();
$user = User::factory()->create()->fresh();
$user->email_verified_at = null;
$user->save();
@@ -130,7 +130,7 @@ class RegistrationTest extends TestCase
/** @test */
public function user_cannot_register_with_existing_email()
{
$user = User::factory()->create();
$user = User::factory()->create()->fresh();
Recipient::factory()->create([
'user_id' => $user->id,
@@ -156,7 +156,7 @@ class RegistrationTest extends TestCase
/** @test */
public function user_cannot_register_with_existing_username()
{
$user = User::factory()->create();
$user = User::factory()->create()->fresh();
Username::factory()->create([
'user_id' => $user->id,

View File

@@ -6,13 +6,13 @@ use App\Mail\ReplyToEmail;
use App\Models\Alias;
use App\Models\Recipient;
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Foundation\Testing\LazilyRefreshDatabase;
use Illuminate\Support\Facades\Mail;
use Tests\TestCase;
class ReplyToEmailTest extends TestCase
{
use RefreshDatabase;
use LazilyRefreshDatabase;
protected $user;
@@ -20,7 +20,7 @@ class ReplyToEmailTest extends TestCase
{
parent::setUp();
$this->user = User::factory()->create();
$this->user = User::factory()->create()->fresh();
$this->user->recipients()->save($this->user->defaultRecipient);
$this->user->defaultRecipient->email = 'will@anonaddy.com';
$this->user->defaultRecipient->save();

View File

@@ -5,13 +5,13 @@ namespace Tests\Feature;
use App\Mail\SendFromEmail;
use App\Models\Alias;
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Foundation\Testing\LazilyRefreshDatabase;
use Illuminate\Support\Facades\Mail;
use Tests\TestCase;
class SendFromEmailTest extends TestCase
{
use RefreshDatabase;
use LazilyRefreshDatabase;
protected $user;
@@ -19,7 +19,7 @@ class SendFromEmailTest extends TestCase
{
parent::setUp();
$this->user = User::factory()->create();
$this->user = User::factory()->create()->fresh();
$this->user->recipients()->save($this->user->defaultRecipient);
$this->user->defaultRecipient->email = 'will@anonaddy.com';
$this->user->defaultRecipient->save();

View File

@@ -11,7 +11,7 @@ use App\Models\Domain;
use App\Models\Recipient;
use App\Models\User;
use App\Models\Username;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Foundation\Testing\LazilyRefreshDatabase;
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Str;
@@ -20,7 +20,7 @@ use Tests\TestCase;
class SettingsTest extends TestCase
{
use RefreshDatabase;
use LazilyRefreshDatabase;
protected $user;
@@ -28,7 +28,7 @@ class SettingsTest extends TestCase
{
parent::setUp();
$this->user = User::factory()->create();
$this->user = User::factory()->create()->fresh();
$this->actingAs($this->user);
$this->user->recipients()->save($this->user->defaultRecipient);
$this->user->usernames()->save($this->user->defaultUsername);
@@ -440,8 +440,8 @@ class SettingsTest extends TestCase
}
/**
* @test
*/
* @test
*/
public function user_can_download_aliases_export()
{
Excel::fake();

View File

@@ -6,13 +6,13 @@ use App\Models\Alias;
use App\Models\AliasRecipient;
use App\Models\Recipient;
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Foundation\Testing\LazilyRefreshDatabase;
use Illuminate\Support\Carbon;
use Tests\TestCase;
class ShowAliasesTest extends TestCase
{
use RefreshDatabase;
use LazilyRefreshDatabase;
protected $user;
@@ -20,7 +20,7 @@ class ShowAliasesTest extends TestCase
{
parent::setUp();
$this->user = User::factory()->create();
$this->user = User::factory()->create()->fresh();
$this->actingAs($this->user);
}

View File

@@ -4,13 +4,13 @@ namespace Tests\Feature;
use App\Models\Domain;
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Foundation\Testing\LazilyRefreshDatabase;
use Illuminate\Support\Carbon;
use Tests\TestCase;
class ShowDomainsTest extends TestCase
{
use RefreshDatabase;
use LazilyRefreshDatabase;
protected $user;
@@ -18,7 +18,7 @@ class ShowDomainsTest extends TestCase
{
parent::setUp();
$this->user = User::factory()->create();
$this->user = User::factory()->create()->fresh();
$this->actingAs($this->user);
}

View File

@@ -4,13 +4,13 @@ namespace Tests\Feature;
use App\Models\FailedDelivery;
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Foundation\Testing\LazilyRefreshDatabase;
use Illuminate\Support\Carbon;
use Tests\TestCase;
class ShowFailedDeliveriesTest extends TestCase
{
use RefreshDatabase;
use LazilyRefreshDatabase;
protected $user;
@@ -18,7 +18,7 @@ class ShowFailedDeliveriesTest extends TestCase
{
parent::setUp();
$this->user = User::factory()->create();
$this->user = User::factory()->create()->fresh();
$this->user->usernames()->save($this->user->defaultUsername);
$this->user->defaultUsername->username = 'johndoe';
$this->user->defaultUsername->save();

View File

@@ -7,7 +7,7 @@ use App\Models\AliasRecipient;
use App\Models\Recipient;
use App\Models\User;
use App\Notifications\CustomVerifyEmail;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Foundation\Testing\LazilyRefreshDatabase;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\Config;
use Illuminate\Support\Facades\Hash;
@@ -17,7 +17,7 @@ use Tests\TestCase;
class ShowRecipientsTest extends TestCase
{
use RefreshDatabase;
use LazilyRefreshDatabase;
protected $user;
@@ -25,7 +25,7 @@ class ShowRecipientsTest extends TestCase
{
parent::setUp();
$this->user = User::factory()->create();
$this->user = User::factory()->create()->fresh();
$this->actingAs($this->user);
}
@@ -76,12 +76,12 @@ class ShowRecipientsTest extends TestCase
]);
Alias::factory()->count(3)->create(['user_id' => $this->user->id])
->each(function ($alias) use ($recipient) {
AliasRecipient::create([
'alias' => $alias,
'recipient' => $recipient,
]);
});
->each(function ($alias) use ($recipient) {
AliasRecipient::create([
'alias' => $alias,
'recipient' => $recipient,
]);
});
$response = $this->get('/recipients');

View File

@@ -4,13 +4,13 @@ namespace Tests\Feature;
use App\Models\User;
use App\Models\Username;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Foundation\Testing\LazilyRefreshDatabase;
use Illuminate\Support\Carbon;
use Tests\TestCase;
class ShowUsernamesTest extends TestCase
{
use RefreshDatabase;
use LazilyRefreshDatabase;
protected $user;
@@ -18,7 +18,7 @@ class ShowUsernamesTest extends TestCase
{
parent::setUp();
$this->user = User::factory()->create();
$this->user = User::factory()->create()->fresh();
$this->actingAs($this->user);
}

View File

@@ -42,7 +42,7 @@ abstract class TestCase extends BaseTestCase
protected function setUpSanctum(): void
{
$this->user = User::factory()->create();
$this->user = User::factory()->create()->fresh();
Sanctum::actingAs($this->user, []);
}
}

View File

@@ -6,12 +6,12 @@ use App\Models\Alias;
use App\Models\AliasRecipient;
use App\Models\Recipient;
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Foundation\Testing\LazilyRefreshDatabase;
use Tests\TestCase;
class AliasTest extends TestCase
{
use RefreshDatabase;
use LazilyRefreshDatabase;
protected $user;
@@ -19,7 +19,7 @@ class AliasTest extends TestCase
{
parent::setUp();
$this->user = User::factory()->create();
$this->user = User::factory()->create()->fresh();
$this->user->recipients()->save($this->user->defaultRecipient);
}

View File

@@ -6,12 +6,12 @@ use App\Models\Alias;
use App\Models\AliasRecipient;
use App\Models\Recipient;
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Foundation\Testing\LazilyRefreshDatabase;
use Tests\TestCase;
class UserTest extends TestCase
{
use RefreshDatabase;
use LazilyRefreshDatabase;
protected $user;
@@ -19,7 +19,7 @@ class UserTest extends TestCase
{
parent::setUp();
$this->user = User::factory()->create();
$this->user = User::factory()->create()->fresh();
$this->user->recipients()->save($this->user->defaultRecipient);
$this->user->usernames()->save($this->user->defaultUsername);
$this->user->defaultUsername->username = 'johndoe';

View File

@@ -1,4 +1,4 @@
alias,description,recipient_ids
abc@example.com,My awesome alias!,477b1a7f-280e-4153-9178-76862b2e0404
alias,description,recipients
abc@example.com,My awesome alias!,recipient1@example.com
no-description@example.com,,
xyz@example.com,"Alias with 2 recipients, use a comma separated list of IDs.","477b1a7f-280e-4153-9178-76862b2e0404,9040f2c0-e330-4650-9e7c-96d51b6c5d17"
xyz@example.com,"Alias with 2 recipients, use a comma separated list of recipient emails.","recipient1@example.com,recipient2@example.com"
1 alias description recipient_ids recipients
2 abc@example.com My awesome alias! 477b1a7f-280e-4153-9178-76862b2e0404 recipient1@example.com
3 no-description@example.com
4 xyz@example.com Alias with 2 recipients, use a comma separated list of IDs. Alias with 2 recipients, use a comma separated list of recipient emails. 477b1a7f-280e-4153-9178-76862b2e0404,9040f2c0-e330-4650-9e7c-96d51b6c5d17 recipient1@example.com,recipient2@example.com