Skip to content

Commit 5c6460c

Browse files
committed
chore: bump action-semantic-release to v2
1 parent a8ae45d commit 5c6460c

9 files changed

Lines changed: 672 additions & 2 deletions

File tree

‎.github/workflows/release.yml‎

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,10 +45,9 @@ jobs:
4545
cache: maven
4646

4747
- name: Run semantic-release
48-
uses: mridang/action-semantic-release@v1
48+
uses: mridang/action-semantic-release@v2
4949
with:
5050
github-token: ${{ secrets.GITHUB_TOKEN }}
51-
allow-force-install: 'true'
5251
env:
5352
DOCKER_REGISTRY_USER: ${{ github.actor }}
5453
DOCKER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }}

‎src/main/java/com/zitadel/ApiClient.java‎

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,19 +15,29 @@
1515
import org.apache.hc.client5.http.entity.mime.MultipartEntityBuilder;
1616
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
1717
import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse;
18+
import org.apache.hc.client5.http.impl.classic.HttpClientBuilder;
1819
import org.apache.hc.client5.http.impl.classic.HttpClients;
1920
import org.apache.hc.client5.http.protocol.HttpClientContext;
21+
import org.apache.hc.client5.http.ssl.NoopHostnameVerifier;
22+
import org.apache.hc.client5.http.ssl.SSLConnectionSocketFactoryBuilder;
23+
import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManagerBuilder;
2024
import org.apache.hc.core5.http.*;
2125
import org.apache.hc.core5.http.io.entity.ByteArrayEntity;
2226
import org.apache.hc.core5.http.io.entity.EntityUtils;
2327
import org.apache.hc.core5.http.io.entity.FileEntity;
2428
import org.apache.hc.core5.http.io.entity.StringEntity;
2529
import org.apache.hc.core5.http.io.support.ClassicRequestBuilder;
30+
import org.apache.hc.core5.http.message.BasicHeader;
2631
import org.apache.hc.core5.http.message.BasicNameValuePair;
2732
import org.openapitools.jackson.nullable.JsonNullableModule;
2833

2934
import javax.annotation.Nullable;
35+
import javax.net.ssl.SSLContext;
36+
import javax.net.ssl.TrustManager;
37+
import javax.net.ssl.TrustManagerFactory;
38+
import javax.net.ssl.X509TrustManager;
3039
import java.io.File;
40+
import java.io.FileInputStream;
3141
import java.io.IOException;
3242
import java.lang.reflect.Type;
3343
import java.net.URLEncoder;
@@ -37,13 +47,17 @@
3747
import java.nio.file.Files;
3848
import java.nio.file.Paths;
3949
import java.nio.file.StandardCopyOption;
50+
import java.security.KeyStore;
51+
import java.security.cert.CertificateFactory;
52+
import java.security.cert.X509Certificate;
4053
import java.text.DateFormat;
4154
import java.time.OffsetDateTime;
4255
import java.time.format.DateTimeFormatter;
4356
import java.util.*;
4457
import java.util.Map.Entry;
4558
import java.util.regex.Matcher;
4659
import java.util.regex.Pattern;
60+
import java.util.stream.Collectors;
4761

