From b8c0004271c5c8a99d78950edb6e65d51597612a Mon Sep 17 00:00:00 2001 From: mohamed230101033 Date: Mon, 24 Mar 2025 05:36:16 +0200 Subject: [PATCH 1/2] test --- WebSecService/resources/views/users/profile.blade.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WebSecService/resources/views/users/profile.blade.php b/WebSecService/resources/views/users/profile.blade.php index cc4b19fe..5176e9e8 100644 --- a/WebSecService/resources/views/users/profile.blade.php +++ b/WebSecService/resources/views/users/profile.blade.php @@ -47,4 +47,4 @@ -@endsection +@endsection \ No newline at end of file From 1d02399094a7862be35e59935d50076f31d91cd1 Mon Sep 17 00:00:00 2001 From: mohamed230101033 Date: Mon, 24 Mar 2025 11:56:13 +0200 Subject: [PATCH 2/2] Midterm --- .../Controllers/Web/ProductsController.php | 121 +++++++--- .../Http/Controllers/Web/UsersController.php | 228 +++++++++++------- WebSecService/app/Models/Product.php | 18 +- WebSecService/app/Models/Purchase.php | 21 ++ WebSecService/app/Models/User.php | 8 + WebSecService/composer.json | 3 +- WebSecService/composer.lock | 89 ++++++- WebSecService/config/permission.php | 202 ++++++++++++++++ ...03_24_081800_add_credit_to_users_table.php | 21 ++ ..._24_082603_add_stock_to_products_table.php | 21 ++ ...25_03_24_082634_create_purchases_table.php | 24 ++ ...03_add_created_by_admin_to_users_table.php | 21 ++ .../resources/views/layouts/menu.blade.php | 7 +- .../resources/views/products/edit.blade.php | 29 ++- .../resources/views/products/list.blade.php | 179 ++++++++------ .../views/users/add_credit.blade.php | 25 ++ .../resources/views/users/edit.blade.php | 62 +++-- .../resources/views/users/list.blade.php | 9 +- .../resources/views/users/profile.blade.php | 47 ++-- .../resources/views/users/purchases.blade.php | 38 +++ .../resources/views/users/register.blade.php | 71 +++--- WebSecService/routes/web.php | 10 +- 22 files changed, 935 insertions(+), 319 deletions(-) create mode 100644 WebSecService/app/Models/Purchase.php create mode 100644 WebSecService/config/permission.php create mode 100644 WebSecService/database/migrations/2025_03_24_081800_add_credit_to_users_table.php create mode 100644 WebSecService/database/migrations/2025_03_24_082603_add_stock_to_products_table.php create mode 100644 WebSecService/database/migrations/2025_03_24_082634_create_purchases_table.php create mode 100644 WebSecService/database/migrations/2025_03_24_092003_add_created_by_admin_to_users_table.php create mode 100644 WebSecService/resources/views/users/add_credit.blade.php create mode 100644 WebSecService/resources/views/users/purchases.blade.php diff --git a/WebSecService/app/Http/Controllers/Web/ProductsController.php b/WebSecService/app/Http/Controllers/Web/ProductsController.php index 2d69f221..9ab340b2 100644 --- a/WebSecService/app/Http/Controllers/Web/ProductsController.php +++ b/WebSecService/app/Http/Controllers/Web/ProductsController.php @@ -1,6 +1,7 @@ middleware('auth:web')->except('list'); - } + { + $this->middleware('auth:web')->except('list'); + } - public function list(Request $request) { + public function list(Request $request) + { $query = Product::select("products.*"); - $query->when($request->keywords, - fn($q)=> $q->where("name", "like", "%$request->keywords%")); + $query->when( + $request->keywords, + fn($q) => $q->where("name", "like", "%$request->keywords%") + ); + + $query->when( + $request->min_price, + fn($q) => $q->where("price", ">=", $request->min_price) + ); - $query->when($request->min_price, - fn($q)=> $q->where("price", ">=", $request->min_price)); - - $query->when($request->max_price, fn($q)=> - $q->where("price", "<=", $request->max_price)); - - $query->when($request->order_by, - fn($q)=> $q->orderBy($request->order_by, $request->order_direction??"ASC")); + $query->when($request->max_price, fn($q) => + $q->where("price", "<=", $request->max_price)); + + $query->when( + $request->order_by, + fn($q) => $q->orderBy($request->order_by, $request->order_direction ?? "ASC") + ); $products = $query->get(); return view('products.list', compact('products')); } - public function edit(Request $request, Product $product = null) { - - if(!auth()->user()) return redirect('/'); + public function edit(Request $request, Product $product = null) + { + if (!auth()->user()->hasRole('Employee')) { + abort(403, 'Only employees can manage products.'); + } - $product = $product??new Product(); + $product = $product ?? new Product(); return view('products.edit', compact('product')); } - public function save(Request $request, Product $product = null) { + public function save(Request $request, Product $product = null) + { + if (!auth()->user()->hasRole('Employee')) { + abort(403, 'Only employees can manage products.'); + } $this->validate($request, [ - 'code' => ['required', 'string', 'max:32'], - 'name' => ['required', 'string', 'max:128'], - 'model' => ['required', 'string', 'max:256'], - 'description' => ['required', 'string', 'max:1024'], - 'price' => ['required', 'numeric'], - ]); - - $product = $product??new Product(); + 'code' => ['required', 'string', 'max:32'], + 'name' => ['required', 'string', 'max:128'], + 'model' => ['required', 'string', 'max:256'], + 'description' => ['required', 'string', 'max:1024'], + 'price' => ['required', 'numeric'], + 'photo' => ['required', 'string', 'max:255'], + 'stock' => ['required', 'integer', 'min:0'], + ]); + + $product = $product ?? new Product(); $product->fill($request->all()); $product->save(); return redirect()->route('products_list'); } - public function delete(Request $request, Product $product) { + public function delete(Request $request, Product $product) + { + if (!auth()->user()->hasRole('Employee')) { + abort(403, 'Only employees can manage products.'); + } - if(!auth()->user()->hasPermissionTo('delete_products')) abort(401); + if (!auth()->user()->hasPermissionTo('delete_products')) { + abort(401); + } $product->delete(); return redirect()->route('products_list'); } -} \ No newline at end of file + public function purchase(Request $request, Product $product) + { + $user = auth()->user(); + + // Check if the user is a customer + if (!$user || !$user->hasRole('Customer')) { + abort(403, 'Only customers can purchase products.'); + } + + // Check if the product is in stock + if ($product->stock <= 0) { + return redirect()->back()->withErrors('Product is out of stock.'); + } + + // Check if the user has enough credit + if ($user->credit < $product->price) { + return redirect()->back()->withErrors('Insufficient credit to purchase this product.'); + } + + // Deduct the price from the user's credit + $user->credit -= $product->price; + $user->save(); + + // Reduce the product's stock + $product->stock -= 1; + $product->save(); + + // Record the purchase + $purchase = new Purchase(); + $purchase->user_id = $user->id; + $purchase->product_id = $product->id; + $purchase->price = $product->price; + $purchase->save(); + + return redirect()->route('products_list')->with('success', 'Product purchased successfully!'); + } +} \ No newline at end of file diff --git a/WebSecService/app/Http/Controllers/Web/UsersController.php b/WebSecService/app/Http/Controllers/Web/UsersController.php index 2d892491..d68c7ba6 100644 --- a/WebSecService/app/Http/Controllers/Web/UsersController.php +++ b/WebSecService/app/Http/Controllers/Web/UsersController.php @@ -13,54 +13,68 @@ use App\Http\Controllers\Controller; use App\Models\User; -class UsersController extends Controller { +class UsersController extends Controller +{ - use ValidatesRequests; + use ValidatesRequests; - public function list(Request $request) { - if(!auth()->user()->hasPermissionTo('show_users'))abort(401); + public function __construct() + { + $this->middleware('auth:web')->except(['register', 'doRegister', 'login', 'doLogin']); + } + + public function list(Request $request) + { + if (!auth()->user()->hasPermissionTo('show_users')) + abort(401); $query = User::select('*'); - $query->when($request->keywords, - fn($q)=> $q->where("name", "like", "%$request->keywords%")); + $query->when( + $request->keywords, + fn($q) => $q->where("name", "like", "%$request->keywords%") + ); $users = $query->get(); return view('users.list', compact('users')); } - public function register(Request $request) { + public function register(Request $request) + { return view('users.register'); } - public function doRegister(Request $request) { - - try { - $this->validate($request, [ - 'name' => ['required', 'string', 'min:5'], - 'email' => ['required', 'email', 'unique:users'], - 'password' => ['required', 'confirmed', Password::min(8)->numbers()->letters()->mixedCase()->symbols()], - ]); - } - catch(\Exception $e) { + public function doRegister(Request $request) + { + try { + $this->validate($request, [ + 'name' => ['required', 'string', 'min:5'], + 'email' => ['required', 'email', 'unique:users'], + 'password' => ['required', 'confirmed', Password::min(8)->numbers()->letters()->mixedCase()->symbols()], + ]); + } catch (\Exception $e) { + return redirect()->back()->withInput($request->input())->withErrors('Invalid registration information.'); + } - return redirect()->back()->withInput($request->input())->withErrors('Invalid registration information.'); - } + $user = new User(); + $user->name = $request->name; + $user->email = $request->email; + $user->password = bcrypt($request->password); + $user->credit = 0.00; + $user->created_by_admin = false; // Public registration + $user->save(); - - $user = new User(); - $user->name = $request->name; - $user->email = $request->email; - $user->password = bcrypt($request->password); //Secure - $user->save(); + $user->assignRole('Customer'); return redirect('/'); } - public function login(Request $request) { + public function login(Request $request) + { return view('users.login'); } - public function doLogin(Request $request) { - - if(!Auth::attempt(['email' => $request->email, 'password' => $request->password])) + public function doLogin(Request $request) + { + + if (!Auth::attempt(['email' => $request->email, 'password' => $request->password])) return redirect()->back()->withInput($request->input())->withErrors('Invalid login information.'); $user = User::where('email', $request->email)->first(); @@ -69,26 +83,29 @@ public function doLogin(Request $request) { return redirect('/'); } - public function doLogout(Request $request) { - - Auth::logout(); + public function doLogout(Request $request) + { + + Auth::logout(); return redirect('/'); } - public function profile(Request $request, User $user = null) { + public function profile(Request $request, User $user = null) + { - $user = $user??auth()->user(); - if(auth()->id()!=$user->id) { - if(!auth()->user()->hasPermissionTo('show_users')) abort(401); + $user = $user ?? auth()->user(); + if (auth()->id() != $user->id) { + if (!auth()->user()->hasPermissionTo('show_users')) + abort(401); } $permissions = []; - foreach($user->permissions as $permission) { + foreach ($user->permissions as $permission) { $permissions[] = $permission; } - foreach($user->roles as $role) { - foreach($role->permissions as $permission) { + foreach ($user->roles as $role) { + foreach ($role->permissions as $permission) { $permissions[] = $permission; } } @@ -96,86 +113,86 @@ public function profile(Request $request, User $user = null) { return view('users.profile', compact('user', 'permissions')); } - public function edit(Request $request, User $user = null) { - - $user = $user??auth()->user(); - if(auth()->id()!=$user?->id) { - if(!auth()->user()->hasPermissionTo('edit_users')) abort(401); - } - - $roles = []; - foreach(Role::all() as $role) { - $role->taken = ($user->hasRole($role->name)); - $roles[] = $role; + public function edit(Request $request, User $user = null) + { + if (!auth()->user()->hasPermissionTo('edit_users')) { + abort(401); } - - $permissions = []; - $directPermissionsIds = $user->permissions()->pluck('id')->toArray(); - foreach(Permission::all() as $permission) { - $permission->taken = in_array($permission->id, $directPermissionsIds); - $permissions[] = $permission; - } - + $user = $user ?? new User(); + $roles = Role::all(); + $permissions = Permission::all(); return view('users.edit', compact('user', 'roles', 'permissions')); } - public function save(Request $request, User $user) { - - if(auth()->id()!=$user->id) { - if(!auth()->user()->hasPermissionTo('show_users')) abort(401); + public function save(Request $request, User $user = null) + { + if (!auth()->user()->hasPermissionTo('edit_users')) { + abort(401); } + $this->validate($request, [ + 'name' => ['required', 'string', 'min:5'], + 'email' => ['required', 'email', 'unique:users,email,' . ($user->id ?? 'NULL') . ',id'], + ]); + + $user = $user ?? new User(); $user->name = $request->name; + $user->email = $request->email; + $user->credit = $user->credit ?? 0.00; + $user->created_by_admin = $user->exists ? $user->created_by_admin : true; // Set to true for new users created by admin $user->save(); - if(auth()->user()->hasPermissionTo('admin_users')) { - - $user->syncRoles($request->roles); - $user->syncPermissions($request->permissions); - - Artisan::call('cache:clear'); + if (auth()->user()->hasPermissionTo('admin_users')) { + $roles = $request->roles ?? []; + // Prevent assigning "Employee" role to users who registered publicly + if (!$user->created_by_admin && in_array('Employee', $roles)) { + return redirect()->back()->withErrors('Cannot assign the Employee role to a user who registered publicly.'); + } + $user->syncRoles($roles); + $user->syncPermissions($request->permissions ?? []); } - //$user->syncRoles([1]); - //Artisan::call('cache:clear'); - - return redirect(route('profile', ['user'=>$user->id])); + return redirect()->route('users'); } - public function delete(Request $request, User $user) { + public function delete(Request $request, User $user) + { - if(!auth()->user()->hasPermissionTo('delete_users')) abort(401); + if (!auth()->user()->hasPermissionTo('delete_users')) + abort(401); //$user->delete(); return redirect()->route('users'); } - public function editPassword(Request $request, User $user = null) { + public function editPassword(Request $request, User $user = null) + { - $user = $user??auth()->user(); - if(auth()->id()!=$user?->id) { - if(!auth()->user()->hasPermissionTo('edit_users')) abort(401); + $user = $user ?? auth()->user(); + if (auth()->id() != $user?->id) { + if (!auth()->user()->hasPermissionTo('edit_users')) + abort(401); } return view('users.edit_password', compact('user')); } - public function savePassword(Request $request, User $user) { + public function savePassword(Request $request, User $user) + { + + if (auth()->id() == $user?->id) { - if(auth()->id()==$user?->id) { - $this->validate($request, [ 'password' => ['required', 'confirmed', Password::min(8)->numbers()->letters()->mixedCase()->symbols()], ]); - if(!Auth::attempt(['email' => $user->email, 'password' => $request->old_password])) { - + if (!Auth::attempt(['email' => $user->email, 'password' => $request->old_password])) { + Auth::logout(); return redirect('/'); } - } - else if(!auth()->user()->hasPermissionTo('edit_users')) { + } else if (!auth()->user()->hasPermissionTo('edit_users')) { abort(401); } @@ -183,6 +200,43 @@ public function savePassword(Request $request, User $user) { $user->password = bcrypt($request->password); //Secure $user->save(); - return redirect(route('profile', ['user'=>$user->id])); + return redirect(route('profile', ['user' => $user->id])); + } + public function purchases(Request $request) + { + $user = auth()->user(); + + if (!$user || !$user->hasRole('Customer')) { + abort(403, 'Only customers can view their purchases.'); + } + + $purchases = $user->purchases()->with('product')->get(); + + return view('users.purchases', compact('purchases')); + } + + public function addCredit(Request $request, User $user) + { + if (!auth()->user()->hasRole('Employee')) { + abort(403, 'Only employees can add credit.'); + } + + return view('users.add_credit', compact('user')); + } + + public function saveCredit(Request $request, User $user) + { + if (!auth()->user()->hasRole('Employee')) { + abort(403, 'Only employees can add credit.'); + } + + $this->validate($request, [ + 'credit' => ['required', 'numeric', 'min:0'], + ]); + + $user->credit += $request->credit; + $user->save(); + + return redirect()->route('users')->with('success', 'Credit added successfully!'); } -} \ No newline at end of file +} \ No newline at end of file diff --git a/WebSecService/app/Models/Product.php b/WebSecService/app/Models/Product.php index cb81413c..ae71f605 100644 --- a/WebSecService/app/Models/Product.php +++ b/WebSecService/app/Models/Product.php @@ -3,14 +3,14 @@ use Illuminate\Database\Eloquent\Model; -class Product extends Model { - - protected $fillable = [ - 'code', - 'name', - 'price', - 'model', - 'description', - 'photo' +class Product extends Model +{ + protected $fillable = [ + 'code', 'name', 'model', 'description', 'price', 'photo', 'stock', ]; + + public function purchases() + { + return $this->hasMany(Purchase::class); + } } \ No newline at end of file diff --git a/WebSecService/app/Models/Purchase.php b/WebSecService/app/Models/Purchase.php new file mode 100644 index 00000000..5b1ccd4c --- /dev/null +++ b/WebSecService/app/Models/Purchase.php @@ -0,0 +1,21 @@ +belongsTo(User::class); + } + + public function product() + { + return $this->belongsTo(Product::class); + } +} \ No newline at end of file diff --git a/WebSecService/app/Models/User.php b/WebSecService/app/Models/User.php index d7281e86..e701bd34 100644 --- a/WebSecService/app/Models/User.php +++ b/WebSecService/app/Models/User.php @@ -25,6 +25,8 @@ class User extends Authenticatable 'name', 'email', 'password', + 'credit', + 'created_by_admin', ]; /** @@ -42,6 +44,12 @@ class User extends Authenticatable * * @return array */ + + public function purchases() + { + return $this->hasMany(Purchase::class); + } + protected function casts(): array { return [ diff --git a/WebSecService/composer.json b/WebSecService/composer.json index e0f05f82..a83cf0b0 100644 --- a/WebSecService/composer.json +++ b/WebSecService/composer.json @@ -8,7 +8,8 @@ "require": { "php": "^8.2", "laravel/framework": "^11.31", - "laravel/tinker": "^2.9" + "laravel/tinker": "^2.9", + "spatie/laravel-permission": "^6.16" }, "require-dev": { "fakerphp/faker": "^1.23", diff --git a/WebSecService/composer.lock b/WebSecService/composer.lock index 693619f8..9e3dd328 100644 --- a/WebSecService/composer.lock +++ b/WebSecService/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "626b9e7ddd47fb7eff9aaa53cce0c9ad", + "content-hash": "e31737b84a8dbb627d6fb77189375a8a", "packages": [ { "name": "brick/math", @@ -3299,6 +3299,89 @@ ], "time": "2024-04-27T21:32:50+00:00" }, + { + "name": "spatie/laravel-permission", + "version": "6.16.0", + "source": { + "type": "git", + "url": "https://github.com/spatie/laravel-permission.git", + "reference": "4fa03c06509e037a4d42c131d0f181e3e4bbd483" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/spatie/laravel-permission/zipball/4fa03c06509e037a4d42c131d0f181e3e4bbd483", + "reference": "4fa03c06509e037a4d42c131d0f181e3e4bbd483", + "shasum": "" + }, + "require": { + "illuminate/auth": "^8.12|^9.0|^10.0|^11.0|^12.0", + "illuminate/container": "^8.12|^9.0|^10.0|^11.0|^12.0", + "illuminate/contracts": "^8.12|^9.0|^10.0|^11.0|^12.0", + "illuminate/database": "^8.12|^9.0|^10.0|^11.0|^12.0", + "php": "^8.0" + }, + "require-dev": { + "laravel/passport": "^11.0|^12.0", + "laravel/pint": "^1.0", + "orchestra/testbench": "^6.23|^7.0|^8.0|^9.0|^10.0", + "phpunit/phpunit": "^9.4|^10.1|^11.5" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Spatie\\Permission\\PermissionServiceProvider" + ] + }, + "branch-alias": { + "dev-main": "6.x-dev", + "dev-master": "6.x-dev" + } + }, + "autoload": { + "files": [ + "src/helpers.php" + ], + "psr-4": { + "Spatie\\Permission\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Freek Van der Herten", + "email": "freek@spatie.be", + "homepage": "https://spatie.be", + "role": "Developer" + } + ], + "description": "Permission handling for Laravel 8.0 and up", + "homepage": "https://github.com/spatie/laravel-permission", + "keywords": [ + "acl", + "laravel", + "permission", + "permissions", + "rbac", + "roles", + "security", + "spatie" + ], + "support": { + "issues": "https://github.com/spatie/laravel-permission/issues", + "source": "https://github.com/spatie/laravel-permission/tree/6.16.0" + }, + "funding": [ + { + "url": "https://github.com/spatie", + "type": "github" + } + ], + "time": "2025-02-28T20:29:57+00:00" + }, { "name": "symfony/clock", "version": "v7.2.0", @@ -8078,12 +8161,12 @@ ], "aliases": [], "minimum-stability": "stable", - "stability-flags": [], + "stability-flags": {}, "prefer-stable": true, "prefer-lowest": false, "platform": { "php": "^8.2" }, - "platform-dev": [], + "platform-dev": {}, "plugin-api-version": "2.6.0" } diff --git a/WebSecService/config/permission.php b/WebSecService/config/permission.php new file mode 100644 index 00000000..8e84e9d5 --- /dev/null +++ b/WebSecService/config/permission.php @@ -0,0 +1,202 @@ + [ + + /* + * When using the "HasPermissions" trait from this package, we need to know which + * Eloquent model should be used to retrieve your permissions. Of course, it + * is often just the "Permission" model but you may use whatever you like. + * + * The model you want to use as a Permission model needs to implement the + * `Spatie\Permission\Contracts\Permission` contract. + */ + + 'permission' => Spatie\Permission\Models\Permission::class, + + /* + * When using the "HasRoles" trait from this package, we need to know which + * Eloquent model should be used to retrieve your roles. Of course, it + * is often just the "Role" model but you may use whatever you like. + * + * The model you want to use as a Role model needs to implement the + * `Spatie\Permission\Contracts\Role` contract. + */ + + 'role' => Spatie\Permission\Models\Role::class, + + ], + + 'table_names' => [ + + /* + * When using the "HasRoles" trait from this package, we need to know which + * table should be used to retrieve your roles. We have chosen a basic + * default value but you may easily change it to any table you like. + */ + + 'roles' => 'roles', + + /* + * When using the "HasPermissions" trait from this package, we need to know which + * table should be used to retrieve your permissions. We have chosen a basic + * default value but you may easily change it to any table you like. + */ + + 'permissions' => 'permissions', + + /* + * When using the "HasPermissions" trait from this package, we need to know which + * table should be used to retrieve your models permissions. We have chosen a + * basic default value but you may easily change it to any table you like. + */ + + 'model_has_permissions' => 'model_has_permissions', + + /* + * When using the "HasRoles" trait from this package, we need to know which + * table should be used to retrieve your models roles. We have chosen a + * basic default value but you may easily change it to any table you like. + */ + + 'model_has_roles' => 'model_has_roles', + + /* + * When using the "HasRoles" trait from this package, we need to know which + * table should be used to retrieve your roles permissions. We have chosen a + * basic default value but you may easily change it to any table you like. + */ + + 'role_has_permissions' => 'role_has_permissions', + ], + + 'column_names' => [ + /* + * Change this if you want to name the related pivots other than defaults + */ + 'role_pivot_key' => null, // default 'role_id', + 'permission_pivot_key' => null, // default 'permission_id', + + /* + * Change this if you want to name the related model primary key other than + * `model_id`. + * + * For example, this would be nice if your primary keys are all UUIDs. In + * that case, name this `model_uuid`. + */ + + 'model_morph_key' => 'model_id', + + /* + * Change this if you want to use the teams feature and your related model's + * foreign key is other than `team_id`. + */ + + 'team_foreign_key' => 'team_id', + ], + + /* + * When set to true, the method for checking permissions will be registered on the gate. + * Set this to false if you want to implement custom logic for checking permissions. + */ + + 'register_permission_check_method' => true, + + /* + * When set to true, Laravel\Octane\Events\OperationTerminated event listener will be registered + * this will refresh permissions on every TickTerminated, TaskTerminated and RequestTerminated + * NOTE: This should not be needed in most cases, but an Octane/Vapor combination benefited from it. + */ + 'register_octane_reset_listener' => false, + + /* + * Events will fire when a role or permission is assigned/unassigned: + * \Spatie\Permission\Events\RoleAttached + * \Spatie\Permission\Events\RoleDetached + * \Spatie\Permission\Events\PermissionAttached + * \Spatie\Permission\Events\PermissionDetached + * + * To enable, set to true, and then create listeners to watch these events. + */ + 'events_enabled' => false, + + /* + * Teams Feature. + * When set to true the package implements teams using the 'team_foreign_key'. + * If you want the migrations to register the 'team_foreign_key', you must + * set this to true before doing the migration. + * If you already did the migration then you must make a new migration to also + * add 'team_foreign_key' to 'roles', 'model_has_roles', and 'model_has_permissions' + * (view the latest version of this package's migration file) + */ + + 'teams' => false, + + /* + * The class to use to resolve the permissions team id + */ + 'team_resolver' => \Spatie\Permission\DefaultTeamResolver::class, + + /* + * Passport Client Credentials Grant + * When set to true the package will use Passports Client to check permissions + */ + + 'use_passport_client_credentials' => false, + + /* + * When set to true, the required permission names are added to exception messages. + * This could be considered an information leak in some contexts, so the default + * setting is false here for optimum safety. + */ + + 'display_permission_in_exception' => false, + + /* + * When set to true, the required role names are added to exception messages. + * This could be considered an information leak in some contexts, so the default + * setting is false here for optimum safety. + */ + + 'display_role_in_exception' => false, + + /* + * By default wildcard permission lookups are disabled. + * See documentation to understand supported syntax. + */ + + 'enable_wildcard_permission' => false, + + /* + * The class to use for interpreting wildcard permissions. + * If you need to modify delimiters, override the class and specify its name here. + */ + // 'permission.wildcard_permission' => Spatie\Permission\WildcardPermission::class, + + /* Cache-specific settings */ + + 'cache' => [ + + /* + * By default all permissions are cached for 24 hours to speed up performance. + * When permissions or roles are updated the cache is flushed automatically. + */ + + 'expiration_time' => \DateInterval::createFromDateString('24 hours'), + + /* + * The cache key used to store all permissions. + */ + + 'key' => 'spatie.permission.cache', + + /* + * You may optionally indicate a specific cache driver to use for permission and + * role caching using any of the `store` drivers listed in the cache.php config + * file. Using 'default' here means to use the `default` set in cache.php. + */ + + 'store' => 'default', + ], +]; diff --git a/WebSecService/database/migrations/2025_03_24_081800_add_credit_to_users_table.php b/WebSecService/database/migrations/2025_03_24_081800_add_credit_to_users_table.php new file mode 100644 index 00000000..95195830 --- /dev/null +++ b/WebSecService/database/migrations/2025_03_24_081800_add_credit_to_users_table.php @@ -0,0 +1,21 @@ +decimal('credit', 10, 2)->default(0.00); // Credit with 2 decimal places + }); + } + + public function down(): void + { + Schema::table('users', function (Blueprint $table) { + $table->dropColumn('credit'); + }); + } +}; \ No newline at end of file diff --git a/WebSecService/database/migrations/2025_03_24_082603_add_stock_to_products_table.php b/WebSecService/database/migrations/2025_03_24_082603_add_stock_to_products_table.php new file mode 100644 index 00000000..c21e504c --- /dev/null +++ b/WebSecService/database/migrations/2025_03_24_082603_add_stock_to_products_table.php @@ -0,0 +1,21 @@ +integer('stock')->default(0); // Stock as an integer + }); + } + + public function down(): void + { + Schema::table('products', function (Blueprint $table) { + $table->dropColumn('stock'); + }); + } +}; \ No newline at end of file diff --git a/WebSecService/database/migrations/2025_03_24_082634_create_purchases_table.php b/WebSecService/database/migrations/2025_03_24_082634_create_purchases_table.php new file mode 100644 index 00000000..75420ad6 --- /dev/null +++ b/WebSecService/database/migrations/2025_03_24_082634_create_purchases_table.php @@ -0,0 +1,24 @@ +id(); + $table->foreignId('user_id')->constrained()->onDelete('cascade'); + $table->foreignId('product_id')->constrained()->onDelete('cascade'); + $table->decimal('price', 10, 2); // Price at the time of purchase + $table->integer('quantity')->default(1); // Number of items purchased + $table->timestamps(); + }); + } + + public function down(): void + { + Schema::dropIfExists('purchases'); + } +}; \ No newline at end of file diff --git a/WebSecService/database/migrations/2025_03_24_092003_add_created_by_admin_to_users_table.php b/WebSecService/database/migrations/2025_03_24_092003_add_created_by_admin_to_users_table.php new file mode 100644 index 00000000..ce6d7380 --- /dev/null +++ b/WebSecService/database/migrations/2025_03_24_092003_add_created_by_admin_to_users_table.php @@ -0,0 +1,21 @@ +boolean('created_by_admin')->default(false); + }); + } + + public function down(): void + { + Schema::table('users', function (Blueprint $table) { + $table->dropColumn('created_by_admin'); + }); + } +}; \ No newline at end of file diff --git a/WebSecService/resources/views/layouts/menu.blade.php b/WebSecService/resources/views/layouts/menu.blade.php index 6f0d3475..c534a7e1 100644 --- a/WebSecService/resources/views/layouts/menu.blade.php +++ b/WebSecService/resources/views/layouts/menu.blade.php @@ -27,6 +27,11 @@ + @if(auth()->user()->hasRole('Customer')) + + @endif @@ -40,4 +45,4 @@ @endauth - + \ No newline at end of file diff --git a/WebSecService/resources/views/products/edit.blade.php b/WebSecService/resources/views/products/edit.blade.php index 892cce5f..a3d0cf45 100644 --- a/WebSecService/resources/views/products/edit.blade.php +++ b/WebSecService/resources/views/products/edit.blade.php @@ -1,9 +1,8 @@ @extends('layouts.master') -@section('title', 'Prime Numbers') +@section('title', $product->exists ? 'Edit Product' : 'Add Product') @section('content')
- {{ csrf_field() }} {{ csrf_field() }} @foreach($errors->all() as $error)
@@ -13,35 +12,41 @@
- +
- +
- +
- - + + +
+
+ +
+
+
- - + +
- - + +
-@endsection +@endsection \ No newline at end of file diff --git a/WebSecService/resources/views/products/list.blade.php b/WebSecService/resources/views/products/list.blade.php index 3d25e4ae..70862825 100644 --- a/WebSecService/resources/views/products/list.blade.php +++ b/WebSecService/resources/views/products/list.blade.php @@ -1,85 +1,116 @@ @extends('layouts.master') -@section('title', 'Test Page') +@section('title', 'Products') @section('content') -
-
-

Products

-
-
- @can('add_products') - Add Product - @endcan -
-
-
-
-
- -
-
- -
-
- -
-
- +
+
+

Products

-
- -
-
- -
-
- +
+ @if(auth()->check() && auth()->user()->hasRole('Employee') && auth()->user()->hasPermissionTo('add_products')) + Add Product + @endif
- - +
+
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+
-@foreach($products as $product) -
-
-
-
- photo")}}" class="img-thumbnail" alt="{{$product->name}}" width="100%"> -
-
-
-
-

{{$product->name}}

-
-
- @can('edit_products') - Edit - @endcan -
-
- @can('delete_products') - Delete - @endcan -
-
+ @foreach($products as $product) +
+
+
+
+ photo")}}" class="img-thumbnail" alt="{{$product->name}}" + width="100%"> +
+
+
+
+

{{$product->name}}

+
+ @if(auth()->check() && auth()->user()->hasRole('Customer')) +
+
+ @csrf + +
+
+ @endif +
+ @if(auth()->check() && auth()->user()->hasRole('Employee') && auth()->user()->hasPermissionTo('edit_products')) + Edit + @endif +
+
+ @if(auth()->check() && auth()->user()->hasRole('Employee') && auth()->user()->hasPermissionTo('delete_products')) + Delete + @endif +
+
- - - - - - -
Name{{$product->name}}
Model{{$product->model}}
Code{{$product->code}}
Price{{$product->price}}
Description{{$product->description}}
+ + + + + + + + + + + + + + + + + + + + + + + + + +
Name{{$product->name}}
Model{{$product->model}}
Code{{$product->code}}
Price${{$product->price}}
Stock{{$product->stock}}
Description{{$product->description}}
+
-
-@endforeach + @endforeach @endsection \ No newline at end of file diff --git a/WebSecService/resources/views/users/add_credit.blade.php b/WebSecService/resources/views/users/add_credit.blade.php new file mode 100644 index 00000000..1fe2f4d9 --- /dev/null +++ b/WebSecService/resources/views/users/add_credit.blade.php @@ -0,0 +1,25 @@ +@extends('layouts.master') +@section('title', 'Add Credit') +@section('content') +
+
+
+ {{ csrf_field() }} + @foreach($errors->all() as $error) +
+ Error! {{$error}} +
+ @endforeach + +
+
+ + +
+
+ + +
+
+
+@endsection \ No newline at end of file diff --git a/WebSecService/resources/views/users/edit.blade.php b/WebSecService/resources/views/users/edit.blade.php index d13a686d..47845fe6 100644 --- a/WebSecService/resources/views/users/edit.blade.php +++ b/WebSecService/resources/views/users/edit.blade.php @@ -1,17 +1,6 @@ @extends('layouts.master') -@section('title', 'Edit User') +@section('title', $user->exists ? 'Edit User' : 'Add User') @section('content') - -
@@ -21,37 +10,46 @@ Error! {{$error}}
@endforeach + +
+
+ + +
+
- - + +
- @can('admin_users') -
- (reset) - name, $user->roles->pluck('name')->toArray()) ? 'checked' : ''}}> + +
@endforeach - +
-
- (reset) - hasPermissionTo($permission->name) ? 'checked' : ''}}> + +
@endforeach - +
- @endcan + @endif
-@endsection +@endsection \ No newline at end of file diff --git a/WebSecService/resources/views/users/list.blade.php b/WebSecService/resources/views/users/list.blade.php index 5bf682eb..d10900b0 100644 --- a/WebSecService/resources/views/users/list.blade.php +++ b/WebSecService/resources/views/users/list.blade.php @@ -9,7 +9,7 @@
- +
@@ -46,6 +46,9 @@ @can('edit_users') id])}}'>Edit @endcan + @if(auth()->user()->hasRole('Employee') && $user->hasRole('Customer')) + id])}}'>Add Credit + @endif @can('admin_users') id])}}'>Change Password @endcan @@ -58,6 +61,4 @@
- - -@endsection +@endsection \ No newline at end of file diff --git a/WebSecService/resources/views/users/profile.blade.php b/WebSecService/resources/views/users/profile.blade.php index 5176e9e8..92c6fb41 100644 --- a/WebSecService/resources/views/users/profile.blade.php +++ b/WebSecService/resources/views/users/profile.blade.php @@ -1,19 +1,21 @@ @extends('layouts.master') -@section('title', 'User Profile') +@section('title', 'Profile') @section('content') -
-
+
+
+

