From 2358309f72d5547d8370c8ec3e21ae39cd1fca54 Mon Sep 17 00:00:00 2001 From: jaymode Date: Fri, 9 Sep 2016 10:13:54 -0400 Subject: [PATCH] security: allow enabled and username fields in put user request body The enabled and username fields are both now allowed in the request body for the put user request. This makes it easier to perform a get and update a user without needing to edit more of the request body than necessary. Closes elastic/elasticsearch#3391 Original commit: elastic/x-pack-elasticsearch@ab763e843bbd7efd40f24336cfaac0d5038c8eb2 --- .../security/action/user/PutUserRequest.java | 12 ++++- .../action/user/PutUserRequestBuilder.java | 24 ++++++++- .../authc/esnative/NativeUsersStore.java | 23 ++++---- .../user/PutUserRequestBuilderTests.java | 17 ++++++ .../rest-api-spec/test/users/10_basic.yaml | 54 +++++++++++++++++++ .../test/users/31_create_disabled.yaml | 44 +++++++++++++++ 6 files changed, 158 insertions(+), 16 deletions(-) create mode 100644 elasticsearch/x-pack/security/src/test/resources/rest-api-spec/test/users/31_create_disabled.yaml diff --git a/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/action/user/PutUserRequest.java b/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/action/user/PutUserRequest.java index cff1922825c..4cf63262fda 100644 --- a/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/action/user/PutUserRequest.java +++ b/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/action/user/PutUserRequest.java @@ -8,7 +8,6 @@ package org.elasticsearch.xpack.security.action.user; import org.elasticsearch.action.ActionRequest; import org.elasticsearch.action.ActionRequestValidationException; import org.elasticsearch.action.support.WriteRequest; -import org.elasticsearch.action.support.WriteRequest.RefreshPolicy; import org.elasticsearch.common.Nullable; import org.elasticsearch.common.bytes.BytesArray; import org.elasticsearch.common.bytes.BytesReference; @@ -33,6 +32,7 @@ public class PutUserRequest extends ActionRequest implements Use private String email; private Map metadata; private char[] passwordHash; + private boolean enabled = true; private RefreshPolicy refreshPolicy = RefreshPolicy.IMMEDIATE; public PutUserRequest() { @@ -79,6 +79,10 @@ public class PutUserRequest extends ActionRequest implements Use this.passwordHash = passwordHash; } + public boolean enabled() { + return enabled; + } + /** * Should this request trigger a refresh ({@linkplain RefreshPolicy#IMMEDIATE}, the default), wait for a refresh ( * {@linkplain RefreshPolicy#WAIT_UNTIL}), or proceed ignore refreshes entirely ({@linkplain RefreshPolicy#NONE}). @@ -119,6 +123,10 @@ public class PutUserRequest extends ActionRequest implements Use return passwordHash; } + public void enabled(boolean enabled) { + this.enabled = enabled; + } + @Override public String[] usernames() { return new String[] { username }; @@ -139,6 +147,7 @@ public class PutUserRequest extends ActionRequest implements Use email = in.readOptionalString(); metadata = in.readBoolean() ? in.readMap() : null; refreshPolicy = RefreshPolicy.readFrom(in); + enabled = in.readBoolean(); } @Override @@ -162,5 +171,6 @@ public class PutUserRequest extends ActionRequest implements Use out.writeMap(metadata); } refreshPolicy.writeTo(out); + out.writeBoolean(enabled); } } diff --git a/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/action/user/PutUserRequestBuilder.java b/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/action/user/PutUserRequestBuilder.java index 38888f19c00..9867d035644 100644 --- a/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/action/user/PutUserRequestBuilder.java +++ b/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/action/user/PutUserRequestBuilder.java @@ -16,6 +16,7 @@ import org.elasticsearch.common.ValidationException; import org.elasticsearch.common.bytes.BytesReference; import org.elasticsearch.common.xcontent.XContentHelper; import org.elasticsearch.common.xcontent.XContentParser; +import org.elasticsearch.common.xcontent.XContentParser.Token; import org.elasticsearch.xpack.security.authc.support.Hasher; import org.elasticsearch.xpack.security.authc.support.SecuredString; import org.elasticsearch.xpack.security.support.Validation; @@ -84,6 +85,11 @@ public class PutUserRequestBuilder extends ActionRequestBuilder) () -> new ParameterizedMessage("unable to put user [{}]", request.username()), e); @@ -398,7 +398,8 @@ public class NativeUsersStore extends AbstractComponent implements ClusterStateL User.Fields.ROLES.getPreferredName(), putUserRequest.roles(), User.Fields.FULL_NAME.getPreferredName(), putUserRequest.fullName(), User.Fields.EMAIL.getPreferredName(), putUserRequest.email(), - User.Fields.METADATA.getPreferredName(), putUserRequest.metadata()) + User.Fields.METADATA.getPreferredName(), putUserRequest.metadata(), + User.Fields.ENABLED.getPreferredName(), putUserRequest.enabled()) .setRefreshPolicy(putUserRequest.getRefreshPolicy()) .execute(new ActionListener() { @Override @@ -424,27 +425,21 @@ public class NativeUsersStore extends AbstractComponent implements ClusterStateL }); } - private void upsertUser(final PutUserRequest putUserRequest, final ActionListener listener) { + private void indexUser(final PutUserRequest putUserRequest, final ActionListener listener) { assert putUserRequest.passwordHash() != null; - client.prepareUpdate(SecurityTemplateService.SECURITY_INDEX_NAME, + client.prepareIndex(SecurityTemplateService.SECURITY_INDEX_NAME, USER_DOC_TYPE, putUserRequest.username()) - .setDoc(User.Fields.USERNAME.getPreferredName(), putUserRequest.username(), - User.Fields.PASSWORD.getPreferredName(), String.valueOf(putUserRequest.passwordHash()), - User.Fields.ROLES.getPreferredName(), putUserRequest.roles(), - User.Fields.FULL_NAME.getPreferredName(), putUserRequest.fullName(), - User.Fields.EMAIL.getPreferredName(), putUserRequest.email(), - User.Fields.METADATA.getPreferredName(), putUserRequest.metadata()) - .setUpsert(User.Fields.USERNAME.getPreferredName(), putUserRequest.username(), + .setSource(User.Fields.USERNAME.getPreferredName(), putUserRequest.username(), User.Fields.PASSWORD.getPreferredName(), String.valueOf(putUserRequest.passwordHash()), User.Fields.ROLES.getPreferredName(), putUserRequest.roles(), User.Fields.FULL_NAME.getPreferredName(), putUserRequest.fullName(), User.Fields.EMAIL.getPreferredName(), putUserRequest.email(), User.Fields.METADATA.getPreferredName(), putUserRequest.metadata(), - User.Fields.ENABLED.getPreferredName(), true) + User.Fields.ENABLED.getPreferredName(), putUserRequest.enabled()) .setRefreshPolicy(putUserRequest.getRefreshPolicy()) - .execute(new ActionListener() { + .execute(new ActionListener() { @Override - public void onResponse(UpdateResponse updateResponse) { + public void onResponse(IndexResponse updateResponse) { clearRealmCache(putUserRequest.username(), listener, updateResponse.getResult() == DocWriteResponse.Result.CREATED); } diff --git a/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/action/user/PutUserRequestBuilderTests.java b/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/action/user/PutUserRequestBuilderTests.java index e049dac1d4c..14724e09b6b 100644 --- a/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/action/user/PutUserRequestBuilderTests.java +++ b/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/action/user/PutUserRequestBuilderTests.java @@ -40,6 +40,7 @@ public class PutUserRequestBuilderTests extends ESTestCase { assertThat(request.fullName(), nullValue()); assertThat(request.email(), nullValue()); assertThat(request.metadata().isEmpty(), is(true)); + assertTrue(request.enabled()); } public void testMissingEmailFullName() throws Exception { @@ -113,4 +114,20 @@ public class PutUserRequestBuilderTests extends ESTestCase { () -> builder.source("kibana4", new BytesArray(json.getBytes(StandardCharsets.UTF_8)))); assertThat(e.getMessage(), containsString("expected field [email] to be of type string")); } + + public void testWithEnabled() throws IOException { + final String json = "{\n" + + " \"roles\": [\n" + + " \"kibana4\"\n" + + " ],\n" + + " \"full_name\": \"Kibana User\",\n" + + " \"email\": \"kibana@elastic.co\",\n" + + " \"metadata\": {}\n," + + " \"enabled\": false\n" + + "}"; + + PutUserRequestBuilder builder = new PutUserRequestBuilder(mock(Client.class)); + PutUserRequest request = builder.source("kibana4", new BytesArray(json.getBytes(StandardCharsets.UTF_8))).request(); + assertFalse(request.enabled()); + } } diff --git a/elasticsearch/x-pack/security/src/test/resources/rest-api-spec/test/users/10_basic.yaml b/elasticsearch/x-pack/security/src/test/resources/rest-api-spec/test/users/10_basic.yaml index 1e18f51ac13..fda190e590f 100644 --- a/elasticsearch/x-pack/security/src/test/resources/rest-api-spec/test/users/10_basic.yaml +++ b/elasticsearch/x-pack/security/src/test/resources/rest-api-spec/test/users/10_basic.yaml @@ -47,3 +47,57 @@ teardown: - match: { joe.email: "joe@bazooka.gum" } - match: { joe.metadata.key1: "val1" } - match: { joe.metadata.key2: "val2" } + +--- +"Test put user with username in body": + - do: + xpack.security.put_user: + username: "joe" + body: > + { + "username": "joe", + "password" : "s3krit", + "roles" : [ "superuser" ], + "full_name" : "Bazooka Joe", + "email" : "joe@bazooka.gum", + "metadata" : { + "key1" : "val1", + "key2" : "val2" + } + } + - match: { user: { created: true } } + + - do: + headers: + Authorization: "Basic am9lOnMza3JpdA==" + cluster.health: {} + - match: { timed_out: false } + + - do: + xpack.security.get_user: + username: "joe" + - match: { joe.username: "joe" } + - match: { joe.roles.0: "superuser" } + - match: { joe.full_name: "Bazooka Joe" } + - match: { joe.email: "joe@bazooka.gum" } + - match: { joe.metadata.key1: "val1" } + - match: { joe.metadata.key2: "val2" } + +--- +"Test put user with different username in body": + - do: + catch: request + xpack.security.put_user: + username: "joe" + body: > + { + "username": "joey", + "password" : "s3krit", + "roles" : [ "superuser" ], + "full_name" : "Bazooka Joe", + "email" : "joe@bazooka.gum", + "metadata" : { + "key1" : "val1", + "key2" : "val2" + } + } diff --git a/elasticsearch/x-pack/security/src/test/resources/rest-api-spec/test/users/31_create_disabled.yaml b/elasticsearch/x-pack/security/src/test/resources/rest-api-spec/test/users/31_create_disabled.yaml new file mode 100644 index 00000000000..c2ca1c4b343 --- /dev/null +++ b/elasticsearch/x-pack/security/src/test/resources/rest-api-spec/test/users/31_create_disabled.yaml @@ -0,0 +1,44 @@ +--- +setup: + - skip: + features: [headers, catch_unauthorized] + - do: + cluster.health: + wait_for_status: yellow + + - do: + xpack.security.put_user: + username: "joe" + body: > + { + "password": "s3krit", + "roles" : [ "superuser" ], + "enabled": false + } +--- +teardown: + - do: + xpack.security.delete_user: + username: "joe" + ignore: 404 + +--- +"Test disable then enable user": +# validate user cannot login + - do: + catch: unauthorized + headers: + Authorization: "Basic am9lOnMza3JpdA==" + cluster.health: {} + +# enable + - do: + xpack.security.enable_user: + username: "joe" + +# validate user can login + - do: + headers: + Authorization: "Basic am9lOnMza3JpdA==" + cluster.health: {} + - match: { timed_out: false }