1 package org.argeo.cms.ui.workbench.internal.useradmin.parts;
2
3 import java.util.ArrayList;
4 import java.util.HashMap;
5 import java.util.List;
6 import java.util.Map;
7
8 import javax.transaction.SystemException;
9 import javax.transaction.UserTransaction;
10
11 import org.apache.commons.logging.Log;
12 import org.apache.commons.logging.LogFactory;
13 import org.argeo.cms.CmsException;
14 import org.argeo.cms.auth.CurrentUser;
15 import org.argeo.cms.ui.workbench.internal.useradmin.UiAdminUtils;
16 import org.argeo.cms.ui.workbench.internal.useradmin.UserAdminWrapper;
17 import org.argeo.cms.ui.workbench.internal.useradmin.providers.CommonNameLP;
18 import org.argeo.cms.ui.workbench.internal.useradmin.providers.DomainNameLP;
19 import org.argeo.cms.ui.workbench.internal.useradmin.providers.MailLP;
20 import org.argeo.cms.ui.workbench.internal.useradmin.providers.UserNameLP;
21 import org.argeo.cms.util.UserAdminUtils;
22 import org.argeo.eclipse.ui.ColumnDefinition;
23 import org.argeo.eclipse.ui.EclipseUiUtils;
24 import org.argeo.eclipse.ui.parts.LdifUsersTable;
25 import org.argeo.naming.LdapAttrs;
26 import org.argeo.naming.LdapObjs;
27 import org.argeo.node.NodeConstants;
28 import org.eclipse.jface.dialogs.IPageChangeProvider;
29 import org.eclipse.jface.dialogs.IPageChangedListener;
30 import org.eclipse.jface.dialogs.MessageDialog;
31 import org.eclipse.jface.dialogs.PageChangedEvent;
32 import org.eclipse.jface.wizard.IWizardContainer;
33 import org.eclipse.jface.wizard.Wizard;
34 import org.eclipse.jface.wizard.WizardPage;
35 import org.eclipse.swt.SWT;
36 import org.eclipse.swt.events.ModifyEvent;
37 import org.eclipse.swt.events.ModifyListener;
38 import org.eclipse.swt.events.SelectionAdapter;
39 import org.eclipse.swt.events.SelectionEvent;
40 import org.eclipse.swt.layout.GridData;
41 import org.eclipse.swt.layout.GridLayout;
42 import org.eclipse.swt.widgets.Button;
43 import org.eclipse.swt.widgets.Combo;
44 import org.eclipse.swt.widgets.Composite;
45 import org.eclipse.swt.widgets.Text;
46 import org.osgi.framework.InvalidSyntaxException;
47 import org.osgi.service.useradmin.Role;
48 import org.osgi.service.useradmin.User;
49 import org.osgi.service.useradmin.UserAdminEvent;
50
51
52 public class UserBatchUpdateWizard extends Wizard {
53
54 private final static Log log = LogFactory.getLog(UserBatchUpdateWizard.class);
55 private UserAdminWrapper userAdminWrapper;
56
57
58 private ChooseCommandWizardPage chooseCommandPage;
59 private ChooseUsersWizardPage userListPage;
60 private ValidateAndLaunchWizardPage validatePage;
61
62
63 private final static String CMD_UPDATE_PASSWORD = "resetPassword";
64 private final static String CMD_UPDATE_EMAIL = "resetEmail";
65 private final static String CMD_GROUP_MEMBERSHIP = "groupMembership";
66
67 private final Map<String, String> commands = new HashMap<String, String>() {
68 private static final long serialVersionUID = 1L;
69 {
70 put("Reset password(s)", CMD_UPDATE_PASSWORD);
71 put("Reset email(s)", CMD_UPDATE_EMAIL);
72
73
74 }
75 };
76
77 public UserBatchUpdateWizard(UserAdminWrapper userAdminWrapper) {
78 this.userAdminWrapper = userAdminWrapper;
79 }
80
81 @Override
82 public void addPages() {
83 chooseCommandPage = new ChooseCommandWizardPage();
84 addPage(chooseCommandPage);
85 userListPage = new ChooseUsersWizardPage();
86 addPage(userListPage);
87 validatePage = new ValidateAndLaunchWizardPage();
88 addPage(validatePage);
89 }
90
91 @Override
92 public boolean performFinish() {
93 if (!canFinish())
94 return false;
95 UserTransaction ut = userAdminWrapper.getUserTransaction();
96 try {
97 if (ut.getStatus() != javax.transaction.Status.STATUS_NO_TRANSACTION
98 && !MessageDialog.openConfirm(getShell(), "Existing Transaction",
99 "A user transaction is already existing, " + "are you sure you want to proceed ?"))
100 return false;
101 } catch (SystemException e) {
102 throw new CmsException("Cannot get user transaction state " + "before user batch update", e);
103 }
104
105
106
107
108
109
110
111 if (CMD_UPDATE_PASSWORD.equals(chooseCommandPage.getCommand())) {
112 char[] newValue = chooseCommandPage.getPwdValue();
113 if (newValue == null)
114 throw new CmsException("Password cannot be null or an empty string");
115 ResetPassword job = new ResetPassword(userAdminWrapper, userListPage.getSelectedUsers(), newValue);
116 job.doUpdate();
117 } else if (CMD_UPDATE_EMAIL.equals(chooseCommandPage.getCommand())) {
118 String newValue = chooseCommandPage.getEmailValue();
119 if (newValue == null)
120 throw new CmsException("Password cannot be null or an empty string");
121 ResetEmail job = new ResetEmail(userAdminWrapper, userListPage.getSelectedUsers(), newValue);
122 job.doUpdate();
123 }
124 return true;
125 }
126
127 public boolean canFinish() {
128 if (this.getContainer().getCurrentPage() == validatePage)
129 return true;
130 return false;
131 }
132
133 private class ResetPassword {
134 private char[] newPwd;
135 private UserAdminWrapper userAdminWrapper;
136 private List<User> usersToUpdate;
137
138 public ResetPassword(UserAdminWrapper userAdminWrapper, List<User> usersToUpdate, char[] newPwd) {
139 this.newPwd = newPwd;
140 this.usersToUpdate = usersToUpdate;
141 this.userAdminWrapper = userAdminWrapper;
142 }
143
144 @SuppressWarnings("unchecked")
145 protected void doUpdate() {
146 userAdminWrapper.beginTransactionIfNeeded();
147 try {
148 for (User user : usersToUpdate) {
149
150 user.getCredentials().put(null, newPwd.clone());
151 }
152 userAdminWrapper.commitOrNotifyTransactionStateChange();
153 } catch (Exception e) {
154 throw new CmsException("Cannot perform batch update on users", e);
155 } finally {
156 UserTransaction ut = userAdminWrapper.getUserTransaction();
157 try {
158 if (ut.getStatus() != javax.transaction.Status.STATUS_NO_TRANSACTION)
159 ut.rollback();
160 } catch (IllegalStateException | SecurityException | SystemException e) {
161 log.error("Unable to rollback session in 'finally', " + "the system might be in a dirty state");
162 e.printStackTrace();
163 }
164 }
165 }
166 }
167
168 private class ResetEmail {
169 private String newEmail;
170 private UserAdminWrapper userAdminWrapper;
171 private List<User> usersToUpdate;
172
173 public ResetEmail(UserAdminWrapper userAdminWrapper, List<User> usersToUpdate, String newEmail) {
174 this.newEmail = newEmail;
175 this.usersToUpdate = usersToUpdate;
176 this.userAdminWrapper = userAdminWrapper;
177 }
178
179 @SuppressWarnings("unchecked")
180 protected void doUpdate() {
181 userAdminWrapper.beginTransactionIfNeeded();
182 try {
183 for (User user : usersToUpdate) {
184
185 user.getProperties().put(LdapAttrs.mail.name(), newEmail);
186 }
187
188 userAdminWrapper.commitOrNotifyTransactionStateChange();
189 if (!usersToUpdate.isEmpty())
190 userAdminWrapper.notifyListeners(
191 new UserAdminEvent(null, UserAdminEvent.ROLE_CHANGED, usersToUpdate.get(0)));
192 } catch (Exception e) {
193 throw new CmsException("Cannot perform batch update on users", e);
194 } finally {
195 UserTransaction ut = userAdminWrapper.getUserTransaction();
196 try {
197 if (ut.getStatus() != javax.transaction.Status.STATUS_NO_TRANSACTION)
198 ut.rollback();
199 } catch (IllegalStateException | SecurityException | SystemException e) {
200 log.error("Unable to rollback session in finally block, the system might be in a dirty state");
201 e.printStackTrace();
202 }
203 }
204 }
205 }
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285 private class ChooseCommandWizardPage extends WizardPage {
286 private static final long serialVersionUID = -8069434295293996633L;
287 private Combo chooseCommandCmb;
288 private Button trueChk;
289 private Text valueTxt;
290 private Text pwdTxt;
291 private Text pwd2Txt;
292
293 public ChooseCommandWizardPage() {
294 super("Choose a command to run.");
295 setTitle("Choose a command to run.");
296 }
297
298 @Override
299 public void createControl(Composite parent) {
300 GridLayout gl = new GridLayout();
301 Composite container = new Composite(parent, SWT.NO_FOCUS);
302 container.setLayout(gl);
303
304 chooseCommandCmb = new Combo(container, SWT.READ_ONLY);
305 chooseCommandCmb.setLayoutData(EclipseUiUtils.fillWidth());
306 String[] values = commands.keySet().toArray(new String[0]);
307 chooseCommandCmb.setItems(values);
308
309 final Composite bottomPart = new Composite(container, SWT.NO_FOCUS);
310 bottomPart.setLayoutData(EclipseUiUtils.fillAll());
311 bottomPart.setLayout(EclipseUiUtils.noSpaceGridLayout());
312
313 chooseCommandCmb.addSelectionListener(new SelectionAdapter() {
314 private static final long serialVersionUID = 1L;
315
316 @Override
317 public void widgetSelected(SelectionEvent e) {
318 if (getCommand().equals(CMD_UPDATE_PASSWORD))
319 populatePasswordCmp(bottomPart);
320 else if (getCommand().equals(CMD_UPDATE_EMAIL))
321 populateEmailCmp(bottomPart);
322 else if (getCommand().equals(CMD_GROUP_MEMBERSHIP))
323 populateGroupCmp(bottomPart);
324 else
325 populateBooleanFlagCmp(bottomPart);
326 checkPageComplete();
327 bottomPart.layout(true, true);
328 }
329 });
330 setControl(container);
331 }
332
333 private void populateBooleanFlagCmp(Composite parent) {
334 EclipseUiUtils.clear(parent);
335 trueChk = new Button(parent, SWT.CHECK);
336 trueChk.setText("Do it. (It will to the contrary if unchecked)");
337 trueChk.setSelection(true);
338 trueChk.setLayoutData(new GridData(SWT.LEFT, SWT.TOP, false, false));
339 }
340
341 private void populatePasswordCmp(Composite parent) {
342 EclipseUiUtils.clear(parent);
343 Composite body = new Composite(parent, SWT.NO_FOCUS);
344
345 ModifyListener ml = new ModifyListener() {
346 private static final long serialVersionUID = -1558726363536729634L;
347
348 @Override
349 public void modifyText(ModifyEvent event) {
350 checkPageComplete();
351 }
352 };
353
354 body.setLayout(new GridLayout(2, false));
355 body.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
356 pwdTxt = EclipseUiUtils.createGridLP(body, "New password", ml);
357 pwd2Txt = EclipseUiUtils.createGridLP(body, "Repeat password", ml);
358 }
359
360 private void populateEmailCmp(Composite parent) {
361 EclipseUiUtils.clear(parent);
362 Composite body = new Composite(parent, SWT.NO_FOCUS);
363
364 ModifyListener ml = new ModifyListener() {
365 private static final long serialVersionUID = 2147704227294268317L;
366
367 @Override
368 public void modifyText(ModifyEvent event) {
369 checkPageComplete();
370 }
371 };
372
373 body.setLayout(new GridLayout(2, false));
374 body.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
375 valueTxt = EclipseUiUtils.createGridLT(body, "New e-mail", ml);
376 }
377
378 private void checkPageComplete() {
379 String errorMsg = null;
380 if (chooseCommandCmb.getSelectionIndex() < 0)
381 errorMsg = "Please select an action";
382 else if (CMD_UPDATE_EMAIL.equals(getCommand())) {
383 if (!valueTxt.getText().matches(UiAdminUtils.EMAIL_PATTERN))
384 errorMsg = "Not a valid e-mail address";
385 } else if (CMD_UPDATE_PASSWORD.equals(getCommand())) {
386 if (EclipseUiUtils.isEmpty(pwdTxt.getText()) || pwdTxt.getText().length() < 4)
387 errorMsg = "Please enter a password that is at least 4 character long";
388 else if (!pwdTxt.getText().equals(pwd2Txt.getText()))
389 errorMsg = "Passwords are different";
390 }
391 if (EclipseUiUtils.notEmpty(errorMsg)) {
392 setMessage(errorMsg, WizardPage.ERROR);
393 setPageComplete(false);
394 } else {
395 setMessage("Page complete, you can proceed to user choice", WizardPage.INFORMATION);
396 setPageComplete(true);
397 }
398
399 getContainer().updateButtons();
400 }
401
402 private void populateGroupCmp(Composite parent) {
403 EclipseUiUtils.clear(parent);
404 trueChk = new Button(parent, SWT.CHECK);
405 trueChk.setText("Add to group. (It will remove user(s) from the " + "corresponding group if unchecked)");
406 trueChk.setSelection(true);
407 trueChk.setLayoutData(new GridData(SWT.LEFT, SWT.TOP, false, false));
408 }
409
410 protected String getCommand() {
411 return commands.get(chooseCommandCmb.getItem(chooseCommandCmb.getSelectionIndex()));
412 }
413
414 protected String getCommandLbl() {
415 return chooseCommandCmb.getItem(chooseCommandCmb.getSelectionIndex());
416 }
417
418 @SuppressWarnings("unused")
419 protected boolean getBoleanValue() {
420
421 if ("argeo:enabled".equals(getCommand()))
422 return trueChk.getSelection();
423 else
424 return !trueChk.getSelection();
425 }
426
427 @SuppressWarnings("unused")
428 protected String getStringValue() {
429 String value = null;
430 if (valueTxt != null) {
431 value = valueTxt.getText();
432 if ("".equals(value.trim()))
433 value = null;
434 }
435 return value;
436 }
437
438 protected char[] getPwdValue() {
439
440
441
442
443 if (pwdTxt == null || pwdTxt.isDisposed())
444 return null;
445 else
446 return pwdTxt.getText().toCharArray();
447 }
448
449 protected String getEmailValue() {
450
451
452
453
454 if (valueTxt == null || valueTxt.isDisposed())
455 return null;
456 else
457 return valueTxt.getText();
458 }
459 }
460
461
462
463
464
465 private class ChooseUsersWizardPage extends WizardPage implements IPageChangedListener {
466 private static final long serialVersionUID = 7651807402211214274L;
467 private ChooseUserTableViewer userTableCmp;
468
469 public ChooseUsersWizardPage() {
470 super("Choose Users");
471 setTitle("Select users who will be impacted");
472 }
473
474 @Override
475 public void createControl(Composite parent) {
476 Composite pageCmp = new Composite(parent, SWT.NONE);
477 pageCmp.setLayout(EclipseUiUtils.noSpaceGridLayout());
478
479
480 List<ColumnDefinition> columnDefs = new ArrayList<ColumnDefinition>();
481 columnDefs.add(new ColumnDefinition(new CommonNameLP(), "Common Name", 150));
482 columnDefs.add(new ColumnDefinition(new MailLP(), "E-mail", 150));
483 columnDefs.add(new ColumnDefinition(new DomainNameLP(), "Domain", 200));
484
485
486 if (CurrentUser.isInRole(NodeConstants.ROLE_ADMIN))
487 columnDefs.add(new ColumnDefinition(new UserNameLP(), "Distinguished Name", 300));
488
489 userTableCmp = new ChooseUserTableViewer(pageCmp, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL);
490 userTableCmp.setLayoutData(EclipseUiUtils.fillAll());
491 userTableCmp.setColumnDefinitions(columnDefs);
492 userTableCmp.populate(true, true);
493 userTableCmp.refresh();
494
495 setControl(pageCmp);
496
497
498 final IWizardContainer wContainer = this.getContainer();
499 if (wContainer instanceof IPageChangeProvider) {
500 ((IPageChangeProvider) wContainer).addPageChangedListener(this);
501 }
502
503 }
504
505 @Override
506 public void pageChanged(PageChangedEvent event) {
507 if (event.getSelectedPage() == this) {
508 String msg = "Chosen batch action: " + chooseCommandPage.getCommandLbl();
509 ((WizardPage) event.getSelectedPage()).setMessage(msg);
510 }
511 }
512
513 protected List<User> getSelectedUsers() {
514 return userTableCmp.getSelectedUsers();
515 }
516
517 private class ChooseUserTableViewer extends LdifUsersTable {
518 private static final long serialVersionUID = 5080437561015853124L;
519 private final String[] knownProps = { LdapAttrs.uid.name(), LdapAttrs.DN, LdapAttrs.cn.name(),
520 LdapAttrs.givenName.name(), LdapAttrs.sn.name(), LdapAttrs.mail.name() };
521
522 public ChooseUserTableViewer(Composite parent, int style) {
523 super(parent, style);
524 }
525
526 @Override
527 protected List<User> listFilteredElements(String filter) {
528 Role[] roles;
529
530 try {
531 StringBuilder builder = new StringBuilder();
532
533 StringBuilder tmpBuilder = new StringBuilder();
534 if (EclipseUiUtils.notEmpty(filter))
535 for (String prop : knownProps) {
536 tmpBuilder.append("(");
537 tmpBuilder.append(prop);
538 tmpBuilder.append("=*");
539 tmpBuilder.append(filter);
540 tmpBuilder.append("*)");
541 }
542 if (tmpBuilder.length() > 1) {
543 builder.append("(&(").append(LdapAttrs.objectClass.name()).append("=")
544 .append(LdapObjs.inetOrgPerson.name()).append(")(|");
545 builder.append(tmpBuilder.toString());
546 builder.append("))");
547 } else
548 builder.append("(").append(LdapAttrs.objectClass.name()).append("=")
549 .append(LdapObjs.inetOrgPerson.name()).append(")");
550 roles = userAdminWrapper.getUserAdmin().getRoles(builder.toString());
551 } catch (InvalidSyntaxException e) {
552 throw new CmsException("Unable to get roles with filter: " + filter, e);
553 }
554 List<User> users = new ArrayList<User>();
555 for (Role role : roles)
556
557
558 if (!UserAdminUtils.isCurrentUser((User) role))
559 users.add((User) role);
560 return users;
561 }
562 }
563 }
564
565
566 private class ValidateAndLaunchWizardPage extends WizardPage implements IPageChangedListener {
567 private static final long serialVersionUID = 7098918351451743853L;
568 private ChosenUsersTableViewer userTableCmp;
569
570 public ValidateAndLaunchWizardPage() {
571 super("Validate and launch");
572 setTitle("Validate and launch");
573 }
574
575 @Override
576 public void createControl(Composite parent) {
577 Composite pageCmp = new Composite(parent, SWT.NO_FOCUS);
578 pageCmp.setLayout(EclipseUiUtils.noSpaceGridLayout());
579
580 List<ColumnDefinition> columnDefs = new ArrayList<ColumnDefinition>();
581 columnDefs.add(new ColumnDefinition(new CommonNameLP(), "Common Name", 150));
582 columnDefs.add(new ColumnDefinition(new MailLP(), "E-mail", 150));
583 columnDefs.add(new ColumnDefinition(new DomainNameLP(), "Domain", 200));
584
585 if (CurrentUser.isInRole(NodeConstants.ROLE_ADMIN))
586 columnDefs.add(new ColumnDefinition(new UserNameLP(), "Distinguished Name", 300));
587 userTableCmp = new ChosenUsersTableViewer(pageCmp, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL);
588 userTableCmp.setLayoutData(EclipseUiUtils.fillAll());
589 userTableCmp.setColumnDefinitions(columnDefs);
590 userTableCmp.populate(false, false);
591 userTableCmp.refresh();
592 setControl(pageCmp);
593
594 final IWizardContainer wContainer = this.getContainer();
595 if (wContainer instanceof IPageChangeProvider) {
596 ((IPageChangeProvider) wContainer).addPageChangedListener(this);
597 }
598 }
599
600 @Override
601 public void pageChanged(PageChangedEvent event) {
602 if (event.getSelectedPage() == this) {
603 @SuppressWarnings({ "unchecked", "rawtypes" })
604 Object[] values = ((ArrayList) userListPage.getSelectedUsers())
605 .toArray(new Object[userListPage.getSelectedUsers().size()]);
606 userTableCmp.getTableViewer().setInput(values);
607 String msg = "Following batch action: [" + chooseCommandPage.getCommandLbl()
608 + "] will be perfomed on the users listed below.\n";
609
610 setMessage(msg);
611 }
612 }
613
614 private class ChosenUsersTableViewer extends LdifUsersTable {
615 private static final long serialVersionUID = 7814764735794270541L;
616
617 public ChosenUsersTableViewer(Composite parent, int style) {
618 super(parent, style);
619 }
620
621 @Override
622 protected List<User> listFilteredElements(String filter) {
623 return userListPage.getSelectedUsers();
624 }
625 }
626 }
627 }