Skip to content

[HLRC] Put Role #36209

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

Merged
merged 27 commits into from
Dec 10, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
2999459
Put Role request
albertzaharovits Nov 29, 2018
53ad4bb
RequestConvertTests
albertzaharovits Nov 29, 2018
862bdcd
Merge branch 'master' into hlrc_put_role_2
albertzaharovits Nov 29, 2018
345838a
Remove Transient metadata from the Role builder
albertzaharovits Nov 29, 2018
e55ee55
Revert "Remove Transient metadata from the Role builder"
albertzaharovits Dec 2, 2018
c918ca3
Merge branch 'master' into hlrc_put_role_2
albertzaharovits Dec 2, 2018
d085ef7
Put Role security client and request
albertzaharovits Dec 2, 2018
9707795
Transient metadata
albertzaharovits Dec 2, 2018
d087f98
SecurityDocumentationIT
albertzaharovits Dec 2, 2018
fcbaad3
IndicesPrivilegesTests
albertzaharovits Dec 2, 2018
e09be47
PutRoleRequestTests
albertzaharovits Dec 3, 2018
8c8cf33
SecurityIT in progress
albertzaharovits Dec 3, 2018
ff5d788
transient metadata no longer public
albertzaharovits Dec 3, 2018
e273930
Almost there!
albertzaharovits Dec 4, 2018
86941b5
And docs
albertzaharovits Dec 4, 2018
fa48020
Trimming
albertzaharovits Dec 4, 2018
730f835
Unsaved trimming
albertzaharovits Dec 4, 2018
f1f0550
Merge branch 'master' into hlrc_put_role_2
albertzaharovits Dec 5, 2018
302f62b
Review
albertzaharovits Dec 5, 2018
8234a2f
Merge branch 'master' into hlrc_put_role_2
albertzaharovits Dec 6, 2018
0499df0
Pull transient metadata out of Role
albertzaharovits Dec 6, 2018
6ae513e
Minor left over
albertzaharovits Dec 6, 2018
e65f81a
supported-apis
albertzaharovits Dec 6, 2018
0137e25
Checkstyle
albertzaharovits Dec 7, 2018
e798779
Merge branch 'master' into hlrc_put_role_2
albertzaharovits Dec 7, 2018
4c94995
Merge branch 'master' into hlrc_put_role_2
albertzaharovits Dec 9, 2018
6699ad3
Checkstyle
albertzaharovits Dec 9, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@
import org.elasticsearch.client.security.PutPrivilegesResponse;
import org.elasticsearch.client.security.PutRoleMappingRequest;
import org.elasticsearch.client.security.PutRoleMappingResponse;
import org.elasticsearch.client.security.PutRoleRequest;
import org.elasticsearch.client.security.PutRoleResponse;
import org.elasticsearch.client.security.PutUserRequest;
import org.elasticsearch.client.security.PutUserResponse;