Profile

+
+
+
+
- - - - - - + + + @@ -21,30 +23,15 @@
Name{{$user->name}}
Email{{$user->email}}
Name{{auth()->user()->name}}
Email{{auth()->user()->email}}
Credit${{auth()->user()->credit}}
Roles - @foreach($user->roles as $role) + @foreach(auth()->user()->roles as $role) {{$role->name}} @endforeach
Permissions - @foreach($permissions as $permission) - {{$permission->display_name}} + @foreach(auth()->user()->getAllPermissions() as $permission) + {{$permission->name}} @endforeach
- -
-
-
- @if(auth()->user()->hasPermissionTo('admin_users')||auth()->id()==$user->id) - - @else -
-
- @endif - @if(auth()->user()->hasPermissionTo('edit_users')||auth()->id()==$user->id) -
- Edit -
- @endif -
+ @if(auth()->check() && auth()->user()->hasRole('Admin')) + Change Password + @endif
@endsection \ No newline at end of file diff --git a/WebSecService/resources/views/users/purchases.blade.php b/WebSecService/resources/views/users/purchases.blade.php new file mode 100644 index 00000000..b47c1d4d --- /dev/null +++ b/WebSecService/resources/views/users/purchases.blade.php @@ -0,0 +1,38 @@ +@extends('layouts.master') +@section('title', 'My Purchases') +@section('content') +
+
+

