From 151112bd2acf3716b15769a1d1f66df02e4ab847 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C5=91cze=20Bence?= Date: Sun, 9 Apr 2023 01:35:40 +0200 Subject: [PATCH 1/8] make it possible to have username --- .../structure/20230409_0101_username.sql | 3 +++ src/Cli/AddUserCommand.php | 5 +++++ src/Controller/LoginController.php | 8 ++++---- src/PersistentData/Model/User.php | 14 +++++++++++++- src/Repository/UserRepository.php | 17 +++++++++++++++++ views/account/account.php | 1 + views/login/login.php | 2 +- 7 files changed, 44 insertions(+), 6 deletions(-) create mode 100644 database/migrations/structure/20230409_0101_username.sql diff --git a/database/migrations/structure/20230409_0101_username.sql b/database/migrations/structure/20230409_0101_username.sql new file mode 100644 index 0000000..adfcb72 --- /dev/null +++ b/database/migrations/structure/20230409_0101_username.sql @@ -0,0 +1,3 @@ +ALTER TABLE `users` +ADD `username` varchar(100) DEFAULT NULL, +ADD UNIQUE `username` (`username`); diff --git a/src/Cli/AddUserCommand.php b/src/Cli/AddUserCommand.php index 27a0d0b..980cb79 100644 --- a/src/Cli/AddUserCommand.php +++ b/src/Cli/AddUserCommand.php @@ -21,6 +21,11 @@ class AddUserCommand extends Command public function execute(InputInterface $input, OutputInterface $output): int { + if (!filter_var($input->getArgument('email'), FILTER_VALIDATE_EMAIL)) { + $output->writeln('Please provide a valid email address.'); + return 1; + } + $user = new User(); $user->setEmail($input->getArgument('email')); $user->setPlainPassword($input->getArgument('password')); diff --git a/src/Controller/LoginController.php b/src/Controller/LoginController.php index 34baae4..7a83d8c 100644 --- a/src/Controller/LoginController.php +++ b/src/Controller/LoginController.php @@ -109,11 +109,11 @@ class LoginController return new JsonContent(['success' => true]); } - $user = $this->userRepository->getByEmail($this->request->post('email')); + $user = $this->userRepository->getByEmailOrUsername($this->request->post('email')); if ($user === null || !$user->checkPassword($this->request->post('password'))) { return new JsonContent([ 'error' => [ - 'errorText' => 'No user found with the given email address or the given password is wrong. You can request password reset!' ] ]); @@ -200,11 +200,11 @@ class LoginController } } - $user = $this->userRepository->getByEmail($this->request->post('email')); + $user = $this->userRepository->getByEmailOrUsername($this->request->post('email')); if ($user === null) { return new JsonContent([ 'error' => [ - 'errorText' => 'No user found with the given email address.' + 'errorText' => 'No user found with the given email address / username.' ] ]); } diff --git a/src/PersistentData/Model/User.php b/src/PersistentData/Model/User.php index 1a284d4..92c4495 100644 --- a/src/PersistentData/Model/User.php +++ b/src/PersistentData/Model/User.php @@ -8,12 +8,14 @@ class User extends Model implements IUser { protected static string $table = 'users'; - protected static array $fields = ['email', 'password', 'type', 'google_sub', 'created']; + protected static array $fields = ['email', 'username', 'password', 'type', 'google_sub', 'created']; private static array $types = ['user', 'admin']; private string $email = ''; + private ?string $username = null; + private ?string $password = null; private string $type = 'user'; @@ -27,6 +29,11 @@ class User extends Model implements IUser $this->email = $email; } + public function setUsername(?string $username): void + { + $this->username = $username; + } + public function setPassword(?string $hashedPassword): void { $this->password = $hashedPassword; @@ -64,6 +71,11 @@ class User extends Model implements IUser return $this->email; } + public function getUsername(): ?string + { + return $this->username; + } + public function getPassword(): ?string { return $this->password; diff --git a/src/Repository/UserRepository.php b/src/Repository/UserRepository.php index cc03b44..3882d9d 100644 --- a/src/Repository/UserRepository.php +++ b/src/Repository/UserRepository.php @@ -27,6 +27,23 @@ class UserRepository implements IUserRepository return $this->pdm->selectFromDb($select, User::class); } + public function getByUsername(string $username): ?User + { + $select = new Select(\Container::$dbConnection); + $select->where('username', '=', $username); + + return $this->pdm->selectFromDb($select, User::class); + } + + public function getByEmailOrUsername(string $emailOrUsername): ?User + { + if (filter_var($emailOrUsername, FILTER_VALIDATE_EMAIL)) { + return $this->getByEmail($emailOrUsername); + } + + return $this->getByUsername($emailOrUsername); + } + public function getByGoogleSub(string $sub): ?User { $select = new Select(\Container::$dbConnection); diff --git a/views/account/account.php b/views/account/account.php index 315c71b..6a26290 100644 --- a/views/account/account.php +++ b/views/account/account.php @@ -25,6 +25,7 @@
+

diff --git a/views/login/login.php b/views/login/login.php index 6c3cc79..d8dabfb 100644 --- a/views/login/login.php +++ b/views/login/login.php @@ -4,7 +4,7 @@

Login

- +

From a0fe77fe666bdaafd884b76fc680ed2bc887c524 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C5=91cze=20Bence?= Date: Sun, 9 Apr 2023 02:10:32 +0200 Subject: [PATCH 2/8] make it possible to modify email and username --- src/Controller/UserController.php | 43 ++++++++++++++++++++++++++++--- views/account/account.php | 7 +++-- 2 files changed, 42 insertions(+), 8 deletions(-) diff --git a/src/Controller/UserController.php b/src/Controller/UserController.php index 8110d52..f8c1f36 100644 --- a/src/Controller/UserController.php +++ b/src/Controller/UserController.php @@ -13,6 +13,7 @@ use SokoWeb\Response\HtmlContent; use SokoWeb\Response\JsonContent; use SokoWeb\Response\Redirect; use SokoWeb\Util\JwtParser; +use RVR\Repository\UserRepository; class UserController implements ISecured { @@ -20,10 +21,13 @@ class UserController implements ISecured private PersistentDataManager $pdm; + private UserRepository $userRepository; + public function __construct(IRequest $request) { $this->request = $request; $this->pdm = new PersistentDataManager(); + $this->userRepository = new UserRepository(); } public function authorize(): bool @@ -126,8 +130,39 @@ class UserController implements ISecured return new JsonContent(['error' => ['errorText' => $error]]); } - if (strlen($this->request->post('password_new')) > 0) { - if (strlen($this->request->post('password_new')) < 6) { + $newEmail = $this->request->post('email'); + if ($newEmail !== $user->getEmail()) { + if (!filter_var($newEmail, FILTER_VALIDATE_EMAIL)) { + return new JsonContent(['error' => ['errorText' => 'Please provide a valid email address.']]); + } + + if ($this->userRepository->getByEmail($newEmail) !== null) { + return new JsonContent(['error' => ['errorText' => 'The given email address belongs to another account.']]); + } + + $user->setEmail($newEmail); + } + + $newUsername = $this->request->post('username'); + if ($newUsername !== $user->getUsername()) { + if (strlen($newUsername) > 0) { + if (filter_var($newUsername, FILTER_VALIDATE_EMAIL)) { + return new JsonContent(['error' => ['errorText' => 'Please select a username that is not a valid email address.']]); + } + + if ($this->userRepository->getByUsername($newUsername) !== null) { + return new JsonContent(['error' => ['errorText' => 'The given username is already taken.']]); + } + + $user->setUsername($newUsername); + } else { + $user->setUsername(null); + } + } + + $newPassword = $this->request->post('password_new'); + if (strlen($newPassword) > 0) { + if (strlen($newPassword) < 6) { return new JsonContent([ 'error' => [ 'errorText' => 'The given new password is too short. Please choose a password that is at least 6 characters long!' @@ -135,7 +170,7 @@ class UserController implements ISecured ]); } - if ($this->request->post('password_new') !== $this->request->post('password_new_confirm')) { + if ($newPassword !== $this->request->post('password_new_confirm')) { return new JsonContent([ 'error' => [ 'errorText' => 'The given new passwords do not match.' @@ -143,7 +178,7 @@ class UserController implements ISecured ]); } - $user->setPlainPassword($this->request->post('password_new')); + $user->setPlainPassword($newPassword); } $this->pdm->saveToDb($user); diff --git a/views/account/account.php b/views/account/account.php index 6a26290..12a979e 100644 --- a/views/account/account.php +++ b/views/account/account.php @@ -5,7 +5,7 @@ @section(main)

Account

- +

Please confirm your identity with your password or with Google to modify your account.

@@ -23,9 +23,8 @@

- - - + +

From 6df63373ab42f64cb1c0cd17b6ef92b27234a5b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C5=91cze=20Bence?= Date: Sun, 9 Apr 2023 02:11:09 +0200 Subject: [PATCH 3/8] fix observeInput logic --- public/static/js/rvr.js | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/public/static/js/rvr.js b/public/static/js/rvr.js index dcf3d45..ed2ef94 100644 --- a/public/static/js/rvr.js +++ b/public/static/js/rvr.js @@ -158,12 +158,17 @@ var RVR = { document.getElementById('cover').style.visibility = 'hidden'; }, - observeInput: function (input, buttonToToggle) { - if (input.defaultValue !== input.value) { - buttonToToggle.disabled = false; - } else { - buttonToToggle.disabled = true; + observeInput: function (form, observedInputs) { + var anyChanged = false; + + for (var i = 0; i < observedInputs.length; i++) { + var input = form.elements[observedInputs[i]]; + if (input.defaultValue !== input.value) { + anyChanged = true; + } } + + form.elements.submit.disabled = !anyChanged; }, observeInputsInForm: function (form, observedInputs) { @@ -174,12 +179,12 @@ var RVR = { case 'INPUT': case 'TEXTAREA': input.oninput = function () { - RVR.observeInput(this, form.elements.submit); + RVR.observeInput(form, observedInputs); }; break; case 'SELECT': input.onchange = function () { - RVR.observeInput(this, form.elements.submit); + RVR.observeInput(form, observedInputs); }; break; } From de346c0c6e7886e29ce4cd934709ccae88774f06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C5=91cze=20Bence?= Date: Sun, 9 Apr 2023 00:56:03 +0200 Subject: [PATCH 4/8] fix HTML syntax error in account.php --- views/account/account.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/views/account/account.php b/views/account/account.php index 12a979e..723094f 100644 --- a/views/account/account.php +++ b/views/account/account.php @@ -9,7 +9,7 @@

Please confirm your identity with your password or with Google to modify your account.

-
From df3bf8907944358a2c42b5c7603878bf9b30612d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C5=91cze=20Bence?= Date: Sun, 9 Apr 2023 02:28:59 +0200 Subject: [PATCH 5/8] add personal user data fields --- .../20230409_0214_user_personal_data.sql | 5 ++ src/PersistentData/Model/User.php | 52 ++++++++++++++++++- 2 files changed, 55 insertions(+), 2 deletions(-) create mode 100644 database/migrations/structure/20230409_0214_user_personal_data.sql diff --git a/database/migrations/structure/20230409_0214_user_personal_data.sql b/database/migrations/structure/20230409_0214_user_personal_data.sql new file mode 100644 index 0000000..5085241 --- /dev/null +++ b/database/migrations/structure/20230409_0214_user_personal_data.sql @@ -0,0 +1,5 @@ +ALTER TABLE `users` +ADD `full_name` varchar(255) NOT NULL DEFAULT '', +ADD `nickname` varchar(255) NOT NULL DEFAULT '', +ADD `phone` varchar(255) NOT NULL DEFAULT '', +ADD `id_number` varchar(255) NOT NULL DEFAULT ''; diff --git a/src/PersistentData/Model/User.php b/src/PersistentData/Model/User.php index 92c4495..d0af3a9 100644 --- a/src/PersistentData/Model/User.php +++ b/src/PersistentData/Model/User.php @@ -8,7 +8,7 @@ class User extends Model implements IUser { protected static string $table = 'users'; - protected static array $fields = ['email', 'username', 'password', 'type', 'google_sub', 'created']; + protected static array $fields = ['email', 'username', 'password', 'type', 'google_sub', 'created', 'full_name', 'nickname', 'phone', 'id_number']; private static array $types = ['user', 'admin']; @@ -24,6 +24,14 @@ class User extends Model implements IUser private DateTime $created; + private string $fullName = ''; + + private string $nickname = ''; + + private string $phone = ''; + + private string $idNumber = ''; + public function setEmail(string $email): void { $this->email = $email; @@ -66,6 +74,26 @@ class User extends Model implements IUser $this->created = new DateTime($created); } + public function setFullName(string $fullName): void + { + $this->fullName = $fullName; + } + + public function setNickname(string $nickname): void + { + $this->nickname = $nickname; + } + + public function setPhone(string $phone): void + { + $this->phone = $phone; + } + + public function setIdNumber(string $idNumber): void + { + $this->idNumber = $idNumber; + } + public function getEmail(): string { return $this->email; @@ -101,6 +129,26 @@ class User extends Model implements IUser return $this->created->format('Y-m-d H:i:s'); } + public function getFullName(): string + { + return $this->fullName; + } + + public function getNickname(): string + { + return $this->nickname; + } + + public function getPhone(): string + { + return $this->phone; + } + + public function getIdNumber(): string + { + return $this->idNumber; + } + public function hasPermission(int $permission): bool { switch ($permission) { @@ -120,7 +168,7 @@ class User extends Model implements IUser public function getDisplayName(): string { - return $this->email; + return $this->nickname ?: $this->fullName; } public function checkPassword(string $password): bool From 749b93e3affa3476aa6e6f3d0d306bd2020c2369 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C5=91cze=20Bence?= Date: Sun, 9 Apr 2023 02:29:23 +0200 Subject: [PATCH 6/8] make it possible to modify personal user data fields --- src/Controller/UserController.php | 4 ++++ views/account/account.php | 7 ++++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/Controller/UserController.php b/src/Controller/UserController.php index f8c1f36..84c8c73 100644 --- a/src/Controller/UserController.php +++ b/src/Controller/UserController.php @@ -181,6 +181,10 @@ class UserController implements ISecured $user->setPlainPassword($newPassword); } + $user->setNickname($this->request->post('nickname')); + $user->setPhone($this->request->post('phone')); + $user->setIdNumber($this->request->post('id_number')); + $this->pdm->saveToDb($user); $this->request->session()->delete('authenticated_with_google_until'); diff --git a/views/account/account.php b/views/account/account.php index 723094f..8d4984b 100644 --- a/views/account/account.php +++ b/views/account/account.php @@ -5,7 +5,7 @@ @section(main)

Account

- +

Please confirm your identity with your password or with Google to modify your account.

@@ -27,6 +27,11 @@ +
+ + + +

From b8095420835cc00bd5fddf62f6ac6d7f49ea39d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C5=91cze=20Bence?= Date: Sun, 9 Apr 2023 02:35:38 +0200 Subject: [PATCH 7/8] reload account page after save --- views/account/account.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/views/account/account.php b/views/account/account.php index 8d4984b..b24e73d 100644 --- a/views/account/account.php +++ b/views/account/account.php @@ -5,7 +5,7 @@ @section(main)

Account

- +

Please confirm your identity with your password or with Google to modify your account.

From 71aed9dceceb7af81dce7fd31d3b59581d66cfaf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C5=91cze=20Bence?= Date: Sun, 9 Apr 2023 02:53:45 +0200 Subject: [PATCH 8/8] send all user data with oauth --- src/Controller/OAuthLoginController.php | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/Controller/OAuthLoginController.php b/src/Controller/OAuthLoginController.php index 6bee3b0..7a970d4 100644 --- a/src/Controller/OAuthLoginController.php +++ b/src/Controller/OAuthLoginController.php @@ -106,7 +106,12 @@ class OAuthLoginController 'exp' => (int)$token->getExpiresDate()->format('U'), 'nonce' => $token->getNonce(), 'sub' => $user->getId(), - 'email' => $user->getEmail() + 'email' => $user->getEmail(), + 'username' => $user->getUsername(), + 'full_name' => $user->getFullName(), + 'nickname' => $user->getNickname(), + 'phone' => $user->getPhone(), + 'id_number' => $user->getIdNumber() ]; $privateKey = file_get_contents(ROOT . '/' . $_ENV['JWT_RSA_PRIVATE_KEY']); $jwt = JWT::encode($payload, $privateKey, 'RS256');