Skip to content

Commit 83d82ff

Browse files
Merge branch 'refs/heads/main' into fix-one-of-7.22
2 parents 4e2caa3 + fcac789 commit 83d82ff

21 files changed

Lines changed: 493 additions & 57 deletions

File tree

.github/workflows/blackduck.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,14 @@ on:
55
schedule:
66
- cron: 0 23 * * *
77

8+
permissions: {}
9+
810
jobs:
911
scan:
1012
name: "Blackduck Scan"
1113
runs-on: ubuntu-latest
14+
permissions:
15+
contents: read
1216
timeout-minutes: 15
1317
steps:
1418
- uses: actions/checkout@v6
@@ -19,6 +23,8 @@ jobs:
1923

2024
notify-job:
2125
runs-on: ubuntu-latest
26+
permissions:
27+
contents: read
2228
needs: [ scan ]
2329
if: ${{ failure() && github.ref == 'refs/heads/main' }}
2430
steps:

.github/workflows/continuous-integration.yaml

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,12 +50,16 @@ env:
5050
MVN_SINGLE_THREADED_ARGS: --batch-mode --no-transfer-progress --fail-at-end --show-version --threads 1
5151
MVN_SKIP_CI_PLUGINS: -DskipFormatting -Denforcer.skip -Djacoco.skip -Dmdep.analyze.skip
5252

53+
permissions: {}
54+
5355
jobs:
5456
context:
5557
name: "Collect Context"
5658
outputs:
5759
commit: ${{ steps.calculate-commit-sha.outputs.COMMIT }}
5860
runs-on: ubuntu-latest
61+
permissions:
62+
contents: read
5963
steps:
6064
- name: "Calculate Commit SHA"
6165
id: calculate-commit-sha
@@ -79,6 +83,8 @@ jobs:
7983
name: "Check Formatting"
8084
needs: [ context ]
8185
runs-on: ubuntu-latest
86+
permissions:
87+
contents: read
8288
steps:
8389
- name: "Checkout Repository"
8490
uses: actions/checkout@v6
@@ -106,6 +112,8 @@ jobs:
106112
name: "Build"
107113
needs: [ context, check-formatting ]
108114
runs-on: ubuntu-latest
115+
permissions:
116+
contents: read # upload-artifacts does not use github-token
109117
steps:
110118
- name: "Checkout repository"
111119
uses: actions/checkout@v6
@@ -160,6 +168,8 @@ jobs:
160168
name: "Test"
161169
needs: [ context, build ]
162170
runs-on: ubuntu-latest
171+
permissions:
172+
contents: read
163173
steps:
164174
- name: "Checkout repository"
165175
uses: actions/checkout@v6
@@ -202,6 +212,8 @@ jobs:
202212
static-code-analysis:
203213
needs: [ context, build ]
204214
runs-on: ubuntu-latest
215+
permissions:
216+
contents: read
205217
strategy:
206218
matrix:
207219
task:
@@ -262,6 +274,9 @@ jobs:
262274
name: "Run CodeQL Analysis"
263275
needs: [ context ]
264276
runs-on: ubuntu-latest
277+
permissions:
278+
contents: read
279+
security-events: write # needed for Perform CodeQL Analysis
265280
steps:
266281
- name: "Checkout repository"
267282
uses: actions/checkout@v6
@@ -301,6 +316,8 @@ jobs:
301316
test-archetypes:
302317
runs-on: ubuntu-latest
303318
needs: [ context, build ]
319+
permissions:
320+
contents: read
304321
strategy:
305322
matrix:
306323
task:
@@ -396,6 +413,8 @@ jobs:
396413
if: ${{ github.event.inputs.run-blackduck-scan == 'true' }}
397414
needs: [ context ]
398415
runs-on: ubuntu-latest
416+
permissions:
417+
contents: read
399418
steps:
400419
- name: "Checkout repository"
401420
uses: actions/checkout@v6
@@ -412,6 +431,8 @@ jobs:
412431
if: ${{ github.event.inputs.run-security-rating == 'true' }}
413432
needs: [ context ]
414433
runs-on: ubuntu-latest
434+
permissions:
435+
contents: write # needed for Run FOSStars Rating
415436
steps:
416437
- name: "Checkout repository"
417438
uses: actions/checkout@v6

