1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.syncope.core.provisioning.java.propagation;
20
21 import java.util.HashSet;
22 import java.util.List;
23 import java.util.Objects;
24 import java.util.Optional;
25 import java.util.Set;
26 import java.util.TreeSet;
27 import org.apache.commons.jexl3.JexlContext;
28 import org.apache.commons.jexl3.MapContext;
29 import org.apache.commons.lang3.StringUtils;
30 import org.apache.syncope.common.lib.request.MembershipUR;
31 import org.apache.syncope.common.lib.request.UserUR;
32 import org.apache.syncope.common.lib.to.Provision;
33 import org.apache.syncope.common.lib.types.AnyTypeKind;
34 import org.apache.syncope.common.lib.types.PatchOperation;
35 import org.apache.syncope.common.lib.types.ResourceOperation;
36 import org.apache.syncope.core.persistence.api.dao.GroupDAO;
37 import org.apache.syncope.core.persistence.api.dao.UserDAO;
38 import org.apache.syncope.core.persistence.api.entity.ExternalResource;
39 import org.apache.syncope.core.persistence.api.entity.group.Group;
40 import org.apache.syncope.core.persistence.api.entity.task.PropagationData;
41 import org.apache.syncope.core.persistence.api.entity.user.User;
42 import org.apache.syncope.core.provisioning.api.DerAttrHandler;
43 import org.apache.syncope.core.provisioning.api.jexl.JexlUtils;
44 import org.apache.syncope.core.provisioning.api.propagation.PropagationActions;
45 import org.apache.syncope.core.provisioning.api.propagation.PropagationTaskInfo;
46 import org.apache.syncope.core.spring.implementation.InstanceScope;
47 import org.apache.syncope.core.spring.implementation.SyncopeImplementation;
48 import org.identityconnectors.framework.common.objects.AttributeBuilder;
49 import org.identityconnectors.framework.common.objects.AttributeDeltaBuilder;
50 import org.identityconnectors.framework.common.objects.AttributeDeltaUtil;
51 import org.identityconnectors.framework.common.objects.AttributeUtil;
52 import org.slf4j.Logger;
53 import org.slf4j.LoggerFactory;
54 import org.springframework.beans.factory.annotation.Autowired;
55 import org.springframework.transaction.annotation.Transactional;
56
57
58
59
60
61
62
63 @SyncopeImplementation(scope = InstanceScope.PER_CONTEXT)
64 public class LDAPMembershipPropagationActions implements PropagationActions {
65
66 protected static final Logger LOG = LoggerFactory.getLogger(LDAPMembershipPropagationActions.class);
67
68 @Autowired
69 protected DerAttrHandler derAttrHandler;
70
71 @Autowired
72 protected UserDAO userDAO;
73
74 @Autowired
75 protected GroupDAO groupDAO;
76
77
78
79
80
81
82 protected String getGroupMembershipAttrName() {
83 return "ldapGroups";
84 }
85
86 protected String evaluateGroupConnObjectLink(final String connObjectLinkTemplate, final Group group) {
87 LOG.debug("Evaluating connObjectLink for {}", group);
88
89 JexlContext jexlContext = new MapContext();
90 JexlUtils.addFieldsToContext(group, jexlContext);
91 JexlUtils.addPlainAttrsToContext(group.getPlainAttrs(), jexlContext);
92 JexlUtils.addDerAttrsToContext(group, derAttrHandler, jexlContext);
93
94 return JexlUtils.evaluate(connObjectLinkTemplate, jexlContext).toString();
95 }
96
97 protected void buildManagedGroupConnObjectLinks(
98 final ExternalResource resource,
99 final String connObjectLinkTemplate,
100 final Set<String> connObjectLinks) {
101
102 List<Group> managedGroups = groupDAO.findByResource(resource);
103 managedGroups.forEach(group -> connObjectLinks.add(evaluateGroupConnObjectLink(connObjectLinkTemplate, group)));
104 }
105
106 @Transactional(readOnly = true)
107 @Override
108 public void before(final PropagationTaskInfo taskInfo) {
109 if (AnyTypeKind.USER != taskInfo.getAnyTypeKind() || taskInfo.getOperation() == ResourceOperation.DELETE) {
110 return;
111 }
112
113 taskInfo.getResource().getProvisionByAnyType(AnyTypeKind.GROUP.name()).
114 map(Provision::getMapping).
115 filter(mapping -> StringUtils.isNotBlank(mapping.getConnObjectLink())).ifPresentOrElse(mapping -> {
116
117 User user = userDAO.find(taskInfo.getEntityKey());
118 Set<String> groups = new HashSet<>();
119
120
121
122 userDAO.findAllGroupKeys(user).stream().
123 map(groupDAO::find).
124 filter(group -> group.getResources().contains(taskInfo.getResource())).
125 forEach(group -> {
126 String groupConnObjectLink = evaluateGroupConnObjectLink(
127 mapping.getConnObjectLink(), group);
128
129 LOG.debug("ConnObjectLink for {} is '{}'", group, groupConnObjectLink);
130 if (StringUtils.isNotBlank(groupConnObjectLink)) {
131 groups.add(groupConnObjectLink);
132 }
133 });
134 LOG.debug("Group connObjectLinks to propagate for membership: {}", groups);
135
136 PropagationData data = taskInfo.getPropagationData();
137
138
139 Optional.ofNullable(AttributeUtil.find(getGroupMembershipAttrName(), data.getAttributes())).
140 ifPresent(ldapGroups -> {
141 Optional.ofNullable(ldapGroups.getValue()).
142 ifPresent(value -> value.forEach(obj -> groups.add(obj.toString())));
143
144 data.getAttributes().remove(ldapGroups);
145 });
146 LOG.debug("Group connObjectLinks after including the ones from mapping: {}", groups);
147
148
149 taskInfo.getBeforeObj().
150 map(beforeObj -> beforeObj.getAttributeByName(getGroupMembershipAttrName())).
151 filter(Objects::nonNull).
152 ifPresent(beforeLdapGroups -> {
153 Set<String> connObjectLinks = new TreeSet<>(String.CASE_INSENSITIVE_ORDER);
154 buildManagedGroupConnObjectLinks(
155 taskInfo.getResource(),
156 mapping.getConnObjectLink(),
157 connObjectLinks);
158
159 LOG.debug("Memberships not managed by Syncope: {}", beforeLdapGroups);
160 beforeLdapGroups.getValue().stream().
161 filter(value -> !connObjectLinks.contains(String.valueOf(value))).
162 forEach(value -> groups.add(String.valueOf(value)));
163 });
164
165 LOG.debug("Adding Group connObjectLinks to attributes: {}={}", getGroupMembershipAttrName(), groups);
166 data.getAttributes().add(AttributeBuilder.build(getGroupMembershipAttrName(), groups));
167
168 if (data.getAttributeDeltas() != null && taskInfo.getUpdateRequest() != null) {
169 Set<String> groupsToAdd = new HashSet<>();
170 Set<String> groupsToRemove = new HashSet<>();
171
172
173 for (MembershipUR memb : ((UserUR) taskInfo.getUpdateRequest()).getMemberships()) {
174 String connObjectLink = evaluateGroupConnObjectLink(
175 mapping.getConnObjectLink(),
176 groupDAO.find(memb.getGroup()));
177 if (memb.getOperation() == PatchOperation.ADD_REPLACE) {
178 groupsToAdd.add(connObjectLink);
179 } else {
180 groupsToRemove.add(connObjectLink);
181 }
182 }
183
184
185 Optional.ofNullable(
186 AttributeDeltaUtil.find(getGroupMembershipAttrName(), data.getAttributeDeltas())).
187 ifPresent(ldapGroups -> {
188 Optional.ofNullable(ldapGroups.getValuesToAdd()).
189 ifPresent(value -> value.forEach(obj -> groupsToAdd.add(obj.toString())));
190 Optional.ofNullable(ldapGroups.getValuesToRemove()).
191 ifPresent(value -> value.forEach(obj -> groupsToRemove.add(obj.toString())));
192
193 data.getAttributeDeltas().remove(ldapGroups);
194 });
195
196 if (!groupsToAdd.isEmpty() || !groupsToRemove.isEmpty()) {
197 LOG.debug("Adding Group connObjectLinks to attribute deltas: {}={},{}",
198 getGroupMembershipAttrName(), groupsToAdd, groupsToRemove);
199 data.getAttributeDeltas().add(
200 AttributeDeltaBuilder.build(getGroupMembershipAttrName(), groupsToAdd,
201 groupsToRemove));
202 }
203 }
204 }, () -> LOG.debug("Not about user, or group mapping missing for resource: not doing anything"));
205 }
206 }