[PkiRealm] Invalidate cache on role mappings change (#31510)

PkiRealm caches successful authentications and provides ways to
invalidate the cache. But in some scenario's the cache was not being
invalidated on role mapping change.
PkiRealm does not inform role mapper to be notified for cache
refresh on role mapping updates.
The logic in `TransportClearRealmCacheAction#nodeOperation`
which gets invoked for refreshing cache on realms, considers null or
empty realm names in the request as clear cache on all realms. When
LDAP realm is not present then it clears cache for all realms so it
works fine, but when LDAP realm is configured then role mapper
sends a request with LDAP realm names and so the cache is cleared
only for those realms.

This commit resolves the issue by registering PkiRealm with role
mapper for cache refresh. PkiRealm implements CachingRealm and as it
does not extend CachingUsernamePasswordRealm, have modified the
interface method `refreshRealmOnChange` to accept CachingRealm.
This commit is contained in:
Yogesh Gaikwad 2018-06-22 17:47:20 +10:00 committed by GitHub
parent 724438a0b0
commit 009ae48cba
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 15 additions and 6 deletions

View File

@ -86,6 +86,7 @@ public class PkiRealm extends Realm implements CachingRealm {
this.trustManager = trustManagers(config);
this.principalPattern = PkiRealmSettings.USERNAME_PATTERN_SETTING.get(config.settings());
this.roleMapper = roleMapper;
this.roleMapper.refreshRealmOnChange(this);
this.cache = CacheBuilder.<BytesKey, User>builder()
.setExpireAfterWrite(PkiRealmSettings.CACHE_TTL_SETTING.get(config.settings()))
.setMaximumWeight(PkiRealmSettings.CACHE_MAX_USERS_SETTING.get(config.settings()))

View File

@ -13,6 +13,11 @@ import org.elasticsearch.xpack.core.security.authc.Realm;
*/
public interface CachingRealm {
/**
* @return The name of this realm.
*/
String name();
/**
* Expires a single user from the cache identified by the String agument
* @param username the identifier of the user to be cleared

View File

@ -69,7 +69,7 @@ public class DnRoleMapper implements UserRoleMapper {
}
@Override
public void refreshRealmOnChange(CachingUsernamePasswordRealm realm) {
public void refreshRealmOnChange(CachingRealm realm) {
addListener(realm::expireAll);
}

View File

@ -44,7 +44,7 @@ public interface UserRoleMapper {
* the whole cluster depending on whether this role-mapper has node-local data or cluster-wide
* data.
*/
void refreshRealmOnChange(CachingUsernamePasswordRealm realm);
void refreshRealmOnChange(CachingRealm realm);
/**
* A representation of a user for whom roles should be mapped.

View File

@ -16,7 +16,7 @@ import org.elasticsearch.action.ActionListener;
import org.elasticsearch.action.support.GroupedActionListener;
import org.elasticsearch.watcher.ResourceWatcherService;
import org.elasticsearch.xpack.core.security.authc.RealmConfig;
import org.elasticsearch.xpack.security.authc.support.CachingUsernamePasswordRealm;
import org.elasticsearch.xpack.security.authc.support.CachingRealm;
import org.elasticsearch.xpack.security.authc.support.DnRoleMapper;
import org.elasticsearch.xpack.security.authc.support.UserRoleMapper;
@ -48,7 +48,7 @@ public class CompositeRoleMapper implements UserRoleMapper {
}
@Override
public void refreshRealmOnChange(CachingUsernamePasswordRealm realm) {
public void refreshRealmOnChange(CachingRealm realm) {
this.delegates.forEach(mapper -> mapper.refreshRealmOnChange(realm));
}

View File

@ -34,7 +34,7 @@ import org.elasticsearch.xpack.core.security.action.rolemapping.PutRoleMappingRe
import org.elasticsearch.xpack.core.security.authc.support.mapper.ExpressionRoleMapping;
import org.elasticsearch.xpack.core.security.authc.support.mapper.expressiondsl.ExpressionModel;
import org.elasticsearch.xpack.core.security.client.SecurityClient;
import org.elasticsearch.xpack.security.authc.support.CachingUsernamePasswordRealm;
import org.elasticsearch.xpack.security.authc.support.CachingRealm;
import org.elasticsearch.xpack.security.authc.support.UserRoleMapper;
import org.elasticsearch.xpack.security.support.SecurityIndexManager;
@ -369,7 +369,7 @@ public class NativeRoleMappingStore extends AbstractComponent implements UserRol
* @see ClearRealmCacheAction
*/
@Override
public void refreshRealmOnChange(CachingUsernamePasswordRealm realm) {
public void refreshRealmOnChange(CachingRealm realm) {
realmsToRefresh.add(realm.name());
}
}

View File

@ -50,6 +50,7 @@ import static org.mockito.Mockito.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
public class PkiRealmTests extends ESTestCase {
@ -104,6 +105,7 @@ public class PkiRealmTests extends ESTestCase {
UserRoleMapper roleMapper = mock(UserRoleMapper.class);
PkiRealm realm = new PkiRealm(new RealmConfig("", Settings.EMPTY, globalSettings, TestEnvironment.newEnvironment(globalSettings),
new ThreadContext(globalSettings)), roleMapper);
verify(roleMapper).refreshRealmOnChange(realm);
Mockito.doAnswer(invocation -> {
final UserRoleMapper.UserData userData = (UserRoleMapper.UserData) invocation.getArguments()[0];
final ActionListener<Set<String>> listener = (ActionListener<Set<String>>) invocation.getArguments()[1];
@ -144,6 +146,7 @@ public class PkiRealmTests extends ESTestCase {
final int numTimes = invalidate ? 2 : 1;
verify(roleMapper, times(numTimes)).resolveRoles(any(UserRoleMapper.UserData.class), any(ActionListener.class));
verifyNoMoreInteractions(roleMapper);
}
public void testCustomUsernamePattern() throws Exception {