]> git.argeo.org Git - lgpl/argeo-commons.git/blob - org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/wizards/UserBatchUpdateWizard.java
Plug automatic refresh of the transaction edition commands.
[lgpl/argeo-commons.git] / org.argeo.security.ui.admin / src / org / argeo / security / ui / admin / wizards / UserBatchUpdateWizard.java
1 /*
2 * Copyright (C) 2007-2012 Argeo GmbH
3 *
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
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
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.
15 */
16 package org.argeo.security.ui.admin.wizards;
17
18 import java.util.ArrayList;
19 import java.util.HashMap;
20 import java.util.List;
21 import java.util.Map;
22
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;
28
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;
62
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;
69
70 // pages
71 private ChooseCommandWizardPage chooseCommandPage;
72 private ChooseUsersWizardPage userListPage;
73 private ValidateAndLaunchWizardPage validatePage;
74
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";
79
80 private final Map<String, String> commands = new HashMap<String, String>() {
81 private static final long serialVersionUID = 1L;
82 {
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);
90 }
91 };
92
93 public UserBatchUpdateWizard(Session session,
94 UserAdminService userAdminService) {
95 this.session = session;
96 this.userAdminService = userAdminService;
97 }
98
99 @Override
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);
107 }
108
109 @Override
110 public boolean performFinish() {
111 if (!canFinish())
112 return false;
113
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
130 .getCommand())) {
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);
141 }
142
143 if (job != null)
144 job.schedule();
145 return true;
146 }
147
148 public void setSession(Session session) {
149 this.session = session;
150 }
151
152 public boolean canFinish() {
153 if (this.getContainer().getCurrentPage() == validatePage)
154 return true;
155 return false;
156 }
157
158 // /////////////////////////
159 // REEL UPDATE JOB
160 private class UpdateBoolean extends UpdateJob {
161 private String propertyName;
162 private boolean value;
163
164 public UpdateBoolean(Session session, List<Node> nodesToUpdate,
165 String propertyName, boolean value) {
166 super(session, nodesToUpdate);
167 this.propertyName = propertyName;
168 this.value = value;
169 }
170
171 protected void doUpdate(Node node) {
172 try {
173 node.setProperty(propertyName, value);
174 } catch (RepositoryException re) {
175 throw new ArgeoException(
176 "Unable to update boolean value for node " + node, re);
177 }
178 }
179 }
180
181 private class ResetPassword extends UpdateJob {
182 private String newValue;
183 private UserAdminService userAdminService;
184
185 public ResetPassword(Session session,
186 UserAdminService userAdminService, List<Node> nodesToUpdate,
187 String newValue) {
188 super(session, nodesToUpdate);
189 this.newValue = newValue;
190 this.userAdminService = userAdminService;
191 }
192
193 protected void doUpdate(Node node) {
194 try {
195 String userId = node.getProperty(ArgeoNames.ARGEO_USER_ID)
196 .getString();
197 if (userAdminService.userExists(userId)) {
198 JcrUserDetails userDetails = (JcrUserDetails) userAdminService
199 .loadUserByUsername(userId);
200 userAdminService.updateUser(userDetails
201 .cloneWithNewPassword(newValue));
202 }
203 } catch (RepositoryException re) {
204 throw new ArgeoException(
205 "Unable to update boolean value for node " + node, re);
206 }
207 }
208 }
209
210 @SuppressWarnings("unused")
211 private class AddToGroup extends UpdateJob {
212 private String groupID;
213 private Session session;
214
215 public AddToGroup(Session session, List<Node> nodesToUpdate,
216 String groupID) {
217 super(session, nodesToUpdate);
218 this.session = session;
219 this.groupID = groupID;
220 }
221
222 protected void doUpdate(Node node) {
223 log.info("Add/Remove to group actions are not yet implemented");
224 // TODO implement this
225 // try {
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);
230 // }
231 }
232 }
233
234 /**
235 * Base privileged job that will be run asynchronously to perform the batch
236 * update
237 */
238 private abstract class UpdateJob extends PrivilegedJob {
239
240 private final Session currSession;
241 private final List<Node> nodesToUpdate;
242
243 protected abstract void doUpdate(Node node);
244
245 public UpdateJob(Session session, List<Node> nodesToUpdate) {
246 super("Perform update");
247 try {
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()));
256 }
257 this.nodesToUpdate = nodes;
258 } catch (RepositoryException e) {
259 throw new ArgeoException("Error while dupplicating "
260 + "session for job", e);
261 }
262 }
263
264 @Override
265 protected IStatus doRun(IProgressMonitor progressMonitor) {
266 try {
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();
274 vm.checkout(path);
275 doUpdate(node);
276 currSession.save();
277 vm.checkin(path);
278 monitor.worked(1);
279 }
280 } catch (Exception e) {
281 log.error("Cannot perform batch update on users", e);
282 // e.printStackTrace();
283
284 // Dig exception to find the root cause that will enable the
285 // user to understand the problem
286 Throwable cause = e;
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();
294 }
295 return new Status(IStatus.ERROR, SecurityAdminPlugin.PLUGIN_ID,
296 "Cannot perform updates.", originalCause);
297 } finally {
298 JcrUtils.logoutQuietly(currSession);
299 }
300 return Status.OK_STATUS;
301 }
302 }
303
304 // //////////////////////
305 // Pages definition
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;
309
310 private Combo chooseCommandCmb;
311 private Button trueChk;
312 private Text valueTxt;
313 private Text pwdTxt;
314 private Text pwd2Txt;
315
316 public ChooseCommandWizardPage() {
317 super("Choose a command to run.");
318 setTitle("Choose a command to run.");
319 }
320
321 @Override
322 public void createControl(Composite parent) {
323 GridLayout gl = new GridLayout();
324 Composite container = new Composite(parent, SWT.NO_FOCUS);
325 container.setLayout(gl);
326
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,
332 true, false));
333
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,
339 true));
340
341 chooseCommandCmb.addSelectionListener(new SelectionListener() {
342 private static final long serialVersionUID = 1L;
343
344 @Override
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);
350 else
351 populateBooleanFlagCmp(bottomPart);
352 bottomPart.pack(true);
353 bottomPart.layout();
354 }
355
356 @Override
357 public void widgetDefaultSelected(SelectionEvent e) {
358 }
359 });
360
361 setControl(container);
362 }
363
364 private void cleanParent(Composite parent) {
365 if (parent.getChildren().length > 0) {
366 for (Control control : parent.getChildren())
367 control.dispose();
368 }
369 }
370
371 private void populateBooleanFlagCmp(Composite parent) {
372 cleanParent(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));
377 }
378
379 private void populatePasswordCmp(Composite parent) {
380 cleanParent(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", "");
386 }
387
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));
392 lbl.setText(label);
393 Text text = new Text(body, SWT.BORDER | SWT.PASSWORD);
394 text.setText(value);
395 text.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
396 return text;
397 }
398
399 private void populateGroupCmp(Composite parent) {
400 if (parent.getChildren().length > 0) {
401 for (Control control : parent.getChildren())
402 control.dispose();
403 }
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));
409 }
410
411 protected String getCommand() {
412 return commands.get(chooseCommandCmb.getItem(chooseCommandCmb
413 .getSelectionIndex()));
414 }
415
416 protected String getCommandLbl() {
417 return chooseCommandCmb.getItem(chooseCommandCmb
418 .getSelectionIndex());
419 }
420
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();
425 else
426 return !trueChk.getSelection();
427 }
428
429 @SuppressWarnings("unused")
430 protected String getStringValue() {
431 String value = null;
432 if (valueTxt != null) {
433 value = valueTxt.getText();
434 if ("".equals(value.trim()))
435 value = null;
436 }
437 return value;
438 }
439
440 protected String getPwdValue() {
441 String newPwd = null;
442 if (pwdTxt == null || pwd2Txt == null)
443 return null;
444 if (!pwdTxt.getText().equals("") || !pwd2Txt.getText().equals("")) {
445 if (pwdTxt.getText().equals(pwd2Txt.getText())) {
446 newPwd = pwdTxt.getText();
447 pwdTxt.setText("");
448 pwd2Txt.setText("");
449 } else {
450 pwdTxt.setText("");
451 pwd2Txt.setText("");
452 throw new ArgeoException("Passwords are not equals");
453 }
454 }
455 return newPwd;
456 }
457 }
458
459 /**
460 * Displays a list of users with a check box to be able to choose some of
461 * them
462 */
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;
469
470 public ChooseUsersWizardPage(Session session) {
471 super("Choose Users");
472 this.session = session;
473 setTitle("Select users who will be impacted");
474 }
475
476 @Override
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);
483
484 // Add listener to update message when shown
485 final IWizardContainer container = this.getContainer();
486 if (container instanceof IPageChangeProvider) {
487 ((IPageChangeProvider) container).addPageChangedListener(this);
488 }
489
490 }
491
492 @Override
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);
498 }
499 }
500
501 protected List<Node> getSelectedUsers() {
502 return userTableCmp.getSelectedUsers();
503 }
504
505 private class MyUserTableCmp extends UsersTable {
506
507 private static final long serialVersionUID = 1L;
508
509 public MyUserTableCmp(Composite parent, int style, Session session) {
510 super(parent, style, session);
511 }
512
513 @Override
514 protected void refreshFilteredList() {
515 List<Node> nodes = new ArrayList<Node>();
516 try {
517 NodeIterator ni = listFilteredElements(session,
518 getFilterString());
519
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()))
525 continue users;
526 else
527 nodes.add(currNode);
528 }
529 getTableViewer().setInput(nodes.toArray());
530 } catch (RepositoryException e) {
531 throw new ArgeoException("Unable to list users", e);
532 }
533 }
534 }
535 }
536
537 /**
538 * Recapitulation of input data before running real update
539 */
540 private class ValidateAndLaunchWizardPage extends WizardPage implements
541 IPageChangedListener {
542 private static final long serialVersionUID = 1L;
543 private UsersTable userTableCmp;
544 private Session session;
545
546 public ValidateAndLaunchWizardPage(Session session) {
547 super("Validate and launch");
548 this.session = session;
549 setTitle("Validate and launch");
550 }
551
552 @Override
553 public void createControl(Composite parent) {
554 Composite mainCmp = new Composite(parent, SWT.NO_FOCUS);
555 mainCmp.setLayout(new FillLayout());
556
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);
561 }
562
563 userTableCmp = new UsersTable(mainCmp, SWT.NO_FOCUS, session);
564 userTableCmp.populate(false, false);
565 setControl(mainCmp);
566 }
567
568 @Override
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()
574 .size()]);
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);
581 }
582 }
583
584 // private class MyUserTableCmp extends UserTableComposite {
585 // public MyUserTableCmp(Composite parent, int style, Session session) {
586 // super(parent, style, session);
587 // }
588 //
589 // @Override
590 // protected void refreshFilteredList() {
591 // @SuppressWarnings({ "unchecked", "rawtypes" })
592 //
593 // setFilteredList(values);
594 // }
595 //
596 // @Override
597 // public void setVisible(boolean visible) {
598 // super.setVisible(visible);
599 // if (visible)
600 // refreshFilteredList();
601 // }
602 // }
603 }
604 }