You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@directory.apache.org by er...@apache.org on 2005/10/28 05:37:22 UTC

svn commit: r329063 - in /directory/shared/kerberos/trunk/common/src/java/org/apache/kerberos/store: ./ operations/

Author: erodriguez
Date: Thu Oct 27 20:37:16 2005
New Revision: 329063

URL: http://svn.apache.org/viewcvs?rev=329063&view=rev
Log:
Added multi-realm support to kerberos-common:
o  Updates to the principal store to support switching strategies based on configuration.
o  Catalog for resolving realms to domain components.
o  Multi-base search strategy.
o  Single-base search strategy.

Added:
    directory/shared/kerberos/trunk/common/src/java/org/apache/kerberos/store/KerberosCatalog.java   (with props)
    directory/shared/kerberos/trunk/common/src/java/org/apache/kerberos/store/MultiBaseSearch.java   (with props)
    directory/shared/kerberos/trunk/common/src/java/org/apache/kerberos/store/SearchStrategy.java   (with props)
    directory/shared/kerberos/trunk/common/src/java/org/apache/kerberos/store/SingleBaseSearch.java   (with props)
Modified:
    directory/shared/kerberos/trunk/common/src/java/org/apache/kerberos/store/JndiPrincipalStoreImpl.java
    directory/shared/kerberos/trunk/common/src/java/org/apache/kerberos/store/PrincipalStore.java
    directory/shared/kerberos/trunk/common/src/java/org/apache/kerberos/store/operations/ChangePassword.java
    directory/shared/kerberos/trunk/common/src/java/org/apache/kerberos/store/operations/GetPrincipal.java

Modified: directory/shared/kerberos/trunk/common/src/java/org/apache/kerberos/store/JndiPrincipalStoreImpl.java
URL: http://svn.apache.org/viewcvs/directory/shared/kerberos/trunk/common/src/java/org/apache/kerberos/store/JndiPrincipalStoreImpl.java?rev=329063&r1=329062&r2=329063&view=diff
==============================================================================
--- directory/shared/kerberos/trunk/common/src/java/org/apache/kerberos/store/JndiPrincipalStoreImpl.java (original)
+++ directory/shared/kerberos/trunk/common/src/java/org/apache/kerberos/store/JndiPrincipalStoreImpl.java Thu Oct 27 20:37:16 2005
@@ -16,55 +16,58 @@
  */
 package org.apache.kerberos.store;
 
-import javax.naming.Name;
-import javax.naming.ldap.LdapContext;
+import javax.naming.spi.InitialContextFactory;
 import javax.security.auth.kerberos.KerberosKey;
 import javax.security.auth.kerberos.KerberosPrincipal;
 
