secretless (#360)
This commit is contained in:
Родитель
2b49b63feb
Коммит
1eef873847
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
Загрузка…
Ссылка в новой задаче