4862
@SuppressFBWarnings("THROWS_METHOD_THROWS_RUNTIMEEXCEPTION")
4963
public class ApiClient {
@@ -91,6 +105,74 @@ public ApiClient(Authenticator authenticator) {
91105
this(authenticator, HttpClients.custom().setUserAgent(USER_AGENT).build());
92106
}
93107

108+
@SuppressFBWarnings("PATH_TRAVERSAL_IN")
109+
public ApiClient(Authenticator authenticator, TransportOptions transportOptions) {
110+
this(authenticator, buildHttpClient(transportOptions));
111+
}
112+
113+
@SuppressFBWarnings("PATH_TRAVERSAL_IN")
114+
private static CloseableHttpClient buildHttpClient(TransportOptions transportOptions) {
115+
try {
116+
HttpClientBuilder builder = HttpClients.custom().setUserAgent(USER_AGENT);
117+
118+
if (transportOptions.isInsecure()) {
119+
SSLContext sslContext = SSLContext.getInstance("TLS");
120+
sslContext.init(null, new TrustManager[]{new X509TrustManager() {
121+
@Override
122+
public void checkClientTrusted(X509Certificate[] chain, String authType) {
123+
// trust all
124+
}
125+
126+
@Override
127+
public void checkServerTrusted(X509Certificate[] chain, String authType) {
128+
// trust all
129+
}
130+
131+
@Override
132+
public X509Certificate[] getAcceptedIssuers() {
133+
return new X509Certificate[0];
134+
}
135+
}}, null);
136+
SSLConnectionSocketFactoryBuilder sslSocketFactoryBuilder = SSLConnectionSocketFactoryBuilder.create()
137+
.setSslContext(sslContext)
138+
.setHostnameVerifier(NoopHostnameVerifier.INSTANCE);
139+
builder.setConnectionManager(PoolingHttpClientConnectionManagerBuilder.create()
140+
.setSSLSocketFactory(sslSocketFactoryBuilder.build())
141+
.build());
142+
} else if (transportOptions.getCaCertPath() != null) {
143+
CertificateFactory cf = CertificateFactory.getInstance("X.509");
144+
java.security.cert.Certificate caCert;
145+
try (FileInputStream fis = new FileInputStream(transportOptions.getCaCertPath())) {
146+
caCert = cf.generateCertificate(fis);
147+
}
148+
KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
149+
ks.load(null, null);
150+
ks.setCertificateEntry("custom-ca", caCert);
151+
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
152+
tmf.init(ks);
153+
SSLContext sslContext = SSLContext.getInstance("TLS");
154+
sslContext.init(null, tmf.getTrustManagers(), null);
155+
SSLConnectionSocketFactoryBuilder sslSocketFactoryBuilder = SSLConnectionSocketFactoryBuilder.create()
156+
.setSslContext(sslContext)
157+
.setHostnameVerifier(NoopHostnameVerifier.INSTANCE);
158+
builder.setConnectionManager(PoolingHttpClientConnectionManagerBuilder.create()
159+
.setSSLSocketFactory(sslSocketFactoryBuilder.build())
160+
.build());
161+
}
162+
163+
if (!transportOptions.getDefaultHeaders().isEmpty()) {
164+
List<Header> headers = transportOptions.getDefaultHeaders().entrySet().stream()
165+
.map(e -> (Header) new BasicHeader(e.getKey(), e.getValue()))
166+
.collect(Collectors.toList());
167+
builder.setDefaultHeaders(headers);
168+
}
169+
170+
return builder.build();
171+
} catch (Exception e) {
172+
throw new RuntimeException("Failed to build HTTP client with transport options: " + e.getMessage(), e);
173+
}
174+
}
175+
94176
public static DateFormat buildDefaultDateFormat() {
95177
return new RFC3339DateFormat();
96178
}
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
package com.zitadel;
2+
3+
import javax.annotation.Nullable;
4+
import java.util.Collections;
5+
import java.util.LinkedHashMap;
6+
import java.util.Map;
7+
8+
/**
9+
* Immutable transport options for configuring HTTP connections.
10+
*
11+
* <p>Provides settings for default headers, custom CA certificate paths, and insecure
12+
* (trust-all) TLS connections. Use the {@link Builder} to construct instances.
13+
*/
14+
public class TransportOptions {
15+
16+
private final Map<String, String> defaultHeaders;
17+
@Nullable
18+
private final String caCertPath;
19+
private final boolean insecure;
20+
21+
private TransportOptions(Map<String, String> defaultHeaders, @Nullable String caCertPath, boolean insecure) {
22+
this.defaultHeaders = Collections.unmodifiableMap(new LinkedHashMap<>(defaultHeaders));
23+
this.caCertPath = caCertPath;
24+
this.insecure = insecure;
25+
}
26+
27+
/**
28+
* Returns a default {@code TransportOptions} instance with no custom headers,
29+
* no CA cert path, and insecure mode disabled.
30+
*
31+
* @return the default transport options.
32+
*/
33+
public static TransportOptions defaults() {
34+
return new TransportOptions(Collections.emptyMap(), null, false);
35+
}
36+
37+
/**
38+
* Returns the default headers to include in every request.
39+
*
40+
* @return an unmodifiable map of header names to values.
41+
*/
42+
public Map<String, String> getDefaultHeaders() {
43+
return defaultHeaders;
44+
}
45+
46+
/**
47+
* Returns the path to a custom CA certificate file used for TLS verification.
48+
*
49+
* @return the CA cert file path, or {@code null} if not set.
50+
*/
51+
@Nullable
52+
public String getCaCertPath() {
53+
return caCertPath;
54+
}
55+
56+
/**
57+
* Returns whether insecure (trust-all) TLS mode is enabled.
58+
*
59+
* @return {@code true} if insecure mode is enabled; {@code false} otherwise.
60+
*/
61+
public boolean isInsecure() {
62+
return insecure;
63+
}
64+
65+
/**
66+
* Builder for constructing {@link TransportOptions} instances.
67+
*/
68+
public static class Builder {
69+
70+
private final Map<String, String> defaultHeaders = new LinkedHashMap<>();
71+
@Nullable
72+
private String caCertPath;
73+
private boolean insecure;
74+
75+
/**
76+
* Adds a default header to include in every request.
77+
*
78+
* @param key the header name.
79+
* @param value the header value.
80+
* @return this builder.
81+
*/
82+
public Builder defaultHeader(String key, String value) {
83+
this.defaultHeaders.put(key, value);
84+
return this;
85+
}
86+
87+
/**
88+
* Sets the path to a custom CA certificate file for TLS verification.
89+
*
90+
* @param path the CA cert file path.
91+
* @return this builder.
92+
*/
93+
public Builder caCertPath(String path) {
94+
this.caCertPath = path;
95+
return this;
96+
}
97+
98+
/**
99+
* Sets whether insecure (trust-all) TLS mode should be enabled.
100+
*
101+
* @param insecure {@code true} to trust all certificates; {@code false} otherwise.
102+
* @return this builder.
103+
*/
104+
public Builder insecure(boolean insecure) {
105+
this.insecure = insecure;
106+
return this;
107+
}
108+
109+
/**
110+
* Builds the {@link TransportOptions} instance.
111+
*
112+
* @return a new immutable {@code TransportOptions}.
113+
*/
114+
public TransportOptions build() {
115+
return new TransportOptions(defaultHeaders, caCertPath, insecure);
116+
}
117+
}
118+
}

‎src/main/java/com/zitadel/Zitadel.java‎

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,53 @@ public Zitadel(Authenticator authenticator) {
231231
});
232232
}
233233

