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