1 package org
.argeo
.cms
.ui
.workbench
.internal
.useradmin
.parts
;
3 import java
.util
.ArrayList
;
4 import java
.util
.HashMap
;
8 import javax
.transaction
.SystemException
;
9 import javax
.transaction
.UserTransaction
;
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
.node
.ArgeoNames
;
24 import org
.argeo
.node
.NodeConstants
;
25 import org
.argeo
.osgi
.useradmin
.LdifName
;
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
;
48 /** Wizard to update users */
49 public class UserBatchUpdateWizard
extends Wizard
{
51 private final static Log log
= LogFactory
52 .getLog(UserBatchUpdateWizard
.class);
53 private UserAdminWrapper userAdminWrapper
;
56 private ChooseCommandWizardPage chooseCommandPage
;
57 private ChooseUsersWizardPage userListPage
;
58 private ValidateAndLaunchWizardPage validatePage
;
60 // Various implemented commands keys
61 private final static String CMD_UPDATE_PASSWORD
= "resetPassword";
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 // TODO implement role / group management
69 // put("Add/Remove from group", CMD_GROUP_MEMBERSHIP);
73 public UserBatchUpdateWizard(UserAdminWrapper userAdminWrapper
) {
74 this.userAdminWrapper
= userAdminWrapper
;
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
);
88 public boolean performFinish() {
91 UserTransaction ut
= userAdminWrapper
.getUserTransaction();
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 ?"))
99 } catch (SystemException e
) {
100 throw new CmsException("Cannot get user transaction state "
101 + "before user batch update", e
);
104 // We cannot use jobs, user modifications are still meant to be done in
106 // UpdateJob job = null;
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
);
122 public boolean canFinish() {
123 if (this.getContainer().getCurrentPage() == validatePage
)
128 private class ResetPassword
{
129 private char[] newPwd
;
130 private UserAdminWrapper userAdminWrapper
;
131 private List
<User
> usersToUpdate
;
133 public ResetPassword(UserAdminWrapper userAdminWrapper
,
134 List
<User
> usersToUpdate
, char[] newPwd
) {
135 this.newPwd
= newPwd
;
136 this.usersToUpdate
= usersToUpdate
;
137 this.userAdminWrapper
= userAdminWrapper
;
140 @SuppressWarnings("unchecked")
141 protected void doUpdate() {
142 userAdminWrapper
.beginTransactionIfNeeded();
144 for (User user
: usersToUpdate
) {
145 // the char array is emptied after being used.
146 user
.getCredentials().put(null, newPwd
.clone());
148 userAdminWrapper
.commitOrNotifyTransactionStateChange();
149 } catch (Exception e
) {
150 throw new CmsException("Cannot perform batch update on users",
153 UserTransaction ut
= userAdminWrapper
.getUserTransaction();
155 if (ut
.getStatus() != javax
.transaction
.Status
.STATUS_NO_TRANSACTION
)
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");
167 // @SuppressWarnings("unused")
168 // private class AddToGroup extends UpdateJob {
169 // private String groupID;
170 // private Session session;
172 // public AddToGroup(Session session, List<Node> nodesToUpdate,
174 // super(session, nodesToUpdate);
175 // this.session = session;
176 // this.groupID = groupID;
179 // protected void doUpdate(Node node) {
180 // log.info("Add/Remove to group actions are not yet implemented");
181 // // TODO implement this
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);
192 // * Base privileged job that will be run asynchronously to perform the
196 // private abstract class UpdateJob extends PrivilegedJob {
198 // private final UserAdminWrapper userAdminWrapper;
199 // private final List<User> usersToUpdate;
201 // protected abstract void doUpdate(User user);
203 // public UpdateJob(UserAdminWrapper userAdminWrapper,
204 // List<User> usersToUpdate) {
205 // super("Perform update");
206 // this.usersToUpdate = usersToUpdate;
207 // this.userAdminWrapper = userAdminWrapper;
211 // protected IStatus doRun(IProgressMonitor progressMonitor) {
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) {
219 // monitor.worked(1);
221 // userAdminWrapper.getUserTransaction().commit();
222 // } catch (Exception e) {
223 // throw new CmsException(
224 // "Cannot perform batch update on users", e);
226 // UserTransaction ut = userAdminWrapper.getUserTransaction();
228 // if (ut.getStatus() != javax.transaction.Status.STATUS_NO_TRANSACTION)
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();
237 // return Status.OK_STATUS;
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
;
249 private Text pwd2Txt
;
251 public ChooseCommandWizardPage() {
252 super("Choose a command to run.");
253 setTitle("Choose a command to run.");
257 public void createControl(Composite parent
) {
258 GridLayout gl
= new GridLayout();
259 Composite container
= new Composite(parent
, SWT
.NO_FOCUS
);
260 container
.setLayout(gl
);
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
);
267 final Composite bottomPart
= new Composite(container
, SWT
.NO_FOCUS
);
268 bottomPart
.setLayoutData(EclipseUiUtils
.fillAll());
269 bottomPart
.setLayout(EclipseUiUtils
.noSpaceGridLayout());
271 chooseCommandCmb
.addSelectionListener(new SelectionAdapter() {
272 private static final long serialVersionUID
= 1L;
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
);
281 populateBooleanFlagCmp(bottomPart
);
283 bottomPart
.layout(true, true);
286 setControl(container
);
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));
297 private void populatePasswordCmp(Composite parent
) {
298 EclipseUiUtils
.clear(parent
);
299 Composite body
= new Composite(parent
, SWT
.NO_FOCUS
);
301 ModifyListener ml
= new ModifyListener() {
302 private static final long serialVersionUID
= -1558726363536729634L;
305 public void modifyText(ModifyEvent event
) {
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
);
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";
327 if (EclipseUiUtils
.notEmpty(errorMsg
)) {
328 setMessage(errorMsg
, WizardPage
.ERROR
);
329 setPageComplete(false);
331 setMessage("Page complete, you can proceed to user choice",
332 WizardPage
.INFORMATION
);
333 setPageComplete(true);
336 getContainer().updateButtons();
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));
348 protected String
getCommand() {
349 return commands
.get(chooseCommandCmb
.getItem(chooseCommandCmb
350 .getSelectionIndex()));
353 protected String
getCommandLbl() {
354 return chooseCommandCmb
.getItem(chooseCommandCmb
355 .getSelectionIndex());
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();
364 return !trueChk
.getSelection();
367 @SuppressWarnings("unused")
368 protected String
getStringValue() {
370 if (valueTxt
!= null) {
371 value
= valueTxt
.getText();
372 if ("".equals(value
.trim()))
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())
386 return pwdTxt
.getText().toCharArray();
391 * Displays a list of users with a check box to be able to choose some of
394 private class ChooseUsersWizardPage
extends WizardPage
implements
395 IPageChangedListener
{
396 private static final long serialVersionUID
= 7651807402211214274L;
397 private ChooseUserTableViewer userTableCmp
;
399 public ChooseUsersWizardPage() {
400 super("Choose Users");
401 setTitle("Select users who will be impacted");
405 public void createControl(Composite parent
) {
406 Composite pageCmp
= new Composite(parent
, SWT
.NONE
);
407 pageCmp
.setLayout(EclipseUiUtils
.noSpaceGridLayout());
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",
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));
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();
431 // Add listener to update message when shown
432 final IWizardContainer wContainer
= this.getContainer();
433 if (wContainer
instanceof IPageChangeProvider
) {
434 ((IPageChangeProvider
) wContainer
).addPageChangedListener(this);
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
);
448 protected List
<User
> getSelectedUsers() {
449 return userTableCmp
.getSelectedUsers();
452 private class ChooseUserTableViewer
extends LdifUsersTable
{
453 private static final long serialVersionUID
= 5080437561015853124L;
454 private final String
[] knownProps
= { LdifName
.uid
.name(),
455 LdifName
.dn
.name(), LdifName
.cn
.name(),
456 LdifName
.givenName
.name(), LdifName
.sn
.name(),
457 LdifName
.mail
.name() };
459 public ChooseUserTableViewer(Composite parent
, int style
) {
460 super(parent
, style
);
464 protected List
<User
> listFilteredElements(String filter
) {
468 StringBuilder builder
= new StringBuilder();
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("*)");
479 if (tmpBuilder
.length() > 1) {
480 builder
.append("(&(")
481 .append(LdifName
.objectClass
.name())
483 .append(LdifName
.inetOrgPerson
.name())
485 builder
.append(tmpBuilder
.toString());
486 builder
.append("))");
488 builder
.append("(").append(LdifName
.objectClass
.name())
490 .append(LdifName
.inetOrgPerson
.name())
492 roles
= userAdminWrapper
.getUserAdmin().getRoles(
494 } catch (InvalidSyntaxException e
) {
495 throw new CmsException("Unable to get roles with filter: "
498 List
<User
> users
= new ArrayList
<User
>();
499 for (Role role
: roles
)
500 // Prevent current logged in user to perform batch on
502 if (!UserAdminUtils
.isCurrentUser((User
) role
))
503 users
.add((User
) role
);
509 /** Summary of input data before launching the process */
510 private class ValidateAndLaunchWizardPage
extends WizardPage
implements
511 IPageChangedListener
{
512 private static final long serialVersionUID
= 7098918351451743853L;
513 private ChosenUsersTableViewer userTableCmp
;
515 public ValidateAndLaunchWizardPage() {
516 super("Validate and launch");
517 setTitle("Validate and launch");
521 public void createControl(Composite parent
) {
522 Composite pageCmp
= new Composite(parent
, SWT
.NO_FOCUS
);
523 pageCmp
.setLayout(EclipseUiUtils
.noSpaceGridLayout());
525 List
<ColumnDefinition
> columnDefs
= new ArrayList
<ColumnDefinition
>();
526 columnDefs
.add(new ColumnDefinition(new CommonNameLP(),
527 "Common Name", 150));
528 columnDefs
.add(new ColumnDefinition(new MailLP(), "E-mail", 150));
529 columnDefs
.add(new ColumnDefinition(new DomainNameLP(), "Domain",
531 // Only show technical DN to admin
532 if (UserAdminUtils
.isUserInRole(NodeConstants
.ROLE_ADMIN
))
533 columnDefs
.add(new ColumnDefinition(new UserNameLP(),
534 "Distinguished Name", 300));
535 userTableCmp
= new ChosenUsersTableViewer(pageCmp
, SWT
.MULTI
536 | SWT
.H_SCROLL
| SWT
.V_SCROLL
);
537 userTableCmp
.setLayoutData(EclipseUiUtils
.fillAll());
538 userTableCmp
.setColumnDefinitions(columnDefs
);
539 userTableCmp
.populate(false, false);
540 userTableCmp
.refresh();
542 // Add listener to update message when shown
543 final IWizardContainer wContainer
= this.getContainer();
544 if (wContainer
instanceof IPageChangeProvider
) {
545 ((IPageChangeProvider
) wContainer
).addPageChangedListener(this);
550 public void pageChanged(PageChangedEvent event
) {
551 if (event
.getSelectedPage() == this) {
552 @SuppressWarnings({ "unchecked", "rawtypes" })
553 Object
[] values
= ((ArrayList
) userListPage
.getSelectedUsers())
554 .toArray(new Object
[userListPage
.getSelectedUsers()
556 userTableCmp
.getTableViewer().setInput(values
);
557 String msg
= "Following batch action: ["
558 + chooseCommandPage
.getCommandLbl()
559 + "] will be perfomed on the users listed below.\n";
560 // + "Are you sure you want to proceed?";
565 private class ChosenUsersTableViewer
extends LdifUsersTable
{
566 private static final long serialVersionUID
= 7814764735794270541L;
568 public ChosenUsersTableViewer(Composite parent
, int style
) {
569 super(parent
, style
);
573 protected List
<User
> listFilteredElements(String filter
) {
574 return userListPage
.getSelectedUsers();