Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
},
"require-dev": {
"friendsofphp/php-cs-fixer": "^3.34.1",
"illuminate/validation": "^10.0",
"nunomaduro/collision": "^7.0",
"pestphp/pest": "^2.33.2",
"phpstan/phpstan": "^1.10.38",
Expand Down
6 changes: 5 additions & 1 deletion scripts/pest-bootstrap.php
Original file line number Diff line number Diff line change
Expand Up @@ -291,7 +291,11 @@ function now($tz = null): Illuminate\Support\Carbon
}

if (!class_exists('Illuminate\Foundation\Http\FormRequest') && class_exists('Illuminate\Http\Request')) {
eval('namespace Illuminate\Foundation\Http; class FormRequest extends \Illuminate\Http\Request { public function authorize() { return true; } public function rules() { return []; } public function responseWithErrors(\Illuminate\Contracts\Validation\Validator $validator) { return $validator; } }');
// Enough of the real FormRequest for a controller to build one from a plain request and
// validate it the way the router does: setContainer(), setRedirector() and
// validateResolved(), which asks the bound validator factory and throws the usual
// ValidationException on failure.
eval('namespace Illuminate\Foundation\Http; class FormRequest extends \Illuminate\Http\Request { protected $container; public function authorize() { return true; } public function rules() { return []; } public function responseWithErrors(\Illuminate\Contracts\Validation\Validator $validator) { return $validator; } public function setContainer($container) { $this->container = $container; return $this; } public function setRedirector($redirector) { return $this; } public function validateResolved(): void { if (method_exists($this, "prepareForValidation")) { $this->prepareForValidation(); } if (!$this->authorize()) { throw new \Illuminate\Auth\Access\AuthorizationException(); } $factory = $this->container ? $this->container->make("validator") : null; if (!$factory) { return; } $validator = $factory->make($this->all(), $this->rules(), method_exists($this, "messages") ? $this->messages() : [], method_exists($this, "attributes") ? $this->attributes() : []); if ($validator->fails()) { if (method_exists($this, "failedValidation")) { $this->failedValidation($validator); } throw new \Illuminate\Validation\ValidationException($validator); } } }');
}

