]> git.argeo.org Git - lgpl/argeo-commons.git/blob - security/plugins/org.argeo.security.ui.admin/src/main/java/org/argeo/security/ui/admin/wizards/UserBatchUpdateWizard.java
Prevent modification on current user while launching user batch update
[lgpl/argeo-commons.git] / security / plugins / org.argeo.security.ui.admin / src / main / java / 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.jcr.ArgeoNames;
35 import org.argeo.jcr.JcrUtils;
36 import org.argeo.security.UserAdminService;
37 import org.argeo.security.jcr.JcrSecurityModel;
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.argeo.security.ui.admin.UserTableComposite;
42 import org.eclipse.core.runtime.IProgressMonitor;
43 import org.eclipse.core.runtime.IStatus;
44 import org.eclipse.core.runtime.Status;
45 import org.eclipse.jface.dialogs.IPageChangeProvider;
46 import org.eclipse.jface.dialogs.IPageChangedListener;
47 import org.eclipse.jface.dialogs.PageChangedEvent;
48 import org.eclipse.jface.wizard.IWizardContainer;
49 import org.eclipse.jface.wizard.Wizard;
50 import org.eclipse.jface.wizard.WizardPage;
51 import org.eclipse.swt.SWT;
52 import org.eclipse.swt.events.SelectionEvent;
53 import org.eclipse.swt.events.SelectionListener;
54 import org.eclipse.swt.layout.FillLayout;
55 import org.eclipse.swt.layout.GridData;
56 import org.eclipse.swt.layout.GridLayout;
57 import org.eclipse.swt.widgets.Button;
58 import org.eclipse.swt.widgets.Combo;
59 import org.eclipse.swt.widgets.Composite;
60 import org.eclipse.swt.widgets.Control;
61 import org.eclipse.swt.widgets.Label;
62 import org.eclipse.swt.widgets.Text;
63
64 /** Wizard to update users */
65 public class UserBatchUpdateWizard extends Wizard {
66 private final static Log log = LogFactory
67 .getLog(UserBatchUpdateWizard.class);
68 private Session session;
69 private UserAdminService userAdminService;
70
71 // pages
72 private ChooseCommandWizardPage chooseCommandPage;
73 private ChooseUsersWizardPage userListPage;
74 private ValidateAndLaunchWizardPage validatePage;
75
76 // /////////////////////////////////////////////////
77 // / Definition of the various implemented commands
78 private final static String CMD_UPDATE_PASSWORD = "resetPassword";
79 private final static String CMD_GROUP_MEMBERSHIP = "groupMembership";
80
81 private final Map<String, String> commands = new HashMap<String, String>() {
82 private static final long serialVersionUID = 1L;
83 {
84 put("Enable user(s)", ArgeoNames.ARGEO_ENABLED);
85 put("Expire credentials", ArgeoNames.ARGEO_CREDENTIALS_NON_EXPIRED);
86 put("Expire account(s)", ArgeoNames.ARGEO_ACCOUNT_NON_EXPIRED);
87 put("Lock account(s)", ArgeoNames.ARGEO_ACCOUNT_NON_LOCKED);
88 put("Reset password(s)", CMD_UPDATE_PASSWORD);
89 // TODO implement role / group management
90 // put("Add/Remove from group", CMD_GROUP_MEMBERSHIP);
91 }
92 };
93
94 public UserBatchUpdateWizard(Session session,
95 UserAdminService userAdminService, JcrSecurityModel jcrSecurityModel) {
96 this.session = session;
97 this.userAdminService = userAdminService;
98 // this.jcrSecurityModel = jcrSecurityModel;
99 }
100
101 @Override
102 public void addPages() {
103 chooseCommandPage = new ChooseCommandWizardPage();
104 addPage(chooseCommandPage);
105 userListPage = new ChooseUsersWizardPage(session);
106 addPage(userListPage);
107 validatePage = new ValidateAndLaunchWizardPage(session);
108 addPage(validatePage);
109 }
110
111 @Override
112 public boolean performFinish() {
113 if (!canFinish())
114 return false;
115
116 UpdateJob job = null;
117 if (ArgeoNames.ARGEO_ENABLED.equals(chooseCommandPage.getCommand())) {
118 job = new UpdateBoolean(session, userListPage.getSelectedUsers(),
119 ArgeoNames.ARGEO_ENABLED,
120 chooseCommandPage.getBoleanValue());
121 } else if (ArgeoNames.ARGEO_CREDENTIALS_NON_EXPIRED
122 .equals(chooseCommandPage.getCommand())) {
123 job = new UpdateBoolean(session, userListPage.getSelectedUsers(),
124 ArgeoNames.ARGEO_CREDENTIALS_NON_EXPIRED,
125 chooseCommandPage.getBoleanValue());
126 } else if (ArgeoNames.ARGEO_ACCOUNT_NON_EXPIRED
127 .equals(chooseCommandPage.getCommand())) {
128 job = new UpdateBoolean(session, userListPage.getSelectedUsers(),
129 ArgeoNames.ARGEO_ACCOUNT_NON_EXPIRED,
130 chooseCommandPage.getBoleanValue());
131 } else if (ArgeoNames.ARGEO_ACCOUNT_NON_LOCKED.equals(chooseCommandPage
132 .getCommand())) {
133 job = new UpdateBoolean(session, userListPage.getSelectedUsers(),
134 ArgeoNames.ARGEO_ACCOUNT_NON_LOCKED,
135 chooseCommandPage.getBoleanValue());
136 } else if (CMD_UPDATE_PASSWORD.equals(chooseCommandPage.getCommand())) {
137 String newValue = chooseCommandPage.getPwdValue();
138 if (newValue == null)
139 throw new ArgeoException(
140 "Password cannot be null or an empty string");
141 job = new ResetPassword(session, userAdminService,
142 userListPage.getSelectedUsers(), newValue);
143 }
144
145 if (job != null)
146 job.schedule();
147 return true;
148 }
149
150 public void setSession(Session session) {
151 this.session = session;
152 }
153
154 public boolean canFinish() {
155 if (this.getContainer().getCurrentPage() == validatePage)
156 return true;
157 return false;
158 }
159
160 // /////////////////////////
161 // REEL UPDATE JOB
162 private class UpdateBoolean extends UpdateJob {
163 private String propertyName;
164 private boolean value;
165
166 public UpdateBoolean(Session session, List<Node> nodesToUpdate,
167 String propertyName, boolean value) {
168 super(session, nodesToUpdate);
169 this.propertyName = propertyName;
170 this.value = value;
171 }
172
173 protected void doUpdate(Node node) {
174 try {
175 node.setProperty(propertyName, value);
176 } catch (RepositoryException re) {
177 throw new ArgeoException(
178 "Unable to update boolean value for node " + node, re);
179 }
180 }
181 }
182
183 private class ResetPassword extends UpdateJob {
184 private String newValue;
185 private UserAdminService userAdminService;
186
187 public ResetPassword(Session session,
188 UserAdminService userAdminService, List<Node> nodesToUpdate,
189 String newValue) {
190 super(session, nodesToUpdate);
191 this.newValue = newValue;
192 this.userAdminService = userAdminService;
193 }
194
195 protected void doUpdate(Node node) {
196 try {
197 String userId = node.getProperty(ArgeoNames.ARGEO_USER_ID)
198 .getString();
199 if (userAdminService.userExists(userId)) {
200 JcrUserDetails userDetails = (JcrUserDetails) userAdminService
201 .loadUserByUsername(userId);
202 userAdminService.updateUser(userDetails
203 .cloneWithNewPassword(newValue));
204 }
205 } catch (RepositoryException re) {
206 throw new ArgeoException(
207 "Unable to update boolean value for node " + node, re);
208 }
209 }
210 }
211
212 @SuppressWarnings("unused")
213 private class AddToGroup extends UpdateJob {
214 private String groupID;
215 private Session session;
216
217 public AddToGroup(Session session, List<Node> nodesToUpdate,
218 String groupID) {
219 super(session, nodesToUpdate);
220 this.session = session;
221 this.groupID = groupID;
222 }
223
224 protected void doUpdate(Node node) {
225 log.info("Add/Remove to group actions are not yet implemented");
226 // TODO implement this
227 // try {
228 // throw new ArgeoException("Not yet implemented");
229 // } catch (RepositoryException re) {
230 // throw new ArgeoException(
231 // "Unable to update boolean value for node " + node, re);
232 // }
233 }
234 }
235
236 /**
237 * Base privileged job that will be run asynchronously to perform the batch
238 * update
239 */
240 private abstract class UpdateJob extends PrivilegedJob {
241
242 private final Session currSession;
243 private final List<Node> nodesToUpdate;
244
245 protected abstract void doUpdate(Node node);
246
247 public UpdateJob(Session session, List<Node> nodesToUpdate) {
248 super("Perform update");
249 try {
250 this.currSession = session.getRepository().login();
251 // "move" nodes to update in the new session
252 // the "old" session will be closed by the calling command
253 // before the job has effectively ran
254 // TODO there must be a cleaner way to do.
255 List<Node> nodes = new ArrayList<Node>();
256 for (Node node : nodesToUpdate) {
257 nodes.add(currSession.getNode(node.getPath()));
258 }
259 this.nodesToUpdate = nodes;
260 } catch (RepositoryException e) {
261 throw new ArgeoException("Error while dupplicating "
262 + "session for job", e);
263 }
264 }
265
266 @Override
267 protected IStatus doRun(IProgressMonitor progressMonitor) {
268 try {
269 ArgeoMonitor monitor = new EclipseArgeoMonitor(progressMonitor);
270 VersionManager vm = currSession.getWorkspace()
271 .getVersionManager();
272 int total = nodesToUpdate.size();
273 monitor.beginTask("Performing change", total);
274 for (Node node : nodesToUpdate) {
275 String path = node.getPath();
276 vm.checkout(path);
277 doUpdate(node);
278 currSession.save();
279 vm.checkin(path);
280 monitor.worked(1);
281 }
282 } catch (Exception e) {
283 log.error("Cannot perform batch update on users", e);
284 // e.printStackTrace();
285
286 // Dig exception to find the root cause that will enable the
287 // user to understand the problem
288 Throwable cause = e;
289 Throwable originalCause = e;
290 while (cause != null) {
291 if (log.isTraceEnabled())
292 log.trace("Parent Cause message : "
293 + cause.getMessage());
294 originalCause = cause;
295 cause = cause.getCause();
296 }
297 return new Status(IStatus.ERROR, SecurityAdminPlugin.PLUGIN_ID,
298 "Cannot perform updates.", originalCause);
299 } finally {
300 JcrUtils.logoutQuietly(currSession);
301 }
302 return Status.OK_STATUS;
303 }
304 }
305
306 // //////////////////////
307 // Pages definition
308 /** Displays a combo box that enables user to choose which action to perform */
309 private class ChooseCommandWizardPage extends WizardPage {
310 private static final long serialVersionUID = 1L;
311
312 private Combo chooseCommandCmb;
313 private Button trueChk;
314 private Text valueTxt;
315 private Text pwdTxt;
316 private Text pwd2Txt;
317
318 public ChooseCommandWizardPage() {
319 super("Choose a command to run.");
320 setTitle("Choose a command to run.");
321 }
322
323 @Override
324 public void createControl(Composite parent) {
325 GridLayout gl = new GridLayout();
326 Composite container = new Composite(parent, SWT.NO_FOCUS);
327 container.setLayout(gl);
328
329 chooseCommandCmb = new Combo(container, SWT.NO_FOCUS);
330 String[] values = commands.keySet().toArray(
331 new String[commands.size()]);
332 chooseCommandCmb.setItems(values);
333 chooseCommandCmb.setLayoutData(new GridData(SWT.FILL, SWT.TOP,
334 true, false));
335
336 final Composite bottomPart = new Composite(container, SWT.NO_FOCUS);
337 gl = new GridLayout();
338 gl.horizontalSpacing = gl.marginWidth = gl.verticalSpacing = 0;
339 bottomPart.setLayout(gl);
340 bottomPart.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true,
341 true));
342
343 chooseCommandCmb.addSelectionListener(new SelectionListener() {
344 private static final long serialVersionUID = 1L;
345
346 @Override
347 public void widgetSelected(SelectionEvent e) {
348 if (getCommand().equals(CMD_UPDATE_PASSWORD))
349 populatePasswordCmp(bottomPart);
350 else if (getCommand().equals(CMD_GROUP_MEMBERSHIP))
351 populateGroupCmp(bottomPart);
352 else
353 populateBooleanFlagCmp(bottomPart);
354 bottomPart.pack(true);
355 bottomPart.layout();
356 }
357
358 @Override
359 public void widgetDefaultSelected(SelectionEvent e) {
360 }
361 });
362
363 setControl(container);
364 }
365
366 private void cleanParent(Composite parent) {
367 if (parent.getChildren().length > 0) {
368 for (Control control : parent.getChildren())
369 control.dispose();
370 }
371 }
372
373 private void populateBooleanFlagCmp(Composite parent) {
374 cleanParent(parent);
375 trueChk = new Button(parent, SWT.CHECK);
376 trueChk.setText("Do it. (It will to the contrary if unchecked)");
377 trueChk.setSelection(true);
378 trueChk.setLayoutData(new GridData(SWT.LEFT, SWT.TOP, false, false));
379 }
380
381 private void populatePasswordCmp(Composite parent) {
382 cleanParent(parent);
383 Composite body = new Composite(parent, SWT.NO_FOCUS);
384 body.setLayout(new GridLayout(2, false));
385 body.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
386 pwdTxt = createLP(body, "New password", "");
387 pwd2Txt = createLP(body, "Repeat password", "");
388 }
389
390 /** Creates label and password. */
391 protected Text createLP(Composite body, String label, String value) {
392 Label lbl = new Label(body, SWT.NONE);
393 lbl.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false));
394 lbl.setText(label);
395 Text text = new Text(body, SWT.BORDER | SWT.PASSWORD);
396 text.setText(value);
397 text.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
398 return text;
399 }
400
401 private void populateGroupCmp(Composite parent) {
402 if (parent.getChildren().length > 0) {
403 for (Control control : parent.getChildren())
404 control.dispose();
405 }
406 trueChk = new Button(parent, SWT.CHECK);
407 trueChk.setText("Add to group. (It will remove user(s) from the "
408 + "corresponding group if unchecked)");
409 trueChk.setSelection(true);
410 trueChk.setLayoutData(new GridData(SWT.LEFT, SWT.TOP, false, false));
411 }
412
413 protected String getCommand() {
414 return commands.get(chooseCommandCmb.getItem(chooseCommandCmb
415 .getSelectionIndex()));
416 }
417
418 protected String getCommandLbl() {
419 return chooseCommandCmb.getItem(chooseCommandCmb
420 .getSelectionIndex());
421 }
422
423 protected boolean getBoleanValue() {
424 // FIXME this is not consistent and will lead to errors.
425 if (ArgeoNames.ARGEO_ENABLED.equals(getCommand()))
426 return trueChk.getSelection();
427 else
428 return !trueChk.getSelection();
429 }
430
431 @SuppressWarnings("unused")
432 protected String getStringValue() {
433 String value = null;
434 if (valueTxt != null) {
435 value = valueTxt.getText();
436 if ("".equals(value.trim()))
437 value = null;
438 }
439 return value;
440 }
441
442 protected String getPwdValue() {
443 String newPwd = null;
444 if (pwdTxt == null || pwd2Txt == null)
445 return null;
446 if (!pwdTxt.getText().equals("") || !pwd2Txt.getText().equals("")) {
447 if (pwdTxt.getText().equals(pwd2Txt.getText())) {
448 newPwd = pwdTxt.getText();
449 pwdTxt.setText("");
450 pwd2Txt.setText("");
451 } else {
452 pwdTxt.setText("");
453 pwd2Txt.setText("");
454 throw new ArgeoException("Passwords are not equals");
455 }
456 }
457 return newPwd;
458 }
459 }
460
461 /**
462 * Displays a list of users with a check box to be able to choose some of
463 * them
464 */
465 private class ChooseUsersWizardPage extends WizardPage implements
466 IPageChangedListener {
467 private static final long serialVersionUID = 1L;
468 private UserTableComposite userTableCmp;
469 private Composite container;
470 private Session session;
471
472 public ChooseUsersWizardPage(Session session) {
473 super("Choose Users");
474 this.session = session;
475 setTitle("Select users who will be impacted");
476 }
477
478 @Override
479 public void createControl(Composite parent) {
480 container = new Composite(parent, SWT.NONE);
481 container.setLayout(new FillLayout());
482 userTableCmp = new MyUserTableCmp(container, SWT.NO_FOCUS, session);
483 userTableCmp.populate(true, true);
484 setControl(container);
485
486 // Add listener to update message when shown
487 final IWizardContainer container = this.getContainer();
488 if (container instanceof IPageChangeProvider) {
489 ((IPageChangeProvider) container).addPageChangedListener(this);
490 }
491
492 }
493
494 @Override
495 public void pageChanged(PageChangedEvent event) {
496 if (event.getSelectedPage() == this) {
497 String msg = "Chosen batch action: "
498 + chooseCommandPage.getCommandLbl();
499 ((WizardPage) event.getSelectedPage()).setMessage(msg);
500 }
501 }
502
503 protected List<Node> getSelectedUsers() {
504 return userTableCmp.getSelectedUsers();
505 }
506
507 private class MyUserTableCmp extends UserTableComposite {
508
509 private static final long serialVersionUID = 1L;
510
511 public MyUserTableCmp(Composite parent, int style, Session session) {
512 super(parent, style, session);
513 }
514
515 @Override
516 protected void refreshFilteredList() {
517 List<Node> nodes = new ArrayList<Node>();
518 try {
519 NodeIterator ni = listFilteredElements(session,
520 getFilterString());
521
522 users: while (ni.hasNext()) {
523 Node currNode = ni.nextNode();
524 String username = currNode.hasProperty(ARGEO_USER_ID) ? currNode
525 .getProperty(ARGEO_USER_ID).getString() : "";
526 if (username.equals(session.getUserID()))
527 continue users;
528 else
529 nodes.add(currNode);
530 }
531 getTableViewer().setInput(nodes.toArray());
532 } catch (RepositoryException e) {
533 throw new ArgeoException("Unable to list users", e);
534 }
535 }
536 }
537 }
538
539 /**
540 * Recapitulation of input data before running real update
541 */
542 private class ValidateAndLaunchWizardPage extends WizardPage implements
543 IPageChangedListener {
544 private static final long serialVersionUID = 1L;
545 private UserTableComposite userTableCmp;
546 private Session session;
547
548 public ValidateAndLaunchWizardPage(Session session) {
549 super("Validate and launch");
550 this.session = session;
551 setTitle("Validate and launch");
552 }
553
554 @Override
555 public void createControl(Composite parent) {
556 Composite mainCmp = new Composite(parent, SWT.NO_FOCUS);
557 mainCmp.setLayout(new FillLayout());
558
559 // Add listener to update user list when shown
560 final IWizardContainer container = this.getContainer();
561 if (container instanceof IPageChangeProvider) {
562 ((IPageChangeProvider) container).addPageChangedListener(this);
563 }
564
565 userTableCmp = new UserTableComposite(mainCmp, SWT.NO_FOCUS,
566 session);
567 userTableCmp.populate(false, false);
568 setControl(mainCmp);
569 }
570
571 @Override
572 public void pageChanged(PageChangedEvent event) {
573 if (event.getSelectedPage() == this) {
574 @SuppressWarnings({ "unchecked", "rawtypes" })
575 Object[] values = ((ArrayList) userListPage.getSelectedUsers())
576 .toArray(new Object[userListPage.getSelectedUsers()
577 .size()]);
578 userTableCmp.getTableViewer().setInput(values);
579 String msg = "Following batch action: ["
580 + chooseCommandPage.getCommandLbl()
581 + "] will be perfomed on the users listed below.\n"
582 + "Are you sure you want to proceed?";
583 ((WizardPage) event.getSelectedPage()).setMessage(msg);
584 }
585 }
586
587 // private class MyUserTableCmp extends UserTableComposite {
588 // public MyUserTableCmp(Composite parent, int style, Session session) {
589 // super(parent, style, session);
590 // }
591 //
592 // @Override
593 // protected void refreshFilteredList() {
594 // @SuppressWarnings({ "unchecked", "rawtypes" })
595 //
596 // setFilteredList(values);
597 // }
598 //
599 // @Override
600 // public void setVisible(boolean visible) {
601 // super.setVisible(visible);
602 // if (visible)
603 // refreshFilteredList();
604 // }
605 // }
606 }
607 }