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 manage_own_api_key cluster privilege #45897

Merged
merged 19 commits into from
Aug 27, 2019
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
9e61a9c
Simplify API key service API (#44935)
bizybot Aug 6, 2019
522f005
REST API changes for manage-own-api-key privilege (#44936)
bizybot Aug 7, 2019
9170c81
Merge branch 'master' into manage-own-api-key-privilege
Aug 12, 2019
1950e38
Consider `owner` flag when retrieving/invalidating keys with API key …
bizybot Aug 14, 2019
fcd2b51
Merge branch 'master' into manage-own-api-key-privilege
Aug 15, 2019
9cf2432
HLRC: add support for retrieval/invalidation of owned API keys (#45474)
bizybot Aug 15, 2019
4d1bed0
Allow API key to retrieve its own information with no API key privile…
bizybot Aug 15, 2019
2a1ca90
Merge branch 'master' into manage-own-api-key-privilege
Aug 21, 2019
a07f319
Merge branch 'master' into manage-own-api-key-privilege
Aug 21, 2019
5661e98
Add support for authentication based predicate for cluster permission…
bizybot Aug 22, 2019
af236cc
Merge branch 'master' into manage-own-api-key-privilege
Aug 22, 2019
4c23349
Merge branch 'master' into manage-own-api-key-privilege
Aug 22, 2019
ed2062f
Add `manage_own_api_key` cluster privilege (#45696)
bizybot Aug 23, 2019
e6d50ff
Document API key API changes for `owner` flag (#45698)
bizybot Aug 23, 2019
db47780
Merge branch 'master' into manage-own-api-key-privilege
Aug 23, 2019
b1afdf9
Merge branch 'master' into manage-own-api-key-privilege
bizybot Aug 26, 2019
a562c3d
Add the code back removed due to conflict resolve
bizybot Aug 26, 2019
d657520
resolve compilation failures due to merge
Aug 27, 2019
6d9ee4e
increase built in cluster privilege count
Aug 27, 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 @@ -294,7 +294,7 @@ static Request getApiKey(final GetApiKeyRequest getApiKeyRequest) throws IOExcep
if (Strings.hasText(getApiKeyRequest.getRealmName())) {
request.addParameter("realm_name", getApiKeyRequest.getRealmName());
}

request.addParameter("owner", Boolean.toString(getApiKeyRequest.ownedByAuthenticatedUser()));
return request;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,27 +36,34 @@ public final class GetApiKeyRequest implements Validatable, ToXContentObject {
private final String userName;
private final String id;
private final String name;
private final boolean ownedByAuthenticatedUser;

// pkg scope for testing
GetApiKeyRequest(@Nullable String realmName, @Nullable String userName, @Nullable String apiKeyId,
@Nullable String apiKeyName) {
@Nullable String apiKeyName, boolean ownedByAuthenticatedUser) {
if (Strings.hasText(realmName) == false && Strings.hasText(userName) == false && Strings.hasText(apiKeyId) == false
&& Strings.hasText(apiKeyName) == false) {
throwValidationError("One of [api key id, api key name, username, realm name] must be specified");
&& Strings.hasText(apiKeyName) == false && ownedByAuthenticatedUser == false) {
throwValidationError("One of [api key id, api key name, username, realm name] must be specified if [owner] flag is false");
}
if (Strings.hasText(apiKeyId) || Strings.hasText(apiKeyName)) {
if (Strings.hasText(realmName) || Strings.hasText(userName)) {
throwValidationError(
"username or realm name must not be specified when the api key id or api key name is specified");
}
}
if (ownedByAuthenticatedUser) {
if (Strings.hasText(realmName) || Strings.hasText(userName)) {
throwValidationError("neither username nor realm-name may be specified when retrieving owned API keys");
}
}
if (Strings.hasText(apiKeyId) && Strings.hasText(apiKeyName)) {
throwValidationError("only one of [api key id, api key name] can be specified");
}
this.realmName = realmName;
this.userName = userName;
this.id = apiKeyId;
this.name = apiKeyName;
this.ownedByAuthenticatedUser = ownedByAuthenticatedUser;
}

private void throwValidationError(String message) {
Expand All @@ -79,13 +86,17 @@ public String getName() {
return name;
}

public boolean ownedByAuthenticatedUser() {
return ownedByAuthenticatedUser;
}

/**
* Creates get API key request for given realm name
* @param realmName realm name
* @return {@link GetApiKeyRequest}
*/
public static GetApiKeyRequest usingRealmName(String realmName) {
return new GetApiKeyRequest(realmName, null, null, null);
return new GetApiKeyRequest(realmName, null, null, null, false);
}

/**
Expand All @@ -94,7 +105,7 @@ public static GetApiKeyRequest usingRealmName(String realmName) {
* @return {@link GetApiKeyRequest}
*/
public static GetApiKeyRequest usingUserName(String userName) {
return new GetApiKeyRequest(null, userName, null, null);
return new GetApiKeyRequest(null, userName, null, null, false);
}

/**
Expand All @@ -104,25 +115,36 @@ public static GetApiKeyRequest usingUserName(String userName) {
* @return {@link GetApiKeyRequest}
*/
public static GetApiKeyRequest usingRealmAndUserName(String realmName, String userName) {
return new GetApiKeyRequest(realmName, userName, null, null);
return new GetApiKeyRequest(realmName, userName, null, null, false);
}

/**
* Creates get API key request for given api key id
* @param apiKeyId api key id
* @param ownedByAuthenticatedUser set {@code true} if the request is only for the API keys owned by current
* authenticated user else{@code false}
* @return {@link GetApiKeyRequest}
*/
public static GetApiKeyRequest usingApiKeyId(String apiKeyId) {
return new GetApiKeyRequest(null, null, apiKeyId, null);
public static GetApiKeyRequest usingApiKeyId(String apiKeyId, boolean ownedByAuthenticatedUser) {
return new GetApiKeyRequest(null, null, apiKeyId, null, ownedByAuthenticatedUser);
}

/**
* Creates get API key request for given api key name
* @param apiKeyName api key name
* @param ownedByAuthenticatedUser set {@code true} if the request is only for the API keys owned by current
* authenticated user else{@code false}
* @return {@link GetApiKeyRequest}
*/
public static GetApiKeyRequest usingApiKeyName(String apiKeyName) {
return new GetApiKeyRequest(null, null, null, apiKeyName);
public static GetApiKeyRequest usingApiKeyName(String apiKeyName, boolean ownedByAuthenticatedUser) {
return new GetApiKeyRequest(null, null, null, apiKeyName, ownedByAuthenticatedUser);
}

/**
* Creates get api key request to retrieve api key information for the api keys owned by the current authenticated user.
*/
public static GetApiKeyRequest forOwnedApiKeys() {
return new GetApiKeyRequest(null, null, null, null, true);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,27 +36,34 @@ public final class InvalidateApiKeyRequest implements Validatable, ToXContentObj
private final String userName;
private final String id;
private final String name;
private final boolean ownedByAuthenticatedUser;

// pkg scope for testing
InvalidateApiKeyRequest(@Nullable String realmName, @Nullable String userName, @Nullable String apiKeyId,
@Nullable String apiKeyName) {
@Nullable String apiKeyName, boolean ownedByAuthenticatedUser) {
if (Strings.hasText(realmName) == false && Strings.hasText(userName) == false && Strings.hasText(apiKeyId) == false
&& Strings.hasText(apiKeyName) == false) {
throwValidationError("One of [api key id, api key name, username, realm name] must be specified");
&& Strings.hasText(apiKeyName) == false && ownedByAuthenticatedUser == false) {
throwValidationError("One of [api key id, api key name, username, realm name] must be specified if [owner] flag is false");
}
if (Strings.hasText(apiKeyId) || Strings.hasText(apiKeyName)) {
if (Strings.hasText(realmName) || Strings.hasText(userName)) {
throwValidationError(
"username or realm name must not be specified when the api key id or api key name is specified");
}
}
if (ownedByAuthenticatedUser) {
if (Strings.hasText(realmName) || Strings.hasText(userName)) {
throwValidationError("neither username nor realm-name may be specified when invalidating owned API keys");
}
}
if (Strings.hasText(apiKeyId) && Strings.hasText(apiKeyName)) {
throwValidationError("only one of [api key id, api key name] can be specified");
}
this.realmName = realmName;
this.userName = userName;
this.id = apiKeyId;
this.name = apiKeyName;
this.ownedByAuthenticatedUser = ownedByAuthenticatedUser;
}

private void throwValidationError(String message) {
Expand All @@ -79,13 +86,17 @@ public String getName() {
return name;
}

public boolean ownedByAuthenticatedUser() {
return ownedByAuthenticatedUser;
}

/**
* Creates invalidate API key request for given realm name
* @param realmName realm name
* @return {@link InvalidateApiKeyRequest}
*/
public static InvalidateApiKeyRequest usingRealmName(String realmName) {
return new InvalidateApiKeyRequest(realmName, null, null, null);
return new InvalidateApiKeyRequest(realmName, null, null, null, false);
}

/**
Expand All @@ -94,7 +105,7 @@ public static InvalidateApiKeyRequest usingRealmName(String realmName) {
* @return {@link InvalidateApiKeyRequest}
*/
public static InvalidateApiKeyRequest usingUserName(String userName) {
return new InvalidateApiKeyRequest(null, userName, null, null);
return new InvalidateApiKeyRequest(null, userName, null, null, false);
}

/**
Expand All @@ -104,25 +115,36 @@ public static InvalidateApiKeyRequest usingUserName(String userName) {
* @return {@link InvalidateApiKeyRequest}
*/
public static InvalidateApiKeyRequest usingRealmAndUserName(String realmName, String userName) {
return new InvalidateApiKeyRequest(realmName, userName, null, null);
return new InvalidateApiKeyRequest(realmName, userName, null, null, false);
}

/**
* Creates invalidate API key request for given api key id
* @param apiKeyId api key id
* @param ownedByAuthenticatedUser set {@code true} if the request is only for the API keys owned by current authenticated user else
* {@code false}
* @return {@link InvalidateApiKeyRequest}
*/
public static InvalidateApiKeyRequest usingApiKeyId(String apiKeyId) {
return new InvalidateApiKeyRequest(null, null, apiKeyId, null);
public static InvalidateApiKeyRequest usingApiKeyId(String apiKeyId, boolean ownedByAuthenticatedUser) {
return new InvalidateApiKeyRequest(null, null, apiKeyId, null, ownedByAuthenticatedUser);
}

/**
* Creates invalidate API key request for given api key name
* @param apiKeyName api key name
* @param ownedByAuthenticatedUser set {@code true} if the request is only for the API keys owned by current authenticated user else
* {@code false}
* @return {@link InvalidateApiKeyRequest}
*/
public static InvalidateApiKeyRequest usingApiKeyName(String apiKeyName) {
return new InvalidateApiKeyRequest(null, null, null, apiKeyName);
public static InvalidateApiKeyRequest usingApiKeyName(String apiKeyName, boolean ownedByAuthenticatedUser) {
return new InvalidateApiKeyRequest(null, null, null, apiKeyName, ownedByAuthenticatedUser);
}

/**
* Creates invalidate api key request to invalidate api keys owned by the current authenticated user.
*/
public static InvalidateApiKeyRequest forOwnedApiKeys() {
return new InvalidateApiKeyRequest(null, null, null, null, true);
}

@Override
Expand All @@ -140,6 +162,7 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws
if (name != null) {
builder.field("name", name);
}
builder.field("owner", ownedByAuthenticatedUser);
return builder.endObject();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -446,10 +446,11 @@ public void testGetApiKey() throws IOException {
final Request request = SecurityRequestConverters.getApiKey(getApiKeyRequest);
assertEquals(HttpGet.METHOD_NAME, request.getMethod());
assertEquals("/_security/api_key", request.getEndpoint());
Map<String, String> mapOfParameters = new HashMap<>();
mapOfParameters.put("realm_name", realmName);
mapOfParameters.put("username", userName);
assertThat(request.getParameters(), equalTo(mapOfParameters));
Map<String, String> expectedMapOfParameters = new HashMap<>();
expectedMapOfParameters.put("realm_name", realmName);
expectedMapOfParameters.put("username", userName);
expectedMapOfParameters.put("owner", Boolean.FALSE.toString());
assertThat(request.getParameters(), equalTo(expectedMapOfParameters));
}

public void testInvalidateApiKey() throws IOException {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1917,7 +1917,7 @@ public void testGetApiKey() throws Exception {
Instant.now().plusMillis(expiration.getMillis()), false, "test_user", "default_file");
{
// tag::get-api-key-id-request
GetApiKeyRequest getApiKeyRequest = GetApiKeyRequest.usingApiKeyId(createApiKeyResponse1.getId());
GetApiKeyRequest getApiKeyRequest = GetApiKeyRequest.usingApiKeyId(createApiKeyResponse1.getId(), false);
// end::get-api-key-id-request

// tag::get-api-key-execute
Expand All @@ -1931,7 +1931,7 @@ public void testGetApiKey() throws Exception {

{
// tag::get-api-key-name-request
GetApiKeyRequest getApiKeyRequest = GetApiKeyRequest.usingApiKeyName(createApiKeyResponse1.getName());
GetApiKeyRequest getApiKeyRequest = GetApiKeyRequest.usingApiKeyName(createApiKeyResponse1.getName(), false);
// end::get-api-key-name-request

GetApiKeyResponse getApiKeyResponse = client.security().getApiKey(getApiKeyRequest, RequestOptions.DEFAULT);
Expand Down Expand Up @@ -1965,6 +1965,18 @@ public void testGetApiKey() throws Exception {
verifyApiKey(getApiKeyResponse.getApiKeyInfos().get(0), expectedApiKeyInfo);
}

{
// tag::get-api-keys-owned-by-authenticated-user-request
GetApiKeyRequest getApiKeyRequest = GetApiKeyRequest.forOwnedApiKeys();
// end::get-api-keys-owned-by-authenticated-user-request

GetApiKeyResponse getApiKeyResponse = client.security().getApiKey(getApiKeyRequest, RequestOptions.DEFAULT);

assertThat(getApiKeyResponse.getApiKeyInfos(), is(notNullValue()));
assertThat(getApiKeyResponse.getApiKeyInfos().size(), is(1));
verifyApiKey(getApiKeyResponse.getApiKeyInfos().get(0), expectedApiKeyInfo);
}

{
// tag::get-user-realm-api-keys-request
GetApiKeyRequest getApiKeyRequest = GetApiKeyRequest.usingRealmAndUserName("default_file", "test_user");
Expand All @@ -1980,7 +1992,7 @@ public void testGetApiKey() throws Exception {
}

{
GetApiKeyRequest getApiKeyRequest = GetApiKeyRequest.usingApiKeyId(createApiKeyResponse1.getId());
GetApiKeyRequest getApiKeyRequest = GetApiKeyRequest.usingApiKeyId(createApiKeyResponse1.getId(), false);

ActionListener<GetApiKeyResponse> listener;
// tag::get-api-key-execute-listener
Expand Down Expand Up @@ -2041,7 +2053,7 @@ public void testInvalidateApiKey() throws Exception {

{
// tag::invalidate-api-key-id-request
InvalidateApiKeyRequest invalidateApiKeyRequest = InvalidateApiKeyRequest.usingApiKeyId(createApiKeyResponse1.getId());
InvalidateApiKeyRequest invalidateApiKeyRequest = InvalidateApiKeyRequest.usingApiKeyId(createApiKeyResponse1.getId(), false);
// end::invalidate-api-key-id-request

// tag::invalidate-api-key-execute
Expand All @@ -2066,7 +2078,8 @@ public void testInvalidateApiKey() throws Exception {
assertNotNull(createApiKeyResponse2.getKey());

// tag::invalidate-api-key-name-request
InvalidateApiKeyRequest invalidateApiKeyRequest = InvalidateApiKeyRequest.usingApiKeyName(createApiKeyResponse2.getName());
InvalidateApiKeyRequest invalidateApiKeyRequest = InvalidateApiKeyRequest.usingApiKeyName(createApiKeyResponse2.getName(),
false);
// end::invalidate-api-key-name-request

InvalidateApiKeyResponse invalidateApiKeyResponse = client.security().invalidateApiKey(invalidateApiKeyRequest,
Expand Down Expand Up @@ -2159,7 +2172,7 @@ public void testInvalidateApiKey() throws Exception {
assertThat(createApiKeyResponse6.getName(), equalTo("k6"));
assertNotNull(createApiKeyResponse6.getKey());

InvalidateApiKeyRequest invalidateApiKeyRequest = InvalidateApiKeyRequest.usingApiKeyId(createApiKeyResponse6.getId());
InvalidateApiKeyRequest invalidateApiKeyRequest = InvalidateApiKeyRequest.usingApiKeyId(createApiKeyResponse6.getId(), false);

ActionListener<InvalidateApiKeyResponse> listener;
// tag::invalidate-api-key-execute-listener
Expand Down Expand Up @@ -2195,5 +2208,29 @@ public void onFailure(Exception e) {
assertThat(invalidatedApiKeyIds, containsInAnyOrder(expectedInvalidatedApiKeyIds.toArray(Strings.EMPTY_ARRAY)));
assertThat(response.getPreviouslyInvalidatedApiKeys().size(), equalTo(0));
}

{
createApiKeyRequest = new CreateApiKeyRequest("k7", roles, expiration, refreshPolicy);
CreateApiKeyResponse createApiKeyResponse7 = client.security().createApiKey(createApiKeyRequest, RequestOptions.DEFAULT);
assertThat(createApiKeyResponse7.getName(), equalTo("k7"));
assertNotNull(createApiKeyResponse7.getKey());

// tag::invalidate-api-keys-owned-by-authenticated-user-request
InvalidateApiKeyRequest invalidateApiKeyRequest = InvalidateApiKeyRequest.forOwnedApiKeys();
// end::invalidate-api-keys-owned-by-authenticated-user-request

InvalidateApiKeyResponse invalidateApiKeyResponse = client.security().invalidateApiKey(invalidateApiKeyRequest,
RequestOptions.DEFAULT);

final List<ElasticsearchException> errors = invalidateApiKeyResponse.getErrors();
final List<String> invalidatedApiKeyIds = invalidateApiKeyResponse.getInvalidatedApiKeys();
final List<String> previouslyInvalidatedApiKeyIds = invalidateApiKeyResponse.getPreviouslyInvalidatedApiKeys();

assertTrue(errors.isEmpty());
List<String> expectedInvalidatedApiKeyIds = Arrays.asList(createApiKeyResponse7.getId());
assertThat(invalidatedApiKeyIds, containsInAnyOrder(expectedInvalidatedApiKeyIds.toArray(Strings.EMPTY_ARRAY)));
assertThat(previouslyInvalidatedApiKeyIds.size(), equalTo(0));
}

}
}
Loading