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 de.agilecoders.wicket.core.markup.html.bootstrap.dialog.Modal;
22 import java.io.Serializable;
23 import java.util.ArrayList;
24 import java.util.Collection;
25 import java.util.List;
26 import java.util.stream.Collectors;
27 import org.apache.commons.lang3.SerializationUtils;
28 import org.apache.syncope.client.console.SyncopeConsoleSession;
29 import org.apache.syncope.client.console.SyncopeWebApplication;
30 import org.apache.syncope.client.console.audit.AuditHistoryModal;
31 import org.apache.syncope.client.console.commons.IdRepoConstants;
32 import org.apache.syncope.client.console.notifications.NotificationTasks;
33 import org.apache.syncope.client.console.pages.BasePage;
34 import org.apache.syncope.client.console.rest.UserRestClient;
35 import org.apache.syncope.client.console.status.ChangePasswordModal;
36 import org.apache.syncope.client.console.tasks.AnyPropagationTasks;
37 import org.apache.syncope.client.console.wicket.markup.html.form.ActionLink;
38 import org.apache.syncope.client.console.wicket.markup.html.form.ActionLink.ActionType;
39 import org.apache.syncope.client.console.wicket.markup.html.form.ActionsPanel;
40 import org.apache.syncope.client.console.wizards.WizardMgtPanel;
41 import org.apache.syncope.client.ui.commons.Constants;
42 import org.apache.syncope.client.ui.commons.wizards.AjaxWizard;
43 import org.apache.syncope.client.ui.commons.wizards.any.AnyWrapper;
44 import org.apache.syncope.client.ui.commons.wizards.any.UserWrapper;
45 import org.apache.syncope.common.lib.AnyOperations;
46 import org.apache.syncope.common.lib.SyncopeConstants;
47 import org.apache.syncope.common.lib.info.PlatformInfo;
48 import org.apache.syncope.common.lib.request.UserUR;
49 import org.apache.syncope.common.lib.to.AnyTypeClassTO;
50 import org.apache.syncope.common.lib.to.DerSchemaTO;
51 import org.apache.syncope.common.lib.to.PlainSchemaTO;
52 import org.apache.syncope.common.lib.to.ProvisioningResult;
53 import org.apache.syncope.common.lib.to.UserTO;
54 import org.apache.syncope.common.lib.types.AnyTypeKind;
55 import org.apache.syncope.common.lib.types.IdRepoEntitlement;
56 import org.apache.syncope.common.rest.api.service.UserSelfService;
57 import org.apache.wicket.PageReference;
58 import org.apache.wicket.ajax.AjaxRequestTarget;
59 import org.apache.wicket.event.Broadcast;
60 import org.apache.wicket.model.CompoundPropertyModel;
61 import org.apache.wicket.model.IModel;
62 import org.apache.wicket.model.Model;
63 import org.apache.wicket.model.ResourceModel;
64 import org.apache.wicket.model.StringResourceModel;
65
66 public class UserDirectoryPanel extends AnyDirectoryPanel<UserTO, UserRestClient> {
67
68 private static final long serialVersionUID = -1100228004207271270L;
69
70 protected UserDirectoryPanel(final String id, final Builder builder) {
71 this(id, builder, true);
72 }
73
74 protected UserDirectoryPanel(final String id, final Builder builder, final boolean wizardInModal) {
75 super(id, builder, wizardInModal);
76
77 altDefaultModal.setWindowClosedCallback(target -> {
78 updateResultTable(target);
79 modal.show(false);
80 });
81 }
82
83 @Override
84 protected String paginatorRowsKey() {
85 return IdRepoConstants.PREF_USERS_PAGINATOR_ROWS;
86 }
87
88 @Override
89 protected String[] getDefaultAttributeSelection() {
90 return UserDisplayAttributesModalPanel.DEFAULT_SELECTION;
91 }
92
93 @Override
94 protected Collection<ActionType> getBatches() {
95 List<ActionType> batches = new ArrayList<>();
96 batches.add(ActionType.MUSTCHANGEPASSWORD);
97 batches.add(ActionType.DELETE);
98 batches.add(ActionType.SUSPEND);
99 batches.add(ActionType.REACTIVATE);
100 return batches;
101 }
102
103 @Override
104 public ActionsPanel<Serializable> getHeader(final String componentId) {
105 final ActionsPanel<Serializable> panel = super.getHeader(componentId);
106
107 panel.add(new ActionLink<>() {
108
109 private static final long serialVersionUID = -7978723352517770644L;
110
111 @Override
112 public void onClick(final AjaxRequestTarget target, final Serializable ignore) {
113 target.add(displayAttributeModal.setContent(new UserDisplayAttributesModalPanel<>(
114 displayAttributeModal,
115 page.getPageReference(),
116 plainSchemas.stream().map(PlainSchemaTO::getKey).collect(Collectors.toList()),
117 derSchemas.stream().map(DerSchemaTO::getKey).collect(Collectors.toList()))));
118
119 displayAttributeModal.header(new ResourceModel("any.attr.display"));
120 displayAttributeModal.addSubmitButton();
121 displayAttributeModal.show(true);
122 }
123
124 @Override
125 protected boolean statusCondition(final Serializable modelObject) {
126 return wizardInModal;
127 }
128 }, ActionType.CHANGE_VIEW, IdRepoEntitlement.USER_READ).hideLabel();
129 return panel;
130 }
131
132 @Override
133 public ActionsPanel<UserTO> getActions(final IModel<UserTO> model) {
134 final ActionsPanel<UserTO> panel = super.getActions(model);
135
136 panel.add(new ActionLink<>() {
137
138 private static final long serialVersionUID = -7978723352517770644L;
139
140 @Override
141 public void onClick(final AjaxRequestTarget target, final UserTO ignore) {
142 send(UserDirectoryPanel.this, Broadcast.EXACT,
143 new AjaxWizard.EditItemActionEvent<>(
144 new UserWrapper(restClient.read(model.getObject().getKey())), target));
145 }
146 }, ActionType.EDIT,
147 String.format("%s,%s", IdRepoEntitlement.USER_READ, IdRepoEntitlement.USER_UPDATE)).
148 setRealms(realm, model.getObject().getDynRealms());
149
150 panel.add(new ActionLink<>() {
151
152 private static final long serialVersionUID = -7978723352517770644L;
153
154 @Override
155 public void onClick(final AjaxRequestTarget target, final UserTO ignore) {
156 try {
157 model.setObject(restClient.read(model.getObject().getKey()));
158 restClient.mustChangePassword(
159 model.getObject().getETagValue(),
160 !model.getObject().isMustChangePassword(),
161 model.getObject().getKey());
162
163 SyncopeConsoleSession.get().success(getString(Constants.OPERATION_SUCCEEDED));
164 target.add(container);
165 } catch (Exception e) {
166 LOG.error("While actioning object {}", model.getObject().getKey(), e);
167 SyncopeConsoleSession.get().onException(e);
168 }
169 ((BasePage) pageRef.getPage()).getNotificationPanel().refresh(target);
170 }
171 }, ActionType.MUSTCHANGEPASSWORD, IdRepoEntitlement.USER_UPDATE).
172 setRealms(realm, model.getObject().getDynRealms());
173
174 if (wizardInModal) {
175 panel.add(new ActionLink<>() {
176
177 private static final long serialVersionUID = -4875218360625971340L;
178
179 @Override
180 public void onClick(final AjaxRequestTarget target, final UserTO ignore) {
181 model.setObject(restClient.read(model.getObject().getKey()));
182 IModel<AnyWrapper<UserTO>> formModel = new CompoundPropertyModel<>(
183 new UserWrapper(model.getObject()));
184 displayAttributeModal.setFormModel(formModel);
185
186 target.add(displayAttributeModal.setContent(new ChangePasswordModal(
187 displayAttributeModal,
188 new UserWrapper(model.getObject()),
189 pageRef)));
190
191 displayAttributeModal.header(new Model<>(
192 getString("any.edit", new Model<>(new UserWrapper(model.getObject())))));
193
194 displayAttributeModal.size(Modal.Size.Large);
195 displayAttributeModal.show(true);
196 }
197 }, ActionType.PASSWORD_MANAGEMENT, IdRepoEntitlement.USER_UPDATE).
198 setRealms(realm, model.getObject().getDynRealms());
199
200 PlatformInfo platformInfo = SyncopeConsoleSession.get().getAnonymousClient().platform();
201 if (platformInfo.isPwdResetAllowed() && !platformInfo.isPwdResetRequiringSecurityQuestions()) {
202 panel.add(new ActionLink<>() {
203
204 private static final long serialVersionUID = -7978723352517770644L;
205
206 @Override
207 public void onClick(final AjaxRequestTarget target, final UserTO ignore) {
208 try {
209 SyncopeConsoleSession.get().getAnonymousClient().getService(UserSelfService.class).
210 requestPasswordReset(model.getObject().getUsername(), null);
211
212 SyncopeConsoleSession.get().success(getString(Constants.OPERATION_SUCCEEDED));
213 target.add(container);
214 } catch (Exception e) {
215 LOG.error("While actioning object {}", model.getObject().getKey(), e);
216 SyncopeConsoleSession.get().onException(e);
217 }
218 ((BasePage) pageRef.getPage()).getNotificationPanel().refresh(target);
219 }
220 }, ActionType.REQUEST_PASSWORD_RESET, IdRepoEntitlement.USER_UPDATE).
221 setRealms(realm, model.getObject().getDynRealms());
222 }
223
224 SyncopeWebApplication.get().getAnyDirectoryPanelAdditionalActionLinksProvider().get(
225 model,
226 realm,
227 altDefaultModal,
228 getString("any.edit", new Model<>(new UserWrapper(model.getObject()))),
229 this,
230 pageRef).forEach(panel::add);
231
232 panel.add(new ActionLink<>() {
233
234 private static final long serialVersionUID = -7978723352517770644L;
235
236 @Override
237 public void onClick(final AjaxRequestTarget target, final UserTO ignore) {
238 target.add(utilityModal.setContent(new AnyPropagationTasks(
239 utilityModal, AnyTypeKind.USER, model.getObject().getKey(), pageRef)));
240
241 utilityModal.header(new StringResourceModel("any.propagation.tasks", model));
242 utilityModal.show(true);
243 }
244 }, ActionType.PROPAGATION_TASKS, IdRepoEntitlement.TASK_LIST);
245
246 panel.add(new ActionLink<>() {
247
248 private static final long serialVersionUID = -7978723352517770644L;
249
250 @Override
251 public void onClick(final AjaxRequestTarget target, final UserTO ignore) {
252 target.add(utilityModal.setContent(
253 new NotificationTasks(AnyTypeKind.USER, model.getObject().getKey(), pageRef)));
254 utilityModal.header(new StringResourceModel("any.notification.tasks", model));
255 utilityModal.show(true);
256 target.add(utilityModal);
257 }
258 }, ActionType.NOTIFICATION_TASKS, IdRepoEntitlement.TASK_LIST);
259 }
260
261 if (wizardInModal) {
262 panel.add(new ActionLink<>() {
263
264 private static final long serialVersionUID = -1978723352517770644L;
265
266 @Override
267 public void onClick(final AjaxRequestTarget target, final UserTO ignore) {
268 model.setObject(restClient.read(model.getObject().getKey()));
269 target.add(altDefaultModal.setContent(new AuditHistoryModal<>(
270 null,
271 null,
272 model.getObject(),
273 IdRepoEntitlement.USER_UPDATE,
274 auditRestClient) {
275
276 private static final long serialVersionUID = 959378158400669867L;
277
278 @Override
279 protected void restore(final String json, final AjaxRequestTarget target) {
280
281
282
283
284
285
286
287
288 UserTO original = model.getObject();
289 try {
290 UserTO updated = MAPPER.readValue(json, UserTO.class);
291 UserUR updateReq = AnyOperations.diff(updated, original, false);
292 updateReq.setPassword(null);
293 updateReq.setSecurityAnswer(null);
294 ProvisioningResult<UserTO> result =
295 restClient.update(original.getETagValue(), updateReq);
296 model.getObject().setLastChangeDate(result.getEntity().getLastChangeDate());
297
298 SyncopeConsoleSession.get().success(getString(Constants.OPERATION_SUCCEEDED));
299 target.add(container);
300 } catch (Exception e) {
301 LOG.error("While restoring user {}", model.getObject().getKey(), e);
302 SyncopeConsoleSession.get().onException(e);
303 }
304 ((BasePage) pageRef.getPage()).getNotificationPanel().refresh(target);
305 }
306 }));
307
308 altDefaultModal.header(new Model<>(
309 getString("auditHistory.title", new Model<>(new UserWrapper(model.getObject())))));
310
311 altDefaultModal.show(true);
312 }
313 }, ActionType.VIEW_AUDIT_HISTORY,
314 String.format("%s,%s", IdRepoEntitlement.USER_READ, IdRepoEntitlement.AUDIT_LIST)).
315 setRealms(realm, model.getObject().getDynRealms());
316 }
317
318 panel.add(new ActionLink<>() {
319
320 private static final long serialVersionUID = -7978723352517770644L;
321
322 @Override
323 public void onClick(final AjaxRequestTarget target, final UserTO ignore) {
324 UserTO clone = SerializationUtils.clone(model.getObject());
325 clone.setKey(null);
326 clone.setUsername(model.getObject().getUsername() + "_clone");
327 send(UserDirectoryPanel.this, Broadcast.EXACT,
328 new AjaxWizard.NewItemActionEvent<>(new UserWrapper(clone), target));
329 }
330
331 @Override
332 protected boolean statusCondition(final UserTO modelObject) {
333 return addAjaxLink.isVisibleInHierarchy() && realm.startsWith(SyncopeConstants.ROOT_REALM);
334 }
335 }, ActionType.CLONE, IdRepoEntitlement.USER_CREATE).setRealm(realm);
336
337 panel.add(new ActionLink<>() {
338
339 private static final long serialVersionUID = -7978723352517770644L;
340
341 @Override
342 public void onClick(final AjaxRequestTarget target, final UserTO ignore) {
343 try {
344 restClient.delete(model.getObject().getETagValue(), model.getObject().getKey());
345
346 SyncopeConsoleSession.get().success(getString(Constants.OPERATION_SUCCEEDED));
347 target.add(container);
348 } catch (Exception e) {
349 LOG.error("While deleting user {}", model.getObject().getKey(), e);
350 SyncopeConsoleSession.get().onException(e);
351 }
352 ((BasePage) pageRef.getPage()).getNotificationPanel().refresh(target);
353 }
354
355 @Override
356 protected boolean statusCondition(final UserTO modelObject) {
357 return realm.startsWith(SyncopeConstants.ROOT_REALM);
358 }
359 }, ActionType.DELETE, IdRepoEntitlement.USER_DELETE, true).setRealm(realm);
360
361 return panel;
362 }
363
364 public static class Builder extends AnyDirectoryPanel.Builder<UserTO, UserRestClient> {
365
366 private static final long serialVersionUID = -6603152478702381900L;
367
368 public Builder(
369 final List<AnyTypeClassTO> anyTypeClassTOs,
370 final UserRestClient restClient,
371 final String type,
372 final PageReference pageRef) {
373
374 super(anyTypeClassTOs, restClient, type, pageRef);
375 setShowResultPage(true);
376 }
377
378 @Override
379 protected WizardMgtPanel<AnyWrapper<UserTO>> newInstance(final String id, final boolean wizardInModal) {
380 return new UserDirectoryPanel(id, this, wizardInModal);
381 }
382 }
383 }