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.panels;
20
21 import java.util.ArrayList;
22 import java.util.Comparator;
23 import java.util.List;
24 import java.util.stream.Collectors;
25 import org.apache.commons.lang3.StringUtils;
26 import org.apache.syncope.client.console.SyncopeConsoleSession;
27 import org.apache.syncope.client.console.layout.AnyLayout;
28 import org.apache.syncope.client.console.layout.AnyLayoutUtils;
29 import org.apache.syncope.client.console.layout.IdMUserFormLayoutInfo;
30 import org.apache.syncope.client.console.layout.LinkedAccountForm;
31 import org.apache.syncope.client.console.layout.LinkedAccountFormLayoutInfo;
32 import org.apache.syncope.client.console.pages.BasePage;
33 import org.apache.syncope.client.console.rest.AnyTypeRestClient;
34 import org.apache.syncope.client.console.rest.RoleRestClient;
35 import org.apache.syncope.client.console.rest.UserRestClient;
36 import org.apache.syncope.client.console.status.LinkedAccountStatusPanel;
37 import org.apache.syncope.client.console.status.ReconTaskPanel;
38 import org.apache.syncope.client.console.wicket.markup.html.bootstrap.dialog.BaseModal;
39 import org.apache.syncope.client.console.wicket.markup.html.form.ActionLink;
40 import org.apache.syncope.client.console.wicket.markup.html.form.ActionLinksTogglePanel;
41 import org.apache.syncope.client.console.wizards.WizardMgtPanel;
42 import org.apache.syncope.client.console.wizards.any.LinkedAccountWizardBuilder;
43 import org.apache.syncope.client.ui.commons.ConnIdSpecialName;
44 import org.apache.syncope.client.ui.commons.Constants;
45 import org.apache.syncope.client.ui.commons.panels.ModalPanel;
46 import org.apache.syncope.client.ui.commons.wizards.AjaxWizard;
47 import org.apache.syncope.common.lib.SyncopeClientException;
48 import org.apache.syncope.common.lib.request.LinkedAccountUR;
49 import org.apache.syncope.common.lib.request.UserUR;
50 import org.apache.syncope.common.lib.to.AnyTypeTO;
51 import org.apache.syncope.common.lib.to.LinkedAccountTO;
52 import org.apache.syncope.common.lib.to.PullTaskTO;
53 import org.apache.syncope.common.lib.to.PushTaskTO;
54 import org.apache.syncope.common.lib.to.UserTO;
55 import org.apache.syncope.common.lib.types.IdRepoEntitlement;
56 import org.apache.syncope.common.lib.types.PatchOperation;
57 import org.apache.wicket.AttributeModifier;
58 import org.apache.wicket.Component;
59 import org.apache.wicket.PageReference;
60 import org.apache.wicket.ajax.AjaxRequestTarget;
61 import org.apache.wicket.ajax.markup.html.AjaxLink;
62 import org.apache.wicket.event.Broadcast;
63 import org.apache.wicket.markup.html.basic.Label;
64 import org.apache.wicket.markup.html.panel.Panel;
65 import org.apache.wicket.model.IModel;
66 import org.apache.wicket.model.Model;
67 import org.apache.wicket.model.StringResourceModel;
68 import org.apache.wicket.spring.injection.annot.SpringBean;
69 import org.slf4j.Logger;
70 import org.slf4j.LoggerFactory;
71
72 public class LinkedAccountModalPanel extends Panel implements ModalPanel {
73
74 private static final long serialVersionUID = -4603032036433309900L;
75
76 protected static final Logger LOG = LoggerFactory.getLogger(LinkedAccountModalPanel.class);
77
78 @SpringBean
79 protected AnyTypeRestClient anyTypeRestClient;
80
81 @SpringBean
82 protected RoleRestClient roleRestClient;
83
84 @SpringBean
85 protected UserRestClient userRestClient;
86
87 protected LinkedAccountForm wizard;
88
89 protected final WizardMgtPanel<LinkedAccountTO> list;
90
91 private final AjaxLink<LinkedAccountTO> addAjaxLink;
92
93 protected ActionLinksTogglePanel<LinkedAccountTO> actionTogglePanel;
94
95 protected final List<LinkedAccountTO> linkedAccountTOs;
96
97 @SuppressWarnings("unchecked")
98 public LinkedAccountModalPanel(
99 final IModel<UserTO> model,
100 final PageReference pageRef,
101 final boolean recounciliationOnly) {
102
103 super(BaseModal.CONTENT_ID, model);
104
105 final MultilevelPanel mlp = new MultilevelPanel("mlpContainer");
106 mlp.setOutputMarkupId(true);
107
108 setOutputMarkupId(true);
109
110 actionTogglePanel = new ActionLinksTogglePanel<>("toggle", pageRef);
111 add(actionTogglePanel);
112
113 AnyLayout anyLayout = AnyLayoutUtils.fetch(
114 roleRestClient,
115 anyTypeRestClient.listAnyTypes().stream().map(AnyTypeTO::getKey).collect(Collectors.toList()));
116 LinkedAccountFormLayoutInfo linkedAccountFormLayoutInfo =
117 anyLayout.getUser() instanceof IdMUserFormLayoutInfo
118 ? IdMUserFormLayoutInfo.class.cast(anyLayout.getUser()).getLinkedAccountFormLayoutInfo()
119 : new LinkedAccountFormLayoutInfo();
120
121 try {
122 wizard = linkedAccountFormLayoutInfo.getFormClass().getConstructor(
123 IModel.class,
124 LinkedAccountFormLayoutInfo.class,
125 UserRestClient.class,
126 AnyTypeRestClient.class,
127 PageReference.class).
128 newInstance(model, linkedAccountFormLayoutInfo, userRestClient, anyTypeRestClient, pageRef);
129 } catch (Exception e) {
130 LOG.error("Error instantiating form layout", e);
131 wizard = new LinkedAccountWizardBuilder(
132 model, linkedAccountFormLayoutInfo, userRestClient, anyTypeRestClient, pageRef);
133 }
134
135 ListViewPanel.Builder<LinkedAccountTO> builder = new ListViewPanel.Builder<>(
136 LinkedAccountTO.class, pageRef) {
137
138 private static final long serialVersionUID = -5322423525438435153L;
139
140 @Override
141 protected LinkedAccountTO getActualItem(final LinkedAccountTO item, final List<LinkedAccountTO> list) {
142 return item == null
143 ? null
144 : list.stream().filter(
145 in -> ((item.getKey() == null && in.getKey() == null)
146 || (in.getKey() != null && in.getKey().equals(item.getKey())))).
147 findAny().orElse(null);
148 }
149
150 @Override
151 protected void customActionCallback(final AjaxRequestTarget target) {
152
153 send(LinkedAccountModalPanel.this, Broadcast.BUBBLE, new BaseModal.ChangeFooterVisibilityEvent(target));
154 }
155
156 @Override
157 protected void customActionOnCancelCallback(final AjaxRequestTarget target) {
158
159 send(LinkedAccountModalPanel.this, Broadcast.BUBBLE, new BaseModal.ChangeFooterVisibilityEvent(target));
160 }
161
162 @Override
163 @SuppressWarnings("unchecked")
164 protected void customActionOnFinishCallback(final AjaxRequestTarget target) {
165 checkAddButton(model.getObject().getRealm());
166
167 linkedAccountTOs.clear();
168 linkedAccountTOs.addAll(model.getObject().getLinkedAccounts());
169 sortLinkedAccounts();
170
171 ListViewPanel.class.cast(list).refreshList(linkedAccountTOs);
172
173
174 send(LinkedAccountModalPanel.this, Broadcast.BUBBLE, new BaseModal.ChangeFooterVisibilityEvent(target));
175 }
176
177 @Override
178 protected Component getValueComponent(final String key, final LinkedAccountTO bean) {
179 if ("suspended".equalsIgnoreCase(key)) {
180 Label label = new Label("field", StringUtils.EMPTY);
181 if (bean.isSuspended()) {
182 label.add(new AttributeModifier("class", "fa fa-check"));
183 label.add(new AttributeModifier("style", "display: table-cell; text-align: center;"));
184 }
185 return label;
186 }
187 return super.getValueComponent(key, bean);
188 }
189
190 @Override
191 protected ActionLinksTogglePanel<LinkedAccountTO> getTogglePanel() {
192 return actionTogglePanel;
193 }
194 };
195
196 linkedAccountTOs = new ArrayList<>(model.getObject().getLinkedAccounts());
197 sortLinkedAccounts();
198
199 builder.setItems(linkedAccountTOs);
200 builder.includes("connObjectKeyValue", "resource", "suspended");
201 builder.setReuseItem(false);
202 builder.withChecks(ListViewPanel.CheckAvailability.NONE);
203
204 builder.addAction(new ActionLink<>() {
205
206 private static final long serialVersionUID = 2555747430358755813L;
207
208 @Override
209 public void onClick(final AjaxRequestTarget target, final LinkedAccountTO linkedAccountTO) {
210 mlp.next(linkedAccountTO.getResource(), new LinkedAccountStatusPanel(
211 linkedAccountTO.getResource(),
212 model.getObject().getType(),
213 linkedAccountTO.getConnObjectKeyValue()),
214 target);
215 target.add(mlp);
216
217 ((BasePage) pageRef.getPage()).getNotificationPanel().refresh(target);
218 send(LinkedAccountModalPanel.this, Broadcast.BREADTH,
219 new ActionLinksTogglePanel.ActionLinkToggleCloseEventPayload(target));
220 }
221 }, ActionLink.ActionType.VIEW, IdRepoEntitlement.USER_READ);
222
223 if (!recounciliationOnly) {
224 builder.addAction(new ActionLink<>() {
225
226 private static final long serialVersionUID = 2555747430358755813L;
227
228 @Override
229 public void onClick(final AjaxRequestTarget target, final LinkedAccountTO linkedAccountTO) {
230 try {
231 send(LinkedAccountModalPanel.this, Broadcast.DEPTH,
232 new AjaxWizard.NewItemActionEvent<>(linkedAccountTO, 1, target).
233 setTitleModel(new StringResourceModel("inner.edit.linkedAccount",
234 LinkedAccountModalPanel.this, Model.of(linkedAccountTO))));
235 } catch (SyncopeClientException e) {
236 LOG.error("While attempting to create new linked account", e);
237 SyncopeConsoleSession.get().onException(e);
238 ((BasePage) pageRef.getPage()).getNotificationPanel().refresh(target);
239 }
240
241 send(LinkedAccountModalPanel.this, Broadcast.BREADTH,
242 new ActionLinksTogglePanel.ActionLinkToggleCloseEventPayload(target));
243 }
244 }, ActionLink.ActionType.EDIT, IdRepoEntitlement.USER_UPDATE);
245
246 builder.addAction(new ActionLink<>() {
247
248 private static final long serialVersionUID = 2555747430358755813L;
249
250 @Override
251 public void onClick(final AjaxRequestTarget target, final LinkedAccountTO linkedAccountTO) {
252 try {
253 linkedAccountTO.setSuspended(!linkedAccountTO.isSuspended());
254 LinkedAccountUR linkedAccountUR = new LinkedAccountUR.Builder().
255 operation(PatchOperation.ADD_REPLACE).
256 linkedAccountTO(linkedAccountTO).build();
257
258 UserUR req = new UserUR();
259 req.setKey(model.getObject().getKey());
260 req.getLinkedAccounts().add(linkedAccountUR);
261 model.setObject(userRestClient.update(model.getObject().getETagValue(), req).getEntity());
262
263 SyncopeConsoleSession.get().success(getString(Constants.OPERATION_SUCCEEDED));
264 } catch (SyncopeClientException e) {
265 LOG.error("While toggling status of linked account", e);
266 SyncopeConsoleSession.get().onException(e);
267 ((BasePage) pageRef.getPage()).getNotificationPanel().refresh(target);
268 }
269
270 checkAddButton(model.getObject().getRealm());
271 ((BasePage) pageRef.getPage()).getNotificationPanel().refresh(target);
272 send(LinkedAccountModalPanel.this, Broadcast.DEPTH, new ListViewPanel.ListViewReload<>(target));
273 }
274 }, ActionLink.ActionType.ENABLE, IdRepoEntitlement.USER_UPDATE);
275 }
276
277 builder.addAction(new ActionLink<>() {
278
279 private static final long serialVersionUID = 2555747430358755813L;
280
281 @Override
282 public void onClick(final AjaxRequestTarget target, final LinkedAccountTO linkedAccountTO) {
283 mlp.next("PUSH " + linkedAccountTO.getResource(),
284 new ReconTaskPanel(
285 linkedAccountTO.getResource(),
286 new PushTaskTO(),
287 model.getObject().getType(),
288 null,
289 ConnIdSpecialName.UID + "==" + linkedAccountTO.getConnObjectKeyValue(),
290 true,
291 mlp,
292 pageRef),
293 target);
294 target.add(mlp);
295
296 ((BasePage) pageRef.getPage()).getNotificationPanel().refresh(target);
297 send(LinkedAccountModalPanel.this, Broadcast.BREADTH,
298 new ActionLinksTogglePanel.ActionLinkToggleCloseEventPayload(target));
299 }
300 }, ActionLink.ActionType.RECONCILIATION_PUSH,
301 String.format("%s,%s", IdRepoEntitlement.USER_READ, IdRepoEntitlement.TASK_EXECUTE));
302
303 builder.addAction(new ActionLink<>() {
304
305 private static final long serialVersionUID = 2555747430358755813L;
306
307 @Override
308 public void onClick(final AjaxRequestTarget target, final LinkedAccountTO linkedAccountTO) {
309 mlp.next("PULL " + linkedAccountTO.getResource(),
310 new ReconTaskPanel(
311 linkedAccountTO.getResource(),
312 new PullTaskTO(),
313 model.getObject().getType(),
314 null,
315 ConnIdSpecialName.UID + "==" + linkedAccountTO.getConnObjectKeyValue(),
316 true,
317 mlp,
318 pageRef),
319 target);
320 target.add(mlp);
321
322 ((BasePage) pageRef.getPage()).getNotificationPanel().refresh(target);
323 send(LinkedAccountModalPanel.this, Broadcast.BREADTH,
324 new ActionLinksTogglePanel.ActionLinkToggleCloseEventPayload(target));
325 }
326 }, ActionLink.ActionType.RECONCILIATION_PULL,
327 String.format("%s,%s", IdRepoEntitlement.USER_READ, IdRepoEntitlement.TASK_EXECUTE));
328
329 if (!recounciliationOnly) {
330 builder.addAction(new ActionLink<>() {
331
332 private static final long serialVersionUID = 2555747430358755813L;
333
334 @Override
335 public void onClick(final AjaxRequestTarget target, final LinkedAccountTO linkedAccountTO) {
336 try {
337 LinkedAccountUR linkedAccountUR = new LinkedAccountUR.Builder().
338 operation(PatchOperation.DELETE).
339 linkedAccountTO(linkedAccountTO).build();
340
341 UserUR req = new UserUR();
342 req.setKey(model.getObject().getKey());
343 req.getLinkedAccounts().add(linkedAccountUR);
344 model.setObject(userRestClient.update(model.getObject().getETagValue(), req).getEntity());
345 linkedAccountTOs.remove(linkedAccountTO);
346
347 SyncopeConsoleSession.get().success(getString(Constants.OPERATION_SUCCEEDED));
348 } catch (Exception e) {
349 LOG.error("While removing linked account {}", linkedAccountTO.getKey(), e);
350 SyncopeConsoleSession.get().onException(e);
351 }
352
353 checkAddButton(model.getObject().getRealm());
354 ((BasePage) pageRef.getPage()).getNotificationPanel().refresh(target);
355 send(LinkedAccountModalPanel.this, Broadcast.DEPTH, new ListViewPanel.ListViewReload<>(target));
356 }
357 }, ActionLink.ActionType.DELETE, IdRepoEntitlement.USER_UPDATE, true);
358 }
359
360 builder.addNewItemPanelBuilder(wizard);
361
362 list = builder.build(MultilevelPanel.FIRST_LEVEL_ID);
363 list.setOutputMarkupId(true);
364 list.setReadOnly(!SyncopeConsoleSession.get().
365 owns(IdRepoEntitlement.USER_UPDATE, model.getObject().getRealm()));
366
367 addAjaxLink = new AjaxLink<>("add") {
368
369 private static final long serialVersionUID = -7978723352517770644L;
370
371 @Override
372 public void onClick(final AjaxRequestTarget target) {
373 send(LinkedAccountModalPanel.this, Broadcast.BREADTH,
374 new ActionLinksTogglePanel.ActionLinkToggleCloseEventPayload(target));
375
376
377 send(list, Broadcast.DEPTH, new AjaxWizard.NewItemActionEvent<>(new LinkedAccountTO(), target).
378 setTitleModel(
379 new StringResourceModel("inner.create.linkedAccount", LinkedAccountModalPanel.this)));
380 }
381 };
382 list.addOrReplaceInnerObject(addAjaxLink.setEnabled(!recounciliationOnly).setVisible(!recounciliationOnly));
383
384 add(mlp.setFirstLevel(list));
385 }
386
387 private void sortLinkedAccounts() {
388 linkedAccountTOs.sort(Comparator.comparing(LinkedAccountTO::getConnObjectKeyValue));
389 }
390
391 private void checkAddButton(final String realm) {
392 addAjaxLink.setVisible(SyncopeConsoleSession.get().owns(IdRepoEntitlement.USER_UPDATE, realm));
393 }
394 }