-import org.apache.kerberos.store.operations.ChangePassword;
-import org.apache.kerberos.store.operations.GetPrincipal;
-import org.apache.protocol.common.store.ContextOperation;
+import org.apache.protocol.common.ServiceConfiguration;
 
 /**
- * A simple implementation of the PrincipalStore interface using a JNDI-based store.
+ * A JNDI-backed implementation of the PrincipalStore interface.  This PrincipalStore uses
+ * the Strategy pattern to either serve principals based on a single base DN or to lookup
+ * catalog mappings from configuration in the DIT.  The strategy is chosen based on the
+ * presence of a catalog base DN.  If the catalog base DN is not present, the single
+ * entry base DN is searched, instead.
  *
  * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
  * @version $Rev$, $Date$
  */
 public class JndiPrincipalStoreImpl implements PrincipalStore
 {
-    /** a handle on the provider context */
-    private LdapContext ctx;
+    /** a handle on the configuration */
+    private ServiceConfiguration config;
+    /** a handle on the provider factory */
+    private InitialContextFactory factory;
+    /** a handle on the search strategy */
+    private SearchStrategy strategy;
 
-    /** the search searchBase relative to the DN of the context supplied */
-    private Name searchBase;
-
-    /**
-     * Creates the action to be used against the embedded ApacheDS DIT.  Note
-     * the searchBase is a relative name to the context and not a DN.
-     *
-     * @param ctx the JNDI context to the store
-     * @param searchBase the name relative to the context to use as the search base
-     */
-    public JndiPrincipalStoreImpl( LdapContext ctx, Name searchBase )
+    public JndiPrincipalStoreImpl( ServiceConfiguration config, InitialContextFactory factory )
     {
-        this.ctx = ctx;
+        this.config = config;
+        this.factory = factory;
 
-        this.searchBase = searchBase;
+        strategy = getSearchStrategy();
     }
 
     public PrincipalStoreEntry getPrincipal( KerberosPrincipal principal ) throws Exception
     {
-        return (PrincipalStoreEntry) execute( new GetPrincipal( principal ) );
+        return strategy.getPrincipal( principal );
     }
 
     public String changePassword( KerberosPrincipal principal, KerberosKey newKey ) throws Exception
     {
-        return (String) execute( new ChangePassword( principal, newKey ) );
+        return strategy.changePassword( principal, newKey );
     }
 
-    private Object execute( ContextOperation operation ) throws Exception
+    private SearchStrategy getSearchStrategy()
     {
-        return operation.execute( ctx, searchBase );
+        if ( config.getCatalogBaseDn() != null )
+        {
+            // build a catalog from the backing store
+            return new MultiBaseSearch( config, factory );
+        }
+
+        // search only the configured entry baseDN
+        return new SingleBaseSearch( config, factory );
     }
 }

