mirror of
https://github.com/BookStackApp/BookStack.git
synced 2026-01-03 23:42:28 +03:00
Aligned logic to entity_permission role_id usage change
Now idenitifies fallback using role_id and user_id = null. Lays some foundations for handling user_id.
This commit is contained in:
@@ -10,6 +10,7 @@ use Illuminate\Database\Eloquent\Relations\MorphTo;
|
||||
/**
|
||||
* @property int $id
|
||||
* @property int $role_id
|
||||
* @property int $user_id
|
||||
* @property int $entity_id
|
||||
* @property string $entity_type
|
||||
* @property boolean $view
|
||||
|
||||
@@ -245,7 +245,9 @@ class JointPermissionBuilder
|
||||
// Create a mapping of explicit entity permissions
|
||||
$permissionMap = [];
|
||||
foreach ($permissions as $permission) {
|
||||
$key = $permission->entity_type . ':' . $permission->entity_id . ':' . $permission->role_id;
|
||||
$type = $permission->role_id ? 'role' : ($permission->user_id ? 'user' : 'fallback');
|
||||
$id = $permission->role_id ?? $permission->user_id ?? '0';
|
||||
$key = $permission->entity_type . ':' . $permission->entity_id . ':' . $type . ':' . $id;
|
||||
$permissionMap[$key] = $permission->view;
|
||||
}
|
||||
|
||||
@@ -376,18 +378,18 @@ class JointPermissionBuilder
|
||||
protected function entityPermissionsActiveForRole(array $permissionMap, SimpleEntityData $entity, int $roleId): bool
|
||||
{
|
||||
$keyPrefix = $entity->type . ':' . $entity->id . ':';
|
||||
return isset($permissionMap[$keyPrefix . $roleId]) || isset($permissionMap[$keyPrefix . '0']);
|
||||
return isset($permissionMap[$keyPrefix . 'role:' . $roleId]) || isset($permissionMap[$keyPrefix . 'fallback:0']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check for an active restriction in an entity map.
|
||||
*/
|
||||
protected function mapHasActiveRestriction(array $entityMap, SimpleEntityData $entity, int $roleId): bool
|
||||
protected function mapHasActiveRestriction(array $permissionMap, SimpleEntityData $entity, int $roleId): bool
|
||||
{
|
||||
$roleKey = $entity->type . ':' . $entity->id . ':' . $roleId;
|
||||
$defaultKey = $entity->type . ':' . $entity->id . ':0';
|
||||
$roleKey = $entity->type . ':' . $entity->id . ':role:' . $roleId;
|
||||
$defaultKey = $entity->type . ':' . $entity->id . ':fallback:0';
|
||||
|
||||
return $entityMap[$roleKey] ?? $entityMap[$defaultKey] ?? false;
|
||||
return $permissionMap[$roleKey] ?? $permissionMap[$defaultKey] ?? false;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -78,26 +78,53 @@ class PermissionApplicator
|
||||
}
|
||||
|
||||
foreach ($chain as $currentEntity) {
|
||||
$allowedByRoleId = $currentEntity->permissions()
|
||||
->whereIn('role_id', [0, ...$userRoleIds])
|
||||
->pluck($action, 'role_id');
|
||||
$relevantPermissions = $currentEntity->permissions()
|
||||
->where(function (Builder $query) use ($userRoleIds) {
|
||||
$query->whereIn('role_id', $userRoleIds)
|
||||
->orWhere(function (Builder $query) {
|
||||
$query->whereNull(['role_id', 'user_id']);
|
||||
});
|
||||
})
|
||||
->get(['role_id', 'user_id', $action])
|
||||
->all();
|
||||
|
||||
// TODO - Update below for user permissions
|
||||
|
||||
// 1. Default fallback set and allows, no role permissions -> True
|
||||
// 2. Default fallback set and prevents, no role permissions -> False
|
||||
// 3. Role permission allows, fallback set and allows -> True
|
||||
// 3. Role permission allows, fallback set and prevents -> True
|
||||
// 3. Role permission allows, fallback not set -> True
|
||||
// 3. Role permission prevents, fallback set and allows -> False
|
||||
// 3. Role permission prevents, fallback set and prevents -> False
|
||||
// 3. Role permission prevents, fallback not set -> False
|
||||
// 4. Nothing exists -> Continue
|
||||
|
||||
// If the default is set, we have to return something here.
|
||||
$allowedById = [];
|
||||
foreach ($relevantPermissions as $permission) {
|
||||
$allowedById[$permission->role_id . ':' . $permission->user_id] = $permission->$action;
|
||||
}
|
||||
|
||||
// Continue up the chain if no applicable entity permission overrides.
|
||||
if ($allowedByRoleId->isEmpty()) {
|
||||
if (empty($allowedById)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// If we have user-role-specific permissions set, allow if any of those
|
||||
// role permissions allow access.
|
||||
$hasDefault = $allowedByRoleId->has(0);
|
||||
if (!$hasDefault || $allowedByRoleId->count() > 1) {
|
||||
return $allowedByRoleId->search(function (bool $allowed, int $roleId) {
|
||||
return $roleId !== 0 && $allowed;
|
||||
}) !== false;
|
||||
$hasDefault = isset($allowedById[':']);
|
||||
if (!$hasDefault || count($allowedById) > 1) {
|
||||
foreach ($allowedById as $key => $allowed) {
|
||||
if ($key !== ':' && $allowed) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Otherwise, return the default "Other roles" fallback value.
|
||||
return $allowedByRoleId->get(0);
|
||||
return $allowedById[':'];
|
||||
}
|
||||
|
||||
return null;
|
||||
@@ -114,6 +141,7 @@ class PermissionApplicator
|
||||
$permissionQuery = EntityPermission::query()
|
||||
->where($action, '=', true)
|
||||
->whereIn('role_id', $this->getCurrentUserRoleIds());
|
||||
// TODO - Update for user permission
|
||||
|
||||
if (!empty($entityClass)) {
|
||||
/** @var Entity $entityInstance */
|
||||
@@ -134,6 +162,7 @@ class PermissionApplicator
|
||||
{
|
||||
return $query->where(function (Builder $parentQuery) {
|
||||
$parentQuery->whereHas('jointPermissions', function (Builder $permissionQuery) {
|
||||
// TODO - Update for user permission
|
||||
$permissionQuery->whereIn('role_id', $this->getCurrentUserRoleIds())
|
||||
->where(function (Builder $query) {
|
||||
$this->addJointHasPermissionCheck($query, $this->currentUser()->id);
|
||||
@@ -170,6 +199,7 @@ class PermissionApplicator
|
||||
$pageMorphClass = (new Page())->getMorphClass();
|
||||
|
||||
$q = $query->whereExists(function ($permissionQuery) use (&$tableDetails) {
|
||||
// TODO - Update for user permission
|
||||
/** @var Builder $permissionQuery */
|
||||
$permissionQuery->select(['role_id'])->from('joint_permissions')
|
||||
->whereColumn('joint_permissions.entity_id', '=', $tableDetails['tableName'] . '.' . $tableDetails['entityIdColumn'])
|
||||
@@ -203,6 +233,7 @@ class PermissionApplicator
|
||||
$fullPageIdColumn = $tableName . '.' . $pageIdColumn;
|
||||
$morphClass = (new Page())->getMorphClass();
|
||||
|
||||
// TODO - Update for user permission
|
||||
$existsQuery = function ($permissionQuery) use ($fullPageIdColumn, $morphClass) {
|
||||
/** @var Builder $permissionQuery */
|
||||
$permissionQuery->select('joint_permissions.role_id')->from('joint_permissions')
|
||||
|
||||
@@ -21,7 +21,7 @@ class PermissionFormData
|
||||
{
|
||||
return $this->entity->permissions()
|
||||
->with('role')
|
||||
->where('role_id', '!=', 0)
|
||||
->whereNotNull('role_id')
|
||||
->get()
|
||||
->sortBy('role.display_name')
|
||||
->all();
|
||||
@@ -33,7 +33,7 @@ class PermissionFormData
|
||||
*/
|
||||
public function rolesNotAssigned(): array
|
||||
{
|
||||
$assigned = $this->entity->permissions()->pluck('role_id');
|
||||
$assigned = $this->entity->permissions()->whereNotNull('role_id')->pluck('role_id');
|
||||
return Role::query()
|
||||
->where('system_name', '!=', 'admin')
|
||||
->whereNotIn('id', $assigned)
|
||||
@@ -49,20 +49,19 @@ class PermissionFormData
|
||||
{
|
||||
/** @var ?EntityPermission $permission */
|
||||
$permission = $this->entity->permissions()
|
||||
->where('role_id', '=', 0)
|
||||
->whereNull(['role_id', 'user_id'])
|
||||
->first();
|
||||
return $permission ?? (new EntityPermission());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the "Everyone Else" role entry.
|
||||
* Check if the "Everyone else" option is inheriting default role system permissions.
|
||||
* Is determined by any system entity_permission existing for the current entity.
|
||||
*/
|
||||
public function everyoneElseRole(): Role
|
||||
public function everyoneElseInheriting(): bool
|
||||
{
|
||||
return (new Role())->forceFill([
|
||||
'id' => 0,
|
||||
'display_name' => trans('entities.permissions_role_everyone_else'),
|
||||
'description' => trans('entities.permissions_role_everyone_else_desc'),
|
||||
]);
|
||||
return !$this->entity->permissions()
|
||||
->whereNull(['role_id', 'user_id'])
|
||||
->exists();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,13 +58,30 @@ class PermissionsUpdater
|
||||
protected function formatPermissionsFromRequestToEntityPermissions(array $permissions): array
|
||||
{
|
||||
$formatted = [];
|
||||
$columnsByType = [
|
||||
'role' => 'role_id',
|
||||
'user' => 'user_id',
|
||||
'fallback' => '',
|
||||
];
|
||||
|
||||
foreach ($permissions as $roleId => $info) {
|
||||
$entityPermissionData = ['role_id' => $roleId];
|
||||
foreach (EntityPermission::PERMISSIONS as $permission) {
|
||||
$entityPermissionData[$permission] = (($info[$permission] ?? false) === "true");
|
||||
foreach ($permissions as $type => $byId) {
|
||||
$column = $columnsByType[$type] ?? null;
|
||||
if (is_null($column)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach ($byId as $id => $info) {
|
||||
$entityPermissionData = [];
|
||||
|
||||
if (!empty($column)) {
|
||||
$entityPermissionData[$column] = $id;
|
||||
}
|
||||
|
||||
foreach (EntityPermission::PERMISSIONS as $permission) {
|
||||
$entityPermissionData[$permission] = (($info[$permission] ?? false) === "true");
|
||||
}
|
||||
$formatted[] = $entityPermissionData;
|
||||
}
|
||||
$formatted[] = $entityPermissionData;
|
||||
}
|
||||
|
||||
return $formatted;
|
||||
|
||||
@@ -162,10 +162,14 @@ class PermissionsController extends Controller
|
||||
{
|
||||
$this->checkPermissionOr('restrictions-manage-all', fn() => userCan('restrictions-manage-own'));
|
||||
|
||||
/** @var Role $role */
|
||||
$role = Role::query()->findOrFail($roleId);
|
||||
|
||||
return view('form.entity-permissions-row', [
|
||||
'role' => $role,
|
||||
'modelType' => 'role',
|
||||
'modelId' => $role->id,
|
||||
'modelName' => $role->display_name,
|
||||
'modelDescription' => $role->description,
|
||||
'permission' => new EntityPermission(),
|
||||
'entityType' => $entityType,
|
||||
'inheriting' => false,
|
||||
|
||||
Reference in New Issue
Block a user