Skip to content
Closed
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
104 changes: 104 additions & 0 deletions phpunit/functional/UserTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -1857,4 +1857,108 @@ public function testUnsetUndisclosedFieldsWithPartialFields()

$this->assertEquals(['name' => 'test'], $fields);
}

public function testApplyRightRulesWithDefaultProfile()
{
$this->login();

// Create a test user with login info for authentication
$user = new \User();
$uid = $user->add([
'name' => 'test_apply_right_rules_user',
'password' => 'test_password',
'password2' => 'test_password',
]);
$this->assertGreaterThan(0, $uid);
$this->assertTrue($user->getFromDB($uid));

// Get default profile and tech profile
$default_profile = \Profile::getDefault();
$tech_profile = getItemByTypeName('Profile', 'Technician', true);
$this->assertGreaterThan(0, $default_profile);

// Remove the default profile that was automatically added during user creation
$profile_user = new \Profile_User();
$existing_profiles = \Profile_User::getForUser($uid, false);
foreach ($existing_profiles as $existing_profile) {
$profile_user->delete(['id' => $existing_profile['id']]);
}

// Verify user has no profiles
$profiles_before = \Profile_User::getForUser($uid, false);
$this->assertCount(0, $profiles_before);

// Create a right rule that assigns Technician profile based on user name
$rule_right = new \RuleRight();
$rule_id = $rule_right->add([
'name' => 'Test rule for profile assignment',
'is_active' => 1,
'sub_type' => 'RuleRight',
'match' => 'AND',
'condition' => 0,
]);
$this->assertGreaterThan(0, $rule_id);

// Add criteria: if login equals test_apply_right_rules_user
$rule_criteria = new \RuleCriteria();
$criteria_id = $rule_criteria->add([
'rules_id' => $rule_id,
'criteria' => 'LOGIN',
'condition' => 0, // is
'pattern' => 'test_apply_right_rules_user',
]);
$this->assertGreaterThan(0, $criteria_id);

// Add action: assign Technician profile on root entity
$rule_action = new \RuleAction();
$action_id = $rule_action->add([
'rules_id' => $rule_id,
'action_type' => 'assign',
'field' => 'profiles_id',
'value' => $tech_profile,
]);
$this->assertGreaterThan(0, $action_id);

// Add action: assign to root entity
$entity_action_id = $rule_action->add([
'rules_id' => $rule_id,
'action_type' => 'assign',
'field' => 'entities_id',
'value' => 0,
]);
$this->assertGreaterThan(0, $entity_action_id);

// Login as the test user to trigger rule processing
$this->login('test_apply_right_rules_user', 'test_password');

// Verify user now has the Technician profile (assigned by rule)
$profiles_after_rule = \Profile_User::getForUser($uid, false);
$this->assertCount(1, $profiles_after_rule);
$profile_after_rule = reset($profiles_after_rule);
$this->assertEquals($tech_profile, $profile_after_rule['profiles_id']);
$this->assertEquals(1, $profile_after_rule['is_dynamic']); // Profile assigned by rule is dynamic

// Now modify the rule criteria so it doesn't match anymore
$this->assertTrue($rule_criteria->update([
'id' => $criteria_id,
'pattern' => 'different_user_name', // This won't match our user
]));

// Login again to trigger rule re-processing
$this->login('test_apply_right_rules_user', 'test_password');

// Verify user now has the default profile (since rule doesn't match anymore)
$profiles_after_no_match = \Profile_User::getForUser($uid, false);
$this->assertCount(1, $profiles_after_no_match);

$profile_after_no_match = reset($profiles_after_no_match);
$this->assertEquals($default_profile, $profile_after_no_match['profiles_id']);
$this->assertEquals(0, $profile_after_no_match['entities_id']);
$this->assertEquals(1, $profile_after_no_match['is_recursive']);
$this->assertEquals(1, $profile_after_no_match['is_dynamic']);
$this->assertEquals(1, $profile_after_no_match['is_default_profile']);

// Clean up: delete the test rule
$this->assertTrue($rule_right->delete(['id' => $rule_id]));
}
}
19 changes: 19 additions & 0 deletions src/User.php
Original file line number Diff line number Diff line change
Expand Up @@ -1417,6 +1417,25 @@ public function applyRightRules()
$right->delete($db_profile);
}
}

// Check if user has any profile left after deletion, if not add default profile
$remaining_profiles = Profile_User::getForUser($this->fields["id"], false);
if (count($remaining_profiles) == 0) {
$default_profile = Profile::getDefault();
if ($default_profile > 0) {
$affectation = [
'entities_id' => 0, // Root entity
'profiles_id' => $default_profile,
'is_recursive' => 1,
Comment on lines +1427 to +1429
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are we sure we want the user to have access to all entities? If there is not match with autorizarion rules, should we take the risk adding some default authorization?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good question. Basically, a user has rights by default, so it seemed logical to me to give them back their default rights when they no longer have any other rights.

Because a user without rights is weird, isn't it?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From a security point of view, it's probably better to get an user without rights than an user with inappropriate ones.
I have no idea what a correct default could be, but giving access recursively to all entities seems too large.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's what I am thinking too.

You don't want to update GLPI and then notice out of nowhere that some users have new profiles, it might be a critical issue if people don't expect it.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree. The current behavior seems to be the correct one. If all authorization assignments are dynamic (applied by rules) and a user no longer matches those rules, we shouldn't force any authorizations on the user. It is up to the GLPI admin to define a default rule, or use the one that already existed, if they want that kind of behavior.

There is also the case where a user is deleted in LDAP where an admin may have chosen to handle it by withdrawing authorizations but keeping the user out of the trash. If an authorization is forced, this goes against the admin's choice.

'users_id' => $this->fields['id'],
'is_dynamic' => 1,
'is_default_profile' => 1,
];
$right = new Profile_User();
$right->add($affectation);
}
}

$this->must_process_ruleright = false;
}
return $return;
Expand Down