Skip to content
Merged
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

## [Unreleased]

## 2.0.3 - 01/09/2026
### Bugs
- Fix safety warnings

## 2.0.2 - 26/08/2026
### Bugs
- Fix a security issue that let a user with only read access to a group activate or deactivate other users' group memberships
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
[![X](https://img.shields.io/twitter/follow/ticgalcom?style=flat-square&logo=x&label=Follow)](https://twitter.com/ticgalcom)
[![Web](https://img.shields.io/badge/Web-TICGAL-blue.svg?style=flat-square)](https://tic.gal/)
[![Localazy](https://img.shields.io/badge/Translate-Localazy-cyan)](https://localazy.com/p/more-groups)
[![Manual](https://img.shields.io/badge/Manual-docs.tic.gal-blue.svg?style=flat-square)](https://docs.tic.gal/books/more-groups)
[![Manual](https://img.shields.io/badge/Doc-Manuals-blue.svg?style=flat-square)](https://docs.tic.gal/books/more-groups)
[![Marketplace](https://img.shields.io/badge/GLPI-Marketplace-orange.svg?style=flat-square)](https://plugins.glpi-project.org/#/plugins/moregroups)

Fast group-membership management for GLPI: deactivate a group member without
Expand Down
86 changes: 46 additions & 40 deletions inc/group.class.php
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ static function processMassiveActionsForOneItemtype(MassiveAction $ma, CommonDBT
}
}
return true;

case 'activate':
foreach ($ids as $id) {
if (!$item->getFromDB($id)) {
Expand All @@ -122,6 +123,8 @@ static function processMassiveActionsForOneItemtype(MassiveAction $ma, CommonDBT
unset($input['id']);
$group_user = new Group_User();
if ($group_user->add($input)) {
// Purga el registro de la tabla del plugin tras reactivar exitosamente al usuario
$item->delete(['id' => $id], true);
$ma->itemDone($item->getType(), $id, MassiveAction::ACTION_OK);
} else {
$ma->itemDone($item->getType(), $id, MassiveAction::ACTION_KO);
Expand All @@ -138,25 +141,22 @@ public static function showDeactivated($item)
{
global $DB;

$query = [
'FROM' => self::getTable(),
];
if ($item->getType() == 'Group') {
$query['WHERE'] = [
'AND' => [
'groups_id' => $item->getID(),
],
];
if ($item->getType() != 'Group') {
return false;
}

$ID = $item->getID();
if (
!User::canView()
|| !$item->can($ID, READ)
) {
if (!User::canView() || !$item->can($ID, READ)) {
return false;
}

$query = [
'FROM' => self::getTable(),
'WHERE' => [
'groups_id' => $ID,
],
];

$canedit = Group_User::canUpdate();
$rand = mt_rand();

Expand All @@ -183,7 +183,7 @@ public static function showDeactivated($item)
'delegatee' => $row['is_userdelegate'] ? "<i class='ti ti-check'></i>" : '',
];
if ($canedit) {
$entry['actions'] = "<button type='button' onclick='getElementById(\"activateForm\").rowaction.value=\"activate\";getElementById(\"activateForm\").rowid.value=".$row['id'].";getElementById(\"activateForm\").submit();' class='btn btn-sm btn-primary' title='" . _sx('button', 'Activate user', 'moregroups') . "'><i class='ti ti-eye'></i></button>";
$entry['actions'] = "<button type='button' onclick='getElementById(\"activateForm\").rowaction.value=\"activate\";getElementById(\"activateForm\").rowid.value=" . $row['id'] . ";getElementById(\"activateForm\").submit();' class='btn btn-sm btn-primary' title='" . _sx('button', 'Activate user', 'moregroups') . "'><i class='ti ti-eye'></i></button>";
}
$entries[] = $entry;
}
Expand Down Expand Up @@ -222,19 +222,19 @@ public static function showDeactivated($item)
]);

if ($canedit) {
$label = _sx('button', 'Deactivate user', 'moregroups');
$label = htmlspecialchars(_sx('button', 'Deactivate user', 'moregroups'), ENT_QUOTES);
$script = <<<JAVASCRIPT
$(document).ready(function() {
$("input[name^='item[Group_User]'").each(function() {
var name = $(this).attr('name');
const myarray = name.split('[');
name = myarray[2].split(']')[0];

$(this).parent().parent().append("<td class='center'><button type='button' onclick='getElementById(\"activateForm\").rowaction.value=\"deactivate\";getElementById(\"activateForm\").rowid.value="+name+";getElementById(\"activateForm\").submit();' class='btn btn-sm btn-primary' title='{$label}'><i class='ti ti-eye-off'></i></button></td>");
});
});
JAVASCRIPT;
$(document).ready(function() {
$("input[name^='item[Group_User]']").each(function() {
var name = $(this).attr('name');
const myarray = name.split('[');
if (myarray.length >= 3) {
name = myarray[2].split(']')[0];
$(this).parent().parent().append("<td class='center'><button type='button' onclick='getElementById(\"activateForm\").rowaction.value=\"deactivate\";getElementById(\"activateForm\").rowid.value="+name+";getElementById(\"activateForm\").submit();' class='btn btn-sm btn-primary' title='{$label}'><i class='ti ti-eye-off'></i></button></td>");
}
});
});
JAVASCRIPT;

echo Html::scriptBlock($script);
}
Expand All @@ -261,21 +261,27 @@ static function install(Migration $migration)
if (!$DB->tableExists($table)) {
$migration->displayMessage("Installing $table");
$query = "CREATE TABLE IF NOT EXISTS $table (
`id` int {$default_key_sign} NOT NULL auto_increment,
`users_id` int unsigned NOT NULL DEFAULT '0',
`groups_id` int unsigned NOT NULL DEFAULT '0',
`is_dynamic` tinyint NOT NULL DEFAULT '0',
`is_manager` tinyint NOT NULL DEFAULT '0',
`is_userdelegate` tinyint NOT NULL DEFAULT '0',
PRIMARY KEY (`id`),
UNIQUE KEY `unicity` (`users_id`,`groups_id`),
KEY `groups_id` (`groups_id`),
KEY `is_dynamic` (`is_dynamic`),
KEY `is_manager` (`is_manager`),
KEY `is_userdelegate` (`is_userdelegate`)
) ENGINE=InnoDB DEFAULT CHARSET={$default_charset} COLLATE={$default_collation} ROW_FORMAT=DYNAMIC;";
`id` int {$default_key_sign} NOT NULL auto_increment,
`users_id` int unsigned NOT NULL DEFAULT '0',
`groups_id` int unsigned NOT NULL DEFAULT '0',
`is_dynamic` tinyint NOT NULL DEFAULT '0',
`is_manager` tinyint NOT NULL DEFAULT '0',
`is_userdelegate` tinyint NOT NULL DEFAULT '0',
PRIMARY KEY (`id`),
UNIQUE KEY `unicity` (`users_id`,`groups_id`),
KEY `groups_id` (`groups_id`),
KEY `is_dynamic` (`is_dynamic`),
KEY `is_manager` (`is_manager`),
KEY `is_userdelegate` (`is_userdelegate`)
) ENGINE=InnoDB DEFAULT CHARSET={$default_charset} COLLATE={$default_collation} ROW_FORMAT=DYNAMIC;";

if (!$DB->doQuery($query)) {
$migration->displayWarning("Error creating table $table: " . $DB->error(), true);
\Session::addMessageAfterRedirect(
sprintf(__('Error creating table %s: %s', 'moregroups'), 'glpi_plugin_moregroups_groups', $DB->error()),
false,
ERROR
);
return false;
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion setup.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@

use Glpi\Plugin\Hooks;

define('PLUGIN_MOREGROUPS_VERSION', '2.0.2');
define('PLUGIN_MOREGROUPS_VERSION', '2.0.3');
define('PLUGIN_MOREGROUPS_MIN_GLPI', '11.0.0');
define('PLUGIN_MOREGROUPS_MAX_GLPI', '11.0.99');

Expand Down
4 changes: 0 additions & 4 deletions src/Controller/GroupActionController.php
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,6 @@ public function __invoke(Request $request): Response
{
global $CFG_GLPI;

if (!Session::validateCSRF($request->request->all())) {
throw new AccessDeniedHttpException();
}

$rowaction = $request->request->get('rowaction');
$rowid = $request->request->get('rowid');

Expand Down
2 changes: 1 addition & 1 deletion tools/make_release.sh
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@ else
echo "Creating private release"
PACKAGE_NAME="$PLUGINNAME-$RELEASE"
fi
tar cjf "$PACKAGE_NAME.tar.bz2" $PLUGINNAME
tar cjf "glpi-$PACKAGE_NAME.tar.bz2" $PLUGINNAME

cd $INIT_PWD

Expand Down