Token API supports the client_credentials grant (#33106)
This change adds support for the client credentials grant type to the token api. The client credentials grant allows for a client to authenticate with the authorization server and obtain a token to access as itself. Per RFC 6749, a refresh token should not be included with the access token and as such a refresh token is not issued when the client credentials grant is used. The addition of the client credentials grant will allow users authenticated with mechanisms such as kerberos or PKI to obtain a token that can be used for subsequent access.
This commit is contained in:
parent
309fb22181
commit
5d9c270608
|
@ -38,16 +38,19 @@ The following parameters can be specified in the body of a POST request and
|
|||
pertain to creating a token:
|
||||
|
||||
`grant_type`::
|
||||
(string) The type of grant. Valid grant types are: `password` and `refresh_token`.
|
||||
(string) The type of grant. Supported grant types are: `password`,
|
||||
`client_credentials` and `refresh_token`.
|
||||
|
||||
`password`::
|
||||
(string) The user's password. If you specify the `password` grant type, this
|
||||
parameter is required.
|
||||
parameter is required. This parameter is not valid with any other supported
|
||||
grant type.
|
||||
|
||||
`refresh_token`::
|
||||
(string) If you specify the `refresh_token` grant type, this parameter is
|
||||
required. It contains the string that was returned when you created the token
|
||||
and enables you to extend its life.
|
||||
and enables you to extend its life. This parameter is not valid with any other
|
||||
supported grant type.
|
||||
|
||||
`scope`::
|
||||
(string) The scope of the token. Currently tokens are only issued for a scope of
|
||||
|
@ -55,11 +58,48 @@ and enables you to extend its life.
|
|||
|
||||
`username`::
|
||||
(string) The username that identifies the user. If you specify the `password`
|
||||
grant type, this parameter is required.
|
||||
grant type, this parameter is required. This parameter is not valid with any
|
||||
other supported grant type.
|
||||
|
||||
==== Examples
|
||||
|
||||
The following example obtains a token for the `test_admin` user:
|
||||
The following example obtains a token using the `client_credentials` grant type,
|
||||
which simply creates a token as the authenticated user:
|
||||
|
||||
[source,js]
|
||||
--------------------------------------------------
|
||||
POST /_xpack/security/oauth2/token
|
||||
{
|
||||
"grant_type" : "client_credentials"
|
||||
}
|
||||
--------------------------------------------------
|
||||
// CONSOLE
|
||||
|
||||
The following example output contains the access token, the amount of time (in
|
||||
seconds) that the token expires in, and the type:
|
||||
|
||||
[source,js]
|
||||
--------------------------------------------------
|
||||
{
|
||||
"access_token" : "dGhpcyBpcyBub3QgYSByZWFsIHRva2VuIGJ1dCBpdCBpcyBvbmx5IHRlc3QgZGF0YS4gZG8gbm90IHRyeSB0byByZWFkIHRva2VuIQ==",
|
||||
"type" : "Bearer",
|
||||
"expires_in" : 1200
|
||||
}
|
||||
--------------------------------------------------
|
||||
// TESTRESPONSE[s/dGhpcyBpcyBub3QgYSByZWFsIHRva2VuIGJ1dCBpdCBpcyBvbmx5IHRlc3QgZGF0YS4gZG8gbm90IHRyeSB0byByZWFkIHRva2VuIQ==/$body.access_token/]
|
||||
|
||||
The token returned by this API can be used by sending a request with a
|
||||
`Authorization` header with a value having the prefix `Bearer ` followed
|
||||
by the value of the `access_token`.
|
||||
|
||||
[source,shell]
|
||||
--------------------------------------------------
|
||||
curl -H "Authorization: Bearer dGhpcyBpcyBub3QgYSByZWFsIHRva2VuIGJ1dCBpdCBpcyBvbmx5IHRlc3QgZGF0YS4gZG8gbm90IHRyeSB0byByZWFkIHRva2VuIQ==" http://localhost:9200/_cluster/health
|
||||
--------------------------------------------------
|
||||
// NOTCONSOLE
|
||||
|
||||
The following example obtains a token for the `test_admin` user using the
|
||||
`password` grant type:
|
||||
|
||||
[source,js]
|
||||
--------------------------------------------------
|
||||
|
@ -73,7 +113,7 @@ POST /_xpack/security/oauth2/token
|
|||
// CONSOLE
|
||||
|
||||
The following example output contains the access token, the amount of time (in
|
||||
seconds) that the token expires in, and the type:
|
||||
seconds) that the token expires in, the type, and the refresh token:
|
||||
|
||||
[source,js]
|
||||
--------------------------------------------------
|
||||
|
@ -87,19 +127,10 @@ seconds) that the token expires in, and the type:
|
|||
// TESTRESPONSE[s/dGhpcyBpcyBub3QgYSByZWFsIHRva2VuIGJ1dCBpdCBpcyBvbmx5IHRlc3QgZGF0YS4gZG8gbm90IHRyeSB0byByZWFkIHRva2VuIQ==/$body.access_token/]
|
||||
// TESTRESPONSE[s/vLBPvmAB6KvwvJZr27cS/$body.refresh_token/]
|
||||
|
||||
The token returned by this API can be used by sending a request with a
|
||||
`Authorization` header with a value having the prefix `Bearer ` followed
|
||||
by the value of the `access_token`.
|
||||
|
||||
[source,shell]
|
||||
--------------------------------------------------
|
||||
curl -H "Authorization: Bearer dGhpcyBpcyBub3QgYSByZWFsIHRva2VuIGJ1dCBpdCBpcyBvbmx5IHRlc3QgZGF0YS4gZG8gbm90IHRyeSB0byByZWFkIHRva2VuIQ==" http://localhost:9200/_cluster/health
|
||||
--------------------------------------------------
|
||||
// NOTCONSOLE
|
||||
|
||||
[[security-api-refresh-token]]
|
||||
To extend the life of an existing token, you can call the API again with the
|
||||
refresh token within 24 hours of the token's creation. For example:
|
||||
To extend the life of an existing token obtained using the `password` grant type,
|
||||
you can call the API again with the refresh token within 24 hours of the token's
|
||||
creation. For example:
|
||||
|
||||
[source,js]
|
||||
--------------------------------------------------
|
||||
|
|
|
@ -19,6 +19,10 @@ import org.elasticsearch.common.CharArrays;
|
|||
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.EnumSet;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.elasticsearch.action.ValidateActions.addValidationError;
|
||||
|
||||
|
@ -29,6 +33,37 @@ import static org.elasticsearch.action.ValidateActions.addValidationError;
|
|||
*/
|
||||
public final class CreateTokenRequest extends ActionRequest {
|
||||
|
||||
public enum GrantType {
|
||||
PASSWORD("password"),
|
||||
REFRESH_TOKEN("refresh_token"),
|
||||
AUTHORIZATION_CODE("authorization_code"),
|
||||
CLIENT_CREDENTIALS("client_credentials");
|
||||
|
||||
private final String value;
|
||||
|
||||
GrantType(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public static GrantType fromString(String grantType) {
|
||||
if (grantType != null) {
|
||||
for (GrantType type : values()) {
|
||||
if (type.getValue().equals(grantType)) {
|
||||
return type;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static final Set<GrantType> SUPPORTED_GRANT_TYPES = Collections.unmodifiableSet(
|
||||
EnumSet.of(GrantType.PASSWORD, GrantType.REFRESH_TOKEN, GrantType.CLIENT_CREDENTIALS));
|
||||
|
||||
private String grantType;
|
||||
private String username;
|
||||
private SecureString password;
|
||||
|
@ -49,33 +84,58 @@ public final class CreateTokenRequest extends ActionRequest {
|
|||
@Override
|
||||
public ActionRequestValidationException validate() {
|
||||
ActionRequestValidationException validationException = null;
|
||||
if ("password".equals(grantType)) {
|
||||
if (Strings.isNullOrEmpty(username)) {
|
||||
validationException = addValidationError("username is missing", validationException);
|
||||
}
|
||||
if (password == null || password.getChars() == null || password.getChars().length == 0) {
|
||||
validationException = addValidationError("password is missing", validationException);
|
||||
}
|
||||
if (refreshToken != null) {
|
||||
validationException =
|
||||
addValidationError("refresh_token is not supported with the password grant_type", validationException);
|
||||
}
|
||||
} else if ("refresh_token".equals(grantType)) {
|
||||
if (username != null) {
|
||||
validationException =
|
||||
addValidationError("username is not supported with the refresh_token grant_type", validationException);
|
||||
}
|
||||
if (password != null) {
|
||||
validationException =
|
||||
addValidationError("password is not supported with the refresh_token grant_type", validationException);
|
||||
}
|
||||
if (refreshToken == null) {
|
||||
validationException = addValidationError("refresh_token is missing", validationException);
|
||||
GrantType type = GrantType.fromString(grantType);
|
||||
if (type != null) {
|
||||
switch (type) {
|
||||
case PASSWORD:
|
||||
if (Strings.isNullOrEmpty(username)) {
|
||||
validationException = addValidationError("username is missing", validationException);
|
||||
}
|
||||
if (password == null || password.getChars() == null || password.getChars().length == 0) {
|
||||
validationException = addValidationError("password is missing", validationException);
|
||||
}
|
||||
if (refreshToken != null) {
|
||||
validationException =
|
||||
addValidationError("refresh_token is not supported with the password grant_type", validationException);
|
||||
}
|
||||
break;
|
||||
case REFRESH_TOKEN:
|
||||
if (username != null) {
|
||||
validationException =
|
||||
addValidationError("username is not supported with the refresh_token grant_type", validationException);
|
||||
}
|
||||
if (password != null) {
|
||||
validationException =
|
||||
addValidationError("password is not supported with the refresh_token grant_type", validationException);
|
||||
}
|
||||
if (refreshToken == null) {
|
||||
validationException = addValidationError("refresh_token is missing", validationException);
|
||||
}
|
||||
break;
|
||||
case CLIENT_CREDENTIALS:
|
||||
if (username != null) {
|
||||
validationException =
|
||||
addValidationError("username is not supported with the client_credentials grant_type", validationException);
|
||||
}
|
||||
if (password != null) {
|
||||
validationException =
|
||||
addValidationError("password is not supported with the client_credentials grant_type", validationException);
|
||||
}
|
||||
if (refreshToken != null) {
|
||||
validationException = addValidationError("refresh_token is not supported with the client_credentials grant_type",
|
||||
validationException);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
validationException = addValidationError("grant_type only supports the values: [" +
|
||||
SUPPORTED_GRANT_TYPES.stream().map(GrantType::getValue).collect(Collectors.joining(", ")) + "]",
|
||||
validationException);
|
||||
}
|
||||
} else {
|
||||
validationException = addValidationError("grant_type only supports the values: [password, refresh_token]", validationException);
|
||||
validationException = addValidationError("grant_type only supports the values: [" +
|
||||
SUPPORTED_GRANT_TYPES.stream().map(GrantType::getValue).collect(Collectors.joining(", ")) + "]",
|
||||
validationException);
|
||||
}
|
||||
|
||||
return validationException;
|
||||
}
|
||||
|
||||
|
@ -126,6 +186,11 @@ public final class CreateTokenRequest extends ActionRequest {
|
|||
@Override
|
||||
public void writeTo(StreamOutput out) throws IOException {
|
||||
super.writeTo(out);
|
||||
if (out.getVersion().before(Version.V_7_0_0_alpha1) && GrantType.CLIENT_CREDENTIALS.getValue().equals(grantType)) {
|
||||
throw new IllegalArgumentException("a request with the client_credentials grant_type cannot be sent to version [" +
|
||||
out.getVersion() + "]");
|
||||
}
|
||||
|
||||
out.writeString(grantType);
|
||||
if (out.getVersion().onOrAfter(Version.V_6_2_0)) {
|
||||
out.writeOptionalString(username);
|
||||
|
|
|
@ -59,8 +59,14 @@ public final class CreateTokenResponse extends ActionResponse implements ToXCont
|
|||
out.writeString(tokenString);
|
||||
out.writeTimeValue(expiresIn);
|
||||
out.writeOptionalString(scope);
|
||||
if (out.getVersion().onOrAfter(Version.V_6_2_0)) {
|
||||
out.writeString(refreshToken);
|
||||
if (out.getVersion().onOrAfter(Version.V_7_0_0_alpha1)) { // TODO change to V_6_5_0 after backport
|
||||
out.writeOptionalString(refreshToken);
|
||||
} else if (out.getVersion().onOrAfter(Version.V_6_2_0)) {
|
||||
if (refreshToken == null) {
|
||||
out.writeString("");
|
||||
} else {
|
||||
out.writeString(refreshToken);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -70,7 +76,9 @@ public final class CreateTokenResponse extends ActionResponse implements ToXCont
|
|||
tokenString = in.readString();
|
||||
expiresIn = in.readTimeValue();
|
||||
scope = in.readOptionalString();
|
||||
if (in.getVersion().onOrAfter(Version.V_6_2_0)) {
|
||||
if (in.getVersion().onOrAfter(Version.V_7_0_0_alpha1)) { // TODO change to V_6_5_0 after backport
|
||||
refreshToken = in.readOptionalString();
|
||||
} else if (in.getVersion().onOrAfter(Version.V_6_2_0)) {
|
||||
refreshToken = in.readString();
|
||||
}
|
||||
}
|
||||
|
@ -90,4 +98,20 @@ public final class CreateTokenResponse extends ActionResponse implements ToXCont
|
|||
}
|
||||
return builder.endObject();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
CreateTokenResponse that = (CreateTokenResponse) o;
|
||||
return Objects.equals(tokenString, that.tokenString) &&
|
||||
Objects.equals(expiresIn, that.expiresIn) &&
|
||||
Objects.equals(scope, that.scope) &&
|
||||
Objects.equals(refreshToken, that.refreshToken);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(tokenString, expiresIn, scope, refreshToken);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
package org.elasticsearch.xpack.security.action.token;
|
||||
package org.elasticsearch.xpack.core.security.action.token;
|
||||
|
||||
import org.elasticsearch.action.ActionRequestValidationException;
|
||||
import org.elasticsearch.common.settings.SecureString;
|
||||
|
@ -20,7 +20,7 @@ public class CreateTokenRequestTests extends ESTestCase {
|
|||
ActionRequestValidationException ve = request.validate();
|
||||
assertNotNull(ve);
|
||||
assertEquals(1, ve.validationErrors().size());
|
||||
assertThat(ve.validationErrors().get(0), containsString("[password, refresh_token]"));
|
||||
assertThat(ve.validationErrors().get(0), containsString("[password, refresh_token, client_credentials]"));
|
||||
assertThat(ve.validationErrors().get(0), containsString("grant_type"));
|
||||
|
||||
request.setGrantType("password");
|
||||
|
@ -72,5 +72,19 @@ public class CreateTokenRequestTests extends ESTestCase {
|
|||
assertNotNull(ve);
|
||||
assertEquals(1, ve.validationErrors().size());
|
||||
assertThat(ve.validationErrors(), hasItem("refresh_token is missing"));
|
||||
|
||||
request.setGrantType("client_credentials");
|
||||
ve = request.validate();
|
||||
assertNull(ve);
|
||||
|
||||
request.setUsername(randomAlphaOfLengthBetween(1, 32));
|
||||
request.setPassword(new SecureString(randomAlphaOfLengthBetween(1, 32).toCharArray()));
|
||||
request.setRefreshToken(randomAlphaOfLengthBetween(1, 32));
|
||||
ve = request.validate();
|
||||
assertNotNull(ve);
|
||||
assertEquals(3, ve.validationErrors().size());
|
||||
assertThat(ve.validationErrors(), hasItem(containsString("username is not supported")));
|
||||
assertThat(ve.validationErrors(), hasItem(containsString("password is not supported")));
|
||||
assertThat(ve.validationErrors(), hasItem(containsString("refresh_token is not supported")));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,92 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
package org.elasticsearch.xpack.core.security.action.token;
|
||||
|
||||
import org.elasticsearch.Version;
|
||||
import org.elasticsearch.common.io.stream.BytesStreamOutput;
|
||||
import org.elasticsearch.common.io.stream.StreamInput;
|
||||
import org.elasticsearch.common.unit.TimeValue;
|
||||
import org.elasticsearch.test.ESTestCase;
|
||||
import org.elasticsearch.test.VersionUtils;
|
||||
|
||||
public class CreateTokenResponseTests extends ESTestCase {
|
||||
|
||||
public void testSerialization() throws Exception {
|
||||
CreateTokenResponse response = new CreateTokenResponse(randomAlphaOfLengthBetween(1, 10), TimeValue.timeValueMinutes(20L),
|
||||
randomBoolean() ? null : "FULL", randomAlphaOfLengthBetween(1, 10));
|
||||
try (BytesStreamOutput output = new BytesStreamOutput()) {
|
||||
response.writeTo(output);
|
||||
try (StreamInput input = output.bytes().streamInput()) {
|
||||
CreateTokenResponse serialized = new CreateTokenResponse();
|
||||
serialized.readFrom(input);
|
||||
assertEquals(response, serialized);
|
||||
}
|
||||
}
|
||||
|
||||
response = new CreateTokenResponse(randomAlphaOfLengthBetween(1, 10), TimeValue.timeValueMinutes(20L),
|
||||
randomBoolean() ? null : "FULL", null);
|
||||
try (BytesStreamOutput output = new BytesStreamOutput()) {
|
||||
response.writeTo(output);
|
||||
try (StreamInput input = output.bytes().streamInput()) {
|
||||
CreateTokenResponse serialized = new CreateTokenResponse();
|
||||
serialized.readFrom(input);
|
||||
assertEquals(response, serialized);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void testSerializationToPre62Version() throws Exception {
|
||||
CreateTokenResponse response = new CreateTokenResponse(randomAlphaOfLengthBetween(1, 10), TimeValue.timeValueMinutes(20L),
|
||||
randomBoolean() ? null : "FULL", randomBoolean() ? null : randomAlphaOfLengthBetween(1, 10));
|
||||
final Version version = VersionUtils.randomVersionBetween(random(), Version.V_6_0_0, Version.V_6_1_4);
|
||||
try (BytesStreamOutput output = new BytesStreamOutput()) {
|
||||
output.setVersion(version);
|
||||
response.writeTo(output);
|
||||
try (StreamInput input = output.bytes().streamInput()) {
|
||||
input.setVersion(version);
|
||||
CreateTokenResponse serialized = new CreateTokenResponse();
|
||||
serialized.readFrom(input);
|
||||
assertNull(serialized.getRefreshToken());
|
||||
assertEquals(response.getTokenString(), serialized.getTokenString());
|
||||
assertEquals(response.getExpiresIn(), serialized.getExpiresIn());
|
||||
assertEquals(response.getScope(), serialized.getScope());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void testSerializationToPost62Pre65Version() throws Exception {
|
||||
CreateTokenResponse response = new CreateTokenResponse(randomAlphaOfLengthBetween(1, 10), TimeValue.timeValueMinutes(20L),
|
||||
randomBoolean() ? null : "FULL", randomAlphaOfLengthBetween(1, 10));
|
||||
final Version version = VersionUtils.randomVersionBetween(random(), Version.V_6_2_0, Version.V_6_4_0);
|
||||
try (BytesStreamOutput output = new BytesStreamOutput()) {
|
||||
output.setVersion(version);
|
||||
response.writeTo(output);
|
||||
try (StreamInput input = output.bytes().streamInput()) {
|
||||
input.setVersion(version);
|
||||
CreateTokenResponse serialized = new CreateTokenResponse();
|
||||
serialized.readFrom(input);
|
||||
assertEquals(response, serialized);
|
||||
}
|
||||
}
|
||||
|
||||
// no refresh token
|
||||
response = new CreateTokenResponse(randomAlphaOfLengthBetween(1, 10), TimeValue.timeValueMinutes(20L),
|
||||
randomBoolean() ? null : "FULL", null);
|
||||
try (BytesStreamOutput output = new BytesStreamOutput()) {
|
||||
output.setVersion(version);
|
||||
response.writeTo(output);
|
||||
try (StreamInput input = output.bytes().streamInput()) {
|
||||
input.setVersion(version);
|
||||
CreateTokenResponse serialized = new CreateTokenResponse();
|
||||
serialized.readFrom(input);
|
||||
assertEquals("", serialized.getRefreshToken());
|
||||
assertEquals(response.getTokenString(), serialized.getTokenString());
|
||||
assertEquals(response.getExpiresIn(), serialized.getExpiresIn());
|
||||
assertEquals(response.getScope(), serialized.getScope());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -61,7 +61,7 @@ public final class TransportSamlAuthenticateAction extends HandledTransportActio
|
|||
final TimeValue expiresIn = tokenService.getExpirationDelay();
|
||||
listener.onResponse(
|
||||
new SamlAuthenticateResponse(authentication.getUser().principal(), tokenString, tuple.v2(), expiresIn));
|
||||
}, listener::onFailure), tokenMeta);
|
||||
}, listener::onFailure), tokenMeta, true);
|
||||
}, e -> {
|
||||
logger.debug(() -> new ParameterizedMessage("SamlToken [{}] could not be authenticated", saml), e);
|
||||
listener.onFailure(e);
|
||||
|
|
|
@ -22,6 +22,7 @@ import org.elasticsearch.xpack.security.authc.AuthenticationService;
|
|||
import org.elasticsearch.xpack.security.authc.TokenService;
|
||||
import org.elasticsearch.xpack.core.security.authc.support.UsernamePasswordToken;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
|
||||
/**
|
||||
|
@ -48,29 +49,52 @@ public final class TransportCreateTokenAction extends HandledTransportAction<Cre
|
|||
|
||||
@Override
|
||||
protected void doExecute(Task task, CreateTokenRequest request, ActionListener<CreateTokenResponse> listener) {
|
||||
CreateTokenRequest.GrantType type = CreateTokenRequest.GrantType.fromString(request.getGrantType());
|
||||
assert type != null : "type should have been validated in the action";
|
||||
switch (type) {
|
||||
case PASSWORD:
|
||||
authenticateAndCreateToken(request, listener);
|
||||
break;
|
||||
case CLIENT_CREDENTIALS:
|
||||
Authentication authentication = Authentication.getAuthentication(threadPool.getThreadContext());
|
||||
createToken(request, authentication, authentication, false, listener);
|
||||
break;
|
||||
default:
|
||||
listener.onFailure(new IllegalStateException("grant_type [" + request.getGrantType() +
|
||||
"] is not supported by the create token action"));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void authenticateAndCreateToken(CreateTokenRequest request, ActionListener<CreateTokenResponse> listener) {
|
||||
Authentication originatingAuthentication = Authentication.getAuthentication(threadPool.getThreadContext());
|
||||
try (ThreadContext.StoredContext ignore = threadPool.getThreadContext().stashContext()) {
|
||||
final UsernamePasswordToken authToken = new UsernamePasswordToken(request.getUsername(), request.getPassword());
|
||||
authenticationService.authenticate(CreateTokenAction.NAME, request, authToken,
|
||||
ActionListener.wrap(authentication -> {
|
||||
request.getPassword().close();
|
||||
tokenService.createUserToken(authentication, originatingAuthentication, ActionListener.wrap(tuple -> {
|
||||
final String tokenStr = tokenService.getUserTokenString(tuple.v1());
|
||||
final String scope = getResponseScopeValue(request.getScope());
|
||||
ActionListener.wrap(authentication -> {
|
||||
request.getPassword().close();
|
||||
createToken(request, authentication, originatingAuthentication, true, listener);
|
||||
}, e -> {
|
||||
// clear the request password
|
||||
request.getPassword().close();
|
||||
listener.onFailure(e);
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
final CreateTokenResponse response =
|
||||
new CreateTokenResponse(tokenStr, tokenService.getExpirationDelay(), scope, tuple.v2());
|
||||
listener.onResponse(response);
|
||||
}, e -> {
|
||||
// clear the request password
|
||||
request.getPassword().close();
|
||||
listener.onFailure(e);
|
||||
}), Collections.emptyMap());
|
||||
}, e -> {
|
||||
// clear the request password
|
||||
request.getPassword().close();
|
||||
listener.onFailure(e);
|
||||
}));
|
||||
private void createToken(CreateTokenRequest request, Authentication authentication, Authentication originatingAuth,
|
||||
boolean includeRefreshToken, ActionListener<CreateTokenResponse> listener) {
|
||||
try {
|
||||
tokenService.createUserToken(authentication, originatingAuth, ActionListener.wrap(tuple -> {
|
||||
final String tokenStr = tokenService.getUserTokenString(tuple.v1());
|
||||
final String scope = getResponseScopeValue(request.getScope());
|
||||
|
||||
final CreateTokenResponse response =
|
||||
new CreateTokenResponse(tokenStr, tokenService.getExpirationDelay(), scope, tuple.v2());
|
||||
listener.onResponse(response);
|
||||
}, listener::onFailure), Collections.emptyMap(), includeRefreshToken);
|
||||
} catch (IOException e) {
|
||||
listener.onFailure(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -212,7 +212,8 @@ public final class TokenService extends AbstractComponent {
|
|||
* The created token will be stored in the security index.
|
||||
*/
|
||||
public void createUserToken(Authentication authentication, Authentication originatingClientAuth,
|
||||
ActionListener<Tuple<UserToken, String>> listener, Map<String, Object> metadata) throws IOException {
|
||||
ActionListener<Tuple<UserToken, String>> listener, Map<String, Object> metadata,
|
||||
boolean includeRefreshToken) throws IOException {
|
||||
ensureEnabled();
|
||||
if (authentication == null) {
|
||||
listener.onFailure(new IllegalArgumentException("authentication must be provided"));
|
||||
|
@ -226,13 +227,14 @@ public final class TokenService extends AbstractComponent {
|
|||
new Authentication(authentication.getUser(), authentication.getAuthenticatedBy(), authentication.getLookedUpBy(),
|
||||
version);
|
||||
final UserToken userToken = new UserToken(version, matchingVersionAuth, expiration, metadata);
|
||||
final String refreshToken = UUIDs.randomBase64UUID();
|
||||
final String refreshToken = includeRefreshToken ? UUIDs.randomBase64UUID() : null;
|
||||
|
||||
try (XContentBuilder builder = XContentFactory.jsonBuilder()) {
|
||||
builder.startObject();
|
||||
builder.field("doc_type", "token");
|
||||
builder.field("creation_time", created.toEpochMilli());
|
||||
builder.startObject("refresh_token")
|
||||
if (includeRefreshToken) {
|
||||
builder.startObject("refresh_token")
|
||||
.field("token", refreshToken)
|
||||
.field("invalidated", false)
|
||||
.field("refreshed", false)
|
||||
|
@ -242,6 +244,7 @@ public final class TokenService extends AbstractComponent {
|
|||
.field("realm", originatingClientAuth.getAuthenticatedBy().getName())
|
||||
.endObject()
|
||||
.endObject();
|
||||
}
|
||||
builder.startObject("access_token")
|
||||
.field("invalidated", false)
|
||||
.field("user_token", userToken)
|
||||
|
@ -734,7 +737,7 @@ public final class TokenService extends AbstractComponent {
|
|||
.request();
|
||||
executeAsyncWithOrigin(client.threadPool().getThreadContext(), SECURITY_ORIGIN, updateRequest,
|
||||
ActionListener.<UpdateResponse>wrap(
|
||||
updateResponse -> createUserToken(authentication, userAuth, listener, metadata),
|
||||
updateResponse -> createUserToken(authentication, userAuth, listener, metadata, true),
|
||||
e -> {
|
||||
Throwable cause = ExceptionsHelper.unwrapCause(e);
|
||||
if (cause instanceof VersionConflictEngineException ||
|
||||
|
|
|
@ -316,7 +316,7 @@ public class TransportSamlInvalidateSessionActionTests extends SamlTestCase {
|
|||
new RealmRef("native", NativeRealmSettings.TYPE, "node01"), null);
|
||||
final Map<String, Object> metadata = samlRealm.createTokenMetadata(nameId, session);
|
||||
final PlainActionFuture<Tuple<UserToken, String>> future = new PlainActionFuture<>();
|
||||
tokenService.createUserToken(authentication, authentication, future, metadata);
|
||||
tokenService.createUserToken(authentication, authentication, future, metadata, true);
|
||||
return future.actionGet();
|
||||
}
|
||||
|
||||
|
|
|
@ -222,7 +222,7 @@ public class TransportSamlLogoutActionTests extends SamlTestCase {
|
|||
new SamlNameId(NameID.TRANSIENT, nameId, null, null, null), session);
|
||||
|
||||
final PlainActionFuture<Tuple<UserToken, String>> future = new PlainActionFuture<>();
|
||||
tokenService.createUserToken(authentication, authentication, future, tokenMetaData);
|
||||
tokenService.createUserToken(authentication, authentication, future, tokenMetaData, true);
|
||||
final UserToken userToken = future.actionGet().v1();
|
||||
mockGetTokenFromId(userToken, client);
|
||||
final String tokenString = tokenService.getUserTokenString(userToken);
|
||||
|
|
|
@ -0,0 +1,195 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
package org.elasticsearch.xpack.security.action.token;
|
||||
|
||||
import org.elasticsearch.action.ActionListener;
|
||||
import org.elasticsearch.action.get.GetAction;
|
||||
import org.elasticsearch.action.get.GetRequestBuilder;
|
||||
import org.elasticsearch.action.get.GetResponse;
|
||||
import org.elasticsearch.action.get.MultiGetAction;
|
||||
import org.elasticsearch.action.get.MultiGetItemResponse;
|
||||
import org.elasticsearch.action.get.MultiGetRequest;
|
||||
import org.elasticsearch.action.get.MultiGetRequestBuilder;
|
||||
import org.elasticsearch.action.get.MultiGetResponse;
|
||||
import org.elasticsearch.action.index.IndexAction;
|
||||
import org.elasticsearch.action.index.IndexRequest;
|
||||
import org.elasticsearch.action.index.IndexRequestBuilder;
|
||||
import org.elasticsearch.action.index.IndexResponse;
|
||||
import org.elasticsearch.action.support.ActionFilters;
|
||||
import org.elasticsearch.action.support.PlainActionFuture;
|
||||
import org.elasticsearch.action.update.UpdateAction;
|
||||
import org.elasticsearch.action.update.UpdateRequestBuilder;
|
||||
import org.elasticsearch.client.Client;
|
||||
import org.elasticsearch.cluster.service.ClusterService;
|
||||
import org.elasticsearch.common.settings.SecureString;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.node.Node;
|
||||
import org.elasticsearch.protocol.xpack.security.User;
|
||||
import org.elasticsearch.test.ClusterServiceUtils;
|
||||
import org.elasticsearch.test.ESTestCase;
|
||||
import org.elasticsearch.threadpool.TestThreadPool;
|
||||
import org.elasticsearch.threadpool.ThreadPool;
|
||||
import org.elasticsearch.transport.TransportService;
|
||||
import org.elasticsearch.xpack.core.XPackSettings;
|
||||
import org.elasticsearch.xpack.core.security.action.token.CreateTokenAction;
|
||||
import org.elasticsearch.xpack.core.security.action.token.CreateTokenRequest;
|
||||
import org.elasticsearch.xpack.core.security.action.token.CreateTokenResponse;
|
||||
import org.elasticsearch.xpack.core.security.authc.Authentication;
|
||||
import org.elasticsearch.xpack.core.security.authc.support.UsernamePasswordToken;
|
||||
import org.elasticsearch.xpack.security.authc.AuthenticationService;
|
||||
import org.elasticsearch.xpack.security.authc.TokenService;
|
||||
import org.elasticsearch.xpack.security.support.SecurityIndexManager;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
|
||||
import java.time.Clock;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import static org.mockito.Matchers.any;
|
||||
import static org.mockito.Matchers.anyString;
|
||||
import static org.mockito.Matchers.eq;
|
||||
import static org.mockito.Mockito.doAnswer;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
public class TransportCreateTokenActionTests extends ESTestCase {
|
||||
|
||||
private static final Settings SETTINGS = Settings.builder().put(Node.NODE_NAME_SETTING.getKey(), "TokenServiceTests")
|
||||
.put(XPackSettings.TOKEN_SERVICE_ENABLED_SETTING.getKey(), true).build();
|
||||
|
||||
private ThreadPool threadPool;
|
||||
private Client client;
|
||||
private SecurityIndexManager securityIndex;
|
||||
private ClusterService clusterService;
|
||||
private AtomicReference<IndexRequest> idxReqReference;
|
||||
private AuthenticationService authenticationService;
|
||||
|
||||
@Before
|
||||
public void setupClient() {
|
||||
threadPool = new TestThreadPool(getTestName());
|
||||
client = mock(Client.class);
|
||||
idxReqReference = new AtomicReference<>();
|
||||
authenticationService = mock(AuthenticationService.class);
|
||||
when(client.threadPool()).thenReturn(threadPool);
|
||||
when(client.settings()).thenReturn(SETTINGS);
|
||||
doAnswer(invocationOnMock -> {
|
||||
GetRequestBuilder builder = new GetRequestBuilder(client, GetAction.INSTANCE);
|
||||
builder.setIndex((String) invocationOnMock.getArguments()[0])
|
||||
.setType((String) invocationOnMock.getArguments()[1])
|
||||
.setId((String) invocationOnMock.getArguments()[2]);
|
||||
return builder;
|
||||
}).when(client).prepareGet(anyString(), anyString(), anyString());
|
||||
when(client.prepareMultiGet()).thenReturn(new MultiGetRequestBuilder(client, MultiGetAction.INSTANCE));
|
||||
doAnswer(invocationOnMock -> {
|
||||
ActionListener<MultiGetResponse> listener = (ActionListener<MultiGetResponse>) invocationOnMock.getArguments()[1];
|
||||
MultiGetResponse response = mock(MultiGetResponse.class);
|
||||
MultiGetItemResponse[] responses = new MultiGetItemResponse[2];
|
||||
when(response.getResponses()).thenReturn(responses);
|
||||
|
||||
GetResponse oldGetResponse = mock(GetResponse.class);
|
||||
when(oldGetResponse.isExists()).thenReturn(false);
|
||||
responses[0] = new MultiGetItemResponse(oldGetResponse, null);
|
||||
|
||||
GetResponse getResponse = mock(GetResponse.class);
|
||||
responses[1] = new MultiGetItemResponse(getResponse, null);
|
||||
when(getResponse.isExists()).thenReturn(false);
|
||||
listener.onResponse(response);
|
||||
return Void.TYPE;
|
||||
}).when(client).multiGet(any(MultiGetRequest.class), any(ActionListener.class));
|
||||
when(client.prepareIndex(any(String.class), any(String.class), any(String.class)))
|
||||
.thenReturn(new IndexRequestBuilder(client, IndexAction.INSTANCE));
|
||||
when(client.prepareUpdate(any(String.class), any(String.class), any(String.class)))
|
||||
.thenReturn(new UpdateRequestBuilder(client, UpdateAction.INSTANCE));
|
||||
doAnswer(invocationOnMock -> {
|
||||
idxReqReference.set((IndexRequest) invocationOnMock.getArguments()[1]);
|
||||
ActionListener<IndexResponse> responseActionListener = (ActionListener<IndexResponse>) invocationOnMock.getArguments()[2];
|
||||
responseActionListener.onResponse(new IndexResponse());
|
||||
return null;
|
||||
}).when(client).execute(eq(IndexAction.INSTANCE), any(IndexRequest.class), any(ActionListener.class));
|
||||
|
||||
// setup lifecycle service
|
||||
securityIndex = mock(SecurityIndexManager.class);
|
||||
doAnswer(invocationOnMock -> {
|
||||
Runnable runnable = (Runnable) invocationOnMock.getArguments()[1];
|
||||
runnable.run();
|
||||
return null;
|
||||
}).when(securityIndex).prepareIndexIfNeededThenExecute(any(Consumer.class), any(Runnable.class));
|
||||
|
||||
doAnswer(invocationOnMock -> {
|
||||
UsernamePasswordToken token = (UsernamePasswordToken) invocationOnMock.getArguments()[2];
|
||||
User user = new User(token.principal());
|
||||
Authentication authentication = new Authentication(user, new Authentication.RealmRef("fake", "mock", "n1"), null);
|
||||
authentication.writeToContext(threadPool.getThreadContext());
|
||||
ActionListener<Authentication> authListener = (ActionListener<Authentication>) invocationOnMock.getArguments()[3];
|
||||
authListener.onResponse(authentication);
|
||||
return Void.TYPE;
|
||||
}).when(authenticationService).authenticate(eq(CreateTokenAction.NAME), any(CreateTokenRequest.class),
|
||||
any(UsernamePasswordToken.class), any(ActionListener.class));
|
||||
|
||||
this.clusterService = ClusterServiceUtils.createClusterService(threadPool);
|
||||
}
|
||||
|
||||
@After
|
||||
public void stopThreadPool() throws Exception {
|
||||
if (threadPool != null) {
|
||||
terminate(threadPool);
|
||||
}
|
||||
}
|
||||
|
||||
public void testClientCredentialsCreatesWithoutRefreshToken() throws Exception {
|
||||
final TokenService tokenService = new TokenService(SETTINGS, Clock.systemUTC(), client, securityIndex, clusterService);
|
||||
Authentication authentication = new Authentication(new User("joe"), new Authentication.RealmRef("realm", "type", "node"), null);
|
||||
authentication.writeToContext(threadPool.getThreadContext());
|
||||
|
||||
final TransportCreateTokenAction action = new TransportCreateTokenAction(SETTINGS, threadPool,
|
||||
mock(TransportService.class), new ActionFilters(Collections.emptySet()), tokenService,
|
||||
authenticationService);
|
||||
final CreateTokenRequest createTokenRequest = new CreateTokenRequest();
|
||||
createTokenRequest.setGrantType("client_credentials");
|
||||
|
||||
PlainActionFuture<CreateTokenResponse> tokenResponseFuture = new PlainActionFuture<>();
|
||||
action.doExecute(null, createTokenRequest, tokenResponseFuture);
|
||||
CreateTokenResponse createTokenResponse = tokenResponseFuture.get();
|
||||
assertNull(createTokenResponse.getRefreshToken());
|
||||
assertNotNull(createTokenResponse.getTokenString());
|
||||
|
||||
assertNotNull(idxReqReference.get());
|
||||
Map<String, Object> sourceMap = idxReqReference.get().sourceAsMap();
|
||||
assertNotNull(sourceMap);
|
||||
assertNotNull(sourceMap.get("access_token"));
|
||||
assertNull(sourceMap.get("refresh_token"));
|
||||
}
|
||||
|
||||
public void testPasswordGrantTypeCreatesWithRefreshToken() throws Exception {
|
||||
final TokenService tokenService = new TokenService(SETTINGS, Clock.systemUTC(), client, securityIndex, clusterService);
|
||||
Authentication authentication = new Authentication(new User("joe"), new Authentication.RealmRef("realm", "type", "node"), null);
|
||||
authentication.writeToContext(threadPool.getThreadContext());
|
||||
|
||||
final TransportCreateTokenAction action = new TransportCreateTokenAction(SETTINGS, threadPool,
|
||||
mock(TransportService.class), new ActionFilters(Collections.emptySet()), tokenService,
|
||||
authenticationService);
|
||||
final CreateTokenRequest createTokenRequest = new CreateTokenRequest();
|
||||
createTokenRequest.setGrantType("password");
|
||||
createTokenRequest.setUsername("user");
|
||||
createTokenRequest.setPassword(new SecureString("password".toCharArray()));
|
||||
|
||||
PlainActionFuture<CreateTokenResponse> tokenResponseFuture = new PlainActionFuture<>();
|
||||
action.doExecute(null, createTokenRequest, tokenResponseFuture);
|
||||
CreateTokenResponse createTokenResponse = tokenResponseFuture.get();
|
||||
assertNotNull(createTokenResponse.getRefreshToken());
|
||||
assertNotNull(createTokenResponse.getTokenString());
|
||||
|
||||
assertNotNull(idxReqReference.get());
|
||||
Map<String, Object> sourceMap = idxReqReference.get().sourceAsMap();
|
||||
assertNotNull(sourceMap);
|
||||
assertNotNull(sourceMap.get("access_token"));
|
||||
assertNotNull(sourceMap.get("refresh_token"));
|
||||
}
|
||||
}
|
|
@ -896,7 +896,7 @@ public class AuthenticationServiceTests extends ESTestCase {
|
|||
PlainActionFuture<Tuple<UserToken, String>> tokenFuture = new PlainActionFuture<>();
|
||||
try (ThreadContext.StoredContext ctx = threadContext.stashContext()) {
|
||||
Authentication originatingAuth = new Authentication(new User("creator"), new RealmRef("test", "test", "test"), null);
|
||||
tokenService.createUserToken(expected, originatingAuth, tokenFuture, Collections.emptyMap());
|
||||
tokenService.createUserToken(expected, originatingAuth, tokenFuture, Collections.emptyMap(), true);
|
||||
}
|
||||
String token = tokenService.getUserTokenString(tokenFuture.get().v1());
|
||||
mockGetTokenFromId(tokenFuture.get().v1(), client);
|
||||
|
@ -975,7 +975,7 @@ public class AuthenticationServiceTests extends ESTestCase {
|
|||
PlainActionFuture<Tuple<UserToken, String>> tokenFuture = new PlainActionFuture<>();
|
||||
try (ThreadContext.StoredContext ctx = threadContext.stashContext()) {
|
||||
Authentication originatingAuth = new Authentication(new User("creator"), new RealmRef("test", "test", "test"), null);
|
||||
tokenService.createUserToken(expected, originatingAuth, tokenFuture, Collections.emptyMap());
|
||||
tokenService.createUserToken(expected, originatingAuth, tokenFuture, Collections.emptyMap(), true);
|
||||
}
|
||||
String token = tokenService.getUserTokenString(tokenFuture.get().v1());
|
||||
mockGetTokenFromId(tokenFuture.get().v1(), client);
|
||||
|
|
|
@ -341,6 +341,39 @@ public class TokenAuthIntegTests extends SecurityIntegTestCase {
|
|||
assertEquals(SecuritySettingsSource.TEST_USER_NAME, response.user().principal());
|
||||
}
|
||||
|
||||
public void testClientCredentialsGrant() throws Exception {
|
||||
Client client = client().filterWithHeader(Collections.singletonMap("Authorization",
|
||||
UsernamePasswordToken.basicAuthHeaderValue(SecuritySettingsSource.TEST_SUPERUSER,
|
||||
SecuritySettingsSourceField.TEST_PASSWORD_SECURE_STRING)));
|
||||
SecurityClient securityClient = new SecurityClient(client);
|
||||
CreateTokenResponse createTokenResponse = securityClient.prepareCreateToken()
|
||||
.setGrantType("client_credentials")
|
||||
.get();
|
||||
assertNull(createTokenResponse.getRefreshToken());
|
||||
|
||||
AuthenticateRequest request = new AuthenticateRequest();
|
||||
request.username(SecuritySettingsSource.TEST_SUPERUSER);
|
||||
PlainActionFuture<AuthenticateResponse> authFuture = new PlainActionFuture<>();
|
||||
client.filterWithHeader(Collections.singletonMap("Authorization", "Bearer " + createTokenResponse.getTokenString()))
|
||||
.execute(AuthenticateAction.INSTANCE, request, authFuture);
|
||||
AuthenticateResponse response = authFuture.get();
|
||||
assertEquals(SecuritySettingsSource.TEST_SUPERUSER, response.user().principal());
|
||||
|
||||
// invalidate
|
||||
PlainActionFuture<InvalidateTokenResponse> invalidateResponseFuture = new PlainActionFuture<>();
|
||||
InvalidateTokenRequest invalidateTokenRequest =
|
||||
new InvalidateTokenRequest(createTokenResponse.getTokenString(), InvalidateTokenRequest.Type.ACCESS_TOKEN);
|
||||
securityClient.invalidateToken(invalidateTokenRequest, invalidateResponseFuture);
|
||||
assertTrue(invalidateResponseFuture.get().isCreated());
|
||||
|
||||
ElasticsearchSecurityException e = expectThrows(ElasticsearchSecurityException.class, () -> {
|
||||
PlainActionFuture<AuthenticateResponse> responseFuture = new PlainActionFuture<>();
|
||||
client.filterWithHeader(Collections.singletonMap("Authorization", "Bearer " + createTokenResponse.getTokenString()))
|
||||
.execute(AuthenticateAction.INSTANCE, request, responseFuture);
|
||||
responseFuture.actionGet();
|
||||
});
|
||||
}
|
||||
|
||||
@Before
|
||||
public void waitForSecurityIndexWritable() throws Exception {
|
||||
assertSecurityIndexActive();
|
||||
|
|
|
@ -157,7 +157,7 @@ public class TokenServiceTests extends ESTestCase {
|
|||
TokenService tokenService = new TokenService(tokenServiceEnabledSettings, systemUTC(), client, securityIndex, clusterService);
|
||||
Authentication authentication = new Authentication(new User("joe", "admin"), new RealmRef("native_realm", "native", "node1"), null);
|
||||
PlainActionFuture<Tuple<UserToken, String>> tokenFuture = new PlainActionFuture<>();
|
||||
tokenService.createUserToken(authentication, authentication, tokenFuture, Collections.emptyMap());
|
||||
tokenService.createUserToken(authentication, authentication, tokenFuture, Collections.emptyMap(), true);
|
||||
final UserToken token = tokenFuture.get().v1();
|
||||
assertNotNull(token);
|
||||
mockGetTokenFromId(token);
|
||||
|
@ -203,7 +203,7 @@ public class TokenServiceTests extends ESTestCase {
|
|||
TokenService tokenService = new TokenService(tokenServiceEnabledSettings, systemUTC(), client, securityIndex, clusterService);
|
||||
Authentication authentication = new Authentication(new User("joe", "admin"), new RealmRef("native_realm", "native", "node1"), null);
|
||||
PlainActionFuture<Tuple<UserToken, String>> tokenFuture = new PlainActionFuture<>();
|
||||
tokenService.createUserToken(authentication, authentication, tokenFuture, Collections.emptyMap());
|
||||
tokenService.createUserToken(authentication, authentication, tokenFuture, Collections.emptyMap(), true);
|
||||
final UserToken token = tokenFuture.get().v1();
|
||||
assertNotNull(token);
|
||||
mockGetTokenFromId(token);
|
||||
|
@ -227,7 +227,7 @@ public class TokenServiceTests extends ESTestCase {
|
|||
}
|
||||
|
||||
PlainActionFuture<Tuple<UserToken, String>> newTokenFuture = new PlainActionFuture<>();
|
||||
tokenService.createUserToken(authentication, authentication, newTokenFuture, Collections.emptyMap());
|
||||
tokenService.createUserToken(authentication, authentication, newTokenFuture, Collections.emptyMap(), true);
|
||||
final UserToken newToken = newTokenFuture.get().v1();
|
||||
assertNotNull(newToken);
|
||||
assertNotEquals(tokenService.getUserTokenString(newToken), tokenService.getUserTokenString(token));
|
||||
|
@ -262,7 +262,7 @@ public class TokenServiceTests extends ESTestCase {
|
|||
otherTokenService.refreshMetaData(tokenService.getTokenMetaData());
|
||||
Authentication authentication = new Authentication(new User("joe", "admin"), new RealmRef("native_realm", "native", "node1"), null);
|
||||
PlainActionFuture<Tuple<UserToken, String>> tokenFuture = new PlainActionFuture<>();
|
||||
tokenService.createUserToken(authentication, authentication, tokenFuture, Collections.emptyMap());
|
||||
tokenService.createUserToken(authentication, authentication, tokenFuture, Collections.emptyMap(), true);
|
||||
final UserToken token = tokenFuture.get().v1();
|
||||
assertNotNull(token);
|
||||
mockGetTokenFromId(token);
|
||||
|
@ -292,7 +292,7 @@ public class TokenServiceTests extends ESTestCase {
|
|||
TokenService tokenService = new TokenService(tokenServiceEnabledSettings, systemUTC(), client, securityIndex, clusterService);
|
||||
Authentication authentication = new Authentication(new User("joe", "admin"), new RealmRef("native_realm", "native", "node1"), null);
|
||||
PlainActionFuture<Tuple<UserToken, String>> tokenFuture = new PlainActionFuture<>();
|
||||
tokenService.createUserToken(authentication, authentication, tokenFuture, Collections.emptyMap());
|
||||
tokenService.createUserToken(authentication, authentication, tokenFuture, Collections.emptyMap(), true);
|
||||
final UserToken token = tokenFuture.get().v1();
|
||||
assertNotNull(token);
|
||||
mockGetTokenFromId(token);
|
||||
|
@ -322,7 +322,7 @@ public class TokenServiceTests extends ESTestCase {
|
|||
}
|
||||
|
||||
PlainActionFuture<Tuple<UserToken, String>> newTokenFuture = new PlainActionFuture<>();
|
||||
tokenService.createUserToken(authentication, authentication, newTokenFuture, Collections.emptyMap());
|
||||
tokenService.createUserToken(authentication, authentication, newTokenFuture, Collections.emptyMap(), true);
|
||||
final UserToken newToken = newTokenFuture.get().v1();
|
||||
assertNotNull(newToken);
|
||||
assertNotEquals(tokenService.getUserTokenString(newToken), tokenService.getUserTokenString(token));
|
||||
|
@ -353,7 +353,7 @@ public class TokenServiceTests extends ESTestCase {
|
|||
TokenService tokenService = new TokenService(tokenServiceEnabledSettings, systemUTC(), client, securityIndex, clusterService);
|
||||
Authentication authentication = new Authentication(new User("joe", "admin"), new RealmRef("native_realm", "native", "node1"), null);
|
||||
PlainActionFuture<Tuple<UserToken, String>> tokenFuture = new PlainActionFuture<>();
|
||||
tokenService.createUserToken(authentication, authentication, tokenFuture, Collections.emptyMap());
|
||||
tokenService.createUserToken(authentication, authentication, tokenFuture, Collections.emptyMap(), true);
|
||||
final UserToken token = tokenFuture.get().v1();
|
||||
assertNotNull(token);
|
||||
mockGetTokenFromId(token);
|
||||
|
@ -383,7 +383,7 @@ public class TokenServiceTests extends ESTestCase {
|
|||
Authentication authentication = new Authentication(new User("joe", "admin"), new RealmRef("native_realm", "native", "node1"), null);
|
||||
|
||||
PlainActionFuture<Tuple<UserToken, String>> tokenFuture = new PlainActionFuture<>();
|
||||
tokenService.createUserToken(authentication, authentication, tokenFuture, Collections.emptyMap());
|
||||
tokenService.createUserToken(authentication, authentication, tokenFuture, Collections.emptyMap(), true);
|
||||
UserToken token = tokenFuture.get().v1();
|
||||
assertThat(tokenService.getUserTokenString(token), notNullValue());
|
||||
|
||||
|
@ -397,7 +397,7 @@ public class TokenServiceTests extends ESTestCase {
|
|||
new TokenService(tokenServiceEnabledSettings, systemUTC(), client, securityIndex, clusterService);
|
||||
Authentication authentication = new Authentication(new User("joe", "admin"), new RealmRef("native_realm", "native", "node1"), null);
|
||||
PlainActionFuture<Tuple<UserToken, String>> tokenFuture = new PlainActionFuture<>();
|
||||
tokenService.createUserToken(authentication, authentication, tokenFuture, Collections.emptyMap());
|
||||
tokenService.createUserToken(authentication, authentication, tokenFuture, Collections.emptyMap(), true);
|
||||
final UserToken token = tokenFuture.get().v1();
|
||||
assertNotNull(token);
|
||||
doAnswer(invocationOnMock -> {
|
||||
|
@ -451,7 +451,7 @@ public class TokenServiceTests extends ESTestCase {
|
|||
TokenService tokenService = new TokenService(tokenServiceEnabledSettings, clock, client, securityIndex, clusterService);
|
||||
Authentication authentication = new Authentication(new User("joe", "admin"), new RealmRef("native_realm", "native", "node1"), null);
|
||||
PlainActionFuture<Tuple<UserToken, String>> tokenFuture = new PlainActionFuture<>();
|
||||
tokenService.createUserToken(authentication, authentication, tokenFuture, Collections.emptyMap());
|
||||
tokenService.createUserToken(authentication, authentication, tokenFuture, Collections.emptyMap(), true);
|
||||
final UserToken token = tokenFuture.get().v1();
|
||||
mockGetTokenFromId(token);
|
||||
|
||||
|
@ -501,7 +501,8 @@ public class TokenServiceTests extends ESTestCase {
|
|||
.put(XPackSettings.TOKEN_SERVICE_ENABLED_SETTING.getKey(), false)
|
||||
.build(),
|
||||
Clock.systemUTC(), client, securityIndex, clusterService);
|
||||
IllegalStateException e = expectThrows(IllegalStateException.class, () -> tokenService.createUserToken(null, null, null, null));
|
||||
IllegalStateException e = expectThrows(IllegalStateException.class,
|
||||
() -> tokenService.createUserToken(null, null, null, null, true));
|
||||
assertEquals("tokens are not enabled", e.getMessage());
|
||||
|
||||
PlainActionFuture<UserToken> future = new PlainActionFuture<>();
|
||||
|
@ -559,7 +560,7 @@ public class TokenServiceTests extends ESTestCase {
|
|||
new TokenService(tokenServiceEnabledSettings, systemUTC(), client, securityIndex, clusterService);
|
||||
Authentication authentication = new Authentication(new User("joe", "admin"), new RealmRef("native_realm", "native", "node1"), null);
|
||||
PlainActionFuture<Tuple<UserToken, String>> tokenFuture = new PlainActionFuture<>();
|
||||
tokenService.createUserToken(authentication, authentication, tokenFuture, Collections.emptyMap());
|
||||
tokenService.createUserToken(authentication, authentication, tokenFuture, Collections.emptyMap(), true);
|
||||
final UserToken token = tokenFuture.get().v1();
|
||||
assertNotNull(token);
|
||||
mockGetTokenFromId(token);
|
||||
|
|
|
@ -158,6 +158,7 @@ subprojects {
|
|||
} else {
|
||||
String systemKeyFile = version.before('6.3.0') ? 'x-pack/system_key' : 'system_key'
|
||||
extraConfigFile systemKeyFile, "${mainProject.projectDir}/src/test/resources/system_key"
|
||||
keystoreSetting 'xpack.security.authc.token.passphrase', 'token passphrase'
|
||||
}
|
||||
setting 'xpack.watcher.encrypt_sensitive_data', 'true'
|
||||
}
|
||||
|
@ -199,6 +200,9 @@ subprojects {
|
|||
setting 'xpack.watcher.encrypt_sensitive_data', 'true'
|
||||
keystoreFile 'xpack.watcher.encryption_key', "${mainProject.projectDir}/src/test/resources/system_key"
|
||||
}
|
||||
if (version.before('6.0.0')) {
|
||||
keystoreSetting 'xpack.security.authc.token.passphrase', 'token passphrase'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue