function authenticated_via_ldap_cache($username, $password, &$ldap_displayname) { global $LDAP_options, $auto_tags; // Destroy the cache each time config changes. if (sha1(serialize($LDAP_options)) != loadScript('LDAPConfigHash')) { discardLDAPCache(); saveScript('LDAPConfigHash', sha1(serialize($LDAP_options))); } $oldinfo = acquireLDAPCache($username, sha1($password), $LDAP_options['cache_expiry']); if ($oldinfo === NULL) { // On cache miss execute complete procedure and return the result. In case // of successful authentication put a record into cache. $newinfo = queryLDAPServer($username, $password); if ($newinfo['result'] == 'ACK') { $ldap_displayname = $newinfo['displayed_name']; foreach ($newinfo['memberof'] as $autotag) { $auto_tags[] = array('tag' => $autotag); } replaceLDAPCacheRecord($username, sha1($password), $newinfo['displayed_name'], $newinfo['memberof']); releaseLDAPCache(); discardLDAPCache($LDAP_options['cache_expiry']); return TRUE; } releaseLDAPCache(); return FALSE; } // cache HIT // There are two confidence levels of cache hits: "certain" and "uncertain". In either case // expect authentication success, unless it's well-timed to perform a retry, // which may sometimes bring a NAK decision. if ($oldinfo['success_age'] < $LDAP_options['cache_refresh'] or $oldinfo['retry_age'] < $LDAP_options['cache_retry']) { releaseLDAPCache(); $ldap_displayname = $oldinfo['displayed_name']; foreach ($oldinfo['memberof'] as $autotag) { $auto_tags[] = array('tag' => $autotag); } return TRUE; } // Either refresh threshold or retry threshold reached. $newinfo = queryLDAPServer($username, $password); switch ($newinfo['result']) { case 'ACK': // refresh existing record $ldap_displayname = $newinfo['displayed_name']; foreach ($newinfo['memberof'] as $autotag) { $auto_tags[] = array('tag' => $autotag); } replaceLDAPCacheRecord($username, sha1($password), $newinfo['displayed_name'], $newinfo['memberof']); releaseLDAPCache(); discardLDAPCache($LDAP_options['cache_expiry']); return TRUE; case 'NAK': // The record isn't valid any more. deleteLDAPCacheRecord($username); releaseLDAPCache(); discardLDAPCache($LDAP_options['cache_expiry']); return FALSE; case 'CAN': // retry failed, do nothing, use old value till next retry $ldap_displayname = $oldinfo['displayed_name']; foreach ($oldinfo['memberof'] as $autotag) { $auto_tags[] = array('tag' => $autotag); } touchLDAPCacheRecord($username); releaseLDAPCache(); discardLDAPCache($LDAP_options['cache_expiry']); return TRUE; default: throw new RackTablesError('structure error', RackTablesError::INTERNAL); } // This is never reached. return FALSE; }
function replaceLDAPCacheRecord($form_username, $password_hash, $dname, $memberof) { // FIXME: This sequence is able to trigger a deadlock, namely, when executed // in parallel from multiple working copies of the same user, which for some // reason has no valid record in LDAPCache. Perhaps, using REPLACE INTO can // lower the chances of this. deleteLDAPCacheRecord($form_username); usePreparedInsertBlade('LDAPCache', array('presented_username' => $form_username, 'successful_hash' => $password_hash, 'displayed_name' => $dname, 'memberof' => base64_encode(serialize($memberof)))); }
function authenticated_via_ldap_cache($username, $password, &$ldap_displayname) { global $LDAP_options, $auto_tags; $user_data = array(); // fill auto_tags and ldap_displayname from this array $password_hash = sha1($password); // first try to get cache row without locking it (quick way) $cache_row = fetchLDAPCacheRow($username); if (isLDAPCacheValid($cache_row, $password_hash, TRUE)) { $user_data = $cache_row; } else { // cache miss or expired. Try to lock LDAPCache for $username $cache_row = acquireLDAPCache($username); if (isLDAPCacheValid($cache_row, $password_hash, TRUE)) { $user_data = $cache_row; } else { $ldap_answer = queryLDAPServer($username, $password); switch ($ldap_answer['result']) { case 'ACK': replaceLDAPCacheRecord($username, $password_hash, $ldap_answer['displayed_name'], $ldap_answer['memberof']); $user_data = $ldap_answer; break; case 'NAK': // The record isn't valid any more. // TODO: negative result caching deleteLDAPCacheRecord($username); break; case 'CAN': // LDAP query failed, use old value till next retry if (isLDAPCacheValid($cache_row, $password_hash, FALSE)) { touchLDAPCacheRecord($username); $user_data = $cache_row; } else { deleteLDAPCacheRecord($username); } break; default: throw new RackTablesError('structure error', RackTablesError::INTERNAL); } } releaseLDAPCache(); discardLDAPCache($LDAP_options['cache_expiry']); // clear expired rows of other users } if ($user_data) { $ldap_displayname = $user_data['displayed_name']; foreach ($user_data['memberof'] as $autotag) { $auto_tags[] = array('tag' => $autotag); } return TRUE; } return FALSE; }