Skip to content

Commit 2da9b1e

Browse files
committed
Merge branch 'master' of https://github.com/elastic/elasticsearch into ql_fields_api_implementation
2 parents 520acf6 + 0349266 commit 2da9b1e

File tree

106 files changed

+3063
-525
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

106 files changed

+3063
-525
lines changed

.idea/scopes/llrc.xml

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

CONTRIBUTING.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -323,10 +323,10 @@ Please follow these formatting guidelines:
323323
decrease in consistency.
324324
* Note that Javadoc and block comments i.e. `/* ... */` are not formatted,
325325
but line comments i.e `// ...` are.
326-
* There is an implicit rule that negative boolean expressions should use
327-
the form `foo == false` instead of `!foo` for better readability of the
328-
code. While this isn't strictly enforced, if might get called out in PR
329-
reviews as something to change.
326+
* Negative boolean expressions must use the form `foo == false` instead of
327+
`!foo` for better readability of the code. This is enforced via
328+
Checkstyle. Conversely, you should not write e.g. `if (foo == true)`, but
329+
just `if (foo)`.
330330

331331
#### Editor / IDE Support
332332

buildSrc/src/main/java/org/elasticsearch/gradle/internal/precommit/LicenseHeadersTask.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,7 @@ public void runRat() {
153153
// we keep this here, in case someone adds BSD code for some reason, it should never be allowed.
154154
matchers.add(subStringMatcher("BSD4 ", "Original BSD License (with advertising clause)", "All advertising materials"));
155155
// Apache
156-
matchers.add(subStringMatcher("AL ", "Apache", "Licensed to Elasticsearch under one or more contributor"));
156+
matchers.add(subStringMatcher("AL ", "Apache", "Licensed to Elasticsearch B.V. under one or more contributor"));
157157
// Generated resources
158158
matchers.add(subStringMatcher("GEN ", "Generated", "ANTLR GENERATED CODE"));
159159
// Vendored Code

buildSrc/src/main/resources/checkstyle.xml

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -124,17 +124,17 @@
124124
</module>
125125

126126
<!-- Forbid using '!' for logical negations in favour of checking against 'false' explicitly. -->
127-
<!-- This is disabled for now because there are many violations, hence the rule is reporting at the "warning" severity. -->
128-
<!--
127+
<!-- This is only reported in the IDE for now because there are many violations -->
129128
<module name="DescendantToken">
130-
<property name="id" value="BooleanNegation" />
131-
<property name="tokens" value="EXPR"/>
132-
<property name="limitedTokens" value="LNOT"/>
133-
<property name="maximumNumber" value="0"/>
134-
<property name="maximumDepth" value="1"/>
135-
<message key="descendant.token.max" value="Do not negate boolean expressions with '!', but check explicitly with '== false' as it is more explicit"/>
129+
<property name="id" value="BooleanNegation" />
130+
<property name="tokens" value="EXPR"/>
131+
<property name="limitedTokens" value="LNOT"/>
132+
<property name="maximumNumber" value="0"/>
133+
<property name="maximumDepth" value="2"/>
134+
<message
135+
key="descendant.token.max"
136+
value="Do not negate boolean expressions with '!', but check explicitly with '== false' as it is more explicit"/>
136137
</module>
137-
-->
138138

139139
</module>
140140
</module>

buildSrc/src/main/resources/checkstyle_ide_fragment.xml

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -8,18 +8,6 @@
88
<!-- Checkstyle config by the `:configureIdeCheckstyle` task. -->
99

1010
<module name="IdeFragment">
11-
<!-- Forbid using '!' for logical negations in favour of checking against 'false' explicitly. -->
12-
<!-- This is only reported in the IDE for now because there are many violations -->
13-
<module name="DescendantToken">
14-
<property name="id" value="BooleanNegation" />
15-
<property name="tokens" value="EXPR"/>
16-
<property name="limitedTokens" value="LNOT"/>
17-
<property name="maximumNumber" value="0"/>
18-
<property name="maximumDepth" value="2"/>
19-
<message
20-
key="descendant.token.max"
21-
value="Do not negate boolean expressions with '!', but check explicitly with '== false' as it is more explicit"/>
22-
</module>
2311

2412
<!-- See CONTRIBUTING.md for our guidelines on Javadoc -->
2513

client/rest-high-level/src/main/java/org/elasticsearch/client/SecurityClient.java

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@
5252
import org.elasticsearch.client.security.GetUserPrivilegesResponse;
5353
import org.elasticsearch.client.security.GetUsersRequest;
5454
import org.elasticsearch.client.security.GetUsersResponse;
55+
import org.elasticsearch.client.security.GrantApiKeyRequest;
5556
import org.elasticsearch.client.security.HasPrivilegesRequest;
5657
import org.elasticsearch.client.security.HasPrivilegesResponse;
5758
import org.elasticsearch.client.security.InvalidateApiKeyRequest;
@@ -1064,6 +1065,37 @@ public Cancellable invalidateApiKeyAsync(final InvalidateApiKeyRequest request,
10641065
InvalidateApiKeyResponse::fromXContent, listener, emptySet());
10651066
}
10661067

