From ac2dd23ad74f3182851907a94c8262a796c8f732 Mon Sep 17 00:00:00 2001 From: James Collins Date: Mon, 8 May 2023 10:40:48 +1000 Subject: [PATCH] remove usernames --- app/Conductors/UserConductor.php | 4 +- app/Http/Controllers/Api/AuthController.php | 10 +- app/Http/Controllers/Api/UserController.php | 62 ++--- app/Http/Requests/AuthLoginRequest.php | 2 +- .../Requests/UserForgotPasswordRequest.php | 2 +- .../Requests/UserForgotUsernameRequest.php | 22 -- app/Http/Requests/UserRegisterRequest.php | 5 +- app/Http/Requests/UserRequest.php | 42 +++- .../Requests/UserResendVerifyEmailRequest.php | 2 +- app/Mail/ForgotUsername.php | 60 ----- app/Models/User.php | 1 - database/factories/UserFactory.php | 4 +- ...71954_remove_username_from_users_table.php | 35 +++ database/seeders/DatabaseSeeder.php | 2 +- resources/js/router/index.js | 10 + resources/js/views/Article.vue | 4 +- resources/js/views/ForgotPassword.vue | 17 +- resources/js/views/ForgotUsername.vue | 90 ------- resources/js/views/Login.vue | 12 +- resources/js/views/Register.vue | 44 +--- resources/js/views/ResendEmailVerify.vue | 14 +- resources/js/views/dashboard/ArticleEdit.vue | 4 +- resources/js/views/dashboard/UserEdit.vue | 86 ++++--- resources/js/views/dashboard/UserList.vue | 228 ++++++++++++------ .../emails/user/change_email_verify.blade.php | 2 +- .../user/change_email_verify_plain.blade.php | 2 +- .../views/emails/user/changed_email.blade.php | 2 +- .../emails/user/changed_email_plain.blade.php | 2 +- .../emails/user/changed_password.blade.php | 2 +- .../user/changed_password_plain.blade.php | 2 +- .../views/emails/user/email_verify.blade.php | 2 +- .../emails/user/email_verify_plain.blade.php | 2 +- .../emails/user/forgot_password.blade.php | 2 +- .../user/forgot_password_plain.blade.php | 2 +- .../emails/user/forgot_username.blade.php | 131 ---------- .../user/forgot_username_plain.blade.php | 24 -- .../user/subscription_confirm.blade.php | 116 --------- .../user/subscription_confirm_plain.php | 14 -- .../user/subscription_unsubscribed.blade.php | 111 --------- .../user/subscription_unsubscribed_plain.php | 10 - routes/api.php | 4 - tests/Feature/AuthApiTest.php | 16 +- tests/Feature/UsersApiTest.php | 28 +-- 43 files changed, 372 insertions(+), 864 deletions(-) delete mode 100644 app/Http/Requests/UserForgotUsernameRequest.php delete mode 100644 app/Mail/ForgotUsername.php create mode 100644 database/migrations/2023_05_04_071954_remove_username_from_users_table.php delete mode 100644 resources/js/views/ForgotUsername.vue delete mode 100644 resources/views/emails/user/forgot_username.blade.php delete mode 100644 resources/views/emails/user/forgot_username_plain.blade.php delete mode 100644 resources/views/emails/user/subscription_confirm.blade.php delete mode 100644 resources/views/emails/user/subscription_confirm_plain.php delete mode 100644 resources/views/emails/user/subscription_unsubscribed.blade.php delete mode 100644 resources/views/emails/user/subscription_unsubscribed_plain.php diff --git a/app/Conductors/UserConductor.php b/app/Conductors/UserConductor.php index 1dce2af..4c21e69 100644 --- a/app/Conductors/UserConductor.php +++ b/app/Conductors/UserConductor.php @@ -23,7 +23,7 @@ class UserConductor extends Conductor { $user = auth()->user(); if ($user === null || $user->hasPermission('admin/users') === false) { - return ['id', 'username']; + return ['id', 'display_name']; } return parent::fields($model); @@ -41,7 +41,7 @@ class UserConductor extends Conductor $data = $model->toArray(); if ($user === null || ($user->hasPermission('admin/users') === false && strcasecmp($user->id, $model->id) !== 0)) { - $fields = ['id', 'username', 'display_name']; + $fields = ['id', 'display_name']; $data = arrayLimitKeys($data, $fields); } else { $data['permissions'] = $user->permissions; diff --git a/app/Http/Controllers/Api/AuthController.php b/app/Http/Controllers/Api/AuthController.php index 11b67fd..e25a48b 100644 --- a/app/Http/Controllers/Api/AuthController.php +++ b/app/Http/Controllers/Api/AuthController.php @@ -47,18 +47,18 @@ class AuthController extends ApiController */ public function login(AuthLoginRequest $request) { - $user = User::where('username', '=', $request->input('username'))->first(); + $user = User::where('email', '=', $request->input('email'))->first(); if ($user !== null && Hash::check($request->input('password'), $user->password) === true) { if ($user->email_verified_at === null) { return $this->respondWithErrors([ - 'username' => 'Email address has not been verified.' + 'email' => 'Email address has not been verified.' ]); } if ($user->disabled === true) { return $this->respondWithErrors([ - 'username' => 'Account has been disabled.' + 'email' => 'Account has been disabled.' ]); } @@ -78,8 +78,8 @@ class AuthController extends ApiController }//end if return $this->respondWithErrors([ - 'username' => 'Invalid username or password', - 'password' => 'Invalid username or password', + 'email' => 'Invalid email or password', + 'password' => 'Invalid email or password', ]); } diff --git a/app/Http/Controllers/Api/UserController.php b/app/Http/Controllers/Api/UserController.php index 6b8a5a5..d7ac235 100644 --- a/app/Http/Controllers/Api/UserController.php +++ b/app/Http/Controllers/Api/UserController.php @@ -5,7 +5,6 @@ namespace App\Http\Controllers\Api; use App\Enum\HttpResponseCodes; use App\Http\Requests\UserRequest; use App\Http\Requests\UserForgotPasswordRequest; -use App\Http\Requests\UserForgotUsernameRequest; use App\Http\Requests\UserRegisterRequest; use App\Http\Requests\UserResendVerifyEmailRequest; use App\Http\Requests\UserResetPasswordRequest; @@ -14,7 +13,6 @@ use App\Jobs\SendEmailJob; use App\Mail\ChangedEmail; use App\Mail\ChangedPassword; use App\Mail\ChangeEmailVerify; -use App\Mail\ForgotUsername; use App\Mail\ForgotPassword; use App\Mail\EmailVerify; use App\Models\User; @@ -37,7 +35,6 @@ class UserController extends ApiController 'register', 'exists', 'forgotPassword', - 'forgotUsername', 'resetPassword', 'verifyEmail', 'resendVerifyEmailCode' @@ -105,7 +102,7 @@ class UserController extends ApiController { if (UserConductor::updatable($user) === true) { $input = []; - $updatable = ['username', 'first_name', 'last_name', 'email', 'phone', 'password', 'display_name']; + $updatable = ['first_name', 'last_name', 'email', 'phone', 'password', 'display_name']; if ($request->user()->hasPermission('admin/user') === true) { $updatable = array_merge($updatable, ['email_verified_at']); @@ -149,15 +146,28 @@ class UserController extends ApiController public function register(UserRegisterRequest $request) { try { - $user = User::create([ - 'first_name' => $request->input('first_name'), - 'last_name' => $request->input('last_name'), - 'username' => $request->input('username'), - 'email' => $request->input('email'), - 'phone' => $request->input('phone', ''), - 'password' => Hash::make($request->input('password')), - 'display_name' => $request->input('display_name', $request->input('username')), - ]); + $user = User::where('email', $request->input('email')) + ->whereNull('password') + ->first(); + + if ($user === null) { + $user = User::create([ + 'first_name' => $request->input('first_name'), + 'last_name' => $request->input('last_name'), + 'email' => $request->input('email'), + 'phone' => $request->input('phone', ''), + 'password' => Hash::make($request->input('password')), + 'display_name' => $request->input('display_name'), + ]); + } else { + $user->update([ + 'first_name' => $request->input('first_name'), + 'last_name' => $request->input('last_name'), + 'phone' => $request->input('phone', ''), + 'password' => Hash::make($request->input('password')), + 'display_name' => $request->input('display_name'), + ]); + }//end if $code = $user->codes()->create([ 'action' => 'verify-email', @@ -175,26 +185,6 @@ class UserController extends ApiController }//end try } - /** - * Sends an email with all the usernames registered at that address - * - * @param \App\Http\Requests\UserForgotUsernameRequest $request The forgot username request. - * @return \Illuminate\Http\Response - */ - public function forgotUsername(UserForgotUsernameRequest $request) - { - $users = User::where('email', $request->input('email'))->whereNotNull('email_verified_at')->get(); - if ($users->count() > 0) { - dispatch((new SendEmailJob( - $users->first()->email, - new ForgotUsername($users->pluck('username')->toArray()) - )))->onQueue('mail'); - return $this->respondNoContent(); - } - - return $this->respondJson(['message' => 'Username send to the email address if registered']); - } - /** * Generates a new reset password code * @@ -203,7 +193,7 @@ class UserController extends ApiController */ public function forgotPassword(UserForgotPasswordRequest $request) { - $user = User::where('username', $request->input('username'))->first(); + $user = User::where('email', $request->input('email'))->first(); if ($user !== null) { $user->codes()->where('action', 'reset-password')->delete(); $code = $user->codes()->create([ @@ -299,7 +289,7 @@ class UserController extends ApiController { UserCode::clearExpired(); - $user = User::where('username', $request->input('username'))->first(); + $user = User::where('email', $request->input('email'))->first(); if ($user !== null) { $code = $user->codes()->where('action', 'verify-email')->first(); $code->regenerate(); @@ -324,7 +314,7 @@ class UserController extends ApiController */ public function resendVerifyEmailCode(UserResendVerifyEmailRequest $request) { - $user = User::where('username', $request->input('username'))->first(); + $user = User::where('email', $request->input('email'))->first(); if ($user !== null) { $user->codes()->where('action', 'verify-email')->delete(); diff --git a/app/Http/Requests/AuthLoginRequest.php b/app/Http/Requests/AuthLoginRequest.php index ba18db1..12baa20 100644 --- a/app/Http/Requests/AuthLoginRequest.php +++ b/app/Http/Requests/AuthLoginRequest.php @@ -14,7 +14,7 @@ class AuthLoginRequest extends FormRequest public function rules() { return [ - 'username' => 'required|string|min:6|max:255', + 'email' => 'required|string|min:6|max:255', 'password' => 'required|string|min:6', ]; } diff --git a/app/Http/Requests/UserForgotPasswordRequest.php b/app/Http/Requests/UserForgotPasswordRequest.php index 42ebbca..eaf6ccf 100644 --- a/app/Http/Requests/UserForgotPasswordRequest.php +++ b/app/Http/Requests/UserForgotPasswordRequest.php @@ -15,7 +15,7 @@ class UserForgotPasswordRequest extends FormRequest public function rules() { return [ - 'username' => 'required|exists:users,username', + 'email' => 'required|exists:users,email', // 'captcha_token' => [new Recaptcha()], ]; } diff --git a/app/Http/Requests/UserForgotUsernameRequest.php b/app/Http/Requests/UserForgotUsernameRequest.php deleted file mode 100644 index 6ffa2b9..0000000 --- a/app/Http/Requests/UserForgotUsernameRequest.php +++ /dev/null @@ -1,22 +0,0 @@ - - */ - public function rules() - { - return [ - 'email' => 'required|email|max:255', - // 'captcha_token' => [new Recaptcha()], - ]; - } -} diff --git a/app/Http/Requests/UserRegisterRequest.php b/app/Http/Requests/UserRegisterRequest.php index 9bd6515..fe83921 100644 --- a/app/Http/Requests/UserRegisterRequest.php +++ b/app/Http/Requests/UserRegisterRequest.php @@ -16,9 +16,8 @@ class UserRegisterRequest extends FormRequest return [ 'first_name' => 'required|string|max:255', 'last_name' => 'required|string|max:255', - 'display_name' => 'required|string|max:255', - 'email' => 'required|string|email|max:255', - 'username' => 'required|string|min:4|max:255|unique:users', + 'display_name' => 'required|string|max:255|uniqueish:users', + 'email' => 'required|string|email|max:255|unique:users', 'password' => 'required|string|min:8', ]; } diff --git a/app/Http/Requests/UserRequest.php b/app/Http/Requests/UserRequest.php index 6187e1f..5c62ad1 100644 --- a/app/Http/Requests/UserRequest.php +++ b/app/Http/Requests/UserRequest.php @@ -3,9 +3,18 @@ namespace App\Http\Requests; use Illuminate\Validation\Rule; +use App\Rules\Uniqueish; class UserRequest extends BaseRequest { + /** + * Fields that are required unless all are null. + * + * @var string[] + */ + protected $required_with_all = ['first_name','last_name','display_name','phone']; + + /** * Apply the additional POST base rules to this request * @@ -14,11 +23,10 @@ class UserRequest extends BaseRequest public function postRules() { return [ - 'username' => 'required|string|max:255|min:4|unique:users', 'first_name' => 'required|string|max:255|min:2', 'last_name' => 'required|string|max:255|min:2', - 'display_name' => 'required|string|max:255', - 'email' => 'required|string|email|max:255', + 'display_name' => 'required|string|max:255|uniqueish:users', + 'email' => 'required|string|email|max:255|unique:users', 'phone' => ['string', 'regex:/^(\+|00)?[0-9][0-9 \-\(\)\.]{7,32}$/'], 'email_verified_at' => 'date' ]; @@ -33,24 +41,32 @@ class UserRequest extends BaseRequest { $user = $this->route('user'); + $required_with_all = count($this->required_with_all) > 0 ? 'required_with_all:' . implode(',', $this->required_with_all) : ''; + return [ - 'username' => [ + 'first_name' => "nullable|string|required_if_any:users,last_name,display_name,phone,password|between:2,255", + 'last_name' => "nullable|required_if_any:users,first_name,display_name,phone,password|string|max:255|min:2", + 'display_name' => [ + 'nullable', + 'required_if_any:users,first_name,last_name,phone,password', 'string', 'max:255', - 'min:4', + 'min:2', + (new Uniqueish('users', 'display_name'))->ignore($user->id), + ], + 'email' => [ + 'string', + 'email', + 'max:255', Rule::unique('users')->ignore($user->id)->when( - $this->username !== $user->username, + $this->email !== $user->email, function ($query) { - return $query->where('username', $this->username); + return $query->where('email', $this->email); } ), ], - 'first_name' => 'string|max:255|min:2', - 'last_name' => 'string|max:255|min:2', - 'display_name' => 'string|max:255|min:2', - 'email' => 'string|email|max:255', - 'phone' => ['nullable','regex:/^(\+|00)?[0-9][0-9 \-\(\)\.]{7,32}$/'], - 'password' => 'string|min:8' + 'phone' => ['nullable', 'regex:/^(\+|00)?[0-9][0-9 \-\(\)\.]{7,32}$/'], + 'password' => "nullable|{$required_with_all}|string|min:8" ]; } } diff --git a/app/Http/Requests/UserResendVerifyEmailRequest.php b/app/Http/Requests/UserResendVerifyEmailRequest.php index 1bf2579..92d34b2 100644 --- a/app/Http/Requests/UserResendVerifyEmailRequest.php +++ b/app/Http/Requests/UserResendVerifyEmailRequest.php @@ -15,7 +15,7 @@ class UserResendVerifyEmailRequest extends FormRequest public function rules() { return [ - 'username' => 'required|exists:users,username', + 'email' => 'required|exists:users,email', // 'captcha_token' => [new Recaptcha()], ]; } diff --git a/app/Mail/ForgotUsername.php b/app/Mail/ForgotUsername.php deleted file mode 100644 index fac4fe0..0000000 --- a/app/Mail/ForgotUsername.php +++ /dev/null @@ -1,60 +0,0 @@ -usernames = $usernames; - } - - /** - * Get the message envelope. - * - * @return \Illuminate\Mail\Mailables\Envelope - */ - public function envelope() - { - return new Envelope( - subject: '🤦 Forgot your username?', - ); - } - - /** - * Get the message content definition. - * - * @return \Illuminate\Mail\Mailables\Content - */ - public function content() - { - return new Content( - view: 'emails.user.forgot_username', - text: 'emails.user.forgot_username_plain', - ); - } -} diff --git a/app/Models/User.php b/app/Models/User.php index 7187110..9ec6998 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -25,7 +25,6 @@ class User extends Authenticatable implements Auditable * @var array */ protected $fillable = [ - 'username', 'first_name', 'last_name', 'email', diff --git a/database/factories/UserFactory.php b/database/factories/UserFactory.php index 98527a1..c4e7f11 100644 --- a/database/factories/UserFactory.php +++ b/database/factories/UserFactory.php @@ -20,14 +20,12 @@ class UserFactory extends Factory $faker = \Faker\Factory::create(); $faker->addProvider(new \Faker\Provider\CustomInternetProvider($faker)); - $username = $faker->unique()->userNameWithMinLength(6); $first_name = $faker->firstName(); $last_name = $faker->lastName(); - $display_name = $faker->randomElement([$username, $first_name . ' ' . $last_name]); + $display_name = $first_name . ' ' . $last_name; return [ - 'username' => $username, 'first_name' => $first_name, 'last_name' => $last_name, 'email' => $faker->safeEmail(), diff --git a/database/migrations/2023_05_04_071954_remove_username_from_users_table.php b/database/migrations/2023_05_04_071954_remove_username_from_users_table.php new file mode 100644 index 0000000..05bde34 --- /dev/null +++ b/database/migrations/2023_05_04_071954_remove_username_from_users_table.php @@ -0,0 +1,35 @@ +dropColumn('username'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('users', function (Blueprint $table) { + $table->string('username')->unique(); + }); + + DB::table('users')->update(['username' => DB::raw('display_name')]); + } +}; diff --git a/database/seeders/DatabaseSeeder.php b/database/seeders/DatabaseSeeder.php index 7914d10..1080a0f 100644 --- a/database/seeders/DatabaseSeeder.php +++ b/database/seeders/DatabaseSeeder.php @@ -20,7 +20,7 @@ class DatabaseSeeder extends Seeder \App\Models\User::factory(40)->create(); \App\Models\User::factory()->create([ - 'username' => 'nomadjimbob', + 'display_name' => 'James Collins', 'first_name' => 'James', 'last_name' => 'Collins', 'email' => 'james@stemmechanics.com.au', diff --git a/resources/js/router/index.js b/resources/js/router/index.js index 8f1757e..5b217ec 100644 --- a/resources/js/router/index.js +++ b/resources/js/router/index.js @@ -288,6 +288,16 @@ export const routes = [ component: () => import("@/views/dashboard/UserList.vue"), }, + { + path: "create", + name: "dashboard-user-create", + meta: { + title: "Create User", + middleware: "authenticated", + }, + component: () => + import("@/views/dashboard/UserEdit.vue"), + }, { path: ":id", name: "dashboard-user-edit", diff --git a/resources/js/views/Article.vue b/resources/js/views/Article.vue index 03e2124..2f8536a 100644 --- a/resources/js/views/Article.vue +++ b/resources/js/views/Article.vue @@ -8,7 +8,7 @@
-
By {{ article.user.username }}
+
By {{ article.user.display_name }}
{{ formattedDate(article.publish_at) }}
= ref({ title: "", - user: { username: "" }, + user: { display_name: "" }, }); /** diff --git a/resources/js/views/ForgotPassword.vue b/resources/js/views/ForgotPassword.vue index 5cb2dc7..49546de 100644 --- a/resources/js/views/ForgotPassword.vue +++ b/resources/js/views/ForgotPassword.vue @@ -5,11 +5,10 @@