]> git.argeo.org Git - lgpl/argeo-commons.git/blob - org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/parts/UserBatchUpdateWizard.java
Fix URI when using default LDIF
[lgpl/argeo-commons.git] / org.argeo.security.ui.admin / src / org / argeo / security / ui / admin / internal / parts / UserBatchUpdateWizard.java
1 package org.argeo.security.ui.admin.internal.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.ArgeoException;
14 import org.argeo.cms.auth.AuthConstants;
15 import org.argeo.eclipse.ui.ColumnDefinition;
16 import org.argeo.eclipse.ui.EclipseUiUtils;
17 import org.argeo.eclipse.ui.parts.LdifUsersTable;
18 import org.argeo.jcr.ArgeoNames;
19 import org.argeo.osgi.useradmin.LdifName;
20 import org.argeo.security.ui.admin.internal.UiAdminUtils;
21 import org.argeo.security.ui.admin.internal.UserAdminWrapper;
22 import org.argeo.security.ui.admin.internal.providers.CommonNameLP;
23 import org.argeo.security.ui.admin.internal.providers.DomainNameLP;
24 import org.argeo.security.ui.admin.internal.providers.MailLP;
25 import org.argeo.security.ui.admin.internal.providers.UserNameLP;
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 ArgeoException("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 ArgeoException(
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 UserTransaction userTransaction = userAdminWrapper
143 .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 userTransaction.commit();
150 UiAdminUtils.notifyTransactionStateChange(userTransaction);
151 } catch (Exception e) {
152 throw new ArgeoException(
153 "Cannot perform batch update on users", e);
154 } finally {
155 UserTransaction ut = userAdminWrapper.getUserTransaction();
156 try {
157 if (ut.getStatus() != javax.transaction.Status.STATUS_NO_TRANSACTION)
158 ut.rollback();
159 } catch (IllegalStateException | SecurityException
160 | SystemException e) {
161 log.error("Unable to rollback session in 'finally', "
162 + "the system might be in a dirty state");
163 e.printStackTrace();
164 }
165 }
166 }
167 }
168
169 // @SuppressWarnings("unused")
170 // private class AddToGroup extends UpdateJob {
171 // private String groupID;
172 // private Session session;
173 //
174 // public AddToGroup(Session session, List<Node> nodesToUpdate,
175 // String groupID) {
176 // super(session, nodesToUpdate);
177 // this.session = session;
178 // this.groupID = groupID;
179 // }
180 //
181 // protected void doUpdate(Node node) {
182 // log.info("Add/Remove to group actions are not yet implemented");
183 // // TODO implement this
184 // // try {
185 // // throw new ArgeoException("Not yet implemented");
186 // // } catch (RepositoryException re) {
187 // // throw new ArgeoException(
188 // // "Unable to update boolean value for node " + node, re);
189 // // }
190 // }
191 // }
192
193 // /**
194 // * Base privileged job that will be run asynchronously to perform the
195 // batch
196 // * update
197 // */
198 // private abstract class UpdateJob extends PrivilegedJob {
199 //
200 // private final UserAdminWrapper userAdminWrapper;
201 // private final List<User> usersToUpdate;
202 //
203 // protected abstract void doUpdate(User user);
204 //
205 // public UpdateJob(UserAdminWrapper userAdminWrapper,
206 // List<User> usersToUpdate) {
207 // super("Perform update");
208 // this.usersToUpdate = usersToUpdate;
209 // this.userAdminWrapper = userAdminWrapper;
210 // }
211 //
212 // @Override
213 // protected IStatus doRun(IProgressMonitor progressMonitor) {
214 // try {
215 // ArgeoMonitor monitor = new EclipseArgeoMonitor(progressMonitor);
216 // int total = usersToUpdate.size();
217 // monitor.beginTask("Performing change", total);
218 // userAdminWrapper.beginTransactionIfNeeded();
219 // for (User user : usersToUpdate) {
220 // doUpdate(user);
221 // monitor.worked(1);
222 // }
223 // userAdminWrapper.getUserTransaction().commit();
224 // } catch (Exception e) {
225 // throw new ArgeoException(
226 // "Cannot perform batch update on users", e);
227 // } finally {
228 // UserTransaction ut = userAdminWrapper.getUserTransaction();
229 // try {
230 // if (ut.getStatus() != javax.transaction.Status.STATUS_NO_TRANSACTION)
231 // ut.rollback();
232 // } catch (IllegalStateException | SecurityException
233 // | SystemException e) {
234 // log.error("Unable to rollback session in 'finally', "
235 // + "the system might be in a dirty state");
236 // e.printStackTrace();
237 // }
238 // }
239 // return Status.OK_STATUS;
240 // }
241 // }
242
243 // PAGES
244 /** Displays a combo box that enables user to choose which action to perform */
245 private class ChooseCommandWizardPage extends WizardPage {
246 private static final long serialVersionUID = -8069434295293996633L;
247 private Combo chooseCommandCmb;
248 private Button trueChk;
249 private Text valueTxt;
250 private Text pwdTxt;
251 private Text pwd2Txt;
252
253 public ChooseCommandWizardPage() {
254 super("Choose a command to run.");
255 setTitle("Choose a command to run.");
256 }
257
258 @Override
259 public void createControl(Composite parent) {
260 GridLayout gl = new GridLayout();
261 Composite container = new Composite(parent, SWT.NO_FOCUS);
262 container.setLayout(gl);
263
264 chooseCommandCmb = new Combo(container, SWT.READ_ONLY);
265 chooseCommandCmb.setLayoutData(EclipseUiUtils.fillWidth());
266 String[] values = commands.keySet().toArray(new String[0]);
267 chooseCommandCmb.setItems(values);
268
269 final Composite bottomPart = new Composite(container, SWT.NO_FOCUS);
270 bottomPart.setLayoutData(EclipseUiUtils.fillAll());
271 bottomPart.setLayout(EclipseUiUtils.noSpaceGridLayout());
272
273 chooseCommandCmb.addSelectionListener(new SelectionAdapter() {
274 private static final long serialVersionUID = 1L;
275
276 @Override
277 public void widgetSelected(SelectionEvent e) {
278 if (getCommand().equals(CMD_UPDATE_PASSWORD))
279 populatePasswordCmp(bottomPart);
280 else if (getCommand().equals(CMD_GROUP_MEMBERSHIP))
281 populateGroupCmp(bottomPart);
282 else
283 populateBooleanFlagCmp(bottomPart);
284 checkPageComplete();
285 bottomPart.layout(true, true);
286 }
287 });
288 setControl(container);
289 }
290
291 private void populateBooleanFlagCmp(Composite parent) {
292 EclipseUiUtils.clear(parent);
293 trueChk = new Button(parent, SWT.CHECK);
294 trueChk.setText("Do it. (It will to the contrary if unchecked)");
295 trueChk.setSelection(true);
296 trueChk.setLayoutData(new GridData(SWT.LEFT, SWT.TOP, false, false));
297 }
298
299 private void populatePasswordCmp(Composite parent) {
300 EclipseUiUtils.clear(parent);
301 Composite body = new Composite(parent, SWT.NO_FOCUS);
302
303 ModifyListener ml = new ModifyListener() {
304 private static final long serialVersionUID = -1558726363536729634L;
305
306 @Override
307 public void modifyText(ModifyEvent event) {
308 checkPageComplete();
309 }
310 };
311
312 body.setLayout(new GridLayout(2, false));
313 body.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
314 pwdTxt = EclipseUiUtils.createGridLP(body, "New password", ml);
315 pwd2Txt = EclipseUiUtils.createGridLP(body, "Repeat password", ml);
316 }
317
318 private void checkPageComplete() {
319 String errorMsg = null;
320 if (chooseCommandCmb.getSelectionIndex() < 0)
321 errorMsg = "Please select an action";
322 else if (CMD_UPDATE_PASSWORD.equals(getCommand())) {
323 if (EclipseUiUtils.isEmpty(pwdTxt.getText())
324 || pwdTxt.getText().length() < 4)
325 errorMsg = "Please enter a password that is at least 4 character long";
326 else if (!pwdTxt.getText().equals(pwd2Txt.getText()))
327 errorMsg = "Passwords are different";
328 }
329 if (EclipseUiUtils.notEmpty(errorMsg)) {
330 setMessage(errorMsg, WizardPage.ERROR);
331 setPageComplete(false);
332 } else {
333 setMessage("Page complete, you can proceed to user choice",
334 WizardPage.INFORMATION);
335 setPageComplete(true);
336 }
337
338 getContainer().updateButtons();
339 }
340
341 private void populateGroupCmp(Composite parent) {
342 EclipseUiUtils.clear(parent);
343 trueChk = new Button(parent, SWT.CHECK);
344 trueChk.setText("Add to group. (It will remove user(s) from the "
345 + "corresponding group if unchecked)");
346 trueChk.setSelection(true);
347 trueChk.setLayoutData(new GridData(SWT.LEFT, SWT.TOP, false, false));
348 }
349
350 protected String getCommand() {
351 return commands.get(chooseCommandCmb.getItem(chooseCommandCmb
352 .getSelectionIndex()));
353 }
354
355 protected String getCommandLbl() {
356 return chooseCommandCmb.getItem(chooseCommandCmb
357 .getSelectionIndex());
358 }
359
360 @SuppressWarnings("unused")
361 protected boolean getBoleanValue() {
362 // FIXME this is not consistent and will lead to errors.
363 if (ArgeoNames.ARGEO_ENABLED.equals(getCommand()))
364 return trueChk.getSelection();
365 else
366 return !trueChk.getSelection();
367 }
368
369 @SuppressWarnings("unused")
370 protected String getStringValue() {
371 String value = null;
372 if (valueTxt != null) {
373 value = valueTxt.getText();
374 if ("".equals(value.trim()))
375 value = null;
376 }
377 return value;
378 }
379
380 protected char[] getPwdValue() {
381 // We do not directly reset the password text fields: There is no
382 // need to over secure this process: setting a pwd to multi users
383 // at the same time is anyhow a bad practice and should be used only
384 // in test environment or for temporary access
385 if (pwdTxt == null || pwdTxt.isDisposed())
386 return null;
387 else
388 return pwdTxt.getText().toCharArray();
389 }
390 }
391
392 /**
393 * Displays a list of users with a check box to be able to choose some of
394 * them
395 */
396 private class ChooseUsersWizardPage extends WizardPage implements
397 IPageChangedListener {
398 private static final long serialVersionUID = 7651807402211214274L;
399 private ChooseUserTableViewer userTableCmp;
400
401 public ChooseUsersWizardPage() {
402 super("Choose Users");
403 setTitle("Select users who will be impacted");
404 }
405
406 @Override
407 public void createControl(Composite parent) {
408 Composite pageCmp = new Composite(parent, SWT.NONE);
409 pageCmp.setLayout(EclipseUiUtils.noSpaceGridLayout());
410
411 // Define the displayed columns
412 List<ColumnDefinition> columnDefs = new ArrayList<ColumnDefinition>();
413 columnDefs.add(new ColumnDefinition(new CommonNameLP(),
414 "Common Name", 150));
415 columnDefs.add(new ColumnDefinition(new MailLP(), "E-mail", 150));
416 columnDefs.add(new ColumnDefinition(new DomainNameLP(), "Domain",
417 200));
418
419 // Only show technical DN to admin
420 if (UiAdminUtils.isUserInRole(AuthConstants.ROLE_ADMIN))
421 columnDefs.add(new ColumnDefinition(new UserNameLP(),
422 "Distinguished Name", 300));
423
424 userTableCmp = new ChooseUserTableViewer(pageCmp, SWT.MULTI
425 | SWT.H_SCROLL | SWT.V_SCROLL);
426 userTableCmp.setLayoutData(EclipseUiUtils.fillAll());
427 userTableCmp.setColumnDefinitions(columnDefs);
428 userTableCmp.populate(true, true);
429 userTableCmp.refresh();
430
431 setControl(pageCmp);
432
433 // Add listener to update message when shown
434 final IWizardContainer wContainer = this.getContainer();
435 if (wContainer instanceof IPageChangeProvider) {
436 ((IPageChangeProvider) wContainer).addPageChangedListener(this);
437 }
438
439 }
440
441 @Override
442 public void pageChanged(PageChangedEvent event) {
443 if (event.getSelectedPage() == this) {
444 String msg = "Chosen batch action: "
445 + chooseCommandPage.getCommandLbl();
446 ((WizardPage) event.getSelectedPage()).setMessage(msg);
447 }
448 }
449
450 protected List<User> getSelectedUsers() {
451 return userTableCmp.getSelectedUsers();
452 }
453
454 private class ChooseUserTableViewer extends LdifUsersTable {
455 private static final long serialVersionUID = 5080437561015853124L;
456 private final String[] knownProps = { LdifName.uid.name(),
457 LdifName.dn.name(), LdifName.cn.name(),
458 LdifName.givenname.name(), LdifName.sn.name(),
459 LdifName.mail.name() };
460
461 public ChooseUserTableViewer(Composite parent, int style) {
462 super(parent, style);
463 }
464
465 @Override
466 protected List<User> listFilteredElements(String filter) {
467 Role[] roles;
468
469 try {
470 StringBuilder builder = new StringBuilder();
471
472 StringBuilder tmpBuilder = new StringBuilder();
473 if (UiAdminUtils.notNull(filter))
474 for (String prop : knownProps) {
475 tmpBuilder.append("(");
476 tmpBuilder.append(prop);
477 tmpBuilder.append("=*");
478 tmpBuilder.append(filter);
479 tmpBuilder.append("*)");
480 }
481 if (tmpBuilder.length() > 1) {
482 builder.append("(&(")
483 .append(LdifName.objectClass.name())
484 .append("=")
485 .append(LdifName.inetOrgPerson.name())
486 .append(")(|");
487 builder.append(tmpBuilder.toString());
488 builder.append("))");
489 } else
490 builder.append("(").append(LdifName.objectClass.name())
491 .append("=")
492 .append(LdifName.inetOrgPerson.name())
493 .append(")");
494 roles = userAdminWrapper.getUserAdmin().getRoles(
495 builder.toString());
496 } catch (InvalidSyntaxException e) {
497 throw new ArgeoException(
498 "Unable to get roles with filter: " + 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 (!UiAdminUtils.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 (UiAdminUtils.isUserInRole(AuthConstants.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 }