Fix X509AuthenticationToken principal (#43932)
Fixes a bug in the PKI authentication. This manifests when there are multiple PKI realms configured in the chain, with different principal parse patterns. There are a few configuration scenarios where one PKI realm might parse the principal from the Subject DN (according to the `username_pattern` realm setting) but another one might do the truststore validation (according to the truststore.* realm settings). This is caused by the two passes through the realm chain, first to build the authentication token and secondly to authenticate it, and that the X509AuthenticationToken sets the principal during construction.
This commit is contained in:
parent
8d35583c43
commit
e490ecb7d3
|
@ -110,7 +110,30 @@ public class PkiRealm extends Realm implements CachingRealm {
|
|||
|
||||
@Override
|
||||
public X509AuthenticationToken token(ThreadContext context) {
|
||||
return token(context.getTransient(PKI_CERT_HEADER_NAME), principalPattern, logger);
|
||||
Object pkiHeaderValue = context.getTransient(PKI_CERT_HEADER_NAME);
|
||||
if (pkiHeaderValue == null) {
|
||||
return null;
|
||||
}
|
||||
assert pkiHeaderValue instanceof X509Certificate[];
|
||||
X509Certificate[] certificates = (X509Certificate[]) pkiHeaderValue;
|
||||
if (certificates.length == 0) {
|
||||
return null;
|
||||
}
|
||||
X509AuthenticationToken token = new X509AuthenticationToken(certificates);
|
||||
// the following block of code maintains BWC:
|
||||
// When constructing the token object we only return it if the Subject DN of the certificate can be parsed by at least one PKI
|
||||
// realm. We then consider the parsed Subject DN as the "principal" even though it is potentially incorrect because when several
|
||||
// realms are installed the one that first parses the principal might not be the one that finally authenticates (does trusted chain
|
||||
// validation). In this case the principal should be set by the realm that completes the authentication. But in the common case,
|
||||
// where a single PKI realm is configured, there is no risk of eagerly parsing the principal before authentication and it also
|
||||
// maintains BWC.
|
||||
String parsedPrincipal = getPrincipalFromSubjectDN(principalPattern, token, logger);
|
||||
if (parsedPrincipal == null) {
|
||||
return null;
|
||||
}
|
||||
token.setPrincipal(parsedPrincipal);
|
||||
// end BWC code block
|
||||
return token;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -122,25 +145,41 @@ public class PkiRealm extends Realm implements CachingRealm {
|
|||
User user = cache.get(fingerprint);
|
||||
if (user != null) {
|
||||
if (delegatedRealms.hasDelegation()) {
|
||||
delegatedRealms.resolve(token.principal(), listener);
|
||||
delegatedRealms.resolve(user.principal(), listener);
|
||||
} else {
|
||||
listener.onResponse(AuthenticationResult.success(user));
|
||||
}
|
||||
} else if (isCertificateChainTrusted(trustManager, token, logger) == false) {
|
||||
listener.onResponse(AuthenticationResult.unsuccessful("Certificate for " + token.dn() + " is not trusted", null));
|
||||
} else {
|
||||
final ActionListener<AuthenticationResult> cachingListener = ActionListener.wrap(result -> {
|
||||
if (result.isAuthenticated()) {
|
||||
try (ReleasableLock ignored = readLock.acquire()) {
|
||||
cache.put(fingerprint, result.getUser());
|
||||
}
|
||||
}
|
||||
listener.onResponse(result);
|
||||
}, listener::onFailure);
|
||||
if (delegatedRealms.hasDelegation()) {
|
||||
delegatedRealms.resolve(token.principal(), cachingListener);
|
||||
// parse the principal again after validating the cert chain, and do not rely on the token.principal one, because that could
|
||||
// be set by a different realm that failed trusted chain validation. We SHOULD NOT parse the principal BEFORE this step, but
|
||||
// we do it for BWC purposes. Changing this is a breaking change.
|
||||
final String principal = getPrincipalFromSubjectDN(principalPattern, token, logger);
|
||||
if (principal == null) {
|
||||
logger.debug((Supplier<?>) () -> new ParameterizedMessage(
|
||||
"the extracted principal after cert chain validation, from DN [{}], using pattern [{}] is null", token.dn(),
|
||||
principalPattern.toString()));
|
||||
listener.onResponse(AuthenticationResult.unsuccessful("Could not parse principal from Subject DN " + token.dn(), null));
|
||||
} else {
|
||||
this.buildUser(token, cachingListener);
|
||||
final ActionListener<AuthenticationResult> cachingListener = ActionListener.wrap(result -> {
|
||||
if (result.isAuthenticated()) {
|
||||
try (ReleasableLock ignored = readLock.acquire()) {
|
||||
cache.put(fingerprint, result.getUser());
|
||||
}
|
||||
}
|
||||
listener.onResponse(result);
|
||||
}, listener::onFailure);
|
||||
if (false == principal.equals(token.principal())) {
|
||||
logger.debug((Supplier<?>) () -> new ParameterizedMessage(
|
||||
"the extracted principal before [{}] and after [{}] cert chain validation, for DN [{}], are different",
|
||||
token.principal(), principal, token.dn()));
|
||||
}
|
||||
if (delegatedRealms.hasDelegation()) {
|
||||
delegatedRealms.resolve(principal, cachingListener);
|
||||
} else {
|
||||
buildUser(token, principal, cachingListener);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (CertificateEncodingException e) {
|
||||
|
@ -148,13 +187,12 @@ public class PkiRealm extends Realm implements CachingRealm {
|
|||
}
|
||||
}
|
||||
|
||||
private void buildUser(X509AuthenticationToken token, ActionListener<AuthenticationResult> listener) {
|
||||
private void buildUser(X509AuthenticationToken token, String principal, ActionListener<AuthenticationResult> listener) {
|
||||
final Map<String, Object> metadata = Collections.singletonMap("pki_dn", token.dn());
|
||||
final UserRoleMapper.UserData userData = new UserRoleMapper.UserData(token.principal(),
|
||||
token.dn(), Collections.emptySet(), metadata, this.config);
|
||||
final UserRoleMapper.UserData userData = new UserRoleMapper.UserData(principal, token.dn(), Collections.emptySet(), metadata,
|
||||
this.config);
|
||||
roleMapper.resolveRoles(userData, ActionListener.wrap(roles -> {
|
||||
final User computedUser =
|
||||
new User(token.principal(), roles.toArray(new String[roles.size()]), null, null, metadata, true);
|
||||
final User computedUser = new User(principal, roles.toArray(new String[roles.size()]), null, null, metadata, true);
|
||||
listener.onResponse(AuthenticationResult.success(computedUser));
|
||||
}, listener::onFailure));
|
||||
}
|
||||
|
@ -164,47 +202,33 @@ public class PkiRealm extends Realm implements CachingRealm {
|
|||
listener.onResponse(null);
|
||||
}
|
||||
|
||||
static X509AuthenticationToken token(Object pkiHeaderValue, Pattern principalPattern, Logger logger) {
|
||||
if (pkiHeaderValue == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
assert pkiHeaderValue instanceof X509Certificate[];
|
||||
X509Certificate[] certificates = (X509Certificate[]) pkiHeaderValue;
|
||||
if (certificates.length == 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
String dn = certificates[0].getSubjectX500Principal().toString();
|
||||
static String getPrincipalFromSubjectDN(Pattern principalPattern, X509AuthenticationToken token, Logger logger) {
|
||||
String dn = token.credentials()[0].getSubjectX500Principal().toString();
|
||||
Matcher matcher = principalPattern.matcher(dn);
|
||||
if (!matcher.find()) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("certificate authentication succeeded for [{}] but could not extract principal from DN", dn);
|
||||
}
|
||||
if (false == matcher.find()) {
|
||||
logger.debug((Supplier<?>) () -> new ParameterizedMessage("could not extract principal from DN [{}] using pattern [{}]", dn,
|
||||
principalPattern.toString()));
|
||||
return null;
|
||||
}
|
||||
|
||||
String principal = matcher.group(1);
|
||||
if (Strings.isNullOrEmpty(principal)) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("certificate authentication succeeded for [{}] but extracted principal was empty", dn);
|
||||
}
|
||||
logger.debug((Supplier<?>) () -> new ParameterizedMessage("the extracted principal from DN [{}] using pattern [{}] is empty",
|
||||
dn, principalPattern.toString()));
|
||||
return null;
|
||||
}
|
||||
return new X509AuthenticationToken(certificates, principal, dn);
|
||||
return principal;
|
||||
}
|
||||
|
||||
static boolean isCertificateChainTrusted(X509TrustManager trustManager, X509AuthenticationToken token, Logger logger) {
|
||||
private static boolean isCertificateChainTrusted(X509TrustManager trustManager, X509AuthenticationToken token, Logger logger) {
|
||||
if (trustManager != null) {
|
||||
try {
|
||||
trustManager.checkClientTrusted(token.credentials(), AUTH_TYPE);
|
||||
return true;
|
||||
} catch (CertificateException e) {
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace((Supplier<?>)
|
||||
() -> new ParameterizedMessage("failed certificate validation for principal [{}]", token.principal()), e);
|
||||
logger.trace("failed certificate validation for Subject DN [" + token.dn() + "]", e);
|
||||
} else if (logger.isDebugEnabled()) {
|
||||
logger.debug("failed certificate validation for principal [{}]", token.principal());
|
||||
logger.debug("failed certificate validation for Subject DN [{}]", token.dn());
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
@ -214,7 +238,7 @@ public class PkiRealm extends Realm implements CachingRealm {
|
|||
return true;
|
||||
}
|
||||
|
||||
X509TrustManager trustManagers(RealmConfig realmConfig) {
|
||||
private X509TrustManager trustManagers(RealmConfig realmConfig) {
|
||||
final List<String> certificateAuthorities = realmConfig.hasSetting(PkiRealmSettings.CAPATH_SETTING) ?
|
||||
realmConfig.getSetting(PkiRealmSettings.CAPATH_SETTING) : null;
|
||||
String truststorePath = realmConfig.getSetting(PkiRealmSettings.TRUST_STORE_PATH).orElse(null);
|
||||
|
|
|
@ -8,17 +8,18 @@ package org.elasticsearch.xpack.security.authc.pki;
|
|||
import org.elasticsearch.xpack.core.security.authc.AuthenticationToken;
|
||||
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.Objects;
|
||||
|
||||
public class X509AuthenticationToken implements AuthenticationToken {
|
||||
|
||||
private final String principal;
|
||||
private final String dn;
|
||||
private X509Certificate[] credentials;
|
||||
private final X509Certificate[] credentials;
|
||||
private String principal;
|
||||
|
||||
public X509AuthenticationToken(X509Certificate[] certificates, String principal, String dn) {
|
||||
this.principal = principal;
|
||||
this.credentials = certificates;
|
||||
this.dn = dn;
|
||||
public X509AuthenticationToken(X509Certificate[] certificates) {
|
||||
this.credentials = Objects.requireNonNull(certificates);
|
||||
this.dn = certificates.length == 0 ? "" : certificates[0].getSubjectX500Principal().toString();
|
||||
this.principal = this.dn;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -26,6 +27,10 @@ public class X509AuthenticationToken implements AuthenticationToken {
|
|||
return principal;
|
||||
}
|
||||
|
||||
public void setPrincipal(String principal) {
|
||||
this.principal = principal;
|
||||
}
|
||||
|
||||
@Override
|
||||
public X509Certificate[] credentials() {
|
||||
return credentials;
|
||||
|
@ -37,6 +42,6 @@ public class X509AuthenticationToken implements AuthenticationToken {
|
|||
|
||||
@Override
|
||||
public void clearCredentials() {
|
||||
credentials = null;
|
||||
// noop
|
||||
}
|
||||
}
|
||||
|
|
|
@ -67,9 +67,16 @@ public class PkiAuthenticationTests extends SecuritySingleNodeTestCase {
|
|||
.put("xpack.security.authc.realms.file.file.order", "0")
|
||||
.put("xpack.security.authc.realms.pki.pki1.order", "1")
|
||||
.putList("xpack.security.authc.realms.pki.pki1.certificate_authorities",
|
||||
getDataPath("/org/elasticsearch/xpack/security/transport/ssl/certs/simple/testclient.crt").toString())
|
||||
.put("xpack.security.authc.realms.pki.pki1.files.role_mapping", getDataPath("role_mapping.yml"))
|
||||
.put("xpack.security.authc.realms.pki.pki1.files.role_mapping", getDataPath("role_mapping.yml"))
|
||||
// pki1 never authenticates because of the principal pattern
|
||||
.put("xpack.security.authc.realms.pki.pki1.username_pattern", "CN=(MISMATCH.*?)(?:,|$)")
|
||||
.put("xpack.security.authc.realms.pki.pki2.order", "2")
|
||||
.putList("xpack.security.authc.realms.pki.pki2.certificate_authorities",
|
||||
getDataPath("/org/elasticsearch/xpack/security/transport/ssl/certs/simple/testnode.crt").toString(),
|
||||
getDataPath("/org/elasticsearch/xpack/security/transport/ssl/certs/simple/testnode_ec.crt").toString())
|
||||
.put("xpack.security.authc.realms.pki.pki1.files.role_mapping", getDataPath("role_mapping.yml"));
|
||||
.put("xpack.security.authc.realms.pki.pki2.files.role_mapping", getDataPath("role_mapping.yml"));
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
|
|
|
@ -79,7 +79,7 @@ public class PkiRealmTests extends ESTestCase {
|
|||
|
||||
assertThat(realm.supports(null), is(false));
|
||||
assertThat(realm.supports(new UsernamePasswordToken("", new SecureString(new char[0]))), is(false));
|
||||
assertThat(realm.supports(new X509AuthenticationToken(new X509Certificate[0], "", "")), is(true));
|
||||
assertThat(realm.supports(new X509AuthenticationToken(new X509Certificate[0])), is(true));
|
||||
}
|
||||
|
||||
public void testExtractToken() throws Exception {
|
||||
|
@ -92,7 +92,6 @@ public class PkiRealmTests extends ESTestCase {
|
|||
X509AuthenticationToken token = realm.token(threadContext);
|
||||
assertThat(token, is(notNullValue()));
|
||||
assertThat(token.dn(), is("CN=Elasticsearch Test Node, OU=elasticsearch, O=org"));
|
||||
assertThat(token.principal(), is("Elasticsearch Test Node"));
|
||||
}
|
||||
|
||||
public void testAuthenticateBasedOnCertToken() throws Exception {
|
||||
|
@ -112,7 +111,8 @@ public class PkiRealmTests extends ESTestCase {
|
|||
PkiRealm realm = buildRealm(roleMapper, globalSettings);
|
||||
verify(roleMapper).refreshRealmOnChange(realm);
|
||||
|
||||
final String expectedUsername = token.principal();
|
||||
final String expectedUsername = PkiRealm.getPrincipalFromSubjectDN(Pattern.compile(PkiRealmSettings.DEFAULT_USERNAME_PATTERN),
|
||||
token, NoOpLogger.INSTANCE);
|
||||
final AuthenticationResult result = authenticate(token, realm);
|
||||
final PlainActionFuture<AuthenticationResult> future;
|
||||
assertThat(result.getStatus(), is(AuthenticationResult.Status.SUCCESS));
|
||||
|
@ -133,10 +133,9 @@ public class PkiRealmTests extends ESTestCase {
|
|||
realm.expire(expectedUsername);
|
||||
}
|
||||
}
|
||||
future = new PlainActionFuture<>();
|
||||
realm.authenticate(token, future);
|
||||
assertEquals(AuthenticationResult.Status.SUCCESS, future.actionGet().getStatus());
|
||||
assertEquals(user, future.actionGet().getUser());
|
||||
final AuthenticationResult result2 = authenticate(token, realm);
|
||||
assertThat(AuthenticationResult.Status.SUCCESS, is(result2.getStatus()));
|
||||
assertThat(user, is(result2.getUser()));
|
||||
}
|
||||
|
||||
final int numTimes = invalidate ? 2 : 1;
|
||||
|
@ -144,6 +143,16 @@ public class PkiRealmTests extends ESTestCase {
|
|||
verifyNoMoreInteractions(roleMapper);
|
||||
}
|
||||
|
||||
private UserRoleMapper buildRoleMapper() {
|
||||
UserRoleMapper roleMapper = mock(UserRoleMapper.class);
|
||||
Mockito.doAnswer(invocation -> {
|
||||
ActionListener<Set<String>> listener = (ActionListener<Set<String>>) invocation.getArguments()[1];
|
||||
listener.onResponse(Collections.emptySet());
|
||||
return null;
|
||||
}).when(roleMapper).resolveRoles(any(UserRoleMapper.UserData.class), any(ActionListener.class));
|
||||
return roleMapper;
|
||||
}
|
||||
|
||||
private UserRoleMapper buildRoleMapper(Set<String> roles, String dn) {
|
||||
UserRoleMapper roleMapper = mock(UserRoleMapper.class);
|
||||
Mockito.doAnswer(invocation -> {
|
||||
|
@ -172,7 +181,7 @@ public class PkiRealmTests extends ESTestCase {
|
|||
|
||||
private X509AuthenticationToken buildToken() throws Exception {
|
||||
X509Certificate certificate = readCert(getDataPath("/org/elasticsearch/xpack/security/transport/ssl/certs/simple/testnode.crt"));
|
||||
return new X509AuthenticationToken(new X509Certificate[]{certificate}, "Elasticsearch Test Node", "CN=Elasticsearch Test Node,");
|
||||
return new X509AuthenticationToken(new X509Certificate[]{certificate});
|
||||
}
|
||||
|
||||
private AuthenticationResult authenticate(X509AuthenticationToken token, PkiRealm realm) {
|
||||
|
@ -181,38 +190,44 @@ public class PkiRealmTests extends ESTestCase {
|
|||
return future.actionGet();
|
||||
}
|
||||
|
||||
public void testCustomUsernamePattern() throws Exception {
|
||||
public void testCustomUsernamePatternMatches() throws Exception {
|
||||
final Settings settings = Settings.builder()
|
||||
.put(globalSettings)
|
||||
.put("xpack.security.authc.realms.pki.my_pki.username_pattern", "OU=(.*?),")
|
||||
.build();
|
||||
ThreadContext threadContext = new ThreadContext(settings);
|
||||
X509Certificate certificate = readCert(getDataPath("/org/elasticsearch/xpack/security/transport/ssl/certs/simple/testnode.crt"));
|
||||
UserRoleMapper roleMapper = mock(UserRoleMapper.class);
|
||||
PkiRealm realm = new PkiRealm(new RealmConfig(new RealmConfig.RealmIdentifier("pki", "my_pki"), settings,
|
||||
TestEnvironment.newEnvironment(settings), threadContext), roleMapper);
|
||||
realm.initialize(Collections.emptyList(), licenseState);
|
||||
Mockito.doAnswer(invocation -> {
|
||||
ActionListener<Set<String>> listener = (ActionListener<Set<String>>) invocation.getArguments()[1];
|
||||
listener.onResponse(Collections.emptySet());
|
||||
return null;
|
||||
}).when(roleMapper).resolveRoles(any(UserRoleMapper.UserData.class), any(ActionListener.class));
|
||||
UserRoleMapper roleMapper = buildRoleMapper();
|
||||
PkiRealm realm = buildRealm(roleMapper, settings);
|
||||
threadContext.putTransient(PkiRealm.PKI_CERT_HEADER_NAME, new X509Certificate[] { certificate });
|
||||
|
||||
X509AuthenticationToken token = realm.token(threadContext);
|
||||
PlainActionFuture<AuthenticationResult> future = new PlainActionFuture<>();
|
||||
realm.authenticate(token, future);
|
||||
User user = future.actionGet().getUser();
|
||||
User user = authenticate(token, realm).getUser();
|
||||
assertThat(user, is(notNullValue()));
|
||||
assertThat(user.principal(), is("elasticsearch"));
|
||||
assertThat(user.roles(), is(notNullValue()));
|
||||
assertThat(user.roles().length, is(0));
|
||||
}
|
||||
|
||||
public void testCustomUsernamePatternMismatchesAndNullToken() throws Exception {
|
||||
final Settings settings = Settings.builder()
|
||||
.put(globalSettings)
|
||||
.put("xpack.security.authc.realms.pki.my_pki.username_pattern", "OU=(mismatch.*?),")
|
||||
.build();
|
||||
ThreadContext threadContext = new ThreadContext(settings);
|
||||
X509Certificate certificate = readCert(getDataPath("/org/elasticsearch/xpack/security/transport/ssl/certs/simple/testnode.crt"));
|
||||
UserRoleMapper roleMapper = buildRoleMapper();
|
||||
PkiRealm realm = buildRealm(roleMapper, settings);
|
||||
threadContext.putTransient(PkiRealm.PKI_CERT_HEADER_NAME, new X509Certificate[] { certificate });
|
||||
|
||||
X509AuthenticationToken token = realm.token(threadContext);
|
||||
assertThat(token, is(nullValue()));
|
||||
}
|
||||
|
||||
public void testVerificationUsingATruststore() throws Exception {
|
||||
X509Certificate certificate = readCert(getDataPath("/org/elasticsearch/xpack/security/transport/ssl/certs/simple/testnode.crt"));
|
||||
|
||||
UserRoleMapper roleMapper = mock(UserRoleMapper.class);
|
||||
UserRoleMapper roleMapper = buildRoleMapper();
|
||||
MockSecureSettings secureSettings = new MockSecureSettings();
|
||||
secureSettings.setString("xpack.security.authc.realms.pki.my_pki.truststore.secure_password", "testnode");
|
||||
Settings settings = Settings.builder()
|
||||
|
@ -222,21 +237,12 @@ public class PkiRealmTests extends ESTestCase {
|
|||
.setSecureSettings(secureSettings)
|
||||
.build();
|
||||
ThreadContext threadContext = new ThreadContext(globalSettings);
|
||||
PkiRealm realm = new PkiRealm(new RealmConfig(new RealmConfig.RealmIdentifier("pki", "my_pki"), settings,
|
||||
TestEnvironment.newEnvironment(globalSettings), threadContext), roleMapper);
|
||||
realm.initialize(Collections.emptyList(), licenseState);
|
||||
Mockito.doAnswer(invocation -> {
|
||||
ActionListener<Set<String>> listener = (ActionListener<Set<String>>) invocation.getArguments()[1];
|
||||
listener.onResponse(Collections.emptySet());
|
||||
return null;
|
||||
}).when(roleMapper).resolveRoles(any(UserRoleMapper.UserData.class), any(ActionListener.class));
|
||||
PkiRealm realm = buildRealm(roleMapper, settings);
|
||||
|
||||
threadContext.putTransient(PkiRealm.PKI_CERT_HEADER_NAME, new X509Certificate[] { certificate });
|
||||
|
||||
X509AuthenticationToken token = realm.token(threadContext);
|
||||
PlainActionFuture<AuthenticationResult> future = new PlainActionFuture<>();
|
||||
realm.authenticate(token, future);
|
||||
User user = future.actionGet().getUser();
|
||||
User user = authenticate(token, realm).getUser();
|
||||
assertThat(user, is(notNullValue()));
|
||||
assertThat(user.principal(), is("Elasticsearch Test Node"));
|
||||
assertThat(user.roles(), is(notNullValue()));
|
||||
|
@ -245,32 +251,25 @@ public class PkiRealmTests extends ESTestCase {
|
|||
|
||||
public void testVerificationFailsUsingADifferentTruststore() throws Exception {
|
||||
X509Certificate certificate = readCert(getDataPath("/org/elasticsearch/xpack/security/transport/ssl/certs/simple/testnode.crt"));
|
||||
UserRoleMapper roleMapper = mock(UserRoleMapper.class);
|
||||
UserRoleMapper roleMapper = buildRoleMapper();
|
||||
MockSecureSettings secureSettings = new MockSecureSettings();
|
||||
secureSettings.setString("xpack.security.authc.realms.pki.mypki.truststore.secure_password", "testnode-client-profile");
|
||||
secureSettings.setString("xpack.security.authc.realms.pki.my_pki.truststore.secure_password", "testnode-client-profile");
|
||||
Settings settings = Settings.builder()
|
||||
.put(globalSettings)
|
||||
.put("xpack.security.authc.realms.pki.mypki.truststore.path",
|
||||
.put("xpack.security.authc.realms.pki.my_pki.truststore.path",
|
||||
getDataPath("/org/elasticsearch/xpack/security/transport/ssl/certs/simple/testnode-client-profile.jks"))
|
||||
.setSecureSettings(secureSettings)
|
||||
.build();
|
||||
final ThreadContext threadContext = new ThreadContext(settings);
|
||||
PkiRealm realm = new PkiRealm(new RealmConfig(new RealmConfig.RealmIdentifier("pki", "mypki"), settings,
|
||||
TestEnvironment.newEnvironment(settings), threadContext), roleMapper);
|
||||
realm.initialize(Collections.emptyList(), licenseState);
|
||||
Mockito.doAnswer(invocation -> {
|
||||
ActionListener<Set<String>> listener = (ActionListener<Set<String>>) invocation.getArguments()[1];
|
||||
listener.onResponse(Collections.emptySet());
|
||||
return null;
|
||||
}).when(roleMapper).resolveRoles(any(UserRoleMapper.UserData.class), any(ActionListener.class));
|
||||
ThreadContext threadContext = new ThreadContext(settings);
|
||||
PkiRealm realm = buildRealm(roleMapper, settings);
|
||||
|
||||
threadContext.putTransient(PkiRealm.PKI_CERT_HEADER_NAME, new X509Certificate[] { certificate });
|
||||
|
||||
X509AuthenticationToken token = realm.token(threadContext);
|
||||
PlainActionFuture<AuthenticationResult> future = new PlainActionFuture<>();
|
||||
realm.authenticate(token, future);
|
||||
User user = future.actionGet().getUser();
|
||||
assertThat(user, is(nullValue()));
|
||||
AuthenticationResult result = authenticate(token, realm);
|
||||
assertThat(result.getStatus(), equalTo(AuthenticationResult.Status.CONTINUE));
|
||||
assertThat(result.getMessage(), containsString("not trusted"));
|
||||
assertThat(result.getUser(), is(nullValue()));
|
||||
}
|
||||
|
||||
public void testTruststorePathWithoutPasswordThrowsException() throws Exception {
|
||||
|
@ -306,11 +305,13 @@ public class PkiRealmTests extends ESTestCase {
|
|||
X500Principal principal = new X500Principal("CN=PKI Client");
|
||||
when(certificate.getSubjectX500Principal()).thenReturn(principal);
|
||||
|
||||
X509AuthenticationToken token = PkiRealm.token(new X509Certificate[]{certificate},
|
||||
Pattern.compile(PkiRealmSettings.DEFAULT_USERNAME_PATTERN), NoOpLogger.INSTANCE);
|
||||
X509AuthenticationToken token = new X509AuthenticationToken(new X509Certificate[]{certificate});
|
||||
assertThat(token, notNullValue());
|
||||
assertThat(token.principal(), is("PKI Client"));
|
||||
assertThat(token.dn(), is("CN=PKI Client"));
|
||||
|
||||
String parsedPrincipal = PkiRealm.getPrincipalFromSubjectDN(Pattern.compile(PkiRealmSettings.DEFAULT_USERNAME_PATTERN), token,
|
||||
NoOpLogger.INSTANCE);
|
||||
assertThat(parsedPrincipal, is("PKI Client"));
|
||||
}
|
||||
|
||||
public void testCertificateWithCnAndOuExtractsProperly() throws Exception {
|
||||
|
@ -318,11 +319,13 @@ public class PkiRealmTests extends ESTestCase {
|
|||
X500Principal principal = new X500Principal("CN=PKI Client, OU=Security");
|
||||
when(certificate.getSubjectX500Principal()).thenReturn(principal);
|
||||
|
||||
X509AuthenticationToken token = PkiRealm.token(new X509Certificate[]{certificate},
|
||||
Pattern.compile(PkiRealmSettings.DEFAULT_USERNAME_PATTERN), NoOpLogger.INSTANCE);
|
||||
X509AuthenticationToken token = new X509AuthenticationToken(new X509Certificate[]{certificate});
|
||||
assertThat(token, notNullValue());
|
||||
assertThat(token.principal(), is("PKI Client"));
|
||||
assertThat(token.dn(), is("CN=PKI Client, OU=Security"));
|
||||
|
||||
String parsedPrincipal = PkiRealm.getPrincipalFromSubjectDN(Pattern.compile(PkiRealmSettings.DEFAULT_USERNAME_PATTERN), token,
|
||||
NoOpLogger.INSTANCE);
|
||||
assertThat(parsedPrincipal, is("PKI Client"));
|
||||
}
|
||||
|
||||
public void testCertificateWithCnInMiddle() throws Exception {
|
||||
|
@ -330,11 +333,13 @@ public class PkiRealmTests extends ESTestCase {
|
|||
X500Principal principal = new X500Principal("EMAILADDRESS=pki@elastic.co, CN=PKI Client, OU=Security");
|
||||
when(certificate.getSubjectX500Principal()).thenReturn(principal);
|
||||
|
||||
X509AuthenticationToken token = PkiRealm.token(new X509Certificate[]{certificate},
|
||||
Pattern.compile(PkiRealmSettings.DEFAULT_USERNAME_PATTERN), NoOpLogger.INSTANCE);
|
||||
X509AuthenticationToken token = new X509AuthenticationToken(new X509Certificate[]{certificate});
|
||||
assertThat(token, notNullValue());
|
||||
assertThat(token.principal(), is("PKI Client"));
|
||||
assertThat(token.dn(), is("EMAILADDRESS=pki@elastic.co, CN=PKI Client, OU=Security"));
|
||||
|
||||
String parsedPrincipal = PkiRealm.getPrincipalFromSubjectDN(Pattern.compile(PkiRealmSettings.DEFAULT_USERNAME_PATTERN), token,
|
||||
NoOpLogger.INSTANCE);
|
||||
assertThat(parsedPrincipal, is("PKI Client"));
|
||||
}
|
||||
|
||||
public void testPKIRealmSettingsPassValidation() throws Exception {
|
||||
|
@ -355,10 +360,12 @@ public class PkiRealmTests extends ESTestCase {
|
|||
|
||||
public void testDelegatedAuthorization() throws Exception {
|
||||
final X509AuthenticationToken token = buildToken();
|
||||
String parsedPrincipal = PkiRealm.getPrincipalFromSubjectDN(Pattern.compile(PkiRealmSettings.DEFAULT_USERNAME_PATTERN), token,
|
||||
NoOpLogger.INSTANCE);
|
||||
|
||||
final MockLookupRealm otherRealm = new MockLookupRealm(new RealmConfig(new RealmConfig.RealmIdentifier("mock", "other_realm"),
|
||||
globalSettings, TestEnvironment.newEnvironment(globalSettings), new ThreadContext(globalSettings)));
|
||||
final User lookupUser = new User(token.principal());
|
||||
final User lookupUser = new User(parsedPrincipal);
|
||||
otherRealm.registerUser(lookupUser);
|
||||
|
||||
final Settings realmSettings = Settings.builder()
|
||||
|
@ -373,7 +380,7 @@ public class PkiRealmTests extends ESTestCase {
|
|||
assertThat(result.getUser(), sameInstance(lookupUser));
|
||||
|
||||
// check that the authorizing realm is consulted even for cached principals
|
||||
final User lookupUser2 = new User(token.principal());
|
||||
final User lookupUser2 = new User(parsedPrincipal);
|
||||
otherRealm.registerUser(lookupUser2);
|
||||
|
||||
result = authenticate(token, pkiRealm);
|
||||
|
|
Loading…
Reference in New Issue