Skip to content

Add granular privileges for API keys #42020

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

Closed
wants to merge 32 commits into from
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
f58d5e0
Add granular privileges for API keys
May 8, 2019
167b98e
add check for realm name in conditional predicate
May 9, 2019
08e1163
fix test
May 9, 2019
5be1187
Merge branch 'master' into api-key-privileges
May 9, 2019
25e97ab
style fix
May 10, 2019
7cbb19e
add action instead of flags for conditional privilege
May 17, 2019
a125018
fix line length
May 17, 2019
eecbdbe
Merge branch 'master' into api-key-privileges
May 17, 2019
cccdb96
fix parser
May 17, 2019
9062acb
fix integ test
May 18, 2019
06c3c66
remove unwanted check and add check that realms and users contain onl…
May 29, 2019
68109f9
Merge branch 'master' into api-key-privileges
May 29, 2019
be7a093
provide default conditional cluster privileges for API keys
Jun 3, 2019
13a86f3
Merge branch 'master' into api-key-privileges
Jun 3, 2019
9958b81
fix error message and compilation issue
Jun 3, 2019
d69224e
keep the conditional cluster privileges same and introduce plain cond…
Jun 3, 2019
8f6caad
revert unwanted change
Jun 3, 2019
04fe24d
fix checkstyle errors
Jun 3, 2019
8952ffd
add test, javadocs, cleanup
Jun 4, 2019
3d977ec
Merge branch 'master' into api-key-privileges
Jun 11, 2019
108278c
address review comments - remove unwanted api conditional cluster pri…
Jun 11, 2019
07bb117
change ConditionalClusterPrivilege to GlobalClusterPrivilege and Plai…
Jun 11, 2019
aa07ee3
refactor cluster privilege and extract enum for default cluster privi…
Jun 11, 2019
8aeeafa
add docs
Jun 11, 2019
b20f4c3
address review comments
Jun 18, 2019
598f3de
Merge branch 'master' into api-key-privileges
Jun 18, 2019
3a1c361
remove unwanted code
Jun 18, 2019
e39e263
fix code and tests
Jun 18, 2019
9826c81
resolve precommit errors
Jun 18, 2019
d69db4e
address intermittent failure
Jun 18, 2019
fa8386b
Merge branch 'master' into api-key-privileges
Jun 18, 2019
aa14195
refactor - to address review comments
Jun 19, 2019
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 @@ -312,14 +312,15 @@ public static class ClusterPrivilegeName {
public static final String MANAGE_SECURITY = "manage_security";
public static final String MANAGE_SAML = "manage_saml";
public static final String MANAGE_TOKEN = "manage_token";
public static final String MANAGE_API_KEY = "manage_api_key";
public static final String MANAGE_PIPELINE = "manage_pipeline";
public static final String MANAGE_CCR = "manage_ccr";
public static final String READ_CCR = "read_ccr";
public static final String MANAGE_ILM = "manage_ilm";
public static final String READ_ILM = "read_ilm";
public static final String[] ALL_ARRAY = new String[] { NONE, ALL, MONITOR, MONITOR_ML, MONITOR_WATCHER, MONITOR_ROLLUP, MANAGE,
MANAGE_ML, MANAGE_WATCHER, MANAGE_ROLLUP, MANAGE_INDEX_TEMPLATES, MANAGE_INGEST_PIPELINES, TRANSPORT_CLIENT,
MANAGE_SECURITY, MANAGE_SAML, MANAGE_TOKEN, MANAGE_PIPELINE, MANAGE_CCR, READ_CCR, MANAGE_ILM, READ_ILM };
MANAGE_SECURITY, MANAGE_SAML, MANAGE_TOKEN, MANAGE_API_KEY, MANAGE_PIPELINE, MANAGE_CCR, READ_CCR, MANAGE_ILM, READ_ILM };
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,8 @@
import org.elasticsearch.xpack.core.security.authc.support.mapper.expressiondsl.FieldExpression;
import org.elasticsearch.xpack.core.security.authc.support.mapper.expressiondsl.RoleMapperExpression;
import org.elasticsearch.xpack.core.security.authz.privilege.ConditionalClusterPrivilege;
import org.elasticsearch.xpack.core.security.authz.privilege.ConditionalClusterPrivileges;
import org.elasticsearch.xpack.core.security.authz.privilege.ManageApiKeyConditionalPrivileges;
import org.elasticsearch.xpack.core.security.authz.privilege.ManageApplicationPrivileges;
import org.elasticsearch.xpack.core.security.transport.netty4.SecurityNetty4Transport;
import org.elasticsearch.xpack.core.sql.SqlFeatureSetUsage;
import org.elasticsearch.xpack.core.ssl.SSLService;
Expand Down Expand Up @@ -414,8 +415,11 @@ public List<NamedWriteableRegistry.Entry> getNamedWriteables() {
new NamedWriteableRegistry.Entry(XPackFeatureSet.Usage.class, XPackField.SECURITY, SecurityFeatureSetUsage::new),
// security : conditional privileges
new NamedWriteableRegistry.Entry(ConditionalClusterPrivilege.class,
ConditionalClusterPrivileges.ManageApplicationPrivileges.WRITEABLE_NAME,
ConditionalClusterPrivileges.ManageApplicationPrivileges::createFrom),
ManageApplicationPrivileges.WRITEABLE_NAME,
ManageApplicationPrivileges::createFrom),
new NamedWriteableRegistry.Entry(ConditionalClusterPrivilege.class,
ManageApiKeyConditionalPrivileges.WRITEABLE_NAME,
ManageApiKeyConditionalPrivileges::createFrom),
// security : role-mappings
new NamedWriteableRegistry.Entry(RoleMapperExpression.class, AllExpression.NAME, AllExpression::new),
new NamedWriteableRegistry.Entry(RoleMapperExpression.class, AnyExpression.NAME, AnyExpression::new),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,16 +117,6 @@ public ActionRequestValidationException validate() {
validationException = addValidationError("One of [api key id, api key name, username, realm name] must be specified",
validationException);
}
if (Strings.hasText(apiKeyId) || Strings.hasText(apiKeyName)) {
if (Strings.hasText(realmName) || Strings.hasText(userName)) {
validationException = addValidationError(
"username or realm name must not be specified when the api key id or api key name is specified",
validationException);
}
}
if (Strings.hasText(apiKeyId) && Strings.hasText(apiKeyName)) {
validationException = addValidationError("only one of [api key id, api key name] can be specified", validationException);
}
return validationException;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,16 +117,6 @@ public ActionRequestValidationException validate() {
validationException = addValidationError("One of [api key id, api key name, username, realm name] must be specified",
validationException);
}
if (Strings.hasText(id) || Strings.hasText(name)) {
if (Strings.hasText(realmName) || Strings.hasText(userName)) {
validationException = addValidationError(
"username or realm name must not be specified when the api key id or api key name is specified",
validationException);
}
}
if (Strings.hasText(id) && Strings.hasText(name)) {
validationException = addValidationError("only one of [api key id, api key name] can be specified", validationException);
}
return validationException;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import org.apache.lucene.util.automaton.Operations;
import org.elasticsearch.common.collect.Tuple;
import org.elasticsearch.transport.TransportRequest;
import org.elasticsearch.xpack.core.security.authc.Authentication;
import org.elasticsearch.xpack.core.security.authz.privilege.ClusterPrivilege;
import org.elasticsearch.xpack.core.security.authz.privilege.ConditionalClusterPrivilege;

