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.wizards;
20
21 import de.agilecoders.wicket.extensions.markup.html.bootstrap.form.fileinput.BootstrapFileInputField;
22 import de.agilecoders.wicket.extensions.markup.html.bootstrap.form.fileinput.FileInputConfig;
23 import java.io.ByteArrayInputStream;
24 import java.util.ArrayList;
25 import java.util.List;
26 import java.util.Locale;
27 import java.util.stream.Collectors;
28 import java.util.stream.Stream;
29 import org.apache.commons.lang3.StringUtils;
30 import org.apache.syncope.client.console.SyncopeConsoleSession;
31 import org.apache.syncope.client.console.SyncopeWebApplication;
32 import org.apache.syncope.client.console.pages.BasePage;
33 import org.apache.syncope.client.console.panels.CSVConfPanel;
34 import org.apache.syncope.client.console.rest.ImplementationRestClient;
35 import org.apache.syncope.client.console.rest.ReconciliationRestClient;
36 import org.apache.syncope.client.ui.commons.Constants;
37 import org.apache.syncope.client.ui.commons.markup.html.form.AjaxCheckBoxPanel;
38 import org.apache.syncope.client.ui.commons.markup.html.form.AjaxDropDownChoicePanel;
39 import org.apache.syncope.client.ui.commons.markup.html.form.AjaxPalettePanel;
40 import org.apache.syncope.common.lib.to.ImplementationTO;
41 import org.apache.syncope.common.lib.to.ProvisioningReport;
42 import org.apache.syncope.common.lib.types.ConflictResolutionAction;
43 import org.apache.syncope.common.lib.types.IdMImplementationType;
44 import org.apache.syncope.common.lib.types.MatchingRule;
45 import org.apache.syncope.common.lib.types.UnmatchingRule;
46 import org.apache.syncope.common.rest.api.beans.CSVPullSpec;
47 import org.apache.wicket.PageReference;
48 import org.apache.wicket.ajax.AjaxRequestTarget;
49 import org.apache.wicket.ajax.form.AjaxFormSubmitBehavior;
50 import org.apache.wicket.ajax.markup.html.form.AjaxCheckBox;
51 import org.apache.wicket.event.IEventSink;
52 import org.apache.wicket.extensions.wizard.WizardModel;
53 import org.apache.wicket.extensions.wizard.WizardStep;
54 import org.apache.wicket.markup.html.basic.Label;
55 import org.apache.wicket.markup.html.form.Radio;
56 import org.apache.wicket.markup.html.form.RadioGroup;
57 import org.apache.wicket.markup.html.form.upload.FileUpload;
58 import org.apache.wicket.markup.html.list.ListItem;
59 import org.apache.wicket.markup.html.list.ListView;
60 import org.apache.wicket.model.IModel;
61 import org.apache.wicket.model.LoadableDetachableModel;
62 import org.apache.wicket.model.Model;
63 import org.apache.wicket.model.PropertyModel;
64 import org.apache.wicket.model.util.ListModel;
65 import org.apache.wicket.util.lang.Bytes;
66
67 public class CSVPullWizardBuilder extends BaseAjaxWizardBuilder<CSVPullSpec> {
68
69 private static final long serialVersionUID = -4049787433975145383L;
70
71 public enum LineSeparator {
72 LF("\n"),
73 CR("\r"),
74 CRLF("\r\n");
75
76 private final String repr;
77
78 LineSeparator(final String repr) {
79 this.repr = repr;
80 }
81
82 public String getRepr() {
83 return repr;
84 }
85
86 public static LineSeparator byRepr(final String repr) {
87 for (LineSeparator value : values()) {
88 if (value.getRepr().equals(repr)) {
89 return value;
90 }
91 }
92 return null;
93 }
94 }
95
96 protected final Model<byte[]> csv = new Model<>();
97
98 protected final ReconciliationRestClient reconciliationRestClient;
99
100 protected final ImplementationRestClient implementationRestClient;
101
102 protected final Bytes maxUploadSize;
103
104 public CSVPullWizardBuilder(
105 final CSVPullSpec defaultItem,
106 final ReconciliationRestClient reconciliationRestClient,
107 final ImplementationRestClient implementationRestClient,
108 final PageReference pageRef) {
109
110 super(defaultItem, pageRef);
111
112 this.reconciliationRestClient = reconciliationRestClient;
113 this.implementationRestClient = implementationRestClient;
114 this.maxUploadSize = Bytes.megabytes(SyncopeWebApplication.get().getMaxUploadFileSizeMB());
115 }
116
117 @Override
118 public CSVPullWizardBuilder setEventSink(final IEventSink eventSink) {
119 super.setEventSink(eventSink);
120 return this;
121 }
122
123 @Override
124 protected ArrayList<ProvisioningReport> onApplyInternal(final CSVPullSpec modelObject) {
125 return reconciliationRestClient.pull(modelObject, new ByteArrayInputStream(csv.getObject()));
126 }
127
128 @Override
129 protected WizardModel buildModelSteps(final CSVPullSpec modelObject, final WizardModel wizardModel) {
130 wizardModel.add(new Details(modelObject));
131 wizardModel.add(new Columns(modelObject));
132 wizardModel.add(new PullTask(modelObject));
133 return wizardModel;
134 }
135
136 public class Details extends WizardStep {
137
138 private static final long serialVersionUID = -4736870165235853919L;
139
140 public Details(final CSVPullSpec spec) {
141 FileInputConfig csvFile = new FileInputConfig().
142 showUpload(false).showRemove(false).showPreview(false).
143 browseClass("btn btn-success").browseIcon("<i class=\"fas fa-folder-open\"></i> ");
144 String language = SyncopeConsoleSession.get().getLocale().getLanguage();
145 if (!Locale.ENGLISH.getLanguage().equals(language)) {
146 csvFile.withLocale(language);
147 }
148 BootstrapFileInputField csvUpload =
149 new BootstrapFileInputField("csvUpload", new ListModel<>(new ArrayList<>()), csvFile);
150 csvUpload.add(new AjaxFormSubmitBehavior(Constants.ON_CHANGE) {
151
152 private static final long serialVersionUID = 5538299138211283825L;
153
154 @Override
155 protected void onSubmit(final AjaxRequestTarget target) {
156 FileUpload uploadedFile = csvUpload.getFileUpload();
157 if (uploadedFile != null) {
158 if (maxUploadSize != null && uploadedFile.getSize() > maxUploadSize.bytes()) {
159 SyncopeConsoleSession.get().error(getString("tooLargeFile").
160 replace("${maxUploadSizeB}", String.valueOf(maxUploadSize.bytes())).
161 replace("${maxUploadSizeMB}", String.valueOf(maxUploadSize.bytes() / 1000000L)));
162 ((BasePage) getPageReference().getPage()).getNotificationPanel().refresh(target);
163 } else {
164 csv.setObject(uploadedFile.getBytes());
165 }
166 }
167 }
168 });
169 add(csvUpload.setRequired(true).setOutputMarkupId(true));
170
171 add(new CSVConfPanel("csvconf", spec));
172 }
173 }
174
175 public class Columns extends WizardStep implements WizardModel.ICondition {
176
177 private static final long serialVersionUID = -4136764210454067728L;
178
179 private final CSVPullSpec spec;
180
181 private final ListModel<String> columnsModel = new ListModel<>();
182
183 public Columns(final CSVPullSpec spec) {
184 this.spec = spec;
185
186 RadioGroup<String> keyColumn = new RadioGroup<>("keyColumn", new PropertyModel<>(spec, "keyColumn"));
187 keyColumn.setOutputMarkupId(true);
188 keyColumn.setOutputMarkupPlaceholderTag(true);
189 keyColumn.setRenderBodyOnly(false);
190 keyColumn.setRequired(true);
191 add(keyColumn);
192
193 keyColumn.add(new ListView<>("columns", columnsModel) {
194
195 private static final long serialVersionUID = -9112553137618363167L;
196
197 @Override
198 protected void populateItem(final ListItem<String> item) {
199 item.add(new Label("column", item.getModelObject()));
200 item.add(new Radio<>("key", new Model<>(item.getModelObject()), keyColumn));
201 item.add(new AjaxCheckBox("ignore", new Model<>()) {
202
203 private static final long serialVersionUID = -6139318907146065915L;
204
205 @Override
206 protected void onUpdate(final AjaxRequestTarget target) {
207 if (spec.getIgnoreColumns().contains(item.getModelObject())) {
208 spec.getIgnoreColumns().remove(item.getModelObject());
209 } else {
210 spec.getIgnoreColumns().add(item.getModelObject());
211 }
212 }
213 });
214 }
215 }.setReuseItems(true));
216 }
217
218 @Override
219 public boolean evaluate() {
220 if (csv.getObject() != null) {
221 String header = StringUtils.substringBefore(new String(csv.getObject()), spec.getLineSeparator());
222 columnsModel.setObject(Stream.of(StringUtils.split(header, spec.getColumnSeparator())).
223 map(value -> {
224 String unquoted = value;
225 if (unquoted.charAt(0) == spec.getQuoteChar()) {
226 unquoted = unquoted.substring(1);
227 }
228 if (unquoted.charAt(unquoted.length() - 1) == spec.getQuoteChar()) {
229 unquoted = unquoted.substring(0, unquoted.length() - 1);
230 }
231 return unquoted;
232 }).collect(Collectors.toList()));
233 }
234
235 return true;
236 }
237 }
238
239 public class PullTask extends WizardStep {
240
241 private static final long serialVersionUID = -8954789648303078732L;
242
243 private final IModel<List<String>> pullActions = new LoadableDetachableModel<>() {
244
245 private static final long serialVersionUID = 4659376149825914247L;
246
247 @Override
248 protected List<String> load() {
249 return implementationRestClient.list(IdMImplementationType.PULL_ACTIONS).stream().
250 map(ImplementationTO::getKey).sorted().collect(Collectors.toList());
251 }
252 };
253
254 private final IModel<List<String>> pullCorrelationRules = new LoadableDetachableModel<>() {
255
256 private static final long serialVersionUID = 4659376149825914247L;
257
258 @Override
259 protected List<String> load() {
260 return implementationRestClient.list(IdMImplementationType.PULL_CORRELATION_RULE).stream().
261 map(ImplementationTO::getKey).sorted().collect(Collectors.toList());
262 }
263 };
264
265 public PullTask(final CSVPullSpec spec) {
266 AjaxCheckBoxPanel remediation = new AjaxCheckBoxPanel(
267 "remediation", "remediation", new PropertyModel<>(spec, "remediation"), false);
268 add(remediation);
269
270 AjaxDropDownChoicePanel<MatchingRule> matchingRule = new AjaxDropDownChoicePanel<>(
271 "matchingRule", "matchingRule", new PropertyModel<>(spec, "matchingRule"), false);
272 matchingRule.setChoices(List.of(MatchingRule.values()));
273 add(matchingRule);
274
275 AjaxDropDownChoicePanel<UnmatchingRule> unmatchingRule = new AjaxDropDownChoicePanel<>(
276 "unmatchingRule", "unmatchingRule", new PropertyModel<>(spec, "unmatchingRule"),
277 false);
278 unmatchingRule.setChoices(List.of(UnmatchingRule.values()));
279 add(unmatchingRule);
280
281 AjaxPalettePanel<String> provisioningActions =
282 new AjaxPalettePanel.Builder<String>().build("provisioningActions",
283 new PropertyModel<>(spec, "provisioningActions"), new ListModel<>(pullActions.getObject()));
284 add(provisioningActions);
285
286 AjaxDropDownChoicePanel<ConflictResolutionAction> conflictResolutionAction = new AjaxDropDownChoicePanel<>(
287 "conflictResolutionAction", "conflictResolutionAction",
288 new PropertyModel<>(spec, "conflictResolutionAction"), false);
289 conflictResolutionAction.setChoices(List.of(ConflictResolutionAction.values()));
290 conflictResolutionAction.setRequired(true);
291 add(conflictResolutionAction);
292
293 AjaxDropDownChoicePanel<String> pullCorrelationRule = new AjaxDropDownChoicePanel<>(
294 "pullCorrelationRule", "pullCorrelationRule", new PropertyModel<>(spec, "pullCorrelationRule"),
295 false);
296 pullCorrelationRule.setChoices(pullCorrelationRules);
297 add(pullCorrelationRule);
298 }
299 }
300 }