aboutsummaryrefslogtreecommitdiffhomepage
path: root/services/app/src/Forms/LoginForm.php
blob: 4c69ad5c2a416bcc826498697778f7a723c66261 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
<?php

declare(strict_types=1);

namespace Nsfisis\Albatross\Forms;

use Nsfisis\Albatross\Auth\AuthenticationResult;
use Nsfisis\Albatross\Auth\AuthProviderInterface;
use Nsfisis\Albatross\Exceptions\EntityValidationException;
use Nsfisis\Albatross\Form\FormBase;
use Nsfisis\Albatross\Form\FormItem;
use Nsfisis\Albatross\Form\FormState;
use Nsfisis\Albatross\Form\FormSubmissionFailureException;
use Nsfisis\Albatross\Repositories\UserRepository;
use Slim\Interfaces\RouteParserInterface;

final class LoginForm extends FormBase
{
    public function __construct(
        ?FormState $state,
        private readonly ?string $destination,
        private readonly RouteParserInterface $routeParser,
        private readonly UserRepository $userRepo,
        private readonly AuthProviderInterface $authProvider,
    ) {
        if (!isset($state)) {
            $state = new FormState();
        }
        parent::__construct($state);
    }

    public function pageTitle(): string
    {
        return 'ログイン';
    }

    public function redirectUrl(): string
    {
        return $this->destination ?? $this->routeParser->urlFor('quiz_list');
    }

    protected function submitLabel(): string
    {
        return 'ログイン';
    }

    /**
     * @return list<FormItem>
     */
    protected function items(): array
    {
        return [
            new FormItem(
                name: 'username',
                type: 'text',
                label: 'ユーザ名',
                isRequired: true,
            ),
            new FormItem(
                name: 'password',
                type: 'password',
                label: 'パスワード',
                isRequired: true,
            ),
        ];
    }

    public function submit(): void
    {
        $username = $this->state->get('username') ?? '';
        $password = $this->state->get('password') ?? '';

        $this->validate($username, $password);

        $authResult = $this->authProvider->login($username, $password);
        if ($authResult === AuthenticationResult::InvalidCredentials) {
            $this->state->setErrors(['general' => 'ユーザ名またはパスワードが異なります']);
            throw new FormSubmissionFailureException(code: 403);
        } elseif ($authResult === AuthenticationResult::InvalidJson || $authResult === AuthenticationResult::UnknownError) {
            throw new FormSubmissionFailureException(code: 500);
        } else {
            $user = $this->userRepo->findByUsername($username);
            if ($user === null) {
                try {
                    $user_id = $this->userRepo->create(
                        $username,
                        is_admin: false,
                    );
                } catch (EntityValidationException $e) {
                    $this->state->setErrors($e->toFormErrors());
                    throw new FormSubmissionFailureException(previous: $e);
                }
                $_SESSION['user_id'] = $user_id;
            } else {
                $_SESSION['user_id'] = $user->user_id;
            }
        }
    }

    private function validate(string $username, string $password): void
    {
        $errors = [];
        if (strlen($username) < 1) {
            $errors['username'] = 'ユーザ名は必須です';
        }

        if (strlen($password) < 1) {
            $errors['password'] = 'パスワードは必須です';
        }

        if (count($errors) > 0) {
            $this->state->setErrors($errors);
            throw new FormSubmissionFailureException(code: 400);
        }
    }
}