You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nifi.apache.org by jo...@apache.org on 2022/12/01 18:17:04 UTC

[nifi] branch support/nifi-1.19 updated (eb0d07fda3 -> 2b9b9ebb09)

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

joewitt pushed a change to branch support/nifi-1.19
in repository https://gitbox.apache.org/repos/asf/nifi.git


    from eb0d07fda3 NIFI-10913 updating versions post commit landing
     new fba7b4dd26 NIFI-10177: Implemented ID token logout and revoke access token logout for NiFi Registry when using OIDC/OAuth 2.0 providers NIFI-10177: Addressed latest PR reviews. Reworded comments in the logout endpoint, use nifi registry properties to configure HTTP client timeouts for OIDC logout request, used NiFiUserUtils.getNiFiUserIdentity to retrieve identity used to delete the key
     new 053f6bf01d NIFI-10917 Upgraded Spring Framework from 5.3.23 to 5.3.24
     new 89ac8f74f8 NIFI-10909 Upgraded Jackson from 2.14.0 to 2.14.1
     new 0f9541aa1e NIFI-10914 Adjusting input check for loading nested versioned flows (#6741)
     new 2b9b9ebb09 NIFI-10919 Corrected SCRAM SASL Mechanism for Kafka Components

The 5 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 .../apache/nifi/web/api/ProcessGroupResource.java  |   6 +-
 .../login/DelegatingLoginConfigProvider.java       |   2 +-
 .../nifi/kafka/shared/property/SaslMechanism.java  |  10 +
 .../StandardKafkaPropertyNameProvider.java         |  16 +-
 .../provider/StandardKafkaPropertyProvider.java    |   2 +-
 .../login/DelegatingLoginConfigProviderTest.java   |  69 ++++
 .../StandardKafkaPropertyProviderTest.java         |  80 +++++
 nifi-registry/nifi-registry-assembly/NOTICE        |   7 +
 .../nifi-registry-web-api/pom.xml                  |   4 +
 .../nifi/registry/web/api/AccessResource.java      | 379 +++++++++++++++++----
 .../nifi/registry/web/api/ApplicationResource.java |   3 +
 .../authentication/jwt/JwtIdentityProvider.java    |   2 +-
 .../security/authentication/jwt/JwtService.java    |  22 +-
 .../authentication/oidc/OidcIdentityProvider.java  |  27 +-
 .../security/authentication/oidc/OidcService.java  |  45 ++-
 .../oidc/StandardOidcIdentityProvider.java         | 129 ++++++-
 .../authentication/oidc/OidcServiceTest.java       |  14 +-
 .../nifi/registry/web/filter/LogoutFilter.java     |   3 +-
 .../src/main/webapp/nf-registry.js                 |   4 +-
 .../src/main/webapp/services/nf-registry.api.js    |   4 +-
 nifi-registry/pom.xml                              |   2 +-
 pom.xml                                            |   6 +-
 22 files changed, 714 insertions(+), 122 deletions(-)
 create mode 100644 nifi-nar-bundles/nifi-kafka-bundle/nifi-kafka-shared/src/test/java/org/apache/nifi/kafka/shared/login/DelegatingLoginConfigProviderTest.java
 create mode 100644 nifi-nar-bundles/nifi-kafka-bundle/nifi-kafka-shared/src/test/java/org/apache/nifi/kafka/shared/property/provider/StandardKafkaPropertyProviderTest.java


[nifi] 03/05: NIFI-10909 Upgraded Jackson from 2.14.0 to 2.14.1

Posted by jo...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 89ac8f74f881097a64364aa17f6adab6eecaffff
Author: exceptionfactory <ex...@apache.org>
AuthorDate: Wed Nov 30 16:34:49 2022 -0600

    NIFI-10909 Upgraded Jackson from 2.14.0 to 2.14.1
    
    Signed-off-by: Pierre Villard <pi...@gmail.com>
    
    This closes #6738.
---
 pom.xml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/pom.xml b/pom.xml
index a6b7270031..dc8edd1bba 100644
--- a/pom.xml
+++ b/pom.xml
@@ -120,7 +120,7 @@
         <org.slf4j.version>1.7.36</org.slf4j.version>
         <ranger.version>2.3.0</ranger.version>
         <jetty.version>9.4.49.v20220914</jetty.version>
-        <jackson.bom.version>2.14.0</jackson.bom.version>
+        <jackson.bom.version>2.14.1</jackson.bom.version>
         <avro.version>1.11.1</avro.version>
         <jaxb.runtime.version>2.3.5</jaxb.runtime.version>
         <javax.annotation-api.version>1.3.2</javax.annotation-api.version>


[nifi] 01/05: NIFI-10177: Implemented ID token logout and revoke access token logout for NiFi Registry when using OIDC/OAuth 2.0 providers NIFI-10177: Addressed latest PR reviews. Reworded comments in the logout endpoint, use nifi registry properties to configure HTTP client timeouts for OIDC logout request, used NiFiUserUtils.getNiFiUserIdentity to retrieve identity used to delete the key

Posted by jo...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit fba7b4dd265f5ad4d2d4b63b8e4358f5dceef5e9
Author: Emilio Setiadarma <em...@gmail.com>
AuthorDate: Thu Nov 3 19:51:08 2022 -0700

    NIFI-10177: Implemented ID token logout and revoke access token logout for NiFi Registry when using OIDC/OAuth 2.0 providers
    NIFI-10177: Addressed latest PR reviews. Reworded comments in the logout endpoint, use nifi registry properties to configure HTTP client timeouts for OIDC logout request, used NiFiUserUtils.getNiFiUserIdentity to retrieve identity used to delete the key
    
    Signed-off-by: Nathan Gough <th...@gmail.com>
    
    This closes #6637.
---
 nifi-registry/nifi-registry-assembly/NOTICE        |   7 +
 .../nifi-registry-web-api/pom.xml                  |   4 +
 .../nifi/registry/web/api/AccessResource.java      | 379 +++++++++++++++++----
 .../nifi/registry/web/api/ApplicationResource.java |   3 +
 .../authentication/jwt/JwtIdentityProvider.java    |   2 +-
 .../security/authentication/jwt/JwtService.java    |  22 +-
 .../authentication/oidc/OidcIdentityProvider.java  |  27 +-
 .../security/authentication/oidc/OidcService.java  |  45 ++-
 .../oidc/StandardOidcIdentityProvider.java         | 129 ++++++-
 .../authentication/oidc/OidcServiceTest.java       |  14 +-
 .../nifi/registry/web/filter/LogoutFilter.java     |   3 +-
 .../src/main/webapp/nf-registry.js                 |   4 +-
 .../src/main/webapp/services/nf-registry.api.js    |   4 +-
 13 files changed, 535 insertions(+), 108 deletions(-)

diff --git a/nifi-registry/nifi-registry-assembly/NOTICE b/nifi-registry/nifi-registry-assembly/NOTICE
index 07548043b5..9a3925e883 100644
--- a/nifi-registry/nifi-registry-assembly/NOTICE
+++ b/nifi-registry/nifi-registry-assembly/NOTICE
@@ -260,6 +260,13 @@ The following binary components are provided under the Apache Software License v
       Guava
       Copyright 2015 The Guava Authors
 
+  (ASLv2) Apache HttpComponents Client
+      The following NOTICE information applies:
+        Copyright 1999-2022 The Apache Software Foundation
+
+        This product includes software developed at
+        The Apache Software Foundation (https://www.apache.org/).
+
 ************************
 Common Development and Distribution License 1.1
 ************************
diff --git a/nifi-registry/nifi-registry-core/nifi-registry-web-api/pom.xml b/nifi-registry/nifi-registry-core/nifi-registry-web-api/pom.xml
index a55512eefc..6e65c7976e 100644
--- a/nifi-registry/nifi-registry-core/nifi-registry-web-api/pom.xml
+++ b/nifi-registry/nifi-registry-core/nifi-registry-web-api/pom.xml
@@ -480,5 +480,9 @@
             <version>2.5.18</version>
             <scope>test</scope>
         </dependency>
+        <dependency>
+            <groupId>org.springframework.security</groupId>
+            <artifactId>spring-security-oauth2-resource-server</artifactId>
+        </dependency>
     </dependencies>
 </project>
diff --git a/nifi-registry/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/AccessResource.java b/nifi-registry/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/AccessResource.java
index 3c5db2670a..162cb10243 100644
--- a/nifi-registry/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/AccessResource.java
+++ b/nifi-registry/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/AccessResource.java
@@ -20,6 +20,7 @@ import com.nimbusds.oauth2.sdk.AuthorizationCode;
 import com.nimbusds.oauth2.sdk.AuthorizationCodeGrant;
 import com.nimbusds.oauth2.sdk.AuthorizationGrant;
 import com.nimbusds.oauth2.sdk.ParseException;
+import com.nimbusds.oauth2.sdk.http.HTTPResponse;
 import com.nimbusds.oauth2.sdk.id.State;
 import com.nimbusds.openid.connect.sdk.AuthenticationErrorResponse;
 import com.nimbusds.openid.connect.sdk.AuthenticationResponseParser;
@@ -31,6 +32,14 @@ import io.swagger.annotations.ApiResponse;
 import io.swagger.annotations.ApiResponses;
 import io.swagger.annotations.Authorization;
 import org.apache.commons.lang3.StringUtils;
+import org.apache.http.NameValuePair;
+import org.apache.http.client.config.RequestConfig;
+import org.apache.http.client.entity.UrlEncodedFormEntity;
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClientBuilder;
+import org.apache.http.message.BasicNameValuePair;
 import org.apache.nifi.registry.authorization.CurrentUser;
 import org.apache.nifi.registry.event.EventService;
 import org.apache.nifi.registry.exception.AdministrationException;
@@ -44,6 +53,7 @@ import org.apache.nifi.registry.security.authentication.exception.IdentityAccess
 import org.apache.nifi.registry.security.authentication.exception.InvalidCredentialsException;
 import org.apache.nifi.registry.security.authorization.user.NiFiUser;
 import org.apache.nifi.registry.security.authorization.user.NiFiUserUtils;
+import org.apache.nifi.registry.util.FormatUtils;
 import org.apache.nifi.registry.web.exception.UnauthorizedException;
 import org.apache.nifi.registry.web.security.authentication.jwt.JwtService;
 import org.apache.nifi.registry.web.security.authentication.kerberos.KerberosSpnegoIdentityProvider;
@@ -56,6 +66,7 @@ import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.lang.Nullable;
 import org.springframework.stereotype.Component;
 
+import javax.net.ssl.SSLContext;
 import javax.servlet.ServletContext;
 import javax.servlet.http.Cookie;
 import javax.servlet.http.HttpServletRequest;
@@ -72,11 +83,14 @@ import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
 import javax.ws.rs.core.UriBuilder;
 import javax.ws.rs.core.UriInfo;
+import java.io.IOException;
 import java.net.URI;
+import java.security.NoSuchAlgorithmException;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Objects;
 import java.util.UUID;
+import java.util.concurrent.TimeUnit;
 import java.util.stream.Collectors;
 
 @Component
@@ -91,6 +105,9 @@ public class AccessResource extends ApplicationResource {
 
     private static final String OIDC_REQUEST_IDENTIFIER = "oidc-request-identifier";
     private static final String OIDC_ERROR_TITLE = "Unable to continue login sequence";
+    private static final String REVOKE_ACCESS_TOKEN_LOGOUT = "oidc_access_token_logout";
+    private static final String ID_TOKEN_LOGOUT = "oidc_id_token_logout";
+    private static final String STANDARD_LOGOUT = "oidc_standard_logout";
 
     private NiFiRegistryProperties properties;
     private JwtService jwtService;
@@ -298,17 +315,17 @@ public class AccessResource extends ApplicationResource {
                     @ApiResponse(code = 500, message = "Client failed to log out."),
             }
     )
-    public Response logOut(@Context HttpServletRequest httpServletRequest, @Context HttpServletResponse httpServletResponse) {
+    public Response logout(@Context HttpServletRequest httpServletRequest, @Context HttpServletResponse httpServletResponse) {
         if (!httpServletRequest.isSecure()) {
             throw new IllegalStateException("User authentication/authorization is only supported when running over HTTPS.");
         }
 
-        String userIdentity = NiFiUserUtils.getNiFiUserIdentity();
+        final String userIdentity = NiFiUserUtils.getNiFiUserIdentity();
 
         if(userIdentity != null && !userIdentity.isEmpty()) {
             try {
                 logger.info("Logging out user " + userIdentity);
-                jwtService.logOut(userIdentity);
+                jwtService.deleteKey(userIdentity);
                 return generateOkResponse().build();
             } catch (final JwtException e) {
                 logger.error("Logout of user " + userIdentity + " failed due to: " + e.getMessage());
@@ -319,6 +336,30 @@ public class AccessResource extends ApplicationResource {
         }
     }
 
+    @GET
+    @Consumes(MediaType.WILDCARD)
+    @Produces(MediaType.WILDCARD)
+    @Path("/logout/complete")
+    @ApiOperation(
+            value = "Completes the logout sequence.",
+            notes = NON_GUARANTEED_ENDPOINT
+    )
+    @ApiResponses(
+            value = {
+                    @ApiResponse(code = 200, message = "User was logged out successfully."),
+                    @ApiResponse(code = 401, message = "Authentication token provided was empty or not in the correct JWT format."),
+                    @ApiResponse(code = 500, message = "Client failed to log out."),
+            }
+    )
+    public void logoutComplete(@Context HttpServletRequest httpServletRequest, @Context HttpServletResponse httpServletResponse) throws IOException {
+        if (!httpServletRequest.isSecure()) {
+            throw new IllegalStateException("User logout is only supported when running over HTTPS.");
+        }
+
+        // redirect to NiFi Registry page after logout completes
+        httpServletResponse.sendRedirect(getNiFiRegistryUri());
+    }
+
     @POST
     @Consumes(MediaType.WILDCARD)
     @Produces(MediaType.TEXT_PLAIN)
@@ -542,30 +583,10 @@ public class AccessResource extends ApplicationResource {
             throw new IllegalStateException("OpenId Connect is not configured.");
         }
 
-        final String oidcRequestIdentifier = UUID.randomUUID().toString();
-
-        // generate a cookie to associate this login sequence
-        final Cookie cookie = new Cookie(OIDC_REQUEST_IDENTIFIER, oidcRequestIdentifier);
-        cookie.setPath("/");
-        cookie.setHttpOnly(true);
-        cookie.setMaxAge(60);
-        cookie.setSecure(true);
-        httpServletResponse.addCookie(cookie);
-
-        // get the state for this request
-        final State state = oidcService.createState(oidcRequestIdentifier);
-
-        // build the authorization uri
-        final URI authorizationUri = UriBuilder.fromUri(oidcService.getAuthorizationEndpoint())
-                .queryParam("client_id", oidcService.getClientId())
-                .queryParam("response_type", "code")
-                .queryParam("scope", oidcService.getScope().toString())
-                .queryParam("state", state.getValue())
-                .queryParam("redirect_uri", getOidcCallback())
-                .build();
+        final URI authorizationURI = oidcRequestAuthorizationCode(httpServletResponse, getOidcCallback());
 
         // generate the response
-        httpServletResponse.sendRedirect(authorizationUri.toString());
+        httpServletResponse.sendRedirect(authorizationURI.toString());
     }
 
     @GET
@@ -589,44 +610,25 @@ public class AccessResource extends ApplicationResource {
             throw new IllegalStateException("OpenId Connect is not configured.");
         }
 
-        final String oidcRequestIdentifier = getCookieValue(httpServletRequest.getCookies(), OIDC_REQUEST_IDENTIFIER);
+        final String oidcRequestIdentifier = getOidcRequestIdentifier(httpServletRequest);
         if (oidcRequestIdentifier == null) {
             throw new IllegalStateException("The login request identifier was not found in the request. Unable to continue.");
         }
 
-        final com.nimbusds.openid.connect.sdk.AuthenticationResponse oidcResponse;
-        try {
-            oidcResponse = AuthenticationResponseParser.parse(getRequestUri());
-        } catch (final ParseException e) {
-            logger.error("Unable to parse the redirect URI from the OpenId Connect Provider. Unable to continue login process.");
-
-            // remove the oidc request cookie
-            removeOidcRequestCookie(httpServletResponse);
 
-            // forward to the error page
-            throw new IllegalStateException("Unable to parse the redirect URI from the OpenId Connect Provider. Unable to continue login process.");
-        }
+        final com.nimbusds.openid.connect.sdk.AuthenticationResponse oidcResponse =
+                parseAuthenticationResponse(getRequestUri(), httpServletResponse, true);
 
         if (oidcResponse.indicatesSuccess()) {
             final AuthenticationSuccessResponse successfulOidcResponse = (AuthenticationSuccessResponse) oidcResponse;
 
-            // confirm state
-            final State state = successfulOidcResponse.getState();
-            if (state == null || !oidcService.isStateValid(oidcRequestIdentifier, state)) {
-                logger.error("The state value returned by the OpenId Connect Provider does not match the stored state. Unable to continue login process.");
-
-                // remove the oidc request cookie
-                removeOidcRequestCookie(httpServletResponse);
-
-                // forward to the error page
-                throw new IllegalStateException("Purposed state does not match the stored state. Unable to continue login process.");
-            }
+            validateOIDCState(oidcRequestIdentifier, successfulOidcResponse, httpServletResponse, true);
 
             try {
                 // exchange authorization code for id token
                 final AuthorizationCode authorizationCode = successfulOidcResponse.getAuthorizationCode();
                 final AuthorizationGrant authorizationGrant = new AuthorizationCodeGrant(authorizationCode, URI.create(getOidcCallback()));
-                oidcService.exchangeAuthorizationCode(oidcRequestIdentifier, authorizationGrant);
+                oidcService.exchangeAuthorizationCodeForLoginAuthenticationToken(oidcRequestIdentifier, authorizationGrant);
             } catch (final Exception e) {
                 logger.error("Unable to exchange authorization for ID token: " + e.getMessage(), e);
 
@@ -669,7 +671,7 @@ public class AccessResource extends ApplicationResource {
             throw new IllegalStateException("OpenId Connect is not configured.");
         }
 
-        final String oidcRequestIdentifier = getCookieValue(httpServletRequest.getCookies(), OIDC_REQUEST_IDENTIFIER);
+        final String oidcRequestIdentifier = getOidcRequestIdentifier(httpServletRequest);
         if (oidcRequestIdentifier == null) {
             throw new IllegalArgumentException("The login request identifier was not found in the request. Unable to continue.");
         }
@@ -687,7 +689,7 @@ public class AccessResource extends ApplicationResource {
         return generateOkResponse(jwt).build();
     }
 
-    @DELETE
+    @GET
     @Consumes(MediaType.WILDCARD)
     @Produces(MediaType.WILDCARD)
     @Path("/oidc/logout")
@@ -704,20 +706,130 @@ public class AccessResource extends ApplicationResource {
             throw new IllegalStateException("OpenId Connect is not configured.");
         }
 
-        final String tokenHeader = httpServletRequest.getHeader(JwtService.AUTHORIZATION);
-        jwtService.logOutUsingAuthHeader(tokenHeader);
+        // Checks if OIDC service supports logout using either by invoking the revocation endpoint (for OAuth 2.0 providers)
+        // or the end session endpoint (for OIDC providers). If either is supported, send a request to get an authorization
+        // code that can be eventually exchanged for a token that is required as a parameter for the logout request.
+        final String logoutMethod = determineLogoutMethod();
+        switch (logoutMethod) {
+            case REVOKE_ACCESS_TOKEN_LOGOUT:
+            case ID_TOKEN_LOGOUT:
+                final URI authorizationURI = oidcRequestAuthorizationCode(httpServletResponse, getOidcLogoutCallback());
+                httpServletResponse.sendRedirect(authorizationURI.toString());
+                break;
+            default:
+                // If the above logout methods are not supported, last ditch effort to logout by providing the client_id,
+                // to the end session endpoint if it exists. This is a way to logout defined in the OIDC specs, but the
+                // id_token_hint logout method is recommended. This option is not available when using the POST request
+                // to the revocation endpoint (OAuth 2.0 providers).
+                final URI endSessionEndpoint = oidcService.getEndSessionEndpoint();
+                if (endSessionEndpoint != null) {
+                    final String postLogoutRedirectUri = getNiFiRegistryUri();
+                    final URI logoutUri = UriBuilder.fromUri(endSessionEndpoint)
+                            .queryParam("post_logout_redirect_uri", postLogoutRedirectUri)
+                            .queryParam("client_id", oidcService.getClientId())
+                            .build();
+                    httpServletResponse.sendRedirect(logoutUri.toString());
+                } else {
+                    throw new IllegalStateException("Unable to initiate logout: Logout method unrecognized");
+                }
+                break;
+        }
+    }
+
+    @GET
+    @Consumes(MediaType.WILDCARD)
+    @Produces(MediaType.WILDCARD)
+    @Path("/oidc/logout/callback")
+    @ApiOperation(
+            value = "Redirect/callback URI for processing the result of the OpenId Connect logout sequence.",
+            notes = NON_GUARANTEED_ENDPOINT
+    )
+    public void oidcLogoutCallback(@Context HttpServletRequest httpServletRequest, @Context HttpServletResponse httpServletResponse) throws Exception {
+        if (!httpServletRequest.isSecure()) {
+            throw new IllegalStateException("User logout is only supported when running over HTTPS.");
+        }
+
+        if (!oidcService.isOidcEnabled()) {
+            throw new IllegalStateException("OpenId Connect is not configured.");
+        }
+
+        final String oidcRequestIdentifier = getOidcRequestIdentifier(httpServletRequest);
+        if (oidcRequestIdentifier == null) {
+            throw new IllegalStateException("The OIDC request identifier was not found in the request. Unable to continue.");
+        }
+
+        final com.nimbusds.openid.connect.sdk.AuthenticationResponse oidcResponse =
+                parseAuthenticationResponse(getRequestUri(), httpServletResponse, false);
+
+        if (oidcResponse.indicatesSuccess()) {
+            final AuthenticationSuccessResponse successfulOidcResponse = (AuthenticationSuccessResponse) oidcResponse;
 
-        URI endSessionEndpoint = oidcService.getEndSessionEndpoint();
-        String postLogoutRedirectUri = generateResourceUri("..", "nifi-registry");
+            validateOIDCState(oidcRequestIdentifier, successfulOidcResponse, httpServletResponse, false);
 
-        if (endSessionEndpoint == null) {
-            // handle the case, where the OpenID Provider does not have an end session endpoint
-            //httpServletResponse.sendRedirect(postLogoutRedirectUri);
+            try {
+                // exchange authorization code for id token
+                final AuthorizationCode authorizationCode = successfulOidcResponse.getAuthorizationCode();
+                final AuthorizationGrant authorizationGrant = new AuthorizationCodeGrant(authorizationCode, URI.create(getOidcLogoutCallback()));
+
+                final String logoutMethod = determineLogoutMethod();
+                switch(logoutMethod) {
+                    case REVOKE_ACCESS_TOKEN_LOGOUT:
+                        final String accessToken;
+                        try {
+                            accessToken = oidcService.exchangeAuthorizationCodeForAccessToken(authorizationGrant);
+                        } catch (final Exception e) {
+                            final String errorMsg = "Unable to exchange authorization for the access token: " + e.getMessage();
+                            logger.error(errorMsg, e);
+
+                            throw new IllegalStateException(errorMsg);
+                        }
+
+                        final URI revokeEndpoint = oidcService.getRevocationEndpoint();
+                        try {
+                            revokeEndpointRequest(httpServletResponse, accessToken, revokeEndpoint);
+                        } catch (final IOException e) {
+                            final String errorMsg = "There was an error logging out of the OpenID Connect Provider: " + e.getMessage();
+                            logger.error(errorMsg, e);
+
+                            throw new IllegalStateException(errorMsg);
+                        }
+                        break;
+                    case ID_TOKEN_LOGOUT:
+                        final String idToken;
+                        try {
+                            idToken = oidcService.exchangeAuthorizationCodeForIdToken(authorizationGrant);
+                        } catch (final Exception e) {
+                            final String errorMsg = "Unable to exchange authorization for the ID token: " + e.getMessage();
+                            logger.error(errorMsg, e);
+
+                            throw new IllegalStateException(errorMsg);
+                        }
+
+                        final URI endSessionEndpoint = oidcService.getEndSessionEndpoint();
+                        final String postLogoutRedirectUri = getNiFiRegistryUri();
+                        final URI logoutUri = UriBuilder.fromUri(endSessionEndpoint)
+                                .queryParam("id_token_hint", idToken)
+                                .queryParam("post_logout_redirect_uri", postLogoutRedirectUri)
+                                .build();
+                        httpServletResponse.sendRedirect(logoutUri.toString());
+                        break;
+                    default:
+                        // there should be no other way to logout at this point, return error
+                        throw new IllegalStateException("Unable to complete logout: Logout method unrecognized");
+                }
+            } catch (final Exception e) {
+                logger.error(e.getMessage(), e);
+
+                removeOidcRequestCookie(httpServletResponse);
+
+                throw e;
+            }
         } else {
-            URI logoutUri = UriBuilder.fromUri(endSessionEndpoint)
-                    .queryParam("post_logout_redirect_uri", postLogoutRedirectUri)
-                    .build();
-            httpServletResponse.sendRedirect(logoutUri.toString());
+            removeOidcRequestCookie(httpServletResponse);
+
+            // report the unsuccessful logout
+            final AuthenticationErrorResponse errorOidcResponse = (AuthenticationErrorResponse) oidcResponse;
+            throw new IllegalStateException("Unsuccessful logout attempt: " + errorOidcResponse.getErrorObject().getDescription());
         }
     }
 
@@ -748,6 +860,10 @@ public class AccessResource extends ApplicationResource {
         return generateResourceUri("access", "oidc", "callback");
     }
 
+    private String getOidcLogoutCallback() {
+        return generateResourceUri("access", "oidc", "logout", "callback");
+    }
+
     private void removeOidcRequestCookie(final HttpServletResponse httpServletResponse) {
         final Cookie cookie = new Cookie(OIDC_REQUEST_IDENTIFIER, null);
         cookie.setPath("/");
@@ -761,12 +877,6 @@ public class AccessResource extends ApplicationResource {
         return uriInfo.getRequestUri();
     }
 
-    private String getNiFiRegistryUri() {
-        final String nifiRegistryApiUrl = generateResourceUri();
-        final String baseUrl = StringUtils.substringBeforeLast(nifiRegistryApiUrl, "/nifi-registry-api");
-        return baseUrl + "/nifi-registry";
-    }
-
     private void forwardToMessagePage(final HttpServletRequest httpServletRequest, final HttpServletResponse httpServletResponse, final String message) throws Exception {
         httpServletRequest.setAttribute("title", OIDC_ERROR_TITLE);
         httpServletRequest.setAttribute("messages", message);
@@ -832,4 +942,137 @@ public class AccessResource extends ApplicationResource {
     private boolean isOIDCLoginSupported(HttpServletRequest request) {
         return request.isSecure() && oidcService != null && oidcService.isOidcEnabled();
     }
+
+    private String determineLogoutMethod() {
+        if (oidcService.getEndSessionEndpoint() != null) {
+            return ID_TOKEN_LOGOUT;
+        } else if (oidcService.getRevocationEndpoint() != null) {
+            return REVOKE_ACCESS_TOKEN_LOGOUT;
+        } else {
+            return STANDARD_LOGOUT;
+        }
+    }
+
+    /**
+     * Generates the request Authorization URI for the OpenID Connect Provider. Returns an authorization
+     * URI using the provided callback URI.
+     *
+     * @param httpServletResponse the servlet response
+     * @param callback the OIDC callback URI
+     * @return the authorization URI
+     */
+    private URI oidcRequestAuthorizationCode(@Context final HttpServletResponse httpServletResponse, final String callback) {
+        final String oidcRequestIdentifier = UUID.randomUUID().toString();
+        // generate a cookie to associate this login sequence
+        final Cookie cookie = new Cookie(OIDC_REQUEST_IDENTIFIER, oidcRequestIdentifier);
+        cookie.setPath("/");
+        cookie.setHttpOnly(true);
+        cookie.setMaxAge(60);
+        cookie.setSecure(true);
+        httpServletResponse.addCookie(cookie);
+
+        // get the state for this request
+        final State state = oidcService.createState(oidcRequestIdentifier);
+
+        // build the authorization uri
+        final URI authorizationUri = UriBuilder.fromUri(oidcService.getAuthorizationEndpoint())
+                .queryParam("client_id", oidcService.getClientId())
+                .queryParam("response_type", "code")
+                .queryParam("scope", oidcService.getScope().toString())
+                .queryParam("state", state.getValue())
+                .queryParam("redirect_uri", callback)
+                .build();
+        return authorizationUri;
+    }
+
+    private String getOidcRequestIdentifier(final HttpServletRequest httpServletRequest) {
+        return getCookieValue(httpServletRequest.getCookies(), OIDC_REQUEST_IDENTIFIER);
+    }
+
+    private com.nimbusds.openid.connect.sdk.AuthenticationResponse parseAuthenticationResponse(final URI requestUri,
+                                                                                               final HttpServletResponse httpServletResponse,
+                                                                                               final boolean isLogin) {
+        final com.nimbusds.openid.connect.sdk.AuthenticationResponse oidcResponse;
+        try {
+            oidcResponse = AuthenticationResponseParser.parse(requestUri);
+        } catch (final ParseException e) {
+            final String loginOrLogoutString = isLogin ? "login" : "logout";
+            logger.error(String.format("Unable to parse the redirect URI from the OpenId Connect Provider. Unable to continue %s process.", loginOrLogoutString));
+
+            // remove the oidc request cookie
+            removeOidcRequestCookie(httpServletResponse);
+
+            throw new IllegalStateException(String.format("Unable to parse the redirect URI from the OpenId Connect Provider. Unable to continue %s process.", loginOrLogoutString));
+        }
+        return oidcResponse;
+    }
+
+    private void validateOIDCState(final String oidcRequestIdentifier,
+                                   final AuthenticationSuccessResponse successfulOidcResponse,
+                                   final HttpServletResponse httpServletResponse,
+                                   final boolean isLogin) {
+        // confirm state
+        final State state = successfulOidcResponse.getState();
+        if (state == null || !oidcService.isStateValid(oidcRequestIdentifier, state)) {
+            final String loginOrLogoutMessage = isLogin ? "login" : "logout";
+            logger.error(String.format("The state value returned by the OpenId Connect Provider does not match the stored state. Unable to continue %s process.", loginOrLogoutMessage));
+
+            // remove the oidc request cookie
+            removeOidcRequestCookie(httpServletResponse);
+
+            throw new IllegalStateException(String.format("Proposed state does not match the stored state. Unable to continue %s process.", loginOrLogoutMessage));
+        }
+    }
+
+    /**
+     * Sends a POST request to the revoke endpoint to log out of the ID Provider.
+     *
+     * @param httpServletResponse the servlet response
+     * @param accessToken the OpenID Connect Provider access token
+     * @param revokeEndpoint the name of the cookie
+     * @throws IOException exceptional case for communication error with the OpenId Connect Provider
+     */
+    private void revokeEndpointRequest(@Context HttpServletResponse httpServletResponse, String accessToken, URI revokeEndpoint) throws IOException, NoSuchAlgorithmException {
+        final CloseableHttpClient httpClient = getHttpClient();
+        HttpPost httpPost = new HttpPost(revokeEndpoint);
+
+        List<NameValuePair> params = new ArrayList<>();
+        // Append a query param with the access token
+        params.add(new BasicNameValuePair("token", accessToken));
+        httpPost.setEntity(new UrlEncodedFormEntity(params));
+
+        try (CloseableHttpResponse response = httpClient.execute(httpPost)) {
+            if (response.getStatusLine().getStatusCode() == HTTPResponse.SC_OK) {
+                // redirect to NiFi Registry page after logout completes
+                logger.debug("You are logged out of the OpenId Connect Provider.");
+                final String postLogoutRedirectUri = getNiFiRegistryUri();
+                httpServletResponse.sendRedirect(postLogoutRedirectUri);
+            } else {
+                logger.error("There was an error logging out of the OpenId Connect Provider. " +
+                        "Response status: " + response.getStatusLine().getStatusCode());
+            }
+        } finally {
+            httpClient.close();
+        }
+    }
+
+    private CloseableHttpClient getHttpClient() throws NoSuchAlgorithmException {
+        final String rawConnectTimeout = properties.getOidcConnectTimeout();
+        final String rawReadTimeout = properties.getOidcReadTimeout();
+        final int oidcConnectTimeout = (int) FormatUtils.getPreciseTimeDuration(rawConnectTimeout, TimeUnit.MILLISECONDS);
+        final int oidcReadTimeout = (int) FormatUtils.getPreciseTimeDuration(rawReadTimeout, TimeUnit.MILLISECONDS);
+
+        final RequestConfig config = RequestConfig.custom()
+                .setConnectTimeout(oidcConnectTimeout)
+                .setConnectionRequestTimeout(oidcReadTimeout)
+                .setSocketTimeout(oidcReadTimeout)
+                .build();
+
+        final HttpClientBuilder builder = HttpClientBuilder
+                .create()
+                .setDefaultRequestConfig(config)
+                .setSSLContext(SSLContext.getDefault());
+
+        return builder.build();
+    }
 }
diff --git a/nifi-registry/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/ApplicationResource.java b/nifi-registry/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/ApplicationResource.java
index bce1e3911c..b16bae5905 100644
--- a/nifi-registry/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/ApplicationResource.java
+++ b/nifi-registry/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/ApplicationResource.java
@@ -230,4 +230,7 @@ public class ApplicationResource {
         return revisionInfo;
     }
 
+    protected String getNiFiRegistryUri() {
+        return generateResourceUri("..", "nifi-registry");
+    }
 }
diff --git a/nifi-registry/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/authentication/jwt/JwtIdentityProvider.java b/nifi-registry/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/authentication/jwt/JwtIdentityProvider.java
index d3f12c9114..b318e3a0cb 100644
--- a/nifi-registry/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/authentication/jwt/JwtIdentityProvider.java
+++ b/nifi-registry/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/authentication/jwt/JwtIdentityProvider.java
@@ -69,7 +69,7 @@ public class JwtIdentityProvider extends BearerAuthIdentityProvider implements I
         }
 
         try {
-            final String jwtPrincipal = jwtService.getAuthenticationFromToken(jwtAuthToken);
+            final String jwtPrincipal = jwtService.getUserIdentityFromToken(jwtAuthToken);
             return new AuthenticationResponse(jwtPrincipal, jwtPrincipal, expiration, issuer);
         } catch (JwtException e) {
             throw new InvalidAuthenticationException(e.getMessage(), e);
diff --git a/nifi-registry/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/authentication/jwt/JwtService.java b/nifi-registry/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/authentication/jwt/JwtService.java
index a0e2d252c7..95e54e8325 100644
--- a/nifi-registry/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/authentication/jwt/JwtService.java
+++ b/nifi-registry/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/authentication/jwt/JwtService.java
@@ -31,7 +31,6 @@ import org.apache.commons.lang3.StringUtils;
 import org.apache.nifi.registry.security.authentication.AuthenticationResponse;
 import org.apache.nifi.registry.security.key.Key;
 import org.apache.nifi.registry.security.key.KeyService;
-import org.apache.nifi.registry.web.security.authentication.exception.InvalidAuthenticationException;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
@@ -40,7 +39,6 @@ import java.nio.charset.StandardCharsets;
 import java.text.SimpleDateFormat;
 import java.util.Calendar;
 import java.util.concurrent.TimeUnit;
-import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
 // TODO, look into replacing this JwtService service with Apache Licensed JJWT library
@@ -62,7 +60,7 @@ public class JwtService {
         this.keyService = keyService;
     }
 
-    public String getAuthenticationFromToken(final String base64EncodedToken) throws JwtException {
+    public String getUserIdentityFromToken(final String base64EncodedToken) throws JwtException {
         // The library representations of the JWT should be kept internal to this service.
         try {
             final Jws<Claims> jws = parseTokenFromBase64EncodedString(base64EncodedToken);
@@ -175,7 +173,7 @@ public class JwtService {
 
     }
 
-    public void logOut(String userIdentity) {
+    public void deleteKey(final String userIdentity) {
         if (userIdentity == null || userIdentity.isEmpty()) {
             throw new JwtException("Log out failed: The user identity was not present in the request token to log out user.");
         }
@@ -184,7 +182,7 @@ public class JwtService {
             keyService.deleteKey(userIdentity);
             logger.info("Deleted token from database.");
         } catch (Exception e) {
-            logger.error("Unable to log out user: " + userIdentity + ". Failed to remove their token from database.");
+            logger.error("Unable to delete token for user: [" + userIdentity + "].");
             throw e;
         }
     }
@@ -228,18 +226,4 @@ public class JwtService {
                 .append(" ms remaining]")
                 .toString();
     }
-
-    public void logOutUsingAuthHeader(String authorizationHeader) {
-        String base64EncodedToken = getTokenFromHeader(authorizationHeader);
-        logOut(getAuthenticationFromToken(base64EncodedToken));
-    }
-
-    public static String getTokenFromHeader(String authenticationHeader) {
-        Matcher matcher = tokenPattern.matcher(authenticationHeader);
-        if(matcher.matches()) {
-            return matcher.group(1);
-        } else {
-            throw new InvalidAuthenticationException("JWT did not match expected pattern.");
-        }
-    }
 }
diff --git a/nifi-registry/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/authentication/oidc/OidcIdentityProvider.java b/nifi-registry/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/authentication/oidc/OidcIdentityProvider.java
index 53e3fe22a0..008f3ac69a 100644
--- a/nifi-registry/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/authentication/oidc/OidcIdentityProvider.java
+++ b/nifi-registry/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/authentication/oidc/OidcIdentityProvider.java
@@ -60,6 +60,13 @@ public interface OidcIdentityProvider {
      */
     URI getEndSessionEndpoint();
 
+    /**
+     * Returns the URI for the revocation endpoint.
+     *
+     * @return uri for the revocation endpoint
+     */
+    URI getRevocationEndpoint();
+
     /**
      * Returns the scopes supported by the OIDC provider.
      *
@@ -75,5 +82,23 @@ public interface OidcIdentityProvider {
      * @return a NiFi JWT
      * @throws IOException if there was an exceptional error while communicating with the OIDC provider
      */
-    String exchangeAuthorizationCode(AuthorizationGrant authorizationGrant) throws IOException;
+    String exchangeAuthorizationCodeForLoginAuthenticationToken(AuthorizationGrant authorizationGrant) throws IOException;
+
+    /**
+     * Exchanges the supplied authorization grant for an Access Token.
+     *
+     * @param authorizationGrant authorization grant for invoking the Token Endpoint
+     * @return an Access Token String
+     * @throws Exception if there was an exceptional error while communicating with the OIDC provider
+     */
+    String exchangeAuthorizationCodeForAccessToken(AuthorizationGrant authorizationGrant) throws Exception;
+
+    /**
+     * Exchanges the supplied authorization grant for an ID Token.
+     *
+     * @param authorizationGrant authorization grant for invoking the Token Endpoint
+     * @return an ID Token String
+     * @throws IOException if there was an exceptional error while communicating with the OIDC provider
+     */
+    String exchangeAuthorizationCodeForIdToken(final AuthorizationGrant authorizationGrant) throws IOException;
 }
diff --git a/nifi-registry/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/authentication/oidc/OidcService.java b/nifi-registry/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/authentication/oidc/OidcService.java
index 1dd3cfbf0b..0ea870ce84 100644
--- a/nifi-registry/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/authentication/oidc/OidcService.java
+++ b/nifi-registry/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/authentication/oidc/OidcService.java
@@ -101,6 +101,15 @@ public class OidcService {
         return identityProvider.getEndSessionEndpoint();
     }
 
+    /**
+     * Returns the URI for the revocation endpoint.
+     *
+     * @return uri for the revocation endpoint
+     */
+    public URI getRevocationEndpoint() {
+        return identityProvider.getRevocationEndpoint();
+    }
+
     /**
      * Returns the OpenId Connect scope.
      *
@@ -195,13 +204,13 @@ public class OidcService {
      * @param authorizationGrant authorization grant
      * @throws IOException exceptional case for communication error with the OpenId Connect provider
      */
-    public void exchangeAuthorizationCode(final String oidcRequestIdentifier, final AuthorizationGrant authorizationGrant) throws IOException {
+    public void exchangeAuthorizationCodeForLoginAuthenticationToken(final String oidcRequestIdentifier, final AuthorizationGrant authorizationGrant) throws IOException {
         if (!isOidcEnabled()) {
             throw new IllegalStateException(OidcIdentityProvider.OPEN_ID_CONNECT_SUPPORT_IS_NOT_CONFIGURED);
         }
 
         final CacheKey oidcRequestIdentifierKey = new CacheKey(oidcRequestIdentifier);
-        final String nifiJwt = identityProvider.exchangeAuthorizationCode(authorizationGrant);
+        final String nifiJwt = identityProvider.exchangeAuthorizationCodeForLoginAuthenticationToken(authorizationGrant);
 
         try {
             // cache the jwt for later retrieval
@@ -216,6 +225,38 @@ public class OidcService {
         }
     }
 
+    /**
+     * Exchanges the specified authorization grant for an access token.
+     *
+     * @param authorizationGrant authorization grant
+     * @return an Access Token string
+     * @throws IOException exceptional case for communication error with the OpenId Connect provider
+     */
+    public String exchangeAuthorizationCodeForAccessToken(final AuthorizationGrant authorizationGrant) throws Exception {
+        if (!isOidcEnabled()) {
+            throw new IllegalStateException(OidcIdentityProvider.OPEN_ID_CONNECT_SUPPORT_IS_NOT_CONFIGURED);
+        }
+
+        // Retrieve access token
+        return identityProvider.exchangeAuthorizationCodeForAccessToken(authorizationGrant);
+    }
+
+    /**
+     * Exchanges the specified authorization grant for an ID Token.
+     *
+     * @param authorizationGrant authorization grant
+     * @return an ID Token string
+     * @throws IOException exceptional case for communication error with the OpenId Connect provider
+     */
+    public String exchangeAuthorizationCodeForIdToken(final AuthorizationGrant authorizationGrant) throws IOException {
+        if (!isOidcEnabled()) {
+            throw new IllegalStateException(OidcIdentityProvider.OPEN_ID_CONNECT_SUPPORT_IS_NOT_CONFIGURED);
+        }
+
+        // Retrieve ID token
+        return identityProvider.exchangeAuthorizationCodeForIdToken(authorizationGrant);
+    }
+
     /**
      * Returns the resulting JWT for the given request identifier. Will return null if the request
      * identifier is not associated with a JWT or if the login sequence was not completed before
diff --git a/nifi-registry/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/authentication/oidc/StandardOidcIdentityProvider.java b/nifi-registry/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/authentication/oidc/StandardOidcIdentityProvider.java
index f43bef08fc..77b1b3c075 100644
--- a/nifi-registry/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/authentication/oidc/StandardOidcIdentityProvider.java
+++ b/nifi-registry/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/authentication/oidc/StandardOidcIdentityProvider.java
@@ -26,6 +26,11 @@ import java.util.List;
 import java.util.Map;
 import java.util.concurrent.TimeUnit;
 import java.util.stream.Collectors;
+
+import com.nimbusds.oauth2.sdk.token.AccessToken;
+import com.nimbusds.openid.connect.sdk.claims.AccessTokenHash;
+import com.nimbusds.openid.connect.sdk.validators.AccessTokenValidator;
+import com.nimbusds.openid.connect.sdk.validators.InvalidHashException;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.nifi.registry.properties.NiFiRegistryProperties;
 import org.apache.nifi.registry.security.authentication.exception.IdentityAccessException;
@@ -276,6 +281,14 @@ public class StandardOidcIdentityProvider implements OidcIdentityProvider {
         return oidcProviderMetadata.getEndSessionEndpointURI();
     }
 
+    @Override
+    public URI getRevocationEndpoint() {
+        if (!isOidcEnabled()) {
+            throw new IllegalStateException(OPEN_ID_CONNECT_SUPPORT_IS_NOT_CONFIGURED);
+        }
+        return oidcProviderMetadata.getRevocationEndpointURI();
+    }
+
     @Override
     public Scope getScope() {
         if (!isOidcEnabled()) {
@@ -302,7 +315,7 @@ public class StandardOidcIdentityProvider implements OidcIdentityProvider {
     }
 
     @Override
-    public String exchangeAuthorizationCode(final AuthorizationGrant authorizationGrant) throws IOException {
+    public String exchangeAuthorizationCodeForLoginAuthenticationToken(final AuthorizationGrant authorizationGrant) throws IOException {
         // Check if OIDC is enabled
         if (!isOidcEnabled()) {
             throw new IllegalStateException(OPEN_ID_CONNECT_SUPPORT_IS_NOT_CONFIGURED);
@@ -314,20 +327,60 @@ public class StandardOidcIdentityProvider implements OidcIdentityProvider {
         try {
             // Build the token request
             final HTTPRequest tokenHttpRequest = createTokenHTTPRequest(authorizationGrant, clientAuthentication);
-            return authorizeClient(tokenHttpRequest);
+            final TokenResponse response =  authorizeClient(tokenHttpRequest);
+            return convertOIDCTokenToNiFiToken((OIDCTokenResponse) response);
+        } catch (final ParseException | JOSEException | BadJOSEException | java.text.ParseException e) {
+            throw new RuntimeException("Unable to parse the response from the Token request: " + e.getMessage());
+        }
+    }
+
+    @Override
+    public String exchangeAuthorizationCodeForAccessToken(final AuthorizationGrant authorizationGrant) throws Exception {
+        // Check if OIDC is enabled
+        if (!isOidcEnabled()) {
+            throw new IllegalStateException(OPEN_ID_CONNECT_SUPPORT_IS_NOT_CONFIGURED);
+        }
 
+        // Build ClientAuthentication
+        final ClientAuthentication clientAuthentication = createClientAuthentication();
+
+        try {
+            // Build the token request
+            final HTTPRequest tokenHttpRequest = createTokenHTTPRequest(authorizationGrant, clientAuthentication);
+            final TokenResponse response = authorizeClient(tokenHttpRequest);
+            return getAccessTokenString((OIDCTokenResponse) response);
         } catch (final ParseException | JOSEException | BadJOSEException | java.text.ParseException e) {
             throw new RuntimeException("Unable to parse the response from the Token request: " + e.getMessage());
         }
     }
 
-    private String authorizeClient(HTTPRequest tokenHttpRequest) throws ParseException, IOException, BadJOSEException, JOSEException, java.text.ParseException {
+    @Override
+    public String exchangeAuthorizationCodeForIdToken(final AuthorizationGrant authorizationGrant) {
+        // Check if OIDC is enabled
+        if (!isOidcEnabled()) {
+            throw new IllegalStateException(OPEN_ID_CONNECT_SUPPORT_IS_NOT_CONFIGURED);
+        }
+
+        // Build ClientAuthentication
+        final ClientAuthentication clientAuthentication = createClientAuthentication();
+
+        try {
+            // Build the token request
+            final HTTPRequest tokenHttpRequest = createTokenHTTPRequest(authorizationGrant, clientAuthentication);
+            final TokenResponse response = authorizeClient(tokenHttpRequest);
+            return getIdTokenString((OIDCTokenResponse) response);
+        } catch (final RuntimeException | JOSEException | BadJOSEException | ParseException | IOException | java.text.ParseException e) {
+            throw new RuntimeException("Unable to parse the response from the Token request: " + e.getMessage(), e);
+        }
+    }
+
+    private TokenResponse authorizeClient(HTTPRequest tokenHttpRequest) throws ParseException, IOException, BadJOSEException, JOSEException, java.text.ParseException {
         // Get the token response
         final TokenResponse response = OIDCTokenResponseParser.parse(tokenHttpRequest.send());
 
         // Handle success
         if (response.indicatesSuccess()) {
-            return convertOIDCTokenToNiFiToken((OIDCTokenResponse) response);
+            return response;
         } else {
             // If the response was not successful
             final TokenErrorResponse errorResponse = (TokenErrorResponse) response;
@@ -463,4 +516,72 @@ public class StandardOidcIdentityProvider implements OidcIdentityProvider {
         }
     }
 
+    private String getAccessTokenString(final OIDCTokenResponse response) throws Exception {
+        final OIDCTokens oidcTokens = response.getOIDCTokens();
+
+        // Validate the Access Token
+        validateAccessToken(oidcTokens);
+
+        // Return the Access Token String
+        return oidcTokens.getAccessToken().getValue();
+    }
+
+    private String getIdTokenString(OIDCTokenResponse response) throws BadJOSEException, JOSEException {
+        final OIDCTokens oidcTokens = response.getOIDCTokens();
+
+        // Validate the Token - no nonce required for authorization code flow
+        validateIdToken(oidcTokens.getIDToken());
+
+        // Return the ID Token string
+        return oidcTokens.getIDTokenString();
+    }
+
+    private void validateAccessToken(OIDCTokens oidcTokens) throws Exception {
+        // Get the Access Token to validate
+        final AccessToken accessToken = oidcTokens.getAccessToken();
+
+        // Get the preferredJwsAlgorithm for validation
+        final JWSAlgorithm jwsAlgorithm = extractJwsAlgorithm();
+
+        // Get the accessTokenHash for validation
+        final String atHashString = oidcTokens
+                .getIDToken()
+                .getJWTClaimsSet()
+                .getStringClaim("at_hash");
+
+        // Compute the Access Token hash
+        final AccessTokenHash atHash = new AccessTokenHash(atHashString);
+
+        try {
+            // Validate the Token
+            AccessTokenValidator.validate(accessToken, jwsAlgorithm, atHash);
+        } catch (InvalidHashException e) {
+            throw new Exception("Unable to validate the Access Token: " + e.getMessage());
+        }
+    }
+
+    private IDTokenClaimsSet validateIdToken(JWT oidcJwt) throws BadJOSEException, JOSEException {
+        try {
+            return tokenValidator.validate(oidcJwt, null);
+        } catch (BadJOSEException e) {
+            throw new BadJOSEException("Unable to validate the ID Token: " + e.getMessage());
+        }
+    }
+
+    private JWSAlgorithm extractJwsAlgorithm() {
+
+        final String rawPreferredJwsAlgorithm = properties.getOidcPreferredJwsAlgorithm();
+
+        final JWSAlgorithm preferredJwsAlgorithm;
+        if (StringUtils.isBlank(rawPreferredJwsAlgorithm)) {
+            preferredJwsAlgorithm = JWSAlgorithm.RS256;
+        } else {
+            if ("none".equalsIgnoreCase(rawPreferredJwsAlgorithm)) {
+                preferredJwsAlgorithm = null;
+            } else {
+                preferredJwsAlgorithm = JWSAlgorithm.parse(rawPreferredJwsAlgorithm);
+            }
+        }
+        return preferredJwsAlgorithm;
+    }
 }
diff --git a/nifi-registry/nifi-registry-core/nifi-registry-web-api/src/test/java/org/apache/nifi/registry/web/security/authentication/oidc/OidcServiceTest.java b/nifi-registry/nifi-registry-core/nifi-registry-web-api/src/test/java/org/apache/nifi/registry/web/security/authentication/oidc/OidcServiceTest.java
index c3e5701d7c..2b3b2d08ff 100644
--- a/nifi-registry/nifi-registry-core/nifi-registry-web-api/src/test/java/org/apache/nifi/registry/web/security/authentication/oidc/OidcServiceTest.java
+++ b/nifi-registry/nifi-registry-core/nifi-registry-web-api/src/test/java/org/apache/nifi/registry/web/security/authentication/oidc/OidcServiceTest.java
@@ -83,14 +83,14 @@ public class OidcServiceTest {
     @Test(expected = IllegalStateException.class)
     public void testOidcNotEnabledExchangeCode() throws Exception {
         final OidcService service = getServiceWithNoOidcSupport();
-        service.exchangeAuthorizationCode(TEST_REQUEST_IDENTIFIER, getAuthorizationCodeGrant());
+        service.exchangeAuthorizationCodeForLoginAuthenticationToken(TEST_REQUEST_IDENTIFIER, getAuthorizationCodeGrant());
     }
 
     @Test(expected = IllegalStateException.class)
     public void testExchangeCodeMultipleInvocation() throws Exception {
         final OidcService service = getServiceWithOidcSupport();
-        service.exchangeAuthorizationCode(TEST_REQUEST_IDENTIFIER, getAuthorizationCodeGrant());
-        service.exchangeAuthorizationCode(TEST_REQUEST_IDENTIFIER, getAuthorizationCodeGrant());
+        service.exchangeAuthorizationCodeForLoginAuthenticationToken(TEST_REQUEST_IDENTIFIER, getAuthorizationCodeGrant());
+        service.exchangeAuthorizationCodeForLoginAuthenticationToken(TEST_REQUEST_IDENTIFIER, getAuthorizationCodeGrant());
     }
 
     @Test(expected = IllegalStateException.class)
@@ -102,14 +102,14 @@ public class OidcServiceTest {
     @Test
     public void testGetJwt() throws Exception {
         final OidcService service = getServiceWithOidcSupport();
-        service.exchangeAuthorizationCode(TEST_REQUEST_IDENTIFIER, getAuthorizationCodeGrant());
+        service.exchangeAuthorizationCodeForLoginAuthenticationToken(TEST_REQUEST_IDENTIFIER, getAuthorizationCodeGrant());
         assertNotNull(service.getJwt(TEST_REQUEST_IDENTIFIER));
     }
 
     @Test
     public void testGetJwtExpiration() throws Exception {
         final OidcService service = getServiceWithOidcSupportAndCustomExpiration(1, TimeUnit.SECONDS);
-        service.exchangeAuthorizationCode(TEST_REQUEST_IDENTIFIER, getAuthorizationCodeGrant());
+        service.exchangeAuthorizationCodeForLoginAuthenticationToken(TEST_REQUEST_IDENTIFIER, getAuthorizationCodeGrant());
 
         Thread.sleep(3 * 1000);
 
@@ -129,7 +129,7 @@ public class OidcServiceTest {
     private OidcService getServiceWithOidcSupport() throws Exception {
         final OidcIdentityProvider provider = mock(OidcIdentityProvider.class);
         when(provider.isOidcEnabled()).thenReturn(true);
-        when(provider.exchangeAuthorizationCode(any())).then(invocation -> UUID.randomUUID().toString());
+        when(provider.exchangeAuthorizationCodeForLoginAuthenticationToken(any())).then(invocation -> UUID.randomUUID().toString());
 
         final OidcService service = new OidcService(provider);
         assertTrue(service.isOidcEnabled());
@@ -140,7 +140,7 @@ public class OidcServiceTest {
     private OidcService getServiceWithOidcSupportAndCustomExpiration(final int duration, final TimeUnit units) throws Exception {
         final OidcIdentityProvider provider = mock(OidcIdentityProvider.class);
         when(provider.isOidcEnabled()).thenReturn(true);
-        when(provider.exchangeAuthorizationCode(any())).then(invocation -> UUID.randomUUID().toString());
+        when(provider.exchangeAuthorizationCodeForLoginAuthenticationToken(any())).then(invocation -> UUID.randomUUID().toString());
 
         final OidcService service = new OidcService(provider, duration, units);
         assertTrue(service.isOidcEnabled());
diff --git a/nifi-registry/nifi-registry-core/nifi-registry-web-ui/src/main/java/org/apache/nifi/registry/web/filter/LogoutFilter.java b/nifi-registry/nifi-registry-core/nifi-registry-web-ui/src/main/java/org/apache/nifi/registry/web/filter/LogoutFilter.java
index a005d5dfdc..627fb89c08 100644
--- a/nifi-registry/nifi-registry-core/nifi-registry-web-ui/src/main/java/org/apache/nifi/registry/web/filter/LogoutFilter.java
+++ b/nifi-registry/nifi-registry-core/nifi-registry-web-ui/src/main/java/org/apache/nifi/registry/web/filter/LogoutFilter.java
@@ -40,13 +40,12 @@ public class LogoutFilter implements Filter {
     @Override
     public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException {
         final boolean supportsOidc = Boolean.parseBoolean(servletContext.getInitParameter("oidc-supported"));
-
         if (supportsOidc) {
             final ServletContext apiContext = servletContext.getContext("/nifi-registry-api");
             apiContext.getRequestDispatcher("/access/oidc/logout").forward(request, response);
         } else {
             final ServletContext apiContext = servletContext.getContext("/nifi-registry-api");
-            apiContext.getRequestDispatcher("/access/logout").forward(request, response);
+            apiContext.getRequestDispatcher("/access/logout/complete").forward(request, response);
         }
     }
 
diff --git a/nifi-registry/nifi-registry-core/nifi-registry-web-ui/src/main/webapp/nf-registry.js b/nifi-registry/nifi-registry-core/nifi-registry-web-ui/src/main/webapp/nf-registry.js
index 2d7130871e..827ca389f4 100644
--- a/nifi-registry/nifi-registry-core/nifi-registry-web-ui/src/main/webapp/nf-registry.js
+++ b/nifi-registry/nifi-registry-core/nifi-registry-web-ui/src/main/webapp/nf-registry.js
@@ -72,7 +72,7 @@ NfRegistry.prototype = {
      */
     logout: function () {
         var self = this;
-        self.nfRegistryApi.deleteToLogout('../nifi-registry/logout').subscribe(
+        self.nfRegistryApi.deleteToLogout().subscribe(
             function () {
                 // next call
             },
@@ -84,7 +84,7 @@ NfRegistry.prototype = {
                 self.nfStorage.removeItem('jwt');
                 delete self.nfRegistryService.currentUser.identity;
                 delete self.nfRegistryService.currentUser.anonymous;
-                self.router.navigateByUrl('login');
+                window.location.href = location.origin + '/nifi-registry/logout';
             }
         );
     },
diff --git a/nifi-registry/nifi-registry-core/nifi-registry-web-ui/src/main/webapp/services/nf-registry.api.js b/nifi-registry/nifi-registry-core/nifi-registry-web-ui/src/main/webapp/services/nf-registry.api.js
index bb04339b58..fbc6740afe 100644
--- a/nifi-registry/nifi-registry-core/nifi-registry-web-ui/src/main/webapp/services/nf-registry.api.js
+++ b/nifi-registry/nifi-registry-core/nifi-registry-web-ui/src/main/webapp/services/nf-registry.api.js
@@ -866,7 +866,7 @@ NfRegistryApi.prototype = {
      *
      * @returns {*}
      */
-    deleteToLogout: function (url) {
+    deleteToLogout: function () {
         var self = this;
         var options = {
             headers: headers,
@@ -874,7 +874,7 @@ NfRegistryApi.prototype = {
             responseType: 'text'
         };
 
-        return this.http.delete(url, options).pipe(
+        return this.http.delete('../nifi-registry-api/access/logout', options).pipe(
             map(function (response) {
                 return response;
             }),


[nifi] 04/05: NIFI-10914 Adjusting input check for loading nested versioned flows (#6741)

Posted by jo...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 0f9541aa1e14ebf373dfe216a0b353308dd12323
Author: simonbence <61...@users.noreply.github.com>
AuthorDate: Thu Dec 1 17:09:51 2022 +0100

    NIFI-10914 Adjusting input check for loading nested versioned flows (#6741)
---
 .../src/main/java/org/apache/nifi/web/api/ProcessGroupResource.java | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ProcessGroupResource.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ProcessGroupResource.java
index dee9a7e9bd..126bc41ca3 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ProcessGroupResource.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ProcessGroupResource.java
@@ -2015,9 +2015,11 @@ public class ProcessGroupResource extends FlowUpdateResource<ProcessGroupImportE
             final RegisteredFlowSnapshot flowSnapshot = getFlowFromRegistry(versionControlInfo);
 
             // Step 3: Enrich version control info came from UI
-            if (flowSnapshot.getFlowContents() != null && flowSnapshot.getFlowContents().getVersionedFlowCoordinates() != null) {
+            if (flowSnapshot.getFlowContents() != null) {
                 final VersionedFlowCoordinates versionedFlowCoordinates = flowSnapshot.getFlowContents().getVersionedFlowCoordinates();
-                flowSnapshot.getFlowContents().getVersionedFlowCoordinates().setStorageLocation(versionedFlowCoordinates.getStorageLocation());
+                if (versionedFlowCoordinates != null) {
+                    versionControlInfo.setStorageLocation(versionedFlowCoordinates.getStorageLocation());
+                }
             }
 
             // Step 4: Resolve Bundle info


[nifi] 05/05: NIFI-10919 Corrected SCRAM SASL Mechanism for Kafka Components

Posted by jo...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 2b9b9ebb098035eac9ef88ddfd570cba0e79e2b1
Author: exceptionfactory <ex...@apache.org>
AuthorDate: Thu Dec 1 08:15:20 2022 -0600

    NIFI-10919 Corrected SCRAM SASL Mechanism for Kafka Components
    
    This closes #6743
    Signed-off-by: Paul Grey <gr...@apache.org>
---
 .../login/DelegatingLoginConfigProvider.java       |  2 +-
 .../nifi/kafka/shared/property/SaslMechanism.java  | 10 +++
 .../StandardKafkaPropertyNameProvider.java         | 16 +++--
 .../provider/StandardKafkaPropertyProvider.java    |  2 +-
 .../login/DelegatingLoginConfigProviderTest.java   | 69 +++++++++++++++++++
 .../StandardKafkaPropertyProviderTest.java         | 80 ++++++++++++++++++++++
 6 files changed, 171 insertions(+), 8 deletions(-)

diff --git a/nifi-nar-bundles/nifi-kafka-bundle/nifi-kafka-shared/src/main/java/org/apache/nifi/kafka/shared/login/DelegatingLoginConfigProvider.java b/nifi-nar-bundles/nifi-kafka-bundle/nifi-kafka-shared/src/main/java/org/apache/nifi/kafka/shared/login/DelegatingLoginConfigProvider.java
index c9b81bc595..2be8274606 100644
--- a/nifi-nar-bundles/nifi-kafka-bundle/nifi-kafka-shared/src/main/java/org/apache/nifi/kafka/shared/login/DelegatingLoginConfigProvider.java
+++ b/nifi-nar-bundles/nifi-kafka-bundle/nifi-kafka-shared/src/main/java/org/apache/nifi/kafka/shared/login/DelegatingLoginConfigProvider.java
@@ -47,7 +47,7 @@ public class DelegatingLoginConfigProvider implements LoginConfigProvider {
     @Override
     public String getConfiguration(final PropertyContext context) {
         final String saslMechanismProperty = context.getProperty(KafkaClientComponent.SASL_MECHANISM).getValue();
-        final SaslMechanism saslMechanism = SaslMechanism.valueOf(saslMechanismProperty);
+        final SaslMechanism saslMechanism = SaslMechanism.getSaslMechanism(saslMechanismProperty);
         final LoginConfigProvider loginConfigProvider = PROVIDERS.getOrDefault(saslMechanism, SCRAM_PROVIDER);
         return loginConfigProvider.getConfiguration(context);
     }
diff --git a/nifi-nar-bundles/nifi-kafka-bundle/nifi-kafka-shared/src/main/java/org/apache/nifi/kafka/shared/property/SaslMechanism.java b/nifi-nar-bundles/nifi-kafka-bundle/nifi-kafka-shared/src/main/java/org/apache/nifi/kafka/shared/property/SaslMechanism.java
index 619daeb86a..a9da7714e3 100644
--- a/nifi-nar-bundles/nifi-kafka-bundle/nifi-kafka-shared/src/main/java/org/apache/nifi/kafka/shared/property/SaslMechanism.java
+++ b/nifi-nar-bundles/nifi-kafka-bundle/nifi-kafka-shared/src/main/java/org/apache/nifi/kafka/shared/property/SaslMechanism.java
@@ -18,6 +18,9 @@ package org.apache.nifi.kafka.shared.property;
 
 import org.apache.nifi.components.DescribedValue;
 
+import java.util.Arrays;
+import java.util.Optional;
+
 /**
  * Enumeration of supported Kafka SASL Mechanisms
  */
@@ -42,6 +45,13 @@ public enum SaslMechanism implements DescribedValue {
         this.description = description;
     }
 
+    public static SaslMechanism getSaslMechanism(final String value) {
+        final Optional<SaslMechanism> foundSaslMechanism = Arrays.stream(SaslMechanism.values())
+                .filter(saslMechanism -> saslMechanism.getValue().equals(value))
+                .findFirst();
+        return foundSaslMechanism.orElseThrow(() -> new IllegalArgumentException(String.format("SaslMechanism value [%s] not found", value)));
+    }
+
     @Override
     public String getValue() {
         return value;
diff --git a/nifi-nar-bundles/nifi-kafka-bundle/nifi-kafka-shared/src/main/java/org/apache/nifi/kafka/shared/property/provider/StandardKafkaPropertyNameProvider.java b/nifi-nar-bundles/nifi-kafka-bundle/nifi-kafka-shared/src/main/java/org/apache/nifi/kafka/shared/property/provider/StandardKafkaPropertyNameProvider.java
index 10979716a4..2083bfc7e7 100644
--- a/nifi-nar-bundles/nifi-kafka-bundle/nifi-kafka-shared/src/main/java/org/apache/nifi/kafka/shared/property/provider/StandardKafkaPropertyNameProvider.java
+++ b/nifi-nar-bundles/nifi-kafka-bundle/nifi-kafka-shared/src/main/java/org/apache/nifi/kafka/shared/property/provider/StandardKafkaPropertyNameProvider.java
@@ -19,6 +19,7 @@ package org.apache.nifi.kafka.shared.property.provider;
 import java.lang.reflect.Field;
 import java.lang.reflect.Modifier;
 import java.util.LinkedHashSet;
+import java.util.Optional;
 import java.util.Set;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
@@ -58,9 +59,12 @@ public class StandardKafkaPropertyNameProvider implements KafkaPropertyNameProvi
         final Set<String> propertyNames = new LinkedHashSet<>();
 
         for (final String propertyClassName : PROPERTY_CLASSES) {
-            final Class<?> propertyClass = getClass(propertyClassName);
-            final Set<String> classPropertyNames = getStaticStringPropertyNames(propertyClass);
-            propertyNames.addAll(classPropertyNames);
+            final Optional<Class<?>> propertyClassFound = findClass(propertyClassName);
+            if (propertyClassFound.isPresent()) {
+                final Class<?> propertyClass = propertyClassFound.get();
+                final Set<String> classPropertyNames = getStaticStringPropertyNames(propertyClass);
+                propertyNames.addAll(classPropertyNames);
+            }
         }
 
         return propertyNames;
@@ -93,11 +97,11 @@ public class StandardKafkaPropertyNameProvider implements KafkaPropertyNameProvi
         }
     }
 
-    private static Class<?> getClass(final String className) {
+    private static Optional<Class<?>> findClass(final String className) {
         try {
-            return Class.forName(className);
+            return Optional.of(Class.forName(className));
         } catch (final ClassNotFoundException e) {
-            throw new IllegalStateException("Kafka Configuration Class not found", e);
+            return Optional.empty();
         }
     }
 }
diff --git a/nifi-nar-bundles/nifi-kafka-bundle/nifi-kafka-shared/src/main/java/org/apache/nifi/kafka/shared/property/provider/StandardKafkaPropertyProvider.java b/nifi-nar-bundles/nifi-kafka-bundle/nifi-kafka-shared/src/main/java/org/apache/nifi/kafka/shared/property/provider/StandardKafkaPropertyProvider.java
index a9ae4d6320..fd06acb6d0 100644
--- a/nifi-nar-bundles/nifi-kafka-bundle/nifi-kafka-shared/src/main/java/org/apache/nifi/kafka/shared/property/provider/StandardKafkaPropertyProvider.java
+++ b/nifi-nar-bundles/nifi-kafka-bundle/nifi-kafka-shared/src/main/java/org/apache/nifi/kafka/shared/property/provider/StandardKafkaPropertyProvider.java
@@ -83,7 +83,7 @@ public class StandardKafkaPropertyProvider implements KafkaPropertyProvider {
             final String loginConfig = LOGIN_CONFIG_PROVIDER.getConfiguration(context);
             properties.put(SASL_JAAS_CONFIG.getProperty(), loginConfig);
 
-            final SaslMechanism saslMechanism = SaslMechanism.valueOf(context.getProperty(SASL_MECHANISM).getValue());
+            final SaslMechanism saslMechanism = SaslMechanism.getSaslMechanism(context.getProperty(SASL_MECHANISM).getValue());
             if (SaslMechanism.GSSAPI == saslMechanism && isCustomKerberosLoginFound()) {
                 properties.put(SASL_LOGIN_CLASS.getProperty(), SASL_GSSAPI_CUSTOM_LOGIN_CLASS);
             }
diff --git a/nifi-nar-bundles/nifi-kafka-bundle/nifi-kafka-shared/src/test/java/org/apache/nifi/kafka/shared/login/DelegatingLoginConfigProviderTest.java b/nifi-nar-bundles/nifi-kafka-bundle/nifi-kafka-shared/src/test/java/org/apache/nifi/kafka/shared/login/DelegatingLoginConfigProviderTest.java
new file mode 100644
index 0000000000..fef038a0ee
--- /dev/null
+++ b/nifi-nar-bundles/nifi-kafka-bundle/nifi-kafka-shared/src/test/java/org/apache/nifi/kafka/shared/login/DelegatingLoginConfigProviderTest.java
@@ -0,0 +1,69 @@
+/*
+ * 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.kafka.shared.login;
+
+import org.apache.nifi.context.PropertyContext;
+import org.apache.nifi.kafka.shared.component.KafkaClientComponent;
+import org.apache.nifi.kafka.shared.property.SaslMechanism;
+import org.apache.nifi.util.NoOpProcessor;
+import org.apache.nifi.util.TestRunner;
+import org.apache.nifi.util.TestRunners;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+class DelegatingLoginConfigProviderTest {
+
+    private static final String PLAIN_LOGIN_MODULE = "PlainLoginModule";
+
+    private static final String SCRAM_LOGIN_MODULE = "ScramLoginModule";
+
+    DelegatingLoginConfigProvider provider;
+
+    TestRunner runner;
+
+    @BeforeEach
+    void setProvider() {
+        provider = new DelegatingLoginConfigProvider();
+        runner = TestRunners.newTestRunner(NoOpProcessor.class);
+        runner.setValidateExpressionUsage(false);
+    }
+
+    @Test
+    void testGetConfigurationPlain() {
+        runner.setProperty(KafkaClientComponent.SASL_MECHANISM, SaslMechanism.PLAIN.getValue());
+        final PropertyContext propertyContext = runner.getProcessContext();
+
+        final String configuration = provider.getConfiguration(propertyContext);
+
+        assertNotNull(configuration);
+        assertTrue(configuration.contains(PLAIN_LOGIN_MODULE), "PLAIN configuration not found");
+    }
+
+    @Test
+    void testGetConfigurationScram() {
+        runner.setProperty(KafkaClientComponent.SASL_MECHANISM, SaslMechanism.SCRAM_SHA_512.getValue());
+        final PropertyContext propertyContext = runner.getProcessContext();
+
+        final String configuration = provider.getConfiguration(propertyContext);
+
+        assertNotNull(configuration);
+        assertTrue(configuration.contains(SCRAM_LOGIN_MODULE), "SCRAM configuration not found");
+    }
+}
diff --git a/nifi-nar-bundles/nifi-kafka-bundle/nifi-kafka-shared/src/test/java/org/apache/nifi/kafka/shared/property/provider/StandardKafkaPropertyProviderTest.java b/nifi-nar-bundles/nifi-kafka-bundle/nifi-kafka-shared/src/test/java/org/apache/nifi/kafka/shared/property/provider/StandardKafkaPropertyProviderTest.java
new file mode 100644
index 0000000000..bce3412186
--- /dev/null
+++ b/nifi-nar-bundles/nifi-kafka-bundle/nifi-kafka-shared/src/test/java/org/apache/nifi/kafka/shared/property/provider/StandardKafkaPropertyProviderTest.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.kafka.shared.property.provider;
+
+import org.apache.nifi.context.PropertyContext;
+import org.apache.nifi.kafka.shared.component.KafkaClientComponent;
+import org.apache.nifi.kafka.shared.property.KafkaClientProperty;
+import org.apache.nifi.kafka.shared.property.SaslMechanism;
+import org.apache.nifi.kafka.shared.property.SecurityProtocol;
+import org.apache.nifi.util.NoOpProcessor;
+import org.apache.nifi.util.TestRunner;
+import org.apache.nifi.util.TestRunners;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import java.util.Map;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+class StandardKafkaPropertyProviderTest {
+
+    private static final String SCRAM_LOGIN_MODULE = "ScramLoginModule";
+
+    StandardKafkaPropertyProvider provider;
+
+    TestRunner runner;
+
+    @BeforeEach
+    void setProvider() {
+        provider = new StandardKafkaPropertyProvider(String.class);
+        runner = TestRunners.newTestRunner(NoOpProcessor.class);
+        runner.setValidateExpressionUsage(false);
+    }
+
+    @Test
+    void testGetProperties() {
+        final SecurityProtocol securityProtocol = SecurityProtocol.PLAINTEXT;
+
+        runner.setProperty(KafkaClientComponent.SECURITY_PROTOCOL, securityProtocol.name());
+        final PropertyContext propertyContext = runner.getProcessContext();
+
+        final Map<String, Object> properties = provider.getProperties(propertyContext);
+
+        assertEquals(securityProtocol.name(), properties.get(KafkaClientComponent.SECURITY_PROTOCOL.getName()));
+    }
+
+    @Test
+    void testGetPropertiesSaslMechanismScram() {
+        final SecurityProtocol securityProtocol = SecurityProtocol.SASL_PLAINTEXT;
+
+        runner.setProperty(KafkaClientComponent.SECURITY_PROTOCOL, securityProtocol.name());
+        runner.setProperty(KafkaClientComponent.SASL_MECHANISM, SaslMechanism.SCRAM_SHA_256.getValue());
+        final PropertyContext propertyContext = runner.getProcessContext();
+
+        final Map<String, Object> properties = provider.getProperties(propertyContext);
+
+        final Object securityProtocolProperty = properties.get(KafkaClientComponent.SECURITY_PROTOCOL.getName());
+        assertEquals(securityProtocol.name(), securityProtocolProperty);
+
+        final Object saslConfigProperty = properties.get(KafkaClientProperty.SASL_JAAS_CONFIG.getProperty());
+        assertNotNull(saslConfigProperty, "SASL configuration not found");
+        assertTrue(saslConfigProperty.toString().contains(SCRAM_LOGIN_MODULE), "SCRAM configuration not found");
+    }
+}


[nifi] 02/05: NIFI-10917 Upgraded Spring Framework from 5.3.23 to 5.3.24

Posted by jo...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 053f6bf01d6f92f5dc9eb2bc904e7b84ac17819f
Author: exceptionfactory <ex...@apache.org>
AuthorDate: Wed Nov 30 16:39:08 2022 -0600

    NIFI-10917 Upgraded Spring Framework from 5.3.23 to 5.3.24
    
    - Upgraded Spring Security from 5.7.5 to 5.8.0
    - Upgraded Spring Boot from 2.7.4 to 2.7.6 for Registry
    
    Signed-off-by: Pierre Villard <pi...@gmail.com>
    
    This closes #6737.
---
 nifi-registry/pom.xml | 2 +-
 pom.xml               | 4 ++--
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/nifi-registry/pom.xml b/nifi-registry/pom.xml
index 2781a77ffc..e6544b09f1 100644
--- a/nifi-registry/pom.xml
+++ b/nifi-registry/pom.xml
@@ -36,7 +36,7 @@
     </modules>
     <properties>
         <jax.rs.api.version>2.1</jax.rs.api.version>
-        <spring.boot.version>2.7.4</spring.boot.version>
+        <spring.boot.version>2.7.6</spring.boot.version>
         <flyway.version>8.5.13</flyway.version>
         <flyway.tests.version>7.0.0</flyway.tests.version>
         <swagger.ui.version>3.12.0</swagger.ui.version>
diff --git a/pom.xml b/pom.xml
index 147cc4c5d9..a6b7270031 100644
--- a/pom.xml
+++ b/pom.xml
@@ -142,8 +142,8 @@
         <netty.3.version>3.10.6.Final</netty.3.version>
         <snakeyaml.version>1.33</snakeyaml.version>
         <netty.4.version>4.1.85.Final</netty.4.version>
-        <spring.version>5.3.23</spring.version>
-        <spring.security.version>5.7.5</spring.security.version>
+        <spring.version>5.3.24</spring.version>
+        <spring.security.version>5.8.0</spring.security.version>
         <swagger.annotations.version>1.6.6</swagger.annotations.version>
         <h2.version>2.1.214</h2.version>
         <zookeeper.version>3.8.0</zookeeper.version>