1 package org
.argeo
.cms
.ui
.workbench
.internal
.useradmin
.parts
;
3 import java
.util
.ArrayList
;
4 import java
.util
.HashMap
;
8 import org
.argeo
.api
.cms
.CmsConstants
;
9 import org
.argeo
.api
.cms
.CmsLog
;
10 import org
.argeo
.cms
.CmsException
;
11 import org
.argeo
.cms
.auth
.CurrentUser
;
12 import org
.argeo
.cms
.auth
.UserAdminUtils
;
13 import org
.argeo
.cms
.ui
.workbench
.internal
.useradmin
.UiAdminUtils
;
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
.eclipse
.ui
.ColumnDefinition
;
20 import org
.argeo
.eclipse
.ui
.EclipseUiUtils
;
21 import org
.argeo
.eclipse
.ui
.parts
.LdifUsersTable
;
22 import org
.argeo
.osgi
.transaction
.WorkTransaction
;
23 import org
.argeo
.util
.naming
.LdapAttrs
;
24 import org
.argeo
.util
.naming
.LdapObjs
;
25 import org
.eclipse
.jface
.dialogs
.IPageChangeProvider
;
26 import org
.eclipse
.jface
.dialogs
.IPageChangedListener
;
27 import org
.eclipse
.jface
.dialogs
.MessageDialog
;
28 import org
.eclipse
.jface
.dialogs
.PageChangedEvent
;
29 import org
.eclipse
.jface
.wizard
.IWizardContainer
;
30 import org
.eclipse
.jface
.wizard
.Wizard
;
31 import org
.eclipse
.jface
.wizard
.WizardPage
;
32 import org
.eclipse
.swt
.SWT
;
33 import org
.eclipse
.swt
.events
.ModifyEvent
;
34 import org
.eclipse
.swt
.events
.ModifyListener
;
35 import org
.eclipse
.swt
.events
.SelectionAdapter
;
36 import org
.eclipse
.swt
.events
.SelectionEvent
;
37 import org
.eclipse
.swt
.layout
.GridData
;
38 import org
.eclipse
.swt
.layout
.GridLayout
;
39 import org
.eclipse
.swt
.widgets
.Button
;
40 import org
.eclipse
.swt
.widgets
.Combo
;
41 import org
.eclipse
.swt
.widgets
.Composite
;
42 import org
.eclipse
.swt
.widgets
.Text
;
43 import org
.osgi
.framework
.InvalidSyntaxException
;
44 import org
.osgi
.service
.useradmin
.Role
;
45 import org
.osgi
.service
.useradmin
.User
;
46 import org
.osgi
.service
.useradmin
.UserAdminEvent
;
48 /** Wizard to update users */
49 public class UserBatchUpdateWizard
extends Wizard
{
51 private final static CmsLog log
= CmsLog
.getLog(UserBatchUpdateWizard
.class);
52 private UserAdminWrapper userAdminWrapper
;
55 private ChooseCommandWizardPage chooseCommandPage
;
56 private ChooseUsersWizardPage userListPage
;
57 private ValidateAndLaunchWizardPage validatePage
;
59 // Various implemented commands keys
60 private final static String CMD_UPDATE_PASSWORD
= "resetPassword";
61 private final static String CMD_UPDATE_EMAIL
= "resetEmail";
62 private final static String CMD_GROUP_MEMBERSHIP
= "groupMembership";
64 private final Map
<String
, String
> commands
= new HashMap
<String
, String
>() {
65 private static final long serialVersionUID
= 1L;
67 put("Reset password(s)", CMD_UPDATE_PASSWORD
);
68 put("Reset email(s)", CMD_UPDATE_EMAIL
);
69 // TODO implement role / group management
70 // put("Add/Remove from group", CMD_GROUP_MEMBERSHIP);
74 public UserBatchUpdateWizard(UserAdminWrapper userAdminWrapper
) {
75 this.userAdminWrapper
= userAdminWrapper
;
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
);
89 public boolean performFinish() {
92 WorkTransaction ut
= userAdminWrapper
.getUserTransaction();
93 if (!ut
.isNoTransactionStatus() && !MessageDialog
.openConfirm(getShell(), "Existing Transaction",
94 "A user transaction is already existing, " + "are you sure you want to proceed ?"))
97 // We cannot use jobs, user modifications are still meant to be done in
99 // UpdateJob job = null;
103 if (CMD_UPDATE_PASSWORD
.equals(chooseCommandPage
.getCommand())) {
104 char[] newValue
= chooseCommandPage
.getPwdValue();
105 if (newValue
== null)
106 throw new CmsException("Password cannot be null or an empty string");
107 ResetPassword job
= new ResetPassword(userAdminWrapper
, userListPage
.getSelectedUsers(), newValue
);
109 } else if (CMD_UPDATE_EMAIL
.equals(chooseCommandPage
.getCommand())) {
110 String newValue
= chooseCommandPage
.getEmailValue();
111 if (newValue
== null)
112 throw new CmsException("Password cannot be null or an empty string");
113 ResetEmail job
= new ResetEmail(userAdminWrapper
, userListPage
.getSelectedUsers(), newValue
);
119 public boolean canFinish() {
120 if (this.getContainer().getCurrentPage() == validatePage
)
125 private class ResetPassword
{
126 private char[] newPwd
;
127 private UserAdminWrapper userAdminWrapper
;
128 private List
<User
> usersToUpdate
;
130 public ResetPassword(UserAdminWrapper userAdminWrapper
, List
<User
> usersToUpdate
, char[] newPwd
) {
131 this.newPwd
= newPwd
;
132 this.usersToUpdate
= usersToUpdate
;
133 this.userAdminWrapper
= userAdminWrapper
;
136 @SuppressWarnings("unchecked")
137 protected void doUpdate() {
138 userAdminWrapper
.beginTransactionIfNeeded();
140 for (User user
: usersToUpdate
) {
141 // the char array is emptied after being used.
142 user
.getCredentials().put(null, newPwd
.clone());
144 userAdminWrapper
.commitOrNotifyTransactionStateChange();
145 } catch (Exception e
) {
146 throw new CmsException("Cannot perform batch update on users", e
);
148 WorkTransaction ut
= userAdminWrapper
.getUserTransaction();
149 if (!ut
.isNoTransactionStatus())
155 private class ResetEmail
{
156 private String newEmail
;
157 private UserAdminWrapper userAdminWrapper
;
158 private List
<User
> usersToUpdate
;
160 public ResetEmail(UserAdminWrapper userAdminWrapper
, List
<User
> usersToUpdate
, String newEmail
) {
161 this.newEmail
= newEmail
;
162 this.usersToUpdate
= usersToUpdate
;
163 this.userAdminWrapper
= userAdminWrapper
;
166 @SuppressWarnings("unchecked")
167 protected void doUpdate() {
168 userAdminWrapper
.beginTransactionIfNeeded();
170 for (User user
: usersToUpdate
) {
171 // the char array is emptied after being used.
172 user
.getProperties().put(LdapAttrs
.mail
.name(), newEmail
);
175 userAdminWrapper
.commitOrNotifyTransactionStateChange();
176 if (!usersToUpdate
.isEmpty())
177 userAdminWrapper
.notifyListeners(
178 new UserAdminEvent(null, UserAdminEvent
.ROLE_CHANGED
, usersToUpdate
.get(0)));
179 } catch (Exception e
) {
180 throw new CmsException("Cannot perform batch update on users", e
);
182 WorkTransaction ut
= userAdminWrapper
.getUserTransaction();
183 if (!ut
.isNoTransactionStatus())
189 // @SuppressWarnings("unused")
190 // private class AddToGroup extends UpdateJob {
191 // private String groupID;
192 // private Session session;
194 // public AddToGroup(Session session, List<Node> nodesToUpdate,
196 // super(session, nodesToUpdate);
197 // this.session = session;
198 // this.groupID = groupID;
201 // protected void doUpdate(Node node) {
202 // log.info("Add/Remove to group actions are not yet implemented");
203 // // TODO implement this
205 // // throw new CmsException("Not yet implemented");
206 // // } catch (RepositoryException re) {
207 // // throw new CmsException(
208 // // "Unable to update boolean value for node " + node, re);
214 // * Base privileged job that will be run asynchronously to perform the
218 // private abstract class UpdateJob extends PrivilegedJob {
220 // private final UserAdminWrapper userAdminWrapper;
221 // private final List<User> usersToUpdate;
223 // protected abstract void doUpdate(User user);
225 // public UpdateJob(UserAdminWrapper userAdminWrapper,
226 // List<User> usersToUpdate) {
227 // super("Perform update");
228 // this.usersToUpdate = usersToUpdate;
229 // this.userAdminWrapper = userAdminWrapper;
233 // protected IStatus doRun(IProgressMonitor progressMonitor) {
235 // JcrMonitor monitor = new EclipseJcrMonitor(progressMonitor);
236 // int total = usersToUpdate.size();
237 // monitor.beginTask("Performing change", total);
238 // userAdminWrapper.beginTransactionIfNeeded();
239 // for (User user : usersToUpdate) {
241 // monitor.worked(1);
243 // userAdminWrapper.getUserTransaction().commit();
244 // } catch (Exception e) {
245 // throw new CmsException(
246 // "Cannot perform batch update on users", e);
248 // UserTransaction ut = userAdminWrapper.getUserTransaction();
250 // if (ut.getStatus() != javax.transaction.Status.STATUS_NO_TRANSACTION)
252 // } catch (IllegalStateException | SecurityException
253 // | SystemException e) {
254 // log.error("Unable to rollback session in 'finally', "
255 // + "the system might be in a dirty state");
256 // e.printStackTrace();
259 // return Status.OK_STATUS;
265 * Displays a combo box that enables user to choose which action to perform
267 private class ChooseCommandWizardPage
extends WizardPage
{
268 private static final long serialVersionUID
= -8069434295293996633L;
269 private Combo chooseCommandCmb
;
270 private Button trueChk
;
271 private Text valueTxt
;
273 private Text pwd2Txt
;
275 public ChooseCommandWizardPage() {
276 super("Choose a command to run.");
277 setTitle("Choose a command to run.");
281 public void createControl(Composite parent
) {
282 GridLayout gl
= new GridLayout();
283 Composite container
= new Composite(parent
, SWT
.NO_FOCUS
);
284 container
.setLayout(gl
);
286 chooseCommandCmb
= new Combo(container
, SWT
.READ_ONLY
);
287 chooseCommandCmb
.setLayoutData(EclipseUiUtils
.fillWidth());
288 String
[] values
= commands
.keySet().toArray(new String
[0]);
289 chooseCommandCmb
.setItems(values
);
291 final Composite bottomPart
= new Composite(container
, SWT
.NO_FOCUS
);
292 bottomPart
.setLayoutData(EclipseUiUtils
.fillAll());
293 bottomPart
.setLayout(EclipseUiUtils
.noSpaceGridLayout());
295 chooseCommandCmb
.addSelectionListener(new SelectionAdapter() {
296 private static final long serialVersionUID
= 1L;
299 public void widgetSelected(SelectionEvent e
) {
300 if (getCommand().equals(CMD_UPDATE_PASSWORD
))
301 populatePasswordCmp(bottomPart
);
302 else if (getCommand().equals(CMD_UPDATE_EMAIL
))
303 populateEmailCmp(bottomPart
);
304 else if (getCommand().equals(CMD_GROUP_MEMBERSHIP
))
305 populateGroupCmp(bottomPart
);
307 populateBooleanFlagCmp(bottomPart
);
309 bottomPart
.layout(true, true);
312 setControl(container
);
315 private void populateBooleanFlagCmp(Composite parent
) {
316 EclipseUiUtils
.clear(parent
);
317 trueChk
= new Button(parent
, SWT
.CHECK
);
318 trueChk
.setText("Do it. (It will to the contrary if unchecked)");
319 trueChk
.setSelection(true);
320 trueChk
.setLayoutData(new GridData(SWT
.LEFT
, SWT
.TOP
, false, false));
323 private void populatePasswordCmp(Composite parent
) {
324 EclipseUiUtils
.clear(parent
);
325 Composite body
= new Composite(parent
, SWT
.NO_FOCUS
);
327 ModifyListener ml
= new ModifyListener() {
328 private static final long serialVersionUID
= -1558726363536729634L;
331 public void modifyText(ModifyEvent event
) {
336 body
.setLayout(new GridLayout(2, false));
337 body
.setLayoutData(new GridData(SWT
.FILL
, SWT
.FILL
, true, true));
338 pwdTxt
= EclipseUiUtils
.createGridLP(body
, "New password", ml
);
339 pwd2Txt
= EclipseUiUtils
.createGridLP(body
, "Repeat password", ml
);
342 private void populateEmailCmp(Composite parent
) {
343 EclipseUiUtils
.clear(parent
);
344 Composite body
= new Composite(parent
, SWT
.NO_FOCUS
);
346 ModifyListener ml
= new ModifyListener() {
347 private static final long serialVersionUID
= 2147704227294268317L;
350 public void modifyText(ModifyEvent event
) {
355 body
.setLayout(new GridLayout(2, false));
356 body
.setLayoutData(new GridData(SWT
.FILL
, SWT
.FILL
, true, true));
357 valueTxt
= EclipseUiUtils
.createGridLT(body
, "New e-mail", ml
);
360 private void checkPageComplete() {
361 String errorMsg
= null;
362 if (chooseCommandCmb
.getSelectionIndex() < 0)
363 errorMsg
= "Please select an action";
364 else if (CMD_UPDATE_EMAIL
.equals(getCommand())) {
365 if (!valueTxt
.getText().matches(UiAdminUtils
.EMAIL_PATTERN
))
366 errorMsg
= "Not a valid e-mail address";
367 } else if (CMD_UPDATE_PASSWORD
.equals(getCommand())) {
368 if (EclipseUiUtils
.isEmpty(pwdTxt
.getText()) || pwdTxt
.getText().length() < 4)
369 errorMsg
= "Please enter a password that is at least 4 character long";
370 else if (!pwdTxt
.getText().equals(pwd2Txt
.getText()))
371 errorMsg
= "Passwords are different";
373 if (EclipseUiUtils
.notEmpty(errorMsg
)) {
374 setMessage(errorMsg
, WizardPage
.ERROR
);
375 setPageComplete(false);
377 setMessage("Page complete, you can proceed to user choice", WizardPage
.INFORMATION
);
378 setPageComplete(true);
381 getContainer().updateButtons();
384 private void populateGroupCmp(Composite parent
) {
385 EclipseUiUtils
.clear(parent
);
386 trueChk
= new Button(parent
, SWT
.CHECK
);
387 trueChk
.setText("Add to group. (It will remove user(s) from the " + "corresponding group if unchecked)");
388 trueChk
.setSelection(true);
389 trueChk
.setLayoutData(new GridData(SWT
.LEFT
, SWT
.TOP
, false, false));
392 protected String
getCommand() {
393 return commands
.get(chooseCommandCmb
.getItem(chooseCommandCmb
.getSelectionIndex()));
396 protected String
getCommandLbl() {
397 return chooseCommandCmb
.getItem(chooseCommandCmb
.getSelectionIndex());
400 @SuppressWarnings("unused")
401 protected boolean getBoleanValue() {
402 // FIXME this is not consistent and will lead to errors.
403 if ("argeo:enabled".equals(getCommand()))
404 return trueChk
.getSelection();
406 return !trueChk
.getSelection();
409 @SuppressWarnings("unused")
410 protected String
getStringValue() {
412 if (valueTxt
!= null) {
413 value
= valueTxt
.getText();
414 if ("".equals(value
.trim()))
420 protected char[] getPwdValue() {
421 // We do not directly reset the password text fields: There is no
422 // need to over secure this process: setting a pwd to multi users
423 // at the same time is anyhow a bad practice and should be used only
424 // in test environment or for temporary access
425 if (pwdTxt
== null || pwdTxt
.isDisposed())
428 return pwdTxt
.getText().toCharArray();
431 protected String
getEmailValue() {
432 // We do not directly reset the password text fields: There is no
433 // need to over secure this process: setting a pwd to multi users
434 // at the same time is anyhow a bad practice and should be used only
435 // in test environment or for temporary access
436 if (valueTxt
== null || valueTxt
.isDisposed())
439 return valueTxt
.getText();
444 * Displays a list of users with a check box to be able to choose some of them
446 private class ChooseUsersWizardPage
extends WizardPage
implements IPageChangedListener
{
447 private static final long serialVersionUID
= 7651807402211214274L;
448 private ChooseUserTableViewer userTableCmp
;
450 public ChooseUsersWizardPage() {
451 super("Choose Users");
452 setTitle("Select users who will be impacted");
456 public void createControl(Composite parent
) {
457 Composite pageCmp
= new Composite(parent
, SWT
.NONE
);
458 pageCmp
.setLayout(EclipseUiUtils
.noSpaceGridLayout());
460 // Define the displayed columns
461 List
<ColumnDefinition
> columnDefs
= new ArrayList
<ColumnDefinition
>();
462 columnDefs
.add(new ColumnDefinition(new CommonNameLP(), "Common Name", 150));
463 columnDefs
.add(new ColumnDefinition(new MailLP(), "E-mail", 150));
464 columnDefs
.add(new ColumnDefinition(new DomainNameLP(), "Domain", 200));
466 // Only show technical DN to admin
467 if (CurrentUser
.isInRole(CmsConstants
.ROLE_ADMIN
))
468 columnDefs
.add(new ColumnDefinition(new UserNameLP(), "Distinguished Name", 300));
470 userTableCmp
= new ChooseUserTableViewer(pageCmp
, SWT
.MULTI
| SWT
.H_SCROLL
| SWT
.V_SCROLL
);
471 userTableCmp
.setLayoutData(EclipseUiUtils
.fillAll());
472 userTableCmp
.setColumnDefinitions(columnDefs
);
473 userTableCmp
.populate(true, true);
474 userTableCmp
.refresh();
478 // Add listener to update message when shown
479 final IWizardContainer wContainer
= this.getContainer();
480 if (wContainer
instanceof IPageChangeProvider
) {
481 ((IPageChangeProvider
) wContainer
).addPageChangedListener(this);
487 public void pageChanged(PageChangedEvent event
) {
488 if (event
.getSelectedPage() == this) {
489 String msg
= "Chosen batch action: " + chooseCommandPage
.getCommandLbl();
490 ((WizardPage
) event
.getSelectedPage()).setMessage(msg
);
494 protected List
<User
> getSelectedUsers() {
495 return userTableCmp
.getSelectedUsers();
498 private class ChooseUserTableViewer
extends LdifUsersTable
{
499 private static final long serialVersionUID
= 5080437561015853124L;
500 private final String
[] knownProps
= { LdapAttrs
.uid
.name(), LdapAttrs
.DN
, LdapAttrs
.cn
.name(),
501 LdapAttrs
.givenName
.name(), LdapAttrs
.sn
.name(), LdapAttrs
.mail
.name() };
503 public ChooseUserTableViewer(Composite parent
, int style
) {
504 super(parent
, style
);
508 protected List
<User
> listFilteredElements(String filter
) {
512 StringBuilder builder
= new StringBuilder();
514 StringBuilder tmpBuilder
= new StringBuilder();
515 if (EclipseUiUtils
.notEmpty(filter
))
516 for (String prop
: knownProps
) {
517 tmpBuilder
.append("(");
518 tmpBuilder
.append(prop
);
519 tmpBuilder
.append("=*");
520 tmpBuilder
.append(filter
);
521 tmpBuilder
.append("*)");
523 if (tmpBuilder
.length() > 1) {
524 builder
.append("(&(").append(LdapAttrs
.objectClass
.name()).append("=")
525 .append(LdapObjs
.inetOrgPerson
.name()).append(")(|");
526 builder
.append(tmpBuilder
.toString());
527 builder
.append("))");
529 builder
.append("(").append(LdapAttrs
.objectClass
.name()).append("=")
530 .append(LdapObjs
.inetOrgPerson
.name()).append(")");
531 roles
= userAdminWrapper
.getUserAdmin().getRoles(builder
.toString());
532 } catch (InvalidSyntaxException e
) {
533 throw new CmsException("Unable to get roles with filter: " + filter
, e
);
535 List
<User
> users
= new ArrayList
<User
>();
536 for (Role role
: roles
)
537 // Prevent current logged in user to perform batch on
539 if (!UserAdminUtils
.isCurrentUser((User
) role
))
540 users
.add((User
) role
);
546 /** Summary of input data before launching the process */
547 private class ValidateAndLaunchWizardPage
extends WizardPage
implements IPageChangedListener
{
548 private static final long serialVersionUID
= 7098918351451743853L;
549 private ChosenUsersTableViewer userTableCmp
;
551 public ValidateAndLaunchWizardPage() {
552 super("Validate and launch");
553 setTitle("Validate and launch");
557 public void createControl(Composite parent
) {
558 Composite pageCmp
= new Composite(parent
, SWT
.NO_FOCUS
);
559 pageCmp
.setLayout(EclipseUiUtils
.noSpaceGridLayout());
561 List
<ColumnDefinition
> columnDefs
= new ArrayList
<ColumnDefinition
>();
562 columnDefs
.add(new ColumnDefinition(new CommonNameLP(), "Common Name", 150));
563 columnDefs
.add(new ColumnDefinition(new MailLP(), "E-mail", 150));
564 columnDefs
.add(new ColumnDefinition(new DomainNameLP(), "Domain", 200));
565 // Only show technical DN to admin
566 if (CurrentUser
.isInRole(CmsConstants
.ROLE_ADMIN
))
567 columnDefs
.add(new ColumnDefinition(new UserNameLP(), "Distinguished Name", 300));
568 userTableCmp
= new ChosenUsersTableViewer(pageCmp
, SWT
.MULTI
| SWT
.H_SCROLL
| SWT
.V_SCROLL
);
569 userTableCmp
.setLayoutData(EclipseUiUtils
.fillAll());
570 userTableCmp
.setColumnDefinitions(columnDefs
);
571 userTableCmp
.populate(false, false);
572 userTableCmp
.refresh();
574 // Add listener to update message when shown
575 final IWizardContainer wContainer
= this.getContainer();
576 if (wContainer
instanceof IPageChangeProvider
) {
577 ((IPageChangeProvider
) wContainer
).addPageChangedListener(this);
582 public void pageChanged(PageChangedEvent event
) {
583 if (event
.getSelectedPage() == this) {
584 @SuppressWarnings({ "unchecked", "rawtypes" })
585 Object
[] values
= ((ArrayList
) userListPage
.getSelectedUsers())
586 .toArray(new Object
[userListPage
.getSelectedUsers().size()]);
587 userTableCmp
.getTableViewer().setInput(values
);
588 String msg
= "Following batch action: [" + chooseCommandPage
.getCommandLbl()
589 + "] will be perfomed on the users listed below.\n";
590 // + "Are you sure you want to proceed?";
595 private class ChosenUsersTableViewer
extends LdifUsersTable
{
596 private static final long serialVersionUID
= 7814764735794270541L;
598 public ChosenUsersTableViewer(Composite parent
, int style
) {
599 super(parent
, style
);
603 protected List
<User
> listFilteredElements(String filter
) {
604 return userListPage
.getSelectedUsers();