2 * Copyright (C) 2007-2012 Argeo GmbH
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
16 package org
.argeo
.security
.ui
.admin
.internal
.parts
;
18 import java
.util
.ArrayList
;
19 import java
.util
.HashMap
;
20 import java
.util
.List
;
23 import javax
.jcr
.Node
;
24 import javax
.jcr
.NodeIterator
;
25 import javax
.jcr
.RepositoryException
;
26 import javax
.jcr
.Session
;
27 import javax
.jcr
.version
.VersionManager
;
29 import org
.apache
.commons
.logging
.Log
;
30 import org
.apache
.commons
.logging
.LogFactory
;
31 import org
.argeo
.ArgeoException
;
32 import org
.argeo
.ArgeoMonitor
;
33 import org
.argeo
.eclipse
.ui
.EclipseArgeoMonitor
;
34 import org
.argeo
.eclipse
.ui
.parts
.UsersTable
;
35 import org
.argeo
.jcr
.ArgeoNames
;
36 import org
.argeo
.jcr
.JcrUtils
;
37 import org
.argeo
.security
.UserAdminService
;
38 import org
.argeo
.security
.jcr
.JcrUserDetails
;
39 import org
.argeo
.security
.ui
.PrivilegedJob
;
40 import org
.argeo
.security
.ui
.admin
.SecurityAdminPlugin
;
41 import org
.eclipse
.core
.runtime
.IProgressMonitor
;
42 import org
.eclipse
.core
.runtime
.IStatus
;
43 import org
.eclipse
.core
.runtime
.Status
;
44 import org
.eclipse
.jface
.dialogs
.IPageChangeProvider
;
45 import org
.eclipse
.jface
.dialogs
.IPageChangedListener
;
46 import org
.eclipse
.jface
.dialogs
.PageChangedEvent
;
47 import org
.eclipse
.jface
.wizard
.IWizardContainer
;
48 import org
.eclipse
.jface
.wizard
.Wizard
;
49 import org
.eclipse
.jface
.wizard
.WizardPage
;
50 import org
.eclipse
.swt
.SWT
;
51 import org
.eclipse
.swt
.events
.SelectionEvent
;
52 import org
.eclipse
.swt
.events
.SelectionListener
;
53 import org
.eclipse
.swt
.layout
.FillLayout
;
54 import org
.eclipse
.swt
.layout
.GridData
;
55 import org
.eclipse
.swt
.layout
.GridLayout
;
56 import org
.eclipse
.swt
.widgets
.Button
;
57 import org
.eclipse
.swt
.widgets
.Combo
;
58 import org
.eclipse
.swt
.widgets
.Composite
;
59 import org
.eclipse
.swt
.widgets
.Control
;
60 import org
.eclipse
.swt
.widgets
.Label
;
61 import org
.eclipse
.swt
.widgets
.Text
;
63 /** Wizard to update users */
64 public class UserBatchUpdateWizard
extends Wizard
{
65 private final static Log log
= LogFactory
66 .getLog(UserBatchUpdateWizard
.class);
67 private Session session
;
68 private UserAdminService userAdminService
;
71 private ChooseCommandWizardPage chooseCommandPage
;
72 private ChooseUsersWizardPage userListPage
;
73 private ValidateAndLaunchWizardPage validatePage
;
75 // /////////////////////////////////////////////////
76 // / Definition of the various implemented commands
77 private final static String CMD_UPDATE_PASSWORD
= "resetPassword";
78 private final static String CMD_GROUP_MEMBERSHIP
= "groupMembership";
80 private final Map
<String
, String
> commands
= new HashMap
<String
, String
>() {
81 private static final long serialVersionUID
= 1L;
83 put("Enable user(s)", ArgeoNames
.ARGEO_ENABLED
);
84 put("Expire credentials", ArgeoNames
.ARGEO_CREDENTIALS_NON_EXPIRED
);
85 put("Expire account(s)", ArgeoNames
.ARGEO_ACCOUNT_NON_EXPIRED
);
86 put("Lock account(s)", ArgeoNames
.ARGEO_ACCOUNT_NON_LOCKED
);
87 put("Reset password(s)", CMD_UPDATE_PASSWORD
);
88 // TODO implement role / group management
89 // put("Add/Remove from group", CMD_GROUP_MEMBERSHIP);
93 public UserBatchUpdateWizard(Session session
,
94 UserAdminService userAdminService
) {
95 this.session
= session
;
96 this.userAdminService
= userAdminService
;
100 public void addPages() {
101 chooseCommandPage
= new ChooseCommandWizardPage();
102 addPage(chooseCommandPage
);
103 userListPage
= new ChooseUsersWizardPage(session
);
104 addPage(userListPage
);
105 validatePage
= new ValidateAndLaunchWizardPage(session
);
106 addPage(validatePage
);
110 public boolean performFinish() {
114 UpdateJob job
= null;
115 if (ArgeoNames
.ARGEO_ENABLED
.equals(chooseCommandPage
.getCommand())) {
116 job
= new UpdateBoolean(session
, userListPage
.getSelectedUsers(),
117 ArgeoNames
.ARGEO_ENABLED
,
118 chooseCommandPage
.getBoleanValue());
119 } else if (ArgeoNames
.ARGEO_CREDENTIALS_NON_EXPIRED
120 .equals(chooseCommandPage
.getCommand())) {
121 job
= new UpdateBoolean(session
, userListPage
.getSelectedUsers(),
122 ArgeoNames
.ARGEO_CREDENTIALS_NON_EXPIRED
,
123 chooseCommandPage
.getBoleanValue());
124 } else if (ArgeoNames
.ARGEO_ACCOUNT_NON_EXPIRED
125 .equals(chooseCommandPage
.getCommand())) {
126 job
= new UpdateBoolean(session
, userListPage
.getSelectedUsers(),
127 ArgeoNames
.ARGEO_ACCOUNT_NON_EXPIRED
,
128 chooseCommandPage
.getBoleanValue());
129 } else if (ArgeoNames
.ARGEO_ACCOUNT_NON_LOCKED
.equals(chooseCommandPage
131 job
= new UpdateBoolean(session
, userListPage
.getSelectedUsers(),
132 ArgeoNames
.ARGEO_ACCOUNT_NON_LOCKED
,
133 chooseCommandPage
.getBoleanValue());
134 } else if (CMD_UPDATE_PASSWORD
.equals(chooseCommandPage
.getCommand())) {
135 String newValue
= chooseCommandPage
.getPwdValue();
136 if (newValue
== null)
137 throw new ArgeoException(
138 "Password cannot be null or an empty string");
139 job
= new ResetPassword(session
, userAdminService
,
140 userListPage
.getSelectedUsers(), newValue
);
148 public void setSession(Session session
) {
149 this.session
= session
;
152 public boolean canFinish() {
153 if (this.getContainer().getCurrentPage() == validatePage
)
158 // /////////////////////////
160 private class UpdateBoolean
extends UpdateJob
{
161 private String propertyName
;
162 private boolean value
;
164 public UpdateBoolean(Session session
, List
<Node
> nodesToUpdate
,
165 String propertyName
, boolean value
) {
166 super(session
, nodesToUpdate
);
167 this.propertyName
= propertyName
;
171 protected void doUpdate(Node node
) {
173 node
.setProperty(propertyName
, value
);
174 } catch (RepositoryException re
) {
175 throw new ArgeoException(
176 "Unable to update boolean value for node " + node
, re
);
181 private class ResetPassword
extends UpdateJob
{
182 private String newValue
;
183 private UserAdminService userAdminService
;
185 public ResetPassword(Session session
,
186 UserAdminService userAdminService
, List
<Node
> nodesToUpdate
,
188 super(session
, nodesToUpdate
);
189 this.newValue
= newValue
;
190 this.userAdminService
= userAdminService
;
193 protected void doUpdate(Node node
) {
195 String userId
= node
.getProperty(ArgeoNames
.ARGEO_USER_ID
)
197 if (userAdminService
.userExists(userId
)) {
198 JcrUserDetails userDetails
= (JcrUserDetails
) userAdminService
199 .loadUserByUsername(userId
);
200 userAdminService
.updateUser(userDetails
201 .cloneWithNewPassword(newValue
));
203 } catch (RepositoryException re
) {
204 throw new ArgeoException(
205 "Unable to update boolean value for node " + node
, re
);
210 @SuppressWarnings("unused")
211 private class AddToGroup
extends UpdateJob
{
212 private String groupID
;
213 private Session session
;
215 public AddToGroup(Session session
, List
<Node
> nodesToUpdate
,
217 super(session
, nodesToUpdate
);
218 this.session
= session
;
219 this.groupID
= groupID
;
222 protected void doUpdate(Node node
) {
223 log
.info("Add/Remove to group actions are not yet implemented");
224 // TODO implement this
226 // throw new ArgeoException("Not yet implemented");
227 // } catch (RepositoryException re) {
228 // throw new ArgeoException(
229 // "Unable to update boolean value for node " + node, re);
235 * Base privileged job that will be run asynchronously to perform the batch
238 private abstract class UpdateJob
extends PrivilegedJob
{
240 private final Session currSession
;
241 private final List
<Node
> nodesToUpdate
;
243 protected abstract void doUpdate(Node node
);
245 public UpdateJob(Session session
, List
<Node
> nodesToUpdate
) {
246 super("Perform update");
248 this.currSession
= session
.getRepository().login();
249 // "move" nodes to update in the new session
250 // the "old" session will be closed by the calling command
251 // before the job has effectively ran
252 // TODO there must be a cleaner way to do.
253 List
<Node
> nodes
= new ArrayList
<Node
>();
254 for (Node node
: nodesToUpdate
) {
255 nodes
.add(currSession
.getNode(node
.getPath()));
257 this.nodesToUpdate
= nodes
;
258 } catch (RepositoryException e
) {
259 throw new ArgeoException("Error while dupplicating "
260 + "session for job", e
);
265 protected IStatus
doRun(IProgressMonitor progressMonitor
) {
267 ArgeoMonitor monitor
= new EclipseArgeoMonitor(progressMonitor
);
268 VersionManager vm
= currSession
.getWorkspace()
269 .getVersionManager();
270 int total
= nodesToUpdate
.size();
271 monitor
.beginTask("Performing change", total
);
272 for (Node node
: nodesToUpdate
) {
273 String path
= node
.getPath();
280 } catch (Exception e
) {
281 log
.error("Cannot perform batch update on users", e
);
282 // e.printStackTrace();
284 // Dig exception to find the root cause that will enable the
285 // user to understand the problem
287 Throwable originalCause
= e
;
288 while (cause
!= null) {
289 if (log
.isTraceEnabled())
290 log
.trace("Parent Cause message : "
291 + cause
.getMessage());
292 originalCause
= cause
;
293 cause
= cause
.getCause();
295 return new Status(IStatus
.ERROR
, SecurityAdminPlugin
.PLUGIN_ID
,
296 "Cannot perform updates.", originalCause
);
298 JcrUtils
.logoutQuietly(currSession
);
300 return Status
.OK_STATUS
;
304 // //////////////////////
306 /** Displays a combo box that enables user to choose which action to perform */
307 private class ChooseCommandWizardPage
extends WizardPage
{
308 private static final long serialVersionUID
= 1L;
310 private Combo chooseCommandCmb
;
311 private Button trueChk
;
312 private Text valueTxt
;
314 private Text pwd2Txt
;
316 public ChooseCommandWizardPage() {
317 super("Choose a command to run.");
318 setTitle("Choose a command to run.");
322 public void createControl(Composite parent
) {
323 GridLayout gl
= new GridLayout();
324 Composite container
= new Composite(parent
, SWT
.NO_FOCUS
);
325 container
.setLayout(gl
);
327 chooseCommandCmb
= new Combo(container
, SWT
.NO_FOCUS
);
328 String
[] values
= commands
.keySet().toArray(
329 new String
[commands
.size()]);
330 chooseCommandCmb
.setItems(values
);
331 chooseCommandCmb
.setLayoutData(new GridData(SWT
.FILL
, SWT
.TOP
,
334 final Composite bottomPart
= new Composite(container
, SWT
.NO_FOCUS
);
335 gl
= new GridLayout();
336 gl
.horizontalSpacing
= gl
.marginWidth
= gl
.verticalSpacing
= 0;
337 bottomPart
.setLayout(gl
);
338 bottomPart
.setLayoutData(new GridData(SWT
.FILL
, SWT
.FILL
, true,
341 chooseCommandCmb
.addSelectionListener(new SelectionListener() {
342 private static final long serialVersionUID
= 1L;
345 public void widgetSelected(SelectionEvent e
) {
346 if (getCommand().equals(CMD_UPDATE_PASSWORD
))
347 populatePasswordCmp(bottomPart
);
348 else if (getCommand().equals(CMD_GROUP_MEMBERSHIP
))
349 populateGroupCmp(bottomPart
);
351 populateBooleanFlagCmp(bottomPart
);
352 bottomPart
.pack(true);
357 public void widgetDefaultSelected(SelectionEvent e
) {
361 setControl(container
);
364 private void cleanParent(Composite parent
) {
365 if (parent
.getChildren().length
> 0) {
366 for (Control control
: parent
.getChildren())
371 private void populateBooleanFlagCmp(Composite parent
) {
373 trueChk
= new Button(parent
, SWT
.CHECK
);
374 trueChk
.setText("Do it. (It will to the contrary if unchecked)");
375 trueChk
.setSelection(true);
376 trueChk
.setLayoutData(new GridData(SWT
.LEFT
, SWT
.TOP
, false, false));
379 private void populatePasswordCmp(Composite parent
) {
381 Composite body
= new Composite(parent
, SWT
.NO_FOCUS
);
382 body
.setLayout(new GridLayout(2, false));
383 body
.setLayoutData(new GridData(SWT
.FILL
, SWT
.FILL
, true, true));
384 pwdTxt
= createLP(body
, "New password", "");
385 pwd2Txt
= createLP(body
, "Repeat password", "");
388 /** Creates label and password. */
389 protected Text
createLP(Composite body
, String label
, String value
) {
390 Label lbl
= new Label(body
, SWT
.NONE
);
391 lbl
.setLayoutData(new GridData(SWT
.RIGHT
, SWT
.CENTER
, false, false));
393 Text text
= new Text(body
, SWT
.BORDER
| SWT
.PASSWORD
);
395 text
.setLayoutData(new GridData(SWT
.FILL
, SWT
.CENTER
, true, false));
399 private void populateGroupCmp(Composite parent
) {
400 if (parent
.getChildren().length
> 0) {
401 for (Control control
: parent
.getChildren())
404 trueChk
= new Button(parent
, SWT
.CHECK
);
405 trueChk
.setText("Add to group. (It will remove user(s) from the "
406 + "corresponding group if unchecked)");
407 trueChk
.setSelection(true);
408 trueChk
.setLayoutData(new GridData(SWT
.LEFT
, SWT
.TOP
, false, false));
411 protected String
getCommand() {
412 return commands
.get(chooseCommandCmb
.getItem(chooseCommandCmb
413 .getSelectionIndex()));
416 protected String
getCommandLbl() {
417 return chooseCommandCmb
.getItem(chooseCommandCmb
418 .getSelectionIndex());
421 protected boolean getBoleanValue() {
422 // FIXME this is not consistent and will lead to errors.
423 if (ArgeoNames
.ARGEO_ENABLED
.equals(getCommand()))
424 return trueChk
.getSelection();
426 return !trueChk
.getSelection();
429 @SuppressWarnings("unused")
430 protected String
getStringValue() {
432 if (valueTxt
!= null) {
433 value
= valueTxt
.getText();
434 if ("".equals(value
.trim()))
440 protected String
getPwdValue() {
441 String newPwd
= null;
442 if (pwdTxt
== null || pwd2Txt
== null)
444 if (!pwdTxt
.getText().equals("") || !pwd2Txt
.getText().equals("")) {
445 if (pwdTxt
.getText().equals(pwd2Txt
.getText())) {
446 newPwd
= pwdTxt
.getText();
452 throw new ArgeoException("Passwords are not equals");
460 * Displays a list of users with a check box to be able to choose some of
463 private class ChooseUsersWizardPage
extends WizardPage
implements
464 IPageChangedListener
{
465 private static final long serialVersionUID
= 1L;
466 private UsersTable userTableCmp
;
467 private Composite container
;
468 private Session session
;
470 public ChooseUsersWizardPage(Session session
) {
471 super("Choose Users");
472 this.session
= session
;
473 setTitle("Select users who will be impacted");
477 public void createControl(Composite parent
) {
478 container
= new Composite(parent
, SWT
.NONE
);
479 container
.setLayout(new FillLayout());
480 userTableCmp
= new MyUserTableCmp(container
, SWT
.NO_FOCUS
, session
);
481 userTableCmp
.populate(true, true);
482 setControl(container
);
484 // Add listener to update message when shown
485 final IWizardContainer container
= this.getContainer();
486 if (container
instanceof IPageChangeProvider
) {
487 ((IPageChangeProvider
) container
).addPageChangedListener(this);
493 public void pageChanged(PageChangedEvent event
) {
494 if (event
.getSelectedPage() == this) {
495 String msg
= "Chosen batch action: "
496 + chooseCommandPage
.getCommandLbl();
497 ((WizardPage
) event
.getSelectedPage()).setMessage(msg
);
501 protected List
<Node
> getSelectedUsers() {
502 return userTableCmp
.getSelectedUsers();
505 private class MyUserTableCmp
extends UsersTable
{
507 private static final long serialVersionUID
= 1L;
509 public MyUserTableCmp(Composite parent
, int style
, Session session
) {
510 super(parent
, style
, session
);
514 protected void refreshFilteredList() {
515 List
<Node
> nodes
= new ArrayList
<Node
>();
517 NodeIterator ni
= listFilteredElements(session
,
520 users
: while (ni
.hasNext()) {
521 Node currNode
= ni
.nextNode();
522 String username
= currNode
.hasProperty(ARGEO_USER_ID
) ? currNode
523 .getProperty(ARGEO_USER_ID
).getString() : "";
524 if (username
.equals(session
.getUserID()))
529 getTableViewer().setInput(nodes
.toArray());
530 } catch (RepositoryException e
) {
531 throw new ArgeoException("Unable to list users", e
);
538 * Recapitulation of input data before running real update
540 private class ValidateAndLaunchWizardPage
extends WizardPage
implements
541 IPageChangedListener
{
542 private static final long serialVersionUID
= 1L;
543 private UsersTable userTableCmp
;
544 private Session session
;
546 public ValidateAndLaunchWizardPage(Session session
) {
547 super("Validate and launch");
548 this.session
= session
;
549 setTitle("Validate and launch");
553 public void createControl(Composite parent
) {
554 Composite mainCmp
= new Composite(parent
, SWT
.NO_FOCUS
);
555 mainCmp
.setLayout(new FillLayout());
557 // Add listener to update user list when shown
558 final IWizardContainer container
= this.getContainer();
559 if (container
instanceof IPageChangeProvider
) {
560 ((IPageChangeProvider
) container
).addPageChangedListener(this);
563 userTableCmp
= new UsersTable(mainCmp
, SWT
.NO_FOCUS
, session
);
564 userTableCmp
.populate(false, false);
569 public void pageChanged(PageChangedEvent event
) {
570 if (event
.getSelectedPage() == this) {
571 @SuppressWarnings({ "unchecked", "rawtypes" })
572 Object
[] values
= ((ArrayList
) userListPage
.getSelectedUsers())
573 .toArray(new Object
[userListPage
.getSelectedUsers()
575 userTableCmp
.getTableViewer().setInput(values
);
576 String msg
= "Following batch action: ["
577 + chooseCommandPage
.getCommandLbl()
578 + "] will be perfomed on the users listed below.\n"
579 + "Are you sure you want to proceed?";
580 ((WizardPage
) event
.getSelectedPage()).setMessage(msg
);
584 // private class MyUserTableCmp extends UserTableComposite {
585 // public MyUserTableCmp(Composite parent, int style, Session session) {
586 // super(parent, style, session);
590 // protected void refreshFilteredList() {
591 // @SuppressWarnings({ "unchecked", "rawtypes" })
593 // setFilteredList(values);
597 // public void setVisible(boolean visible) {
598 // super.setVisible(visible);
600 // refreshFilteredList();