1 package org
.argeo
.security
.ui
.admin
.internal
.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
.auth
.AuthConstants
;
15 import org
.argeo
.cms
.util
.useradmin
.UserAdminUtils
;
16 import org
.argeo
.eclipse
.ui
.ColumnDefinition
;
17 import org
.argeo
.eclipse
.ui
.EclipseUiUtils
;
18 import org
.argeo
.eclipse
.ui
.parts
.LdifUsersTable
;
19 import org
.argeo
.jcr
.ArgeoNames
;
20 import org
.argeo
.osgi
.useradmin
.LdifName
;
21 import org
.argeo
.security
.ui
.admin
.internal
.UiAdminUtils
;
22 import org
.argeo
.security
.ui
.admin
.internal
.UserAdminWrapper
;
23 import org
.argeo
.security
.ui
.admin
.internal
.providers
.CommonNameLP
;
24 import org
.argeo
.security
.ui
.admin
.internal
.providers
.DomainNameLP
;
25 import org
.argeo
.security
.ui
.admin
.internal
.providers
.MailLP
;
26 import org
.argeo
.security
.ui
.admin
.internal
.providers
.UserNameLP
;
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
;
49 /** Wizard to update users */
50 public class UserBatchUpdateWizard
extends Wizard
{
52 private final static Log log
= LogFactory
53 .getLog(UserBatchUpdateWizard
.class);
54 private UserAdminWrapper userAdminWrapper
;
57 private ChooseCommandWizardPage chooseCommandPage
;
58 private ChooseUsersWizardPage userListPage
;
59 private ValidateAndLaunchWizardPage validatePage
;
61 // Various implemented commands keys
62 private final static String CMD_UPDATE_PASSWORD
= "resetPassword";
63 private final static String CMD_GROUP_MEMBERSHIP
= "groupMembership";
65 private final Map
<String
, String
> commands
= new HashMap
<String
, String
>() {
66 private static final long serialVersionUID
= 1L;
68 put("Reset password(s)", CMD_UPDATE_PASSWORD
);
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 UserTransaction ut
= userAdminWrapper
.getUserTransaction();
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 ?"))
100 } catch (SystemException e
) {
101 throw new CmsException("Cannot get user transaction state "
102 + "before user batch update", e
);
105 // We cannot use jobs, user modifications are still meant to be done in
107 // UpdateJob job = null;
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
);
123 public boolean canFinish() {
124 if (this.getContainer().getCurrentPage() == validatePage
)
129 private class ResetPassword
{
130 private char[] newPwd
;
131 private UserAdminWrapper userAdminWrapper
;
132 private List
<User
> usersToUpdate
;
134 public ResetPassword(UserAdminWrapper userAdminWrapper
,
135 List
<User
> usersToUpdate
, char[] newPwd
) {
136 this.newPwd
= newPwd
;
137 this.usersToUpdate
= usersToUpdate
;
138 this.userAdminWrapper
= userAdminWrapper
;
141 @SuppressWarnings("unchecked")
142 protected void doUpdate() {
143 UserTransaction userTransaction
= userAdminWrapper
144 .beginTransactionIfNeeded();
146 for (User user
: usersToUpdate
) {
147 // the char array is emptied after being used.
148 user
.getCredentials().put(null, newPwd
.clone());
150 userTransaction
.commit();
151 UiAdminUtils
.notifyTransactionStateChange(userTransaction
);
152 } catch (Exception e
) {
153 throw new CmsException(
154 "Cannot perform batch update on users", e
);
156 UserTransaction ut
= userAdminWrapper
.getUserTransaction();
158 if (ut
.getStatus() != javax
.transaction
.Status
.STATUS_NO_TRANSACTION
)
160 } catch (IllegalStateException
| SecurityException
161 | SystemException e
) {
162 log
.error("Unable to rollback session in 'finally', "
163 + "the system might be in a dirty state");
170 // @SuppressWarnings("unused")
171 // private class AddToGroup extends UpdateJob {
172 // private String groupID;
173 // private Session session;
175 // public AddToGroup(Session session, List<Node> nodesToUpdate,
177 // super(session, nodesToUpdate);
178 // this.session = session;
179 // this.groupID = groupID;
182 // protected void doUpdate(Node node) {
183 // log.info("Add/Remove to group actions are not yet implemented");
184 // // TODO implement this
186 // // throw new CmsException("Not yet implemented");
187 // // } catch (RepositoryException re) {
188 // // throw new CmsException(
189 // // "Unable to update boolean value for node " + node, re);
195 // * Base privileged job that will be run asynchronously to perform the
199 // private abstract class UpdateJob extends PrivilegedJob {
201 // private final UserAdminWrapper userAdminWrapper;
202 // private final List<User> usersToUpdate;
204 // protected abstract void doUpdate(User user);
206 // public UpdateJob(UserAdminWrapper userAdminWrapper,
207 // List<User> usersToUpdate) {
208 // super("Perform update");
209 // this.usersToUpdate = usersToUpdate;
210 // this.userAdminWrapper = userAdminWrapper;
214 // protected IStatus doRun(IProgressMonitor progressMonitor) {
216 // ArgeoMonitor monitor = new EclipseArgeoMonitor(progressMonitor);
217 // int total = usersToUpdate.size();
218 // monitor.beginTask("Performing change", total);
219 // userAdminWrapper.beginTransactionIfNeeded();
220 // for (User user : usersToUpdate) {
222 // monitor.worked(1);
224 // userAdminWrapper.getUserTransaction().commit();
225 // } catch (Exception e) {
226 // throw new CmsException(
227 // "Cannot perform batch update on users", e);
229 // UserTransaction ut = userAdminWrapper.getUserTransaction();
231 // if (ut.getStatus() != javax.transaction.Status.STATUS_NO_TRANSACTION)
233 // } catch (IllegalStateException | SecurityException
234 // | SystemException e) {
235 // log.error("Unable to rollback session in 'finally', "
236 // + "the system might be in a dirty state");
237 // e.printStackTrace();
240 // return Status.OK_STATUS;
245 /** Displays a combo box that enables user to choose which action to perform */
246 private class ChooseCommandWizardPage
extends WizardPage
{
247 private static final long serialVersionUID
= -8069434295293996633L;
248 private Combo chooseCommandCmb
;
249 private Button trueChk
;
250 private Text valueTxt
;
252 private Text pwd2Txt
;
254 public ChooseCommandWizardPage() {
255 super("Choose a command to run.");
256 setTitle("Choose a command to run.");
260 public void createControl(Composite parent
) {
261 GridLayout gl
= new GridLayout();
262 Composite container
= new Composite(parent
, SWT
.NO_FOCUS
);
263 container
.setLayout(gl
);
265 chooseCommandCmb
= new Combo(container
, SWT
.READ_ONLY
);
266 chooseCommandCmb
.setLayoutData(EclipseUiUtils
.fillWidth());
267 String
[] values
= commands
.keySet().toArray(new String
[0]);
268 chooseCommandCmb
.setItems(values
);
270 final Composite bottomPart
= new Composite(container
, SWT
.NO_FOCUS
);
271 bottomPart
.setLayoutData(EclipseUiUtils
.fillAll());
272 bottomPart
.setLayout(EclipseUiUtils
.noSpaceGridLayout());
274 chooseCommandCmb
.addSelectionListener(new SelectionAdapter() {
275 private static final long serialVersionUID
= 1L;
278 public void widgetSelected(SelectionEvent e
) {
279 if (getCommand().equals(CMD_UPDATE_PASSWORD
))
280 populatePasswordCmp(bottomPart
);
281 else if (getCommand().equals(CMD_GROUP_MEMBERSHIP
))
282 populateGroupCmp(bottomPart
);
284 populateBooleanFlagCmp(bottomPart
);
286 bottomPart
.layout(true, true);
289 setControl(container
);
292 private void populateBooleanFlagCmp(Composite parent
) {
293 EclipseUiUtils
.clear(parent
);
294 trueChk
= new Button(parent
, SWT
.CHECK
);
295 trueChk
.setText("Do it. (It will to the contrary if unchecked)");
296 trueChk
.setSelection(true);
297 trueChk
.setLayoutData(new GridData(SWT
.LEFT
, SWT
.TOP
, false, false));
300 private void populatePasswordCmp(Composite parent
) {
301 EclipseUiUtils
.clear(parent
);
302 Composite body
= new Composite(parent
, SWT
.NO_FOCUS
);
304 ModifyListener ml
= new ModifyListener() {
305 private static final long serialVersionUID
= -1558726363536729634L;
308 public void modifyText(ModifyEvent event
) {
313 body
.setLayout(new GridLayout(2, false));
314 body
.setLayoutData(new GridData(SWT
.FILL
, SWT
.FILL
, true, true));
315 pwdTxt
= EclipseUiUtils
.createGridLP(body
, "New password", ml
);
316 pwd2Txt
= EclipseUiUtils
.createGridLP(body
, "Repeat password", ml
);
319 private void checkPageComplete() {
320 String errorMsg
= null;
321 if (chooseCommandCmb
.getSelectionIndex() < 0)
322 errorMsg
= "Please select an action";
323 else if (CMD_UPDATE_PASSWORD
.equals(getCommand())) {
324 if (EclipseUiUtils
.isEmpty(pwdTxt
.getText())
325 || pwdTxt
.getText().length() < 4)
326 errorMsg
= "Please enter a password that is at least 4 character long";
327 else if (!pwdTxt
.getText().equals(pwd2Txt
.getText()))
328 errorMsg
= "Passwords are different";
330 if (EclipseUiUtils
.notEmpty(errorMsg
)) {
331 setMessage(errorMsg
, WizardPage
.ERROR
);
332 setPageComplete(false);
334 setMessage("Page complete, you can proceed to user choice",
335 WizardPage
.INFORMATION
);
336 setPageComplete(true);
339 getContainer().updateButtons();
342 private void populateGroupCmp(Composite parent
) {
343 EclipseUiUtils
.clear(parent
);
344 trueChk
= new Button(parent
, SWT
.CHECK
);
345 trueChk
.setText("Add to group. (It will remove user(s) from the "
346 + "corresponding group if unchecked)");
347 trueChk
.setSelection(true);
348 trueChk
.setLayoutData(new GridData(SWT
.LEFT
, SWT
.TOP
, false, false));
351 protected String
getCommand() {
352 return commands
.get(chooseCommandCmb
.getItem(chooseCommandCmb
353 .getSelectionIndex()));
356 protected String
getCommandLbl() {
357 return chooseCommandCmb
.getItem(chooseCommandCmb
358 .getSelectionIndex());
361 @SuppressWarnings("unused")
362 protected boolean getBoleanValue() {
363 // FIXME this is not consistent and will lead to errors.
364 if (ArgeoNames
.ARGEO_ENABLED
.equals(getCommand()))
365 return trueChk
.getSelection();
367 return !trueChk
.getSelection();
370 @SuppressWarnings("unused")
371 protected String
getStringValue() {
373 if (valueTxt
!= null) {
374 value
= valueTxt
.getText();
375 if ("".equals(value
.trim()))
381 protected char[] getPwdValue() {
382 // We do not directly reset the password text fields: There is no
383 // need to over secure this process: setting a pwd to multi users
384 // at the same time is anyhow a bad practice and should be used only
385 // in test environment or for temporary access
386 if (pwdTxt
== null || pwdTxt
.isDisposed())
389 return pwdTxt
.getText().toCharArray();
394 * Displays a list of users with a check box to be able to choose some of
397 private class ChooseUsersWizardPage
extends WizardPage
implements
398 IPageChangedListener
{
399 private static final long serialVersionUID
= 7651807402211214274L;
400 private ChooseUserTableViewer userTableCmp
;
402 public ChooseUsersWizardPage() {
403 super("Choose Users");
404 setTitle("Select users who will be impacted");
408 public void createControl(Composite parent
) {
409 Composite pageCmp
= new Composite(parent
, SWT
.NONE
);
410 pageCmp
.setLayout(EclipseUiUtils
.noSpaceGridLayout());
412 // Define the displayed columns
413 List
<ColumnDefinition
> columnDefs
= new ArrayList
<ColumnDefinition
>();
414 columnDefs
.add(new ColumnDefinition(new CommonNameLP(),
415 "Common Name", 150));
416 columnDefs
.add(new ColumnDefinition(new MailLP(), "E-mail", 150));
417 columnDefs
.add(new ColumnDefinition(new DomainNameLP(), "Domain",
420 // Only show technical DN to admin
421 if (UserAdminUtils
.isUserInRole(AuthConstants
.ROLE_ADMIN
))
422 columnDefs
.add(new ColumnDefinition(new UserNameLP(),
423 "Distinguished Name", 300));
425 userTableCmp
= new ChooseUserTableViewer(pageCmp
, SWT
.MULTI
426 | SWT
.H_SCROLL
| SWT
.V_SCROLL
);
427 userTableCmp
.setLayoutData(EclipseUiUtils
.fillAll());
428 userTableCmp
.setColumnDefinitions(columnDefs
);
429 userTableCmp
.populate(true, true);
430 userTableCmp
.refresh();
434 // Add listener to update message when shown
435 final IWizardContainer wContainer
= this.getContainer();
436 if (wContainer
instanceof IPageChangeProvider
) {
437 ((IPageChangeProvider
) wContainer
).addPageChangedListener(this);
443 public void pageChanged(PageChangedEvent event
) {
444 if (event
.getSelectedPage() == this) {
445 String msg
= "Chosen batch action: "
446 + chooseCommandPage
.getCommandLbl();
447 ((WizardPage
) event
.getSelectedPage()).setMessage(msg
);
451 protected List
<User
> getSelectedUsers() {
452 return userTableCmp
.getSelectedUsers();
455 private class ChooseUserTableViewer
extends LdifUsersTable
{
456 private static final long serialVersionUID
= 5080437561015853124L;
457 private final String
[] knownProps
= { LdifName
.uid
.name(),
458 LdifName
.dn
.name(), LdifName
.cn
.name(),
459 LdifName
.givenName
.name(), LdifName
.sn
.name(),
460 LdifName
.mail
.name() };
462 public ChooseUserTableViewer(Composite parent
, int style
) {
463 super(parent
, style
);
467 protected List
<User
> listFilteredElements(String filter
) {
471 StringBuilder builder
= new StringBuilder();
473 StringBuilder tmpBuilder
= new StringBuilder();
474 if (EclipseUiUtils
.notEmpty(filter
))
475 for (String prop
: knownProps
) {
476 tmpBuilder
.append("(");
477 tmpBuilder
.append(prop
);
478 tmpBuilder
.append("=*");
479 tmpBuilder
.append(filter
);
480 tmpBuilder
.append("*)");
482 if (tmpBuilder
.length() > 1) {
483 builder
.append("(&(")
484 .append(LdifName
.objectClass
.name())
486 .append(LdifName
.inetOrgPerson
.name())
488 builder
.append(tmpBuilder
.toString());
489 builder
.append("))");
491 builder
.append("(").append(LdifName
.objectClass
.name())
493 .append(LdifName
.inetOrgPerson
.name())
495 roles
= userAdminWrapper
.getUserAdmin().getRoles(
497 } catch (InvalidSyntaxException e
) {
498 throw new CmsException(
499 "Unable to get roles with filter: " + filter
, e
);
501 List
<User
> users
= new ArrayList
<User
>();
502 for (Role role
: roles
)
503 // Prevent current logged in user to perform batch on
505 if (!UserAdminUtils
.isCurrentUser((User
) role
))
506 users
.add((User
) role
);
512 /** Summary of input data before launching the process */
513 private class ValidateAndLaunchWizardPage
extends WizardPage
implements
514 IPageChangedListener
{
515 private static final long serialVersionUID
= 7098918351451743853L;
516 private ChosenUsersTableViewer userTableCmp
;
518 public ValidateAndLaunchWizardPage() {
519 super("Validate and launch");
520 setTitle("Validate and launch");
524 public void createControl(Composite parent
) {
525 Composite pageCmp
= new Composite(parent
, SWT
.NO_FOCUS
);
526 pageCmp
.setLayout(EclipseUiUtils
.noSpaceGridLayout());
528 List
<ColumnDefinition
> columnDefs
= new ArrayList
<ColumnDefinition
>();
529 columnDefs
.add(new ColumnDefinition(new CommonNameLP(),
530 "Common Name", 150));
531 columnDefs
.add(new ColumnDefinition(new MailLP(), "E-mail", 150));
532 columnDefs
.add(new ColumnDefinition(new DomainNameLP(), "Domain",
534 // Only show technical DN to admin
535 if (UserAdminUtils
.isUserInRole(AuthConstants
.ROLE_ADMIN
))
536 columnDefs
.add(new ColumnDefinition(new UserNameLP(),
537 "Distinguished Name", 300));
538 userTableCmp
= new ChosenUsersTableViewer(pageCmp
, SWT
.MULTI
539 | SWT
.H_SCROLL
| SWT
.V_SCROLL
);
540 userTableCmp
.setLayoutData(EclipseUiUtils
.fillAll());
541 userTableCmp
.setColumnDefinitions(columnDefs
);
542 userTableCmp
.populate(false, false);
543 userTableCmp
.refresh();
545 // Add listener to update message when shown
546 final IWizardContainer wContainer
= this.getContainer();
547 if (wContainer
instanceof IPageChangeProvider
) {
548 ((IPageChangeProvider
) wContainer
).addPageChangedListener(this);
553 public void pageChanged(PageChangedEvent event
) {
554 if (event
.getSelectedPage() == this) {
555 @SuppressWarnings({ "unchecked", "rawtypes" })
556 Object
[] values
= ((ArrayList
) userListPage
.getSelectedUsers())
557 .toArray(new Object
[userListPage
.getSelectedUsers()
559 userTableCmp
.getTableViewer().setInput(values
);
560 String msg
= "Following batch action: ["
561 + chooseCommandPage
.getCommandLbl()
562 + "] will be perfomed on the users listed below.\n";
563 // + "Are you sure you want to proceed?";
568 private class ChosenUsersTableViewer
extends LdifUsersTable
{
569 private static final long serialVersionUID
= 7814764735794270541L;
571 public ChosenUsersTableViewer(Composite parent
, int style
) {
572 super(parent
, style
);
576 protected List
<User
> listFilteredElements(String filter
) {
577 return userListPage
.getSelectedUsers();