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

pvillard pushed a commit to branch support/nifi-1.x
in repository https://gitbox.apache.org/repos/asf/nifi.git


The following commit(s) were added to refs/heads/support/nifi-1.x by this push:
     new e5a0b58aa4 NIFI-12037 Update AzureUserGroupProvider to allow 
configuration of the graph endpoint and API scope to support regional clouds.
e5a0b58aa4 is described below

commit e5a0b58aa42a410848a073b017bf9960ecd3ff1d
Author: Justin <22872623+ren...@users.noreply.github.com>
AuthorDate: Fri Sep 8 17:53:56 2023 -0600

    NIFI-12037 Update AzureUserGroupProvider to allow configuration of the 
graph endpoint and API scope to support regional clouds.
    
    Signed-off-by: Pierre Villard <pierre.villard...@gmail.com>
    
    This closes #7675.
---
 .../src/main/asciidoc/administration-guide.adoc    |  4 +-
 .../azure/AzureGraphUserGroupProvider.java         | 46 +++++++++++++---------
 .../azure/ClientCredentialAuthProvider.java        | 34 ++++++++++------
 3 files changed, 54 insertions(+), 30 deletions(-)

diff --git a/nifi-docs/src/main/asciidoc/administration-guide.adoc 
b/nifi-docs/src/main/asciidoc/administration-guide.adoc
index 7e81718e57..530e36781a 100644
--- a/nifi-docs/src/main/asciidoc/administration-guide.adoc
+++ b/nifi-docs/src/main/asciidoc/administration-guide.adoc
@@ -801,7 +801,9 @@ The AzureGraphUserGroupProvider has the following 
properties:
 
|==================================================================================================================================================
 | Property Name | Description
 |`Refresh Delay` | Duration of delay between each user and group refresh. 
Default is `5 mins`.
-|`Authority Endpoint` | The endpoint of the Azure AD login. This can be found 
in the Azure portal under Azure Active Directory -> App registrations -> 
[application name] -> Endpoints. For example, the global authority endpoint is 
https://login.microsoftonline.com.
+|`Authority Endpoint` | The endpoint of the Azure AD login. This can be found 
in the Azure portal under Azure Active Directory -> App registrations -> 
[application name] -> Endpoints. For example, the global authority endpoint is 
`https://login.microsoftonline.com`.
+|`Graph Endpoint` | The endpoint of the Azure Graph API, with the version 
identifier attached. The base url can be found in the Azure portal under Azure 
Active Directory -> App registrations -> [application name] -> Endpoints. For 
example, the global graph endpoint is `https://graph.microsoft.com/v1.0`, which 
is also the default setting.
+|`Graph Scope` | The url for the Graph api scope.  See 
https://learn.microsoft.com/en-us/azure/active-directory/develop/scopes-oidc 
for an explanation of scopes.  This usually only needs to be changed if you are 
connecting to a different `Graph Endpoint`.  The Azure global default scope is 
`https://graph.microsoft.com/.default`, which is also the default setting.
 |`Directory ID` | Tenant ID or Directory ID of the Azure AD tenant. This can 
be found in the Azure portal under Azure Active Directory -> App registrations 
-> [application name] -> Directory (tenant) ID.
 |`Application ID` | Client ID or Application ID of the Azure app registration. 
This can be found in the Azure portal under Azure Active Directory -> App 
registrations -> [application name] -> Overview -> Application (client) ID.
 |`Client Secret` | A client secret from the Azure app registration. Secrets 
can be created in the Azure portal under Azure Active Directory -> App 
registrations -> [application name] -> Certificates & secrets -> Client secrets 
-> [+] New client secret.
diff --git 
a/nifi-nar-bundles/nifi-azure-bundle/nifi-azure-graph-authorizer/src/main/java/org/apache/nifi/authorization/azure/AzureGraphUserGroupProvider.java
 
