Skip to content

Commit 205f8b2

Browse files
committed
Merge remote-tracking branch 'refs/remotes/gitdedalo/v7_developer' into v7_developer
2 parents 121adf9 + b6d8151 commit 205f8b2

5 files changed

Lines changed: 171 additions & 10 deletions

File tree

core/api/v1/common/class.dd_core_api.php

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1389,19 +1389,18 @@ public static function count(object $rqo) : object {
13891389
}
13901390

13911391
// permissions check. If user don't have access to any section, set total to zero and prevent search
1392+
// SEC-09: check permissions for all sections, not only DEDALO_SECTION_USERS_TIPO
13921393
$ar_section_tipo = $sqo->section_tipo;
13931394
if( empty($ar_section_tipo) ){
13941395
$response->result = 0;
13951396
return $response;
13961397
}
13971398
foreach ($ar_section_tipo as $current_section_tipo) {
1398-
if($current_section_tipo===DEDALO_SECTION_USERS_TIPO) {
1399-
$permissions = common::get_permissions($current_section_tipo, $current_section_tipo);
1400-
if($permissions<1){
1401-
$result = (object)[
1402-
'total' => 0
1403-
];
1404-
}
1399+
$permissions = common::get_permissions($current_section_tipo, $current_section_tipo);
1400+
if ($permissions < 1) {
1401+
$response->result = (object)['total' => 0];
1402+
$response->msg = 'OK. Request done successfully';
1403+
return $response;
14051404
}
14061405
}
14071406

@@ -1634,7 +1633,7 @@ public static function get_section_elements_context(object $rqo) : object {
16341633
$ar_section_tipo = (array)$options->ar_section_tipo;
16351634
$use_real_sections = $options->use_real_sections ?? false;
16361635
$ar_components_exclude = $options->ar_components_exclude ?? null;
1637-
$skip_permissions = $options->skip_permissions ?? false;
1636+
$skip_permissions = false; // SEC-07: never allow client to skip permissions
16381637

16391638
// section_elements_context_options
16401639
$section_elements_context_options = (object)[

core/api/v1/common/class.dd_diffusion_api.php

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,14 @@ public static function diffuse(object $rqo): object {
8181
throw new Exception("No section related to $diffusion_tipo");
8282
}
8383

84+
// SEC-13: check read permissions for the section being diffused
85+
$permissions = common::get_permissions($main_section_tipo, $main_section_tipo);
86+
if ($permissions < 1) {
87+
$response->errors[] = 'insufficient permissions';
88+
$response->msg = "Error. Insufficient permissions to diffuse section ($main_section_tipo)";
89+
return $response;
90+
}
91+
8492
// =====================================================
8593
// BUILD LANGS
8694
// =====================================================
@@ -264,6 +272,14 @@ public static function validate(object $rqo): object {
264272
public static function get_ontology_map(object $rqo): object {
265273
$response = new stdClass();
266274

275+
// SEC-14: Restrict ontology map to global admins
276+
if (security::is_global_admin(logged_user_id()) !== true) {
277+
$response->result = false;
278+
$response->errors[] = 'insufficient permissions';
279+
$response->msg = 'Error. Insufficient permissions to access ontology map.';
280+
return $response;
281+
}
282+
267283
$diffusion_tipo = $rqo->options->diffusion_tipo ?? null;
268284
if (!$diffusion_tipo) {
269285
$response->result = false;

core/api/v1/common/class.dd_tools_api.php

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -143,8 +143,20 @@ public static function tool_request(object $rqo) : object {
143143
require_once $class_file;
144144

145145
// method (static)
146-
if (!method_exists($tool_name, $tool_method)) {
147-
$response->msg = 'Error. tool method \''.$tool_method.'\' do not exists ';
146+
$is_valid = false;
147+
if (method_exists($tool_name, $tool_method)) {
148+
try {
149+
$reflection = new ReflectionMethod($tool_name, $tool_method);
150+
if ($reflection->isPublic() && $reflection->isStatic()) {
151+
$is_valid = true;
152+
}
153+
} catch (Exception $e) {
154+
// Ignore exception and leave is_valid false
155+
}
156+
}
157+
if (!$is_valid) {
158+
$response->msg = 'Error. tool method not accessible: '.$tool_method;
159+
$response->errors[] = 'unauthorized_method';
148160
return $response;
149161
}
150162

core/api/v1/common/class.dd_ts_api.php

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -258,6 +258,14 @@ public static function add_child(object $rqo) : object {
258258
$section_tipo = $source->section_tipo;
259259
$section_id = $source->section_id;
260260

261+
// SEC-10: check write permissions
262+
$permissions = common::get_permissions($section_tipo, $section_tipo);
263+
if ($permissions < 2) {
264+
$response->errors[] = 'insufficient permissions';
265+
$response->msg = "Error. Insufficient permissions to create in section ($section_tipo)";
266+
return $response;
267+
}
268+
261269
// new section. Create a new empty section
262270
$new_section = section::get_instance($section_tipo);
263271
$new_section_id = $new_section->create_record();
@@ -467,6 +475,14 @@ public static function update_parent_data(object $rqo) : object {
467475
$new_parent_section_id = $source->new_parent_section_id;
468476
$new_parent_section_tipo = $source->new_parent_section_tipo;
469477

478+
// SEC-11: check write permissions
479+
$permissions = common::get_permissions($section_tipo, $section_tipo);
480+
if ($permissions < 2) {
481+
$response->errors[] = 'insufficient permissions';
482+
$response->msg = "Error. Insufficient permissions to update in section ($section_tipo)";
483+
return $response;
484+
}
485+
470486
// component_relation_parent
471487
$parent_tipo = section::get_ar_children_tipo_by_model_name_in_section($section_tipo, ['component_relation_parent'], true, true, true, true)[0];
472488
$model_name = ontology_node::get_model_by_tipo($parent_tipo,true);
@@ -577,6 +593,14 @@ public static function save_order(object $rqo) : object {
577593
$parent_section_tipo= $source->parent_section_tipo ?? null;
578594
$parent_section_id = $source->parent_section_id ?? null;
579595

596+
// SEC-12: check write permissions
597+
$permissions = common::get_permissions($section_tipo, $section_tipo);
598+
if ($permissions < 2) {
599+
$response->errors[] = 'insufficient permissions';
600+
$response->msg = "Error. Insufficient permissions to update order in section ($section_tipo)";
601+
return $response;
602+
}
603+
580604
// validate parent context
581605
if (empty($parent_section_tipo) || empty($parent_section_id)) {
582606
$response->msg = 'Error. parent_section_tipo and parent_section_id are required';

test/server/api/SecurityAudit_Test.php

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,116 @@ public function test_read_raw_permission_check() : void {
156156
$this->assertContains('insufficient permissions', $response->errors, "Should return insufficient permissions error for read_raw");
157157
}
158158

159+
/**
160+
* TEST_TS_ADD_CHILD_PERMISSION_CHECK
161+
* Verify that add_child is blocked for sections the user cannot write to.
162+
*/
163+
public function test_ts_add_child_permission_check() : void {
164+
$this->force_limited_user_login(999);
165+
166+
$rqo = (object)[
167+
'action' => 'add_child',
168+
'dd_api' => 'dd_ts_api',
169+
'source' => (object)[
170+
'section_tipo' => self::$section_tipo,
171+
'section_id' => '1'
172+
]
173+
];
174+
175+
$response = dd_ts_api::add_child($rqo);
176+
177+
$this->assertContains('insufficient permissions', $response->errors, "Should return insufficient permissions error for add_child");
178+
}
179+
180+
/**
181+
* TEST_TS_UPDATE_PARENT_PERMISSION_CHECK
182+
* Verify that update_parent_data is blocked for sections the user cannot write to.
183+
*/
184+
public function test_ts_update_parent_permission_check() : void {
185+
$this->force_limited_user_login(999);
186+
187+
$rqo = (object)[
188+
'action' => 'update_parent_data',
189+
'dd_api' => 'dd_ts_api',
190+
'source' => (object)[
191+
'section_tipo' => self::$section_tipo,
192+
'section_id' => '1',
193+
'old_parent_section_tipo' => self::$section_tipo,
194+
'old_parent_section_id' => '2',
195+
'new_parent_section_tipo' => self::$section_tipo,
196+
'new_parent_section_id' => '3'
197+
]
198+
];
199+
200+
$response = dd_ts_api::update_parent_data($rqo);
201+
202+
$this->assertContains('insufficient permissions', $response->errors, "Should return insufficient permissions error for update_parent_data");
203+
}
204+
205+
/**
206+
* TEST_TS_SAVE_ORDER_PERMISSION_CHECK
207+
* Verify that save_order is blocked for sections the user cannot write to.
208+
*/
209+
public function test_ts_save_order_permission_check() : void {
210+
$this->force_limited_user_login(999);
211+
212+
$rqo = (object)[
213+
'action' => 'save_order',
214+
'dd_api' => 'dd_ts_api',
215+
'source' => (object)[
216+
'section_tipo' => self::$section_tipo,
217+
'ar_locators' => [],
218+
'parent_section_tipo' => self::$section_tipo,
219+
'parent_section_id' => '1'
220+
]
221+
];
222+
223+
$response = dd_ts_api::save_order($rqo);
224+
225+
$this->assertContains('insufficient permissions', $response->errors, "Should return insufficient permissions error for save_order");
226+
}
227+
228+
/**
229+
* TEST_DIFFUSION_ONTOLOGY_MAP_PERMISSION_CHECK
230+
* Verify that get_ontology_map is blocked for non-admin users.
231+
*/
232+
public function test_diffusion_ontology_map_permission_check() : void {
233+
$this->force_limited_user_login(999);
234+
235+
$rqo = (object)[
236+
'action' => 'get_ontology_map',
237+
'dd_api' => 'dd_diffusion_api',
238+
'options' => (object)[
239+
'diffusion_tipo' => 'rsc450'
240+
]
241+
];
242+
243+
$response = dd_diffusion_api::get_ontology_map($rqo);
244+
245+
$this->assertContains('insufficient permissions', $response->errors, "Should return insufficient permissions error for get_ontology_map for non-admins");
246+
}
247+
248+
/**
249+
* TEST_TOOL_REQUEST_REFLECTION_CHECK
250+
* Verify that tool_request blocks non-public/non-static methods.
251+
*/
252+
public function test_tool_request_reflection_check() : void {
253+
$this->user_login();
254+
255+
$rqo = (object)[
256+
'action' => 'tool_request',
257+
'dd_api' => 'dd_tools_api',
258+
'source' => (object)[
259+
'model' => 'tool_time_machine', // valid standard tool
260+
'action' => '__construct'
261+
]
262+
];
263+
264+
$response = dd_tools_api::tool_request($rqo);
265+
266+
$this->assertContains('unauthorized_method', $response->errors, "Should return unauthorized_method error for non-callable methods");
267+
}
268+
159269
private function force_limited_user_login(int $user_id) : void {
160270
login_test::force_login($user_id);
161271
$_SESSION['dedalo']['auth']['is_global_admin'] = false;

0 commit comments

Comments
 (0)