Added: directory/shared/kerberos/trunk/common/src/java/org/apache/kerberos/store/KerberosCatalog.java
URL: http://svn.apache.org/viewcvs/directory/shared/kerberos/trunk/common/src/java/org/apache/kerberos/store/KerberosCatalog.java?rev=329063&view=auto
==============================================================================
--- directory/shared/kerberos/trunk/common/src/java/org/apache/kerberos/store/KerberosCatalog.java (added)
+++ directory/shared/kerberos/trunk/common/src/java/org/apache/kerberos/store/KerberosCatalog.java Thu Oct 27 20:37:16 2005
@@ -0,0 +1,71 @@
+/*
+ *   Copyright 2005 The Apache Software Foundation
+ *
+ *   Licensed under the Apache License, Version 2.0 (the "License");
+ *   you may not use this file except in compliance with the License.
+ *   You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *   Unless required by applicable law or agreed to in writing, software
+ *   distributed under the License is distributed on an "AS IS" BASIS,
+ *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *   See the License for the specific language governing permissions and
+ *   limitations under the License.
+ *
+ */
+
+package org.apache.kerberos.store;
+
+import java.util.Map;
+
+import org.apache.protocol.common.catalog.Catalog;
+
+/**
+ * A catalog for mapping Kerberos realms to search base DN's. 
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+class KerberosCatalog implements Catalog
+{
+    private Map map;
+
+    public KerberosCatalog( Map map )
+    {
+        this.map = map;
+    }
+
+    public String getBaseDn( String name )
+    {
+        name = name.toLowerCase();
+
+        if ( name.endsWith( "." ) )
+        {
+            int last = name.lastIndexOf( "." );
+            name = name.substring( 0, last );
+        }
+
+        while ( !name.equals( "" ) && name != null )
+        {
+            String candidate = (String) map.get( name );
+            if ( candidate != null )
+            {
+                return candidate;
+            }
+
+            int period = name.indexOf( "." );
+
+            if ( period > -1 )
+            {
+                name = name.substring( period + 1 );
+            }
+            else
+            {
+                return "";
+            }
+        }
+
+        return "";
+    }
+}

Propchange: directory/shared/kerberos/trunk/common/src/java/org/apache/kerberos/store/KerberosCatalog.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: directory/shared/kerberos/trunk/common/src/java/org/apache/kerberos/store/MultiBaseSearch.java
URL: http://svn.apache.org/viewcvs/directory/shared/kerberos/trunk/common/src/java/org/apache/kerberos/store/MultiBaseSearch.java?rev=329063&view=auto
==============================================================================
--- directory/shared/kerberos/trunk/common/src/java/org/apache/kerberos/store/MultiBaseSearch.java (added)
+++ directory/shared/kerberos/trunk/common/src/java/org/apache/kerberos/store/MultiBaseSearch.java Thu Oct 27 20:37:16 2005
@@ -0,0 +1,109 @@
+/*
+ *   Copyright 2005 The Apache Software Foundation
+ *
+ *   Licensed under the Apache License, Version 2.0 (the "License");
+ *   you may not use this file except in compliance with the License.
+ *   You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *   Unless required by applicable law or agreed to in writing, software
+ *   distributed under the License is distributed on an "AS IS" BASIS,
+ *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *   See the License for the specific language governing permissions and
+ *   limitations under the License.
+ *
+ */
+
+package org.apache.kerberos.store;
+
+import java.util.Hashtable;
+import java.util.Map;
+
+import javax.naming.Context;
+import javax.naming.NamingException;
+import javax.naming.directory.DirContext;
+import javax.naming.spi.InitialContextFactory;
+import javax.security.auth.kerberos.KerberosKey;
+import javax.security.auth.kerberos.KerberosPrincipal;
+
+import org.apache.kerberos.store.operations.ChangePassword;
+import org.apache.kerberos.store.operations.GetPrincipal;
+import org.apache.ldap.server.configuration.ConfigurationException;
+import org.apache.protocol.common.ServiceConfiguration;
+import org.apache.protocol.common.catalog.Catalog;
+import org.apache.protocol.common.catalog.GetCatalog;
+import org.apache.protocol.common.store.ContextOperation;
+
+/**
+ * A JNDI-backed search strategy implementation.  This search strategy builds a
+ * catalog from configuration in the DIT to determine where realms are to search
+ * for Kerberos principals.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+class MultiBaseSearch implements SearchStrategy
+{
+    private InitialContextFactory factory;
+    private Hashtable env;
+
+    private Catalog catalog;
+
+    MultiBaseSearch( ServiceConfiguration config, InitialContextFactory factory )
+    {
+        this.factory = factory;
+
+        env = new Hashtable( config.toJndiEnvironment() );
+        env.put( Context.INITIAL_CONTEXT_FACTORY, config.getInitialContextFactory() );
+        env.put( Context.PROVIDER_URL, config.getCatalogBaseDn() );
+
+        try
+        {
+            DirContext ctx = (DirContext) factory.getInitialContext( env );
+            catalog = new KerberosCatalog( (Map) execute( ctx, new GetCatalog() ) );
+        }
+        catch ( Exception e )
+        {
+            String message = "Failed to get catalog context " + (String) env.get( Context.PROVIDER_URL );
+            throw new ConfigurationException( message );
+        }
+    }
+
+    public PrincipalStoreEntry getPrincipal( KerberosPrincipal principal ) throws Exception
+    {
+        env.put( Context.PROVIDER_URL, catalog.getBaseDn( principal.getRealm() ) );
+
+        try
+        {
+            DirContext ctx = (DirContext) factory.getInitialContext( env );
+            return (PrincipalStoreEntry) execute( ctx, new GetPrincipal( principal ) );
+        }
+        catch ( NamingException ne )
+        {
+            String message = "Failed to get initial context " + (String) env.get( Context.PROVIDER_URL );
+            throw new ConfigurationException( message );
+        }
+    }
+
+    public String changePassword( KerberosPrincipal principal, KerberosKey newKey ) throws Exception
+    {
+        env.put( Context.PROVIDER_URL, catalog.getBaseDn( principal.getRealm() ) );
+
+        try
+        {
+            DirContext ctx = (DirContext) factory.getInitialContext( env );
+            return (String) execute( ctx, new ChangePassword( principal, newKey ) );
+        }
+        catch ( NamingException ne )
+        {
+            String message = "Failed to get initial context " + (String) env.get( Context.PROVIDER_URL );
+            throw new ConfigurationException( message );
+        }
+    }
+
+    private Object execute( DirContext ctx, ContextOperation operation ) throws Exception
+    {
+        return operation.execute( ctx, null );
+    }
+}

Propchange: directory/shared/kerberos/trunk/common/src/java/org/apache/kerberos/store/MultiBaseSearch.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: directory/shared/kerberos/trunk/common/src/java/org/apache/kerberos/store/PrincipalStore.java
URL: http://svn.apache.org/viewcvs/directory/shared/kerberos/trunk/common/src/java/org/apache/kerberos/store/PrincipalStore.java?rev=329063&r1=329062&r2=329063&view=diff
==============================================================================
--- directory/shared/kerberos/trunk/common/src/java/org/apache/kerberos/store/PrincipalStore.java (original)
+++ directory/shared/kerberos/trunk/common/src/java/org/apache/kerberos/store/PrincipalStore.java Thu Oct 27 20:37:16 2005
@@ -20,6 +20,9 @@
 import javax.security.auth.kerberos.KerberosPrincipal;
 
 /**
+ * The store interface used by Kerberos protocols to lookup principals and
+ * to change their passwords.
+ * 
  * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
  * @version $Rev$, $Date$
  */

