Compare commits
9 commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
fca7d88894 | ||
![]() |
67c824ce1c | ||
![]() |
abab703ecf | ||
![]() |
e7045cc7f7 | ||
![]() |
c832b1b556 | ||
![]() |
aa0addeacd | ||
![]() |
adddee0ba1 | ||
![]() |
5d0270d880 | ||
![]() |
16933763d0 |
32 changed files with 2008 additions and 1678 deletions
|
@ -409,7 +409,7 @@ If you get close to your limit (over 80%) you'll be sent an email letting you kn
|
|||
|
||||
## Can I login using an additional username?
|
||||
|
||||
Yes, you can login with any of your usernames. You can add 1 additional username as a Lite user and up to 10 additional usernames as a Pro user for totals of 2 and 11 respectively (including the one you signed up with).
|
||||
Yes, you can login with any of your usernames. You can add 5 additional username as a Lite user and up to 20 additional usernames as a Pro user for totals of 6 and 21 respectively (including the one you signed up with).
|
||||
|
||||
## I'm not receiving any emails, what's wrong?
|
||||
|
||||
|
|
|
@ -260,7 +260,6 @@ smtpd_recipient_restrictions =
|
|||
reject_rhsbl_reverse_client dbl.spamhaus.org,
|
||||
reject_rhsbl_sender dbl.spamhaus.org,
|
||||
reject_rbl_client zen.spamhaus.org
|
||||
reject_rbl_client dul.dnsbl.sorbs.net
|
||||
|
||||
# Block clients that speak too early.
|
||||
smtpd_data_restrictions = reject_unauth_pipelining
|
||||
|
@ -1359,11 +1358,8 @@ npm run production
|
|||
# Run any database migrations
|
||||
php artisan migrate --force
|
||||
|
||||
# Clear cache
|
||||
php artisan config:cache
|
||||
php artisan view:cache
|
||||
php artisan route:cache
|
||||
php artisan event:cache
|
||||
# Cache config, events, routes and views
|
||||
php artisan optimize
|
||||
|
||||
# Restart queue workers to reflect changes
|
||||
php artisan queue:restart
|
||||
|
|
|
@ -3,9 +3,10 @@
|
|||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\GeneralAliasBulkRequest;
|
||||
use App\Http\Requests\RecipientsAliasBulkRequest;
|
||||
use App\Http\Resources\AliasResource;
|
||||
use App\Rules\VerifiedRecipientId;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Ramsey\Uuid\Uuid;
|
||||
|
||||
|
@ -16,13 +17,8 @@ class AliasBulkController extends Controller
|
|||
$this->middleware('throttle:12,1');
|
||||
}
|
||||
|
||||
public function get(Request $request)
|
||||
public function get(GeneralAliasBulkRequest $request)
|
||||
{
|
||||
$request->validate([
|
||||
'ids' => 'required|array|max:25|min:1',
|
||||
'ids.*' => 'required|uuid|distinct',
|
||||
]);
|
||||
|
||||
$aliases = user()->aliases()->withTrashed()
|
||||
->whereIn('id', $request->ids)
|
||||
->get();
|
||||
|
@ -35,13 +31,8 @@ class AliasBulkController extends Controller
|
|||
return AliasResource::collection($aliases);
|
||||
}
|
||||
|
||||
public function activate(Request $request)
|
||||
public function activate(GeneralAliasBulkRequest $request)
|
||||
{
|
||||
$request->validate([
|
||||
'ids' => 'required|array|max:25|min:1',
|
||||
'ids.*' => 'required|uuid|distinct',
|
||||
]);
|
||||
|
||||
$aliasesWithTrashed = user()->aliases()->withTrashed()
|
||||
->select(['id', 'user_id', 'active', 'deleted_at'])
|
||||
->where('active', false)
|
||||
|
@ -75,13 +66,8 @@ class AliasBulkController extends Controller
|
|||
], 200);
|
||||
}
|
||||
|
||||
public function deactivate(Request $request)
|
||||
public function deactivate(GeneralAliasBulkRequest $request)
|
||||
{
|
||||
$request->validate([
|
||||
'ids' => 'required|array|max:25|min:1',
|
||||
'ids.*' => 'required|uuid|distinct',
|
||||
]);
|
||||
|
||||
$aliasIds = user()->aliases()
|
||||
->where('active', true)
|
||||
->whereIn('id', $request->ids)
|
||||
|
@ -100,13 +86,8 @@ class AliasBulkController extends Controller
|
|||
], 200);
|
||||
}
|
||||
|
||||
public function delete(Request $request)
|
||||
public function delete(GeneralAliasBulkRequest $request)
|
||||
{
|
||||
$request->validate([
|
||||
'ids' => 'required|array|max:25|min:1',
|
||||
'ids.*' => 'required|uuid|distinct',
|
||||
]);
|
||||
|
||||
$aliasIds = user()->aliases()
|
||||
->whereIn('id', $request->ids)
|
||||
->pluck('id');
|
||||
|
@ -129,13 +110,8 @@ class AliasBulkController extends Controller
|
|||
], 200);
|
||||
}
|
||||
|
||||
public function forget(Request $request)
|
||||
public function forget(GeneralAliasBulkRequest $request)
|
||||
{
|
||||
$request->validate([
|
||||
'ids' => 'required|array|max:25|min:1',
|
||||
'ids.*' => 'required|uuid|distinct',
|
||||
]);
|
||||
|
||||
$aliasIds = user()->aliases()->withTrashed()
|
||||
->whereIn('id', $request->ids)
|
||||
->pluck('id');
|
||||
|
@ -183,13 +159,8 @@ class AliasBulkController extends Controller
|
|||
], 200);
|
||||
}
|
||||
|
||||
public function restore(Request $request)
|
||||
public function restore(GeneralAliasBulkRequest $request)
|
||||
{
|
||||
$request->validate([
|
||||
'ids' => 'required|array|max:25|min:1',
|
||||
'ids.*' => 'required|uuid|distinct',
|
||||
]);
|
||||
|
||||
$aliasIds = user()->aliases()->onlyTrashed()
|
||||
->whereIn('id', $request->ids)
|
||||
->pluck('id');
|
||||
|
@ -209,7 +180,7 @@ class AliasBulkController extends Controller
|
|||
], 200);
|
||||
}
|
||||
|
||||
public function recipients(Request $request)
|
||||
public function recipients(RecipientsAliasBulkRequest $request)
|
||||
{
|
||||
$request->validate([
|
||||
'ids' => 'required|array|max:25|min:1',
|
||||
|
|
|
@ -29,7 +29,30 @@ class AliasController extends Controller
|
|||
->when($request->input('sort'), function ($query, $sort) {
|
||||
$direction = strpos($sort, '-') === 0 ? 'desc' : 'asc';
|
||||
$sort = ltrim($sort, '-');
|
||||
$compareOperator = $direction === 'desc' ? '>' : '<';
|
||||
|
||||
// If sort is last_used then order by all and return
|
||||
if ($sort === 'last_used') {
|
||||
return $query
|
||||
->orderByRaw(
|
||||
"CASE
|
||||
WHEN (last_forwarded {$compareOperator} last_replied
|
||||
OR (last_forwarded IS NOT NULL
|
||||
AND last_replied IS NULL))
|
||||
AND (last_forwarded {$compareOperator} last_sent
|
||||
OR (last_forwarded IS NOT NULL
|
||||
AND last_sent IS NULL))
|
||||
THEN last_forwarded
|
||||
WHEN last_replied {$compareOperator} last_sent
|
||||
OR (last_replied IS NOT NULL
|
||||
AND last_sent IS NULL)
|
||||
THEN last_replied
|
||||
ELSE last_sent
|
||||
END {$direction}"
|
||||
)->orderBy('created_at', 'desc');
|
||||
}
|
||||
|
||||
// If sort is created at then simply return as no need for secondary sorting below
|
||||
if ($sort === 'created_at') {
|
||||
return $query->orderBy($sort, $direction);
|
||||
}
|
||||
|
|
|
@ -6,9 +6,12 @@ use App\Facades\Webauthn;
|
|||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\ApiAuthenticationLoginRequest;
|
||||
use App\Http\Requests\ApiAuthenticationMfaRequest;
|
||||
use App\Http\Requests\DestroyAccountRequest;
|
||||
use App\Jobs\DeleteAccount;
|
||||
use App\Models\User;
|
||||
use App\Models\Username;
|
||||
use Illuminate\Contracts\Encryption\DecryptException;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Carbon;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\Support\Facades\Crypt;
|
||||
|
@ -20,34 +23,50 @@ class ApiAuthenticationController extends Controller
|
|||
public function __construct()
|
||||
{
|
||||
$this->middleware('throttle:3,1');
|
||||
|
||||
$this->middleware(['auth:sanctum', 'verified'])->only(['logout', 'destroy']);
|
||||
}
|
||||
|
||||
public function login(ApiAuthenticationLoginRequest $request)
|
||||
{
|
||||
$user = Username::firstWhere('username', $request->username)?->user;
|
||||
$user = Username::select(['user_id', 'username'])->firstWhere('username', $request->username)?->user;
|
||||
|
||||
if (! $user || ! Hash::check($request->password, $user->password)) {
|
||||
return response()->json([
|
||||
'error' => 'The provided credentials are incorrect',
|
||||
'message' => 'The provided credentials are incorrect.',
|
||||
], 401);
|
||||
}
|
||||
|
||||
if (! $user->hasVerifiedDefaultRecipient()) {
|
||||
return response()->json([
|
||||
'message' => 'Your email address is not verified.',
|
||||
], 401);
|
||||
}
|
||||
|
||||
// Check if user has 2FA enabled, if needs OTP then return mfa_key
|
||||
if ($user->two_factor_enabled) {
|
||||
return response()->json([
|
||||
'message' => "OTP required, please make a request to /api/auth/mfa with the 'mfa_key', 'otp' and 'device_name' including a 'X-CSRF-TOKEN' header",
|
||||
'message' => "OTP required, please make a request to /api/auth/mfa with the 'mfa_key', 'otp' and 'device_name' including a 'X-CSRF-TOKEN' header.",
|
||||
'mfa_key' => Crypt::encryptString($user->id.'|'.config('anonaddy.secret').'|'.Carbon::now()->addMinutes(5)->getTimestamp()),
|
||||
'csrf_token' => csrf_token(),
|
||||
], 422);
|
||||
} elseif (Webauthn::enabled($user)) {
|
||||
// If WebAuthn is enabled then return currently unsupported message
|
||||
return response()->json([
|
||||
'error' => 'Security key authentication is not currently supported from the extension or mobile apps, please use an API key to login instead',
|
||||
'message' => 'Security key authentication is not currently supported from the extension or mobile apps, please use an API key to login instead.',
|
||||
], 403);
|
||||
}
|
||||
|
||||
// day, week, month, year or null
|
||||
if ($request->expiration) {
|
||||
$method = 'add'.ucfirst($request->expiration);
|
||||
$expiration = now()->{$method}();
|
||||
} else {
|
||||
$expiration = null;
|
||||
}
|
||||
|
||||
// Token expires after 3 months, user must re-login
|
||||
$newToken = $user->createToken($request->device_name, ['*'], now()->addMonths(3));
|
||||
$newToken = $user->createToken($request->device_name, ['*'], $expiration);
|
||||
$token = $newToken->accessToken;
|
||||
|
||||
// If the user doesn't use 2FA then return the new API key
|
||||
|
@ -65,7 +84,7 @@ class ApiAuthenticationController extends Controller
|
|||
$mfaKey = Crypt::decryptString($request->mfa_key);
|
||||
} catch (DecryptException $e) {
|
||||
return response()->json([
|
||||
'error' => 'Invalid mfa_key',
|
||||
'message' => 'Invalid mfa_key.',
|
||||
], 401);
|
||||
}
|
||||
$parts = explode('|', $mfaKey, 3);
|
||||
|
@ -73,15 +92,17 @@ class ApiAuthenticationController extends Controller
|
|||
$user = User::find($parts[0]);
|
||||
|
||||
if (! $user || $parts[1] !== config('anonaddy.secret')) {
|
||||
|
||||
return response()->json([
|
||||
'error' => 'Invalid mfa_key',
|
||||
'message' => 'Invalid mfa_key.',
|
||||
], 401);
|
||||
}
|
||||
|
||||
// Check if the mfa_key has expired
|
||||
if (Carbon::now()->getTimestamp() > $parts[2]) {
|
||||
|
||||
return response()->json([
|
||||
'error' => 'mfa_key expired, please request a new one at /api/auth/login',
|
||||
'message' => 'mfa_key expired, please request a new one at /api/auth/login.',
|
||||
], 401);
|
||||
}
|
||||
|
||||
|
@ -91,8 +112,9 @@ class ApiAuthenticationController extends Controller
|
|||
$timestamp = $google2fa->verifyKeyNewer($user->two_factor_secret, $request->otp, $lastTimeStamp, config('google2fa.window'));
|
||||
|
||||
if (! $timestamp) {
|
||||
|
||||
return response()->json([
|
||||
'error' => 'The \'One Time Password\' typed was wrong',
|
||||
'message' => 'The \'One Time Password\' typed was wrong.',
|
||||
], 401);
|
||||
}
|
||||
|
||||
|
@ -100,7 +122,15 @@ class ApiAuthenticationController extends Controller
|
|||
Cache::put('2fa_ts:'.$user->id, $timestamp, now()->addMinutes(5));
|
||||
}
|
||||
|
||||
$newToken = $user->createToken($request->device_name, ['*'], now()->addMonths(3));
|
||||
// day, week, month, year or null
|
||||
if ($request->expiration) {
|
||||
$method = 'add'.ucfirst($request->expiration);
|
||||
$expiration = now()->{$method}();
|
||||
} else {
|
||||
$expiration = null;
|
||||
}
|
||||
|
||||
$newToken = $user->createToken($request->device_name, ['*'], $expiration);
|
||||
$token = $newToken->accessToken;
|
||||
|
||||
return response()->json([
|
||||
|
@ -110,4 +140,26 @@ class ApiAuthenticationController extends Controller
|
|||
'expires_at' => $token->expires_at?->toDateTimeString(),
|
||||
]);
|
||||
}
|
||||
|
||||
public function logout(Request $request)
|
||||
{
|
||||
$token = $request->user()?->currentAccessToken();
|
||||
|
||||
if (! $token) {
|
||||
return response()->json([
|
||||
'message', 'API key not found.',
|
||||
], 404);
|
||||
}
|
||||
|
||||
$token->delete();
|
||||
|
||||
return response()->json([], 204);
|
||||
}
|
||||
|
||||
public function destroy(DestroyAccountRequest $request)
|
||||
{
|
||||
DeleteAccount::dispatch($request->user());
|
||||
|
||||
return response()->json([], 204);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -58,6 +58,7 @@ class ShowAliasController extends Controller
|
|||
'last_blocked',
|
||||
'last_replied',
|
||||
'last_sent',
|
||||
'last_used',
|
||||
'active',
|
||||
'created_at',
|
||||
'updated_at',
|
||||
|
@ -73,6 +74,7 @@ class ShowAliasController extends Controller
|
|||
'-last_blocked',
|
||||
'-last_replied',
|
||||
'-last_sent',
|
||||
'-last_used',
|
||||
'-active',
|
||||
'-created_at',
|
||||
'-updated_at',
|
||||
|
@ -95,10 +97,12 @@ class ShowAliasController extends Controller
|
|||
|
||||
$sort = $request->session()->get('aliasesSort', 'created_at');
|
||||
$direction = $request->session()->get('aliasesSortDirection', 'desc');
|
||||
$compareOperator = $request->session()->get('aliasesSortCompareOperator', '>');
|
||||
|
||||
if ($request->has('sort')) {
|
||||
$direction = strpos($request->input('sort'), '-') === 0 ? 'desc' : 'asc';
|
||||
$sort = ltrim($request->input('sort'), '-');
|
||||
$compareOperator = $direction === 'desc' ? '>' : '<';
|
||||
|
||||
$request->session()->put('aliasesSort', $sort);
|
||||
$request->session()->put('aliasesSortDirection', $direction);
|
||||
|
@ -115,11 +119,32 @@ class ShowAliasController extends Controller
|
|||
->when($request->input('username'), function ($query, $id) {
|
||||
return $query->belongsToAliasable('App\Models\Username', $id);
|
||||
})
|
||||
->when($sort !== 'created_at' || $direction !== 'desc', function ($query) use ($sort, $direction) {
|
||||
->when($sort !== 'created_at' || $direction !== 'desc', function ($query) use ($sort, $direction, $compareOperator) {
|
||||
if ($sort === 'created_at') {
|
||||
return $query->orderBy($sort, $direction);
|
||||
}
|
||||
|
||||
// If sort is last_used then order by all and return
|
||||
if ($sort === 'last_used') {
|
||||
return $query
|
||||
->orderByRaw(
|
||||
"CASE
|
||||
WHEN (last_forwarded {$compareOperator} last_replied
|
||||
OR (last_forwarded IS NOT NULL
|
||||
AND last_replied IS NULL))
|
||||
AND (last_forwarded {$compareOperator} last_sent
|
||||
OR (last_forwarded IS NOT NULL
|
||||
AND last_sent IS NULL))
|
||||
THEN last_forwarded
|
||||
WHEN last_replied {$compareOperator} last_sent
|
||||
OR (last_replied IS NOT NULL
|
||||
AND last_sent IS NULL)
|
||||
THEN last_replied
|
||||
ELSE last_sent
|
||||
END {$direction}"
|
||||
)->orderBy('created_at', 'desc');
|
||||
}
|
||||
|
||||
// Secondary order by latest first
|
||||
return $query
|
||||
->orderBy($sort, $direction)
|
||||
|
|
|
@ -24,9 +24,27 @@ class ApiAuthenticationLoginRequest extends FormRequest
|
|||
public function rules()
|
||||
{
|
||||
return [
|
||||
'username' => 'required|string',
|
||||
'password' => 'required|string',
|
||||
'device_name' => 'required|string|max:50',
|
||||
'username' => [
|
||||
'required',
|
||||
'regex:/^[a-zA-Z0-9]*$/',
|
||||
'min:1',
|
||||
'max:20',
|
||||
],
|
||||
'password' => [
|
||||
'required',
|
||||
'string',
|
||||
],
|
||||
'device_name' => [
|
||||
'required',
|
||||
'string',
|
||||
'max:50',
|
||||
],
|
||||
'expiration' => [
|
||||
'nullable',
|
||||
'string',
|
||||
'max:5',
|
||||
'in:day,week,month,year',
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
40
app/Http/Requests/GeneralAliasBulkRequest.php
Normal file
40
app/Http/Requests/GeneralAliasBulkRequest.php
Normal file
|
@ -0,0 +1,40 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Requests;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
use Illuminate\Support\Arr;
|
||||
|
||||
class GeneralAliasBulkRequest extends FormRequest
|
||||
{
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*/
|
||||
public function authorize(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare the data for validation.
|
||||
*/
|
||||
protected function prepareForValidation(): void
|
||||
{
|
||||
$this->merge([
|
||||
'ids' => Arr::whereNotNull($this->ids ?? []),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'ids' => 'required|array|max:25|min:1',
|
||||
'ids.*' => 'required|uuid|distinct',
|
||||
];
|
||||
}
|
||||
}
|
|
@ -75,6 +75,7 @@ class IndexAliasRequest extends FormRequest
|
|||
'last_blocked',
|
||||
'last_replied',
|
||||
'last_sent',
|
||||
'last_used',
|
||||
'active',
|
||||
'created_at',
|
||||
'updated_at',
|
||||
|
@ -90,6 +91,7 @@ class IndexAliasRequest extends FormRequest
|
|||
'-last_blocked',
|
||||
'-last_replied',
|
||||
'-last_sent',
|
||||
'-last_used',
|
||||
'-active',
|
||||
'-created_at',
|
||||
'-updated_at',
|
||||
|
|
47
app/Http/Requests/RecipientsAliasBulkRequest.php
Normal file
47
app/Http/Requests/RecipientsAliasBulkRequest.php
Normal file
|
@ -0,0 +1,47 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Requests;
|
||||
|
||||
use App\Rules\VerifiedRecipientId;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
use Illuminate\Support\Arr;
|
||||
|
||||
class RecipientsAliasBulkRequest extends FormRequest
|
||||
{
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*/
|
||||
public function authorize(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare the data for validation.
|
||||
*/
|
||||
protected function prepareForValidation(): void
|
||||
{
|
||||
$this->merge([
|
||||
'ids' => Arr::whereNotNull($this->ids ?? []),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'ids' => 'required|array|max:25|min:1',
|
||||
'ids.*' => 'required|uuid|distinct',
|
||||
'recipient_ids' => [
|
||||
'array',
|
||||
'max:10',
|
||||
new VerifiedRecipientId,
|
||||
],
|
||||
'recipient_ids.*' => 'required|uuid|distinct',
|
||||
];
|
||||
}
|
||||
}
|
|
@ -30,7 +30,7 @@ class UserResource extends JsonResource
|
|||
'banner_location' => $this->banner_location,
|
||||
'bandwidth' => $this->bandwidth,
|
||||
'bandwidth_limit' => $this->getBandwidthLimit(),
|
||||
'username_count' => $this->username_count,
|
||||
'username_count' => $this->usernames()->count(),
|
||||
'username_limit' => config('anonaddy.additional_username_limit'),
|
||||
'default_username_id' => $this->default_username_id,
|
||||
'default_recipient_id' => $this->default_recipient_id,
|
||||
|
|
|
@ -41,7 +41,7 @@ class TokenExpiringSoon extends Mailable implements ShouldBeEncrypted, ShouldQue
|
|||
public function build()
|
||||
{
|
||||
return $this
|
||||
->subject('Your addy.io API key expires soon')
|
||||
->subject('Your '.config('app.name').' API key expires soon')
|
||||
->markdown('mail.token_expiring_soon', [
|
||||
'user' => $this->user,
|
||||
'userId' => $this->user->id,
|
||||
|
|
|
@ -430,6 +430,10 @@ class User extends Authenticatable implements MustVerifyEmail
|
|||
|
||||
public function hasVerifiedDefaultRecipient()
|
||||
{
|
||||
if (! isset($this->defaultRecipient->email_verified_at)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return ! is_null($this->defaultRecipient->email_verified_at);
|
||||
}
|
||||
|
||||
|
@ -499,7 +503,7 @@ class User extends Authenticatable implements MustVerifyEmail
|
|||
|
||||
public function hasReachedUsernameLimit()
|
||||
{
|
||||
return $this->username_count >= config('anonaddy.additional_username_limit');
|
||||
return $this->usernames()->count() >= config('anonaddy.additional_username_limit');
|
||||
}
|
||||
|
||||
public function isVerifiedRecipient($email)
|
||||
|
|
|
@ -33,7 +33,7 @@ class UsernameReminder extends Notification implements ShouldBeEncrypted, Should
|
|||
public function toMail($notifiable)
|
||||
{
|
||||
return (new MailMessage)
|
||||
->subject('addy.io Username Reminder')
|
||||
->subject(config('app.name').' Username Reminder')
|
||||
->markdown('mail.username_reminder', [
|
||||
'username' => $notifiable->user->username,
|
||||
'userId' => $notifiable->user_id,
|
||||
|
|
|
@ -18,8 +18,8 @@
|
|||
"laravel/tinker": "^2.7",
|
||||
"laravel/ui": "^4.0",
|
||||
"maatwebsite/excel": "^3.1",
|
||||
"mews/captcha": "^3.0.0",
|
||||
"php-mime-mail-parser/php-mime-mail-parser": "^8.0",
|
||||
"mews/captcha": "3.3.3",
|
||||
"php-mime-mail-parser/php-mime-mail-parser": "^9.0",
|
||||
"pragmarx/google2fa-laravel": "^2.0.0",
|
||||
"ramsey/uuid": "^4.0",
|
||||
"tightenco/ziggy": "^2.0.0"
|
||||
|
|
2009
composer.lock
generated
2009
composer.lock
generated
File diff suppressed because it is too large
Load diff
|
@ -13,7 +13,7 @@ return [
|
|||
|
|
||||
*/
|
||||
|
||||
'name' => env('APP_NAME', 'Laravel'),
|
||||
'name' => env('APP_NAME', 'addy.io'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
901
package-lock.json
generated
901
package-lock.json
generated
File diff suppressed because it is too large
Load diff
275
postfix/composer.lock
generated
275
postfix/composer.lock
generated
|
@ -290,16 +290,16 @@
|
|||
},
|
||||
{
|
||||
"name": "illuminate/collections",
|
||||
"version": "v11.19.0",
|
||||
"version": "v11.34.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/illuminate/collections.git",
|
||||
"reference": "2be24113fe25ef18be33bbd083ad36bf1e751eb5"
|
||||
"reference": "fd2103ddc121449a7926fc34a9d220e5b88183c1"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/illuminate/collections/zipball/2be24113fe25ef18be33bbd083ad36bf1e751eb5",
|
||||
"reference": "2be24113fe25ef18be33bbd083ad36bf1e751eb5",
|
||||
"url": "https://api.github.com/repos/illuminate/collections/zipball/fd2103ddc121449a7926fc34a9d220e5b88183c1",
|
||||
"reference": "fd2103ddc121449a7926fc34a9d220e5b88183c1",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
@ -341,20 +341,20 @@
|
|||
"issues": "https://github.com/laravel/framework/issues",
|
||||
"source": "https://github.com/laravel/framework"
|
||||
},
|
||||
"time": "2024-07-23T14:08:10+00:00"
|
||||
"time": "2024-11-27T14:51:56+00:00"
|
||||
},
|
||||
{
|
||||
"name": "illuminate/conditionable",
|
||||
"version": "v11.19.0",
|
||||
"version": "v11.34.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/illuminate/conditionable.git",
|
||||
"reference": "362dd761b9920367bca1427a902158225e9e3a23"
|
||||
"reference": "911df1bda950a3b799cf80671764e34eede131c6"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/illuminate/conditionable/zipball/362dd761b9920367bca1427a902158225e9e3a23",
|
||||
"reference": "362dd761b9920367bca1427a902158225e9e3a23",
|
||||
"url": "https://api.github.com/repos/illuminate/conditionable/zipball/911df1bda950a3b799cf80671764e34eede131c6",
|
||||
"reference": "911df1bda950a3b799cf80671764e34eede131c6",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
@ -387,20 +387,20 @@
|
|||
"issues": "https://github.com/laravel/framework/issues",
|
||||
"source": "https://github.com/laravel/framework"
|
||||
},
|
||||
"time": "2024-06-28T20:10:30+00:00"
|
||||
"time": "2024-11-21T16:28:56+00:00"
|
||||
},
|
||||
{
|
||||
"name": "illuminate/container",
|
||||
"version": "v11.19.0",
|
||||
"version": "v11.34.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/illuminate/container.git",
|
||||
"reference": "122c62229b209678013c0833793d7cf94d14bbbd"
|
||||
"reference": "b057b0bbb38d7c7524df1ca5c38e7318f4c64d26"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/illuminate/container/zipball/122c62229b209678013c0833793d7cf94d14bbbd",
|
||||
"reference": "122c62229b209678013c0833793d7cf94d14bbbd",
|
||||
"url": "https://api.github.com/repos/illuminate/container/zipball/b057b0bbb38d7c7524df1ca5c38e7318f4c64d26",
|
||||
"reference": "b057b0bbb38d7c7524df1ca5c38e7318f4c64d26",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
@ -438,20 +438,20 @@
|
|||
"issues": "https://github.com/laravel/framework/issues",
|
||||
"source": "https://github.com/laravel/framework"
|
||||
},
|
||||
"time": "2024-07-26T06:12:27+00:00"
|
||||
"time": "2024-11-21T20:07:31+00:00"
|
||||
},
|
||||
{
|
||||
"name": "illuminate/contracts",
|
||||
"version": "v11.19.0",
|
||||
"version": "v11.34.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/illuminate/contracts.git",
|
||||
"reference": "ebe2b8d69b8fb1c07111e3500d464e77dfab3202"
|
||||
"reference": "184317f701ba20ca265e36808ed54b75b115972d"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/illuminate/contracts/zipball/ebe2b8d69b8fb1c07111e3500d464e77dfab3202",
|
||||
"reference": "ebe2b8d69b8fb1c07111e3500d464e77dfab3202",
|
||||
"url": "https://api.github.com/repos/illuminate/contracts/zipball/184317f701ba20ca265e36808ed54b75b115972d",
|
||||
"reference": "184317f701ba20ca265e36808ed54b75b115972d",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
@ -486,20 +486,20 @@
|
|||
"issues": "https://github.com/laravel/framework/issues",
|
||||
"source": "https://github.com/laravel/framework"
|
||||
},
|
||||
"time": "2024-07-29T06:48:51+00:00"
|
||||
"time": "2024-11-25T15:33:38+00:00"
|
||||
},
|
||||
{
|
||||
"name": "illuminate/database",
|
||||
"version": "v11.19.0",
|
||||
"version": "v11.34.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/illuminate/database.git",
|
||||
"reference": "db151e9a3221705cb4149c39dce2c51799708637"
|
||||
"reference": "f08bb9ffa1d829cb6f8bb713e5c9cd3a47636ff2"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/illuminate/database/zipball/db151e9a3221705cb4149c39dce2c51799708637",
|
||||
"reference": "db151e9a3221705cb4149c39dce2c51799708637",
|
||||
"url": "https://api.github.com/repos/illuminate/database/zipball/f08bb9ffa1d829cb6f8bb713e5c9cd3a47636ff2",
|
||||
"reference": "f08bb9ffa1d829cb6f8bb713e5c9cd3a47636ff2",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
@ -514,11 +514,12 @@
|
|||
},
|
||||
"suggest": {
|
||||
"ext-filter": "Required to use the Postgres database driver.",
|
||||
"fakerphp/faker": "Required to use the eloquent factory builder (^1.21).",
|
||||
"fakerphp/faker": "Required to use the eloquent factory builder (^1.24).",
|
||||
"illuminate/console": "Required to use the database commands (^11.0).",
|
||||
"illuminate/events": "Required to use the observers with Eloquent (^11.0).",
|
||||
"illuminate/filesystem": "Required to use the migrations (^11.0).",
|
||||
"illuminate/pagination": "Required to paginate the result set (^11.0).",
|
||||
"laravel/serializable-closure": "Required to handle circular references in model serialization (^1.3).",
|
||||
"symfony/finder": "Required to use Eloquent model factories (^7.0)."
|
||||
},
|
||||
"type": "library",
|
||||
|
@ -554,11 +555,11 @@
|
|||
"issues": "https://github.com/laravel/framework/issues",
|
||||
"source": "https://github.com/laravel/framework"
|
||||
},
|
||||
"time": "2024-07-30T07:00:12+00:00"
|
||||
"time": "2024-11-25T22:44:52+00:00"
|
||||
},
|
||||
{
|
||||
"name": "illuminate/macroable",
|
||||
"version": "v11.19.0",
|
||||
"version": "v11.34.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/illuminate/macroable.git",
|
||||
|
@ -604,16 +605,16 @@
|
|||
},
|
||||
{
|
||||
"name": "illuminate/support",
|
||||
"version": "v11.19.0",
|
||||
"version": "v11.34.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/illuminate/support.git",
|
||||
"reference": "f3c19ee61d875dca1045df2d90000ae97f1645f9"
|
||||
"reference": "2b718a86571baed50fdc5d5748a846c2e58e07eb"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/illuminate/support/zipball/f3c19ee61d875dca1045df2d90000ae97f1645f9",
|
||||
"reference": "f3c19ee61d875dca1045df2d90000ae97f1645f9",
|
||||
"url": "https://api.github.com/repos/illuminate/support/zipball/2b718a86571baed50fdc5d5748a846c2e58e07eb",
|
||||
"reference": "2b718a86571baed50fdc5d5748a846c2e58e07eb",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
@ -625,9 +626,9 @@
|
|||
"illuminate/conditionable": "^11.0",
|
||||
"illuminate/contracts": "^11.0",
|
||||
"illuminate/macroable": "^11.0",
|
||||
"nesbot/carbon": "^2.72.2|^3.0",
|
||||
"nesbot/carbon": "^2.72.2|^3.4",
|
||||
"php": "^8.2",
|
||||
"voku/portable-ascii": "^2.0"
|
||||
"voku/portable-ascii": "^2.0.2"
|
||||
},
|
||||
"conflict": {
|
||||
"tightenco/collect": "<5.5.33"
|
||||
|
@ -637,12 +638,13 @@
|
|||
},
|
||||
"suggest": {
|
||||
"illuminate/filesystem": "Required to use the composer class (^11.0).",
|
||||
"laravel/serializable-closure": "Required to use the once function (^1.3).",
|
||||
"league/commonmark": "Required to use Str::markdown() and Stringable::markdown() (^2.0.2).",
|
||||
"ramsey/uuid": "Required to use Str::uuid() (^4.7).",
|
||||
"symfony/process": "Required to use the composer class (^7.0).",
|
||||
"symfony/uid": "Required to use Str::ulid() (^7.0).",
|
||||
"symfony/var-dumper": "Required to use the dd function (^7.0).",
|
||||
"vlucas/phpdotenv": "Required to use the Env class and env helper (^5.4.1)."
|
||||
"vlucas/phpdotenv": "Required to use the Env class and env helper (^5.6.1)."
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
|
@ -652,6 +654,7 @@
|
|||
},
|
||||
"autoload": {
|
||||
"files": [
|
||||
"functions.php",
|
||||
"helpers.php"
|
||||
],
|
||||
"psr-4": {
|
||||
|
@ -674,24 +677,24 @@
|
|||
"issues": "https://github.com/laravel/framework/issues",
|
||||
"source": "https://github.com/laravel/framework"
|
||||
},
|
||||
"time": "2024-07-30T06:57:25+00:00"
|
||||
"time": "2024-11-27T14:58:17+00:00"
|
||||
},
|
||||
{
|
||||
"name": "nesbot/carbon",
|
||||
"version": "3.7.0",
|
||||
"version": "3.8.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/briannesbitt/Carbon.git",
|
||||
"reference": "cb4374784c87d0a0294e8513a52eb63c0aff3139"
|
||||
"reference": "e1268cdbc486d97ce23fef2c666dc3c6b6de9947"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/cb4374784c87d0a0294e8513a52eb63c0aff3139",
|
||||
"reference": "cb4374784c87d0a0294e8513a52eb63c0aff3139",
|
||||
"url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/e1268cdbc486d97ce23fef2c666dc3c6b6de9947",
|
||||
"reference": "e1268cdbc486d97ce23fef2c666dc3c6b6de9947",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"carbonphp/carbon-doctrine-types": "*",
|
||||
"carbonphp/carbon-doctrine-types": "<100.0",
|
||||
"ext-json": "*",
|
||||
"php": "^8.1",
|
||||
"psr/clock": "^1.0",
|
||||
|
@ -780,7 +783,7 @@
|
|||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2024-07-16T22:29:20+00:00"
|
||||
"time": "2024-11-07T17:46:48+00:00"
|
||||
},
|
||||
{
|
||||
"name": "paragonie/constant_time_encoding",
|
||||
|
@ -1078,16 +1081,16 @@
|
|||
},
|
||||
{
|
||||
"name": "symfony/clock",
|
||||
"version": "v7.1.1",
|
||||
"version": "v7.2.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/clock.git",
|
||||
"reference": "3dfc8b084853586de51dd1441c6242c76a28cbe7"
|
||||
"reference": "b81435fbd6648ea425d1ee96a2d8e68f4ceacd24"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/clock/zipball/3dfc8b084853586de51dd1441c6242c76a28cbe7",
|
||||
"reference": "3dfc8b084853586de51dd1441c6242c76a28cbe7",
|
||||
"url": "https://api.github.com/repos/symfony/clock/zipball/b81435fbd6648ea425d1ee96a2d8e68f4ceacd24",
|
||||
"reference": "b81435fbd6648ea425d1ee96a2d8e68f4ceacd24",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
@ -1132,7 +1135,7 @@
|
|||
"time"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/clock/tree/v7.1.1"
|
||||
"source": "https://github.com/symfony/clock/tree/v7.2.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
@ -1148,24 +1151,91 @@
|
|||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2024-05-31T14:57:53+00:00"
|
||||
"time": "2024-09-25T14:21:43+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/polyfill-ctype",
|
||||
"version": "v1.30.0",
|
||||
"name": "symfony/deprecation-contracts",
|
||||
"version": "v3.5.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/polyfill-ctype.git",
|
||||
"reference": "0424dff1c58f028c451efff2045f5d92410bd540"
|
||||
"url": "https://github.com/symfony/deprecation-contracts.git",
|
||||
"reference": "74c71c939a79f7d5bf3c1ce9f5ea37ba0114c6f6"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/0424dff1c58f028c451efff2045f5d92410bd540",
|
||||
"reference": "0424dff1c58f028c451efff2045f5d92410bd540",
|
||||
"url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/74c71c939a79f7d5bf3c1ce9f5ea37ba0114c6f6",
|
||||
"reference": "74c71c939a79f7d5bf3c1ce9f5ea37ba0114c6f6",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.1"
|
||||
"php": ">=8.1"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-main": "3.5-dev"
|
||||
},
|
||||
"thanks": {
|
||||
"name": "symfony/contracts",
|
||||
"url": "https://github.com/symfony/contracts"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"files": [
|
||||
"function.php"
|
||||
]
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Nicolas Grekas",
|
||||
"email": "p@tchwork.com"
|
||||
},
|
||||
{
|
||||
"name": "Symfony Community",
|
||||
"homepage": "https://symfony.com/contributors"
|
||||
}
|
||||
],
|
||||
"description": "A generic function and convention to trigger deprecation notices",
|
||||
"homepage": "https://symfony.com",
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/deprecation-contracts/tree/v3.5.1"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://symfony.com/sponsor",
|
||||
"type": "custom"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/fabpot",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2024-09-25T14:20:29+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/polyfill-ctype",
|
||||
"version": "v1.31.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/polyfill-ctype.git",
|
||||
"reference": "a3cc8b044a6ea513310cbd48ef7333b384945638"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/a3cc8b044a6ea513310cbd48ef7333b384945638",
|
||||
"reference": "a3cc8b044a6ea513310cbd48ef7333b384945638",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.2"
|
||||
},
|
||||
"provide": {
|
||||
"ext-ctype": "*"
|
||||
|
@ -1176,8 +1246,8 @@
|
|||
"type": "library",
|
||||
"extra": {
|
||||
"thanks": {
|
||||
"name": "symfony/polyfill",
|
||||
"url": "https://github.com/symfony/polyfill"
|
||||
"url": "https://github.com/symfony/polyfill",
|
||||
"name": "symfony/polyfill"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
|
@ -1211,7 +1281,7 @@
|
|||
"portable"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/polyfill-ctype/tree/v1.30.0"
|
||||
"source": "https://github.com/symfony/polyfill-ctype/tree/v1.31.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
@ -1227,24 +1297,24 @@
|
|||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2024-05-31T15:07:36+00:00"
|
||||
"time": "2024-09-09T11:45:10+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/polyfill-mbstring",
|
||||
"version": "v1.30.0",
|
||||
"version": "v1.31.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/polyfill-mbstring.git",
|
||||
"reference": "fd22ab50000ef01661e2a31d850ebaa297f8e03c"
|
||||
"reference": "85181ba99b2345b0ef10ce42ecac37612d9fd341"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/fd22ab50000ef01661e2a31d850ebaa297f8e03c",
|
||||
"reference": "fd22ab50000ef01661e2a31d850ebaa297f8e03c",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/85181ba99b2345b0ef10ce42ecac37612d9fd341",
|
||||
"reference": "85181ba99b2345b0ef10ce42ecac37612d9fd341",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.1"
|
||||
"php": ">=7.2"
|
||||
},
|
||||
"provide": {
|
||||
"ext-mbstring": "*"
|
||||
|
@ -1291,7 +1361,7 @@
|
|||
"shim"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/polyfill-mbstring/tree/v1.30.0"
|
||||
"source": "https://github.com/symfony/polyfill-mbstring/tree/v1.31.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
@ -1307,30 +1377,30 @@
|
|||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2024-06-19T12:30:46+00:00"
|
||||
"time": "2024-09-09T11:45:10+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/polyfill-php80",
|
||||
"version": "v1.30.0",
|
||||
"version": "v1.31.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/polyfill-php80.git",
|
||||
"reference": "77fa7995ac1b21ab60769b7323d600a991a90433"
|
||||
"reference": "60328e362d4c2c802a54fcbf04f9d3fb892b4cf8"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/77fa7995ac1b21ab60769b7323d600a991a90433",
|
||||
"reference": "77fa7995ac1b21ab60769b7323d600a991a90433",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/60328e362d4c2c802a54fcbf04f9d3fb892b4cf8",
|
||||
"reference": "60328e362d4c2c802a54fcbf04f9d3fb892b4cf8",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.1"
|
||||
"php": ">=7.2"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"thanks": {
|
||||
"name": "symfony/polyfill",
|
||||
"url": "https://github.com/symfony/polyfill"
|
||||
"url": "https://github.com/symfony/polyfill",
|
||||
"name": "symfony/polyfill"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
|
@ -1371,7 +1441,7 @@
|
|||
"shim"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/polyfill-php80/tree/v1.30.0"
|
||||
"source": "https://github.com/symfony/polyfill-php80/tree/v1.31.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
@ -1387,30 +1457,30 @@
|
|||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2024-05-31T15:07:36+00:00"
|
||||
"time": "2024-09-09T11:45:10+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/polyfill-php83",
|
||||
"version": "v1.30.0",
|
||||
"version": "v1.31.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/polyfill-php83.git",
|
||||
"reference": "dbdcdf1a4dcc2743591f1079d0c35ab1e2dcbbc9"
|
||||
"reference": "2fb86d65e2d424369ad2905e83b236a8805ba491"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-php83/zipball/dbdcdf1a4dcc2743591f1079d0c35ab1e2dcbbc9",
|
||||
"reference": "dbdcdf1a4dcc2743591f1079d0c35ab1e2dcbbc9",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-php83/zipball/2fb86d65e2d424369ad2905e83b236a8805ba491",
|
||||
"reference": "2fb86d65e2d424369ad2905e83b236a8805ba491",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.1"
|
||||
"php": ">=7.2"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"thanks": {
|
||||
"name": "symfony/polyfill",
|
||||
"url": "https://github.com/symfony/polyfill"
|
||||
"url": "https://github.com/symfony/polyfill",
|
||||
"name": "symfony/polyfill"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
|
@ -1447,7 +1517,7 @@
|
|||
"shim"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/polyfill-php83/tree/v1.30.0"
|
||||
"source": "https://github.com/symfony/polyfill-php83/tree/v1.31.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
@ -1463,24 +1533,25 @@
|
|||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2024-06-19T12:35:24+00:00"
|
||||
"time": "2024-09-09T11:45:10+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/translation",
|
||||
"version": "v7.1.3",
|
||||
"version": "v7.2.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/translation.git",
|
||||
"reference": "8d5e50c813ba2859a6dfc99a0765c550507934a1"
|
||||
"reference": "dc89e16b44048ceecc879054e5b7f38326ab6cc5"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/translation/zipball/8d5e50c813ba2859a6dfc99a0765c550507934a1",
|
||||
"reference": "8d5e50c813ba2859a6dfc99a0765c550507934a1",
|
||||
"url": "https://api.github.com/repos/symfony/translation/zipball/dc89e16b44048ceecc879054e5b7f38326ab6cc5",
|
||||
"reference": "dc89e16b44048ceecc879054e5b7f38326ab6cc5",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=8.2",
|
||||
"symfony/deprecation-contracts": "^2.5|^3",
|
||||
"symfony/polyfill-mbstring": "~1.0",
|
||||
"symfony/translation-contracts": "^2.5|^3.0"
|
||||
},
|
||||
|
@ -1541,7 +1612,7 @@
|
|||
"description": "Provides tools to internationalize your application",
|
||||
"homepage": "https://symfony.com",
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/translation/tree/v7.1.3"
|
||||
"source": "https://github.com/symfony/translation/tree/v7.2.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
@ -1557,20 +1628,20 @@
|
|||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2024-07-26T12:41:01+00:00"
|
||||
"time": "2024-11-12T20:47:56+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/translation-contracts",
|
||||
"version": "v3.5.0",
|
||||
"version": "v3.5.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/translation-contracts.git",
|
||||
"reference": "b9d2189887bb6b2e0367a9fc7136c5239ab9b05a"
|
||||
"reference": "4667ff3bd513750603a09c8dedbea942487fb07c"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/translation-contracts/zipball/b9d2189887bb6b2e0367a9fc7136c5239ab9b05a",
|
||||
"reference": "b9d2189887bb6b2e0367a9fc7136c5239ab9b05a",
|
||||
"url": "https://api.github.com/repos/symfony/translation-contracts/zipball/4667ff3bd513750603a09c8dedbea942487fb07c",
|
||||
"reference": "4667ff3bd513750603a09c8dedbea942487fb07c",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
@ -1619,7 +1690,7 @@
|
|||
"standards"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/translation-contracts/tree/v3.5.0"
|
||||
"source": "https://github.com/symfony/translation-contracts/tree/v3.5.1"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
@ -1635,7 +1706,7 @@
|
|||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2024-04-18T09:32:20+00:00"
|
||||
"time": "2024-09-25T14:20:29+00:00"
|
||||
},
|
||||
{
|
||||
"name": "vlucas/phpdotenv",
|
||||
|
@ -1723,16 +1794,16 @@
|
|||
},
|
||||
{
|
||||
"name": "voku/portable-ascii",
|
||||
"version": "2.0.1",
|
||||
"version": "2.0.3",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/voku/portable-ascii.git",
|
||||
"reference": "b56450eed252f6801410d810c8e1727224ae0743"
|
||||
"reference": "b1d923f88091c6bf09699efcd7c8a1b1bfd7351d"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/voku/portable-ascii/zipball/b56450eed252f6801410d810c8e1727224ae0743",
|
||||
"reference": "b56450eed252f6801410d810c8e1727224ae0743",
|
||||
"url": "https://api.github.com/repos/voku/portable-ascii/zipball/b1d923f88091c6bf09699efcd7c8a1b1bfd7351d",
|
||||
"reference": "b1d923f88091c6bf09699efcd7c8a1b1bfd7351d",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
@ -1757,7 +1828,7 @@
|
|||
"authors": [
|
||||
{
|
||||
"name": "Lars Moelleken",
|
||||
"homepage": "http://www.moelleken.org/"
|
||||
"homepage": "https://www.moelleken.org/"
|
||||
}
|
||||
],
|
||||
"description": "Portable ASCII library - performance optimized (ascii) string functions for php.",
|
||||
|
@ -1769,7 +1840,7 @@
|
|||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/voku/portable-ascii/issues",
|
||||
"source": "https://github.com/voku/portable-ascii/tree/2.0.1"
|
||||
"source": "https://github.com/voku/portable-ascii/tree/2.0.3"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
@ -1793,7 +1864,7 @@
|
|||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2022-03-08T17:03:00+00:00"
|
||||
"time": "2024-11-21T01:49:47+00:00"
|
||||
}
|
||||
],
|
||||
"packages-dev": [],
|
||||
|
|
|
@ -1162,7 +1162,7 @@
|
|||
</p>
|
||||
<p class="mt-4 text-grey-700">
|
||||
To send from an alias you must send the email from a <b>verified recipient</b> on your
|
||||
addy.io account.
|
||||
account.
|
||||
</p>
|
||||
<label for="send_from_alias" class="block font-medium leading-6 text-grey-600 text-sm my-2">
|
||||
Alias to send from
|
||||
|
@ -1575,6 +1575,10 @@ const sortOptions = [
|
|||
value: 'last_sent',
|
||||
label: 'Last Sent At',
|
||||
},
|
||||
{
|
||||
value: 'last_used',
|
||||
label: 'Last Used At',
|
||||
},
|
||||
{
|
||||
value: 'updated_at',
|
||||
label: 'Updated At',
|
||||
|
|
|
@ -1003,7 +1003,7 @@ const validateNewDomain = e => {
|
|||
|
||||
if (!newDomain.value) {
|
||||
errors.value.newDomain = 'Domain name required'
|
||||
} else if (newDomain.value.length > 50) {
|
||||
} else if (newDomain.value.length > 100) {
|
||||
errors.value.newDomain = 'That domain name is too long'
|
||||
} else if (!validDomain(newDomain.value)) {
|
||||
errors.value.newDomain = 'Please enter a valid domain name'
|
||||
|
|
|
@ -21,13 +21,21 @@
|
|||
class="text-indigo-700"
|
||||
>Firefox</a
|
||||
>
|
||||
or
|
||||
,
|
||||
<a
|
||||
href="https://chrome.google.com/webstore/detail/addyio-anonymous-email-fo/iadbdpnoknmbdeolbapdackdcogdmjpe"
|
||||
target="_blank"
|
||||
rel="nofollow noopener noreferrer"
|
||||
class="text-indigo-700"
|
||||
>Chrome / Brave</a
|
||||
>Chrome, Brave</a
|
||||
>
|
||||
or
|
||||
<a
|
||||
href="https://microsoftedge.microsoft.com/addons/detail/addyio-anonymous-email/ohjlgpcfncgkijjfmabldlgnccmgcehl"
|
||||
target="_blank"
|
||||
rel="nofollow noopener noreferrer"
|
||||
class="text-indigo-700"
|
||||
>Edge</a
|
||||
>
|
||||
to create new aliases. They can also be used with the mobile apps. Simply paste a key
|
||||
you've created into the browser extension or mobile apps to get started. Your API access
|
||||
|
@ -197,7 +205,7 @@
|
|||
<div class="text-center">
|
||||
<img :src="qrCode" class="inline-block" alt="QR Code" />
|
||||
<p class="text-left text-sm text-grey-700">
|
||||
You can scan this QR code to automatically login to the addy.io mobile app by Stjin.
|
||||
You can scan this QR code to automatically login to the addy.io mobile app.
|
||||
</p>
|
||||
</div>
|
||||
<div class="mt-6">
|
||||
|
|
|
@ -721,7 +721,7 @@
|
|||
<div class="flex flex-col sm:flex-row space-y-4 sm:space-y-0 sm:space-x-4">
|
||||
<button
|
||||
@click="disableKey"
|
||||
class="bg-red-500 hover:bg-red-600 text-white font-bold py-3 px-4 rounded focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600 disable:cursor-not-allowed"
|
||||
class="bg-red-500 hover:bg-red-600 text-white font-bold py-3 px-4 rounded focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600 disabled:cursor-not-allowed"
|
||||
:disabled="disableKeyLoading"
|
||||
>
|
||||
Disable
|
||||
|
@ -771,7 +771,7 @@
|
|||
<div class="flex flex-col sm:flex-row space-y-4 sm:space-y-0 sm:space-x-4">
|
||||
<button
|
||||
@click="deleteKey"
|
||||
class="bg-red-500 hover:bg-red-600 text-white font-bold py-3 px-4 rounded focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600 disable:cursor-not-allowed"
|
||||
class="bg-red-500 hover:bg-red-600 text-white font-bold py-3 px-4 rounded focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600 disabled:cursor-not-allowed"
|
||||
:disabled="deleteKeyLoading"
|
||||
>
|
||||
Remove
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
# Domain MX records invalid
|
||||
|
||||
A recent DNS record check on your custom domain **{{ $domain }}** on addy.io showed that your MX records are no longer pointing to the addy.io server. This means that addy.io will not be able to handle your emails for you.
|
||||
A recent DNS record check on your custom domain **{{ $domain }}** on {{ config('app.name') }} showed that your MX records are no longer pointing to the {{ config('app.name') }} server. This means that {{ config('app.name') }} will not be able to handle your emails for you.
|
||||
|
||||
If this MX record change was intentional then you can ignore this email.
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
# Domain Unverified For Sending
|
||||
|
||||
A recent DNS record check on your custom domain **{{ $domain }}** failed on addy.io. This means that your domain had been unverified for sending until the DNS records are added correctly.
|
||||
A recent DNS record check on your custom domain **{{ $domain }}** failed on {{ config('app.name') }}. This means that your domain had been unverified for sending until the DNS records are added correctly.
|
||||
|
||||
The check failed for the following reason:
|
||||
|
||||
|
@ -10,7 +10,7 @@ The check failed for the following reason:
|
|||
|
||||
Please visit the domains page on the site by clicking the button below to resolve the issue.
|
||||
|
||||
Emails for your custom domain will be sent from an addy.io domain in the mean time.
|
||||
Emails for your custom domain will be sent from an {{ config('app.name') }} domain in the mean time.
|
||||
|
||||
@component('mail::button', ['url' => config('app.url').'/domains'])
|
||||
Check Domain
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
# Failed two factor authentication login attempt
|
||||
|
||||
Someone just entered an incorrect OTP while trying to login to your addy.io account. The username (**{{ $username }}**) and password were correct.
|
||||
Someone just entered an incorrect OTP while trying to login to your {{ config('app.name') }} account. The username (**{{ $username }}**) and password were correct.
|
||||
|
||||
The login has been blocked. If this was you, then you can ignore this notification.
|
||||
|
||||
|
|
|
@ -3,9 +3,9 @@
|
|||
# Your API key expires soon
|
||||
|
||||
@if($tokenName)
|
||||
Your API key named "**{{ $tokenName }}**" on your addy.io account expires in **one weeks time**.
|
||||
Your API key named "**{{ $tokenName }}**" on your {{ config('app.name') }} account expires in **one weeks time**.
|
||||
@else
|
||||
One of the API keys on your addy.io account will expire in **one weeks time**.
|
||||
One of the API keys on your {{ config('app.name') }} account will expire in **one weeks time**.
|
||||
@endif
|
||||
|
||||
If you are not using this API key for the browser extensions, mobile apps or to access the API then you do not need to take any action.
|
||||
|
|
|
@ -28,6 +28,7 @@ use App\Http\Controllers\Api\ReorderRuleController;
|
|||
use App\Http\Controllers\Api\RuleController;
|
||||
use App\Http\Controllers\Api\UsernameController;
|
||||
use App\Http\Controllers\Api\UsernameDefaultRecipientController;
|
||||
use App\Http\Controllers\Auth\ApiAuthenticationController;
|
||||
use App\Http\Controllers\RecipientVerificationController;
|
||||
use Illuminate\Support\Facades\Route;
|
||||
|
||||
|
@ -42,6 +43,12 @@ use Illuminate\Support\Facades\Route;
|
|||
|
|
||||
*/
|
||||
|
||||
// API auth routes for mobile apps and browser extension
|
||||
Route::controller(ApiAuthenticationController::class)->prefix('auth')->group(function () {
|
||||
Route::post('/logout', 'logout');
|
||||
Route::post('/delete-account', 'destroy');
|
||||
});
|
||||
|
||||
Route::group([
|
||||
'middleware' => ['auth:sanctum', 'verified'],
|
||||
'prefix' => 'v1',
|
||||
|
|
|
@ -51,9 +51,11 @@ use Illuminate\Support\Facades\Route;
|
|||
|
||||
Auth::routes(['verify' => true, 'register' => config('anonaddy.enable_registration')]);
|
||||
|
||||
// Get API access token
|
||||
Route::post('api/auth/login', [ApiAuthenticationController::class, 'login']);
|
||||
Route::post('api/auth/mfa', [ApiAuthenticationController::class, 'mfa']);
|
||||
// API login route needs CSRF middleware so that it can pass it to api/auth/mfa
|
||||
Route::controller(ApiAuthenticationController::class)->prefix('api/auth')->group(function () {
|
||||
Route::post('/login', 'login');
|
||||
Route::post('/mfa', 'mfa');
|
||||
});
|
||||
|
||||
Route::controller(ForgotUsernameController::class)->group(function () {
|
||||
Route::get('/username/reminder', 'show')->name('username.reminder.show');
|
||||
|
|
|
@ -441,6 +441,7 @@ class AliasesTest extends TestCase
|
|||
'ids' => [
|
||||
$alias->id,
|
||||
$alias2->id,
|
||||
null,
|
||||
],
|
||||
]);
|
||||
|
||||
|
@ -482,6 +483,7 @@ class AliasesTest extends TestCase
|
|||
'ids' => [
|
||||
$alias->id,
|
||||
$alias2->id,
|
||||
null,
|
||||
],
|
||||
]);
|
||||
|
||||
|
@ -544,6 +546,7 @@ class AliasesTest extends TestCase
|
|||
'ids' => [
|
||||
$alias->id,
|
||||
$alias2->id,
|
||||
null,
|
||||
],
|
||||
]);
|
||||
|
||||
|
@ -575,6 +578,7 @@ class AliasesTest extends TestCase
|
|||
'ids' => [
|
||||
$alias->id,
|
||||
$alias2->id,
|
||||
null,
|
||||
],
|
||||
]);
|
||||
|
||||
|
@ -607,6 +611,7 @@ class AliasesTest extends TestCase
|
|||
'ids' => [
|
||||
$alias->id,
|
||||
$alias2->id,
|
||||
null,
|
||||
],
|
||||
]);
|
||||
|
||||
|
@ -637,6 +642,7 @@ class AliasesTest extends TestCase
|
|||
'ids' => [
|
||||
$alias->id,
|
||||
$alias2->id,
|
||||
null,
|
||||
],
|
||||
]);
|
||||
|
||||
|
@ -669,6 +675,7 @@ class AliasesTest extends TestCase
|
|||
'ids' => [
|
||||
$alias->id,
|
||||
$alias2->id,
|
||||
null,
|
||||
],
|
||||
'recipient_ids' => [
|
||||
$recipient->id,
|
||||
|
@ -683,4 +690,41 @@ class AliasesTest extends TestCase
|
|||
'recipient_id' => $recipient->id,
|
||||
]);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function user_cannot_bulk_update_recipients_for_invalid_aliases()
|
||||
{
|
||||
$alias = Alias::factory()->create([
|
||||
'user_id' => $this->user->id,
|
||||
'active' => true,
|
||||
]);
|
||||
|
||||
$alias2 = Alias::factory()->create([
|
||||
'user_id' => '00000000-0000-0000-0000-000000000000',
|
||||
'active' => true,
|
||||
]);
|
||||
|
||||
$recipient = Recipient::factory()->create([
|
||||
'user_id' => $this->user->id,
|
||||
]);
|
||||
|
||||
$response = $this->json('POST', '/api/v1/aliases/recipients/bulk', [
|
||||
'ids' => [
|
||||
$alias->id,
|
||||
$alias2->id,
|
||||
null,
|
||||
],
|
||||
'recipient_ids' => [
|
||||
$recipient->id,
|
||||
],
|
||||
]);
|
||||
|
||||
$response->assertStatus(200);
|
||||
$this->assertCount(1, $response->getData()->ids);
|
||||
$this->assertEquals('recipients updated for 1 alias successfully', $response->getData()->message);
|
||||
$this->assertDatabaseHas('alias_recipients', [
|
||||
'alias_id' => $alias->id,
|
||||
'recipient_id' => $recipient->id,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -88,8 +88,8 @@ class UsernamesTest extends TestCase
|
|||
]);
|
||||
|
||||
$response->assertStatus(403);
|
||||
$this->assertEquals(3, $this->user->username_count);
|
||||
$this->assertCount(4, $this->user->usernames);
|
||||
$this->assertEquals(2, $this->user->username_count);
|
||||
$this->assertCount(3, $this->user->usernames);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
|
|
|
@ -62,7 +62,7 @@ class ApiAuthenticationTest extends TestCase
|
|||
]);
|
||||
|
||||
$response->assertUnauthorized();
|
||||
$response->assertExactJson(['error' => 'The provided credentials are incorrect']);
|
||||
$response->assertExactJson(['message' => 'The provided credentials are incorrect.']);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
|
@ -120,7 +120,7 @@ class ApiAuthenticationTest extends TestCase
|
|||
]);
|
||||
|
||||
$response->assertForbidden();
|
||||
$response->assertExactJson(['error' => 'Security key authentication is not currently supported from the extension or mobile apps, please use an API key to login instead']);
|
||||
$response->assertExactJson(['message' => 'Security key authentication is not currently supported from the extension or mobile apps, please use an API key to login instead.']);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
|
@ -156,7 +156,7 @@ class ApiAuthenticationTest extends TestCase
|
|||
]);
|
||||
|
||||
$response2->assertUnauthorized();
|
||||
$response2->assertExactJson(['error' => 'The \'One Time Password\' typed was wrong']);
|
||||
$response2->assertExactJson(['message' => 'The \'One Time Password\' typed was wrong.']);
|
||||
|
||||
$response3 = $this->withHeaders([
|
||||
'X-CSRF-TOKEN' => $csrfToken,
|
||||
|
@ -169,4 +169,88 @@ class ApiAuthenticationTest extends TestCase
|
|||
$response3->assertSuccessful();
|
||||
$this->assertEquals($this->user->tokens[0]->token, hash('sha256', $response3->json()['api_key']));
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function user_can_logout_via_api()
|
||||
{
|
||||
$this->withoutMiddleware(ThrottleRequestsWithRedis::class);
|
||||
|
||||
$this->user->defaultUsername->username = 'janedoe';
|
||||
$this->user->defaultUsername->save();
|
||||
|
||||
$token = $this->user->createToken('New');
|
||||
|
||||
$response = $this->withHeaders([
|
||||
'Authorization' => 'Bearer '.$token->plainTextToken,
|
||||
])->json('POST', '/api/auth/logout', []);
|
||||
|
||||
$response->assertStatus(204);
|
||||
$this->assertDatabaseMissing('personal_access_tokens', [
|
||||
'tokenable_id' => $this->user->id,
|
||||
]);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function user_can_delete_account_via_api()
|
||||
{
|
||||
$this->withoutMiddleware(ThrottleRequestsWithRedis::class);
|
||||
|
||||
$this->user->defaultUsername->username = 'janedoe';
|
||||
$this->user->defaultUsername->save();
|
||||
|
||||
$token = $this->user->createToken('New');
|
||||
|
||||
$response = $this->withHeaders([
|
||||
'Authorization' => 'Bearer '.$token->plainTextToken,
|
||||
])->json('POST', '/api/auth/delete-account', [
|
||||
'password' => 'mypassword',
|
||||
]);
|
||||
|
||||
$response->assertStatus(204);
|
||||
$this->assertDatabaseMissing('usernames', [
|
||||
'username' => 'janedoe',
|
||||
]);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function user_must_enter_correct_password_to_delete_account()
|
||||
{
|
||||
$this->withoutMiddleware(ThrottleRequestsWithRedis::class);
|
||||
|
||||
$this->user->defaultUsername->username = 'janedoe';
|
||||
$this->user->defaultUsername->save();
|
||||
|
||||
$token = $this->user->createToken('New');
|
||||
|
||||
$response = $this->withHeaders([
|
||||
'Authorization' => 'Bearer '.$token->plainTextToken,
|
||||
])->json('POST', '/api/auth/delete-account', [
|
||||
'password' => 'incorrect',
|
||||
]);
|
||||
|
||||
$response->assertJsonValidationErrorFor('password');
|
||||
$this->assertDatabaseHas('usernames', [
|
||||
'username' => 'janedoe',
|
||||
]);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function user_must_have_valid_api_key_to_delete_account()
|
||||
{
|
||||
$this->withoutMiddleware(ThrottleRequestsWithRedis::class);
|
||||
|
||||
$this->user->defaultUsername->username = 'janedoe';
|
||||
$this->user->defaultUsername->save();
|
||||
|
||||
$response = $this->withHeaders([
|
||||
'Authorization' => 'Bearer invalid-api-key',
|
||||
])->json('POST', '/api/auth/delete-account', [
|
||||
'password' => 'mypassword',
|
||||
]);
|
||||
|
||||
$response->assertUnauthorized();
|
||||
$this->assertDatabaseHas('usernames', [
|
||||
'username' => 'janedoe',
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue