caching.cpp 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600
  1. /*##############################################################################
  2. HPCC SYSTEMS software Copyright (C) 2012 HPCC Systems®.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. ############################################################################## */
  13. #include "caching.hpp"
  14. #include "jtime.hpp"
  15. /**********************************************************
  16. * CResPermissionsCache *
  17. * (used by CPermissionsCache defined below) *
  18. **********************************************************/
  19. time_t getThreadCreateTime()
  20. {
  21. time_t t;
  22. void* tslval = getThreadLocalVal();
  23. if(tslval == NULL)
  24. return 0;
  25. memcpy(&t, tslval, 4);
  26. return t;
  27. }
  28. CResPermissionsCache::~CResPermissionsCache()
  29. {
  30. MapResAccess::const_iterator i;
  31. MapResAccess::const_iterator iEnd = m_resAccessMap.end();
  32. for (i = m_resAccessMap.begin(); i != iEnd; i++)
  33. {
  34. ISecResource* ptr = ((*i).second).second;
  35. if(ptr)
  36. {
  37. ptr->Release();
  38. }
  39. }
  40. }
  41. int CResPermissionsCache::lookup( IArrayOf<ISecResource>& resources, bool* pFound )
  42. {
  43. time_t tstamp;
  44. time(&tstamp);
  45. int timeout = m_pParentCache->getCacheTimeout();
  46. if(timeout == 0 && m_pParentCache->isTransactionalEnabled())
  47. timeout = 10; //Transactional timeout is set to 10 seconds for long transactions that might take over 10 seconds.
  48. tstamp -= timeout;
  49. if (m_tLastCleanup < tstamp)
  50. removeStaleEntries(tstamp);
  51. int nresources = resources.ordinality();
  52. int nFound = 0;
  53. for (int i = 0; i < nresources; i++)
  54. {
  55. ISecResource& secResource = resources.item(i);
  56. const char* resource = secResource.getName();
  57. if(resource == NULL)
  58. {
  59. *pFound++ = false;
  60. continue;
  61. }
  62. //DBGLOG("CACHE: Looking up %s:%s", m_user.c_str(), resource);
  63. MapResAccess::iterator it = m_resAccessMap.find(SecCacheKeyEntry(resource, secResource.getResourceType()));
  64. if (it != m_resAccessMap.end())//exists in cache
  65. {
  66. ResPermCacheEntry& resParamCacheEntry = (*it).second;
  67. const time_t timestamp = resParamCacheEntry.first;
  68. if (timestamp < tstamp)//entry was not stale during last cleanup but is stale now
  69. *pFound++ = false;
  70. else if(!m_pParentCache->isCacheEnabled() && m_pParentCache->isTransactionalEnabled())//m_pParentCache->getOriginalTimeout() == 0)
  71. {
  72. time_t tctime = getThreadCreateTime();
  73. if(tctime <= 0 || timestamp < tctime)
  74. {
  75. *pFound++ = false;
  76. }
  77. else
  78. {
  79. secResource.copy(resParamCacheEntry.second);
  80. *pFound++ = true;
  81. nFound++;
  82. }
  83. }
  84. else
  85. {
  86. secResource.copy(resParamCacheEntry.second);
  87. //DBGLOG("CACHE: Found %s:%s=>%d", m_user.c_str(), resource, resParamCacheEntry.second);
  88. *pFound++ = true;
  89. nFound++;
  90. }
  91. }
  92. else
  93. *pFound++ = false;
  94. }
  95. return nFound;
  96. }
  97. void CResPermissionsCache::add( IArrayOf<ISecResource>& resources )
  98. {
  99. time_t tstamp;
  100. time(&tstamp);
  101. int nresources = resources.ordinality();
  102. for (int i = 0; i < nresources; i++)
  103. {
  104. ISecResource* secResource = &resources.item(i);
  105. if(!secResource)
  106. continue;
  107. const char* resource = secResource->getName();
  108. SecResourceType resourcetype = secResource->getResourceType();
  109. if(resource == NULL)
  110. continue;
  111. int permissions = secResource->getAccessFlags();
  112. if(permissions == -1)
  113. continue;
  114. MapResAccess::iterator it = m_resAccessMap.find(SecCacheKeyEntry(resource, resourcetype));
  115. if (it != m_resAccessMap.end())//already exists so overwrite it but first remove existing timestamp info
  116. {
  117. ResPermCacheEntry& resParamCacheEntry = (*it).second;
  118. time_t oldtstamp = resParamCacheEntry.first;
  119. //there may be multiple resources associated with the same timestamp
  120. //in the multimap so find this entry
  121. //
  122. MapTimeStamp::iterator itL = m_timestampMap.lower_bound( oldtstamp );
  123. MapTimeStamp::iterator itU = m_timestampMap.upper_bound( oldtstamp );
  124. MapTimeStamp::iterator its;
  125. for ( its = itL; its != itU; its++)
  126. {
  127. SecCacheKeyEntry& cachekey = (*its).second;
  128. if (cachekey.first == resource && cachekey.second == resourcetype)
  129. {
  130. m_timestampMap.erase(its);
  131. break;
  132. }
  133. }
  134. m_resAccessMap.erase(SecCacheKeyEntry(resource, resourcetype));
  135. }
  136. //DBGLOG("CACHE: Adding %s:%s(%d)", m_user.c_str(), resource, permissions);
  137. m_resAccessMap.insert( pair<SecCacheKeyEntry, ResPermCacheEntry>(SecCacheKeyEntry(resource, resourcetype), ResPermCacheEntry(tstamp, secResource->clone())));
  138. m_timestampMap.insert( pair<time_t, SecCacheKeyEntry>(tstamp, SecCacheKeyEntry(resource, resourcetype)));
  139. }
  140. }
  141. void CResPermissionsCache::removeStaleEntries(time_t tstamp)
  142. {
  143. MapTimeStamp::iterator i;
  144. MapTimeStamp::iterator itL = m_timestampMap.lower_bound(tstamp);
  145. MapTimeStamp::iterator iBegin = m_timestampMap.begin();
  146. for (i = iBegin; i != itL; i++)
  147. {
  148. SecCacheKeyEntry& cachekey = (*i).second;
  149. MapResAccess::iterator it = m_resAccessMap.find(cachekey);
  150. if (it != m_resAccessMap.end())//exists in cache
  151. {
  152. ResPermCacheEntry& entry = (*it).second;
  153. if(entry.second)
  154. entry.second->Release();
  155. }
  156. m_resAccessMap.erase(cachekey);
  157. }
  158. m_timestampMap.erase(iBegin, itL);
  159. m_tLastCleanup = tstamp;
  160. }
  161. void CResPermissionsCache::remove(SecResourceType rtype, const char* resourcename)
  162. {
  163. SecCacheKeyEntry key(resourcename, rtype);
  164. MapResAccess::iterator it = m_resAccessMap.find(key);
  165. if (it != m_resAccessMap.end())//exists in cache
  166. {
  167. ResPermCacheEntry& entry = (*it).second;
  168. if(entry.second)
  169. entry.second->Release();
  170. }
  171. m_resAccessMap.erase(key);
  172. }
  173. /**********************************************************
  174. * CPermissionsCache *
  175. **********************************************************/
  176. CPermissionsCache::~CPermissionsCache()
  177. {
  178. flush();
  179. }
  180. int CPermissionsCache::lookup( ISecUser& sec_user, IArrayOf<ISecResource>& resources,
  181. bool* pFound)
  182. {
  183. synchronized block(m_cachemonitor);
  184. const char* userId = sec_user.getName();
  185. int nFound;
  186. MapResPermissionsCache::const_iterator i = m_resPermissionsMap.find( userId );
  187. if (i != m_resPermissionsMap.end())
  188. {
  189. CResPermissionsCache* pResPermissionsCache = (*i).second;
  190. nFound = pResPermissionsCache->lookup( resources, pFound );
  191. }
  192. else
  193. {
  194. nFound = 0;
  195. memset(pFound, 0, sizeof(bool)*resources.ordinality());
  196. //DBGLOG("CACHE: Looking up %s:*", userId);
  197. }
  198. return nFound;
  199. }
  200. void CPermissionsCache::add( ISecUser& sec_user, IArrayOf<ISecResource>& resources )
  201. {
  202. synchronized block(m_cachemonitor);
  203. const char* user = sec_user.getName();
  204. MapResPermissionsCache::const_iterator i = m_resPermissionsMap.find( user );
  205. CResPermissionsCache* pResPermissionsCache;
  206. if (i == m_resPermissionsMap.end())
  207. {
  208. //DBGLOG("CACHE: Adding cache for %s", user);
  209. pResPermissionsCache = new CResPermissionsCache(this, user);
  210. m_resPermissionsMap.insert(pair<string, CResPermissionsCache*>(user, pResPermissionsCache));
  211. }
  212. else
  213. pResPermissionsCache = (*i).second;
  214. pResPermissionsCache->add( resources );
  215. }
  216. void CPermissionsCache::removePermissions( ISecUser& sec_user)
  217. {
  218. synchronized block(m_cachemonitor);
  219. const char* user = sec_user.getName();
  220. if(user != NULL && *user != '\0')
  221. {
  222. m_resPermissionsMap.erase(user);
  223. }
  224. }
  225. void CPermissionsCache::remove(SecResourceType rtype, const char* resourcename)
  226. {
  227. synchronized block(m_cachemonitor);
  228. MapResPermissionsCache::const_iterator i;
  229. MapResPermissionsCache::const_iterator iEnd = m_resPermissionsMap.end();
  230. for (i = m_resPermissionsMap.begin(); i != iEnd; i++)
  231. {
  232. i->second->remove(rtype, resourcename);
  233. }
  234. }
  235. bool CPermissionsCache::lookup(ISecUser& sec_user)
  236. {
  237. if(!isCacheEnabled())
  238. return false;
  239. const char* username = sec_user.getName();
  240. if(!username || !*username)
  241. return false;
  242. synchronized block(m_userCacheMonitor);
  243. string key(username);
  244. MapUserCache::iterator it = m_userCache.find(key);
  245. if (it == m_userCache.end())
  246. return false;
  247. CachedUser* user = (CachedUser*)(it->second);
  248. time_t now;
  249. time(&now);
  250. if(user->getTimestamp() < (now - m_cacheTimeout))
  251. {
  252. m_userCache.erase(username);
  253. delete user;
  254. return false;
  255. }
  256. const char* cachedpw = user->queryUser()->credentials().getPassword();
  257. StringBuffer pw(sec_user.credentials().getPassword());
  258. if(cachedpw && pw.length() > 0)
  259. {
  260. StringBuffer md5pbuf;
  261. md5_string(pw, md5pbuf);
  262. if(strcmp(cachedpw, md5pbuf.str()) == 0)
  263. {
  264. // Copy cached user to the sec_user structure, but still keep the original clear text password.
  265. user->queryUser()->copyTo(sec_user);
  266. sec_user.credentials().setPassword(pw.str());
  267. return true;
  268. }
  269. else
  270. {
  271. m_userCache.erase(username);
  272. delete user;
  273. return false;
  274. }
  275. }
  276. return false;
  277. }
  278. ISecUser* CPermissionsCache::getCachedUser( ISecUser& sec_user)
  279. {
  280. if(!isCacheEnabled())
  281. return NULL;
  282. const char* username = sec_user.getName();
  283. if(!username || !*username)
  284. return NULL;
  285. synchronized block(m_userCacheMonitor);
  286. string key(username);
  287. MapUserCache::iterator it = m_userCache.find(key);
  288. if (it == m_userCache.end())
  289. return NULL;
  290. CachedUser* user = (CachedUser*)(it->second);
  291. return LINK(user->queryUser());
  292. }
  293. void CPermissionsCache::add(ISecUser& sec_user)
  294. {
  295. if(!isCacheEnabled())
  296. return;
  297. const char* username = sec_user.getName();
  298. if(!username || !*username)
  299. return;
  300. synchronized block(m_userCacheMonitor);
  301. string key(username);
  302. MapUserCache::iterator it = m_userCache.find(key);
  303. CachedUser* user = NULL;
  304. if (it != m_userCache.end())
  305. {
  306. user = (CachedUser*)(it->second);
  307. m_userCache.erase(username);
  308. delete user;
  309. }
  310. m_userCache[username] = new CachedUser(sec_user.clone());
  311. }
  312. void CPermissionsCache::removeFromUserCache(ISecUser& sec_user)
  313. {
  314. const char* username = sec_user.getName();
  315. if(username && *username)
  316. {
  317. synchronized block(m_userCacheMonitor);
  318. string key(username);
  319. MapUserCache::iterator it = m_userCache.find(key);
  320. if (it != m_userCache.end())
  321. {
  322. CachedUser* user = (CachedUser*)(it->second);
  323. m_userCache.erase(username);
  324. delete user;
  325. }
  326. }
  327. }
  328. bool CPermissionsCache::addManagedFileScopes(IArrayOf<ISecResource>& scopes)
  329. {
  330. synchronized block(m_managedFileScopesCacheMonitor);
  331. ForEachItemIn(x, scopes)
  332. {
  333. ISecResource* scope = &scopes.item(x);
  334. if(!scope)
  335. continue;
  336. const char* cachekey = scope->getName();
  337. if(cachekey == NULL)
  338. continue;
  339. map<string, ISecResource*>::iterator it = m_managedFileScopesMap.find(cachekey);
  340. if (it != m_managedFileScopesMap.end())
  341. {
  342. ISecResource *res = (*it).second;
  343. res->Release();
  344. m_managedFileScopesMap.erase(it);
  345. }
  346. #ifdef _DEBUG
  347. DBGLOG("Caching Managed File Scope %s",cachekey);
  348. #endif
  349. m_managedFileScopesMap.insert( pair<string, ISecResource*>(cachekey, LINK(scope)));
  350. }
  351. return true;
  352. }
  353. inline void CPermissionsCache::removeManagedFileScopes(IArrayOf<ISecResource>& scopes)
  354. {
  355. synchronized block(m_managedFileScopesCacheMonitor);
  356. ForEachItemIn(x, scopes)
  357. {
  358. ISecResource* scope = &scopes.item(x);
  359. if(!scope)
  360. continue;
  361. const char* cachekey = scope->getName();
  362. if(cachekey == NULL)
  363. continue;
  364. map<string, ISecResource*>::iterator it = m_managedFileScopesMap.find(cachekey);
  365. if (it != m_managedFileScopesMap.end())
  366. {
  367. ISecResource *res = (*it).second;
  368. res->Release();
  369. m_managedFileScopesMap.erase(it);
  370. }
  371. }
  372. }
  373. inline void CPermissionsCache::removeAllManagedFileScopes()
  374. {
  375. synchronized block(m_managedFileScopesCacheMonitor);
  376. map<string, ISecResource*>::const_iterator cit;
  377. map<string, ISecResource*>::const_iterator iEnd = m_managedFileScopesMap.end();
  378. for (cit = m_managedFileScopesMap.begin(); cit != iEnd; cit++)
  379. {
  380. ISecResource *res = (*cit).second;
  381. res->Release();
  382. }
  383. m_managedFileScopesMap.clear();
  384. }
  385. /*
  386. if perms set on 'scopeA::scopeB' only and lookup of 'scopeA::scopeB::scopeC::scopeD'
  387. need to lookup:
  388. 'scopeA'
  389. no match=>continue
  390. match=>continue if read permissions (if no read, implies can't "see" child scopes)
  391. 'scopeA::scopeB'
  392. no match=>continue
  393. match=>continue if read permissions (if no read, implies can't "see" child scopes)
  394. etc. Until full scope path checked, or no read permissions hit on ancestor scope.
  395. */
  396. bool CPermissionsCache::queryPermsManagedFileScope(ISecUser& sec_user, const char * fullScope, StringBuffer& managedScope, int * accessFlags)
  397. {
  398. if (!fullScope || !*fullScope)
  399. {
  400. *accessFlags = queryDefaultPermission(sec_user);
  401. return true;
  402. }
  403. time_t now;
  404. time(&now);
  405. if (m_secMgr && (0 == m_lastManagedFileScopesRefresh || m_lastManagedFileScopesRefresh < (now - m_cacheTimeout)))
  406. {
  407. removeAllManagedFileScopes();
  408. IArrayOf<ISecResource> scopes;
  409. aindex_t count = m_secMgr->getManagedFileScopes(scopes);
  410. if (count)
  411. addManagedFileScopes(scopes);
  412. m_defaultPermission = SecAccess_Unknown;//trigger refresh
  413. m_lastManagedFileScopesRefresh = now;
  414. }
  415. if (m_managedFileScopesMap.empty())
  416. {
  417. *accessFlags = queryDefaultPermission(sec_user);
  418. return true;
  419. }
  420. StringArray scopes;
  421. {
  422. StringBuffer scope;
  423. const char * p = fullScope;
  424. while (*p)
  425. {
  426. if (*p == ':')
  427. {
  428. if (*(p+1) != ':')
  429. return false;//Malformed scope string, let LDAP figure it out
  430. scopes.append(scope.str());
  431. scope.append(*(p++));
  432. }
  433. scope.append(*(p++));
  434. }
  435. scopes.append(scope.str());
  436. }
  437. synchronized block(m_managedFileScopesCacheMonitor);
  438. ISecResource *matchedRes = NULL;
  439. ISecResource *res = NULL;
  440. bool isManaged = false;
  441. for(unsigned i = 0; i < scopes.length(); i++)
  442. {
  443. const char* scope = scopes.item(i);
  444. map<string, ISecResource*>::const_iterator it = m_managedFileScopesMap.find(scope);
  445. if (it != m_managedFileScopesMap.end())
  446. {
  447. isManaged = true;
  448. res = (*it).second;
  449. res->setResourceType(RT_FILE_SCOPE);
  450. LINK(res);
  451. IArrayOf<ISecResource> secResArr;
  452. secResArr.append(*res);
  453. bool found;
  454. int nFound = lookup(sec_user, secResArr, &found);
  455. if (nFound && found)
  456. {
  457. if (0 == (res->getAccessFlags() & SecAccess_Read))
  458. {
  459. *accessFlags = res->getAccessFlags();
  460. managedScope.append(const_cast<char *>(res->getName()));
  461. #ifdef _DEBUG
  462. DBGLOG("FileScope %s for %s(%s) access denied %d",fullScope, sec_user.getName(), res->getName(), *accessFlags);
  463. #endif
  464. return true;
  465. }
  466. else
  467. matchedRes = res;//allowed at this scope, but must also look at child scopes
  468. }
  469. }
  470. }
  471. bool rc;
  472. if (isManaged)
  473. {
  474. if (matchedRes)
  475. {
  476. *accessFlags = matchedRes->getAccessFlags();
  477. managedScope.append(const_cast<char *>(matchedRes->getName()));
  478. #ifdef _DEBUG
  479. DBGLOG("FileScope %s for %s(%s) access granted %d", fullScope, sec_user.getName(), matchedRes->getName(), *accessFlags);
  480. #endif
  481. rc = true;
  482. }
  483. else
  484. {
  485. managedScope.append(const_cast<char *>(res->getName()));
  486. #ifdef _DEBUG
  487. DBGLOG("FileScope %s for %s(%s) managed but not cached", fullScope, sec_user.getName(), res->getName());
  488. #endif
  489. rc = false;//need to go to LDAP to check
  490. }
  491. }
  492. else
  493. {
  494. *accessFlags = queryDefaultPermission(sec_user);
  495. #ifdef _DEBUG
  496. DBGLOG("FileScope %s for %s not managed, using default %d", fullScope, sec_user.getName(),*accessFlags);
  497. #endif
  498. rc = true;
  499. }
  500. return rc;
  501. }
  502. int CPermissionsCache::queryDefaultPermission(ISecUser& user)
  503. {
  504. if (m_defaultPermission == SecAccess_Unknown)
  505. {
  506. if (m_secMgr)
  507. m_defaultPermission = m_secMgr->queryDefaultPermission(user);
  508. else
  509. m_defaultPermission = SecAccess_None;
  510. }
  511. return m_defaultPermission;
  512. }
  513. void CPermissionsCache::flush()
  514. {
  515. {
  516. synchronized block(m_cachemonitor);
  517. MapResPermissionsCache::const_iterator i;
  518. MapResPermissionsCache::const_iterator iEnd = m_resPermissionsMap.end();
  519. for (i = m_resPermissionsMap.begin(); i != iEnd; i++)
  520. delete (*i).second;
  521. m_resPermissionsMap.clear();
  522. }
  523. {
  524. synchronized block(m_userCacheMonitor);
  525. MapUserCache::const_iterator ui;
  526. MapUserCache::const_iterator uiEnd = m_userCache.end();
  527. for (ui = m_userCache.begin(); ui != uiEnd; ui++)
  528. delete (*ui).second;
  529. m_userCache.clear();
  530. }
  531. m_lastManagedFileScopesRefresh = 0;
  532. m_defaultPermission = SecAccess_Unknown;//trigger refresh
  533. }