Added: directory/shared/kerberos/trunk/common/src/java/org/apache/kerberos/store/SearchStrategy.java
URL: http://svn.apache.org/viewcvs/directory/shared/kerberos/trunk/common/src/java/org/apache/kerberos/store/SearchStrategy.java?rev=329063&view=auto
==============================================================================
--- directory/shared/kerberos/trunk/common/src/java/org/apache/kerberos/store/SearchStrategy.java (added)
+++ directory/shared/kerberos/trunk/common/src/java/org/apache/kerberos/store/SearchStrategy.java Thu Oct 27 20:37:16 2005
@@ -0,0 +1,36 @@
+/*
+ *   Copyright 2005 The Apache Software Foundation
+ *
+ *   Licensed under the Apache License, Version 2.0 (the "License");
+ *   you may not use this file except in compliance with the License.
+ *   You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *   Unless required by applicable law or agreed to in writing, software
+ *   distributed under the License is distributed on an "AS IS" BASIS,
+ *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *   See the License for the specific language governing permissions and
+ *   limitations under the License.
+ *
+ */
+
+package org.apache.kerberos.store;
+
+import javax.security.auth.kerberos.KerberosKey;
+import javax.security.auth.kerberos.KerberosPrincipal;
+
+/**
+ * Interface for search strategies.  Kerberos protocols may search a single
+ * base DN for principals or use a catalog to lookup principals in multiple
+ * search base DN's.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+interface SearchStrategy
+{
+    public PrincipalStoreEntry getPrincipal( KerberosPrincipal principal ) throws Exception;
+
+    public String changePassword( KerberosPrincipal principal, KerberosKey newKey ) throws Exception;
+}

Propchange: directory/shared/kerberos/trunk/common/src/java/org/apache/kerberos/store/SearchStrategy.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: directory/shared/kerberos/trunk/common/src/java/org/apache/kerberos/store/SingleBaseSearch.java
URL: http://svn.apache.org/viewcvs/directory/shared/kerberos/trunk/common/src/java/org/apache/kerberos/store/SingleBaseSearch.java?rev=329063&view=auto
==============================================================================
--- directory/shared/kerberos/trunk/common/src/java/org/apache/kerberos/store/SingleBaseSearch.java (added)
+++ directory/shared/kerberos/trunk/common/src/java/org/apache/kerberos/store/SingleBaseSearch.java Thu Oct 27 20:37:16 2005
@@ -0,0 +1,77 @@
+/*
+ *   Copyright 2005 The Apache Software Foundation
+ *
+ *   Licensed under the Apache License, Version 2.0 (the "License");
+ *   you may not use this file except in compliance with the License.
+ *   You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *   Unless required by applicable law or agreed to in writing, software
+ *   distributed under the License is distributed on an "AS IS" BASIS,
+ *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *   See the License for the specific language governing permissions and
+ *   limitations under the License.
+ *
+ */
+
+package org.apache.kerberos.store;
+
+import java.util.Hashtable;
+
+import javax.naming.Context;
+import javax.naming.NamingException;
+import javax.naming.directory.DirContext;
+import javax.naming.spi.InitialContextFactory;
+import javax.security.auth.kerberos.KerberosKey;
+import javax.security.auth.kerberos.KerberosPrincipal;
+
+import org.apache.kerberos.store.operations.ChangePassword;
+import org.apache.kerberos.store.operations.GetPrincipal;
+import org.apache.ldap.server.configuration.ConfigurationException;
+import org.apache.protocol.common.ServiceConfiguration;
+import org.apache.protocol.common.store.ContextOperation;
+
+/**
+ * A JNDI-backed search strategy implementation.  This search strategy searches a
+ * single base DN for Kerberos principals.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+class SingleBaseSearch implements SearchStrategy
+{
+    private DirContext ctx;
+
+    SingleBaseSearch( ServiceConfiguration config, InitialContextFactory factory )
+    {
+        Hashtable env = new Hashtable( config.toJndiEnvironment() );
+        env.put( Context.INITIAL_CONTEXT_FACTORY, config.getInitialContextFactory() );
+        env.put( Context.PROVIDER_URL, config.getEntryBaseDn() );
+
+        try
+        {
+            ctx = (DirContext) factory.getInitialContext( env );
+        }
+        catch ( NamingException ne )
+        {
+            String message = "Failed to get initial context " + (String) env.get( Context.PROVIDER_URL );
+            throw new ConfigurationException( message );
+        }
+    }
+
+    public PrincipalStoreEntry getPrincipal( KerberosPrincipal principal ) throws Exception
+    {
+        return (PrincipalStoreEntry) execute( new GetPrincipal( principal ) );
+    }
+
+    public String changePassword( KerberosPrincipal principal, KerberosKey newKey ) throws Exception
+    {
+        return (String) execute( new ChangePassword( principal, newKey ) );
+    }
+
+    private Object execute( ContextOperation operation ) throws Exception
+    {
+        return operation.execute( ctx, null );
+    }
+}

Propchange: directory/shared/kerberos/trunk/common/src/java/org/apache/kerberos/store/SingleBaseSearch.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: directory/shared/kerberos/trunk/common/src/java/org/apache/kerberos/store/operations/ChangePassword.java
URL: http://svn.apache.org/viewcvs/directory/shared/kerberos/trunk/common/src/java/org/apache/kerberos/store/operations/ChangePassword.java?rev=329063&r1=329062&r2=329063&view=diff
==============================================================================
--- directory/shared/kerberos/trunk/common/src/java/org/apache/kerberos/store/operations/ChangePassword.java (original)
+++ directory/shared/kerberos/trunk/common/src/java/org/apache/kerberos/store/operations/ChangePassword.java Thu Oct 27 20:37:16 2005
@@ -72,8 +72,8 @@
 
         try
         {
-            dn = search( ctx, searchBaseDn, principal.getName() );
-            Name rdn = getRelativeName( ctx, dn );
+            dn = search( ctx, principal.getName() );
+            Name rdn = getRelativeName( ctx.getNameInNamespace(), dn );
             ctx.modifyAttributes( rdn, mods );
         }
         catch ( NamingException e )
@@ -85,17 +85,15 @@
         return dn;
     }
 
-    private String search( DirContext ctx, Name searchBaseDn, String principal )
-            throws NamingException
+    private String search( DirContext ctx, String principal ) throws NamingException
     {
-        String[] attrIDs = { KerberosAttribute.PRINCIPAL, KerberosAttribute.VERSION,
-                KerberosAttribute.TYPE, KerberosAttribute.KEY };
+        String[] attrIDs = { KerberosAttribute.PRINCIPAL, KerberosAttribute.VERSION, KerberosAttribute.TYPE,
+                KerberosAttribute.KEY };
 
         Attributes matchAttrs = new BasicAttributes( false ); // case-sensitive
         matchAttrs.put( new BasicAttribute( KerberosAttribute.PRINCIPAL, principal ) );
 
-        // Search for objects that have those matching attributes
-        NamingEnumeration answer = ctx.search( searchBaseDn, matchAttrs, attrIDs );
+        NamingEnumeration answer = ctx.search( "", matchAttrs, attrIDs );
 
         if ( answer.hasMore() )
         {
@@ -109,32 +107,27 @@
         return null;
     }
 
-    private Name getRelativeName( DirContext ctx, String baseDn ) throws NamingException
+    private Name getRelativeName( String nameInNamespace, String baseDn ) throws NamingException
     {
         Properties props = new Properties();
         props.setProperty( "jndi.syntax.direction", "right_to_left" );
         props.setProperty( "jndi.syntax.separator", "," );
+        props.setProperty( "jndi.syntax.ignorecase", "true" );
+        props.setProperty( "jndi.syntax.trimblanks", "true" );
 
         Name searchBaseDn = null;
 
-        try
-        {
-            Name ctxRoot = new CompoundName( ctx.getNameInNamespace(), props );
-            searchBaseDn = new CompoundName( baseDn, props );
+        Name ctxRoot = new CompoundName( nameInNamespace, props );
+        searchBaseDn = new CompoundName( baseDn, props );
 
-            if ( !searchBaseDn.startsWith( ctxRoot ) )
-            {
-                throw new NamingException( "Invalid search base for Apache profiles." );
-            }
-
-            for ( int ii = 0; ii < ctxRoot.size(); ii++ )
-            {
-                searchBaseDn.remove( 0 );
-            }
+        if ( !searchBaseDn.startsWith( ctxRoot ) )
+        {
+            throw new NamingException( "Invalid search base " + baseDn );
         }
-        catch ( NamingException e )
+
+        for ( int ii = 0; ii < ctxRoot.size(); ii++ )
         {
-            throw new NamingException( "Failed to initialize search base for Apache profiles." );
+            searchBaseDn.remove( 0 );
         }
 
         return searchBaseDn;

Modified: directory/shared/kerberos/trunk/common/src/java/org/apache/kerberos/store/operations/GetPrincipal.java
URL: http://svn.apache.org/viewcvs/directory/shared/kerberos/trunk/common/src/java/org/apache/kerberos/store/operations/GetPrincipal.java?rev=329063&r1=329062&r2=329063&view=diff
==============================================================================
--- directory/shared/kerberos/trunk/common/src/java/org/apache/kerberos/store/operations/GetPrincipal.java (original)
+++ directory/shared/kerberos/trunk/common/src/java/org/apache/kerberos/store/operations/GetPrincipal.java Thu Oct 27 20:37:16 2005
@@ -62,20 +62,17 @@
             return null;
         }
 
-        String[] attrIDs = { KerberosAttribute.PRINCIPAL, KerberosAttribute.VERSION,
-                KerberosAttribute.TYPE, KerberosAttribute.KEY, KerberosAttribute.SAM_TYPE };
+        String[] attrIDs = { KerberosAttribute.PRINCIPAL, KerberosAttribute.VERSION, KerberosAttribute.TYPE,
+                KerberosAttribute.KEY, KerberosAttribute.SAM_TYPE };
 
         Attributes matchAttrs = new BasicAttributes( false ); // case-sensitive
-
         matchAttrs.put( new BasicAttribute( KerberosAttribute.PRINCIPAL, principal.getName() ) );
 
         PrincipalStoreEntry entry = null;
 
         try
         {
-            // Search for objects that have those matching attributes
-
-            NamingEnumeration answer = ctx.search( base, matchAttrs, attrIDs );
+            NamingEnumeration answer = ctx.search( "", matchAttrs, attrIDs );
 
             if ( answer.hasMore() )
             {