HLRC: Add security Create Token API (#34791)

This adds the Create Token API (POST /_xpack/security/oauth2/token)
to the High Level Rest Client.

Relates: #29827
This commit is contained in:
Tim Vernum 2018-10-29 17:17:56 +11:00 committed by GitHub
parent bb5b59004e
commit 9c27b407f0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 662 additions and 5 deletions

View File

@ -88,6 +88,7 @@ integTestCluster {
systemProperty 'es.scripting.update.ctx_in_params', 'false'
setting 'xpack.license.self_generated.type', 'trial'
setting 'xpack.security.enabled', 'true'
setting 'xpack.security.authc.token.enabled', 'true'
// Truststore settings are not used since TLS is not enabled. Included for testing the get certificates API
setting 'xpack.ssl.certificate_authorities', 'testnode.crt'
setting 'xpack.security.transport.ssl.truststore.path', 'testnode.jks'

View File

@ -20,12 +20,15 @@
package org.elasticsearch.client;
import org.elasticsearch.action.ActionListener;
import org.elasticsearch.client.security.ChangePasswordRequest;
import org.elasticsearch.client.security.ClearRolesCacheRequest;
import org.elasticsearch.client.security.ClearRolesCacheResponse;
import org.elasticsearch.client.security.CreateTokenRequest;
import org.elasticsearch.client.security.CreateTokenResponse;
import org.elasticsearch.client.security.DeleteRoleMappingRequest;
import org.elasticsearch.client.security.DeleteRoleMappingResponse;
import org.elasticsearch.client.security.DeleteRoleRequest;
import org.elasticsearch.client.security.DeleteRoleResponse;
import org.elasticsearch.client.security.PutRoleMappingRequest;
import org.elasticsearch.client.security.PutRoleMappingResponse;
import org.elasticsearch.client.security.DisableUserRequest;
import org.elasticsearch.client.security.EmptyResponse;
import org.elasticsearch.client.security.EnableUserRequest;
@ -33,11 +36,10 @@ import org.elasticsearch.client.security.GetRoleMappingsRequest;
import org.elasticsearch.client.security.GetRoleMappingsResponse;
import org.elasticsearch.client.security.GetSslCertificatesRequest;
import org.elasticsearch.client.security.GetSslCertificatesResponse;
import org.elasticsearch.client.security.PutRoleMappingRequest;
import org.elasticsearch.client.security.PutRoleMappingResponse;
import org.elasticsearch.client.security.PutUserRequest;
import org.elasticsearch.client.security.PutUserResponse;
import org.elasticsearch.client.security.ChangePasswordRequest;
import org.elasticsearch.client.security.DeleteRoleMappingRequest;
import org.elasticsearch.client.security.DeleteRoleMappingResponse;
import java.io.IOException;
@ -350,4 +352,32 @@ public final class SecurityClient {
DeleteRoleResponse::fromXContent, listener, singleton(404));
}
/**
* Creates an OAuth2 token.
* See <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/security-api-get-token.html">
* the docs</a> for more.
*
* @param request the request for the token
* @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
* @return the response from the create token call
* @throws IOException in case there is a problem sending the request or parsing back the response
*/
public CreateTokenResponse createToken(CreateTokenRequest request, RequestOptions options) throws IOException {
return restHighLevelClient.performRequestAndParseEntity(request, SecurityRequestConverters::createToken, options,
CreateTokenResponse::fromXContent, emptySet());
}
/**
* Asynchronously creates an OAuth2 token.
* See <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/security-api-get-token.html">
* the docs</a> for more.
*
* @param request the request for the token
* @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
* @param listener the listener to be notified upon request completion
*/
public void createTokenAsync(CreateTokenRequest request, RequestOptions options, ActionListener<CreateTokenResponse> listener) {
restHighLevelClient.performRequestAsyncAndParseEntity(request, SecurityRequestConverters::createToken, options,
CreateTokenResponse::fromXContent, listener, emptySet());
}
}

View File

@ -24,6 +24,7 @@ import org.apache.http.client.methods.HttpDelete;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpPut;
import org.elasticsearch.client.security.ClearRolesCacheRequest;
import org.elasticsearch.client.security.CreateTokenRequest;
import org.elasticsearch.client.security.DeleteRoleMappingRequest;
import org.elasticsearch.client.security.DeleteRoleRequest;
import org.elasticsearch.client.security.PutRoleMappingRequest;
@ -140,4 +141,10 @@ final class SecurityRequestConverters {
params.withRefreshPolicy(deleteRoleRequest.getRefreshPolicy());
return request;
}
static Request createToken(CreateTokenRequest createTokenRequest) throws IOException {
Request request = new Request(HttpPost.METHOD_NAME, "/_xpack/security/oauth2/token");
request.setEntity(createEntity(createTokenRequest, REQUEST_BODY_CONTENT_TYPE));
return request;
}
}

