This is an automated email from the ASF dual-hosted git repository.

markap14 pushed a commit to branch NIFI-15258
in repository https://gitbox.apache.org/repos/asf/nifi.git

commit 7417597727e616d5ba89e870c54f96c50c2571e8
Author: Matt Gilman <[email protected]>
AuthorDate: Mon Dec 22 09:01:28 2025 -0500

    NIFI-15356: Adding authorization to the StandardNiFiConnectorWebContext. 
(#10660)
---
 .../nifi/web/api/entity/NarDetailsEntity.java      |  10 +
 .../java/org/apache/nifi/nar/NarInstallTask.java   |   4 +-
 .../nifi/web/StandardNiFiConnectorWebContext.java  |  44 ----
 .../apache/nifi/web/StandardNiFiServiceFacade.java |   2 +
 .../configuration/WebApplicationConfiguration.java |  12 +-
 .../connector/StandardNiFiConnectorWebContext.java |  99 +++++++++
 .../authorization/AuthorizingConnectionFacade.java |  55 +++++
 .../AuthorizingConnectorInvocationHandler.java     | 130 +++++++++++
 .../AuthorizingControllerServiceFacade.java        |  99 +++++++++
 .../AuthorizingControllerServiceLifecycle.java     |  56 +++++
 .../authorization/AuthorizingFlowContext.java      |  70 ++++++
 .../AuthorizingParameterContextFacade.java         |  72 +++++++
 .../AuthorizingProcessGroupFacade.java             | 145 +++++++++++++
 .../AuthorizingProcessGroupLifecycle.java          |  88 ++++++++
 .../authorization/AuthorizingProcessorFacade.java  |  99 +++++++++
 .../AuthorizingProcessorLifecycle.java             |  80 +++++++
 .../AuthorizingStatelessGroupLifecycle.java        |  55 +++++
 .../ConnectorAuthorizationContext.java             |  74 +++++++
 .../StandardNiFiConnectorWebContextTest.java       | 219 +++++++++++++++++++
 .../AuthorizingConnectorInvocationHandlerTest.java | 239 +++++++++++++++++++++
 .../authorization/AuthorizingFlowContextTest.java  | 167 ++++++++++++++
 .../AuthorizingParameterContextFacadeTest.java     | 162 ++++++++++++++
 22 files changed, 1935 insertions(+), 46 deletions(-)

diff --git 
a/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/NarDetailsEntity.java
 
b/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/NarDetailsEntity.java
index 799677beee..35c4735e47 100644
--- 
a/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/NarDetailsEntity.java
+++ 
b/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/NarDetailsEntity.java
@@ -36,6 +36,7 @@ public class NarDetailsEntity extends Entity {
     private Set<DocumentedTypeDTO> parameterProviderTypes;
     private Set<DocumentedTypeDTO> flowRegistryClientTypes;
     private Set<DocumentedTypeDTO> flowAnalysisRuleTypes;
+    private Set<DocumentedTypeDTO> connectorTypes;
 
     @Schema(description = "The NAR summary")
     public NarSummaryDTO getNarSummary() {
@@ -108,4 +109,13 @@ public class NarDetailsEntity extends Entity {
     public void setFlowAnalysisRuleTypes(final Set<DocumentedTypeDTO> 
flowAnalysisRuleTypes) {
         this.flowAnalysisRuleTypes = flowAnalysisRuleTypes;
     }
+
+    @Schema(description = "The Connector types contained in the NAR")
+    public Set<DocumentedTypeDTO> getConnectorTypes() {
+        return connectorTypes;
+    }
+
+    public void setConnectorTypes(final Set<DocumentedTypeDTO> connectorTypes) 
{
+        this.connectorTypes = connectorTypes;
+    }
 }
diff --git 
a/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/nar/NarInstallTask.java
 
b/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/nar/NarInstallTask.java
index 8c2673353d..ffdb2fa435 100644
--- 
a/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/nar/NarInstallTask.java
+++ 
b/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/nar/NarInstallTask.java
@@ -20,6 +20,7 @@ package org.apache.nifi.nar;
 import org.apache.nifi.bundle.Bundle;
 import org.apache.nifi.bundle.BundleCoordinate;
 import org.apache.nifi.bundle.BundleDetails;
+import org.apache.nifi.components.connector.Connector;
 import org.apache.nifi.controller.ControllerService;
 import org.apache.nifi.controller.service.ControllerServiceProvider;
 import org.apache.nifi.flowanalysis.FlowAnalysisRule;
@@ -54,7 +55,8 @@ public class NarInstallTask implements Runnable {
             ReportingTask.class,
             FlowRegistryClient.class,
             FlowAnalysisRule.class,
-            ParameterProvider.class
+            ParameterProvider.class,
+            Connector.class
     );
 
     private final NarNode narNode;
diff --git 
a/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiConnectorWebContext.java
 
b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiConnectorWebContext.java
deleted file mode 100644
index 67c011330a..0000000000
--- 
a/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiConnectorWebContext.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * 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.nifi.web;
-
-import org.apache.nifi.components.connector.ConnectorNode;
-import org.apache.nifi.web.dao.ConnectorDAO;
-
-/**
- * Implements the NiFiConnectorWebContext interface to provide
- * Connector instances to connector custom UIs.
- */
-public class StandardNiFiConnectorWebContext implements 
NiFiConnectorWebContext {
-
-    private ConnectorDAO connectorDAO;
-
-    @Override
-    @SuppressWarnings("unchecked")
-    public <T> ConnectorWebContext<T> getConnectorWebContext(final String 
connectorId) throws IllegalArgumentException {
-        final ConnectorNode connectorNode = 
connectorDAO.getConnector(connectorId);
-        if (connectorNode == null) {
-            throw new IllegalArgumentException("Unable to find connector with 
id: " + connectorId);
-        }
-        return new ConnectorWebContext<>((T) connectorNode.getConnector(), 
connectorNode.getWorkingFlowContext(), connectorNode.getActiveFlowContext());
-    }
-
-    public void setConnectorDAO(final ConnectorDAO connectorDAO) {
-        this.connectorDAO = connectorDAO;
-    }
-}
-
diff --git 
a/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiServiceFacade.java
 
b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiServiceFacade.java
index 9130a348f7..a1472b8c6d 100644
--- 
a/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiServiceFacade.java
+++ 
b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiServiceFacade.java
@@ -78,6 +78,7 @@ import org.apache.nifi.components.PropertyDescriptor;
 import org.apache.nifi.components.RequiredPermission;
 import org.apache.nifi.components.ValidationResult;
 import org.apache.nifi.components.Validator;
+import org.apache.nifi.components.connector.Connector;
 import org.apache.nifi.components.connector.ConnectorNode;
 import org.apache.nifi.components.connector.ConnectorUpdateContext;
 import org.apache.nifi.components.connector.Secret;
@@ -7374,6 +7375,7 @@ public class StandardNiFiServiceFacade implements 
NiFiServiceFacade {
         
componentTypesEntity.setParameterProviderTypes(dtoFactory.fromDocumentedTypes(getTypes(extensionDefinitions,
 ParameterProvider.class)));
         
componentTypesEntity.setFlowRegistryClientTypes(dtoFactory.fromDocumentedTypes(getTypes(extensionDefinitions,
 FlowRegistryClient.class)));
         
componentTypesEntity.setFlowAnalysisRuleTypes(dtoFactory.fromDocumentedTypes(getTypes(extensionDefinitions,
 FlowAnalysisRule.class)));
+        
componentTypesEntity.setConnectorTypes(dtoFactory.fromDocumentedTypes(getTypes(extensionDefinitions,
 Connector.class)));
         return componentTypesEntity;
     }
 
diff --git 
a/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/configuration/WebApplicationConfiguration.java
 
b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/configuration/WebApplicationConfiguration.java
index 0659d8ef72..ef53f7b877 100644
--- 
a/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/configuration/WebApplicationConfiguration.java
+++ 
b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/configuration/WebApplicationConfiguration.java
@@ -18,6 +18,7 @@ package org.apache.nifi.web.configuration;
 
 import org.apache.nifi.admin.service.AuditService;
 import org.apache.nifi.audit.NiFiAuditor;
+import org.apache.nifi.authorization.AuthorizableLookup;
 import org.apache.nifi.authorization.Authorizer;
 import org.apache.nifi.authorization.StandardAuthorizableLookup;
 import org.apache.nifi.cluster.coordination.ClusterCoordinator;
@@ -35,7 +36,7 @@ import org.apache.nifi.web.NiFiServiceFacade;
 import org.apache.nifi.web.NiFiServiceFacadeLock;
 import org.apache.nifi.web.NiFiConnectorWebContext;
 import org.apache.nifi.web.NiFiWebConfigurationContext;
-import org.apache.nifi.web.StandardNiFiConnectorWebContext;
+import org.apache.nifi.web.connector.StandardNiFiConnectorWebContext;
 import org.apache.nifi.web.StandardNiFiContentAccess;
 import org.apache.nifi.web.StandardNiFiServiceFacade;
 import org.apache.nifi.web.StandardNiFiWebConfigurationContext;
@@ -109,6 +110,8 @@ public class WebApplicationConfiguration {
 
     private ConnectorDAO connectorDAO;
 
+    private AuthorizableLookup authorizableLookup;
+
     public WebApplicationConfiguration(
             final Authorizer authorizer,
             final AccessPolicyDAO accessPolicyDao,
@@ -155,6 +158,11 @@ public class WebApplicationConfiguration {
         this.connectorDAO = connectorDAO;
     }
 
+    @Autowired
+    public void setAuthorizableLookup(final AuthorizableLookup 
authorizableLookup) {
+        this.authorizableLookup = authorizableLookup;
+    }
+
     @Bean
     public EntityFactory entityFactory() {
         return new EntityFactory();
@@ -270,6 +278,8 @@ public class WebApplicationConfiguration {
     public NiFiConnectorWebContext nifiConnectorWebContext() {
         final StandardNiFiConnectorWebContext context = new 
StandardNiFiConnectorWebContext();
         context.setConnectorDAO(connectorDAO);
+        context.setAuthorizer(authorizer);
+        context.setAuthorizableLookup(authorizableLookup);
         return context;
     }
 
diff --git 
a/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/connector/StandardNiFiConnectorWebContext.java
 
b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/connector/StandardNiFiConnectorWebContext.java
new file mode 100644
index 0000000000..5b5018174c
--- /dev/null
+++ 
b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/connector/StandardNiFiConnectorWebContext.java
@@ -0,0 +1,99 @@
+/*
+ * 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.nifi.web.connector;
+
+import org.apache.nifi.authorization.AuthorizableLookup;
+import org.apache.nifi.authorization.Authorizer;
+import org.apache.nifi.components.connector.ConnectorNode;
+import org.apache.nifi.components.connector.components.FlowContext;
+import org.apache.nifi.web.NiFiConnectorWebContext;
+import 
org.apache.nifi.web.connector.authorization.AuthorizingConnectorInvocationHandler;
+import org.apache.nifi.web.connector.authorization.AuthorizingFlowContext;
+import 
org.apache.nifi.web.connector.authorization.ConnectorAuthorizationContext;
+import org.apache.nifi.web.dao.ConnectorDAO;
+
+import java.lang.reflect.Proxy;
+
+/**
+ * Implements the NiFiConnectorWebContext interface to provide
+ * Connector instances to connector custom UIs.
+ *
+ * <p>The returned Connector instance is wrapped in an authorization proxy that
+ * enforces permissions based on the {@link ConnectorWebMethod} annotation on
+ * the connector interface methods. Methods without this annotation cannot be
+ * invoked through the proxy.</p>
+ *
+ * <p>The returned FlowContext instances are also wrapped in authorization 
wrappers
+ * that enforce read/write permissions on all operations.</p>
+ */
+public class StandardNiFiConnectorWebContext implements 
NiFiConnectorWebContext {
+
+    private ConnectorDAO connectorDAO;
+    private Authorizer authorizer;
+    private AuthorizableLookup authorizableLookup;
+
+    @Override
+    @SuppressWarnings("unchecked")
+    public <T> ConnectorWebContext<T> getConnectorWebContext(final String 
connectorId) throws IllegalArgumentException {
+        final ConnectorNode connectorNode = 
connectorDAO.getConnector(connectorId);
+        if (connectorNode == null) {
+            throw new IllegalArgumentException("Unable to find connector with 
id: " + connectorId);
+        }
+
+        final ConnectorAuthorizationContext authContext = new 
ConnectorAuthorizationContext(connectorId, authorizer, authorizableLookup);
+
+        final T connector = (T) connectorNode.getConnector();
+        final T authorizedConnectorProxy = createAuthorizingProxy(connector, 
connectorId);
+
+        final FlowContext workingFlowContext = new 
AuthorizingFlowContext(connectorNode.getWorkingFlowContext(), authContext);
+        final FlowContext activeFlowContext = new 
AuthorizingFlowContext(connectorNode.getActiveFlowContext(), authContext);
+
+        return new ConnectorWebContext<>(authorizedConnectorProxy, 
workingFlowContext, activeFlowContext);
+    }
+
+    /**
+     * Creates a proxy around the given connector that enforces authorization
+     * based on {@link ConnectorWebMethod} annotations.
+     *
+     * @param <T> the type of the connector
+     * @param connector the connector instance to wrap
+     * @param connectorId the ID of the connector
+     * @return a proxy that enforces authorization on method invocations
+     */
+    @SuppressWarnings("unchecked")
+    private <T> T createAuthorizingProxy(final T connector, final String 
connectorId) {
+        final AuthorizingConnectorInvocationHandler<T> handler = new 
AuthorizingConnectorInvocationHandler<>(
+                connector, connectorId, authorizer, authorizableLookup);
+
+        return (T) Proxy.newProxyInstance(
+                connector.getClass().getClassLoader(),
+                connector.getClass().getInterfaces(),
+                handler);
+    }
+
+    public void setConnectorDAO(final ConnectorDAO connectorDAO) {
+        this.connectorDAO = connectorDAO;
+    }
+
+    public void setAuthorizer(final Authorizer authorizer) {
+        this.authorizer = authorizer;
+    }
+
+    public void setAuthorizableLookup(final AuthorizableLookup 
authorizableLookup) {
+        this.authorizableLookup = authorizableLookup;
+    }
+}
diff --git 
a/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/connector/authorization/AuthorizingConnectionFacade.java
 
b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/connector/authorization/AuthorizingConnectionFacade.java
new file mode 100644
index 0000000000..f96642e2b7
--- /dev/null
+++ 
b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/connector/authorization/AuthorizingConnectionFacade.java
@@ -0,0 +1,55 @@
+/*
+ * 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.nifi.web.connector.authorization;
+
+import org.apache.nifi.components.connector.components.ConnectionFacade;
+import org.apache.nifi.controller.queue.QueueSize;
+import org.apache.nifi.flow.VersionedConnection;
+
+/**
+ * A wrapper around {@link ConnectionFacade} that enforces authorization 
before delegating
+ * to the underlying implementation.
+ */
+public class AuthorizingConnectionFacade implements ConnectionFacade {
+
+    private final ConnectionFacade delegate;
+    private final ConnectorAuthorizationContext authContext;
+
+    public AuthorizingConnectionFacade(final ConnectionFacade delegate, final 
ConnectorAuthorizationContext authContext) {
+        this.delegate = delegate;
+        this.authContext = authContext;
+    }
+
+    @Override
+    public VersionedConnection getDefinition() {
+        authContext.authorizeRead();
+        return delegate.getDefinition();
+    }
+
+    @Override
+    public QueueSize getQueueSize() {
+        authContext.authorizeRead();
+        return delegate.getQueueSize();
+    }
+
+    @Override
+    public void purge() {
+        authContext.authorizeWrite();
+        delegate.purge();
+    }
+}
+
diff --git 
a/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/connector/authorization/AuthorizingConnectorInvocationHandler.java
 
b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/connector/authorization/AuthorizingConnectorInvocationHandler.java
new file mode 100644
index 0000000000..130fa43248
--- /dev/null
+++ 
b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/connector/authorization/AuthorizingConnectorInvocationHandler.java
@@ -0,0 +1,130 @@
+/*
+ * 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.nifi.web.connector.authorization;
+
+import org.apache.nifi.authorization.AuthorizableLookup;
+import org.apache.nifi.authorization.Authorizer;
+import org.apache.nifi.authorization.RequestAction;
+import org.apache.nifi.authorization.resource.Authorizable;
+import org.apache.nifi.authorization.user.NiFiUser;
+import org.apache.nifi.authorization.user.NiFiUserUtils;
+import org.apache.nifi.web.ConnectorWebMethod;
+
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+/**
+ * An InvocationHandler that wraps a Connector instance and enforces 
authorization
+ * based on the {@link ConnectorWebMethod} annotation present on the invoked 
method.
+ *
+ * <p>Methods must be annotated with {@link ConnectorWebMethod} to be 
invokable through
+ * this handler. The annotation specifies whether READ or WRITE access is 
required.
+ * Methods without the annotation will result in an {@link 
IllegalStateException}.</p>
+ *
+ * @param <T> the type of the Connector being proxied
+ */
+public class AuthorizingConnectorInvocationHandler<T> implements 
InvocationHandler {
+
+    private final T delegate;
+    private final String connectorId;
+    private final Authorizer authorizer;
+    private final AuthorizableLookup authorizableLookup;
+
+    /**
+     * Constructs an AuthorizingConnectorInvocationHandler.
+     *
+     * @param delegate the actual Connector instance to delegate method calls 
to
+     * @param connectorId the ID of the connector, used for authorization 
lookups
+     * @param authorizer the Authorizer to use for authorization checks
+     * @param authorizableLookup the lookup service to obtain the Authorizable 
for the connector
+     */
+    public AuthorizingConnectorInvocationHandler(final T delegate, final 
String connectorId,
+                                                  final Authorizer authorizer, 
final AuthorizableLookup authorizableLookup) {
+        this.delegate = delegate;
+        this.connectorId = connectorId;
+        this.authorizer = authorizer;
+        this.authorizableLookup = authorizableLookup;
+    }
+
+    @Override
+    public Object invoke(final Object proxy, final Method method, final 
Object[] args) throws Throwable {
+        final ConnectorWebMethod annotation = 
findConnectorWebMethodAnnotation(method);
+
+        if (annotation == null) {
+            throw new IllegalStateException(String.format(
+                    "Method [%s] on connector [%s] is not annotated with 
@ConnectorWebMethod and cannot be invoked through the Connector Web Context",
+                    method.getName(), connectorId));
+        }
+
+        final RequestAction requiredAction = 
mapAccessTypeToRequestAction(annotation.value());
+        final Authorizable connector = 
authorizableLookup.getConnector(connectorId);
+        final NiFiUser user = NiFiUserUtils.getNiFiUser();
+
+        connector.authorize(authorizer, requiredAction, user);
+
+        try {
+            return method.invoke(delegate, args);
+        } catch (final InvocationTargetException e) {
+            throw e.getCause();
+        }
+    }
+
+    /**
+     * Maps the ConnectorWebMethod.AccessType to the corresponding 
RequestAction.
+     *
+     * @param accessType the access type from the annotation
+     * @return the corresponding RequestAction
+     */
+    private RequestAction mapAccessTypeToRequestAction(final 
ConnectorWebMethod.AccessType accessType) {
+        return switch (accessType) {
+            case READ -> RequestAction.READ;
+            case WRITE -> RequestAction.WRITE;
+        };
+    }
+
+    /**
+     * Finds the ConnectorWebMethod annotation on the given method. This 
method searches
+     * the declaring class's interfaces to find the annotation, as the method 
parameter
+     * may be from the proxy class rather than the interface.
+     *
+     * @param method the method to search for the annotation
+     * @return the ConnectorWebMethod annotation, or null if not found
+     */
+    private ConnectorWebMethod findConnectorWebMethodAnnotation(final Method 
method) {
+        final ConnectorWebMethod directAnnotation = 
method.getAnnotation(ConnectorWebMethod.class);
+        if (directAnnotation != null) {
+            return directAnnotation;
+        }
+
+        for (final Class<?> iface : delegate.getClass().getInterfaces()) {
+            try {
+                final Method interfaceMethod = 
iface.getMethod(method.getName(), method.getParameterTypes());
+                final ConnectorWebMethod interfaceAnnotation = 
interfaceMethod.getAnnotation(ConnectorWebMethod.class);
+                if (interfaceAnnotation != null) {
+                    return interfaceAnnotation;
+                }
+            } catch (final NoSuchMethodException ignored) {
+                // Method not found on this interface; continue searching 
other interfaces
+                continue;
+            }
+        }
+
+        return null;
+    }
+}
+
diff --git 
a/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/connector/authorization/AuthorizingControllerServiceFacade.java
 
b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/connector/authorization/AuthorizingControllerServiceFacade.java
new file mode 100644
index 0000000000..2086c875d4
--- /dev/null
+++ 
b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/connector/authorization/AuthorizingControllerServiceFacade.java
@@ -0,0 +1,99 @@
+/*
+ * 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.nifi.web.connector.authorization;
+
+import org.apache.nifi.components.ConfigVerificationResult;
+import org.apache.nifi.components.ValidationResult;
+import org.apache.nifi.components.connector.InvocationFailedException;
+import org.apache.nifi.components.connector.components.ControllerServiceFacade;
+import 
org.apache.nifi.components.connector.components.ControllerServiceLifecycle;
+import org.apache.nifi.flow.VersionedControllerService;
+import org.apache.nifi.flow.VersionedExternalFlow;
+import org.apache.nifi.flow.VersionedParameterContext;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * A wrapper around {@link ControllerServiceFacade} that enforces 
authorization before delegating
+ * to the underlying implementation.
+ */
+public class AuthorizingControllerServiceFacade implements 
ControllerServiceFacade {
+
+    private final ControllerServiceFacade delegate;
+    private final ConnectorAuthorizationContext authContext;
+
+    public AuthorizingControllerServiceFacade(final ControllerServiceFacade 
delegate, final ConnectorAuthorizationContext authContext) {
+        this.delegate = delegate;
+        this.authContext = authContext;
+    }
+
+    @Override
+    public VersionedControllerService getDefinition() {
+        authContext.authorizeRead();
+        return delegate.getDefinition();
+    }
+
+    @Override
+    public ControllerServiceLifecycle getLifecycle() {
+        authContext.authorizeRead();
+        return new 
AuthorizingControllerServiceLifecycle(delegate.getLifecycle(), authContext);
+    }
+
+    @Override
+    public List<ValidationResult> validate() {
+        authContext.authorizeRead();
+        return delegate.validate();
+    }
+
+    @Override
+    public List<ValidationResult> validate(final Map<String, String> 
propertyValues) {
+        authContext.authorizeRead();
+        return delegate.validate(propertyValues);
+    }
+
+    @Override
+    public List<ConfigVerificationResult> verify(final Map<String, String> 
propertyValues, final Map<String, String> variables) {
+        authContext.authorizeRead();
+        return delegate.verify(propertyValues, variables);
+    }
+
+    @Override
+    public List<ConfigVerificationResult> verify(final Map<String, String> 
propertyValues, final VersionedParameterContext parameterContext, final 
Map<String, String> variables) {
+        authContext.authorizeRead();
+        return delegate.verify(propertyValues, parameterContext, variables);
+    }
+
+    @Override
+    public List<ConfigVerificationResult> verify(final VersionedExternalFlow 
versionedExternalFlow, final Map<String, String> variables) {
+        authContext.authorizeRead();
+        return delegate.verify(versionedExternalFlow, variables);
+    }
+
+    @Override
+    public Object invokeConnectorMethod(final String methodName, final 
Map<String, Object> arguments) throws InvocationFailedException {
+        authContext.authorizeWrite();
+        return delegate.invokeConnectorMethod(methodName, arguments);
+    }
+
+    @Override
+    public <T> T invokeConnectorMethod(final String methodName, final 
Map<String, Object> arguments, final Class<T> returnType) throws 
InvocationFailedException {
+        authContext.authorizeWrite();
+        return delegate.invokeConnectorMethod(methodName, arguments, 
returnType);
+    }
+}
+
diff --git 
a/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/connector/authorization/AuthorizingControllerServiceLifecycle.java
 
b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/connector/authorization/AuthorizingControllerServiceLifecycle.java
new file mode 100644
index 0000000000..838632fdfb
--- /dev/null
+++ 
b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/connector/authorization/AuthorizingControllerServiceLifecycle.java
@@ -0,0 +1,56 @@
+/*
+ * 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.nifi.web.connector.authorization;
+
+import 
org.apache.nifi.components.connector.components.ControllerServiceLifecycle;
+import org.apache.nifi.components.connector.components.ControllerServiceState;
+
+import java.util.concurrent.CompletableFuture;
+
+/**
+ * A wrapper around {@link ControllerServiceLifecycle} that enforces 
authorization before delegating
+ * to the underlying implementation.
+ */
+public class AuthorizingControllerServiceLifecycle implements 
ControllerServiceLifecycle {
+
+    private final ControllerServiceLifecycle delegate;
+    private final ConnectorAuthorizationContext authContext;
+
+    public AuthorizingControllerServiceLifecycle(final 
ControllerServiceLifecycle delegate, final ConnectorAuthorizationContext 
authContext) {
+        this.delegate = delegate;
+        this.authContext = authContext;
+    }
+
+    @Override
+    public ControllerServiceState getState() {
+        authContext.authorizeRead();
+        return delegate.getState();
+    }
+
+    @Override
+    public CompletableFuture<Void> enable() {
+        authContext.authorizeWrite();
+        return delegate.enable();
+    }
+
+    @Override
+    public CompletableFuture<Void> disable() {
+        authContext.authorizeWrite();
+        return delegate.disable();
+    }
+}
+
diff --git 
a/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/connector/authorization/AuthorizingFlowContext.java
 
b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/connector/authorization/AuthorizingFlowContext.java
new file mode 100644
index 0000000000..d03b5e71fb
--- /dev/null
+++ 
b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/connector/authorization/AuthorizingFlowContext.java
@@ -0,0 +1,70 @@
+/*
+ * 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.nifi.web.connector.authorization;
+
+import org.apache.nifi.components.connector.ConnectorConfigurationContext;
+import org.apache.nifi.components.connector.components.FlowContext;
+import org.apache.nifi.components.connector.components.FlowContextType;
+import org.apache.nifi.components.connector.components.ParameterContextFacade;
+import org.apache.nifi.components.connector.components.ProcessGroupFacade;
+import org.apache.nifi.flow.Bundle;
+
+/**
+ * A wrapper around {@link FlowContext} that enforces authorization before 
delegating
+ * to the underlying implementation.
+ */
+public class AuthorizingFlowContext implements FlowContext {
+
+    private final FlowContext delegate;
+    private final ConnectorAuthorizationContext authContext;
+
+    public AuthorizingFlowContext(final FlowContext delegate, final 
ConnectorAuthorizationContext authContext) {
+        this.delegate = delegate;
+        this.authContext = authContext;
+    }
+
+    @Override
+    public ProcessGroupFacade getRootGroup() {
+        authContext.authorizeRead();
+        return new AuthorizingProcessGroupFacade(delegate.getRootGroup(), 
authContext);
+    }
+
+    @Override
+    public ParameterContextFacade getParameterContext() {
+        authContext.authorizeRead();
+        return new 
AuthorizingParameterContextFacade(delegate.getParameterContext(), authContext);
+    }
+
+    @Override
+    public ConnectorConfigurationContext getConfigurationContext() {
+        authContext.authorizeRead();
+        return delegate.getConfigurationContext();
+    }
+
+    @Override
+    public FlowContextType getType() {
+        authContext.authorizeRead();
+        return delegate.getType();
+    }
+
+    @Override
+    public Bundle getBundle() {
+        authContext.authorizeRead();
+        return delegate.getBundle();
+    }
+}
+
diff --git 
a/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/connector/authorization/AuthorizingParameterContextFacade.java
 
b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/connector/authorization/AuthorizingParameterContextFacade.java
new file mode 100644
index 0000000000..ead31b9481
--- /dev/null
+++ 
b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/connector/authorization/AuthorizingParameterContextFacade.java
@@ -0,0 +1,72 @@
+/*
+ * 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.nifi.web.connector.authorization;
+
+import org.apache.nifi.asset.Asset;
+import org.apache.nifi.components.connector.components.ParameterContextFacade;
+import org.apache.nifi.components.connector.components.ParameterValue;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Collection;
+import java.util.Set;
+
+/**
+ * A wrapper around {@link ParameterContextFacade} that enforces authorization 
before delegating
+ * to the underlying implementation.
+ */
+public class AuthorizingParameterContextFacade implements 
ParameterContextFacade {
+
+    private final ParameterContextFacade delegate;
+    private final ConnectorAuthorizationContext authContext;
+
+    public AuthorizingParameterContextFacade(final ParameterContextFacade 
delegate, final ConnectorAuthorizationContext authContext) {
+        this.delegate = delegate;
+        this.authContext = authContext;
+    }
+
+    @Override
+    public void updateParameters(final Collection<ParameterValue> 
parameterValues) {
+        authContext.authorizeWrite();
+        delegate.updateParameters(parameterValues);
+    }
+
+    @Override
+    public String getValue(final String parameterName) {
+        authContext.authorizeRead();
+        return delegate.getValue(parameterName);
+    }
+
+    @Override
+    public Set<String> getDefinedParameterNames() {
+        authContext.authorizeRead();
+        return delegate.getDefinedParameterNames();
+    }
+
+    @Override
+    public boolean isSensitive(final String parameterName) {
+        authContext.authorizeRead();
+        return delegate.isSensitive(parameterName);
+    }
+
+    @Override
+    public Asset createAsset(final InputStream inputStream) throws IOException 
{
+        authContext.authorizeWrite();
+        return delegate.createAsset(inputStream);
+    }
+}
+
diff --git 
a/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/connector/authorization/AuthorizingProcessGroupFacade.java
 
b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/connector/authorization/AuthorizingProcessGroupFacade.java
new file mode 100644
index 0000000000..21ade9740d
--- /dev/null
+++ 
b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/connector/authorization/AuthorizingProcessGroupFacade.java
@@ -0,0 +1,145 @@
+/*
+ * 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.nifi.web.connector.authorization;
+
+import org.apache.nifi.components.connector.components.ConnectionFacade;
+import org.apache.nifi.components.connector.components.ControllerServiceFacade;
+import 
org.apache.nifi.components.connector.components.ControllerServiceReferenceHierarchy;
+import 
org.apache.nifi.components.connector.components.ControllerServiceReferenceScope;
+import org.apache.nifi.components.connector.components.ProcessGroupFacade;
+import org.apache.nifi.components.connector.components.ProcessGroupLifecycle;
+import org.apache.nifi.components.connector.components.ProcessorFacade;
+import org.apache.nifi.components.connector.components.StatelessGroupLifecycle;
+import org.apache.nifi.controller.queue.QueueSize;
+import org.apache.nifi.flow.VersionedProcessGroup;
+
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/**
+ * A wrapper around {@link ProcessGroupFacade} that enforces authorization 
before delegating
+ * to the underlying implementation.
+ */
+public class AuthorizingProcessGroupFacade implements ProcessGroupFacade {
+
+    private final ProcessGroupFacade delegate;
+    private final ConnectorAuthorizationContext authContext;
+
+    public AuthorizingProcessGroupFacade(final ProcessGroupFacade delegate, 
final ConnectorAuthorizationContext authContext) {
+        this.delegate = delegate;
+        this.authContext = authContext;
+    }
+
+    @Override
+    public VersionedProcessGroup getDefinition() {
+        authContext.authorizeRead();
+        return delegate.getDefinition();
+    }
+
+    @Override
+    public ProcessorFacade getProcessor(final String id) {
+        authContext.authorizeRead();
+        final ProcessorFacade processor = delegate.getProcessor(id);
+        return processor == null ? null : new 
AuthorizingProcessorFacade(processor, authContext);
+    }
+
+    @Override
+    public Set<ProcessorFacade> getProcessors() {
+        authContext.authorizeRead();
+        return delegate.getProcessors().stream()
+                .map(p -> new AuthorizingProcessorFacade(p, authContext))
+                .collect(Collectors.toSet());
+    }
+
+    @Override
+    public ControllerServiceFacade getControllerService(final String id) {
+        authContext.authorizeRead();
+        final ControllerServiceFacade service = 
delegate.getControllerService(id);
+        return service == null ? null : new 
AuthorizingControllerServiceFacade(service, authContext);
+    }
+
+    @Override
+    public Set<ControllerServiceFacade> getControllerServices() {
+        authContext.authorizeRead();
+        return delegate.getControllerServices().stream()
+                .map(s -> new AuthorizingControllerServiceFacade(s, 
authContext))
+                .collect(Collectors.toSet());
+    }
+
+    @Override
+    public Set<ControllerServiceFacade> getControllerServices(final 
ControllerServiceReferenceScope referenceScope, final 
ControllerServiceReferenceHierarchy hierarchy) {
+        authContext.authorizeRead();
+        return delegate.getControllerServices(referenceScope, 
hierarchy).stream()
+                .map(s -> new AuthorizingControllerServiceFacade(s, 
authContext))
+                .collect(Collectors.toSet());
+    }
+
+    @Override
+    public ConnectionFacade getConnection(final String id) {
+        authContext.authorizeRead();
+        final ConnectionFacade connection = delegate.getConnection(id);
+        return connection == null ? null : new 
AuthorizingConnectionFacade(connection, authContext);
+    }
+
+    @Override
+    public Set<ConnectionFacade> getConnections() {
+        authContext.authorizeRead();
+        return delegate.getConnections().stream()
+                .map(c -> new AuthorizingConnectionFacade(c, authContext))
+                .collect(Collectors.toSet());
+    }
+
+    @Override
+    public ProcessGroupFacade getProcessGroup(final String id) {
+        authContext.authorizeRead();
+        final ProcessGroupFacade group = delegate.getProcessGroup(id);
+        return group == null ? null : new AuthorizingProcessGroupFacade(group, 
authContext);
+    }
+
+    @Override
+    public Set<ProcessGroupFacade> getProcessGroups() {
+        authContext.authorizeRead();
+        return delegate.getProcessGroups().stream()
+                .map(g -> new AuthorizingProcessGroupFacade(g, authContext))
+                .collect(Collectors.toSet());
+    }
+
+    @Override
+    public QueueSize getQueueSize() {
+        authContext.authorizeRead();
+        return delegate.getQueueSize();
+    }
+
+    @Override
+    public boolean isFlowEmpty() {
+        authContext.authorizeRead();
+        return delegate.isFlowEmpty();
+    }
+
+    @Override
+    public StatelessGroupLifecycle getStatelessLifecycle() {
+        authContext.authorizeRead();
+        return new 
AuthorizingStatelessGroupLifecycle(delegate.getStatelessLifecycle(), 
authContext);
+    }
+
+    @Override
+    public ProcessGroupLifecycle getLifecycle() {
+        authContext.authorizeRead();
+        return new AuthorizingProcessGroupLifecycle(delegate.getLifecycle(), 
authContext);
+    }
+}
+
diff --git 
a/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/connector/authorization/AuthorizingProcessGroupLifecycle.java
 
b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/connector/authorization/AuthorizingProcessGroupLifecycle.java
new file mode 100644
index 0000000000..d29a056425
--- /dev/null
+++ 
b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/connector/authorization/AuthorizingProcessGroupLifecycle.java
@@ -0,0 +1,88 @@
+/*
+ * 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.nifi.web.connector.authorization;
+
+import 
org.apache.nifi.components.connector.components.ControllerServiceReferenceHierarchy;
+import 
org.apache.nifi.components.connector.components.ControllerServiceReferenceScope;
+import org.apache.nifi.components.connector.components.ProcessGroupLifecycle;
+
+import java.util.Collection;
+import java.util.concurrent.CompletableFuture;
+
+/**
+ * A wrapper around {@link ProcessGroupLifecycle} that enforces authorization 
before delegating
+ * to the underlying implementation. All lifecycle operations require WRITE 
authorization.
+ */
+public class AuthorizingProcessGroupLifecycle implements ProcessGroupLifecycle 
{
+
+    private final ProcessGroupLifecycle delegate;
+    private final ConnectorAuthorizationContext authContext;
+
+    public AuthorizingProcessGroupLifecycle(final ProcessGroupLifecycle 
delegate, final ConnectorAuthorizationContext authContext) {
+        this.delegate = delegate;
+        this.authContext = authContext;
+    }
+
+    @Override
+    public CompletableFuture<Void> enableControllerServices(final 
ControllerServiceReferenceScope scope, final 
ControllerServiceReferenceHierarchy hierarchy) {
+        authContext.authorizeWrite();
+        return delegate.enableControllerServices(scope, hierarchy);
+    }
+
+    @Override
+    public CompletableFuture<Void> enableControllerServices(final 
Collection<String> serviceIdentifiers) {
+        authContext.authorizeWrite();
+        return delegate.enableControllerServices(serviceIdentifiers);
+    }
+
+    @Override
+    public CompletableFuture<Void> disableControllerServices(final 
ControllerServiceReferenceHierarchy hierarchy) {
+        authContext.authorizeWrite();
+        return delegate.disableControllerServices(hierarchy);
+    }
+
+    @Override
+    public CompletableFuture<Void> disableControllerServices(final 
Collection<String> serviceIdentifiers) {
+        authContext.authorizeWrite();
+        return delegate.disableControllerServices(serviceIdentifiers);
+    }
+
+    @Override
+    public CompletableFuture<Void> startProcessors() {
+        authContext.authorizeWrite();
+        return delegate.startProcessors();
+    }
+
+    @Override
+    public CompletableFuture<Void> start(final ControllerServiceReferenceScope 
serviceReferenceScope) {
+        authContext.authorizeWrite();
+        return delegate.start(serviceReferenceScope);
+    }
+
+    @Override
+    public CompletableFuture<Void> stop() {
+        authContext.authorizeWrite();
+        return delegate.stop();
+    }
+
+    @Override
+    public CompletableFuture<Void> stopProcessors() {
+        authContext.authorizeWrite();
+        return delegate.stopProcessors();
+    }
+}
+
diff --git 
a/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/connector/authorization/AuthorizingProcessorFacade.java
 
b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/connector/authorization/AuthorizingProcessorFacade.java
new file mode 100644
index 0000000000..22fe562230
--- /dev/null
+++ 
b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/connector/authorization/AuthorizingProcessorFacade.java
@@ -0,0 +1,99 @@
+/*
+ * 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.nifi.web.connector.authorization;
+
+import org.apache.nifi.components.ConfigVerificationResult;
+import org.apache.nifi.components.ValidationResult;
+import org.apache.nifi.components.connector.InvocationFailedException;
+import org.apache.nifi.components.connector.components.ProcessorFacade;
+import org.apache.nifi.components.connector.components.ProcessorLifecycle;
+import org.apache.nifi.flow.VersionedExternalFlow;
+import org.apache.nifi.flow.VersionedParameterContext;
+import org.apache.nifi.flow.VersionedProcessor;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * A wrapper around {@link ProcessorFacade} that enforces authorization before 
delegating
+ * to the underlying implementation.
+ */
+public class AuthorizingProcessorFacade implements ProcessorFacade {
+
+    private final ProcessorFacade delegate;
+    private final ConnectorAuthorizationContext authContext;
+
+    public AuthorizingProcessorFacade(final ProcessorFacade delegate, final 
ConnectorAuthorizationContext authContext) {
+        this.delegate = delegate;
+        this.authContext = authContext;
+    }
+
+    @Override
+    public VersionedProcessor getDefinition() {
+        authContext.authorizeRead();
+        return delegate.getDefinition();
+    }
+
+    @Override
+    public ProcessorLifecycle getLifecycle() {
+        authContext.authorizeRead();
+        return new AuthorizingProcessorLifecycle(delegate.getLifecycle(), 
authContext);
+    }
+
+    @Override
+    public List<ValidationResult> validate() {
+        authContext.authorizeRead();
+        return delegate.validate();
+    }
+
+    @Override
+    public List<ValidationResult> validate(final Map<String, String> 
propertyValues) {
+        authContext.authorizeRead();
+        return delegate.validate(propertyValues);
+    }
+
+    @Override
+    public List<ConfigVerificationResult> verify(final Map<String, String> 
propertyValues, final Map<String, String> attributes) {
+        authContext.authorizeRead();
+        return delegate.verify(propertyValues, attributes);
+    }
+
+    @Override
+    public List<ConfigVerificationResult> verify(final Map<String, String> 
propertyValues, final VersionedParameterContext parameterContext, final 
Map<String, String> attributes) {
+        authContext.authorizeRead();
+        return delegate.verify(propertyValues, parameterContext, attributes);
+    }
+
+    @Override
+    public List<ConfigVerificationResult> verify(final VersionedExternalFlow 
versionedExternalFlow, final Map<String, String> attributes) {
+        authContext.authorizeRead();
+        return delegate.verify(versionedExternalFlow, attributes);
+    }
+
+    @Override
+    public Object invokeConnectorMethod(final String methodName, final 
Map<String, Object> arguments) throws InvocationFailedException {
+        authContext.authorizeWrite();
+        return delegate.invokeConnectorMethod(methodName, arguments);
+    }
+
+    @Override
+    public <T> T invokeConnectorMethod(final String methodName, final 
Map<String, Object> arguments, final Class<T> returnType) throws 
InvocationFailedException {
+        authContext.authorizeWrite();
+        return delegate.invokeConnectorMethod(methodName, arguments, 
returnType);
+    }
+}
+
diff --git 
a/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/connector/authorization/AuthorizingProcessorLifecycle.java
 
b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/connector/authorization/AuthorizingProcessorLifecycle.java
new file mode 100644
index 0000000000..562551def8
--- /dev/null
+++ 
b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/connector/authorization/AuthorizingProcessorLifecycle.java
@@ -0,0 +1,80 @@
+/*
+ * 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.nifi.web.connector.authorization;
+
+import org.apache.nifi.components.connector.components.ProcessorLifecycle;
+import org.apache.nifi.components.connector.components.ProcessorState;
+
+import java.util.concurrent.CompletableFuture;
+
+/**
+ * A wrapper around {@link ProcessorLifecycle} that enforces authorization 
before delegating
+ * to the underlying implementation.
+ */
+public class AuthorizingProcessorLifecycle implements ProcessorLifecycle {
+
+    private final ProcessorLifecycle delegate;
+    private final ConnectorAuthorizationContext authContext;
+
+    public AuthorizingProcessorLifecycle(final ProcessorLifecycle delegate, 
final ConnectorAuthorizationContext authContext) {
+        this.delegate = delegate;
+        this.authContext = authContext;
+    }
+
+    @Override
+    public ProcessorState getState() {
+        authContext.authorizeRead();
+        return delegate.getState();
+    }
+
+    @Override
+    public int getActiveThreadCount() {
+        authContext.authorizeRead();
+        return delegate.getActiveThreadCount();
+    }
+
+    @Override
+    public void terminate() {
+        authContext.authorizeWrite();
+        delegate.terminate();
+    }
+
+    @Override
+    public CompletableFuture<Void> stop() {
+        authContext.authorizeWrite();
+        return delegate.stop();
+    }
+
+    @Override
+    public CompletableFuture<Void> start() {
+        authContext.authorizeWrite();
+        return delegate.start();
+    }
+
+    @Override
+    public void disable() {
+        authContext.authorizeWrite();
+        delegate.disable();
+    }
+
+    @Override
+    public void enable() {
+        authContext.authorizeWrite();
+        delegate.enable();
+    }
+}
+
diff --git 
a/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/connector/authorization/AuthorizingStatelessGroupLifecycle.java
 
b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/connector/authorization/AuthorizingStatelessGroupLifecycle.java
new file mode 100644
index 0000000000..bf822669a6
--- /dev/null
+++ 
b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/connector/authorization/AuthorizingStatelessGroupLifecycle.java
@@ -0,0 +1,55 @@
+/*
+ * 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.nifi.web.connector.authorization;
+
+import org.apache.nifi.components.connector.components.StatelessGroupLifecycle;
+
+import java.util.concurrent.CompletableFuture;
+
+/**
+ * A wrapper around {@link StatelessGroupLifecycle} that enforces 
authorization before delegating
+ * to the underlying implementation. All lifecycle operations require WRITE 
authorization.
+ */
+public class AuthorizingStatelessGroupLifecycle implements 
StatelessGroupLifecycle {
+
+    private final StatelessGroupLifecycle delegate;
+    private final ConnectorAuthorizationContext authContext;
+
+    public AuthorizingStatelessGroupLifecycle(final StatelessGroupLifecycle 
delegate, final ConnectorAuthorizationContext authContext) {
+        this.delegate = delegate;
+        this.authContext = authContext;
+    }
+
+    @Override
+    public CompletableFuture<Void> start() {
+        authContext.authorizeWrite();
+        return delegate.start();
+    }
+
+    @Override
+    public CompletableFuture<Void> stop() {
+        authContext.authorizeWrite();
+        return delegate.stop();
+    }
+
+    @Override
+    public CompletableFuture<Void> terminate() {
+        authContext.authorizeWrite();
+        return delegate.terminate();
+    }
+}
+
diff --git 
a/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/connector/authorization/ConnectorAuthorizationContext.java
 
b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/connector/authorization/ConnectorAuthorizationContext.java
new file mode 100644
index 0000000000..00f9930047
--- /dev/null
+++ 
b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/connector/authorization/ConnectorAuthorizationContext.java
@@ -0,0 +1,74 @@
+/*
+ * 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.nifi.web.connector.authorization;
+
+import org.apache.nifi.authorization.AuthorizableLookup;
+import org.apache.nifi.authorization.Authorizer;
+import org.apache.nifi.authorization.RequestAction;
+import org.apache.nifi.authorization.resource.Authorizable;
+import org.apache.nifi.authorization.user.NiFiUser;
+import org.apache.nifi.authorization.user.NiFiUserUtils;
+
+/**
+ * Holds the authorization context needed to authorize operations on a 
Connector's FlowContext
+ * and its associated facades.
+ */
+public class ConnectorAuthorizationContext {
+
+    private final String connectorId;
+    private final Authorizer authorizer;
+    private final AuthorizableLookup authorizableLookup;
+
+    public ConnectorAuthorizationContext(final String connectorId, final 
Authorizer authorizer, final AuthorizableLookup authorizableLookup) {
+        this.connectorId = connectorId;
+        this.authorizer = authorizer;
+        this.authorizableLookup = authorizableLookup;
+    }
+
+    /**
+     * Authorizes the current user for read access to the connector.
+     */
+    public void authorizeRead() {
+        authorize(RequestAction.READ);
+    }
+
+    /**
+     * Authorizes the current user for write access to the connector.
+     */
+    public void authorizeWrite() {
+        authorize(RequestAction.WRITE);
+    }
+
+    private void authorize(final RequestAction action) {
+        final Authorizable connector = 
authorizableLookup.getConnector(connectorId);
+        final NiFiUser user = NiFiUserUtils.getNiFiUser();
+        connector.authorize(authorizer, action, user);
+    }
+
+    public String getConnectorId() {
+        return connectorId;
+    }
+
+    public Authorizer getAuthorizer() {
+        return authorizer;
+    }
+
+    public AuthorizableLookup getAuthorizableLookup() {
+        return authorizableLookup;
+    }
+}
+
diff --git 
a/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/web/connector/StandardNiFiConnectorWebContextTest.java
 
b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/web/connector/StandardNiFiConnectorWebContextTest.java
new file mode 100644
index 0000000000..321e7ff02c
--- /dev/null
+++ 
b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/web/connector/StandardNiFiConnectorWebContextTest.java
@@ -0,0 +1,219 @@
+/*
+ * 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.nifi.web.connector;
+
+import org.apache.nifi.authorization.AccessDeniedException;
+import org.apache.nifi.components.connector.Connector;
+import org.apache.nifi.web.ConnectorWebMethod;
+import org.apache.nifi.web.ConnectorWebMethod.AccessType;
+import org.apache.nifi.authorization.AuthorizableLookup;
+import org.apache.nifi.authorization.AuthorizationRequest;
+import org.apache.nifi.authorization.AuthorizationResult;
+import org.apache.nifi.authorization.Authorizer;
+import org.apache.nifi.authorization.RequestAction;
+import org.apache.nifi.authorization.resource.Authorizable;
+import org.apache.nifi.authorization.user.NiFiUser;
+import org.apache.nifi.authorization.user.NiFiUserDetails;
+import org.apache.nifi.authorization.user.StandardNiFiUser;
+import org.apache.nifi.components.connector.ConnectorNode;
+import org.apache.nifi.components.connector.FrameworkFlowContext;
+import org.apache.nifi.web.NiFiConnectorWebContext.ConnectorWebContext;
+import org.apache.nifi.web.connector.authorization.AuthorizingFlowContext;
+import org.apache.nifi.web.dao.ConnectorDAO;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.context.SecurityContext;
+import org.springframework.security.core.context.SecurityContextHolder;
+
+import java.lang.reflect.Proxy;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNotSame;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.lenient;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import static org.mockito.Mockito.withSettings;
+
+@ExtendWith(MockitoExtension.class)
+public class StandardNiFiConnectorWebContextTest {
+
+    private static final String CONNECTOR_ID = "test-connector-id";
+    private static final String USER_IDENTITY = "test-user";
+
+    @Mock
+    private ConnectorDAO connectorDAO;
+
+    @Mock
+    private Authorizer authorizer;
+
+    @Mock
+    private AuthorizableLookup authorizableLookup;
+
+    @Mock
+    private ConnectorNode connectorNode;
+
+    @Mock
+    private FrameworkFlowContext workingFlowContext;
+
+    @Mock
+    private FrameworkFlowContext activeFlowContext;
+
+    @Mock
+    private Authorizable connectorAuthorizable;
+
+    @Mock
+    private SecurityContext securityContext;
+
+    @Mock
+    private Authentication authentication;
+
+    private StandardNiFiConnectorWebContext context;
+
+    private TestConnector testConnectorMock;
+    private String lastWrittenValue;
+
+    @BeforeEach
+    void setUp() {
+        context = new StandardNiFiConnectorWebContext();
+        context.setConnectorDAO(connectorDAO);
+        context.setAuthorizer(authorizer);
+        context.setAuthorizableLookup(authorizableLookup);
+
+        testConnectorMock = mock(TestConnector.class, 
withSettings().extraInterfaces(Connector.class).lenient());
+        lenient().when(testConnectorMock.readData()).thenReturn("read-result");
+        
lenient().when(testConnectorMock.writeData(any())).thenAnswer(invocation -> {
+            lastWrittenValue = invocation.getArgument(0);
+            return null;
+        });
+
+        
lenient().when(connectorDAO.getConnector(CONNECTOR_ID)).thenReturn(connectorNode);
+        lenient().when(connectorNode.getConnector()).thenReturn((Connector) 
testConnectorMock);
+        
lenient().when(connectorNode.getWorkingFlowContext()).thenReturn(workingFlowContext);
+        
lenient().when(connectorNode.getActiveFlowContext()).thenReturn(activeFlowContext);
+        
lenient().when(authorizableLookup.getConnector(CONNECTOR_ID)).thenReturn(connectorAuthorizable);
+        
lenient().when(authorizer.authorize(any(AuthorizationRequest.class))).thenReturn(AuthorizationResult.approved());
+
+        final NiFiUser user = new 
StandardNiFiUser.Builder().identity(USER_IDENTITY).build();
+        final NiFiUserDetails userDetails = new NiFiUserDetails(user);
+        lenient().when(authentication.getPrincipal()).thenReturn(userDetails);
+        
lenient().when(securityContext.getAuthentication()).thenReturn(authentication);
+        SecurityContextHolder.setContext(securityContext);
+    }
+
+    @AfterEach
+    void tearDown() {
+        SecurityContextHolder.clearContext();
+    }
+
+    @Test
+    void testGetConnectorWebContextReturnsProxiedConnector() {
+        final ConnectorWebContext<TestConnector> webContext = 
context.getConnectorWebContext(CONNECTOR_ID);
+
+        assertNotNull(webContext);
+        assertNotNull(webContext.connector());
+        assertTrue(Proxy.isProxyClass(webContext.connector().getClass()));
+        assertNotSame(testConnectorMock, webContext.connector());
+    }
+
+    @Test
+    void testGetConnectorWebContextReturnsWrappedFlowContexts() {
+        final ConnectorWebContext<TestConnector> webContext = 
context.getConnectorWebContext(CONNECTOR_ID);
+
+        assertNotNull(webContext.workingFlowContext());
+        assertNotNull(webContext.activeFlowContext());
+        assertTrue(webContext.workingFlowContext() instanceof 
AuthorizingFlowContext);
+        assertTrue(webContext.activeFlowContext() instanceof 
AuthorizingFlowContext);
+    }
+
+    @Test
+    void testGetConnectorWebContextThrowsForNonExistentConnector() {
+        when(connectorDAO.getConnector("non-existent-id")).thenReturn(null);
+
+        final IllegalArgumentException exception = 
assertThrows(IllegalArgumentException.class,
+                () -> context.getConnectorWebContext("non-existent-id"));
+        assertEquals("Unable to find connector with id: non-existent-id", 
exception.getMessage());
+    }
+
+    @Test
+    void testProxiedConnectorEnforcesReadAuthorization() {
+        final ConnectorWebContext<TestConnector> webContext = 
context.getConnectorWebContext(CONNECTOR_ID);
+        final TestConnector proxy = webContext.connector();
+
+        final String result = proxy.readData();
+
+        assertEquals("read-result", result);
+        verify(connectorAuthorizable).authorize(any(Authorizer.class), 
any(RequestAction.class), any(NiFiUser.class));
+    }
+
+    @Test
+    void testProxiedConnectorEnforcesWriteAuthorization() {
+        final ConnectorWebContext<TestConnector> webContext = 
context.getConnectorWebContext(CONNECTOR_ID);
+        final TestConnector proxy = webContext.connector();
+
+        proxy.writeData("test-value");
+
+        assertEquals("test-value", lastWrittenValue);
+        verify(connectorAuthorizable).authorize(any(Authorizer.class), 
any(RequestAction.class), any(NiFiUser.class));
+    }
+
+    @Test
+    void testProxiedConnectorBlocksUnannotatedMethods() {
+        final ConnectorWebContext<TestConnector> webContext = 
context.getConnectorWebContext(CONNECTOR_ID);
+        final TestConnector proxy = webContext.connector();
+
+        assertThrows(IllegalStateException.class, proxy::unannotatedMethod);
+    }
+
+    @Test
+    void testProxiedConnectorPropagatesAuthorizationFailure() {
+        doThrow(new AccessDeniedException("Access denied"))
+                .when(connectorAuthorizable).authorize(any(Authorizer.class), 
any(RequestAction.class), any(NiFiUser.class));
+
+        final ConnectorWebContext<TestConnector> webContext = 
context.getConnectorWebContext(CONNECTOR_ID);
+        final TestConnector proxy = webContext.connector();
+
+        assertThrows(AccessDeniedException.class, proxy::readData);
+    }
+
+    /**
+     * Test interface representing a Connector with annotated methods.
+     * This interface is used with Mockito's extraInterfaces to create a mock
+     * that implements both this interface and Connector.
+     */
+    public interface TestConnector {
+
+        @ConnectorWebMethod(AccessType.READ)
+        String readData();
+
+        @ConnectorWebMethod(AccessType.WRITE)
+        Void writeData(String value);
+
+        void unannotatedMethod();
+    }
+}
+
diff --git 
a/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/web/connector/authorization/AuthorizingConnectorInvocationHandlerTest.java
 
b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/web/connector/authorization/AuthorizingConnectorInvocationHandlerTest.java
new file mode 100644
index 0000000000..e12b7366f9
--- /dev/null
+++ 
b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/web/connector/authorization/AuthorizingConnectorInvocationHandlerTest.java
@@ -0,0 +1,239 @@
+/*
+ * 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.nifi.web.connector.authorization;
+
+import org.apache.nifi.authorization.AccessDeniedException;
+import org.apache.nifi.authorization.AuthorizableLookup;
+import org.apache.nifi.authorization.AuthorizationRequest;
+import org.apache.nifi.authorization.AuthorizationResult;
+import org.apache.nifi.authorization.Authorizer;
+import org.apache.nifi.authorization.RequestAction;
+import org.apache.nifi.authorization.resource.Authorizable;
+import org.apache.nifi.authorization.user.NiFiUser;
+import org.apache.nifi.authorization.user.NiFiUserDetails;
+import org.apache.nifi.authorization.user.StandardNiFiUser;
+import org.apache.nifi.web.ConnectorWebMethod;
+import org.apache.nifi.web.ConnectorWebMethod.AccessType;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.context.SecurityContext;
+import org.springframework.security.core.context.SecurityContextHolder;
+
+import java.lang.reflect.Proxy;
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.lenient;
+import static org.mockito.Mockito.verify;
+
+@ExtendWith(MockitoExtension.class)
+public class AuthorizingConnectorInvocationHandlerTest {
+
+    private static final String CONNECTOR_ID = "test-connector-id";
+    private static final String USER_IDENTITY = "test-user";
+
+    @Mock
+    private Authorizer authorizer;
+
+    @Mock
+    private AuthorizableLookup authorizableLookup;
+
+    @Mock
+    private Authorizable connectorAuthorizable;
+
+    @Mock
+    private SecurityContext securityContext;
+
+    @Mock
+    private Authentication authentication;
+
+    private TestConnectorImpl connectorImpl;
+
+    @BeforeEach
+    void setUp() {
+        connectorImpl = new TestConnectorImpl();
+        
lenient().when(authorizableLookup.getConnector(CONNECTOR_ID)).thenReturn(connectorAuthorizable);
+
+        final NiFiUser user = new 
StandardNiFiUser.Builder().identity(USER_IDENTITY).build();
+        final NiFiUserDetails userDetails = new NiFiUserDetails(user);
+        lenient().when(authentication.getPrincipal()).thenReturn(userDetails);
+        
lenient().when(securityContext.getAuthentication()).thenReturn(authentication);
+        SecurityContextHolder.setContext(securityContext);
+
+        
lenient().when(authorizer.authorize(any(AuthorizationRequest.class))).thenReturn(AuthorizationResult.approved());
+    }
+
+    @AfterEach
+    void tearDown() {
+        SecurityContextHolder.clearContext();
+    }
+
+    @Test
+    void testReadMethodAuthorizesWithReadAction() {
+        final TestConnector proxy = createProxy();
+
+        final String result = proxy.readData();
+
+        assertEquals("read-result", result);
+        final ArgumentCaptor<RequestAction> actionCaptor = 
ArgumentCaptor.forClass(RequestAction.class);
+        verify(connectorAuthorizable).authorize(eq(authorizer), 
actionCaptor.capture(), any(NiFiUser.class));
+        assertEquals(RequestAction.READ, actionCaptor.getValue());
+    }
+
+    @Test
+    void testWriteMethodAuthorizesWithWriteAction() {
+        final TestConnector proxy = createProxy();
+
+        proxy.writeData("test-value");
+
+        assertEquals("test-value", connectorImpl.getLastWrittenValue());
+        final ArgumentCaptor<RequestAction> actionCaptor = 
ArgumentCaptor.forClass(RequestAction.class);
+        verify(connectorAuthorizable).authorize(eq(authorizer), 
actionCaptor.capture(), any(NiFiUser.class));
+        assertEquals(RequestAction.WRITE, actionCaptor.getValue());
+    }
+
+    @Test
+    void testMethodWithoutAnnotationThrowsException() {
+        final TestConnector proxy = createProxy();
+
+        final IllegalStateException exception = 
assertThrows(IllegalStateException.class, proxy::unannotatedMethod);
+        assertEquals(String.format("Method [unannotatedMethod] on connector 
[%s] is not annotated with "
+                + "@ConnectorWebMethod and cannot be invoked through the 
Connector Web Context", CONNECTOR_ID), exception.getMessage());
+    }
+
+    @Test
+    void testAuthorizationFailurePropagatesAccessDeniedException() {
+        doThrow(new AccessDeniedException("Access denied for testing"))
+                .when(connectorAuthorizable).authorize(any(Authorizer.class), 
any(RequestAction.class), any(NiFiUser.class));
+
+        final TestConnector proxy = createProxy();
+
+        assertThrows(AccessDeniedException.class, proxy::readData);
+    }
+
+    @Test
+    void testMethodArgumentsPassedCorrectly() {
+        final TestConnector proxy = createProxy();
+
+        final int result = proxy.processItems(List.of("a", "b", "c"), 10);
+
+        assertEquals(13, result);
+    }
+
+    @Test
+    void testDelegateExceptionUnwrapped() {
+        final TestConnector proxy = createProxy();
+
+        final RuntimeException exception = 
assertThrows(RuntimeException.class, proxy::throwingMethod);
+        assertEquals("Intentional test exception", exception.getMessage());
+    }
+
+    @Test
+    void testReadMethodWithReturnValue() {
+        final TestConnector proxy = createProxy();
+
+        final List<String> result = proxy.getItems();
+
+        assertEquals(List.of("item1", "item2", "item3"), result);
+    }
+
+    private TestConnector createProxy() {
+        final AuthorizingConnectorInvocationHandler<TestConnector> handler = 
new AuthorizingConnectorInvocationHandler<>(
+                connectorImpl, CONNECTOR_ID, authorizer, authorizableLookup);
+
+        return (TestConnector) Proxy.newProxyInstance(
+                TestConnector.class.getClassLoader(),
+                new Class<?>[]{TestConnector.class},
+                handler);
+    }
+
+    /**
+     * Test interface representing a Connector with annotated methods.
+     */
+    public interface TestConnector {
+
+        @ConnectorWebMethod(AccessType.READ)
+        String readData();
+
+        @ConnectorWebMethod(AccessType.WRITE)
+        void writeData(String value);
+
+        @ConnectorWebMethod(AccessType.READ)
+        List<String> getItems();
+
+        @ConnectorWebMethod(AccessType.WRITE)
+        int processItems(List<String> items, int multiplier);
+
+        @ConnectorWebMethod(AccessType.READ)
+        void throwingMethod();
+
+        void unannotatedMethod();
+    }
+
+    /**
+     * Test implementation of the TestConnector interface.
+     */
+    public static class TestConnectorImpl implements TestConnector {
+
+        private String lastWrittenValue;
+
+        @Override
+        public String readData() {
+            return "read-result";
+        }
+
+        @Override
+        public void writeData(final String value) {
+            this.lastWrittenValue = value;
+        }
+
+        @Override
+        public List<String> getItems() {
+            return List.of("item1", "item2", "item3");
+        }
+
+        @Override
+        public int processItems(final List<String> items, final int 
multiplier) {
+            return items.size() + multiplier;
+        }
+
+        @Override
+        public void throwingMethod() {
+            throw new RuntimeException("Intentional test exception");
+        }
+
+        @Override
+        public void unannotatedMethod() {
+            // This method is intentionally not annotated
+        }
+
+        public String getLastWrittenValue() {
+            return lastWrittenValue;
+        }
+    }
+}
+
diff --git 
a/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/web/connector/authorization/AuthorizingFlowContextTest.java
 
b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/web/connector/authorization/AuthorizingFlowContextTest.java
new file mode 100644
index 0000000000..ddc4f277ba
--- /dev/null
+++ 
b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/web/connector/authorization/AuthorizingFlowContextTest.java
@@ -0,0 +1,167 @@
+/*
+ * 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.nifi.web.connector.authorization;
+
+import org.apache.nifi.authorization.AccessDeniedException;
+import org.apache.nifi.authorization.AuthorizableLookup;
+import org.apache.nifi.authorization.AuthorizationRequest;
+import org.apache.nifi.authorization.AuthorizationResult;
+import org.apache.nifi.authorization.Authorizer;
+import org.apache.nifi.authorization.RequestAction;
+import org.apache.nifi.authorization.resource.Authorizable;
+import org.apache.nifi.authorization.user.NiFiUser;
+import org.apache.nifi.authorization.user.NiFiUserDetails;
+import org.apache.nifi.authorization.user.StandardNiFiUser;
+import org.apache.nifi.components.connector.ConnectorConfigurationContext;
+import org.apache.nifi.components.connector.components.FlowContext;
+import org.apache.nifi.components.connector.components.FlowContextType;
+import org.apache.nifi.components.connector.components.ParameterContextFacade;
+import org.apache.nifi.components.connector.components.ProcessGroupFacade;
+import org.apache.nifi.flow.Bundle;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.context.SecurityContext;
+import org.springframework.security.core.context.SecurityContextHolder;
+
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.lenient;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+@ExtendWith(MockitoExtension.class)
+public class AuthorizingFlowContextTest {
+
+    private static final String CONNECTOR_ID = "test-connector-id";
+    private static final String USER_IDENTITY = "test-user";
+
+    @Mock
+    private FlowContext delegate;
+
+    @Mock
+    private Authorizer authorizer;
+
+    @Mock
+    private AuthorizableLookup authorizableLookup;
+
+    @Mock
+    private Authorizable connectorAuthorizable;
+
+    @Mock
+    private ProcessGroupFacade processGroupFacade;
+
+    @Mock
+    private ParameterContextFacade parameterContextFacade;
+
+    @Mock
+    private ConnectorConfigurationContext configurationContext;
+
+    @Mock
+    private SecurityContext securityContext;
+
+    @Mock
+    private Authentication authentication;
+
+    private AuthorizingFlowContext authorizingFlowContext;
+
+    @BeforeEach
+    void setUp() {
+        
when(authorizableLookup.getConnector(CONNECTOR_ID)).thenReturn(connectorAuthorizable);
+        
lenient().when(authorizer.authorize(any(AuthorizationRequest.class))).thenReturn(AuthorizationResult.approved());
+
+        final NiFiUser user = new 
StandardNiFiUser.Builder().identity(USER_IDENTITY).build();
+        final NiFiUserDetails userDetails = new NiFiUserDetails(user);
+        when(authentication.getPrincipal()).thenReturn(userDetails);
+        when(securityContext.getAuthentication()).thenReturn(authentication);
+        SecurityContextHolder.setContext(securityContext);
+
+        final ConnectorAuthorizationContext authContext = new 
ConnectorAuthorizationContext(CONNECTOR_ID, authorizer, authorizableLookup);
+        authorizingFlowContext = new AuthorizingFlowContext(delegate, 
authContext);
+    }
+
+    @AfterEach
+    void tearDown() {
+        SecurityContextHolder.clearContext();
+    }
+
+    @Test
+    void testGetRootGroupAuthorizesReadAndReturnsWrappedFacade() {
+        when(delegate.getRootGroup()).thenReturn(processGroupFacade);
+
+        final ProcessGroupFacade result = 
authorizingFlowContext.getRootGroup();
+
+        assertNotNull(result);
+        assertTrue(result instanceof AuthorizingProcessGroupFacade);
+        verify(connectorAuthorizable).authorize(any(Authorizer.class), 
any(RequestAction.class), any(NiFiUser.class));
+    }
+
+    @Test
+    void testGetParameterContextAuthorizesReadAndReturnsWrappedFacade() {
+        
when(delegate.getParameterContext()).thenReturn(parameterContextFacade);
+
+        final ParameterContextFacade result = 
authorizingFlowContext.getParameterContext();
+
+        assertNotNull(result);
+        assertTrue(result instanceof AuthorizingParameterContextFacade);
+        verify(connectorAuthorizable).authorize(any(Authorizer.class), 
any(RequestAction.class), any(NiFiUser.class));
+    }
+
+    @Test
+    void testGetConfigurationContextAuthorizesRead() {
+        
when(delegate.getConfigurationContext()).thenReturn(configurationContext);
+
+        final ConnectorConfigurationContext result = 
authorizingFlowContext.getConfigurationContext();
+
+        assertNotNull(result);
+        verify(connectorAuthorizable).authorize(any(Authorizer.class), 
any(RequestAction.class), any(NiFiUser.class));
+    }
+
+    @Test
+    void testGetTypeAuthorizesRead() {
+        when(delegate.getType()).thenReturn(FlowContextType.WORKING);
+
+        authorizingFlowContext.getType();
+
+        verify(connectorAuthorizable).authorize(any(Authorizer.class), 
any(RequestAction.class), any(NiFiUser.class));
+    }
+
+    @Test
+    void testGetBundleAuthorizesRead() {
+        when(delegate.getBundle()).thenReturn(new Bundle("group", "artifact", 
"version"));
+
+        authorizingFlowContext.getBundle();
+
+        verify(connectorAuthorizable).authorize(any(Authorizer.class), 
any(RequestAction.class), any(NiFiUser.class));
+    }
+
+    @Test
+    void testAuthorizationFailurePropagates() {
+        doThrow(new AccessDeniedException("Access denied"))
+                .when(connectorAuthorizable).authorize(any(Authorizer.class), 
any(RequestAction.class), any(NiFiUser.class));
+
+        assertThrows(AccessDeniedException.class, () -> 
authorizingFlowContext.getRootGroup());
+    }
+}
+
diff --git 
a/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/web/connector/authorization/AuthorizingParameterContextFacadeTest.java
 
b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/web/connector/authorization/AuthorizingParameterContextFacadeTest.java
new file mode 100644
index 0000000000..6fe3cdfeb3
--- /dev/null
+++ 
b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/web/connector/authorization/AuthorizingParameterContextFacadeTest.java
@@ -0,0 +1,162 @@
+/*
+ * 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.nifi.web.connector.authorization;
+
+import org.apache.nifi.asset.Asset;
+import org.apache.nifi.authorization.AuthorizableLookup;
+import org.apache.nifi.authorization.AuthorizationRequest;
+import org.apache.nifi.authorization.AuthorizationResult;
+import org.apache.nifi.authorization.Authorizer;
+import org.apache.nifi.authorization.RequestAction;
+import org.apache.nifi.authorization.resource.Authorizable;
+import org.apache.nifi.authorization.user.NiFiUser;
+import org.apache.nifi.authorization.user.NiFiUserDetails;
+import org.apache.nifi.authorization.user.StandardNiFiUser;
+import org.apache.nifi.components.connector.components.ParameterContextFacade;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.context.SecurityContext;
+import org.springframework.security.core.context.SecurityContextHolder;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.util.Collections;
+import java.util.Set;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.lenient;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+@ExtendWith(MockitoExtension.class)
+public class AuthorizingParameterContextFacadeTest {
+
+    private static final String CONNECTOR_ID = "test-connector-id";
+    private static final String USER_IDENTITY = "test-user";
+
+    @Mock
+    private ParameterContextFacade delegate;
+
+    @Mock
+    private Authorizer authorizer;
+
+    @Mock
+    private AuthorizableLookup authorizableLookup;
+
+    @Mock
+    private Authorizable connectorAuthorizable;
+
+    @Mock
+    private SecurityContext securityContext;
+
+    @Mock
+    private Authentication authentication;
+
+    @Mock
+    private Asset asset;
+
+    private AuthorizingParameterContextFacade authorizingFacade;
+
+    @BeforeEach
+    void setUp() {
+        
when(authorizableLookup.getConnector(CONNECTOR_ID)).thenReturn(connectorAuthorizable);
+        
lenient().when(authorizer.authorize(any(AuthorizationRequest.class))).thenReturn(AuthorizationResult.approved());
+
+        final NiFiUser user = new 
StandardNiFiUser.Builder().identity(USER_IDENTITY).build();
+        final NiFiUserDetails userDetails = new NiFiUserDetails(user);
+        when(authentication.getPrincipal()).thenReturn(userDetails);
+        when(securityContext.getAuthentication()).thenReturn(authentication);
+        SecurityContextHolder.setContext(securityContext);
+
+        final ConnectorAuthorizationContext authContext = new 
ConnectorAuthorizationContext(CONNECTOR_ID, authorizer, authorizableLookup);
+        authorizingFacade = new AuthorizingParameterContextFacade(delegate, 
authContext);
+    }
+
+    @AfterEach
+    void tearDown() {
+        SecurityContextHolder.clearContext();
+    }
+
+    @Test
+    void testGetValueAuthorizesWithReadAction() {
+        when(delegate.getValue("param1")).thenReturn("value1");
+
+        final String result = authorizingFacade.getValue("param1");
+
+        assertEquals("value1", result);
+        final ArgumentCaptor<RequestAction> actionCaptor = 
ArgumentCaptor.forClass(RequestAction.class);
+        verify(connectorAuthorizable).authorize(eq(authorizer), 
actionCaptor.capture(), any(NiFiUser.class));
+        assertEquals(RequestAction.READ, actionCaptor.getValue());
+    }
+
+    @Test
+    void testGetDefinedParameterNamesAuthorizesWithReadAction() {
+        when(delegate.getDefinedParameterNames()).thenReturn(Set.of("param1", 
"param2"));
+
+        final Set<String> result = 
authorizingFacade.getDefinedParameterNames();
+
+        assertEquals(Set.of("param1", "param2"), result);
+        final ArgumentCaptor<RequestAction> actionCaptor = 
ArgumentCaptor.forClass(RequestAction.class);
+        verify(connectorAuthorizable).authorize(eq(authorizer), 
actionCaptor.capture(), any(NiFiUser.class));
+        assertEquals(RequestAction.READ, actionCaptor.getValue());
+    }
+
+    @Test
+    void testIsSensitiveAuthorizesWithReadAction() {
+        when(delegate.isSensitive("param1")).thenReturn(true);
+
+        final boolean result = authorizingFacade.isSensitive("param1");
+
+        assertTrue(result);
+        final ArgumentCaptor<RequestAction> actionCaptor = 
ArgumentCaptor.forClass(RequestAction.class);
+        verify(connectorAuthorizable).authorize(eq(authorizer), 
actionCaptor.capture(), any(NiFiUser.class));
+        assertEquals(RequestAction.READ, actionCaptor.getValue());
+    }
+
+    @Test
+    void testUpdateParametersAuthorizesWithWriteAction() {
+        authorizingFacade.updateParameters(Collections.emptyList());
+
+        final ArgumentCaptor<RequestAction> actionCaptor = 
ArgumentCaptor.forClass(RequestAction.class);
+        verify(connectorAuthorizable).authorize(eq(authorizer), 
actionCaptor.capture(), any(NiFiUser.class));
+        assertEquals(RequestAction.WRITE, actionCaptor.getValue());
+        verify(delegate).updateParameters(Collections.emptyList());
+    }
+
+    @Test
+    void testCreateAssetAuthorizesWithWriteAction() throws IOException {
+        final ByteArrayInputStream inputStream = new ByteArrayInputStream(new 
byte[0]);
+        when(delegate.createAsset(inputStream)).thenReturn(asset);
+
+        final Asset result = authorizingFacade.createAsset(inputStream);
+
+        assertEquals(asset, result);
+        final ArgumentCaptor<RequestAction> actionCaptor = 
ArgumentCaptor.forClass(RequestAction.class);
+        verify(connectorAuthorizable).authorize(eq(authorizer), 
actionCaptor.capture(), any(NiFiUser.class));
+        assertEquals(RequestAction.WRITE, actionCaptor.getValue());
+    }
+}
+


Reply via email to