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 java.io.Serializable;
22  import java.util.ArrayList;
23  import java.util.Arrays;
24  import java.util.List;
25  import java.util.Optional;
26  import java.util.stream.Collectors;
27  import org.apache.syncope.client.console.SyncopeConsoleSession;
28  import org.apache.syncope.client.console.SyncopeWebApplication;
29  import org.apache.syncope.client.console.panels.AbstractModalPanel;
30  import org.apache.syncope.client.console.rest.PolicyRestClient;
31  import org.apache.syncope.client.console.wicket.markup.html.bootstrap.dialog.BaseModal;
32  import org.apache.syncope.client.ui.commons.Constants;
33  import org.apache.syncope.client.ui.commons.ajax.form.IndicatorAjaxFormComponentUpdatingBehavior;
34  import org.apache.syncope.client.ui.commons.markup.html.form.AjaxCheckBoxPanel;
35  import org.apache.syncope.client.ui.commons.markup.html.form.AjaxDropDownChoicePanel;
36  import org.apache.syncope.client.ui.commons.markup.html.form.AjaxPalettePanel;
37  import org.apache.syncope.client.ui.commons.markup.html.form.AjaxSpinnerFieldPanel;
38  import org.apache.syncope.client.ui.commons.markup.html.form.AjaxTextFieldPanel;
39  import org.apache.syncope.client.ui.commons.pages.BaseWebPage;
40  import org.apache.syncope.client.ui.commons.panels.WizardModalPanel;
41  import org.apache.syncope.client.ui.commons.wizards.AbstractModalPanelBuilder;
42  import org.apache.syncope.client.ui.commons.wizards.AjaxWizard;
43  import org.apache.syncope.common.lib.policy.PolicyTO;
44  import org.apache.syncope.common.lib.types.BackOffStrategy;
45  import org.apache.syncope.common.lib.types.ConflictResolutionAction;
46  import org.apache.syncope.common.lib.types.PolicyType;
47  import org.apache.wicket.Application;
48  import org.apache.wicket.Component;
49  import org.apache.wicket.PageReference;
50  import org.apache.wicket.ajax.AjaxRequestTarget;
51  import org.apache.wicket.core.util.lang.PropertyResolver;
52  import org.apache.wicket.core.util.lang.PropertyResolverConverter;
53  import org.apache.wicket.markup.html.list.ListItem;
54  import org.apache.wicket.markup.html.list.ListView;
55  import org.apache.wicket.model.IModel;
56  import org.apache.wicket.model.LoadableDetachableModel;
57  import org.apache.wicket.model.PropertyModel;
58  import org.apache.wicket.model.util.ListModel;
59  
60  public class PolicyModalPanelBuilder<T extends PolicyTO> extends AbstractModalPanelBuilder<T> {
61  
62      private static final long serialVersionUID = 5945391813567245081L;
63  
64      protected static class BackOffParamsModel<N extends Number> implements IModel<N> {
65  
66          private static final long serialVersionUID = 28839546672164L;
67  
68          protected final PropertyModel<String> backOffParamsModel;
69  
70          protected final int index;
71  
72          BackOffParamsModel(final PropertyModel<String> backOffParamsModel, final int index) {
73              this.backOffParamsModel = backOffParamsModel;
74              this.index = index;
75          }
76  
77          @SuppressWarnings("unchecked")
78          @Override
79          public N getObject() {
80              String[] split = backOffParamsModel.getObject().split(";");
81              if (index >= split.length) {
82                  return null;
83              }
84  
85              return index == 2
86                      ? (N) Double.valueOf(backOffParamsModel.getObject().split(";")[index])
87                      : (N) Long.valueOf(backOffParamsModel.getObject().split(";")[index]);
88          }
89  
90          @Override
91          public void setObject(final N object) {
92              String[] split = backOffParamsModel.getObject().split(";");
93              if (index < split.length) {
94                  split[index] = object.toString();
95                  backOffParamsModel.setObject(Arrays.stream(split).collect(Collectors.joining(";")));
96              }
97          }
98      }
99  
100     protected final BaseModal<T> modal;
101 
102     protected final PolicyType type;
103 
104     protected final PolicyRestClient policyRestClient;
105 
106     public PolicyModalPanelBuilder(
107             final PolicyType type,
108             final T policyTO,
109             final BaseModal<T> modal,
110             final PolicyRestClient policyRestClient,
111             final PageReference pageRef) {
112 
113         super(policyTO, pageRef);
114 
115         this.type = type;
116         this.modal = modal;
117         this.policyRestClient = policyRestClient;
118     }
119 
120     @Override
121     public WizardModalPanel<T> build(final String id, final int index, final AjaxWizard.Mode mode) {
122         return new Profile(newModelObject(), modal, pageRef);
123     }
124 
125     private class Profile extends AbstractModalPanel<T> {
126 
127         private static final long serialVersionUID = -3043839139187792810L;
128 
129         private final T policyTO;
130 
131         private final LoadableDetachableModel<List<String>> resources = new LoadableDetachableModel<>() {
132 
133             private static final long serialVersionUID = 5275935387613157437L;
134 
135             @Override
136             protected List<String> load() {
137                 return SyncopeWebApplication.get().getResourceProvider().get();
138             }
139         };
140 
141         private final LoadableDetachableModel<List<String>> accessPolicyConfClasses = new LoadableDetachableModel<>() {
142 
143             private static final long serialVersionUID = 5275935387613157437L;
144 
145             @Override
146             protected List<String> load() {
147                 return SyncopeWebApplication.get().getAccessPolicyConfProvider().get();
148             }
149         };
150 
151         Profile(final T policyTO, final BaseModal<T> modal, final PageReference pageRef) {
152             super(modal, pageRef);
153             modal.setFormModel(policyTO);
154 
155             this.policyTO = policyTO;
156 
157             List<Component> fields = new ArrayList<>();
158 
159             fields.add(new AjaxTextFieldPanel("field", Constants.NAME_FIELD_NAME,
160                     new PropertyModel<>(policyTO, Constants.NAME_FIELD_NAME), false).setRequired(true));
161 
162             switch (type) {
163                 case ACCOUNT:
164                     fields.add(new AjaxSpinnerFieldPanel.Builder<Integer>().min(1).build(
165                             "field",
166                             "maxAuthenticationAttempts",
167                             Integer.class,
168                             new PropertyModel<>(policyTO, "maxAuthenticationAttempts")));
169 
170                     fields.add(new AjaxCheckBoxPanel(
171                             "field",
172                             "propagateSuspension",
173                             new PropertyModel<>(policyTO, "propagateSuspension"),
174                             false));
175 
176                     fields.add(new AjaxPalettePanel.Builder<String>().setName("passthroughResources").build(
177                             "field",
178                             new PropertyModel<>(policyTO, "passthroughResources"),
179                             new ListModel<>(resources.getObject())));
180                     break;
181 
182                 case PASSWORD:
183                     fields.add(new AjaxSpinnerFieldPanel.Builder<Integer>().min(1).build(
184                             "field",
185                             "historyLength",
186                             Integer.class,
187                             new PropertyModel<>(policyTO, "historyLength")));
188 
189                     fields.add(new AjaxCheckBoxPanel(
190                             "field",
191                             "allowNullPassword",
192                             new PropertyModel<>(policyTO, "allowNullPassword"),
193                             false));
194                     break;
195 
196                 case PROPAGATION:
197                     fields.add(new AjaxCheckBoxPanel(
198                             "field",
199                             "fetchAroundProvisioning",
200                             new PropertyModel<>(policyTO, "fetchAroundProvisioning"),
201                             false));
202 
203                     fields.add(new AjaxCheckBoxPanel(
204                             "field",
205                             "updateDelta",
206                             new PropertyModel<>(policyTO, "updateDelta"),
207                             false));
208 
209                     fields.add(new AjaxSpinnerFieldPanel.Builder<Integer>().min(1).build(
210                             "field",
211                             "maxAttempts",
212                             Integer.class,
213                             new PropertyModel<>(policyTO, "maxAttempts")));
214                     AjaxDropDownChoicePanel<Serializable> backOffStrategy = new AjaxDropDownChoicePanel<>(
215                             "field",
216                             "backOffStrategy",
217                             new PropertyModel<>(policyTO, "backOffStrategy")).
218                             setChoices(List.of((Serializable[]) BackOffStrategy.values()));
219                     fields.add(backOffStrategy);
220 
221                     PropertyModel<String> backOffParamsModel = new PropertyModel<>(policyTO, "backOffParams");
222 
223                     AjaxSpinnerFieldPanel<Long> initialInterval = new AjaxSpinnerFieldPanel.Builder<Long>().
224                             min(1L).build(
225                             "field",
226                             "initialInterval",
227                             Long.class,
228                             new BackOffParamsModel<>(backOffParamsModel, 0));
229                     fields.add(initialInterval.setOutputMarkupPlaceholderTag(true));
230                     AjaxSpinnerFieldPanel<Long> maxInterval = new AjaxSpinnerFieldPanel.Builder<Long>().
231                             min(1L).build(
232                             "field",
233                             "maxInterval",
234                             Long.class,
235                             new BackOffParamsModel<>(backOffParamsModel, 1));
236                     fields.add(maxInterval.setOutputMarkupPlaceholderTag(true).setVisible(false));
237                     AjaxSpinnerFieldPanel<Double> multiplier = new AjaxSpinnerFieldPanel.Builder<Double>().
238                             min(1D).build(
239                             "field",
240                             "multiplier",
241                             Double.class,
242                             new BackOffParamsModel<>(backOffParamsModel, 2));
243                     fields.add(multiplier.setOutputMarkupPlaceholderTag(true).setVisible(false));
244 
245                     showHide(backOffStrategy, initialInterval, maxInterval, multiplier);
246 
247                     backOffStrategy.getField().add(new IndicatorAjaxFormComponentUpdatingBehavior(Constants.ON_CHANGE) {
248 
249                         private static final long serialVersionUID = -1107858522700306810L;
250 
251                         @Override
252                         protected void onUpdate(final AjaxRequestTarget target) {
253                             BackOffStrategy strategy = (BackOffStrategy) backOffStrategy.getField().getModelObject();
254                             backOffParamsModel.setObject(strategy.getDefaultBackOffParams());
255 
256                             showHide(backOffStrategy, initialInterval, maxInterval, multiplier);
257 
258                             target.add(initialInterval);
259                             target.add(maxInterval);
260                             target.add(multiplier);
261                         }
262                     });
263                     break;
264 
265                 case PULL:
266                 case PUSH:
267                     fields.add(new AjaxDropDownChoicePanel<>(
268                             "field",
269                             "conflictResolutionAction",
270                             new PropertyModel<>(policyTO, "conflictResolutionAction")).
271                             setChoices(List.of((Serializable[]) ConflictResolutionAction.values())));
272                     break;
273 
274                 case ACCESS:
275                     fields.add(new AjaxDropDownChoicePanel<>(
276                             "field",
277                             "conf",
278                             new IModel<>() {
279 
280                         private static final long serialVersionUID = -6515946495655944432L;
281 
282                         @Override
283                         public Serializable getObject() {
284                             return Optional.ofNullable(PropertyResolver.getValue("conf", policyTO)).
285                                     map(obj -> obj.getClass().getName()).
286                                     orElse(null);
287                         }
288 
289                         @Override
290                         public void setObject(final Serializable object) {
291                             Object conf = Optional.ofNullable(object).map(o -> {
292                                 try {
293                                     return Class.forName(object.toString()).getDeclaredConstructor().newInstance();
294                                 } catch (Exception e) {
295                                     LOG.error("Could not instantiate {}", object, e);
296                                     return null;
297                                 }
298                             }).orElse(null);
299 
300                             PropertyResolverConverter prc = new PropertyResolverConverter(
301                                     Application.get().getConverterLocator(),
302                                     SyncopeConsoleSession.get().getLocale());
303                             PropertyResolver.setValue(
304                                     "conf",
305                                     policyTO,
306                                     Optional.ofNullable(conf).orElse(null),
307                                     prc);
308                         }
309                     }).setChoices(accessPolicyConfClasses).setRequired(true));
310                     break;
311 
312                 case ATTR_RELEASE:
313                     fields.add(new AjaxSpinnerFieldPanel.Builder<Integer>().build(
314                             "field",
315                             "order",
316                             Integer.class,
317                             new PropertyModel<>(policyTO, "order")));
318                     fields.add(new AjaxCheckBoxPanel(
319                             "field",
320                             "status",
321                             new PropertyModel<>(policyTO, "status"),
322                             false));
323                     break;
324 
325                 case AUTH:
326                 default:
327             }
328 
329             add(new ListView<>("fields", fields) {
330 
331                 private static final long serialVersionUID = -9180479401817023838L;
332 
333                 @Override
334                 protected void populateItem(final ListItem<Component> item) {
335                     item.add(item.getModelObject());
336                 }
337             });
338         }
339 
340         private void showHide(
341                 final AjaxDropDownChoicePanel<Serializable> backOffStrategy,
342                 final AjaxSpinnerFieldPanel<Long> initialInterval,
343                 final AjaxSpinnerFieldPanel<Long> maxInterval,
344                 final AjaxSpinnerFieldPanel<Double> multiplier) {
345 
346             BackOffStrategy strategy = (BackOffStrategy) backOffStrategy.getField().getModelObject();
347 
348             switch (strategy) {
349                 case EXPONENTIAL:
350                     initialInterval.addLabel("initialInterval");
351                     maxInterval.setVisible(true);
352                     multiplier.setVisible(true);
353                     break;
354 
355                 case RANDOM:
356                     initialInterval.addLabel("initialInterval");
357                     maxInterval.setVisible(true);
358                     multiplier.setVisible(true);
359                     break;
360 
361                 case FIXED:
362                 default:
363                     initialInterval.addLabel("period");
364                     maxInterval.setVisible(false);
365                     multiplier.setVisible(false);
366             }
367         }
368 
369         @Override
370         public void onSubmit(final AjaxRequestTarget target) {
371             try {
372                 if (policyTO.getKey() == null) {
373                     policyRestClient.create(type, policyTO);
374                 } else {
375                     policyRestClient.update(type, policyTO);
376                 }
377                 SyncopeConsoleSession.get().success(getString(Constants.OPERATION_SUCCEEDED));
378                 Profile.this.modal.close(target);
379             } catch (Exception e) {
380                 LOG.error("While creating/updating policy", e);
381                 SyncopeConsoleSession.get().onException(e);
382             }
383             ((BaseWebPage) pageRef.getPage()).getNotificationPanel().refresh(target);
384         }
385     }
386 }