1068+
/**
1069+
* Create an API Key on behalf of another user.<br>
1070+
* See <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/security-api-grant-api-key.html">
1071+
* the docs</a> for more.
1072+
*
1073+
* @param request the request to grant an API key
1074+
* @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
1075+
* @return the response from the create API key call
1076+
* @throws IOException in case there is a problem sending the request or parsing back the response
1077+
*/
1078+
public CreateApiKeyResponse grantApiKey(final GrantApiKeyRequest request, final RequestOptions options) throws IOException {
1079+
return restHighLevelClient.performRequestAndParseEntity(request, SecurityRequestConverters::grantApiKey, options,
1080+
CreateApiKeyResponse::fromXContent, emptySet());
1081+
}
1082+
1083+
/**
1084+
* Asynchronously creates an API key on behalf of another user.<br>
1085+
* See <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/security-api-grant-api-key.html">
1086+
* the docs</a> for more.
1087+
*
1088+
* @param request the request to grant an API key
1089+
* @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
1090+
* @param listener the listener to be notified upon request completion
1091+
* @return cancellable that may be used to cancel the request
1092+
*/
1093+
public Cancellable grantApiKeyAsync(final GrantApiKeyRequest request, final RequestOptions options,
1094+
final ActionListener<CreateApiKeyResponse> listener) {
1095+
return restHighLevelClient.performRequestAsyncAndParseEntity(request, SecurityRequestConverters::grantApiKey, options,
1096+
CreateApiKeyResponse::fromXContent, listener, emptySet());
1097+
}
1098+
10671099
/**
10681100
* Get an Elasticsearch access token from an {@code X509Certificate} chain. The certificate chain is that of the client from a mutually
10691101
* authenticated TLS session, and it is validated by the PKI realms with {@code delegation.enabled} toggled to {@code true}.<br>

client/rest-high-level/src/main/java/org/elasticsearch/client/SecurityRequestConverters.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
import org.elasticsearch.client.security.GetRoleMappingsRequest;
3232
import org.elasticsearch.client.security.GetRolesRequest;
3333
import org.elasticsearch.client.security.GetUsersRequest;
34+
import org.elasticsearch.client.security.GrantApiKeyRequest;
3435
import org.elasticsearch.client.security.HasPrivilegesRequest;
3536
import org.elasticsearch.client.security.InvalidateApiKeyRequest;
3637
import org.elasticsearch.client.security.InvalidateTokenRequest;
@@ -296,6 +297,15 @@ static Request createApiKey(final CreateApiKeyRequest createApiKeyRequest) throw
296297
return request;
297298
}
298299

300+
static Request grantApiKey(final GrantApiKeyRequest grantApiKeyRequest) throws IOException {
301+
final Request request = new Request(HttpPost.METHOD_NAME, "/_security/api_key/grant");
302+
request.setEntity(createEntity(grantApiKeyRequest, REQUEST_BODY_CONTENT_TYPE));
303+
final RequestConverters.Params params = new RequestConverters.Params();
304+
params.withRefreshPolicy(grantApiKeyRequest.getApiKeyRequest().getRefreshPolicy());
305+
request.addParameters(params.asMap());
306+
return request;
307+
}
308+
299309
static Request getApiKey(final GetApiKeyRequest getApiKeyRequest) throws IOException {
300310
final Request request = new Request(HttpGet.METHOD_NAME, "/_security/api_key");
301311
if (Strings.hasText(getApiKeyRequest.getId())) {
Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License
4+
* 2.0 and the Server Side Public License, v 1; you may not use this file except
5+
* in compliance with, at your election, the Elastic License 2.0 or the Server
6+
* Side Public License, v 1.
7+
*/
8+
9+
package org.elasticsearch.client.security;
10+
11+
import org.elasticsearch.client.Validatable;
12+
import org.elasticsearch.common.CharArrays;
13+
import org.elasticsearch.common.xcontent.ToXContentFragment;
14+
import org.elasticsearch.common.xcontent.ToXContentObject;
15+
import org.elasticsearch.common.xcontent.XContentBuilder;
16+
17+
import java.io.IOException;
18+
import java.util.Arrays;
19+
import java.util.Objects;
20+
21+
/**
22+
* Request to create a new API Key on behalf of another user.
23+
*/
24+
public final class GrantApiKeyRequest implements Validatable, ToXContentObject {
25+
26+
private final Grant grant;
27+
private final CreateApiKeyRequest apiKeyRequest;
28+
29+
public static class Grant implements ToXContentFragment {
30+
private final String grantType;
31+
private final String username;
32+
private final char[] password;
33+
private final String accessToken;
34+
35+
private Grant(String grantType, String username, char[] password, String accessToken) {
36+
this.grantType = Objects.requireNonNull(grantType, "Grant type may not be null");
37+
this.username = username;
38+
this.password = password;
39+
this.accessToken = accessToken;
40+
}
41+
42+
public static Grant passwordGrant(String username, char[] password) {
43+
return new Grant(
44+
"password",
45+
Objects.requireNonNull(username, "Username may not be null"),
46+
Objects.requireNonNull(password, "Password may not be null"),
47+
null);
48+
}
49+
50+
public static Grant accessTokenGrant(String accessToken) {
51+
return new Grant(
52+
"access_token",
53+
null,
54+
null,
55+
Objects.requireNonNull(accessToken, "Access token may not be null")
56+
);
57+
}
58+
59+
public String getGrantType() {
60+
return grantType;
61+
}
62+
63+
public String getUsername() {
64+
return username;
65+
}
66+
67+
public char[] getPassword() {
68+
return password;
69+
}
70+
71+
public String getAccessToken() {
72+
return accessToken;
73+
}
74+
75+
@Override
76+
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
77+
builder.field("grant_type", grantType);
78+
if (username != null) {
79+
builder.field("username", username);
80+
}
81+
if (password != null) {
82+
byte[] passwordBytes = CharArrays.toUtf8Bytes(password);
83+
try {
84+
builder.field("password").utf8Value(passwordBytes, 0, passwordBytes.length);
85+
} finally {
86+
Arrays.fill(passwordBytes, (byte) 0);
87+
}
88+
}
89+
if (accessToken != null) {
90+
builder.field("access_token", accessToken);
91+
}
92+
return builder;
93+
}
94+
95+
@Override
96+
public boolean equals(Object o) {
97+
if (this == o) {
98+
return true;
99+
}
100+
if (o == null || getClass() != o.getClass()) {
101+
return false;
102+
}
103+
Grant grant = (Grant) o;
104+
return grantType.equals(grant.grantType)
105+
&& Objects.equals(username, grant.username)
106+
&& Arrays.equals(password, grant.password)
107+
&& Objects.equals(accessToken, grant.accessToken);
108+
}
109+
110+
@Override
111+
public int hashCode() {
112+
int result = Objects.hash(grantType, username, accessToken);
113+
result = 31 * result + Arrays.hashCode(password);
114+
return result;
115+
}
116+
}
117+
118+
public GrantApiKeyRequest(Grant grant, CreateApiKeyRequest apiKeyRequest) {
119+
this.grant = Objects.requireNonNull(grant, "Grant may not be null");
120+
this.apiKeyRequest = Objects.requireNonNull(apiKeyRequest, "Create API key request may not be null");
121+
}
122+
123+
public Grant getGrant() {
124+
return grant;
125+
}
126+
127+
public CreateApiKeyRequest getApiKeyRequest() {
128+
return apiKeyRequest;
129+
}
130+
131+
@Override
132+
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
133+
builder.startObject();
134+
grant.toXContent(builder, params);
135+
builder.field("api_key", apiKeyRequest);
136+
return builder.endObject();
137+
}
138+
139+
@Override
140+
public boolean equals(Object o) {
141+
if (this == o) {
142+
return true;
143+
}
144+
if (o == null || getClass() != o.getClass()) {
145+
return false;
146+
}
147+
final GrantApiKeyRequest that = (GrantApiKeyRequest) o;
148+
return Objects.equals(this.grant, that.grant)
149+
&& Objects.equals(this.apiKeyRequest, that.apiKeyRequest);
150+
}
151+
152+
@Override
153+
public int hashCode() {
154+
return Objects.hash(grant, apiKeyRequest);
155+
}
156+
}

