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