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