View Javadoc
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.cms.e4.users;
17  
18  import static org.argeo.cms.auth.UserAdminUtils.getProperty;
19  import static org.argeo.naming.LdapAttrs.cn;
20  import static org.argeo.naming.LdapAttrs.givenName;
21  import static org.argeo.naming.LdapAttrs.mail;
22  import static org.argeo.naming.LdapAttrs.sn;
23  import static org.argeo.naming.LdapAttrs.uid;
24  
25  import java.util.ArrayList;
26  import java.util.Iterator;
27  import java.util.List;
28  
29  import javax.inject.Inject;
30  
31  import org.argeo.api.NodeConstants;
32  import org.argeo.cms.auth.CurrentUser;
33  import org.argeo.cms.auth.UserAdminUtils;
34  import org.argeo.cms.e4.users.providers.CommonNameLP;
35  import org.argeo.cms.e4.users.providers.DomainNameLP;
36  import org.argeo.cms.e4.users.providers.RoleIconLP;
37  import org.argeo.cms.e4.users.providers.UserFilter;
38  import org.argeo.cms.ui.eclipse.forms.AbstractFormPart;
39  //import org.argeo.cms.ui.eclipse.forms.FormToolkit;
40  import org.argeo.cms.ui.eclipse.forms.IManagedForm;
41  import org.argeo.cms.ui.util.CmsUiUtils;
42  import org.argeo.eclipse.ui.ColumnDefinition;
43  import org.argeo.eclipse.ui.EclipseUiUtils;
44  import org.argeo.eclipse.ui.parts.LdifUsersTable;
45  import org.argeo.naming.LdapAttrs;
46  import org.eclipse.e4.ui.workbench.modeling.EPartService;
47  import org.eclipse.jface.action.Action;
48  import org.eclipse.jface.action.ToolBarManager;
49  import org.eclipse.jface.dialogs.MessageDialog;
50  import org.eclipse.jface.dialogs.TrayDialog;
51  import org.eclipse.jface.resource.ImageDescriptor;
52  import org.eclipse.jface.viewers.ISelection;
53  import org.eclipse.jface.viewers.IStructuredSelection;
54  import org.eclipse.jface.viewers.TableViewer;
55  import org.eclipse.jface.viewers.Viewer;
56  import org.eclipse.jface.viewers.ViewerDropAdapter;
57  import org.eclipse.swt.SWT;
58  import org.eclipse.swt.dnd.DND;
59  import org.eclipse.swt.dnd.DropTargetEvent;
60  import org.eclipse.swt.dnd.TextTransfer;
61  import org.eclipse.swt.dnd.Transfer;
62  import org.eclipse.swt.dnd.TransferData;
63  import org.eclipse.swt.events.ModifyEvent;
64  import org.eclipse.swt.events.ModifyListener;
65  import org.eclipse.swt.events.SelectionAdapter;
66  import org.eclipse.swt.events.SelectionEvent;
67  import org.eclipse.swt.layout.GridData;
68  import org.eclipse.swt.layout.GridLayout;
69  import org.eclipse.swt.widgets.Button;
70  import org.eclipse.swt.widgets.Composite;
71  import org.eclipse.swt.widgets.Control;
72  import org.eclipse.swt.widgets.Display;
73  import org.eclipse.swt.widgets.Link;
74  import org.eclipse.swt.widgets.Shell;
75  import org.eclipse.swt.widgets.Text;
76  import org.eclipse.swt.widgets.ToolBar;
77  import org.osgi.service.useradmin.Group;
78  import org.osgi.service.useradmin.Role;
79  import org.osgi.service.useradmin.User;
80  import org.osgi.service.useradmin.UserAdmin;
81  import org.osgi.service.useradmin.UserAdminEvent;
82  
83  /** Display/edit the properties of a given user */
84  public class UserEditor extends AbstractRoleEditor {
85  	// final static String ID = "UserEditor.mainPage";
86  
87  	@Inject
88  	private EPartService partService;
89  
90  	// private final UserEditor editor;
91  	// private UserAdminWrapper userAdminWrapper;
92  
93  	// Local configuration
94  	// private final int PRE_TITLE_INDENT = 10;
95  
96  	// public UserMainPage(FormEditor editor, UserAdminWrapper userAdminWrapper) {
97  	// super(editor, ID, "Main");
98  	// this.editor = (UserEditor) editor;
99  	// this.userAdminWrapper = userAdminWrapper;
100 	// }
101 
102 	// protected void createFormContent(final IManagedForm mf) {
103 	// ScrolledForm form = mf.getForm();
104 	// Composite body = form.getBody();
105 	// GridLayout mainLayout = new GridLayout();
106 	// // mainLayout.marginRight = 10;
107 	// body.setLayout(mainLayout);
108 	// User user = editor.getDisplayedUser();
109 	// appendOverviewPart(body, user);
110 	// // Remove to ability to force the password for his own user. The user
111 	// // must then use the change pwd feature
112 	// appendMemberOfPart(body, user);
113 	// }
114 
115 	@Override
116 	protected void createUi(Composite body) {
117 		// Composite body = new Composite(parent, SWT.BORDER);
118 		GridLayout mainLayout = new GridLayout();
119 		// mainLayout.marginRight = 10;
120 		body.setLayout(mainLayout);
121 		// body.getParent().setLayout(new GridLayout());
122 		// body.setLayoutData(CmsUiUtils.fillAll());
123 		User user = getDisplayedUser();
124 		appendOverviewPart(body, user);
125 		// Remove to ability to force the password for his own user. The user
126 		// must then use the change pwd feature
127 		appendMemberOfPart(body, user);
128 	}
129 
130 	/** Creates the general section */
131 	private void appendOverviewPart(final Composite parent, final User user) {
132 		// FormToolkit tk = getManagedForm().getToolkit();
133 
134 		// Section section = tk.createSection(parent, SWT.NO_FOCUS);
135 		// GridData gd = EclipseUiUtils.fillWidth();
136 		// // gd.verticalAlignment = PRE_TITLE_INDENT;
137 		// section.setLayoutData(gd);
138 		Composite body = new Composite(parent, SWT.NONE);
139 		body.setLayoutData(EclipseUiUtils.fillWidth());
140 		// section.setClient(body);
141 		// body.setLayout(new GridLayout(6, false));
142 		body.setLayout(new GridLayout(2, false));
143 
144 		Text commonName = createReadOnlyLT(body, "Name", getProperty(user, cn));
145 		Text distinguishedName = createReadOnlyLT(body, "Login", getProperty(user, uid));
146 		Text firstName = createLT(body, "First name", getProperty(user, givenName));
147 		Text lastName = createLT(body, "Last name", getProperty(user, sn));
148 		Text email = createLT(body, "Email", getProperty(user, mail));
149 
150 		Link resetPwdLk = new Link(body, SWT.NONE);
151 		if (!UserAdminUtils.isCurrentUser(user)) {
152 			resetPwdLk.setText("<a>Reset password</a>");
153 		}
154 		resetPwdLk.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false, 2, 1));
155 
156 		// create form part (controller)
157 		AbstractFormPart part = new AbstractFormPart() {
158 			private MainInfoListener listener;
159 
160 			@Override
161 			public void initialize(IManagedForm form) {
162 				super.initialize(form);
163 				listener = new MainInfoListener(parent.getDisplay(), this);
164 				userAdminWrapper.addListener(listener);
165 			}
166 
167 			@Override
168 			public void dispose() {
169 				userAdminWrapper.removeListener(listener);
170 				super.dispose();
171 			}
172 
173 			@SuppressWarnings("unchecked")
174 			public void commit(boolean onSave) {
175 				// TODO Sanity checks (mail validity...)
176 				user.getProperties().put(LdapAttrs.givenName.name(), firstName.getText());
177 				user.getProperties().put(LdapAttrs.sn.name(), lastName.getText());
178 				user.getProperties().put(LdapAttrs.cn.name(), commonName.getText());
179 				user.getProperties().put(LdapAttrs.mail.name(), email.getText());
180 				super.commit(onSave);
181 			}
182 
183 			@Override
184 			public void refresh() {
185 				distinguishedName.setText(UserAdminUtils.getProperty(user, LdapAttrs.uid.name()));
186 				commonName.setText(UserAdminUtils.getProperty(user, LdapAttrs.cn.name()));
187 				firstName.setText(UserAdminUtils.getProperty(user, LdapAttrs.givenName.name()));
188 				lastName.setText(UserAdminUtils.getProperty(user, LdapAttrs.sn.name()));
189 				email.setText(UserAdminUtils.getProperty(user, LdapAttrs.mail.name()));
190 				refreshFormTitle(user);
191 				super.refresh();
192 			}
193 		};
194 
195 		// Improve this: automatically generate CN when first or last name
196 		// changes
197 		ModifyListener cnML = new ModifyListener() {
198 			private static final long serialVersionUID = 4298649222869835486L;
199 
200 			@Override
201 			public void modifyText(ModifyEvent event) {
202 				String first = firstName.getText();
203 				String last = lastName.getText();
204 				String cn = first.trim() + " " + last.trim() + " ";
205 				cn = cn.trim();
206 				commonName.setText(cn);
207 				// getManagedForm().getForm().setText(cn);
208 				updateEditorTitle(cn);
209 			}
210 		};
211 		firstName.addModifyListener(cnML);
212 		lastName.addModifyListener(cnML);
213 
214 		ModifyListener defaultListener = new FormPartML(part);
215 		firstName.addModifyListener(defaultListener);
216 		lastName.addModifyListener(defaultListener);
217 		email.addModifyListener(defaultListener);
218 
219 		if (!UserAdminUtils.isCurrentUser(user))
220 			resetPwdLk.addSelectionListener(new SelectionAdapter() {
221 				private static final long serialVersionUID = 5881800534589073787L;
222 
223 				@Override
224 				public void widgetSelected(SelectionEvent e) {
225 					new ChangePasswordDialog(user, "Reset password").open();
226 				}
227 			});
228 
229 		getManagedForm().addPart(part);
230 	}
231 
232 	private class ChangePasswordDialog extends TrayDialog {
233 		private static final long serialVersionUID = 2843538207460082349L;
234 
235 		private User user;
236 		private Text password1;
237 		private Text password2;
238 		private String title;
239 		// private FormToolkit tk;
240 
241 		public ChangePasswordDialog(User user, String title) {
242 			super(Display.getDefault().getActiveShell());
243 			// this.tk = tk;
244 			this.user = user;
245 			this.title = title;
246 		}
247 
248 		protected Control createDialogArea(Composite parent) {
249 			Composite dialogarea = (Composite) super.createDialogArea(parent);
250 			dialogarea.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
251 			Composite body = new Composite(dialogarea, SWT.NO_FOCUS);
252 			body.setLayoutData(EclipseUiUtils.fillAll());
253 			GridLayout layout = new GridLayout(2, false);
254 			body.setLayout(layout);
255 
256 			password1 = createLP(body, "New password", "");
257 			password2 = createLP(body, "Repeat password", "");
258 			parent.pack();
259 			return body;
260 		}
261 
262 		@SuppressWarnings("unchecked")
263 		@Override
264 		protected void okPressed() {
265 			String msg = null;
266 
267 			if (password1.getText().equals(""))
268 				msg = "Password cannot be empty";
269 			else if (password1.getText().equals(password2.getText())) {
270 				char[] newPassword = password1.getText().toCharArray();
271 				// userAdminWrapper.beginTransactionIfNeeded();
272 				userAdminWrapper.beginTransactionIfNeeded();
273 				user.getCredentials().put(null, newPassword);
274 				userAdminWrapper.commitOrNotifyTransactionStateChange();
275 				super.okPressed();
276 			} else {
277 				msg = "Passwords are not equals";
278 			}
279 
280 			if (EclipseUiUtils.notEmpty(msg))
281 				MessageDialog.openError(getParentShell(), "Cannot reset pasword", msg);
282 		}
283 
284 		protected void configureShell(Shell shell) {
285 			super.configureShell(shell);
286 			shell.setText(title);
287 		}
288 	}
289 
290 	private LdifUsersTable appendMemberOfPart(final Composite parent, User user) {
291 		// Section section = addSection(tk, parent, "Roles");
292 		// Composite body = (Composite) section.getClient();
293 		// Composite body= parent;
294 		Composite body = new Composite(parent, SWT.BORDER);
295 		body.setLayout(new GridLayout());
296 		body.setLayoutData(CmsUiUtils.fillAll());
297 
298 		// boolean isAdmin = CurrentUser.isInRole(NodeConstants.ROLE_ADMIN);
299 
300 		// Displayed columns
301 		List<ColumnDefinition> columnDefs = new ArrayList<ColumnDefinition>();
302 		columnDefs.add(new ColumnDefinition(new RoleIconLP(), "", 0, 24));
303 		columnDefs.add(new ColumnDefinition(new CommonNameLP(), "Name", 150));
304 		columnDefs.add(new ColumnDefinition(new DomainNameLP(), "Domain", 100));
305 		// Only show technical DN to administrators
306 		// if (isAdmin)
307 		// columnDefs.add(new ColumnDefinition(new UserNameLP(), "Distinguished Name",
308 		// 300));
309 
310 		// Create and configure the table
311 		final LdifUsersTable userViewerCmp = new MyUserTableViewer(body, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL, user);
312 
313 		userViewerCmp.setColumnDefinitions(columnDefs);
314 		// if (isAdmin)
315 		// userViewerCmp.populateWithStaticFilters(false, false);
316 		// else
317 		userViewerCmp.populate(true, false);
318 		GridData gd = EclipseUiUtils.fillAll();
319 		gd.heightHint = 500;
320 		userViewerCmp.setLayoutData(gd);
321 
322 		// Controllers
323 		TableViewer userViewer = userViewerCmp.getTableViewer();
324 		userViewer.addDoubleClickListener(new UserTableDefaultDClickListener(partService));
325 		int operations = DND.DROP_COPY | DND.DROP_MOVE;
326 		Transfer[] tt = new Transfer[] { TextTransfer.getInstance() };
327 		GroupDropListener dropL = new GroupDropListener(userAdminWrapper, userViewer, user);
328 		userViewer.addDropSupport(operations, tt, dropL);
329 
330 		AbstractFormPart part = new AbstractFormPart() {
331 
332 			private GroupChangeListener listener;
333 
334 			@Override
335 			public void initialize(IManagedForm form) {
336 				super.initialize(form);
337 				listener = new GroupChangeListener(parent.getDisplay(), this);
338 				userAdminWrapper.addListener(listener);
339 			}
340 
341 			public void commit(boolean onSave) {
342 				super.commit(onSave);
343 			}
344 
345 			@Override
346 			public void dispose() {
347 				userAdminWrapper.removeListener(listener);
348 				super.dispose();
349 			}
350 
351 			@Override
352 			public void refresh() {
353 				userViewerCmp.refresh();
354 				super.refresh();
355 			}
356 		};
357 		getManagedForm().addPart(part);
358 		// addRemoveAbitily(body, userViewer, user);
359 		// userViewerCmp.refresh();
360 		String tooltip = "Remove " + UserAdminUtils.getUserLocalId(user.getName()) + " from the below selected groups";
361 		Action action = new RemoveMembershipAction(userViewer, user, tooltip, SecurityAdminImages.ICON_REMOVE_DESC);
362 		ToolBarManager toolBarManager = new ToolBarManager(SWT.FLAT);
363 		ToolBar toolBar = toolBarManager.createControl(body);
364 		toolBar.setLayoutData(CmsUiUtils.fillWidth());
365 		toolBarManager.add(action);
366 		toolBarManager.update(true);
367 		return userViewerCmp;
368 	}
369 
370 	private class MyUserTableViewer extends LdifUsersTable {
371 		private static final long serialVersionUID = 2653790051461237329L;
372 
373 		private Button showSystemRoleBtn;
374 
375 		private final User user;
376 		private final UserFilter userFilter;
377 
378 		public MyUserTableViewer(Composite parent, int style, User user) {
379 			super(parent, style, true);
380 			this.user = user;
381 			userFilter = new UserFilter();
382 		}
383 
384 		protected void populateStaticFilters(Composite staticFilterCmp) {
385 			staticFilterCmp.setLayout(new GridLayout());
386 			showSystemRoleBtn = new Button(staticFilterCmp, SWT.CHECK);
387 			showSystemRoleBtn.setText("Show system roles");
388 			boolean showSysRole = CurrentUser.isInRole(NodeConstants.ROLE_ADMIN);
389 			showSystemRoleBtn.setSelection(showSysRole);
390 			userFilter.setShowSystemRole(showSysRole);
391 			showSystemRoleBtn.addSelectionListener(new SelectionAdapter() {
392 				private static final long serialVersionUID = -7033424592697691676L;
393 
394 				@Override
395 				public void widgetSelected(SelectionEvent e) {
396 					userFilter.setShowSystemRole(showSystemRoleBtn.getSelection());
397 					refresh();
398 				}
399 			});
400 		}
401 
402 		@Override
403 		protected List<User> listFilteredElements(String filter) {
404 			List<User> users = (List<User>) getFlatGroups(null);
405 			List<User> filteredUsers = new ArrayList<User>();
406 			if (users.contains(user))
407 				users.remove(user);
408 			userFilter.setSearchText(filter);
409 			for (User user : users)
410 				if (userFilter.select(null, null, user))
411 					filteredUsers.add(user);
412 			return filteredUsers;
413 		}
414 	}
415 
416 	// private void addRemoveAbility(Composite parent, TableViewer userViewer, User
417 	// user) {
418 	// // Section section = sectionPart.getSection();
419 	// ToolBarManager toolBarManager = new ToolBarManager(SWT.FLAT);
420 	// ToolBar toolbar = toolBarManager.createControl(parent);
421 	// final Cursor handCursor = new Cursor(Display.getCurrent(), SWT.CURSOR_HAND);
422 	// toolbar.setCursor(handCursor);
423 	// toolbar.addDisposeListener(new DisposeListener() {
424 	// private static final long serialVersionUID = 3882131405820522925L;
425 	//
426 	// public void widgetDisposed(DisposeEvent e) {
427 	// if ((handCursor != null) && (handCursor.isDisposed() == false)) {
428 	// handCursor.dispose();
429 	// }
430 	// }
431 	// });
432 	//
433 	// String tooltip = "Remove " + UserAdminUtils.getUserLocalId(user.getName()) +
434 	// " from the below selected groups";
435 	// Action action = new RemoveMembershipAction(userViewer, user, tooltip,
436 	// SecurityAdminImages.ICON_REMOVE_DESC);
437 	// toolBarManager.add(action);
438 	// toolBarManager.update(true);
439 	// // section.setTextClient(toolbar);
440 	// }
441 
442 	private class RemoveMembershipAction extends Action {
443 		private static final long serialVersionUID = -1337713097184522588L;
444 
445 		private final TableViewer userViewer;
446 		private final User user;
447 
448 		RemoveMembershipAction(TableViewer userViewer, User user, String name, ImageDescriptor img) {
449 			super(name, img);
450 			this.userViewer = userViewer;
451 			this.user = user;
452 		}
453 
454 		@Override
455 		public void run() {
456 			ISelection selection = userViewer.getSelection();
457 			if (selection.isEmpty())
458 				return;
459 
460 			@SuppressWarnings("unchecked")
461 			Iterator<Group> it = ((IStructuredSelection) selection).iterator();
462 			List<Group> groups = new ArrayList<Group>();
463 			while (it.hasNext()) {
464 				Group currGroup = it.next();
465 				groups.add(currGroup);
466 			}
467 
468 			userAdminWrapper.beginTransactionIfNeeded();
469 			for (Group group : groups) {
470 				group.removeMember(user);
471 			}
472 			userAdminWrapper.commitOrNotifyTransactionStateChange();
473 			for (Group group : groups) {
474 				userAdminWrapper.notifyListeners(new UserAdminEvent(null, UserAdminEvent.ROLE_CHANGED, group));
475 			}
476 		}
477 	}
478 
479 	/**
480 	 * Defines the table as being a potential target to add group memberships
481 	 * (roles) to this user
482 	 */
483 	private class GroupDropListener extends ViewerDropAdapter {
484 		private static final long serialVersionUID = 2893468717831451621L;
485 
486 		private final UserAdminWrapper myUserAdminWrapper;
487 		private final User myUser;
488 
489 		public GroupDropListener(UserAdminWrapper userAdminWrapper, Viewer userViewer, User user) {
490 			super(userViewer);
491 			this.myUserAdminWrapper = userAdminWrapper;
492 			this.myUser = user;
493 		}
494 
495 		@Override
496 		public boolean validateDrop(Object target, int operation, TransferData transferType) {
497 			// Target is always OK in a list only view
498 			// TODO check if not a string
499 			boolean validDrop = true;
500 			return validDrop;
501 		}
502 
503 		@Override
504 		public void drop(DropTargetEvent event) {
505 			String name = (String) event.data;
506 			UserAdmin myUserAdmin = myUserAdminWrapper.getUserAdmin();
507 			Role role = myUserAdmin.getRole(name);
508 			// TODO this check should be done before.
509 			if (role.getType() == Role.GROUP) {
510 				// TODO check if the user is already member of this group
511 
512 				myUserAdminWrapper.beginTransactionIfNeeded();
513 				Group group = (Group) role;
514 				group.addMember(myUser);
515 				userAdminWrapper.commitOrNotifyTransactionStateChange();
516 				myUserAdminWrapper.notifyListeners(new UserAdminEvent(null, UserAdminEvent.ROLE_CHANGED, group));
517 			}
518 			super.drop(event);
519 		}
520 
521 		@Override
522 		public boolean performDrop(Object data) {
523 			// userTableViewerCmp.refresh();
524 			return true;
525 		}
526 	}
527 
528 	// LOCAL HELPERS
529 	private void refreshFormTitle(User group) {
530 		// getManagedForm().getForm().setText(UserAdminUtils.getProperty(group,
531 		// LdapAttrs.cn.name()));
532 	}
533 
534 	/** Appends a section with a title */
535 	// private Section addSection(FormToolkit tk, Composite parent, String title) {
536 	// Section section = tk.createSection(parent, Section.TITLE_BAR);
537 	// GridData gd = EclipseUiUtils.fillWidth();
538 	// gd.verticalAlignment = PRE_TITLE_INDENT;
539 	// section.setLayoutData(gd);
540 	// section.setText(title);
541 	// // section.getMenu().setVisible(true);
542 	//
543 	// Composite body = tk.createComposite(section, SWT.WRAP);
544 	// body.setLayoutData(EclipseUiUtils.fillAll());
545 	// section.setClient(body);
546 	//
547 	// return section;
548 	// }
549 
550 }