This commit is contained in:
AsafMah 2024-04-11 10:18:20 +03:00 коммит произвёл GitHub
Родитель 2b49b63feb
Коммит 1eef873847
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: B5690EEEBB952194
7 изменённых файлов: 109 добавлений и 32 удалений

22
.github/workflows/build.yml поставляемый
Просмотреть файл

@ -4,30 +4,29 @@ name: Build Java
on:
push:
branches: [ '**' ]
branches: [ 'master' ]
pull_request:
branches: [ '**' ]
jobs:
build:
runs-on: ubuntu-latest
environment: build
permissions:
checks: write
pull-requests: write
id-token: write
contents: read
strategy:
matrix:
java: ['8', '11','17','21']
name: Java ${{ matrix.java }}
steps:
# Uncomment to run locally with "act"
# - name: Download Maven
# run: |
# curl -sL https://www-eu.apache.org/dist/maven/maven-3/3.8.4/binaries/apache-maven-3.8.4-bin.zip -o maven.zip
# apt-get update
# apt-get -y install unzip
# unzip -d /usr/share maven.zip
# rm maven.zip
# ln -s /usr/share/apache-maven-3.8.4/bin/mvn /usr/bin/mvn
# echo "M2_HOME=/usr/share/apache-maven-3.8.4" | tee -a /etc/environment
- name: Azure login
uses: azure/login@v2
with:
client-id: ${{ secrets.APP_ID }}
tenant-id: ${{ secrets.TENANT_ID }}
subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
- uses: actions/checkout@v2
- name: Setup java ${{ matrix.java }}
uses: actions/setup-java@v3
@ -42,7 +41,6 @@ jobs:
ENGINE_CONNECTION_STRING: https://sdkse2etest.eastus.kusto.windows.net
TEST_DATABASE: e2e
APP_ID: ${{ secrets.APP_ID }}
APP_KEY: ${{ secrets.APP_KEY }}
TENANT_ID: ${{ secrets.TENANT_ID }}
CI_EXECUTION: 1
- name: Publish Unit Test Results

9
.github/workflows/release.yml поставляемый
Просмотреть файл

@ -5,6 +5,7 @@ permissions:
packages: write
deployments: write
contents: write
id-token: write
on:
push:
@ -32,6 +33,7 @@ on:
jobs:
release:
runs-on: ubuntu-latest
environment: build
strategy:
matrix:
java: [ '8' ]
@ -40,6 +42,12 @@ jobs:
- uses: actions/checkout@v4
with:
ref: ${{ github.event.inputs.tag || github.ref }}
- name: Azure login
uses: azure/login@v2
with:
client-id: ${{ secrets.APP_ID }}
tenant-id: ${{ secrets.TENANT_ID }}
subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
- name: Setup java ${{ matrix.java }}
uses: actions/setup-java@v3
with:
@ -53,7 +61,6 @@ jobs:
ENGINE_CONNECTION_STRING: https://sdkse2etest.eastus.kusto.windows.net
TEST_DATABASE: e2e
APP_ID: ${{ secrets.APP_ID }}
APP_KEY: ${{ secrets.APP_KEY }}
TENANT_ID: ${{ secrets.TENANT_ID }}
CI_EXECUTION: 1
- name: Get version

Просмотреть файл

@ -5,6 +5,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [Unreleased]
### Added
- Azure CLI authentication
## [6.0.0] - 2024-03-07
### Changed

Просмотреть файл

@ -0,0 +1,51 @@
package com.microsoft.azure.kusto.data.auth;
import com.azure.core.credential.AccessToken;
import com.azure.core.credential.TokenRequestContext;
import com.azure.core.http.HttpClient;
import com.azure.identity.AzureCliCredential;
import com.azure.identity.AzureCliCredentialBuilder;
import com.microsoft.azure.kusto.data.exceptions.DataClientException;
import com.microsoft.azure.kusto.data.exceptions.DataServiceException;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.net.URISyntaxException;
public class AzureCliTokenProvider extends CloudDependentTokenProviderBase {
public static final String AZURE_CLI_TOKEN_PROVIDER = "AzureCliTokenProvider";
private TokenRequestContext tokenRequestContext;
private AzureCliCredential azureCliCredential;
public AzureCliTokenProvider(@NotNull String clusterUrl, @Nullable HttpClient httpClient) throws URISyntaxException {
super(clusterUrl, httpClient);
}
@Override
protected void initializeWithCloudInfo(CloudInfo cloudInfo) throws DataServiceException, DataClientException {
super.initializeWithCloudInfo(cloudInfo);
AzureCliCredentialBuilder builder = new AzureCliCredentialBuilder();
if (httpClient != null) {
builder = builder.httpClient(new HttpClientWrapper(httpClient));
}
this.azureCliCredential = builder.build();
tokenRequestContext = new TokenRequestContext().addScopes(scopes.toArray(new String[0]));
}
@Override
protected String acquireAccessTokenImpl() throws DataServiceException {
AccessToken accessToken = azureCliCredential.getToken(tokenRequestContext).block();
if (accessToken == null) {
throw new DataServiceException(clusterUrl, "Couldn't get token from Azure Identity", true);
}
return accessToken.getToken();
}
@Override
protected String getAuthMethod() {
return AZURE_CLI_TOKEN_PROVIDER;
}
}

Просмотреть файл

