From 25e98954db6faeec4ba9950f651e81fbea595b0c Mon Sep 17 00:00:00 2001 From: Mathieu Baudier Date: Sat, 28 Nov 2015 19:51:50 +0000 Subject: [PATCH] - Make LDIF backend more robust - Introduce Posix Account git-svn-id: https://svn.argeo.org/commons/trunk@8651 4cfe0d0a-d680-48aa-b62c-e0a02a3f76cc --- .../osgi/useradmin/AbstractUserDirectory.java | 108 +++++++++++++++++- .../argeo/osgi/useradmin/LdapUserAdmin.java | 10 +- .../org/argeo/osgi/useradmin/LdifName.java | 6 +- .../org/argeo/osgi/useradmin/LdifUser.java | 76 +++++++++--- .../argeo/osgi/useradmin/LdifUserAdmin.java | 36 +++++- .../ui/admin/internal/commands/NewUser.java | 2 +- .../internal/parts/UserBatchUpdateWizard.java | 2 +- .../ui/admin/internal/parts/UserMainPage.java | 6 +- .../ui/admin/internal/parts/UsersView.java | 2 +- .../internal/providers/CommonNameLP.java | 7 +- .../admin/internal/providers/UserFilter.java | 2 +- 11 files changed, 209 insertions(+), 48 deletions(-) diff --git a/org.argeo.security.core/src/org/argeo/osgi/useradmin/AbstractUserDirectory.java b/org.argeo.security.core/src/org/argeo/osgi/useradmin/AbstractUserDirectory.java index 671f63496..0ed712c0b 100644 --- a/org.argeo.security.core/src/org/argeo/osgi/useradmin/AbstractUserDirectory.java +++ b/org.argeo.security.core/src/org/argeo/osgi/useradmin/AbstractUserDirectory.java @@ -1,10 +1,15 @@ package org.argeo.osgi.useradmin; +import static org.argeo.osgi.useradmin.LdifName.gidNumber; +import static org.argeo.osgi.useradmin.LdifName.homeDirectory; import static org.argeo.osgi.useradmin.LdifName.inetOrgPerson; import static org.argeo.osgi.useradmin.LdifName.objectClass; import static org.argeo.osgi.useradmin.LdifName.organizationalPerson; import static org.argeo.osgi.useradmin.LdifName.person; +import static org.argeo.osgi.useradmin.LdifName.posixAccount; import static org.argeo.osgi.useradmin.LdifName.top; +import static org.argeo.osgi.useradmin.LdifName.uid; +import static org.argeo.osgi.useradmin.LdifName.uidNumber; import java.io.File; import java.net.URI; @@ -18,6 +23,7 @@ import java.util.Iterator; import java.util.List; import javax.naming.InvalidNameException; +import javax.naming.NamingException; import javax.naming.directory.Attributes; import javax.naming.directory.BasicAttribute; import javax.naming.directory.BasicAttributes; @@ -29,6 +35,7 @@ import javax.transaction.TransactionManager; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.argeo.ArgeoException; import org.osgi.framework.Filter; import org.osgi.framework.FrameworkUtil; import org.osgi.framework.InvalidSyntaxException; @@ -56,13 +63,16 @@ abstract class AbstractUserDirectory implements UserAdmin, UserDirectory { private String memberAttributeId = "member"; private List credentialAttributeIds = Arrays - .asList(new String[] { LdifName.userpassword.name() }); + .asList(new String[] { LdifName.userPassword.name() }); private TransactionManager transactionManager; // private TransactionSynchronizationRegistry transactionRegistry; // private Xid editingTransactionXid = null; private WcXaResource xaResource = new WcXaResource(this); + // POSIX + private String homeDirectoryBase = "/home"; + AbstractUserDirectory(Dictionary props) { properties = new Hashtable(); for (Enumeration keys = props.keys(); keys.hasMoreElements();) { @@ -259,8 +269,8 @@ abstract class AbstractUserDirectory implements UserAdmin, UserDirectory { protected void doGetUser(String key, String value, List collectedUsers) { try { - Filter f = FrameworkUtil.createFilter("(&(" + objectClass + "=" - + getUserObjectClass() + ")(" + key + "=" + value + "))"); + Filter f = FrameworkUtil + .createFilter("(" + key + "=" + value + ")"); List users = doGetRoles(f); collectedUsers.addAll(users); } catch (InvalidSyntaxException e) { @@ -303,9 +313,29 @@ abstract class AbstractUserDirectory implements UserAdmin, UserDirectory { LdifUser newRole; BasicAttribute objClass = new BasicAttribute(objectClass.name()); if (type == Role.USER) { - String userObjClass = getUserObjectClass(); + String userObjClass = newUserObjectClass(dn); objClass.add(userObjClass); - if (inetOrgPerson.name().equals(userObjClass)) { + if (posixAccount.name().equals(userObjClass)) { + objClass.add(inetOrgPerson.name()); + objClass.add(organizationalPerson.name()); + objClass.add(person.name()); + + String username; + try { + username = dn.getRdn(dn.size() - 1).toAttributes() + .get(uid.name()).get().toString(); + } catch (NamingException e) { + throw new UserDirectoryException( + "Cannot extract username from " + dn, e); + } + // TODO look for uid in attributes too? + attrs.put(uidNumber.name(), new Long(max(uidNumber.name()) + 1)); + attrs.put(homeDirectory.name(), generateHomeDirectory(username)); + // TODO create user private group + // NB: on RHEL, the 'users' group has gid 100 + attrs.put(gidNumber.name(), 100); + // attrs.put(LdifName.loginShell.name(),"/sbin/nologin"); + } else if (inetOrgPerson.name().equals(userObjClass)) { objClass.add(organizationalPerson.name()); objClass.add(person.name()); } else if (organizationalPerson.name().equals(userObjClass)) { @@ -315,7 +345,10 @@ abstract class AbstractUserDirectory implements UserAdmin, UserDirectory { attrs.put(objClass); newRole = new LdifUser(this, dn, attrs); } else if (type == Role.GROUP) { - objClass.add(getGroupObjectClass()); + String groupObjClass = getGroupObjectClass(); + objClass.add(groupObjClass); + objClass.add(LdifName.extensibleObject.name()); + attrs.put(gidNumber.name(), new Long(max(gidNumber.name()) + 1)); objClass.add(top); attrs.put(objClass); newRole = new LdifGroup(this, dn, attrs); @@ -345,6 +378,60 @@ abstract class AbstractUserDirectory implements UserAdmin, UserDirectory { return actuallyDeleted; } + // POSIX + /** Generate path for a new user home */ + protected String generateHomeDirectory(String username) { + String base = homeDirectoryBase; + int atIndex = username.indexOf('@'); + if (atIndex > 0) { + String domain = username.substring(0, atIndex); + String name = username.substring(atIndex + 1); + return base + '/' + firstCharsToPath(domain, 2) + '/' + domain + + '/' + firstCharsToPath(name, 2) + '/' + name; + } else if (atIndex == 0 || atIndex == (username.length() - 1)) { + throw new ArgeoException("Unsupported username " + username); + } else { + return base + '/' + firstCharsToPath(username, 2) + '/' + username; + } + } + + protected long max(String attr) { + long max; + try { + List users = doGetRoles(FrameworkUtil + .createFilter("(" + attr + "=*)")); + max = 1000; + for (DirectoryUser user : users) { + long uid = Long.parseLong(user.getAttributes().get(attr).get() + .toString()); + if (uid > max) + max = uid; + } + } catch (Exception e) { + throw new UserDirectoryException("Cannot get max of " + attr, e); + } + return max; + } + + /** + * Creates depth from a string (typically a username) by adding levels based + * on its first characters: "aBcD",2 => a/aB + */ + public static String firstCharsToPath(String str, Integer nbrOfChars) { + if (str.length() < nbrOfChars) + throw new ArgeoException("String " + str + + " length must be greater or equal than " + nbrOfChars); + StringBuffer path = new StringBuffer(""); + StringBuffer curr = new StringBuffer(""); + for (int i = 0; i < nbrOfChars; i++) { + curr.append(str.charAt(i)); + path.append(curr); + if (i < nbrOfChars - 1) + path.append('/'); + } + return path.toString(); + } + // TRANSACTION protected void prepare(UserDirectoryWorkingCopy wc) { @@ -418,10 +505,19 @@ abstract class AbstractUserDirectory implements UserAdmin, UserDirectory { return baseDn; } + /** dn can be null, in that case a default should be returned. */ protected String getUserObjectClass() { return userObjectClass; } + protected String newUserObjectClass(LdapName dn) { + if (dn != null + && dn.getRdn(dn.size() - 1).toAttributes().get(uid.name()) != null) + return posixAccount.name(); + else + return getUserObjectClass(); + } + protected String getGroupObjectClass() { return groupObjectClass; } diff --git a/org.argeo.security.core/src/org/argeo/osgi/useradmin/LdapUserAdmin.java b/org.argeo.security.core/src/org/argeo/osgi/useradmin/LdapUserAdmin.java index 1dc4df981..838486b6d 100644 --- a/org.argeo.security.core/src/org/argeo/osgi/useradmin/LdapUserAdmin.java +++ b/org.argeo.security.core/src/org/argeo/osgi/useradmin/LdapUserAdmin.java @@ -42,7 +42,7 @@ public class LdapUserAdmin extends AbstractUserDirectory { "com.sun.jndi.ldap.LdapCtxFactory"); connEnv.put(Context.PROVIDER_URL, getUri().toString()); connEnv.put("java.naming.ldap.attributes.binary", - LdifName.userpassword.name()); + LdifName.userPassword.name()); initialLdapContext = new InitialLdapContext(connEnv, null); // StartTlsResponse tls = (StartTlsResponse) ctx @@ -111,7 +111,6 @@ public class LdapUserAdmin extends AbstractUserDirectory { @Override protected List doGetRoles(Filter f) { - // TODO Auto-generated method stub try { String searchFilter = f != null ? f.toString() : "(|(" + objectClass + "=" + getUserObjectClass() + ")(" @@ -127,15 +126,14 @@ public class LdapUserAdmin extends AbstractUserDirectory { while (results.hasMoreElements()) { SearchResult searchResult = results.next(); Attributes attrs = searchResult.getAttributes(); + LdapName dn = toDn(searchBase, searchResult); LdifUser role; if (attrs.get(objectClass.name()).contains( getGroupObjectClass())) - role = new LdifGroup(this, toDn(searchBase, searchResult), - attrs); + role = new LdifGroup(this, dn, attrs); else if (attrs.get(objectClass.name()).contains( getUserObjectClass())) - role = new LdifUser(this, toDn(searchBase, searchResult), - attrs); + role = new LdifUser(this, dn, attrs); else throw new UserDirectoryException( "Unsupported LDAP type for " diff --git a/org.argeo.security.core/src/org/argeo/osgi/useradmin/LdifName.java b/org.argeo.security.core/src/org/argeo/osgi/useradmin/LdifName.java index a63f51288..25125e0fb 100644 --- a/org.argeo.security.core/src/org/argeo/osgi/useradmin/LdifName.java +++ b/org.argeo.security.core/src/org/argeo/osgi/useradmin/LdifName.java @@ -9,9 +9,11 @@ import javax.naming.ldap.LdapName; */ public enum LdifName { // Attributes - dn, cn, sn, uid, mail, displayName, objectClass, userpassword, givenname, description, + dn, cn, sn, uid, mail, displayName, objectClass, userPassword, givenName, description, + // POSIX attributes + uidNumber, gidNumber, homeDirectory, loginShell, gecos, // Object classes - inetOrgPerson, organizationalPerson, person, groupOfNames, top; + posixAccount, inetOrgPerson, organizationalPerson, person, groupOfNames, groupOfUniqueNames, extensibleObject, top; public final static String PREFIX = "ldap:"; diff --git a/org.argeo.security.core/src/org/argeo/osgi/useradmin/LdifUser.java b/org.argeo.security.core/src/org/argeo/osgi/useradmin/LdifUser.java index 41f33c3ba..d5ddba50c 100644 --- a/org.argeo.security.core/src/org/argeo/osgi/useradmin/LdifUser.java +++ b/org.argeo.security.core/src/org/argeo/osgi/useradmin/LdifUser.java @@ -1,5 +1,6 @@ package org.argeo.osgi.useradmin; +import java.io.UnsupportedEncodingException; import java.nio.ByteBuffer; import java.nio.CharBuffer; import java.nio.charset.Charset; @@ -8,8 +9,10 @@ import java.util.Arrays; import java.util.Collections; import java.util.Dictionary; import java.util.Enumeration; +import java.util.HashSet; import java.util.Iterator; import java.util.List; +import java.util.Set; import javax.naming.NamingEnumeration; import javax.naming.NamingException; @@ -73,7 +76,7 @@ class LdifUser implements DirectoryUser { // TODO check other sources (like PKCS12) char[] password = toChars(value); byte[] hashedPassword = hash(password); - return hasCredential(LdifName.userpassword.name(), hashedPassword); + return hasCredential(LdifName.userPassword.name(), hashedPassword); } Object storedValue = getCredentials().get(key); @@ -161,13 +164,6 @@ class LdifUser implements DirectoryUser { publishedAttributes = modifiedAttributes; } - // protected synchronized void stopEditing(boolean apply) { - // assert getModifiedAttributes() != null; - // if (apply) - // publishedAttributes = getModifiedAttributes(); - // // modifiedAttributes = null; - // } - public DirectoryUser getPublished() { return new LdifUser(userAdmin, dn, publishedAttributes, true); } @@ -248,12 +244,7 @@ class LdifUser implements DirectoryUser { @Override public Object nextElement() { String key = it.next(); - try { - return getAttributes().get(key).get(); - } catch (NamingException e) { - throw new UserDirectoryException( - "Cannot get value for key " + key, e); - } + return get(key); } }; @@ -265,7 +256,32 @@ class LdifUser implements DirectoryUser { Attribute attr = getAttributes().get(key.toString()); if (attr == null) return null; - return attr.get(); + Object value = attr.get(); + if (value instanceof byte[]) { + if (key.equals(LdifName.userPassword.name())) + // TODO other cases (certificates, images) + return value; + value = new String((byte[]) value, Charset.forName("UTF-8")); + } + if (attr.size() == 1) + return value; + if (!attr.getID().equals(LdifName.objectClass.name())) + return value; + // special case for object class + NamingEnumeration en = attr.getAll(); + Set objectClasses = new HashSet(); + while (en.hasMore()) { + String objectClass = en.next().toString(); + objectClasses.add(objectClass); + } + + if (objectClasses.contains(userAdmin.getUserObjectClass())) + return userAdmin.getUserObjectClass(); + else if (objectClasses + .contains(userAdmin.getGroupObjectClass())) + return userAdmin.getGroupObjectClass(); + else + return value; } catch (NamingException e) { throw new UserDirectoryException( "Cannot get value for attribute " + key, e); @@ -278,7 +294,7 @@ class LdifUser implements DirectoryUser { // TODO persist to other sources (like PKCS12) char[] password = toChars(value); byte[] hashedPassword = hash(password); - return put(LdifName.userpassword.name(), hashedPassword); + return put(LdifName.userPassword.name(), hashedPassword); } userAdmin.checkEdit(); @@ -299,7 +315,16 @@ class LdifUser implements DirectoryUser { Attribute attribute = getModifiedAttributes().get( key.toString()); attribute = new BasicAttribute(key.toString()); - attribute.add(value); + if (value instanceof String + && !isAsciiPrintable(((String) value))) + try { + attribute.add(((String) value).getBytes("UTF-8")); + } catch (UnsupportedEncodingException e) { + throw new UserDirectoryException("Cannot encode " + + value, e); + } + else + attribute.add(value); Attribute previousAttribute = getModifiedAttributes().put( attribute); if (previousAttribute != null) @@ -337,4 +362,21 @@ class LdifUser implements DirectoryUser { } } + private static boolean isAsciiPrintable(String str) { + if (str == null) { + return false; + } + int sz = str.length(); + for (int i = 0; i < sz; i++) { + if (isAsciiPrintable(str.charAt(i)) == false) { + return false; + } + } + return true; + } + + private static boolean isAsciiPrintable(char ch) { + return ch >= 32 && ch < 127; + } + } diff --git a/org.argeo.security.core/src/org/argeo/osgi/useradmin/LdifUserAdmin.java b/org.argeo.security.core/src/org/argeo/osgi/useradmin/LdifUserAdmin.java index bee73e54d..859adde09 100644 --- a/org.argeo.security.core/src/org/argeo/osgi/useradmin/LdifUserAdmin.java +++ b/org.argeo.security.core/src/org/argeo/osgi/useradmin/LdifUserAdmin.java @@ -1,5 +1,8 @@ package org.argeo.osgi.useradmin; +import static org.argeo.osgi.useradmin.LdifName.inetOrgPerson; +import static org.argeo.osgi.useradmin.LdifName.objectClass; + import java.io.File; import java.io.FileOutputStream; import java.io.IOException; @@ -7,8 +10,10 @@ import java.io.InputStream; import java.io.OutputStream; import java.util.ArrayList; import java.util.Dictionary; +import java.util.HashSet; import java.util.Hashtable; import java.util.List; +import java.util.Set; import java.util.SortedMap; import java.util.TreeMap; @@ -98,14 +103,28 @@ public class LdifUserAdmin extends AbstractUserDirectory { SortedMap allEntries = ldifParser.read(in); for (LdapName key : allEntries.keySet()) { Attributes attributes = allEntries.get(key); + // check for inconsistency + Set lowerCase = new HashSet(); + NamingEnumeration ids = attributes.getIDs(); + while (ids.hasMoreElements()) { + String id = ids.nextElement().toLowerCase(); + if (lowerCase.contains(id)) + throw new UserDirectoryException(key + + " has duplicate id " + id); + lowerCase.add(id); + } + + // analyse object classes NamingEnumeration objectClasses = attributes.get( - "objectClass").getAll(); + objectClass.name()).getAll(); + // System.out.println(key); objectClasses: while (objectClasses.hasMore()) { String objectClass = objectClasses.next().toString(); - if (objectClass.equals("inetOrgPerson")) { + // System.out.println(" " + objectClass); + if (objectClass.equals(inetOrgPerson.name())) { users.put(key, new LdifUser(this, key, attributes)); break objectClasses; - } else if (objectClass.equals("groupOfNames")) { + } else if (objectClass.equals(getGroupObjectClass())) { groups.put(key, new LdifGroup(this, key, attributes)); break objectClasses; } @@ -143,10 +162,17 @@ public class LdifUserAdmin extends AbstractUserDirectory { res.addAll(users.values()); res.addAll(groups.values()); } else { - // Filter f = FrameworkUtil.createFilter(filter); - for (DirectoryUser user : users.values()) + for (DirectoryUser user : users.values()) { +// System.out.println("\n" + user.getName()); +// Dictionary props = user.getProperties(); +// for (Enumeration keys = props.keys(); keys +// .hasMoreElements();) { +// String key = keys.nextElement(); +// System.out.println(" " + key + "=" + props.get(key)); +// } if (f.match(user.getProperties())) res.add(user); + } for (DirectoryUser group : groups.values()) if (f.match(group.getProperties())) res.add(group); diff --git a/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/commands/NewUser.java b/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/commands/NewUser.java index c336753dc..6d853c919 100644 --- a/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/commands/NewUser.java +++ b/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/commands/NewUser.java @@ -117,7 +117,7 @@ public class NewUser extends AbstractHandler { String firstNameStr = firstNameTxt.getText(); if (UiAdminUtils.notNull(firstNameStr)) - props.put(LdifName.givenname.name(), firstNameStr); + props.put(LdifName.givenName.name(), firstNameStr); String cn = UiAdminUtils .getDefaultCn(firstNameStr, lastNameStr); diff --git a/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/parts/UserBatchUpdateWizard.java b/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/parts/UserBatchUpdateWizard.java index 5edcfbd4b..f2038f225 100644 --- a/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/parts/UserBatchUpdateWizard.java +++ b/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/parts/UserBatchUpdateWizard.java @@ -455,7 +455,7 @@ public class UserBatchUpdateWizard extends Wizard { private static final long serialVersionUID = 5080437561015853124L; private final String[] knownProps = { LdifName.uid.name(), LdifName.dn.name(), LdifName.cn.name(), - LdifName.givenname.name(), LdifName.sn.name(), + LdifName.givenName.name(), LdifName.sn.name(), LdifName.mail.name() }; public ChooseUserTableViewer(Composite parent, int style) { diff --git a/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/parts/UserMainPage.java b/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/parts/UserMainPage.java index 58acface8..5875698b5 100644 --- a/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/parts/UserMainPage.java +++ b/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/parts/UserMainPage.java @@ -127,7 +127,7 @@ public class UserMainPage extends FormPage implements ArgeoNames { commonName.setEnabled(false); final Text firstName = createLT(tk, body, "First name", - UiAdminUtils.getProperty(user, LdifName.givenname.name())); + UiAdminUtils.getProperty(user, LdifName.givenName.name())); final Text lastName = createLT(tk, body, "Last name", UiAdminUtils.getProperty(user, LdifName.sn.name())); @@ -155,7 +155,7 @@ public class UserMainPage extends FormPage implements ArgeoNames { @SuppressWarnings("unchecked") public void commit(boolean onSave) { // TODO Sanity checks (mail validity...) - user.getProperties().put(LdifName.givenname.name(), + user.getProperties().put(LdifName.givenName.name(), firstName.getText()); user.getProperties() .put(LdifName.sn.name(), lastName.getText()); @@ -172,7 +172,7 @@ public class UserMainPage extends FormPage implements ArgeoNames { commonName.setText(UiAdminUtils.getProperty(user, LdifName.cn.name())); firstName.setText(UiAdminUtils.getProperty(user, - LdifName.givenname.name())); + LdifName.givenName.name())); lastName.setText(UiAdminUtils.getProperty(user, LdifName.sn.name())); email.setText(UiAdminUtils.getProperty(user, diff --git a/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/parts/UsersView.java b/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/parts/UsersView.java index 79e94abc7..afa278212 100644 --- a/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/parts/UsersView.java +++ b/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/parts/UsersView.java @@ -115,7 +115,7 @@ public class UsersView extends ViewPart implements ArgeoNames { private final String[] knownProps = { LdifName.uid.name(), LdifName.dn.name(), LdifName.cn.name(), - LdifName.givenname.name(), LdifName.sn.name(), + LdifName.givenName.name(), LdifName.sn.name(), LdifName.mail.name() }; public MyUserTableViewer(Composite parent, int style) { diff --git a/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/providers/CommonNameLP.java b/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/providers/CommonNameLP.java index 1bd387e20..46326d268 100644 --- a/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/providers/CommonNameLP.java +++ b/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/providers/CommonNameLP.java @@ -1,6 +1,7 @@ package org.argeo.security.ui.admin.internal.providers; import org.argeo.osgi.useradmin.LdifName; +import org.argeo.security.ui.admin.internal.UiAdminUtils; import org.osgi.service.useradmin.User; /** Simply declare a label provider that returns the common name of a user */ @@ -9,10 +10,6 @@ public class CommonNameLP extends UserAdminAbstractLP { @Override public String getText(User user) { - Object obj = user.getProperties().get(LdifName.cn.name()); - if (obj != null) - return (String) obj; - else - return ""; + return UiAdminUtils.getProperty(user, LdifName.cn.name()); } } \ No newline at end of file diff --git a/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/providers/UserFilter.java b/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/providers/UserFilter.java index 59e83852c..742b7d393 100644 --- a/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/providers/UserFilter.java +++ b/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/providers/UserFilter.java @@ -16,7 +16,7 @@ public class UserFilter extends ViewerFilter { private boolean showSystemRole = true; private final String[] knownProps = { LdifName.dn.name(), - LdifName.cn.name(), LdifName.givenname.name(), LdifName.sn.name(), + LdifName.cn.name(), LdifName.givenName.name(), LdifName.sn.name(), LdifName.uid.name(), LdifName.description.name(), LdifName.mail.name() }; -- 2.30.2