[HLRC] Add support for get roles API (#35787)
This commits adds support for the Get Roles API to the HLRC Relates: #29827
This commit is contained in:
parent
dfd93deabe
commit
8daa854f90
|
@ -42,6 +42,8 @@ import org.elasticsearch.client.security.GetPrivilegesRequest;
|
|||
import org.elasticsearch.client.security.GetPrivilegesResponse;
|
||||
import org.elasticsearch.client.security.GetRoleMappingsRequest;
|
||||
import org.elasticsearch.client.security.GetRoleMappingsResponse;
|
||||
import org.elasticsearch.client.security.GetRolesRequest;
|
||||
import org.elasticsearch.client.security.GetRolesResponse;
|
||||
import org.elasticsearch.client.security.GetSslCertificatesRequest;
|
||||
import org.elasticsearch.client.security.GetSslCertificatesResponse;
|
||||
import org.elasticsearch.client.security.HasPrivilegesRequest;
|
||||
|
@ -407,6 +409,35 @@ public final class SecurityClient {
|
|||
DeleteRoleMappingResponse::fromXContent, emptySet());
|
||||
}
|
||||
|
||||
/**
|
||||
* Asynchronously retrieves roles from the native roles store.
|
||||
* See <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/security-api-get-role.html">
|
||||
* the docs</a> for more.
|
||||
*
|
||||
* @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
|
||||
* @param listener the listener to be notified upon request completion
|
||||
*/
|
||||
public void getRolesAsync(GetRolesRequest request, RequestOptions options, ActionListener<GetRolesResponse> listener) {
|
||||
restHighLevelClient.performRequestAsyncAndParseEntity(request, SecurityRequestConverters::getRoles, options,
|
||||
GetRolesResponse::fromXContent, listener, emptySet());
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves roles from the native roles store.
|
||||
* See <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/security-api-get-role.html">
|
||||
* the docs</a> for more.
|
||||
*
|
||||
* @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
|
||||
* @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 delete a role mapping.
|
||||
* See <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/security-api-delete-role-mapping.html">
|
||||
|
|
|
@ -32,6 +32,7 @@ import org.elasticsearch.client.security.GetPrivilegesRequest;
|
|||
import org.elasticsearch.client.security.DeleteRoleMappingRequest;
|
||||
import org.elasticsearch.client.security.DeleteRoleRequest;
|
||||
import org.elasticsearch.client.security.InvalidateTokenRequest;
|
||||
import org.elasticsearch.client.security.GetRolesRequest;
|
||||
import org.elasticsearch.client.security.PutRoleMappingRequest;
|
||||
import org.elasticsearch.client.security.HasPrivilegesRequest;
|
||||
import org.elasticsearch.client.security.DisableUserRequest;
|
||||
|
@ -170,6 +171,15 @@ final class SecurityRequestConverters {
|
|||
return request;
|
||||
}
|
||||
|
||||
static Request getRoles(GetRolesRequest getRolesRequest) {
|
||||
RequestConverters.EndpointBuilder builder = new RequestConverters.EndpointBuilder();
|
||||
builder.addPathPartAsIs("_xpack/security/role");
|
||||
if (getRolesRequest.getRoleNames().size() > 0) {
|
||||
builder.addPathPart(Strings.collectionToCommaDelimitedString(getRolesRequest.getRoleNames()));
|
||||
}
|
||||
return new Request(HttpGet.METHOD_NAME, builder.build());
|
||||
}
|
||||
|
||||
static Request createToken(CreateTokenRequest createTokenRequest) throws IOException {
|
||||
Request request = new Request(HttpPost.METHOD_NAME, "/_xpack/security/oauth2/token");
|
||||
request.setEntity(createEntity(createTokenRequest, REQUEST_BODY_CONTENT_TYPE));
|
||||
|
|
|
@ -0,0 +1,64 @@
|
|||
/*
|
||||
* 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.common.util.set.Sets;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Request object to retrieve roles from the native roles store
|
||||
*/
|
||||
public final class GetRolesRequest implements Validatable {
|
||||
|
||||
private final Set<String> roleNames;
|
||||
|
||||
public GetRolesRequest(final String... roleNames) {
|
||||
if (roleNames != null) {
|
||||
this.roleNames = Collections.unmodifiableSet(Sets.newHashSet(roleNames));
|
||||
} else {
|
||||
this.roleNames = Collections.emptySet();
|
||||
}
|
||||
}
|
||||
|
||||
public Set<String> getRoleNames() {
|
||||
return roleNames;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
final GetRolesRequest that = (GetRolesRequest) o;
|
||||
return Objects.equals(roleNames, that.roleNames);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(roleNames);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,71 @@
|
|||
/*
|
||||
* 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.security.user.privileges.Role;
|
||||
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.List;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Response when requesting one or more roles.
|
||||
* Returns a List of {@link Role} objects
|
||||
*/
|
||||
public final class GetRolesResponse {
|
||||
|
||||
private final List<Role> roles;
|
||||
|
||||
public GetRolesResponse(List<Role> roles) {
|
||||
this.roles = Collections.unmodifiableList(roles);
|
||||
}
|
||||
|
||||
public List<Role> getRoles() {
|
||||
return roles;
|
||||
}
|
||||
|
||||
public static GetRolesResponse fromXContent(XContentParser parser) throws IOException {
|
||||
XContentParserUtils.ensureExpectedToken(XContentParser.Token.START_OBJECT, parser.nextToken(), parser::getTokenLocation);
|
||||
final List<Role> roles = new ArrayList<>();
|
||||
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()));
|
||||
}
|
||||
return new GetRolesResponse(roles);
|
||||
}
|
||||
|
||||
@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);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(roles);
|
||||
}
|
||||
}
|
|
@ -249,7 +249,7 @@ public final class IndicesPrivileges implements ToXContentObject {
|
|||
private @Nullable Collection<String> deniedFields = null;
|
||||
private @Nullable String query = null;
|
||||
|
||||
private Builder() {
|
||||
public Builder() {
|
||||
}
|
||||
|
||||
public Builder indices(String... indices) {
|
||||
|
|
|
@ -21,15 +21,11 @@ package org.elasticsearch.client.security.user.privileges;
|
|||
|
||||
import org.elasticsearch.common.Nullable;
|
||||
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 org.elasticsearch.common.xcontent.ObjectParser.ValueType;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
|
@ -42,10 +38,9 @@ import static org.elasticsearch.common.xcontent.ConstructingObjectParser.constru
|
|||
import static org.elasticsearch.common.xcontent.ConstructingObjectParser.optionalConstructorArg;
|
||||
|
||||
/**
|
||||
* Represents an aggregation of privileges. This does not have a name
|
||||
* identifier.
|
||||
* Represents an aggregation of privileges.
|
||||
*/
|
||||
public final class Role implements ToXContentObject {
|
||||
public final class Role {
|
||||
|
||||
public static final ParseField CLUSTER = new ParseField("cluster");
|
||||
public static final ParseField GLOBAL = new ParseField("global");
|
||||
|
@ -53,10 +48,11 @@ public final class Role implements ToXContentObject {
|
|||
public static final ParseField APPLICATIONS = new ParseField("applications");
|
||||
public static final ParseField RUN_AS = new ParseField("run_as");
|
||||
public static final ParseField METADATA = new ParseField("metadata");
|
||||
public static final ParseField TRANSIENT_METADATA = new ParseField("transient_metadata");
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static final ConstructingObjectParser<Role, Void> PARSER = new ConstructingObjectParser<>("role_descriptor", false,
|
||||
constructorObjects -> {
|
||||
public static final ConstructingObjectParser<Role, String> PARSER = new ConstructingObjectParser<>("role_descriptor", false,
|
||||
(constructorObjects, roleName) -> {
|
||||
// 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.
|
||||
|
@ -67,31 +63,44 @@ public final class Role implements ToXContentObject {
|
|||
final Collection<ApplicationResourcePrivileges> applicationResourcePrivileges =
|
||||
(Collection<ApplicationResourcePrivileges>) constructorObjects[i++];
|
||||
final Collection<String> runAsPrivilege = (Collection<String>) constructorObjects[i++];
|
||||
final Map<String, Object> metadata = (Map<String, Object>) constructorObjects[i];
|
||||
return new Role(clusterPrivileges, globalApplicationPrivileges, indicesPrivileges, applicationResourcePrivileges,
|
||||
runAsPrivilege, metadata);
|
||||
final Map<String, Object> metadata = (Map<String, Object>) constructorObjects[i++];
|
||||
final Map<String, Object> transientMetadata = (Map<String, Object>) constructorObjects[i];
|
||||
return new Role(roleName, clusterPrivileges, globalApplicationPrivileges, indicesPrivileges, applicationResourcePrivileges,
|
||||
runAsPrivilege, metadata, transientMetadata);
|
||||
});
|
||||
|
||||
static {
|
||||
PARSER.declareStringArray(optionalConstructorArg(), CLUSTER);
|
||||
PARSER.declareObject(optionalConstructorArg(), GlobalPrivileges.PARSER, GLOBAL);
|
||||
PARSER.declareFieldArray(optionalConstructorArg(), IndicesPrivileges.PARSER, INDICES, ValueType.OBJECT_ARRAY);
|
||||
PARSER.declareFieldArray(optionalConstructorArg(), ApplicationResourcePrivileges.PARSER, APPLICATIONS, ValueType.OBJECT_ARRAY);
|
||||
PARSER.declareObject(optionalConstructorArg(), (parser,c)-> GlobalPrivileges.PARSER.parse(parser,null), GLOBAL);
|
||||
PARSER.declareFieldArray(optionalConstructorArg(), (parser,c)->IndicesPrivileges.PARSER.parse(parser,null), INDICES,
|
||||
ValueType.OBJECT_ARRAY);
|
||||
PARSER.declareFieldArray(optionalConstructorArg(), (parser,c)->ApplicationResourcePrivileges.PARSER.parse(parser,null),
|
||||
APPLICATIONS, ValueType.OBJECT_ARRAY);
|
||||
PARSER.declareStringArray(optionalConstructorArg(), RUN_AS);
|
||||
PARSER.declareObject(constructorArg(), (parser, c) -> parser.map(), METADATA);
|
||||
PARSER.declareObject(constructorArg(), (parser, c) -> parser.map(), TRANSIENT_METADATA);
|
||||
}
|
||||
|
||||
private final String name;
|
||||
private final Set<String> clusterPrivileges;
|
||||
private final @Nullable GlobalPrivileges globalApplicationPrivileges;
|
||||
private final Set<IndicesPrivileges> indicesPrivileges;
|
||||
private final Set<ApplicationResourcePrivileges> applicationResourcePrivileges;
|
||||
private final Set<String> runAsPrivilege;
|
||||
private final Map<String, Object> metadata;
|
||||
private final Map<String, Object> transientMetadata;
|
||||
|
||||
private Role(@Nullable Collection<String> clusterPrivileges, @Nullable GlobalPrivileges globalApplicationPrivileges,
|
||||
@Nullable Collection<IndicesPrivileges> indicesPrivileges,
|
||||
@Nullable Collection<ApplicationResourcePrivileges> applicationResourcePrivileges, @Nullable Collection<String> runAsPrivilege,
|
||||
@Nullable Map<String, Object> metadata) {
|
||||
private Role(String name, @Nullable Collection<String> clusterPrivileges,
|
||||
@Nullable GlobalPrivileges globalApplicationPrivileges,
|
||||
@Nullable Collection<IndicesPrivileges> indicesPrivileges,
|
||||
@Nullable Collection<ApplicationResourcePrivileges> applicationResourcePrivileges,
|
||||
@Nullable Collection<String> runAsPrivilege, @Nullable Map<String, Object> metadata,
|
||||
@Nullable Map<String, Object> transientMetadata) {
|
||||
if (Strings.hasText(name) == false){
|
||||
throw new IllegalArgumentException("role name must be provided");
|
||||
} else {
|
||||
this.name = name;
|
||||
}
|
||||
// no cluster privileges are granted unless otherwise specified
|
||||
this.clusterPrivileges = Collections
|
||||
.unmodifiableSet(clusterPrivileges != null ? new HashSet<>(clusterPrivileges) : Collections.emptySet());
|
||||
|
@ -105,6 +114,11 @@ public final class Role implements ToXContentObject {
|
|||
// no run as privileges are granted unless otherwise specified
|
||||
this.runAsPrivilege = Collections.unmodifiableSet(runAsPrivilege != null ? new HashSet<>(runAsPrivilege) : Collections.emptySet());
|
||||
this.metadata = metadata != null ? Collections.unmodifiableMap(metadata) : Collections.emptyMap();
|
||||
this.transientMetadata = transientMetadata != null ? Collections.unmodifiableMap(transientMetadata) : Collections.emptyMap();
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public Set<String> getClusterPrivileges() {
|
||||
|
@ -136,55 +150,67 @@ public final class Role implements ToXContentObject {
|
|||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
Role that = (Role) o;
|
||||
return clusterPrivileges.equals(that.clusterPrivileges)
|
||||
&& Objects.equals(globalApplicationPrivileges, that.globalApplicationPrivileges)
|
||||
&& indicesPrivileges.equals(that.indicesPrivileges)
|
||||
&& applicationResourcePrivileges.equals(that.applicationResourcePrivileges)
|
||||
&& runAsPrivilege.equals(that.runAsPrivilege)
|
||||
&& metadata.equals(that.metadata);
|
||||
return name.equals(that.name)
|
||||
&& clusterPrivileges.equals(that.clusterPrivileges)
|
||||
&& Objects.equals(globalApplicationPrivileges, that.globalApplicationPrivileges)
|
||||
&& indicesPrivileges.equals(that.indicesPrivileges)
|
||||
&& applicationResourcePrivileges.equals(that.applicationResourcePrivileges)
|
||||
&& runAsPrivilege.equals(that.runAsPrivilege)
|
||||
&& metadata.equals(that.metadata)
|
||||
&& transientMetadata.equals(that.transientMetadata);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(clusterPrivileges, globalApplicationPrivileges, indicesPrivileges, applicationResourcePrivileges,
|
||||
runAsPrivilege, metadata);
|
||||
return Objects.hash(name, clusterPrivileges, globalApplicationPrivileges, indicesPrivileges, applicationResourcePrivileges,
|
||||
runAsPrivilege, metadata, transientMetadata);
|
||||
}
|
||||
|
||||
@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();
|
||||
StringBuilder sb = new StringBuilder("{");
|
||||
sb.append("Name=").append(name).append(",");
|
||||
if (false == clusterPrivileges.isEmpty()) {
|
||||
builder.field(CLUSTER.getPreferredName(), clusterPrivileges);
|
||||
sb.append("ClusterPrivileges=");
|
||||
sb.append(clusterPrivileges.toString());
|
||||
sb.append(", ");
|
||||
}
|
||||
if (null != globalApplicationPrivileges) {
|
||||
builder.field(GLOBAL.getPreferredName(), globalApplicationPrivileges);
|
||||
if (globalApplicationPrivileges != null) {
|
||||
sb.append("GlobalApplcationPrivileges=");
|
||||
sb.append(globalApplicationPrivileges.toString());
|
||||
sb.append(", ");
|
||||
}
|
||||
if (false == indicesPrivileges.isEmpty()) {
|
||||
builder.field(INDICES.getPreferredName(), indicesPrivileges);
|
||||
sb.append("IndicesPrivileges=");
|
||||
sb.append(indicesPrivileges.toString());
|
||||
sb.append(", ");
|
||||
}
|
||||
if (false == applicationResourcePrivileges.isEmpty()) {
|
||||
builder.field(APPLICATIONS.getPreferredName(), applicationResourcePrivileges);
|
||||
sb.append("ApplicationPrivileges=");
|
||||
sb.append(applicationResourcePrivileges.toString());
|
||||
sb.append(", ");
|
||||
}
|
||||
if (false == runAsPrivilege.isEmpty()) {
|
||||
builder.field(RUN_AS.getPreferredName(), runAsPrivilege);
|
||||
sb.append("RunAsPrivilege=");
|
||||
sb.append(runAsPrivilege.toString());
|
||||
sb.append(", ");
|
||||
}
|
||||
if (false == metadata.isEmpty()) {
|
||||
builder.field(METADATA.getPreferredName(), metadata);
|
||||
sb.append("Metadata=[");
|
||||
sb.append(metadata.toString());
|
||||
sb.append("], ");
|
||||
}
|
||||
return builder.endObject();
|
||||
if (false == transientMetadata.isEmpty()) {
|
||||
sb.append("TransientMetadata=[");
|
||||
sb.append(transientMetadata.toString());
|
||||
sb.append("] ");
|
||||
}
|
||||
sb.append("}");
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public static Role fromXContent(XContentParser parser) {
|
||||
return PARSER.apply(parser, null);
|
||||
public static Role fromXContent(XContentParser parser, String name) {
|
||||
return PARSER.apply(parser, name);
|
||||
}
|
||||
|
||||
public static Builder builder() {
|
||||
|
@ -193,16 +219,27 @@ public final class Role implements ToXContentObject {
|
|||
|
||||
public static final class Builder {
|
||||
|
||||
private @Nullable String name = null;
|
||||
private @Nullable Collection<String> clusterPrivileges = null;
|
||||
private @Nullable GlobalPrivileges globalApplicationPrivileges = null;
|
||||
private @Nullable Collection<IndicesPrivileges> indicesPrivileges = null;
|
||||
private @Nullable Collection<ApplicationResourcePrivileges> applicationResourcePrivileges = null;
|
||||
private @Nullable Collection<String> runAsPrivilege = null;
|
||||
private @Nullable Map<String, Object> metadata = null;
|
||||
private @Nullable Map<String, Object> transientMetadata = null;
|
||||
|
||||
private Builder() {
|
||||
}
|
||||
|
||||
public Builder name(String name) {
|
||||
if (Strings.hasText(name) == false){
|
||||
throw new IllegalArgumentException("role name must be provided");
|
||||
} else {
|
||||
this.name = name;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder clusterPrivileges(String... clusterPrivileges) {
|
||||
return clusterPrivileges(Arrays
|
||||
.asList(Objects.requireNonNull(clusterPrivileges, "Cluster privileges cannot be null. Pass an empty array instead.")));
|
||||
|
@ -214,7 +251,7 @@ public final class Role implements ToXContentObject {
|
|||
return this;
|
||||
}
|
||||
|
||||
public Builder glabalApplicationPrivileges(GlobalPrivileges globalApplicationPrivileges) {
|
||||
public Builder globalApplicationPrivileges(GlobalPrivileges globalApplicationPrivileges) {
|
||||
this.globalApplicationPrivileges = globalApplicationPrivileges;
|
||||
return this;
|
||||
}
|
||||
|
@ -257,9 +294,15 @@ public final class Role implements ToXContentObject {
|
|||
return this;
|
||||
}
|
||||
|
||||
public Builder transientMetadata(Map<String, Object> transientMetadata) {
|
||||
this.transientMetadata =
|
||||
Objects.requireNonNull(transientMetadata, "Transient metadata cannot be null. Pass an empty map instead.");
|
||||
return this;
|
||||
}
|
||||
|
||||
public Role build() {
|
||||
return new Role(clusterPrivileges, globalApplicationPrivileges, indicesPrivileges, applicationResourcePrivileges,
|
||||
runAsPrivilege, metadata);
|
||||
return new Role(name, clusterPrivileges, globalApplicationPrivileges, indicesPrivileges, applicationResourcePrivileges,
|
||||
runAsPrivilege, metadata, transientMetadata);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -282,6 +325,7 @@ public final class Role implements ToXContentObject {
|
|||
public static final String TRANSPORT_CLIENT = "transport_client";
|
||||
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_PIPELINE = "manage_pipeline";
|
||||
public static final String MANAGE_CCR = "manage_ccr";
|
||||
public static final String READ_CCR = "read_ccr";
|
||||
|
|
|
@ -32,6 +32,7 @@ import org.elasticsearch.client.security.EnableUserRequest;
|
|||
import org.elasticsearch.client.security.GetPrivilegesRequest;
|
||||
import org.elasticsearch.client.security.GetRoleMappingsRequest;
|
||||
import org.elasticsearch.client.security.ChangePasswordRequest;
|
||||
import org.elasticsearch.client.security.GetRolesRequest;
|
||||
import org.elasticsearch.client.security.PutRoleMappingRequest;
|
||||
import org.elasticsearch.client.security.PutUserRequest;
|
||||
import org.elasticsearch.client.security.RefreshPolicy;
|
||||
|
@ -202,6 +203,22 @@ public class SecurityRequestConvertersTests extends ESTestCase {
|
|||
assertNull(request.getEntity());
|
||||
}
|
||||
|
||||
public void testGetRoles() {
|
||||
final String[] roles = randomArray(0, 5, String[]::new, () -> randomAlphaOfLength(5));
|
||||
final GetRolesRequest getRolesRequest = new GetRolesRequest(roles);
|
||||
final Request request = SecurityRequestConverters.getRoles(getRolesRequest);
|
||||
|
||||
assertEquals(HttpGet.METHOD_NAME, request.getMethod());
|
||||
if (roles.length == 0) {
|
||||
assertEquals("/_xpack/security/role", request.getEndpoint());
|
||||
} else {
|
||||
assertEquals("/_xpack/security/role/" + Strings.collectionToCommaDelimitedString(getRolesRequest.getRoleNames()),
|
||||
request.getEndpoint());
|
||||
}
|
||||
assertNull(request.getEntity());
|
||||
assertEquals(Collections.emptyMap(), request.getParameters());
|
||||
}
|
||||
|
||||
public void testDeleteRole() {
|
||||
final String name = randomAlphaOfLengthBetween(1, 12);
|
||||
final RefreshPolicy refreshPolicy = randomFrom(RefreshPolicy.values());
|
||||
|
|
|
@ -52,6 +52,8 @@ import org.elasticsearch.client.security.GetPrivilegesRequest;
|
|||
import org.elasticsearch.client.security.GetPrivilegesResponse;
|
||||
import org.elasticsearch.client.security.GetRoleMappingsRequest;
|
||||
import org.elasticsearch.client.security.GetRoleMappingsResponse;
|
||||
import org.elasticsearch.client.security.GetRolesRequest;
|
||||
import org.elasticsearch.client.security.GetRolesResponse;
|
||||
import org.elasticsearch.client.security.GetSslCertificatesResponse;
|
||||
import org.elasticsearch.client.security.HasPrivilegesRequest;
|
||||
import org.elasticsearch.client.security.HasPrivilegesResponse;
|
||||
|
@ -67,6 +69,7 @@ import org.elasticsearch.client.security.support.expressiondsl.RoleMapperExpress
|
|||
import org.elasticsearch.client.security.support.expressiondsl.expressions.AnyRoleMapperExpression;
|
||||
import org.elasticsearch.client.security.support.expressiondsl.fields.FieldRoleMapperExpression;
|
||||
import org.elasticsearch.client.security.user.User;
|
||||
import org.elasticsearch.client.security.user.privileges.Role;
|
||||
import org.elasticsearch.client.security.user.privileges.ApplicationPrivilege;
|
||||
import org.elasticsearch.client.security.user.privileges.IndicesPrivileges;
|
||||
import org.elasticsearch.common.Strings;
|
||||
|
@ -401,6 +404,89 @@ public class SecurityDocumentationIT extends ESRestHighLevelClientTestCase {
|
|||
}
|
||||
}
|
||||
|
||||
public void testGetRoles() throws Exception {
|
||||
final RestHighLevelClient client = highLevelClient();
|
||||
addRole("my_role");
|
||||
addRole("my_role2");
|
||||
addRole("my_role3");
|
||||
{
|
||||
//tag::get-roles-request
|
||||
GetRolesRequest request = new GetRolesRequest("my_role");
|
||||
//end::get-roles-request
|
||||
//tag::get-roles-execute
|
||||
GetRolesResponse response = client.security().getRoles(request, RequestOptions.DEFAULT);
|
||||
//end::get-roles-execute
|
||||
//tag::get-roles-response
|
||||
List<Role> roles = response.getRoles();
|
||||
//end::get-roles-response
|
||||
|
||||
assertNotNull(response);
|
||||
assertThat(roles.size(), equalTo(1));
|
||||
assertThat(roles.get(0).getName(), equalTo("my_role"));
|
||||
assertThat(roles.get(0).getClusterPrivileges().contains("all"), equalTo(true));
|
||||
}
|
||||
|
||||
{
|
||||
//tag::get-roles-list-request
|
||||
GetRolesRequest request = new GetRolesRequest("my_role", "my_role2");
|
||||
GetRolesResponse response = client.security().getRoles(request, RequestOptions.DEFAULT);
|
||||
//end::get-roles-list-request
|
||||
|
||||
List<Role> roles = response.getRoles();
|
||||
assertNotNull(response);
|
||||
assertThat(roles.size(), equalTo(2));
|
||||
assertThat(roles.get(0).getClusterPrivileges().contains("all"), equalTo(true));
|
||||
assertThat(roles.get(1).getClusterPrivileges().contains("all"), equalTo(true));
|
||||
}
|
||||
|
||||
{
|
||||
//tag::get-roles-all-request
|
||||
GetRolesRequest request = new GetRolesRequest();
|
||||
GetRolesResponse response = client.security().getRoles(request, RequestOptions.DEFAULT);
|
||||
//end::get-roles-all-request
|
||||
|
||||
List<Role> roles = response.getRoles();
|
||||
assertNotNull(response);
|
||||
// 21 system roles plus the three we created
|
||||
assertThat(roles.size(), equalTo(24));
|
||||
}
|
||||
|
||||
{
|
||||
GetRolesRequest request = new GetRolesRequest("my_role");
|
||||
ActionListener<GetRolesResponse> listener;
|
||||
|
||||
//tag::get-roles-execute-listener
|
||||
listener = new ActionListener<GetRolesResponse>() {
|
||||
@Override
|
||||
public void onResponse(GetRolesResponse getRolesResponse) {
|
||||
// <1>
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Exception e) {
|
||||
// <2>
|
||||
}
|
||||
};
|
||||
//end::get-roles-execute-listener
|
||||
|
||||
assertNotNull(listener);
|
||||
|
||||
// Replace the empty listener by a blocking listener in test
|
||||
final PlainActionFuture<GetRolesResponse> future = new PlainActionFuture<>();
|
||||
listener = future;
|
||||
|
||||
//tag::get-roles-execute-async
|
||||
client.security().getRolesAsync(request, RequestOptions.DEFAULT, listener); // <1>
|
||||
//end::get-roles-execute-async
|
||||
|
||||
final GetRolesResponse response = future.get(30, TimeUnit.SECONDS);
|
||||
assertNotNull(response);
|
||||
assertThat(response.getRoles().size(), equalTo(1));
|
||||
assertThat(response.getRoles().get(0).getName(), equalTo("my_role"));
|
||||
assertThat(response.getRoles().get(0).getClusterPrivileges().contains("all"), equalTo(true));
|
||||
}
|
||||
}
|
||||
|
||||
public void testAuthenticate() throws Exception {
|
||||
RestHighLevelClient client = highLevelClient();
|
||||
{
|
||||
|
@ -414,7 +500,7 @@ public class SecurityDocumentationIT extends ESRestHighLevelClientTestCase {
|
|||
//end::authenticate-response
|
||||
|
||||
assertThat(user.getUsername(), is("test_user"));
|
||||
assertThat(user.getRoles(), contains(new String[] {"superuser"}));
|
||||
assertThat(user.getRoles(), contains(new String[]{"superuser"}));
|
||||
assertThat(user.getFullName(), nullValue());
|
||||
assertThat(user.getEmail(), nullValue());
|
||||
assertThat(user.getMetadata().isEmpty(), is(true));
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
* 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.test.ESTestCase;
|
||||
import org.elasticsearch.test.EqualsHashCodeTestUtils;
|
||||
|
||||
import static org.hamcrest.Matchers.containsInAnyOrder;
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
|
||||
public class GetRolesRequestTests extends ESTestCase {
|
||||
|
||||
public void testGetRolesRequest() {
|
||||
final String[] roles = randomArray(0, 5, String[]::new, () -> randomAlphaOfLength(5));
|
||||
final GetRolesRequest getRolesRequest = new GetRolesRequest(roles);
|
||||
assertThat(getRolesRequest.getRoleNames().size(), equalTo(roles.length));
|
||||
assertThat(getRolesRequest.getRoleNames(), containsInAnyOrder(roles));
|
||||
}
|
||||
|
||||
public void testEqualsHashCode() {
|
||||
final String[] roles = randomArray(0, 5, String[]::new, () -> randomAlphaOfLength(5));
|
||||
final GetRolesRequest getRolesRequest = new GetRolesRequest(roles);
|
||||
assertNotNull(getRolesRequest);
|
||||
EqualsHashCodeTestUtils.checkEqualsAndHashCode(getRolesRequest, (original) -> {
|
||||
return new GetRolesRequest(original.getRoleNames().toArray(new String[0]));
|
||||
});
|
||||
EqualsHashCodeTestUtils.checkEqualsAndHashCode(getRolesRequest, (original) -> {
|
||||
return new GetRolesRequest(original.getRoleNames().toArray(new String[0]));
|
||||
}, GetRolesRequestTests::mutateTestItem);
|
||||
}
|
||||
|
||||
private static GetRolesRequest mutateTestItem(GetRolesRequest original) {
|
||||
return new GetRolesRequest(randomArray(0, 5, String[]::new, () -> randomAlphaOfLength(5)));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,199 @@
|
|||
/*
|
||||
* 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.security.user.privileges.IndicesPrivileges;
|
||||
import org.elasticsearch.client.security.user.privileges.Role;
|
||||
import org.elasticsearch.common.xcontent.DeprecationHandler;
|
||||
import org.elasticsearch.common.xcontent.NamedXContentRegistry;
|
||||
import org.elasticsearch.common.xcontent.XContentType;
|
||||
import org.elasticsearch.test.ESTestCase;
|
||||
import org.elasticsearch.test.EqualsHashCodeTestUtils;
|
||||
|
||||
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.stream.Collectors;
|
||||
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
|
||||
public class GetRolesResponseTests extends ESTestCase {
|
||||
|
||||
public void testFromXContent() throws IOException {
|
||||
String json =
|
||||
"{\n" +
|
||||
" \"my_admin_role\": {\n" +
|
||||
" \"cluster\" : [ \"all\" ],\n" +
|
||||
" \"indices\" : [\n" +
|
||||
" {\n" +
|
||||
" \"names\" : [ \"index1\", \"index2\" ],\n" +
|
||||
" \"privileges\" : [ \"all\" ],\n" +
|
||||
" \"field_security\" : {\n" +
|
||||
" \"grant\" : [ \"title\", \"body\" ]}\n" +
|
||||
" }\n" +
|
||||
" ],\n" +
|
||||
" \"applications\" : [ ],\n" +
|
||||
" \"run_as\" : [ \"other_user\" ],\n" +
|
||||
" \"metadata\" : {\n" +
|
||||
" \"version\" : 1\n" +
|
||||
" },\n" +
|
||||
" \"transient_metadata\" : {\n" +
|
||||
" \"enabled\" : true\n" +
|
||||
" }\n" +
|
||||
" }\n" +
|
||||
"}";
|
||||
final GetRolesResponse response = GetRolesResponse.fromXContent((XContentType.JSON.xContent().createParser(
|
||||
new NamedXContentRegistry(Collections.emptyList()), new DeprecationHandler() {
|
||||
@Override
|
||||
public void usedDeprecatedName(String usedName, String modernName) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void usedDeprecatedField(String usedName, String replacedWith) {
|
||||
}
|
||||
}, json)));
|
||||
assertThat(response.getRoles().size(), equalTo(1));
|
||||
final Role role = response.getRoles().get(0);
|
||||
assertThat(role.getName(), equalTo("my_admin_role"));
|
||||
assertThat(role.getClusterPrivileges().size(), equalTo(1));
|
||||
IndicesPrivileges expectedIndicesPrivileges = new IndicesPrivileges.Builder()
|
||||
.indices("index1", "index2")
|
||||
.privileges("all")
|
||||
.grantedFields("title", "body")
|
||||
.build();
|
||||
assertThat(role.getIndicesPrivileges().contains(expectedIndicesPrivileges), equalTo(true));
|
||||
final Map<String, Object> expectedMetadata = new HashMap<>();
|
||||
expectedMetadata.put("version", 1);
|
||||
final Map<String, Object> expectedTransientMetadata = new HashMap<>();
|
||||
expectedTransientMetadata.put("enabled", true);
|
||||
final Role expectedRole = Role.builder()
|
||||
.name("my_admin_role")
|
||||
.clusterPrivileges("all")
|
||||
.indicesPrivileges(expectedIndicesPrivileges)
|
||||
.runAsPrivilege("other_user")
|
||||
.metadata(expectedMetadata)
|
||||
.transientMetadata(expectedTransientMetadata)
|
||||
.build();
|
||||
assertThat(role, equalTo(expectedRole));
|
||||
}
|
||||
|
||||
public void testEqualsHashCode() {
|
||||
final List<Role> roles = new ArrayList<>();
|
||||
IndicesPrivileges indicesPrivileges = new IndicesPrivileges.Builder()
|
||||
.indices("index1", "index2")
|
||||
.privileges("write", "monitor", "delete")
|
||||
.grantedFields("field1", "field2")
|
||||
.deniedFields("field3", "field4")
|
||||
.build();
|
||||
Map<String, Object> metadata = new HashMap<>();
|
||||
metadata.put("key", "value");
|
||||
Map<String, Object> transientMetadata = new HashMap<>();
|
||||
transientMetadata.put("transient_key", "transient_value");
|
||||
final Role role = Role.builder()
|
||||
.name("role_name")
|
||||
.clusterPrivileges("monitor", "manage", "manage_saml")
|
||||
.indicesPrivileges(indicesPrivileges)
|
||||
.runAsPrivilege("run_as_user")
|
||||
.metadata(metadata)
|
||||
.transientMetadata(transientMetadata)
|
||||
.build();
|
||||
roles.add(role);
|
||||
IndicesPrivileges indicesPrivileges2 = new IndicesPrivileges.Builder()
|
||||
.indices("other_index1", "other_index2")
|
||||
.privileges("write", "monitor", "delete")
|
||||
.grantedFields("other_field1", "other_field2")
|
||||
.deniedFields("other_field3", "other_field4")
|
||||
.build();
|
||||
Map<String, Object> metadata2 = new HashMap<>();
|
||||
metadata.put("other_key", "other_value");
|
||||
Map<String, Object> transientMetadata2 = new HashMap<>();
|
||||
transientMetadata2.put("other_transient_key", "other_transient_value");
|
||||
final Role role2 = Role.builder()
|
||||
.name("role2_name")
|
||||
.clusterPrivileges("monitor", "manage", "manage_saml")
|
||||
.indicesPrivileges(indicesPrivileges2)
|
||||
.runAsPrivilege("other_run_as_user")
|
||||
.metadata(metadata2)
|
||||
.transientMetadata(transientMetadata2)
|
||||
.build();
|
||||
roles.add(role2);
|
||||
final GetRolesResponse getRolesResponse = new GetRolesResponse(roles);
|
||||
assertNotNull(getRolesResponse);
|
||||
EqualsHashCodeTestUtils.checkEqualsAndHashCode(getRolesResponse, (original) -> {
|
||||
return new GetRolesResponse(original.getRoles());
|
||||
});
|
||||
EqualsHashCodeTestUtils.checkEqualsAndHashCode(getRolesResponse, (original) -> {
|
||||
return new GetRolesResponse(original.getRoles());
|
||||
}, GetRolesResponseTests::mutateTestItem);
|
||||
|
||||
}
|
||||
|
||||
private static GetRolesResponse mutateTestItem(GetRolesResponse original) {
|
||||
if (randomBoolean()) {
|
||||
final List<Role> roles = new ArrayList<>();
|
||||
IndicesPrivileges indicesPrivileges = new IndicesPrivileges.Builder()
|
||||
.indices("index1", "index2")
|
||||
.privileges("write", "monitor", "delete")
|
||||
.grantedFields("field1", "field2")
|
||||
.deniedFields("field3", "field4")
|
||||
.build();
|
||||
Map<String, Object> metadata = new HashMap<String, Object>();
|
||||
metadata.put("key", "value");
|
||||
Map<String, Object> transientMetadata = new HashMap<>();
|
||||
transientMetadata.put("transient_key", "transient_value");
|
||||
final Role role = Role.builder()
|
||||
.name("role_name")
|
||||
.clusterPrivileges("monitor", "manage", "manage_saml")
|
||||
.indicesPrivileges(indicesPrivileges)
|
||||
.runAsPrivilege("run_as_user")
|
||||
.metadata(metadata)
|
||||
.transientMetadata(transientMetadata)
|
||||
.build();
|
||||
roles.add(role);
|
||||
return new GetRolesResponse(roles);
|
||||
} else {
|
||||
IndicesPrivileges indicesPrivileges = new IndicesPrivileges.Builder()
|
||||
.indices("index1_changed", "index2")
|
||||
.privileges("write", "monitor", "delete")
|
||||
.grantedFields("field1", "field2")
|
||||
.deniedFields("field3", "field4")
|
||||
.build();
|
||||
Map<String, Object> metadata = new HashMap<String, Object>();
|
||||
metadata.put("key", "value");
|
||||
Map<String, Object> transientMetadata = new HashMap<>();
|
||||
transientMetadata.put("transient_key", "transient_value");
|
||||
final Role role = Role.builder()
|
||||
.name("role_name")
|
||||
.clusterPrivileges("monitor", "manage", "manage_saml")
|
||||
.indicesPrivileges(indicesPrivileges)
|
||||
.runAsPrivilege("run_as_user")
|
||||
.metadata(metadata)
|
||||
.transientMetadata(transientMetadata)
|
||||
.build();
|
||||
List<Role> newRoles = original.getRoles().stream().collect(Collectors.toList());
|
||||
newRoles.remove(0);
|
||||
newRoles.add(role);
|
||||
return new GetRolesResponse(newRoles);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
|
||||
--
|
||||
:api: get-roles
|
||||
:request: GetRolesRequest
|
||||
:respnse: GetRolesResponse
|
||||
--
|
||||
|
||||
[id="{upid}-{api}"]
|
||||
=== Get Roles API
|
||||
|
||||
[id="{upid}-{api}-request"]
|
||||
==== Get Roles Request
|
||||
|
||||
Retrieving a role can be performed using the `security().getRoles()`
|
||||
method and by setting the role name on +{request}+:
|
||||
|
||||
["source","java",subs="attributes,callouts,macros"]
|
||||
--------------------------------------------------
|
||||
include-tagged::{doc-tests-file}[{api}-request]
|
||||
--------------------------------------------------
|
||||
|
||||
Retrieving multiple roles can be performed using the `security().getRoles()`
|
||||
method and by setting multiple role names on +{request}+:
|
||||
|
||||
["source","java",subs="attributes,callouts,macros"]
|
||||
--------------------------------------------------
|
||||
include-tagged::{doc-tests-file}[{api}-list-request]
|
||||
--------------------------------------------------
|
||||
|
||||
Retrieving all roles can be performed using the `security().getRoles()`
|
||||
method without specifying any role names on +{request}+:
|
||||
|
||||
["source","java",subs="attributes,callouts,macros"]
|
||||
--------------------------------------------------
|
||||
include-tagged::{doc-tests-file}[{api}-all-request]
|
||||
--------------------------------------------------
|
||||
|
||||
include::../execution.asciidoc[]
|
||||
|
||||
[id="{upid}-{api}-response"]
|
||||
==== Get Roles Response
|
||||
|
||||
The returned +{response}+ allows getting information about the retrieved roles as follows.
|
||||
|
||||
["source","java",subs="attributes,callouts,macros"]
|
||||
--------------------------------------------------
|
||||
include-tagged::{doc-tests-file}[{api}-response]
|
||||
--------------------------------------------------
|
|
@ -366,6 +366,7 @@ The Java High Level REST Client supports the following Security APIs:
|
|||
* <<java-rest-high-security-enable-user>>
|
||||
* <<java-rest-high-security-disable-user>>
|
||||
* <<java-rest-high-security-change-password>>
|
||||
* <<{upid}-get-roles>>
|
||||
* <<java-rest-high-security-delete-role>>
|
||||
* <<{upid}-clear-roles-cache>>
|
||||
* <<{upid}-clear-realm-cache>>
|
||||
|
|
Loading…
Reference in New Issue