فهرست منبع

HPCC-15696 Add column-level security support for Security Managers

Add new LDAP Security Manager interfaces for View support. With these a
service can create/enum/delete views, add/enum/delete users from a view, and
add/enum/delete lfn/column pairs from a view. Also added an authenticate
method where a service can see if a given user/view allows access to a
set of lfn/columns

Signed-off-by: Russ Whitehead <william.whitehead@lexisnexis.com>
Russ Whitehead 9 سال پیش
والد
کامیت
ee83912b50

+ 386 - 14
system/security/LdapSecurity/ldapconnection.cpp

@@ -240,6 +240,7 @@ private:
     StringBuffer         m_group_basedn;
     StringBuffer         m_resource_basedn;
     StringBuffer         m_filescope_basedn;
+    StringBuffer         m_view_basedn;
     StringBuffer         m_workunitscope_basedn;
     StringBuffer         m_sudoers_basedn;
     StringBuffer         m_template_name;
@@ -374,6 +375,12 @@ public:
             LdapUtils::normalizeDn(dnbuf.str(), m_basedn.str(), m_filescope_basedn);
 
         dnbuf.clear();
+        cfg->getProp(".//@viewsBasedn", dnbuf);
+        if(dnbuf.length() == 0)
+            dnbuf.append("ou=views,ou=ecl");//viewsBasedn will not exist in legacy environment files
+        LdapUtils::normalizeDn(dnbuf.str(), m_basedn.str(), m_view_basedn);
+
+        dnbuf.clear();
         cfg->getProp(".//@workunitsBasedn", dnbuf);
         if(dnbuf.length() > 0)
             LdapUtils::normalizeDn(dnbuf.str(), m_basedn.str(), m_workunitscope_basedn);
@@ -554,12 +561,19 @@ public:
         return m_group_basedn.str();
     }
 
+    virtual const char* getViewBasedn()
+    {
+        return m_view_basedn.str();
+    }
+
     virtual const char* getResourceBasedn(SecResourceType rtype)
     {
         if(rtype == RT_DEFAULT || rtype == RT_MODULE || rtype == RT_SERVICE)
             return m_resource_basedn.str();
         else if(rtype == RT_FILE_SCOPE)
             return m_filescope_basedn.str();
+        else if(rtype == RT_VIEW_SCOPE)
+            return m_view_basedn.str();
         else if(rtype == RT_WORKUNIT_SCOPE)
             return m_workunitscope_basedn.str();
         else if(rtype == RT_SUDOERS)
@@ -622,6 +636,10 @@ public:
         {
             LdapUtils::normalizeDn(rbasedn, m_basedn.str(), m_filescope_basedn);
         }
+        else if(rtype == RT_VIEW_SCOPE)
+        {
+            LdapUtils::normalizeDn(rbasedn, m_basedn.str(), m_view_basedn);
+        }
         else if(rtype == RT_WORKUNIT_SCOPE)
         {
             LdapUtils::normalizeDn(rbasedn, m_basedn.str(), m_workunitscope_basedn);
@@ -1371,6 +1389,7 @@ public:
             }
             createLdapBasedn(NULL, m_ldapconfig->getResourceBasedn(RT_DEFAULT), PT_ADMINISTRATORS_ONLY);
             createLdapBasedn(NULL, m_ldapconfig->getResourceBasedn(RT_FILE_SCOPE), PT_ADMINISTRATORS_ONLY);
+            createLdapBasedn(NULL, m_ldapconfig->getResourceBasedn(RT_VIEW_SCOPE), PT_ADMINISTRATORS_ONLY);
             createLdapBasedn(NULL, m_ldapconfig->getResourceBasedn(RT_WORKUNIT_SCOPE), PT_ADMINISTRATORS_ONLY);
             createLdapBasedn(NULL, m_ldapconfig->getResourceBasedn(RT_SUDOERS), PT_ADMINISTRATORS_ONLY);
 
@@ -1672,7 +1691,7 @@ public:
         return true;
     };
 
-    virtual bool authorize(SecResourceType rtype, ISecUser& user, IArrayOf<ISecResource>& resources)
+    virtual bool authorize(SecResourceType rtype, ISecUser& user, IArrayOf<ISecResource>& resources, const char * resName = nullptr)
     {
         bool ok = false;
         const char* basedn = m_ldapconfig->getResourceBasedn(rtype);
@@ -1781,6 +1800,53 @@ public:
 
             return ok;
         }