Expand All @@ -32,7 +33,7 @@ public ClusterPrivilege privilege() {
return privilege;
}

public abstract boolean check(String action, TransportRequest request);
public abstract boolean check(String action, TransportRequest request, Authentication authentication);

public boolean grants(ClusterPrivilege clusterPrivilege) {
return Operations.subsetOf(clusterPrivilege.getAutomaton(), this.privilege().getAutomaton());
Expand All @@ -55,7 +56,7 @@ public static class SimpleClusterPermission extends ClusterPermission {
}

@Override
public boolean check(String action, TransportRequest request) {
public boolean check(String action, TransportRequest request, Authentication authentication) {
return predicate.test(action);
}

Expand All @@ -77,8 +78,8 @@ public ConditionalClusterPermission(ConditionalClusterPrivilege conditionalPrivi
}

@Override
public boolean check(String action, TransportRequest request) {
return super.privilege.predicate().test(action) && conditionalPrivilege.getRequestPredicate().test(request);
public boolean check(String action, TransportRequest request, Authentication authentication) {
return super.privilege.predicate().test(action) && conditionalPrivilege.getRequestPredicate().test(request, authentication);
}

@Override
Expand Down Expand Up @@ -113,8 +114,8 @@ public List<Tuple<ClusterPrivilege, ConditionalClusterPrivilege>> privileges() {
}

@Override
public boolean check(String action, TransportRequest request) {
return children.stream().anyMatch(p -> p.check(action, request));
public boolean check(String action, TransportRequest request, Authentication authentication) {
return children.stream().anyMatch(p -> p.check(action, request, authentication));
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import org.apache.lucene.util.automaton.Automaton;
import org.elasticsearch.cluster.metadata.AliasOrIndex;
import org.elasticsearch.transport.TransportRequest;
import org.elasticsearch.xpack.core.security.authc.Authentication;
import org.elasticsearch.xpack.core.security.authz.accesscontrol.IndicesAccessControl;
import org.elasticsearch.xpack.core.security.authz.privilege.ApplicationPrivilegeDescriptor;
import org.elasticsearch.xpack.core.security.authz.privilege.ClusterPrivilege;
Expand Down Expand Up @@ -129,8 +130,8 @@ public ResourcePrivilegesMap checkIndicesPrivileges(Set<String> checkForIndexPat
* @return {@code true} if action is allowed else returns {@code false}
*/
@Override
public boolean checkClusterAction(String action, TransportRequest request) {
return super.checkClusterAction(action, request) && limitedBy.checkClusterAction(action, request);
public boolean checkClusterAction(String action, TransportRequest request, Authentication authentication) {
return super.checkClusterAction(action, request, authentication) && limitedBy.checkClusterAction(action, request, authentication);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import org.elasticsearch.common.collect.Tuple;
import org.elasticsearch.common.util.set.Sets;
import org.elasticsearch.transport.TransportRequest;
import org.elasticsearch.xpack.core.security.authc.Authentication;
import org.elasticsearch.xpack.core.security.authz.RoleDescriptor;
import org.elasticsearch.xpack.core.security.authz.accesscontrol.IndicesAccessControl;
import org.elasticsearch.xpack.core.security.authz.privilege.ApplicationPrivilege;
Expand Down Expand Up @@ -124,10 +125,11 @@ public ResourcePrivilegesMap checkIndicesPrivileges(Set<String> checkForIndexPat
*
* @param action cluster action
* @param request {@link TransportRequest}
* @param authentication {@link Authentication} current authenticated user
* @return {@code true} if action is allowed else returns {@code false}
*/
public boolean checkClusterAction(String action, TransportRequest request) {
return cluster.check(action, request);
public boolean checkClusterAction(String action, TransportRequest request, Authentication authentication) {
return cluster.check(action, request, authentication);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@ public final class ClusterPrivilege extends Privilege {
InvalidateTokenAction.NAME, RefreshTokenAction.NAME);
private static final Automaton MANAGE_OIDC_AUTOMATON = patterns("cluster:admin/xpack/security/oidc/*");
private static final Automaton MANAGE_TOKEN_AUTOMATON = patterns("cluster:admin/xpack/security/token/*");

private static final Automaton MANAGE_API_KEY_AUTOMATON = patterns("cluster:admin/xpack/security/api_key/*");

private static final Automaton MONITOR_AUTOMATON = patterns("cluster:monitor/*");
private static final Automaton MONITOR_ML_AUTOMATON = patterns("cluster:monitor/xpack/ml/*");
private static final Automaton MONITOR_DATA_FRAME_AUTOMATON = patterns("cluster:monitor/data_frame/*");
Expand Down Expand Up @@ -74,6 +77,7 @@ public final class ClusterPrivilege extends Privilege {
public static final ClusterPrivilege MANAGE_DATA_FRAME =
new ClusterPrivilege("manage_data_frame_transforms", MANAGE_DATA_FRAME_AUTOMATON);
public static final ClusterPrivilege MANAGE_TOKEN = new ClusterPrivilege("manage_token", MANAGE_TOKEN_AUTOMATON);
public static final ClusterPrivilege MANAGE_API_KEY = new ClusterPrivilege("manage_api_key", MANAGE_API_KEY_AUTOMATON);
public static final ClusterPrivilege MANAGE_WATCHER = new ClusterPrivilege("manage_watcher", MANAGE_WATCHER_AUTOMATON);
public static final ClusterPrivilege MANAGE_ROLLUP = new ClusterPrivilege("manage_rollup", MANAGE_ROLLUP_AUTOMATON);
public static final ClusterPrivilege MANAGE_IDX_TEMPLATES =
Expand Down Expand Up @@ -105,6 +109,7 @@ public final class ClusterPrivilege extends Privilege {
entry("manage_ml", MANAGE_ML),
entry("manage_data_frame_transforms", MANAGE_DATA_FRAME),
entry("manage_token", MANAGE_TOKEN),
entry("manage_api_key", MANAGE_API_KEY),
entry("manage_watcher", MANAGE_WATCHER),
entry("manage_index_templates", MANAGE_IDX_TEMPLATES),
entry("manage_ingest_pipelines", MANAGE_INGEST_PIPELINES),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,11 @@
import org.elasticsearch.common.xcontent.ToXContentFragment;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.transport.TransportRequest;
import org.elasticsearch.xpack.core.security.authc.Authentication;

import java.io.IOException;
import java.util.Collection;
import java.util.function.BiPredicate;
import java.util.function.Predicate;

/**
Expand All @@ -34,9 +36,10 @@ public interface ConditionalClusterPrivilege extends NamedWriteable, ToXContentF
ClusterPrivilege getPrivilege();

/**
* The request-level privilege (as a {@link Predicate}) that is required by this conditional privilege.
* The request-level privilege (as a {@link BiPredicate}) that is required by this conditional privilege.
* Conditions can also be evaluated based on the {@link Authentication} details.
*/
Predicate<TransportRequest> getRequestPredicate();
BiPredicate<TransportRequest, Authentication> getRequestPredicate();

/**
* A {@link ConditionalClusterPrivilege} should generate a fragment of {@code XContent}, which consists of
Expand All @@ -52,7 +55,8 @@ public interface ConditionalClusterPrivilege extends NamedWriteable, ToXContentF
* from the categories.
*/
enum Category {
APPLICATION(new ParseField("application"));
APPLICATION(new ParseField("application")),
API_KEYS(new ParseField("api_keys"));

public final ParseField field;

Expand Down
Loading