if (!class_exists('Illuminate\Validation\Rules\RequiredIf')) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -483,6 +483,25 @@ public function request($method, $absUrl, $headers, $params, $hasFile, $apiMode
],
]);
bindCustomerNotificationDispatcher();
// Codes sent by SMS are stored hashed (core-api 1.6.69), so the plain code is only in
// the text itself: record what the provider was given and read the code back from it.
$twilio = new class {
public array $messages = [];

public function message(string $to, string $message, array $media = [], array $params = []): object
{
$this->messages[] = $message;

return (object) ['sid' => 'sms_test'];
}
};
app()->instance('twilio', $twilio);
Illuminate\Support\Facades\Facade::clearResolvedInstance('twilio');
$lastSmsCode = function () use ($twilio): ?string {
$message = $twilio->messages ? $twilio->messages[array_key_last($twilio->messages)] : '';

return preg_match('/(\d{6})$/', trim($message), $match) ? $match[1] : null;
};
$sentry = new class {
public array $exceptions = [];

Expand All @@ -506,19 +525,21 @@ public function captureException(Throwable $exception): void
);
$closureDeliveryFailure = $controller->startAccountClosure(Request::create('/customer/closure', 'POST'));
$connection->statement('DROP TRIGGER fail_closure_verification_insert');
// Core texts the code to the phone on file, so the customer needs one for the text
// (and its plain code) to exist at all.
$connection->table('users')->where('uuid', 'user_uuid')->update(['phone' => '+97699112200']);
$phoneConflict = $controller->requestPhoneVerification(Request::create('/customer/phone', 'POST', [
'phone' => '97699112233',
]));
$invalidPhoneCode = $controller->verifyPhoneNumber(Request::create('/customer/phone/verify', 'POST', [
'code' => 'invalid-code',
]));
$generatedCode = $connection->table('verification_codes')
->where('for', 'storefront_verify_phone')
->value('code');
$generatedCode = $lastSmsCode();
$verifiedPhone = $controller->verifyPhoneNumber(Request::create('/customer/phone/verify', 'POST', [
'code' => $generatedCode,
]));
$closureStarted = $controller->startAccountClosure(Request::create('/customer/closure', 'POST'));
$closureCode = $lastSmsCode();
$connection->statement(
"CREATE TRIGGER fail_phone_verification_insert BEFORE INSERT ON verification_codes BEGIN SELECT RAISE(ABORT, 'verification insert failed'); END"
);
Expand All @@ -527,10 +548,6 @@ public function captureException(Throwable $exception): void
'phone' => '+97699112234',
]));
$connection->statement('DROP TRIGGER fail_phone_verification_insert');
$closureCode = $connection->table('verification_codes')
->where('for', 'storefront_account_closure')
->where('meta', 'like', '%+97699112233%')
->value('code');
$invalidClosure = $controller->confirmAccountClosure(Request::create('/customer/closure/confirm', 'POST', [
'code' => 'invalid-code',
]));
Expand All @@ -541,8 +558,11 @@ public function captureException(Throwable $exception): void
'code' => $closureCode,
]));
$connection->statement('DROP TRIGGER fail_customer_user_delete');
// A code is used up by the check that accepted it, so the failed deletion burned this
// one and the customer asks for a new code to try again.
$controller->startAccountClosure(Request::create('/customer/closure', 'POST'));
$closed = $controller->confirmAccountClosure(Request::create('/customer/closure/confirm', 'POST', [
'code' => $closureCode,
'code' => $lastSmsCode(),
]));
app()->offsetUnset(Illuminate\Contracts\Notifications\Dispatcher::class);
app()->forgetInstance('sentry');
Expand Down Expand Up @@ -590,7 +610,7 @@ public function captureException(Throwable $exception): void
->and($verifiedPhone)->toBeInstanceOf(Fleetbase\Storefront\Http\Resources\Customer::class)
->and($connection->table('users')->where('uuid', 'user_uuid')->value('phone'))->toBe('+97699112233')
->and($connection->table('contacts')->where('uuid', $customerUuid)->value('phone'))->toBe('+97699112233')
->and($connection->table('verification_codes')->where('code', $generatedCode)->value('deleted_at'))->not->toBeNull()
->and($connection->table('verification_codes')->where('for', 'storefront_verify_phone')->value('status'))->toBe('used')
->and(json_encode($closureStarted->getData(true)))->toBe('{"status":"OK"}')
->and($phoneDeliveryFailure->getData(true))->toHaveKey('error')
->and($invalidClosure->getData(true))->toBe(['error' => 'Invalid verification code provided!'])
Expand Down Expand Up @@ -904,8 +924,10 @@ public function message(string $to, string $message, array $media = [], array $p
$schema->dropIfExists('verification_codes');
$schema->create('verification_codes', function ($table) {
$table->increments('id');
$table->string('uuid')->nullable();
$table->string('code')->nullable();
$table->string('for')->nullable();
$table->string('status')->nullable();
$table->text('meta')->nullable();
$table->timestamp('expires_at')->nullable();
$table->timestamps();
Expand Down Expand Up @@ -936,8 +958,10 @@ public function message(string $to, string $message, array $media = [], array $p
$schema->dropIfExists('verification_codes');
$schema->create('verification_codes', function ($table) {
$table->increments('id');
$table->string('uuid')->nullable();
$table->string('code')->nullable();
$table->string('for')->nullable();
$table->string('status')->nullable();
$table->text('meta')->nullable();
$table->timestamp('expires_at')->nullable();
$table->timestamps();
Expand Down Expand Up @@ -1741,6 +1765,7 @@ public function sendNow($notifiables, $notification)
$table->string('subject_uuid')->nullable();
$table->string('code')->nullable();
$table->string('for')->nullable();
$table->string('status')->nullable();
$table->text('meta')->nullable();
$table->timestamp('expires_at')->nullable();
$table->timestamps();
Expand Down