+        else if (rtype == RT_VIEW_SCOPE)
+        {
+            int defPerm = queryDefaultPermission(user); //default perm to be applied when no lfn or column provided
+
+            //Get view lfn/col mappings for this view
+            assertex(resources.ordinality() > 0);
+            assertex(resName && *resName != '\0');
+            StringArray viewFiles;
+            StringArray viewColumns;
+            queryViewColumns(resName, viewFiles, viewColumns);
+            unsigned numViewMembers = viewFiles.ordinality();
+            assertex(numViewMembers == viewColumns.ordinality());
+
+            StringAttr lfn;
+            StringAttr col;
+            unsigned fails = 0;
+            ForEachItemIn(idx, resources) //Iterate over all resources in list
+            {
+                ISecResource& res = resources.item(idx);
+                assertex(RT_VIEW_SCOPE == res.getResourceType());
+
+                lfn.set(res.getParameter("file"));
+                col.set(res.getParameter("column"));
+#ifdef _DEBUG
+                DBGLOG("Checking '%s' RT_VIEW_SCOPE for lfn %s, col %s", resName, lfn.str(), col.str());
+#endif
+                if (lfn.isEmpty() || col.isEmpty())
+                    res.setAccessFlags(defPerm);
+                else
+                {
+                    //Check LDAP
+                    res.setAccessFlags(SecAccess_None);
+                    ++fails;
+                    for (unsigned vIdx = 0; vIdx < numViewMembers; vIdx++)
+                    {
+                        if (0 == stricmp(lfn.str(), viewFiles.item(vIdx)) &&
+                            0 == stricmp(col.str(), viewColumns.item(vIdx)))
+                        {
+                            res.setAccessFlags(SecAccess_Full);
+                            --fails;
+                            break;
+                        }
+                    }
+                }
+            }
+            return fails == 0 ? true : false;
+        }
         else
         {
             IArrayOf<CSecurityDescriptor> sdlist;
@@ -3474,7 +3540,7 @@ public:
         return true;
     }
 
-    virtual void getAllGroups(StringArray & groups, StringArray & managedBy, StringArray & descriptions)
+    virtual void getAllGroups(StringArray & groups, StringArray & managedBy, StringArray & descriptions, const char * baseDN=nullptr)
     {
         if(m_ldapconfig->getServerType() == ACTIVE_DIRECTORY)
         {
@@ -3508,7 +3574,7 @@ public:
         LDAP* ld = ((CLdapConnection*)lconn.get())->getLd();
         char *attrs[] = {"cn", "managedBy", "description", NULL};
 
-        CPagedLDAPSearch pagedSrch(ld, (char*)m_ldapconfig->getGroupBasedn(), LDAP_SCOPE_SUBTREE, (char*)filter.str(), attrs);
+        CPagedLDAPSearch pagedSrch(ld, baseDN==nullptr ? (char*)m_ldapconfig->getGroupBasedn() : (char*)baseDN, LDAP_SCOPE_SUBTREE, (char*)filter.str(), attrs);
         for (message = pagedSrch.getFirstEntry(); message; message = pagedSrch.getNextEntry())
         {
             // Go through the search results by checking message types
@@ -3799,11 +3865,11 @@ public:
         }       
     }
 
-    virtual void changeUserGroup(const char* action, const char* username, const char* groupname)
+    virtual void changeUserGroup(const char* action, const char* username, const char* groupname, const char * groupDN=nullptr)
     {
         StringBuffer userdn, groupdn;
         getUserDN(username, userdn);
-        getGroupDN(groupname, groupdn);
+        getGroupDN(groupname, groupdn, groupDN);
         // Not needed for Active Directory
         // changeUserMemberOf(action, userdn.str(), groupdn.str());
         changeGroupMember(action, groupdn.str(), userdn.str());
@@ -3958,7 +4024,7 @@ public:
 
     }
 
-    virtual void deleteGroup(const char* groupname)
+    virtual void deleteGroup(const char* groupname, const char * groupsDN=nullptr)
     {
         if(groupname == NULL || *groupname == '\0')
             throw MakeStringException(-1, "group name can't be empty");
@@ -3975,7 +4041,7 @@ public:
         }
 
         StringBuffer dn;
-        getGroupDN(groupname, dn);
+        getGroupDN(groupname, dn, groupsDN);
         
         Owned<ILdapConnection> lconn = m_connections->getConnection();
         LDAP* ld = ((CLdapConnection*)lconn.get())->getLd();
@@ -3988,7 +4054,7 @@ public:
         }
     }
 
