Skip to content

Commit b0f7d89

Browse files
committed
Bug #14787
Report the fix from branch 6.4.x: Two new listeners are added for the Node API. The first one listens for events about change on the roles played by a user in a component instance (either directly or through a group playing a role) and carried by the UserRoleEvent event. The second one listens for events about change on the roles played by a group in a component instance and carried by the ProfileInstEvent event. The goal of the two listeners is to figuring out the users and the groups that don't play anymore a role in a component instance and then invoke the NodeProfileInstUpdater service to remove from the specific access rights of nodes the related users and groups.
1 parent 7b60ff6 commit b0f7d89

File tree

5 files changed

+411
-16
lines changed

5 files changed

+411
-16
lines changed

core-library/src/integration-test/resources/org/silverpeas/core/admin/user/notification/role/create_database.sql

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,37 @@ CREATE TABLE ST_UserRole_Group_Rel
219219
CONSTRAINT FK_UserRole_Group_Rel_2 FOREIGN KEY (groupId) REFERENCES ST_Group (id)
220220
);
221221

222+
CREATE TABLE SB_Node_Node
223+
(
224+
nodeId INT NOT NULL,
225+
nodeName VARCHAR (1000) NOT NULL,
226+
nodeDescription VARCHAR (2000) NULL,
227+
nodeCreationDate VARCHAR (10) NOT NULL,
228+
nodeCreatorId VARCHAR (100) NOT NULL,
229+
nodePath VARCHAR (1000) NOT NULL,
230+
nodeLevelNumber INT NOT NULL,
231+
nodeFatherId INT NOT NULL,
232+
modelId VARCHAR (1000) NULL,
233+
nodeStatus VARCHAR (1000) NULL,
234+
instanceId VARCHAR (50) NOT NULL,
235+
type VARCHAR (50) NULL,
236+
orderNumber INT DEFAULT (0) NULL,
237+
lang CHAR(2),
238+
rightsDependsOn INT DEFAULT (-1) NOT NULL,
239+
CONSTRAINT PK_Node_Node PRIMARY KEY (nodeId, instanceId)
240+
);
241+
242+
CREATE TABLE SB_Node_NodeI18N
243+
(
244+
id INT NOT NULL,
245+
nodeId INT NOT NULL,
246+
lang CHAR (2) NOT NULL,
247+
nodeName VARCHAR (1000) NOT NULL,
248+
nodeDescription VARCHAR (2000),
249+
CONSTRAINT PK_Node_NodeI18N PRIMARY KEY (id),
250+
CONSTRAINT UN_Node_NodeI18N UNIQUE (nodeId, lang)
251+
);
252+
222253
/*
223254
* The SQL tables of a given component used in tests
224255
*/

core-library/src/main/java/org/silverpeas/core/admin/user/notification/role/ProfileInstUpdateEventListener.java

Lines changed: 45 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -30,12 +30,15 @@
3030
import org.silverpeas.core.admin.user.model.ProfileInst;
3131
import org.silverpeas.core.admin.user.notification.ProfileInstEvent;
3232
import org.silverpeas.core.annotation.Service;
33-
import org.silverpeas.core.notification.system.CDIResourceEventListener;
33+
import org.silverpeas.core.notification.system.CDIAfterSuccessfulTransactionResourceEventListener;
3434
import org.silverpeas.core.notification.system.ResourceEvent;
3535

3636
import javax.inject.Inject;
37+
import java.util.HashMap;
3738
import java.util.List;
39+
import java.util.Map;
3840
import java.util.Set;
41+
import java.util.function.Predicate;
3942
import java.util.stream.Collectors;
4043
import java.util.stream.Stream;
4144