My Purchases

+
+
+ +@if($purchases->isEmpty()) +

No purchases yet.

+@else +
+
+ + + + + + + + + + + @foreach($purchases as $purchase) + + + + + + + + @endforeach +
#Product NamePriceQuantityPurchased At
{{$purchase->id}}{{$purchase->product->name}}${{$purchase->price}}{{$purchase->quantity}}{{$purchase->created_at}}
+
+
+@endif +@endsection \ No newline at end of file diff --git a/WebSecService/resources/views/users/register.blade.php b/WebSecService/resources/views/users/register.blade.php index f0a52c2c..7be375e7 100644 --- a/WebSecService/resources/views/users/register.blade.php +++ b/WebSecService/resources/views/users/register.blade.php @@ -2,37 +2,44 @@ @section('title', 'Register') @section('content')
-
-
- - {{ csrf_field() }} -
- @foreach($errors->all() as $error) -
- Error! {{$error}} -
- @endforeach -
- - -
-
- - -
-
- - -
-
- - -
-
- -
- +
+

Customer Registration

+

This form is for customers only. Employee accounts must be created by an administrator.

+
+ {{ csrf_field() }} + @foreach($errors->all() as $error) +
+ Error! {{$error}} +
+ @endforeach + +
+
+ + +
+
+
+
+ + +
+
+
+
+ + +
+
+
+
+ + +
+
+ + +
-
-@endsection +@endsection \ No newline at end of file diff --git a/WebSecService/routes/web.php b/WebSecService/routes/web.php index f6801f6d..98dde84a 100644 --- a/WebSecService/routes/web.php +++ b/WebSecService/routes/web.php @@ -12,24 +12,28 @@ Route::get('users', [UsersController::class, 'list'])->name('users'); Route::get('profile/{user?}', [UsersController::class, 'profile'])->name('profile'); Route::get('users/edit/{user?}', [UsersController::class, 'edit'])->name('users_edit'); -Route::post('users/save/{user}', [UsersController::class, 'save'])->name('users_save'); +Route::post('users/save/{user?}', [UsersController::class, 'save'])->name('users_save'); Route::get('users/delete/{user}', [UsersController::class, 'delete'])->name('users_delete'); Route::get('users/edit_password/{user?}', [UsersController::class, 'editPassword'])->name('edit_password'); Route::post('users/save_password/{user}', [UsersController::class, 'savePassword'])->name('save_password'); - +Route::get('users/add_credit/{user}', [UsersController::class, 'addCredit'])->name('users_add_credit'); +Route::post('users/save_credit/{user}', [UsersController::class, 'saveCredit'])->name('users_save_credit'); Route::get('products', [ProductsController::class, 'list'])->name('products_list'); Route::get('products/edit/{product?}', [ProductsController::class, 'edit'])->name('products_edit'); Route::post('products/save/{product?}', [ProductsController::class, 'save'])->name('products_save'); Route::get('products/delete/{product}', [ProductsController::class, 'delete'])->name('products_delete'); +Route::post('products/purchase/{product}', [ProductsController::class, 'purchase'])->name('products_purchase'); + +Route::get('purchases', [UsersController::class, 'purchases'])->name('purchases'); Route::get('/', function () { return view('welcome'); }); Route::get('/multable', function (Request $request) { - $j = $request->number??5; + $j = $request->number ?? 5; $msg = $request->msg; return view('multable', compact("j", "msg")); });