View File

@ -0,0 +1,152 @@
/*
* Licensed to Elasticsearch under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.elasticsearch.client.security;
import org.elasticsearch.client.Validatable;
import org.elasticsearch.common.CharArrays;
import org.elasticsearch.common.Nullable;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.xcontent.ToXContentObject;
import org.elasticsearch.common.xcontent.XContentBuilder;
import java.io.IOException;
import java.util.Arrays;
import java.util.Objects;
/**
* Request to create a new OAuth2 token from the Elasticsearch cluster.
*/
public final class CreateTokenRequest implements Validatable, ToXContentObject {
private final String grantType;
private final String scope;
private final String username;
private final char[] password;
private final String refreshToken;
/**
* General purpose constructor. This constructor is typically not useful, and one of the following factory methods should be used
* instead:
* <ul>
* <li>{@link #passwordGrant(String, char[])}</li>
* <li>{@link #refreshTokenGrant(String)}</li>
* <li>{@link #clientCredentialsGrant()}</li>
* </ul>
*/
public CreateTokenRequest(String grantType, @Nullable String scope, @Nullable String username, @Nullable char[] password,
@Nullable String refreshToken) {
if (Strings.isNullOrEmpty(grantType)) {
throw new IllegalArgumentException("grant_type is required");
}
this.grantType = grantType;
this.username = username;
this.password = password;
this.scope = scope;
this.refreshToken = refreshToken;
}
public static CreateTokenRequest passwordGrant(String username, char[] password) {
if (Strings.isNullOrEmpty(username)) {
throw new IllegalArgumentException("username is required");
}
if (password == null || password.length == 0) {
throw new IllegalArgumentException("password is required");
}
return new CreateTokenRequest("password", null, username, password, null);
}
public static CreateTokenRequest refreshTokenGrant(String refreshToken) {
if (Strings.isNullOrEmpty(refreshToken)) {
throw new IllegalArgumentException("refresh_token is required");
}
return new CreateTokenRequest("refresh_token", null, null, null, refreshToken);
}
public static CreateTokenRequest clientCredentialsGrant() {
return new CreateTokenRequest("client_credentials", null, null, null, null);
}
public String getGrantType() {
return grantType;
}
public String getScope() {
return scope;
}
public String getUsername() {
return username;
}
public char[] getPassword() {
return password;
}
public String getRefreshToken() {
return refreshToken;
}
@Override
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
builder.startObject()
.field("grant_type", grantType);
if (scope != null) {
builder.field("scope", scope);
}
if (username != null) {
builder.field("username", username);
}
if (password != null) {
byte[] passwordBytes = CharArrays.toUtf8Bytes(password);
try {
builder.field("password").utf8Value(passwordBytes, 0, passwordBytes.length);
} finally {
Arrays.fill(passwordBytes, (byte) 0);
}
}
if (refreshToken != null) {
builder.field("refresh_token", refreshToken);
}
return builder.endObject();
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
final CreateTokenRequest that = (CreateTokenRequest) o;
return Objects.equals(grantType, that.grantType) &&
Objects.equals(scope, that.scope) &&
Objects.equals(username, that.username) &&
Arrays.equals(password, that.password) &&
Objects.equals(refreshToken, that.refreshToken);
}
@Override
public int hashCode() {
int result = Objects.hash(grantType, scope, username, refreshToken);
result = 31 * result + Arrays.hashCode(password);
return result;
}
}

View File

