Improve user admin
authorMathieu Baudier <mbaudier@argeo.org>
Thu, 19 Jan 2012 21:49:29 +0000 (21:49 +0000)
committerMathieu Baudier <mbaudier@argeo.org>
Thu, 19 Jan 2012 21:49:29 +0000 (21:49 +0000)
git-svn-id: https://svn.argeo.org/commons/trunk@4990 4cfe0d0a-d680-48aa-b62c-e0a02a3f76cc

16 files changed:
eclipse/runtime/org.argeo.eclipse.ui/src/main/java/org/argeo/eclipse/ui/EclipseUiUtils.java
security/plugins/org.argeo.security.ui.admin/icons/sync.gif [new file with mode: 0644]
security/plugins/org.argeo.security.ui.admin/plugin.xml
security/plugins/org.argeo.security.ui.admin/pom.xml
security/plugins/org.argeo.security.ui.admin/src/main/java/org/argeo/security/ui/admin/SecurityAdminPerspective.java
security/plugins/org.argeo.security.ui.admin/src/main/java/org/argeo/security/ui/admin/editors/ArgeoUserEditor.java
security/plugins/org.argeo.security.ui.admin/src/main/java/org/argeo/security/ui/admin/editors/DefaultUserMainPage.java
security/plugins/org.argeo.security.ui.admin/src/main/java/org/argeo/security/ui/admin/views/UsersView.java
security/plugins/org.argeo.security.ui.admin/src/main/java/org/argeo/security/ui/admin/wizards/MainUserInfoWizardPage.java
security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/UserAdminService.java
security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/jcr/JcrUserDetails.java
security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/jcr/OsJcrAuthenticationProvider.java
security/runtime/org.argeo.security.ldap/src/main/java/org/argeo/security/ldap/jcr/JcrLdapSynchronizer.java
server/dep/org.argeo.server.dep.ads/pom.xml
server/runtime/org.argeo.server.jackrabbit/src/main/java/org/argeo/jackrabbit/JackrabbitWrapper.java
server/runtime/org.argeo.server.jcr/src/main/java/org/argeo/jcr/JcrUtils.java

index e70d819d19e69c922a6534256830a5a18d4b89d9..5c85802c32a48b3518052d4923178aaa3b2b9616 100644 (file)
@@ -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 (file)
index 0000000..b4fa052
Binary files /dev/null and b/security/plugins/org.argeo.security.ui.admin/icons/sync.gif differ
index 1ad3c079fa3909a861c4cafc3969fc9affa19420..9d09879582f39d828ff4eb4e63c19f64e22de3fb 100644 (file)
@@ -97,9 +97,9 @@
             </command>
             <command
                   commandId="org.argeo.security.ui.admin.refreshRoles"
-                  icon="icons/refresh.png"
-                  label="Refresh"
-                  tooltip="Refresh roles">
+                  icon="icons/sync.gif"
+                  label="LDAP Roles Sync"
+                  tooltip="Synchronize roles from LDAP">
             </command>
         </menuContribution>
        <menuContribution
             </command>
             <command
                   commandId="org.argeo.security.ui.admin.refreshUsersList"
-                  icon="icons/refresh.png"
-                  label="Refresh"
-                  tooltip="Refresh user list">
+                  icon="icons/sync.gif"
+                  label="LDAP Users Sync"
+                  tooltip="Synchronize users from LDAP">
             </command>
         </menuContribution>
   </extension>
index 4a4e0be59bd1985b12b3cada31abae186aa4e7c5..05bf5b76cfca4bb5eb2456669a0704d1dfb2c621 100644 (file)
                        <version>0.3.4-SNAPSHOT</version>
                        <scope>provided</scope>
                </dependency>
+               <dependency>
+                       <groupId>org.argeo.commons.eclipse</groupId>
+                       <artifactId>org.argeo.eclipse.ui.rcp</artifactId>
+                       <version>0.3.4-SNAPSHOT</version>
+                       <scope>provided</scope>
+               </dependency>
 
                <!-- Commons -->
                <dependency>
