diff --git a/conf/functions_worker.yml b/conf/functions_worker.yml index b41ac8f37a44f..bb15e0ca416da 100644 --- a/conf/functions_worker.yml +++ b/conf/functions_worker.yml @@ -311,6 +311,8 @@ authenticationProviders: authorizationProvider: org.apache.pulsar.broker.authorization.PulsarAuthorizationProvider # Set of role names that are treated as "super-user", meaning they will be able to access any admin-api superUserRoles: +# Set of role names that are treated as "proxy" roles. These are the roles that can supply the originalPrincipal. +proxyRoles: #### tls configuration for worker service # Enable TLS diff --git a/pulsar-broker-common/src/main/java/org/apache/pulsar/broker/authentication/AuthenticationParameters.java b/pulsar-broker-common/src/main/java/org/apache/pulsar/broker/authentication/AuthenticationParameters.java new file mode 100644 index 0000000000000..638772345bdf3 --- /dev/null +++ b/pulsar-broker-common/src/main/java/org/apache/pulsar/broker/authentication/AuthenticationParameters.java @@ -0,0 +1,67 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF 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.apache.pulsar.broker.authentication; + +import lombok.Builder; + +/** + * A class to collect all the common fields used for authentication. Because the authentication data source is + * not always consistent when using the Pulsar Protocol and the Pulsar Proxy + * (see 19332), this class is currently restricted + * to use only in authenticating HTTP requests. + */ +@Builder +@lombok.Value +public class AuthenticationParameters { + + /** + * The original principal (or role) of the client. + *

