Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
14.29% |
4 / 28 |
|
50.00% |
1 / 2 |
CRAP | |
0.00% |
0 / 1 |
VerifyOtpInteractor | |
14.29% |
4 / 28 |
|
50.00% |
1 / 2 |
28.67 | |
0.00% |
0 / 1 |
supports | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
3 | |||
interactWith | |
0.00% |
0 / 24 |
|
0.00% |
0 / 1 |
12 |
1 | <?php |
2 | namespace Apie\Console\Helpers; |
3 | |
4 | use Apie\Core\Context\ApieContext; |
5 | use Apie\Core\ContextConstants; |
6 | use Apie\Core\Metadata\MetadataInterface; |
7 | use Apie\OtpValueObjects\VerifyOTP; |
8 | use LogicException; |
9 | use ReflectionClass; |
10 | use Symfony\Component\Console\Helper\HelperSet; |
11 | use Symfony\Component\Console\Helper\QuestionHelper; |
12 | use Symfony\Component\Console\Input\InputInterface; |
13 | use Symfony\Component\Console\Output\OutputInterface; |
14 | use Symfony\Component\Console\Question\Question; |
15 | |
16 | final class VerifyOtpInteractor implements InputInteractorInterface |
17 | { |
18 | public function supports(MetadataInterface $metadata): bool |
19 | { |
20 | $class = $metadata->toClass(); |
21 | if (!$class) { |
22 | return false; |
23 | } |
24 | return class_exists(VerifyOTP::class) && $class->isSubclassOf(VerifyOTP::class); |
25 | } |
26 | public function interactWith( |
27 | MetadataInterface $metadata, |
28 | HelperSet $helperSet, |
29 | InputInterface $input, |
30 | OutputInterface $output, |
31 | ApieContext $context |
32 | ): mixed { |
33 | $helper = $helperSet->get('question'); |
34 | assert($helper instanceof QuestionHelper); |
35 | |
36 | $resource = $context->getContext(ContextConstants::RESOURCE); |
37 | $otpClass = $metadata->toClass(); |
38 | assert($otpClass !== null); |
39 | $property = $otpClass->getMethod('getOtpReference')->invoke(null); |
40 | $label = $otpClass->getMethod('getOtpLabel')->invoke(null, $resource); |
41 | $secret = $property->getValue($resource); |
42 | assert(is_callable([$secret, 'verify'])); |
43 | |
44 | $output->writeln('Open your authenticator application and add this code manually:'); |
45 | $output->writeln('Name: ' . $label); |
46 | $output->writeln('Secret code: ' . $secret); |
47 | $output->writeln('Type: ' . preg_replace('/Secret$/', '', (new ReflectionClass($secret))->getShortName())); |
48 | |
49 | if (is_callable([$secret, 'getQrCodeUri'])) { |
50 | $output->writeln('Or scan this QR code: ' . $secret->getQrCodeUri($label)); |
51 | } |
52 | |
53 | $question = new Question('Please enter the code shown in your authenticator application: '); |
54 | $question->setValidator(function ($input) use ($metadata, $secret) { |
55 | $otpInstance = $metadata->toClass()->getMethod('fromNative')->invoke(null, $input); |
56 | assert($otpInstance instanceof VerifyOTP); |
57 | if (!$secret->verify($otpInstance)) { |
58 | throw new LogicException('Code is not valid!'); |
59 | } |
60 | return $otpInstance->toNative(); |
61 | }); |
62 | return (string) $helper->ask($input, $output, $question); |
63 | } |
64 | } |