@ -0,0 +1,110 @@
/*
* Licensed to Elasticsearch under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.elasticsearch.client.security;
import org.elasticsearch.common.ParseField;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.common.xcontent.ConstructingObjectParser;
import org.elasticsearch.common.xcontent.XContentParser;
import java.io.IOException;
import java.util.Objects;
import static org.elasticsearch.common.xcontent.ConstructingObjectParser.constructorArg;
import static org.elasticsearch.common.xcontent.ConstructingObjectParser.optionalConstructorArg;
/**
* Response when creating a new OAuth2 token in the Elasticsearch cluster. Contains an access token, the token's expiry, and an optional
* refresh token.
*/
public final class CreateTokenResponse {
private final String accessToken;
private final String type;
private final TimeValue expiresIn;
private final String scope;
private final String refreshToken;
public CreateTokenResponse(String accessToken, String type, TimeValue expiresIn, String scope, String refreshToken) {
this.accessToken = accessToken;
this.type = type;
this.expiresIn = expiresIn;
this.scope = scope;
this.refreshToken = refreshToken;
}
public String getAccessToken() {
return accessToken;
}
public String getType() {
return type;
}
public TimeValue getExpiresIn() {
return expiresIn;
}
public String getScope() {
return scope;
}
public String getRefreshToken() {
return refreshToken;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
final CreateTokenResponse that = (CreateTokenResponse) o;
return Objects.equals(accessToken, that.accessToken) &&
Objects.equals(type, that.type) &&
Objects.equals(expiresIn, that.expiresIn) &&
Objects.equals(scope, that.scope) &&
Objects.equals(refreshToken, that.refreshToken);
}
@Override
public int hashCode() {
return Objects.hash(accessToken, type, expiresIn, scope, refreshToken);
}
private static final ConstructingObjectParser<CreateTokenResponse, Void> PARSER = new ConstructingObjectParser<>(
"create_token_response", true, args -> new CreateTokenResponse(
(String) args[0], (String) args[1], TimeValue.timeValueSeconds((Long) args[2]), (String) args[3], (String) args[4]));
static {
PARSER.declareString(constructorArg(), new ParseField("access_token"));
PARSER.declareString(constructorArg(), new ParseField("type"));
PARSER.declareLong(constructorArg(), new ParseField("expires_in"));
PARSER.declareStringOrNull(optionalConstructorArg(), new ParseField("scope"));
PARSER.declareStringOrNull(optionalConstructorArg(), new ParseField("refresh_token"));
}
public static CreateTokenResponse fromXContent(XContentParser parser) throws IOException {
return PARSER.parse(parser, null);
}
}

View File

@ -23,6 +23,7 @@ import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpDelete;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpPut;
import org.elasticsearch.client.security.CreateTokenRequest;
import org.elasticsearch.client.security.DeleteRoleMappingRequest;
import org.elasticsearch.client.security.DeleteRoleRequest;
import org.elasticsearch.client.security.DisableUserRequest;
@ -211,4 +212,34 @@ public class SecurityRequestConvertersTests extends ESTestCase {
assertEquals(expectedParams, request.getParameters());
assertNull(request.getEntity());
}
public void testCreateTokenWithPasswordGrant() throws Exception {
final String username = randomAlphaOfLengthBetween(1, 12);
final String password = randomAlphaOfLengthBetween(8, 12);
CreateTokenRequest createTokenRequest = CreateTokenRequest.passwordGrant(username, password.toCharArray());
Request request = SecurityRequestConverters.createToken(createTokenRequest);
assertEquals(HttpPost.METHOD_NAME, request.getMethod());
assertEquals("/_xpack/security/oauth2/token", request.getEndpoint());
assertEquals(0, request.getParameters().size());
assertToXContentBody(createTokenRequest, request.getEntity());
}
public void testCreateTokenWithRefreshTokenGrant() throws Exception {
final String refreshToken = randomAlphaOfLengthBetween(8, 24);
CreateTokenRequest createTokenRequest = CreateTokenRequest.refreshTokenGrant(refreshToken);
Request request = SecurityRequestConverters.createToken(createTokenRequest);
assertEquals(HttpPost.METHOD_NAME, request.getMethod());
assertEquals("/_xpack/security/oauth2/token", request.getEndpoint());
assertEquals(0, request.getParameters().size());
assertToXContentBody(createTokenRequest, request.getEntity());
}
public void testCreateTokenWithClientCredentialsGrant() throws Exception {
CreateTokenRequest createTokenRequest = CreateTokenRequest.clientCredentialsGrant();
Request request = SecurityRequestConverters.createToken(createTokenRequest);
assertEquals(HttpPost.METHOD_NAME, request.getMethod());
assertEquals("/_xpack/security/oauth2/token", request.getEndpoint());
assertEquals(0, request.getParameters().size());
assertToXContentBody(createTokenRequest, request.getEntity());
}
}

View File

