NIFI-11267 Removed deprecated OAuth2TokenProviderImpl

Signed-off-by: Pierre Villard <pierre.villard.fr@gmail.com>

This closes #7029.
This commit is contained in:
exceptionfactory 2023-03-10 09:30:40 -06:00 committed by Pierre Villard
parent 665b1696ef
commit b23b2621ac
No known key found for this signature in database
GPG Key ID: F92A93B30C07C6D5
4 changed files with 4 additions and 333 deletions

View File

@ -40,31 +40,20 @@
<artifactId>nifi-utils</artifactId>
<version>2.0.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-record</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-ssl-context-service-api</artifactId>
<version>2.0.0-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>log4j-over-slf4j</artifactId>
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>mockwebserver</artifactId>
@ -76,10 +65,5 @@
<version>2.0.0-SNAPSHOT</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<scope>compile</scope>
</dependency>
</dependencies>
</project>

View File

@ -1,186 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.nifi.oauth2;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import javax.net.ssl.SSLContext;
import javax.net.ssl.X509TrustManager;
import okhttp3.FormBody;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
import org.apache.nifi.annotation.documentation.CapabilityDescription;
import org.apache.nifi.annotation.documentation.DeprecationNotice;
import org.apache.nifi.annotation.documentation.Tags;
import org.apache.nifi.annotation.lifecycle.OnEnabled;
import org.apache.nifi.components.PropertyDescriptor;
import org.apache.nifi.controller.AbstractControllerService;
import org.apache.nifi.controller.ConfigurationContext;
import org.apache.nifi.processor.exception.ProcessException;
import org.apache.nifi.ssl.SSLContextService;
import org.apache.nifi.util.StringUtils;
@Deprecated
@DeprecationNotice(alternatives = {StandardOauth2AccessTokenProvider.class})
@Tags({"oauth2", "provider", "authorization" })
@CapabilityDescription("This controller service provides a way of working with access and refresh tokens via the " +
"password and client_credential grant flows in the OAuth2 specification. It is meant to provide a way for components " +
"to get a token from an oauth2 provider and pass that token as a part of a header to another service.")
public class OAuth2TokenProviderImpl extends AbstractControllerService implements OAuth2TokenProvider {
@Override
public List<PropertyDescriptor> getSupportedPropertyDescriptors() {
return PROPERTIES;
}
private String resourceServerUrl;
private SSLContext sslContext;
private SSLContextService sslService;
@OnEnabled
public void onEnabled(ConfigurationContext context) {
resourceServerUrl = context.getProperty(ACCESS_TOKEN_URL).evaluateAttributeExpressions().getValue();
sslService = context.getProperty(SSL_CONTEXT).asControllerService(SSLContextService.class);
sslContext = sslService == null ? null : sslService.createContext();
}
@Override
public AccessToken getAccessTokenByPassword(String clientId, String clientSecret,
String username, String password) throws AccessTokenAcquisitionException {
OkHttpClient.Builder builder = getClientBuilder();
OkHttpClient client = builder.build();
RequestBody body = new FormBody.Builder()
.add("username", username)
.add("password", password)
.add("client_id", clientId)
.add("client_secret", clientSecret)
.add("grant_type", "password")
.build();
Request newRequest = new Request.Builder()
.url(resourceServerUrl)
.post(body)
.build();
return executePost(client, newRequest);
}
private OkHttpClient.Builder getClientBuilder() {
OkHttpClient.Builder clientBuilder = new OkHttpClient.Builder();
if (sslService != null) {
final X509TrustManager trustManager = sslService.createTrustManager();
clientBuilder.sslSocketFactory(sslContext.getSocketFactory(), trustManager);
}
return clientBuilder;
}
private AccessToken executePost(OkHttpClient httpClient, Request newRequest) throws AccessTokenAcquisitionException {
try {
Response response = httpClient.newCall(newRequest).execute();
String body = response.body().string();
if (response.code() >= 300) {
getLogger().error(String.format("Bad response from the server during oauth2 request:\n%s", body));
throw new AccessTokenAcquisitionException(String.format("Got HTTP %d during oauth2 request.", response.code()));
}
return parseTokenResponse(body);
} catch (IOException e) {
throw new AccessTokenAcquisitionException(e);
}
}
@Override
public AccessToken getAccessTokenByClientCredentials(String clientId, String clientSecret) throws AccessTokenAcquisitionException {
OkHttpClient.Builder builder = getClientBuilder();
OkHttpClient client = builder.build();
RequestBody body = new FormBody.Builder()
.add("grant_type", "client_credentials")
.add("client_id", clientId)
.add("client_secret", clientSecret)
.build();
Request newRequest = new Request.Builder()
.url(resourceServerUrl)
.post(body)
.build();
return executePost(client, newRequest);
}
@Override
public AccessToken refreshToken(AccessToken refreshThis) throws AccessTokenAcquisitionException {
if (StringUtils.isEmpty(refreshThis.getRefreshToken())) {
throw new ProcessException("Missing refresh token. Refresh cannot happen.");
}
OkHttpClient.Builder builder = getClientBuilder();
OkHttpClient client = builder.build();
RequestBody body = new FormBody.Builder()
.add("grant_type", "refresh_token")
.add("refresh_token", refreshThis.getRefreshToken())
.build();
Request newRequest = new Request.Builder()
.url(resourceServerUrl)
.post(body)
.build();
return executePost(client, newRequest);
}
private static final ObjectMapper MAPPER = new ObjectMapper();
public static final String KEY_ACCESS_TOKEN = "access_token";
public static final String KEY_REFRESH_TOKEN = "refresh_token";
public static final String KEY_EXPIRES = "expires_in";
public static final String KEY_TOKEN_TYPE = "token_type";
public static final String KEY_SCOPE = "scope";
public AccessToken parseTokenResponse(String rawResponse) {
try {
Map<String, Object> parsed = MAPPER.readValue(rawResponse, Map.class);
String accessToken = (String)parsed.get(KEY_ACCESS_TOKEN);
String refreshToken = (String)parsed.get(KEY_REFRESH_TOKEN);
Integer expires = (Integer)parsed.get(KEY_EXPIRES);
String tokenType = (String)parsed.get(KEY_TOKEN_TYPE);
String scope = (String)parsed.get(KEY_SCOPE);
if (StringUtils.isEmpty(accessToken)) {
throw new Exception(String.format("Missing value for %s", KEY_ACCESS_TOKEN));
}
if (StringUtils.isEmpty(tokenType)) {
throw new Exception(String.format("Missing value for %s", KEY_TOKEN_TYPE));
}
return new AccessToken(accessToken, refreshToken, tokenType, expires, scope);
} catch (Exception ex) {
throw new ProcessException(ex);
}
}
}