client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/Role.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,17 @@ public static final class Builder {
212212
private Builder() {
213213
}
214214

215+
public Builder clone(Role role) {
216+
return this
217+
.name(role.name)
218+
.clusterPrivileges(role.clusterPrivileges)
219+
.globalApplicationPrivileges(role.globalPrivileges)
220+
.indicesPrivileges(role.indicesPrivileges)
221+
.applicationResourcePrivileges(role.applicationPrivileges)
222+
.runAsPrivilege(role.runAsPrivilege)
223+
.metadata(role.metadata);
224+
}
225+
215226
public Builder name(String name) {
216227
if (Strings.hasText(name) == false){
217228
throw new IllegalArgumentException("role name must be provided");

client/rest-high-level/src/test/java/org/elasticsearch/client/SecurityRequestConvertersTests.java

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import org.elasticsearch.client.security.GetRoleMappingsRequest;
2828
import org.elasticsearch.client.security.GetRolesRequest;
2929
import org.elasticsearch.client.security.GetUsersRequest;
30+
import org.elasticsearch.client.security.GrantApiKeyRequest;
3031
import org.elasticsearch.client.security.InvalidateApiKeyRequest;
3132
import org.elasticsearch.client.security.PutPrivilegesRequest;
3233
import org.elasticsearch.client.security.PutRoleMappingRequest;
@@ -425,23 +426,52 @@ public void testPutRole() throws IOException {
425426
}
426427

427428
public void testCreateApiKey() throws IOException {
429+
final CreateApiKeyRequest createApiKeyRequest = buildCreateApiKeyRequest();
430+
431+
final Map<String, String> expectedParams;
432+
final RefreshPolicy refreshPolicy = createApiKeyRequest.getRefreshPolicy();
433+
if (refreshPolicy != RefreshPolicy.NONE) {
434+
expectedParams = Collections.singletonMap("refresh", refreshPolicy.getValue());
435+
} else {
436+
expectedParams = Collections.emptyMap();
437+
}
438+
439+
final Request request = SecurityRequestConverters.createApiKey(createApiKeyRequest);
440+
assertEquals(HttpPost.METHOD_NAME, request.getMethod());
441+
assertEquals("/_security/api_key", request.getEndpoint());
442+
assertEquals(expectedParams, request.getParameters());
443+
assertToXContentBody(createApiKeyRequest, request.getEntity());
444+
}
445+
446+
private CreateApiKeyRequest buildCreateApiKeyRequest() {
428447
final String name = randomAlphaOfLengthBetween(4, 7);
429448
final List<Role> roles = Collections.singletonList(Role.builder().name("r1").clusterPrivileges(ClusterPrivilegeName.ALL)
430449
.indicesPrivileges(IndicesPrivileges.builder().indices("ind-x").privileges(IndexPrivilegeName.ALL).build()).build());
431450
final TimeValue expiration = randomBoolean() ? null : TimeValue.timeValueHours(24);
432451
final RefreshPolicy refreshPolicy = randomFrom(RefreshPolicy.values());
452+
final CreateApiKeyRequest createApiKeyRequest = new CreateApiKeyRequest(name, roles, expiration, refreshPolicy);
453+
return createApiKeyRequest;
454+
}
455+
456+
public void testGrantApiKey() throws IOException {
457+
final CreateApiKeyRequest createApiKeyRequest = buildCreateApiKeyRequest();
458+
final GrantApiKeyRequest grantApiKeyRequest = new GrantApiKeyRequest(randomBoolean()
459+
? GrantApiKeyRequest.Grant.accessTokenGrant(randomAlphaOfLength(24))
460+
: GrantApiKeyRequest.Grant.passwordGrant(randomAlphaOfLengthBetween(4, 12), randomAlphaOfLengthBetween(14, 18).toCharArray()),
461+
createApiKeyRequest);
433462
final Map<String, String> expectedParams;
463+
final RefreshPolicy refreshPolicy = createApiKeyRequest.getRefreshPolicy();
434464
if (refreshPolicy != RefreshPolicy.NONE) {
435465
expectedParams = Collections.singletonMap("refresh", refreshPolicy.getValue());
436466
} else {
437467
expectedParams = Collections.emptyMap();
438468
}
439-
final CreateApiKeyRequest createApiKeyRequest = new CreateApiKeyRequest(name, roles, expiration, refreshPolicy);
440-
final Request request = SecurityRequestConverters.createApiKey(createApiKeyRequest);
469+
470+
final Request request = SecurityRequestConverters.grantApiKey(grantApiKeyRequest);
441471
assertEquals(HttpPost.METHOD_NAME, request.getMethod());
442-
assertEquals("/_security/api_key", request.getEndpoint());
472+
assertEquals("/_security/api_key/grant", request.getEndpoint());
443473
assertEquals(expectedParams, request.getParameters());
444-
assertToXContentBody(createApiKeyRequest, request.getEntity());
474+
assertToXContentBody(grantApiKeyRequest, request.getEntity());
445475
}
446476

447477
public void testGetApiKey() throws IOException {

0 commit comments

Comments
 (0)