Skip to content

Use consistent view of realms for authentication #38815

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Feb 14, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,15 @@ it to the action without an associated user (not via REST or transport - this is
*/
final String securityAction = actionMapper.action(action, request);
authcService.authenticate(securityAction, request, SystemUser.INSTANCE,
ActionListener.wrap((authc) -> authorizeRequest(authc, securityAction, request, listener), listener::onFailure));
ActionListener.wrap((authc) -> {
if (authc != null) {
authorizeRequest(authc, securityAction, request, listener);
} else if (licenseState.isAuthAllowed() == false) {
listener.onResponse(null);
} else {
listener.onFailure(new IllegalStateException("no authentication present but auth is allowed"));
}
}, listener::onFailure));
}

private <Request extends ActionRequest> void authorizeRequest(Authentication authentication, String securityAction, Request request,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ protected void doExecute(Task task, SamlAuthenticateRequest request, ActionListe
listener.onFailure(new IllegalStateException("Cannot find AuthenticationResult on thread context"));
return;
}
assert authentication != null : "authentication should never be null at this point";
final Map<String, Object> tokenMeta = (Map<String, Object>) result.getMetadata().get(SamlRealm.CONTEXT_TOKEN_DATA);
tokenService.createUserToken(authentication, originatingAuthentication,
ActionListener.wrap(tuple -> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,11 @@ private void authenticateAndCreateToken(CreateTokenRequest request, ActionListen
authenticationService.authenticate(CreateTokenAction.NAME, request, authToken,
ActionListener.wrap(authentication -> {
request.getPassword().close();
createToken(request, authentication, originatingAuthentication, true, listener);
if (authentication != null) {
createToken(request, authentication, originatingAuthentication, true, listener);
} else {
listener.onFailure(new UnsupportedOperationException("cannot create token if authentication is not allowed"));
}
}, e -> {
// clear the request password
request.getPassword().close();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -196,8 +196,9 @@ class Authenticator {

private final AuditableRequest request;
private final User fallbackUser;

private final List<Realm> defaultOrderedRealmList;
private final ActionListener<Authentication> listener;

private RealmRef authenticatedBy = null;
private RealmRef lookedupBy = null;
private AuthenticationToken authenticationToken = null;
Expand All @@ -215,6 +216,7 @@ class Authenticator {
private Authenticator(AuditableRequest auditableRequest, User fallbackUser, ActionListener<Authentication> listener) {
this.request = auditableRequest;
this.fallbackUser = fallbackUser;
this.defaultOrderedRealmList = realms.asList();
this.listener = listener;
}

Expand All @@ -233,27 +235,33 @@ private Authenticator(AuditableRequest auditableRequest, User fallbackUser, Acti
* </ol>
*/
private void authenticateAsync() {
lookForExistingAuthentication((authentication) -> {
if (authentication != null) {
listener.onResponse(authentication);
} else {
tokenService.getAndValidateToken(threadContext, ActionListener.wrap(userToken -> {
if (userToken != null) {
writeAuthToContext(userToken.getAuthentication());
} else {
checkForApiKey();
}
}, e -> {
if (e instanceof ElasticsearchSecurityException &&
if (defaultOrderedRealmList.isEmpty()) {
// this happens when the license state changes between the call to authenticate and the actual invocation
// to get the realm list
listener.onResponse(null);
} else {
lookForExistingAuthentication((authentication) -> {
if (authentication != null) {
listener.onResponse(authentication);
} else {
tokenService.getAndValidateToken(threadContext, ActionListener.wrap(userToken -> {
if (userToken != null) {
writeAuthToContext(userToken.getAuthentication());
} else {
checkForApiKey();
}
}, e -> {
if (e instanceof ElasticsearchSecurityException &&
tokenService.isExpiredTokenException((ElasticsearchSecurityException) e) == false) {
// intentionally ignore the returned exception; we call this primarily
// for the auditing as we already have a purpose built exception
request.tamperedRequest();
}
listener.onFailure(e);
}));
}
});
// intentionally ignore the returned exception; we call this primarily
// for the auditing as we already have a purpose built exception
request.tamperedRequest();
}
listener.onFailure(e);
}));
}
});
}
}

private void checkForApiKey() {
Expand Down Expand Up @@ -320,7 +328,7 @@ void extractToken(Consumer<AuthenticationToken> consumer) {
if (authenticationToken != null) {
action = () -> consumer.accept(authenticationToken);
} else {
for (Realm realm : realms) {
for (Realm realm : defaultOrderedRealmList) {
final AuthenticationToken token = realm.token(threadContext);
if (token != null) {
action = () -> consumer.accept(token);
Expand Down Expand Up @@ -388,6 +396,7 @@ private void consumeToken(AuthenticationToken token) {
userListener.onResponse(null);
}
};

final IteratingActionListener<User, Realm> authenticatingListener =
new IteratingActionListener<>(ContextPreservingActionListener.wrapPreservingContext(ActionListener.wrap(
(user) -> consumeUser(user, messages),
Expand All @@ -402,24 +411,24 @@ private void consumeToken(AuthenticationToken token) {
}

private List<Realm> getRealmList(String principal) {
final List<Realm> defaultOrderedRealms = realms.asList();
final List<Realm> orderedRealmList = this.defaultOrderedRealmList;
if (lastSuccessfulAuthCache != null) {
final Realm lastSuccess = lastSuccessfulAuthCache.get(principal);
if (lastSuccess != null) {
final int index = defaultOrderedRealms.indexOf(lastSuccess);
final int index = orderedRealmList.indexOf(lastSuccess);
if (index > 0) {
final List<Realm> smartOrder = new ArrayList<>(defaultOrderedRealms.size());
final List<Realm> smartOrder = new ArrayList<>(orderedRealmList.size());
smartOrder.add(lastSuccess);
for (int i = 1; i < defaultOrderedRealms.size(); i++) {
for (int i = 1; i < orderedRealmList.size(); i++) {
if (i != index) {
smartOrder.add(defaultOrderedRealms.get(i));
smartOrder.add(orderedRealmList.get(i));
}
}
return Collections.unmodifiableList(smartOrder);
}
}
}
return defaultOrderedRealms;
return orderedRealmList;
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -188,12 +188,12 @@ protected Map<String, ServerTransportFilter> initializeProfileFilters(Destructiv
case "client":
profileFilters.put(entry.getKey(), new ServerTransportFilter.ClientProfile(authcService, authzService,
threadPool.getThreadContext(), extractClientCert, destructiveOperations, reservedRealmEnabled,
securityContext));
securityContext, licenseState));
break;
case "node":
profileFilters.put(entry.getKey(), new ServerTransportFilter.NodeProfile(authcService, authzService,
threadPool.getThreadContext(), extractClientCert, destructiveOperations, reservedRealmEnabled,
securityContext));
securityContext, licenseState));
break;
default:
throw new IllegalStateException("unknown profile type: " + type);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import org.elasticsearch.action.admin.indices.open.OpenIndexAction;
import org.elasticsearch.action.support.DestructiveOperations;
import org.elasticsearch.common.util.concurrent.ThreadContext;
import org.elasticsearch.license.XPackLicenseState;
import org.elasticsearch.transport.TaskTransportChannel;
import org.elasticsearch.transport.TcpChannel;
import org.elasticsearch.transport.TcpTransportChannel;
Expand Down Expand Up @@ -66,17 +67,19 @@ class NodeProfile implements ServerTransportFilter {
private final DestructiveOperations destructiveOperations;
private final boolean reservedRealmEnabled;
private final SecurityContext securityContext;
private final XPackLicenseState licenseState;

NodeProfile(AuthenticationService authcService, AuthorizationService authzService,
ThreadContext threadContext, boolean extractClientCert, DestructiveOperations destructiveOperations,
boolean reservedRealmEnabled, SecurityContext securityContext) {
boolean reservedRealmEnabled, SecurityContext securityContext, XPackLicenseState licenseState) {
this.authcService = authcService;
this.authzService = authzService;
this.threadContext = threadContext;
this.extractClientCert = extractClientCert;
this.destructiveOperations = destructiveOperations;
this.reservedRealmEnabled = reservedRealmEnabled;
this.securityContext = securityContext;
this.licenseState = licenseState;
}

@Override
Expand Down Expand Up @@ -116,14 +119,20 @@ requests from all the nodes are attached with a user (either a serialize

final Version version = transportChannel.getVersion();
authcService.authenticate(securityAction, request, (User)null, ActionListener.wrap((authentication) -> {
if (securityAction.equals(TransportService.HANDSHAKE_ACTION_NAME) &&
SystemUser.is(authentication.getUser()) == false) {
securityContext.executeAsUser(SystemUser.INSTANCE, (ctx) -> {
final Authentication replaced = Authentication.getAuthentication(threadContext);
authzService.authorize(replaced, securityAction, request, listener);
}, version);
if (authentication != null) {
if (securityAction.equals(TransportService.HANDSHAKE_ACTION_NAME) &&
SystemUser.is(authentication.getUser()) == false) {
securityContext.executeAsUser(SystemUser.INSTANCE, (ctx) -> {
final Authentication replaced = Authentication.getAuthentication(threadContext);
authzService.authorize(replaced, securityAction, request, listener);
}, version);
} else {
authzService.authorize(authentication, securityAction, request, listener);
}
} else if (licenseState.isAuthAllowed() == false) {
listener.onResponse(null);
} else {
authzService.authorize(authentication, securityAction, request, listener);
listener.onFailure(new IllegalStateException("no authentication present but auth is allowed"));
}
}, listener::onFailure));
}
Expand All @@ -139,9 +148,9 @@ class ClientProfile extends NodeProfile {

ClientProfile(AuthenticationService authcService, AuthorizationService authzService,
ThreadContext threadContext, boolean extractClientCert, DestructiveOperations destructiveOperations,
boolean reservedRealmEnabled, SecurityContext securityContext) {
boolean reservedRealmEnabled, SecurityContext securityContext, XPackLicenseState licenseState) {
super(authcService, authzService, threadContext, extractClientCert, destructiveOperations, reservedRealmEnabled,
securityContext);
securityContext, licenseState);
}

@Override
Expand Down
Loading