234+
public Zitadel(Authenticator authenticator, TransportOptions transportOptions) {
235+
this(
236+
authenticator,
237+
transportOptions,
238+
apiClient -> {
239+
// Dummy lambda that does nothing, can add default behavior here if needed
240+
});
241+
}
242+
243+
public Zitadel(Authenticator authenticator, TransportOptions transportOptions, Consumer<ApiClient> mutateApiClient) {
244+
this.apiClient = new ApiClient(authenticator, transportOptions);
245+
246+
if (mutateApiClient != null) {
247+
mutateApiClient.accept(this.apiClient);
248+
}
249+
250+
this.betaProjects = new BetaProjectServiceApi(apiClient);
251+
this.betaApps = new BetaAppServiceApi(apiClient);
252+
this.betaOidc = new BetaOidcServiceApi(apiClient);
253+
this.betaUsers = new BetaUserServiceApi(apiClient);
254+
this.betaOrganizations = new BetaOrganizationServiceApi(apiClient);
255+
this.betaSettings = new BetaSettingsServiceApi(apiClient);
256+
this.betaPermissions = new BetaInternalPermissionServiceApi(apiClient);
257+
this.betaAuthorizations = new BetaAuthorizationServiceApi(apiClient);
258+
this.betaSessions = new BetaSessionServiceApi(apiClient);
259+
this.betaInstance = new BetaInstanceServiceApi(apiClient);
260+
this.betaTelemetry = new BetaTelemetryServiceApi(apiClient);
261+
this.betaFeatures = new BetaFeatureServiceApi(apiClient);
262+
this.betaWebkeys = new BetaWebKeyServiceApi(apiClient);
263+
this.betaActions = new BetaActionServiceApi(apiClient);
264+
this.actions = new ActionServiceApi(apiClient);
265+
this.applications = new ApplicationServiceApi(apiClient);
266+
this.authorizations = new AuthorizationServiceApi(apiClient);
267+
this.users = new UserServiceApi(apiClient);
268+
this.sessions = new SessionServiceApi(apiClient);
269+
this.oidc = new OidcServiceApi(apiClient);
270+
this.features = new FeatureServiceApi(apiClient);
271+
this.idps = new IdentityProviderServiceApi(apiClient);
272+
this.instances = new InstanceServiceApi(apiClient);
273+
this.internalPermissions = new InternalPermissionServiceApi(apiClient);
274+
this.organizations = new OrganizationServiceApi(apiClient);
275+
this.projects = new ProjectServiceApi(apiClient);
276+
this.settings = new SettingsServiceApi(apiClient);
277+
this.saml = new SamlServiceApi(apiClient);
278+
this.webkeys = new WebKeyServiceApi(apiClient);
279+
}
280+
234281
public Zitadel(Authenticator authenticator, Consumer<ApiClient> mutateApiClient) {
235282
this.apiClient = new ApiClient(authenticator);
236283

@@ -283,6 +330,18 @@ public static Zitadel withAccessToken(String host, String accessToken) {
283330
return new Zitadel(new PersonalAccessTokenAuthenticator(host, accessToken));
284331
}
285332

333+
/**
334+
* Initialize the SDK with a Personal Access Token (PAT) and custom transport options.
335+
*
336+
* @param host API URL.
337+
* @param accessToken Personal Access Token for Bearer authentication.
338+
* @param transportOptions Transport options for HTTP connections.
339+
* @return Configured Zitadel client instance.
340+
*/
341+
public static Zitadel withAccessToken(String host, String accessToken, TransportOptions transportOptions) {
342+
return new Zitadel(new PersonalAccessTokenAuthenticator(host, accessToken), transportOptions);
343+
}
344+
286345
/**
287346
* Initialize the SDK using OAuth2 Client Credentials flow.
288347
*
@@ -299,6 +358,21 @@ public static Zitadel withClientCredentials(String host, String clientId, String
299358
ClientCredentialsAuthenticator.builder(host, clientId, clientSecret).build());
300359
}
301360

361+
/**
362+
* Initialize the SDK using OAuth2 Client Credentials flow with custom transport options.
363+
*
364+
* @param host API URL.
365+
* @param clientId OAuth2 client identifier.
366+
* @param clientSecret OAuth2 client secret.
367+
* @param transportOptions Transport options for HTTP connections.
368+
* @return Configured Zitadel client instance with token auto-refresh.
369+
*/
370+
public static Zitadel withClientCredentials(String host, String clientId, String clientSecret, TransportOptions transportOptions) {
371+
return new Zitadel(
372+
ClientCredentialsAuthenticator.builder(host, clientId, clientSecret, transportOptions).build(),
373+
transportOptions);
374+
}
375+
302376
/**
303377
* Initialize the SDK via Private Key JWT assertion.
304378
*
@@ -331,6 +405,36 @@ public static Zitadel withPrivateKey(String host, java.io.InputStream inputStrea
331405
return new Zitadel(WebTokenAuthenticator.fromJson(host, inputStream));
332406
}
333407

408+
/**
409+
* Initialize the SDK via Private Key JWT assertion with custom transport options.
410+
*
411+
* @param host API URL.
412+
* @param keyFile Path to service account JSON or PEM key file.
413+
* @param transportOptions Transport options for HTTP connections.
414+
* @return Configured Zitadel client instance using JWT assertion.
415+
*/
416+
@SuppressFBWarnings({"PATH_TRAVERSAL_IN", "THROWS_METHOD_THROWS_RUNTIMEEXCEPTION"})
417+
public static Zitadel withPrivateKey(String host, String keyFile, TransportOptions transportOptions) {
418+
try (java.io.FileInputStream fis = new java.io.FileInputStream(keyFile)) {
419+
return withPrivateKey(host, fis, transportOptions);
420+
} catch (IOException e) {
421+
throw new RuntimeException(
422+
"Unable to read key file at " + keyFile + ": " + e.getMessage(), e);
423+
}
424+
}
425+
426+
/**
427+
* Initialize the SDK via Private Key JWT assertion with custom transport options.
428+
*
429+
* @param host API URL.
430+
* @param inputStream Input stream containing service account JSON or PEM key data.
431+
* @param transportOptions Transport options for HTTP connections.
432+
* @return Configured Zitadel client instance using JWT assertion.
433+
*/
434+
public static Zitadel withPrivateKey(String host, java.io.InputStream inputStream, TransportOptions transportOptions) {
435+
return new Zitadel(WebTokenAuthenticator.fromJson(host, inputStream, transportOptions), transportOptions);
436+
}
437+
334438
/**
335439
* Gets the underlying API client. Recommended over direct field access for testability.
336440
*

0 commit comments

Comments
 (0)