@@ -50,7 +53,8 @@
5053
* @author mmoquillon
5154
*/
5255
@Service
53-
class ProfileInstUpdateEventListener extends CDIResourceEventListener<ProfileInstEvent> {
56+
class ProfileInstUpdateEventListener
57+
extends CDIAfterSuccessfulTransactionResourceEventListener<ProfileInstEvent> {
5458

5559
@Inject
5660
private OrganizationController organization;
@@ -62,15 +66,19 @@ class ProfileInstUpdateEventListener extends CDIResourceEventListener<ProfileIns
6266
public void onUpdate(ProfileInstEvent event) {
6367
ProfileInst before = event.getTransition().getBefore();
6468
ProfileInst after = event.getTransition().getAfter();
65-
ComponentInst componentInst = getComponentInstanceId(before.getComponentFatherId());
66-
Set<String> removedUsers = findRemovedUsersId(componentInst, before, after);
67-
if (!removedUsers.isEmpty()) {
68-
UserRoleEvent userRoleEvent = UserRoleEvent.builderFor(ResourceEvent.Type.DELETION)
69-
.role(before.getName())
70-
.instanceId(componentInst.getId())
71-
.userIds(removedUsers)
72-
.build();
73-
notifier.notify(userRoleEvent);
69+
if (before.getObjectId().isNotDefined()) {
70+
// we take in charge only changes in the right profiles of component instances, no those of
71+
// resources managed by the component instances
72+
ComponentInst componentInst = getComponentInstanceId(before.getComponentFatherId());
73+
Set<String> removedUsers = findRemovedUsersId(componentInst, before, after);
74+
if (!removedUsers.isEmpty()) {
75+
UserRoleEvent userRoleEvent = UserRoleEvent.builderFor(ResourceEvent.Type.DELETION)
76+
.role(before.getName())
77+
.instanceId(componentInst.getId())
78+
.userIds(removedUsers)
79+
.build();
80+
notifier.notify(userRoleEvent);
81+
}
7482
}
7583
}
7684

@@ -90,13 +98,15 @@ private Set<String> findRemovedUsersId(ComponentInst componentInst, ProfileInst
9098
ProfileInst after) {
9199
List<String> usersAfter = after.getAllUsers();
92100
List<String> groupsAfter = after.getAllGroups();
93-
String roleName = before.getName();
101+
String roleName = before.getName();
102+
NoAnymorePlayedRole roleNotAnymorePlayedByUser = new NoAnymorePlayedRole(roleName,
103+
componentInst.getId());
104+
94105
// get all the users directly removed from the profile instance and who don't play anymore
95106
// the role for the application (they can be play another role)
96107
Stream<String> removedUsers = before.getAllUsers().stream()
97108
.filter(user -> !usersAfter.contains(user))
98-
.filter(u -> Stream.of(organization.getUserProfiles(u, componentInst.getId()))
99-
.noneMatch(p -> p.equalsIgnoreCase(roleName)));
109+
.filter(roleNotAnymorePlayedByUser);
100110

101111

102112
// get all the users belonging to the groups removed from the profile instance and who's not
@@ -107,14 +117,33 @@ private Set<String> findRemovedUsersId(ComponentInst componentInst, ProfileInst
107117
.map(g -> organization.getGroup(g))
108118
.flatMap(g -> Stream.of(((Group) g).getUserIds()))
109119
.distinct()
110-
.filter(u -> Stream.of(organization.getUserProfiles(u, componentInst.getId()))
111-
.noneMatch(p -> p.equalsIgnoreCase(roleName)));
120+
.filter(roleNotAnymorePlayedByUser);
112121

113122
return Stream.concat(removedUsers, removedUsersInGroups).collect(Collectors.toSet());
114123
}
115124

116125
private ComponentInst getComponentInstanceId(int localComponentId) {
117126
return organization.getComponentInst(String.valueOf(localComponentId));
118127
}
128+
129+
private class NoAnymorePlayedRole implements Predicate<String> {
130+
131+
private final Map<String, String[]> cache = new HashMap<>();
132+
private final String instanceId;
133+
private final String roleName;
134+
135+
public NoAnymorePlayedRole(String roleName, String componentInstanceId) {
136+
this.instanceId = componentInstanceId;
137+
this.roleName = roleName;
138+
}
139+
140+
@Override
141+
public boolean test(String userId) {
142+
String[] roles = cache.computeIfAbsent(userId,
143+
u -> organization.getUserProfiles(u, instanceId));
144+
return roles.length == 0 ||
145+
Stream.of(roles).noneMatch(p -> p.equalsIgnoreCase(roleName));
146+
}
147+
}
119148
}
120149

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
/*
2+
* Copyright (C) 2000 - 2025 Silverpeas
3+
*
4+
* This program is free software: you can redistribute it and/or modify
5+
* it under the terms of the GNU Affero General Public License as
6+
* published by the Free Software Foundation, either version 3 of the
7+
* License, or (at your option) any later version.
8+
*
9+
* As a special exception to the terms and conditions of version 3.0 of
10+
* the GPL, you may redistribute this Program in connection with Free/Libre
11+
* Open Source Software ("FLOSS") applications as described in Silverpeas's
12+
* FLOSS exception. You should have received a copy of the text describing
13+
* the FLOSS exception, and it is also available here:
14+
* "https://www.silverpeas.org/legal/floss_exception.html"
15+
*
16+
* This program is distributed in the hope that it will be useful,
17+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
18+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19+
* GNU Affero General Public License for more details.
20+
*
21+
* You should have received a copy of the GNU Affero General Public License
22+
* along with this program. If not, see <https://www.gnu.org/licenses/>.
23+
*/
24+
25+
package org.silverpeas.core.node.service;
26+
27+
import org.silverpeas.core.admin.component.model.ComponentInst;
28+
import org.silverpeas.core.admin.service.AdminException;
29+
import org.silverpeas.core.admin.service.Administration;
30+
import org.silverpeas.core.admin.user.model.ProfileInst;
31+
import org.silverpeas.core.admin.user.notification.ProfileInstEvent;
32+
import org.silverpeas.core.annotation.Service;
33+
import org.silverpeas.core.notification.system.CDIResourceEventListener;
34+
import org.silverpeas.kernel.SilverpeasRuntimeException;
35+
36+
import javax.inject.Inject;
37+
import javax.transaction.Transactional;
38+
import java.util.List;
39+
import java.util.Set;
40+
import java.util.stream.Collectors;
41+
42+
/**
43+
* Listener of events about changes in a given right profile instance of a component instance.
44+
* For all the user groups removed from the profile instance related by the event, an invocation
45+
* to the {@link NodeProfileInstUpdater} is performed.
46+
*
47+
* @author mmoquillon
48+
*/
49+
@Service
50+
public class NodeProfileInstEventListener extends CDIResourceEventListener<ProfileInstEvent> {
51+
52+
@Inject
53+
private Administration admin;
54+
@Inject
55+
private NodeProfileInstUpdater updater;
56+
57+
@Transactional
58+
@Override
59+
public void onUpdate(ProfileInstEvent event) {
60+
ProfileInst before = event.getTransition().getBefore();
61+
if (before.isOnComponentInstance()) {
62+
ProfileInst after = event.getTransition().getAfter();
63+
int instanceId = before.getComponentFatherId();
64+
ComponentInst instance = getComponentInstanceId(instanceId);
65+
List<String> groupsAfter = after.getAllGroups();
66+
Set<String> removedGroups = before.getAllGroups().stream()
67+
.filter(group -> !groupsAfter.contains(group))
68+
.collect(Collectors.toSet());
69+
updater.getRemoverFor(instance.getId())
70+
.ofGroups(removedGroups)
71+
.apply();
72+
}
73+
}
74+
75+
private ComponentInst getComponentInstanceId(int localComponentId) {
76+
try {
77+
return admin.getComponentInst(String.valueOf(localComponentId));
78+
} catch (AdminException e) {
79+
throw new SilverpeasRuntimeException(e);
80+
}
81+
}
82+
83+
}
84+

0 commit comments

Comments
 (0)