@ -24,6 +24,7 @@ import org.apache.http.entity.ContentType;
import org.apache.http.nio.entity.NStringEntity;
import org.elasticsearch.action.ActionListener;
import org.elasticsearch.action.LatchedActionListener;
import org.elasticsearch.action.support.PlainActionFuture;
import org.elasticsearch.client.ESRestHighLevelClientTestCase;
import org.elasticsearch.client.Request;
import org.elasticsearch.client.RequestOptions;
@ -31,6 +32,8 @@ import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.client.security.ChangePasswordRequest;
import org.elasticsearch.client.security.ClearRolesCacheRequest;
import org.elasticsearch.client.security.ClearRolesCacheResponse;
import org.elasticsearch.client.security.CreateTokenRequest;
import org.elasticsearch.client.security.CreateTokenResponse;
import org.elasticsearch.client.security.DeleteRoleMappingRequest;
import org.elasticsearch.client.security.DeleteRoleMappingResponse;
import org.elasticsearch.client.security.DeleteRoleRequest;
@ -668,4 +671,79 @@ public class SecurityDocumentationIT extends ESRestHighLevelClientTestCase {
client().performRequest(addRoleRequest);
}
public void testCreateToken() throws Exception {
RestHighLevelClient client = highLevelClient();
{
// Setup user
PutUserRequest putUserRequest = new PutUserRequest("token_user", "password".toCharArray(),
Collections.singletonList("kibana_user"), null, null, true, null, RefreshPolicy.IMMEDIATE);
PutUserResponse putUserResponse = client.security().putUser(putUserRequest, RequestOptions.DEFAULT);
assertTrue(putUserResponse.isCreated());
}
{
// tag::create-token-password-request
final char[] password = new char[]{'p', 'a', 's', 's', 'w', 'o', 'r', 'd'};
CreateTokenRequest createTokenRequest = CreateTokenRequest.passwordGrant("token_user", password);
// end::create-token-password-request
// tag::create-token-execute
CreateTokenResponse createTokenResponse = client.security().createToken(createTokenRequest, RequestOptions.DEFAULT);
// end::create-token-execute
// tag::create-token-response
String accessToken = createTokenResponse.getAccessToken(); // <1>
String refreshToken = createTokenResponse.getRefreshToken(); // <2>
// end::create-token-response
assertNotNull(accessToken);
assertNotNull(refreshToken);
assertNotNull(createTokenResponse.getExpiresIn());
// tag::create-token-refresh-request
createTokenRequest = CreateTokenRequest.refreshTokenGrant(refreshToken);
// end::create-token-refresh-request
CreateTokenResponse refreshResponse = client.security().createToken(createTokenRequest, RequestOptions.DEFAULT);
assertNotNull(refreshResponse.getAccessToken());
assertNotNull(refreshResponse.getRefreshToken());
}
{
// tag::create-token-client-credentials-request
CreateTokenRequest createTokenRequest = CreateTokenRequest.clientCredentialsGrant();
// end::create-token-client-credentials-request
ActionListener<CreateTokenResponse> listener;
//tag::create-token-execute-listener
listener = new ActionListener<CreateTokenResponse>() {
@Override
public void onResponse(CreateTokenResponse createTokenResponse) {
// <1>
}
@Override
public void onFailure(Exception e) {
// <2>
}
};
//end::create-token-execute-listener
// Avoid unused variable warning
assertNotNull(listener);
// Replace the empty listener by a blocking listener in test
final PlainActionFuture<CreateTokenResponse> future = new PlainActionFuture<>();
listener = future;
//tag::create-token-execute-async
client.security().createTokenAsync(createTokenRequest, RequestOptions.DEFAULT, listener); // <1>
//end::create-token-execute-async
assertNotNull(future.get(30, TimeUnit.SECONDS));
assertNotNull(future.get().getAccessToken());
// "client-credentials" grants aren't refreshable
assertNull(future.get().getRefreshToken());
}
}
}

View File

