-
Notifications
You must be signed in to change notification settings - Fork 25.2k
HLRest: model role and privileges #35128
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
albertzaharovits
merged 34 commits into
elastic:master
from
albertzaharovits:hlrc_put_role
Nov 11, 2018
Merged
Changes from all commits
Commits
Show all changes
34 commits
Select commit
Hold shift + click to select a range
706b736
ClusterPrivilege
albertzaharovits 21bc280
IndexPrivilege
albertzaharovits 9e174eb
Index Privileges
albertzaharovits 64a91e2
ApplicationResourcePrivilege WIP
albertzaharovits d98232b
Merge branch 'master' into hlrc_put_role
albertzaharovits aabbc5f
IndicesPrivileges is neat!
albertzaharovits 9883726
Oh my!
albertzaharovits ba34f65
Proper manage app priv
albertzaharovits e03a05b
Bare RoleDescriptor
albertzaharovits a7b176b
Slightly less bare RoleDescriptor
albertzaharovits 701f85e
All Collections are Lists, well..
albertzaharovits 523f108
WIP :((
albertzaharovits ee37904
Fields are Set
albertzaharovits 36f7be5
Role entity
albertzaharovits 395e29c
Merge branch 'master' into hlrc_put_role
albertzaharovits 4604127
Follow Tim's advice
albertzaharovits beaa373
Follow Tim's advice
albertzaharovits 05ccbd0
Removed ManageApplicationsPrivilege
albertzaharovits 4c339e9
Rename to GlobalPrivileges
albertzaharovits 9b75cdb
public ApplicationResourcePrivileges constructor
albertzaharovits 4839eaa
Merge branch 'master' into hlrc_put_role
albertzaharovits 192295a
Merge branch 'master' into hlrc_put_role
albertzaharovits d83113d
Nits + javadoc
albertzaharovits 1d9f874
ApplicationResourcePrivilegesTests
albertzaharovits d4170f2
WIP
albertzaharovits 9869124
More tests
albertzaharovits 3ead50c
More docs
albertzaharovits b68a3cf
WIP
albertzaharovits 769a0e1
Merge branch 'master' into hlrc_put_role
albertzaharovits 24351b4
Done!
albertzaharovits 36d3203
Merge branch 'master' into hlrc_put_role
albertzaharovits 20f14f7
Javadocs and privilegesByCategoryMap as member
albertzaharovits 59014c6
Merge branch 'master' into hlrc_put_role
albertzaharovits 7fd629a
Merge branch 'master' into hlrc_put_role
albertzaharovits File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
156 changes: 156 additions & 0 deletions
156
...java/org/elasticsearch/client/security/user/privileges/ApplicationResourcePrivileges.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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.user.privileges; | ||
|
||
import org.elasticsearch.common.ParseField; | ||
import org.elasticsearch.common.Strings; | ||
import org.elasticsearch.common.xcontent.ConstructingObjectParser; | ||
import org.elasticsearch.common.xcontent.ToXContentObject; | ||
import org.elasticsearch.common.xcontent.XContentBuilder; | ||
import org.elasticsearch.common.xcontent.XContentHelper; | ||
import org.elasticsearch.common.xcontent.XContentParser; | ||
import org.elasticsearch.common.xcontent.XContentType; | ||
|
||
import java.io.IOException; | ||
import java.util.Collection; | ||
import java.util.Collections; | ||
import java.util.HashSet; | ||
import java.util.Objects; | ||
import java.util.Set; | ||
|
||
import static org.elasticsearch.common.xcontent.ConstructingObjectParser.constructorArg; | ||
|
||
/** | ||
* Represents privileges over resources that are scoped under an application. | ||
* The application, resources and privileges are completely managed by the | ||
* client and can be arbitrary string identifiers. Elasticsearch is not | ||
* concerned by any resources under an application scope. | ||
*/ | ||
public final class ApplicationResourcePrivileges implements ToXContentObject { | ||
|
||
private static final ParseField APPLICATION = new ParseField("application"); | ||
private static final ParseField PRIVILEGES = new ParseField("privileges"); | ||
private static final ParseField RESOURCES = new ParseField("resources"); | ||
|
||
@SuppressWarnings("unchecked") | ||
static final ConstructingObjectParser<ApplicationResourcePrivileges, Void> PARSER = new ConstructingObjectParser<>( | ||
"application_privileges", false, constructorObjects -> { | ||
// Don't ignore unknown fields. It is dangerous if the object we parse is also | ||
// part of a request that we build later on, and the fields that we now ignore will | ||
// end up being implicitly set to null in that request. | ||
int i = 0; | ||
final String application = (String) constructorObjects[i++]; | ||
final Collection<String> privileges = (Collection<String>) constructorObjects[i++]; | ||
final Collection<String> resources = (Collection<String>) constructorObjects[i]; | ||
return new ApplicationResourcePrivileges(application, privileges, resources); | ||
}); | ||
|
||
static { | ||
PARSER.declareString(constructorArg(), APPLICATION); | ||
PARSER.declareStringArray(constructorArg(), PRIVILEGES); | ||
PARSER.declareStringArray(constructorArg(), RESOURCES); | ||
} | ||
|
||
private final String application; | ||
private final Set<String> privileges; | ||
private final Set<String> resources; | ||
|
||
/** | ||
* Constructs privileges for resources under an application scope. | ||
* | ||
* @param application | ||
* The application name. This identifier is completely under the | ||
* clients control. | ||
* @param privileges | ||
* The privileges names. Cannot be null or empty. Privilege | ||
* identifiers are completely under the clients control. | ||
* @param resources | ||
* The resources names. Cannot be null or empty. Resource identifiers | ||
* are completely under the clients control. | ||
*/ | ||
public ApplicationResourcePrivileges(String application, Collection<String> privileges, Collection<String> resources) { | ||
if (Strings.isNullOrEmpty(application)) { | ||
throw new IllegalArgumentException("application privileges must have an application name"); | ||
} | ||
if (null == privileges || privileges.isEmpty()) { | ||
throw new IllegalArgumentException("application privileges must define at least one privilege"); | ||
} | ||
if (null == resources || resources.isEmpty()) { | ||
throw new IllegalArgumentException("application privileges must refer to at least one resource"); | ||
} | ||
this.application = application; | ||
this.privileges = Collections.unmodifiableSet(new HashSet<>(privileges)); | ||
this.resources = Collections.unmodifiableSet(new HashSet<>(resources)); | ||
} | ||
|
||
public String getApplication() { | ||
return application; | ||
} | ||
|
||
public Set<String> getResources() { | ||
return this.resources; | ||
} | ||
|
||
public Set<String> getPrivileges() { | ||
return this.privileges; | ||
} | ||
|
||
@Override | ||
public boolean equals(Object o) { | ||
if (this == o) { | ||
return true; | ||
} | ||
if (o == null || this.getClass() != o.getClass()) { | ||
return false; | ||
} | ||
ApplicationResourcePrivileges that = (ApplicationResourcePrivileges) o; | ||
return application.equals(that.application) | ||
&& privileges.equals(that.privileges) | ||
&& resources.equals(that.resources); | ||
} | ||
|
||
@Override | ||
public int hashCode() { | ||
return Objects.hash(application, privileges, resources); | ||
} | ||
|
||
@Override | ||
public String toString() { | ||
try { | ||
return XContentHelper.toXContent(this, XContentType.JSON, true).utf8ToString(); | ||
} catch (IOException e) { | ||
throw new RuntimeException("Unexpected", e); | ||
} | ||
} | ||
|
||
@Override | ||
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { | ||
builder.startObject(); | ||
builder.field(APPLICATION.getPreferredName(), application); | ||
builder.field(PRIVILEGES.getPreferredName(), privileges); | ||
builder.field(RESOURCES.getPreferredName(), resources); | ||
return builder.endObject(); | ||
} | ||
|
||
public static ApplicationResourcePrivileges fromXContent(XContentParser parser) { | ||
return PARSER.apply(parser, null); | ||
} | ||
|
||
} |
99 changes: 99 additions & 0 deletions
99
...main/java/org/elasticsearch/client/security/user/privileges/GlobalOperationPrivilege.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
/* | ||
* 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.user.privileges; | ||
|
||
import org.elasticsearch.common.xcontent.XContentParser; | ||
|
||
import java.io.IOException; | ||
import java.util.Collections; | ||
import java.util.Map; | ||
import java.util.Objects; | ||
|
||
/** | ||
* Represents generic global cluster privileges that can be scoped by categories | ||
* and then further by operations. The privilege's syntactic and semantic | ||
* meaning is specific to each category and operation; there is no general | ||
* definition template. It is not permitted to define different privileges under | ||
* the same category and operation. | ||
*/ | ||
public class GlobalOperationPrivilege { | ||
|
||
private final String category; | ||
private final String operation; | ||
private final Map<String, Object> privilege; | ||
|
||
/** | ||
* Constructs privileges under a specific {@code category} and for some | ||
* {@code operation}. The privilege definition is flexible, it is a {@code Map}, | ||
* and the semantics is bound to the {@code category} and {@code operation}. | ||
* | ||
* @param category | ||
* The category of the privilege. | ||
* @param operation | ||
* The operation of the privilege. | ||
* @param privilege | ||
* The privilege definition. | ||
*/ | ||
public GlobalOperationPrivilege(String category, String operation, Map<String, Object> privilege) { | ||
this.category = Objects.requireNonNull(category); | ||
this.operation = Objects.requireNonNull(operation); | ||
if (privilege == null || privilege.isEmpty()) { | ||
throw new IllegalArgumentException("Privileges cannot be empty or null"); | ||
} | ||
this.privilege = Collections.unmodifiableMap(privilege); | ||
} | ||
|
||
public String getCategory() { | ||
return category; | ||
} | ||
|
||
public String getOperation() { | ||
return operation; | ||
} | ||
|
||
public Map<String, Object> getRaw() { | ||
return privilege; | ||
} | ||
|
||
public static GlobalOperationPrivilege fromXContent(String category, String operation, XContentParser parser) throws IOException { | ||
// parser is still placed on the field name, advance to next token (field value) | ||
assert parser.currentToken().equals(XContentParser.Token.FIELD_NAME); | ||
parser.nextToken(); | ||
return new GlobalOperationPrivilege(category, operation, parser.map()); | ||
} | ||
|
||
@Override | ||
public boolean equals(Object o) { | ||
if (this == o) { | ||
return true; | ||
} | ||
if (o == null || (false == this instanceof GlobalOperationPrivilege)) { | ||
return false; | ||
} | ||
final GlobalOperationPrivilege that = (GlobalOperationPrivilege) o; | ||
return category.equals(that.category) && operation.equals(that.operation) && privilege.equals(that.privilege); | ||
} | ||
|
||
@Override | ||
public int hashCode() { | ||
return Objects.hash(category, operation, privilege); | ||
} | ||
|
||
} |
137 changes: 137 additions & 0 deletions
137
...vel/src/main/java/org/elasticsearch/client/security/user/privileges/GlobalPrivileges.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,137 @@ | ||
/* | ||
* 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.user.privileges; | ||
|
||
import org.elasticsearch.common.ParseField; | ||
import org.elasticsearch.common.xcontent.ConstructingObjectParser; | ||
import org.elasticsearch.common.xcontent.ToXContentObject; | ||
import org.elasticsearch.common.xcontent.XContentBuilder; | ||
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; | ||
import java.util.List; | ||
import java.util.Map; | ||
import java.util.Objects; | ||
import java.util.Set; | ||
import java.util.stream.Collectors; | ||
|
||
import static org.elasticsearch.common.xcontent.ConstructingObjectParser.optionalConstructorArg; | ||
|
||
/** | ||
* Represents global privileges. "Global Privilege" is a mantra for granular | ||
* generic cluster privileges. These privileges are organized into categories. | ||
* Elasticsearch defines the set of categories. Under each category there are | ||
* operations that are under the clients jurisdiction. The privilege is hence | ||
* defined under an operation under a category. | ||
*/ | ||
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")); | ||
|
||
@SuppressWarnings("unchecked") | ||
static final ConstructingObjectParser<GlobalPrivileges, Void> PARSER = new ConstructingObjectParser<>("global_category_privileges", | ||
false, constructorObjects -> { | ||
// ignore_unknown_fields is irrelevant here anyway, but let's keep it to false | ||
// because this conveys strictness (woop woop) | ||
return new GlobalPrivileges((Collection<GlobalOperationPrivilege>) constructorObjects[0]); | ||
}); | ||
|
||
static { | ||
for (final String category : CATEGORIES) { | ||
PARSER.declareNamedObjects(optionalConstructorArg(), | ||
(parser, context, operation) -> GlobalOperationPrivilege.fromXContent(category, operation, parser), | ||
new ParseField(category)); | ||
} | ||
} | ||
|
||
private final Set<? extends GlobalOperationPrivilege> privileges; | ||
// same data as in privileges but broken down by categories; internally, it is | ||
// easier to work with this structure | ||
private final Map<String, List<GlobalOperationPrivilege>> privilegesByCategoryMap; | ||
|
||
/** | ||
* Constructs global privileges by bundling the set of privileges. | ||
* | ||
* @param privileges | ||
* The privileges under a category and for an operation in that category. | ||
*/ | ||
public GlobalPrivileges(Collection<? extends GlobalOperationPrivilege> privileges) { | ||
if (privileges == null || privileges.isEmpty()) { | ||
throw new IllegalArgumentException("Privileges cannot be empty or null"); | ||
} | ||
// duplicates are just ignored | ||
this.privileges = Collections.unmodifiableSet(new HashSet<>(Objects.requireNonNull(privileges))); | ||
this.privilegesByCategoryMap = Collections | ||
.unmodifiableMap(this.privileges.stream().collect(Collectors.groupingBy(GlobalOperationPrivilege::getCategory))); | ||
for (final Map.Entry<String, List<GlobalOperationPrivilege>> privilegesByCategory : privilegesByCategoryMap.entrySet()) { | ||
// all operations for a specific category | ||
final Set<String> allOperations = privilegesByCategory.getValue().stream().map(p -> p.getOperation()) | ||
.collect(Collectors.toSet()); | ||
if (allOperations.size() != privilegesByCategory.getValue().size()) { | ||
throw new IllegalArgumentException("Different privileges for the same category and operation are not permitted"); | ||
} | ||
} | ||
} | ||
|
||
@Override | ||
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { | ||
builder.startObject(); | ||
for (final Map.Entry<String, List<GlobalOperationPrivilege>> privilegesByCategory : this.privilegesByCategoryMap.entrySet()) { | ||
builder.startObject(privilegesByCategory.getKey()); | ||
for (final GlobalOperationPrivilege privilege : privilegesByCategory.getValue()) { | ||
builder.field(privilege.getOperation(), privilege.getRaw()); | ||
} | ||
builder.endObject(); | ||
} | ||
return builder.endObject(); | ||
} | ||
|
||
public static GlobalPrivileges fromXContent(XContentParser parser) { | ||
return PARSER.apply(parser, null); | ||
} | ||
|
||
public Set<? extends GlobalOperationPrivilege> getPrivileges() { | ||
return privileges; | ||
} | ||
|
||
@Override | ||
public boolean equals(Object o) { | ||
if (this == o) { | ||
return true; | ||
} | ||
if (o == null || this.getClass() != o.getClass()) { | ||
return false; | ||
} | ||
final GlobalPrivileges that = (GlobalPrivileges) o; | ||
return privileges.equals(that.privileges); | ||
} | ||
|
||
@Override | ||
public int hashCode() { | ||
return Objects.hash(privileges); | ||
} | ||
|
||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.
This is not true.
The definition
Instructs Elasticsearch that this role is allowed to manage application privileges under the application names "kibana-*".
It's not an instruction for Kibana, it's enforced by Elasticsearch.
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 was wrong, I got carried astray by too much abstraction.
This is now: