NIFI-13998 Add OIDC Client Credentials Flow support to NiFi CLI (#9512)

Signed-off-by: David Handermann <exceptionfactory@apache.org>
This commit is contained in:
Pierre Villard 2024-11-14 04:23:27 +01:00 committed by GitHub
parent 21e7eef660
commit b7e49bfac1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 105 additions and 1 deletions

View File

@ -46,6 +46,7 @@ import org.apache.nifi.toolkit.cli.impl.client.nifi.VersionsClient;
import org.apache.nifi.toolkit.cli.impl.client.nifi.impl.JerseyNiFiClient;
import org.apache.nifi.toolkit.cli.impl.client.nifi.impl.request.BasicAuthRequestConfig;
import org.apache.nifi.toolkit.cli.impl.client.nifi.impl.request.BearerTokenRequestConfig;
import org.apache.nifi.toolkit.cli.impl.client.nifi.impl.request.OIDCClientCredentialsRequestConfig;
import org.apache.nifi.toolkit.cli.impl.client.nifi.impl.request.ProxiedEntityRequestConfig;
import org.apache.nifi.toolkit.cli.impl.command.CommandOption;
@ -83,6 +84,10 @@ public class NiFiClientFactory implements ClientFactory<NiFiClient> {
final String basicAuthUsername = properties.getProperty(CommandOption.BASIC_AUTH_USER.getLongName());
final String basicAuthPassword = properties.getProperty(CommandOption.BASIC_AUTH_PASSWORD.getLongName());
final String oidcTokenUrl = properties.getProperty(CommandOption.OIDC_TOKEN_URL.getLongName());
final String oidcClientId = properties.getProperty(CommandOption.OIDC_CLIENT_ID.getLongName());
final String oidcClientSecret = properties.getProperty(CommandOption.OIDC_CLIENT_SECRET.getLongName());
final String bearerToken = properties.getProperty(CommandOption.BEARER_TOKEN.getLongName());
final boolean secureUrl = url.startsWith("https");
@ -118,6 +123,16 @@ public class NiFiClientFactory implements ClientFactory<NiFiClient> {
+ " is required when specifying " + CommandOption.BASIC_AUTH_PASSWORD.getLongName());
}
if (!StringUtils.isBlank(oidcTokenUrl) && StringUtils.isBlank(oidcClientId)) {
throw new MissingOptionException(CommandOption.OIDC_CLIENT_ID.getLongName()
+ " is required when specifying " + CommandOption.OIDC_TOKEN_URL.getLongName());
}
if (!StringUtils.isBlank(oidcTokenUrl) && StringUtils.isBlank(oidcClientSecret)) {
throw new MissingOptionException(CommandOption.OIDC_CLIENT_SECRET.getLongName()
+ " is required when specifying " + CommandOption.OIDC_TOKEN_URL.getLongName());
}
final NiFiClientConfig.Builder clientConfigBuilder = new NiFiClientConfig.Builder()
.baseUrl(url);
@ -179,6 +194,9 @@ public class NiFiClientFactory implements ClientFactory<NiFiClient> {
} else if (!StringUtils.isBlank(basicAuthUsername) && !StringUtils.isBlank(basicAuthPassword)) {
final RequestConfig basicAuthConfig = new BasicAuthRequestConfig(basicAuthUsername, basicAuthPassword);
return new NiFiClientWithRequestConfig(client, basicAuthConfig);
} else if (!StringUtils.isBlank(oidcTokenUrl) && !StringUtils.isBlank(oidcClientId) && !StringUtils.isBlank(oidcClientSecret)) {
final RequestConfig oidcAuthConfig = new OIDCClientCredentialsRequestConfig(clientConfigBuilder.build(), oidcTokenUrl, oidcClientId, oidcClientSecret);
return new NiFiClientWithRequestConfig(client, oidcAuthConfig);
} else {
return client;
}

View File

@ -0,0 +1,83 @@
/*
* 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.toolkit.cli.impl.client.nifi.impl.request;
import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.ws.rs.client.ClientBuilder;
import jakarta.ws.rs.client.Entity;
import jakarta.ws.rs.client.WebTarget;
import jakarta.ws.rs.core.Form;
import org.apache.nifi.toolkit.cli.impl.client.nifi.NiFiClientConfig;
import org.apache.nifi.toolkit.cli.impl.client.nifi.RequestConfig;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
/**
* Implementation of RequestConfig when using the OAuth Client Credentials Flow
*/
public class OIDCClientCredentialsRequestConfig implements RequestConfig {
public static final String AUTHORIZATION_HEADER = "Authorization";
public static final String BEARER = "Bearer";
private final String token;
public OIDCClientCredentialsRequestConfig(NiFiClientConfig niFiClientConfig, final String oidcTokenUrl, final String oidcClientId, final String oidcClientSecret) {
Objects.requireNonNull(oidcTokenUrl);
Objects.requireNonNull(oidcClientId);
Objects.requireNonNull(oidcClientSecret);
Objects.requireNonNull(niFiClientConfig);
final SSLContext sslContext = niFiClientConfig.getSslContext();
final HostnameVerifier hostnameVerifier = niFiClientConfig.getHostnameVerifier();
final ClientBuilder clientBuilder = ClientBuilder.newBuilder();
if (sslContext != null) {
clientBuilder.sslContext(sslContext);
}
if (hostnameVerifier != null) {
clientBuilder.hostnameVerifier(hostnameVerifier);
}
final Form form = new Form();
form.param("grant_type", "client_credentials");
form.param("client_id", oidcClientId);
form.param("client_secret", oidcClientSecret);
final WebTarget target = clientBuilder.build().target(oidcTokenUrl);
final String response = target.request().post(Entity.form(form), String.class);
ObjectMapper mapper = new ObjectMapper();
try {
this.token = mapper.readTree(response).get("access_token").textValue();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
@Override
public Map<String, String> getHeaders() {
final Map<String, String> headers = new HashMap<>();
headers.put(AUTHORIZATION_HEADER, BEARER + " " + token);
return headers;
}
}

View File

@ -177,6 +177,10 @@ public enum CommandOption {
USERNAME("usr", "username", "The username for authentication when obtaining an access token", true),
PASSWORD("pwd", "password", "The password for authentication when obtaining an access token", true),
OIDC_TOKEN_URL("oidctokenurl", "oidcTokenUrl", "The OIDC URL to access the token endpoint for the OAuth Client Credentials Flow", true),
OIDC_CLIENT_ID("oidcid", "oidcClientId", "The Client ID for the OAuth Client Credentials Flow", true),
OIDC_CLIENT_SECRET("oidcsecret", "oidcClientSecret", "The Client Secret for the OAuth Client Credentials Flow", true),
KERBEROS_PRINCIPAL("krbPr", "kerberosPrincipal", "The kerberos principal", true),
KERBEROS_KEYTAB("krbKt", "kerberosKeytab", "The keytab for a kerberos principal", true, true),
KERBEROS_PASSWORD("krbPw", "kerberosPassword", "The password for a kerberos principal", true),
@ -207,7 +211,6 @@ public enum CommandOption {
this.isFile = isFile;
}
public String getShortName() {
return shortName;
}