View File

@ -12,6 +12,4 @@
# 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.
org.apache.nifi.oauth2.OAuth2TokenProviderImpl
org.apache.nifi.oauth2.StandardOauth2AccessTokenProvider

View File

@ -1,125 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.nifi.oauth2;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import okhttp3.mockwebserver.MockResponse;
import okhttp3.mockwebserver.MockWebServer;
import org.apache.nifi.processor.AbstractProcessor;
import org.apache.nifi.processor.ProcessContext;
import org.apache.nifi.processor.ProcessSession;
import org.apache.nifi.processor.exception.ProcessException;
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.HashMap;
import java.util.Map;
import java.util.UUID;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
public class OAuth2TokenProviderImplTest {
private TestRunner runner;
private MockWebServer mockWebServer;
private OAuth2TokenProvider oAuth2TokenProvider;
@BeforeEach
public void setup() throws Exception {
mockWebServer = new MockWebServer();
final String url = mockWebServer.url("/").toString();
runner = TestRunners.newTestRunner(new AbstractProcessor() {
@Override
public void onTrigger(ProcessContext context, ProcessSession session) throws ProcessException {
}
});
oAuth2TokenProvider = new OAuth2TokenProviderImpl();
runner.addControllerService("provider", oAuth2TokenProvider);
runner.setProperty(oAuth2TokenProvider, OAuth2TokenProvider.ACCESS_TOKEN_URL, url);
runner.enableControllerService(oAuth2TokenProvider);
runner.assertValid();
}
@Test
public void testClientCredentialGrant() throws AccessTokenAcquisitionException, JsonProcessingException {
enqueueTokenResponse();
final AccessToken token = oAuth2TokenProvider.getAccessTokenByClientCredentials(
"test-client",
UUID.randomUUID().toString()
);
assertAccessTokenFound(token);
}
@Test
public void testErrorHandler() {
mockWebServer.enqueue(new MockResponse().setResponseCode(500));
assertThrows(AccessTokenAcquisitionException.class, () -> oAuth2TokenProvider.getAccessTokenByClientCredentials(
"test-client",
UUID.randomUUID().toString()
));
}
@Test
public void testPasswordGrant() throws AccessTokenAcquisitionException, JsonProcessingException {
enqueueTokenResponse();
final AccessToken token = oAuth2TokenProvider.getAccessTokenByPassword(
"test-client",
UUID.randomUUID().toString(),
"user",
"password"
);
assertAccessTokenFound(token);
}
@Test
public void testRefreshToken() throws AccessTokenAcquisitionException, JsonProcessingException {
enqueueTokenResponse();
final AccessToken token = oAuth2TokenProvider.refreshToken(
new AccessToken("token", "refresh", "BEARER", 300, "test")
);
assertAccessTokenFound(token);
}
private void assertAccessTokenFound(final AccessToken accessToken) {
assertNotNull(accessToken);
assertEquals("access token", accessToken.getAccessToken());
assertEquals(5300, accessToken.getExpiresIn());
assertEquals("BEARER", accessToken.getTokenType());
assertFalse(accessToken.isExpired());
}
private void enqueueTokenResponse() throws JsonProcessingException {
final Map<String, Object> token = new HashMap<>();
token.put("access_token", "access token");
token.put("refresh_token", "refresh token");
token.put("token_type", "BEARER");
token.put("expires_in", 5300);
token.put("scope", "test scope");
final String accessToken = new ObjectMapper().writeValueAsString(token);
mockWebServer.enqueue(new MockResponse().setResponseCode(200).addHeader("Content-Type", "application/json").setBody(accessToken));
}
}