-    virtual void getGroupMembers(const char* groupname, StringArray & users)
+    virtual void getGroupMembers(const char* groupname, StringArray & users, const char * groupsDN=nullptr)
     {
         char        *attribute;
         LDAPMessage *message;
@@ -3997,7 +4063,7 @@ public:
             throw MakeStringException(-1, "group name can't be empty");
 
         StringBuffer grpdn;
-        getGroupDN(groupname, grpdn);
+        getGroupDN(groupname, grpdn, groupsDN);
         StringBuffer filter;
         if(m_ldapconfig->getServerType() == ACTIVE_DIRECTORY)
         {
@@ -4030,7 +4096,7 @@ public:
 
         char        *attrs[] = {(char*)memfieldname, NULL};
         StringBuffer groupbasedn;
-        getGroupBaseDN(groupname, groupbasedn);
+        getGroupBaseDN(groupname, groupbasedn, groupsDN);
 
         CPagedLDAPSearch pagedSrch(ld, (char*)groupbasedn.str(), LDAP_SCOPE_SUBTREE, (char*)filter.str(), attrs);
         for (message = pagedSrch.getFirstEntry(); message; message = pagedSrch.getNextEntry())
@@ -4512,7 +4578,7 @@ private:
         }
     }
 
-    virtual void getGroupDN(const char* groupname, StringBuffer& groupdn)
+    virtual void getGroupDN(const char* groupname, StringBuffer& groupdn, const char * groupBaseDN=nullptr)
     {
         if(groupname == NULL)
             return;
@@ -4528,11 +4594,11 @@ private:
         }
         else
         {
-            groupdn.append(m_ldapconfig->getGroupBasedn());
+            groupdn.append(groupBaseDN == nullptr ? m_ldapconfig->getGroupBasedn() : groupBaseDN);
         }
     }
 
-    virtual void getGroupBaseDN(const char* groupname, StringBuffer& groupbasedn)
+    virtual void getGroupBaseDN(const char* groupname, StringBuffer& groupbasedn, const char * groupBaseDN=nullptr)
     {
         if(groupname == NULL)
             return;
@@ -4547,7 +4613,7 @@ private:
         }
         else
         {
-            groupbasedn.append(m_ldapconfig->getGroupBasedn());
+            groupbasedn.append(groupBaseDN==nullptr ? m_ldapconfig->getGroupBasedn() : groupBaseDN);
         }
     }
 
@@ -5782,6 +5848,312 @@ private:
         else
             return -2;
     }