index 4c17bbca6261eb14862a99a4073e6277ba7155bb..f6cb925d735d25fdf695669d369da007eff47cdb 100644 (file)
@@ -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);
        }
index b511e683c5242cea0188f71e2988089791ddc740..87b241867ddd1510f34c17f3854085c7892f6a88 100644 (file)
@@ -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 : "<new user>");
                setPartName(username != null ? username : "<new user>");
index 3581354197e4fc205e60ddd154343e45b1dd9d27..9d08d89ca7eff6f69d5f77adbc7c1ef52825a1e1 100644 (file)
@@ -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();
index a41f20af5560daada24541d3a9f9a860f2445a2d..523b958ef82d02bc283e941b5698f7a7be5bf013 100644 (file)
@@ -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())
index e00decd537bc57a4e4714319bfe714ae76d687d5..496979b84e55eec59685b0355290b889a9d085c9 100644 (file)
@@ -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
                }
index 01e7349ccca83ed445d3b92e6e49276a005394eb..65bc4bb251eb2dba8afe374aa7a3baac656bb818 100644 (file)
@@ -10,9 +10,9 @@ public interface UserAdminService extends UserDetailsManager {
         * Usernames must match this regexp pattern ({@value #USERNAME_PATTERN}).
         * Thanks to <a href=
         * "http://www.mkyong.com/regular-expressions/how-to-validate-username-with-regular-expression/"
-        * >this tip</a> (modified to remove '-' and add upper-case)
+        * >this tip</a> (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}.
index a59eabc0a8dd862083c3bb027bce5524d91b6ce2..05ae165e03fbcccd0d63be4679745b787742f567 100644 (file)
@@ -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.
index e6f90b165c08cc9efc6d498d88d16f68d5bf5e73..bccd1c616d67c1aed8fec1d55ed77f24e683bdb5 100644 (file)
@@ -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
index 632ea58c8778e8c92fab438744faf1cfc98885be..9e6271262daf32c883d1b6788c0dc84f3ccd040a 100644 (file)
@@ -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);
index 5b6012e837fd6ed4e3dc7f8849f80b13b88a1e09..0a69b5d0a62cac4204451e19ddbcb3f16f38a94e 100644 (file)
@@ -1,4 +1,5 @@
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
        <modelVersion>4.0.0</modelVersion>
        <parent>
                <groupId>org.argeo.commons.server</groupId>
@@ -10,7 +11,7 @@
        <packaging>pom</packaging>
        <name>Commons Apache Directory Server Dependencies</name>
        <dependencies>
-               <!--  Commons Dep -->
+               <!-- Commons Dep -->
                <dependency>
                        <groupId>org.argeo.commons.basic</groupId>
                        <artifactId>org.argeo.basic.dep.log4j</artifactId>
                        <groupId>net.sourceforge.jdbm</groupId>
                        <artifactId>com.springsource.jdbm</artifactId>
                </dependency>
-               <!--
-                       TODO: version 2.4.0 is required by Spring-LDAP while Apache Directory
-                       takes 2.1.0
-               -->
+               <dependency>
+                       <groupId>org.argeo.commons.server</groupId>
+                       <artifactId>org.argeo.ext.jdbm</artifactId>
+                       <version>0.3.4-SNAPSHOT</version>
+               </dependency>
+               <!-- TODO: version 2.4.0 is required by Spring-LDAP while Apache Directory 
+                       takes 2.1.0 -->
                <dependency>
                        <groupId>org.apache.commons</groupId>
                        <artifactId>com.springsource.org.apache.commons.lang</artifactId>
index 0dd94bc519342144d9f7bec988104799256accbb..78a8c7bbf54f0a4825f0fa0fa1eff33a1ada5d9d 100644 (file)
@@ -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) {
index 66c6a9388f1f6c1df93e79de91beb6e5a117e832..cf6b666a5cc5a70ff64c3f14be7192685a887e6d 100644 (file)
@@ -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);