thenatog commented on code in PR #6637:
URL: https://github.com/apache/nifi/pull/6637#discussion_r1026848869
##########
nifi-registry/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/ApplicationResource.java:
##########
@@ -230,4 +230,10 @@ protected RevisionInfo getRevisionInfo(final LongParameter
version, final Client
return revisionInfo;
}
+ private String getNiFiRegistryUri() {
+ final String nifiRegistryApiUrl = generateResourceUri();
+ final String baseUrl =
StringUtils.substringBeforeLast(nifiRegistryApiUrl, "/nifi-registry-api");
+
+ return baseUrl + "/nifi-registry/";
Review Comment:
I would check if there's a way to use UriBuilder here to build this path
instead of using string manipulation/substring
##########
nifi-registry/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/authentication/jwt/JwtService.java:
##########
@@ -175,6 +175,20 @@ public String generateSignedToken(String identity, String
preferredUsername, Str
}
+ public void deleteKey(final String userIdentity) {
Review Comment:
Why does this method duplicate the below 'logOut()' method?
##########
nifi-registry/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/AccessResource.java:
##########
@@ -704,20 +707,131 @@ public void oidcLogout(@Context HttpServletRequest
httpServletRequest, @Context
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
+ // 1. revoke access token method, or
+ // 2. ID token logout method.
+ // If either of the above methods are supported,
+ // redirects request to OP to request authorization that can be
exchanged for a token used for logout
Review Comment:
Just want to confirm this - my understanding is that if we have an id_token,
we can make a simple logout request to end_session_endpoint. If we only
received an access_token initially, we need to HTTP request a new access_token
and send this to revocation_endpoint. Is this how we are logging out/what this
comment is saying?
##########
nifi-registry/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/AccessResource.java:
##########
@@ -832,4 +950,133 @@ private boolean isBasicLoginSupported(HttpServletRequest
request) {
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,
Review Comment:
Is there a reason these nimbusds classes are being used vs our
implementation?
##########
nifi-registry/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/AccessResource.java:
##########
@@ -832,4 +950,133 @@ private boolean isBasicLoginSupported(HttpServletRequest
request) {
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("Purposed 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 int msTimeout = 30_000;
Review Comment:
This timeout value could be set as a final member variable and it might be
better with a shorter value. In fact it looks like there's a
nifi.registry.security.user.oidc.read.timeout and
nifi.registry.security.user.oidc.connect.timeout properties that potentially
could be used.
##########
nifi-registry/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/AccessResource.java:
##########
@@ -832,4 +950,133 @@ private boolean isBasicLoginSupported(HttpServletRequest
request) {
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("Purposed state does
not match the stored state. Unable to continue %s process.",
loginOrLogoutMessage));
Review Comment:
Should this say 'Proposed state' ?
##########
nifi-registry/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/AccessResource.java:
##########
@@ -119,6 +137,7 @@ public AccessResource(
this.oidcService = oidcService;
this.kerberosSpnegoIdentityProvider = kerberosSpnegoIdentityProvider;
this.identityProvider = identityProvider;
+ this.bearerTokenResolver = new DefaultBearerTokenResolver();
Review Comment:
This token resolver is using Spring directly whereas NiFi code also contains
a StandardBearerTokenResolver implementation. We might want to confirm if we
should use that instead.
##########
nifi-registry/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/AccessResource.java:
##########
@@ -589,44 +611,25 @@ public void oidcCallback(@Context HttpServletRequest
httpServletRequest, @Contex
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 =
Review Comment:
Again here, any benefit to using these nimbusds implementations of
AuthenticationResponse?
##########
nifi-registry/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/AccessResource.java:
##########
@@ -687,7 +690,7 @@ public Response oidcExchange(@Context HttpServletRequest
httpServletRequest, @Co
return generateOkResponse(jwt).build();
}
- @DELETE
+ @GET
Review Comment:
Is there a reason to change this to a GET request? Historically I assume
this has been a DELETE request for some time now, therefore this would
potentially be changing the API contract.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]