-
Notifications
You must be signed in to change notification settings - Fork 0
Adding end to end endpoint with various changes #1
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
Changes from all commits
1d24b45
da94b82
5913b57
437d315
4eda3db
d4ab332
64732bd
861b588
86b4fd9
d15044b
7f3e40e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,155 @@ | ||
| package org.opensearch.security.action.onbehalf; | ||
|
|
||
| import java.io.IOException; | ||
| import java.util.List; | ||
|
|
||
| import org.apache.cxf.rs.security.jose.jwt.JwtToken; | ||
| import org.apache.logging.log4j.LogManager; | ||
| import org.apache.logging.log4j.Logger; | ||
|
|
||
| import org.opensearch.action.FailedNodeException; | ||
| import org.opensearch.action.support.ActionFilters; | ||
| import org.opensearch.action.support.nodes.TransportNodesAction; | ||
| import org.opensearch.cluster.service.ClusterService; | ||
| import org.opensearch.common.inject.Inject; | ||
| import org.opensearch.common.inject.Provider; | ||
| import org.opensearch.common.io.stream.StreamInput; | ||
| import org.opensearch.common.io.stream.StreamOutput; | ||
| import org.opensearch.common.settings.Settings; | ||
| import org.opensearch.security.auth.BackendRegistry; | ||
| import org.opensearch.security.authtoken.jwt.JwtVendor; | ||
| import org.opensearch.security.configuration.ConfigurationRepository; | ||
| import org.opensearch.security.securityconf.DynamicConfigFactory; | ||
| import org.opensearch.security.securityconf.impl.CType; | ||
| import org.opensearch.threadpool.ThreadPool; | ||
| import org.opensearch.transport.TransportRequest; | ||
| import org.opensearch.transport.TransportService; | ||
| import org.opensearch.rest.BaseRestHandler; | ||
|
|
||
| import java.io.IOException; | ||
| import java.nio.file.Path; | ||
| import java.security.cert.X509Certificate; | ||
| import java.util.Arrays; | ||
| import java.util.List; | ||
| import java.util.Map; | ||
| import java.util.Optional; | ||
| import java.util.stream.Collectors; | ||
|
|
||
| import com.fasterxml.jackson.databind.JsonNode; | ||
| import com.google.common.collect.ImmutableList; | ||
| import com.google.common.collect.ImmutableMap; | ||
| import org.apache.logging.log4j.LogManager; | ||
| import org.apache.logging.log4j.Logger; | ||
|
|
||
| import org.opensearch.client.Client; | ||
| import org.opensearch.cluster.service.ClusterService; | ||
| import org.opensearch.common.bytes.BytesReference; | ||
| import org.opensearch.common.settings.Settings; | ||
| import org.opensearch.core.xcontent.XContentBuilder; | ||
| import org.opensearch.rest.BytesRestResponse; | ||
| import org.opensearch.rest.RestChannel; | ||
| import org.opensearch.rest.RestController; | ||
| import org.opensearch.rest.RestRequest; | ||
| import org.opensearch.rest.RestRequest.Method; | ||
| import org.opensearch.rest.RestStatus; | ||
| import org.opensearch.security.auditlog.AuditLog; | ||
| import org.opensearch.security.configuration.AdminDNs; | ||
| import org.opensearch.security.configuration.ConfigurationRepository; | ||
| import org.opensearch.security.dlic.rest.validation.AbstractConfigurationValidator; | ||
| import org.opensearch.security.privileges.PrivilegesEvaluator; | ||
| import org.opensearch.security.securityconf.impl.CType; | ||
| import org.opensearch.security.securityconf.impl.SecurityDynamicConfiguration; | ||
| import org.opensearch.security.ssl.SecurityKeyStore; | ||
| import org.opensearch.security.ssl.transport.PrincipalExtractor; | ||
| import org.opensearch.security.ssl.util.SSLConfigConstants; | ||
| import org.opensearch.security.support.ConfigConstants; | ||
| import org.opensearch.threadpool.ThreadPool; | ||
| import org.opensearch.client.node.NodeClient; | ||
| import org.opensearch.security.user.User; | ||
|
|
||
| import static org.opensearch.security.dlic.rest.support.Utils.addRoutesPrefix; | ||
|
|
||
| public class CreateOnBehalfOfToken extends BaseRestHandler { | ||
|
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This class was hastily created, it should be re-written and compacted significately. I was also betting that we could use the AuthZ on REST requests so transport actions weren't generated. Might make sense to make this follow the transport actions more formally until that has been merged/backported |
||
|
|
||
| private final JwtVendor vendor; | ||
| private final ThreadPool threadPool; | ||
|
|
||
| public CreateOnBehalfOfToken(final Settings settings, final ThreadPool threadPool) { | ||
| Settings testSettings = Settings.builder() | ||
| .put("signing_key", "1234567890123456") | ||
| .put("encryption_key", "1234567890123456").build(); | ||
|
|
||
| this.vendor = new JwtVendor(testSettings, Optional.empty()); | ||
| this.threadPool = threadPool; | ||
| } | ||
|
|
||
| @Override | ||
| public String getName() { | ||
| return getClass().getSimpleName(); | ||
| } | ||
|
|
||
| @Override | ||
| public List<Route> routes() { | ||
| return addRoutesPrefix( | ||
| ImmutableList.of( | ||
| new Route(Method.POST, "/user/onbehalfof") | ||
| ) | ||
| ); | ||
| } | ||
|
|
||
| @Override | ||
| protected RestChannelConsumer prepareRequest(RestRequest request, NodeClient client) throws IOException { | ||
| switch (request.method()) { | ||
| case POST: | ||
| return handlePost(request, client); | ||
| default: | ||
| throw new IllegalArgumentException(request.method() + " not supported"); | ||
| } | ||
| } | ||
|
|
||
| private RestChannelConsumer handlePost(RestRequest request, NodeClient client) throws IOException { | ||
| return new RestChannelConsumer() { | ||
| @Override | ||
| public void accept(RestChannel channel) throws Exception { | ||
| final XContentBuilder builder = channel.newBuilder(); | ||
| BytesRestResponse response; | ||
| try { | ||
| final Map<String, Object> requestBody = request.contentOrSourceParamParser().map(); | ||
| final String reason = (String)requestBody.getOrDefault("reason", null); | ||
|
|
||
| final Integer tokenDuration = Optional.ofNullable(requestBody.get("duration")) | ||
| .map(value -> (String)value) | ||
| .map(Integer::parseInt) | ||
| .map(value -> Math.min(value, 72)) // Max duration is 72 hours | ||
| .orElse(24); // Fallback to default; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. IMO 24 hours is too wide of a window. This value should match any currently configured REST Handler timeouts.
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good called, we can change the defaults as needed |
||
|
|
||
| final String source = "self-issued"; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If this is cluster name then this should be able to be obtained from the ClusterState which you can get in There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In this REST API's case, the token was created by self-issued by user. If this was generated by a plugin or extension, then it should be marked with the identifier for traceability. This is good feedback around if we have the correct information in the token for the audit log or other use cases |
||
| final User user = threadPool.getThreadContext().getTransient(ConfigConstants.OPENDISTRO_SECURITY_USER); | ||
|
|
||
| builder.startObject(); | ||
| builder.field("user", user.getName()); | ||
| final String token = vendor.createJwt(/* TODO: Update the issuer to represent the cluster */"OpenSearch", | ||
| user.getName(), | ||
| source, | ||
| tokenDuration, | ||
| user.getSecurityRoles().stream().collect(Collectors.toList())); | ||
|
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Need more research into how these roles are populated, I'm not sure if this is correct There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For extensions the token will contain the mapped roles |
||
| builder.field("onBehalfOfToken", token); | ||
| builder.field("duration", tokenDuration); | ||
| builder.endObject(); | ||
|
|
||
| response = new BytesRestResponse(RestStatus.OK, builder); | ||
| } catch (final Exception exception) { | ||
| System.out.println(exception.toString()); | ||
| builder.startObject() | ||
| .field("error", exception.toString()) | ||
| .endObject(); | ||
|
|
||
| response = new BytesRestResponse(RestStatus.INTERNAL_SERVER_ERROR, builder); | ||
| } | ||
| builder.close(); | ||
| channel.sendResponse(response); | ||
| } | ||
| }; | ||
| } | ||
|
|
||
| } | ||
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.
Fixed the build break