.github/workflows/dependabot-automerge.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ env:
1414
jobs:
1515
review-prs:
1616
runs-on: ubuntu-latest
17+
permissions:
18+
contents: read # all write operations use app token
1719
steps:
1820
- name: Checkout
1921
uses: actions/checkout@v6

.github/workflows/deploy-snapshot.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ jobs:
99
deploy-snapshot:
1010
name: Deploy Snapshot
1111
runs-on: ubuntu-latest
12+
permissions:
13+
contents: read
1214
steps:
1315
- name: "Checkout Repository"
1416
uses: actions/checkout@v6

.github/workflows/fosstars-report.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ jobs:
88
create_fosstars_report:
99
runs-on: ubuntu-latest
1010
name: "Security rating"
11+
permissions:
12+
contents: write # needed to push to branch
1113
steps:
1214
- uses: actions/checkout@v6
1315
- uses: SAP/fosstars-rating-core-action@v1.14.0

.github/workflows/javadoc.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ jobs:
1717
build:
1818
name: 'JavaDoc to Documentation Portal'
1919
runs-on: ubuntu-latest
20+
permissions:
21+
contents: read # all write operations use app token
2022

2123
steps:
2224
- name: 'Prepare git'

.github/workflows/prepare-release.yaml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ env:
1818
JAVA_VERSION: 17
1919
DOCS_REPO: SAP/cloud-sdk
2020

21+
permissions: {}
22+
2123
jobs:
2224
bump-version:
2325
name: 'Bump Version'
@@ -29,6 +31,8 @@ jobs:
2931
release-commit: ${{ steps.prepare-release.outputs.RELEASE_COMMIT_ID }}
3032
release-tag: ${{ steps.prepare-release.outputs.TAG_NAME }}
3133
runs-on: ubuntu-latest
34+
permissions:
35+
contents: write # needed for git push
3236
steps:
3337
- name: 'Checkout Repository'
3438
uses: actions/checkout@v6
@@ -180,6 +184,8 @@ jobs:
180184
outputs:
181185
pr-url: ${{ steps.create-release-notes-pr.outputs.PR_URL }}
182186
runs-on: ubuntu-latest
187+
permissions:
188+
contents: read # all write operations use app token
183189
steps:
184190
- name: 'Create GitHub App Token'
185191
id: app-token
@@ -269,6 +275,8 @@ jobs:
269275
outputs:
270276
pr-url: ${{ steps.create-code-pr.outputs.PR_URL }}
271277
runs-on: ubuntu-latest
278+
permissions:
279+
contents: read # all write operations use app token
272280
steps:
273281
- name: 'Create GitHub App Token'
274282
id: app-token

.github/workflows/reuse.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ on:
1111
jobs:
1212
test:
1313
runs-on: ubuntu-latest
14+
permissions:
15+
contents: read
1416
steps:
1517
- uses: actions/checkout@v6
1618
- name: REUSE Compliance Check

cloudplatform/connectivity-oauth/src/main/java/com/sap/cloud/sdk/cloudplatform/connectivity/BtpServicePropertySuppliers.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,7 @@ public OAuth2Options getOAuth2Options()
195195
.peek(format -> builder.withTokenRetrievalParameter("token_format", format));
196196
}
197197
attachClientKeyStore(builder);
198+
getCredential(URI.class, "btp-tenant-api").peek(builder::withBtpTenantApiBaseUri);
198199

