1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.syncope.client.console.policies;
20
21 import com.fasterxml.jackson.databind.json.JsonMapper;
22 import java.io.Serializable;
23 import java.util.ArrayList;
24 import java.util.Collections;
25 import java.util.List;
26 import java.util.Map;
27 import java.util.Optional;
28 import java.util.function.Function;
29 import java.util.stream.Collectors;
30 import org.apache.commons.lang3.StringUtils;
31 import org.apache.syncope.client.console.SyncopeConsoleSession;
32 import org.apache.syncope.client.console.panels.AbstractModalPanel;
33 import org.apache.syncope.client.console.rest.AnyTypeRestClient;
34 import org.apache.syncope.client.console.rest.ImplementationRestClient;
35 import org.apache.syncope.client.console.rest.PolicyRestClient;
36 import org.apache.syncope.client.console.rest.SchemaRestClient;
37 import org.apache.syncope.client.console.wicket.markup.html.bootstrap.dialog.BaseModal;
38 import org.apache.syncope.client.console.wicket.markup.html.form.MultiPanel;
39 import org.apache.syncope.client.ui.commons.Constants;
40 import org.apache.syncope.client.ui.commons.ajax.form.IndicatorAjaxFormComponentUpdatingBehavior;
41 import org.apache.syncope.client.ui.commons.markup.html.form.AjaxCheckBoxPanel;
42 import org.apache.syncope.client.ui.commons.markup.html.form.AjaxDropDownChoicePanel;
43 import org.apache.syncope.client.ui.commons.markup.html.form.AjaxPalettePanel;
44 import org.apache.syncope.client.ui.commons.pages.BaseWebPage;
45 import org.apache.syncope.common.lib.policy.AbstractCorrelationRuleConf;
46 import org.apache.syncope.common.lib.policy.DefaultPullCorrelationRuleConf;
47 import org.apache.syncope.common.lib.policy.DefaultPushCorrelationRuleConf;
48 import org.apache.syncope.common.lib.policy.ProvisioningPolicyTO;
49 import org.apache.syncope.common.lib.policy.PullPolicyTO;
50 import org.apache.syncope.common.lib.to.ImplementationTO;
51 import org.apache.syncope.common.lib.to.SchemaTO;
52 import org.apache.syncope.common.lib.types.AnyTypeKind;
53 import org.apache.syncope.common.lib.types.IdMImplementationType;
54 import org.apache.syncope.common.lib.types.ImplementationEngine;
55 import org.apache.syncope.common.lib.types.PolicyType;
56 import org.apache.syncope.common.lib.types.SchemaType;
57 import org.apache.wicket.PageReference;
58 import org.apache.wicket.WicketRuntimeException;
59 import org.apache.wicket.ajax.AjaxRequestTarget;
60 import org.apache.wicket.markup.html.list.ListItem;
61 import org.apache.wicket.markup.html.panel.Panel;
62 import org.apache.wicket.model.IModel;
63 import org.apache.wicket.model.LoadableDetachableModel;
64 import org.apache.wicket.model.Model;
65 import org.apache.wicket.model.PropertyModel;
66 import org.apache.wicket.spring.injection.annot.SpringBean;
67
68 public class ProvisioningPolicyModalPanel extends AbstractModalPanel<ProvisioningPolicyTO> {
69
70 private static final long serialVersionUID = 2988891313881271124L;
71
72 protected static final JsonMapper MAPPER = JsonMapper.builder().findAndAddModules().build();
73
74 @SpringBean
75 protected ImplementationRestClient implementationRestClient;
76
77 @SpringBean
78 protected PolicyRestClient policyRestClient;
79
80 @SpringBean
81 protected AnyTypeRestClient anyTypeRestClient;
82
83 @SpringBean
84 protected SchemaRestClient schemaRestClient;
85
86 protected final IModel<Map<String, ImplementationTO>> implementations;
87
88 private final IModel<List<CorrelationRule>> model;
89
90 @SuppressWarnings("unchecked")
91 public ProvisioningPolicyModalPanel(
92 final ProvisioningPolicyTO policyTO,
93 final BaseModal<? extends ProvisioningPolicyTO> modal,
94 final PageReference pageRef) {
95
96 super((BaseModal<ProvisioningPolicyTO>) modal, pageRef);
97 ((BaseModal<ProvisioningPolicyTO>) modal).setFormModel(policyTO);
98
99 implementations = new LoadableDetachableModel<>() {
100
101 private static final long serialVersionUID = 5275935387613157437L;
102
103 @Override
104 protected Map<String, ImplementationTO> load() {
105 return implementationRestClient.list(policyTO instanceof PullPolicyTO
106 ? IdMImplementationType.PULL_CORRELATION_RULE
107 : IdMImplementationType.PUSH_CORRELATION_RULE).stream().
108 collect(Collectors.toMap(ImplementationTO::getKey, Function.identity()));
109 }
110 };
111
112 model = new PropertyModel<>(policyTO, "correlationRules") {
113
114 private static final long serialVersionUID = -8168676563540297301L;
115
116 private final List<CorrelationRule> rules = policyTO.getCorrelationRules().keySet().stream().
117 map(anyType -> new CorrelationRule(
118 policyTO instanceof PullPolicyTO
119 ? DefaultPullCorrelationRuleConf.class
120 : DefaultPushCorrelationRuleConf.class,
121 anyType,
122 implementations.getObject().get(policyTO.getCorrelationRules().get(anyType)))).
123 collect(Collectors.toList());
124
125 @Override
126 public List<CorrelationRule> getObject() {
127 return rules;
128 }
129
130 @Override
131 public void setObject(final List<CorrelationRule> object) {
132 policyTO.getCorrelationRules().clear();
133 rules.forEach(rule -> policyTO.getCorrelationRules().put(rule.getAnyType(), rule.getImpl().getKey()));
134 }
135 };
136
137 add(new MultiPanel<>("correlationRules", "correlationRules", model) {
138
139 private static final long serialVersionUID = -2481579077338205547L;
140
141 @Override
142 protected CorrelationRule newModelObject() {
143 return new CorrelationRule(policyTO instanceof PullPolicyTO
144 ? DefaultPullCorrelationRuleConf.class
145 : DefaultPushCorrelationRuleConf.class);
146 }
147
148 @Override
149 protected CorrelationRulePanel getItemPanel(final ListItem<CorrelationRule> item) {
150 return new CorrelationRulePanel("panel", Model.of(item.getModelObject()));
151 }
152
153 @Override
154 protected void sendError(final String message) {
155 SyncopeConsoleSession.get().error(getString(Constants.OPERATION_ERROR));
156 }
157 });
158 }
159
160 @Override
161 public void onSubmit(final AjaxRequestTarget target) {
162 try {
163 getItem().getCorrelationRules().clear();
164 model.getObject().forEach(rule -> {
165 getItem().getCorrelationRules().put(rule.getAnyType(), rule.getImplKey());
166
167 if (rule.getImpl().getEngine() == ImplementationEngine.JAVA && rule.getDefaultRuleConf() != null) {
168 try {
169 implementationRestClient.update(rule.getImpl());
170 } catch (Exception e) {
171 throw new WicketRuntimeException(e);
172 }
173 }
174 });
175 policyRestClient.update(getItem() instanceof PullPolicyTO
176 ? PolicyType.PULL : PolicyType.PUSH, getItem());
177
178 SyncopeConsoleSession.get().success(getString(Constants.OPERATION_SUCCEEDED));
179 this.modal.close(target);
180 } catch (Exception e) {
181 LOG.error("While creating/updating policy", e);
182 SyncopeConsoleSession.get().onException(e);
183 }
184 ((BaseWebPage) pageRef.getPage()).getNotificationPanel().refresh(target);
185 }
186
187 protected class CorrelationRulePanel extends Panel {
188
189 private static final long serialVersionUID = -5380414818290018189L;
190
191 CorrelationRulePanel(final String id, final IModel<CorrelationRule> correlationRule) {
192 super(id);
193
194 AjaxDropDownChoicePanel<String> anyType = new AjaxDropDownChoicePanel<>(
195 "anyType", "anyType", new PropertyModel<String>(correlationRule.getObject(), "anyType")).
196 setNullValid(true).
197 setChoices(anyTypeRestClient.list());
198 anyType.setNullValid(false);
199 anyType.setRequired(true);
200 anyType.setOutputMarkupId(true);
201 add(anyType);
202
203 AjaxDropDownChoicePanel<String> rule = new AjaxDropDownChoicePanel<>(
204 "rule", "rule", new PropertyModel<String>(correlationRule.getObject(), "implKey")).
205 setChoices(implementations.getObject().keySet().stream().sorted().collect(Collectors.toList()));
206 rule.setNullValid(false);
207 rule.setRequired(true);
208 rule.setOutputMarkupId(true);
209 add(rule);
210
211 PropertyModel<Boolean> orSchemasModel =
212 new PropertyModel<>(correlationRule.getObject().getDefaultRuleConf(), "orSchemas") {
213
214 private static final long serialVersionUID = 807008909842554829L;
215
216 @Override
217 public Boolean getObject() {
218 AbstractCorrelationRuleConf conf = correlationRule.getObject().getDefaultRuleConf();
219 return conf instanceof DefaultPullCorrelationRuleConf
220 ? DefaultPullCorrelationRuleConf.class.cast(conf).isOrSchemas()
221 : conf instanceof DefaultPushCorrelationRuleConf
222 ? DefaultPushCorrelationRuleConf.class.cast(conf).isOrSchemas()
223 : false;
224 }
225
226 @Override
227 public void setObject(final Boolean object) {
228 AbstractCorrelationRuleConf conf = correlationRule.getObject().getDefaultRuleConf();
229 if (conf instanceof DefaultPullCorrelationRuleConf) {
230 DefaultPullCorrelationRuleConf.class.cast(conf).setOrSchemas(object);
231 } else if (conf instanceof DefaultPushCorrelationRuleConf) {
232 DefaultPushCorrelationRuleConf.class.cast(conf).setOrSchemas(object);
233 }
234 }
235 };
236 AjaxCheckBoxPanel orSchemas = new AjaxCheckBoxPanel("orSchemas", "orSchemas", orSchemasModel, false);
237 orSchemas.setOutputMarkupPlaceholderTag(true);
238 add(orSchemas.setVisible(correlationRule.getObject().getDefaultRuleConf() != null));
239
240 PropertyModel<List<String>> defaultRuleConfModel =
241 new PropertyModel<>(correlationRule.getObject().getDefaultRuleConf(), "schemas") {
242
243 private static final long serialVersionUID = 3799387950428254072L;
244
245 private List<String> schemas() {
246 AbstractCorrelationRuleConf conf = correlationRule.getObject().getDefaultRuleConf();
247 return conf instanceof DefaultPullCorrelationRuleConf
248 ? DefaultPullCorrelationRuleConf.class.cast(conf).getSchemas()
249 : conf instanceof DefaultPushCorrelationRuleConf
250 ? DefaultPushCorrelationRuleConf.class.cast(conf).getSchemas()
251 : List.of();
252 }
253
254 @Override
255 public List<String> getObject() {
256 List<String> schemas = new ArrayList<>();
257 if (correlationRule.getObject().getDefaultRuleConf() != null) {
258 schemas.addAll(schemas());
259 }
260 return schemas;
261 }
262
263 @Override
264 public void setObject(final List<String> object) {
265 if (correlationRule.getObject().getDefaultRuleConf() != null) {
266 schemas().clear();
267 schemas().addAll(object);
268 }
269 }
270 };
271 AjaxPalettePanel<String> defaultRuleConf = new AjaxPalettePanel.Builder<String>().
272 setName("defaultRuleConf").build("defaultRuleConf",
273 defaultRuleConfModel, new AjaxPalettePanel.Builder.Query<>() {
274
275 private static final long serialVersionUID = -7223078772249308813L;
276
277 @Override
278 public List<String> execute(final String filter) {
279 return getSchemas(correlationRule.getObject());
280 }
281 });
282 defaultRuleConf.hideLabel().setOutputMarkupPlaceholderTag(true);
283 add(defaultRuleConf.setVisible(correlationRule.getObject().getDefaultRuleConf() != null));
284
285 anyType.getField().add(new IndicatorAjaxFormComponentUpdatingBehavior(Constants.ON_CHANGE) {
286
287 private static final long serialVersionUID = -1107858522700306810L;
288
289 @Override
290 protected void onUpdate(final AjaxRequestTarget target) {
291 if (orSchemas.isVisibleInHierarchy()) {
292 target.add(orSchemas);
293 }
294 if (defaultRuleConf.isVisibleInHierarchy()) {
295 correlationRule.getObject().setImpl(null);
296 defaultRuleConf.reload(target);
297 target.add(defaultRuleConf);
298 }
299 }
300 });
301
302 rule.getField().add(new IndicatorAjaxFormComponentUpdatingBehavior(Constants.ON_CHANGE) {
303
304 private static final long serialVersionUID = -1107858522700306810L;
305
306 @Override
307 protected void onUpdate(final AjaxRequestTarget target) {
308 if (correlationRule.getObject().getDefaultRuleConf() == null) {
309 orSchemas.setVisible(false);
310 defaultRuleConf.setVisible(false);
311 } else {
312 orSchemas.setVisible(true);
313 defaultRuleConf.setVisible(true);
314 }
315 target.add(orSchemas);
316 target.add(defaultRuleConf);
317 }
318 });
319 }
320
321 private List<String> getSchemas(final CorrelationRule rule) {
322 List<String> choices = StringUtils.isEmpty(rule.getAnyType())
323 ? new ArrayList<>()
324 : schemaRestClient.getSchemas(SchemaType.PLAIN,
325 rule.getAnyType().equals(AnyTypeKind.USER.name())
326 ? AnyTypeKind.USER
327 : rule.getAnyType().equals(AnyTypeKind.GROUP.name())
328 ? AnyTypeKind.GROUP
329 : AnyTypeKind.ANY_OBJECT).stream().map(SchemaTO::getKey).
330 collect(Collectors.toList());
331 choices.add(Constants.KEY_FIELD_NAME);
332 choices.add(rule.getAnyType().equals(AnyTypeKind.USER.name())
333 ? Constants.USERNAME_FIELD_NAME : Constants.NAME_FIELD_NAME);
334 Collections.sort(choices);
335 return choices;
336 }
337 }
338
339 private class CorrelationRule implements Serializable {
340
341 private static final long serialVersionUID = 4221521483948294336L;
342
343 private final Class<? extends AbstractCorrelationRuleConf> ruleConfClass;
344
345 private String anyType;
346
347 private ImplementationTO impl;
348
349 private AbstractCorrelationRuleConf defaultRuleConf;
350
351 CorrelationRule(final Class<? extends AbstractCorrelationRuleConf> ruleConfClass) {
352 this.ruleConfClass = ruleConfClass;
353 this.anyType = AnyTypeKind.USER.name();
354 }
355
356 CorrelationRule(
357 final Class<? extends AbstractCorrelationRuleConf> ruleConfClass,
358 final String anyType,
359 final ImplementationTO impl) {
360
361 this.ruleConfClass = ruleConfClass;
362 this.anyType = anyType;
363 setImpl(impl);
364 }
365
366 public String getAnyType() {
367 return anyType;
368 }
369
370 public void setAnyType(final String anyType) {
371 this.anyType = anyType;
372 }
373
374 public String getImplKey() {
375 return Optional.ofNullable(impl).map(ImplementationTO::getKey).orElse(null);
376 }
377
378 public void setImplKey(final String key) {
379 setImpl(implementations.getObject().get(key));
380 }
381
382 public final void setImpl(final ImplementationTO impl) {
383 this.impl = impl;
384 if (impl != null) {
385 this.defaultRuleConf = null;
386 try {
387 this.defaultRuleConf = MAPPER.readValue(impl.getBody(), ruleConfClass);
388 } catch (Exception e) {
389 LOG.debug("Could not deserialize {} as {}", impl.getBody(), ruleConfClass.getName());
390 }
391 }
392 }
393
394 public ImplementationTO getImpl() {
395 if (defaultRuleConf != null) {
396 try {
397 this.impl.setBody(MAPPER.writeValueAsString(defaultRuleConf));
398 } catch (Exception e) {
399 LOG.error("Could not serialize {}", defaultRuleConf);
400 }
401 }
402 return impl;
403 }
404
405 public void setDefaultRuleConf(final DefaultPushCorrelationRuleConf defaultRuleConf) {
406 this.defaultRuleConf = defaultRuleConf;
407 }
408
409 public AbstractCorrelationRuleConf getDefaultRuleConf() {
410 return defaultRuleConf;
411 }
412 }
413 }