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

Alternate Models #545

Merged
merged 2 commits into from
Jul 13, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion docs/_changes.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@

## Unreleased

_No unreleased changes yet_
Enhancements:

- Added alternate authorization Models with stronger typing

## 1.1.0

Expand Down
7 changes: 7 additions & 0 deletions docs/extending.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,13 @@ Entities with your own casts and class methods.
If you extend the Model and supply your own validation rules you can also enforce those on the
`AuthController` by providing a `$registrationRules` property in **app/Config/Validation.php**.

### Alternate Models

Since version `1.2.0` this library includes an alternate version of the authorization models
that use specific Entities as their return types. These models are not yet used by the library
itself for backwards-compatibility, but they are highly recommended for any implementing
projects or model extensions.

## Views

Myth:Auth uses its own views by default, but you may want to update these in order to change
Expand Down
1 change: 1 addition & 0 deletions phpstan.neon.dist
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ parameters:
- src/Config/Routes.php
- src/Views/*
ignoreErrors:
- '#.+deprecated class Myth\\Auth\\Authorization\\(Group|Permission)Model.+#'
- '#.+Mockery.+#'
- '#Call to an undefined method .+::shouldReceive\(\)#'
- '#Call to an undefined static method Config\\Services::[A-Za-z]+\(\)#'
Expand Down
197 changes: 7 additions & 190 deletions src/Authorization/GroupModel.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,196 +2,13 @@

namespace Myth\Auth\Authorization;

use CodeIgniter\Model;
use Myth\Auth\Models\GroupModel as BaseModel;

class GroupModel extends Model
/**
* @deprecated 1.2.0 Use Myth\Auth\Models\GroupModel instead
*/
class GroupModel extends BaseModel
{
protected $table = 'auth_groups';
protected $primaryKey = 'id';
protected $returnType = 'object';
protected $allowedFields = [
'name', 'description',
];
protected $useTimestamps = false;
protected $validationRules = [
'name' => 'required|max_length[255]|is_unique[auth_groups.name,name,{name}]',
'description' => 'max_length[255]',
];
protected $validationMessages = [];
protected $skipValidation = false;

//--------------------------------------------------------------------
// Users
//--------------------------------------------------------------------

/**
* Adds a single user to a single group.
*
* @return bool
*/
public function addUserToGroup(int $userId, int $groupId)
{
cache()->delete("{$groupId}_users");
cache()->delete("{$userId}_groups");
cache()->delete("{$userId}_permissions");

$data = [
'user_id' => $userId,
'group_id' => $groupId,
];

return (bool) $this->db->table('auth_groups_users')->insert($data);
}

/**
* Removes a single user from a single group.
*
* @param int|string $groupId
*
* @return bool
*/
public function removeUserFromGroup(int $userId, $groupId)
{
cache()->delete("{$groupId}_users");
cache()->delete("{$userId}_groups");
cache()->delete("{$userId}_permissions");

return $this->db->table('auth_groups_users')
->where([
'user_id' => $userId,
'group_id' => (int) $groupId,
])->delete();
}

/**
* Removes a single user from all groups.
*
* @return bool
*/
public function removeUserFromAllGroups(int $userId)
{
cache()->delete("{$userId}_groups");
cache()->delete("{$userId}_permissions");

return $this->db->table('auth_groups_users')
->where('user_id', $userId)
->delete();
}

/**
* Returns an array of all groups that a user is a member of.
*
* @return array
*/
public function getGroupsForUser(int $userId)
{
if (null === $found = cache("{$userId}_groups")) {
$found = $this->builder()
->select('auth_groups_users.*, auth_groups.name, auth_groups.description')
->join('auth_groups_users', 'auth_groups_users.group_id = auth_groups.id', 'left')
->where('user_id', $userId)
->get()->getResultArray();

cache()->save("{$userId}_groups", $found, 300);
}

return $found;
}

/**
* Returns an array of all users that are members of a group.
*
* @return array
*/
public function getUsersForGroup(int $groupId)
{
if (null === $found = cache("{$groupId}_users")) {
$found = $this->builder()
->select('auth_groups_users.*, users.*')
->join('auth_groups_users', 'auth_groups_users.group_id = auth_groups.id', 'left')
->join('users', 'auth_groups_users.user_id = users.id', 'left')
->where('auth_groups.id', $groupId)
->get()->getResultArray();

cache()->save("{$groupId}_users", $found, 300);
}

return $found;
}

//--------------------------------------------------------------------
// Permissions
//--------------------------------------------------------------------

/**
* Gets all permissions for a group in a way that can be
* easily used to check against:
*
* [
* id => name,
* id => name
* ]
*/
public function getPermissionsForGroup(int $groupId): array
{
$permissionModel = model(PermissionModel::class);
$fromGroup = $permissionModel
->select('auth_permissions.*')
->join('auth_groups_permissions', 'auth_groups_permissions.permission_id = auth_permissions.id', 'inner')
->where('group_id', $groupId)
->findAll();

$found = [];

foreach ($fromGroup as $permission) {
$found[$permission['id']] = $permission;
}

return $found;
}

/**
* Add a single permission to a single group, by IDs.
*
* @return mixed
*/
public function addPermissionToGroup(int $permissionId, int $groupId)
{
$data = [
'permission_id' => $permissionId,
'group_id' => $groupId,
];

return $this->db->table('auth_groups_permissions')->insert($data);
}

//--------------------------------------------------------------------

/**
* Removes a single permission from a single group.
*
* @return mixed
*/
public function removePermissionFromGroup(int $permissionId, int $groupId)
{
return $this->db->table('auth_groups_permissions')
->where([
'permission_id' => $permissionId,
'group_id' => $groupId,
])->delete();
}

//--------------------------------------------------------------------

/**
* Removes a single permission from all groups.
*
* @return mixed
*/
public function removePermissionFromAllGroups(int $permissionId)
{
return $this->db->table('auth_groups_permissions')
->where('permission_id', $permissionId)
->delete();
}
protected $returnType = 'object';
protected string $permissionModel = PermissionModel::class;
}
98 changes: 6 additions & 92 deletions src/Authorization/PermissionModel.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,98 +2,12 @@

