Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Spatie Team Enabled Issues #42

Open
DCox2016 opened this issue Oct 1, 2023 · 14 comments
Open

Spatie Team Enabled Issues #42

DCox2016 opened this issue Oct 1, 2023 · 14 comments
Assignees
Labels
bug Something isn't working

Comments

@DCox2016
Copy link

DCox2016 commented Oct 1, 2023

  • Laravel CRM Version: 10.10
  • PHP Version: 8.2.10

Description:

Enabling Spatie teams breaks role and permissions model.

Steps To Reproduce:

  • Enable Teams in the env file
    LARAVEL_CRM_TEAMS=true

  • Run artisan command: php artisan laravelcrm:install

  • Enter first name

  • Enter last name

  • Enter email address

  • Enter password

SQLSTATE[23000]: Integrity constraint violation: 1048 Column 'team_id' cannot be null (Connection: mysql, SQL: insert into model_has_roles (model_id, model_type, role_id, team_id) values (1, App\Models\User, 1, ?))

@DCox2016
Copy link
Author

DCox2016 commented Oct 1, 2023

To fix this issue, I had to add a check to the LaravelCrmInstall.php file
`
if ($user = \App\User::where('email', $email)->first()) {
$this->info('User already exists, granting crm access...');

            $team = null;  // Initialize the $team variable
        
            // Check if LARAVEL_CRM_TEAMS key in .env is set to true
            if (env('LARAVEL_CRM_TEAMS', false)) {
                $team = Team::firstOrCreate(['name' => 'Owner','user_id' => $user->id, 'team_id' => 1]);
                $user->current_crm_team_id = $team->id;  // Associate the user with the team
                $user->save();
            }
        
            $user->update([
                'crm_access' => 1,
            ]);
        
            if (!$team) {
                if (!$user->hasRole('Owner')) {
                    $user->assignRole('Owner');
                }
            } else {
                if (!$user->hasRole('Owner')) {
                    $user->assignRole('Owner', $team);
                }
            }
        
            $this->info('User access and role updated.');
        } else {
            $user = \App\User::forceCreate([
                'name' => trim($firstname.' '.$lastname),
                'email' => $email,
                'password' => Hash::make($password),
                'crm_access' => 1,
            ]);
        
            $team = null;  // Initialize the $team variable
        
            // Check if LARAVEL_CRM_TEAMS key in .env is set to true
            if (env('LARAVEL_CRM_TEAMS', false)) {
                $team = Team::firstOrCreate(['name' => 'Owner','user_id' => $user->id, 'team_id' => 1]);
                $user->current_crm_team_id = $team->id;  // Associate the user with the team
                $user->save();
            }
        
            if ($team) {
                $user->assignRole('Owner', $team);
            } else {
                $user->assignRole('Owner');
            }
        
            $this->info('User created with owner role');
        }`

@DCox2016
Copy link
Author

DCox2016 commented Oct 1, 2023

I also had to edit Spatie's HasRole trait function assignRole() to accept a team object. As of now the function only accepts a role but it teams is enabled and you don't pass a team object you will get the error above.
assignRole()
` public function assignRole($roles, $team = null)
{
// Ensure roles is always an array
if (!is_array($roles)) {
$roles = [$roles];
}

    $roles = collect($roles)
        ->reduce(function ($array, $role) use ($team) {
            $role = $this->getStoredRole($role);
            if (! $role instanceof Role) {
                return $array;
            }

            $this->ensureModelSharesGuard($role);

            $array[$role->getKey()] = PermissionRegistrar::$teams && ! is_a($this, Permission::class) ?
                [PermissionRegistrar::$teamsKey => ($team ? $team->id : null)] : [];

            return $array;
        }, []);

    $model = $this->getModel();

    if ($model->exists) {
        $this->roles()->sync($roles, false);
        $model->load('roles');
    } else {
        $class = \get_class($model);
        $class::saved(function ($object) use ($roles, $model) {
            if ($model->getKey() != $object->getKey()) {
                return;
            }
            $model->roles()->sync($roles, false);
            $model->load('roles');
        });
    }

    if (is_a($this, get_class($this->getPermissionClass()))) {
        $this->forgetCachedPermissions();
    }

    return $this;
}`
image

@DCox2016
Copy link
Author

DCox2016 commented Oct 1, 2023

Part 2 of the teams enabled bug
image
This a user has has rights to view everything, but the nav menu items are not displaying. This is because we have enabled teams which is a user -> teams -> permissions model so all the @can and the middlewares do not work.

@DCox2016
Copy link
Author

DCox2016 commented Oct 1, 2023

