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.resources;
20
21 import java.io.Serializable;
22 import java.util.ArrayList;
23 import java.util.List;
24 import java.util.Objects;
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.panels.AbstractModalPanel;
29 import org.apache.syncope.client.console.panels.ListViewPanel;
30 import org.apache.syncope.client.console.panels.ListViewPanel.ListViewReload;
31 import org.apache.syncope.client.console.rest.AnyTypeRestClient;
32 import org.apache.syncope.client.console.rest.ConnectorRestClient;
33 import org.apache.syncope.client.console.rest.ResourceRestClient;
34 import org.apache.syncope.client.console.wicket.markup.html.bootstrap.dialog.BaseModal;
35 import org.apache.syncope.client.console.wicket.markup.html.form.ActionLink;
36 import org.apache.syncope.client.console.wicket.markup.html.form.ActionLinksTogglePanel;
37 import org.apache.syncope.client.console.wizards.WizardMgtPanel;
38 import org.apache.syncope.client.ui.commons.Constants;
39 import org.apache.syncope.client.ui.commons.pages.BaseWebPage;
40 import org.apache.syncope.client.ui.commons.wizards.AjaxWizard;
41 import org.apache.syncope.common.lib.SyncopeClientException;
42 import org.apache.syncope.common.lib.SyncopeConstants;
43 import org.apache.syncope.common.lib.to.Item;
44 import org.apache.syncope.common.lib.to.ResourceTO;
45 import org.apache.syncope.common.lib.types.IdMEntitlement;
46 import org.apache.wicket.PageReference;
47 import org.apache.wicket.ajax.AjaxRequestTarget;
48 import org.apache.wicket.ajax.markup.html.AjaxLink;
49 import org.apache.wicket.event.Broadcast;
50 import org.apache.wicket.model.LoadableDetachableModel;
51 import org.apache.wicket.model.Model;
52 import org.apache.wicket.model.StringResourceModel;
53 import org.apache.wicket.spring.injection.annot.SpringBean;
54
55 public class ResourceProvisionPanel extends AbstractModalPanel<Serializable> {
56
57 private static final long serialVersionUID = -7982691107029848579L;
58
59 @SpringBean
60 protected ResourceRestClient resourceRestClient;
61
62 @SpringBean
63 protected ConnectorRestClient connectorRestClient;
64
65 @SpringBean
66 protected AnyTypeRestClient anyTypeRestClient;
67
68 protected final ResourceTO resourceTO;
69
70 protected final List<ResourceProvision> provisions;
71
72 protected final ObjectTypeTogglePanel objectTypeTogglePanel;
73
74 protected final WizardMgtPanel<ResourceProvision> list;
75
76 protected final ProvisionWizardBuilder wizard;
77
78 protected final AjaxLink<ResourceProvision> addAjaxLink;
79
80 protected ActionLinksTogglePanel<ResourceProvision> actionTogglePanel;
81
82 public ResourceProvisionPanel(
83 final BaseModal<Serializable> modal,
84 final ResourceTO resourceTO,
85 final String adminRealm,
86 final PageReference pageRef) {
87
88 super(modal, pageRef);
89 this.resourceTO = resourceTO;
90
91 setOutputMarkupId(true);
92
93 actionTogglePanel = new ActionLinksTogglePanel<>("toggle", pageRef);
94 add(actionTogglePanel);
95
96 wizard = new ProvisionWizardBuilder(resourceTO, adminRealm, connectorRestClient, pageRef);
97
98 ListViewPanel.Builder<ResourceProvision> builder = new ListViewPanel.Builder<>(
99 ResourceProvision.class, pageRef) {
100
101 private static final long serialVersionUID = 4907732721283972943L;
102
103 @Override
104 protected ResourceProvision getActualItem(
105 final ResourceProvision item, final List<ResourceProvision> list) {
106
107 return Optional.ofNullable(item).flatMap(resourceProvision -> list.stream()
108 .filter(in -> ((resourceProvision.getKey() == null && in.getKey() == null)
109 || (in.getKey() != null && in.getKey().equals(resourceProvision.getKey())))
110 && ((resourceProvision.getAnyType() == null && in.getAnyType() == null)
111 || (in.getAnyType() != null && in.getAnyType().equals(resourceProvision.getAnyType())))).
112 findAny()).orElse(null);
113 }
114
115 @Override
116 protected void customActionCallback(final AjaxRequestTarget target) {
117
118 send(ResourceProvisionPanel.this, Broadcast.BUBBLE, new BaseModal.ChangeFooterVisibilityEvent(target));
119 }
120
121 @Override
122 protected void customActionOnCancelCallback(final AjaxRequestTarget target) {
123
124 send(ResourceProvisionPanel.this, Broadcast.BUBBLE, new BaseModal.ChangeFooterVisibilityEvent(target));
125 }
126
127 @Override
128 protected void customActionOnFinishCallback(final AjaxRequestTarget target) {
129 checkAddButton(adminRealm);
130
131
132 sortProvisions();
133
134
135 send(ResourceProvisionPanel.this, Broadcast.BUBBLE, new BaseModal.ChangeFooterVisibilityEvent(target));
136 }
137
138 @Override
139 protected ActionLinksTogglePanel<ResourceProvision> getTogglePanel() {
140 return actionTogglePanel;
141 }
142 };
143
144 provisions = new ArrayList<>();
145 if (resourceTO.getOrgUnit() != null) {
146 provisions.add(new ResourceProvision(resourceTO.getOrgUnit()));
147 }
148 resourceTO.getProvisions().forEach(provision -> provisions.add(new ResourceProvision(provision)));
149
150 sortProvisions();
151
152 builder.setItems(provisions);
153 builder.includes("anyType", "objectClass", "auxClasses");
154 builder.setReuseItem(false);
155
156 builder.addAction(new ActionLink<>() {
157
158 private static final long serialVersionUID = -3722207913631435504L;
159
160 @Override
161 public void onClick(final AjaxRequestTarget target, final ResourceProvision provision) {
162 try {
163 send(ResourceProvisionPanel.this, Broadcast.DEPTH,
164 new AjaxWizard.NewItemActionEvent<>(provision, 1, target).setTitleModel(
165 new StringResourceModel("inner.provision.mapping",
166 ResourceProvisionPanel.this,
167 Model.of(provision))));
168 } catch (SyncopeClientException e) {
169 LOG.error("While contacting resource", e);
170 SyncopeConsoleSession.get().onException(e);
171 ((BaseWebPage) pageRef.getPage()).getNotificationPanel().refresh(target);
172 }
173 }
174 }, ActionLink.ActionType.MAPPING, IdMEntitlement.RESOURCE_READ).
175 addAction(new ActionLink<>() {
176
177 private static final long serialVersionUID = -7780999687733432439L;
178
179 @Override
180 public void onClick(final AjaxRequestTarget target, final ResourceProvision provision) {
181 try {
182 resourceRestClient.setLatestSyncToken(resourceTO.getKey(), provision.getAnyType());
183 SyncopeConsoleSession.get().success(getString(Constants.OPERATION_SUCCEEDED));
184 } catch (Exception e) {
185 LOG.error("While setting latest sync token for {}/{}",
186 resourceTO.getKey(), provision.getAnyType(), e);
187 SyncopeConsoleSession.get().onException(e);
188 }
189 ((BaseWebPage) pageRef.getPage()).getNotificationPanel().refresh(target);
190 }
191 }, ActionLink.ActionType.SET_LATEST_SYNC_TOKEN, IdMEntitlement.RESOURCE_UPDATE).
192 addAction(new ActionLink<>() {
193
194 private static final long serialVersionUID = -7780999687733432439L;
195
196 @Override
197 public void onClick(final AjaxRequestTarget target, final ResourceProvision provision) {
198 try {
199 resourceRestClient.removeSyncToken(resourceTO.getKey(), provision.getAnyType());
200 SyncopeConsoleSession.get().success(getString(Constants.OPERATION_SUCCEEDED));
201 } catch (Exception e) {
202 LOG.error("While removing sync token for {}/{}",
203 resourceTO.getKey(), provision.getAnyType(), e);
204 SyncopeConsoleSession.get().onException(e);
205 }
206 ((BaseWebPage) pageRef.getPage()).getNotificationPanel().refresh(target);
207 }
208 }, ActionLink.ActionType.REMOVE_SYNC_TOKEN, IdMEntitlement.RESOURCE_UPDATE).
209 addAction(new ActionLink<>() {
210
211 private static final long serialVersionUID = -3722207913631435544L;
212
213 @Override
214 public void onClick(final AjaxRequestTarget target, final ResourceProvision provision) {
215 if (provision.getOrgUnitTO() != null) {
216 resourceTO.setOrgUnit(null);
217 } else if (provision.getProvisionTO() != null) {
218 resourceTO.getProvisions().remove(provision.getProvisionTO());
219 }
220 provisions.remove(provision);
221 checkAddButton(adminRealm);
222 send(ResourceProvisionPanel.this, Broadcast.DEPTH, new ListViewReload<>(target));
223 }
224 }, ActionLink.ActionType.DELETE, IdMEntitlement.RESOURCE_UPDATE, true);
225
226 builder.addNewItemPanelBuilder(wizard);
227
228 list = builder.build("provision");
229 list.setReadOnly(!SyncopeConsoleSession.get().owns(IdMEntitlement.RESOURCE_UPDATE, adminRealm));
230
231 addAjaxLink = new AjaxLink<>("add") {
232
233 private static final long serialVersionUID = -7978723352517770644L;
234
235 @Override
236 public void onClick(final AjaxRequestTarget target) {
237 send(ResourceProvisionPanel.this, Broadcast.BREADTH,
238 new ActionLinksTogglePanel.ActionLinkToggleCloseEventPayload(target));
239 objectTypeTogglePanel.setHeaderLabel(target);
240 objectTypeTogglePanel.toggle(target, true);
241 }
242 };
243 list.addOrReplaceInnerObject(addAjaxLink);
244 add(list);
245
246
247
248 final ResourceProvision provision = new ResourceProvision();
249 provision.setAnyType("");
250 objectTypeTogglePanel = new ObjectTypeTogglePanel("objectTypeToggle", provision, getAnyTypes(), pageRef) {
251
252 private static final long serialVersionUID = 7878063325027015067L;
253
254 @Override
255 protected void onSubmit(final String type, final AjaxRequestTarget target) {
256 provision.setAnyType(type);
257
258 send(list, Broadcast.BREADTH,
259 new ActionLinksTogglePanel.ActionLinkToggleCloseEventPayload(target));
260 send(list, Broadcast.DEPTH,
261 new AjaxWizard.NewItemActionEvent<>(provision, target));
262
263 wizard.setObjectClassModelObject(type);
264 }
265
266 };
267 checkAddButton(adminRealm);
268 add(objectTypeTogglePanel);
269 }
270
271 private void checkConnObjectKeyCount(final String anyType, final List<Item> items) {
272 long connObjectKeyCount = items.stream().filter(Item::isConnObjectKey).count();
273
274 if (connObjectKeyCount != 1) {
275 throw new IllegalArgumentException(anyType + ": "
276 + new StringResourceModel("connObjectKeyValidation", ResourceProvisionPanel.this).getString());
277 }
278 }
279
280 @Override
281 public void onSubmit(final AjaxRequestTarget target) {
282 try {
283 if (resourceTO.getOrgUnit() != null) {
284 checkConnObjectKeyCount(SyncopeConstants.REALM_ANYTYPE, resourceTO.getOrgUnit().getItems());
285 }
286
287 new ArrayList<>(resourceTO.getProvisions()).stream().
288 filter(Objects::nonNull).
289 forEachOrdered(provision -> {
290 if (provision.getMapping() == null || provision.getMapping().getItems().isEmpty()) {
291 resourceTO.getProvisions().remove(provision);
292 } else {
293 checkConnObjectKeyCount(provision.getAnyType(), provision.getMapping().getItems());
294 }
295 });
296
297 resourceRestClient.update(resourceTO);
298 SyncopeConsoleSession.get().success(getString(Constants.OPERATION_SUCCEEDED));
299 modal.close(target);
300 } catch (Exception e) {
301 LOG.error("While creating or updating {}", resourceTO, e);
302 SyncopeConsoleSession.get().onException(e);
303 }
304 ((BaseWebPage) pageRef.getPage()).getNotificationPanel().refresh(target);
305 }
306
307 private void sortProvisions() {
308 provisions.sort((o1, o2) -> AnyTypeRestClient.KEY_COMPARATOR.compare(o1.getAnyType(), o2.getAnyType()));
309 }
310
311 private LoadableDetachableModel<List<String>> getAnyTypes() {
312 return new LoadableDetachableModel<>() {
313
314 private static final long serialVersionUID = 5275935387613157437L;
315
316 @Override
317 protected List<String> load() {
318 List<String> anyTypes = anyTypeRestClient.list().stream().
319 filter(anyType -> resourceTO.getProvision(anyType).isEmpty()).
320 collect(Collectors.toList());
321 if (resourceTO.getOrgUnit() == null) {
322 anyTypes.add(SyncopeConstants.REALM_ANYTYPE);
323 }
324
325 anyTypes.sort(AnyTypeRestClient.KEY_COMPARATOR);
326 return anyTypes;
327 }
328 };
329 }
330
331 private void checkAddButton(final String adminRealm) {
332 boolean enabled = SyncopeConsoleSession.get().owns(IdMEntitlement.RESOURCE_UPDATE, adminRealm)
333 && !getAnyTypes().getObject().isEmpty();
334 addAjaxLink.setVisible(enabled);
335 objectTypeTogglePanel.setEnabled(enabled);
336 }
337 }