namespace Myth\Auth\Authorization;

use CodeIgniter\Database\BaseResult;
use CodeIgniter\Database\Query;
use CodeIgniter\Model;
use Myth\Auth\Models\PermissionModel as BaseModel;

class PermissionModel extends Model
/**
* @deprecated 1.2.0 Use Myth\Auth\Models\PermissionModel instead
*/
class PermissionModel extends BaseModel
{
protected $table = 'auth_permissions';
protected $allowedFields = [
'name', 'description',
];
protected $useTimestamps = false;
protected $validationRules = [
'name' => 'required|max_length[255]|is_unique[auth_permissions.name,name,{name}]',
'description' => 'max_length[255]',
];

/**
* Checks to see if a user, or one of their groups,
* has a specific permission.
*/
public function doesUserHavePermission(int $userId, int $permissionId): bool
{
return array_key_exists($permissionId, $this->getPermissionsForUser($userId));
}

/**
* Adds a single permission to a single user.
*
* @return BaseResult|false|Query
*/
public function addPermissionToUser(int $permissionId, int $userId)
{
cache()->delete("{$userId}_permissions");

return $this->db->table('auth_users_permissions')->insert([
'user_id' => $userId,
'permission_id' => $permissionId,
]);
}

/**
* Removes a permission from a user.
*
* @return mixed
*/
public function removePermissionFromUser(int $permissionId, int $userId)
{
$this->db->table('auth_users_permissions')->where([
'user_id' => $userId,
'permission_id' => $permissionId,
])->delete();

cache()->delete("{$userId}_permissions");
}

/**
* Gets all permissions for a user in a way that can be
* easily used to check against:
*
* [
* id => name,
* id => name
* ]
*/
public function getPermissionsForUser(int $userId): array
{
if (null === $found = cache("{$userId}_permissions")) {
$fromUser = $this->db->table('auth_users_permissions')
->select('id, auth_permissions.name')
->join('auth_permissions', 'auth_permissions.id = permission_id', 'inner')
->where('user_id', $userId)
->get()
->getResultObject();
$fromGroup = $this->db->table('auth_groups_users')
->select('auth_permissions.id, auth_permissions.name')
->join('auth_groups_permissions', 'auth_groups_permissions.group_id = auth_groups_users.group_id', 'inner')
->join('auth_permissions', 'auth_permissions.id = auth_groups_permissions.permission_id', 'inner')
->where('user_id', $userId)
->get()
->getResultObject();

$combined = array_merge($fromUser, $fromGroup);

$found = [];

foreach ($combined as $row) {
$found[$row->id] = strtolower($row->name);
}

cache()->save("{$userId}_permissions", $found, 300);
}

return $found;
}
protected $returnType = 'array';
}
18 changes: 18 additions & 0 deletions src/Entities/Group.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?php

namespace Myth\Auth\Entities;

use CodeIgniter\Entity\Entity;

/**
* Group Entity
*
* As of version 1.2 this class is used by the new GroupModel
* to allow using a strongly-typed return. Any logic in this
* class should not be relied on within this library.
*
* @since 1.2.0
*/
class Group extends Entity
{
}
18 changes: 18 additions & 0 deletions src/Entities/Permission.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?php

namespace Myth\Auth\Entities;

use CodeIgniter\Entity\Entity;

/**
* Permission Entity
*
* As of version 1.2 this class is used by the new PermissionModel
* to allow using a strongly-typed return. Any logic in this
* class should not be relied on within this library.
*
* @since 1.2.0
*/
class Permission extends Entity
{
}
Loading