@ -33,6 +33,7 @@ public class ConnectionStringBuilder {
private String managedIdentityClientId;
private boolean useDeviceCodeAuth;
private boolean useManagedIdentityAuth;
private boolean useAzureCli;
private boolean useUserPromptAuth;
private String userNameForTracing;
private String appendedClientVersionForTracing;
@ -87,6 +88,7 @@ public class ConnectionStringBuilder {
this.managedIdentityClientId = null;
this.useDeviceCodeAuth = false;
this.useManagedIdentityAuth = false;
this.useAzureCli = false;
this.useUserPromptAuth = false;
this.userNameForTracing = null;
this.appendedClientVersionForTracing = null;
@ -145,6 +147,7 @@ public class ConnectionStringBuilder {
this.accessToken = other.accessToken;
this.tokenProvider = other.tokenProvider;
this.managedIdentityClientId = other.managedIdentityClientId;
this.useAzureCli = other.useAzureCli;
this.useDeviceCodeAuth = other.useDeviceCodeAuth;
this.useManagedIdentityAuth = other.useManagedIdentityAuth;
this.useUserPromptAuth = other.useUserPromptAuth;
@ -238,6 +241,10 @@ public class ConnectionStringBuilder {
return useManagedIdentityAuth;
}
boolean isUseAzureCli() {
return useAzureCli;
}
boolean isUseUserPromptAuth() {
return useUserPromptAuth;
}
@ -478,6 +485,17 @@ public class ConnectionStringBuilder {
return csb;
}
public static ConnectionStringBuilder createWithAzureCli(String clusterUrl) {
if (StringUtils.isEmpty(clusterUrl)) {
throw new IllegalArgumentException("clusterUrl cannot be null or empty");
}
ConnectionStringBuilder csb = new ConnectionStringBuilder();
csb.clusterUrl = clusterUrl;
csb.useAzureCli = true;
return csb;
}
/**
* Sets the application name and username for Kusto connectors.
*

Просмотреть файл

@ -48,6 +48,8 @@ public class TokenProviderFactory {
return new DeviceAuthTokenProvider(clusterUrl, authorityId, httpClient);
} else if (csb.isUseManagedIdentityAuth()) {
return new ManagedIdentityTokenProvider(clusterUrl, csb.getManagedIdentityClientId(), httpClient);
} else if (csb.isUseAzureCli()) {
return new AzureCliTokenProvider(clusterUrl, httpClient);
} else if (csb.isUseUserPromptAuth()) {
if (StringUtils.isNotBlank(csb.getUserUsernameHint())) {
String usernameHint = csb.getUserUsernameHint();

Просмотреть файл

@ -33,6 +33,7 @@ import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.StopWatch;
import org.apache.http.conn.util.InetAddressUtils;
import org.jetbrains.annotations.NotNull;
import org.junit.jupiter.api.*;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
@ -65,7 +66,7 @@ class E2ETest {
private static StreamingClient streamingClient;
private static final String databaseName = System.getenv("TEST_DATABASE");
private static final String appId = System.getenv("APP_ID");
private static String appKey;
private static final String appKey = System.getenv("APP_KEY");
private static final String tenantId = System.getenv().getOrDefault("TENANT_ID", "microsoft.com");
private static String principalFqn;
private static String resourcesPath;
@ -77,22 +78,12 @@ class E2ETest {
private final ObjectMapper objectMapper = Utils.getObjectMapper();
@BeforeAll
public static void setUp() throws IOException {
appKey = System.getenv("APP_KEY");
if (appKey == null) {
String secretPath = System.getProperty("SecretPath");
if (secretPath == null) {
throw new IllegalArgumentException("SecretPath is not set");
}
appKey = Files.readAllLines(Paths.get(secretPath)).get(0);
}
public static void setUp() {
tableName = "JavaTest_" + new SimpleDateFormat("yyyy_MM_dd_hh_mm_ss_SSS").format(Calendar.getInstance().getTime()) + "_"
+ ThreadLocalRandom.current().nextInt(Integer.MAX_VALUE);
principalFqn = String.format("aadapp=%s;%s", appId, tenantId);
ConnectionStringBuilder dmCsb = ConnectionStringBuilder.createWithAadApplicationCredentials(System.getenv("DM_CONNECTION_STRING"), appId, appKey,
tenantId);
ConnectionStringBuilder dmCsb = createConnection(System.getenv("DM_CONNECTION_STRING"));
dmCsb.setUserNameForTracing("testUser");
try {
dmCslClient = ClientFactory.createClient(dmCsb);
@ -107,8 +98,7 @@ class E2ETest {
Assertions.fail("Failed to create ingest client", ex);
}
ConnectionStringBuilder engineCsb = ConnectionStringBuilder.createWithAadApplicationCredentials(System.getenv("ENGINE_CONNECTION_STRING"), appId,
appKey, tenantId);
ConnectionStringBuilder engineCsb = createConnection(System.getenv("ENGINE_CONNECTION_STRING"));
engineCsb.setUserNameForTracing("Java_E2ETest_ø");
try {
streamingIngestClient = IngestClientFactory.createStreamingIngestClient(engineCsb);
@ -123,6 +113,15 @@ class E2ETest {
createTestData();
}
private static @NotNull ConnectionStringBuilder createConnection(String connectionString) {
if (appKey == null) {
return ConnectionStringBuilder.createWithAzureCli(connectionString);
}
return ConnectionStringBuilder.createWithAadApplicationCredentials(connectionString, appId, appKey,
tenantId);
}
@AfterAll
public static void tearDown() {
try {
@ -466,13 +465,14 @@ class E2ETest {
@Test
void testCreateWithAadApplicationCredentials() {
ConnectionStringBuilder engineCsb = ConnectionStringBuilder.createWithAadApplicationCredentials(System.getenv("ENGINE_CONNECTION_STRING"), appId,
appKey, tenantId);
Assumptions.assumeTrue(appKey != null);
ConnectionStringBuilder engineCsb = createConnection(System.getenv("ENGINE_CONNECTION_STRING"));
assertTrue(canAuthenticate(engineCsb));
}
@Test
void testCreateWithConnectionStringAndAadApplicationCredentials() {
Assumptions.assumeTrue(appKey != null);
ConnectionStringBuilder engineCsb = new ConnectionStringBuilder(
"Data Source=" + System.getenv("ENGINE_CONNECTION_STRING") + ";AppClientId=" + appId + ";AppKey=" + appKey + ";Authority ID=" + tenantId);
assertTrue(canAuthenticate(engineCsb));
@ -606,8 +606,7 @@ class E2ETest {
@Test
void testSameHttpClientInstance() throws DataClientException, DataServiceException, URISyntaxException {
ConnectionStringBuilder engineCsb = ConnectionStringBuilder.createWithAadApplicationCredentials(System.getenv("ENGINE_CONNECTION_STRING"), appId,
appKey, tenantId);
ConnectionStringBuilder engineCsb = createConnection(System.getenv("ENGINE_CONNECTION_STRING"));
HttpClient httpClient = HttpClientFactory.create(null);
HttpClient httpClientSpy = Mockito.spy(httpClient);
Client clientImpl = ClientFactory.createClient(engineCsb, httpClientSpy);