+ * For HTTP Authentication, there are two possibilities. When the client connects directly to the broker, this + * field is null (assuming the client hasn't supplied the original principal header). When the client connects + * through the proxy, the proxy calculates the original principal based on the client's authentication data and + * supplies it in this field. + *

+ */ + String originalPrincipal; + + /** + * The client role. + *

+ * For HTTP Authentication, there are three possibilities. When the client connects directly to the broker, this + * is the client's role. When the client connects through the proxy using mTLS, this is the role of the proxy. + * In this case, the {@link #originalPrincipal} is also supplied and is the role of the original client as + * determined by the proxy. When the client connects through the proxy using any other form of authentication, + * this is the role of the original client. In this case, the {@link #originalPrincipal} is also the role of + * the original client. + *

+ */ + String clientRole; + + /** + * The authentication data source used to generate the {@link #clientRole}. + *

+ * For HTTP Authentication, there are three possibilities. When the client connects directly to the broker, this + * is the client's {@link AuthenticationDataSource}. When the client connects through the proxy using mTLS, this + * is the proxy's {@link AuthenticationDataSource}. When the client connects through the proxy using any other + * form of authentication, this is the original client's {@link AuthenticationDataSource}. + *

+ */ + AuthenticationDataSource clientAuthenticationDataSource; +} diff --git a/pulsar-broker-common/src/main/java/org/apache/pulsar/broker/authorization/AuthorizationService.java b/pulsar-broker-common/src/main/java/org/apache/pulsar/broker/authorization/AuthorizationService.java index 6fff04b33b618..6c730f20092dd 100644 --- a/pulsar-broker-common/src/main/java/org/apache/pulsar/broker/authorization/AuthorizationService.java +++ b/pulsar-broker-common/src/main/java/org/apache/pulsar/broker/authorization/AuthorizationService.java @@ -23,10 +23,12 @@ import java.util.Set; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; +import javax.ws.rs.core.Response; import org.apache.commons.lang3.StringUtils; import org.apache.pulsar.broker.PulsarServerException; import org.apache.pulsar.broker.ServiceConfiguration; import org.apache.pulsar.broker.authentication.AuthenticationDataSource; +import org.apache.pulsar.broker.authentication.AuthenticationParameters; import org.apache.pulsar.broker.resources.PulsarResources; import org.apache.pulsar.common.naming.NamespaceName; import org.apache.pulsar.common.naming.TopicName; @@ -49,6 +51,7 @@ public class AuthorizationService { private static final Logger log = LoggerFactory.getLogger(AuthorizationService.class); + private final PulsarResources resources; private final AuthorizationProvider provider; private final ServiceConfiguration conf; @@ -61,6 +64,7 @@ public AuthorizationService(ServiceConfiguration conf, PulsarResources pulsarRes provider = (AuthorizationProvider) Class.forName(providerClassname) .getDeclaredConstructor().newInstance(); provider.initialize(conf, pulsarResources); + this.resources = pulsarResources; log.info("{} has been loaded.", providerClassname); } else { throw new PulsarServerException("No authorization providers are present."); @@ -72,6 +76,23 @@ public AuthorizationService(ServiceConfiguration conf, PulsarResources pulsarRes } } + public CompletableFuture isSuperUser(AuthenticationParameters authParams) { + if (!isValidOriginalPrincipal(authParams)) { + return CompletableFuture.completedFuture(false); + } + if (isProxyRole(authParams.getClientRole())) { + CompletableFuture isRoleAuthorizedFuture = isSuperUser(authParams.getClientRole(), + authParams.getClientAuthenticationDataSource()); + // The current paradigm is to pass the client auth data when we don't have access to the original auth data. + CompletableFuture isOriginalAuthorizedFuture = isSuperUser(authParams.getOriginalPrincipal(), + authParams.getClientAuthenticationDataSource()); + return isRoleAuthorizedFuture.thenCombine(isOriginalAuthorizedFuture, + (isRoleAuthorized, isOriginalAuthorized) -> isRoleAuthorized && isOriginalAuthorized); + } else { + return isSuperUser(authParams.getClientRole(), authParams.getClientAuthenticationDataSource()); + } + } + public CompletableFuture isSuperUser(String user, AuthenticationDataSource authenticationData) { return provider.isSuperUser(user, authenticationData, conf); } @@ -279,17 +300,118 @@ public CompletableFuture canLookupAsync(TopicName topicName, String rol public CompletableFuture allowFunctionOpsAsync(NamespaceName namespaceName, String role, AuthenticationDataSource authenticationData) { - return provider.allowFunctionOpsAsync(namespaceName, role, authenticationData); + return isSuperUserOrAdmin(namespaceName, role, authenticationData) + .thenCompose(isSuperUserOrAdmin -> isSuperUserOrAdmin + ? CompletableFuture.completedFuture(true) + : provider.allowFunctionOpsAsync(namespaceName, role, authenticationData) + ); + } + + public CompletableFuture allowFunctionOpsAsync(NamespaceName namespaceName, + AuthenticationParameters authParams) { + if (!isValidOriginalPrincipal(authParams)) { + return CompletableFuture.completedFuture(false); + } + if (isProxyRole(authParams.getClientRole())) { + CompletableFuture isRoleAuthorizedFuture = allowFunctionOpsAsync(namespaceName, + authParams.getClientRole(), authParams.getClientAuthenticationDataSource()); + // The current paradigm is to pass the client auth data when we don't have access to the original auth data. + CompletableFuture isOriginalAuthorizedFuture = allowFunctionOpsAsync( + namespaceName, authParams.getOriginalPrincipal(), authParams.getClientAuthenticationDataSource()); + return isRoleAuthorizedFuture.thenCombine(isOriginalAuthorizedFuture, + (isRoleAuthorized, isOriginalAuthorized) -> isRoleAuthorized && isOriginalAuthorized); + } else { + return allowFunctionOpsAsync(namespaceName, authParams.getClientRole(), + authParams.getClientAuthenticationDataSource()); + } } public CompletableFuture allowSourceOpsAsync(NamespaceName namespaceName, String role, AuthenticationDataSource authenticationData) { - return provider.allowSourceOpsAsync(namespaceName, role, authenticationData); + return isSuperUserOrAdmin(namespaceName, role, authenticationData) + .thenCompose(isSuperUserOrAdmin -> isSuperUserOrAdmin + ? CompletableFuture.completedFuture(true) + : provider.allowSourceOpsAsync(namespaceName, role, authenticationData) + ); + } + + public CompletableFuture allowSourceOpsAsync(NamespaceName namespaceName, + AuthenticationParameters authParams) { + if (!isValidOriginalPrincipal(authParams)) { + return CompletableFuture.completedFuture(false); + } + if (isProxyRole(authParams.getClientRole())) { + CompletableFuture isRoleAuthorizedFuture = allowSourceOpsAsync(namespaceName, + authParams.getClientRole(), authParams.getClientAuthenticationDataSource()); + // The current paradigm is to pass the client auth data when we don't have access to the original auth data. + CompletableFuture isOriginalAuthorizedFuture = allowSourceOpsAsync( + namespaceName, authParams.getOriginalPrincipal(), authParams.getClientAuthenticationDataSource()); + return isRoleAuthorizedFuture.thenCombine(isOriginalAuthorizedFuture, + (isRoleAuthorized, isOriginalAuthorized) -> isRoleAuthorized && isOriginalAuthorized); + } else { + return allowSourceOpsAsync(namespaceName, authParams.getClientRole(), + authParams.getClientAuthenticationDataSource()); + } } public CompletableFuture allowSinkOpsAsync(NamespaceName namespaceName, String role, AuthenticationDataSource authenticationData) { - return provider.allowSinkOpsAsync(namespaceName, role, authenticationData); + return isSuperUserOrAdmin(namespaceName, role, authenticationData) + .thenCompose(isSuperUserOrAdmin -> isSuperUserOrAdmin + ? CompletableFuture.completedFuture(true) + : provider.allowSinkOpsAsync(namespaceName, role, authenticationData) + ); + } + + public CompletableFuture allowSinkOpsAsync(NamespaceName namespaceName, + AuthenticationParameters authParams) { + if (!isValidOriginalPrincipal(authParams)) { + return CompletableFuture.completedFuture(false); + } + if (isProxyRole(authParams.getClientRole())) { + CompletableFuture isRoleAuthorizedFuture = allowSinkOpsAsync(namespaceName, + authParams.getClientRole(), authParams.getClientAuthenticationDataSource()); + // The current paradigm is to pass the client auth data when we don't have access to the original auth data. + CompletableFuture isOriginalAuthorizedFuture = allowSinkOpsAsync( + namespaceName, authParams.getOriginalPrincipal(), authParams.getClientAuthenticationDataSource()); + return isRoleAuthorizedFuture.thenCombine(isOriginalAuthorizedFuture, + (isRoleAuthorized, isOriginalAuthorized) -> isRoleAuthorized && isOriginalAuthorized); + } else { + return allowSinkOpsAsync(namespaceName, authParams.getClientRole(), + authParams.getClientAuthenticationDataSource()); + } + } + + /** + * Functions, sources, and sinks each have their own method in this class. This method first checks for + * tenant admin access, then for namespace level permission. + */ + private CompletableFuture isSuperUserOrAdmin(NamespaceName namespaceName, + String role, + AuthenticationDataSource authenticationData) { + return isSuperUser(role, authenticationData) + .thenCompose(isSuperUserOrAdmin -> isSuperUserOrAdmin + ? CompletableFuture.completedFuture(true) + : isTenantAdmin(namespaceName.getTenant(), role, authenticationData)); + } + + private CompletableFuture isTenantAdmin(String tenant, String role, + AuthenticationDataSource authData) { + return resources.getTenantResources() + .getTenantAsync(tenant) + .thenCompose(op -> { + if (op.isPresent()) { + return isTenantAdmin(tenant, role, op.get(), authData); + } else { + return CompletableFuture.failedFuture(new RestException(Response.Status.NOT_FOUND, + "Tenant does not exist")); + } + }); + } + + private boolean isValidOriginalPrincipal(AuthenticationParameters authParams) { + return isValidOriginalPrincipal(authParams.getClientRole(), + authParams.getOriginalPrincipal(), authParams.getClientAuthenticationDataSource()); } /** diff --git a/pulsar-broker/src/main/java/org/apache/pulsar/broker/PulsarService.java b/pulsar-broker/src/main/java/org/apache/pulsar/broker/PulsarService.java index 4af94c339c82f..b2eefe298b53c 100644 --- a/pulsar-broker/src/main/java/org/apache/pulsar/broker/PulsarService.java +++ b/pulsar-broker/src/main/java/org/apache/pulsar/broker/PulsarService.java @@ -1860,6 +1860,7 @@ public static WorkerConfig initializeWorkerConfigFromBrokerConfig(ServiceConfigu // inherit super users workerConfig.setSuperUserRoles(brokerConfig.getSuperUserRoles()); + workerConfig.setProxyRoles(brokerConfig.getProxyRoles()); workerConfig.setFunctionsWorkerEnablePackageManagement(brokerConfig.isFunctionsWorkerEnablePackageManagement()); // inherit the nar package locations diff --git a/pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/impl/BrokerStatsBase.java b/pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/impl/BrokerStatsBase.java index 670fa10b28b88..6d49dd81da13d 100644 --- a/pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/impl/BrokerStatsBase.java +++ b/pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/impl/BrokerStatsBase.java @@ -159,6 +159,7 @@ public LoadManagerReport getLoadReport() throws Exception { protected Map> internalBrokerResourceAvailability(NamespaceName namespace) { try { + validateSuperUserAccess(); LoadManager lm = pulsar().getLoadManager().get(); if (lm instanceof SimpleLoadManagerImpl) { return ((SimpleLoadManagerImpl) lm).getResourceAvailabilityFor(namespace).asMap(); diff --git a/pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/impl/FunctionsBase.java b/pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/impl/FunctionsBase.java index bee81bf5dcdb6..4350316e2f011 100644 --- a/pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/impl/FunctionsBase.java +++ b/pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/impl/FunctionsBase.java @@ -194,7 +194,7 @@ public void registerFunction( ) final @FormDataParam("functionConfig") FunctionConfig functionConfig) { functions().registerFunction(tenant, namespace, functionName, uploadedInputStream, fileDetail, - functionPkgUrl, functionConfig, clientAppId(), clientAuthData()); + functionPkgUrl, functionConfig, authParams()); } @PUT @@ -322,7 +322,7 @@ public void updateFunction( final @FormDataParam("updateOptions") UpdateOptionsImpl updateOptions) throws IOException { functions().updateFunction(tenant, namespace, functionName, uploadedInputStream, fileDetail, - functionPkgUrl, functionConfig, clientAppId(), clientAuthData(), updateOptions); + functionPkgUrl, functionConfig, authParams(), updateOptions); } @@ -343,7 +343,7 @@ public void deregisterFunction( final @PathParam("namespace") String namespace, @ApiParam(value = "The name of a Pulsar Function") final @PathParam("functionName") String functionName) { - functions().deregisterFunction(tenant, namespace, functionName, clientAppId(), clientAuthData()); + functions().deregisterFunction(tenant, namespace, functionName, authParams()); } @GET @@ -365,7 +365,7 @@ public FunctionConfig getFunctionInfo( final @PathParam("namespace") String namespace, @ApiParam(value = "The name of a Pulsar Function") final @PathParam("functionName") String functionName) throws IOException { - return functions().getFunctionInfo(tenant, namespace, functionName, clientAppId(), clientAuthData()); + return functions().getFunctionInfo(tenant, namespace, functionName, authParams()); } @GET @@ -389,7 +389,7 @@ public FunctionStatus.FunctionInstanceStatus.FunctionInstanceStatusData getFunct + " the stats of all instances is returned") final @PathParam("instanceId") String instanceId) throws IOException { return functions().getFunctionInstanceStatus(tenant, namespace, functionName, - instanceId, uri.getRequestUri(), clientAppId(), clientAuthData()); + instanceId, uri.getRequestUri(), authParams()); } @GET @@ -413,7 +413,7 @@ public FunctionStatus getFunctionStatus( @ApiParam(value = "The name of a Pulsar Function") final @PathParam("functionName") String functionName) throws IOException { return functions().getFunctionStatus(tenant, namespace, functionName, uri.getRequestUri(), - clientAppId(), clientAuthData()); + authParams()); } @GET @@ -437,7 +437,7 @@ public FunctionStatsImpl getFunctionStats( @ApiParam(value = "The name of a Pulsar Function") final @PathParam("functionName") String functionName) throws IOException { return functions().getFunctionStats(tenant, namespace, functionName, - uri.getRequestUri(), clientAppId(), clientAuthData()); + uri.getRequestUri(), authParams()); } @GET @@ -461,7 +461,7 @@ public FunctionInstanceStatsDataImpl getFunctionInstanceStats( + " (if instance-id is not provided, the stats of all instances is returned") final @PathParam( "instanceId") String instanceId) throws IOException { return functions().getFunctionsInstanceStats(tenant, namespace, functionName, instanceId, - uri.getRequestUri(), clientAppId(), clientAuthData()); + uri.getRequestUri(), authParams()); } @GET @@ -480,7 +480,7 @@ public List listFunctions( final @PathParam("tenant") String tenant, @ApiParam(value = "The namespace of a Pulsar Function") final @PathParam("namespace") String namespace) { - return functions().listFunctions(tenant, namespace, clientAppId(), clientAuthData()); + return functions().listFunctions(tenant, namespace, authParams()); } @POST @@ -509,7 +509,7 @@ public String triggerFunction( + " consumes from which you want to inject the data to") final @FormDataParam("topic") String topic) { return functions().triggerFunction(tenant, namespace, functionName, triggerValue, - triggerStream, topic, clientAppId(), clientAuthData()); + triggerStream, topic, authParams()); } @GET @@ -533,7 +533,7 @@ public FunctionState getFunctionState( final @PathParam("functionName") String functionName, @ApiParam(value = "The stats key") final @PathParam("key") String key) { - return functions().getFunctionState(tenant, namespace, functionName, key, clientAppId(), clientAuthData()); + return functions().getFunctionState(tenant, namespace, functionName, key, authParams()); } @POST @@ -553,7 +553,7 @@ public void putFunctionState(final @PathParam("tenant") String tenant, final @PathParam("functionName") String functionName, final @PathParam("key") String key, final @FormDataParam("state") FunctionState stateJson) { - functions().putFunctionState(tenant, namespace, functionName, key, stateJson, clientAppId(), clientAuthData()); + functions().putFunctionState(tenant, namespace, functionName, key, stateJson, authParams()); } @POST @@ -574,7 +574,7 @@ public void restartFunction( "The instanceId of a Pulsar Function (if instance-id is not provided, all instances are restarted") final @PathParam("instanceId") String instanceId) { functions().restartFunctionInstance(tenant, namespace, functionName, instanceId, - uri.getRequestUri(), clientAppId(), clientAuthData()); + uri.getRequestUri(), authParams()); } @POST @@ -593,7 +593,7 @@ public void restartFunction( final @PathParam("namespace") String namespace, @ApiParam(value = "The name of a Pulsar Function") final @PathParam("functionName") String functionName) { - functions().restartFunctionInstances(tenant, namespace, functionName, clientAppId(), clientAuthData()); + functions().restartFunctionInstances(tenant, namespace, functionName, authParams()); } @POST @@ -613,7 +613,7 @@ public void stopFunction( "The instanceId of a Pulsar Function (if instance-id is not provided, all instances are stopped. ") final @PathParam("instanceId") String instanceId) { functions().stopFunctionInstance(tenant, namespace, functionName, instanceId, - uri.getRequestUri(), clientAppId(), clientAuthData()); + uri.getRequestUri(), authParams()); } @POST @@ -632,7 +632,7 @@ public void stopFunction( final @PathParam("namespace") String namespace, @ApiParam(value = "The name of a Pulsar Function") final @PathParam("functionName") String functionName) { - functions().stopFunctionInstances(tenant, namespace, functionName, clientAppId(), clientAuthData()); + functions().stopFunctionInstances(tenant, namespace, functionName, authParams()); } @POST @@ -652,7 +652,7 @@ public void startFunction( + " (if instance-id is not provided, all instances sre started. ") final @PathParam("instanceId") String instanceId) { functions().startFunctionInstance(tenant, namespace, functionName, instanceId, - uri.getRequestUri(), clientAppId(), clientAuthData()); + uri.getRequestUri(), authParams()); } @POST @@ -671,7 +671,7 @@ public void startFunction( final @PathParam("namespace") String namespace, @ApiParam(value = "The name of a Pulsar Function") final @PathParam("functionName") String functionName) { - functions().startFunctionInstances(tenant, namespace, functionName, clientAppId(), clientAuthData()); + functions().startFunctionInstances(tenant, namespace, functionName, authParams()); } @POST @@ -683,7 +683,7 @@ public void startFunction( @Consumes(MediaType.MULTIPART_FORM_DATA) public void uploadFunction(final @FormDataParam("data") InputStream uploadedInputStream, final @FormDataParam("path") String path) { - functions().uploadFunction(uploadedInputStream, path, clientAppId(), clientAuthData()); + functions().uploadFunction(uploadedInputStream, path, authParams()); } @GET @@ -693,7 +693,7 @@ public void uploadFunction(final @FormDataParam("data") InputStream uploadedInpu ) @Path("/download") public StreamingOutput downloadFunction(final @QueryParam("path") String path) { - return functions().downloadFunction(path, clientAppId(), clientAuthData()); + return functions().downloadFunction(path, authParams()); } @GET @@ -712,8 +712,7 @@ public StreamingOutput downloadFunction( @ApiParam(value = "Whether to download the transform-function") final @QueryParam("transform-function") boolean transformFunction) { - return functions() - .downloadFunction(tenant, namespace, functionName, clientAppId(), clientAuthData(), transformFunction); + return functions().downloadFunction(tenant, namespace, functionName, authParams(), transformFunction); } @GET @@ -746,7 +745,7 @@ public List getConnectorsList() throws IOException { }) @Path("/builtins/reload") public void reloadBuiltinFunctions() throws IOException { - functions().reloadBuiltinFunctions(clientAppId(), clientAuthData()); + functions().reloadBuiltinFunctions(authParams()); } @GET @@ -763,7 +762,7 @@ public void reloadBuiltinFunctions() throws IOException { @Path("/builtins") @Produces(MediaType.APPLICATION_JSON) public List getBuiltinFunction() { - return functions().getBuiltinFunctions(clientAppId(), clientAuthData()); + return functions().getBuiltinFunctions(authParams()); } @PUT @@ -785,6 +784,6 @@ public void updateFunctionOnWorkerLeader(final @PathParam("tenant") String tenan final @FormDataParam("delete") boolean delete) { functions().updateFunctionOnWorkerLeader(tenant, namespace, functionName, uploadedInputStream, - delete, uri.getRequestUri(), clientAppId(), clientAuthData()); + delete, uri.getRequestUri(), authParams()); } } diff --git a/pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/impl/SinksBase.java b/pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/impl/SinksBase.java index 7a3a554a3a00c..80ad72d6f9aa9 100644 --- a/pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/impl/SinksBase.java +++ b/pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/impl/SinksBase.java @@ -168,7 +168,7 @@ public void registerSink(@ApiParam(value = "The tenant of a Pulsar Sink") final ) final @FormDataParam("sinkConfig") SinkConfig sinkConfig) { sinks().registerSink(tenant, namespace, sinkName, uploadedInputStream, fileDetail, - sinkPkgUrl, sinkConfig, clientAppId(), clientAuthData()); + sinkPkgUrl, sinkConfig, authParams()); } @PUT @@ -271,7 +271,7 @@ public void updateSink(@ApiParam(value = "The tenant of a Pulsar Sink") final @P @ApiParam(value = "Update options for the Pulsar Sink") final @FormDataParam("updateOptions") UpdateOptionsImpl updateOptions) { sinks().updateSink(tenant, namespace, sinkName, uploadedInputStream, fileDetail, - sinkPkgUrl, sinkConfig, clientAppId(), clientAuthData(), updateOptions); + sinkPkgUrl, sinkConfig, authParams(), updateOptions); } @@ -295,7 +295,7 @@ public void deregisterSink(@ApiParam(value = "The tenant of a Pulsar Sink") final @PathParam("namespace") String namespace, @ApiParam(value = "The name of a Pulsar Sink") final @PathParam("sinkName") String sinkName) { - sinks().deregisterFunction(tenant, namespace, sinkName, clientAppId(), clientAuthData()); + sinks().deregisterFunction(tenant, namespace, sinkName, authParams()); } @GET @@ -315,7 +315,7 @@ public SinkConfig getSinkInfo(@ApiParam(value = "The tenant of a Pulsar Sink") final @PathParam("namespace") String namespace, @ApiParam(value = "The name of a Pulsar Sink") final @PathParam("sinkName") String sinkName) throws IOException { - return sinks().getSinkInfo(tenant, namespace, sinkName); + return sinks().getSinkInfo(tenant, namespace, sinkName, authParams()); } @GET @@ -342,7 +342,7 @@ public SinkStatus.SinkInstanceStatus.SinkInstanceStatusData getSinkInstanceStatu @ApiParam(value = "The instanceId of a Pulsar Sink") final @PathParam("instanceId") String instanceId) throws IOException { return sinks().getSinkInstanceStatus( - tenant, namespace, sinkName, instanceId, uri.getRequestUri(), clientAppId(), clientAuthData()); + tenant, namespace, sinkName, instanceId, uri.getRequestUri(), authParams()); } @GET @@ -365,7 +365,7 @@ public SinkStatus getSinkStatus(@ApiParam(value = "The tenant of a Pulsar Sink") final @PathParam("namespace") String namespace, @ApiParam(value = "The name of a Pulsar Sink") final @PathParam("sinkName") String sinkName) throws IOException { - return sinks().getSinkStatus(tenant, namespace, sinkName, uri.getRequestUri(), clientAppId(), clientAuthData()); + return sinks().getSinkStatus(tenant, namespace, sinkName, uri.getRequestUri(), authParams()); } @GET @@ -385,7 +385,7 @@ public List listSinks(@ApiParam(value = "The tenant of a Pulsar Sink") final @PathParam("tenant") String tenant, @ApiParam(value = "The namespace of a Pulsar Sink") final @PathParam("namespace") String namespace) { - return sinks().listFunctions(tenant, namespace, clientAppId(), clientAuthData()); + return sinks().listFunctions(tenant, namespace, authParams()); } @POST @@ -411,7 +411,7 @@ public void restartSink(@ApiParam(value = "The tenant of a Pulsar Sink") @ApiParam(value = "The instanceId of a Pulsar Sink") final @PathParam("instanceId") String instanceId) { sinks().restartFunctionInstance(tenant, namespace, sinkName, instanceId, - uri.getRequestUri(), clientAppId(), clientAuthData()); + uri.getRequestUri(), authParams()); } @POST @@ -432,7 +432,7 @@ public void restartSink(@ApiParam(value = "The tenant of a Pulsar Sink") final @PathParam("namespace") String namespace, @ApiParam(value = "The name of a Pulsar Sink") final @PathParam("sinkName") String sinkName) { - sinks().restartFunctionInstances(tenant, namespace, sinkName, clientAppId(), clientAuthData()); + sinks().restartFunctionInstances(tenant, namespace, sinkName, authParams()); } @POST @@ -456,7 +456,7 @@ public void stopSink(@ApiParam(value = "The tenant of a Pulsar Sink") @ApiParam(value = "The instanceId of a Pulsar Sink") final @PathParam("instanceId") String instanceId) { sinks().stopFunctionInstance(tenant, namespace, - sinkName, instanceId, uri.getRequestUri(), clientAppId(), clientAuthData()); + sinkName, instanceId, uri.getRequestUri(), authParams()); } @POST @@ -477,7 +477,7 @@ public void stopSink(@ApiParam(value = "The tenant of a Pulsar Sink") final @PathParam("namespace") String namespace, @ApiParam(value = "The name of a Pulsar Sink") final @PathParam("sinkName") String sinkName) { - sinks().stopFunctionInstances(tenant, namespace, sinkName, clientAppId(), clientAuthData()); + sinks().stopFunctionInstances(tenant, namespace, sinkName, authParams()); } @POST @@ -501,7 +501,7 @@ public void startSink(@ApiParam(value = "The tenant of a Pulsar Sink") @ApiParam(value = "The instanceId of a Pulsar Sink") final @PathParam("instanceId") String instanceId) { sinks().startFunctionInstance(tenant, namespace, sinkName, instanceId, - uri.getRequestUri(), clientAppId(), clientAuthData()); + uri.getRequestUri(), authParams()); } @POST @@ -522,7 +522,7 @@ public void startSink(@ApiParam(value = "The tenant of a Pulsar Sink") final @PathParam("namespace") String namespace, @ApiParam(value = "The name of a Pulsar Sink") final @PathParam("sinkName") String sinkName) { - sinks().startFunctionInstances(tenant, namespace, sinkName, clientAppId(), clientAuthData()); + sinks().startFunctionInstances(tenant, namespace, sinkName, authParams()); } @GET @@ -571,6 +571,6 @@ public List getSinkConfigDefinition( }) @Path("/reloadBuiltInSinks") public void reloadSinks() { - sinks().reloadConnectors(clientAppId(), clientAuthData()); + sinks().reloadConnectors(authParams()); } } diff --git a/pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/impl/SourcesBase.java b/pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/impl/SourcesBase.java index 1bf092784dd3a..4af0afc0d6ec5 100644 --- a/pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/impl/SourcesBase.java +++ b/pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/impl/SourcesBase.java @@ -142,7 +142,7 @@ public void registerSource( ) final @FormDataParam("sourceConfig") SourceConfig sourceConfig) { sources().registerSource(tenant, namespace, sourceName, uploadedInputStream, fileDetail, - sourcePkgUrl, sourceConfig, clientAppId(), clientAuthData()); + sourcePkgUrl, sourceConfig, authParams()); } @PUT @@ -227,7 +227,7 @@ public void updateSource( @ApiParam(value = "Update options for Pulsar Source") final @FormDataParam("updateOptions") UpdateOptionsImpl updateOptions) { sources().updateSource(tenant, namespace, sourceName, uploadedInputStream, fileDetail, - sourcePkgUrl, sourceConfig, clientAppId(), clientAuthData(), updateOptions); + sourcePkgUrl, sourceConfig, authParams(), updateOptions); } @@ -250,7 +250,7 @@ public void deregisterSource( final @PathParam("namespace") String namespace, @ApiParam(value = "The name of a Pulsar Source") final @PathParam("sourceName") String sourceName) { - sources().deregisterFunction(tenant, namespace, sourceName, clientAppId(), clientAuthData()); + sources().deregisterFunction(tenant, namespace, sourceName, authParams()); } @GET @@ -271,7 +271,7 @@ public SourceConfig getSourceInfo( final @PathParam("namespace") String namespace, @ApiParam(value = "The name of a Pulsar Source") final @PathParam("sourceName") String sourceName) throws IOException { - return sources().getSourceInfo(tenant, namespace, sourceName); + return sources().getSourceInfo(tenant, namespace, sourceName, authParams()); } @GET @@ -294,7 +294,7 @@ public SourceStatus.SourceInstanceStatus.SourceInstanceStatusData getSourceInsta + " (if instance-id is not provided, the stats of all instances is returned).") final @PathParam( "instanceId") String instanceId) throws IOException { return sources().getSourceInstanceStatus( - tenant, namespace, sourceName, instanceId, uri.getRequestUri(), clientAppId(), clientAuthData()); + tenant, namespace, sourceName, instanceId, uri.getRequestUri(), authParams()); } @GET @@ -316,8 +316,7 @@ public SourceStatus getSourceStatus( final @PathParam("namespace") String namespace, @ApiParam(value = "The name of a Pulsar Source") final @PathParam("sourceName") String sourceName) throws IOException { - return sources().getSourceStatus(tenant, namespace, sourceName, uri.getRequestUri(), clientAppId(), - clientAuthData()); + return sources().getSourceStatus(tenant, namespace, sourceName, uri.getRequestUri(), authParams()); } @GET @@ -339,7 +338,7 @@ public List listSources( final @PathParam("tenant") String tenant, @ApiParam(value = "The namespace of a Pulsar Source") final @PathParam("namespace") String namespace) { - return sources().listFunctions(tenant, namespace, clientAppId(), clientAuthData()); + return sources().listFunctions(tenant, namespace, authParams()); } @POST @@ -362,7 +361,7 @@ public void restartSource( + " (if instance-id is not provided, the stats of all instances is returned).") final @PathParam( "instanceId") String instanceId) { sources().restartFunctionInstance(tenant, namespace, sourceName, instanceId, - uri.getRequestUri(), clientAppId(), clientAuthData()); + uri.getRequestUri(), authParams()); } @POST @@ -383,7 +382,7 @@ public void restartSource( final @PathParam("namespace") String namespace, @ApiParam(value = "The name of a Pulsar Source") final @PathParam("sourceName") String sourceName) { - sources().restartFunctionInstances(tenant, namespace, sourceName, clientAppId(), clientAuthData()); + sources().restartFunctionInstances(tenant, namespace, sourceName, authParams()); } @POST @@ -404,7 +403,7 @@ public void stopSource( @ApiParam(value = "The instanceId of a Pulsar Source (if instance-id is not provided," + " the stats of all instances is returned).") final @PathParam("instanceId") String instanceId) { sources().stopFunctionInstance(tenant, namespace, sourceName, instanceId, - uri.getRequestUri(), clientAppId(), clientAuthData()); + uri.getRequestUri(), authParams()); } @POST @@ -425,7 +424,7 @@ public void stopSource( final @PathParam("namespace") String namespace, @ApiParam(value = "The name of a Pulsar Source") final @PathParam("sourceName") String sourceName) { - sources().stopFunctionInstances(tenant, namespace, sourceName, clientAppId(), clientAuthData()); + sources().stopFunctionInstances(tenant, namespace, sourceName, authParams()); } @POST @@ -446,7 +445,7 @@ public void startSource( @ApiParam(value = "The instanceId of a Pulsar Source (if instance-id is not provided," + " the stats of all instances is returned).") final @PathParam("instanceId") String instanceId) { sources().startFunctionInstance(tenant, namespace, sourceName, instanceId, - uri.getRequestUri(), clientAppId(), clientAuthData()); + uri.getRequestUri(), authParams()); } @POST @@ -467,7 +466,7 @@ public void startSource( final @PathParam("namespace") String namespace, @ApiParam(value = "The name of a Pulsar Source") final @PathParam("sourceName") String sourceName) { - sources().startFunctionInstances(tenant, namespace, sourceName, clientAppId(), clientAuthData()); + sources().startFunctionInstances(tenant, namespace, sourceName, authParams()); } @GET @@ -520,6 +519,6 @@ public List getSourceConfigDefinition( }) @Path("/reloadBuiltInSources") public void reloadSources() { - sources().reloadConnectors(clientAppId(), clientAuthData()); + sources().reloadConnectors(authParams()); } } diff --git a/pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/v2/Functions.java b/pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/v2/Functions.java index 0d302bec065bc..69f99a42d0685 100644 --- a/pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/v2/Functions.java +++ b/pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/v2/Functions.java @@ -75,7 +75,7 @@ public Response registerFunction(final @PathParam("tenant") String tenant, final @FormDataParam("functionDetails") String functionDetailsJson) { return functions().registerFunction(tenant, namespace, functionName, uploadedInputStream, fileDetail, - functionPkgUrl, functionDetailsJson, clientAppId()); + functionPkgUrl, functionDetailsJson, authParams()); } @PUT @@ -96,7 +96,7 @@ public Response updateFunction(final @PathParam("tenant") String tenant, final @FormDataParam("functionDetails") String functionDetailsJson) { return functions().updateFunction(tenant, namespace, functionName, uploadedInputStream, fileDetail, - functionPkgUrl, functionDetailsJson, clientAppId()); + functionPkgUrl, functionDetailsJson, authParams()); } @@ -113,7 +113,7 @@ public Response updateFunction(final @PathParam("tenant") String tenant, public Response deregisterFunction(final @PathParam("tenant") String tenant, final @PathParam("namespace") String namespace, final @PathParam("functionName") String functionName) { - return functions().deregisterFunction(tenant, namespace, functionName, clientAppId()); + return functions().deregisterFunction(tenant, namespace, functionName, authParams()); } @GET @@ -132,8 +132,7 @@ public Response getFunctionInfo(final @PathParam("tenant") String tenant, final @PathParam("namespace") String namespace, final @PathParam("functionName") String functionName) throws IOException { - return functions().getFunctionInfo( - tenant, namespace, functionName, clientAppId()); + return functions().getFunctionInfo(tenant, namespace, functionName, authParams()); } @GET @@ -154,7 +153,7 @@ public Response getFunctionInstanceStatus(final @PathParam("tenant") String tena final @PathParam("instanceId") String instanceId) throws IOException { return functions().getFunctionInstanceStatus(tenant, namespace, functionName, instanceId, uri.getRequestUri(), - clientAppId()); + authParams()); } @GET @@ -172,7 +171,7 @@ public Response getFunctionStatus(final @PathParam("tenant") String tenant, final @PathParam("namespace") String namespace, final @PathParam("functionName") String functionName) throws IOException { return functions().getFunctionStatusV2( - tenant, namespace, functionName, uri.getRequestUri(), clientAppId()); + tenant, namespace, functionName, uri.getRequestUri(), authParams()); } @GET @@ -188,7 +187,7 @@ public Response getFunctionStatus(final @PathParam("tenant") String tenant, @Path("/{tenant}/{namespace}") public Response listFunctions(final @PathParam("tenant") String tenant, final @PathParam("namespace") String namespace) { - return functions().listFunctions(tenant, namespace, clientAppId()); + return functions().listFunctions(tenant, namespace, authParams()); } @POST @@ -211,7 +210,7 @@ public Response triggerFunction(final @PathParam("tenant") String tenant, final @FormDataParam("dataStream") InputStream triggerStream, final @FormDataParam("topic") String topic) { return functions().triggerFunction(tenant, namespace, functionName, - triggerValue, triggerStream, topic, clientAppId()); + triggerValue, triggerStream, topic, authParams()); } @GET @@ -230,7 +229,7 @@ public Response getFunctionState(final @PathParam("tenant") String tenant, final @PathParam("namespace") String namespace, final @PathParam("functionName") String functionName, final @PathParam("key") String key) { - return functions().getFunctionState(tenant, namespace, functionName, key, clientAppId()); + return functions().getFunctionState(tenant, namespace, functionName, key, authParams()); } @POST @@ -247,7 +246,7 @@ public Response restartFunction(final @PathParam("tenant") String tenant, final @PathParam("functionName") String functionName, final @PathParam("instanceId") String instanceId) { return functions().restartFunctionInstance(tenant, namespace, functionName, - instanceId, uri.getRequestUri(), clientAppId()); + instanceId, uri.getRequestUri(), authParams()); } @POST @@ -260,7 +259,7 @@ public Response restartFunction(final @PathParam("tenant") String tenant, public Response restartFunction(final @PathParam("tenant") String tenant, final @PathParam("namespace") String namespace, final @PathParam("functionName") String functionName) { - return functions().restartFunctionInstances(tenant, namespace, functionName, clientAppId()); + return functions().restartFunctionInstances(tenant, namespace, functionName, authParams()); } @POST @@ -275,7 +274,7 @@ public Response stopFunction(final @PathParam("tenant") String tenant, final @PathParam("functionName") String functionName, final @PathParam("instanceId") String instanceId) { return functions().stopFunctionInstance(tenant, namespace, functionName, - instanceId, uri.getRequestUri(), clientAppId()); + instanceId, uri.getRequestUri(), authParams()); } @POST @@ -288,7 +287,7 @@ public Response stopFunction(final @PathParam("tenant") String tenant, public Response stopFunction(final @PathParam("tenant") String tenant, final @PathParam("namespace") String namespace, final @PathParam("functionName") String functionName) { - return functions().stopFunctionInstances(tenant, namespace, functionName, clientAppId()); + return functions().stopFunctionInstances(tenant, namespace, functionName, authParams()); } @POST @@ -300,7 +299,7 @@ public Response stopFunction(final @PathParam("tenant") String tenant, @Consumes(MediaType.MULTIPART_FORM_DATA) public Response uploadFunction(final @FormDataParam("data") InputStream uploadedInputStream, final @FormDataParam("path") String path) { - return functions().uploadFunction(uploadedInputStream, path, clientAppId()); + return functions().uploadFunction(uploadedInputStream, path, authParams()); } @GET @@ -310,7 +309,7 @@ public Response uploadFunction(final @FormDataParam("data") InputStream uploaded ) @Path("/download") public Response downloadFunction(final @QueryParam("path") String path) { - return functions().downloadFunction(path, clientAppId()); + return functions().downloadFunction(path, authParams()); } @GET diff --git a/pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/v2/Worker.java b/pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/v2/Worker.java index 80c75b04917ce..3813790e4f428 100644 --- a/pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/v2/Worker.java +++ b/pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/v2/Worker.java @@ -66,7 +66,7 @@ public WorkerService get() { @Path("/cluster") @Produces(MediaType.APPLICATION_JSON) public List getCluster() { - return workers().getCluster(clientAppId()); + return workers().getCluster(authParams()); } @GET @@ -81,7 +81,7 @@ public List getCluster() { @Path("/cluster/leader") @Produces(MediaType.APPLICATION_JSON) public WorkerInfo getClusterLeader() { - return workers().getClusterLeader(clientAppId()); + return workers().getClusterLeader(authParams()); } @GET @@ -96,7 +96,7 @@ public WorkerInfo getClusterLeader() { @Path("/assignments") @Produces(MediaType.APPLICATION_JSON) public Map> getAssignments() { - return workers().getAssignments(clientAppId()); + return workers().getAssignments(authParams()); } @GET @@ -112,7 +112,7 @@ public Map> getAssignments() { @Path("/connectors") @Produces(MediaType.APPLICATION_JSON) public List getConnectorsList() throws IOException { - return workers().getListOfConnectors(clientAppId()); + return workers().getListOfConnectors(authParams()); } @PUT @@ -126,7 +126,7 @@ public List getConnectorsList() throws IOException { }) @Path("/rebalance") public void rebalance() { - workers().rebalance(uri.getRequestUri(), clientAppId()); + workers().rebalance(uri.getRequestUri(), authParams()); } @PUT @@ -142,7 +142,7 @@ public void rebalance() { }) @Path("/leader/drain") public void drainAtLeader(@QueryParam("workerId") String workerId) { - workers().drain(uri.getRequestUri(), workerId, clientAppId(), true); + workers().drain(uri.getRequestUri(), workerId, authParams(), true); } @PUT @@ -158,7 +158,7 @@ public void drainAtLeader(@QueryParam("workerId") String workerId) { }) @Path("/drain") public void drain() { - workers().drain(uri.getRequestUri(), null, clientAppId(), false); + workers().drain(uri.getRequestUri(), null, authParams(), false); } @GET @@ -172,7 +172,7 @@ public void drain() { }) @Path("/leader/drain") public LongRunningProcessStatus getDrainStatusFromLeader(@QueryParam("workerId") String workerId) { - return workers().getDrainStatus(uri.getRequestUri(), workerId, clientAppId(), true); + return workers().getDrainStatus(uri.getRequestUri(), workerId, authParams(), true); } @GET @@ -186,7 +186,7 @@ public LongRunningProcessStatus getDrainStatusFromLeader(@QueryParam("workerId") }) @Path("/drain") public LongRunningProcessStatus getDrainStatus() { - return workers().getDrainStatus(uri.getRequestUri(), null, clientAppId(), false); + return workers().getDrainStatus(uri.getRequestUri(), null, authParams(), false); } @GET @@ -199,6 +199,6 @@ public LongRunningProcessStatus getDrainStatus() { }) @Path("/cluster/leader/ready") public Boolean isLeaderReady() { - return workers().isLeaderReady(clientAppId()); + return workers().isLeaderReady(authParams()); } } diff --git a/pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/v2/WorkerStats.java b/pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/v2/WorkerStats.java index 5a77d81b6f1e3..1716b5e588c77 100644 --- a/pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/v2/WorkerStats.java +++ b/pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/v2/WorkerStats.java @@ -56,7 +56,7 @@ public Workers workers() { }) @Produces(MediaType.APPLICATION_JSON) public Collection getMetrics() throws Exception { - return workers().getWorkerMetrics(clientAppId()); + return workers().getWorkerMetrics(authParams()); } @GET @@ -72,6 +72,6 @@ public Collection getMetrics() throws Exception { }) @Produces(MediaType.APPLICATION_JSON) public List getStats() throws IOException { - return workers().getFunctionsMetrics(clientAppId()); + return workers().getFunctionsMetrics(authParams()); } } diff --git a/pulsar-broker/src/main/java/org/apache/pulsar/broker/web/PulsarWebResource.java b/pulsar-broker/src/main/java/org/apache/pulsar/broker/web/PulsarWebResource.java index a182e4733fdfe..3d23d7812543a 100644 --- a/pulsar-broker/src/main/java/org/apache/pulsar/broker/web/PulsarWebResource.java +++ b/pulsar-broker/src/main/java/org/apache/pulsar/broker/web/PulsarWebResource.java @@ -54,6 +54,7 @@ import org.apache.pulsar.broker.PulsarService; import org.apache.pulsar.broker.ServiceConfiguration; import org.apache.pulsar.broker.authentication.AuthenticationDataSource; +import org.apache.pulsar.broker.authentication.AuthenticationParameters; import org.apache.pulsar.broker.authorization.AuthorizationService; import org.apache.pulsar.broker.loadbalance.extensions.ExtensibleLoadManagerImpl; import org.apache.pulsar.broker.namespace.LookupOptions; @@ -143,6 +144,14 @@ public static String splitPath(String source, int slice) { return PolicyPath.splitPath(source, slice); } + public AuthenticationParameters authParams() { + return AuthenticationParameters.builder() + .originalPrincipal(originalPrincipal()) + .clientRole(clientAppId()) + .clientAuthenticationDataSource(clientAuthData()) + .build(); + } + /** * Gets a caller id (IP + role). * diff --git a/pulsar-functions/runtime/src/main/java/org/apache/pulsar/functions/worker/WorkerConfig.java b/pulsar-functions/runtime/src/main/java/org/apache/pulsar/functions/worker/WorkerConfig.java index b87477f78c641..3b8ddf774d162 100644 --- a/pulsar-functions/runtime/src/main/java/org/apache/pulsar/functions/worker/WorkerConfig.java +++ b/pulsar-functions/runtime/src/main/java/org/apache/pulsar/functions/worker/WorkerConfig.java @@ -563,6 +563,12 @@ public boolean isBrokerClientAuthenticationEnabled() { ) private Set superUserRoles = new TreeSet<>(); + @FieldContext( + category = CATEGORY_WORKER_SECURITY, + doc = "Role names that are treated as `proxy roles`. These are the only roles that can supply the " + + "originalPrincipal.") + private Set proxyRoles = new TreeSet<>(); + @FieldContext( category = CATEGORY_WORKER_SECURITY, doc = "This is a regexp, which limits the range of possible ids which can connect to the Broker using SASL." diff --git a/pulsar-functions/src/test/resources/test_worker_config.yml b/pulsar-functions/src/test/resources/test_worker_config.yml index f0ecf2bd71bc6..a297788037035 100644 --- a/pulsar-functions/src/test/resources/test_worker_config.yml +++ b/pulsar-functions/src/test/resources/test_worker_config.yml @@ -23,6 +23,9 @@ pulsarServiceUrl: pulsar://localhost:6650 functionMetadataTopicName: test-function-metadata-topic numFunctionPackageReplicas: 3 maxPendingAsyncRequests: 200 +proxyRoles: + - "proxyA" + - "proxyB" properties: # Fake Bookkeeper Client config to be applied to the DLog Bookkeeper Client bookkeeper_testKey: "fakeValue" diff --git a/pulsar-functions/worker/src/main/java/org/apache/pulsar/functions/worker/rest/FunctionApiResource.java b/pulsar-functions/worker/src/main/java/org/apache/pulsar/functions/worker/rest/FunctionApiResource.java index d37f7f15c606e..cfce3d3e68912 100644 --- a/pulsar-functions/worker/src/main/java/org/apache/pulsar/functions/worker/rest/FunctionApiResource.java +++ b/pulsar-functions/worker/src/main/java/org/apache/pulsar/functions/worker/rest/FunctionApiResource.java @@ -24,12 +24,14 @@ import javax.ws.rs.core.Context; import javax.ws.rs.core.UriInfo; import org.apache.pulsar.broker.authentication.AuthenticationDataSource; +import org.apache.pulsar.broker.authentication.AuthenticationParameters; import org.apache.pulsar.broker.web.AuthenticationFilter; import org.apache.pulsar.functions.worker.WorkerService; public class FunctionApiResource implements Supplier { public static final String ATTRIBUTE_FUNCTION_WORKER = "function-worker"; + public static final String ORIGINAL_PRINCIPAL_HEADER = "X-Original-Principal"; private WorkerService workerService; @Context @@ -47,12 +49,28 @@ public synchronized WorkerService get() { return this.workerService; } + public AuthenticationParameters authParams() { + return AuthenticationParameters.builder() + .originalPrincipal(httpRequest.getHeader(ORIGINAL_PRINCIPAL_HEADER)) + .clientRole(clientAppId()) + .clientAuthenticationDataSource(clientAuthData()) + .build(); + } + + /** + * @deprecated use {@link #authParams()} instead. + */ + @Deprecated public String clientAppId() { return httpRequest != null ? (String) httpRequest.getAttribute(AuthenticationFilter.AuthenticatedRoleAttributeName) : null; } + /** + * @deprecated use {@link #authParams()} instead. + */ + @Deprecated public AuthenticationDataSource clientAuthData() { return (AuthenticationDataSource) httpRequest.getAttribute(AuthenticationFilter.AuthenticatedDataAttributeName); } diff --git a/pulsar-functions/worker/src/main/java/org/apache/pulsar/functions/worker/rest/api/ComponentImpl.java b/pulsar-functions/worker/src/main/java/org/apache/pulsar/functions/worker/rest/api/ComponentImpl.java index 0b6c2bbd61f55..7daa9c9aee8ed 100644 --- a/pulsar-functions/worker/src/main/java/org/apache/pulsar/functions/worker/rest/api/ComponentImpl.java +++ b/pulsar-functions/worker/src/main/java/org/apache/pulsar/functions/worker/rest/api/ComponentImpl.java @@ -67,6 +67,7 @@ import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.StringUtils; import org.apache.pulsar.broker.authentication.AuthenticationDataSource; +import org.apache.pulsar.broker.authentication.AuthenticationParameters; import org.apache.pulsar.client.admin.PulsarAdminException; import org.apache.pulsar.client.admin.internal.FunctionsImpl; import org.apache.pulsar.client.api.Message; @@ -84,7 +85,6 @@ import org.apache.pulsar.common.naming.TopicName; import org.apache.pulsar.common.policies.data.FunctionInstanceStatsDataImpl; import org.apache.pulsar.common.policies.data.FunctionStatsImpl; -import org.apache.pulsar.common.policies.data.TenantInfoImpl; import org.apache.pulsar.common.util.Codec; import org.apache.pulsar.common.util.RestException; import org.apache.pulsar.functions.instance.InstanceUtils; @@ -456,23 +456,14 @@ private void deleteStatestoreTableAsync(String namespace, String table) { public void deregisterFunction(final String tenant, final String namespace, final String componentName, - final String clientRole, - AuthenticationDataSource clientAuthenticationDataHttps) { + final AuthenticationParameters authParams) { if (!isWorkerServiceAvailable()) { throwUnavailableException(); } - try { - if (!isAuthorizedRole(tenant, namespace, clientRole, clientAuthenticationDataHttps)) { - log.warn("{}/{}/{} Client [{}] is not authorized to deregister {}", tenant, namespace, - componentName, clientRole, ComponentTypeUtils.toString(componentType)); - throw new RestException(Status.UNAUTHORIZED, "Client is not authorized to perform operation"); - } - } catch (PulsarAdminException e) { - log.error("{}/{}/{} Failed to authorize [{}]", tenant, namespace, componentName, e); - throw new RestException(Status.INTERNAL_SERVER_ERROR, e.getMessage()); - } + throwRestExceptionIfUnauthorizedForNamespace(tenant, namespace, componentName, "deregister", + authParams); // validate parameters try { @@ -540,23 +531,14 @@ private void deleteComponentFromStorage(String tenant, String namespace, String public FunctionConfig getFunctionInfo(final String tenant, final String namespace, final String componentName, - final String clientRole, - final AuthenticationDataSource clientAuthenticationDataHttps) { + final AuthenticationParameters authParams) { if (!isWorkerServiceAvailable()) { throwUnavailableException(); } - try { - if (!isAuthorizedRole(tenant, namespace, clientRole, clientAuthenticationDataHttps)) { - log.warn("{}/{}/{} Client [{}] is not authorized to get {}", tenant, namespace, - componentName, clientRole, ComponentTypeUtils.toString(componentType)); - throw new RestException(Status.UNAUTHORIZED, "Client is not authorized to perform operation"); - } - } catch (PulsarAdminException e) { - log.error("{}/{}/{} Failed to authorize [{}]", tenant, namespace, componentName, e); - throw new RestException(Status.INTERNAL_SERVER_ERROR, e.getMessage()); - } + throwRestExceptionIfUnauthorizedForNamespace(tenant, namespace, componentName, "get", + authParams); // validate parameters try { @@ -591,10 +573,8 @@ public void stopFunctionInstance(final String tenant, final String componentName, final String instanceId, final URI uri, - final String clientRole, - final AuthenticationDataSource clientAuthenticationDataHttps) { - changeFunctionInstanceStatus(tenant, namespace, componentName, instanceId, false, uri, clientRole, - clientAuthenticationDataHttps); + final AuthenticationParameters authParams) { + changeFunctionInstanceStatus(tenant, namespace, componentName, instanceId, false, uri, authParams); } @Override @@ -603,12 +583,11 @@ public void startFunctionInstance(final String tenant, final String componentName, final String instanceId, final URI uri, - final String clientRole, - final AuthenticationDataSource clientAuthenticationDataHttps) { - changeFunctionInstanceStatus(tenant, namespace, componentName, instanceId, true, uri, clientRole, - clientAuthenticationDataHttps); + final AuthenticationParameters authParams) { + changeFunctionInstanceStatus(tenant, namespace, componentName, instanceId, true, uri, authParams); } + @Deprecated public void changeFunctionInstanceStatus(final String tenant, final String namespace, final String componentName, @@ -617,21 +596,27 @@ public void changeFunctionInstanceStatus(final String tenant, final URI uri, final String clientRole, final AuthenticationDataSource clientAuthenticationDataHttps) { + AuthenticationParameters authParams = AuthenticationParameters.builder() + .clientRole(clientRole) + .clientAuthenticationDataSource(clientAuthenticationDataHttps) + .build(); + changeFunctionInstanceStatus(tenant, namespace, componentName, instanceId, start, uri, authParams); + } + + public void changeFunctionInstanceStatus(final String tenant, + final String namespace, + final String componentName, + final String instanceId, + final boolean start, + final URI uri, + final AuthenticationParameters authParams) { if (!isWorkerServiceAvailable()) { throwUnavailableException(); } - try { - if (!isAuthorizedRole(tenant, namespace, clientRole, clientAuthenticationDataHttps)) { - log.warn("{}/{}/{} Client [{}] is not authorized to start/stop {}", tenant, namespace, - componentName, clientRole, ComponentTypeUtils.toString(componentType)); - throw new RestException(Status.UNAUTHORIZED, "Client is not authorized to perform operation"); - } - } catch (PulsarAdminException e) { - log.error("{}/{}/{} Failed to authorize [{}]", tenant, namespace, componentName, e); - throw new RestException(Status.INTERNAL_SERVER_ERROR, e.getMessage()); - } + throwRestExceptionIfUnauthorizedForNamespace(tenant, namespace, componentName, "start/stop", + authParams); // validate parameters try { @@ -678,22 +663,13 @@ public void restartFunctionInstance(final String tenant, final String componentName, final String instanceId, final URI uri, - final String clientRole, - final AuthenticationDataSource clientAuthenticationDataHttps) { + final AuthenticationParameters authParams) { if (!isWorkerServiceAvailable()) { throwUnavailableException(); } - try { - if (!isAuthorizedRole(tenant, namespace, clientRole, clientAuthenticationDataHttps)) { - log.warn("{}/{}/{} Client [{}] is not authorized to restart {}", tenant, namespace, - componentName, clientRole, ComponentTypeUtils.toString(componentType)); - throw new RestException(Status.UNAUTHORIZED, "Client is not authorized to perform operation"); - } - } catch (PulsarAdminException e) { - log.error("{}/{}/{} Failed to authorize [{}]", tenant, namespace, componentName, e); - throw new RestException(Status.INTERNAL_SERVER_ERROR, e.getMessage()); - } + throwRestExceptionIfUnauthorizedForNamespace(tenant, namespace, componentName, "restart", + authParams); // validate parameters try { @@ -738,43 +714,41 @@ public void restartFunctionInstance(final String tenant, public void stopFunctionInstances(final String tenant, final String namespace, final String componentName, - final String clientRole, - final AuthenticationDataSource clientAuthenticationDataHttps) { - changeFunctionStatusAllInstances(tenant, namespace, componentName, false, clientRole, - clientAuthenticationDataHttps); + final AuthenticationParameters authParams) { + changeFunctionStatusAllInstances(tenant, namespace, componentName, false, authParams); } @Override public void startFunctionInstances(final String tenant, final String namespace, final String componentName, - final String clientRole, - final AuthenticationDataSource clientAuthenticationDataHttps) { - changeFunctionStatusAllInstances(tenant, namespace, componentName, true, clientRole, - clientAuthenticationDataHttps); + final AuthenticationParameters authParams) { + changeFunctionStatusAllInstances(tenant, namespace, componentName, true, authParams); } + @Deprecated public void changeFunctionStatusAllInstances(final String tenant, final String namespace, final String componentName, final boolean start, final String clientRole, final AuthenticationDataSource clientAuthenticationDataHttps) { + AuthenticationParameters authParams = AuthenticationParameters.builder().clientRole(clientRole) + .clientAuthenticationDataSource(clientAuthenticationDataHttps).build(); + changeFunctionStatusAllInstances(tenant, namespace, componentName, start, authParams); + } + public void changeFunctionStatusAllInstances(final String tenant, + final String namespace, + final String componentName, + final boolean start, + final AuthenticationParameters authParams) { if (!isWorkerServiceAvailable()) { throwUnavailableException(); } - try { - if (!isAuthorizedRole(tenant, namespace, clientRole, clientAuthenticationDataHttps)) { - log.warn("{}/{}/{} Client [{}] is not authorized to start/stop {}", tenant, namespace, - componentName, clientRole, ComponentTypeUtils.toString(componentType)); - throw new RestException(Status.UNAUTHORIZED, "Client is not authorized to perform operation"); - } - } catch (PulsarAdminException e) { - log.error("{}/{}/{} Failed to authorize [{}]", tenant, namespace, componentName, e); - throw new RestException(Status.INTERNAL_SERVER_ERROR, e.getMessage()); - } + throwRestExceptionIfUnauthorizedForNamespace(tenant, namespace, componentName, "start/stop", + authParams); // validate parameters try { @@ -815,25 +789,17 @@ public void changeFunctionStatusAllInstances(final String tenant, namespace, componentName)); } + @Override public void restartFunctionInstances(final String tenant, final String namespace, final String componentName, - final String clientRole, - final AuthenticationDataSource clientAuthenticationDataHttps) { + final AuthenticationParameters authParams) { if (!isWorkerServiceAvailable()) { throwUnavailableException(); } - try { - if (!isAuthorizedRole(tenant, namespace, clientRole, clientAuthenticationDataHttps)) { - log.warn("{}/{}/{} Client [{}] is not authorized to restart {}", tenant, namespace, - componentName, clientRole, ComponentTypeUtils.toString(componentType)); - throw new RestException(Status.UNAUTHORIZED, "Client is not authorized to perform operation"); - } - } catch (PulsarAdminException e) { - log.error("{}/{}/{} Failed to authorize [{}]", tenant, namespace, componentName, e); - throw new RestException(Status.INTERNAL_SERVER_ERROR, e.getMessage()); - } + throwRestExceptionIfUnauthorizedForNamespace(tenant, namespace, componentName, "restart", + authParams); // validate parameters try { @@ -873,26 +839,18 @@ public void restartFunctionInstances(final String tenant, } } + @Override public FunctionStatsImpl getFunctionStats(final String tenant, final String namespace, final String componentName, final URI uri, - final String clientRole, - final AuthenticationDataSource clientAuthenticationDataHttps) { + final AuthenticationParameters authParams) { if (!isWorkerServiceAvailable()) { throwUnavailableException(); } - try { - if (!isAuthorizedRole(tenant, namespace, clientRole, clientAuthenticationDataHttps)) { - log.warn("{}/{}/{} Client [{}] is not authorized to get stats for {}", tenant, namespace, - componentName, clientRole, ComponentTypeUtils.toString(componentType)); - throw new RestException(Status.UNAUTHORIZED, "Client is not authorized to perform operation"); - } - } catch (PulsarAdminException e) { - log.error("{}/{}/{} Failed to authorize [{}]", tenant, namespace, componentName, e); - throw new RestException(Status.INTERNAL_SERVER_ERROR, e.getMessage()); - } + throwRestExceptionIfUnauthorizedForNamespace(tenant, namespace, componentName, "get stats for", + authParams); // validate parameters try { @@ -941,22 +899,13 @@ public FunctionStatsImpl getFunctionStats(final String tenant, final String componentName, final String instanceId, final URI uri, - final String clientRole, - final AuthenticationDataSource clientAuthenticationDataHttps) { + final AuthenticationParameters authParams) { if (!isWorkerServiceAvailable()) { throwUnavailableException(); } - try { - if (!isAuthorizedRole(tenant, namespace, clientRole, clientAuthenticationDataHttps)) { - log.warn("{}/{}/{} Client [{}] is not authorized to get stats for {}", tenant, namespace, - componentName, clientRole, ComponentTypeUtils.toString(componentType)); - throw new RestException(Status.UNAUTHORIZED, "Client is not authorized to perform operation"); - } - } catch (PulsarAdminException e) { - log.error("{}/{}/{} Failed to authorize [{}]", tenant, namespace, componentName, e); - throw new RestException(Status.INTERNAL_SERVER_ERROR, e.getMessage()); - } + throwRestExceptionIfUnauthorizedForNamespace(tenant, namespace, componentName, "get stats for", + authParams); // validate parameters try { @@ -1012,23 +961,13 @@ public FunctionStatsImpl getFunctionStats(final String tenant, @Override public List listFunctions(final String tenant, final String namespace, - final String clientRole, - final AuthenticationDataSource clientAuthenticationDataHttps) { + final AuthenticationParameters authParams) { if (!isWorkerServiceAvailable()) { throwUnavailableException(); } - try { - if (!isAuthorizedRole(tenant, namespace, clientRole, clientAuthenticationDataHttps)) { - log.warn("{}/{} Client [{}] is not authorized to list {}", tenant, namespace, clientRole, - ComponentTypeUtils.toString(componentType)); - throw new RestException(Status.UNAUTHORIZED, "Client is not authorized to perform operation"); - } - } catch (PulsarAdminException e) { - log.error("{}/{} Failed to authorize [{}]", tenant, namespace, e); - throw new RestException(Status.INTERNAL_SERVER_ERROR, e.getMessage()); - } + throwRestExceptionIfUnauthorizedForNamespace(tenant, namespace, null, "list", authParams); // validate parameters try { @@ -1070,13 +1009,13 @@ public List getListOfConnectors() { } @Override - public void reloadConnectors(String clientRole, AuthenticationDataSource authenticationData) { + public void reloadConnectors(AuthenticationParameters authParams) { if (!isWorkerServiceAvailable()) { throwUnavailableException(); } if (worker().getWorkerConfig().isAuthorizationEnabled()) { // Only superuser has permission to do this operation. - if (!isSuperUser(clientRole, authenticationData)) { + if (!isSuperUser(authParams)) { throw new RestException(Status.UNAUTHORIZED, "This operation requires super-user access"); } } @@ -1094,23 +1033,13 @@ public String triggerFunction(final String tenant, final String input, final InputStream uploadedInputStream, final String topic, - final String clientRole, - final AuthenticationDataSource clientAuthenticationDataHttps) { + final AuthenticationParameters authParams) { if (!isWorkerServiceAvailable()) { throwUnavailableException(); } - try { - if (!isAuthorizedRole(tenant, namespace, clientRole, clientAuthenticationDataHttps)) { - log.warn("{}/{}/{} Client [{}] is not authorized to trigger {}", tenant, namespace, - functionName, clientRole, ComponentTypeUtils.toString(componentType)); - throw new RestException(Status.UNAUTHORIZED, "Client is not authorized to perform operation"); - } - } catch (PulsarAdminException e) { - log.error("{}/{}/{} Failed to authorize [{}]", tenant, namespace, functionName, e); - throw new RestException(Status.INTERNAL_SERVER_ERROR, e.getMessage()); - } + throwRestExceptionIfUnauthorizedForNamespace(tenant, namespace, functionName, "trigger", authParams); // validate parameters try { @@ -1224,23 +1153,14 @@ public FunctionState getFunctionState(final String tenant, final String namespace, final String functionName, final String key, - final String clientRole, - final AuthenticationDataSource clientAuthenticationDataHttps) { + final AuthenticationParameters authParams) { if (!isWorkerServiceAvailable()) { throwUnavailableException(); } - try { - if (!isAuthorizedRole(tenant, namespace, clientRole, clientAuthenticationDataHttps)) { - log.warn("{}/{}/{} Client [{}] is not authorized to get state for {}", tenant, namespace, - functionName, clientRole, ComponentTypeUtils.toString(componentType)); - throw new RestException(Status.UNAUTHORIZED, "Client is not authorized to perform operation"); - } - } catch (PulsarAdminException e) { - log.error("{}/{}/{} Failed to authorize [{}]", tenant, namespace, functionName, e); - throw new RestException(Status.INTERNAL_SERVER_ERROR, e.getMessage()); - } + throwRestExceptionIfUnauthorizedForNamespace(tenant, namespace, functionName, "get state for", + authParams); if (null == worker().getStateStoreAdminClient()) { throwStateStoreUnvailableResponse(); @@ -1310,8 +1230,7 @@ public void putFunctionState(final String tenant, final String functionName, final String key, final FunctionState state, - final String clientRole, - final AuthenticationDataSource clientAuthenticationDataHttps) { + final AuthenticationParameters authParams) { if (!isWorkerServiceAvailable()) { throwUnavailableException(); @@ -1321,16 +1240,8 @@ public void putFunctionState(final String tenant, throwStateStoreUnvailableResponse(); } - try { - if (!isAuthorizedRole(tenant, namespace, clientRole, clientAuthenticationDataHttps)) { - log.warn("{}/{}/{} Client [{}] is not authorized to put state for {}", tenant, namespace, - functionName, clientRole, ComponentTypeUtils.toString(componentType)); - throw new RestException(Status.UNAUTHORIZED, "Client is not authorized to perform operation"); - } - } catch (PulsarAdminException e) { - log.error("{}/{}/{} Failed to authorize [{}]", tenant, namespace, functionName, e); - throw new RestException(Status.INTERNAL_SERVER_ERROR, e.getMessage()); - } + throwRestExceptionIfUnauthorizedForNamespace(tenant, namespace, functionName, "put state for", + authParams); if (!key.equals(state.getKey())) { log.error("{}/{}/{} Bad putFunction Request, path key doesn't match key in json", tenant, namespace, @@ -1386,14 +1297,14 @@ public void putFunctionState(final String tenant, } @Override - public void uploadFunction(final InputStream uploadedInputStream, final String path, String clientRole, - AuthenticationDataSource authenticationData) { + public void uploadFunction(final InputStream uploadedInputStream, final String path, + AuthenticationParameters authParams) { if (!isWorkerServiceAvailable()) { throwUnavailableException(); } - if (worker().getWorkerConfig().isAuthorizationEnabled() && !isSuperUser(clientRole, authenticationData)) { + if (worker().getWorkerConfig().isAuthorizationEnabled() && !isSuperUser(authParams)) { throw new RestException(Status.UNAUTHORIZED, "Client is not authorized to perform operation"); } @@ -1428,23 +1339,13 @@ public void uploadFunction(final InputStream uploadedInputStream, final String p @Override public StreamingOutput downloadFunction(String tenant, String namespace, String componentName, - String clientRole, AuthenticationDataSource clientAuthenticationDataHttps, - boolean transformFunction) { + AuthenticationParameters authParams, boolean transformFunction) { if (!isWorkerServiceAvailable()) { throwUnavailableException(); } - try { - if (!isAuthorizedRole(tenant, namespace, clientRole, clientAuthenticationDataHttps)) { - log.warn("{}/{}/{} Client [{}] is not admin and authorized to download package for {} ", tenant, - namespace, - componentName, clientRole, ComponentTypeUtils.toString(componentType)); - throw new RestException(Status.UNAUTHORIZED, "Client is not authorized to perform operation"); - } - } catch (PulsarAdminException e) { - log.error("{}/{}/{} Failed to authorize [{}]", tenant, namespace, componentName, e); - throw new RestException(Status.INTERNAL_SERVER_ERROR, e.getMessage()); - } + throwRestExceptionIfUnauthorizedForNamespace(tenant, namespace, componentName, "download package for", + authParams); FunctionMetaDataManager functionMetaDataManager = worker().getFunctionMetaDataManager(); if (!functionMetaDataManager.containsFunction(tenant, namespace, componentName)) { @@ -1529,8 +1430,7 @@ private Path getBuiltinArchivePath(String pkgPath, FunctionDetails.ComponentType } @Override - public StreamingOutput downloadFunction( - final String path, String clientRole, AuthenticationDataSource clientAuthenticationDataHttps) { + public StreamingOutput downloadFunction(final String path, final AuthenticationParameters authParams) { if (!isWorkerServiceAvailable()) { throwUnavailableException(); @@ -1544,19 +1444,10 @@ public StreamingOutput downloadFunction( String namespace = tokens[1]; String componentName = tokens[2]; - try { - if (!isAuthorizedRole(tenant, namespace, clientRole, clientAuthenticationDataHttps)) { - log.warn("{}/{}/{} Client [{}] is not admin and authorized to download package for {} ", tenant, - namespace, - componentName, clientRole, ComponentTypeUtils.toString(componentType)); - throw new RestException(Status.UNAUTHORIZED, "Client is not authorized to perform operation"); - } - } catch (PulsarAdminException e) { - log.error("{}/{}/{} Failed to authorize [{}]", tenant, namespace, componentName, e); - throw new RestException(Status.INTERNAL_SERVER_ERROR, e.getMessage()); - } + throwRestExceptionIfUnauthorizedForNamespace(tenant, namespace, componentName, "download package for", + authParams); } else { - if (!isSuperUser(clientRole, clientAuthenticationDataHttps)) { + if (!isSuperUser(authParams)) { throw new RestException(Status.UNAUTHORIZED, "Client is not authorized to perform operation"); } } @@ -1694,57 +1585,61 @@ public static String createPackagePath(String tenant, String namespace, String f getUniquePackageName(Codec.encode(fileName))); } + /** + * @deprecated use {@link #isAuthorizedRole(String, String, AuthenticationParameters)} instead. + */ + @Deprecated public boolean isAuthorizedRole(String tenant, String namespace, String clientRole, AuthenticationDataSource authenticationData) throws PulsarAdminException { - if (worker().getWorkerConfig().isAuthorizationEnabled()) { - // skip authorization if client role is super-user - if (isSuperUser(clientRole, authenticationData)) { - return true; - } - - if (clientRole != null) { - try { - TenantInfoImpl tenantInfo = - (TenantInfoImpl) worker().getBrokerAdmin().tenants().getTenantInfo(tenant); - if (tenantInfo != null && worker().getAuthorizationService() - .isTenantAdmin(tenant, clientRole, tenantInfo, authenticationData).get()) { - return true; - } - } catch (PulsarAdminException.NotFoundException | InterruptedException | ExecutionException e) { + AuthenticationParameters authParams = AuthenticationParameters.builder().clientRole(clientRole) + .clientAuthenticationDataSource(authenticationData).build(); + return isAuthorizedRole(tenant, namespace, authParams); + } - } - } + public boolean isAuthorizedRole(String tenant, String namespace, + AuthenticationParameters authParams) throws PulsarAdminException { + if (worker().getWorkerConfig().isAuthorizationEnabled()) { + return allowFunctionOps(NamespaceName.get(tenant, namespace), authParams); + } else { + return true; + } + } - // check if role has permissions granted - if (clientRole != null && authenticationData != null) { - return allowFunctionOps(NamespaceName.get(tenant, namespace), clientRole, authenticationData); - } else { - return false; + public void throwRestExceptionIfUnauthorizedForNamespace(String tenant, String namespace, String componentName, + String action, AuthenticationParameters authParams) { + try { + if (!isAuthorizedRole(tenant, namespace, authParams)) { + log.warn("{}/{}/{} Client with role [{}] and originalPrincipal [{}] is not authorized to {} {}", + tenant, namespace, componentName, authParams.getClientRole(), + authParams.getOriginalPrincipal(), action, ComponentTypeUtils.toString(componentType)); + throw new RestException(Status.UNAUTHORIZED, "Client is not authorized to perform operation"); } + } catch (PulsarAdminException e) { + log.error("{}/{}/{} Failed to authorize [{}]", tenant, namespace, componentName, e); + throw new RestException(Status.INTERNAL_SERVER_ERROR, e.getMessage()); } - return true; } - + @Deprecated protected void componentStatusRequestValidate(final String tenant, final String namespace, final String componentName, final String clientRole, final AuthenticationDataSource clientAuthenticationDataHttps) { + AuthenticationParameters authParams = AuthenticationParameters.builder().clientRole(clientRole) + .clientAuthenticationDataSource(clientAuthenticationDataHttps).build(); + componentStatusRequestValidate(tenant, namespace, componentName, authParams); + } + + protected void componentStatusRequestValidate(final String tenant, final String namespace, + final String componentName, + final AuthenticationParameters authParams) { if (!isWorkerServiceAvailable()) { throw new RestException(Status.SERVICE_UNAVAILABLE, "Function worker service is not done initializing. Please try again in a little while."); } - try { - if (!isAuthorizedRole(tenant, namespace, clientRole, clientAuthenticationDataHttps)) { - log.warn("{}/{}/{} Client [{}] is not authorized get status for {}", tenant, namespace, - componentName, clientRole, ComponentTypeUtils.toString(componentType)); - throw new RestException(Status.UNAUTHORIZED, "Client is not authorized to perform operation"); - } - } catch (PulsarAdminException e) { - log.error("{}/{}/{} Failed to authorize [{}]", tenant, namespace, componentName, e); - throw new RestException(Status.INTERNAL_SERVER_ERROR, e.getMessage()); - } + throwRestExceptionIfUnauthorizedForNamespace(tenant, namespace, componentName, "get status for", + authParams); // validate parameters try { @@ -1773,13 +1668,25 @@ protected void componentStatusRequestValidate(final String tenant, final String } } + @Deprecated protected void componentInstanceStatusRequestValidate(final String tenant, final String namespace, final String componentName, final int instanceId, final String clientRole, final AuthenticationDataSource clientAuthenticationDataHttps) { - componentStatusRequestValidate(tenant, namespace, componentName, clientRole, clientAuthenticationDataHttps); + AuthenticationParameters authParams = AuthenticationParameters.builder().clientRole(clientRole) + .clientAuthenticationDataSource(clientAuthenticationDataHttps).build(); + componentInstanceStatusRequestValidate(tenant, namespace, componentName, instanceId, authParams); + } + + protected void componentInstanceStatusRequestValidate(final String tenant, + final String namespace, + final String componentName, + final int instanceId, + final AuthenticationParameters authParams) { + + componentStatusRequestValidate(tenant, namespace, componentName, authParams); FunctionMetaDataManager functionMetaDataManager = worker().getFunctionMetaDataManager(); FunctionMetaData functionMetaData = @@ -1794,44 +1701,58 @@ protected void componentInstanceStatusRequestValidate(final String tenant, } } - public boolean isSuperUser(String clientRole, AuthenticationDataSource authenticationData) { - if (clientRole != null) { + public boolean isSuperUser(AuthenticationParameters authParams) { + if (authParams.getClientRole() != null) { try { - if ((worker().getWorkerConfig().getSuperUserRoles() != null - && worker().getWorkerConfig().getSuperUserRoles().contains(clientRole))) { - return true; - } - return worker().getAuthorizationService().isSuperUser(clientRole, authenticationData) - .get(worker().getWorkerConfig().getMetadataStoreOperationTimeoutSeconds(), SECONDS); + return worker().getAuthorizationService().isSuperUser(authParams) + .get(worker().getWorkerConfig().getMetadataStoreOperationTimeoutSeconds(), SECONDS); } catch (InterruptedException e) { - log.warn("Time-out {} sec while checking the role {} is a super user role ", - worker().getWorkerConfig().getMetadataStoreOperationTimeoutSeconds(), clientRole); + log.warn("Time-out {} sec while checking the role {} originalPrincipal {} is a super user role ", + worker().getWorkerConfig().getMetadataStoreOperationTimeoutSeconds(), + authParams.getClientRole(), authParams.getOriginalPrincipal()); throw new RestException(Status.INTERNAL_SERVER_ERROR, e.getMessage()); } catch (Exception e) { - log.warn("Admin-client with Role - failed to check the role {} is a super user role {} ", clientRole, - e.getMessage(), e); + log.warn("Failed verifying role {} originalPrincipal {} is a super user role", + authParams.getClientRole(), authParams.getOriginalPrincipal(), e); throw new RestException(Status.INTERNAL_SERVER_ERROR, e.getMessage()); } } return false; } + /** + * @deprecated use {@link #isSuperUser(AuthenticationParameters)} + */ + @Deprecated + public boolean isSuperUser(String clientRole, AuthenticationDataSource authenticationData) { + AuthenticationParameters authParams = AuthenticationParameters.builder().clientRole(clientRole) + .clientAuthenticationDataSource(authenticationData).build(); + return isSuperUser(authParams); + } + + /** + * @deprecated use {@link #isSuperUser(AuthenticationParameters)} + */ + @Deprecated public boolean allowFunctionOps(NamespaceName namespaceName, String role, AuthenticationDataSource authenticationData) { + AuthenticationParameters authParams = AuthenticationParameters.builder().clientRole(role) + .clientAuthenticationDataSource(authenticationData).build(); + return allowFunctionOps(namespaceName, authParams); + } + + public boolean allowFunctionOps(NamespaceName namespaceName, AuthenticationParameters authParams) { try { switch (componentType) { case SINK: - return worker().getAuthorizationService().allowSinkOpsAsync( - namespaceName, role, authenticationData) + return worker().getAuthorizationService().allowSinkOpsAsync(namespaceName, authParams) .get(worker().getWorkerConfig().getMetadataStoreOperationTimeoutSeconds(), SECONDS); case SOURCE: - return worker().getAuthorizationService().allowSourceOpsAsync( - namespaceName, role, authenticationData) + return worker().getAuthorizationService().allowSourceOpsAsync(namespaceName, authParams) .get(worker().getWorkerConfig().getMetadataStoreOperationTimeoutSeconds(), SECONDS); case FUNCTION: default: - return worker().getAuthorizationService().allowFunctionOpsAsync( - namespaceName, role, authenticationData) + return worker().getAuthorizationService().allowFunctionOpsAsync(namespaceName, authParams) .get(worker().getWorkerConfig().getMetadataStoreOperationTimeoutSeconds(), SECONDS); } } catch (InterruptedException e) { @@ -1839,9 +1760,9 @@ public boolean allowFunctionOps(NamespaceName namespaceName, String role, worker().getWorkerConfig().getMetadataStoreOperationTimeoutSeconds(), namespaceName); throw new RestException(Status.INTERNAL_SERVER_ERROR, e.getMessage()); } catch (Exception e) { - log.warn("Admin-client with Role - {} failed to get function permissions for namespace - {}. {}", role, - namespaceName, - e.getMessage(), e); + log.warn("Admin-client with Role [{}] originalPrincipal [{}] failed to get function permissions for " + + "namespace - {}. {}", authParams.getClientRole(), + authParams.getOriginalPrincipal(), namespaceName, e.getMessage(), e); throw new RestException(Status.INTERNAL_SERVER_ERROR, e.getMessage()); } } diff --git a/pulsar-functions/worker/src/main/java/org/apache/pulsar/functions/worker/rest/api/FunctionsImpl.java b/pulsar-functions/worker/src/main/java/org/apache/pulsar/functions/worker/rest/api/FunctionsImpl.java index b7883e14c91e8..c7967600da5f6 100644 --- a/pulsar-functions/worker/src/main/java/org/apache/pulsar/functions/worker/rest/api/FunctionsImpl.java +++ b/pulsar-functions/worker/src/main/java/org/apache/pulsar/functions/worker/rest/api/FunctionsImpl.java @@ -38,7 +38,7 @@ import javax.ws.rs.core.UriBuilder; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; -import org.apache.pulsar.broker.authentication.AuthenticationDataSource; +import org.apache.pulsar.broker.authentication.AuthenticationParameters; import org.apache.pulsar.client.admin.PulsarAdminException; import org.apache.pulsar.common.functions.FunctionConfig; import org.apache.pulsar.common.functions.FunctionDefinition; @@ -78,8 +78,7 @@ public void registerFunction(final String tenant, final FormDataContentDisposition fileDetail, final String functionPkgUrl, final FunctionConfig functionConfig, - final String clientRole, - AuthenticationDataSource clientAuthenticationDataHttps) { + final AuthenticationParameters authParams) { if (!isWorkerServiceAvailable()) { throwUnavailableException(); @@ -98,16 +97,7 @@ public void registerFunction(final String tenant, throw new RestException(Response.Status.BAD_REQUEST, "Function config is not provided"); } - try { - if (!isAuthorizedRole(tenant, namespace, clientRole, clientAuthenticationDataHttps)) { - log.error("{}/{}/{} Client [{}] is not authorized to register {}", tenant, namespace, - functionName, clientRole, ComponentTypeUtils.toString(componentType)); - throw new RestException(Response.Status.UNAUTHORIZED, "Client is not authorized to perform operation"); - } - } catch (PulsarAdminException e) { - log.error("{}/{}/{} Failed to authorize [{}]", tenant, namespace, functionName, e); - throw new RestException(Response.Status.INTERNAL_SERVER_ERROR, e.getMessage()); - } + throwRestExceptionIfUnauthorizedForNamespace(tenant, namespace, functionName, "register", authParams); try { // Check tenant exists @@ -124,8 +114,8 @@ public void registerFunction(final String tenant, } } } catch (PulsarAdminException.NotAuthorizedException e) { - log.error("{}/{}/{} Client [{}] is not authorized to operate {} on tenant", tenant, namespace, - functionName, clientRole, ComponentTypeUtils.toString(componentType)); + log.error("{}/{}/{} Client is not authorized to operate {} on tenant", tenant, namespace, + functionName, ComponentTypeUtils.toString(componentType)); throw new RestException(Response.Status.UNAUTHORIZED, "Client is not authorized to perform operation"); } catch (PulsarAdminException.NotFoundException e) { log.error("{}/{}/{} Tenant {} does not exist", tenant, namespace, functionName, tenant); @@ -193,11 +183,12 @@ public void registerFunction(final String tenant, worker().getFunctionRuntimeManager() .getRuntimeFactory() .getAuthProvider().ifPresent(functionAuthProvider -> { - if (clientAuthenticationDataHttps != null) { + if (authParams.getClientAuthenticationDataSource() != null) { try { Optional functionAuthData = functionAuthProvider - .cacheAuthData(finalFunctionDetails, clientAuthenticationDataHttps); + .cacheAuthData(finalFunctionDetails, + authParams.getClientAuthenticationDataSource()); functionAuthData.ifPresent(authData -> functionMetaDataBuilder.setFunctionAuthSpec( Function.FunctionAuthenticationSpec.newBuilder() @@ -245,8 +236,7 @@ public void updateFunction(final String tenant, final FormDataContentDisposition fileDetail, final String functionPkgUrl, final FunctionConfig functionConfig, - final String clientRole, - AuthenticationDataSource clientAuthenticationDataHttps, + final AuthenticationParameters authParams, UpdateOptionsImpl updateOptions) { if (!isWorkerServiceAvailable()) { @@ -266,17 +256,8 @@ public void updateFunction(final String tenant, throw new RestException(Response.Status.BAD_REQUEST, "Function config is not provided"); } - try { - if (!isAuthorizedRole(tenant, namespace, clientRole, clientAuthenticationDataHttps)) { - log.error("{}/{}/{} Client [{}] is not authorized to update {}", tenant, namespace, - functionName, clientRole, ComponentTypeUtils.toString(componentType)); - throw new RestException(Response.Status.UNAUTHORIZED, "Client is not authorized to perform operation"); - - } - } catch (PulsarAdminException e) { - log.error("{}/{}/{} Failed to authorize [{}]", tenant, namespace, functionName, e); - throw new RestException(Response.Status.INTERNAL_SERVER_ERROR, e.getMessage()); - } + throwRestExceptionIfUnauthorizedForNamespace(tenant, namespace, functionName, "update", + authParams); FunctionMetaDataManager functionMetaDataManager = worker().getFunctionMetaDataManager(); @@ -357,7 +338,7 @@ public void updateFunction(final String tenant, worker().getFunctionRuntimeManager() .getRuntimeFactory() .getAuthProvider().ifPresent(functionAuthProvider -> { - if (clientAuthenticationDataHttps != null && updateOptions + if (authParams.getClientAuthenticationDataSource() != null && updateOptions != null && updateOptions.isUpdateAuthData()) { // get existing auth data if it exists Optional existingFunctionAuthData = Optional.empty(); @@ -369,7 +350,7 @@ public void updateFunction(final String tenant, try { Optional newFunctionAuthData = functionAuthProvider .updateAuthData(finalFunctionDetails, existingFunctionAuthData, - clientAuthenticationDataHttps); + authParams.getClientAuthenticationDataSource()); if (newFunctionAuthData.isPresent()) { functionMetaDataBuilder.setFunctionAuthSpec( @@ -597,12 +578,11 @@ public FunctionStatus.FunctionInstanceStatus.FunctionInstanceStatusData getFunct final String componentName, final String instanceId, final URI uri, - final String clientRole, - final AuthenticationDataSource clientAuthenticationDataHttps) { + final AuthenticationParameters authParams) { // validate parameters componentInstanceStatusRequestValidate(tenant, namespace, componentName, Integer.parseInt(instanceId), - clientRole, clientAuthenticationDataHttps); + authParams); FunctionStatus.FunctionInstanceStatus.FunctionInstanceStatusData functionInstanceStatusData; try { @@ -632,11 +612,10 @@ public FunctionStatus getFunctionStatus(final String tenant, final String namespace, final String componentName, final URI uri, - final String clientRole, - final AuthenticationDataSource clientAuthenticationDataHttps) { + final AuthenticationParameters authParams) { // validate parameters - componentStatusRequestValidate(tenant, namespace, componentName, clientRole, clientAuthenticationDataHttps); + componentStatusRequestValidate(tenant, namespace, componentName, authParams); FunctionStatus functionStatus; try { @@ -658,17 +637,18 @@ public void updateFunctionOnWorkerLeader(final String tenant, final InputStream uploadedInputStream, final boolean delete, URI uri, - final String clientRole, - AuthenticationDataSource authenticationData) { + final AuthenticationParameters authParams) { if (!isWorkerServiceAvailable()) { throwUnavailableException(); } if (worker().getWorkerConfig().isAuthorizationEnabled()) { - if (!isSuperUser(clientRole, authenticationData)) { - log.error("{}/{}/{} Client [{}] is not superuser to update on worker leader {}", tenant, namespace, - functionName, clientRole, ComponentTypeUtils.toString(componentType)); + if (!isSuperUser(authParams)) { + log.error("{}/{}/{} Client with role [{}] and originalPrincipal [{}] is not superuser to update on" + + " worker leader {}", tenant, namespace, functionName, authParams.getClientRole(), + authParams.getClientAuthenticationDataSource(), + ComponentTypeUtils.toString(componentType)); throw new RestException(Response.Status.UNAUTHORIZED, "Client is not authorized to perform operation"); } } @@ -713,26 +693,26 @@ public void updateFunctionOnWorkerLeader(final String tenant, } @Override - public void reloadBuiltinFunctions(String clientRole, AuthenticationDataSource authenticationData) + public void reloadBuiltinFunctions(AuthenticationParameters authParams) throws IOException { if (!isWorkerServiceAvailable()) { throwUnavailableException(); } - if (worker().getWorkerConfig().isAuthorizationEnabled() && !isSuperUser(clientRole, authenticationData)) { + if (worker().getWorkerConfig().isAuthorizationEnabled() + && !isSuperUser(authParams)) { throw new RestException(Response.Status.UNAUTHORIZED, "Client is not authorized to perform operation"); } worker().getFunctionsManager().reloadFunctions(worker().getWorkerConfig()); } @Override - public List getBuiltinFunctions(String clientRole, - AuthenticationDataSource authenticationData) { + public List getBuiltinFunctions(AuthenticationParameters authParams) { if (!isWorkerServiceAvailable()) { throwUnavailableException(); } - if (worker().getWorkerConfig().isAuthorizationEnabled() && !isSuperUser(clientRole, authenticationData)) { + if (worker().getWorkerConfig().isAuthorizationEnabled() && !isSuperUser(authParams)) { throw new RestException(Response.Status.UNAUTHORIZED, "Client is not authorized to perform operation"); } return this.worker().getFunctionsManager().getFunctionDefinitions(); diff --git a/pulsar-functions/worker/src/main/java/org/apache/pulsar/functions/worker/rest/api/FunctionsImplV2.java b/pulsar-functions/worker/src/main/java/org/apache/pulsar/functions/worker/rest/api/FunctionsImplV2.java index 7b7f84aa82823..059db75542d59 100644 --- a/pulsar-functions/worker/src/main/java/org/apache/pulsar/functions/worker/rest/api/FunctionsImplV2.java +++ b/pulsar-functions/worker/src/main/java/org/apache/pulsar/functions/worker/rest/api/FunctionsImplV2.java @@ -28,6 +28,7 @@ import java.util.stream.Collectors; import javax.ws.rs.core.Response; import lombok.extern.slf4j.Slf4j; +import org.apache.pulsar.broker.authentication.AuthenticationParameters; import org.apache.pulsar.common.functions.FunctionConfig; import org.apache.pulsar.common.functions.FunctionState; import org.apache.pulsar.common.io.ConnectorDefinition; @@ -59,11 +60,11 @@ public FunctionsImplV2(FunctionsImpl delegate) { @Override public Response getFunctionInfo(final String tenant, final String namespace, - final String functionName, String clientRole) + final String functionName, AuthenticationParameters authParams) throws IOException { // run just for parameter checks - delegate.getFunctionInfo(tenant, namespace, functionName, clientRole, null); + delegate.getFunctionInfo(tenant, namespace, functionName, authParams); FunctionMetaDataManager functionMetaDataManager = delegate.worker().getFunctionMetaDataManager(); @@ -75,11 +76,12 @@ public Response getFunctionInfo(final String tenant, final String namespace, @Override public Response getFunctionInstanceStatus(final String tenant, final String namespace, final String functionName, - final String instanceId, URI uri, String clientRole) throws IOException { + final String instanceId, URI uri, + AuthenticationParameters authParams) throws IOException { org.apache.pulsar.common.policies.data.FunctionStatus.FunctionInstanceStatus.FunctionInstanceStatusData functionInstanceStatus = delegate.getFunctionInstanceStatus(tenant, namespace, - functionName, instanceId, uri, clientRole, null); + functionName, instanceId, uri, authParams); String jsonResponse = FunctionCommon.printJson(toProto(functionInstanceStatus, instanceId)); return Response.status(Response.Status.OK).entity(jsonResponse).build(); @@ -87,10 +89,10 @@ public Response getFunctionInstanceStatus(final String tenant, final String name @Override public Response getFunctionStatusV2(String tenant, String namespace, String functionName, - URI requestUri, String clientRole) throws + URI requestUri, AuthenticationParameters authParams) throws IOException { FunctionStatus functionStatus = delegate.getFunctionStatus(tenant, namespace, - functionName, requestUri, clientRole, null); + functionName, requestUri, authParams); InstanceCommunication.FunctionStatusList.Builder functionStatusList = InstanceCommunication.FunctionStatusList.newBuilder(); functionStatus.instances.forEach(functionInstanceStatus -> functionStatusList.addFunctionStatusList( @@ -103,7 +105,7 @@ public Response getFunctionStatusV2(String tenant, String namespace, String func @Override public Response registerFunction(String tenant, String namespace, String functionName, InputStream uploadedInputStream, FormDataContentDisposition fileDetail, String functionPkgUrl, String - functionDetailsJson, String clientRole) { + functionDetailsJson, AuthenticationParameters authParams) { Function.FunctionDetails.Builder functionDetailsBuilder = Function.FunctionDetails.newBuilder(); try { @@ -114,14 +116,15 @@ public Response registerFunction(String tenant, String namespace, String functio FunctionConfig functionConfig = FunctionConfigUtils.convertFromDetails(functionDetailsBuilder.build()); delegate.registerFunction(tenant, namespace, functionName, uploadedInputStream, fileDetail, - functionPkgUrl, functionConfig, clientRole, null); + functionPkgUrl, functionConfig, authParams); return Response.ok().build(); } @Override public Response updateFunction(String tenant, String namespace, String functionName, InputStream uploadedInputStream, FormDataContentDisposition fileDetail, - String functionPkgUrl, String functionDetailsJson, String clientRole) { + String functionPkgUrl, String functionDetailsJson, + AuthenticationParameters authParams) { Function.FunctionDetails.Builder functionDetailsBuilder = Function.FunctionDetails.newBuilder(); try { @@ -132,35 +135,36 @@ public Response updateFunction(String tenant, String namespace, String functionN FunctionConfig functionConfig = FunctionConfigUtils.convertFromDetails(functionDetailsBuilder.build()); delegate.updateFunction(tenant, namespace, functionName, uploadedInputStream, fileDetail, - functionPkgUrl, functionConfig, clientRole, null, null); + functionPkgUrl, functionConfig, authParams, null); return Response.ok().build(); } @Override - public Response deregisterFunction(String tenant, String namespace, String functionName, String clientAppId) { - delegate.deregisterFunction(tenant, namespace, functionName, clientAppId, null); + public Response deregisterFunction(String tenant, String namespace, String functionName, + AuthenticationParameters authParams) { + delegate.deregisterFunction(tenant, namespace, functionName, authParams); return Response.ok().build(); } @Override - public Response listFunctions(String tenant, String namespace, String clientRole) { - Collection functionStateList = delegate.listFunctions(tenant, namespace, clientRole, null); + public Response listFunctions(String tenant, String namespace, AuthenticationParameters authParams) { + Collection functionStateList = delegate.listFunctions(tenant, namespace, authParams); return Response.status(Response.Status.OK).entity(new Gson().toJson(functionStateList.toArray())).build(); } @Override public Response triggerFunction(String tenant, String namespace, String functionName, String triggerValue, - InputStream triggerStream, String topic, String clientRole) { + InputStream triggerStream, String topic, AuthenticationParameters authParams) { String result = delegate.triggerFunction(tenant, namespace, functionName, - triggerValue, triggerStream, topic, clientRole, null); + triggerValue, triggerStream, topic, authParams); return Response.status(Response.Status.OK).entity(result).build(); } @Override public Response getFunctionState(String tenant, String namespace, String functionName, - String key, String clientRole) { + String key, AuthenticationParameters authParams) { FunctionState functionState = delegate.getFunctionState( - tenant, namespace, functionName, key, clientRole, null); + tenant, namespace, functionName, key, authParams); String value; if (functionState.getNumberValue() != null) { @@ -175,39 +179,41 @@ public Response getFunctionState(String tenant, String namespace, String functio @Override public Response restartFunctionInstance(String tenant, String namespace, String functionName, String instanceId, URI - uri, String clientRole) { - delegate.restartFunctionInstance(tenant, namespace, functionName, instanceId, uri, clientRole, null); + uri, AuthenticationParameters authParams) { + delegate.restartFunctionInstance(tenant, namespace, functionName, instanceId, uri, authParams); return Response.ok().build(); } @Override - public Response restartFunctionInstances(String tenant, String namespace, String functionName, String clientRole) { - delegate.restartFunctionInstances(tenant, namespace, functionName, clientRole, null); + public Response restartFunctionInstances(String tenant, String namespace, String functionName, + AuthenticationParameters authParams) { + delegate.restartFunctionInstances(tenant, namespace, functionName, authParams); return Response.ok().build(); } @Override public Response stopFunctionInstance(String tenant, String namespace, String functionName, String instanceId, URI - uri, String clientRole) { - delegate.stopFunctionInstance(tenant, namespace, functionName, instanceId, uri, clientRole, null); + uri, AuthenticationParameters authParams) { + delegate.stopFunctionInstance(tenant, namespace, functionName, instanceId, uri, authParams); return Response.ok().build(); } @Override - public Response stopFunctionInstances(String tenant, String namespace, String functionName, String clientRole) { - delegate.stopFunctionInstances(tenant, namespace, functionName, clientRole, null); + public Response stopFunctionInstances(String tenant, String namespace, String functionName, + AuthenticationParameters authParams) { + delegate.stopFunctionInstances(tenant, namespace, functionName, authParams); return Response.ok().build(); } @Override - public Response uploadFunction(InputStream uploadedInputStream, String path, String clientRole) { - delegate.uploadFunction(uploadedInputStream, path, clientRole, null); + public Response uploadFunction(InputStream uploadedInputStream, String path, AuthenticationParameters authParams) { + delegate.uploadFunction(uploadedInputStream, path, authParams); return Response.ok().build(); } @Override - public Response downloadFunction(String path, String clientRole) { - return Response.status(Response.Status.OK).entity(delegate.downloadFunction(path, clientRole, null)).build(); + public Response downloadFunction(String path, AuthenticationParameters authParams) { + return Response.status(Response.Status.OK).entity(delegate.downloadFunction(path, authParams)).build(); } @Override diff --git a/pulsar-functions/worker/src/main/java/org/apache/pulsar/functions/worker/rest/api/SinksImpl.java b/pulsar-functions/worker/src/main/java/org/apache/pulsar/functions/worker/rest/api/SinksImpl.java index fff376c5dcca1..98450c4a0b5d0 100644 --- a/pulsar-functions/worker/src/main/java/org/apache/pulsar/functions/worker/rest/api/SinksImpl.java +++ b/pulsar-functions/worker/src/main/java/org/apache/pulsar/functions/worker/rest/api/SinksImpl.java @@ -38,7 +38,7 @@ import javax.ws.rs.core.Response; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; -import org.apache.pulsar.broker.authentication.AuthenticationDataSource; +import org.apache.pulsar.broker.authentication.AuthenticationParameters; import org.apache.pulsar.client.admin.PulsarAdminException; import org.apache.pulsar.common.functions.UpdateOptionsImpl; import org.apache.pulsar.common.functions.Utils; @@ -77,8 +77,7 @@ public void registerSink(final String tenant, final FormDataContentDisposition fileDetail, final String sinkPkgUrl, final SinkConfig sinkConfig, - final String clientRole, - AuthenticationDataSource clientAuthenticationDataHttps) { + final AuthenticationParameters authParams) { if (!isWorkerServiceAvailable()) { throwUnavailableException(); @@ -97,16 +96,7 @@ public void registerSink(final String tenant, throw new RestException(Response.Status.BAD_REQUEST, "Sink config is not provided"); } - try { - if (!isAuthorizedRole(tenant, namespace, clientRole, clientAuthenticationDataHttps)) { - log.warn("{}/{}/{} Client [{}] is not authorized to register {}", tenant, namespace, - sinkName, clientRole, ComponentTypeUtils.toString(componentType)); - throw new RestException(Response.Status.UNAUTHORIZED, "Client is not authorized to perform operation"); - } - } catch (PulsarAdminException e) { - log.error("{}/{}/{} Failed to authorize [{}]", tenant, namespace, sinkName, e); - throw new RestException(Response.Status.INTERNAL_SERVER_ERROR, e.getMessage()); - } + throwRestExceptionIfUnauthorizedForNamespace(tenant, namespace, sinkName, "register", authParams); try { // Check tenant exists @@ -123,8 +113,8 @@ public void registerSink(final String tenant, } } } catch (PulsarAdminException.NotAuthorizedException e) { - log.error("{}/{}/{} Client [{}] is not authorized to operate {} on tenant", tenant, namespace, - sinkName, clientRole, ComponentTypeUtils.toString(componentType)); + log.error("{}/{}/{} Client is not authorized to operate {} on tenant", tenant, namespace, + sinkName, ComponentTypeUtils.toString(componentType)); throw new RestException(Response.Status.UNAUTHORIZED, "Client is not authorized to perform operation"); } catch (PulsarAdminException.NotFoundException e) { log.error("{}/{}/{} Tenant {} does not exist", tenant, namespace, sinkName, tenant); @@ -193,11 +183,12 @@ public void registerSink(final String tenant, worker().getFunctionRuntimeManager() .getRuntimeFactory() .getAuthProvider().ifPresent(functionAuthProvider -> { - if (clientAuthenticationDataHttps != null) { + if (authParams.getClientAuthenticationDataSource() != null) { try { Optional functionAuthData = functionAuthProvider - .cacheAuthData(finalFunctionDetails, clientAuthenticationDataHttps); + .cacheAuthData(finalFunctionDetails, + authParams.getClientAuthenticationDataSource()); functionAuthData.ifPresent(authData -> functionMetaDataBuilder.setFunctionAuthSpec( Function.FunctionAuthenticationSpec.newBuilder() @@ -251,8 +242,7 @@ public void updateSink(final String tenant, final FormDataContentDisposition fileDetail, final String sinkPkgUrl, final SinkConfig sinkConfig, - final String clientRole, - AuthenticationDataSource clientAuthenticationDataHttps, + final AuthenticationParameters authParams, UpdateOptionsImpl updateOptions) { if (!isWorkerServiceAvailable()) { @@ -272,17 +262,7 @@ public void updateSink(final String tenant, throw new RestException(Response.Status.BAD_REQUEST, "Sink config is not provided"); } - try { - if (!isAuthorizedRole(tenant, namespace, clientRole, clientAuthenticationDataHttps)) { - log.warn("{}/{}/{} Client [{}] is not authorized to update {}", tenant, namespace, - sinkName, clientRole, ComponentTypeUtils.toString(componentType)); - throw new RestException(Response.Status.UNAUTHORIZED, "Client is not authorized to perform operation"); - - } - } catch (PulsarAdminException e) { - log.error("{}/{}/{} Failed to authorize [{}]", tenant, namespace, sinkName, e); - throw new RestException(Response.Status.INTERNAL_SERVER_ERROR, e.getMessage()); - } + throwRestExceptionIfUnauthorizedForNamespace(tenant, namespace, sinkName, "update", authParams); FunctionMetaDataManager functionMetaDataManager = worker().getFunctionMetaDataManager(); @@ -363,7 +343,7 @@ public void updateSink(final String tenant, worker().getFunctionRuntimeManager() .getRuntimeFactory() .getAuthProvider().ifPresent(functionAuthProvider -> { - if (clientAuthenticationDataHttps != null && updateOptions != null + if (authParams.getClientAuthenticationDataSource() != null && updateOptions != null && updateOptions.isUpdateAuthData()) { // get existing auth data if it exists Optional existingFunctionAuthData = Optional.empty(); @@ -375,7 +355,7 @@ public void updateSink(final String tenant, try { Optional newFunctionAuthData = functionAuthProvider .updateAuthData(finalFunctionDetails, existingFunctionAuthData, - clientAuthenticationDataHttps); + authParams.getClientAuthenticationDataSource()); if (newFunctionAuthData.isPresent()) { functionMetaDataBuilder.setFunctionAuthSpec( @@ -629,12 +609,11 @@ private ExceptionInformation getExceptionInformation(InstanceCommunication.Funct final String sinkName, final String instanceId, final URI uri, - final String clientRole, - final AuthenticationDataSource clientAuthenticationDataHttps) { + final AuthenticationParameters authParams) { // validate parameters - componentInstanceStatusRequestValidate(tenant, namespace, sinkName, Integer.parseInt(instanceId), clientRole, - clientAuthenticationDataHttps); + componentInstanceStatusRequestValidate(tenant, namespace, sinkName, Integer.parseInt(instanceId), + authParams); SinkStatus.SinkInstanceStatus.SinkInstanceStatusData sinkInstanceStatusData; @@ -655,11 +634,10 @@ public SinkStatus getSinkStatus(final String tenant, final String namespace, final String componentName, final URI uri, - final String clientRole, - final AuthenticationDataSource clientAuthenticationDataHttps) { + final AuthenticationParameters authParams) { // validate parameters - componentStatusRequestValidate(tenant, namespace, componentName, clientRole, clientAuthenticationDataHttps); + componentStatusRequestValidate(tenant, namespace, componentName, authParams); SinkStatus sinkStatus; try { @@ -677,38 +655,12 @@ public SinkStatus getSinkStatus(final String tenant, @Override public SinkConfig getSinkInfo(final String tenant, final String namespace, - final String componentName) { - - if (!isWorkerServiceAvailable()) { - throwUnavailableException(); - } - - // validate parameters - try { - validateGetFunctionRequestParams(tenant, namespace, componentName, componentType); - } catch (IllegalArgumentException e) { - log.error("Invalid get {} request @ /{}/{}/{}", ComponentTypeUtils.toString(componentType), tenant, - namespace, componentName, e); - throw new RestException(Response.Status.BAD_REQUEST, e.getMessage()); - } - - FunctionMetaDataManager functionMetaDataManager = worker().getFunctionMetaDataManager(); - if (!functionMetaDataManager.containsFunction(tenant, namespace, componentName)) { - log.error("{} does not exist @ /{}/{}/{}", ComponentTypeUtils.toString(componentType), tenant, namespace, - componentName); - throw new RestException(Response.Status.NOT_FOUND, - String.format(ComponentTypeUtils.toString(componentType) + " %s doesn't exist", componentName)); - } + final String componentName, + final AuthenticationParameters authParams) { + componentStatusRequestValidate(tenant, namespace, componentName, authParams); Function.FunctionMetaData functionMetaData = - functionMetaDataManager.getFunctionMetaData(tenant, namespace, componentName); - if (!InstanceUtils.calculateSubjectType(functionMetaData.getFunctionDetails()).equals(componentType)) { - log.error("{}/{}/{} is not a {}", tenant, namespace, componentName, - ComponentTypeUtils.toString(componentType)); - throw new RestException(Response.Status.NOT_FOUND, - String.format(ComponentTypeUtils.toString(componentType) + " %s doesn't exist", componentName)); - } - SinkConfig config = SinkConfigUtils.convertFromDetails(functionMetaData.getFunctionDetails()); - return config; + worker().getFunctionMetaDataManager().getFunctionMetaData(tenant, namespace, componentName); + return SinkConfigUtils.convertFromDetails(functionMetaData.getFunctionDetails()); } @Override diff --git a/pulsar-functions/worker/src/main/java/org/apache/pulsar/functions/worker/rest/api/SourcesImpl.java b/pulsar-functions/worker/src/main/java/org/apache/pulsar/functions/worker/rest/api/SourcesImpl.java index 2c5921bf7ea9d..876c7e7572e78 100644 --- a/pulsar-functions/worker/src/main/java/org/apache/pulsar/functions/worker/rest/api/SourcesImpl.java +++ b/pulsar-functions/worker/src/main/java/org/apache/pulsar/functions/worker/rest/api/SourcesImpl.java @@ -37,7 +37,7 @@ import javax.ws.rs.core.Response; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; -import org.apache.pulsar.broker.authentication.AuthenticationDataSource; +import org.apache.pulsar.broker.authentication.AuthenticationParameters; import org.apache.pulsar.client.admin.PulsarAdminException; import org.apache.pulsar.common.functions.UpdateOptionsImpl; import org.apache.pulsar.common.functions.Utils; @@ -76,8 +76,7 @@ public void registerSource(final String tenant, final FormDataContentDisposition fileDetail, final String sourcePkgUrl, final SourceConfig sourceConfig, - final String clientRole, - AuthenticationDataSource clientAuthenticationDataHttps) { + final AuthenticationParameters authParams) { if (!isWorkerServiceAvailable()) { throwUnavailableException(); @@ -96,16 +95,7 @@ public void registerSource(final String tenant, throw new RestException(Response.Status.BAD_REQUEST, "Source config is not provided"); } - try { - if (!isAuthorizedRole(tenant, namespace, clientRole, clientAuthenticationDataHttps)) { - log.warn("{}/{}/{} Client [{}] is not authorized to register {}", tenant, namespace, - sourceName, clientRole, ComponentTypeUtils.toString(componentType)); - throw new RestException(Response.Status.UNAUTHORIZED, "Client is not authorized to perform operation"); - } - } catch (PulsarAdminException e) { - log.error("{}/{}/{} Failed to authorize [{}]", tenant, namespace, sourceName, e); - throw new RestException(Response.Status.INTERNAL_SERVER_ERROR, e.getMessage()); - } + throwRestExceptionIfUnauthorizedForNamespace(tenant, namespace, sourceName, "register", authParams); try { // Check tenant exists @@ -122,8 +112,8 @@ public void registerSource(final String tenant, } } } catch (PulsarAdminException.NotAuthorizedException e) { - log.error("{}/{}/{} Client [{}] is not authorized to operate {} on tenant", tenant, namespace, - sourceName, clientRole, ComponentTypeUtils.toString(componentType)); + log.error("{}/{}/{} Client is not authorized to operate {} on tenant", tenant, namespace, + sourceName, ComponentTypeUtils.toString(componentType)); throw new RestException(Response.Status.UNAUTHORIZED, "Client is not authorized to perform operation"); } catch (PulsarAdminException.NotFoundException e) { log.error("{}/{}/{} Tenant {} does not exist", tenant, namespace, sourceName, tenant); @@ -193,11 +183,12 @@ public void registerSource(final String tenant, worker().getFunctionRuntimeManager() .getRuntimeFactory() .getAuthProvider().ifPresent(functionAuthProvider -> { - if (clientAuthenticationDataHttps != null) { + if (authParams.getClientAuthenticationDataSource() != null) { try { Optional functionAuthData = functionAuthProvider - .cacheAuthData(finalFunctionDetails, clientAuthenticationDataHttps); + .cacheAuthData(finalFunctionDetails, + authParams.getClientAuthenticationDataSource()); functionAuthData.ifPresent(authData -> functionMetaDataBuilder.setFunctionAuthSpec( Function.FunctionAuthenticationSpec.newBuilder() @@ -245,8 +236,7 @@ public void updateSource(final String tenant, final FormDataContentDisposition fileDetail, final String sourcePkgUrl, final SourceConfig sourceConfig, - final String clientRole, - AuthenticationDataSource clientAuthenticationDataHttps, + final AuthenticationParameters authParams, UpdateOptionsImpl updateOptions) { if (!isWorkerServiceAvailable()) { @@ -266,17 +256,7 @@ public void updateSource(final String tenant, throw new RestException(Response.Status.BAD_REQUEST, "Source config is not provided"); } - try { - if (!isAuthorizedRole(tenant, namespace, clientRole, clientAuthenticationDataHttps)) { - log.warn("{}/{}/{} Client [{}] is not authorized to update {}", tenant, namespace, - sourceName, clientRole, ComponentTypeUtils.toString(componentType)); - throw new RestException(Response.Status.UNAUTHORIZED, "Client is not authorized to perform operation"); - - } - } catch (PulsarAdminException e) { - log.error("{}/{}/{} Failed to authorize [{}]", tenant, namespace, sourceName, e); - throw new RestException(Response.Status.INTERNAL_SERVER_ERROR, e.getMessage()); - } + throwRestExceptionIfUnauthorizedForNamespace(tenant, namespace, sourceName, "update", authParams); FunctionMetaDataManager functionMetaDataManager = worker().getFunctionMetaDataManager(); @@ -357,7 +337,7 @@ public void updateSource(final String tenant, worker().getFunctionRuntimeManager() .getRuntimeFactory() .getAuthProvider().ifPresent(functionAuthProvider -> { - if (clientAuthenticationDataHttps != null && updateOptions != null + if (authParams.getClientAuthenticationDataSource() != null && updateOptions != null && updateOptions.isUpdateAuthData()) { // get existing auth data if it exists Optional existingFunctionAuthData = Optional.empty(); @@ -369,7 +349,7 @@ public void updateSource(final String tenant, try { Optional newFunctionAuthData = functionAuthProvider .updateAuthData(finalFunctionDetails, existingFunctionAuthData, - clientAuthenticationDataHttps); + authParams.getClientAuthenticationDataSource()); if (newFunctionAuthData.isPresent()) { functionMetaDataBuilder.setFunctionAuthSpec( @@ -591,10 +571,10 @@ public SourceStatus emptyStatus(final int parallelism) { public SourceStatus getSourceStatus(final String tenant, final String namespace, final String componentName, - final URI uri, final String clientRole, - final AuthenticationDataSource clientAuthenticationDataHttps) { + final URI uri, + final AuthenticationParameters authParams) { // validate parameters - componentStatusRequestValidate(tenant, namespace, componentName, clientRole, clientAuthenticationDataHttps); + componentStatusRequestValidate(tenant, namespace, componentName, authParams); SourceStatus sourceStatus; try { @@ -616,11 +596,9 @@ public SourceStatus getSourceStatus(final String tenant, final String sourceName, final String instanceId, final URI uri, - final String clientRole, - final AuthenticationDataSource clientAuthenticationDataHttps) { + final AuthenticationParameters authParams) { // validate parameters - componentInstanceStatusRequestValidate(tenant, namespace, sourceName, Integer.parseInt(instanceId), clientRole, - clientAuthenticationDataHttps); + componentInstanceStatusRequestValidate(tenant, namespace, sourceName, Integer.parseInt(instanceId), authParams); SourceStatus.SourceInstanceStatus.SourceInstanceStatusData sourceInstanceStatusData; try { @@ -638,38 +616,12 @@ public SourceStatus getSourceStatus(final String tenant, @Override public SourceConfig getSourceInfo(final String tenant, final String namespace, - final String componentName) { - - if (!isWorkerServiceAvailable()) { - throwUnavailableException(); - } - - // validate parameters - try { - validateGetFunctionRequestParams(tenant, namespace, componentName, componentType); - } catch (IllegalArgumentException e) { - log.error("Invalid get {} request @ /{}/{}/{}", ComponentTypeUtils.toString(componentType), tenant, - namespace, componentName, e); - throw new RestException(Response.Status.BAD_REQUEST, e.getMessage()); - } - - FunctionMetaDataManager functionMetaDataManager = worker().getFunctionMetaDataManager(); - if (!functionMetaDataManager.containsFunction(tenant, namespace, componentName)) { - log.error("{} does not exist @ /{}/{}/{}", ComponentTypeUtils.toString(componentType), tenant, namespace, - componentName); - throw new RestException(Response.Status.NOT_FOUND, - String.format(ComponentTypeUtils.toString(componentType) + " %s doesn't exist", componentName)); - } + final String componentName, + final AuthenticationParameters authParams) { + componentStatusRequestValidate(tenant, namespace, componentName, authParams); Function.FunctionMetaData functionMetaData = - functionMetaDataManager.getFunctionMetaData(tenant, namespace, componentName); - if (!InstanceUtils.calculateSubjectType(functionMetaData.getFunctionDetails()).equals(componentType)) { - log.error("{}/{}/{} is not a {}", tenant, namespace, componentName, - ComponentTypeUtils.toString(componentType)); - throw new RestException(Response.Status.NOT_FOUND, - String.format(ComponentTypeUtils.toString(componentType) + " %s doesn't exist", componentName)); - } - SourceConfig config = SourceConfigUtils.convertFromDetails(functionMetaData.getFunctionDetails()); - return config; + worker().getFunctionMetaDataManager().getFunctionMetaData(tenant, namespace, componentName); + return SourceConfigUtils.convertFromDetails(functionMetaData.getFunctionDetails()); } @Override diff --git a/pulsar-functions/worker/src/main/java/org/apache/pulsar/functions/worker/rest/api/WorkerImpl.java b/pulsar-functions/worker/src/main/java/org/apache/pulsar/functions/worker/rest/api/WorkerImpl.java index 364c3ec1ab104..0b77be4ab0212 100644 --- a/pulsar-functions/worker/src/main/java/org/apache/pulsar/functions/worker/rest/api/WorkerImpl.java +++ b/pulsar-functions/worker/src/main/java/org/apache/pulsar/functions/worker/rest/api/WorkerImpl.java @@ -18,6 +18,7 @@ */ package org.apache.pulsar.functions.worker.rest.api; +import static java.util.concurrent.TimeUnit.SECONDS; import static org.apache.pulsar.functions.worker.rest.RestUtils.throwUnavailableException; import java.io.IOException; import java.net.URI; @@ -27,12 +28,15 @@ import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeoutException; import java.util.function.Supplier; import javax.ws.rs.WebApplicationException; import javax.ws.rs.core.Response; import javax.ws.rs.core.Response.Status; import javax.ws.rs.core.UriBuilder; import lombok.extern.slf4j.Slf4j; +import org.apache.pulsar.broker.authentication.AuthenticationParameters; import org.apache.pulsar.client.admin.LongRunningProcessStatus; import org.apache.pulsar.common.functions.WorkerInfo; import org.apache.pulsar.common.io.ConnectorDefinition; @@ -77,29 +81,24 @@ private boolean isWorkerServiceAvailable() { } @Override - public List getCluster(String clientRole) { + public List getCluster(AuthenticationParameters authParams) { if (!isWorkerServiceAvailable()) { throwUnavailableException(); } - if (worker().getWorkerConfig().isAuthorizationEnabled() && !isSuperUser(clientRole)) { - throw new RestException(Status.UNAUTHORIZED, "Client is not authorized to perform operation"); - } + throwIfNotSuperUser(authParams, "get cluster"); List workers = worker().getMembershipManager().getCurrentMembership(); return workers; } @Override - public WorkerInfo getClusterLeader(String clientRole) { + public WorkerInfo getClusterLeader(AuthenticationParameters authParams) { if (!isWorkerServiceAvailable()) { throwUnavailableException(); } - if (worker().getWorkerConfig().isAuthorizationEnabled() && !isSuperUser(clientRole)) { - log.error("Client [{}] is not authorized to get cluster leader", clientRole); - throw new RestException(Status.UNAUTHORIZED, "Client is not authorized to perform operation"); - } + throwIfNotSuperUser(authParams, "get cluster leader"); MembershipManager membershipManager = worker().getMembershipManager(); WorkerInfo leader = membershipManager.getLeader(); @@ -112,15 +111,12 @@ public WorkerInfo getClusterLeader(String clientRole) { } @Override - public Map> getAssignments(String clientRole) { + public Map> getAssignments(AuthenticationParameters authParams) { if (!isWorkerServiceAvailable()) { throwUnavailableException(); } - if (worker().getWorkerConfig().isAuthorizationEnabled() && !isSuperUser(clientRole)) { - log.error("Client [{}] is not authorized to get cluster assignments", clientRole); - throw new RestException(Status.UNAUTHORIZED, "Client is not authorized to perform operation"); - } + throwIfNotSuperUser(authParams, "get cluster assignments"); FunctionRuntimeManager functionRuntimeManager = worker().getFunctionRuntimeManager(); Map> assignments = functionRuntimeManager.getCurrentAssignments(); @@ -131,33 +127,41 @@ public Map> getAssignments(String clientRole) { return ret; } - private boolean isSuperUser(final String clientRole) { - return clientRole != null && worker().getWorkerConfig().getSuperUserRoles().contains(clientRole); + private void throwIfNotSuperUser(AuthenticationParameters authParams, String action) { + if (worker().getWorkerConfig().isAuthorizationEnabled()) { + try { + if (authParams.getClientRole() == null || !worker().getAuthorizationService().isSuperUser(authParams) + .get(worker().getWorkerConfig().getMetadataStoreOperationTimeoutSeconds(), SECONDS)) { + log.error("Client with role [{}] and originalPrincipal [{}] is not authorized to {}", + authParams.getClientRole(), authParams.getOriginalPrincipal(), action); + throw new RestException(Status.UNAUTHORIZED, "Client is not authorized to perform operation"); + } + } catch (ExecutionException | TimeoutException | InterruptedException e) { + log.warn("Time-out {} sec while checking the role {} originalPrincipal {} is a super user role ", + worker().getWorkerConfig().getMetadataStoreOperationTimeoutSeconds(), + authParams.getClientRole(), authParams.getOriginalPrincipal()); + throw new RestException(Status.INTERNAL_SERVER_ERROR, e.getMessage()); + } + } } @Override - public List getWorkerMetrics(final String clientRole) { + public List getWorkerMetrics(final AuthenticationParameters authParams) { if (!isWorkerServiceAvailable() || worker().getMetricsGenerator() == null) { throwUnavailableException(); } - - if (worker().getWorkerConfig().isAuthorizationEnabled() && !isSuperUser(clientRole)) { - log.error("Client [{}] is not authorized to get worker stats", clientRole); - throw new RestException(Status.UNAUTHORIZED, "Client is not authorized to perform operation"); - } + throwIfNotSuperUser(authParams, "get worker stats"); return worker().getMetricsGenerator().generate(); } @Override - public List getFunctionsMetrics(String clientRole) throws IOException { + public List getFunctionsMetrics(AuthenticationParameters authParams) + throws IOException { if (!isWorkerServiceAvailable()) { throwUnavailableException(); } - if (worker().getWorkerConfig().isAuthorizationEnabled() && !isSuperUser(clientRole)) { - log.error("Client [{}] is not authorized to get function stats", clientRole); - throw new RestException(Status.UNAUTHORIZED, "Client is not authorized to perform operation"); - } + throwIfNotSuperUser(authParams, "get function stats"); Map functionRuntimes = worker().getFunctionRuntimeManager() .getFunctionRuntimeInfos(); @@ -196,28 +200,20 @@ public List getFunctionsMetrics(String clientRole) } @Override - public List getListOfConnectors(String clientRole) { + public List getListOfConnectors(AuthenticationParameters authParams) { if (!isWorkerServiceAvailable()) { throwUnavailableException(); } - - if (worker().getWorkerConfig().isAuthorizationEnabled() && !isSuperUser(clientRole)) { - throw new RestException(Status.UNAUTHORIZED, "Client is not authorized to perform operation"); - } - + throwIfNotSuperUser(authParams, "get list of connectors"); return this.worker().getConnectorsManager().getConnectorDefinitions(); } @Override - public void rebalance(final URI uri, final String clientRole) { + public void rebalance(final URI uri, final AuthenticationParameters authParams) { if (!isWorkerServiceAvailable()) { throwUnavailableException(); } - - if (worker().getWorkerConfig().isAuthorizationEnabled() && !isSuperUser(clientRole)) { - log.error("Client [{}] is not authorized to rebalance cluster", clientRole); - throw new RestException(Status.UNAUTHORIZED, "Client is not authorized to perform operation"); - } + throwIfNotSuperUser(authParams, "rebalance cluster"); if (worker().getLeaderService().isLeader()) { try { @@ -239,7 +235,8 @@ public void rebalance(final URI uri, final String clientRole) { } @Override - public void drain(final URI uri, final String inWorkerId, final String clientRole, boolean calledOnLeaderUri) { + public void drain(final URI uri, final String inWorkerId, final AuthenticationParameters authParams, + boolean calledOnLeaderUri) { if (!isWorkerServiceAvailable()) { throwUnavailableException(); } @@ -248,15 +245,13 @@ public void drain(final URI uri, final String inWorkerId, final String clientRol final String workerId = (inWorkerId == null || inWorkerId.isEmpty()) ? actualWorkerId : inWorkerId; if (log.isDebugEnabled()) { - log.debug("drain called with URI={}, inWorkerId={}, workerId={}, clientRole={}, calledOnLeaderUri={}, " - + "on actual worker-id={}", - uri, inWorkerId, workerId, clientRole, calledOnLeaderUri, actualWorkerId); + log.debug("drain called with URI={}, inWorkerId={}, workerId={}, clientRole={}, originalPrincipal={}, " + + "calledOnLeaderUri={}, on actual worker-id={}", + uri, inWorkerId, workerId, authParams.getClientRole(), authParams.getOriginalPrincipal(), + calledOnLeaderUri, actualWorkerId); } - if (worker().getWorkerConfig().isAuthorizationEnabled() && !isSuperUser(clientRole)) { - log.error("Client [{}] is not authorized to drain worker {}", clientRole, workerId); - throw new RestException(Status.UNAUTHORIZED, "Client is not authorized to perform drain operation"); - } + throwIfNotSuperUser(authParams, "drain worker"); // Depending on which operations we decide to allow, we may add checks here to error/exception if // calledOnLeaderUri is true on a non-leader @@ -285,7 +280,8 @@ public void drain(final URI uri, final String inWorkerId, final String clientRol } @Override - public LongRunningProcessStatus getDrainStatus(final URI uri, final String inWorkerId, final String clientRole, + public LongRunningProcessStatus getDrainStatus(final URI uri, final String inWorkerId, + final AuthenticationParameters authParams, boolean calledOnLeaderUri) { if (!isWorkerServiceAvailable()) { throwUnavailableException(); @@ -296,15 +292,12 @@ public LongRunningProcessStatus getDrainStatus(final URI uri, final String inWor if (log.isDebugEnabled()) { log.debug("getDrainStatus called with uri={}, inWorkerId={}, workerId={}, clientRole={}, " - + " calledOnLeaderUri={}, on actual workerId={}", - uri, inWorkerId, workerId, clientRole, calledOnLeaderUri, actualWorkerId); + + "originalPrincipal={}, calledOnLeaderUri={}, on actual workerId={}", + uri, inWorkerId, workerId, authParams.getClientRole(), authParams.getOriginalPrincipal(), + calledOnLeaderUri, actualWorkerId); } - if (worker().getWorkerConfig().isAuthorizationEnabled() && !isSuperUser(clientRole)) { - log.error("Client [{}] is not authorized to get drain status of worker {}", clientRole, workerId); - throw new RestException(Status.UNAUTHORIZED, - "Client is not authorized to get the status of a drain operation"); - } + throwIfNotSuperUser(authParams, "get drain status of worker"); // Depending on which operations we decide to allow, we may add checks here to error/exception if // calledOnLeaderUri is true on a non-leader @@ -321,7 +314,7 @@ public LongRunningProcessStatus getDrainStatus(final URI uri, final String inWor } @Override - public Boolean isLeaderReady(final String clientRole) { + public boolean isLeaderReady(AuthenticationParameters authParams) { if (!isWorkerServiceAvailable()) { throwUnavailableException(); } diff --git a/pulsar-functions/worker/src/main/java/org/apache/pulsar/functions/worker/rest/api/v2/FunctionsApiV2Resource.java b/pulsar-functions/worker/src/main/java/org/apache/pulsar/functions/worker/rest/api/v2/FunctionsApiV2Resource.java index 9176013b783f6..0b125756b30a1 100644 --- a/pulsar-functions/worker/src/main/java/org/apache/pulsar/functions/worker/rest/api/v2/FunctionsApiV2Resource.java +++ b/pulsar-functions/worker/src/main/java/org/apache/pulsar/functions/worker/rest/api/v2/FunctionsApiV2Resource.java @@ -72,7 +72,7 @@ public Response registerFunction(final @PathParam("tenant") String tenant, final @FormDataParam("functionDetails") String functionDetailsJson) { return functions().registerFunction(tenant, namespace, functionName, uploadedInputStream, fileDetail, - functionPkgUrl, functionDetailsJson, clientAppId()); + functionPkgUrl, functionDetailsJson, authParams()); } @PUT @@ -93,7 +93,7 @@ public Response updateFunction(final @PathParam("tenant") String tenant, final @FormDataParam("functionDetails") String functionDetailsJson) { return functions().updateFunction(tenant, namespace, functionName, uploadedInputStream, fileDetail, - functionPkgUrl, functionDetailsJson, clientAppId()); + functionPkgUrl, functionDetailsJson, authParams()); } @@ -110,7 +110,7 @@ public Response updateFunction(final @PathParam("tenant") String tenant, public Response deregisterFunction(final @PathParam("tenant") String tenant, final @PathParam("namespace") String namespace, final @PathParam("functionName") String functionName) { - return functions().deregisterFunction(tenant, namespace, functionName, clientAppId()); + return functions().deregisterFunction(tenant, namespace, functionName, authParams()); } @GET @@ -129,8 +129,7 @@ public Response getFunctionInfo(final @PathParam("tenant") String tenant, final @PathParam("namespace") String namespace, final @PathParam("functionName") String functionName) throws IOException { - return functions().getFunctionInfo( - tenant, namespace, functionName, clientAppId()); + return functions().getFunctionInfo(tenant, namespace, functionName, authParams()); } @GET @@ -151,7 +150,7 @@ public Response getFunctionInstanceStatus(final @PathParam("tenant") String tena final @PathParam("instanceId") String instanceId) throws IOException { return functions().getFunctionInstanceStatus(tenant, namespace, functionName, instanceId, uri.getRequestUri(), - clientAppId()); + authParams()); } @GET @@ -168,7 +167,7 @@ public Response getFunctionInstanceStatus(final @PathParam("tenant") String tena public Response getFunctionStatus(final @PathParam("tenant") String tenant, final @PathParam("namespace") String namespace, final @PathParam("functionName") String functionName) throws IOException { - return functions().getFunctionStatusV2(tenant, namespace, functionName, uri.getRequestUri(), clientAppId()); + return functions().getFunctionStatusV2(tenant, namespace, functionName, uri.getRequestUri(), authParams()); } @GET @@ -184,7 +183,7 @@ public Response getFunctionStatus(final @PathParam("tenant") String tenant, @Path("/{tenant}/{namespace}") public Response listFunctions(final @PathParam("tenant") String tenant, final @PathParam("namespace") String namespace) { - return functions().listFunctions(tenant, namespace, clientAppId()); + return functions().listFunctions(tenant, namespace, authParams()); } @POST @@ -207,7 +206,7 @@ public Response triggerFunction(final @PathParam("tenant") String tenant, final @FormDataParam("dataStream") InputStream triggerStream, final @FormDataParam("topic") String topic) { return functions().triggerFunction(tenant, namespace, functionName, triggerValue, triggerStream, topic, - clientAppId()); + authParams()); } @GET @@ -226,7 +225,7 @@ public Response getFunctionState(final @PathParam("tenant") String tenant, final @PathParam("namespace") String namespace, final @PathParam("functionName") String functionName, final @PathParam("key") String key) { - return functions().getFunctionState(tenant, namespace, functionName, key, clientAppId()); + return functions().getFunctionState(tenant, namespace, functionName, key, authParams()); } @POST @@ -243,7 +242,7 @@ public Response restartFunction(final @PathParam("tenant") String tenant, final @PathParam("functionName") String functionName, final @PathParam("instanceId") String instanceId) { return functions().restartFunctionInstance(tenant, namespace, functionName, instanceId, uri.getRequestUri(), - clientAppId()); + authParams()); } @POST @@ -256,7 +255,7 @@ public Response restartFunction(final @PathParam("tenant") String tenant, public Response restartFunction(final @PathParam("tenant") String tenant, final @PathParam("namespace") String namespace, final @PathParam("functionName") String functionName) { - return functions().restartFunctionInstances(tenant, namespace, functionName, clientAppId()); + return functions().restartFunctionInstances(tenant, namespace, functionName, authParams()); } @POST @@ -271,7 +270,7 @@ public Response stopFunction(final @PathParam("tenant") String tenant, final @PathParam("functionName") String functionName, final @PathParam("instanceId") String instanceId) { return functions().stopFunctionInstance(tenant, namespace, functionName, instanceId, uri.getRequestUri(), - clientAppId()); + authParams()); } @POST @@ -284,7 +283,7 @@ public Response stopFunction(final @PathParam("tenant") String tenant, public Response stopFunction(final @PathParam("tenant") String tenant, final @PathParam("namespace") String namespace, final @PathParam("functionName") String functionName) { - return functions().stopFunctionInstances(tenant, namespace, functionName, clientAppId()); + return functions().stopFunctionInstances(tenant, namespace, functionName, authParams()); } @POST @@ -296,7 +295,7 @@ public Response stopFunction(final @PathParam("tenant") String tenant, @Consumes(MediaType.MULTIPART_FORM_DATA) public Response uploadFunction(final @FormDataParam("data") InputStream uploadedInputStream, final @FormDataParam("path") String path) { - return functions().uploadFunction(uploadedInputStream, path, clientAppId()); + return functions().uploadFunction(uploadedInputStream, path, authParams()); } @GET @@ -306,7 +305,7 @@ public Response uploadFunction(final @FormDataParam("data") InputStream uploaded ) @Path("/download") public Response downloadFunction(final @QueryParam("path") String path) { - return functions().downloadFunction(path, clientAppId()); + return functions().downloadFunction(path, authParams()); } @GET diff --git a/pulsar-functions/worker/src/main/java/org/apache/pulsar/functions/worker/rest/api/v2/WorkerApiV2Resource.java b/pulsar-functions/worker/src/main/java/org/apache/pulsar/functions/worker/rest/api/v2/WorkerApiV2Resource.java index 496f6f43028fe..276d0ae86d876 100644 --- a/pulsar-functions/worker/src/main/java/org/apache/pulsar/functions/worker/rest/api/v2/WorkerApiV2Resource.java +++ b/pulsar-functions/worker/src/main/java/org/apache/pulsar/functions/worker/rest/api/v2/WorkerApiV2Resource.java @@ -39,11 +39,14 @@ import javax.ws.rs.core.MediaType; import javax.ws.rs.core.UriInfo; import lombok.extern.slf4j.Slf4j; +import org.apache.pulsar.broker.authentication.AuthenticationDataSource; +import org.apache.pulsar.broker.authentication.AuthenticationParameters; import org.apache.pulsar.broker.web.AuthenticationFilter; import org.apache.pulsar.client.admin.LongRunningProcessStatus; import org.apache.pulsar.common.functions.WorkerInfo; import org.apache.pulsar.common.io.ConnectorDefinition; import org.apache.pulsar.functions.worker.WorkerService; +import org.apache.pulsar.functions.worker.rest.FunctionApiResource; import org.apache.pulsar.functions.worker.service.api.Workers; @Slf4j @@ -75,12 +78,25 @@ Workers workers() { return get().getWorkers(); } + /** + * @deprecated use {@link #authParams()} instead + */ + @Deprecated public String clientAppId() { return httpRequest != null ? (String) httpRequest.getAttribute(AuthenticationFilter.AuthenticatedRoleAttributeName) : null; } + public AuthenticationParameters authParams() { + return AuthenticationParameters.builder() + .clientRole(clientAppId()) + .originalPrincipal(httpRequest.getHeader(FunctionApiResource.ORIGINAL_PRINCIPAL_HEADER)) + .clientAuthenticationDataSource((AuthenticationDataSource) + httpRequest.getAttribute(AuthenticationFilter.AuthenticatedDataAttributeName)) + .build(); + } + @GET @ApiOperation( value = "Fetches information about the Pulsar cluster running Pulsar Functions", @@ -94,7 +110,7 @@ public String clientAppId() { @Path("/cluster") @Produces(MediaType.APPLICATION_JSON) public List getCluster() { - return workers().getCluster(clientAppId()); + return workers().getCluster(authParams()); } @GET @@ -109,7 +125,7 @@ public List getCluster() { @Path("/cluster/leader") @Produces(MediaType.APPLICATION_JSON) public WorkerInfo getClusterLeader() { - return workers().getClusterLeader(clientAppId()); + return workers().getClusterLeader(authParams()); } @GET @@ -124,7 +140,7 @@ public WorkerInfo getClusterLeader() { @Path("/assignments") @Produces(MediaType.APPLICATION_JSON) public Map> getAssignments() { - return workers().getAssignments(clientAppId()); + return workers().getAssignments(authParams()); } @GET @@ -139,7 +155,7 @@ public Map> getAssignments() { }) @Path("/connectors") public List getConnectorsList() throws IOException { - return workers().getListOfConnectors(clientAppId()); + return workers().getListOfConnectors(authParams()); } @PUT @@ -153,7 +169,7 @@ public List getConnectorsList() throws IOException { }) @Path("/rebalance") public void rebalance() { - workers().rebalance(uri.getRequestUri(), clientAppId()); + workers().rebalance(uri.getRequestUri(), authParams()); } @PUT @@ -169,7 +185,7 @@ public void rebalance() { }) @Path("/leader/drain") public void drainAtLeader(@QueryParam("workerId") String workerId) { - workers().drain(uri.getRequestUri(), workerId, clientAppId(), true); + workers().drain(uri.getRequestUri(), workerId, authParams(), true); } @PUT @@ -185,7 +201,7 @@ public void drainAtLeader(@QueryParam("workerId") String workerId) { }) @Path("/drain") public void drain() { - workers().drain(uri.getRequestUri(), null, clientAppId(), false); + workers().drain(uri.getRequestUri(), null, authParams(), false); } @GET @@ -199,7 +215,7 @@ public void drain() { }) @Path("/leader/drain") public LongRunningProcessStatus getDrainStatus(@QueryParam("workerId") String workerId) { - return workers().getDrainStatus(uri.getRequestUri(), workerId, clientAppId(), true); + return workers().getDrainStatus(uri.getRequestUri(), workerId, authParams(), true); } @GET @@ -213,7 +229,7 @@ public LongRunningProcessStatus getDrainStatus(@QueryParam("workerId") String wo }) @Path("/drain") public LongRunningProcessStatus getDrainStatus() { - return workers().getDrainStatus(uri.getRequestUri(), null, clientAppId(), false); + return workers().getDrainStatus(uri.getRequestUri(), null, authParams(), false); } @GET @@ -226,6 +242,6 @@ public LongRunningProcessStatus getDrainStatus() { }) @Path("/cluster/leader/ready") public Boolean isLeaderReady() { - return workers().isLeaderReady(clientAppId()); + return workers().isLeaderReady(authParams()); } } diff --git a/pulsar-functions/worker/src/main/java/org/apache/pulsar/functions/worker/rest/api/v2/WorkerStatsApiV2Resource.java b/pulsar-functions/worker/src/main/java/org/apache/pulsar/functions/worker/rest/api/v2/WorkerStatsApiV2Resource.java index b6a7914f0fdfd..712505a4ba08a 100644 --- a/pulsar-functions/worker/src/main/java/org/apache/pulsar/functions/worker/rest/api/v2/WorkerStatsApiV2Resource.java +++ b/pulsar-functions/worker/src/main/java/org/apache/pulsar/functions/worker/rest/api/v2/WorkerStatsApiV2Resource.java @@ -34,9 +34,12 @@ import javax.ws.rs.core.Context; import javax.ws.rs.core.MediaType; import lombok.extern.slf4j.Slf4j; +import org.apache.pulsar.broker.authentication.AuthenticationDataSource; +import org.apache.pulsar.broker.authentication.AuthenticationParameters; import org.apache.pulsar.broker.web.AuthenticationFilter; import org.apache.pulsar.common.policies.data.WorkerFunctionInstanceStats; import org.apache.pulsar.functions.worker.WorkerService; +import org.apache.pulsar.functions.worker.rest.FunctionApiResource; import org.apache.pulsar.functions.worker.service.api.Workers; @Slf4j @@ -66,6 +69,19 @@ Workers workers() { return get().getWorkers(); } + AuthenticationParameters authParams() { + return AuthenticationParameters.builder() + .clientRole(clientAppId()) + .originalPrincipal(httpRequest.getHeader(FunctionApiResource.ORIGINAL_PRINCIPAL_HEADER)) + .clientAuthenticationDataSource((AuthenticationDataSource) + httpRequest.getAttribute(AuthenticationFilter.AuthenticatedDataAttributeName)) + .build(); + } + + /** + * @deprecated use {@link AuthenticationParameters} instead + */ + @Deprecated public String clientAppId() { return httpRequest != null ? (String) httpRequest.getAttribute(AuthenticationFilter.AuthenticatedRoleAttributeName) @@ -85,7 +101,7 @@ public String clientAppId() { }) @Produces(MediaType.APPLICATION_JSON) public List getMetrics() throws Exception { - return workers().getWorkerMetrics(clientAppId()); + return workers().getWorkerMetrics(authParams()); } @GET @@ -101,6 +117,6 @@ public List getMetrics() throws Exceptio }) @Produces(MediaType.APPLICATION_JSON) public List getStats() throws IOException { - return workers().getFunctionsMetrics(clientAppId()); + return workers().getFunctionsMetrics(authParams()); } } diff --git a/pulsar-functions/worker/src/main/java/org/apache/pulsar/functions/worker/rest/api/v3/FunctionsApiV3Resource.java b/pulsar-functions/worker/src/main/java/org/apache/pulsar/functions/worker/rest/api/v3/FunctionsApiV3Resource.java index bca0cf4bf3d75..7bdc86d5fae3e 100644 --- a/pulsar-functions/worker/src/main/java/org/apache/pulsar/functions/worker/rest/api/v3/FunctionsApiV3Resource.java +++ b/pulsar-functions/worker/src/main/java/org/apache/pulsar/functions/worker/rest/api/v3/FunctionsApiV3Resource.java @@ -73,7 +73,7 @@ public void registerFunction(final @PathParam("tenant") String tenant, final @FormDataParam("functionConfig") FunctionConfig functionConfig) { functions().registerFunction(tenant, namespace, functionName, uploadedInputStream, fileDetail, - functionPkgUrl, functionConfig, clientAppId(), clientAuthData()); + functionPkgUrl, functionConfig, authParams()); } @@ -90,7 +90,7 @@ public void updateFunction(final @PathParam("tenant") String tenant, final @FormDataParam("updateOptions") UpdateOptionsImpl updateOptions) { functions().updateFunction(tenant, namespace, functionName, uploadedInputStream, fileDetail, - functionPkgUrl, functionConfig, clientAppId(), clientAuthData(), updateOptions); + functionPkgUrl, functionConfig, authParams(), updateOptions); } @@ -99,7 +99,7 @@ public void updateFunction(final @PathParam("tenant") String tenant, public void deregisterFunction(final @PathParam("tenant") String tenant, final @PathParam("namespace") String namespace, final @PathParam("functionName") String functionName) { - functions().deregisterFunction(tenant, namespace, functionName, clientAppId(), clientAuthData()); + functions().deregisterFunction(tenant, namespace, functionName, authParams()); } @GET @@ -107,7 +107,7 @@ public void deregisterFunction(final @PathParam("tenant") String tenant, public FunctionConfig getFunctionInfo(final @PathParam("tenant") String tenant, final @PathParam("namespace") String namespace, final @PathParam("functionName") String functionName) { - return functions().getFunctionInfo(tenant, namespace, functionName, clientAppId(), clientAuthData()); + return functions().getFunctionInfo(tenant, namespace, functionName, authParams()); } @GET @@ -115,7 +115,7 @@ public FunctionConfig getFunctionInfo(final @PathParam("tenant") String tenant, @Path("/{tenant}/{namespace}") public List listFunctions(final @PathParam("tenant") String tenant, final @PathParam("namespace") String namespace) { - return functions().listFunctions(tenant, namespace, clientAppId(), clientAuthData()); + return functions().listFunctions(tenant, namespace, authParams()); } @GET @@ -137,7 +137,7 @@ public FunctionStatus.FunctionInstanceStatus.FunctionInstanceStatusData getFunct final @PathParam("functionName") String functionName, final @PathParam("instanceId") String instanceId) throws IOException { return functions().getFunctionInstanceStatus( - tenant, namespace, functionName, instanceId, uri.getRequestUri(), clientAppId(), clientAuthData()); + tenant, namespace, functionName, instanceId, uri.getRequestUri(), authParams()); } @GET @@ -158,7 +158,7 @@ public FunctionStatus getFunctionStatus( final @PathParam("namespace") String namespace, final @PathParam("functionName") String functionName) throws IOException { return functions().getFunctionStatus( - tenant, namespace, functionName, uri.getRequestUri(), clientAppId(), clientAuthData()); + tenant, namespace, functionName, uri.getRequestUri(), authParams()); } @GET @@ -178,7 +178,7 @@ public FunctionStatsImpl getFunctionStats(final @PathParam("tenant") String tena final @PathParam("namespace") String namespace, final @PathParam("functionName") String functionName) throws IOException { return functions().getFunctionStats(tenant, namespace, functionName, - uri.getRequestUri(), clientAppId(), clientAuthData()); + uri.getRequestUri(), authParams()); } @GET @@ -200,7 +200,7 @@ public FunctionInstanceStatsDataImpl getFunctionInstanceStats( final @PathParam("functionName") String functionName, final @PathParam("instanceId") String instanceId) throws IOException { return functions().getFunctionsInstanceStats( - tenant, namespace, functionName, instanceId, uri.getRequestUri(), clientAppId(), clientAuthData()); + tenant, namespace, functionName, instanceId, uri.getRequestUri(), authParams()); } @POST @@ -213,7 +213,7 @@ public String triggerFunction(final @PathParam("tenant") String tenant, final @FormDataParam("dataStream") InputStream uploadedInputStream, final @FormDataParam("topic") String topic) { return functions().triggerFunction(tenant, namespace, functionName, input, - uploadedInputStream, topic, clientAppId(), clientAuthData()); + uploadedInputStream, topic, authParams()); } @POST @@ -231,7 +231,7 @@ public void restartFunction(final @PathParam("tenant") String tenant, final @PathParam("functionName") String functionName, final @PathParam("instanceId") String instanceId) { functions().restartFunctionInstance(tenant, namespace, functionName, instanceId, - this.uri.getRequestUri(), clientAppId(), clientAuthData()); + this.uri.getRequestUri(), authParams()); } @POST @@ -246,7 +246,7 @@ public void restartFunction(final @PathParam("tenant") String tenant, public void restartFunction(final @PathParam("tenant") String tenant, final @PathParam("namespace") String namespace, final @PathParam("functionName") String functionName) { - functions().restartFunctionInstances(tenant, namespace, functionName, clientAppId(), clientAuthData()); + functions().restartFunctionInstances(tenant, namespace, functionName, authParams()); } @POST @@ -263,7 +263,7 @@ public void stopFunction(final @PathParam("tenant") String tenant, final @PathParam("functionName") String functionName, final @PathParam("instanceId") String instanceId) { functions().stopFunctionInstance(tenant, namespace, functionName, instanceId, - this.uri.getRequestUri(), clientAppId(), clientAuthData()); + this.uri.getRequestUri(), authParams()); } @POST @@ -278,7 +278,7 @@ public void stopFunction(final @PathParam("tenant") String tenant, public void stopFunction(final @PathParam("tenant") String tenant, final @PathParam("namespace") String namespace, final @PathParam("functionName") String functionName) { - functions().stopFunctionInstances(tenant, namespace, functionName, clientAppId(), clientAuthData()); + functions().stopFunctionInstances(tenant, namespace, functionName, authParams()); } @POST @@ -295,7 +295,7 @@ public void startFunction(final @PathParam("tenant") String tenant, final @PathParam("functionName") String functionName, final @PathParam("instanceId") String instanceId) { functions().startFunctionInstance(tenant, namespace, functionName, instanceId, - this.uri.getRequestUri(), clientAppId(), clientAuthData()); + this.uri.getRequestUri(), authParams()); } @POST @@ -310,7 +310,7 @@ public void startFunction(final @PathParam("tenant") String tenant, public void startFunction(final @PathParam("tenant") String tenant, final @PathParam("namespace") String namespace, final @PathParam("functionName") String functionName) { - functions().startFunctionInstances(tenant, namespace, functionName, clientAppId(), clientAuthData()); + functions().startFunctionInstances(tenant, namespace, functionName, authParams()); } @POST @@ -318,13 +318,13 @@ public void startFunction(final @PathParam("tenant") String tenant, @Consumes(MediaType.MULTIPART_FORM_DATA) public void uploadFunction(final @FormDataParam("data") InputStream uploadedInputStream, final @FormDataParam("path") String path) { - functions().uploadFunction(uploadedInputStream, path, clientAppId(), clientAuthData()); + functions().uploadFunction(uploadedInputStream, path, authParams()); } @GET @Path("/download") public StreamingOutput downloadFunction(final @QueryParam("path") String path) { - return functions().downloadFunction(path, clientAppId(), clientAuthData()); + return functions().downloadFunction(path, authParams()); } @GET @@ -344,7 +344,7 @@ public StreamingOutput downloadFunction( final @QueryParam("transform-function") boolean transformFunction) { return functions() - .downloadFunction(tenant, namespace, functionName, clientAppId(), clientAuthData(), transformFunction); + .downloadFunction(tenant, namespace, functionName, authParams(), transformFunction); } @GET @@ -368,7 +368,7 @@ public List getConnectorsList() throws IOException { }) @Path("/builtins/reload") public void reloadBuiltinFunctions() throws IOException { - functions().reloadBuiltinFunctions(clientAppId(), clientAuthData()); + functions().reloadBuiltinFunctions(authParams()); } @GET @@ -385,7 +385,7 @@ public void reloadBuiltinFunctions() throws IOException { @Path("/builtins") @Produces(MediaType.APPLICATION_JSON) public List getBuiltinFunctions() { - return functions().getBuiltinFunctions(clientAppId(), clientAuthData()); + return functions().getBuiltinFunctions(authParams()); } @GET @@ -394,7 +394,7 @@ public FunctionState getFunctionState(final @PathParam("tenant") String tenant, final @PathParam("namespace") String namespace, final @PathParam("functionName") String functionName, final @PathParam("key") String key) throws IOException { - return functions().getFunctionState(tenant, namespace, functionName, key, clientAppId(), clientAuthData()); + return functions().getFunctionState(tenant, namespace, functionName, key, authParams()); } @POST @@ -405,7 +405,7 @@ public void putFunctionState(final @PathParam("tenant") String tenant, final @PathParam("functionName") String functionName, final @PathParam("key") String key, final @FormDataParam("state") FunctionState stateJson) throws IOException { - functions().putFunctionState(tenant, namespace, functionName, key, stateJson, clientAppId(), clientAuthData()); + functions().putFunctionState(tenant, namespace, functionName, key, stateJson, authParams()); } @PUT @@ -427,6 +427,6 @@ public void updateFunctionOnWorkerLeader(final @PathParam("tenant") String tenan final @FormDataParam("delete") boolean delete) { functions().updateFunctionOnWorkerLeader(tenant, namespace, functionName, uploadedInputStream, - delete, uri.getRequestUri(), clientAppId(), clientAuthData()); + delete, uri.getRequestUri(), authParams()); } } diff --git a/pulsar-functions/worker/src/main/java/org/apache/pulsar/functions/worker/rest/api/v3/SinksApiV3Resource.java b/pulsar-functions/worker/src/main/java/org/apache/pulsar/functions/worker/rest/api/v3/SinksApiV3Resource.java index b110c8f72c677..8081f76cfd8ec 100644 --- a/pulsar-functions/worker/src/main/java/org/apache/pulsar/functions/worker/rest/api/v3/SinksApiV3Resource.java +++ b/pulsar-functions/worker/src/main/java/org/apache/pulsar/functions/worker/rest/api/v3/SinksApiV3Resource.java @@ -70,7 +70,7 @@ public void registerSink(final @PathParam("tenant") String tenant, final @FormDataParam("sinkConfig") SinkConfig sinkConfig) { sinks().registerSink(tenant, namespace, sinkName, uploadedInputStream, fileDetail, - functionPkgUrl, sinkConfig, clientAppId(), clientAuthData()); + functionPkgUrl, sinkConfig, authParams()); } @PUT @@ -86,7 +86,7 @@ public void updateSink(final @PathParam("tenant") String tenant, final @FormDataParam("updateOptions") UpdateOptionsImpl updateOptions) { sinks().updateSink(tenant, namespace, sinkName, uploadedInputStream, fileDetail, - functionPkgUrl, sinkConfig, clientAppId(), clientAuthData(), updateOptions); + functionPkgUrl, sinkConfig, authParams(), updateOptions); } @DELETE @@ -94,7 +94,7 @@ public void updateSink(final @PathParam("tenant") String tenant, public void deregisterSink(final @PathParam("tenant") String tenant, final @PathParam("namespace") String namespace, final @PathParam("sinkName") String sinkName) { - sinks().deregisterFunction(tenant, namespace, sinkName, clientAppId(), clientAuthData()); + sinks().deregisterFunction(tenant, namespace, sinkName, authParams()); } @GET @@ -103,7 +103,7 @@ public SinkConfig getSinkInfo(final @PathParam("tenant") String tenant, final @PathParam("namespace") String namespace, final @PathParam("sinkName") String sinkName) throws IOException { - return sinks().getSinkInfo(tenant, namespace, sinkName); + return sinks().getSinkInfo(tenant, namespace, sinkName, authParams()); } @GET @@ -124,9 +124,8 @@ public SinkStatus.SinkInstanceStatus.SinkInstanceStatusData getSinkInstanceStatu final @PathParam("namespace") String namespace, final @PathParam("sinkName") String sinkName, final @PathParam("instanceId") String instanceId) throws IOException { - return sinks() - .getSinkInstanceStatus(tenant, namespace, sinkName, instanceId, uri.getRequestUri(), clientAppId(), - clientAuthData()); + return sinks().getSinkInstanceStatus(tenant, namespace, sinkName, instanceId, uri.getRequestUri(), + authParams()); } @GET @@ -145,14 +144,14 @@ public SinkStatus.SinkInstanceStatus.SinkInstanceStatusData getSinkInstanceStatu public SinkStatus getSinkStatus(final @PathParam("tenant") String tenant, final @PathParam("namespace") String namespace, final @PathParam("sinkName") String sinkName) throws IOException { - return sinks().getSinkStatus(tenant, namespace, sinkName, uri.getRequestUri(), clientAppId(), clientAuthData()); + return sinks().getSinkStatus(tenant, namespace, sinkName, uri.getRequestUri(), authParams()); } @GET @Path("/{tenant}/{namespace}") public List listSink(final @PathParam("tenant") String tenant, final @PathParam("namespace") String namespace) { - return sinks().listFunctions(tenant, namespace, clientAppId(), clientAuthData()); + return sinks().listFunctions(tenant, namespace, authParams()); } @POST @@ -169,7 +168,7 @@ public void restartSink(final @PathParam("tenant") String tenant, final @PathParam("sinkName") String sinkName, final @PathParam("instanceId") String instanceId) { sinks().restartFunctionInstance(tenant, namespace, sinkName, instanceId, this.uri.getRequestUri(), - clientAppId(), clientAuthData()); + authParams()); } @POST @@ -182,7 +181,7 @@ public void restartSink(final @PathParam("tenant") String tenant, public void restartSink(final @PathParam("tenant") String tenant, final @PathParam("namespace") String namespace, final @PathParam("sinkName") String sinkName) { - sinks().restartFunctionInstances(tenant, namespace, sinkName, clientAppId(), clientAuthData()); + sinks().restartFunctionInstances(tenant, namespace, sinkName, authParams()); } @POST @@ -196,8 +195,8 @@ public void stopSink(final @PathParam("tenant") String tenant, final @PathParam("namespace") String namespace, final @PathParam("sinkName") String sinkName, final @PathParam("instanceId") String instanceId) { - sinks().stopFunctionInstance(tenant, namespace, sinkName, instanceId, this.uri.getRequestUri(), clientAppId(), - clientAuthData()); + sinks().stopFunctionInstance(tenant, namespace, sinkName, instanceId, this.uri.getRequestUri(), + authParams()); } @POST @@ -210,7 +209,7 @@ public void stopSink(final @PathParam("tenant") String tenant, public void stopSink(final @PathParam("tenant") String tenant, final @PathParam("namespace") String namespace, final @PathParam("sinkName") String sinkName) { - sinks().stopFunctionInstances(tenant, namespace, sinkName, clientAppId(), clientAuthData()); + sinks().stopFunctionInstances(tenant, namespace, sinkName, authParams()); } @POST @@ -224,8 +223,8 @@ public void startSink(final @PathParam("tenant") String tenant, final @PathParam("namespace") String namespace, final @PathParam("sinkName") String sinkName, final @PathParam("instanceId") String instanceId) { - sinks().startFunctionInstance(tenant, namespace, sinkName, instanceId, this.uri.getRequestUri(), clientAppId(), - clientAuthData()); + sinks().startFunctionInstance(tenant, namespace, sinkName, instanceId, this.uri.getRequestUri(), + authParams()); } @POST @@ -238,7 +237,7 @@ public void startSink(final @PathParam("tenant") String tenant, public void startSink(final @PathParam("tenant") String tenant, final @PathParam("namespace") String namespace, final @PathParam("sinkName") String sinkName) { - sinks().startFunctionInstances(tenant, namespace, sinkName, clientAppId(), clientAuthData()); + sinks().startFunctionInstances(tenant, namespace, sinkName, authParams()); } @GET @@ -278,6 +277,6 @@ public List getSinkConfigDefinition( }) @Path("/reloadBuiltInSinks") public void reloadSinks() { - sinks().reloadConnectors(clientAppId(), clientAuthData()); + sinks().reloadConnectors(authParams()); } } diff --git a/pulsar-functions/worker/src/main/java/org/apache/pulsar/functions/worker/rest/api/v3/SourcesApiV3Resource.java b/pulsar-functions/worker/src/main/java/org/apache/pulsar/functions/worker/rest/api/v3/SourcesApiV3Resource.java index a669d4f089dd0..23df423045090 100644 --- a/pulsar-functions/worker/src/main/java/org/apache/pulsar/functions/worker/rest/api/v3/SourcesApiV3Resource.java +++ b/pulsar-functions/worker/src/main/java/org/apache/pulsar/functions/worker/rest/api/v3/SourcesApiV3Resource.java @@ -70,7 +70,7 @@ public void registerSource(final @PathParam("tenant") String tenant, final @FormDataParam("sourceConfig") SourceConfig sourceConfig) { sources().registerSource(tenant, namespace, sourceName, uploadedInputStream, fileDetail, - functionPkgUrl, sourceConfig, clientAppId(), clientAuthData()); + functionPkgUrl, sourceConfig, authParams()); } @@ -87,7 +87,7 @@ public void updateSource(final @PathParam("tenant") String tenant, final @FormDataParam("updateOptions") UpdateOptionsImpl updateOptions) { sources().updateSource(tenant, namespace, sourceName, uploadedInputStream, fileDetail, - functionPkgUrl, sourceConfig, clientAppId(), clientAuthData(), updateOptions); + functionPkgUrl, sourceConfig, authParams(), updateOptions); } @@ -96,7 +96,7 @@ public void updateSource(final @PathParam("tenant") String tenant, public void deregisterSource(final @PathParam("tenant") String tenant, final @PathParam("namespace") String namespace, final @PathParam("sourceName") String sourceName) { - sources().deregisterFunction(tenant, namespace, sourceName, clientAppId(), clientAuthData()); + sources().deregisterFunction(tenant, namespace, sourceName, authParams()); } @GET @@ -106,7 +106,7 @@ public SourceConfig getSourceInfo(final @PathParam("tenant") String tenant, final @PathParam("namespace") String namespace, final @PathParam("sourceName") String sourceName) throws IOException { - return sources().getSourceInfo(tenant, namespace, sourceName); + return sources().getSourceInfo(tenant, namespace, sourceName, authParams()); } @GET @@ -127,8 +127,8 @@ public SourceStatus.SourceInstanceStatus.SourceInstanceStatusData getSourceInsta final @PathParam("namespace") String namespace, final @PathParam("sourceName") String sourceName, final @PathParam("instanceId") String instanceId) throws IOException { - return sources().getSourceInstanceStatus( - tenant, namespace, sourceName, instanceId, uri.getRequestUri(), clientAppId(), clientAuthData()); + return sources().getSourceInstanceStatus(tenant, namespace, sourceName, instanceId, uri.getRequestUri(), + authParams()); } @GET @@ -148,7 +148,7 @@ public SourceStatus getSourceStatus(final @PathParam("tenant") String tenant, final @PathParam("namespace") String namespace, final @PathParam("sourceName") String sourceName) throws IOException { return sources() - .getSourceStatus(tenant, namespace, sourceName, uri.getRequestUri(), clientAppId(), clientAuthData()); + .getSourceStatus(tenant, namespace, sourceName, uri.getRequestUri(), authParams()); } @GET @@ -156,7 +156,7 @@ public SourceStatus getSourceStatus(final @PathParam("tenant") String tenant, @Path("/{tenant}/{namespace}") public List listSources(final @PathParam("tenant") String tenant, final @PathParam("namespace") String namespace) { - return sources().listFunctions(tenant, namespace, clientAppId(), clientAuthData()); + return sources().listFunctions(tenant, namespace, authParams()); } @POST @@ -173,7 +173,7 @@ public void restartSource(final @PathParam("tenant") String tenant, final @PathParam("sourceName") String sourceName, final @PathParam("instanceId") String instanceId) { sources().restartFunctionInstance(tenant, namespace, sourceName, instanceId, this.uri.getRequestUri(), - clientAppId(), clientAuthData()); + authParams()); } @POST @@ -186,7 +186,7 @@ public void restartSource(final @PathParam("tenant") String tenant, public void restartSource(final @PathParam("tenant") String tenant, final @PathParam("namespace") String namespace, final @PathParam("sourceName") String sourceName) { - sources().restartFunctionInstances(tenant, namespace, sourceName, clientAppId(), clientAuthData()); + sources().restartFunctionInstances(tenant, namespace, sourceName, authParams()); } @POST @@ -201,7 +201,7 @@ public void stopSource(final @PathParam("tenant") String tenant, final @PathParam("sourceName") String sourceName, final @PathParam("instanceId") String instanceId) { sources().stopFunctionInstance(tenant, namespace, sourceName, instanceId, this.uri.getRequestUri(), - clientAppId(), clientAuthData()); + authParams()); } @POST @@ -214,7 +214,7 @@ public void stopSource(final @PathParam("tenant") String tenant, public void stopSource(final @PathParam("tenant") String tenant, final @PathParam("namespace") String namespace, final @PathParam("sourceName") String sourceName) { - sources().stopFunctionInstances(tenant, namespace, sourceName, clientAppId(), clientAuthData()); + sources().stopFunctionInstances(tenant, namespace, sourceName, authParams()); } @POST @@ -229,7 +229,7 @@ public void startSource(final @PathParam("tenant") String tenant, final @PathParam("sourceName") String sourceName, final @PathParam("instanceId") String instanceId) { sources().startFunctionInstance(tenant, namespace, sourceName, instanceId, this.uri.getRequestUri(), - clientAppId(), clientAuthData()); + authParams()); } @POST @@ -242,7 +242,7 @@ public void startSource(final @PathParam("tenant") String tenant, public void startSource(final @PathParam("tenant") String tenant, final @PathParam("namespace") String namespace, final @PathParam("sourceName") String sourceName) { - sources().startFunctionInstances(tenant, namespace, sourceName, clientAppId(), clientAuthData()); + sources().startFunctionInstances(tenant, namespace, sourceName, authParams()); } @GET @@ -293,6 +293,6 @@ public List getSourceConfigDefinition( }) @Path("/reloadBuiltInSources") public void reloadSources() { - sources().reloadConnectors(clientAppId(), clientAuthData()); + sources().reloadConnectors(authParams()); } } diff --git a/pulsar-functions/worker/src/main/java/org/apache/pulsar/functions/worker/service/api/Component.java b/pulsar-functions/worker/src/main/java/org/apache/pulsar/functions/worker/service/api/Component.java index fc01ec55b6c4c..770cced1731da 100644 --- a/pulsar-functions/worker/src/main/java/org/apache/pulsar/functions/worker/service/api/Component.java +++ b/pulsar-functions/worker/src/main/java/org/apache/pulsar/functions/worker/service/api/Component.java @@ -22,8 +22,7 @@ import java.net.URI; import java.util.List; import javax.ws.rs.core.StreamingOutput; -import org.apache.pulsar.broker.authentication.AuthenticationDataHttps; -import org.apache.pulsar.broker.authentication.AuthenticationDataSource; +import org.apache.pulsar.broker.authentication.AuthenticationParameters; import org.apache.pulsar.common.functions.FunctionConfig; import org.apache.pulsar.common.functions.FunctionState; import org.apache.pulsar.common.io.ConnectorDefinition; @@ -40,167 +39,55 @@ public interface Component { W worker(); - void deregisterFunction(String tenant, - String namespace, - String componentName, - String clientRole, - AuthenticationDataSource clientAuthenticationDataHttps); - - @Deprecated - default void deregisterFunction(String tenant, - String namespace, - String componentName, - String clientRole, - AuthenticationDataHttps clientAuthenticationDataHttps) { - deregisterFunction( - tenant, - namespace, - componentName, - clientRole, - (AuthenticationDataSource) clientAuthenticationDataHttps); - } - - FunctionConfig getFunctionInfo(String tenant, - String namespace, - String componentName, - String clientRole, - AuthenticationDataSource clientAuthenticationDataHttps); - - void stopFunctionInstance(String tenant, - String namespace, - String componentName, - String instanceId, - URI uri, - String clientRole, - AuthenticationDataSource clientAuthenticationDataHttps); - - void startFunctionInstance(String tenant, - String namespace, - String componentName, - String instanceId, - URI uri, - String clientRole, - AuthenticationDataSource clientAuthenticationDataHttps); - - void restartFunctionInstance(String tenant, - String namespace, - String componentName, - String instanceId, - URI uri, - String clientRole, - AuthenticationDataSource clientAuthenticationDataHttps); - - void startFunctionInstances(String tenant, - String namespace, - String componentName, - String clientRole, - AuthenticationDataSource clientAuthenticationDataHttps); - - void stopFunctionInstances(String tenant, - String namespace, - String componentName, - String clientRole, - AuthenticationDataSource clientAuthenticationDataHttps); - - void restartFunctionInstances(String tenant, - String namespace, - String componentName, - String clientRole, - AuthenticationDataSource clientAuthenticationDataHttps); - - FunctionStatsImpl getFunctionStats(String tenant, - String namespace, - String componentName, - URI uri, - String clientRole, - AuthenticationDataSource clientAuthenticationDataHttps); - - FunctionInstanceStatsDataImpl getFunctionsInstanceStats(String tenant, - String namespace, - String componentName, - String instanceId, - URI uri, - String clientRole, - AuthenticationDataSource clientAuthenticationDataHttps); - - String triggerFunction(String tenant, - String namespace, - String functionName, - String input, - InputStream uploadedInputStream, - String topic, - String clientRole, - AuthenticationDataSource clientAuthenticationDataHttps); - - List listFunctions(String tenant, - String namespace, - String clientRole, - AuthenticationDataSource clientAuthenticationDataHttps); - - FunctionState getFunctionState(String tenant, - String namespace, - String functionName, - String key, - String clientRole, - AuthenticationDataSource clientAuthenticationDataHttps); - - void putFunctionState(String tenant, - String namespace, - String functionName, - String key, - FunctionState state, - String clientRole, - AuthenticationDataSource clientAuthenticationDataHttps); - - void uploadFunction(InputStream uploadedInputStream, - String path, - String clientRole, - AuthenticationDataSource clientAuthenticationDataHttps); - - StreamingOutput downloadFunction(String path, - String clientRole, - AuthenticationDataSource clientAuthenticationDataHttps); - - @Deprecated - default StreamingOutput downloadFunction(String path, - String clientRole, - AuthenticationDataHttps clientAuthenticationDataHttps) { - return downloadFunction(path, clientRole, (AuthenticationDataSource) clientAuthenticationDataHttps); - } - - StreamingOutput downloadFunction(String tenant, - String namespace, - String componentName, - String clientRole, - AuthenticationDataSource clientAuthenticationDataHttps, - boolean transformFunction); - - @Deprecated - default StreamingOutput downloadFunction(String tenant, - String namespace, - String componentName, - String clientRole, - AuthenticationDataSource clientAuthenticationDataHttps) { - return downloadFunction(tenant, namespace, componentName, clientRole, clientAuthenticationDataHttps, false); - } - - @Deprecated - default StreamingOutput downloadFunction(String tenant, - String namespace, - String componentName, - String clientRole, - AuthenticationDataHttps clientAuthenticationDataHttps) { - return downloadFunction( - tenant, - namespace, - componentName, - clientRole, - (AuthenticationDataSource) clientAuthenticationDataHttps, - false); - } + void deregisterFunction(String tenant, String namespace, String componentName, AuthenticationParameters authParams); - List getListOfConnectors(); + FunctionConfig getFunctionInfo(String tenant, String namespace, String componentName, + AuthenticationParameters authParams); + + void stopFunctionInstance(String tenant, String namespace, String componentName, String instanceId, URI uri, + AuthenticationParameters authParams); + + void startFunctionInstance(String tenant, String namespace, String componentName, String instanceId, URI uri, + AuthenticationParameters authParams); + + void restartFunctionInstance(String tenant, String namespace, String componentName, String instanceId, URI uri, + AuthenticationParameters authParams); + + void startFunctionInstances(String tenant, String namespace, String componentName, + AuthenticationParameters authParams); + + void stopFunctionInstances(String tenant, String namespace, String componentName, + AuthenticationParameters authParams); + + void restartFunctionInstances(String tenant, String namespace, String componentName, + AuthenticationParameters authParams); + + FunctionStatsImpl getFunctionStats(String tenant, String namespace, String componentName, URI uri, + AuthenticationParameters authParams); + + FunctionInstanceStatsDataImpl getFunctionsInstanceStats(String tenant, String namespace, String componentName, + String instanceId, URI uri, + AuthenticationParameters authParams); + String triggerFunction(String tenant, String namespace, String functionName, String input, + InputStream uploadedInputStream, String topic, AuthenticationParameters authParams); + + List listFunctions(String tenant, String namespace, AuthenticationParameters authParams); + + FunctionState getFunctionState(String tenant, String namespace, String functionName, String key, + AuthenticationParameters authParams); + + void putFunctionState(String tenant, String namespace, String functionName, String key, FunctionState state, + AuthenticationParameters authParams); + + void uploadFunction(InputStream uploadedInputStream, String path, AuthenticationParameters authParams); + + StreamingOutput downloadFunction(String path, AuthenticationParameters authParams); + + StreamingOutput downloadFunction(String tenant, String namespace, String componentName, + AuthenticationParameters authParams, boolean transformFunction); + + List getListOfConnectors(); - void reloadConnectors(String clientRole, AuthenticationDataSource clientAuthenticationDataHttps); + void reloadConnectors(AuthenticationParameters authParams); } diff --git a/pulsar-functions/worker/src/main/java/org/apache/pulsar/functions/worker/service/api/Functions.java b/pulsar-functions/worker/src/main/java/org/apache/pulsar/functions/worker/service/api/Functions.java index 954ab1d26182f..28bc73fd0a831 100644 --- a/pulsar-functions/worker/src/main/java/org/apache/pulsar/functions/worker/service/api/Functions.java +++ b/pulsar-functions/worker/src/main/java/org/apache/pulsar/functions/worker/service/api/Functions.java @@ -22,8 +22,7 @@ import java.io.InputStream; import java.net.URI; import java.util.List; -import org.apache.pulsar.broker.authentication.AuthenticationDataHttps; -import org.apache.pulsar.broker.authentication.AuthenticationDataSource; +import org.apache.pulsar.broker.authentication.AuthenticationParameters; import org.apache.pulsar.common.functions.FunctionConfig; import org.apache.pulsar.common.functions.FunctionDefinition; import org.apache.pulsar.common.functions.UpdateOptionsImpl; @@ -46,8 +45,7 @@ public interface Functions extends Component { * @param fileDetail A form-data content disposition header * @param functionPkgUrl URL path of the Pulsar Function package * @param functionConfig Configuration of Pulsar Function - * @param clientRole Client role for running the pulsar function - * @param clientAuthenticationDataHttps Authentication status of the http client + * @param authParams the authentication parameters associated with the request */ void registerFunction(String tenant, String namespace, @@ -56,35 +54,7 @@ void registerFunction(String tenant, FormDataContentDisposition fileDetail, String functionPkgUrl, FunctionConfig functionConfig, - String clientRole, - AuthenticationDataSource clientAuthenticationDataHttps); - - /** - * This method uses an incorrect signature 'AuthenticationDataHttps' that prevents the extension of auth status, - * so it is marked as deprecated and kept here only for backward compatibility. Please use the method that accepts - * the signature of the AuthenticationDataSource. - */ - @Deprecated - default void registerFunction(String tenant, - String namespace, - String functionName, - InputStream uploadedInputStream, - FormDataContentDisposition fileDetail, - String functionPkgUrl, - FunctionConfig functionConfig, - String clientRole, - AuthenticationDataHttps clientAuthenticationDataHttps) { - registerFunction( - tenant, - namespace, - functionName, - uploadedInputStream, - fileDetail, - functionPkgUrl, - functionConfig, - clientRole, - (AuthenticationDataSource) clientAuthenticationDataHttps); - } + AuthenticationParameters authParams); /** * Update a function. @@ -95,8 +65,7 @@ default void registerFunction(String tenant, * @param fileDetail A form-data content disposition header * @param functionPkgUrl URL path of the Pulsar Function package * @param functionConfig Configuration of Pulsar Function - * @param clientRole Client role for running the Pulsar Function - * @param clientAuthenticationDataHttps Authentication status of the http client + * @param authParams the authentication parameters associated with the request * @param updateOptions Options while updating the function */ void updateFunction(String tenant, @@ -106,66 +75,31 @@ void updateFunction(String tenant, FormDataContentDisposition fileDetail, String functionPkgUrl, FunctionConfig functionConfig, - String clientRole, - AuthenticationDataSource clientAuthenticationDataHttps, + AuthenticationParameters authParams, UpdateOptionsImpl updateOptions); - /** - * This method uses an incorrect signature 'AuthenticationDataHttps' that prevents the extension of auth status, - * so it is marked as deprecated and kept here only for backward compatibility. Please use the method that accepts - * the signature of the AuthenticationDataSource. - */ - @Deprecated - default void updateFunction(String tenant, - String namespace, - String functionName, - InputStream uploadedInputStream, - FormDataContentDisposition fileDetail, - String functionPkgUrl, - FunctionConfig functionConfig, - String clientRole, - AuthenticationDataHttps clientAuthenticationDataHttps, - UpdateOptionsImpl updateOptions) { - updateFunction( - tenant, - namespace, - functionName, - uploadedInputStream, - fileDetail, - functionPkgUrl, - functionConfig, - clientRole, - (AuthenticationDataSource) clientAuthenticationDataHttps, - updateOptions); - } - void updateFunctionOnWorkerLeader(String tenant, String namespace, String functionName, InputStream uploadedInputStream, boolean delete, URI uri, - String clientRole, - AuthenticationDataSource clientAuthenticationDataHttps); + AuthenticationParameters authParams); FunctionStatus getFunctionStatus(String tenant, String namespace, String componentName, URI uri, - String clientRole, - AuthenticationDataSource clientAuthenticationDataHttps); + AuthenticationParameters authParams); FunctionInstanceStatusData getFunctionInstanceStatus(String tenant, String namespace, String componentName, String instanceId, URI uri, - String clientRole, - AuthenticationDataSource clientAuthenticationDataHttps); + AuthenticationParameters authParams); - void reloadBuiltinFunctions(String clientRole, AuthenticationDataSource clientAuthenticationDataHttps) - throws IOException; + void reloadBuiltinFunctions(AuthenticationParameters authParams) throws IOException; - List getBuiltinFunctions(String clientRole, - AuthenticationDataSource clientAuthenticationDataHttps); + List getBuiltinFunctions(AuthenticationParameters authParams); } diff --git a/pulsar-functions/worker/src/main/java/org/apache/pulsar/functions/worker/service/api/FunctionsV2.java b/pulsar-functions/worker/src/main/java/org/apache/pulsar/functions/worker/service/api/FunctionsV2.java index 6a4be7cdf6f0e..d78d241b3e6a0 100644 --- a/pulsar-functions/worker/src/main/java/org/apache/pulsar/functions/worker/service/api/FunctionsV2.java +++ b/pulsar-functions/worker/src/main/java/org/apache/pulsar/functions/worker/service/api/FunctionsV2.java @@ -23,6 +23,7 @@ import java.net.URI; import java.util.List; import javax.ws.rs.core.Response; +import org.apache.pulsar.broker.authentication.AuthenticationParameters; import org.apache.pulsar.common.io.ConnectorDefinition; import org.apache.pulsar.functions.worker.WorkerService; import org.glassfish.jersey.media.multipart.FormDataContentDisposition; @@ -35,20 +36,20 @@ public interface FunctionsV2 { Response getFunctionInfo(String tenant, String namespace, String functionName, - String clientRole) throws IOException; + AuthenticationParameters authParams) throws IOException; Response getFunctionInstanceStatus(String tenant, - String namespace, - String functionName, - String instanceId, - URI uri, - String clientRole) throws IOException; + String namespace, + String functionName, + String instanceId, + URI uri, + AuthenticationParameters authParams) throws IOException; Response getFunctionStatusV2(String tenant, String namespace, String functionName, URI requestUri, - String clientRole) throws IOException; + AuthenticationParameters authParams) throws IOException; Response registerFunction(String tenant, String namespace, @@ -57,7 +58,8 @@ Response registerFunction(String tenant, FormDataContentDisposition fileDetail, String functionPkgUrl, String functionDetailsJson, - String clientRole); + AuthenticationParameters authParams); + Response updateFunction(String tenant, String namespace, @@ -66,14 +68,12 @@ Response updateFunction(String tenant, FormDataContentDisposition fileDetail, String functionPkgUrl, String functionDetailsJson, - String clientRole); + AuthenticationParameters authParams); - Response deregisterFunction(String tenant, - String namespace, - String functionName, - String clientAppId); + Response deregisterFunction(String tenant, String namespace, String functionName, + AuthenticationParameters authParams); - Response listFunctions(String tenant, String namespace, String clientRole); + Response listFunctions(String tenant, String namespace, AuthenticationParameters authParams); Response triggerFunction(String tenant, String namespace, @@ -81,45 +81,44 @@ Response triggerFunction(String tenant, String triggerValue, InputStream triggerStream, String topic, - String clientRole); + AuthenticationParameters authParams); Response getFunctionState(String tenant, String namespace, String functionName, String key, - String clientRole); - + AuthenticationParameters authParams); Response restartFunctionInstance(String tenant, String namespace, String functionName, String instanceId, URI uri, - String clientRole); + AuthenticationParameters authParams); + Response restartFunctionInstances(String tenant, String namespace, String functionName, - String clientRole); + AuthenticationParameters authParams); Response stopFunctionInstance(String tenant, String namespace, String functionName, String instanceId, URI uri, - String clientRole); + AuthenticationParameters authParams); Response stopFunctionInstances(String tenant, String namespace, String functionName, - String clientRole); + AuthenticationParameters authParams); Response uploadFunction(InputStream uploadedInputStream, String path, - String clientRole); - - Response downloadFunction(String path, String clientRole); + AuthenticationParameters authParams); + Response downloadFunction(String path, AuthenticationParameters authParams); List getListOfConnectors(); diff --git a/pulsar-functions/worker/src/main/java/org/apache/pulsar/functions/worker/service/api/Sinks.java b/pulsar-functions/worker/src/main/java/org/apache/pulsar/functions/worker/service/api/Sinks.java index 2c0dcb82e0dbc..c977f55df4bf3 100644 --- a/pulsar-functions/worker/src/main/java/org/apache/pulsar/functions/worker/service/api/Sinks.java +++ b/pulsar-functions/worker/src/main/java/org/apache/pulsar/functions/worker/service/api/Sinks.java @@ -21,8 +21,7 @@ import java.io.InputStream; import java.net.URI; import java.util.List; -import org.apache.pulsar.broker.authentication.AuthenticationDataHttps; -import org.apache.pulsar.broker.authentication.AuthenticationDataSource; +import org.apache.pulsar.broker.authentication.AuthenticationParameters; import org.apache.pulsar.common.functions.UpdateOptionsImpl; import org.apache.pulsar.common.io.ConfigFieldDefinition; import org.apache.pulsar.common.io.ConnectorDefinition; @@ -46,8 +45,7 @@ public interface Sinks extends Component { * @param fileDetail A form-data content disposition header * @param sinkPkgUrl URL path of the Pulsar Sink package * @param sinkConfig Configuration of Pulsar Sink - * @param clientRole Client role for running the Pulsar Sink - * @param clientAuthenticationDataHttps Authentication status of the http client + * @param authParams the authentication parameters associated with the request */ void registerSink(String tenant, String namespace, @@ -56,35 +54,7 @@ void registerSink(String tenant, FormDataContentDisposition fileDetail, String sinkPkgUrl, SinkConfig sinkConfig, - String clientRole, - AuthenticationDataSource clientAuthenticationDataHttps); - - /** - * This method uses an incorrect signature 'AuthenticationDataHttps' that prevents the extension of auth status, - * so it is marked as deprecated and kept here only for backward compatibility. Please use the method that accepts - * the signature of the AuthenticationDataSource. - */ - @Deprecated - default void registerSink(String tenant, - String namespace, - String sinkName, - InputStream uploadedInputStream, - FormDataContentDisposition fileDetail, - String sinkPkgUrl, - SinkConfig sinkConfig, - String clientRole, - AuthenticationDataHttps clientAuthenticationDataHttps) { - registerSink( - tenant, - namespace, - sinkName, - uploadedInputStream, - fileDetail, - sinkPkgUrl, - sinkConfig, - clientRole, - (AuthenticationDataSource) clientAuthenticationDataHttps); - } + AuthenticationParameters authParams); /** * Update a function. @@ -95,8 +65,7 @@ default void registerSink(String tenant, * @param fileDetail A form-data content disposition header * @param sinkPkgUrl URL path of the Pulsar Sink package * @param sinkConfig Configuration of Pulsar Sink - * @param clientRole Client role for running the Pulsar Sink - * @param clientAuthenticationDataHttps Authentication status of the http client + * @param authParams the authentication parameters associated with the request * @param updateOptions Options while updating the sink */ void updateSink(String tenant, @@ -106,57 +75,26 @@ void updateSink(String tenant, FormDataContentDisposition fileDetail, String sinkPkgUrl, SinkConfig sinkConfig, - String clientRole, - AuthenticationDataSource clientAuthenticationDataHttps, + AuthenticationParameters authParams, UpdateOptionsImpl updateOptions); - /** - * This method uses an incorrect signature 'AuthenticationDataHttps' that prevents the extension of auth status, - * so it is marked as deprecated and kept here only for backward compatibility. Please use the method that accepts - * the signature of the AuthenticationDataSource. - */ - @Deprecated - default void updateSink(String tenant, - String namespace, - String sinkName, - InputStream uploadedInputStream, - FormDataContentDisposition fileDetail, - String sinkPkgUrl, - SinkConfig sinkConfig, - String clientRole, - AuthenticationDataHttps clientAuthenticationDataHttps, - UpdateOptionsImpl updateOptions) { - updateSink( - tenant, - namespace, - sinkName, - uploadedInputStream, - fileDetail, - sinkPkgUrl, - sinkConfig, - clientRole, - (AuthenticationDataSource) clientAuthenticationDataHttps, - updateOptions); - } - SinkInstanceStatusData getSinkInstanceStatus(String tenant, String namespace, String sinkName, String instanceId, URI uri, - String clientRole, - AuthenticationDataSource clientAuthenticationDataHttps); + AuthenticationParameters authParams); SinkStatus getSinkStatus(String tenant, String namespace, String componentName, URI uri, - String clientRole, - AuthenticationDataSource clientAuthenticationDataHttps); + AuthenticationParameters authParams); SinkConfig getSinkInfo(String tenant, String namespace, - String componentName); + String componentName, + AuthenticationParameters authParams); List getSinkList(); diff --git a/pulsar-functions/worker/src/main/java/org/apache/pulsar/functions/worker/service/api/Sources.java b/pulsar-functions/worker/src/main/java/org/apache/pulsar/functions/worker/service/api/Sources.java index 781a25e92cc87..7fe1e56bf9a13 100644 --- a/pulsar-functions/worker/src/main/java/org/apache/pulsar/functions/worker/service/api/Sources.java +++ b/pulsar-functions/worker/src/main/java/org/apache/pulsar/functions/worker/service/api/Sources.java @@ -21,8 +21,7 @@ import java.io.InputStream; import java.net.URI; import java.util.List; -import org.apache.pulsar.broker.authentication.AuthenticationDataHttps; -import org.apache.pulsar.broker.authentication.AuthenticationDataSource; +import org.apache.pulsar.broker.authentication.AuthenticationParameters; import org.apache.pulsar.common.functions.UpdateOptionsImpl; import org.apache.pulsar.common.io.ConfigFieldDefinition; import org.apache.pulsar.common.io.ConnectorDefinition; @@ -46,8 +45,7 @@ public interface Sources extends Component { * @param fileDetail A form-data content disposition header * @param sourcePkgUrl URL path of the Pulsar Source package * @param sourceConfig Configuration of Pulsar Source - * @param clientRole Client role for running the Pulsar Source - * @param clientAuthenticationDataHttps Authentication status of the http client + * @param authParams the authentication parameters associated with the request */ void registerSource(String tenant, String namespace, @@ -56,35 +54,7 @@ void registerSource(String tenant, FormDataContentDisposition fileDetail, String sourcePkgUrl, SourceConfig sourceConfig, - String clientRole, - AuthenticationDataSource clientAuthenticationDataHttps); - - /** - * This method uses an incorrect signature 'AuthenticationDataHttps' that prevents the extension of auth status, - * so it is marked as deprecated and kept here only for backward compatibility. Please use the method that accepts - * the signature of the AuthenticationDataSource. - */ - @Deprecated - default void registerSource(String tenant, - String namespace, - String sourceName, - InputStream uploadedInputStream, - FormDataContentDisposition fileDetail, - String sourcePkgUrl, - SourceConfig sourceConfig, - String clientRole, - AuthenticationDataHttps clientAuthenticationDataHttps) { - registerSource( - tenant, - namespace, - sourceName, - uploadedInputStream, - fileDetail, - sourcePkgUrl, - sourceConfig, - clientRole, - (AuthenticationDataSource) clientAuthenticationDataHttps); - } + AuthenticationParameters authParams); /** * Update a function. @@ -95,8 +65,7 @@ default void registerSource(String tenant, * @param fileDetail A form-data content disposition header * @param sourcePkgUrl URL path of the Pulsar Source package * @param sourceConfig Configuration of Pulsar Source - * @param clientRole Client role for running the Pulsar Source - * @param clientAuthenticationDataHttps Authentication status of the http client + * @param authParams the authentication parameters associated with the request * @param updateOptions Options while updating the source */ void updateSource(String tenant, @@ -106,59 +75,26 @@ void updateSource(String tenant, FormDataContentDisposition fileDetail, String sourcePkgUrl, SourceConfig sourceConfig, - String clientRole, - AuthenticationDataSource clientAuthenticationDataHttps, + AuthenticationParameters authParams, UpdateOptionsImpl updateOptions); - /** - * This method uses an incorrect signature 'AuthenticationDataHttps' that prevents the extension of auth status, - * so it is marked as deprecated and kept here only for backward compatibility. Please use the method that accepts - * the signature of the AuthenticationDataSource. - */ - @Deprecated - default void updateSource(String tenant, - String namespace, - String sourceName, - InputStream uploadedInputStream, - FormDataContentDisposition fileDetail, - String sourcePkgUrl, - SourceConfig sourceConfig, - String clientRole, - AuthenticationDataHttps clientAuthenticationDataHttps, - UpdateOptionsImpl updateOptions) { - updateSource( - tenant, - namespace, - sourceName, - uploadedInputStream, - fileDetail, - sourcePkgUrl, - sourceConfig, - clientRole, - (AuthenticationDataSource) clientAuthenticationDataHttps, - updateOptions); - } - - SourceStatus getSourceStatus(String tenant, String namespace, String componentName, URI uri, - String clientRole, - AuthenticationDataSource clientAuthenticationDataHttps); - + AuthenticationParameters authParams); SourceInstanceStatusData getSourceInstanceStatus(String tenant, String namespace, String sourceName, String instanceId, URI uri, - String clientRole, - AuthenticationDataSource clientAuthenticationDataHttps); + AuthenticationParameters authParams); SourceConfig getSourceInfo(String tenant, String namespace, - String componentName); + String componentName, + AuthenticationParameters authParams); List getSourceList(); diff --git a/pulsar-functions/worker/src/main/java/org/apache/pulsar/functions/worker/service/api/Workers.java b/pulsar-functions/worker/src/main/java/org/apache/pulsar/functions/worker/service/api/Workers.java index a13157ea6476c..420fdcefe8b57 100644 --- a/pulsar-functions/worker/src/main/java/org/apache/pulsar/functions/worker/service/api/Workers.java +++ b/pulsar-functions/worker/src/main/java/org/apache/pulsar/functions/worker/service/api/Workers.java @@ -23,6 +23,7 @@ import java.util.Collection; import java.util.List; import java.util.Map; +import org.apache.pulsar.broker.authentication.AuthenticationParameters; import org.apache.pulsar.client.admin.LongRunningProcessStatus; import org.apache.pulsar.common.functions.WorkerInfo; import org.apache.pulsar.common.io.ConnectorDefinition; @@ -35,25 +36,25 @@ */ public interface Workers { - List getCluster(String clientRole); + List getCluster(AuthenticationParameters authParams); - WorkerInfo getClusterLeader(String clientRole); + WorkerInfo getClusterLeader(AuthenticationParameters authParams); - Map> getAssignments(String clientRole); + Map> getAssignments(AuthenticationParameters authParams); - List getWorkerMetrics(String clientRole); + List getWorkerMetrics(AuthenticationParameters authParams); - List getFunctionsMetrics(String clientRole) throws IOException; + List getFunctionsMetrics(AuthenticationParameters authParams) throws IOException; - List getListOfConnectors(String clientRole); + List getListOfConnectors(AuthenticationParameters authParams); - void rebalance(URI uri, String clientRole); + void rebalance(URI uri, AuthenticationParameters authParams); - void drain(URI uri, String workerId, String clientRole, boolean leaderUri); + void drain(URI uri, String workerId, AuthenticationParameters authParams, boolean leaderUri); - LongRunningProcessStatus getDrainStatus(URI uri, String workerId, String clientRole, + LongRunningProcessStatus getDrainStatus(URI uri, String workerId, AuthenticationParameters authParams, boolean leaderUri); - Boolean isLeaderReady(String clientRole); + boolean isLeaderReady(AuthenticationParameters authParams); } diff --git a/pulsar-functions/worker/src/test/java/org/apache/pulsar/functions/worker/WorkerUtilsTest.java b/pulsar-functions/worker/src/test/java/org/apache/pulsar/functions/worker/WorkerUtilsTest.java index c5e6b4aaded74..0f5fca4a8a5a3 100644 --- a/pulsar-functions/worker/src/test/java/org/apache/pulsar/functions/worker/WorkerUtilsTest.java +++ b/pulsar-functions/worker/src/test/java/org/apache/pulsar/functions/worker/WorkerUtilsTest.java @@ -33,17 +33,20 @@ import java.io.IOException; import java.net.URISyntaxException; import java.net.URL; +import java.util.HashSet; import java.util.concurrent.CompletableFuture; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Supplier; import org.apache.distributedlog.DistributedLogConfiguration; +import org.apache.pulsar.broker.ServiceConfiguration; import org.apache.pulsar.client.api.CompressionType; import org.apache.pulsar.client.api.Producer; import org.apache.pulsar.client.api.ProducerAccessMode; import org.apache.pulsar.client.api.ProducerBuilder; import org.apache.pulsar.client.api.PulsarClient; import org.apache.pulsar.client.api.PulsarClientException; +import org.apache.pulsar.common.configuration.PulsarConfigurationLoader; import org.testng.annotations.Test; public class WorkerUtilsTest { @@ -118,4 +121,15 @@ public void testDLogConfiguration() throws URISyntaxException, IOException { assertEquals(dlogConf.getString("bkc.testKey"), "fakeValue", "The bookkeeper client config mapping should apply."); } + + @Test + public void testProxyRolesInWorkerConfigMapToServiceConfiguration() throws Exception { + URL yamlUrl = getClass().getClassLoader().getResource("test_worker_config.yml"); + WorkerConfig wc = WorkerConfig.load(yamlUrl.toURI().getPath()); + ServiceConfiguration conf = PulsarConfigurationLoader.convertFrom(wc); + HashSet proxyRoles = new HashSet<>(); + proxyRoles.add("proxyA"); + proxyRoles.add("proxyB"); + assertEquals(conf.getProxyRoles(), proxyRoles); + } } diff --git a/pulsar-functions/worker/src/test/java/org/apache/pulsar/functions/worker/rest/api/FunctionsImplTest.java b/pulsar-functions/worker/src/test/java/org/apache/pulsar/functions/worker/rest/api/FunctionsImplTest.java index 998d85683e204..cfe087c78406a 100644 --- a/pulsar-functions/worker/src/test/java/org/apache/pulsar/functions/worker/rest/api/FunctionsImplTest.java +++ b/pulsar-functions/worker/src/test/java/org/apache/pulsar/functions/worker/rest/api/FunctionsImplTest.java @@ -18,7 +18,6 @@ */ package org.apache.pulsar.functions.worker.rest.api; -import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.any; import static org.mockito.Mockito.anyInt; import static org.mockito.Mockito.doReturn; @@ -28,27 +27,39 @@ import static org.mockito.Mockito.when; import static org.testng.Assert.assertFalse; import static org.testng.Assert.assertNotNull; +import static org.testng.Assert.assertThrows; import static org.testng.Assert.assertTrue; import java.io.InputStream; import java.util.Collections; import java.util.HashMap; +import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; +import java.util.Optional; +import java.util.Set; import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ExecutionException; import org.apache.distributedlog.api.namespace.Namespace; import org.apache.pulsar.broker.authentication.AuthenticationDataSource; +import org.apache.pulsar.broker.authentication.AuthenticationParameters; import org.apache.pulsar.broker.authorization.AuthorizationService; +import org.apache.pulsar.broker.resources.NamespaceResources; +import org.apache.pulsar.broker.resources.PulsarResources; +import org.apache.pulsar.broker.resources.TenantResources; import org.apache.pulsar.client.admin.Namespaces; import org.apache.pulsar.client.admin.PulsarAdmin; import org.apache.pulsar.client.admin.PulsarAdminException; import org.apache.pulsar.client.admin.Tenants; import org.apache.pulsar.client.api.PulsarClientException; +import org.apache.pulsar.common.configuration.PulsarConfigurationLoader; import org.apache.pulsar.common.functions.FunctionConfig; +import org.apache.pulsar.common.naming.NamespaceName; +import org.apache.pulsar.common.policies.data.AuthAction; import org.apache.pulsar.common.policies.data.FunctionInstanceStatsImpl; import org.apache.pulsar.common.policies.data.FunctionStatsImpl; +import org.apache.pulsar.common.policies.data.Policies; import org.apache.pulsar.common.policies.data.TenantInfo; +import org.apache.pulsar.common.util.RestException; import org.apache.pulsar.functions.api.Context; import org.apache.pulsar.functions.instance.InstanceConfig; import org.apache.pulsar.functions.instance.JavaInstanceRunnable; @@ -94,6 +105,7 @@ public String process(String input, Context context) { private static final int parallelism = 1; private static final String workerId = "worker-0"; private static final String superUser = "superUser"; + private static final String proxyUser = "proxyUser"; private PulsarWorkerService mockedWorkerService; private PulsarAdmin mockedPulsarAdmin; @@ -196,7 +208,7 @@ public void cleanup() { @Test public void testStatusEmpty() { - assertNotNull(this.resource.getFunctionInstanceStatus(tenant, namespace, function, "0", null, null, null)); + assertNotNull(this.resource.getFunctionInstanceStatus(tenant, namespace, function, "0", null, null)); } @Test @@ -231,83 +243,80 @@ public void testMetricsEmpty() throws PulsarClientException { assertNotNull(functionStats.calculateOverall()); } + // Suppress the deprecation warnings until we actually remove the deprecated method + @SuppressWarnings("deprecation") @Test - public void testIsAuthorizedRole() throws PulsarAdminException, InterruptedException, ExecutionException { + public void testIsAuthorizedRole() throws Exception { - TenantInfo tenantInfo = TenantInfo.builder().build(); AuthenticationDataSource authenticationDataSource = mock(AuthenticationDataSource.class); FunctionsImpl functionImpl = spy(new FunctionsImpl(() -> mockedWorkerService)); - AuthorizationService authorizationService = mock(AuthorizationService.class); - doReturn(authorizationService).when(mockedWorkerService).getAuthorizationService(); WorkerConfig workerConfig = new WorkerConfig(); workerConfig.setAuthorizationEnabled(true); - workerConfig.setSuperUserRoles(Collections.singleton(superUser)); + HashSet superUsers = new HashSet<>(); + superUsers.add(superUser); + superUsers.add(proxyUser); + workerConfig.setSuperUserRoles(superUsers); + workerConfig.setProxyRoles(Collections.singleton(proxyUser)); + // TODO remove mocking by relying on TestPulsarResources. Can't do now because this commit needs to be + // cherry picked back. + PulsarResources pulsarResources = mock(PulsarResources.class); + TenantResources tenantResources = mock(TenantResources.class); + when(pulsarResources.getTenantResources()).thenReturn(tenantResources); + TenantInfo tenantInfo = TenantInfo.builder().adminRoles(Collections.singleton("tenant-admin")).build(); + when(tenantResources.getTenantAsync("test-tenant")) + .thenReturn(CompletableFuture.completedFuture(Optional.of(tenantInfo))); + NamespaceResources namespaceResources = mock(NamespaceResources.class); + when(pulsarResources.getNamespaceResources()).thenReturn(namespaceResources); + Policies p = new Policies(); + p.auth_policies.getNamespaceAuthentication().put("test-function-user", Set.of(AuthAction.functions)); + when(namespaceResources.getPoliciesAsync(NamespaceName.get("test-tenant/test-ns"))) + .thenReturn(CompletableFuture.completedFuture(Optional.of(p))); + + AuthorizationService authorizationService = new AuthorizationService( + PulsarConfigurationLoader.convertFrom(workerConfig), pulsarResources); doReturn(workerConfig).when(mockedWorkerService).getWorkerConfig(); + doReturn(authorizationService).when(mockedWorkerService).getAuthorizationService(); // test super user - assertTrue(functionImpl.isAuthorizedRole("test-tenant", "test-ns", superUser, authenticationDataSource)); - - // test pulsar super user - final String pulsarSuperUser = "pulsarSuperUser"; - when(authorizationService.isSuperUser(eq(pulsarSuperUser), any())) - .thenReturn(CompletableFuture.completedFuture(true)); - assertTrue(functionImpl.isAuthorizedRole("test-tenant", "test-ns", pulsarSuperUser, authenticationDataSource)); - assertTrue(functionImpl.isSuperUser(pulsarSuperUser, null)); + assertTrue(functionImpl.isAuthorizedRole("test-tenant", "test-ns", superUser, + authenticationDataSource)); + assertTrue(functionImpl.isSuperUser(superUser, null)); - // test normal user - functionImpl = spy(new FunctionsImpl(() -> mockedWorkerService)); - doReturn(false).when(functionImpl).allowFunctionOps(any(), any(), any()); - Tenants tenants = mock(Tenants.class); - when(tenants.getTenantInfo(any())).thenReturn(tenantInfo); - PulsarAdmin admin = mock(PulsarAdmin.class); - when(admin.tenants()).thenReturn(tenants); - when(this.mockedWorkerService.getBrokerAdmin()).thenReturn(admin); - when(authorizationService.isTenantAdmin("test-tenant", "test-user", tenantInfo, authenticationDataSource)) - .thenReturn(CompletableFuture.completedFuture(false)); - when(authorizationService.isSuperUser(eq("test-user"), any())) - .thenReturn(CompletableFuture.completedFuture(false)); - assertFalse(functionImpl.isAuthorizedRole("test-tenant", "test-ns", "test-user", authenticationDataSource)); + // test normal user with no permissions + assertFalse(functionImpl.isAuthorizedRole("test-tenant", "test-ns", "test-non-admin-user", + authenticationDataSource)); // if user is tenant admin - functionImpl = spy(new FunctionsImpl(() -> mockedWorkerService)); - doReturn(false).when(functionImpl).allowFunctionOps(any(), any(), any()); - tenants = mock(Tenants.class); - tenantInfo = TenantInfo.builder().adminRoles(Collections.singleton("test-user")).build(); - when(tenants.getTenantInfo(any())).thenReturn(tenantInfo); - - admin = mock(PulsarAdmin.class); - when(admin.tenants()).thenReturn(tenants); - when(this.mockedWorkerService.getBrokerAdmin()).thenReturn(admin); - when(authorizationService.isTenantAdmin("test-tenant", "test-user", tenantInfo, authenticationDataSource)) - .thenReturn(CompletableFuture.completedFuture(true)); - when(authorizationService.isSuperUser("test-user", authenticationDataSource)) - .thenReturn(CompletableFuture.completedFuture(false)); - assertTrue(functionImpl.isAuthorizedRole("test-tenant", "test-ns", "test-user", authenticationDataSource)); + assertTrue(functionImpl.isAuthorizedRole("test-tenant", "test-ns", "tenant-admin", + authenticationDataSource)); // test user allow function action - functionImpl = spy(new FunctionsImpl(() -> mockedWorkerService)); - doReturn(true).when(functionImpl).allowFunctionOps(any(), any(), any()); - tenants = mock(Tenants.class); - tenantInfo = TenantInfo.builder().build(); - when(tenants.getTenantInfo(any())).thenReturn(tenantInfo); - - admin = mock(PulsarAdmin.class); - when(admin.tenants()).thenReturn(tenants); - when(this.mockedWorkerService.getBrokerAdmin()).thenReturn(admin); - when(authorizationService.isTenantAdmin("test-tenant", "test-user", tenantInfo, authenticationDataSource)) - .thenReturn(CompletableFuture.completedFuture(true)); - assertTrue(functionImpl.isAuthorizedRole("test-tenant", "test-ns", "test-user", authenticationDataSource)); + assertTrue(functionImpl.isAuthorizedRole("test-tenant", "test-ns", "test-function-user", + authenticationDataSource)); // test role is null - functionImpl = spy(new FunctionsImpl(() -> mockedWorkerService)); - doReturn(true).when(functionImpl).allowFunctionOps(any(), any(), any()); - tenants = mock(Tenants.class); - when(tenants.getTenantInfo(any())).thenReturn(TenantInfo.builder().build()); - - admin = mock(PulsarAdmin.class); - when(admin.tenants()).thenReturn(tenants); - when(this.mockedWorkerService.getBrokerAdmin()).thenReturn(admin); - assertFalse(functionImpl.isAuthorizedRole("test-tenant", "test-ns", null, authenticationDataSource)); + assertThrows(RestException.class, () -> functionImpl.isAuthorizedRole("test-tenant", + "test-ns", null, authenticationDataSource)); + + // test proxy user with no original principal + assertFalse(functionImpl.isAuthorizedRole("test-tenant", "test-ns", + AuthenticationParameters.builder().clientRole(proxyUser).build())); + + // test proxy user with tenant admin original principal + assertTrue(functionImpl.isAuthorizedRole("test-tenant", "test-ns", + AuthenticationParameters.builder().clientRole(proxyUser).originalPrincipal("tenant-admin").build())); + + // test proxy user with non admin user + assertFalse(functionImpl.isAuthorizedRole("test-tenant", "test-ns", + AuthenticationParameters.builder().clientRole(proxyUser).originalPrincipal("test-non-admin-user").build())); + + // test proxy user with allow function action + assertTrue(functionImpl.isAuthorizedRole("test-tenant", "test-ns", + AuthenticationParameters.builder().clientRole(proxyUser).originalPrincipal("test-function-user").build())); + + // test non-proxy user passing original principal + assertFalse(functionImpl.isAuthorizedRole("test-tenant", "test-ns", + AuthenticationParameters.builder().clientRole("nobody").originalPrincipal("test-non-admin-user").build())); } @Test @@ -320,20 +329,19 @@ public void testIsSuperUser() throws PulsarAdminException { workerConfig.setAuthorizationEnabled(true); workerConfig.setSuperUserRoles(Collections.singleton(superUser)); doReturn(workerConfig).when(mockedWorkerService).getWorkerConfig(); - when(authorizationService.isSuperUser(anyString(), any())) + when(authorizationService.isSuperUser(any(AuthenticationParameters.class))) .thenAnswer((invocationOnMock) -> { - String role = invocationOnMock.getArgument(0, String.class); + String role = invocationOnMock.getArgument(0, AuthenticationParameters.class).getClientRole(); return CompletableFuture.completedFuture(superUser.equals(role)); }); - AuthenticationDataSource authenticationDataSource = mock(AuthenticationDataSource.class); assertTrue(functionImpl.isSuperUser(superUser, null)); assertFalse(functionImpl.isSuperUser("normal-user", null)); assertFalse(functionImpl.isSuperUser(null, null)); // test super roles is null and it's not a pulsar super user - when(authorizationService.isSuperUser(superUser, null)) + when(authorizationService.isSuperUser(AuthenticationParameters.builder().clientRole(superUser).build())) .thenReturn(CompletableFuture.completedFuture(false)); functionImpl = spy(new FunctionsImpl(() -> mockedWorkerService)); workerConfig = new WorkerConfig(); @@ -342,10 +350,10 @@ public void testIsSuperUser() throws PulsarAdminException { assertFalse(functionImpl.isSuperUser(superUser, null)); // test super role is null but the auth datasource contains superuser - when(authorizationService.isSuperUser(anyString(), any(AuthenticationDataSource.class))) + when(authorizationService.isSuperUser(any(AuthenticationParameters.class))) .thenAnswer((invocationOnMock -> { - AuthenticationDataSource authData = invocationOnMock.getArgument(1, AuthenticationDataSource.class); - String user = authData.getHttpHeader("mockedUser"); + AuthenticationParameters authData = invocationOnMock.getArgument(0, AuthenticationParameters.class); + String user = authData.getClientAuthenticationDataSource().getHttpHeader("mockedUser"); return CompletableFuture.completedFuture(superUser.equals(user)); })); AuthenticationDataSource authData = mock(AuthenticationDataSource.class); diff --git a/pulsar-functions/worker/src/test/java/org/apache/pulsar/functions/worker/rest/api/WorkerImplTest.java b/pulsar-functions/worker/src/test/java/org/apache/pulsar/functions/worker/rest/api/WorkerImplTest.java new file mode 100644 index 0000000000000..eb7228383552c --- /dev/null +++ b/pulsar-functions/worker/src/test/java/org/apache/pulsar/functions/worker/rest/api/WorkerImplTest.java @@ -0,0 +1,120 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF 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.apache.pulsar.functions.worker.rest.api; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertTrue; +import static org.testng.Assert.fail; +import java.util.HashSet; +import org.apache.pulsar.broker.authentication.AuthenticationParameters; +import org.apache.pulsar.broker.authorization.AuthorizationService; +import org.apache.pulsar.broker.resources.PulsarResources; +import org.apache.pulsar.common.configuration.PulsarConfigurationLoader; +import org.apache.pulsar.common.util.RestException; +import org.apache.pulsar.functions.worker.LeaderService; +import org.apache.pulsar.functions.worker.MetricsGenerator; +import org.apache.pulsar.functions.worker.PulsarWorkerService; +import org.apache.pulsar.functions.worker.WorkerConfig; +import org.testng.Assert; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +/** + * Unit test of {@link WorkerImpl} to ensure that all methods properly fail if the {@link AuthenticationParameters} + * do not represent a superuser. + */ +public class WorkerImplTest { + WorkerImpl worker; + + @DataProvider(name = "authParamsForNonSuperusers") + public Object[][] partitionedTopicProvider() { + return new Object[][] { + { AuthenticationParameters.builder().build() }, // all fields null should fail + { AuthenticationParameters.builder().clientRole("user").build() }, // Not superuser, no proxy + { AuthenticationParameters.builder() + .clientRole("proxySuperuser").build() }, // Proxy role needs original principal + { AuthenticationParameters.builder() + .clientRole("proxyNotSuperuser").build() }, // Proxy role needs original principal + { AuthenticationParameters.builder().clientRole("proxyNotSuperuser") + .originalPrincipal("superuser").build() }, // Proxy is not superuser + { AuthenticationParameters.builder().clientRole("proxyNotSuperuser") + .originalPrincipal("user").build() }, // Neither proxy nor original principal is superuser + { AuthenticationParameters.builder().clientRole("proxySuperuser") + .originalPrincipal("user").build() }, // Original principal is not superuser + { AuthenticationParameters.builder().clientRole("user") + .originalPrincipal("superuser").build() }, // User is not a proxyRole + { AuthenticationParameters.builder().clientRole("superuser2") + .originalPrincipal("superuser").build() }, // Both are superusers, but client is not a proxyRole + }; + } + + @BeforeClass + void setup() throws Exception { + WorkerConfig config = new WorkerConfig(); + config.setPulsarFunctionsCluster("testThrowIfNotSuperUserFailures"); + config.setAuthorizationEnabled(true); + HashSet proxyRoles = new HashSet<>(); + proxyRoles.add("proxySuperuser"); + proxyRoles.add("proxyNotSuperuser"); + HashSet superUserRoles = new HashSet<>(); + superUserRoles.add("superuser"); + superUserRoles.add("superuser2"); + superUserRoles.add("proxySuperuser"); + config.setSuperUserRoles(superUserRoles); + config.setProxyRoles(proxyRoles); + AuthorizationService authorizationService = new AuthorizationService( + PulsarConfigurationLoader.convertFrom(config), mock(PulsarResources.class)); + PulsarWorkerService pulsarWorkerService = mock(PulsarWorkerService.class); + when(pulsarWorkerService.getWorkerConfig()).thenReturn(config); + when(pulsarWorkerService.getAuthorizationService()).thenReturn(authorizationService); + when(pulsarWorkerService.isInitialized()).thenReturn(true); + when(pulsarWorkerService.getMetricsGenerator()).thenReturn(mock(MetricsGenerator.class)); + LeaderService leaderService = mock(LeaderService.class); + when(leaderService.isLeader()).thenReturn(true); + when(pulsarWorkerService.getLeaderService()).thenReturn(leaderService); + worker = new WorkerImpl(() -> pulsarWorkerService); + } + + @Test(dataProvider = "authParamsForNonSuperusers") + public void ensureNonSuperuserCombinationsFailAuthorization(AuthenticationParameters authParams) throws Throwable { + assertThrowsRestException401(() -> worker.getCluster(authParams)); + assertThrowsRestException401(() -> worker.getClusterLeader(authParams)); + assertThrowsRestException401(() -> worker.getAssignments(authParams)); + assertThrowsRestException401(() -> worker.getWorkerMetrics(authParams)); + assertThrowsRestException401(() -> worker.getFunctionsMetrics(authParams)); + assertThrowsRestException401(() -> worker.getListOfConnectors(authParams)); + assertThrowsRestException401(() -> worker.rebalance(null, authParams)); + assertThrowsRestException401(() -> worker.drain(null, "test", authParams, false)); + assertThrowsRestException401(() -> worker.getDrainStatus(null, "test", authParams, false)); + // This endpoint is not protected + assertTrue(worker.isLeaderReady(authParams)); + } + + private void assertThrowsRestException401(Assert.ThrowingRunnable runnable) throws Throwable { + try { + runnable.run(); + fail("Should have thrown RestException"); + } catch (RestException e) { + assertEquals(e.getResponse().getStatus(), 401); + } + } +} diff --git a/pulsar-functions/worker/src/test/java/org/apache/pulsar/functions/worker/rest/api/v2/FunctionApiV2ResourceTest.java b/pulsar-functions/worker/src/test/java/org/apache/pulsar/functions/worker/rest/api/v2/FunctionApiV2ResourceTest.java index 80b626165a5d8..32a104c576993 100644 --- a/pulsar-functions/worker/src/test/java/org/apache/pulsar/functions/worker/rest/api/v2/FunctionApiV2ResourceTest.java +++ b/pulsar-functions/worker/src/test/java/org/apache/pulsar/functions/worker/rest/api/v2/FunctionApiV2ResourceTest.java @@ -53,6 +53,7 @@ import org.apache.distributedlog.api.namespace.Namespace; import org.apache.logging.log4j.Level; import org.apache.logging.log4j.core.config.Configurator; +import org.apache.pulsar.broker.authentication.AuthenticationParameters; import org.apache.pulsar.client.admin.Functions; import org.apache.pulsar.client.admin.Namespaces; import org.apache.pulsar.client.admin.PulsarAdmin; @@ -542,7 +543,7 @@ private void testRegisterFunctionMissingArguments( details, functionPkgUrl, JsonFormat.printer().print(FunctionConfigUtils.convert(functionConfig, (ClassLoader) null)), - null); + AuthenticationParameters.builder().build()); } catch (InvalidProtocolBufferException e) { throw new RuntimeException(e); } @@ -560,7 +561,7 @@ private void registerDefaultFunction() { mockedFormData, null, JsonFormat.printer().print(FunctionConfigUtils.convert(functionConfig, (ClassLoader) null)), - null); + AuthenticationParameters.builder().build()); } catch (InvalidProtocolBufferException e) { throw new RuntimeException(e); } @@ -943,7 +944,7 @@ private void testUpdateFunctionMissingArguments( details, null, JsonFormat.printer().print(FunctionConfigUtils.convert(functionConfig, (ClassLoader) null)), - null); + AuthenticationParameters.builder().build()); } catch (InvalidProtocolBufferException e) { throw new RuntimeException(e); } @@ -971,7 +972,7 @@ private void updateDefaultFunction() { mockedFormData, null, JsonFormat.printer().print(FunctionConfigUtils.convert(functionConfig, (ClassLoader) null)), - null); + AuthenticationParameters.builder().build()); } catch (InvalidProtocolBufferException e) { throw new RuntimeException(e); } @@ -1048,7 +1049,7 @@ public void testUpdateFunctionWithUrl() throws Exception { null, filePackageUrl, JsonFormat.printer().print(FunctionConfigUtils.convert(functionConfig, (ClassLoader) null)), - null); + AuthenticationParameters.builder().build()); } catch (InvalidProtocolBufferException e) { throw new RuntimeException(e); } @@ -1144,7 +1145,7 @@ private void testDeregisterFunctionMissingArguments( tenant, namespace, function, - null); + AuthenticationParameters.builder().build()); } private void deregisterDefaultFunction() { @@ -1152,7 +1153,7 @@ private void deregisterDefaultFunction() { tenant, namespace, function, - null); + AuthenticationParameters.builder().build()); } @Test(expectedExceptions = RestException.class, expectedExceptionsMessageRegExp = "Function test-function doesn't exist") @@ -1257,7 +1258,8 @@ private void testGetFunctionMissingArguments( resource.getFunctionInfo( tenant, namespace, - function, null + function, + AuthenticationParameters.builder().build() ); } @@ -1266,7 +1268,8 @@ private FunctionDetails getDefaultFunctionInfo() throws IOException { String json = (String) resource.getFunctionInfo( tenant, namespace, - function, null + function, + AuthenticationParameters.builder().build() ).getEntity(); FunctionDetails.Builder functionDetailsBuilder = FunctionDetails.newBuilder(); mergeJson(json, functionDetailsBuilder); @@ -1353,7 +1356,8 @@ private void testListFunctionsMissingArguments( ) { resource.listFunctions( tenant, - namespace, null + namespace, + AuthenticationParameters.builder().build() ); } @@ -1361,7 +1365,8 @@ private void testListFunctionsMissingArguments( private List listDefaultFunctions() { return new Gson().fromJson((String) resource.listFunctions( tenant, - namespace, null + namespace, + AuthenticationParameters.builder().build() ).getEntity(), List.class); } @@ -1390,7 +1395,8 @@ public void testDownloadFunctionHttpUrl() throws Exception { "https://repo1.maven.org/maven2/org/apache/pulsar/pulsar-common/2.4.2/pulsar-common-2.4.2.jar"; String testDir = FunctionApiV2ResourceTest.class.getProtectionDomain().getCodeSource().getLocation().getPath(); FunctionsImplV2 function = new FunctionsImplV2(() -> mockedWorkerService); - StreamingOutput streamOutput = (StreamingOutput) function.downloadFunction(jarHttpUrl, null).getEntity(); + StreamingOutput streamOutput = (StreamingOutput) function.downloadFunction(jarHttpUrl, + AuthenticationParameters.builder().build()).getEntity(); File pkgFile = new File(testDir, UUID.randomUUID().toString()); OutputStream output = new FileOutputStream(pkgFile); streamOutput.write(output); @@ -1407,8 +1413,8 @@ public void testDownloadFunctionFile() throws Exception { String fileLocation = file.getAbsolutePath().replace('\\', '/'); String testDir = FunctionApiV2ResourceTest.class.getProtectionDomain().getCodeSource().getLocation().getPath(); FunctionsImplV2 function = new FunctionsImplV2(() -> mockedWorkerService); - StreamingOutput streamOutput = - (StreamingOutput) function.downloadFunction("file:///" + fileLocation, null).getEntity(); + StreamingOutput streamOutput = (StreamingOutput) function.downloadFunction("file:///" + fileLocation, + AuthenticationParameters.builder().build()).getEntity(); File pkgFile = new File(testDir, UUID.randomUUID().toString()); OutputStream output = new FileOutputStream(pkgFile); streamOutput.write(output); @@ -1440,7 +1446,8 @@ public void testRegisterFunctionFileUrlWithValidSinkClass() throws Exception { functionConfig.setOutputSerdeClassName(outputSerdeClassName); try { resource.registerFunction(tenant, namespace, function, null, null, filePackageUrl, - JsonFormat.printer().print(FunctionConfigUtils.convert(functionConfig, (ClassLoader) null)), null); + JsonFormat.printer().print(FunctionConfigUtils.convert(functionConfig, (ClassLoader) null)), + AuthenticationParameters.builder().build()); } catch (InvalidProtocolBufferException e) { throw new RuntimeException(e); } @@ -1474,7 +1481,8 @@ public void testRegisterFunctionWithConflictingFields() throws Exception { functionConfig.setOutputSerdeClassName(outputSerdeClassName); try { resource.registerFunction(actualTenant, actualNamespace, actualName, null, null, filePackageUrl, - JsonFormat.printer().print(FunctionConfigUtils.convert(functionConfig, (ClassLoader) null)), null); + JsonFormat.printer().print(FunctionConfigUtils.convert(functionConfig, (ClassLoader) null)), + AuthenticationParameters.builder().build()); } catch (InvalidProtocolBufferException e) { throw new RuntimeException(e); } diff --git a/pulsar-functions/worker/src/test/java/org/apache/pulsar/functions/worker/rest/api/v3/FunctionApiV3ResourceTest.java b/pulsar-functions/worker/src/test/java/org/apache/pulsar/functions/worker/rest/api/v3/FunctionApiV3ResourceTest.java index c34b26180f876..337999bf2ad5e 100644 --- a/pulsar-functions/worker/src/test/java/org/apache/pulsar/functions/worker/rest/api/v3/FunctionApiV3ResourceTest.java +++ b/pulsar-functions/worker/src/test/java/org/apache/pulsar/functions/worker/rest/api/v3/FunctionApiV3ResourceTest.java @@ -49,6 +49,7 @@ import org.apache.distributedlog.api.namespace.Namespace; import org.apache.logging.log4j.Level; import org.apache.logging.log4j.core.config.Configurator; +import org.apache.pulsar.broker.authentication.AuthenticationParameters; import org.apache.pulsar.client.admin.Functions; import org.apache.pulsar.client.admin.Namespaces; import org.apache.pulsar.client.admin.Packages; @@ -571,7 +572,7 @@ private void testRegisterFunctionMissingArguments( details, functionPkgUrl, functionConfig, - null, null); + null); } @@ -585,7 +586,7 @@ public void testMissingFunctionConfig() { mockedFormData, null, null, - null, null); + null); } @Test(expectedExceptions = RestException.class, expectedExceptionsMessageRegExp = "Function config is not provided") @@ -600,7 +601,7 @@ public void testUpdateMissingFunctionConfig() { mockedFormData, null, null, - null, null, null); + null, null); } private void registerDefaultFunction() { @@ -617,7 +618,7 @@ private void registerDefaultFunctionWithPackageUrl(String packageUrl) { mockedFormData, packageUrl, functionConfig, - null, null); + null); } @Test(expectedExceptions = RestException.class, expectedExceptionsMessageRegExp = "Function test-function already exists") @@ -795,7 +796,7 @@ public void testRegisterFunctionSuccessK8sNoUpload() throws Exception { mockedFormData, null, functionConfig, - null, null); + null); } } @@ -852,7 +853,7 @@ public void testRegisterFunctionSuccessK8sWithUpload() throws Exception { mockedFormData, null, functionConfig, - null, null); + null); Assert.fail(); } catch (RuntimeException e) { Assert.assertEquals(e.getMessage(), injectedErrMsg); @@ -1115,7 +1116,7 @@ private void testUpdateFunctionMissingArguments( details, null, functionConfig, - null, null, null); + null, null); } @@ -1143,7 +1144,7 @@ private void updateDefaultFunctionWithPackageUrl(String packageUrl) { mockedFormData, packageUrl, functionConfig, - null, null, null); + null, null); } @Test(expectedExceptions = RestException.class, expectedExceptionsMessageRegExp = "Function test-function doesn't exist") @@ -1217,7 +1218,7 @@ public void testUpdateFunctionWithUrl() { null, filePackageUrl, functionConfig, - null, null, null); + null, null); } @@ -1331,7 +1332,7 @@ private void testDeregisterFunctionMissingArguments( tenant, namespace, function, - null, null); + null); } private void deregisterDefaultFunction() { @@ -1339,7 +1340,7 @@ private void deregisterDefaultFunction() { tenant, namespace, function, - null, null); + null); } @Test(expectedExceptions = RestException.class, expectedExceptionsMessageRegExp = "Function test-function doesn't exist") @@ -1444,7 +1445,7 @@ private void testGetFunctionMissingArguments( resource.getFunctionInfo( tenant, namespace, - function,null,null + function,null ); } @@ -1454,7 +1455,6 @@ private FunctionConfig getDefaultFunctionInfo() { tenant, namespace, function, - null, null ); } @@ -1539,7 +1539,7 @@ private void testListFunctionsMissingArguments( ) { resource.listFunctions( tenant, - namespace,null,null + namespace,null ); } @@ -1547,7 +1547,7 @@ private void testListFunctionsMissingArguments( private List listDefaultFunctions() { return resource.listFunctions( tenant, - namespace,null,null + namespace,null ); } @@ -1604,7 +1604,7 @@ public void testDownloadFunctionHttpUrl() throws Exception { "https://repo1.maven.org/maven2/org/apache/pulsar/pulsar-common/2.4.2/pulsar-common-2.4.2.jar"; String testDir = FunctionApiV3ResourceTest.class.getProtectionDomain().getCodeSource().getLocation().getPath(); - StreamingOutput streamOutput = resource.downloadFunction(jarHttpUrl, null, null); + StreamingOutput streamOutput = resource.downloadFunction(jarHttpUrl, null); File pkgFile = new File(testDir, UUID.randomUUID().toString()); OutputStream output = new FileOutputStream(pkgFile); streamOutput.write(output); @@ -1619,7 +1619,7 @@ public void testDownloadFunctionFile() throws Exception { String fileLocation = file.getAbsolutePath().replace('\\', '/'); String testDir = FunctionApiV3ResourceTest.class.getProtectionDomain().getCodeSource().getLocation().getPath(); - StreamingOutput streamOutput = resource.downloadFunction("file:///" + fileLocation, null, null); + StreamingOutput streamOutput = resource.downloadFunction("file:///" + fileLocation, null); File pkgFile = new File(testDir, UUID.randomUUID().toString()); OutputStream output = new FileOutputStream(pkgFile); streamOutput.write(output); @@ -1643,7 +1643,7 @@ public void testDownloadFunctionBuiltinConnector() throws Exception { when(connectorsManager.getConnector("cassandra")).thenReturn(connector); when(mockedWorkerService.getConnectorsManager()).thenReturn(connectorsManager); - StreamingOutput streamOutput = resource.downloadFunction("builtin://cassandra", null, null); + StreamingOutput streamOutput = resource.downloadFunction("builtin://cassandra", null); File pkgFile = new File(testDir, UUID.randomUUID().toString()); OutputStream output = new FileOutputStream(pkgFile); @@ -1672,7 +1672,7 @@ public void testDownloadFunctionBuiltinFunction() throws Exception { when(mockedWorkerService.getConnectorsManager()).thenReturn(mock(ConnectorsManager.class)); when(mockedWorkerService.getFunctionsManager()).thenReturn(functionsManager); - StreamingOutput streamOutput = resource.downloadFunction("builtin://exclamation", null, null); + StreamingOutput streamOutput = resource.downloadFunction("builtin://exclamation", null); File pkgFile = new File(testDir, UUID.randomUUID().toString()); OutputStream output = new FileOutputStream(pkgFile); @@ -1707,7 +1707,8 @@ public void testDownloadFunctionBuiltinConnectorByName() throws Exception { when(connectorsManager.getConnector("cassandra")).thenReturn(connector); when(mockedWorkerService.getConnectorsManager()).thenReturn(connectorsManager); - StreamingOutput streamOutput = resource.downloadFunction(tenant, namespace, function, null, null, false); + StreamingOutput streamOutput = resource.downloadFunction(tenant, namespace, function, + AuthenticationParameters.builder().build(), false); File pkgFile = new File(testDir, UUID.randomUUID().toString()); OutputStream output = new FileOutputStream(pkgFile); streamOutput.write(output); @@ -1740,7 +1741,8 @@ public void testDownloadFunctionBuiltinFunctionByName() throws Exception { when(mockedWorkerService.getConnectorsManager()).thenReturn(mock(ConnectorsManager.class)); when(mockedWorkerService.getFunctionsManager()).thenReturn(functionsManager); - StreamingOutput streamOutput = resource.downloadFunction(tenant, namespace, function, null, null, false); + StreamingOutput streamOutput = resource.downloadFunction(tenant, namespace, function, + AuthenticationParameters.builder().build(), false); File pkgFile = new File(testDir, UUID.randomUUID().toString()); OutputStream output = new FileOutputStream(pkgFile); streamOutput.write(output); @@ -1774,7 +1776,8 @@ public void testDownloadTransformFunctionByName() throws Exception { when(mockedWorkerService.getConnectorsManager()).thenReturn(mock(ConnectorsManager.class)); when(mockedWorkerService.getFunctionsManager()).thenReturn(functionsManager); - StreamingOutput streamOutput = resource.downloadFunction(tenant, namespace, function, null, null, true); + StreamingOutput streamOutput = resource.downloadFunction(tenant, namespace, function, + AuthenticationParameters.builder().build(), true); File pkgFile = new File(testDir, UUID.randomUUID().toString()); OutputStream output = new FileOutputStream(pkgFile); streamOutput.write(output); @@ -1804,7 +1807,7 @@ public void testRegisterFunctionFileUrlWithValidSinkClass() throws Exception { functionConfig.setCustomSerdeInputs(topicsToSerDeClassName); functionConfig.setOutput(outputTopic); functionConfig.setOutputSerdeClassName(outputSerdeClassName); - resource.registerFunction(tenant, namespace, function, null, null, filePackageUrl, functionConfig, null, null); + resource.registerFunction(tenant, namespace, function, null, null, filePackageUrl, functionConfig, null); } @@ -1834,7 +1837,7 @@ public void testRegisterFunctionWithConflictingFields() throws Exception { functionConfig.setOutput(outputTopic); functionConfig.setOutputSerdeClassName(outputSerdeClassName); resource.registerFunction(actualTenant, actualNamespace, actualName, null, null, filePackageUrl, functionConfig, - null, null); + null); } @Test(expectedExceptions = RestException.class, expectedExceptionsMessageRegExp = "Function language runtime is either not set or cannot be determined") @@ -1856,7 +1859,7 @@ public void testCreateFunctionWithoutSettingRuntime() throws Exception { functionConfig.setCustomSerdeInputs(topicsToSerDeClassName); functionConfig.setOutput(outputTopic); functionConfig.setOutputSerdeClassName(outputSerdeClassName); - resource.registerFunction(tenant, namespace, function, null, null, filePackageUrl, functionConfig, null, null); + resource.registerFunction(tenant, namespace, function, null, null, filePackageUrl, functionConfig, null); } diff --git a/pulsar-functions/worker/src/test/java/org/apache/pulsar/functions/worker/rest/api/v3/SinkApiV3ResourceTest.java b/pulsar-functions/worker/src/test/java/org/apache/pulsar/functions/worker/rest/api/v3/SinkApiV3ResourceTest.java index 9f0de2a12e091..9f786c5b73aac 100644 --- a/pulsar-functions/worker/src/test/java/org/apache/pulsar/functions/worker/rest/api/v3/SinkApiV3ResourceTest.java +++ b/pulsar-functions/worker/src/test/java/org/apache/pulsar/functions/worker/rest/api/v3/SinkApiV3ResourceTest.java @@ -52,6 +52,7 @@ import org.apache.distributedlog.api.namespace.Namespace; import org.apache.logging.log4j.Level; import org.apache.logging.log4j.core.config.Configurator; +import org.apache.pulsar.broker.authentication.AuthenticationParameters; import org.apache.pulsar.client.admin.Functions; import org.apache.pulsar.client.admin.Namespaces; import org.apache.pulsar.client.admin.Packages; @@ -534,7 +535,7 @@ private void testRegisterSinkMissingArguments( details, pkgUrl, sinkConfig, - null, null); + null); } @@ -548,7 +549,7 @@ public void testMissingSinkConfig() { mockedFormData, null, null, - null, null); + null); } @Test(expectedExceptions = RestException.class, expectedExceptionsMessageRegExp = "Sink config is not provided") @@ -562,7 +563,7 @@ public void testUpdateMissingSinkConfig() { mockedFormData, null, null, - null, null, null); + null, null); } private void registerDefaultSink() throws IOException { @@ -580,7 +581,7 @@ private void registerDefaultSinkWithPackageUrl(String packageUrl) throws IOExcep mockedFormData, packageUrl, sinkConfig, - null, null); + null); } } @@ -652,7 +653,7 @@ public void testRegisterSinkConflictingFields() throws Exception { mockedFormData, null, sinkConfig, - null, null); + null); } } @@ -754,7 +755,7 @@ public void testRegisterSinkSuccessWithTransformFunction() throws Exception { mockedFormData, null, sinkConfig, - null, null); + null); } } @@ -803,7 +804,7 @@ public void testRegisterSinkFailureWithInvalidTransformFunction() throws Excepti mockedFormData, null, sinkConfig, - null, null); + null); } } catch (RestException e) { // expected exception @@ -1021,7 +1022,7 @@ private void testUpdateSinkMissingArguments( details, null, sinkConfig, - null, null, null); + null, null); } @@ -1064,7 +1065,7 @@ private void updateDefaultSinkWithPackageUrl(String packageUrl) throws Exception mockedFormData, packageUrl, sinkConfig, - null, null, null); + null, null); } } @@ -1149,7 +1150,7 @@ public void testUpdateSinkWithUrl() throws Exception { null, filePackageUrl, sinkConfig, - null, null, null); + null, null); } @Test(expectedExceptions = RestException.class, expectedExceptionsMessageRegExp = "sink failed to register") @@ -1249,7 +1250,7 @@ public void testUpdateSinkDifferentTransformFunction() throws Exception { mockedFormData, null, sinkConfig, - null, null, null); + null, null); } } @@ -1308,7 +1309,7 @@ private void testDeregisterSinkMissingArguments( tenant, namespace, sink, - null, null); + null); } @@ -1317,7 +1318,7 @@ private void deregisterDefaultSink() { tenant, namespace, sink, - null, null); + null); } @Test(expectedExceptions = RestException.class, expectedExceptionsMessageRegExp = "Sink test-sink doesn't exist") @@ -1532,7 +1533,8 @@ private void testGetSinkMissingArguments( resource.getFunctionInfo( tenant, namespace, - sink, null, null + sink, + AuthenticationParameters.builder().build() ); } @@ -1541,7 +1543,8 @@ private SinkConfig getDefaultSinkInfo() { return resource.getSinkInfo( tenant, namespace, - sink + sink, + AuthenticationParameters.builder().build() ); } @@ -1634,7 +1637,8 @@ private void testListSinksMissingArguments( ) { resource.listFunctions( tenant, - namespace, null, null + namespace, + AuthenticationParameters.builder().build() ); } @@ -1642,7 +1646,8 @@ private void testListSinksMissingArguments( private List listDefaultSinks() { return resource.listFunctions( tenant, - namespace, null, null + namespace, + AuthenticationParameters.builder().build() ); } @@ -1779,7 +1784,7 @@ public void testRegisterSinkSuccessK8sNoUpload() throws Exception { mockedFormData, null, sinkConfig, - null, null); + null); } } @@ -1836,7 +1841,7 @@ public void testRegisterSinkSuccessK8sWithUpload() throws Exception { mockedFormData, null, sinkConfig, - null, null); + null); Assert.fail(); } catch (RuntimeException e) { Assert.assertEquals(e.getMessage(), injectedErrMsg); diff --git a/pulsar-functions/worker/src/test/java/org/apache/pulsar/functions/worker/rest/api/v3/SourceApiV3ResourceTest.java b/pulsar-functions/worker/src/test/java/org/apache/pulsar/functions/worker/rest/api/v3/SourceApiV3ResourceTest.java index 303fa559b1d8e..36be2de716661 100644 --- a/pulsar-functions/worker/src/test/java/org/apache/pulsar/functions/worker/rest/api/v3/SourceApiV3ResourceTest.java +++ b/pulsar-functions/worker/src/test/java/org/apache/pulsar/functions/worker/rest/api/v3/SourceApiV3ResourceTest.java @@ -51,6 +51,7 @@ import org.apache.distributedlog.api.namespace.Namespace; import org.apache.logging.log4j.Level; import org.apache.logging.log4j.core.config.Configurator; +import org.apache.pulsar.broker.authentication.AuthenticationParameters; import org.apache.pulsar.client.admin.Functions; import org.apache.pulsar.client.admin.Namespaces; import org.apache.pulsar.client.admin.Packages; @@ -488,7 +489,7 @@ private void testRegisterSourceMissingArguments( details, pkgUrl, sourceConfig, - null, null); + null); } @@ -502,7 +503,7 @@ public void testMissingSinkConfig() { mockedFormData, null, null, - null, null); + null); } @Test(expectedExceptions = RestException.class, expectedExceptionsMessageRegExp = "Source config is not provided") @@ -516,7 +517,7 @@ public void testUpdateMissingSinkConfig() { mockedFormData, null, null, - null, null, null); + null, null); } private void registerDefaultSource() throws IOException { @@ -533,7 +534,7 @@ private void registerDefaultSourceWithPackageUrl(String packageUrl) throws IOExc null, packageUrl, sourceConfig, - null, null); + null); } @Test(expectedExceptions = RestException.class, expectedExceptionsMessageRegExp = "Source test-source already " @@ -634,7 +635,7 @@ public void testRegisterSourceConflictingFields() throws Exception { mockedFormData, null, sourceConfig, - null, null); + null); } } @@ -937,7 +938,7 @@ private void testUpdateSourceMissingArguments( details, null, sourceConfig, - null, null, null); + null, null); } @@ -983,7 +984,7 @@ private void updateDefaultSourceWithPackageUrl(String packageUrl) throws Excepti mockedFormData, packageUrl, sourceConfig, - null, null, null); + null, null); } } @@ -1069,7 +1070,7 @@ public void testUpdateSourceWithUrl() throws Exception { null, filePackageUrl, sourceConfig, - null, null, null); + null, null); } @@ -1182,7 +1183,7 @@ private void testDeregisterSourceMissingArguments( tenant, namespace, function, - null, null); + null); } @@ -1191,7 +1192,7 @@ private void deregisterDefaultSource() { tenant, namespace, source, - null, null); + null); } @Test(expectedExceptions = RestException.class, expectedExceptionsMessageRegExp = "Source test-source doesn't " + @@ -1385,7 +1386,8 @@ private void testGetSourceMissingArguments( resource.getFunctionInfo( tenant, namespace, - source, null, null + source, + AuthenticationParameters.builder().build() ); } @@ -1393,7 +1395,8 @@ private SourceConfig getDefaultSourceInfo() { return resource.getSourceInfo( tenant, namespace, - source + source, + AuthenticationParameters.builder().build() ); } @@ -1476,14 +1479,17 @@ private void testListSourcesMissingArguments( ) { resource.listFunctions( tenant, - namespace, null, null + namespace, + AuthenticationParameters.builder().build() ); } private List listDefaultSources() { return resource.listFunctions( tenant, - namespace, null, null); + namespace, + AuthenticationParameters.builder().build() + ); } @Test