-
Notifications
You must be signed in to change notification settings - Fork 25.6k
HLRest: add xpack put user API #32332
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
4b8c300
3d9c92f
0d484b5
c322f08
fc093c7
771b074
577de8e
488a0b9
2d5d16b
d743360
c19ed15
16c57f5
b799b37
93d109c
f41436d
ec13763
568d53e
400626a
f1df362
4d2b9e3
008bf6a
741321a
213bd47
f4bf017
7d6341c
a225363
373f89a
eeb5ab5
0e93800
9d29380
04f5dee
899dd8b
0a65cf1
e109dea
8633a9c
937a48a
16014ff
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,69 @@ | ||
| /* | ||
| * 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; | ||
|
|
||
| import org.elasticsearch.action.ActionListener; | ||
| import org.elasticsearch.client.security.PutUserRequest; | ||
| import org.elasticsearch.client.security.PutUserResponse; | ||
|
|
||
| import java.io.IOException; | ||
|
|
||
| import static java.util.Collections.emptySet; | ||
|
|
||
| /** | ||
| * A wrapper for the {@link RestHighLevelClient} that provides methods for accessing the Security APIs. | ||
| * <p> | ||
| * See <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/security-api.html">Security APIs on elastic.co</a> | ||
| */ | ||
| public final class SecurityClient { | ||
|
|
||
| private final RestHighLevelClient restHighLevelClient; | ||
|
|
||
| SecurityClient(RestHighLevelClient restHighLevelClient) { | ||
| this.restHighLevelClient = restHighLevelClient; | ||
| } | ||
|
|
||
| /** | ||
| * Create/update a user in the native realm synchronously. | ||
| * See <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/security-api-users.html"> | ||
| * the docs</a> for more. | ||
| * @param request the request with the user's information | ||
| * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized | ||
| * @return the response from the put user call | ||
| * @throws IOException in case there is a problem sending the request or parsing back the response | ||
| */ | ||
| public PutUserResponse putUser(PutUserRequest request, RequestOptions options) throws IOException { | ||
| return restHighLevelClient.performRequestAndParseEntity(request, SecurityRequestConverters::putUser, options, | ||
| PutUserResponse::fromXContent, emptySet()); | ||
| } | ||
|
|
||
| /** | ||
| * Asynchronously create/update a user in the native realm. | ||
| * See <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/security-api-users.html"> | ||
| * the docs</a> for more. | ||
| * @param request the request with the user's information | ||
| * @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 putUserAsync(PutUserRequest request, RequestOptions options, ActionListener<PutUserResponse> listener) { | ||
| restHighLevelClient.performRequestAsyncAndParseEntity(request, SecurityRequestConverters::putUser, options, | ||
| PutUserResponse::fromXContent, listener, emptySet()); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,45 @@ | ||
| /* | ||
| * 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; | ||
|
|
||
| import org.apache.http.client.methods.HttpPut; | ||
| import org.elasticsearch.client.security.PutUserRequest; | ||
|
|
||
| import java.io.IOException; | ||
|
|
||
| import static org.elasticsearch.client.RequestConverters.REQUEST_BODY_CONTENT_TYPE; | ||
| import static org.elasticsearch.client.RequestConverters.createEntity; | ||
|
|
||
| public final class SecurityRequestConverters { | ||
|
|
||
| private SecurityRequestConverters() {} | ||
|
|
||
| static Request putUser(PutUserRequest putUserRequest) throws IOException { | ||
| String endpoint = new RequestConverters.EndpointBuilder() | ||
| .addPathPartAsIs("_xpack/security/user") | ||
| .addPathPart(putUserRequest.getUsername()) | ||
| .build(); | ||
| Request request = new Request(HttpPut.METHOD_NAME, endpoint); | ||
| request.setEntity(createEntity(putUserRequest, REQUEST_BODY_CONTENT_TYPE)); | ||
| RequestConverters.Params params = new RequestConverters.Params(request); | ||
| params.withRefreshPolicy(putUserRequest.getRefreshPolicy()); | ||
| return request; | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,156 @@ | ||
| /* | ||
| * 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.client.ValidationException; | ||
| import org.elasticsearch.common.CharArrays; | ||
| import org.elasticsearch.common.xcontent.ToXContentObject; | ||
| import org.elasticsearch.common.xcontent.XContentBuilder; | ||
|
|
||
| import java.io.Closeable; | ||
| import java.io.IOException; | ||
| import java.util.Arrays; | ||
| import java.util.Collections; | ||
| import java.util.List; | ||
| import java.util.Map; | ||
| import java.util.Objects; | ||
| import java.util.Optional; | ||
|
|
||
| /** | ||
| * Request object to create or update a user in the native realm. | ||
| */ | ||
| public final class PutUserRequest implements Validatable, Closeable, ToXContentObject { | ||
|
|
||
| private final String username; | ||
| private final List<String> roles; | ||
| private final String fullName; | ||
| private final String email; | ||
| private final Map<String, Object> metadata; | ||
| private final char[] password; | ||
| private final boolean enabled; | ||
| private final RefreshPolicy refreshPolicy; | ||
|
|
||
| public PutUserRequest(String username, char[] password, List<String> roles, String fullName, String email, boolean enabled, | ||
| Map<String, Object> metadata, RefreshPolicy refreshPolicy) { | ||
| this.username = Objects.requireNonNull(username, "username is required"); | ||
| this.password = password; | ||
| this.roles = Collections.unmodifiableList(Objects.requireNonNull(roles, "roles must be specified")); | ||
| this.fullName = fullName; | ||
| this.email = email; | ||
| this.enabled = enabled; | ||
| this.metadata = metadata == null ? Collections.emptyMap() : Collections.unmodifiableMap(metadata); | ||
| this.refreshPolicy = refreshPolicy == null ? RefreshPolicy.getDefault() : refreshPolicy; | ||
| } | ||
|
|
||
| public String getUsername() { | ||
| return username; | ||
| } | ||
|
|
||
| public List<String> getRoles() { | ||
| return roles; | ||
| } | ||
|
|
||
| public String getFullName() { | ||
| return fullName; | ||
| } | ||
|
|
||
| public String getEmail() { | ||
| return email; | ||
| } | ||
|
|
||
| public Map<String, Object> getMetadata() { | ||
| return metadata; | ||
| } | ||
|
|
||
| public char[] getPassword() { | ||
| return password; | ||
| } | ||
|
|
||
| public boolean isEnabled() { | ||
| return enabled; | ||
| } | ||
|
|
||
| public RefreshPolicy getRefreshPolicy() { | ||
| return refreshPolicy; | ||
| } | ||
|
|
||
| @Override | ||
| public boolean equals(Object o) { | ||
| if (this == o) return true; | ||
| if (o == null || getClass() != o.getClass()) return false; | ||
| PutUserRequest that = (PutUserRequest) o; | ||
| return enabled == that.enabled && | ||
| Objects.equals(username, that.username) && | ||
| Objects.equals(roles, that.roles) && | ||
| Objects.equals(fullName, that.fullName) && | ||
| Objects.equals(email, that.email) && | ||
| Objects.equals(metadata, that.metadata) && | ||
| Arrays.equals(password, that.password) && | ||
| refreshPolicy == that.refreshPolicy; | ||
| } | ||
|
|
||
| @Override | ||
| public int hashCode() { | ||
| int result = Objects.hash(username, roles, fullName, email, metadata, enabled, refreshPolicy); | ||
| result = 31 * result + Arrays.hashCode(password); | ||
| return result; | ||
| } | ||
|
|
||
| @Override | ||
| public void close() { | ||
| if (password != null) { | ||
| Arrays.fill(password, (char) 0); | ||
| } | ||
| } | ||
|
|
||
| @Override | ||
| public Optional<ValidationException> validate() { | ||
| if (metadata != null && metadata.keySet().stream().anyMatch(s -> s.startsWith("_"))) { | ||
| ValidationException validationException = new ValidationException(); | ||
| validationException.addValidationError("metadata keys may not start with [_]"); | ||
| return Optional.of(validationException); | ||
| } | ||
| return Optional.empty(); | ||
| } | ||
|
|
||
| @Override | ||
| public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { | ||
| builder.startObject(); | ||
jkakavas marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| builder.field("username", username); | ||
| if (password != null) { | ||
| byte[] charBytes = CharArrays.toUtf8Bytes(password); | ||
| builder.field("password").utf8Value(charBytes, 0, charBytes.length); | ||
| } | ||
| if (roles != null) { | ||
| builder.field("roles", roles); | ||
| } | ||
| if (fullName != null) { | ||
| builder.field("full_name", fullName); | ||
| } | ||
| if (email != null) { | ||
| builder.field("email", email); | ||
| } | ||
| if (metadata != null) { | ||
| builder.field("metadata", metadata); | ||
| } | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @jaymode
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. (Adding a test in |
||
| return builder.endObject(); | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
maybe organize under package
org.elasticsearch.client.security.user? As we will keep adding a lot of such classes in the same package for different APIs like role mapping etc.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you think its too much to put them all in the same package, I wont enforce keeping them in one package. I certainly do not think the number of classes that currently exist in all of the action.* classes is too many for a single package here. But I dont think we need to enforce it one way or the other.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think we need more packages. I think our goal should be to keep the number of packages to a minimum and at this point I do not see justification for it.