diff --git a/.github/badges/branches.svg b/.github/badges/branches.svg deleted file mode 100644 index 28be886..0000000 --- a/.github/badges/branches.svg +++ /dev/null @@ -1 +0,0 @@ -branches54.1% \ No newline at end of file diff --git a/.github/badges/coverage-summary.json b/.github/badges/coverage-summary.json deleted file mode 100644 index 92e8263..0000000 --- a/.github/badges/coverage-summary.json +++ /dev/null @@ -1 +0,0 @@ -{"branches": 54.109589041095894, "coverage": 74.64907355418305} \ No newline at end of file diff --git a/.github/badges/jacoco.svg b/.github/badges/jacoco.svg deleted file mode 100644 index 8db44d9..0000000 --- a/.github/badges/jacoco.svg +++ /dev/null @@ -1 +0,0 @@ -coverage74.6% \ No newline at end of file diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 979bab4..8cb1782 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,6 +1,6 @@ # This workflow will build a Java project with Maven, and cache/restore any dependencies to improve the workflow execution time -name: Java CI with Maven, Run Tests, Coverage Report and Badge +name: Build on: push: @@ -15,56 +15,6 @@ jobs: steps: - uses: actions/checkout@v4 - - - name: Setup Java Development Kits - uses: actions/setup-java@v4 - with: - java-version: 17 - distribution: microsoft - cache: maven - + - name: Build with Maven run: mvn -B package --file pom.xml - - - name: Generate JavaCodeCoverage badge - id: jacoco - uses: cicirello/jacoco-badge-generator@v2 - with: - badges-directory: .github/badges - generate-branches-badge: true - generate-summary: true - - - name: Log coverage percentages to workflow output - run: | - echo "coverage = ${{ steps.jacoco.outputs.coverage }}" - echo "branches = ${{ steps.jacoco.outputs.branches }}" - - - name: Upload JaCoCo coverage report as a workflow artifact - uses: actions/upload-artifact@v4 - with: - name: jacoco-report - path: target/site/jacoco/ - - - name: Commit and push the coverage badges and summary file - if: ${{ github.event_name != 'pull_request' }} - run: | - cd .github/badges - if [[ `git status --porcelain *.svg *.json` ]]; then - git config --global user.name 'github-actions' - git config --global user.email '41898282+github-actions[bot]@users.noreply.github.com' - git add *.svg *.json - git commit -m "Autogenerated JaCoCo coverage badges" *.svg *.json - git push - fi - -# - name: Comment on PR with coverage percentages -# if: ${{ github.event_name == 'pull_request' }} -# run: | -# REPORT=$(<.github/badges/coverage-summary.json) -# COVERAGE=$(jq -r '.coverage' <<< "$REPORT")% -# BRANCHES=$(jq -r '.branches' <<< "$REPORT")% -# NEWLINE=$'\n' -# BODY="## JaCoCo Test Coverage Summary Statistics${NEWLINE}* __Coverage:__ ${COVERAGE}${NEWLINE}* __Branches:__ ${BRANCHES}" -# gh pr comment ${{github.event.pull_request.number}} -b "${BODY}" -# env: -# GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file diff --git a/pom.xml b/pom.xml index 70794c5..0f04fd3 100644 --- a/pom.xml +++ b/pom.xml @@ -118,7 +118,7 @@ com.auth0 java-jwt - 4.4.0 + 4.5.1 diff --git a/src/main/java/org/privacyidea/AsyncRequestCallable.java b/src/main/java/org/privacyidea/AsyncRequestCallable.java index 21a0961..ba44831 100644 --- a/src/main/java/org/privacyidea/AsyncRequestCallable.java +++ b/src/main/java/org/privacyidea/AsyncRequestCallable.java @@ -77,8 +77,9 @@ public void onFailure(@NotNull Call call, @NotNull IOException e) @Override public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException { - // Only response.body() is available in OkHttp; ensure it is closed and consumed only once to prevent resource leaks. - // The body can only be consumed once. + // For OkHttp, the response body is always available via `body()`, regardless of HTTP status. + // We must ensure the body is closed to prevent resource leaks, and it can only be consumed once. + // Using try-with-resources guarantees the body is properly closed after reading. try (ResponseBody responseBody = response.body()) { if (responseBody != null) diff --git a/src/main/java/org/privacyidea/PIResponse.java b/src/main/java/org/privacyidea/PIResponse.java index 8b277f7..5b06b4f 100644 --- a/src/main/java/org/privacyidea/PIResponse.java +++ b/src/main/java/org/privacyidea/PIResponse.java @@ -18,6 +18,11 @@ import com.google.gson.Gson; import com.google.gson.GsonBuilder; +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonDeserializer; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import java.lang.reflect.Type; import java.util.ArrayList; import java.util.List; import java.util.function.Predicate; @@ -84,10 +89,11 @@ public boolean authenticationSuccessful() */ public boolean pushAvailable() { - return multiChallenge.stream().anyMatch(c -> isPushOrSmartphoneContainer(c.getType())); + return multiChallenge.stream().anyMatch(c -> isPushOrSmartphoneContainer(c.getType()) && "poll".equals(c.getClientMode())); } - private boolean isPushOrSmartphoneContainer(String type) { + private boolean isPushOrSmartphoneContainer(String type) + { return TOKEN_TYPE_PUSH.equals(type) || CONTAINER_TYPE_SMARTPHONE.equals(type); } @@ -113,7 +119,8 @@ public String otpTransactionId() return null; } - public String pushTransactionId() { + public String pushTransactionId() + { for (Challenge challenge : multiChallenge) { if (isPushOrSmartphoneContainer(challenge.getType())) @@ -142,14 +149,18 @@ private boolean isNotBlank(String str) { */ public String otpMessage() { - return reduceChallengeMessagesWhere(c -> !(isPushOrSmartphoneContainer(c.getType()))); + return reduceChallengeMessagesWhere(c -> "interactive".equals(c.getClientMode())); } private String reduceChallengeMessagesWhere(Predicate predicate) { StringBuilder sb = new StringBuilder(); - sb.append( - multiChallenge.stream().filter(predicate).map(Challenge::getMessage).distinct().reduce("", (a, s) -> a + s + ", ").trim()); + sb.append(this.multiChallenge.stream() + .filter(predicate) + .map(Challenge::getMessage) + .distinct() + .reduce("", (a, s) -> a + s + ", ") + .trim()); if (sb.length() > 0) { @@ -198,7 +209,19 @@ public String toJSON() public static PIResponse fromJSON(String json) { - return new Gson().fromJson(json, PIResponse.class); + JsonDeserializer challengeDeserializer = (jsonElement, type, ctx) -> + { + JsonObject obj = jsonElement.getAsJsonObject(); + String serial = obj.has("serial") && !obj.get("serial").isJsonNull() ? obj.get("serial").getAsString() : ""; + String message = obj.has("message") && !obj.get("message").isJsonNull() ? obj.get("message").getAsString() : ""; + String clientMode = obj.has("clientMode") && !obj.get("clientMode").isJsonNull() ? obj.get("clientMode").getAsString() : ""; + String image = obj.has("image") && !obj.get("image").isJsonNull() ? obj.get("image").getAsString() : ""; + String transactionID = obj.has("transactionID") && !obj.get("transactionID").isJsonNull() ? obj.get("transactionID").getAsString() : ""; + String tokenType = obj.has("type") && !obj.get("type").isJsonNull() ? obj.get("type").getAsString() : ""; + return new Challenge(serial, message, clientMode, image, transactionID, tokenType); + }; + Gson gson = new GsonBuilder().registerTypeAdapter(Challenge.class, challengeDeserializer).create(); + return gson.fromJson(json, PIResponse.class); } @Override