Skip to content
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

Add get-user-privileges API #33928

Merged
merged 20 commits into from
Oct 18, 2018
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
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
@@ -0,0 +1,26 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.xpack.core.security.action.user;

import org.elasticsearch.action.Action;

/**
* Action that lists the set of privileges held by a user.
*/
public class GetUserPrivilegesAction extends Action<GetUserPrivilegesResponse> {
Copy link
Member

Choose a reason for hiding this comment

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

make it final


public static final GetUserPrivilegesAction INSTANCE = new GetUserPrivilegesAction();
public static final String NAME = "cluster:admin/xpack/security/user/list_privileges";

private GetUserPrivilegesAction() {
super(NAME);
}

@Override
public GetUserPrivilegesResponse newResponse() {
return new GetUserPrivilegesResponse();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.xpack.core.security.action.user;

import org.elasticsearch.action.ActionRequest;
import org.elasticsearch.action.ActionRequestValidationException;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput;

import java.io.IOException;

/**
* A request for checking a user's privileges
*/
public class GetUserPrivilegesRequest extends ActionRequest implements UserRequest {
Copy link
Member

Choose a reason for hiding this comment

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

make it final


private String username;

@Override
public ActionRequestValidationException validate() {
return null;
}

/**
* @return the username that this request applies to.
*/
public String username() {
return username;
}

/**
* Set the username that the request applies to. Must not be {@code null}
*/
public void username(String username) {
this.username = username;
}

@Override
public String[] usernames() {
return new String[] { username };
}

@Override
public void readFrom(StreamInput in) throws IOException {
tvernum marked this conversation as resolved.
Show resolved Hide resolved
super.readFrom(in);
this.username = in.readString();
}

@Override
public void writeTo(StreamOutput out) throws IOException {
super.writeTo(out);
out.writeString(username);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.xpack.core.security.action.user;

import org.elasticsearch.action.ActionRequestBuilder;
import org.elasticsearch.client.ElasticsearchClient;

import java.io.IOException;

/**
* Request builder for checking a user's privileges
*/
public class GetUserPrivilegesRequestBuilder
extends ActionRequestBuilder<GetUserPrivilegesRequest, GetUserPrivilegesResponse> {

public GetUserPrivilegesRequestBuilder(ElasticsearchClient client) {
super(client, GetUserPrivilegesAction.INSTANCE, new GetUserPrivilegesRequest());
}

/**
* Set the username of the user that should enabled or disabled. Must not be {@code null}
*/
public GetUserPrivilegesRequestBuilder username(String username) {
request.username(username);
return this;
}

/**
* Set whether the user should be enabled or not
*/
public GetUserPrivilegesRequestBuilder source(String username) throws IOException {
Copy link
Member

Choose a reason for hiding this comment

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

do we really need this method?

request.username(username);
return this;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,220 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.xpack.core.security.action.user;

import org.elasticsearch.action.ActionResponse;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.bytes.BytesReference;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.common.io.stream.Writeable;
import org.elasticsearch.common.xcontent.ToXContentObject;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.xpack.core.security.authz.RoleDescriptor;
import org.elasticsearch.xpack.core.security.authz.permission.FieldPermissionsDefinition;
import org.elasticsearch.xpack.core.security.authz.privilege.ConditionalClusterPrivilege;
import org.elasticsearch.xpack.core.security.authz.privilege.ConditionalClusterPrivileges;

import java.io.IOException;
import java.util.Collection;
import java.util.Collections;
import java.util.Objects;
import java.util.Set;
import java.util.TreeSet;
import java.util.stream.Collectors;

/**
* Response for a {@link GetUserPrivilegesRequest}
*/
public class GetUserPrivilegesResponse extends ActionResponse {

private Set<String> cluster;
private Set<ConditionalClusterPrivilege> conditionalCluster;
private Set<Indices> index;
private Set<RoleDescriptor.ApplicationResourcePrivileges> application;
private Set<String> runAs;

public GetUserPrivilegesResponse() {
this(Collections.emptySet(), Collections.emptySet(), Collections.emptySet(), Collections.emptySet(), Collections.emptySet());
}

public GetUserPrivilegesResponse(Set<String> cluster, Set<ConditionalClusterPrivilege> conditionalCluster,
Set<Indices> index,
Set<RoleDescriptor.ApplicationResourcePrivileges> application,
Set<String> runAs) {
this.cluster = cluster;
this.conditionalCluster = conditionalCluster;
this.index = index;
this.application = application;
this.runAs = runAs;
}

public Set<String> getClusterPrivileges() {
return Collections.unmodifiableSet(cluster);
}
tvernum marked this conversation as resolved.
Show resolved Hide resolved

public Set<ConditionalClusterPrivilege> getConditionalClusterPrivileges() {
return Collections.unmodifiableSet(conditionalCluster);
}

public Set<Indices> getIndexPrivileges() {
return Collections.unmodifiableSet(index);
}

public Set<RoleDescriptor.ApplicationResourcePrivileges> getApplicationPrivileges() {
return Collections.unmodifiableSet(application);
}

public Set<String> getRunAs() {
return runAs;
}

public void readFrom(StreamInput in) throws IOException {
super.readFrom(in);
cluster = in.readSet(StreamInput::readString);
conditionalCluster = in.readSet(ConditionalClusterPrivileges.READER);
index = in.readSet(Indices::new);
application = in.readSet(RoleDescriptor.ApplicationResourcePrivileges::createFrom);
runAs = in.readSet(StreamInput::readString);
}

@Override
public void writeTo(StreamOutput out) throws IOException {
super.writeTo(out);
out.writeCollection(cluster, StreamOutput::writeString);
out.writeCollection(conditionalCluster, ConditionalClusterPrivileges.WRITER);
out.writeCollection(index, (o, p) -> p.writeTo(o));
out.writeCollection(application, (o, p) -> p.writeTo(o));
out.writeCollection(runAs, StreamOutput::writeString);
}

/**
* This is modelled on {@link RoleDescriptor.IndicesPrivileges}, with support for multiple DLS and FLS field sets.
*/
public static class Indices implements ToXContentObject, Writeable {

private final Collection<String> indices;
Copy link
Member

Choose a reason for hiding this comment

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

just make these all sets?

private final Collection<String> privileges;
private final Collection<FieldPermissionsDefinition.FieldGrantExcludeGroup> fieldSecurity;
private final Collection<BytesReference> queries;

public Indices(Collection<String> indices, Collection<String> privileges,
Set<FieldPermissionsDefinition.FieldGrantExcludeGroup> fieldSecurity, Set<BytesReference> queries) {
this.indices = Collections.unmodifiableSet(new TreeSet<>(Objects.requireNonNull(indices)));
tvernum marked this conversation as resolved.
Show resolved Hide resolved
this.privileges = Collections.unmodifiableSet(new TreeSet<>(Objects.requireNonNull(privileges)));
this.fieldSecurity = Collections.unmodifiableSet(Objects.requireNonNull(fieldSecurity));
this.queries = Collections.unmodifiableSet(Objects.requireNonNull(queries));
}

public Indices(StreamInput in) throws IOException {
indices = Collections.unmodifiableSet(in.readSet(StreamInput::readString));
privileges = Collections.unmodifiableSet(in.readSet(StreamInput::readString));
fieldSecurity = Collections.unmodifiableSet(in.readSet(input -> {
final String[] grant = input.readOptionalStringArray();
final String[] exclude = input.readOptionalStringArray();
return new FieldPermissionsDefinition.FieldGrantExcludeGroup(grant, exclude);
}));
queries = Collections.unmodifiableSet(in.readSet(StreamInput::readBytesReference));
}

public Collection<String> getIndices() {
return indices;
}

public Collection<String> getPrivileges() {
return privileges;
}

public Collection<FieldPermissionsDefinition.FieldGrantExcludeGroup> getFieldSecurity() {
return fieldSecurity;
}

public Collection<BytesReference> getQueries() {
return queries;
}

@Override
public String toString() {
StringBuilder sb = new StringBuilder(getClass().getSimpleName())
.append("[")
.append("indices=[").append(Strings.collectionToCommaDelimitedString(indices))
.append("], privileges=[").append(Strings.collectionToCommaDelimitedString(privileges))
.append("]");
if (fieldSecurity.isEmpty() == false) {
sb.append(", fls=[").append(Strings.collectionToCommaDelimitedString(fieldSecurity)).append("]");
}
if (queries.isEmpty() == false) {
sb.append(", dls=[")
.append(queries.stream().map(BytesReference::utf8ToString).collect(Collectors.joining(",")))
.append("]");
}
sb.append("]");
return sb.toString();
}

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

Indices that = (Indices) o;

return this.indices.equals(that.indices)
&& this.privileges.equals(that.privileges)
&& this.fieldSecurity.equals(that.fieldSecurity)
&& this.queries.equals(that.queries);
}

@Override
public int hashCode() {
return Objects.hash(indices, privileges, fieldSecurity, queries);
}

@Override
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
builder.startObject();
builder.field(RoleDescriptor.Fields.NAMES.getPreferredName(), indices);
builder.field(RoleDescriptor.Fields.PRIVILEGES.getPreferredName(), privileges);
if (fieldSecurity.stream().anyMatch(g -> nonEmpty(g.getGrantedFields()) || nonEmpty(g.getExcludedFields()))) {
builder.startArray(RoleDescriptor.Fields.FIELD_PERMISSIONS.getPreferredName());
for (FieldPermissionsDefinition.FieldGrantExcludeGroup group : this.fieldSecurity) {
builder.startObject();
if (nonEmpty(group.getGrantedFields())) {
builder.array(RoleDescriptor.Fields.GRANT_FIELDS.getPreferredName(), group.getGrantedFields());
}
if (nonEmpty(group.getExcludedFields())) {
builder.array(RoleDescriptor.Fields.EXCEPT_FIELDS.getPreferredName(), group.getExcludedFields());
}
builder.endObject();
}
builder.endArray();
}
if (queries.isEmpty() == false) {
builder.startArray(RoleDescriptor.Fields.QUERY.getPreferredName());
for (BytesReference q : queries) {
builder.value(q.utf8ToString());
}
builder.endArray();
}
return builder.endObject();
}

private boolean nonEmpty(String[] grantedFields) {
return grantedFields != null && grantedFields.length != 0;
}

@Override
public void writeTo(StreamOutput out) throws IOException {
out.writeCollection(indices, StreamOutput::writeString);
out.writeCollection(privileges, StreamOutput::writeString);
out.writeCollection(fieldSecurity, (output, fields) -> {
output.writeOptionalStringArray(fields.getGrantedFields());
output.writeOptionalStringArray(fields.getExcludedFields());
});
out.writeCollection(queries, StreamOutput::writeBytesReference);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -763,6 +763,10 @@ public Builder privileges(String... privileges) {
return this;
}

public Builder privileges(Collection<String> privileges) {
return privileges(privileges.toArray(new String[privileges.size()]));
}

public Builder grantedFields(String... grantedFields) {
indicesPrivileges.grantedFields = grantedFields;
return this;
Expand Down Expand Up @@ -919,7 +923,7 @@ public Builder resources(String... resources) {
return this;
}

public Builder resources(List<String> resources) {
public Builder resources(Collection<String> resources) {
return resources(resources.toArray(new String[resources.size()]));
}

Expand Down
Loading