Explorar el Código

HPCC-14205 Add paging functions to ws_access

Four new APIs are added: UserQuery, GroupQuery, and
ResourceQuery, and GroupMemberQuery.

Signed-off-by: wangkx <kevin.wang@lexisnexis.com>
wangkx hace 9 años
padre
commit
e9e96b80b7

+ 97 - 1
esp/scm/ws_access.ecm

@@ -15,6 +15,24 @@
     limitations under the License.
 ############################################################################## */
 
+ESPenum UserSortBy : string
+{
+    Name("Name"),
+    FullName("FullName"),
+    PasswordExpiration("PasswordExpiration"),
+};
+
+ESPenum GroupSortBy : string
+{
+    Name("Name"),
+    ManagedBy("ManagedBy"),
+};
+
+ESPenum ResourceSortBy : string
+{
+    Name("Name"),
+};
+
 ESPstruct UserInfo
 {
     string username;
@@ -69,6 +87,24 @@ ESPresponse UserResponse
     ESParray<ESPstruct UserInfo, User> Users;
 };
 
+ESPrequest [nil_remove] UserQueryRequest
+{
+    string Name;
+    unsigned PageSize;
+    int64 PageStartFrom;
+    ESPenum UserSortBy SortBy;
+    bool Descending(false);
+    int64 CacheHint;
+};
+
+ESPresponse [nil_remove] UserQueryResponse
+{
+    bool NoSecMngr(false);
+    ESParray<ESPstruct UserInfo, User> Users;
+    int64 TotalUsers;
+    int64 CacheHint;
+};
+
 ESPrequest UserEditRequest
 {
     string username;
@@ -260,6 +296,23 @@ ESPresponse GroupResponse
     ESParray<ESPstruct GroupInfo, Group> Groups;
 };
 
+ESPrequest [nil_remove] GroupQueryRequest
+{
+    unsigned PageSize;
+    int64 PageStartFrom;
+    ESPenum GroupSortBy SortBy;
+    bool Descending(false);
+    int64 CacheHint;
+};
+
+ESPresponse [nil_remove] GroupQueryResponse
+{
+    bool NoSecMngr(false);
+    ESParray<ESPstruct GroupInfo, Group> Groups;
+    int64 TotalGroups;
+    int64 CacheHint;
+};
+
 ESPrequest GroupAddRequest
 {
     string groupname;
@@ -301,6 +354,24 @@ ESPresponse GroupEditResponse
     ESParray<ESPstruct UserInfo, User> Users;
 };
 
+ESPrequest [nil_remove] GroupMemberQueryRequest
+{
+    string GroupName;
+    unsigned PageSize;
+    int64 PageStartFrom;
+    ESPenum UserSortBy SortBy;
+    bool Descending(false);
+    int64 CacheHint;
+};
+
+ESPresponse [nil_remove] GroupMemberQueryResponse
+{
+    bool NoSecMngr(false);
+    ESParray<ESPstruct UserInfo, User> Users;
+    int64 TotalUsers;
+    int64 CacheHint;
+};
+
 ESPrequest GroupMemberEditInputRequest
 {
     string searchinput;
@@ -387,6 +458,28 @@ ESPresponse ResourcesResponse
     [min_ver("1.08")] ESPstruct ScopeScanStatusStruct scopeScansStatus;
 };
 
+ESPrequest [nil_remove] ResourceQueryRequest
+{
+    string basedn;
+    string rtype;
+    string rtitle;
+    string prefix;
+    string Name;
+    unsigned PageSize;
+    int64 PageStartFrom;
+    ESPenum ResourceSortBy SortBy;
+    bool Descending(false);
+    int64 CacheHint;
+};
+
+ESPresponse [nil_remove] ResourceQueryResponse
+{
+    bool NoSecMngr(false);
+    ESParray<ESPstruct Resource, Resource> Resources;
+    int64 TotalResources;
+    int64 CacheHint;
+};
+
 ESPrequest ResourceAddInputRequest
 {
     string basedn;
@@ -735,7 +828,10 @@ ESPservice [version("1.09"), exceptions_inline("./smc_xslt/exceptions.xslt")] ws
     ESPmethod [client_xslt("/esp/xslt/access_disablescopescans.xslt")] DisableScopeScans(DisableScopeScansRequest, DisableScopeScansResponse);
     //ESPmethod [client_xslt("/esp/xslt/access_useraccountexport.xslt")] UserAccountExport(UserAccountExportRequest, UserAccountExportResponse);
     ESPmethod UserAccountExport(UserAccountExportRequest, UserAccountExportResponse);
-
+    ESPmethod UserQuery(UserQueryRequest, UserQueryResponse);
+    ESPmethod GroupQuery(GroupQueryRequest, GroupQueryResponse);
+    ESPmethod GroupMemberQuery(GroupMemberQueryRequest, GroupMemberQueryResponse);
+    ESPmethod ResourceQuery(ResourceQueryRequest, ResourceQueryResponse);
 };
 
 SCMexportdef(ws_access);

+ 319 - 0
esp/services/ws_access/ws_accessService.cpp

@@ -469,6 +469,77 @@ bool Cws_accessEx::onUsers(IEspContext &context, IEspUserRequest &req, IEspUserR
     return true;
 }
 