199200
return builder.build();
200201
}
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
package com.sap.cloud.sdk.cloudplatform.connectivity;
2+
3+
import java.io.IOException;
4+
import java.net.URI;
5+
import java.nio.charset.StandardCharsets;
6+
7+
import javax.annotation.Nonnull;
8+
9+
import org.apache.hc.client5.http.classic.methods.HttpGet;
10+
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
11+
import org.apache.hc.client5.http.impl.classic.HttpClients;
12+
import org.apache.hc.core5.http.HttpStatus;
13+
import org.apache.hc.core5.http.io.entity.EntityUtils;
14+
import org.json.JSONObject;
15+
16+
import com.sap.cloud.sdk.cloudplatform.connectivity.exception.DestinationAccessException;
17+
18+
import lombok.extern.slf4j.Slf4j;
19+
import lombok.val;
20+
21+
/**
22+
* Resolves the IAS tenant host for a given tenant ID by querying the BTP tenant API.
23+
* <p>
24+
* The endpoint returns OIDC metadata including a {@code token_endpoint}. The IAS host subdomain is extracted from the
25+
* first host label of that URL, which identifies the IAS tenant.
26+
*/
27+
@Slf4j
28+
class IasTenantHostResolver
29+
{
30+
static final IasTenantHostResolver DEFAULT_INSTANCE = new IasTenantHostResolver();
31+
private static final String TENANT_INFO_ENDPOINT_TEMPLATE = "/sap/rest/tenantLoginInfo?id=%s";
32+
33+
private final CloseableHttpClient httpClient;
34+
35+
private IasTenantHostResolver()
36+
{
37+
this.httpClient = HttpClients.createDefault();
38+
}
39+
40+
/**
41+
* Queries {@code btpTenantApiUri} with {@code ?id=<tenantId>} and extracts the IAS tenant subdomain from the
42+
* {@code token_endpoint} field in the JSON response.
43+
*
44+
* @param btpTenantApiUri
45+
* The full URL of the BTP tenant login-info endpoint.
46+
* @param tenantId
47+
* The tenant ID (app_tid/subaccount ID) to look up.
48+
* @return The subdomain extracted from the {@code token_endpoint} host.
49+
* @throws DestinationAccessException
50+
* if the HTTP request fails, the response is not 200, or the subdomain cannot be parsed from the
51+
* response.
52+
*/
53+
@Nonnull
54+
String resolve( @Nonnull final URI btpTenantApiUri, @Nonnull final String tenantId )
55+
{
56+
val url = btpTenantApiUri.resolve(TENANT_INFO_ENDPOINT_TEMPLATE.formatted(tenantId));
57+
log.debug("Dynamically resolving IAS tenant host for tenant '{}' via {}.", tenantId, url);
58+
val req = new HttpGet(url);
59+
try {
60+
return httpClient.execute(req, response -> {
61+
if( response.getCode() != HttpStatus.SC_OK ) {
62+
throw new DestinationAccessException(
63+
"Failed to query BTP tenant API: Server returned status code %d for GET request to '%s'."
64+
.formatted(response.getCode(), url));
65+
}
66+
val body = EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8);
67+
return extractSubdomainFromTokenEndpoint(body);
68+
});
69+
}
70+
catch( IOException e ) {
71+
throw new DestinationAccessException("Failed to query BTP tenant API: " + e.getMessage(), e);
72+
}
73+
}
74+
75+
@Nonnull
76+
static String extractSubdomainFromTokenEndpoint( @Nonnull final String responseBody )
77+
{
78+
try {
79+
final String tokenEndpoint = new JSONObject(responseBody).getString("token_endpoint");
80+
final String host = URI.create(tokenEndpoint).getHost();
81+
return host.substring(0, host.indexOf('.'));
82+
}
83+
catch( final Exception e ) {
84+
throw new DestinationAccessException(
85+
"Failed to extract IAS tenant host from the BTP tenant API response. The response did not conform to the expected format: "
86+
+ responseBody,
87+
e);
88+
}
89+
}
90+
}

0 commit comments

Comments
 (0)