]> git.argeo.org Git - lgpl/argeo-commons.git/blob - org.argeo.cms.ui.workbench/src/org/argeo/cms/ui/workbench/internal/useradmin/parts/UserBatchUpdateWizard.java
Prepare Argeo Commons 2.1.49 release (+ cosmetic comments refactoring)
[lgpl/argeo-commons.git] / org.argeo.cms.ui.workbench / src / org / argeo / cms / ui / workbench / internal / useradmin / parts / UserBatchUpdateWizard.java
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.ui.workbench.internal.useradmin.UserAdminWrapper;
15 import org.argeo.cms.ui.workbench.internal.useradmin.providers.CommonNameLP;
16 import org.argeo.cms.ui.workbench.internal.useradmin.providers.DomainNameLP;
17 import org.argeo.cms.ui.workbench.internal.useradmin.providers.MailLP;
18 import org.argeo.cms.ui.workbench.internal.useradmin.providers.UserNameLP;
19 import org.argeo.cms.util.useradmin.UserAdminUtils;
20 import org.argeo.eclipse.ui.ColumnDefinition;
21 import org.argeo.eclipse.ui.EclipseUiUtils;
22 import org.argeo.eclipse.ui.parts.LdifUsersTable;
23 import org.argeo.naming.LdapAttrs;
24 import org.argeo.naming.LdapObjs;
25 import org.argeo.node.NodeConstants;
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
48 /** Wizard to update users */
49 public class UserBatchUpdateWizard extends Wizard {
50
51 private final static Log log = LogFactory
52 .getLog(UserBatchUpdateWizard.class);
53 private UserAdminWrapper userAdminWrapper;
54
55 // pages
56 private ChooseCommandWizardPage chooseCommandPage;
57 private ChooseUsersWizardPage userListPage;
58 private ValidateAndLaunchWizardPage validatePage;
59
60 // Various implemented commands keys
61 private final static String CMD_UPDATE_PASSWORD = "resetPassword";
62 private final static String CMD_GROUP_MEMBERSHIP = "groupMembership";
63
64 private final Map<String, String> commands = new HashMap<String, String>() {
65 private static final long serialVersionUID = 1L;
66 {
67 put("Reset password(s)", CMD_UPDATE_PASSWORD);
68 // TODO implement role / group management
69 // put("Add/Remove from group", CMD_GROUP_MEMBERSHIP);
70 }
71 };
72
73 public UserBatchUpdateWizard(UserAdminWrapper userAdminWrapper) {
74 this.userAdminWrapper = userAdminWrapper;
75 }
76
77 @Override
78 public void addPages() {
79 chooseCommandPage = new ChooseCommandWizardPage();
80 addPage(chooseCommandPage);
81 userListPage = new ChooseUsersWizardPage();
82 addPage(userListPage);
83 validatePage = new ValidateAndLaunchWizardPage();
84 addPage(validatePage);
85 }
86
87 @Override
88 public boolean performFinish() {
89 if (!canFinish())
90 return false;
91 UserTransaction ut = userAdminWrapper.getUserTransaction();
92 try {
93 if (ut.getStatus() != javax.transaction.Status.STATUS_NO_TRANSACTION
94 && !MessageDialog.openConfirm(getShell(),
95 "Existing Transaction",
96 "A user transaction is already existing, "
97 + "are you sure you want to proceed ?"))
98 return false;
99 } catch (SystemException e) {
100 throw new CmsException("Cannot get user transaction state "
101 + "before user batch update", e);
102 }
103
104 // We cannot use jobs, user modifications are still meant to be done in
105 // the UIThread
106 // UpdateJob job = null;
107 // if (job != null)
108 // job.schedule();
109
110 if (CMD_UPDATE_PASSWORD.equals(chooseCommandPage.getCommand())) {
111 char[] newValue = chooseCommandPage.getPwdValue();
112 if (newValue == null)
113 throw new CmsException(
114 "Password cannot be null or an empty string");
115 ResetPassword job = new ResetPassword(userAdminWrapper,
116 userListPage.getSelectedUsers(), newValue);
117 job.doUpdate();
118 }
119 return true;
120 }
121
122 public boolean canFinish() {
123 if (this.getContainer().getCurrentPage() == validatePage)
124 return true;
125 return false;
126 }
127
128 private class ResetPassword {
129 private char[] newPwd;
130 private UserAdminWrapper userAdminWrapper;
131 private List<User> usersToUpdate;
132
133 public ResetPassword(UserAdminWrapper userAdminWrapper,
134 List<User> usersToUpdate, char[] newPwd) {
135 this.newPwd = newPwd;
136 this.usersToUpdate = usersToUpdate;
137 this.userAdminWrapper = userAdminWrapper;
138 }
139
140 @SuppressWarnings("unchecked")
141 protected void doUpdate() {
142 userAdminWrapper.beginTransactionIfNeeded();
143 try {
144 for (User user : usersToUpdate) {
145 // the char array is emptied after being used.
146 user.getCredentials().put(null, newPwd.clone());
147 }
148 userAdminWrapper.commitOrNotifyTransactionStateChange();
149 } catch (Exception e) {
150 throw new CmsException("Cannot perform batch update on users",
151 e);
152 } finally {
153 UserTransaction ut = userAdminWrapper.getUserTransaction();
154 try {
155 if (ut.getStatus() != javax.transaction.Status.STATUS_NO_TRANSACTION)
156 ut.rollback();
157 } catch (IllegalStateException | SecurityException
158 | SystemException e) {
159 log.error("Unable to rollback session in 'finally', "
160 + "the system might be in a dirty state");
161 e.printStackTrace();
162 }
163 }
164 }
165 }
166
167 // @SuppressWarnings("unused")
168 // private class AddToGroup extends UpdateJob {
169 // private String groupID;
170 // private Session session;
171 //
172 // public AddToGroup(Session session, List<Node> nodesToUpdate,
173 // String groupID) {
174 // super(session, nodesToUpdate);
175 // this.session = session;
176 // this.groupID = groupID;
177 // }
178 //
179 // protected void doUpdate(Node node) {
180 // log.info("Add/Remove to group actions are not yet implemented");
181 // // TODO implement this
182 // // try {
183 // // throw new CmsException("Not yet implemented");
184 // // } catch (RepositoryException re) {
185 // // throw new CmsException(
186 // // "Unable to update boolean value for node " + node, re);
187 // // }
188 // }
189 // }
190
191 // /**
192 // * Base privileged job that will be run asynchronously to perform the
193 // batch
194 // * update
195 // */
196 // private abstract class UpdateJob extends PrivilegedJob {
197 //
198 // private final UserAdminWrapper userAdminWrapper;
199 // private final List<User> usersToUpdate;
200 //
201 // protected abstract void doUpdate(User user);
202 //
203 // public UpdateJob(UserAdminWrapper userAdminWrapper,
204 // List<User> usersToUpdate) {
205 // super("Perform update");
206 // this.usersToUpdate = usersToUpdate;
207 // this.userAdminWrapper = userAdminWrapper;
208 // }
209 //
210 // @Override
211 // protected IStatus doRun(IProgressMonitor progressMonitor) {
212 // try {
213 // JcrMonitor monitor = new EclipseJcrMonitor(progressMonitor);
214 // int total = usersToUpdate.size();
215 // monitor.beginTask("Performing change", total);
216 // userAdminWrapper.beginTransactionIfNeeded();
217 // for (User user : usersToUpdate) {
218 // doUpdate(user);
219 // monitor.worked(1);
220 // }
221 // userAdminWrapper.getUserTransaction().commit();
222 // } catch (Exception e) {
223 // throw new CmsException(
224 // "Cannot perform batch update on users", e);
225 // } finally {
226 // UserTransaction ut = userAdminWrapper.getUserTransaction();
227 // try {
228 // if (ut.getStatus() != javax.transaction.Status.STATUS_NO_TRANSACTION)
229 // ut.rollback();
230 // } catch (IllegalStateException | SecurityException
231 // | SystemException e) {
232 // log.error("Unable to rollback session in 'finally', "
233 // + "the system might be in a dirty state");
234 // e.printStackTrace();
235 // }
236 // }
237 // return Status.OK_STATUS;
238 // }
239 // }
240
241 // PAGES
242 /** Displays a combo box that enables user to choose which action to perform */
243 private class ChooseCommandWizardPage extends WizardPage {
244 private static final long serialVersionUID = -8069434295293996633L;
245 private Combo chooseCommandCmb;
246 private Button trueChk;
247 private Text valueTxt;
248 private Text pwdTxt;
249 private Text pwd2Txt;
250
251 public ChooseCommandWizardPage() {
252 super("Choose a command to run.");
253 setTitle("Choose a command to run.");
254 }
255
256 @Override
257 public void createControl(Composite parent) {
258 GridLayout gl = new GridLayout();
259 Composite container = new Composite(parent, SWT.NO_FOCUS);
260 container.setLayout(gl);
261
262 chooseCommandCmb = new Combo(container, SWT.READ_ONLY);
263 chooseCommandCmb.setLayoutData(EclipseUiUtils.fillWidth());
264 String[] values = commands.keySet().toArray(new String[0]);
265 chooseCommandCmb.setItems(values);
266
267 final Composite bottomPart = new Composite(container, SWT.NO_FOCUS);
268 bottomPart.setLayoutData(EclipseUiUtils.fillAll());
269 bottomPart.setLayout(EclipseUiUtils.noSpaceGridLayout());
270
271 chooseCommandCmb.addSelectionListener(new SelectionAdapter() {
272 private static final long serialVersionUID = 1L;
273
274 @Override
275 public void widgetSelected(SelectionEvent e) {
276 if (getCommand().equals(CMD_UPDATE_PASSWORD))
277 populatePasswordCmp(bottomPart);
278 else if (getCommand().equals(CMD_GROUP_MEMBERSHIP))
279 populateGroupCmp(bottomPart);
280 else
281 populateBooleanFlagCmp(bottomPart);
282 checkPageComplete();
283 bottomPart.layout(true, true);
284 }
285 });
286 setControl(container);
287 }
288
289 private void populateBooleanFlagCmp(Composite parent) {
290 EclipseUiUtils.clear(parent);
291 trueChk = new Button(parent, SWT.CHECK);
292 trueChk.setText("Do it. (It will to the contrary if unchecked)");
293 trueChk.setSelection(true);
294 trueChk.setLayoutData(new GridData(SWT.LEFT, SWT.TOP, false, false));
295 }
296
297 private void populatePasswordCmp(Composite parent) {
298 EclipseUiUtils.clear(parent);
299 Composite body = new Composite(parent, SWT.NO_FOCUS);
300
301 ModifyListener ml = new ModifyListener() {
302 private static final long serialVersionUID = -1558726363536729634L;
303
304 @Override
305 public void modifyText(ModifyEvent event) {
306 checkPageComplete();
307 }
308 };
309
310 body.setLayout(new GridLayout(2, false));
311 body.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
312 pwdTxt = EclipseUiUtils.createGridLP(body, "New password", ml);
313 pwd2Txt = EclipseUiUtils.createGridLP(body, "Repeat password", ml);
314 }
315
316 private void checkPageComplete() {
317 String errorMsg = null;
318 if (chooseCommandCmb.getSelectionIndex() < 0)
319 errorMsg = "Please select an action";
320 else if (CMD_UPDATE_PASSWORD.equals(getCommand())) {
321 if (EclipseUiUtils.isEmpty(pwdTxt.getText())
322 || pwdTxt.getText().length() < 4)
323 errorMsg = "Please enter a password that is at least 4 character long";
324 else if (!pwdTxt.getText().equals(pwd2Txt.getText()))
325 errorMsg = "Passwords are different";
326 }
327 if (EclipseUiUtils.notEmpty(errorMsg)) {
328 setMessage(errorMsg, WizardPage.ERROR);
329 setPageComplete(false);
330 } else {
331 setMessage("Page complete, you can proceed to user choice",
332 WizardPage.INFORMATION);
333 setPageComplete(true);
334 }
335
336 getContainer().updateButtons();
337 }
338
339 private void populateGroupCmp(Composite parent) {
340 EclipseUiUtils.clear(parent);
341 trueChk = new Button(parent, SWT.CHECK);
342 trueChk.setText("Add to group. (It will remove user(s) from the "
343 + "corresponding group if unchecked)");
344 trueChk.setSelection(true);
345 trueChk.setLayoutData(new GridData(SWT.LEFT, SWT.TOP, false, false));
346 }
347
348 protected String getCommand() {
349 return commands.get(chooseCommandCmb.getItem(chooseCommandCmb
350 .getSelectionIndex()));
351 }
352
353 protected String getCommandLbl() {
354 return chooseCommandCmb.getItem(chooseCommandCmb
355 .getSelectionIndex());
356 }
357
358 @SuppressWarnings("unused")
359 protected boolean getBoleanValue() {
360 // FIXME this is not consistent and will lead to errors.
361 if ("argeo:enabled".equals(getCommand()))
362 return trueChk.getSelection();
363 else
364 return !trueChk.getSelection();
365 }
366
367 @SuppressWarnings("unused")
368 protected String getStringValue() {
369 String value = null;
370 if (valueTxt != null) {
371 value = valueTxt.getText();
372 if ("".equals(value.trim()))
373 value = null;
374 }
375 return value;
376 }
377
378 protected char[] getPwdValue() {
379 // We do not directly reset the password text fields: There is no
380 // need to over secure this process: setting a pwd to multi users
381 // at the same time is anyhow a bad practice and should be used only
382 // in test environment or for temporary access
383 if (pwdTxt == null || pwdTxt.isDisposed())
384 return null;
385 else
386 return pwdTxt.getText().toCharArray();
387 }
388 }
389
390 /**
391 * Displays a list of users with a check box to be able to choose some of
392 * them
393 */
394 private class ChooseUsersWizardPage extends WizardPage implements
395 IPageChangedListener {
396 private static final long serialVersionUID = 7651807402211214274L;
397 private ChooseUserTableViewer userTableCmp;
398
399 public ChooseUsersWizardPage() {
400 super("Choose Users");
401 setTitle("Select users who will be impacted");
402 }
403
404 @Override
405 public void createControl(Composite parent) {
406 Composite pageCmp = new Composite(parent, SWT.NONE);
407 pageCmp.setLayout(EclipseUiUtils.noSpaceGridLayout());
408
409 // Define the displayed columns
410 List<ColumnDefinition> columnDefs = new ArrayList<ColumnDefinition>();
411 columnDefs.add(new ColumnDefinition(new CommonNameLP(),
412 "Common Name", 150));
413 columnDefs.add(new ColumnDefinition(new MailLP(), "E-mail", 150));
414 columnDefs.add(new ColumnDefinition(new DomainNameLP(), "Domain",
415 200));
416
417 // Only show technical DN to admin
418 if (UserAdminUtils.isUserInRole(NodeConstants.ROLE_ADMIN))
419 columnDefs.add(new ColumnDefinition(new UserNameLP(),
420 "Distinguished Name", 300));
421
422 userTableCmp = new ChooseUserTableViewer(pageCmp, SWT.MULTI
423 | SWT.H_SCROLL | SWT.V_SCROLL);
424 userTableCmp.setLayoutData(EclipseUiUtils.fillAll());
425 userTableCmp.setColumnDefinitions(columnDefs);
426 userTableCmp.populate(true, true);
427 userTableCmp.refresh();
428
429 setControl(pageCmp);
430
431 // Add listener to update message when shown
432 final IWizardContainer wContainer = this.getContainer();
433 if (wContainer instanceof IPageChangeProvider) {
434 ((IPageChangeProvider) wContainer).addPageChangedListener(this);
435 }
436
437 }
438
439 @Override
440 public void pageChanged(PageChangedEvent event) {
441 if (event.getSelectedPage() == this) {
442 String msg = "Chosen batch action: "
443 + chooseCommandPage.getCommandLbl();
444 ((WizardPage) event.getSelectedPage()).setMessage(msg);
445 }
446 }
447
448 protected List<User> getSelectedUsers() {
449 return userTableCmp.getSelectedUsers();
450 }
451
452 private class ChooseUserTableViewer extends LdifUsersTable {
453 private static final long serialVersionUID = 5080437561015853124L;
454 private final String[] knownProps = { LdapAttrs.uid.name(),
455 LdapAttrs.DN, LdapAttrs.cn.name(),
456 LdapAttrs.givenName.name(), LdapAttrs.sn.name(),
457 LdapAttrs.mail.name() };
458
459 public ChooseUserTableViewer(Composite parent, int style) {
460 super(parent, style);
461 }
462
463 @Override
464 protected List<User> listFilteredElements(String filter) {
465 Role[] roles;
466
467 try {
468 StringBuilder builder = new StringBuilder();
469
470 StringBuilder tmpBuilder = new StringBuilder();
471 if (EclipseUiUtils.notEmpty(filter))
472 for (String prop : knownProps) {
473 tmpBuilder.append("(");
474 tmpBuilder.append(prop);
475 tmpBuilder.append("=*");
476 tmpBuilder.append(filter);
477 tmpBuilder.append("*)");
478 }
479 if (tmpBuilder.length() > 1) {
480 builder.append("(&(")
481 .append(LdapAttrs.objectClass.name())
482 .append("=")
483 .append(LdapObjs.inetOrgPerson.name())
484 .append(")(|");
485 builder.append(tmpBuilder.toString());
486 builder.append("))");
487 } else
488 builder.append("(")
489 .append(LdapAttrs.objectClass.name())
490 .append("=")
491 .append(LdapObjs.inetOrgPerson.name())
492 .append(")");
493 roles = userAdminWrapper.getUserAdmin().getRoles(
494 builder.toString());
495 } catch (InvalidSyntaxException e) {
496 throw new CmsException("Unable to get roles with filter: "
497 + filter, e);
498 }
499 List<User> users = new ArrayList<User>();
500 for (Role role : roles)
501 // Prevent current logged in user to perform batch on
502 // himself
503 if (!UserAdminUtils.isCurrentUser((User) role))
504 users.add((User) role);
505 return users;
506 }
507 }
508 }
509
510 /** Summary of input data before launching the process */
511 private class ValidateAndLaunchWizardPage extends WizardPage implements
512 IPageChangedListener {
513 private static final long serialVersionUID = 7098918351451743853L;
514 private ChosenUsersTableViewer userTableCmp;
515
516 public ValidateAndLaunchWizardPage() {
517 super("Validate and launch");
518 setTitle("Validate and launch");
519 }
520
521 @Override
522 public void createControl(Composite parent) {
523 Composite pageCmp = new Composite(parent, SWT.NO_FOCUS);
524 pageCmp.setLayout(EclipseUiUtils.noSpaceGridLayout());
525
526 List<ColumnDefinition> columnDefs = new ArrayList<ColumnDefinition>();
527 columnDefs.add(new ColumnDefinition(new CommonNameLP(),
528 "Common Name", 150));
529 columnDefs.add(new ColumnDefinition(new MailLP(), "E-mail", 150));
530 columnDefs.add(new ColumnDefinition(new DomainNameLP(), "Domain",
531 200));
532 // Only show technical DN to admin
533 if (UserAdminUtils.isUserInRole(NodeConstants.ROLE_ADMIN))
534 columnDefs.add(new ColumnDefinition(new UserNameLP(),
535 "Distinguished Name", 300));
536 userTableCmp = new ChosenUsersTableViewer(pageCmp, SWT.MULTI
537 | SWT.H_SCROLL | SWT.V_SCROLL);
538 userTableCmp.setLayoutData(EclipseUiUtils.fillAll());
539 userTableCmp.setColumnDefinitions(columnDefs);
540 userTableCmp.populate(false, false);
541 userTableCmp.refresh();
542 setControl(pageCmp);
543 // Add listener to update message when shown
544 final IWizardContainer wContainer = this.getContainer();
545 if (wContainer instanceof IPageChangeProvider) {
546 ((IPageChangeProvider) wContainer).addPageChangedListener(this);
547 }
548 }
549
550 @Override
551 public void pageChanged(PageChangedEvent event) {
552 if (event.getSelectedPage() == this) {
553 @SuppressWarnings({ "unchecked", "rawtypes" })
554 Object[] values = ((ArrayList) userListPage.getSelectedUsers())
555 .toArray(new Object[userListPage.getSelectedUsers()
556 .size()]);
557 userTableCmp.getTableViewer().setInput(values);
558 String msg = "Following batch action: ["
559 + chooseCommandPage.getCommandLbl()
560 + "] will be perfomed on the users listed below.\n";
561 // + "Are you sure you want to proceed?";
562 setMessage(msg);
563 }
564 }
565
566 private class ChosenUsersTableViewer extends LdifUsersTable {
567 private static final long serialVersionUID = 7814764735794270541L;
568
569 public ChosenUsersTableViewer(Composite parent, int style) {
570 super(parent, style);
571 }
572
573 @Override
574 protected List<User> listFilteredElements(String filter) {
575 return userListPage.getSelectedUsers();
576 }
577 }
578 }
579 }