Expand Down Expand Up @@ -461,14 +463,43 @@ public void getRolesAsync(GetRolesRequest request, RequestOptions options, Actio
*
* @param request the request with the roles to get
* @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
* @return the response from the delete role call
* @return the response from the get roles call
* @throws IOException in case there is a problem sending the request or parsing back the response
*/
public GetRolesResponse getRoles(final GetRolesRequest request, final RequestOptions options) throws IOException {
return restHighLevelClient.performRequestAndParseEntity(request, SecurityRequestConverters::getRoles, options,
GetRolesResponse::fromXContent, emptySet());
}

/**
* Asynchronously creates or updates a role in the native roles store.
* See <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/security-api-put-role.html">
* the docs</a> for more.
*
* @param request the request containing the role to create or update
* @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 putRoleAsync(PutRoleRequest request, RequestOptions options, ActionListener<PutRoleResponse> listener) {
restHighLevelClient.performRequestAsyncAndParseEntity(request, SecurityRequestConverters::putRole, options,
PutRoleResponse::fromXContent, listener, emptySet());
}

/**
* Create or update a role in the native roles store.
* See <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/security-api-put-role.html">
* the docs</a> for more.
*
* @param request the request containing the role to create or update
* @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
* @return the response from the put role call
* @throws IOException in case there is a problem sending the request or parsing back the response
*/
public PutRoleResponse putRole(final PutRoleRequest request, final RequestOptions options) throws IOException {
return restHighLevelClient.performRequestAndParseEntity(request, SecurityRequestConverters::putRole, options,
PutRoleResponse::fromXContent, emptySet());
}

/**
* Asynchronously delete a role mapping.
* See <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/security-api-delete-role-mapping.html">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
import org.elasticsearch.client.security.InvalidateTokenRequest;
import org.elasticsearch.client.security.PutPrivilegesRequest;
import org.elasticsearch.client.security.PutRoleMappingRequest;
import org.elasticsearch.client.security.PutRoleRequest;
import org.elasticsearch.client.security.PutUserRequest;
import org.elasticsearch.client.security.SetUserEnabledRequest;
import org.elasticsearch.common.Strings;
Expand Down Expand Up @@ -233,4 +234,16 @@ static Request deletePrivileges(DeletePrivilegesRequest deletePrivilegeRequest)
params.withRefreshPolicy(deletePrivilegeRequest.getRefreshPolicy());
return request;
}

static Request putRole(final PutRoleRequest putRoleRequest) throws IOException {
final String endpoint = new RequestConverters.EndpointBuilder()
.addPathPartAsIs("_xpack/security/role")
.addPathPart(putRoleRequest.getRole().getName())
.build();
final Request request = new Request(HttpPut.METHOD_NAME, endpoint);
request.setEntity(createEntity(putRoleRequest, REQUEST_BODY_CONTENT_TYPE));
final RequestConverters.Params params = new RequestConverters.Params(request);
params.withRefreshPolicy(putRoleRequest.getRefreshPolicy());
return request;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,16 @@
package org.elasticsearch.client.security;

import org.elasticsearch.client.security.user.privileges.Role;
import org.elasticsearch.common.collect.Tuple;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.common.xcontent.XContentParserUtils;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;

/**
Expand All @@ -36,36 +39,50 @@
public final class GetRolesResponse {

private final List<Role> roles;
private final Map<String, Map<String, Object>> transientMetadataMap;

public GetRolesResponse(List<Role> roles) {
GetRolesResponse(List<Role> roles, Map<String, Map<String, Object>> transientMetadataMap) {
this.roles = Collections.unmodifiableList(roles);
this.transientMetadataMap = Collections.unmodifiableMap(transientMetadataMap);
}

public List<Role> getRoles() {
return roles;
}

public Map<String, Map<String, Object>> getTransientMetadataMap() {
return transientMetadataMap;
}

public Map<String, Object> getTransientMetadata(String roleName) {
return transientMetadataMap.get(roleName);
}

public static GetRolesResponse fromXContent(XContentParser parser) throws IOException {
XContentParserUtils.ensureExpectedToken(XContentParser.Token.START_OBJECT, parser.nextToken(), parser::getTokenLocation);
final List<Role> roles = new ArrayList<>();
final Map<String, Map<String, Object>> transientMetadata = new HashMap<>();
XContentParser.Token token;
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
XContentParserUtils.ensureExpectedToken(XContentParser.Token.FIELD_NAME, token, parser::getTokenLocation);
roles.add(Role.PARSER.parse(parser, parser.currentName()));
final Tuple<Role, Map<String, Object>> roleAndTransientMetadata = Role.PARSER.parse(parser, parser.currentName());
roles.add(roleAndTransientMetadata.v1());
transientMetadata.put(roleAndTransientMetadata.v1().getName(), roleAndTransientMetadata.v2());
}
return new GetRolesResponse(roles);
return new GetRolesResponse(roles, transientMetadata);
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
GetRolesResponse response = (GetRolesResponse) o;
return Objects.equals(roles, response.roles);
return Objects.equals(roles, response.roles)
&& Objects.equals(transientMetadataMap, response.transientMetadataMap);
}

@Override
public int hashCode() {
return Objects.hash(roles);
return Objects.hash(roles, transientMetadataMap);
}
}
Original file line number Diff line number Diff line change
@@ -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.client.Validatable;
import org.elasticsearch.client.security.user.privileges.Role;
import org.elasticsearch.common.Nullable;
import org.elasticsearch.common.xcontent.ToXContentObject;
import org.elasticsearch.common.xcontent.XContentBuilder;

import java.io.IOException;
import java.util.Objects;

/**
* Request object to create or update a role.
*/
public final class PutRoleRequest implements Validatable, ToXContentObject {

private final Role role;
private final RefreshPolicy refreshPolicy;

public PutRoleRequest(Role role, @Nullable final RefreshPolicy refreshPolicy) {
this.role = Objects.requireNonNull(role);
this.refreshPolicy = (refreshPolicy == null) ? RefreshPolicy.getDefault() : refreshPolicy;
}

public Role getRole() {
return role;
}

public RefreshPolicy getRefreshPolicy() {
return refreshPolicy;
}

@Override
public int hashCode() {
return Objects.hash(role, refreshPolicy);
}

@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final PutRoleRequest other = (PutRoleRequest) obj;

return (refreshPolicy == other.getRefreshPolicy()) &&
Objects.equals(role, other.role);
}

@Override
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
builder.startObject();
if (role.getApplicationResourcePrivileges() != null) {
builder.field(Role.APPLICATIONS.getPreferredName(), role.getApplicationResourcePrivileges());
}
if (role.getClusterPrivileges() != null) {
builder.field(Role.CLUSTER.getPreferredName(), role.getClusterPrivileges());
}
if (role.getGlobalApplicationPrivileges() != null) {
builder.field(Role.GLOBAL.getPreferredName(), role.getGlobalApplicationPrivileges());
}
if (role.getIndicesPrivileges() != null) {
builder.field(Role.INDICES.getPreferredName(), role.getIndicesPrivileges());
}
if (role.getMetadata() != null) {
builder.field(Role.METADATA.getPreferredName(), role.getMetadata());
}
if (role.getRunAsPrivilege() != null) {
builder.field(Role.RUN_AS.getPreferredName(), role.getRunAsPrivilege());
}
return builder.endObject();
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
/*
* 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.xcontent.ConstructingObjectParser;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.common.xcontent.XContentParser.Token;

import java.io.IOException;
import java.util.Objects;

import static org.elasticsearch.common.xcontent.ConstructingObjectParser.constructorArg;
import static org.elasticsearch.common.xcontent.XContentParserUtils.ensureExpectedToken;
import static org.elasticsearch.common.xcontent.XContentParserUtils.ensureFieldName;

/**
* Response when adding a role to the native roles store. Returns a
* single boolean field for whether the role was created (true) or updated (false).
*/
public final class PutRoleResponse {

private final boolean created;

public PutRoleResponse(boolean created) {
this.created = created;
}

public boolean isCreated() {
return created;
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
PutRoleResponse that = (PutRoleResponse) o;
return created == that.created;
}

@Override
public int hashCode() {
return Objects.hash(created);
}

private static final ConstructingObjectParser<PutRoleResponse, Void> PARSER = new ConstructingObjectParser<>("put_role_response",
true, args -> new PutRoleResponse((boolean) args[0]));

static {
PARSER.declareBoolean(constructorArg(), new ParseField("created"));
}

public static PutRoleResponse fromXContent(XContentParser parser) throws IOException {
if (parser.currentToken() == null) {
parser.nextToken();
}
// parse extraneous wrapper
ensureExpectedToken(Token.START_OBJECT, parser.currentToken(), parser::getTokenLocation);
ensureFieldName(parser, parser.nextToken(), "role");
parser.nextToken();
final PutRoleResponse roleResponse = PARSER.parse(parser, null);
ensureExpectedToken(Token.END_OBJECT, parser.nextToken(), parser::getTokenLocation);
return roleResponse;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@
import org.elasticsearch.common.xcontent.XContentParser;

import java.io.IOException;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
Expand All @@ -49,7 +48,7 @@ public final class GlobalPrivileges implements ToXContentObject {

// When categories change, adapting this field should suffice. Categories are NOT
// opaque "named_objects", we wish to maintain control over these namespaces
static final List<String> CATEGORIES = Collections.unmodifiableList(Arrays.asList("application"));
public static final List<String> CATEGORIES = Collections.singletonList("application");

@SuppressWarnings("unchecked")
static final ConstructingObjectParser<GlobalPrivileges, Void> PARSER = new ConstructingObjectParser<>("global_category_privileges",
Expand Down Expand Up @@ -134,4 +133,4 @@ public int hashCode() {
return Objects.hash(privileges);
}

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -217,12 +217,12 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws
builder.startObject();
builder.field(NAMES.getPreferredName(), indices);
builder.field(PRIVILEGES.getPreferredName(), privileges);
if (isUsingFieldLevelSecurity()) {
if (grantedFields != null || deniedFields != null) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Although the previous condition was correctly from the server logic stand point, it would hinder testing in IndicesPrivilegesTests.

builder.startObject(FIELD_PERMISSIONS.getPreferredName());
if (grantedFields != null) {
builder.field(GRANT_FIELDS.getPreferredName(), grantedFields);
}
if (hasDeniedFields()) {
if (deniedFields != null) {
builder.field(EXCEPT_FIELDS.getPreferredName(), deniedFields);
}
builder.endObject();
Expand Down
Loading