@ -0,0 +1,98 @@
/*
* Licensed to Elasticsearch under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.elasticsearch.client.security;
import org.elasticsearch.common.Strings;
import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.test.EqualsHashCodeTestUtils;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.nullValue;
public class CreateTokenRequestTests extends ESTestCase {
public void testCreateTokenFromPassword() {
final CreateTokenRequest request = CreateTokenRequest.passwordGrant("jsmith", "top secret password".toCharArray());
assertThat(request.getGrantType(), equalTo("password"));
assertThat(request.getUsername(), equalTo("jsmith"));
assertThat(new String(request.getPassword()), equalTo("top secret password"));
assertThat(request.getScope(), nullValue());
assertThat(request.getRefreshToken(), nullValue());
assertThat(Strings.toString(request), equalTo("{" +
"\"grant_type\":\"password\"," +
"\"username\":\"jsmith\"," +
"\"password\":\"top secret password\"" +
"}"
));
}
public void testCreateTokenFromRefreshToken() {
final CreateTokenRequest request = CreateTokenRequest.refreshTokenGrant("9a7f41cf-9918-4d1f-bfaa-ad3f8f9f02b9");
assertThat(request.getGrantType(), equalTo("refresh_token"));
assertThat(request.getRefreshToken(), equalTo("9a7f41cf-9918-4d1f-bfaa-ad3f8f9f02b9"));
assertThat(request.getScope(), nullValue());
assertThat(request.getUsername(), nullValue());
assertThat(request.getPassword(), nullValue());
assertThat(Strings.toString(request), equalTo("{" +
"\"grant_type\":\"refresh_token\"," +
"\"refresh_token\":\"9a7f41cf-9918-4d1f-bfaa-ad3f8f9f02b9\"" +
"}"
));
}
public void testCreateTokenFromClientCredentials() {
final CreateTokenRequest request = CreateTokenRequest.clientCredentialsGrant();
assertThat(request.getGrantType(), equalTo("client_credentials"));
assertThat(request.getScope(), nullValue());
assertThat(request.getUsername(), nullValue());
assertThat(request.getPassword(), nullValue());
assertThat(request.getRefreshToken(), nullValue());
assertThat(Strings.toString(request), equalTo("{\"grant_type\":\"client_credentials\"}"));
}
public void testEqualsAndHashCode() {
final String grantType = randomAlphaOfLength(8);
final String scope = randomBoolean() ? null : randomAlphaOfLength(6);
final String username = randomBoolean() ? null : randomAlphaOfLengthBetween(4, 10);
final char[] password = randomBoolean() ? null : randomAlphaOfLengthBetween(8, 12).toCharArray();
final String refreshToken = randomBoolean() ? null : randomAlphaOfLengthBetween(12, 24);
final CreateTokenRequest request = new CreateTokenRequest(grantType, scope, username, password, refreshToken);
EqualsHashCodeTestUtils.checkEqualsAndHashCode(request,
r -> new CreateTokenRequest(r.getGrantType(), r.getScope(), r.getUsername(), r.getPassword(), r.getRefreshToken()),
this::mutate);
}
private CreateTokenRequest mutate(CreateTokenRequest req) {
switch (randomIntBetween(1, 5)) {
case 1:
return new CreateTokenRequest("g", req.getScope(), req.getUsername(), req.getPassword(), req.getRefreshToken());
case 2:
return new CreateTokenRequest(req.getGrantType(), "s", req.getUsername(), req.getPassword(), req.getRefreshToken());
case 3:
return new CreateTokenRequest(req.getGrantType(), req.getScope(), "u", req.getPassword(), req.getRefreshToken());
case 4:
final char[] password = {'p'};
return new CreateTokenRequest(req.getGrantType(), req.getScope(), req.getUsername(), password, req.getRefreshToken());
case 5:
return new CreateTokenRequest(req.getGrantType(), req.getScope(), req.getUsername(), req.getPassword(), "r");
}
throw new IllegalStateException("Bad random number");
}
}

View File

@ -0,0 +1,63 @@
/*
* Licensed to Elasticsearch under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.elasticsearch.client.security;
import org.elasticsearch.common.bytes.BytesReference;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentFactory;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.test.ESTestCase;
import java.io.IOException;
import static org.hamcrest.Matchers.equalTo;
public class CreateTokenResponseTests extends ESTestCase {
public void testFromXContent() throws IOException {
final String accessToken = randomAlphaOfLengthBetween(12, 24);
final TimeValue expiresIn = TimeValue.timeValueSeconds(randomIntBetween(30, 10_000));
final String refreshToken = randomBoolean() ? null : randomAlphaOfLengthBetween(12, 24);
final String scope = randomBoolean() ? null : randomAlphaOfLength(4);
final String type = randomAlphaOfLength(6);
final XContentType xContentType = randomFrom(XContentType.values());
final XContentBuilder builder = XContentFactory.contentBuilder(xContentType);
builder.startObject()
.field("access_token", accessToken)
.field("type", type)
.field("expires_in", expiresIn.seconds());
if (refreshToken != null || randomBoolean()) {
builder.field("refresh_token", refreshToken);
}
if (scope != null || randomBoolean()) {
builder.field("scope", scope);
}
builder.endObject();
BytesReference xContent = BytesReference.bytes(builder);
final CreateTokenResponse response = CreateTokenResponse.fromXContent(createParser(xContentType.xContent(), xContent));
assertThat(response.getAccessToken(), equalTo(accessToken));
assertThat(response.getRefreshToken(), equalTo(refreshToken));
assertThat(response.getScope(), equalTo(scope));
assertThat(response.getType(), equalTo(type));
assertThat(response.getExpiresIn(), equalTo(expiresIn));
}
}

View File

@ -0,0 +1,85 @@
[[java-rest-high-security-create-token]]
=== Create Token API
[[java-rest-high-security-create-token-request]]
==== Request
The `CreateTokenRequest` supports three different OAuth2 _grant types_:
===== Password Grants
["source","java",subs="attributes,callouts,macros"]
--------------------------------------------------
include-tagged::{doc-tests}/SecurityDocumentationIT.java[create-token-password-request]
--------------------------------------------------
===== Refresh Token Grants
["source","java",subs="attributes,callouts,macros"]
--------------------------------------------------
include-tagged::{doc-tests}/SecurityDocumentationIT.java[create-token-refresh-request]
--------------------------------------------------
===== Client Credential Grants
["source","java",subs="attributes,callouts,macros"]
--------------------------------------------------
include-tagged::{doc-tests}/SecurityDocumentationIT.java[create-token-client-credentials-request]
--------------------------------------------------
[[java-rest-high-security-create-token-execution]]
==== Execution
Creating a OAuth2 security token can be performed by passing the appropriate request to the
`security().createToken()` method:
["source","java",subs="attributes,callouts,macros"]
--------------------------------------------------
include-tagged::{doc-tests}/SecurityDocumentationIT.java[create-token-execute]
--------------------------------------------------
[[java-rest-high-security-create-token-response]]
==== Response
The returned `CreateTokenResponse` contains the following properties:
`accessToken`:: This is the newly created access token.
It can be used to authenticate to the Elasticsearch cluster.
`type`:: The type of the token, this is always `"Bearer"`.
`expiresIn`:: The length of time until the token will expire.
The token will be considered invalid after that time.
`scope`:: The scope of the token. May be `null`.
`refreshToken`:: A secondary "refresh" token that may be used to extend
the life of an access token. May be `null`.
["source","java",subs="attributes,callouts,macros"]
--------------------------------------------------
include-tagged::{doc-tests}/SecurityDocumentationIT.java[create-token-response]
--------------------------------------------------
<1> The `accessToken` can be used to authentication to Elasticsearch.
<2> The `refreshToken` can be used in to create a new `CreateTokenRequest` with a `refresh_token` grant.
[[java-rest-high-security-create-token-async]]
==== Asynchronous Execution
This request can be executed asynchronously using the `security().createTokenAsync()`
method:
["source","java",subs="attributes,callouts,macros"]
--------------------------------------------------
include-tagged::{doc-tests}/SecurityDocumentationIT.java[create-token-execute-async]
--------------------------------------------------
<1> The `CreateTokenRequest` to execute and the `ActionListener` to use when
the execution completes
The asynchronous method does not block and returns immediately. Once the request
has completed the `ActionListener` is called back using the `onResponse` method
if the execution successfully completed or using the `onFailure` method if
it failed.
A typical listener for a `CreateTokenResponse` looks like:
["source","java",subs="attributes,callouts,macros"]
--------------------------------------------------
include-tagged::{doc-tests}/SecurityDocumentationIT.java[create-token-execute-listener]
--------------------------------------------------
<1> Called when the execution is successfully completed. The response is
provided as an argument
<2> Called in case of failure. The raised exception is provided as an argument

View File

@ -329,6 +329,7 @@ The Java High Level REST Client supports the following Security APIs:
* <<java-rest-high-security-put-role-mapping>>
* <<java-rest-high-security-get-role-mappings>>
* <<java-rest-high-security-delete-role-mapping>>
* <<java-rest-high-security-create-token>>
include::security/put-user.asciidoc[]
include::security/enable-user.asciidoc[]
@ -340,6 +341,7 @@ include::security/get-certificates.asciidoc[]
include::security/put-role-mapping.asciidoc[]
include::security/get-role-mappings.asciidoc[]
include::security/delete-role-mapping.asciidoc[]
include::security/create-token.asciidoc[]
== Watcher APIs