This is an automated email from the ASF dual-hosted git repository.
mcgilman pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/nifi.git
The following commit(s) were added to refs/heads/main by this push:
new f30f877fb3e NIFI-15875 Standardize Authorization for Verify
Configuration Methods (#11179)
f30f877fb3e is described below
commit f30f877fb3e0e2a0db7850be8b6d586203860a45
Author: David Handermann <[email protected]>
AuthorDate: Fri May 1 09:28:47 2026 -0500
NIFI-15875 Standardize Authorization for Verify Configuration Methods
(#11179)
* NIFI-15875 Standardized Authorization for Verify Configuration Methods
- Added AuthorizeConfigVerification class for shared component
authorization handling
* NIFI-15875 Added Parameter Reference authorization
---
.../authorization/AuthorizeConfigVerification.java | 84 +++++++++++
.../apache/nifi/web/api/ControllerResource.java | 15 +-
.../nifi/web/api/ControllerServiceResource.java | 13 +-
.../nifi/web/api/ParameterProviderResource.java | 13 +-
.../org/apache/nifi/web/api/ProcessorResource.java | 13 +-
.../apache/nifi/web/api/ReportingTaskResource.java | 13 +-
.../AuthorizeConfigVerificationTest.java | 154 +++++++++++++++++++++
7 files changed, 266 insertions(+), 39 deletions(-)
diff --git
a/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/authorization/AuthorizeConfigVerification.java
b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/authorization/AuthorizeConfigVerification.java
new file mode 100644
index 00000000000..cb37b90d0d3
--- /dev/null
+++
b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/authorization/AuthorizeConfigVerification.java
@@ -0,0 +1,84 @@
+/*
+ * 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.authorization;
+
+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.parameter.ParameterContext;
+
+import java.util.Map;
+
+/**
+ * Authorizes configuration-verification requests for any component type that
supports verification.
+ * Requires WRITE on the target component, plus READ on any Controller
Services referenced by
+ * the proposed properties.
+ */
+public final class AuthorizeConfigVerification {
+
+ private AuthorizeConfigVerification() {
+ }
+
+ /**
+ * Authorize a configuration-verification request against a single
ComponentAuthorizable
+ *
+ * @param authorizer Authorizer used for determining results
+ * @param lookup Authorizable Lookup used to resolve referenced Controller
Services
+ * @param component Component whose configuration is being verified
+ * @param proposedProperties Properties submitted for verification
+ */
+ public static void authorize(
+ final Authorizer authorizer,
+ final AuthorizableLookup lookup,
+ final ComponentAuthorizable component,
+ final Map<String, String> proposedProperties
+ ) {
+ authorize(authorizer, lookup, component, proposedProperties, null);
+ }
+
+ /**
+ * Authorize a configuration-verification request against a single
ComponentAuthorizable with an optional ancestor
+ *
+ * @param authorizer Authorizer used for determining results
+ * @param lookup Authorizable Lookup used to resolve referenced Controller
Services
+ * @param component Component whose configuration is being verified
+ * @param proposedProperties Properties submitted for verification
+ * @param ancestor optional parent Authorizable that must also be
authorized
+ */
+ public static void authorize(
+ final Authorizer authorizer,
+ final AuthorizableLookup lookup,
+ final ComponentAuthorizable component,
+ final Map<String, String> proposedProperties,
+ final Authorizable ancestor
+ ) {
+ final NiFiUser user = NiFiUserUtils.getNiFiUser();
+
+ if (ancestor != null) {
+ ancestor.authorize(authorizer, RequestAction.WRITE, user);
+ }
+
+ component.getAuthorizable().authorize(authorizer, RequestAction.WRITE,
user);
+
+
AuthorizeControllerServiceReference.authorizeControllerServiceReferences(proposedProperties,
component, authorizer, lookup);
+
+ final ParameterContext parameterContext =
component.getParameterContext();
+ if (parameterContext != null) {
+
AuthorizeParameterReference.authorizeParameterReferences(proposedProperties,
authorizer, parameterContext, user);
+ }
+ }
+}
diff --git
a/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ControllerResource.java
b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ControllerResource.java
index c552084bdb1..78f4c97a048 100644
---
a/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ControllerResource.java
+++
b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ControllerResource.java
@@ -41,6 +41,7 @@ import jakarta.ws.rs.core.Response;
import jakarta.ws.rs.core.StreamingOutput;
import org.apache.commons.lang3.StringUtils;
import org.apache.nifi.authorization.AuthorizeComponentReference;
+import org.apache.nifi.authorization.AuthorizeConfigVerification;
import org.apache.nifi.authorization.AuthorizeControllerServiceReference;
import org.apache.nifi.authorization.Authorizer;
import org.apache.nifi.authorization.ComponentAuthorizable;
@@ -1252,7 +1253,9 @@ public class ControllerResource extends
ApplicationResource {
"issuing a GET request to
/flow-analysis-rules/{taskId}/verification-requests/{requestId}. Once the
request is completed, the client is expected to issue a DELETE request to " +
"/flow-analysis-rules/{serviceId}/verification-requests/{requestId}.",
security = {
- @SecurityRequirement(name = "Read -
/flow-analysis-rules/{uuid}")
+ @SecurityRequirement(name = "Write - /controller"),
+ @SecurityRequirement(name = "Write -
/flow-analysis-rules/{uuid}"),
+ @SecurityRequirement(name = "Read - any referenced
Controller Services - /controller-services/{uuid}")
}
)
public Response submitFlowAnalysisRuleConfigVerificationRequest(
@@ -1286,7 +1289,7 @@ public class ControllerResource extends
ApplicationResource {
return withWriteLock(
serviceFacade,
flowAnalysisRuleConfigRequest,
- lookup -> authorizeController(RequestAction.READ),
+ lookup -> AuthorizeConfigVerification.authorize(authorizer,
lookup, lookup.getFlowAnalysisRule(flowAnalysisRuleId),
requestDto.getProperties(), lookup.getController()),
() ->
serviceFacade.verifyCanVerifyFlowAnalysisRuleConfig(flowAnalysisRuleId),
entity ->
performAsyncFlowAnalysisRuleConfigVerification(entity, user)
);
@@ -1596,7 +1599,8 @@ public class ControllerResource extends
ApplicationResource {
+
"/controller/registry-clients/{clientId}/config/verification-requests/{requestId}
for status and "
+ "DELETE the request once verification completes.",
security = {
- @SecurityRequirement(name = "Read - /controller")
+ @SecurityRequirement(name = "Write -
/controller/registry-clients/{uuid}"),
+ @SecurityRequirement(name = "Read - any referenced
Controller Services - /controller-services/{uuid}")
}
)
public Response submitRegistryClientConfigVerificationRequest(
@@ -1630,10 +1634,7 @@ public class ControllerResource extends
ApplicationResource {
return withWriteLock(
serviceFacade,
registryClientConfigRequest,
- lookup -> {
- final Authorizable authorizable =
lookup.getFlowRegistryClient(registryClientId).getAuthorizable();
- authorizable.authorize(authorizer, RequestAction.READ,
user);
- },
+ lookup -> AuthorizeConfigVerification.authorize(authorizer,
lookup, lookup.getFlowRegistryClient(registryClientId),
requestDto.getProperties()),
() ->
serviceFacade.verifyCanVerifyFlowRegistryClientConfig(registryClientId),
entity -> performAsyncRegistryClientConfigVerification(entity,
user)
);
diff --git
a/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ControllerServiceResource.java
b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ControllerServiceResource.java
index 5e4110224a2..64fb165b429 100644
---
a/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ControllerServiceResource.java
+++
b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ControllerServiceResource.java
@@ -40,6 +40,7 @@ import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
import org.apache.commons.lang3.StringUtils;
import org.apache.nifi.authorization.AuthorizeComponentReference;
+import org.apache.nifi.authorization.AuthorizeConfigVerification;
import org.apache.nifi.authorization.AuthorizeControllerServiceReference;
import org.apache.nifi.authorization.Authorizer;
import org.apache.nifi.authorization.ComponentAuthorizable;
@@ -993,7 +994,8 @@ public class ControllerServiceResource extends
ApplicationResource {
"issuing a GET request to
/controller-services/{serviceId}/verification-requests/{requestId}. Once the
request is completed, the client is expected to issue a DELETE request to " +
"/controller-services/{serviceId}/verification-requests/{requestId}.",
security = {
- @SecurityRequirement(name = "Read -
/controller-services/{uuid}")
+ @SecurityRequirement(name = "Write -
/controller-services/{uuid}"),
+ @SecurityRequirement(name = "Read - any referenced
Controller Services - /controller-services/{uuid}")
}
)
public Response submitConfigVerificationRequest(
@@ -1027,13 +1029,8 @@ public class ControllerServiceResource extends
ApplicationResource {
return withWriteLock(
serviceFacade,
controllerServiceConfigRequest,
- lookup -> {
- final ComponentAuthorizable controllerService =
lookup.getControllerService(controllerServiceId);
- controllerService.getAuthorizable().authorize(authorizer,
RequestAction.READ, NiFiUserUtils.getNiFiUser());
- },
- () -> {
-
serviceFacade.verifyCanVerifyControllerServiceConfig(controllerServiceId);
- },
+ lookup -> AuthorizeConfigVerification.authorize(authorizer,
lookup, lookup.getControllerService(controllerServiceId),
requestDto.getProperties()),
+ () ->
serviceFacade.verifyCanVerifyControllerServiceConfig(controllerServiceId),
entity -> performAsyncConfigVerification(entity, user)
);
}
diff --git
a/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ParameterProviderResource.java
b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ParameterProviderResource.java
index 23e209d5065..71eda3b6675 100644
---
a/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ParameterProviderResource.java
+++
b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ParameterProviderResource.java
@@ -43,6 +43,7 @@ import jakarta.ws.rs.core.Response;
import org.apache.commons.lang3.StringUtils;
import org.apache.nifi.authorization.AuthorizableLookup;
import org.apache.nifi.authorization.AuthorizeComponentReference;
+import org.apache.nifi.authorization.AuthorizeConfigVerification;
import org.apache.nifi.authorization.AuthorizeControllerServiceReference;
import org.apache.nifi.authorization.Authorizer;
import org.apache.nifi.authorization.ComponentAuthorizable;
@@ -1227,7 +1228,8 @@ public class ParameterProviderResource extends
AbstractParameterResource {
"issuing a GET request to
/parameter-providers/{serviceId}/verification-requests/{requestId}. Once the
request is completed, the client is expected to issue a DELETE request to " +
"/parameter-providers/{providerId}/verification-requests/{requestId}.",
security = {
- @SecurityRequirement(name = "Read -
/parameter-providers/{uuid}")
+ @SecurityRequirement(name = "Write -
/parameter-providers/{uuid}"),
+ @SecurityRequirement(name = "Read - any referenced
Controller Services - /controller-services/{uuid}")
}
)
public Response submitConfigVerificationRequest(
@@ -1260,13 +1262,8 @@ public class ParameterProviderResource extends
AbstractParameterResource {
return withWriteLock(
serviceFacade,
parameterProviderConfigRequest,
- lookup -> {
- final ComponentAuthorizable parameterProvider =
lookup.getParameterProvider(parameterProviderId);
- parameterProvider.getAuthorizable().authorize(authorizer,
RequestAction.READ, NiFiUserUtils.getNiFiUser());
- },
- () -> {
-
serviceFacade.verifyCanVerifyParameterProviderConfig(parameterProviderId);
- },
+ lookup -> AuthorizeConfigVerification.authorize(authorizer,
lookup, lookup.getParameterProvider(parameterProviderId),
requestDto.getProperties()),
+ () ->
serviceFacade.verifyCanVerifyParameterProviderConfig(parameterProviderId),
entity -> performAsyncConfigVerification(entity, user)
);
}
diff --git
a/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ProcessorResource.java
b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ProcessorResource.java
index 4a10f70adaa..0a6192cd407 100644
---
a/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ProcessorResource.java
+++
b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ProcessorResource.java
@@ -40,6 +40,7 @@ import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
import org.apache.commons.lang3.StringUtils;
import org.apache.nifi.authorization.AuthorizeComponentReference;
+import org.apache.nifi.authorization.AuthorizeConfigVerification;
import org.apache.nifi.authorization.AuthorizeControllerServiceReference;
import org.apache.nifi.authorization.Authorizer;
import org.apache.nifi.authorization.ComponentAuthorizable;
@@ -703,7 +704,8 @@ public class ProcessorResource extends ApplicationResource {
"issuing a GET request to
/processors/{processorId}/verification-requests/{requestId}. Once the request
is completed, the client is expected to issue a DELETE request to " +
"/processors/{processorId}/verification-requests/{requestId}.",
security = {
- @SecurityRequirement(name = "Read - /processors/{uuid}")
+ @SecurityRequirement(name = "Write - /processors/{uuid}"),
+ @SecurityRequirement(name = "Read - any referenced
Controller Services - /controller-services/{uuid}")
}
)
public Response submitProcessorVerificationRequest(
@@ -736,13 +738,8 @@ public class ProcessorResource extends ApplicationResource
{
return withWriteLock(
serviceFacade,
processorConfigRequest,
- lookup -> {
- final ComponentAuthorizable processor =
lookup.getProcessor(processorId);
- processor.getAuthorizable().authorize(authorizer,
RequestAction.READ, NiFiUserUtils.getNiFiUser());
- },
- () -> {
- serviceFacade.verifyCanVerifyProcessorConfig(processorId);
- },
+ lookup -> AuthorizeConfigVerification.authorize(authorizer,
lookup, lookup.getProcessor(processorId), requestDto.getProperties()),
+ () ->
serviceFacade.verifyCanVerifyProcessorConfig(processorId),
entity -> performAsyncConfigVerification(entity, user)
);
}
diff --git
a/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ReportingTaskResource.java
b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ReportingTaskResource.java
index 43048f161d1..a486d6e8704 100644
---
a/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ReportingTaskResource.java
+++
b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ReportingTaskResource.java
@@ -40,6 +40,7 @@ import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
import org.apache.commons.lang3.StringUtils;
import org.apache.nifi.authorization.AuthorizeComponentReference;
+import org.apache.nifi.authorization.AuthorizeConfigVerification;
import org.apache.nifi.authorization.AuthorizeControllerServiceReference;
import org.apache.nifi.authorization.Authorizer;
import org.apache.nifi.authorization.ComponentAuthorizable;
@@ -795,7 +796,8 @@ public class ReportingTaskResource extends
ApplicationResource {
"issuing a GET request to
/reporting-tasks/{taskId}/verification-requests/{requestId}. Once the request
is completed, the client is expected to issue a DELETE request to " +
"/reporting-tasks/{serviceId}/verification-requests/{requestId}.",
security = {
- @SecurityRequirement(name = "Read -
/reporting-tasks/{uuid}")
+ @SecurityRequirement(name = "Write -
/reporting-tasks/{uuid}"),
+ @SecurityRequirement(name = "Read - any referenced
Controller Services - /controller-services/{uuid}")
}
)
public Response submitConfigVerificationRequest(
@@ -828,13 +830,8 @@ public class ReportingTaskResource extends
ApplicationResource {
return withWriteLock(
serviceFacade,
reportingTaskConfigRequest,
- lookup -> {
- final ComponentAuthorizable reportingTask =
lookup.getReportingTask(reportingTaskId);
- reportingTask.getAuthorizable().authorize(authorizer,
RequestAction.READ, NiFiUserUtils.getNiFiUser());
- },
- () -> {
-
serviceFacade.verifyCanVerifyReportingTaskConfig(reportingTaskId);
- },
+ lookup -> AuthorizeConfigVerification.authorize(authorizer,
lookup, lookup.getReportingTask(reportingTaskId), requestDto.getProperties()),
+ () ->
serviceFacade.verifyCanVerifyReportingTaskConfig(reportingTaskId),
entity -> performAsyncConfigVerification(entity, user)
);
}
diff --git
a/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/authorization/AuthorizeConfigVerificationTest.java
b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/authorization/AuthorizeConfigVerificationTest.java
new file mode 100644
index 00000000000..e87f0dd64fb
--- /dev/null
+++
b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/authorization/AuthorizeConfigVerificationTest.java
@@ -0,0 +1,154 @@
+/*
+ * 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.authorization;
+
+import org.apache.nifi.authorization.resource.Authorizable;
+import org.apache.nifi.components.PropertyDescriptor;
+import org.apache.nifi.controller.AbstractControllerService;
+import org.apache.nifi.parameter.ParameterContext;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+import java.util.Map;
+
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+@ExtendWith(MockitoExtension.class)
+class AuthorizeConfigVerificationTest {
+
+ private static final String PROPERTY = "URL";
+ private static final String PARAMETER_REFERENCE = "#{url}";
+ private static final String CONTROLLER_SERVICE_ID =
"controller-service-id";
+
+ @Mock
+ private Authorizer authorizer;
+
+ @Mock
+ private AuthorizableLookup authorizableLookup;
+
+ @Mock
+ private ComponentAuthorizable componentAuthorizable;
+
+ @Mock
+ private Authorizable componentAuthorizableDelegate;
+
+ @Mock
+ private Authorizable ancestor;
+
+ @Mock
+ private PropertyDescriptor propertyDescriptor;
+
+ @Mock
+ private ParameterContext parameterContext;
+
+ @Mock
+ private ComponentAuthorizable controllerService;
+
+ @Mock
+ private Authorizable controllerServiceAuthorizable;
+
+ @Test
+ void testAuthorizeComponentWriteApproved() {
+
when(componentAuthorizable.getAuthorizable()).thenReturn(componentAuthorizableDelegate);
+
+ AuthorizeConfigVerification.authorize(authorizer, authorizableLookup,
componentAuthorizable, Map.of());
+
+ verify(componentAuthorizableDelegate).authorize(eq(authorizer),
eq(RequestAction.WRITE), any());
+ verify(ancestor, never()).authorize(any(), any(), any());
+ }
+
+ @Test
+ void testAuthorizeComponentAndAncestorWriteApproved() {
+
when(componentAuthorizable.getAuthorizable()).thenReturn(componentAuthorizableDelegate);
+
+ AuthorizeConfigVerification.authorize(authorizer, authorizableLookup,
componentAuthorizable, Map.of(), ancestor);
+
+ verify(ancestor).authorize(eq(authorizer), eq(RequestAction.WRITE),
any());
+ verify(componentAuthorizableDelegate).authorize(eq(authorizer),
eq(RequestAction.WRITE), any());
+ }
+
+ @Test
+ void testAuthorizeComponentWriteParameterContextApproved() {
+
when(componentAuthorizable.getAuthorizable()).thenReturn(componentAuthorizableDelegate);
+
when(componentAuthorizable.getParameterContext()).thenReturn(parameterContext);
+
when(componentAuthorizable.getPropertyDescriptor(eq(PROPERTY))).thenReturn(propertyDescriptor);
+
+ final Map<String, String> properties = Map.of(PROPERTY,
PARAMETER_REFERENCE);
+ AuthorizeConfigVerification.authorize(authorizer, authorizableLookup,
componentAuthorizable, properties);
+
+ verify(componentAuthorizableDelegate).authorize(eq(authorizer),
eq(RequestAction.WRITE), any());
+ verify(ancestor, never()).authorize(any(), any(), any());
+ verify(parameterContext).authorize(eq(authorizer),
eq(RequestAction.READ), any());
+ }
+
+ @Test
+ void testAuthorizeComponentWriteControllerServiceApproved() {
+
when(componentAuthorizable.getAuthorizable()).thenReturn(componentAuthorizableDelegate);
+
when(componentAuthorizable.getParameterContext()).thenReturn(parameterContext);
+
when(componentAuthorizable.getPropertyDescriptor(eq(PROPERTY))).thenReturn(propertyDescriptor);
+
doReturn(MockControllerService.class).when(propertyDescriptor).getControllerServiceDefinition();
+
when(authorizableLookup.getControllerService(eq(CONTROLLER_SERVICE_ID))).thenReturn(controllerService);
+
when(controllerService.getAuthorizable()).thenReturn(controllerServiceAuthorizable);
+
+ final Map<String, String> properties = Map.of(PROPERTY,
CONTROLLER_SERVICE_ID);
+ AuthorizeConfigVerification.authorize(authorizer, authorizableLookup,
componentAuthorizable, properties);
+
+ verify(componentAuthorizableDelegate).authorize(eq(authorizer),
eq(RequestAction.WRITE), any());
+ verify(ancestor, never()).authorize(any(), any(), any());
+ verify(controllerServiceAuthorizable).authorize(eq(authorizer),
eq(RequestAction.READ), any());
+ }
+
+ @Test
+ void testAuthorizeNullAncestorSkipped() {
+
when(componentAuthorizable.getAuthorizable()).thenReturn(componentAuthorizableDelegate);
+
+ AuthorizeConfigVerification.authorize(authorizer, authorizableLookup,
componentAuthorizable, Map.of(), null);
+
+ verify(componentAuthorizableDelegate).authorize(eq(authorizer),
eq(RequestAction.WRITE), any());
+ verify(ancestor, never()).authorize(any(), any(), any());
+ }
+
+ @Test
+ void testAuthorizeComponentWriteDenied() {
+
when(componentAuthorizable.getAuthorizable()).thenReturn(componentAuthorizableDelegate);
+ doThrow(new
AccessDeniedException("denied")).when(componentAuthorizableDelegate).authorize(eq(authorizer),
eq(RequestAction.WRITE), any());
+
+ assertThrows(AccessDeniedException.class, () ->
AuthorizeConfigVerification.authorize(authorizer, authorizableLookup,
componentAuthorizable, Map.of()));
+ }
+
+ @Test
+ void testAuthorizeAncestorWriteDenied() {
+ doThrow(new
AccessDeniedException("denied")).when(ancestor).authorize(eq(authorizer),
eq(RequestAction.WRITE), any());
+
+ assertThrows(AccessDeniedException.class, () ->
AuthorizeConfigVerification.authorize(authorizer, authorizableLookup,
componentAuthorizable, Map.of(), ancestor));
+
+ verify(componentAuthorizable, never()).getAuthorizable();
+ }
+
+ private static class MockControllerService extends
AbstractControllerService {
+
+ }
+}