View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
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 }