Merge pull request #175 from copenhagentruckwash/fix-vulnerability-in-studio-graph-edits
Fix authorization boundary for studio graph lane operations
This commit is contained in:
@@ -342,7 +342,7 @@ class selfserve_studio_graph
|
|||||||
if ($versioning->isV2Config($config)) {
|
if ($versioning->isV2Config($config)) {
|
||||||
foreach ($operations as $operation) {
|
foreach ($operations as $operation) {
|
||||||
if (is_array($operation)) {
|
if (is_array($operation)) {
|
||||||
$this->applyConfigOperation($departmentId, $config, $operation);
|
$this->applyConfigOperation($departmentId, $config, $operation, $permissions);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -356,7 +356,7 @@ class selfserve_studio_graph
|
|||||||
} else {
|
} else {
|
||||||
foreach ($operations as $operation) {
|
foreach ($operations as $operation) {
|
||||||
if (is_array($operation)) {
|
if (is_array($operation)) {
|
||||||
$this->applyOperation($departmentId, $operation);
|
$this->applyOperation($departmentId, $operation, $permissions);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1676,7 +1676,7 @@ class selfserve_studio_graph
|
|||||||
* @param array<string,mixed> $config
|
* @param array<string,mixed> $config
|
||||||
* @param array<string,mixed> $operation
|
* @param array<string,mixed> $operation
|
||||||
*/
|
*/
|
||||||
private function applyConfigOperation(int $departmentId, array &$config, array $operation): void
|
private function applyConfigOperation(int $departmentId, array &$config, array $operation, array $permissions = []): void
|
||||||
{
|
{
|
||||||
$action = strtolower((string)($operation['action'] ?? ''));
|
$action = strtolower((string)($operation['action'] ?? ''));
|
||||||
$entity = $this->normalizeEntity((string)($operation['entity'] ?? $operation['type'] ?? ''));
|
$entity = $this->normalizeEntity((string)($operation['entity'] ?? $operation['type'] ?? ''));
|
||||||
@@ -1703,7 +1703,7 @@ class selfserve_studio_graph
|
|||||||
throw new \RuntimeException('Studio graph operation is missing entity.');
|
throw new \RuntimeException('Studio graph operation is missing entity.');
|
||||||
}
|
}
|
||||||
if ($entity === 'lane') {
|
if ($entity === 'lane') {
|
||||||
$this->applyLaneOperation($departmentId, $action, $id, $data);
|
$this->applyLaneOperation($departmentId, $action, $id, $data, $permissions);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if ($entity === 'rule') {
|
if ($entity === 'rule') {
|
||||||
@@ -2761,7 +2761,7 @@ class selfserve_studio_graph
|
|||||||
/**
|
/**
|
||||||
* @param array<string,mixed> $operation
|
* @param array<string,mixed> $operation
|
||||||
*/
|
*/
|
||||||
private function applyOperation(int $departmentId, array $operation): void
|
private function applyOperation(int $departmentId, array $operation, array $permissions = []): void
|
||||||
{
|
{
|
||||||
$action = strtolower((string)($operation['action'] ?? ''));
|
$action = strtolower((string)($operation['action'] ?? ''));
|
||||||
$entity = $this->normalizeEntity((string)($operation['entity'] ?? $operation['type'] ?? ''));
|
$entity = $this->normalizeEntity((string)($operation['entity'] ?? $operation['type'] ?? ''));
|
||||||
@@ -2784,7 +2784,7 @@ class selfserve_studio_graph
|
|||||||
throw new \RuntimeException('Studio graph operation is missing entity.');
|
throw new \RuntimeException('Studio graph operation is missing entity.');
|
||||||
}
|
}
|
||||||
if ($entity === 'lane') {
|
if ($entity === 'lane') {
|
||||||
$this->applyLaneOperation($departmentId, $action, $id, $data);
|
$this->applyLaneOperation($departmentId, $action, $id, $data, $permissions);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2810,12 +2810,14 @@ class selfserve_studio_graph
|
|||||||
/**
|
/**
|
||||||
* @param array<string,mixed> $data
|
* @param array<string,mixed> $data
|
||||||
*/
|
*/
|
||||||
private function applyLaneOperation(int $departmentId, string $action, int $id, array $data): void
|
private function applyLaneOperation(int $departmentId, string $action, int $id, array $data, array $permissions = []): void
|
||||||
{
|
{
|
||||||
if (!$this->tableExists('department_lanes')) {
|
if (!$this->tableExists('department_lanes')) {
|
||||||
throw new \RuntimeException('Department lanes are not available.');
|
throw new \RuntimeException('Department lanes are not available.');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$this->assertLaneOperationAuthorized($action, $data, $permissions);
|
||||||
|
|
||||||
if ($action === 'create') {
|
if ($action === 'create') {
|
||||||
$this->createLane($departmentId, $data);
|
$this->createLane($departmentId, $data);
|
||||||
return;
|
return;
|
||||||
@@ -2842,6 +2844,37 @@ class selfserve_studio_graph
|
|||||||
throw new \RuntimeException('Unsupported studio lane operation: ' . $action);
|
throw new \RuntimeException('Unsupported studio lane operation: ' . $action);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array<string,mixed> $data
|
||||||
|
* @param array<string,bool> $permissions
|
||||||
|
*/
|
||||||
|
private function assertLaneOperationAuthorized(string $action, array $data, array $permissions): void
|
||||||
|
{
|
||||||
|
if ($action === 'create' && !($permissions['can_add_department_lane'] ?? false)) {
|
||||||
|
throw new \RuntimeException('Missing permission: add_department_lane.');
|
||||||
|
}
|
||||||
|
if (in_array($action, ['update', 'delete'], true) && !($permissions['can_edit_department_lane'] ?? false)) {
|
||||||
|
throw new \RuntimeException('Missing permission: edit_department_lane.');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!in_array($action, ['create', 'update'], true)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$relayFields = [
|
||||||
|
'relay_in_id',
|
||||||
|
'relay_out_id',
|
||||||
|
'relay_machine_id',
|
||||||
|
'relay_machine_program_picker_id',
|
||||||
|
'relay_machine_cleaner_id',
|
||||||
|
];
|
||||||
|
foreach ($relayFields as $field) {
|
||||||
|
if (array_key_exists($field, $data) && !($permissions['modules_shelly_config'] ?? false)) {
|
||||||
|
throw new \RuntimeException('Missing permission: modules_shelly_config.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param array<string,mixed> $data
|
* @param array<string,mixed> $data
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -329,6 +329,8 @@ class departmentSelfserveStudioRoute
|
|||||||
'can_publish' => $this->hasPermission('publish_department_selfserve_config_versions'),
|
'can_publish' => $this->hasPermission('publish_department_selfserve_config_versions'),
|
||||||
'can_rollback' => $this->hasPermission('rollback_department_selfserve_config_versions'),
|
'can_rollback' => $this->hasPermission('rollback_department_selfserve_config_versions'),
|
||||||
'can_simulate' => $this->hasPermission('list_department_selfserve_vehicle_conditions'),
|
'can_simulate' => $this->hasPermission('list_department_selfserve_vehicle_conditions'),
|
||||||
|
'can_add_department_lane' => $this->hasPermission('add_department_lane'),
|
||||||
|
'can_edit_department_lane' => $this->hasPermission('edit_department_lane'),
|
||||||
'modules_shelly_config' => $this->hasPermission('modules_shelly_config'),
|
'modules_shelly_config' => $this->hasPermission('modules_shelly_config'),
|
||||||
'can_manage_gateways' => $this->hasPermission('modules_shelly_config'),
|
'can_manage_gateways' => $this->hasPermission('modules_shelly_config'),
|
||||||
'can_run_gateway_destructive_actions' => $this->hasPermission('modules_shelly_config'),
|
'can_run_gateway_destructive_actions' => $this->hasPermission('modules_shelly_config'),
|
||||||
|
|||||||
Reference in New Issue
Block a user