From d166b35fdbb72ba1e71ad5478ab865e4177a5ae2 Mon Sep 17 00:00:00 2001 From: Sam Maosa Date: Thu, 28 Apr 2022 06:44:19 +0300 Subject: [PATCH] Validation: User Passwords Validation and use of Rule classes and methods to validate - Fixed user password validation and rendering of password frontend fields - Added ability to use Rule::method() types of validation - Improved the logic of determining the validation rules - Enforced password server side validation rules TODO: Implement frontend validation using vuelidate --- .../acacia/Core/Constants/FormFields.php | 1 + publishes/acacia/Core/Repos/GPanelRepo.php | 24 ++++++++------ publishes/acacia/Core/Utilities/helpers.php | 10 ++++-- .../Database/Seeders/UsersDatabaseSeeder.php | 16 +-------- .../Http/Requests/User/DestroyRequest.php | 3 ++ .../Users/Http/Requests/User/DtRequest.php | 3 ++ .../Users/Http/Requests/User/IndexRequest.php | 3 ++ .../Users/Http/Requests/User/StoreRequest.php | 19 +++++++++-- .../Http/Requests/User/UpdateRequest.php | 22 +++++++++++-- .../Users/Http/Requests/User/ViewRequest.php | 3 ++ publishes/acacia/Users/Js/Pages/Create.vue | 2 +- publishes/acacia/Users/Js/Pages/Edit.vue | 2 +- publishes/acacia/Users/Js/Pages/Index.vue | 12 +++---- .../Users/Js/Pages/Partials/CreateForm.vue | 29 +++++++++++++++- .../Users/Js/Pages/Partials/EditForm.vue | 33 +++++++++++++++++-- .../Users/Js/Pages/Partials/ShowForm.vue | 1 + publishes/acacia/Users/Js/Pages/Show.vue | 2 +- publishes/acacia/Users/Models/User.php | 4 ++- .../stubs/js/pages/fields/password.stub | 18 +++++++++- src/Commands/stubs/request.roles.stub | 1 + src/Commands/stubs/request.stub | 2 +- src/Commands/stubs/request.users.stub | 2 ++ 22 files changed, 166 insertions(+), 46 deletions(-) diff --git a/publishes/acacia/Core/Constants/FormFields.php b/publishes/acacia/Core/Constants/FormFields.php index 0536b50..e1afe89 100644 --- a/publishes/acacia/Core/Constants/FormFields.php +++ b/publishes/acacia/Core/Constants/FormFields.php @@ -18,4 +18,5 @@ class FormFields const MASK = "mask"; const MULTI_CHECKBOX = "multi-checkbox"; const PASSWORD = "password"; + const SECRET = "secret"; } diff --git a/publishes/acacia/Core/Repos/GPanelRepo.php b/publishes/acacia/Core/Repos/GPanelRepo.php index fea4359..e1920bb 100644 --- a/publishes/acacia/Core/Repos/GPanelRepo.php +++ b/publishes/acacia/Core/Repos/GPanelRepo.php @@ -90,7 +90,7 @@ public static function generateBlueprintFromTable(string $tableName, bool $force $field->title = \Str::replace("_"," ",\Str::title($key)); $field->name = $key; $field->db_type = $column->getType()->getName(); - $field->html_type = get_default_html_field($column->getType()->getName()); + $field->html_type = get_default_html_field($column->getType()->getName(), $column->getName()); $field->has_options = false; $field->is_guarded = in_array($column->getName(),["id","password","username","created_at","updated_at"]); $field->is_vue = true; @@ -245,12 +245,6 @@ private static function makeServerValidation(Field $field, Column $column, ?stri } } - if (in_array($column->getName(),['password','user_password'])) { - $validation->merge([ - 'confirmed', - 'Illuminate\Validation\Rules\Password::min(8)' - ]); - } $otherRules = match ($field->db_type) { @@ -259,10 +253,20 @@ private static function makeServerValidation(Field $field, Column $column, ?stri "boolean","bool","tinyint", => "boolean", "json","longtext","relationship" => "array", "date","datetime","timestamp" => "date", - default => 'string' + default => 'string', }; - $validation->get('store')->push($otherRules); - $validation->get('update')->push($otherRules); + + if (in_array($column->getName(),['password','user_password'])) { + $validation->get('store') + ->push('confirmed') + ->push('Password::min(8)->mixedCase()->numbers()->symbols()'); + $validation->get('update') + ->push('confirmed') + ->push('Illuminate\Validation\Rules\Password::min(8)->mixedCase()->numbers()->symbols()'); + } else { + $validation->get('store')->push($otherRules); + $validation->get('update')->push($otherRules); + } return $validation->toJson(); } diff --git a/publishes/acacia/Core/Utilities/helpers.php b/publishes/acacia/Core/Utilities/helpers.php index 93258b8..380ef5f 100644 --- a/publishes/acacia/Core/Utilities/helpers.php +++ b/publishes/acacia/Core/Utilities/helpers.php @@ -91,7 +91,7 @@ function prepare_menu(EloquentCollection $menu): EloquentCollection } else { $item->active = $item->active_pattern && Route::is($item->active_pattern); } - $item->shown = Auth::check() && (!$item->permission_name || Auth::user()->hasPermissionTo($item->permission_name)); + $item->shown = Auth::check() && (!$item->permission_name || Auth::user()->can($item->permission_name)); return $item; }); } @@ -110,8 +110,14 @@ function menu_has_active_child(Collection $children): bool } if (!function_exists("get_default_html_field")) { - function get_default_html_field(string $dbColumnType): string + function get_default_html_field(string $dbColumnType, ?string $columnName = null): string { + if (in_array($columnName,['password','user_password'])) { + return FormFields::PASSWORD; + } + if (in_array($columnName,['secret','api_token'])) { + return FormFields::SECRET; + } return match ($dbColumnType) { "boolean", "bool", "tinyinteger" => FormFields::SWITCH, "text", "longtext" => FormFields::TEXTAREA, diff --git a/publishes/acacia/Users/Database/Seeders/UsersDatabaseSeeder.php b/publishes/acacia/Users/Database/Seeders/UsersDatabaseSeeder.php index 8b9021d..3cd62fa 100644 --- a/publishes/acacia/Users/Database/Seeders/UsersDatabaseSeeder.php +++ b/publishes/acacia/Users/Database/Seeders/UsersDatabaseSeeder.php @@ -2,10 +2,8 @@ namespace Acacia\Users\Database\Seeders; -use App\Models\User; use Illuminate\Database\Seeder; use Illuminate\Database\Eloquent\Model; -use Spatie\Permission\Models\Role; class UsersDatabaseSeeder extends Seeder { @@ -27,19 +25,7 @@ public function run() "users.force-delete", "users.review", ]; - try {// Create default admin user - $user = User::firstOrCreate(['email' => 'admin@savannabits.com'],[ - 'email' => 'admin@savannabits.com', - 'name' => 'System Admin', - 'email_verified_at' => now(), - 'password' => \Hash::make('password'), - ]); - - // Give admin role - $admin = Role::query()->where('name','=','administrator')->first(); - if (!$user->hasRole($admin)) { - $user->roles()->save($admin); - } + try { \Savannabits\Acacia\Helpers\Permissions::seedPermissions($perms); } catch (\Throwable $e) { \Log::info($e); diff --git a/publishes/acacia/Users/Http/Requests/User/DestroyRequest.php b/publishes/acacia/Users/Http/Requests/User/DestroyRequest.php index 67a4256..324e977 100644 --- a/publishes/acacia/Users/Http/Requests/User/DestroyRequest.php +++ b/publishes/acacia/Users/Http/Requests/User/DestroyRequest.php @@ -4,6 +4,9 @@ use Illuminate\Foundation\Http\FormRequest; use Acacia\Users\Models\User; +use Illuminate\Validation\Rule; +use Illuminate\Validation\Rules\Password; + class DestroyRequest extends FormRequest { /** diff --git a/publishes/acacia/Users/Http/Requests/User/DtRequest.php b/publishes/acacia/Users/Http/Requests/User/DtRequest.php index fdc2189..e30f1af 100644 --- a/publishes/acacia/Users/Http/Requests/User/DtRequest.php +++ b/publishes/acacia/Users/Http/Requests/User/DtRequest.php @@ -4,6 +4,9 @@ use Illuminate\Foundation\Http\FormRequest; use Acacia\Users\Models\User; +use Illuminate\Validation\Rule; +use Illuminate\Validation\Rules\Password; + class DtRequest extends FormRequest { /** diff --git a/publishes/acacia/Users/Http/Requests/User/IndexRequest.php b/publishes/acacia/Users/Http/Requests/User/IndexRequest.php index cd41253..ad1081a 100644 --- a/publishes/acacia/Users/Http/Requests/User/IndexRequest.php +++ b/publishes/acacia/Users/Http/Requests/User/IndexRequest.php @@ -4,6 +4,9 @@ use Illuminate\Foundation\Http\FormRequest; use Acacia\Users\Models\User; +use Illuminate\Validation\Rule; +use Illuminate\Validation\Rules\Password; + class IndexRequest extends FormRequest { /** diff --git a/publishes/acacia/Users/Http/Requests/User/StoreRequest.php b/publishes/acacia/Users/Http/Requests/User/StoreRequest.php index ad26417..94e5c34 100644 --- a/publishes/acacia/Users/Http/Requests/User/StoreRequest.php +++ b/publishes/acacia/Users/Http/Requests/User/StoreRequest.php @@ -4,6 +4,9 @@ use Illuminate\Foundation\Http\FormRequest; use Acacia\Users\Models\User; +use Illuminate\Validation\Rule; +use Illuminate\Validation\Rules\Password; + class StoreRequest extends FormRequest { /** @@ -15,9 +18,21 @@ public function rules(): array { return [ "name" => ["required", "string"], - "email" => ["required", "string"], + "email" => [ + "required", + "email", + Rule::unique("users", "email"), + "string", + ], "email_verified_at" => ["nullable", "date"], - "password" => ["required", "string"], + "password" => [ + "required", + "confirmed", + Password::min(8) + ->mixedCase() + ->numbers() + ->symbols(), + ], "remember_token" => ["nullable", "string"], ]; } diff --git a/publishes/acacia/Users/Http/Requests/User/UpdateRequest.php b/publishes/acacia/Users/Http/Requests/User/UpdateRequest.php index 4f58c62..04a3856 100644 --- a/publishes/acacia/Users/Http/Requests/User/UpdateRequest.php +++ b/publishes/acacia/Users/Http/Requests/User/UpdateRequest.php @@ -4,6 +4,9 @@ use Illuminate\Foundation\Http\FormRequest; use Acacia\Users\Models\User; +use Illuminate\Validation\Rule; +use Illuminate\Validation\Rules\Password; + class UpdateRequest extends FormRequest { /** @@ -15,9 +18,24 @@ public function rules(): array { return [ "name" => ["sometimes", "string"], - "email" => ["sometimes", "string"], + "email" => [ + "sometimes", + "email", + Rule::unique("users", "email")->ignore( + $this->user->getKey(), + $this->user->getKeyName() + ), + "string", + ], "email_verified_at" => ["nullable", "date"], - "password" => ["sometimes", "string"], + "password" => [ + "sometimes", + "confirmed", + Illuminate\Validation\Rules\Password::min(8) + ->mixedCase() + ->numbers() + ->symbols(), + ], "remember_token" => ["nullable", "string"], ]; } diff --git a/publishes/acacia/Users/Http/Requests/User/ViewRequest.php b/publishes/acacia/Users/Http/Requests/User/ViewRequest.php index ddb43f0..81aaeea 100644 --- a/publishes/acacia/Users/Http/Requests/User/ViewRequest.php +++ b/publishes/acacia/Users/Http/Requests/User/ViewRequest.php @@ -4,6 +4,9 @@ use Illuminate\Foundation\Http\FormRequest; use Acacia\Users\Models\User; +use Illuminate\Validation\Rule; +use Illuminate\Validation\Rules\Password; + class ViewRequest extends FormRequest { /** diff --git a/publishes/acacia/Users/Js/Pages/Create.vue b/publishes/acacia/Users/Js/Pages/Create.vue index 44911a0..973474b 100644 --- a/publishes/acacia/Users/Js/Pages/Create.vue +++ b/publishes/acacia/Users/Js/Pages/Create.vue @@ -30,7 +30,7 @@ import Backend from "@Acacia/Core/Js/Layouts/Backend.vue"; import BackLink from "@Acacia/Core/Js/Components/BackLink.vue"; import route from "ziggy-js"; import { Inertia } from "@inertiajs/inertia"; -import CreateForm from "./Partials/CreateForm.vue"; +import CreateForm from "@Acacia/Users/Js/Pages/Partials/CreateForm.vue"; import { useConfirm } from "primevue/useconfirm"; import { useToast } from "primevue/usetoast"; diff --git a/publishes/acacia/Users/Js/Pages/Edit.vue b/publishes/acacia/Users/Js/Pages/Edit.vue index 61d8684..d03edae 100644 --- a/publishes/acacia/Users/Js/Pages/Edit.vue +++ b/publishes/acacia/Users/Js/Pages/Edit.vue @@ -32,7 +32,7 @@ import route from "ziggy-js"; import { Inertia } from "@inertiajs/inertia"; import { useConfirm } from "primevue/useconfirm"; import { useToast } from "primevue/usetoast"; -import EditForm from "./Partials/EditForm.vue"; +import EditForm from "@Acacia/Users/Js/Pages/Partials/EditForm.vue"; const model = computed(() => usePage().props.value?.model); const confirm = useConfirm(); diff --git a/publishes/acacia/Users/Js/Pages/Index.vue b/publishes/acacia/Users/Js/Pages/Index.vue index 22f6bc6..c65e0fc 100644 --- a/publishes/acacia/Users/Js/Pages/Index.vue +++ b/publishes/acacia/Users/Js/Pages/Index.vue @@ -8,11 +8,11 @@
-
+
- + + +
+
+ + +
+
@@ -57,6 +83,7 @@ import { Inertia } from "@inertiajs/inertia"; import Message from "primevue/message"; import InputText from "primevue/inputtext"; import AcaciaDatepicker from "@/Components/AcaciaDatepicker.vue"; +import Password from "primevue/password"; const emit = defineEmits(["created", "error"]); const flash = computed(() => usePage().props?.value?.flash) as any; const existingTables = ref([]); diff --git a/publishes/acacia/Users/Js/Pages/Partials/EditForm.vue b/publishes/acacia/Users/Js/Pages/Partials/EditForm.vue index 0ffa7e7..35b8cc0 100644 --- a/publishes/acacia/Users/Js/Pages/Partials/EditForm.vue +++ b/publishes/acacia/Users/Js/Pages/Partials/EditForm.vue @@ -23,11 +23,39 @@
- + toggleMask + > + + +
+
+ +
+
usePage().props?.value?.flash) as any; diff --git a/publishes/acacia/Users/Js/Pages/Partials/ShowForm.vue b/publishes/acacia/Users/Js/Pages/Partials/ShowForm.vue index 53d63c4..aed2ba3 100644 --- a/publishes/acacia/Users/Js/Pages/Partials/ShowForm.vue +++ b/publishes/acacia/Users/Js/Pages/Partials/ShowForm.vue @@ -79,6 +79,7 @@ import TabView from "primevue/tabview"; import TabPanel from "primevue/tabpanel"; import InputText from "primevue/inputtext"; import AcaciaDatepicker from "@/Components/AcaciaDatepicker.vue"; +import Password from "primevue/password"; const emit = defineEmits(["updated", "error"]); const props = defineProps({ model: {} }); const flash = computed(() => usePage().props?.value?.flash) as any; diff --git a/publishes/acacia/Users/Js/Pages/Show.vue b/publishes/acacia/Users/Js/Pages/Show.vue index 9a644ba..7582440 100644 --- a/publishes/acacia/Users/Js/Pages/Show.vue +++ b/publishes/acacia/Users/Js/Pages/Show.vue @@ -32,7 +32,7 @@ import route from "ziggy-js"; import { Inertia } from "@inertiajs/inertia"; import { useConfirm } from "primevue/useconfirm"; import { useToast } from "primevue/usetoast"; -import ShowForm from "./Partials/ShowForm.vue"; +import ShowForm from "@Acacia/Users/Js/Pages/Partials/ShowForm.vue"; const model = computed(() => usePage().props.value?.model); const confirm = useConfirm(); diff --git a/publishes/acacia/Users/Models/User.php b/publishes/acacia/Users/Models/User.php index 1287742..b91c570 100644 --- a/publishes/acacia/Users/Models/User.php +++ b/publishes/acacia/Users/Models/User.php @@ -58,6 +58,8 @@ protected static function newFactory(): UserFactory public function toSearchableArray(): array { - return $this->only($this->getFillable()); + return collect($this->only($this->getFillable())) + ->merge(["id" => $this->getKey()]) + ->toArray(); } } diff --git a/src/Commands/stubs/js/pages/fields/password.stub b/src/Commands/stubs/js/pages/fields/password.stub index f7692e8..f6c2dcd 100644 --- a/src/Commands/stubs/js/pages/fields/password.stub +++ b/src/Commands/stubs/js/pages/fields/password.stub @@ -1,4 +1,20 @@
- + + +
+
+ + +
+ diff --git a/src/Commands/stubs/request.roles.stub b/src/Commands/stubs/request.roles.stub index fe7fd91..d742514 100644 --- a/src/Commands/stubs/request.roles.stub +++ b/src/Commands/stubs/request.roles.stub @@ -5,6 +5,7 @@ namespace $NAMESPACE$; use Illuminate\Foundation\Http\FormRequest; use $MODULE_NAMESPACE$\$MODULE_NAME$\$MODEL_NAMESPACE$\$MODEL_NAME$; use Illuminate\Validation\Rule; + class $CLASS$ extends FormRequest { /** diff --git a/src/Commands/stubs/request.stub b/src/Commands/stubs/request.stub index d742514..fdee329 100644 --- a/src/Commands/stubs/request.stub +++ b/src/Commands/stubs/request.stub @@ -5,7 +5,7 @@ namespace $NAMESPACE$; use Illuminate\Foundation\Http\FormRequest; use $MODULE_NAMESPACE$\$MODULE_NAME$\$MODEL_NAMESPACE$\$MODEL_NAME$; use Illuminate\Validation\Rule; - +use Illuminate\Validation\Rules\Password; class $CLASS$ extends FormRequest { /** diff --git a/src/Commands/stubs/request.users.stub b/src/Commands/stubs/request.users.stub index fe7fd91..006cd68 100644 --- a/src/Commands/stubs/request.users.stub +++ b/src/Commands/stubs/request.users.stub @@ -5,6 +5,8 @@ namespace $NAMESPACE$; use Illuminate\Foundation\Http\FormRequest; use $MODULE_NAMESPACE$\$MODULE_NAME$\$MODEL_NAMESPACE$\$MODEL_NAME$; use Illuminate\Validation\Rule; +use Illuminate\Validation\Rules\Password; + class $CLASS$ extends FormRequest { /**