123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600 |
- /*##############################################################################
- HPCC SYSTEMS software Copyright (C) 2012 HPCC Systems®.
- 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.
- ############################################################################## */
- #include "caching.hpp"
- #include "jtime.hpp"
- /**********************************************************
- * CResPermissionsCache *
- * (used by CPermissionsCache defined below) *
- **********************************************************/
- time_t getThreadCreateTime()
- {
- time_t t;
- void* tslval = getThreadLocalVal();
- if(tslval == NULL)
- return 0;
- memcpy(&t, tslval, 4);
- return t;
- }
- CResPermissionsCache::~CResPermissionsCache()
- {
- MapResAccess::const_iterator i;
- MapResAccess::const_iterator iEnd = m_resAccessMap.end();
- for (i = m_resAccessMap.begin(); i != iEnd; i++)
- {
- ISecResource* ptr = ((*i).second).second;
- if(ptr)
- {
- ptr->Release();
- }
- }
- }
- int CResPermissionsCache::lookup( IArrayOf<ISecResource>& resources, bool* pFound )
- {
- time_t tstamp;
- time(&tstamp);
- int timeout = m_pParentCache->getCacheTimeout();
- if(timeout == 0 && m_pParentCache->isTransactionalEnabled())
- timeout = 10; //Transactional timeout is set to 10 seconds for long transactions that might take over 10 seconds.
- tstamp -= timeout;
- if (m_tLastCleanup < tstamp)
- removeStaleEntries(tstamp);
- int nresources = resources.ordinality();
- int nFound = 0;
- for (int i = 0; i < nresources; i++)
- {
- ISecResource& secResource = resources.item(i);
- const char* resource = secResource.getName();
- if(resource == NULL)
- {
- *pFound++ = false;
- continue;
- }
- //DBGLOG("CACHE: Looking up %s:%s", m_user.c_str(), resource);
- MapResAccess::iterator it = m_resAccessMap.find(SecCacheKeyEntry(resource, secResource.getResourceType()));
- if (it != m_resAccessMap.end())//exists in cache
- {
- ResPermCacheEntry& resParamCacheEntry = (*it).second;
- const time_t timestamp = resParamCacheEntry.first;
- if (timestamp < tstamp)//entry was not stale during last cleanup but is stale now
- *pFound++ = false;
- else if(!m_pParentCache->isCacheEnabled() && m_pParentCache->isTransactionalEnabled())//m_pParentCache->getOriginalTimeout() == 0)
- {
- time_t tctime = getThreadCreateTime();
- if(tctime <= 0 || timestamp < tctime)
- {
- *pFound++ = false;
- }
- else
- {
- secResource.copy(resParamCacheEntry.second);
- *pFound++ = true;
- nFound++;
- }
- }
- else
- {
- secResource.copy(resParamCacheEntry.second);
- //DBGLOG("CACHE: Found %s:%s=>%d", m_user.c_str(), resource, resParamCacheEntry.second);
- *pFound++ = true;
- nFound++;
- }
- }
- else
- *pFound++ = false;
- }
- return nFound;
- }
- void CResPermissionsCache::add( IArrayOf<ISecResource>& resources )
- {
- time_t tstamp;
- time(&tstamp);
- int nresources = resources.ordinality();
- for (int i = 0; i < nresources; i++)
- {
- ISecResource* secResource = &resources.item(i);
- if(!secResource)
- continue;
- const char* resource = secResource->getName();
- SecResourceType resourcetype = secResource->getResourceType();
- if(resource == NULL)
- continue;
- int permissions = secResource->getAccessFlags();
- if(permissions == -1)
- continue;
- MapResAccess::iterator it = m_resAccessMap.find(SecCacheKeyEntry(resource, resourcetype));
- if (it != m_resAccessMap.end())//already exists so overwrite it but first remove existing timestamp info
- {
- ResPermCacheEntry& resParamCacheEntry = (*it).second;
- time_t oldtstamp = resParamCacheEntry.first;
- //there may be multiple resources associated with the same timestamp
- //in the multimap so find this entry
- //
- MapTimeStamp::iterator itL = m_timestampMap.lower_bound( oldtstamp );
- MapTimeStamp::iterator itU = m_timestampMap.upper_bound( oldtstamp );
- MapTimeStamp::iterator its;
- for ( its = itL; its != itU; its++)
- {
- SecCacheKeyEntry& cachekey = (*its).second;
- if (cachekey.first == resource && cachekey.second == resourcetype)
- {
- m_timestampMap.erase(its);
- break;
- }
- }
- m_resAccessMap.erase(SecCacheKeyEntry(resource, resourcetype));
- }
- //DBGLOG("CACHE: Adding %s:%s(%d)", m_user.c_str(), resource, permissions);
- m_resAccessMap.insert( pair<SecCacheKeyEntry, ResPermCacheEntry>(SecCacheKeyEntry(resource, resourcetype), ResPermCacheEntry(tstamp, secResource->clone())));
- m_timestampMap.insert( pair<time_t, SecCacheKeyEntry>(tstamp, SecCacheKeyEntry(resource, resourcetype)));
- }
- }
- void CResPermissionsCache::removeStaleEntries(time_t tstamp)
- {
- MapTimeStamp::iterator i;
- MapTimeStamp::iterator itL = m_timestampMap.lower_bound(tstamp);
- MapTimeStamp::iterator iBegin = m_timestampMap.begin();
- for (i = iBegin; i != itL; i++)
- {
- SecCacheKeyEntry& cachekey = (*i).second;
- MapResAccess::iterator it = m_resAccessMap.find(cachekey);
- if (it != m_resAccessMap.end())//exists in cache
- {
- ResPermCacheEntry& entry = (*it).second;
- if(entry.second)
- entry.second->Release();
- }
- m_resAccessMap.erase(cachekey);
- }
- m_timestampMap.erase(iBegin, itL);
- m_tLastCleanup = tstamp;
- }
- void CResPermissionsCache::remove(SecResourceType rtype, const char* resourcename)
- {
- SecCacheKeyEntry key(resourcename, rtype);
- MapResAccess::iterator it = m_resAccessMap.find(key);
- if (it != m_resAccessMap.end())//exists in cache
- {
- ResPermCacheEntry& entry = (*it).second;
- if(entry.second)
- entry.second->Release();
- }
- m_resAccessMap.erase(key);
- }
- /**********************************************************
- * CPermissionsCache *
- **********************************************************/
- CPermissionsCache::~CPermissionsCache()
- {
- flush();
- }
- int CPermissionsCache::lookup( ISecUser& sec_user, IArrayOf<ISecResource>& resources,
- bool* pFound)
- {
- synchronized block(m_cachemonitor);
- const char* userId = sec_user.getName();
- int nFound;
- MapResPermissionsCache::const_iterator i = m_resPermissionsMap.find( userId );
- if (i != m_resPermissionsMap.end())
- {
- CResPermissionsCache* pResPermissionsCache = (*i).second;
- nFound = pResPermissionsCache->lookup( resources, pFound );
- }
- else
- {
- nFound = 0;
- memset(pFound, 0, sizeof(bool)*resources.ordinality());
- //DBGLOG("CACHE: Looking up %s:*", userId);
- }
- return nFound;
- }
- void CPermissionsCache::add( ISecUser& sec_user, IArrayOf<ISecResource>& resources )
- {
- synchronized block(m_cachemonitor);
- const char* user = sec_user.getName();
- MapResPermissionsCache::const_iterator i = m_resPermissionsMap.find( user );
- CResPermissionsCache* pResPermissionsCache;
- if (i == m_resPermissionsMap.end())
- {
- //DBGLOG("CACHE: Adding cache for %s", user);
- pResPermissionsCache = new CResPermissionsCache(this, user);
- m_resPermissionsMap.insert(pair<string, CResPermissionsCache*>(user, pResPermissionsCache));
- }
- else
- pResPermissionsCache = (*i).second;
- pResPermissionsCache->add( resources );
- }
- void CPermissionsCache::removePermissions( ISecUser& sec_user)
- {
- synchronized block(m_cachemonitor);
- const char* user = sec_user.getName();
- if(user != NULL && *user != '\0')
- {
- m_resPermissionsMap.erase(user);
- }
- }
- void CPermissionsCache::remove(SecResourceType rtype, const char* resourcename)
- {
- synchronized block(m_cachemonitor);
- MapResPermissionsCache::const_iterator i;
- MapResPermissionsCache::const_iterator iEnd = m_resPermissionsMap.end();
- for (i = m_resPermissionsMap.begin(); i != iEnd; i++)
- {
- i->second->remove(rtype, resourcename);
- }
- }
- bool CPermissionsCache::lookup(ISecUser& sec_user)
- {
- if(!isCacheEnabled())
- return false;
- const char* username = sec_user.getName();
- if(!username || !*username)
- return false;
- synchronized block(m_userCacheMonitor);
- string key(username);
- MapUserCache::iterator it = m_userCache.find(key);
- if (it == m_userCache.end())
- return false;
- CachedUser* user = (CachedUser*)(it->second);
- time_t now;
- time(&now);
- if(user->getTimestamp() < (now - m_cacheTimeout))
- {
- m_userCache.erase(username);
- delete user;
- return false;
- }
- const char* cachedpw = user->queryUser()->credentials().getPassword();
- StringBuffer pw(sec_user.credentials().getPassword());
-
- if(cachedpw && pw.length() > 0)
- {
- StringBuffer md5pbuf;
- md5_string(pw, md5pbuf);
- if(strcmp(cachedpw, md5pbuf.str()) == 0)
- {
- // Copy cached user to the sec_user structure, but still keep the original clear text password.
- user->queryUser()->copyTo(sec_user);
- sec_user.credentials().setPassword(pw.str());
- return true;
- }
- else
- {
- m_userCache.erase(username);
- delete user;
- return false;
- }
- }
- return false;
- }
- ISecUser* CPermissionsCache::getCachedUser( ISecUser& sec_user)
- {
- if(!isCacheEnabled())
- return NULL;
- const char* username = sec_user.getName();
- if(!username || !*username)
- return NULL;
- synchronized block(m_userCacheMonitor);
- string key(username);
- MapUserCache::iterator it = m_userCache.find(key);
- if (it == m_userCache.end())
- return NULL;
- CachedUser* user = (CachedUser*)(it->second);
- return LINK(user->queryUser());
- }
- void CPermissionsCache::add(ISecUser& sec_user)
- {
- if(!isCacheEnabled())
- return;
-
- const char* username = sec_user.getName();
- if(!username || !*username)
- return;
-
- synchronized block(m_userCacheMonitor);
- string key(username);
- MapUserCache::iterator it = m_userCache.find(key);
- CachedUser* user = NULL;
- if (it != m_userCache.end())
- {
- user = (CachedUser*)(it->second);
- m_userCache.erase(username);
- delete user;
- }
- m_userCache[username] = new CachedUser(sec_user.clone());
- }
- void CPermissionsCache::removeFromUserCache(ISecUser& sec_user)
- {
- const char* username = sec_user.getName();
- if(username && *username)
- {
- synchronized block(m_userCacheMonitor);
- string key(username);
- MapUserCache::iterator it = m_userCache.find(key);
- if (it != m_userCache.end())
- {
- CachedUser* user = (CachedUser*)(it->second);
- m_userCache.erase(username);
- delete user;
- }
- }
- }
- bool CPermissionsCache::addManagedFileScopes(IArrayOf<ISecResource>& scopes)
- {
- synchronized block(m_managedFileScopesCacheMonitor);
- ForEachItemIn(x, scopes)
- {
- ISecResource* scope = &scopes.item(x);
- if(!scope)
- continue;
- const char* cachekey = scope->getName();
- if(cachekey == NULL)
- continue;
- map<string, ISecResource*>::iterator it = m_managedFileScopesMap.find(cachekey);
- if (it != m_managedFileScopesMap.end())
- {
- ISecResource *res = (*it).second;
- res->Release();
- m_managedFileScopesMap.erase(it);
- }
- #ifdef _DEBUG
- DBGLOG("Caching Managed File Scope %s",cachekey);
- #endif
- m_managedFileScopesMap.insert( pair<string, ISecResource*>(cachekey, LINK(scope)));
- }
- return true;
- }
- inline void CPermissionsCache::removeManagedFileScopes(IArrayOf<ISecResource>& scopes)
- {
- synchronized block(m_managedFileScopesCacheMonitor);
- ForEachItemIn(x, scopes)
- {
- ISecResource* scope = &scopes.item(x);
- if(!scope)
- continue;
- const char* cachekey = scope->getName();
- if(cachekey == NULL)
- continue;
- map<string, ISecResource*>::iterator it = m_managedFileScopesMap.find(cachekey);
- if (it != m_managedFileScopesMap.end())
- {
- ISecResource *res = (*it).second;
- res->Release();
- m_managedFileScopesMap.erase(it);
- }
- }
- }
- inline void CPermissionsCache::removeAllManagedFileScopes()
- {
- synchronized block(m_managedFileScopesCacheMonitor);
- map<string, ISecResource*>::const_iterator cit;
- map<string, ISecResource*>::const_iterator iEnd = m_managedFileScopesMap.end();
- for (cit = m_managedFileScopesMap.begin(); cit != iEnd; cit++)
- {
- ISecResource *res = (*cit).second;
- res->Release();
- }
- m_managedFileScopesMap.clear();
- }
- /*
- if perms set on 'scopeA::scopeB' only and lookup of 'scopeA::scopeB::scopeC::scopeD'
- need to lookup:
- 'scopeA'
- no match=>continue
- match=>continue if read permissions (if no read, implies can't "see" child scopes)
- 'scopeA::scopeB'
- no match=>continue
- match=>continue if read permissions (if no read, implies can't "see" child scopes)
- etc. Until full scope path checked, or no read permissions hit on ancestor scope.
- */
- bool CPermissionsCache::queryPermsManagedFileScope(ISecUser& sec_user, const char * fullScope, StringBuffer& managedScope, int * accessFlags)
- {
- if (!fullScope || !*fullScope)
- {
- *accessFlags = queryDefaultPermission(sec_user);
- return true;
- }
- time_t now;
- time(&now);
- if (m_secMgr && (0 == m_lastManagedFileScopesRefresh || m_lastManagedFileScopesRefresh < (now - m_cacheTimeout)))
- {
- removeAllManagedFileScopes();
- IArrayOf<ISecResource> scopes;
- aindex_t count = m_secMgr->getManagedFileScopes(scopes);
- if (count)
- addManagedFileScopes(scopes);
- m_defaultPermission = SecAccess_Unknown;//trigger refresh
- m_lastManagedFileScopesRefresh = now;
- }
- if (m_managedFileScopesMap.empty())
- {
- *accessFlags = queryDefaultPermission(sec_user);
- return true;
- }
- StringArray scopes;
- {
- StringBuffer scope;
- const char * p = fullScope;
- while (*p)
- {
- if (*p == ':')
- {
- if (*(p+1) != ':')
- return false;//Malformed scope string, let LDAP figure it out
- scopes.append(scope.str());
- scope.append(*(p++));
- }
- scope.append(*(p++));
- }
- scopes.append(scope.str());
- }
- synchronized block(m_managedFileScopesCacheMonitor);
- ISecResource *matchedRes = NULL;
- ISecResource *res = NULL;
- bool isManaged = false;
- for(unsigned i = 0; i < scopes.length(); i++)
- {
- const char* scope = scopes.item(i);
- map<string, ISecResource*>::const_iterator it = m_managedFileScopesMap.find(scope);
- if (it != m_managedFileScopesMap.end())
- {
- isManaged = true;
- res = (*it).second;
- res->setResourceType(RT_FILE_SCOPE);
- LINK(res);
- IArrayOf<ISecResource> secResArr;
- secResArr.append(*res);
- bool found;
- int nFound = lookup(sec_user, secResArr, &found);
- if (nFound && found)
- {
- if (0 == (res->getAccessFlags() & SecAccess_Read))
- {
- *accessFlags = res->getAccessFlags();
- managedScope.append(const_cast<char *>(res->getName()));
- #ifdef _DEBUG
- DBGLOG("FileScope %s for %s(%s) access denied %d",fullScope, sec_user.getName(), res->getName(), *accessFlags);
- #endif
- return true;
- }
- else
- matchedRes = res;//allowed at this scope, but must also look at child scopes
- }
- }
- }
- bool rc;
- if (isManaged)
- {
- if (matchedRes)
- {
- *accessFlags = matchedRes->getAccessFlags();
- managedScope.append(const_cast<char *>(matchedRes->getName()));
- #ifdef _DEBUG
- DBGLOG("FileScope %s for %s(%s) access granted %d", fullScope, sec_user.getName(), matchedRes->getName(), *accessFlags);
- #endif
- rc = true;
- }
- else
- {
- managedScope.append(const_cast<char *>(res->getName()));
- #ifdef _DEBUG
- DBGLOG("FileScope %s for %s(%s) managed but not cached", fullScope, sec_user.getName(), res->getName());
- #endif
- rc = false;//need to go to LDAP to check
- }
- }
- else
- {
- *accessFlags = queryDefaultPermission(sec_user);
- #ifdef _DEBUG
- DBGLOG("FileScope %s for %s not managed, using default %d", fullScope, sec_user.getName(),*accessFlags);
- #endif
- rc = true;
- }
- return rc;
- }
- int CPermissionsCache::queryDefaultPermission(ISecUser& user)
- {
- if (m_defaultPermission == SecAccess_Unknown)
- {
- if (m_secMgr)
- m_defaultPermission = m_secMgr->queryDefaultPermission(user);
- else
- m_defaultPermission = SecAccess_None;
- }
- return m_defaultPermission;
- }
- void CPermissionsCache::flush()
- {
- {
- synchronized block(m_cachemonitor);
- MapResPermissionsCache::const_iterator i;
- MapResPermissionsCache::const_iterator iEnd = m_resPermissionsMap.end();
- for (i = m_resPermissionsMap.begin(); i != iEnd; i++)
- delete (*i).second;
- m_resPermissionsMap.clear();
- }
- {
- synchronized block(m_userCacheMonitor);
- MapUserCache::const_iterator ui;
- MapUserCache::const_iterator uiEnd = m_userCache.end();
- for (ui = m_userCache.begin(); ui != uiEnd; ui++)
- delete (*ui).second;
- m_userCache.clear();
- }
- m_lastManagedFileScopesRefresh = 0;
- m_defaultPermission = SecAccess_Unknown;//trigger refresh
- }
|