From 4d665b0700136573b05879aa19897d8f448d8ca4 Mon Sep 17 00:00:00 2001 From: Mathieu Baudier Date: Thu, 19 Jan 2012 21:49:29 +0000 Subject: [PATCH] Improve user admin git-svn-id: https://svn.argeo.org/commons/trunk@4990 4cfe0d0a-d680-48aa-b62c-e0a02a3f76cc --- .../org/argeo/eclipse/ui/EclipseUiUtils.java | 17 ++ .../icons/sync.gif | Bin 0 -> 353 bytes .../org.argeo.security.ui.admin/plugin.xml | 12 +- .../org.argeo.security.ui.admin/pom.xml | 6 + .../ui/admin/SecurityAdminPerspective.java | 2 +- .../ui/admin/editors/ArgeoUserEditor.java | 16 +- .../ui/admin/editors/DefaultUserMainPage.java | 28 +- .../security/ui/admin/views/UsersView.java | 265 ++++++++++++++---- .../admin/wizards/MainUserInfoWizardPage.java | 6 +- .../org/argeo/security/UserAdminService.java | 4 +- .../argeo/security/jcr/JcrUserDetails.java | 20 ++ .../jcr/OsJcrAuthenticationProvider.java | 15 +- .../ldap/jcr/JcrLdapSynchronizer.java | 21 +- server/dep/org.argeo.server.dep.ads/pom.xml | 16 +- .../argeo/jackrabbit/JackrabbitWrapper.java | 4 +- .../src/main/java/org/argeo/jcr/JcrUtils.java | 65 ++++- 16 files changed, 379 insertions(+), 118 deletions(-) create mode 100644 security/plugins/org.argeo.security.ui.admin/icons/sync.gif diff --git a/eclipse/runtime/org.argeo.eclipse.ui/src/main/java/org/argeo/eclipse/ui/EclipseUiUtils.java b/eclipse/runtime/org.argeo.eclipse.ui/src/main/java/org/argeo/eclipse/ui/EclipseUiUtils.java index e70d819d1..5c85802c3 100644 --- a/eclipse/runtime/org.argeo.eclipse.ui/src/main/java/org/argeo/eclipse/ui/EclipseUiUtils.java +++ b/eclipse/runtime/org.argeo.eclipse.ui/src/main/java/org/argeo/eclipse/ui/EclipseUiUtils.java @@ -1,7 +1,9 @@ package org.argeo.eclipse.ui; +import org.eclipse.jface.resource.JFaceResources; import org.eclipse.swt.SWT; import org.eclipse.swt.events.ModifyListener; +import org.eclipse.swt.graphics.Font; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Label; @@ -46,4 +48,19 @@ public class EclipseUiUtils { return txt; } + public static Font getItalicFont(Composite parent) { + return JFaceResources.getFontRegistry().defaultFontDescriptor() + .setStyle(SWT.ITALIC).createFont(parent.getDisplay()); + } + + public static Font getBoldFont(Composite parent) { + return JFaceResources.getFontRegistry().defaultFontDescriptor() + .setStyle(SWT.BOLD).createFont(parent.getDisplay()); + } + + public static Font getBoldItalicFont(Composite parent) { + return JFaceResources.getFontRegistry().defaultFontDescriptor() + .setStyle(SWT.BOLD | SWT.ITALIC).createFont(parent.getDisplay()); + } + } diff --git a/security/plugins/org.argeo.security.ui.admin/icons/sync.gif b/security/plugins/org.argeo.security.ui.admin/icons/sync.gif new file mode 100644 index 0000000000000000000000000000000000000000..b4fa052de1013330d4541abd6d4d72292bf0cc20 GIT binary patch literal 353 zcmV-n0iOOxNk%w1VGsZi0M!5hiG+nnZ@^M!k4#^MRce%2ZJ1SRlUs0_Oks#mWQ$T~ zk6m+}NM)9Fe~(LVqH(I_lEUw!%kiq&`hL0K>G=Qg`u@@K|HtX`{P>*w_?`LfS^V{z z_T^;uAfoP!!Pl}FvXe~_`xH@ zmmKcCF2Rx@rj%^HgcZJxCc20#xrZy?j}NwkF}8v+vwb$Qem1dvI{*Lw0000000000 z00000A^8LW0027xEC2ui01yBW000Jlz@2bNEE + icon="icons/sync.gif" + label="LDAP Roles Sync" + tooltip="Synchronize roles from LDAP"> + icon="icons/sync.gif" + label="LDAP Users Sync" + tooltip="Synchronize users from LDAP"> diff --git a/security/plugins/org.argeo.security.ui.admin/pom.xml b/security/plugins/org.argeo.security.ui.admin/pom.xml index 4a4e0be59..05bf5b76c 100644 --- a/security/plugins/org.argeo.security.ui.admin/pom.xml +++ b/security/plugins/org.argeo.security.ui.admin/pom.xml @@ -69,6 +69,12 @@ 0.3.4-SNAPSHOT provided + + org.argeo.commons.eclipse + org.argeo.eclipse.ui.rcp + 0.3.4-SNAPSHOT + provided + diff --git a/security/plugins/org.argeo.security.ui.admin/src/main/java/org/argeo/security/ui/admin/SecurityAdminPerspective.java b/security/plugins/org.argeo.security.ui.admin/src/main/java/org/argeo/security/ui/admin/SecurityAdminPerspective.java index 4c17bbca6..f6cb925d7 100644 --- a/security/plugins/org.argeo.security.ui.admin/src/main/java/org/argeo/security/ui/admin/SecurityAdminPerspective.java +++ b/security/plugins/org.argeo.security.ui.admin/src/main/java/org/argeo/security/ui/admin/SecurityAdminPerspective.java @@ -13,7 +13,7 @@ public class SecurityAdminPerspective implements IPerspectiveFactory { layout.setFixed(false); IFolderLayout left = layout.createFolder("left", IPageLayout.LEFT, - 0.4f, editorArea); + 0.65f, editorArea); left.addView(UsersView.ID); left.addView(RolesView.ID); } diff --git a/security/plugins/org.argeo.security.ui.admin/src/main/java/org/argeo/security/ui/admin/editors/ArgeoUserEditor.java b/security/plugins/org.argeo.security.ui.admin/src/main/java/org/argeo/security/ui/admin/editors/ArgeoUserEditor.java index b511e683c..87b241867 100644 --- a/security/plugins/org.argeo.security.ui.admin/src/main/java/org/argeo/security/ui/admin/editors/ArgeoUserEditor.java +++ b/security/plugins/org.argeo.security.ui.admin/src/main/java/org/argeo/security/ui/admin/editors/ArgeoUserEditor.java @@ -1,6 +1,7 @@ package org.argeo.security.ui.admin.editors; import javax.jcr.Node; +import javax.jcr.RepositoryException; import javax.jcr.Session; import org.argeo.ArgeoException; @@ -16,6 +17,7 @@ import org.eclipse.ui.IEditorSite; import org.eclipse.ui.IWorkbench; import org.eclipse.ui.PartInitException; import org.eclipse.ui.forms.editor.FormEditor; +import org.springframework.security.GrantedAuthority; /** Editor for an Argeo user. */ public class ArgeoUserEditor extends FormEditor { @@ -33,8 +35,18 @@ public class ArgeoUserEditor extends FormEditor { .getUsername(); userHome = JcrUtils.getUserHome(session, username); - userDetails = (JcrUserDetails) userAdminService - .loadUserByUsername(username); + if (userAdminService.userExists(username)) { + userDetails = (JcrUserDetails) userAdminService + .loadUserByUsername(username); + } else { + GrantedAuthority[] authorities = {}; + try { + userDetails = new JcrUserDetails(session, username, null, + authorities); + } catch (RepositoryException e) { + throw new ArgeoException("Cannot retrieve disabled JCR profile"); + } + } this.setPartProperty("name", username != null ? username : ""); setPartName(username != null ? username : ""); diff --git a/security/plugins/org.argeo.security.ui.admin/src/main/java/org/argeo/security/ui/admin/editors/DefaultUserMainPage.java b/security/plugins/org.argeo.security.ui.admin/src/main/java/org/argeo/security/ui/admin/editors/DefaultUserMainPage.java index 358135419..9d08d89ca 100644 --- a/security/plugins/org.argeo.security.ui.admin/src/main/java/org/argeo/security/ui/admin/editors/DefaultUserMainPage.java +++ b/security/plugins/org.argeo.security.ui.admin/src/main/java/org/argeo/security/ui/admin/editors/DefaultUserMainPage.java @@ -46,8 +46,7 @@ public class DefaultUserMainPage extends FormPage implements ArgeoNames { protected void createFormContent(final IManagedForm mf) { try { ScrolledForm form = mf.getForm(); - form.setText(getProperty(ARGEO_FIRST_NAME) + " " - + getProperty(ARGEO_LAST_NAME)); + refreshFormTitle(form); GridLayout mainLayout = new GridLayout(1, true); form.getBody().setLayout(mainLayout); @@ -71,13 +70,15 @@ public class DefaultUserMainPage extends FormPage implements ArgeoNames { body.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false)); body.setLayout(layout); + final Text commonName = createLT(body, "Displayed Name", + getProperty(Property.JCR_TITLE)); final Text firstName = createLT(body, "First name", getProperty(ARGEO_FIRST_NAME)); final Text lastName = createLT(body, "Last name", getProperty(ARGEO_LAST_NAME)); final Text email = createLT(body, "Email", getProperty(ARGEO_PRIMARY_EMAIL)); - final Text description = createLT(body, "Description", + final Text description = createLMT(body, "Description", getProperty(Property.JCR_DESCRIPTION)); // create form part (controller) @@ -86,6 +87,8 @@ public class DefaultUserMainPage extends FormPage implements ArgeoNames { try { userProfile.getSession().getWorkspace().getVersionManager() .checkout(userProfile.getPath()); + userProfile.setProperty(Property.JCR_TITLE, + commonName.getText()); userProfile.setProperty(ARGEO_FIRST_NAME, firstName.getText()); userProfile @@ -98,6 +101,7 @@ public class DefaultUserMainPage extends FormPage implements ArgeoNames { userProfile.getSession().getWorkspace().getVersionManager() .checkin(userProfile.getPath()); super.commit(onSave); + refreshFormTitle(getManagedForm().getForm()); if (log.isTraceEnabled()) log.trace("General part committed"); } catch (RepositoryException e) { @@ -114,7 +118,13 @@ public class DefaultUserMainPage extends FormPage implements ArgeoNames { getManagedForm().addPart(part); } - /** @return the property, or teh empty string if not set */ + private void refreshFormTitle(ScrolledForm form) throws RepositoryException { + form.setText(getProperty(Property.JCR_TITLE) + + (userProfile.getProperty(ARGEO_ENABLED).getBoolean() ? "" + : " [DISABLED]")); + } + + /** @return the property, or the empty string if not set */ protected String getProperty(String name) throws RepositoryException { return userProfile.hasProperty(name) ? userProfile.getProperty(name) .getString() : ""; @@ -171,6 +181,16 @@ public class DefaultUserMainPage extends FormPage implements ArgeoNames { return text; } + /** Creates label and multiline text. */ + protected Text createLMT(Composite body, String label, String value) { + FormToolkit toolkit = getManagedForm().getToolkit(); + Label lbl = toolkit.createLabel(body, label); + lbl.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false)); + Text text = toolkit.createText(body, value, SWT.BORDER | SWT.MULTI); + text.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, true)); + return text; + } + /** Creates label and password. */ protected Text createLP(Composite body, String label, String value) { FormToolkit toolkit = getManagedForm().getToolkit(); diff --git a/security/plugins/org.argeo.security.ui.admin/src/main/java/org/argeo/security/ui/admin/views/UsersView.java b/security/plugins/org.argeo.security.ui.admin/src/main/java/org/argeo/security/ui/admin/views/UsersView.java index a41f20af5..523b958ef 100644 --- a/security/plugins/org.argeo.security.ui.admin/src/main/java/org/argeo/security/ui/admin/views/UsersView.java +++ b/security/plugins/org.argeo.security.ui.admin/src/main/java/org/argeo/security/ui/admin/views/UsersView.java @@ -5,34 +5,39 @@ import java.util.List; import javax.jcr.Node; import javax.jcr.NodeIterator; +import javax.jcr.Property; import javax.jcr.RepositoryException; import javax.jcr.Session; +import javax.jcr.observation.Event; import javax.jcr.observation.EventIterator; import javax.jcr.observation.EventListener; import javax.jcr.query.Query; import org.argeo.ArgeoException; +import org.argeo.eclipse.ui.EclipseUiUtils; +import org.argeo.eclipse.ui.specific.EclipseUiSpecificUtils; import org.argeo.jcr.ArgeoNames; import org.argeo.jcr.ArgeoTypes; +import org.argeo.jcr.JcrUtils; import org.argeo.security.ui.admin.SecurityAdminPlugin; import org.argeo.security.ui.admin.commands.OpenArgeoUserEditor; import org.eclipse.core.commands.Command; import org.eclipse.core.commands.IParameter; import org.eclipse.core.commands.Parameterization; import org.eclipse.core.commands.ParameterizedCommand; +import org.eclipse.jface.resource.JFaceResources; +import org.eclipse.jface.viewers.ColumnLabelProvider; import org.eclipse.jface.viewers.DoubleClickEvent; import org.eclipse.jface.viewers.IDoubleClickListener; import org.eclipse.jface.viewers.IStructuredContentProvider; import org.eclipse.jface.viewers.IStructuredSelection; -import org.eclipse.jface.viewers.ITableLabelProvider; -import org.eclipse.jface.viewers.LabelProvider; import org.eclipse.jface.viewers.TableViewer; +import org.eclipse.jface.viewers.TableViewerColumn; import org.eclipse.jface.viewers.Viewer; import org.eclipse.swt.SWT; -import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.graphics.Font; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Table; -import org.eclipse.swt.widgets.TableColumn; import org.eclipse.ui.IWorkbench; import org.eclipse.ui.IWorkbenchWindow; import org.eclipse.ui.commands.ICommandService; @@ -40,47 +45,200 @@ import org.eclipse.ui.handlers.IHandlerService; import org.eclipse.ui.part.ViewPart; /** List all users. */ -public class UsersView extends ViewPart implements ArgeoNames, ArgeoTypes, - EventListener { +public class UsersView extends ViewPart implements ArgeoNames, ArgeoTypes { public final static String ID = "org.argeo.security.ui.admin.adminUsersView"; private TableViewer viewer; private Session session; + private UserStructureListener userStructureListener; + + private Font italic; + private Font bold; + @Override public void createPartControl(Composite parent) { - viewer = new TableViewer(createTable(parent)); + italic = EclipseUiUtils.getItalicFont(parent); + bold = EclipseUiUtils.getBoldFont(parent); + + // viewer = new TableViewer(createTable(parent)); + viewer = createTableViewer(parent); + EclipseUiSpecificUtils.enableToolTipSupport(viewer); viewer.setContentProvider(new UsersContentProvider()); - viewer.setLabelProvider(new UsersLabelProvider()); + // viewer.setLabelProvider(new UsersLabelProvider()); viewer.addDoubleClickListener(new ViewDoubleClickListener()); getViewSite().setSelectionProvider(viewer); viewer.setInput(getViewSite()); + + userStructureListener = new UserStructureListener(); + JcrUtils.addListener(session, userStructureListener, Event.NODE_ADDED + | Event.NODE_REMOVED, JcrUtils.DEFAULT_HOME_BASE_PATH, + ArgeoTypes.ARGEO_USER_HOME); } - protected Table createTable(Composite parent) { + protected TableViewer createTableViewer(final Composite parent) { + Table table = new Table(parent, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL); + TableViewer viewer = new TableViewer(table); table.setLinesVisible(true); table.setHeaderVisible(true); - TableColumn column = new TableColumn(table, SWT.LEFT, 0); - column.setText("User"); - column.setWidth(50); - column = new TableColumn(table, SWT.LEFT, 1); - column.setText("First Name"); - column.setWidth(100); - column = new TableColumn(table, SWT.LEFT, 2); - column.setText("Last Name"); - column.setWidth(100); - column = new TableColumn(table, SWT.LEFT, 3); - column.setText("E-mail"); - column.setWidth(100); - return table; + + // User ID + TableViewerColumn column = new TableViewerColumn(viewer, SWT.NONE); + column.getColumn().setText("User ID"); + column.getColumn().setWidth(100); + column.setLabelProvider(new CLProvider() { + public String getText(Object elem) { + return getProperty(elem, ARGEO_USER_ID); + // if (username.equals(session.getUserID())) + // return "[" + username + "]"; + // else + // return username; + } + }); + + // Displayed name + column = new TableViewerColumn(viewer, SWT.NONE); + column.getColumn().setText("Name"); + column.getColumn().setWidth(150); + column.setLabelProvider(new CLProvider() { + public String getText(Object elem) { + return getProperty(elem, Property.JCR_TITLE); + } + }); + + // E-mail + column = new TableViewerColumn(viewer, SWT.NONE); + column.getColumn().setText("E-mail"); + column.getColumn().setWidth(150); + column.setLabelProvider(new CLProvider() { + public String getText(Object elem) { + return getProperty(elem, ARGEO_PRIMARY_EMAIL); + } + }); + + // E-mail + column = new TableViewerColumn(viewer, SWT.NONE); + column.getColumn().setText("Description"); + column.getColumn().setWidth(200); + column.setLabelProvider(new CLProvider() { + public String getText(Object elem) { + return getProperty(elem, Property.JCR_DESCRIPTION); + } + }); + + return viewer; + } + + private class CLProvider extends ColumnLabelProvider { + + public String getToolTipText(Object element) { + return getText(element); + } + + @Override + public Font getFont(Object elem) { + // self + String username = getProperty(elem, ARGEO_USER_ID); + if (username.equals(session.getUserID())) + return bold; + + // disabled + try { + Node userHome = (Node) elem; + Node userProfile = userHome.getNode(ARGEO_PROFILE); + if (!userProfile.getProperty(ARGEO_ENABLED).getBoolean()) + return italic; + else + return null; + } catch (RepositoryException e) { + throw new ArgeoException("Cannot get font for " + username, e); + } + } + } + // protected Table createTable(Composite parent) { + // // TODO use a more flexible API + // Table table = new Table(parent, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL); + // table.setLinesVisible(true); + // table.setHeaderVisible(true); + // TableColumn column = new TableColumn(table, SWT.LEFT, 0); + // column.setText("Username"); + // column.setWidth(100); + // column = new TableColumn(table, SWT.LEFT, 1); + // column.setText("Displayed name"); + // column.setWidth(150); + // column = new TableColumn(table, SWT.LEFT, 2); + // column.setText("E-mail"); + // column.setWidth(100); + // column = new TableColumn(table, SWT.LEFT, 3); + // column.setText("First Name"); + // column.setWidth(100); + // column = new TableColumn(table, SWT.LEFT, 4); + // column.setText("Last Name"); + // column.setWidth(100); + // column = new TableColumn(table, SWT.LEFT, 5); + // column.setText("Status"); + // column.setWidth(50); + // column = new TableColumn(table, SWT.LEFT, 6); + // column.setText("Description"); + // column.setWidth(200); + // return table; + // } + + // private class UsersLabelProvider extends LabelProvider implements + // ITableLabelProvider { + // public String getColumnText(Object element, int columnIndex) { + // try { + // Node userHome = (Node) element; + // Node userProfile = userHome.getNode(ARGEO_PROFILE); + // switch (columnIndex) { + // case 0: + // String username = userHome.getProperty(ARGEO_USER_ID) + // .getString(); + // if (username.equals(session.getUserID())) + // return "[" + username + "]"; + // else + // return username; + // case 1: + // return getProperty(userProfile, Property.JCR_TITLE); + // case 2: + // return getProperty(userProfile, ARGEO_PRIMARY_EMAIL); + // case 3: + // return getProperty(userProfile, ARGEO_FIRST_NAME); + // case 4: + // return getProperty(userProfile, ARGEO_LAST_NAME); + // case 5: + // return userProfile.getProperty(ARGEO_ENABLED).getBoolean() ? "" + // : "disabled"; + // case 6: + // return getProperty(userProfile, Property.JCR_DESCRIPTION); + // default: + // throw new ArgeoException("Unmanaged column " + columnIndex); + // } + // } catch (RepositoryException e) { + // throw new ArgeoException("Cannot get text", e); + // } + // } + // + // public Image getColumnImage(Object element, int columnIndex) { + // return null; + // } + // + // } + @Override public void setFocus() { viewer.getTable().setFocus(); } + @Override + public void dispose() { + JcrUtils.removeListenerQuietly(session, userStructureListener); + super.dispose(); + } + public void setSession(Session session) { this.session = session; } @@ -89,9 +247,29 @@ public class UsersView extends ViewPart implements ArgeoNames, ArgeoTypes, viewer.refresh(); } - @Override - public void onEvent(EventIterator events) { - viewer.refresh(); + protected String getProperty(Node userProfile, String name) + throws RepositoryException { + return userProfile.hasProperty(name) ? userProfile.getProperty(name) + .getString() : ""; + } + + protected String getProperty(Object element, String name) { + try { + Node userHome = (Node) element; + Node userProfile = userHome.getNode(ARGEO_PROFILE); + return userProfile.hasProperty(name) ? userProfile + .getProperty(name).getString() : ""; + } catch (RepositoryException e) { + throw new ArgeoException("Cannot get property " + name, e); + } + } + + private class UserStructureListener implements EventListener { + + @Override + public void onEvent(EventIterator events) { + viewer.refresh(); + } } private class UsersContentProvider implements IStructuredContentProvider { @@ -124,43 +302,6 @@ public class UsersView extends ViewPart implements ArgeoNames, ArgeoTypes, } - private class UsersLabelProvider extends LabelProvider implements - ITableLabelProvider { - public String getColumnText(Object element, int columnIndex) { - try { - Node userHome = (Node) element; - Node userProfile = userHome.getNode(ARGEO_PROFILE); - switch (columnIndex) { - case 0: - String userName = userHome.getProperty(ARGEO_USER_ID) - .getString(); - if (userName.equals(session.getUserID())) - return "[" + userName + "]"; - else - return userName; - case 1: - return userProfile.hasProperty(ARGEO_FIRST_NAME) ? userProfile - .getProperty(ARGEO_FIRST_NAME).getString() : ""; - case 2: - return userProfile.hasProperty(ARGEO_LAST_NAME) ? userProfile - .getProperty(ARGEO_LAST_NAME).getString() : ""; - case 3: - return userProfile.hasProperty(ARGEO_PRIMARY_EMAIL) ? userProfile - .getProperty(ARGEO_PRIMARY_EMAIL).getString() : ""; - default: - throw new ArgeoException("Unmanaged column " + columnIndex); - } - } catch (RepositoryException e) { - throw new ArgeoException("Cannot get text", e); - } - } - - public Image getColumnImage(Object element, int columnIndex) { - return null; - } - - } - class ViewDoubleClickListener implements IDoubleClickListener { public void doubleClick(DoubleClickEvent evt) { if (evt.getSelection().isEmpty()) diff --git a/security/plugins/org.argeo.security.ui.admin/src/main/java/org/argeo/security/ui/admin/wizards/MainUserInfoWizardPage.java b/security/plugins/org.argeo.security.ui.admin/src/main/java/org/argeo/security/ui/admin/wizards/MainUserInfoWizardPage.java index e00decd53..496979b84 100644 --- a/security/plugins/org.argeo.security.ui.admin/src/main/java/org/argeo/security/ui/admin/wizards/MainUserInfoWizardPage.java +++ b/security/plugins/org.argeo.security.ui.admin/src/main/java/org/argeo/security/ui/admin/wizards/MainUserInfoWizardPage.java @@ -57,12 +57,12 @@ public class MainUserInfoWizardPage extends WizardPage implements /** @return error message or null if complete */ protected String checkComplete() { - if (!username.getText().matches(UserAdminService.USERNAME_PATTERN)) - return "Wrong user name format, should be lower case, between 3 and 15 characters with only '_' as acceptable special character."; +// if (!username.getText().matches(UserAdminService.USERNAME_PATTERN)) +// return "Wrong user name format, should be lower case, between 3 and 64 characters with only '_' an '@' as acceptable special character."; try { UserDetails userDetails = userAdminService .loadUserByUsername(username.getText()); - return "User " + userDetails.getUsername() + " alreayd exists"; + return "User " + userDetails.getUsername() + " already exists"; } catch (UsernameNotFoundException e) { // silent } diff --git a/security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/UserAdminService.java b/security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/UserAdminService.java index 01e7349cc..65bc4bb25 100644 --- a/security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/UserAdminService.java +++ b/security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/UserAdminService.java @@ -10,9 +10,9 @@ public interface UserAdminService extends UserDetailsManager { * Usernames must match this regexp pattern ({@value #USERNAME_PATTERN}). * Thanks to this tip (modified to remove '-' and add upper-case) + * >this tip (modified to add upper-case, add '@') */ - public final static String USERNAME_PATTERN = "^[a-zA-Z0-9_]{3,15}$"; + //public final static String USERNAME_PATTERN = "^[a-zA-Z0-9_-@]{3,64}$"; /** * Email addresses must match this regexp pattern ({@value #EMAIL_PATTERN}. diff --git a/security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/jcr/JcrUserDetails.java b/security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/jcr/JcrUserDetails.java index a59eabc0a..05ae165e0 100644 --- a/security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/jcr/JcrUserDetails.java +++ b/security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/jcr/JcrUserDetails.java @@ -5,8 +5,10 @@ import java.util.List; import javax.jcr.Node; import javax.jcr.RepositoryException; +import javax.jcr.Session; import org.argeo.jcr.ArgeoNames; +import org.argeo.jcr.JcrUtils; import org.springframework.security.BadCredentialsException; import org.springframework.security.DisabledException; import org.springframework.security.GrantedAuthority; @@ -46,6 +48,24 @@ public class JcrUserDetails extends User implements ArgeoNames { securityWorkspace = userProfile.getSession().getWorkspace().getName(); } + /** + * Convenience constructor + * + * @param session + * the security session + * @param username + * the username + * @param password + * the password, can be null + * @param authorities + * the granted authorities + */ + public JcrUserDetails(Session session, String username, String password, + GrantedAuthority[] authorities) throws RepositoryException { + this(JcrUtils.getUserProfile(session, username), + password != null ? password : "", authorities); + } + /** * Check the account status in JCR, throwing the exceptions expected by * Spring security if needed. diff --git a/security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/jcr/OsJcrAuthenticationProvider.java b/security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/jcr/OsJcrAuthenticationProvider.java index e6f90b165..bccd1c616 100644 --- a/security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/jcr/OsJcrAuthenticationProvider.java +++ b/security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/jcr/OsJcrAuthenticationProvider.java @@ -4,10 +4,8 @@ import javax.jcr.Node; import javax.jcr.Repository; import javax.jcr.RepositoryException; import javax.jcr.Session; -import javax.jcr.version.VersionManager; import org.argeo.ArgeoException; -import org.argeo.jcr.ArgeoNames; import org.argeo.jcr.JcrUtils; import org.argeo.security.OsAuthenticationToken; import org.argeo.security.core.OsAuthenticationProvider; @@ -40,17 +38,8 @@ public class OsJcrAuthenticationProvider extends OsAuthenticationProvider { // WARNING: at this stage we assume that the java properties // will have the same value String username = System.getProperty("user.name"); - Node userHome = JcrUtils.createUserHomeIfNeeded(securitySession, - username); - Node userProfile = userHome.hasNode(ArgeoNames.ARGEO_PROFILE) ? userHome - .getNode(ArgeoNames.ARGEO_PROFILE) : JcrUtils - .createUserProfile(securitySession, username); - if (securitySession.hasPendingChanges()) - securitySession.save(); - VersionManager versionManager = securitySession.getWorkspace() - .getVersionManager(); - if (versionManager.isCheckedOut(userProfile.getPath())) - versionManager.checkin(userProfile.getPath()); + Node userProfile = JcrUtils.createUserProfileIfNeeded( + securitySession, username); JcrUserDetails.checkAccountStatus(userProfile); // user details diff --git a/security/runtime/org.argeo.security.ldap/src/main/java/org/argeo/security/ldap/jcr/JcrLdapSynchronizer.java b/security/runtime/org.argeo.security.ldap/src/main/java/org/argeo/security/ldap/jcr/JcrLdapSynchronizer.java index 632ea58c8..9e6271262 100644 --- a/security/runtime/org.argeo.security.ldap/src/main/java/org/argeo/security/ldap/jcr/JcrLdapSynchronizer.java +++ b/security/runtime/org.argeo.security.ldap/src/main/java/org/argeo/security/ldap/jcr/JcrLdapSynchronizer.java @@ -20,6 +20,7 @@ import javax.jcr.observation.Event; import javax.jcr.observation.EventIterator; import javax.jcr.observation.EventListener; import javax.jcr.query.Query; +import javax.jcr.version.VersionManager; import javax.naming.Binding; import javax.naming.Name; import javax.naming.NamingException; @@ -185,10 +186,21 @@ public class JcrLdapSynchronizer implements UserDetailsContextMapper, Node userProfile = it.nextNode(); String path = userProfile.getPath(); if (!userPaths.contains(path)) { + log.warn("Path " + + path + + " not found in LDAP, disabling user " + + userProfile.getProperty(ArgeoNames.ARGEO_USER_ID) + .getString()); + VersionManager versionManager = securitySession + .getWorkspace().getVersionManager(); + versionManager.checkout(userProfile.getPath()); userProfile.setProperty(ArgeoNames.ARGEO_ENABLED, false); + securitySession.save(); + versionManager.checkin(userProfile.getPath()); } } } catch (Exception e) { + JcrUtils.discardQuietly(securitySession); throw new ArgeoException("Cannot synchronized LDAP and JCR", e); } } @@ -198,9 +210,9 @@ public class JcrLdapSynchronizer implements UserDetailsContextMapper, final String username, GrantedAuthority[] authorities) { if (ctx == null) throw new ArgeoException("No LDAP information for user " + username); - Node userHome = JcrUtils.getUserHome(securitySession, username); - if (userHome == null) - throw new ArgeoException("No JCR information for user " + username); + Node userProfile = JcrUtils.createUserProfileIfNeeded(securitySession, + username); + JcrUserDetails.checkAccountStatus(userProfile); // password SortedSet passwordAttributes = ctx @@ -216,8 +228,7 @@ public class JcrLdapSynchronizer implements UserDetailsContextMapper, } try { - return new JcrUserDetails(userHome.getNode(ARGEO_PROFILE), - password, authorities); + return new JcrUserDetails(userProfile, password, authorities); } catch (RepositoryException e) { throw new ArgeoException("Cannot retrieve user details for " + username, e); diff --git a/server/dep/org.argeo.server.dep.ads/pom.xml b/server/dep/org.argeo.server.dep.ads/pom.xml index 5b6012e83..0a69b5d0a 100644 --- a/server/dep/org.argeo.server.dep.ads/pom.xml +++ b/server/dep/org.argeo.server.dep.ads/pom.xml @@ -1,4 +1,5 @@ - + 4.0.0 org.argeo.commons.server @@ -10,7 +11,7 @@ pom Commons Apache Directory Server Dependencies - + org.argeo.commons.basic org.argeo.basic.dep.log4j @@ -39,10 +40,13 @@ net.sourceforge.jdbm com.springsource.jdbm - + + org.argeo.commons.server + org.argeo.ext.jdbm + 0.3.4-SNAPSHOT + + org.apache.commons com.springsource.org.apache.commons.lang diff --git a/server/runtime/org.argeo.server.jackrabbit/src/main/java/org/argeo/jackrabbit/JackrabbitWrapper.java b/server/runtime/org.argeo.server.jackrabbit/src/main/java/org/argeo/jackrabbit/JackrabbitWrapper.java index 0dd94bc51..78a8c7bbf 100644 --- a/server/runtime/org.argeo.server.jackrabbit/src/main/java/org/argeo/jackrabbit/JackrabbitWrapper.java +++ b/server/runtime/org.argeo.server.jackrabbit/src/main/java/org/argeo/jackrabbit/JackrabbitWrapper.java @@ -172,8 +172,8 @@ public abstract class JackrabbitWrapper implements Repository { repository = RepositoryImpl.create(repositoryConfig); double duration = ((double) (System.currentTimeMillis() - begin)) / 1000; - if (log.isDebugEnabled()) - log.debug("Created Jackrabbit repository in " + duration + if (log.isTraceEnabled()) + log.trace("Created Jackrabbit repository in " + duration + " s, home: " + getHomeDirectory()); } } catch (Exception e) { diff --git a/server/runtime/org.argeo.server.jcr/src/main/java/org/argeo/jcr/JcrUtils.java b/server/runtime/org.argeo.server.jcr/src/main/java/org/argeo/jcr/JcrUtils.java index 66c6a9388..cf6b666a5 100644 --- a/server/runtime/org.argeo.server.jcr/src/main/java/org/argeo/jcr/JcrUtils.java +++ b/server/runtime/org.argeo.server.jcr/src/main/java/org/argeo/jcr/JcrUtils.java @@ -51,6 +51,7 @@ import javax.jcr.nodetype.NodeType; import javax.jcr.observation.EventListener; import javax.jcr.query.Query; import javax.jcr.query.QueryResult; +import javax.jcr.version.VersionManager; import org.apache.commons.io.IOUtils; import org.apache.commons.logging.Log; @@ -59,6 +60,9 @@ import org.argeo.ArgeoException; /** Utility methods to simplify common JCR operations. */ public class JcrUtils implements ArgeoJcrConstants { + /** The home base path. Not yet configurable */ + public final static String DEFAULT_HOME_BASE_PATH = "/home"; + private final static Log log = LogFactory.getLog(JcrUtils.class); /** @@ -920,6 +924,23 @@ public class JcrUtils implements ArgeoJcrConstants { } } + /** + * Convenient method to add a listener. uuids passed as null, deep=true, + * local=true, only one node type + */ + public static void addListener(Session session, EventListener listener, + int eventTypes, String basePath, String nodeType) { + try { + session.getWorkspace() + .getObservationManager() + .addEventListener(listener, eventTypes, basePath, true, + null, new String[] { nodeType }, true); + } catch (RepositoryException e) { + throw new ArgeoException("Cannot add JCR listener " + listener + + " to session " + session, e); + } + } + /** Removes a listener without throwing exception */ public static void removeListenerQuietly(Session session, EventListener listener) { @@ -941,7 +962,7 @@ public class JcrUtils implements ArgeoJcrConstants { /** User home path is NOT configurable */ public static String getUserHomePath(String username) { - String homeBasePath = "/home"; + String homeBasePath = DEFAULT_HOME_BASE_PATH; return homeBasePath + '/' + firstCharsToPath(username, 2) + '/' + username; } @@ -1025,8 +1046,37 @@ public class JcrUtils implements ArgeoJcrConstants { return userProfile; } catch (RepositoryException e) { discardQuietly(session); - throw new ArgeoException("Cannot create home for " + username - + " in workspace " + session.getWorkspace().getName(), e); + throw new ArgeoException("Cannot create user profile for " + + username + " in workspace " + + session.getWorkspace().getName(), e); + } + } + + /** + * Create user profile if needed, the session IS saved. + * + * @return the user profile + */ + public static Node createUserProfileIfNeeded(Session securitySession, + String username) { + try { + Node userHome = JcrUtils.createUserHomeIfNeeded(securitySession, + username); + Node userProfile = userHome.hasNode(ArgeoNames.ARGEO_PROFILE) ? userHome + .getNode(ArgeoNames.ARGEO_PROFILE) : JcrUtils + .createUserProfile(securitySession, username); + if (securitySession.hasPendingChanges()) + securitySession.save(); + VersionManager versionManager = securitySession.getWorkspace() + .getVersionManager(); + if (versionManager.isCheckedOut(userProfile.getPath())) + versionManager.checkin(userProfile.getPath()); + return userProfile; + } catch (RepositoryException e) { + discardQuietly(securitySession); + throw new ArgeoException("Cannot create user profile for " + + username + " in workspace " + + securitySession.getWorkspace().getName(), e); } } @@ -1109,11 +1159,7 @@ public class JcrUtils implements ArgeoJcrConstants { /** * @return null if not found * - * @deprecated will soon be removed. Call instead - * getUserHome().getNode(ARGEO_PROFILE) on the security - * workspace. */ - @Deprecated public static Node getUserProfile(Session session, String username) { try { Node userHome = getUserHome(session, username); @@ -1131,12 +1177,7 @@ public class JcrUtils implements ArgeoJcrConstants { /** * Get the profile of the user attached to this session. - * - * @deprecated will soon be removed. Call instead - * getUserHome().getNode(ARGEO_PROFILE) on the security - * workspace. */ - @Deprecated public static Node getUserProfile(Session session) { String userID = session.getUserID(); return getUserProfile(session, userID); -- 2.30.2