forked from elastic/elasticsearch
-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add support for API keys to access Elasticsearch (elastic#38291)
X-Pack security supports built-in authentication service `token-service` that allows access tokens to be used to access Elasticsearch without using Basic authentication. The tokens are generated by `token-service` based on OAuth2 spec. The access token is a short-lived token (defaults to 20m) and refresh token with a lifetime of 24 hours, making them unsuitable for long-lived or recurring tasks where the system might go offline thereby failing refresh of tokens. This commit introduces a built-in authentication service `api-key-service` that adds support for long-lived tokens aka API keys to access Elasticsearch. The `api-key-service` is consulted after `token-service` in the authentication chain. By default, if TLS is enabled then `api-key-service` is also enabled. The service can be disabled using the configuration setting. The API keys:- - by default do not have an expiration but expiration can be configured where the API keys need to be expired after a certain amount of time. - when generated will keep authentication information of the user that generated them. - can be defined with a role describing the privileges for accessing Elasticsearch and will be limited by the role of the user that generated them - can be invalidated via invalidation API - information can be retrieved via a get API - that have been expired or invalidated will be retained for 1 week before being deleted. The expired API keys remover task handles this. Following are the API key management APIs:- 1. Create API Key - `PUT/POST /_security/api_key` 2. Get API key(s) - `GET /_security/api_key` 3. Invalidate API Key(s) `DELETE /_security/api_key` The API keys can be used to access Elasticsearch using `Authorization` header, where the auth scheme is `ApiKey` and the credentials, is the base64 encoding of API key Id and API key separated by a colon. Example:- ``` curl -H "Authorization: ApiKey YXBpLWtleS1pZDphcGkta2V5" http://localhost:9200/_cluster/health ``` Closes elastic#34383
- Loading branch information
Showing
142 changed files
with
10,697 additions
and
946 deletions.
There are no files selected for viewing
This file contains 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
This file contains 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
This file contains 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
128 changes: 128 additions & 0 deletions
128
.../rest-high-level/src/main/java/org/elasticsearch/client/security/CreateApiKeyRequest.java
This file contains 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,128 @@ | ||
/* | ||
* 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.client.security.user.privileges.Role; | ||
import org.elasticsearch.common.Nullable; | ||
import org.elasticsearch.common.Strings; | ||
import org.elasticsearch.common.unit.TimeValue; | ||
import org.elasticsearch.common.xcontent.ToXContentObject; | ||
import org.elasticsearch.common.xcontent.XContentBuilder; | ||
|
||
import java.io.IOException; | ||
import java.util.List; | ||
import java.util.Objects; | ||
|
||
/** | ||
* Request to create API key | ||
*/ | ||
public final class CreateApiKeyRequest implements Validatable, ToXContentObject { | ||
|
||
private final String name; | ||
private final TimeValue expiration; | ||
private final List<Role> roles; | ||
private final RefreshPolicy refreshPolicy; | ||
|
||
/** | ||
* Create API Key request constructor | ||
* @param name name for the API key | ||
* @param roles list of {@link Role}s | ||
* @param expiration to specify expiration for the API key | ||
*/ | ||
public CreateApiKeyRequest(String name, List<Role> roles, @Nullable TimeValue expiration, @Nullable final RefreshPolicy refreshPolicy) { | ||
if (Strings.hasText(name)) { | ||
this.name = name; | ||
} else { | ||
throw new IllegalArgumentException("name must not be null or empty"); | ||
} | ||
this.roles = Objects.requireNonNull(roles, "roles may not be null"); | ||
this.expiration = expiration; | ||
this.refreshPolicy = (refreshPolicy == null) ? RefreshPolicy.getDefault() : refreshPolicy; | ||
} | ||
|
||
public String getName() { | ||
return name; | ||
} | ||
|
||
public TimeValue getExpiration() { | ||
return expiration; | ||
} | ||
|
||
public List<Role> getRoles() { | ||
return roles; | ||
} | ||
|
||
public RefreshPolicy getRefreshPolicy() { | ||
return refreshPolicy; | ||
} | ||
|
||
@Override | ||
public int hashCode() { | ||
return Objects.hash(name, refreshPolicy, roles, expiration); | ||
} | ||
|
||
@Override | ||
public boolean equals(Object o) { | ||
if (this == o) { | ||
return true; | ||
} | ||
if (o == null || getClass() != o.getClass()) { | ||
return false; | ||
} | ||
final CreateApiKeyRequest that = (CreateApiKeyRequest) o; | ||
return Objects.equals(name, that.name) && Objects.equals(refreshPolicy, that.refreshPolicy) && Objects.equals(roles, that.roles) | ||
&& Objects.equals(expiration, that.expiration); | ||
} | ||
|
||
@Override | ||
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { | ||
builder.startObject().field("name", name); | ||
if (expiration != null) { | ||
builder.field("expiration", expiration.getStringRep()); | ||
} | ||
builder.startObject("role_descriptors"); | ||
for (Role role : roles) { | ||
builder.startObject(role.getName()); | ||
if (role.getApplicationPrivileges() != null) { | ||
builder.field(Role.APPLICATIONS.getPreferredName(), role.getApplicationPrivileges()); | ||
} | ||
if (role.getClusterPrivileges() != null) { | ||
builder.field(Role.CLUSTER.getPreferredName(), role.getClusterPrivileges()); | ||
} | ||
if (role.getGlobalPrivileges() != null) { | ||
builder.field(Role.GLOBAL.getPreferredName(), role.getGlobalPrivileges()); | ||
} | ||
if (role.getIndicesPrivileges() != null) { | ||
builder.field(Role.INDICES.getPreferredName(), role.getIndicesPrivileges()); | ||
} | ||
if (role.getMetadata() != null) { | ||
builder.field(Role.METADATA.getPreferredName(), role.getMetadata()); | ||
} | ||
if (role.getRunAsPrivilege() != null) { | ||
builder.field(Role.RUN_AS.getPreferredName(), role.getRunAsPrivilege()); | ||
} | ||
builder.endObject(); | ||
} | ||
builder.endObject(); | ||
return builder.endObject(); | ||
} | ||
|
||
} |
Oops, something went wrong.