+
+    bool isReservedGroupName(const char * groupName)
+    {
+        if (stricmp(groupName, "Administrators") == 0 ||
+            stricmp(groupName, "Authenticated Users") == 0 ||
+            stricmp(groupName, "Directory Administrators") == 0)
+        {
+            return true;
+        }
+        return false;
+    }
+
+    //Data View related interfaces
+
+    void createView(const char * viewName, const char * viewDescription)
+    {
+        if(viewName == nullptr || *viewName == '\0')
+            throw MakeStringException(-1, "Can't add view, viewname is empty");
+
+        if (isReservedGroupName(viewName))
+        {
+            throw MakeStringException(-1, "Can't add view, '%s' is a reserved name", viewName);
+        }
+
+        addGroup(viewName, nullptr, nullptr, m_ldapconfig->getViewBasedn());//TODO Save description
+    }
+
+    void deleteView(const char * viewName)
+    {
+        if(viewName == nullptr || *viewName == '\0')
+            throw MakeStringException(-1, "Can't delete view, viewname is empty");
+
+        deleteGroup(viewName, (const char *)m_ldapconfig->getViewBasedn());
+    }
+
+    void queryAllViews(StringArray & viewNames, StringArray & viewDescriptions, StringArray & viewManagedBy)
+    {
+        StringArray names;
+        StringArray managedBy;
+        StringArray desc;
+        getAllGroups(names, managedBy, desc, (const char *)m_ldapconfig->getViewBasedn());
+
+        unsigned len = names.ordinality();
+        for(unsigned idx = 0; idx < len; idx++)
+        {
+            const char * pName = names.item(idx);
+            if (!isReservedGroupName(pName))
+            {
+                viewNames.append(pName);
+                viewDescriptions.append(desc.item(idx));
+                viewManagedBy.append(managedBy.item(idx));
+            }
+        }
+    }
+
+    bool userInView(const char * user, const char* viewName)
+    {
+        if(user == nullptr || *user == '\0')
+            throw MakeStringException(-1, "Can't check user in view, user name is empty");
+
+        if(viewName == nullptr || *viewName == '\0')
+            throw MakeStringException(-1, "Can't check user in view, viewName is empty");
+
+        try
+        {
+            StringBuffer userDN;
+            getUserDN(user, userDN);
+            VStringBuffer viewDN("CN=%s,%s",viewName, m_ldapconfig->getViewBasedn());
+            return userInGroup(userDN.str(), viewDN.str());
+        }
+        catch (IException* e)
+        {
+#ifdef _DEBUG
+            StringBuffer emsg;
+            e->errorMessage(emsg);
+            DBGLOG("userInView(%s,%s) - %s", user, viewName, emsg.str());
+#endif
+            e->Release();
+            return false;
+        }
+    }
+
+    void updateViewContents(const char * viewName, const char * content)
+    {
+        if(viewName == nullptr || *viewName == '\0')
+            throw MakeStringException(-1, "Can't updateViewContents, viewName is empty");
+
+        //Update LDAP description
+        char *desc_values[] = { (content && *content != '\0') ? (char*)content : (char*)"|", NULL };
+        LDAPMod desc_attr = {
+            LDAP_MOD_REPLACE,
+            "description",
+            desc_values
+        };
+
+        LDAPMod *attrs[2];
+        attrs[0] = &desc_attr;
+        attrs[1] = nullptr;
+
+        TIMEVAL timeOut = { LDAPTIMEOUT, 0 };
+        Owned<ILdapConnection> lconn = m_connections->getConnection();
+        LDAP* ld = ((CLdapConnection*) lconn.get())->getLd();
+
+        StringBuffer dn;
+        dn.appendf("CN=%s,%s", viewName, (char*) m_ldapconfig->getViewBasedn());
+        unsigned rc = ldap_modify_ext_s(ld, (char*)dn.str(), attrs, nullptr, nullptr);
+        if (rc != LDAP_SUCCESS )
+            throw MakeStringException(-1, "Error updating view %s - %s", viewName, ldap_err2string( rc ));
+    }
+
+
+    void addViewColumns(const char * viewName, StringArray & files, StringArray & columns)
+    {
+        if(viewName == nullptr || *viewName == '\0')
+            throw MakeStringException(-1, "Can't addViewColumns, viewName is empty");
+
+        StringArray currFiles;
+        StringArray currCols;
+        queryViewColumns(viewName, currFiles, currCols);
+        unsigned vCount = currFiles.ordinality();
+        assertex(vCount == currCols.ordinality());
+
+        unsigned len = files.ordinality();
+        assertex(len == columns.ordinality());
+        bool changed = false;
+        for(unsigned idx = 0; idx < len; idx++)
+        {
+            bool isDup = false;
+            for (unsigned vIdx = 0; vIdx < vCount; vIdx++)//look for dups
+            {
+                if (0 == stricmp(files.item(idx), currFiles.item(vIdx)) &&
+                    0 == stricmp(columns.item(idx), currCols.item(vIdx)))
+                {
+                    isDup = true;//skip duplicate entry
+                    break;
+                }
+            }
+
+            if (!isDup)
+            {
+                currFiles.append(files.item(idx));
+                currCols.append(columns.item(idx));
+                changed = true;
+            }
+        }
+
+        if (!changed)
+        {
+            throw MakeStringException(-1, "Specified columns already exist in view");
+        }
+
+        ///build description buffer containing one or more ||lfn|col
+        StringBuffer description;
+        len = currFiles.ordinality();
+        for(unsigned idx = 0; idx < len; idx++)
+        {
+            description.appendf("||%s|%s",currFiles.item(idx), currCols.item(idx));//use illegal LFN character as separators
+        }
+
+        updateViewContents(viewName, description.str());
+    }
+
+    void removeViewColumns(const char * viewName, StringArray & files, StringArray & columns)
+    {
+        if(viewName == nullptr || *viewName == '\0')
+            throw MakeStringException(-1, "Can't removeViewColumns, viewName is empty");
+
+        StringArray currFiles;
+        StringArray currCols;
+        queryViewColumns(viewName, currFiles, currCols);
+        assertex(currFiles.ordinality() == currCols.ordinality());
+
+        unsigned len = files.ordinality();
+        assertex(len == columns.ordinality());
+        bool changed = false;
+        for(unsigned idx = 0; idx < len; idx++)//for all pairs to be removed
+        {
+            unsigned len2 = currFiles.ordinality();
+            for(unsigned idx2 = 0; idx2 < len2; idx2++)
+            {
+                if (0 == stricmp(files.item(idx), currFiles.item(idx2)) &&
+                    0 == stricmp(columns.item(idx), currCols.item(idx2)))
+                {
+                    currFiles.remove(idx2);
+                    currCols.remove(idx2);
+                    changed = true;
+                    break;
+                }
+            }
+        }
+
+        if (!changed)
+        {
+            throw MakeStringException(-1, "Specified columns do not exist in view");
+        }
+
+        ///build description buffer containing one or more ||lfn|col
+        StringBuffer description;
+        len = currFiles.ordinality();
+        for(unsigned idx = 0; idx < len; idx++)
+        {
+            description.appendf("||%s|%s",currFiles.item(idx), currCols.item(idx));//use illegal LFN character as separators
+        }
+
+        updateViewContents(viewName, description.str());
+    }
+
+    void queryViewColumns(const char * viewName, StringArray & files, StringArray & columns)
+    {
+        if(viewName == nullptr || *viewName == '\0')
+            throw MakeStringException(-1, "Can't queryViewColumns, viewName is empty");
+
+       StringBuffer filter;
+
+        if(m_ldapconfig->getServerType() == ACTIVE_DIRECTORY)
+            filter.append("objectClass=group");
+        else
+            filter.append("objectClass=groupofuniquenames");
+
+        TIMEVAL timeOut = {LDAPTIMEOUT,0};
+
+        Owned<ILdapConnection> lconn = m_connections->getConnection();
+        LDAP* ld = ((CLdapConnection*)lconn.get())->getLd();
+        char *attrs[] = {"description", NULL};
+
+        StringBuffer dn;
+        dn.appendf("CN=%s,%s", viewName, (char*)m_ldapconfig->getViewBasedn() );
+        CPagedLDAPSearch pagedSrch(ld, (char*)dn.str(), LDAP_SCOPE_SUBTREE, (char*)filter.str(), attrs);
+        int idx = 0;
+        LDAPMessage *message = pagedSrch.getFirstEntry();
+        if (message)
+        {
+            CLDAPGetAttributesWrapper atts(ld, message);
+            char * attribute = atts.getFirst();
+            if (attribute)
+            {
+                CLDAPGetValuesLenWrapper vals(ld, message, attribute);
+                if(vals.hasValues() && stricmp(attribute, "description") == 0)
+                {
+                    StringBuffer sb(vals.queryCharValue(0));
+                    if (!sb.isEmpty())
+                    {
+                        StringBuffer sbFile;
+                        StringBuffer sbCol;
+                        unsigned finger = 0;
+                        unsigned len = sb.length();
+                        while (finger < len)
+                        {
+                            while (finger < len && sb.charAt(finger) == '|')
+                                finger++;//skip to lfn
+                            sbFile.clear();
+                            while (finger < len && sb.charAt(finger) != '|')
+                                sbFile.append(sb.charAt(finger++));
+                            while (finger < len && sb.charAt(finger) == '|')
+                                finger++;//skip to column name
+                            sbCol.clear();
+                            while (finger < len && sb.charAt(finger) != '|')
+                                sbCol.append(sb.charAt(finger++));
+
+                            if (!sbFile.isEmpty() && !sbCol.isEmpty())
+                            {
+                                files.append(sbFile.str());
+                                columns.append(sbCol.str());
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    void addViewMembers(const char * viewName, StringArray & viewUsers, StringArray & viewGroups)
+    {
+        if(viewName == nullptr || *viewName == '\0')
+            throw MakeStringException(-1, "Can't addViewMembers, viewName is empty");
+
+        unsigned len = viewUsers.ordinality();
+        for (unsigned idx = 0; idx < len; idx++)
+        {
+            changeUserGroup("add", viewUsers.item(idx), viewName, m_ldapconfig->getViewBasedn());
+        }
+        //TODO handle viewGroups
+
+    }
+
+    void removeViewMembers(const char * viewName, StringArray & viewUsers, StringArray & viewGroups)
+    {
+        if(viewName == nullptr || *viewName == '\0')
+            throw MakeStringException(-1, "Can't removeViewMembers, viewName is empty");
+
+        unsigned len = viewUsers.ordinality();
+        for (unsigned idx = 0; idx < len; idx++)
+        {
+            changeUserGroup("delete", viewUsers.item(idx), viewName, m_ldapconfig->getViewBasedn());
+        }
+        //TODO handle viewGroups
+    }
+
+    void queryViewMembers(const char * viewName, StringArray & viewUsers, StringArray & viewGroups)
+    {
+        if(viewName == nullptr || *viewName == '\0')
+            throw MakeStringException(-1, "Can't queryViewMembers, viewName is empty");
+
+        getGroupMembers(viewName, viewUsers, m_ldapconfig->getViewBasedn());
+        //TODO get viewGroups
+    }
 };
 
 #ifdef _WIN32

+ 19 - 5
system/security/LdapSecurity/ldapconnection.hpp

@@ -234,7 +234,7 @@ interface ILdapClient : extends IInterface
     virtual void init(IPermissionProcessor* pp) = 0;
     virtual LdapServerType getServerType() = 0;
     virtual bool authenticate(ISecUser& user) = 0;
-    virtual bool authorize(SecResourceType rtype, ISecUser&, IArrayOf<ISecResource>& resources) = 0;
+    virtual bool authorize(SecResourceType rtype, ISecUser&, IArrayOf<ISecResource>& resources, const char * resName = nullptr) = 0;
     virtual bool addResources(SecResourceType rtype, ISecUser& user, IArrayOf<ISecResource>& resources, SecPermissionType ptype, const char* basedn) = 0;
     virtual bool addUser(ISecUser& user) = 0;
     virtual void getGroups(const char *user, StringArray& groups) = 0;
@@ -248,7 +248,7 @@ interface ILdapClient : extends IInterface
     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 void getAllGroups(StringArray & groups, StringArray & managedBy, StringArray & descriptions, const char * baseDN = nullptr) = 0;
     virtual IPropertyTreeIterator* getGroupIterator() = 0;
     virtual ISecItemIterator* getGroupsSorted(GroupField* sortOrder, const unsigned pageStartFrom, const unsigned pageSize,
         unsigned *total, __int64 *cachehint) = 0;
@@ -269,11 +269,11 @@ interface ILdapClient : extends IInterface
         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;
+    virtual void changeUserGroup(const char* action, const char* username, const char* groupname, const char * groupDN=nullptr) = 0;
     virtual bool deleteUser(ISecUser* user) = 0;
     virtual void addGroup(const char* groupname, const char * groupOwner, const char * groupDesc) = 0;
-    virtual void deleteGroup(const char* groupname) = 0;
-    virtual void getGroupMembers(const char* groupname, StringArray & users) = 0;
+    virtual void deleteGroup(const char* groupname, const char * groupsDN=nullptr) = 0;
+    virtual void getGroupMembers(const char* groupname, StringArray & users, const char * groupsDN=nullptr) = 0;
     virtual void deleteResource(SecResourceType rtype, const char* name, const char* basedn) = 0;
     virtual void renameResource(SecResourceType rtype, const char* oldname, const char* newname, const char* basedn) = 0;
     virtual void copyResource(SecResourceType rtype, const char* oldname, const char* newname, const char* basedn) = 0;
@@ -287,6 +287,20 @@ interface ILdapClient : extends IInterface
     virtual bool createUserScope(ISecUser& user) = 0;
     virtual aindex_t getManagedFileScopes(IArrayOf<ISecResource>& scopes) = 0;
     virtual int queryDefaultPermission(ISecUser& user) = 0;
+
+    //Data View related interfaces
+    virtual void createView(const char * viewName, const char * viewDescription) = 0;
+    virtual void deleteView(const char * viewName) = 0;
+    virtual void queryAllViews(StringArray & viewNames, StringArray & viewDescriptions, StringArray & viewManagedBy) = 0;
+
+    virtual void addViewColumns(const char * viewName, StringArray & files, StringArray & columns) = 0;
+    virtual void removeViewColumns(const char * viewName, StringArray & files, StringArray & columns) = 0;
+    virtual void queryViewColumns(const char * viewName, StringArray & files, StringArray & columns) = 0;
+
+    virtual void addViewMembers(const char * viewName, StringArray & viewUsers, StringArray & viewGroups) = 0;
+    virtual void removeViewMembers(const char * viewName, StringArray & viewUsers, StringArray & viewGroups) = 0;
+    virtual void queryViewMembers(const char * viewName, StringArray & viewUsers, StringArray & viewGroups) = 0;
+    virtual bool userInView(const char * user, const char* viewName) = 0;
 };
 
 ILdapClient* createLdapClient(IPropertyTree* cfg);

+ 59 - 2
system/security/LdapSecurity/ldapsecurity.cpp

@@ -503,6 +503,7 @@ void CLdapSecManager::init(const char *serviceName, IPropertyTree* cfg)
 {
     for(int i = 0; i < RT_SCOPE_MAX; i++)
         m_cache_off[i] = false;
+    m_cache_off[RT_VIEW_SCOPE] = true;
     
     m_usercache_off = false;
 
@@ -662,7 +663,7 @@ bool CLdapSecManager::authorizeEx(SecResourceType rtype, ISecUser& sec_user, ISe
     }
     else
     {
-        rc = m_ldap_client->authorize(rtype, sec_user, rlist);
+        rc = m_ldap_client->authorize(rtype, sec_user, rlist, reslist->getName());
     }
     return rc;
 }
@@ -858,12 +859,17 @@ int CLdapSecManager::authorizeFileScope(ISecUser & user, const char * filescope)
     else
         return -1;
 }
-    
+
 bool CLdapSecManager::authorizeFileScope(ISecUser & user, ISecResourceList * resources)
 {
     return authorizeEx(RT_FILE_SCOPE, user, resources);
 }
 
+bool CLdapSecManager::authorizeViewScope(ISecUser & user, ISecResourceList * resources)
+{
+    return authorizeEx(RT_VIEW_SCOPE, user, resources);
+}
+
 int CLdapSecManager::authorizeWorkunitScope(ISecUser & user, const char * wuscope)
 {
     if(wuscope == 0 || wuscope[0] == '\0')
@@ -1315,6 +1321,57 @@ bool CLdapSecManager::authenticateUser(ISecUser & user, bool &superUser)
     return true;
 }
 
+//Data View related interfaces
+void CLdapSecManager::createView(const char* viewName, const char * viewDescription)
+{
+    m_ldap_client->createView(viewName, viewDescription);
+}
+
+void CLdapSecManager::deleteView(const char* viewName)
+{
+    m_ldap_client->deleteView(viewName);
+}
+
+void CLdapSecManager::queryAllViews(StringArray & viewNames, StringArray & viewDescriptions, StringArray & viewManagedBy)
+{
+    m_ldap_client->queryAllViews(viewNames, viewDescriptions, viewManagedBy);
+}
+
+void CLdapSecManager::addViewColumns(const char* viewName, StringArray & files, StringArray & columns)
+{
+    m_ldap_client->addViewColumns(viewName, files, columns);
+}
+
+void CLdapSecManager::removeViewColumns(const char* viewName, StringArray & files, StringArray & columns)
+{
+    m_ldap_client->removeViewColumns(viewName, files, columns);
+}
+
+void CLdapSecManager::queryViewColumns(const char* viewName, StringArray & files, StringArray & columns)
+{
+    m_ldap_client->queryViewColumns(viewName, files, columns);
+}
+
+void CLdapSecManager::addViewMembers(const char* viewName, StringArray & viewUsers, StringArray & viewGroups)
+{
+    m_ldap_client->addViewMembers(viewName, viewUsers, viewGroups);
+}
+
+void CLdapSecManager::removeViewMembers(const char* viewName, StringArray & viewUsers, StringArray & viewGroups)
+{
+    m_ldap_client->removeViewMembers(viewName, viewUsers, viewGroups);
+}
+
+void CLdapSecManager::queryViewMembers(const char* viewName, StringArray & viewUsers, StringArray & viewGroups)
+{
+    m_ldap_client->queryViewMembers(viewName, viewUsers, viewGroups);
+}
+
+bool CLdapSecManager::userInView(const char * user, const char* viewName)
+{
+    return m_ldap_client->userInView(user, viewName);
+}
+
 extern "C"
 {
 LDAPSECURITY_API ISecManager * newLdapSecManager(const char *serviceName, IPropertyTree &config)

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

@@ -359,6 +359,7 @@ public:
     int authorizeEx(SecResourceType rtype, ISecUser& sec_user, const char* resourcename, IEspSecureContext* secureContext = NULL);
     virtual int authorizeFileScope(ISecUser & user, const char * filescope);
     virtual bool authorizeFileScope(ISecUser & user, ISecResourceList * resources);
+    virtual bool authorizeViewScope(ISecUser & user, ISecResourceList * resources);
     virtual int authorizeWorkunitScope(ISecUser & user, const char * wuscope);
     virtual bool authorizeWorkunitScope(ISecUser & user, ISecResourceList * resources);
     virtual bool addResources(ISecUser& sec_user, ISecResourceList * resources);
@@ -449,6 +450,20 @@ public:
     virtual bool authenticateUser(ISecUser & user, bool &superUser);
     virtual secManagerType querySecMgrType() { return SMT_LDAP; }
     inline virtual const char* querySecMgrTypeName() { return "LdapSecurity"; }
+
+    //Data View related interfaces
+    virtual void createView(const char * viewName, const char * viewDescription);
+    virtual void deleteView(const char * viewName);
+    virtual void queryAllViews(StringArray & viewNames, StringArray & viewDescriptions, StringArray & viewManagedBy);
+
+    virtual void addViewColumns(const char * viewName, StringArray & files, StringArray & columns);
+    virtual void removeViewColumns(const char * viewName, StringArray & files, StringArray & columns);
+    virtual void queryViewColumns(const char * viewName, StringArray & files, StringArray & columns);
+
+    virtual void addViewMembers(const char * viewName, StringArray & viewUsers, StringArray & viewGroups);
+    virtual void removeViewMembers(const char * viewName, StringArray & viewUsers, StringArray & viewGroups);
+    virtual void queryViewMembers(const char * viewName, StringArray & viewUsers, StringArray & viewGroups);
+    virtual bool userInView(const char * user, const char* viewName);
 };
 
 #endif

+ 15 - 0
system/security/plugins/htpasswdSecurity/htpasswdSecurity.cpp

@@ -147,6 +147,21 @@ protected:
         return SecAccess_Full;//grant full access to authenticated users
     }
 
+    bool authorizeViewScope(ISecUser & user, ISecResourceList * resources)
+    {
+        int nResources = resources->count();
+        for (int ri = 0; ri < nResources; ri++)
+        {
+            ISecResource* res = resources->queryResource(ri);
+            if(res != nullptr)
+            {
+                assertex(res->getResourceType() == RT_VIEW_SCOPE);
+                res->setAccessFlags(SecAccess_Full);//grant full access to authenticated users
+            }
+        }
+        return true;//success
+    }
+
     int authorizeWorkunitScope(ISecUser & user, const char * filescope) override
     {
         return SecAccess_Full;//grant full access to authenticated users

+ 1 - 0
system/security/shared/authmap.cpp

@@ -178,6 +178,7 @@ const char* resTypeDesc(SecResourceType type)
     case RT_WORKUNIT_SCOPE: return "Workunit_Scope";
     case RT_SUDOERS: return "Sudoers";
     case RT_TRIAL: return "Trial";
+    case RT_VIEW_SCOPE: return "View";
     default: return "<unknown>";
     }
 }       

+ 6 - 0
system/security/shared/basesecurity.hpp

@@ -106,6 +106,12 @@ public:
         return false;
     }
 
+    bool authorizeViewScope(ISecUser & user, ISecResourceList * resources)
+    {
+        UNIMPLEMENTED;
+        return false;
+    }
+
     bool addResources(ISecUser & user, ISecResourceList * resources)
     {
         UNIMPLEMENTED;

+ 3 - 1
system/security/shared/seclib.hpp

@@ -69,7 +69,8 @@ enum SecResourceType
     RT_WORKUNIT_SCOPE = 4,
     RT_SUDOERS = 5,
     RT_TRIAL = 6,
-    RT_SCOPE_MAX = 7
+    RT_VIEW_SCOPE = 7,
+    RT_SCOPE_MAX = 8
 };
 
 
@@ -280,6 +281,7 @@ interface ISecManager : extends IInterface
     virtual int getAccessFlagsEx(SecResourceType rtype, ISecUser & user, const char * resourcename) = 0;
     virtual int authorizeFileScope(ISecUser & user, const char * filescope) = 0;
     virtual bool authorizeFileScope(ISecUser & user, ISecResourceList * resources) = 0;
+    virtual bool authorizeViewScope(ISecUser & user, ISecResourceList * resources) = 0;
     virtual bool addResources(ISecUser & user, ISecResourceList * resources) = 0;
     virtual bool addResourcesEx(SecResourceType rtype, ISecUser & user, ISecResourceList * resources, SecPermissionType ptype, const char * basedn) = 0;
     virtual bool addResourceEx(SecResourceType rtype, ISecUser & user, const char * resourcename, SecPermissionType ptype, const char * basedn) = 0;