b/nifi-nar-bundles/nifi-azure-bundle/nifi-azure-graph-authorizer/src/main/java/org/apache/nifi/authorization/azure/AzureGraphUserGroupProvider.java
index 6a45cfe1f1..0bffadd35f 100644
--- 
a/nifi-nar-bundles/nifi-azure-bundle/nifi-azure-graph-authorizer/src/main/java/org/apache/nifi/authorization/azure/AzureGraphUserGroupProvider.java
+++ 
b/nifi-nar-bundles/nifi-azure-bundle/nifi-azure-graph-authorizer/src/main/java/org/apache/nifi/authorization/azure/AzureGraphUserGroupProvider.java
@@ -56,12 +56,13 @@ import org.apache.nifi.components.PropertyValue;
 import org.apache.nifi.util.FormatUtils;
 import org.apache.nifi.util.StopWatch;
 import org.apache.nifi.util.StringUtils;
+import org.jetbrains.annotations.NotNull;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 /**
  * The AzureGraphUserGroupProvider provides support for retrieving users and
- * groups from Azure Activy Driectory (AAD) using graph rest-api & SDK.
+ * groups from Azure Active Directory (AAD) using graph rest-api & SDK.
  */
 public class AzureGraphUserGroupProvider implements UserGroupProvider {
     private final static Logger logger = 
LoggerFactory.getLogger(AzureGraphUserGroupProvider.class);
@@ -73,6 +74,8 @@ public class AzureGraphUserGroupProvider implements 
UserGroupProvider {
     public static final String REFRESH_DELAY_PROPERTY = "Refresh Delay";
     private static final long MINIMUM_SYNC_INTERVAL_MILLISECONDS = 10_000;
     public static final String AUTHORITY_ENDPOINT_PROPERTY = "Authority 
Endpoint";
+    public static final String GRAPH_ENDPOINT_PROPERTY  = "Graph Endpoint";
+    public static final String GRAPH_SCOPE_PROPERTY = "Graph Scope";
     public static final String TENANT_ID_PROPERTY = "Directory ID";
     public static final String APP_REG_CLIENT_ID_PROPERTY = "Application ID";
     public static final String APP_REG_CLIENT_SECRET_PROPERTY = "Client 
Secret";
@@ -80,9 +83,9 @@ public class AzureGraphUserGroupProvider implements 
UserGroupProvider {
     public static final String GROUP_FILTER_LIST_PROPERTY = "Group Filter List 
Inclusion";
     // group filter with startswith
     public static final String GROUP_FILTER_PREFIX_PROPERTY = "Group Filter 
Prefix";
-    // client side group filter 'endswith' operator, due to support limiation 
of azure graph rest-api
+    // client side group filter 'endswith' operator, due to support limitation 
of azure graph rest-api
     public static final String GROUP_FILTER_SUFFIX_PROPERTY = "Group Filter 
Suffix";
-    // client side group filter 'contains' operator, due to support limiation 
of azure graph rest-api
+    // client side group filter 'contains' operator, due to support limitation 
of azure graph rest-api
     public static final String GROUP_FILTER_SUBSTRING_PROPERTY = "Group Filter 
Substring";
     public static final String PAGE_SIZE_PROPERTY = "Page Size";
     // default: upn (or userPrincipalName). possible choices ['upn', 'email']
@@ -93,12 +96,12 @@ public class AzureGraphUserGroupProvider implements 
UserGroupProvider {
     public static final String DEFAULT_CLAIM_FOR_USERNAME = "upn";
     public static final int MAX_PAGE_SIZE = 999;
     public static final String AZURE_PUBLIC_CLOUD = 
"https://login.microsoftonline.com/";;
+    public static final String AZURE_PUBLIC_GRAPH_DEFAULT_SCOPE = 
"https://graph.microsoft.com/.default";;
     static final List<String> REST_CALL_KEYWORDS = Arrays.asList("$select", 
"$top", "$expand", "$search", "$filter", "$format", "$count", "$skip", 
"$orderby");
 
 
-    private ClientCredentialAuthProvider authProvider;
     private IGraphServiceClient graphClient;
-    private final AtomicReference<ImmutableAzureGraphUserGroup> 
azureGraphUserGroupRef = new AtomicReference<ImmutableAzureGraphUserGroup>();
+    private final AtomicReference<ImmutableAzureGraphUserGroup> 
azureGraphUserGroupRef = new AtomicReference<>();
 
     @Override
     public Group getGroup(String identifier) throws 
AuthorizationAccessException {
@@ -135,7 +138,7 @@ public class AzureGraphUserGroupProvider implements 
UserGroupProvider {
             throws AuthorizerCreationException {
         this.scheduler = Executors.newSingleThreadScheduledExecutor(new 
ThreadFactory() {
             @Override
-            public Thread newThread(Runnable r) {
+            public Thread newThread(@NotNull Runnable r) {
                 final Thread thread = 
Executors.defaultThreadFactory().newThread(r);
                 thread.setName(String.format("%s (%s) - UserGroup Refresh", 
getClass().getSimpleName(), initializationContext.getIdentifier()));
                 return thread;
@@ -179,6 +182,8 @@ public class AzureGraphUserGroupProvider implements 
UserGroupProvider {
     public void onConfigured(AuthorizerConfigurationContext 
configurationContext) throws AuthorizerCreationException {
         final long fixedDelay = getDelayProperty(configurationContext, 
REFRESH_DELAY_PROPERTY, DEFAULT_REFRESH_DELAY);
         final String authorityEndpoint = getProperty(configurationContext, 
AUTHORITY_ENDPOINT_PROPERTY, AZURE_PUBLIC_CLOUD);
+        final String graphEndpoint = getProperty(configurationContext, 
GRAPH_ENDPOINT_PROPERTY, null);
+        final String graphScope = getProperty(configurationContext, 
GRAPH_SCOPE_PROPERTY, AZURE_PUBLIC_GRAPH_DEFAULT_SCOPE);
         final String tenantId = getProperty(configurationContext, 
TENANT_ID_PROPERTY, null);
         final String clientId = getProperty(configurationContext, 
APP_REG_CLIENT_ID_PROPERTY, null);
         final String clientSecret = getProperty(configurationContext, 
APP_REG_CLIENT_SECRET_PROPERTY, null);
@@ -199,20 +204,24 @@ public class AzureGraphUserGroupProvider implements 
UserGroupProvider {
         }
 
         try {
-            authProvider = new ClientCredentialAuthProvider.Builder()
-                .authorityEndpoint(authorityEndpoint)
-                .tenantId(tenantId)
-                .clientId(clientId)
-                .clientSecret(clientSecret)
-                .build();
+            ClientCredentialAuthProvider authProvider = new 
ClientCredentialAuthProvider.Builder()
+                    .authorityEndpoint(authorityEndpoint)
+                    .tenantId(tenantId)
+                    .clientId(clientId)
+                    .clientSecret(clientSecret)
+                    .graphScope(graphScope)
+                    .build();
             graphClient = 
GraphServiceClient.builder().authenticationProvider(authProvider).buildClient();
+            if ( ! StringUtils.isBlank(graphEndpoint)) {
+                graphClient.setServiceRoot(graphEndpoint);
+            }
         } catch (final ClientException e) {
             throw new AuthorizerCreationException(String.format("Failed to 
create a GraphServiceClient due to %s", e.getMessage()), e);
         }
 
         // first, load list of group name if there is any prefix, suffix, 
substring
-        // filter defined, paging thru groups.
-        // then, add additonal group list if there is group list inclusion 
defined.
+        // filter defined, paging through groups.
+        // then, add additional group list if there is group list inclusion 
defined.
         final String prefix = getProperty(configurationContext, 
GROUP_FILTER_PREFIX_PROPERTY, null);
         final String suffix = getProperty(configurationContext, 
GROUP_FILTER_SUFFIX_PROPERTY, null);
         final String substring = getProperty(configurationContext, 
GROUP_FILTER_SUBSTRING_PROPERTY, null);
@@ -277,7 +286,7 @@ public class AzureGraphUserGroupProvider implements 
UserGroupProvider {
     /**
      * Get a set of group display names after filtering prefix, suffix, and 
substring
      * @param prefix prefix filter string matching against displayName of 
group directory objects
-     * @param suffix suffix fitler string matching against displayName of 
group directory objects
+     * @param suffix suffix filter string matching against displayName of 
group directory objects
      * @param substring string matching against displayName of group directory 
objects
      * @param pageSize page size to make graph rest calls in pagination
      * @return set of group display names
@@ -288,7 +297,7 @@ public class AzureGraphUserGroupProvider implements 
UserGroupProvider {
         IGroupCollectionPage filterResults;
         if (prefix != null && !prefix.isEmpty()) {
             // build a $filter query option and create a graph request if 
prefix is given
-            final List<Option> requestOptions = Arrays.asList(new 
QueryOption("$filter", String.format("startswith(displayName, '%s')", prefix)));
+            final List<Option> requestOptions = List.of(new 
QueryOption("$filter", String.format("startswith(displayName, '%s')", prefix)));
             gRequest = 
graphClient.groups().buildRequest(requestOptions).select("displayName");
         } else {
             // default group graph request
@@ -333,11 +342,11 @@ public class AzureGraphUserGroupProvider implements 
UserGroupProvider {
     private UserGroupQueryResult getUsersFrom(String groupName, int pageSize) 
throws IOException, ClientException {
         final Set<User> users = new HashSet<>();
 
-        final List<Option> requestOptions = Arrays.asList(new 
QueryOption("$filter", String.format("displayName eq '%s'", groupName)));
+        final List<Option> requestOptions = List.of(new QueryOption("$filter", 
String.format("displayName eq '%s'", groupName)));
         final IGroupCollectionPage results = 
graphClient.groups().buildRequest(requestOptions).get();
         final List<com.microsoft.graph.models.extensions.Group> currentPage = 
results.getCurrentPage();
 
-        if (currentPage != null && currentPage.size() > 0) {
+        if (currentPage != null && !currentPage.isEmpty()) {
             final com.microsoft.graph.models.extensions.Group graphGroup = 
results.getCurrentPage().get(0);
             final Group.Builder groupBuilder =
                 new Group.Builder()
@@ -445,3 +454,4 @@ public class AzureGraphUserGroupProvider implements 
UserGroupProvider {
     }
 
 }
+
diff --git 
a/nifi-nar-bundles/nifi-azure-bundle/nifi-azure-graph-authorizer/src/main/java/org/apache/nifi/authorization/azure/ClientCredentialAuthProvider.java
 
b/nifi-nar-bundles/nifi-azure-bundle/nifi-azure-graph-authorizer/src/main/java/org/apache/nifi/authorization/azure/ClientCredentialAuthProvider.java
index 552bac9f73..b926d51b8e 100644
--- 
a/nifi-nar-bundles/nifi-azure-bundle/nifi-azure-graph-authorizer/src/main/java/org/apache/nifi/authorization/azure/ClientCredentialAuthProvider.java
+++ 
b/nifi-nar-bundles/nifi-azure-bundle/nifi-azure-graph-authorizer/src/main/java/org/apache/nifi/authorization/azure/ClientCredentialAuthProvider.java
@@ -43,9 +43,9 @@ public class ClientCredentialAuthProvider implements 
IAuthenticationProvider {
     private final String tenantId;
     private final String clientId;
     private final String clientSecret;
+    private final String graphScope;
     private LocalDateTime tokenExpiresOnDate;
-    private String lastAcessToken;
-    private static final String GRAPH_DEFAULT_SCOPE = 
"https://graph.microsoft.com/.default";;
+    private String lastAccessToken;
     private static final Logger logger = 
LoggerFactory.getLogger(ClientCredentialAuthProvider.class);
 
     private ClientCredentialAuthProvider(final Builder builder){
@@ -53,11 +53,12 @@ public class ClientCredentialAuthProvider implements 
IAuthenticationProvider {
         this.tenantId = builder.getTenantId();
         this.clientId = builder.getClientId();
         this.clientSecret = builder.getClientSecret();
+        this.graphScope = builder.getGraphScope();
     }
 
     @Override
     public int hashCode() {
-        return Objects.hash(authorityEndpoint, tenantId, clientId, 
clientSecret);
+        return Objects.hash(authorityEndpoint, tenantId, clientId, 
clientSecret, graphScope);
     }
 
     @Override
@@ -67,6 +68,7 @@ public class ClientCredentialAuthProvider implements 
IAuthenticationProvider {
             ", tenantId='" + tenantId + "'" +
             ", clientId='" + clientId + "'" +
             ", clientSecret='" + clientSecret + "'" +
+            ", graphScope='" + graphScope + "'" +
             "}";
     }
 
@@ -79,7 +81,7 @@ public class ClientCredentialAuthProvider implements 
IAuthenticationProvider {
                 .authority(String.format("%s/%s", authorityEndpoint, tenantId))
                 .build();
         ClientCredentialParameters clientCredentialParam = 
ClientCredentialParameters.builder(
-                Collections.singleton(GRAPH_DEFAULT_SCOPE))
+                Collections.singleton(graphScope))
                 .build();
 
         CompletableFuture<IAuthenticationResult> future = 
app.acquireToken(clientCredentialParam);
@@ -93,18 +95,16 @@ public class ClientCredentialAuthProvider implements 
IAuthenticationProvider {
     }
 
     private String getAccessToken() {
-        if ((lastAcessToken != null) && (tokenExpiresOnDate != null) && 
(tokenExpiresOnDate.isAfter(LocalDateTime.now().plusMinutes(1)))) {
-            return lastAcessToken;
-        } else {
+        if ((lastAccessToken == null) || (tokenExpiresOnDate == null) || 
(!tokenExpiresOnDate.isAfter(LocalDateTime.now().plusMinutes(1)))) {
             try {
                 IAuthenticationResult result = 
getAccessTokenByClientCredentialGrant();
                 tokenExpiresOnDate = 
convertToLocalDateTime(result.expiresOnDate());
-                lastAcessToken = result.accessToken();
-            } catch(final Exception e) {
+                lastAccessToken = result.accessToken();
+            } catch (final Exception e) {
                 logger.error("Failed to get access token due to {}", 
e.getMessage(), e);
             }
-            return lastAcessToken;
         }
+        return lastAccessToken;
     }
 
     @Override
@@ -121,6 +121,7 @@ public class ClientCredentialAuthProvider implements 
IAuthenticationProvider {
         private String tenantId = "";
         private String clientId = "";
         private String clientSecret = "";
+        private String graphScope = "";
 
         public Builder authorityEndpoint(final String authorityEndpoint){
             this.authorityEndpoint = authorityEndpoint;
@@ -131,6 +132,15 @@ public class ClientCredentialAuthProvider implements 
IAuthenticationProvider {
             return this.authorityEndpoint;
         }
 
+        public Builder graphScope(final String graphDefaultScope){
+            this.graphScope = graphDefaultScope;
+            return this;
+        }
+
+        public String getGraphScope() {
+            return this.graphScope;
+        }
+
         public Builder tenantId(final String tenantId){
             this.tenantId = tenantId;
             return this;
@@ -160,7 +170,7 @@ public class ClientCredentialAuthProvider implements 
IAuthenticationProvider {
 
         @Override
         public int hashCode() {
-            return Objects.hash(authorityEndpoint, tenantId, clientId, 
clientSecret);
+            return Objects.hash(authorityEndpoint, tenantId, clientId, 
clientSecret, graphScope);
         }
 
         @Override
@@ -170,6 +180,7 @@ public class ClientCredentialAuthProvider implements 
IAuthenticationProvider {
                 ", tenantId='" + getTenantId() + "'" +
                 ", clientId='" + getClientId() + "'" +
                 ", clientSecret='" + getClientSecret() + "'" +
+                ", graphScope='" + getGraphScope() + "'" +
                 "}";
         }
         public ClientCredentialAuthProvider build() {
@@ -177,3 +188,4 @@ public class ClientCredentialAuthProvider implements 
IAuthenticationProvider {
         }
     }
 }
+

Reply via email to