caching.cpp 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589
  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. CachedUser* user = m_userCache[username];
  244. if(user == NULL)
  245. return false;
  246. time_t now;
  247. time(&now);
  248. if(user->getTimestamp() < (now - m_cacheTimeout))
  249. {
  250. m_userCache.erase(username);
  251. delete user;
  252. return false;
  253. }
  254. const char* cachedpw = user->queryUser()->credentials().getPassword();
  255. StringBuffer pw(sec_user.credentials().getPassword());
  256. if(cachedpw && pw.length() > 0)
  257. {
  258. StringBuffer md5pbuf;
  259. md5_string(pw, md5pbuf);
  260. if(strcmp(cachedpw, md5pbuf.str()) == 0)
  261. {
  262. // Copy cached user to the sec_user structure, but still keep the original clear text password.
  263. user->queryUser()->copyTo(sec_user);
  264. sec_user.credentials().setPassword(pw.str());
  265. return true;
  266. }
  267. else
  268. {
  269. m_userCache.erase(username);
  270. delete user;
  271. return false;
  272. }
  273. }
  274. return false;
  275. }
  276. ISecUser* CPermissionsCache::getCachedUser( ISecUser& sec_user)
  277. {
  278. if(!isCacheEnabled())
  279. return NULL;
  280. const char* username = sec_user.getName();
  281. if(!username || !*username)
  282. return NULL;
  283. synchronized block(m_userCacheMonitor);
  284. CachedUser* user = m_userCache[username];
  285. if(user == NULL)
  286. return NULL;
  287. return LINK(user->queryUser());
  288. }
  289. void CPermissionsCache::add(ISecUser& sec_user)
  290. {
  291. if(!isCacheEnabled() || &sec_user == NULL)
  292. return;
  293. const char* username = sec_user.getName();
  294. if(!username || !*username)
  295. return;
  296. synchronized block(m_userCacheMonitor);
  297. CachedUser* user = m_userCache[username];
  298. if(user)
  299. {
  300. m_userCache.erase(username);
  301. delete user;
  302. }
  303. m_userCache[username] = new CachedUser(sec_user.clone());
  304. }
  305. void CPermissionsCache::removeFromUserCache(ISecUser& sec_user)
  306. {
  307. const char* username = sec_user.getName();
  308. if(username && *username)
  309. {
  310. synchronized block(m_userCacheMonitor);
  311. CachedUser* user = m_userCache[username];
  312. if(user)
  313. {
  314. m_userCache.erase(username);
  315. delete user;
  316. }
  317. }
  318. }
  319. bool CPermissionsCache::addManagedFileScopes(IArrayOf<ISecResource>& scopes)
  320. {
  321. synchronized block(m_managedFileScopesCacheMonitor);
  322. ForEachItemIn(x, scopes)
  323. {
  324. ISecResource* scope = &scopes.item(x);
  325. if(!scope)
  326. continue;
  327. const char* cachekey = scope->getName();
  328. if(cachekey == NULL)
  329. continue;
  330. map<string, ISecResource*>::iterator it = m_managedFileScopesMap.find(cachekey);
  331. if (it != m_managedFileScopesMap.end())
  332. {
  333. ISecResource *res = (*it).second;
  334. res->Release();
  335. m_managedFileScopesMap.erase(it);
  336. }
  337. #ifdef _DEBUG
  338. DBGLOG("Caching Managed File Scope %s",cachekey);
  339. #endif
  340. m_managedFileScopesMap.insert( pair<string, ISecResource*>(cachekey, LINK(scope)));
  341. }
  342. return true;
  343. }
  344. inline void CPermissionsCache::removeManagedFileScopes(IArrayOf<ISecResource>& scopes)
  345. {
  346. synchronized block(m_managedFileScopesCacheMonitor);
  347. ForEachItemIn(x, scopes)
  348. {
  349. ISecResource* scope = &scopes.item(x);
  350. if(!scope)
  351. continue;
  352. const char* cachekey = scope->getName();
  353. if(cachekey == NULL)
  354. continue;
  355. map<string, ISecResource*>::iterator it = m_managedFileScopesMap.find(cachekey);
  356. if (it != m_managedFileScopesMap.end())
  357. {
  358. ISecResource *res = (*it).second;
  359. res->Release();
  360. m_managedFileScopesMap.erase(it);
  361. }
  362. }
  363. }
  364. inline void CPermissionsCache::removeAllManagedFileScopes()
  365. {
  366. synchronized block(m_managedFileScopesCacheMonitor);
  367. map<string, ISecResource*>::const_iterator cit;
  368. map<string, ISecResource*>::const_iterator iEnd = m_managedFileScopesMap.end();
  369. for (cit = m_managedFileScopesMap.begin(); cit != iEnd; cit++)
  370. {
  371. ISecResource *res = (*cit).second;
  372. res->Release();
  373. }
  374. m_managedFileScopesMap.clear();
  375. }
  376. /*
  377. if perms set on 'scopeA::scopeB' only and lookup of 'scopeA::scopeB::scopeC::scopeD'
  378. need to lookup:
  379. 'scopeA'
  380. no match=>continue
  381. match=>continue if read permissions (if no read, implies can't "see" child scopes)
  382. 'scopeA::scopeB'
  383. no match=>continue
  384. match=>continue if read permissions (if no read, implies can't "see" child scopes)
  385. etc. Until full scope path checked, or no read permissions hit on ancestor scope.
  386. */
  387. bool CPermissionsCache::queryPermsManagedFileScope(ISecUser& sec_user, const char * fullScope, StringBuffer& managedScope, int * accessFlags)
  388. {
  389. if (!fullScope || !*fullScope)
  390. {
  391. *accessFlags = queryDefaultPermission(sec_user);
  392. return true;
  393. }
  394. time_t now;
  395. time(&now);
  396. if (m_secMgr && (0 == m_lastManagedFileScopesRefresh || m_lastManagedFileScopesRefresh < (now - m_cacheTimeout)))
  397. {
  398. removeAllManagedFileScopes();
  399. IArrayOf<ISecResource> scopes;
  400. aindex_t count = m_secMgr->getManagedFileScopes(scopes);
  401. if (count)
  402. addManagedFileScopes(scopes);
  403. m_defaultPermission = SecAccess_Unknown;//trigger refresh
  404. m_lastManagedFileScopesRefresh = now;
  405. }
  406. if (m_managedFileScopesMap.empty())
  407. {
  408. *accessFlags = queryDefaultPermission(sec_user);
  409. return true;
  410. }
  411. StringArray scopes;
  412. {
  413. StringBuffer scope;
  414. const char * p = fullScope;
  415. while (*p)
  416. {
  417. if (*p == ':')
  418. {
  419. if (*(p+1) != ':')
  420. return false;//Malformed scope string, let LDAP figure it out
  421. scopes.append(scope.str());
  422. scope.append(*(p++));
  423. }
  424. scope.append(*(p++));
  425. }
  426. scopes.append(scope.str());
  427. }
  428. synchronized block(m_managedFileScopesCacheMonitor);
  429. ISecResource *matchedRes = NULL;
  430. ISecResource *res = NULL;
  431. bool isManaged = false;
  432. for(unsigned i = 0; i < scopes.length(); i++)
  433. {
  434. const char* scope = scopes.item(i);
  435. map<string, ISecResource*>::const_iterator it = m_managedFileScopesMap.find(scope);
  436. if (it != m_managedFileScopesMap.end())
  437. {
  438. isManaged = true;
  439. res = (*it).second;
  440. res->setResourceType(RT_FILE_SCOPE);
  441. LINK(res);
  442. IArrayOf<ISecResource> secResArr;
  443. secResArr.append(*res);
  444. bool found;
  445. int nFound = lookup(sec_user, secResArr, &found);
  446. if (nFound && found)
  447. {
  448. if (0 == (res->getAccessFlags() & SecAccess_Read))
  449. {
  450. *accessFlags = res->getAccessFlags();
  451. managedScope.append(const_cast<char *>(res->getName()));
  452. #ifdef _DEBUG
  453. DBGLOG("FileScope %s for %s(%s) access denied %d",fullScope, sec_user.getName(), res->getName(), *accessFlags);
  454. #endif
  455. return true;
  456. }
  457. else
  458. matchedRes = res;//allowed at this scope, but must also look at child scopes
  459. }
  460. }
  461. }
  462. bool rc;
  463. if (isManaged)
  464. {
  465. if (matchedRes)
  466. {
  467. *accessFlags = matchedRes->getAccessFlags();
  468. managedScope.append(const_cast<char *>(matchedRes->getName()));
  469. #ifdef _DEBUG
  470. DBGLOG("FileScope %s for %s(%s) access granted %d", fullScope, sec_user.getName(), matchedRes->getName(), *accessFlags);
  471. #endif
  472. rc = true;
  473. }
  474. else
  475. {
  476. managedScope.append(const_cast<char *>(res->getName()));
  477. #ifdef _DEBUG
  478. DBGLOG("FileScope %s for %s(%s) managed but not cached", fullScope, sec_user.getName(), res->getName());
  479. #endif
  480. rc = false;//need to go to LDAP to check
  481. }
  482. }
  483. else
  484. {
  485. *accessFlags = queryDefaultPermission(sec_user);
  486. #ifdef _DEBUG
  487. DBGLOG("FileScope %s for %s not managed, using default %d", fullScope, sec_user.getName(),*accessFlags);
  488. #endif
  489. rc = true;
  490. }
  491. return rc;
  492. }
  493. int CPermissionsCache::queryDefaultPermission(ISecUser& user)
  494. {
  495. if (m_defaultPermission == SecAccess_Unknown)
  496. {
  497. if (m_secMgr)
  498. m_defaultPermission = m_secMgr->queryDefaultPermission(user);
  499. else
  500. m_defaultPermission = SecAccess_None;
  501. }
  502. return m_defaultPermission;
  503. }
  504. void CPermissionsCache::flush()
  505. {
  506. {
  507. synchronized block(m_cachemonitor);
  508. MapResPermissionsCache::const_iterator i;
  509. MapResPermissionsCache::const_iterator iEnd = m_resPermissionsMap.end();
  510. for (i = m_resPermissionsMap.begin(); i != iEnd; i++)
  511. delete (*i).second;
  512. m_resPermissionsMap.clear();
  513. }
  514. {
  515. synchronized block(m_userCacheMonitor);
  516. MapUserCache::const_iterator ui;
  517. MapUserCache::const_iterator uiEnd = m_userCache.end();
  518. for (ui = m_userCache.begin(); ui != uiEnd; ui++)
  519. delete (*ui).second;
  520. m_userCache.clear();
  521. }
  522. m_lastManagedFileScopesRefresh = 0;
  523. m_defaultPermission = SecAccess_Unknown;//trigger refresh
  524. }