Steps we did to fix this issue

  • After login set users session team_id to there assigned id.
  • Make a Teams model that extends the VentureDrake Team model
    Set the default gaurd_name = 'web'
    *Eloquent was not work correctly so I made the permissions function
   public function permissions(): BelongsToMany
    {
        return $this->morphToMany(
            config('permission.models.permission'),
            'model',
            config('permission.table_names.model_has_permissions'),
            config('permission.column_names.model_morph_key'),
            'permission_id'
        )
        ->withPivot('team_id')
        ->wherePivot('team_id', $this->id)
        ->as('access');
    }
  • Make a TeamPermissionsController use VentureDrake\LaravelCrm\Models\Permission and App\Models\Teams.
    
    {
        $allTeams = Teams::all()->groupBy('team_id')->map(function ($teamGroup) {
            return $teamGroup->first();
        });
        $allPermissions = Permission::all();
        return view('admin.dashboard.teams_permissions', compact('team', 'allPermissions', 'allTeams'));
    }

    public function update(Request $request, Teams $team)
    {
        $currentPermissions = $team->permissions->pluck('id')->toArray();

        // Get the desired permissions from the request.
        $desiredPermissions = $request->input('permissions');

        foreach ($currentPermissions as $permissionId) {
            if (!in_array($permissionId, $desiredPermissions)) {
                $team->permissions()->detach($permissionId);
            }
        }

        foreach ($desiredPermissions as $permissionId) {
            if (!in_array($permissionId, $currentPermissions)) {
                $team->permissions()->attach($permissionId, ['team_id' => $team->id]);
            }
        }

        return redirect()->route('teams_permission.edit', $team)->with('success', 'Permissions updated successfully');
    }
    ```

@DCox2016
Copy link
Author

DCox2016 commented Oct 1, 2023

Create middleware

  • app/Http/Middleware/CheckCustomPermission.php
    `namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\Request;

class CheckCustomPermission
{
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure(\Illuminate\Http\Request): (\Illuminate\Http\Response|\Illuminate\Http\RedirectResponse) $next
* @return \Illuminate\Http\Response|\Illuminate\Http\RedirectResponse
*/
public function handle($request, Closure $next, $action = null, $model = null)
{
$user = $request->user();

    $hasPermission = false;

    if ($action && $model) {
        $hasPermission = $user->can($action, $model);
    } elseif ($action) {
        $hasPermission = $user->can($action);
    }
    if (!$hasPermission) {
        abort(403, 'Unauthorized action.');
    }

    return $next($request);
}    

}
`

Replace
'can' => \Illuminate\Auth\Middleware\Authorize::class,
in app\Http\Kernel.php protected $middlewareAliases with
'can' => \App\Http\Middleware\CheckCustomPermission::class,

@DCox2016
Copy link
Author

DCox2016 commented Oct 1, 2023

Edit User Model
`use VentureDrake\LaravelCrm\Models\Team;

public function team()
{
return $this->belongsTo(Team::class);
}

    public function canForTeam($permission, $teamId)
{

    $team = Teams::find($teamId);

    if ($this->team_id == $teamId) {

        $teamPermissions = $team->permissions->pluck('name')->toArray();

        if (in_array($permission, $teamPermissions)) {
            return true;
        }
    }

    return false;
}

`

@DCox2016
Copy link
Author

DCox2016 commented Oct 1, 2023

Update app/Providers/AuthServiceProvider.php
` public function boot()
{
$this->registerPolicies();

    // List of permissions
    $permissions = \DB::table('permissions')->pluck('name');

    foreach ($permissions as $permission) {
        Gate::define($permission, function ($user) use ($permission) {
            // Direct permission check
            if ($user->getPermissionsViaRoles()->contains($permission)) {
                return true;
            }

            // Team permission check
            if ($user->canForTeam($permission, $user->team_id)) {
               return true;
            }

            return false;
        });
    }`

@DCox2016
Copy link
Author

DCox2016 commented Oct 1, 2023

(config('laravel-crm.teams') && auth()->user()->currentTeam && auth()->user()->currentTeam->user_id == auth()->user()->id

currentTeam function was not working so I update it like so
elseif (config('laravel-crm.teams') && auth()->user()->team->user_id == auth()->user()->id && ! auth()->user()->hasRole('Owner')) {

and on the where I replace it
'team_id' => auth()->user()->team->user_id,

@DCox2016
Copy link
Author

DCox2016 commented Oct 1, 2023

For the middle ware to work each one needs to check
if (Env::get('LARAVEL_CRM_TEAMS')) {
return $user->canForTeam('view crm fields', $user->team_id);
}

@DCox2016
Copy link
Author

DCox2016 commented Oct 1, 2023

image once you attach permissions to a team you will start seeing the nav bar image

@DCox2016
Copy link
Author

DCox2016 commented Oct 1, 2023

image image

@DCox2016
Copy link
Author

DCox2016 commented Oct 1, 2023

image by adding this to you policy you don't have to update your routes image I am going to post this to our clean crm project. It is public. Also, because our team_id id team_id on the user you will need to update your to what every you made it $user->current_crm_team_id the clean project as you can see if current_crm_team_id

@DCox2016
Copy link
Author

DCox2016 commented Oct 1, 2023

https://github.com/Lemnocity/clean-crm here is the link to our clean crm project

@andrewdrake andrewdrake self-assigned this Oct 3, 2023
@andrewdrake andrewdrake added the bug Something isn't working label Oct 3, 2023
@andrewdrake
Copy link
Contributor

Thanks for reporting this. So there are a few issues here.

I built the teams function basically to use with Laravel Jetstream, which has a Teams feature. This is then used to create multi-tenant applications. So as it stands right now you need to be using Jetstream.

I have this working myself in a few projects, however what we need to do is allow for projects that don't use Jetstream. And also I should probably create a few variations of the starter project to make use of Breeze vs Jetstream vs Custom.

Also you have made me aware that the installer will not work with teams, I will need to update that.

Now I think this might be a little confusing, but the "Teams" in the CRM are not the same thing as "teams" in Jetstream. Teams in the CRM are teams of people, say "Sales Team", "Accounts Team", etc

Where as Teams in Jetstream, are seperate tenants in the project. "Company A", "Company B", etc

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

2 participants