CVE-2022-22108: Missing Authorization (CWE-862)
value: In Daybyday CRM, versions 2.0.0 through 2.2.0 are vulnerable to Missing Authorization. An attacker that has the lowest privileges account (employee type user), can view the absences of all users in the system including administrators. This type of user is not authorized to view this kind of information.
CWE description: The product does not perform an authorization check when an actor attempts to access a resource or perform an action.
CVSS v3 base score: 4.3
Published: 2022-01-05T15:15Z
Repository: https://github.com/Bottelet/DaybydayCRM
Language: PHP
Files changed: app/Http/Controllers/UsersController.php, app/Models/User.php, resources/views/users/form.blade.php
Commit date: 2021-06-25 21:31:54 +0200
Commit message: ensure only admin and owner can change role, and password, unless it is yourself

Diff: diff --git a/app/Http/Controllers/UsersController.php b/app/Http/Controllers/UsersController.php
index 3640868..d2893b3 100644
--- a/app/Http/Controllers/UsersController.php
+++ b/app/Http/Controllers/UsersController.php
@@ -38,10 +38,14 @@ class UsersController extends Controller
         return view('users.index')->withUsers(User::all());
     }
 
     public function calendarUsers()
     {
+        if (!auth()->user()->can('absence-view')) {
+            session()->flash('flash_message_warning', __('You do not have permission to view this page'));
+            return redirect()->back();
+        }
         return User::with(['department', 'absences' =>  function ($q) {
             return $q->whereBetween('start_at', [today()->subWeeks(2)->startOfDay(), today()->addWeeks(4)->endOfDay()])
                       ->orWhereBetween('end_at', [today()->subWeeks(2)->startOfDay(), today()->addWeeks(4)->endOfDay()]);
         }
         ])->get();
@@ -242,10 +246,16 @@ class UsersController extends Controller
     {
         $user = $this->findByExternalId($external_id);
         $password = bcrypt($request->password);
         $role = $request->roles;
         $department = $request->departments;
+
+        if( !auth()->user()->canChangePasswordOn($user) ) {
+            unset($request['password']);
+        }
+
+
         if ($request->hasFile('image_path')) {
             $companyname = Setting::first()->external_id;
             $file =  $request->file('image_path');
 
             $filename = str_random(8) . '_' . $file->getClientOriginalName() ;
@@ -271,11 +281,13 @@ class UsersController extends Controller
         $user->fill($input)->save();
         $role = $user->roles->first();
         if ($role && $role->name == Role::OWNER_ROLE && $owners->count() <= 1) {
             Session()->flash('flash_message_warning', __('Not able to change owner role, please choose a new owner first'));
         } else {
-            $user->roles()->sync([$request->roles]);
+            if(auth()->user()->canChangeRole() ) {
+                $user->roles()->sync([$request->roles]);
+            }
         }
         $user->department()->sync([$department]);
 
         Session()->flash('flash_message', __('User successfully updated'));
         return redirect()->back();
diff --git a/app/Models/User.php b/app/Models/User.php
index 2350ecb..bdcf3dd 100644
--- a/app/Models/User.php
+++ b/app/Models/User.php
@@ -96,10 +96,23 @@ class User extends Authenticatable
     public function tokens()
     {
         return $this->hasMany(Token::class, 'user_id', 'id');
     }
 
+    public function canChangePasswordOn(User $user)
+    {
+        if($this->id === $user->id || ( $this->roles->first()->name == Role::OWNER_ROLE || $this->roles->first()->name == Role::ADMIN_ROLE)) {
+            return true;
+        }
+
+        return false;
+    }
+
+    public function canChangeRole()
+    {
+        return $this->roles->first()->name == Role::OWNER_ROLE || $this->roles->first()->name == Role::ADMIN_ROLE;
+    }
 
 
     public function isOnline()
     {
         return Cache::has('user-is-online-' . $this->id);
diff --git a/resources/views/users/form.blade.php b/resources/views/users/form.blade.php
index c0477d2..27d92a1 100644
--- a/resources/views/users/form.blade.php
+++ b/resources/views/users/form.blade.php
@@ -59,39 +59,43 @@
     </div>
 </div>
 <div class="col-sm-12">
     <hr>
 </div>
-
+@if(isset($user) && auth()->user()->canChangePasswordOn($user))
 <div class="col-sm-3">
     <label for="name" class="base-input-label">@lang('Security')</label>
 </div>
+
 <div class="col-sm-9">
     <div class="form-group col-sm-8">
         <label for="password" class="control-label thin-weight">@lang('Password')</label>
         <input type="password" name="password" class="form-control" value="">
     </div>
     <div class="form-group col-sm-8">
         <label for="password_confirmation" class="control-label thin-weight">@lang('Confirm password')</label>
         <input type="password" name="password_confirmation" class="form-control" value="">
     </div>
 </div>
+@endif
 <div class="col-sm-12">
     <hr>
 </div>
 <div class="col-sm-3">
     <label for="name" class="base-input-label">@lang('Access')</label>
 </div>
 <div class="col-sm-9">
+@if(isset($user) && auth()->user()->canChangeRole())
     <div class="form-group col-sm-8">
         <label for="roles" class="control-label thin-weight">@lang('Assign role')</label>
         <select name="roles" id="" class="form-control">
         @foreach($roles as $key => $role)
                 <option {{ isset($user) && optional($user->userRole)->role_id === $key ? "selected" : "" }} value="{{$key}}">{{$role}}</option>
         @endforeach
         </select>
     </div>
+@endif
     <div class="form-group col-sm-8">
         <label for="departments" class="control-label thin-weight">@lang('Assign department')</label>
         <select name="departments" id="" class="form-control">
             @foreach($departments as $key => $department)
                 <option {{ isset($user) && $user->department->first()->id === $key ? "selected" : "" }} value="{{$key}}">{{$department}}</option>

Vulnerable code:             $user->roles()->sync([$request->roles]);

Fixed code:         if (!auth()->user()->can('absence-view')) {
            session()->flash('flash_message_warning', __('You do not have permission to view this page'));
            return redirect()->back();
        }

        if( !auth()->user()->canChangePasswordOn($user) ) {
            unset($request['password']);
        }


            if(auth()->user()->canChangeRole() ) {
                $user->roles()->sync([$request->roles]);
            }
    public function canChangePasswordOn(User $user)
    {
        if($this->id === $user->id || ( $this->roles->first()->name == Role::OWNER_ROLE || $this->roles->first()->name == Role::ADMIN_ROLE)) {
            return true;
        }

        return false;
    }

    public function canChangeRole()
    {
        return $this->roles->first()->name == Role::OWNER_ROLE || $this->roles->first()->name == Role::ADMIN_ROLE;
    }
@if(isset($user) && auth()->user()->canChangePasswordOn($user))

@endif
@if(isset($user) && auth()->user()->canChangeRole())
@endif