+bool Cws_accessEx::onUserQuery(IEspContext &context, IEspUserQueryRequest &req, IEspUserQueryResponse &resp)
+{
+    try
+    {
+        CLdapSecManager* secmgr = queryLDAPSecurityManager(context);
+        if(!secmgr)
+        {
+            resp.setNoSecMngr(true);
+            return true;
+        }
+        checkUser(context);
+
+        __int64 pageStartFrom = 0;
+        unsigned pageSize = 100;
+        if (!req.getPageSize_isNull())
+            pageSize = req.getPageSize();
+        if (!req.getPageStartFrom_isNull())
+            pageStartFrom = req.getPageStartFrom();
+
+        UserField sortOrder[2] = {UFName, UFterm};
+        CUserSortBy sortBy = req.getSortBy();
+        switch (sortBy)
+        {
+        case CUserSortBy_FullName:
+            sortOrder[0] = UFFullName;
+            break;
+        case CUserSortBy_PasswordExpiration:
+            sortOrder[0] = UFPasswordExpiration;
+            break;
+        default:
+            break;
+        }
+        sortOrder[0] = (UserField) (sortOrder[0] | UFnocase);
+        bool descending = req.getDescending();
+        if (descending)
+            sortOrder[0] = (UserField) (sortOrder[0] | UFreverse);
+
+        unsigned total;
+        __int64 cacheHint;
+        IArrayOf<IEspUserInfo> espUsers;
+        Owned<ISecItemIterator> it = secmgr->getUsersSorted(req.getName(), sortOrder, (const __int64) pageStartFrom, (const unsigned) pageSize, &total, &cacheHint);
+        ForEach(*it)
+        {
+            IPropertyTree& usr = it->query();
+            const char* userName = usr.queryProp(getUserFieldNames(UFName));
+            if (!userName || !*userName)
+                continue;
+
+            Owned<IEspUserInfo> userInfo = createUserInfo();
+            userInfo->setUsername(userName);
+            const char* fullName = usr.queryProp(getUserFieldNames(UFFullName));
+            if (fullName && *fullName)
+                userInfo->setFullname(fullName);
+            const char* passwordExpiration = usr.queryProp(getUserFieldNames(UFPasswordExpiration));
+            if (passwordExpiration && *passwordExpiration)
+                userInfo->setPasswordexpiration(passwordExpiration);
+
+            espUsers.append(*userInfo.getClear());
+        }
+
+        resp.setUsers(espUsers);
+        resp.setTotalUsers(total);
+        resp.setCacheHint(cacheHint);
+    }
+    catch(IException* e)
+    {
+        FORWARDEXCEPTION(context, e, ECLWATCH_INTERNAL_ERROR);
+    }
+    return true;
+}
+
 bool Cws_accessEx::onUserEdit(IEspContext &context, IEspUserEditRequest &req, IEspUserEditResponse &resp)
 {
     try
@@ -681,6 +752,75 @@ bool Cws_accessEx::onGroups(IEspContext &context, IEspGroupRequest &req, IEspGro
     return true;
 }
 
+bool Cws_accessEx::onGroupQuery(IEspContext &context, IEspGroupQueryRequest &req, IEspGroupQueryResponse &resp)
+{
+    try
+    {
+        CLdapSecManager* secmgr = queryLDAPSecurityManager(context);
+        if(!secmgr)
+        {
+            resp.setNoSecMngr(true);
+            return true;
+        }
+
+        checkUser(context);
+
+        __int64 pageStartFrom = 0;
+        unsigned pageSize = 100;
+        if (!req.getPageSize_isNull())
+            pageSize = req.getPageSize();
+        if (!req.getPageStartFrom_isNull())
+            pageStartFrom = req.getPageStartFrom();
+
+        GroupField sortOrder[2] = {GFName, GFterm};
+        CGroupSortBy sortBy = req.getSortBy();
+        switch (sortBy)
+        {
+        case CGroupSortBy_ManagedBy:
+            sortOrder[0] = GFManagedBy;
+            break;
+        default:
+            break;
+        }
+        sortOrder[0] = (GroupField) (sortOrder[0] | UFnocase);
+        bool descending = req.getDescending();
+        if (descending)
+            sortOrder[0] = (GroupField) (sortOrder[0] | UFreverse);
+
+        unsigned total;
+        __int64 cacheHint;
+        IArrayOf<IEspGroupInfo> groups;
+        Owned<ISecItemIterator> it = secmgr->getGroupsSorted(sortOrder, (const __int64) pageStartFrom, (const unsigned) pageSize, &total, &cacheHint);
+        ForEach(*it)
+        {
+            IPropertyTree& g = it->query();
+            const char* groupName = g.queryProp(getGroupFieldNames(GFName));
+            if (!groupName || !*groupName)
+                continue;
+
+            Owned<IEspGroupInfo> groupInfo = createGroupInfo();
+            groupInfo->setName(groupName);
+            const char* managedBy = g.queryProp(getGroupFieldNames(GFManagedBy));
+            if (managedBy && *managedBy)
+                groupInfo->setGroupOwner(managedBy);
+            const char* desc = g.queryProp(getGroupFieldNames(GFDesc));
+            if (desc && *desc)
+                groupInfo->setGroupDesc(desc);
+            groups.append(*groupInfo.getClear());
+        }
+
+        resp.setGroups(groups);
+        resp.setTotalGroups(total);
+        resp.setCacheHint(cacheHint);
+    }
+    catch(IException* e)
+    {
+        FORWARDEXCEPTION(context, e, ECLWATCH_INTERNAL_ERROR);
+    }
+
+    return true;
+}
+
 bool Cws_accessEx::onAddUser(IEspContext &context, IEspAddUserRequest &req, IEspAddUserResponse &resp)
 {
     try
@@ -1151,6 +1291,78 @@ bool Cws_accessEx::onGroupEdit(IEspContext &context, IEspGroupEditRequest &req,
     return true;
 }
 
+bool Cws_accessEx::onGroupMemberQuery(IEspContext &context, IEspGroupMemberQueryRequest &req, IEspGroupMemberQueryResponse &resp)
+{
+    try
+    {
+        CLdapSecManager* secmgr = queryLDAPSecurityManager(context);
+        if(!secmgr)
+        {
+            resp.setNoSecMngr(true);
+            return true;
+        }
+
+        checkUser(context);
+
+        __int64 pageStartFrom = 0;
+        unsigned pageSize = 100;
+        if (!req.getPageSize_isNull())
+            pageSize = req.getPageSize();
+        if (!req.getPageStartFrom_isNull())
+            pageStartFrom = req.getPageStartFrom();
+
+        UserField sortOrder[2] = {UFName, UFterm};
+        CUserSortBy sortBy = req.getSortBy();
+        switch (sortBy)
+        {
+        case CUserSortBy_FullName:
+            sortOrder[0] = UFFullName;
+            break;
+        case CUserSortBy_PasswordExpiration:
+            sortOrder[0] = UFPasswordExpiration;
+            break;
+        default:
+            break;
+        }
+        sortOrder[0] = (UserField) (sortOrder[0] | UFnocase);
+        bool descending = req.getDescending();
+        if (descending)
+            sortOrder[0] = (UserField) (sortOrder[0] | UFreverse);
+
+        unsigned total;
+        __int64 cacheHint;
+        IArrayOf<IEspUserInfo> users;
+        Owned<ISecItemIterator> it = secmgr->getGroupMembersSorted(req.getGroupName(), sortOrder, (const __int64) pageStartFrom, (const unsigned) pageSize, &total, &cacheHint);
+        ForEach(*it)
+        {
+            IPropertyTree& usr = it->query();
+            const char* userName = usr.queryProp(getUserFieldNames(UFName));
+            if (!userName || !*userName)
+                continue;
+
+            Owned<IEspUserInfo> userInfo = createUserInfo();
+            userInfo->setUsername(userName);
+            const char* fullName = usr.queryProp(getUserFieldNames(UFFullName));
+            if (fullName && *fullName)
+                userInfo->setFullname(fullName);
+            const char* passwordExpiration = usr.queryProp(getUserFieldNames(UFPasswordExpiration));
+            if (passwordExpiration && *passwordExpiration)
+                userInfo->setPasswordexpiration(passwordExpiration);
+            users.append(*userInfo.getLink());
+        }
+
+        resp.setUsers(users);
+        resp.setTotalUsers(total);
+        resp.setCacheHint(cacheHint);
+    }
+    catch(IException* e)
+    {
+        FORWARDEXCEPTION(context, e, ECLWATCH_INTERNAL_ERROR);
+    }
+
+    return true;
+}
+
 bool Cws_accessEx::onGroupMemberEditInput(IEspContext &context, IEspGroupMemberEditInputRequest &req, IEspGroupMemberEditInputResponse &resp)
 {
     try
@@ -1522,6 +1734,113 @@ bool Cws_accessEx::onResources(IEspContext &context, IEspResourcesRequest &req,
     return true;
 }
 
+bool Cws_accessEx::onResourceQuery(IEspContext &context, IEspResourceQueryRequest &req, IEspResourceQueryResponse &resp)
+{
+    try
+    {
+        CLdapSecManager* secmgr = queryLDAPSecurityManager(context);
+        if(!secmgr)
+        {
+            resp.setNoSecMngr(true);
+            return true;
+        }
+
+        checkUser(context, req.getRtype(), req.getRtitle(), SecAccess_Read);
+
+        const char* rtypeStr = req.getRtype();
+        if (!rtypeStr || !*rtypeStr)
+            throw MakeStringException(ECLWATCH_INVALID_INPUT, "Rtype not specified");
+
+        StringBuffer baseDN;
+        const char* basednStr = req.getBasedn();
+        if (!basednStr || !*basednStr)
+        {
+            basednStr = getBaseDN(context, rtypeStr, baseDN);
+            if (!basednStr || !*basednStr)
+                throw MakeStringException(ECLWATCH_INVALID_INPUT, "BaseDN not found");
+        }
+
+        SecResourceType rtype = str2type(rtypeStr);
+        const char* moduleTemplate = NULL;
+        ForEachItemIn(x, m_basedns)
+        {
+            IEspDnStruct* curbasedn = &(m_basedns.item(x));
+            if(strieq(curbasedn->getBasedn(), basednStr))
+            {
+                moduleTemplate = curbasedn->getTemplatename();
+                break;
+            }
+        }
+
+        StringBuffer nameReq = req.getName();
+        const char* prefix = req.getPrefix();
+        if (!nameReq.length() && req.getRtitle() && !stricmp(req.getRtitle(), "CodeGenerator Permission"))
+            nameReq.set(prefix);
+
+        __int64 pageStartFrom = 0;
+        unsigned pageSize = 100;
+        if (!req.getPageSize_isNull())
+            pageSize = req.getPageSize();
+        if (!req.getPageStartFrom_isNull())
+            pageStartFrom = req.getPageStartFrom();
+
+        ResourceField sortOrder[2] = {(ResourceField) (RFName | RFnocase), RFterm};
+        bool descending = req.getDescending();
+        if (descending)
+            sortOrder[0] = (ResourceField) (sortOrder[0] | RFreverse);
+
+        unsigned total;
+        __int64 cacheHint;
+        IArrayOf<IEspResource> rarray;
+        Owned<ISecItemIterator> it = secmgr->getResourcesSorted(rtype, basednStr, nameReq.str(),
+            RF_RT_FILE_SCOPE_FILE | RF_RT_MODULE_NO_REPOSITORY, sortOrder,
+            (const __int64) pageStartFrom, (const unsigned) pageSize, &total, &cacheHint);
+        ForEach(*it)
+        {
+            IPropertyTree& r = it->query();
+            const char* rname = r.queryProp(getResourceFieldNames(RFName));
+            if(!rname || !*rname)
+                continue;
+
+            if(prefix && *prefix)
+                rname += strlen(prefix); //Remove the prefix from the name
+
+            bool isSpecial = false;
+            if(rtype == RT_MODULE)
+            {
+                if(strieq(rname, "repository"))
+                    isSpecial = true;
+                else
+                {
+                    if(moduleTemplate != NULL && stricmp(rname, moduleTemplate) == 0)
+                        isSpecial = true;
+
+                    rname = rname + 11; //Remove "repository." from the name
+                }
+            }
+
+            Owned<IEspResource> oneresource = createResource();
+            oneresource->setName(rname);
+            oneresource->setIsSpecial(isSpecial);
+            const char* desc = r.queryProp(getResourceFieldNames(RFDesc));
+            if (desc && *desc)
+                oneresource->setDescription(desc);
+
+            rarray.append(*oneresource.getClear());
+        }
+
+        resp.setResources(rarray);
+        resp.setTotalResources(total);
+        resp.setCacheHint(cacheHint);
+    }
+    catch(IException* e)
+    {
+        FORWARDEXCEPTION(context, e, ECLWATCH_INTERNAL_ERROR);
+    }
+
+    return true;
+}
+
 bool Cws_accessEx::onResourceAddInput(IEspContext &context, IEspResourceAddInputRequest &req, IEspResourceAddInputResponse &resp)
 {
     try

+ 4 - 0
esp/services/ws_access/ws_accessService.hpp

@@ -88,12 +88,16 @@ public:
     virtual void init(IPropertyTree *cfg, const char *process, const char *service);
 
     virtual bool onUsers(IEspContext &context, IEspUserRequest &req, IEspUserResponse &resp);
+    virtual bool onUserQuery(IEspContext &context, IEspUserQueryRequest &req, IEspUserQueryResponse &resp);
     virtual bool onUserEdit(IEspContext &context, IEspUserEditRequest &req, IEspUserEditResponse &resp);
     virtual bool onGroups(IEspContext &context, IEspGroupRequest &req, IEspGroupResponse &resp);
+    virtual bool onGroupQuery(IEspContext &context, IEspGroupQueryRequest &req, IEspGroupQueryResponse &resp);
+    virtual bool onGroupMemberQuery(IEspContext &context, IEspGroupMemberQueryRequest &req, IEspGroupMemberQueryResponse &resp);
     virtual bool onAddUser(IEspContext &context, IEspAddUserRequest &req, IEspAddUserResponse &resp);
     virtual bool onUserAction(IEspContext &context, IEspUserActionRequest &req, IEspUserActionResponse &resp);
     virtual bool onPermissions(IEspContext &context, IEspBasednsRequest &req, IEspBasednsResponse &resp);
     virtual bool onResources(IEspContext &context, IEspResourcesRequest &req, IEspResourcesResponse &resp);
+    virtual bool onResourceQuery(IEspContext &context, IEspResourceQueryRequest &req, IEspResourceQueryResponse &resp);
     virtual bool onResourceAdd(IEspContext &context, IEspResourceAddRequest &req, IEspResourceAddResponse &resp);
     virtual bool onResourceAddInput(IEspContext &context, IEspResourceAddInputRequest &req, IEspResourceAddInputResponse &resp);
     virtual bool onResourcePermissions(IEspContext &context, IEspResourcePermissionsRequest &req, IEspResourcePermissionsResponse &resp);

+ 418 - 24
system/security/LdapSecurity/ldapconnection.cpp

@@ -28,6 +28,7 @@
 #include "jrespool.tpp"
 #include "mpbase.hpp"
 #include "dautils.hpp"
+#include "dasds.hpp"
 
 #undef new
 #include <map>
@@ -57,6 +58,73 @@
 
 #define PWD_NEVER_EXPIRES (__int64)0x8000000000000000
 
+const char* UserFieldNames[] = { "@id", "@name", "@fullname", "@passwordexpiration" };
+
+extern __declspec(dllimport) const char* getUserFieldNames(UserField field)
+{
+    if (field < UFterm)
+        return UserFieldNames[field];
+    return NULL;
+}
+
+const char* GroupFieldNames[] = { "@name", "@managedby", "@desc" };
+
+extern __declspec(dllimport) const char* getGroupFieldNames(GroupField field)
+{
+    if (field < GFterm)
+        return GroupFieldNames[field];
+    return NULL;
+}
+
+const char* ResourceFieldNames[] = { "@name", "@desc" };
+
+extern __declspec(dllimport) const char* getResourceFieldNames(ResourceField field)
+{
+    if (field < RFterm)
+        return ResourceFieldNames[field];
+    return NULL;
+}
+
+class CSecItemIterator: public CInterfaceOf<ISecItemIterator>
+{
+    IArrayOf<IPropertyTree> attrs;
+    unsigned index;
+public:
+    CSecItemIterator(IArrayOf<IPropertyTree>& trees)
+    {
+        ForEachItemIn(t, trees)
+            attrs.append(*LINK(&trees.item(t)));
+        index = 0;
+    }
+
+    virtual ~CSecItemIterator()
+    {
+        attrs.kill();
+    }
+
+    bool  first()
+    {
+        index = 0;
+        return (attrs.ordinality()!=0);
+    }
+
+    bool  next()
+    {
+        index++;
+        return (index<attrs.ordinality());
+    }
+
+    bool  isValid()
+    {
+        return (index<attrs.ordinality());
+    }
+
+    IPropertyTree &  query()
+    {
+        return attrs.item(index);
+    }
+};
+
 class CLoadBalancer : public CInterface, implements IInterface
 {
 private:
@@ -2314,6 +2382,95 @@ public:
         return true;
     }
 
+    virtual IPropertyTreeIterator* getUserIterator(const char* userName)
+    {
+        IUserArray users;
+        retrieveUsers(userName, users);
+        Owned<IPropertyTree> usersTree = createPTree("Users");
+        ForEachItemIn(i, users)
+            addUserTree(users.item(i), usersTree);
+
+        return usersTree->getElements("*");
+    }
+
+    void addUserTree(ISecUser& usr, IPropertyTree* users)
+    {
+        const char* usrName = usr.getName();
+        if(!usrName || !*usrName)
+            return;
+
+        const char* fullName = usr.getFullName();
+        StringBuffer sb;
+        switch (usr.getPasswordDaysRemaining())//-1 if expired, -2 if never expires
+        {
+        case scPasswordExpired:
+            sb.set("Expired");
+            break;
+        case scPasswordNeverExpires:
+            sb.set("Never");
+            break;
+        default:
+            CDateTime dt;
+            usr.getPasswordExpiration(dt);
+            dt.getDateString(sb);
+            break;
+        }
+
+        Owned<IPTree> userTree = createPTree("User");
+        userTree->addProp(getUserFieldNames(UFName), usrName);
+        if (fullName && *fullName)
+            userTree->addProp(getUserFieldNames(UFFullName), fullName);
+        userTree->addPropInt(getUserFieldNames(UFUserID), usr.getUserID());
+        userTree->addProp(getUserFieldNames(UFPasswordExpiration), sb.str());
+        users->addPropTree("User", userTree.getClear());
+    }
+
+    ISecItemIterator* getUsersSorted(const char* userName, UserField* sortOrder, const unsigned pageStartFrom, const unsigned pageSize, unsigned* total, __int64* cacheHint)
+    {
+        class CElementsPager : public CSimpleInterface, implements IElementsPager
+        {
+            ILdapClient* ldapClient;
+            StringAttr userName;
+            StringAttr sortOrder;
+
+        public:
+            IMPLEMENT_IINTERFACE_USING(CSimpleInterface);
+
+            CElementsPager(ILdapClient* _ldapClient, const char* _userName, const char*_sortOrder)
+                : ldapClient(_ldapClient), userName(_userName), sortOrder(_sortOrder) { };
+            virtual IRemoteConnection* getElements(IArrayOf<IPropertyTree>& elements)
+            {
+                StringArray unknownAttributes;
+                Owned<IPropertyTreeIterator> iter = ldapClient->getUserIterator(userName.get());
+                sortElements(iter, sortOrder.get(), NULL, NULL, unknownAttributes, elements);
+                return NULL;
+            }
+            virtual bool allMatchingElementsReceived() { return true; }//For now, ldap always returns all of matched users.
+        };
+
+        StringBuffer so;
+        if (sortOrder)
+        {
+            for (unsigned i=0;sortOrder[i]!=UFterm;i++)
+            {
+                if (so.length())
+                    so.append(',');
+                int fmt = sortOrder[i];
+                if (fmt&UFreverse)
+                    so.append('-');
+                if (fmt&UFnocase)
+                    so.append('?');
+                if (fmt&UFnumeric)
+                    so.append('#');
+                so.append(getUserFieldNames((UserField) (fmt&0xff)));
+            }
+        }
+        IArrayOf<IPropertyTree> results;
+        Owned<IElementsPager> elementsPager = new CElementsPager(this, userName, so.length()?so.str():NULL);
+        Owned<IRemoteConnection> conn=getElementsPaged(elementsPager, pageStartFrom, pageSize, NULL, "", cacheHint, results, total, NULL, false);
+        return new CSecItemIterator(results);
+    }
+
     virtual bool userInGroup(const char* userdn, const char* groupdn)
     {
         const char* fldname;
@@ -2450,7 +2607,7 @@ public:
             };
 
             char *homedir_values[] = {(char*)ldapuser->getHomedirectory(), NULL };
-            LDAPMod homedir_attr = 
+            LDAPMod homedir_attr =
             {
                 LDAP_MOD_REPLACE,
                 "homedirectory",
@@ -2529,7 +2686,7 @@ public:
                 };
 
                 char *homedir_values[] = { NULL };
-                LDAPMod homedir_attr = 
+                LDAPMod homedir_attr =
                 {
                     LDAP_MOD_DELETE,
                     "homedirectory",
@@ -2537,7 +2694,7 @@ public:
                 };
 
                 char *loginshell_values[] = { NULL };
-                LDAPMod loginshell_attr = 
+                LDAPMod loginshell_attr =
                 {
                     LDAP_MOD_DELETE,
                     "loginshell",
@@ -2563,7 +2720,7 @@ public:
             CLdapSecUser* ldapuser = dynamic_cast<CLdapSecUser*>(&user);
 
             char *cn_values[] = {(char*)username, NULL };
-            LDAPMod cn_attr = 
+            LDAPMod cn_attr =
             {
                 LDAP_MOD_ADD,
                 "cn",
@@ -2579,7 +2736,7 @@ public:
             };
 
             char *user_values[] = {(char*)username, NULL };
-            LDAPMod user_attr = 
+            LDAPMod user_attr =
             {
                 LDAP_MOD_ADD,
                 "sudoUser",
@@ -2652,7 +2809,7 @@ public:
 
             Owned<ILdapConnection> lconn = m_connections->getConnection();
             LDAP* ld = ((CLdapConnection*)lconn.get())->getLd();
-            
+
             int rc = ldap_delete_ext_s(ld, (char*)dn.str(), NULL, NULL);
 
             if ( rc != LDAP_SUCCESS )
@@ -2663,29 +2820,29 @@ public:
         else if(stricmp(type, "sudoersupdate") == 0)
         {
             CLdapSecUser* ldapuser = dynamic_cast<CLdapSecUser*>(&user);
-            
+
             char* sudoHost = (char*)ldapuser->getSudoHost();
             char* sudoCommand = (char*)ldapuser->getSudoCommand();
             char* sudoOption = (char*)ldapuser->getSudoOption();
 
             char *host_values[] = {(sudoHost&&*sudoHost)?sudoHost:NULL, NULL };
-            LDAPMod host_attr = 
+            LDAPMod host_attr =
             {
                 LDAP_MOD_REPLACE,
                 "sudoHost",
                 host_values
             };
-            
+
             char *cmd_values[] = {(sudoCommand&&*sudoCommand)?sudoCommand:NULL, NULL };
-            LDAPMod cmd_attr = 
+            LDAPMod cmd_attr =
             {
                 LDAP_MOD_REPLACE,
                 "sudoCommand",
                 cmd_values
             };
-            
+
             char *option_values[] = {(sudoOption&&*sudoOption)?sudoOption:NULL, NULL };
-            LDAPMod option_attr = 
+            LDAPMod option_attr =
             {
                 LDAP_MOD_REPLACE,
                 "sudoOption",
@@ -2694,7 +2851,7 @@ public:
 
             LDAPMod *attrs[4];
             int ind = 0;
-            
+
             attrs[ind++] = &host_attr;
             attrs[ind++] = &cmd_attr;
             attrs[ind++] = &option_attr;
@@ -2735,10 +2892,10 @@ public:
 
         LDAP* ld = ((CLdapConnection*)lconn.get())->getLd();
 
-        char        *attribute, **values = NULL;       
+        char        *attribute, **values = NULL;
         LDAPMessage *message;
 
-        TIMEVAL timeOut = {LDAPTIMEOUT,0};   
+        TIMEVAL timeOut = {LDAPTIMEOUT,0};
 
         StringBuffer filter;
         filter.append("sAMAccountName=").append(username);
@@ -2865,7 +3022,7 @@ public:
     {
         if(!username || !*username)
             return false;
-        
+
         const char* sysuser = m_ldapconfig->getSysUser();
         if(sysuser && *sysuser && strcmp(username, sysuser) == 0)
             throw MakeStringException(-1, "You can't change password of the system user.");
@@ -2951,15 +3108,15 @@ public:
 #endif
             changePasswordSSL(username, newPassword);
         }
-        else 
+        else
         {
             StringBuffer filter;
             filter.append("uid=").append(username);
 
-            char        **values = NULL;       
+            char        **values = NULL;
             LDAPMessage *message;
 
-            TIMEVAL timeOut = {LDAPTIMEOUT,0};   
+            TIMEVAL timeOut = {LDAPTIMEOUT,0};
 
             Owned<ILdapConnection> lconn = m_connections->getConnection();
             LDAP* ld = ((CLdapConnection*)lconn.get())->getLd();
@@ -2976,7 +3133,7 @@ public:
 
             StringBuffer userdn;
             message = LdapFirstEntry( ld, searchResult);
-            
+
             if(message != NULL)
             {
                 char *p = ldap_get_dn(ld, message);
@@ -2984,10 +3141,10 @@ public:
                 ldap_memfree(p);
             }
             char* passwdvalue[] = { (char*)newPassword, NULL };
-            LDAPMod pmod = 
+            LDAPMod pmod =
             {
                 LDAP_MOD_REPLACE,
-                "userpassword", 
+                "userpassword",
                 passwdvalue
             };
 
@@ -3103,7 +3260,7 @@ public:
             filter.appendf(")(|(%s=*%s*)))", "uNCName", searchstr);
         }
 
-        TIMEVAL timeOut = {LDAPTIMEOUT,0};   
+        TIMEVAL timeOut = {LDAPTIMEOUT,0};
 
         Owned<ILdapConnection> lconn = m_connections->getConnection();
         LDAP* ld = ((CLdapConnection*)lconn.get())->getLd();
@@ -3168,6 +3325,95 @@ public:
         return true;
     }
 
+    virtual IPropertyTreeIterator* getResourceIterator(SecResourceType rtype, const char * basedn,
+        const char* prefix, const char* resourceName, unsigned extraNameFilter)
+    {
+        IArrayOf<ISecResource> resources;
+        getResourcesEx(rtype, basedn, prefix, resourceName, resources);
+
+        Owned<IPTree> resourceTree = createPTree("Resources");
+        ForEachItemIn(i, resources)
+        {
+            ISecResource& resource = resources.item(i);
+            const char* resourceName = resource.getName();
+            if (!resourceName || !*resourceName)
+                continue;
+            if (checkResourceNameExtraFilter(rtype, resourceName, extraNameFilter))
+                addResourceTree(resourceName, resource.getDescription(), resourceTree);
+        }
+        return resourceTree->getElements("*");
+    }
+
+    bool checkResourceNameExtraFilter(SecResourceType rtype, const char* name, unsigned extraNameFilter)
+    {
+        if((rtype == RT_FILE_SCOPE) && (extraNameFilter & RF_RT_FILE_SCOPE_FILE) && strieq(name, "file"))
+            return false;
+        if((rtype == RT_MODULE) && (extraNameFilter & RF_RT_MODULE_NO_REPOSITORY) && strnicmp(name, "repository.", 11))
+            return false;
+        return true;
+    }
+
+    void addResourceTree(const char* name, const char* desc, IPropertyTree* elements)
+    {
+        if (!name || !*name)
+            return;
+
+        Owned<IPTree> element = createPTree();
+        element->addProp(getResourceFieldNames(RFName), name);
+        if (desc && *desc)
+            element->addProp(getResourceFieldNames(RFDesc), desc);
+        elements->addPropTree("Resource", element.getClear());
+    }
+
+    ISecItemIterator* getResourcesSorted(SecResourceType rtype, const char * basedn, const char* resourceName, unsigned extraNameFilter,
+        ResourceField* sortOrder, const unsigned pageStartFrom, const unsigned pageSize, unsigned* total, __int64* cacheHint)
+    {
+        class CElementsPager : public CSimpleInterface, implements IElementsPager
+        {
+            ILdapClient* ldapClient;
+            StringAttr sortOrder, basedn, resourceName;
+            SecResourceType rtype;
+            unsigned extraNameFilter;
+
+        public:
+            IMPLEMENT_IINTERFACE_USING(CSimpleInterface);
+
+            CElementsPager(ILdapClient* _ldapClient, SecResourceType _rtype, const char * _basedn, const char* _resourceName,
+                unsigned _extraNameFilter, const char*_sortOrder) : ldapClient(_ldapClient), rtype(_rtype), basedn(_basedn),
+                resourceName(_resourceName), extraNameFilter(_extraNameFilter), sortOrder(_sortOrder) { };
+            virtual IRemoteConnection* getElements(IArrayOf<IPropertyTree>& elements)
+            {
+                StringArray unknownAttributes;
+                Owned<IPropertyTreeIterator> iter = ldapClient->getResourceIterator(rtype, basedn.get(), "", resourceName.get(), extraNameFilter);
+                sortElements(iter, sortOrder.get(), NULL, NULL, unknownAttributes, elements);
+                return NULL;
+            }
+            virtual bool allMatchingElementsReceived() { return true; }//For now, ldap always returns all of matched users.
+        };
+
+        StringBuffer so;
+        if (sortOrder)
+        {
+            for (unsigned i=0;sortOrder[i]!=RFterm;i++)
+            {
+                if (so.length())
+                    so.append(',');
+                int fmt = sortOrder[i];
+                if (fmt&UFreverse)
+                    so.append('-');
+                if (fmt&UFnocase)
+                    so.append('?');
+                if (fmt&UFnumeric)
+                    so.append('#');
+                so.append(getUserFieldNames((UserField) (fmt&0xff)));
+            }
+        }
+        IArrayOf<IPropertyTree> results;
+        Owned<IElementsPager> elementsPager = new CElementsPager(this, rtype, basedn, resourceName, extraNameFilter, so.length()?so.str():NULL);
+        Owned<IRemoteConnection> conn=getElementsPaged(elementsPager, pageStartFrom, pageSize, NULL, "", cacheHint, results, total, NULL, false);
+        return new CSecItemIterator(results);
+    }
+
     virtual bool getPermissionsArray(const char* basedn, SecResourceType rtype, const char* name, IArrayOf<CPermission>& permissions)
     {
         StringBuffer basednbuf;
@@ -3213,7 +3459,7 @@ public:
         else
             filter.append("objectClass=groupofuniquenames");
 
-        TIMEVAL timeOut = {LDAPTIMEOUT,0};   
+        TIMEVAL timeOut = {LDAPTIMEOUT,0};
 
         Owned<ILdapConnection> lconn = m_connections->getConnection();
         LDAP* ld = ((CLdapConnection*)lconn.get())->getLd();
@@ -3246,6 +3492,76 @@ public:
 
     }
 
+    virtual IPropertyTreeIterator* getGroupIterator()
+    {
+        StringArray groupNames, managedBy, descriptions;
+        getAllGroups(groupNames, managedBy, descriptions);
+
+        Owned<IPTree> groups = createPTree("Groups");
+        ForEachItemIn(i, groupNames)
+            addGroupTree(groupNames.item(i), managedBy.item(i), descriptions.item(i), groups);
+        return groups->getElements("*");
+    }
+
+    void addGroupTree(const char* name, const char* manageBy, const char* desc, IPropertyTree* groups)
+    {
+        if (!name || !*name)
+            return;
+
+        Owned<IPTree> group = createPTree();
+        group->addProp(getGroupFieldNames(GFName), name);
+        if (manageBy && *manageBy)
+            group->addProp(getGroupFieldNames(GFManagedBy), manageBy);
+        if (desc && *desc)
+            group->addProp(getGroupFieldNames(GFDesc), desc);
+        groups->addPropTree("Group", group.getClear());
+    }
+
+    ISecItemIterator* getGroupsSorted(GroupField* sortOrder, const unsigned pageStartFrom, const unsigned pageSize, unsigned* total, __int64* cacheHint)
+    {
+        class CElementsPager : public CSimpleInterface, implements IElementsPager
+        {
+            ILdapClient* ldapClient;
+            StringAttr sortOrder;
+
+        public:
+            IMPLEMENT_IINTERFACE_USING(CSimpleInterface);
+
+            CElementsPager(ILdapClient* _ldapClient, const char*_sortOrder)
+                : ldapClient(_ldapClient), sortOrder(_sortOrder) { };
+            virtual IRemoteConnection* getElements(IArrayOf<IPropertyTree>& elements)
+            {
+                StringArray unknownAttributes;
+                Owned<IPropertyTreeIterator> iter = ldapClient->getGroupIterator();
+                sortElements(iter, sortOrder.get(), NULL, NULL, unknownAttributes, elements);
+                return NULL;
+            }
+            virtual bool allMatchingElementsReceived() { return true; }//For now, ldap always returns all of matched users.
+        };
+
+        StringBuffer so;
+        if (sortOrder)
+        {
+            for (unsigned i=0;sortOrder[i]!=GFterm;i++)
+            {
+                if (so.length())
+                    so.append(',');
+                int fmt = sortOrder[i];
+                if (fmt&UFreverse)
+                    so.append('-');
+                if (fmt&UFnocase)
+                    so.append('?');
+                if (fmt&UFnumeric)
+                    so.append('#');
+                so.append(getUserFieldNames((UserField) (fmt&0xff)));
+            }
+        }
+        IArrayOf<IPropertyTree> results;
+        Owned<IElementsPager> elementsPager = new CElementsPager(this, so.length()?so.str():NULL);
+        Owned<IRemoteConnection> conn=getElementsPaged(elementsPager, pageStartFrom, pageSize, NULL, "", cacheHint, results, total, NULL, false);
+        return new CSecItemIterator(results);
+    }
+
     virtual bool changePermission(CPermissionAction& action)
     {
         StringBuffer basednbuf;
@@ -3699,6 +4015,84 @@ public:
 
     }
 
+    virtual IPropertyTreeIterator* getGroupMemberIterator(const char* groupName)
+    {
+        StringArray users;
+        getGroupMembers(groupName, users);
+
+        Owned<IPropertyTree> usersTree = createPTree("Users");
+        ForEachItemIn(i, users)
+        {
+            const char* usrName = users.item(i);
+            if (!usrName || !*usrName)
+                continue;
+
+            IUserArray usersInBaseDN;
+            retrieveUsers(usrName, usersInBaseDN);
+            ForEachItemIn(x, usersInBaseDN)
+            {
+                ISecUser& usr = usersInBaseDN.item(x);
+                const char* usrName0 = usr.getName();
+                if(usrName0 && strieq(usrName, usrName0))
+                {
+                    //BUG#41536: The users in the Administrators group are all the users on the whole
+                    //active directory, while the users in the users list are only the users who are
+                    //under the "usersBasedn" of this environment. So, we should only return the users
+                    //who are in the usersBasedn.
+                    addUserTree(usr, usersTree);
+                    break;
+                }
+            }
+        }
+        return usersTree->getElements("*");
+    }
+
+    ISecItemIterator* getGroupMembersSorted(const char* groupName, UserField* sortOrder, const unsigned pageStartFrom, const unsigned pageSize,
+        unsigned* total, __int64* cacheHint)
+    {
+        class CElementsPager : public CSimpleInterface, implements IElementsPager
+        {
+            ILdapClient* ldapClient;
+            StringAttr sortOrder, groupName;
+
+        public:
+            IMPLEMENT_IINTERFACE_USING(CSimpleInterface);
+
+            CElementsPager(ILdapClient* _ldapClient, const char*_groupName, const char*_sortOrder)
+                : ldapClient(_ldapClient), groupName(_groupName), sortOrder(_sortOrder) { };
+            virtual IRemoteConnection* getElements(IArrayOf<IPropertyTree>& elements)
+            {
+                StringArray unknownAttributes;
+                Owned<IPropertyTreeIterator> iter = ldapClient->getGroupMemberIterator(groupName.str());
+                sortElements(iter, sortOrder.get(), NULL, NULL, unknownAttributes, elements);
+                return NULL;
+            }
+            virtual bool allMatchingElementsReceived() { return true; }//For now, ldap always returns all of matched users.
+        };
+
+        StringBuffer so;
+        if (sortOrder)
+        {
+            for (unsigned i=0;sortOrder[i]!=UFterm;i++)
+            {
+                if (so.length())
+                    so.append(',');
+                int fmt = sortOrder[i];
+                if (fmt&UFreverse)
+                    so.append('-');
+                if (fmt&UFnocase)
+                    so.append('?');
+                if (fmt&UFnumeric)
+                    so.append('#');
+                so.append(getUserFieldNames((UserField) (fmt&0xff)));
+            }
+        }
+        IArrayOf<IPropertyTree> results;
+        Owned<IElementsPager> elementsPager = new CElementsPager(this, groupName, so.length()?so.str():NULL);
+        Owned<IRemoteConnection> conn=getElementsPaged(elementsPager, pageStartFrom, pageSize, NULL, "", cacheHint, results, total, NULL, false);
+        return new CSecItemIterator(results);
+    }
+
     virtual void deleteResource(SecResourceType rtype, const char* name, const char* basedn)
     {
         if(basedn == NULL || *basedn == '\0')

+ 57 - 1
system/security/LdapSecurity/ldapconnection.hpp

@@ -70,7 +70,50 @@ enum ACT_TYPE
     USER_ACT = 0,
     GROUP_ACT = 1
 };
-    
+
+enum UserField
+{
+    UFUserID = 0,
+    UFName = 1,
+    UFFullName = 2,
+    UFPasswordExpiration = 3,
+    UFterm = 4,
+    UFreverse = 256,
+    UFnocase = 512,
+    UFnumeric = 1024
+};
+
+enum GroupField
+{
+    GFName = 0,
+    GFManagedBy = 1,
+    GFDesc = 2,
+    GFterm = 3,
+    GFreverse = 256,
+    GFnocase = 512,
+    GFnumeric = 1024
+};
+
+#define RF_NONE                     0x00
+#define RF_RT_FILE_SCOPE_FILE       0x01
+#define RF_RT_MODULE_NO_REPOSITORY  0x02
+
+enum ResourceField
+{
+    RFName = 0,
+    RFDesc = 1,
+    RFterm = 2,
+    RFreverse = 256,
+    RFnocase = 512,
+    RFnumeric = 1024
+};
+
+extern __declspec(dllimport) const char* getUserFieldNames(UserField feild);
+extern __declspec(dllimport) const char* getGroupFieldNames(GroupField feild);
+extern __declspec(dllimport) const char* getResourceFieldNames(ResourceField feild);
+
+typedef IIteratorOf<IPropertyTree> ISecItemIterator;
+
 interface IPermissionProcessor;
 
 interface ILdapConnection : extends IInterface
@@ -170,7 +213,16 @@ interface ILdapClient : extends IInterface
     virtual void setPermissionProcessor(IPermissionProcessor* pp) = 0;
     virtual bool retrieveUsers(IUserArray& users) = 0;
     virtual bool retrieveUsers(const char* searchstr, IUserArray& users) = 0;
+    virtual IPropertyTreeIterator* getUserIterator(const char* userName) = 0;
+    virtual ISecItemIterator* getUsersSorted(const char* userName, UserField* sortOrder, const unsigned pageStartFrom, const unsigned pageSize,
+        unsigned *total, __int64 *cachehint) = 0;
     virtual void getAllGroups(StringArray & groups, StringArray & managedBy, StringArray & descriptions) = 0;
+    virtual IPropertyTreeIterator* getGroupIterator() = 0;
+    virtual ISecItemIterator* getGroupsSorted(GroupField* sortOrder, const unsigned pageStartFrom, const unsigned pageSize,
+        unsigned *total, __int64 *cachehint) = 0;
+    virtual IPropertyTreeIterator* getGroupMemberIterator(const char* groupName) = 0;
+    virtual ISecItemIterator* getGroupMembersSorted(const char* groupName, UserField* sortOrder, const unsigned pageStartFrom, const unsigned pageSize,
+        unsigned *total, __int64 *cachehint) = 0;
     virtual void setResourceBasedn(const char* rbasedn, SecResourceType rtype = RT_DEFAULT) = 0;
     virtual ILdapConfig* getLdapConfig() = 0;
     virtual bool userInGroup(const char* userdn, const char* groupdn) = 0;
@@ -179,6 +231,10 @@ interface ILdapClient : extends IInterface
     virtual bool updateUserPassword(const char* username, const char* newPassword) = 0;
     virtual bool getResources(SecResourceType rtype, const char * basedn, const char* prefix, IArrayOf<ISecResource>& resources) = 0;
     virtual bool getResourcesEx(SecResourceType rtype, const char * basedn, const char* prefix, const char* searchstr, IArrayOf<ISecResource>& resources) = 0;
+    virtual IPropertyTreeIterator* getResourceIterator(SecResourceType rtype, const char * basedn, const char* prefix,
+        const char* resourceName, unsigned extraNameFilter) = 0;
+    virtual ISecItemIterator* getResourcesSorted(SecResourceType rtype, const char * basedn, const char* resourceName, unsigned extraNameFilter,
+        ResourceField* sortOrder, const unsigned pageStartFrom, const unsigned pageSize, unsigned *total, __int64 *cachehint) = 0;
     virtual bool getPermissionsArray(const char* basedn, SecResourceType rtype, const char* name, IArrayOf<CPermission>& permissions) = 0;
     virtual bool changePermission(CPermissionAction& action) = 0;
     virtual void changeUserGroup(const char* action, const char* username, const char* groupname) = 0;

+ 24 - 0
system/security/LdapSecurity/ldapsecurity.cpp

@@ -968,6 +968,12 @@ void CLdapSecManager::searchUsers(const char* searchstr, IUserArray& users)
     m_ldap_client->retrieveUsers(searchstr, users);
 }
 
+ISecItemIterator* CLdapSecManager::getUsersSorted(const char* userName, UserField* sortOrder, const unsigned pageStartFrom,
+    const unsigned pageSize, unsigned* total, __int64* cacheHint)
+{
+    return m_ldap_client->getUsersSorted(userName, sortOrder, pageStartFrom, pageSize, total, cacheHint);
+}
+
 void CLdapSecManager::getAllUsers(IUserArray& users)
 {
     m_ldap_client->retrieveUsers(users);
@@ -983,6 +989,12 @@ bool CLdapSecManager::getResourcesEx(SecResourceType rtype, const char * basedn,
     return m_ldap_client->getResourcesEx(rtype, basedn, "", searchstr, resources);
 }
 
+ISecItemIterator* CLdapSecManager::getResourcesSorted(SecResourceType rtype, const char * basedn, const char * resourceName, unsigned extraNameFilter,
+    ResourceField* sortOrder, const unsigned pageStartFrom, const unsigned pageSize, unsigned *total, __int64 *cachehint)
+{
+    return m_ldap_client->getResourcesSorted(rtype, basedn, resourceName, extraNameFilter, sortOrder, pageStartFrom, pageSize, total, cachehint);
+}
+
 void CLdapSecManager::setExtraParam(const char * name, const char * value)
 {
     if(name == NULL || name[0] == '\0')
@@ -1129,6 +1141,18 @@ void CLdapSecManager::getAllGroups(StringArray & groups, StringArray & managedBy
     m_ldap_client->getAllGroups(groups, managedBy, descriptions);
 }
 
+ISecItemIterator* CLdapSecManager::getGroupsSorted(GroupField* sortOrder, const unsigned pageStartFrom, const unsigned pageSize,
+    unsigned *total, __int64 *cachehint)
+{
+    return m_ldap_client->getGroupsSorted(sortOrder, pageStartFrom, pageSize, total, cachehint);
+}
+
+ISecItemIterator* CLdapSecManager::getGroupMembersSorted(const char* groupName, UserField* sortOrder, const unsigned pageStartFrom, const unsigned pageSize,
+    unsigned *total, __int64 *cachehint)
+{
+    return m_ldap_client->getGroupMembersSorted(groupName, sortOrder, pageStartFrom, pageSize, total, cachehint);
+}
+
 bool CLdapSecManager::getPermissionsArray(const char* basedn, SecResourceType rtype, const char* name, IArrayOf<CPermission>& permissions)
 {
     return m_ldap_client->getPermissionsArray(basedn, rtype, name, permissions);

+ 5 - 0
system/security/LdapSecurity/ldapsecurity.ipp

@@ -371,6 +371,7 @@ public:
     virtual ISecUser * findUser(const char * username);
     virtual ISecUserIterator * getAllUsers();
     virtual void searchUsers(const char* searchstr, IUserArray& users);
+    virtual ISecItemIterator* getUsersSorted(const char* userName, UserField* sortOrder, const unsigned pageStartFrom, const unsigned pageSize, unsigned* total, __int64* cacheHint);
     virtual void getAllUsers(IUserArray& users);
     virtual void setExtraParam(const char * name, const char * value);
     virtual IAuthMap * createAuthMap(IPropertyTree * authconfig);
@@ -384,10 +385,14 @@ public:
 
     virtual bool getResources(SecResourceType rtype, const char * basedn, IArrayOf<ISecResource>& resources);
     virtual bool getResourcesEx(SecResourceType rtype, const char * basedn, const char * searchstr, IArrayOf<ISecResource>& resources);
+    virtual ISecItemIterator* getResourcesSorted(SecResourceType rtype, const char* basedn, const char* resourceName, unsigned extraNameFilter,
+        ResourceField* sortOrder, const unsigned pageStartFrom, const unsigned pageSize, unsigned* total, __int64* cacheHint);
     virtual void cacheSwitch(SecResourceType rtype, bool on);
 
     virtual bool getPermissionsArray(const char* basedn, SecResourceType rtype, const char* name, IArrayOf<CPermission>& permissions);
     virtual void getAllGroups(StringArray & groups, StringArray & managedBy, StringArray & descriptions);
+    virtual ISecItemIterator* getGroupsSorted(GroupField* sortOrder, const unsigned pageStartFrom, const unsigned pageSize, unsigned* total, __int64* cacheHint);
+    virtual ISecItemIterator* getGroupMembersSorted(const char* groupName, UserField* sortOrder, const unsigned pageStartFrom, const unsigned pageSize, unsigned* total, __int64* cacheHint);
     virtual void getGroups(const char* username, StringArray & groups);
     virtual bool changePermission(CPermissionAction& action);
     virtual void changeUserGroup(const char* action, const char* username, const char* groupname);