Skip to content

General: Restore LTI Moodle integration broken by Spring Boot 4 upgrade - #12769

Merged
krusche merged 6 commits into
developfrom
bugfix/lti/spring7-restore-moodle-integration
May 24, 2026
Merged

krusche merged 6 commits into
developfrom
bugfix/lti/spring7-restore-moodle-integration

Conversation

@krusche

@krusche krusche commented May 23, 2026 •

Copy link
Copy Markdown
Member

Summary

Spring Framework 7 removed UriComponentsBuilder.fromHttpUrl(String). The upstream uk.ac.ox.ctl:spring-security-lti13:0.3.4 still calls it during the very first step of an LTI 1.3 launch, so every Moodle (or any other LMS) login crashed with NoSuchMethodError after the Spring Boot 4 upgrade. This PR ships a one-line patched copy of the upstream resolver — placed in the same package so it preserves the existing 404/500 semantics — and closes the regression coverage gap with an end-to-end server integration test for Step 1 plus broader LTI step coverage.

Checklist

General

Server

  • Important: I implemented the changes with a very good performance and prevented too many (unnecessary) and too complex database calls.
  • I strictly followed the principle of data economy for all database calls.
  • I strictly followed the server coding and design guidelines and the REST API guidelines.
  • I added multiple integration tests (Spring) related to the features (with a high test coverage).
  • I added pre-authorization annotations according to the guidelines and checked the course groups for all new REST Calls (security).
  • I documented the Java code using JavaDoc style.

Motivation and Context

Fixes #12739 ("LTI/Moodle Integration broken").

Reported scenario: clicking the Artemis link inside a Moodle course returns "Ihre Anfrage kann nicht bearbeitet werden."; the Artemis server log shows

java.lang.NoSuchMethodError: 'org.springframework.web.util.UriComponentsBuilder
    org.springframework.web.util.UriComponentsBuilder.fromHttpUrl(java.lang.String)'
    at uk.ac.ox.ctl.lti13.security.oauth2.client.lti.web
        .OIDCInitiatingLoginRequestResolver.expandRedirectUri(...:173)

UriComponentsBuilder.fromHttpUrl(String) was deprecated in Spring 6 and removed in Spring 7. spring-security-lti13:0.3.4 was compiled against Spring 6.1.12 and still references the method; once Artemis upgraded to Spring Boot 4 / Spring Framework 7 (#12381), every Step 1 of the LTI 1.3 OIDC third-party initiated login flow crashed.

Rolling back Spring Boot 4 is not an option. An upstream PR fixing this is open (oxctl/spring-security-lti13#60) but unreleased.

Description

Library audit first. Exhaustive grep of spring-security-lti13:0.3.4 confirmed only two Spring 6→7 removed-API call sites:

  1. UriComponentsBuilder.fromHttpUrl in OIDCInitiatingLoginRequestResolver:173.
  2. AntPathRequestMatcher in PathOIDCInitiationRegistrationResolver — already shimmed in Development: Upgrade to Spring Boot 4, Spring Framework 7, and Spring AI 2.0.0-M4 #12381 by Lti13PathRegistrationResolver.

No other removed APIs (NimbusJwtDecoder, JwtDecoder, OAuth2AuthorizationRequest, AbstractHttpConfigurer, OncePerRequestFilter, HttpSecurity#with, etc. all still exist in Spring 7). RestTemplate is deprecated but functional, and Artemis does not use the upstream TokenRetriever/NamesRoleService anyway — it has its own Lti13TokenRetriever. Upstream PR #60 also fixes only these same two sites; the remaining diff entries (pom.xml version bumps, three test-side migrations) have no Artemis impact.

Fix. Mirror the existing Lti13PathRegistrationResolver workaround:

  • New Lti13InitiatingLoginRequestResolver — a copy of OIDCInitiatingLoginRequestResolver with two intentional modifications: (a) fromHttpUrl → fromUriString (matches upstream PR oxctl/spring-security-lti13#60), (b) reject blank/whitespace iss / login_hint / target_link_uri in addition to nulls (LTI 1.3 spec compliance — Artemis-specific tightening on top of upstream). Placed in the upstream package so it can still throw the package-private InvalidClientRegistrationIdException and the filter's 404 (unknown registration) / 500 (other failures) bucketing keeps working unchanged.
  • CustomLti13Configurer.configureInitiationFilter now instantiates the patched resolver instead of the upstream one. Removal path documented in the shim's Javadoc: once the upstream library releases a Spring 7-compatible version, delete the shim and revert one import line; if modification (b) is to be preserved, contribute it upstream first.

Why this wasn't caught in #12381. Three independent reasons lined up: (1) the bug is a runtime linkage error, not a compile error — the library jar is precompiled against Spring 6.1.12, and the JVM only resolves the static method at first invocation, so application startup and health endpoints all pass; (2) the pre-existing Lti13LaunchIntegrationTest covers Step 3 (auth-callback) only — its header comment explicitly says "Step 1 ... does not require additional testing here"; (3) the Spring Boot 4 upgrade did notice the same library was breaking — and shimmed AntPathRequestMatcher — but stopped at the first symptom instead of asking "is this library Spring-7-compatible at all?"

Closing the coverage gap. Added a comprehensive server integration test suite:

  • Lti13InitiationIntegrationTest (new, 6 tests) — exercises the full Spring Security filter chain end-to-end for Step 1: happy path with a persisted LtiPlatformConfiguration asserts 302 to platform's authorization_uri with all required OIDC parameters; unknown registration returns 404 (with a real platform under a different registrationId saved first, so the assertion proves active rejection rather than an empty repository); missing iss returns 400; blank iss / login_hint / target_link_uri each return 400 to lock in the spec-compliance tightening.
  • Lti13LaunchIntegrationTest extended (+6 tests) — symmetric /deep-link redirect-proxy coverage (happy + 3 negative paths) and additional /auth-login filter routing tests (POST variant for form_post, anonymous-user variant).
  • Lti13InitiatingLoginRequestResolverTest (new, 7 unit tests) — focused unit tests for the resolver, including an explicit NoSuchMethodError regression guard for re-introduction of fromHttpUrl inside the shim itself and dedicated tests for each blank-rejection case. Runs in ~1 s without Docker.

Total: +19 LTI tests. Full LTI suite is 172 tests / 171 pass / 1 unrelated pre-existing skip, 54.1 s.

Steps for Testing

Prerequisites:

  • An Artemis instance running on Spring Boot 4 (i.e. current develop).
  • A configured Moodle (or any LTI 1.3 platform) registration in Course Administration → External LMS Connection with a valid authorization_uri, client_id, token_uri, and jwk_set_uri.
  1. Without this PR (on develop head), click the Artemis tool link inside a Moodle course. The browser shows "Ihre Anfrage kann nicht bearbeitet werden." and the server log contains the NoSuchMethodError stack trace pointing at OIDCInitiatingLoginRequestResolver:173.
  2. Deploy this branch and repeat the same click. The browser is redirected through Moodle's authorization URI, completes the OIDC handshake, and lands on the corresponding Artemis course/exercise page as expected.
  3. Run the new server integration tests locally:
    ./gradlew test --tests Lti13InitiationIntegrationTest -x webapp
    ./gradlew test --tests Lti13LaunchIntegrationTest -x webapp
    ./gradlew test --tests Lti13InitiatingLoginRequestResolverTest -x webapp
    All three pass against a Postgres Testcontainers backend.

Testserver States

You can manage test servers using Helios. Check environment statuses in the environment list. To deploy to a test server, go to the CI/CD page, find your PR or branch, and trigger the deployment.

Review Progress

Performance Review

  • I (as a reviewer) confirm that the server changes (in particular related to database calls) are implemented with a very good performance even for very large courses with more than 2000 students.

Code Review

  • Code Review 1
  • Code Review 2

Manual Tests

  • LTI 1.3 launch from Moodle reaches the Artemis course/exercise page (Step 1 → Step 2 → Step 3).
  • LTI 1.3 deep-linking flow still works (instructor selects an exercise inside Moodle, content is inserted in the Moodle course).

Summary by CodeRabbit

  • New Features

    • Improved LTI 1.3 initiation and third‑party login flow with deep‑link redirect support and stricter validation of required parameters.
  • Bug Fixes

    • Restored compatibility with Spring 7 / Spring Security 7 to ensure stable OAuth/OIDC initiation flows and prevent startup/runtime issues.
  • Tests

    • Added unit and integration tests covering initiation, launch, deep‑link redirects and numerous failure cases (missing/blank/unknown parameters, cached-request scenarios).

Review Change Stack

Spring Framework 7 removed UriComponentsBuilder.fromHttpUrl(String); the
upstream uk.ac.ox.ctl:spring-security-lti13:0.3.4 still calls it inside
OIDCInitiatingLoginRequestResolver.expandRedirectUri, so every Step 1
(third-party initiated login) request crashed with NoSuchMethodError
once Artemis moved to Spring Boot 4 (#12381). Reported in #12739.

Add Lti13InitiatingLoginRequestResolver as a one-line patched copy of the
upstream resolver, placed in the same package so it can still throw the
package-private InvalidClientRegistrationIdException and preserve the
filter's 404/500 bucketing. Wire it into CustomLti13Configurer in place
of the upstream class. The class header documents removal once an
upstream Spring 7-compatible release ships (oxctl PR #60).

Close the regression coverage gap: add Lti13InitiationIntegrationTest
that exercises the full Spring Security filter chain end-to-end through
the new resolver, extend Lti13LaunchIntegrationTest with symmetric
deep-link redirect proxy tests and additional auth-login routing tests,
and add a focused unit test for the resolver that catches the same bug
without Docker.

Closes #12739.
Copilot AI review requested due to automatic review settings May 23, 2026 14:00
@krusche
krusche requested a review from a team as a code owner May 23, 2026 14:00
@krusche krusche added this to the 9.3 milestone May 23, 2026
@krusche krusche self-assigned this May 23, 2026
@github-project-automation github-project-automation Bot moved this to Work In Progress in Artemis Development May 23, 2026
@github-actions github-actions Bot added tests server Pull requests that update Java code. (Added Automatically!) lti Pull requests that affect the corresponding module labels May 23, 2026
@krusche krusche changed the title LTI: Restore Moodle integration broken by Spring Boot 4 upgrade General: Restore LTI Moodle integration broken by Spring Boot 4 upgrade May 23, 2026
@krusche

krusche commented May 23, 2026

Copy link
Copy Markdown
Member Author

@Claudia-Anthropica review

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Fixes a Spring Boot 4 / Spring Framework 7 runtime regression in the LTI 1.3 OIDC initiation flow (Step 1) by replacing the upstream resolver that calls the removed UriComponentsBuilder.fromHttpUrl(String), and adds end-to-end + unit test coverage to prevent future regressions in Moodle/LMS launches.

Changes:

  • Introduce a Spring 7-compatible drop-in Lti13InitiatingLoginRequestResolver shim (upstream-package copy with fromUriString).
  • Wire the shim into CustomLti13Configurer so Step 1 no longer crashes with NoSuchMethodError.
  • Add Step 1 integration tests and expand Step 3 redirect-proxy / filter-wiring coverage.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
src/main/java/de/tum/cit/aet/artemis/lti/config/CustomLti13Configurer.java Switches initiation filter wiring to use the patched resolver and documents the Spring 7 shims.
src/main/java/uk/ac/ox/ctl/lti13/security/oauth2/client/lti/web/Lti13InitiatingLoginRequestResolver.java Adds the patched resolver implementation (copied from upstream with a one-line Spring 7 API replacement).
src/test/java/de/tum/cit/aet/artemis/lti/Lti13InitiationIntegrationTest.java New server integration tests for LTI Step 1 initiation redirect and key error cases (404/400).
src/test/java/de/tum/cit/aet/artemis/lti/Lti13LaunchIntegrationTest.java Extends coverage for /deep-link redirect proxy and additional /auth-login filter wiring scenarios.
src/test/java/de/tum/cit/aet/artemis/lti/config/Lti13InitiatingLoginRequestResolverTest.java New unit tests validating the shim resolver behavior and guarding against reintroducing the removed Spring API call.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/main/java/de/tum/cit/aet/artemis/lti/config/CustomLti13Configurer.java Outdated
@Claudia-Anthropica

Copy link
Copy Markdown
Contributor

@krusche got it — starting the review right away.

@coderabbitai

coderabbitai Bot commented May 23, 2026 •

Copy link
Copy Markdown
Contributor

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 3fa21907-0348-49bf-a56e-a8976ab3298d

📥 Commits

Reviewing files that changed from the base of the PR and between f81a9e0 and 0b6ca77.

📒 Files selected for processing (2)
  • src/main/java/uk/ac/ox/ctl/lti13/security/oauth2/client/lti/web/Lti13InitiatingLoginRequestResolver.java
  • src/test/java/de/tum/cit/aet/artemis/lti/Lti13InitiationIntegrationTest.java

Walkthrough

Adds Lti13InitiatingLoginRequestResolver (Spring 7-compatible), wires it into CustomLti13Configurer, and adds unit and integration tests for initiation, deep-link, and auth-login flows; the resolver validates initiation parameters, expands redirect URIs, and builds OAuth2AuthorizationRequest objects.

Changes

LTI 1.3 Initiation Resolver Implementation

Layer / File(s) Summary
Lti13InitiatingLoginRequestResolver implementation
src/main/java/uk/ac/ox/ctl/lti13/security/oauth2/client/lti/web/Lti13InitiatingLoginRequestResolver.java
New OAuth2AuthorizationRequestResolver that resolves registrationId via OIDCInitiationRegistrationResolver, fetches ClientRegistration, enforces IMPLICIT grant type, validates iss, login_hint, and target_link_uri (and optional client_id), expands redirect URIs with UriComponentsBuilder.fromUriString(...), and builds OAuth2AuthorizationRequest with initiation-specific parameters and request attributes.
Resolver unit test coverage
src/test/java/de/tum/cit/aet/artemis/lti/config/Lti13InitiatingLoginRequestResolverTest.java
Unit tests verify resolve() builds a valid OAuth2AuthorizationRequest, returns null for non-matching paths, throws InvalidInitiationRequestException on missing/blank params, and guards against NoSuchMethodError from removed Spring API; includes Mockito setup and helpers.
Spring Security configuration and initiation integration tests
src/main/java/de/tum/cit/aet/artemis/lti/config/CustomLti13Configurer.java, src/test/java/de/tum/cit/aet/artemis/lti/Lti13InitiationIntegrationTest.java
CustomLti13Configurer now imports/instantiates Lti13InitiatingLoginRequestResolver (Javadoc updated). Integration tests cover Step 1 initiation redirect and OIDC parameters, negative initiation cases (unknown registration -> 404, missing iss -> 400, blank iss -> 400), and include a helper to persist LtiPlatformConfiguration.
Deep-link and auth-login integration tests
src/test/java/de/tum/cit/aet/artemis/lti/Lti13LaunchIntegrationTest.java
Adds deep-link redirect endpoint tests (/api/lti/public/lti13/deep-link) and Step 3b auth-login failure-path tests for POST/anonymous no-cached-request scenarios; repositions existing noRequestCached test.

Sequence Diagram(s)

sequenceDiagram
  participant Browser
  participant Resolver as Lti13InitiatingLoginRequestResolver
  participant RegResolver as OIDCInitiationRegistrationResolver
  participant ClientRepo as ClientRegistrationRepository
  participant Platform as PlatformAuthorizationEndpoint

  Browser->>Resolver: GET /initiate-login?iss=...&login_hint=...&target_link_uri=...
  Resolver->>RegResolver: resolveClientRegistrationId(request)
  RegResolver-->>Resolver: registrationId
  Resolver->>ClientRepo: findByRegistrationId(registrationId)
  ClientRepo-->>Resolver: ClientRegistration
  Resolver->>Resolver: validate params, expand redirect_uri, generate state/nonce
  Resolver->>Platform: redirect to authorizationUri with OAuth2 params (response_type=id_token, response_mode=form_post, login_hint, prompt=none, state, nonce, registration_id)
  Platform-->>Browser: 302 Location -> authorizationUri?...
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related issues

  • LTI/Moodle Integration broken #12739: Replaces OIDCInitiatingLoginRequestResolver and switches fromHttpUrl(...) → fromUriString(...), addressing the NoSuchMethodError reported in that issue.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 16.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title directly addresses the main change: restoring LTI Moodle integration that was broken by a Spring Boot 4 (Spring 7) upgrade.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch bugfix/lti/spring7-restore-moodle-integration

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
src/test/java/de/tum/cit/aet/artemis/lti/Lti13InitiationIntegrationTest.java (1)

46-47: ⚡ Quick win

Use fixed registration IDs in tests instead of random UUIDs.

These tests can stay isolated with deterministic IDs, which improves reproducibility and aligns with the test-data guideline.

🔧 Suggested fix
-    private static final String AUTH_URI = "https://platform.example.com/mod/lti/auth.php";
+    private static final String AUTH_URI = "https://platform.example.com/mod/lti/auth.php";
+    private static final String REGISTRATION_ID_REDIRECT = "test-platform-redirect";
+    private static final String REGISTRATION_ID_MISSING_ISS = "test-platform-missing-iss";
...
-        String registrationId = "test-platform-" + UUID.randomUUID();
+        String registrationId = REGISTRATION_ID_REDIRECT;
...
-        String registrationId = "test-platform-" + UUID.randomUUID();
+        String registrationId = REGISTRATION_ID_MISSING_ISS;

As per coding guidelines: "src/test/java/**/*.java: ... fixed_data: true".

Also applies to: 77-78

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/test/java/de/tum/cit/aet/artemis/lti/Lti13InitiationIntegrationTest.java`
around lines 46 - 47, Replace non-deterministic registration IDs that use
UUID.randomUUID() with fixed, deterministic strings to comply with test-data
guidelines; change the variable registrationId assignment (and the other
occurrence using UUID.randomUUID()) to use a stable literal such as
"test-platform-1" (and a distinct fixed value for the second case, e.g.,
"test-platform-2") and keep the subsequent savePlatform(registrationId) calls
unchanged so tests remain isolated but reproducible.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@src/main/java/uk/ac/ox/ctl/lti13/security/oauth2/client/lti/web/Lti13InitiatingLoginRequestResolver.java`:
- Around line 87-100: The current Lti13InitiatingLoginRequestResolver code only
checks for null parameters (iss, login_hint, target_link_uri) and therefore
allows empty or whitespace-only values; update the validation in the method that
reads request.getParameter(...) so that after fetching each parameter (iss,
login_hint, target_link_uri) you normalize by trimming and reject when the
trimmed value is empty (e.g., treat null or trimmed.isEmpty() as invalid) by
throwing the same InvalidInitiationRequestException with the existing messages.

---

Nitpick comments:
In
`@src/test/java/de/tum/cit/aet/artemis/lti/Lti13InitiationIntegrationTest.java`:
- Around line 46-47: Replace non-deterministic registration IDs that use
UUID.randomUUID() with fixed, deterministic strings to comply with test-data
guidelines; change the variable registrationId assignment (and the other
occurrence using UUID.randomUUID()) to use a stable literal such as
"test-platform-1" (and a distinct fixed value for the second case, e.g.,
"test-platform-2") and keep the subsequent savePlatform(registrationId) calls
unchanged so tests remain isolated but reproducible.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 59323aa2-0dac-4032-aa55-c259261b5f88

📥 Commits

Reviewing files that changed from the base of the PR and between f0e7a6d and 0f06d65.

📒 Files selected for processing (5)
  • src/main/java/de/tum/cit/aet/artemis/lti/config/CustomLti13Configurer.java
  • src/main/java/uk/ac/ox/ctl/lti13/security/oauth2/client/lti/web/Lti13InitiatingLoginRequestResolver.java
  • src/test/java/de/tum/cit/aet/artemis/lti/Lti13InitiationIntegrationTest.java
  • src/test/java/de/tum/cit/aet/artemis/lti/Lti13LaunchIntegrationTest.java
  • src/test/java/de/tum/cit/aet/artemis/lti/config/Lti13InitiatingLoginRequestResolverTest.java

@github-project-automation github-project-automation Bot moved this from Work In Progress to Ready For Review in Artemis Development May 23, 2026
- Add explicit Apache 2.0 attribution header to Lti13InitiatingLoginRequestResolver
  matching the JHipster pattern used in CachingHttpHeadersFilter.
- Qualify ambiguous PR #60 reference as oxctl/spring-security-lti13#60 with full
  upstream URL in CustomLti13Configurer Javadoc.
@coderabbitai

coderabbitai Bot commented May 23, 2026

Copy link
Copy Markdown
Contributor

Caution

Failed to replace (edit) comment. This is likely due to insufficient permissions or the comment being deleted.

Error details
{"name":"HttpError","status":401,"request":{"method":"PATCH","url":"https://api.github.com/repos/ls1intum/Artemis/issues/comments/4525587351","headers":{"accept":"application/vnd.github.v3+json","user-agent":"octokit.js/0.0.0-development octokit-core.js/7.0.6 Node.js/24","authorization":"token [REDACTED]","content-type":"application/json; charset=utf-8"},"body":{"body":"<!-- This is an auto-generated comment: summarize by coderabbit.ai -->\nNo actionable comments were generated in the recent review. 🎉\n\n<details>\n<summary>ℹ️ Recent review info</summary>\n\n<details>\n<summary>⚙️ Run configuration</summary>\n\n**Configuration used**: Path: .coderabbit.yaml\n\n**Review profile**: CHILL\n\n**Plan**: Pro\n\n**Run ID**: `cf086bbd-1dbc-42b3-af0c-9c441114874d`\n\n</details>\n\n<details>\n<summary>📥 Commits</summary>\n\nReviewing files that changed from the base of the PR and between 0f06d65d5adebe4ebf6da38cf4d8dca309028149 and 90c0f7a379245a2914abb26a9e32e2eb2724ff2a.\n\n</details>\n\n<details>\n<summary>📒 Files selected for processing (2)</summary>\n\n* `src/main/java/de/tum/cit/aet/artemis/lti/config/CustomLti13Configurer.java`\n* `src/main/java/uk/ac/ox/ctl/lti13/security/oauth2/client/lti/web/Lti13InitiatingLoginRequestResolver.java`\n\n</details>\n\n</details>\n\n---\n<!-- walkthrough_start -->\n\n## Walkthrough\n\nReplaces the upstream OIDCInitiatingLoginRequestResolver with a new Lti13InitiatingLoginRequestResolver, wires it into CustomLti13Configurer, and adds unit and integration tests covering initiation, deep-link, and auth-login flows and relevant failure cases.\n\n## Changes\n\n**LTI 1.3 Initiation Resolver Implementation**\n\n|Layer / File(s)|Summary|\n|---|---|\n|**Lti13InitiatingLoginRequestResolver implementation** <br> `src/main/java/uk/ac/ox/ctl/lti13/security/oauth2/client/lti/web/Lti13InitiatingLoginRequestResolver.java`|New `OAuth2AuthorizationRequestResolver` that resolves `registrationId` via injected `OIDCInitiationRegistrationResolver`, fetches `ClientRegistration`, validates required parameters (`iss`, `login_hint`, `target_link_uri`), optionally validates `client_id`, and builds `OAuth2AuthorizationRequest` with OIDC parameters (`response_type=id_token`, `response_mode=form_post`, `nonce`, `state`, `prompt=none`). Uses `UriComponentsBuilder.fromUriString(...)` to expand redirect URIs.|\n|**Resolver unit test coverage** <br> `src/test/java/de/tum/cit/aet/artemis/lti/config/Lti13InitiatingLoginRequestResolverTest.java`|Unit tests verify `resolve()` builds a valid authorization request for valid initiation, returns `null` for non-matching paths, throws `InvalidInitiationRequestException` when `iss` is missing, and does not surface `NoSuchMethodError`. Includes Mockito setup and helpers.|\n|**Spring Security configuration and integration tests** <br> `src/main/java/de/tum/cit/aet/artemis/lti/config/CustomLti13Configurer.java`, `src/test/java/de/tum/cit/aet/artemis/lti/Lti13InitiationIntegrationTest.java`, `src/test/java/de/tum/cit/aet/artemis/lti/Lti13LaunchIntegrationTest.java`|`CustomLti13Configurer` now imports and instantiates `Lti13InitiatingLoginRequestResolver` (Javadoc expanded for Spring 7/Spring Security 7 incompatibilities). Integration tests cover Step 1 initiation redirects (302 + OIDC params), negative initiation cases (unknown registration -> 404, missing `iss` -> 400), deep-link redirect tests, and auth-login failure paths. `savePlatform` helper persists `LtiPlatformConfiguration` for tests.|\n\n## Estimated code review effort\n\n🎯 3 (Moderate) | ⏱️ ~20 minutes\n\n## Possibly related issues\n\n- ls1intum/Artemis#12739: Replaces the upstream resolver and adjusts redirect-uri expansion to avoid the NoSuchMethodError introduced by Spring Framework 7, matching the issue description.\n\n<!-- walkthrough_end -->\n<!-- pre_merge_checks_walkthrough_start -->\n\n<details>\n<summary>🚥 Pre-merge checks | ✅ 5</summary>\n\n<details>\n<summary>✅ Passed checks (5 passed)</summary>\n\n|         Check name         | Status   | Explanation                                                                                                                                                                                                                 |\n| :------------------------: | :------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n|      Description Check     | ✅ Passed | Check skipped - CodeRabbit’s high-level summary is enabled.                                                                                                                                                                 |\n|         Title check        | ✅ Passed | The title accurately describes the main change: restoring LTI Moodle integration by addressing Spring Boot 4 upgrade compatibility issues. It is concise and specific, clearly communicating the primary purpose of the PR. |\n|     Docstring Coverage     | ✅ Passed | No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.                                                                                                                  |\n|     Linked Issues check    | ✅ Passed | Check skipped because no linked issues were found for this pull request.                                                                                                                                                    |\n| Out of Scope Changes check | ✅ Passed | Check skipped because no linked issues were found for this pull request.                                                                                                                                                    |\n\n</details>\n\n<sub>✏️ Tip: You can configure your own custom pre-merge checks in the settings.</sub>\n\n</details>\n\n<!-- pre_merge_checks_walkthrough_end -->\n<!-- finishing_touch_checkbox_start -->\n\n<details>\n<summary>✨ Finishing Touches</summary>\n\n<details>\n<summary>📝 Generate docstrings</summary>\n\n- [ ] <!-- {\"checkboxId\": \"7962f53c-55bc-4827-bfbf-6a18da830691\"} --> Create stacked PR\n- [ ] <!-- {\"checkboxId\": \"3e1879ae-f29b-4d0d-8e06-d12b7ba33d98\"} --> Commit on current branch\n\n</details>\n<details>\n<summary>🧪 Generate unit tests (beta)</summary>\n\n- [ ] <!-- {\"checkboxId\": \"f47ac10b-58cc-4372-a567-0e02b2c3d479\", \"radioGroupId\": \"utg-output-choice-group-unknown_comment_id\"} -->   Create PR with unit tests\n- [ ] <!-- {\"checkboxId\": \"6ba7b810-9dad-11d1-80b4-00c04fd430c8\", \"radioGroupId\": \"utg-output-choice-group-unknown_comment_id\"} -->   Commit unit tests in branch `bugfix/lti/spring7-restore-moodle-integration`\n\n</details>\n\n</details>\n\n<!-- finishing_touch_checkbox_end -->\n<!-- tips_start -->\n\n---\n\nThanks for using [CodeRabbit](https://coderabbit.ai?utm_source=oss&utm_medium=github&utm_campaign=ls1intum/Artemis&utm_content=12769)! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.\n\n<details>\n<summary>❤️ Share</summary>\n\n- [X](https://twitter.com/intent/tweet?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A&url=https%3A//coderabbit.ai)\n- [Mastodon](https://mastodon.social/share?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A%20https%3A%2F%2Fcoderabbit.ai)\n- [Reddit](https://www.reddit.com/submit?title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&text=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code.%20Check%20it%20out%3A%20https%3A//coderabbit.ai)\n- [LinkedIn](https://www.linkedin.com/sharing/share-offsite/?url=https%3A%2F%2Fcoderabbit.ai&mini=true&title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&summary=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code)\n\n</details>\n\n\n<sub>Comment `@coderabbitai help` to get the list of available commands and usage tips.</sub>\n\n<!-- tips_end -->\n<!-- internal state start -->\n\n\n<!-- DwQgtGAEAqAWCWBnSTIEMB26CuAXA9mAOYCmGJATmriQCaQDG+Ats2bgFyQAOFk+AIwBWJBrngA3EsgEBPRvlqU0AgfFwA6NPEgQAfACgjoCEYDEZyAAUASpETZWaCrKPR1AGxJcA4mWUeXDbSBBQkkAAy0ACSkACy+IpeKBg0RFTi+FgCFPgA1mSQcpAAyrzwGESQAEKJuJAALJDY3OloSpAAFLaQZgCMAEwA7ABsAJwAlJCQBtVUGAywXALYRABm8AAeAPQe4tuI5ZVDYGGIoSRgzIm0XmAVaRnwWZCASYSQSlIe+NzTBiW4ajYRBcH6FQAoBJAAIJ4WD4ChcPIUYGLcKQgCqNgiXFguFw3BB222RHUsGwAg0TGYu0QfQejm2UIoNGYSG23GwHg820GozGbgQyB6G020nQkGRqXgbEgADl8CVsIs4iRcHDaABRCi5PgMNDAuhFeRlCgVKoAMSobAA7vC8pAhhKSNcJGbIOjTQBhFjcLLsRDVbDwDxKCgaNa5ZgACTx3A9Hk6ANNlQmGhgsHCLXOYTQzEgHngOWc8mweS0DA0+E2lNwgUOyaIYEQomwptwsjAe3gfQAzBwAAwaHsaJp6rnINXUSBsNWKFKQADy0QAIp7ohh1PBqGaIvgSRhggBHbAhYKIfAeKQUAA0jH1iDdUVifSHpRovz6XTV8AotDA3GcdsUk3ahDW+fcpgISA1m0Dx0DWGg+DVcITTdWp8HqJptlKI4qkdFo2iUDQjE9WBMFIEEDCgKFaFoZAInEXt1xA8RKl3fcjxPc4zwvK8uDQSArwEbc8yYbh5HwNZIGQ5oCVwHM8zOXjKEga1SXFB9KmSRZyPCCMWGgyMY3xeM3kMlgPXgJMzTTOBwgYDw0EQZBUG4RyGENCppIzWTsxIXMeDQBg8jQUhpPwHgzkoKRAuC0LLnKCRQMgEhNg87hMmyEgyNdeF0AwehUqQViqgafsGm2ABWft+3sZ1MHEBhEGIqBPWBAhmAY7se29DANiIVtKEpLJ+sG5jxG3LJzWDRDIAwfBrRSc4Gq3GgJx8gDcFReglMvFSKnOfz6Ek7zMzkhT+HIFrIAAKTQJLaHwBgPiexx/SdF00DgzbYEujzTt8+T/LzAsixcJ0vCcsUBNQypAEQCE4qU2wtkivB8smIgwIkLKhwf1Wh1C4K94A2Q0sg8eRcFtD78CkehYcbEZXkdKErGiFysHrM0mxbNsOy7XsuH06NY1MzpdrpyAMzCKZMHoKFUisahYE4kI4moVE+E6L6c1oeREAQVhDX8igC0oVNF2QvhfRoKUvopxgyMqQ1WfZ1TKHsjNgsNOXoPwbACrmjDwvgtZRHqJkWSQTHoBCM0OCo6FaMNXtIhiaSQhBSJGJ7cbVueDB10eSaMFj84uh7DPzkQCZby63sIn1BZYCLkg2kysv6k6ABqEYq9wGu65zvPtzYvcKlV7jpGUihO66JoA/Ufua+u4JfWZQ0n3sIMaC4PohgGZeun3z8AOc29P0QPJ4G4bg6FryAAD9KoaDRL+u9WMGwL6nV4RQlUyvleghFkhrXqFSZgctkB/1dEoWgABuI+kp0BEG0BgcuVh8DnHSGKTuTBUhoMoM1IwwQw5hAWNIIWWwxT9GGD2MYaZ0TnWBvwNKtYDi4R5gwVs6h+Y52sHYMwIxar4MBAdAGiBcye10kUPAKBkABzCJDZstBMYWEgN6VgS82DOXisgBwTgXBGHXA5bAcCFBaPqO0WgZxmzIG9NwYMwcwiuhIItCB/oE7UVosgVKbl4AMCXlCACqJIADA0LVag8lCx4ALlLI6KkoL11zhuCaJV2ITxIMeU8089p8EgVtBAlRIr4BEGIQKeJKAYGugARW/gWUm60zp+QCj0MIZCyD/ScqwsQ3IuaVC4Tw9snYc5CMiYHCopilBNMBhdTEER5xtXOCwZJvVRphD4HdB6T1Mb6GMOAKAZBjpSX1AQYg/gMiGg8akLgvB+DCHDpIMUxQmChhUGoTQ2hdBgEMCYKAcBUCoEwDgM5pByCXPoNczgEo0CLQMZA8GLzFDKFUOoLQOg9n7NMAYRAFAGDbEgRUbYQh7poG2EobYuAGSBNwNsNAqo6Ub1ZIgXY+x8H9W2EsjqqyRrwAGhsjQJKkoJwAERioMOoqE0RzngtAvQeFxZ+BSR0s7RARgAAGXKVk5zWXywaFB1WyVoKBGZW8XyV3Al5CoLE4kSxUmpBsXAl5hDckFMUMlQa41kIATAJkDqqXKuEe6Tx4HiyVxXAPE8mGrUmqSA6rklBp3CGyeEbclXnVbea0CBtJZFYkGYpUEVjBnoDJMIJJswlydOePJgktxxuSUrNUwRy3yRLpG9NtkfLqvZXqsIiapozUoJ0DQI6JiGq2e0J68iUqbAAgVQ0UFHrcLYKkHCDYHTbAZqUXmvCHQpCRtuNQBZxDQ0DjJQ4ogSYBNmSwnI/lr7FIENle6zwtaTI8GYt0Ml1XykVMqVU6otQ6kNXqZsstnJPVWoaGNv0ZIXh2mmoafwjASssFCPYyhMoTgijJJQDlnAl2QCdPx8IaDHWtuSAsz12CbmkEYKA8pyBGGxuQZAKrSC0C4N3PolVthgBGEYDU5xpRyoUB0Fx8A3EpTWGsUjXAVQE0cAYMVIr6PYtxfiwlGBiWku2KWOl+KqzbF6ay7qBwd3tm2PgU5sABjGbNqkUz2xrQkAENsBNqT85jw4mGnJ1aryCtJaK8VkrpVgsw4aBV4MTrsbowYGidF63D086PIgGTQ3ZKnv5ygGb8pxoXDCNUAxCtwlNAALzbb5rLM9DWyb4Ga181q0lxLWN8a0zVIDRHqHa5AwKmv5xeOqstxUnhZGiLQQ1roBL+pXGuFLmVm0jcq9lg1t5vjtBmQUxYbpNUOdTS20bGBauRnFLtyTqRFsVoWyQX0D5QiyFy77NUuR2txvXElAstBPR7cu62zK42NRpRu5laNGYsDzSdAdytqBZMB1UZ1+oH34DGrWt5KcMke38p9rCeE8AKuALaKu9sd9p3quiHEKwERohrmgIazoGFpZqWbN5F7frohcjbl9JkA0V24EB+lEHD8wjHh/GKdVSBEC5fVZajAAB9IpuBHuB3VYCCgpBcCy4LBgPIsueGGuF+GwKVpVREK6AzygTPwjPYWmzjASPaD9oy+G/nwOC7qofr7H4mV7byHtyauNDlzsa+R4a0KaDy4Y95VjyFe3IdLcymmLrp1shBhDL1rA/qSvFZx+VyrmXFeqXUpHvqvarl7bpTnvHlbMTRFvEjPAhowgEzCOUmvdeo+DXlWJaQt5wsQrjctGgSv6DWM3FkH+/XK0AWN4hZAnQhtt3jwXWXIfbwL8OFkZssvickAALzI+3/kMgUuZfy4eFLs4vp0EkFl9cJQu+6vMFl7dxXa/5oUKl3/ZgGVd/v5IO728L3AuH3dAWiHbLsW/aQSRUgM/VIUHQoXgaQdgW8X2ZsAeJ0fPdAPEU0FYVHd9T9YpMIa4GgTrKwTtcIdVPxOWYIZvcOSyQ1DMDwO+XUTfeSABXrONYSZseMY7AyGSSZFgN0A3EIIBGdOdRLYvdZRvOgUXVvGwWIYEHbSyTRK/f0QMYtIaYWSyaySoYdUdOnfAgmYpJKU0FQZIBwAQYTKlQBOrONYbK7AucbKXLgkgHglAwOIA8fLkeQdVIKQXTGcwNDDDQ7bDAGPDRyEIpVMQ0jMmCjAQKjFKKUE9NVROBLQ0cgRaDkeI69fDZyLgeNZLG1bzTJfPdtHLecdVDTAlNBHTJKPTPIAzKzHYEzAWHsczbhPmKzGzOzQPdgJzFzNzDzIotLZNKrVNFbQLJKdVROaIb/LwXnOgclUQCIw0TPWEbPWcXPa7UoxDA1BOaYKANYorErXHfHAuFNKtZSToYybgEoaKLwVNTA4Q84MdAwA4/LLPE4rY84sYy4vaa42MO4igCQB4i4543AW8HQqoewv7Rw2gV47xcxLIhI/BbMABeEfIoY5rYop3PzGeTob7IPX7Q7NeLBdQeEeQXoi7RfBwrIUku7Ck28ANObG1Okmk2EtklbOPWk0NFbV4lDVDeITAEmEQ6aZIBWL6WQMrSgZjCoMUWLTjSAbjSqXjMAfsQTYTApK5ZFJ0VxRaEgGTOTeIGQpTFTNTMAIwKosBWoslClKlakGlOlBlQCZ0NkLsdzQo5rMbVINuQ7TuSYtAYLVTULGVCLeVRwBFCSZVJ2CiIwNI+gAo7qR3VuduAuTuQ1KCGBZHFCd8SAT8B4X0ytJgK8eKP2erdOc1byH8P8afICSfMjfMENRPeoB4XIWgJUaGfuNHcBe2GZesuJI5X0B4QvWNcXZyE/ENWA1/UQlXZwdXTXCoHXPXFA5ySgdA6bHsfsQ+c0f2AqfXaQK/ZsW8YmRpAGeNJ6EuBghJPgZaZkZAGDAGTHTvHgRyXAR/EFUrKvQBNvI3OxAGJvWQ+oeZSAbJcGbMM0dwkfVcu8jAoMJvRcWbI3KRWfLoQwnbKk4PCbNfU/BXC/A8zfG/HfKXRAbvPCjfa/W/ZFT/SMDKd3F84Ebss8gClvDXPXaSOc1UGZSOV03rWEMAMcDwYSYKRI2gIc1IZs9ADwc8UAxLcgVBcQGKMBZAWwgOPIeaa0DPGEw7JwroPxcOP1MqJoeUeoHcuHOi32Ww5lTSKoMcyXXS2dfSuNMqWqaodoSAFNd3cgrs3IjmKZTsyo+6EgKwV8x/K8pgxJMieoZgh8auPLRM4K6gR/XVflS8roLS6HWgOvWPZHFAyvM4rISlI/bTG6AAdQAGkSh3R5Ca5mgVEjQAZ+yXhBz8AHgAjBT0NEJCMQ5cNliCMsMoiSMN5yMeBKNr0aNkj6M5QpMCBuBOwSAvhvLHJnIZK6B8ifLs4kz5tHCfTUysh0yKirSQgbSljKVqV1AnTaUXTmUnMsSBtC4dq/SQgAzpiGNpqRCZx1QVrFT1UJAWr6B6ySB0saDALEBoB8AEq3z4RmAvivyC5LJOgx0ugbp0RUk40AABdMqYLyRMpiLa704uDuEIF6qaxaMBacADOcaxVauNX65HYCNJQGkNEq0kFGtShaUNKHa7KlCgdBYy0ygqBGunZG1G9VDGomrGjPW6kuFMx684Ym2UN68uD6ym5Ob62m/6+bRm/cZmtUOICXM0dmRAYIbm9BVy2gFNQWpGlGpeUWzGioqW/7B6kudMxOBWxaRg5g8m2cEfVW/IxKZKdW+wQKiGx/RMaJQg9k7S+Ew1bGh27agmtMom2U1jR2XSRU7uMYCqNUjU8QLUyFHUiTKTA0uraFBTeAU08VKidTPFSlI6oVW0kgU6h086+lS6plN0/YZJRuAORYGW52p6+uoM9qsLC5UTKLKM1O1VAUVAMm9a9S/dD9aZGSsfDAH+Ys5QMKWwhrSuAEG7SASuJQG7TsRcp0Wg8pJ9HKV9eq0fYpdVOlRxJzZEgJUzXsJYo+rXPIDMzOacH8HUL9HyIqYTW+ivNUfi+2ISz+u8P8zoe3U+wC28Kyt0QfEgBB/WgtIq28CoWBggAoDAC2JPL6aS6xZAXe34HsAQBQEszevKb9e++AR+0a/FVokB2ATsENWrNreq1s/+QJAtamDI6CWCQaf8ZWZeUEcgMs6wBcEoaAMAFw+gd8iwxBl4GDf2KxO8baWCkQz3CR2wzALIWQa4Big0CgRATNbNK3ABzYYqN0QAHAJ5oU1PQgoMxaBABcAkgB8A1GgEEeDEGi7Ncncmg1JC8hkgEdIf3ooebDEDiQfKoPnUKlnUclXvuwsV52IRQ0lWCK6qgh6vw0iOI1nRiOGqfuoySMkxSKgBVG9q+vyMDsPu4BYzyGBpYqsFyE2FkARpZxt0gBdwyjd3tpzm7ubj7sJrlq6BCRCjCnVSIntMpDRVbq0Hbuai7Hd0Tiqc+qprVr+o+BIBu0aeafDlaarFkF/UBBoE6et1e16ZBwGe6iGd7qdtGcVzWYpp9rgVqe2fqf2ZkJabaZOfwDBtwYudgFZx6aBz6ayBjslsGabgeYTr2qTsqdeZqZps+d2YacXIObECOfafey+mR0BbIGBdBeuf6djphZ7pbkecTrlpeeqc2Y+bpr+oYHNDa3NFgkQGfywUePDVlwcbGKce2mJe6dJchduYblhapfhdLkReNPpd9tRaZeRxZbZY5dl30YwEMf9kQHRGbAoD5fwEcecboGFaufBZufJbuclZGZpeeaxjlLYxjOpu7iGCGD43VIMCE1ztE1eXCELv1MNOZHkxNOYGU0rogAtJxRrutPrpOvtOMxbudKWac0xw9M2uGKBrGLKNngHqCzDeDLQxHtlQbPHqiNixSPjKS3TexJGJ8x2JW3TJQJGrCGaFRrJocHUD0jyhxpSQzdGPrZqy8oEaUoUBLyx3FGuG9gTMJPYGJMq1u3JJcFD0DhPMpkirsMQ0Fq8RqFT0SwEnfzAC/i5A+PWJhvytxLGdiYcrEGx02NhpeB/Iws62XFvGYvDiqtr3sG7zMdEIKHkFH292+gIxnFNz0cEnxY1tZKwHBOumXHwDFHmnqEubjV/SVFgHWcUCA3hDpzLVsTiXXqoGob4CINpkNC3RZjZlqvilTETmNtbHQTjSPY8HgKwFLV+J+hegQ+Di21gx8ifPgsauyChg/eujgFBbJztwg8dxTVFaO1UjBzPIl0NWn2Qv2mQEQcqBjh8g2GSEIYimIfiXCvyVedCNRLYPPqhm2EE+nCentCjGgGgCsC0diqewiiLRDA0lzDcgoJnepM5v6YNHoGKDY4mIFPaqyf6pyZ8nCL6oLiIykkGobLyhKcSPEAmtSNVsWqcizh7cTRxJTWzf2uxsOvOGOrtLOsuqTajhZXdNTbjvy6zd2P9PruJvjKWLycNGVsS0tdxr7brfDUK6Jv2N0EVflVVCYUFreJG5+u2ZoGcDg40sm/eJm7prtXULTzPbz3DR3IoAADUpO8aMAlvpvA67U4PpBjKxOFpUP/1vasOKBLQWBggXQ6AGZgl4BjvDjTvEM6OebEBZROQPASqwdG1YBzv/uMJ1ZClHdXLmxPvRu/ipArv2tgeyBDalYZ9KA9bnIzR4f1UEhgobigSQTVQLjBOLaoTuSOTC5o6pvDiCe8gif7jSffiXDpOxiw7110r/taflvfP9sl8XgMK53MpJvETtT0FARUgOYq3eua3M2B28l0zhvDibANQfBogZGbAoQYgFxZRZcVwpdPQqcNRZRoADflwpcoR0RoAoxZca8rebBoANQ4hNfZc1flxog1fPRzeHe19ohZRogYgdfog9fZdqgoQSgNR7f5Dpj7WU6FSuNewAAOd1nOkTBs313UyTANku4NxTUNs0quwwAwP5ESqI05QgPvH1lgXnLgKgOFCMxVJFN5VFT5DFH5Uvg5dMQFdPEFKv0ezP2v9gLgJQMRLwfOjoeI2z7eQxeQeYCieq31qgNv9Fb5EvgAbQAG8RUF+SBxsRUOBd/dJZcGBxghhKo1gGAGAX4hg0ARVrwRUfpD+RUSvaVY3yvm7Ku27qubrPS7qbWCLc4AGQf6v9VcuAFjCQEP59BH+RySAYfx7AjBH+49F/kXDbIdkOCAjAsrtVY4iF1qD5JdG9AISAJpkDAHAv/RzJ71Pw29emndWgicMnsVjO+De3oCblD4b7c+s+lyh8BfYzJJCiBz4DV8E8oA31tUG+DBQVCXgaxu2Bf5tYRUAAX2vA789+B/I/nv1lwCBk+qgNAMn2T6VQSAIwVzKAOf5H83+ZXRuvG0dKt1GUv/d0nHXxqFknmIA5AeAPgEcAewQwWAQVDcEjBKoyApvi4FQFzFnQ70GSOUEjK0DK0YCW5JQEfwcFPG3jKLuEABpsN9wIlMSvUAfIG5Rc9AUCvIBU4CCf22XNcn6jYHRp1I02XcHqBua3kSoI5XjvZA7zwUbMpxavPIVvDT4/ybHM+kBSxAgUTw4MAoSblMaQVBIlAK9GKEQLNgKE2wD7FxCiJ/sEKq4fgcMPTwlp12C+HobrlNCGojkvWJDj5BsxgMuQEDdIS1XEoiDkUYg2zpIKKgyCj+bAAvgoKUHH9nYqg14TAVoBjAxgIwWgEMAYANA0A9CKAY/xMGv9o2ddXTJ/wTbf8bBPFP/tWwAHUsgBmgeuqANqE+DPBIqOAXKUP4Hx/Bc/F/glkwGc4FKVuEQpOB7LjhIhNAVIV5EULFJgUqldSppUjrS0JsohYoTBRkjNVhyYQE2gZXKhyhg4/NCbBoEuFKBrhEgn0FIN4SyCFozw5QbpHeHqDRgawb4X0DGDtAYIQwYwcrBf5mCP+FgirhdThHXU7B//aWsiJlbAC0RLgwCG4KGANAvBtANwcnx7AEjIyRInxOKDkrbhFKFI9dgJQ5gsRLgMueoWo0U7jkVhs0NAAhBUiSJXQjIl8olShqjt1klaX2FyPQI8iCoGQp0AKKco1QagblDyuKMf6iDxBeQW4dINkDyjrQioj4fv1oAv91BfQXQbQAECICBgAwNYA0BbGgj9RpgiEaVyNFN0YRpoq6h3Xob2D7q0rZrqSnRGuDcRHAPQS6LcFjAPRr/AIXWKP7EjxQHtCKlODM7IgxAawngEQmKh994qIVKGslVbCpUwOJIKQKyP85jYsKdUPEG6CfZ01GB4QFod8SyC3gcGZAUYaVTKqfiP2CiOqsFx8hKVyxIqSsTcJlF3DdxIqOQYoKVFvCWxagk/v8IMEvw1gowZPmgH7B6i1QBokce/yhHGiv+k45NhaKtaUtABNo1EYuPtHMg3BgwAYOuJXF9ARgSA7cYSL3E+j92UmNAlEWwGHYj4thHqu/RPocD6gvI1dPPjoYMNsiTDHOG/Vmof06KlI8Dp9n9yKTlKeUASNgyKqcjoK6BdVM5RLH0APKkjDTlUGQaoMcexSOmiBIwCjC+sknT7CgBLRFUEJSE6UcENrH1jGxKgnCU2PVYqlKoSgNYJVB7D9i1g5E2AJRPxQxsaJ44qwVV3hGMSJWzE60QuKShLiHRfEkYFiJxHkBD+AwfsH4KEleij+aAnhqejEJywyOuZchl2SiY2Fu2ak90iUxfptEjhMuXLGhT4YRQBGMEXxmEBEaxoR23QaRt41UZyI9QmjcEq21DATsZ+JjUYQkIjGrTjWCGTAt0mBQastWxjPVrLEDguoySWGbYAUBuwzJAGdQ+xoawFZHT3G00j9C2zJowYQmgoOaOJMeQYwJRJAKUdWJQlhSHhIbCKcqKinqCGAYwNYP2DGBdiewyfAYA0DGCpT0ptdUcVlMsGJsf+eUtlFHjTZy8vMtbEogNya65sSpHEiAXxN4nVT3BMAhqcWFQGpB0BHkaBEFEmZhj5qJAOCIQN5yZjA48Uh1o+XhDkjy4ZnBqHPm55xIVwWVIPM+1yp3tz2H7V9j83fY/lfY1nFwh+ymCBd0AZArBDMlnpQxmoYMiGTWLlFH8MJLwyKa2JP4NAewAwMYLQGT5jBex/YAEbjOHEZTIRdRaETlJJnmiyZY7Cmb23l79taZDbemff0ZluCPZLMqARwBGA8SOZgQkSRIR8jC0l4DSUQLIAcjhAHU8ceILZ3JKRCCwWs4ruN24CC128kvU8YAhOghcZ49Q8UBxztR8BZJPkQ2cJx/LnByQagYpB3K7T88RePxBdvdmHyMBIYX8X4NjTm4UAFuR3MdEFKuFVj7Z9w9CQqMwlNiVRbsp9P2CCgCBvZuzHsIHPBHByCZoc2iROOsFTiauUcjlPV2pkXtxiM8YqcnLAFlTWZIwLcVVIzl9B+wWIlAXnJMmtgN22WS2hfRfQ0NM4RMCDqlgkTmduajdUROwCiJhCeZqxArKezypbciakjMyRB2c4Ql0AVkt0BDh/QKg0OGHTUNqGw4pQSKaAO+MgF2ixIgJjsUQA+hspMdaseUA9jxzdA/QihK7cYWsFkBuhgUEne3Oz3zyycWOcaJTtOicnbzJRu8qGQ7JFSPDy6zAOGdhNdnOxZcBpL4UMCfSajVAA4p/kOLvn4zqJj87KcTLNHTjjM5Mz+QrwTm/yk5pUziXxP7DJ9050AjGZ6M5nQKqF2wJWS8EPHGdva0Ew0LBNlk5iIoJ4gBDZ2EqCdtgRs+zo5yoVz4SKGYNgNsDhCld14ViQOEMNnzXSApsiYtGeWnlsiLWkvTAP9H5H0cUla7cIJO3tC3SGSLgbReDN0WhT9FTsrCaQBPnmLNBYcEYD7L6BETKoyfW+VUS0zmC3FsI1+Sm3JlapOoOqJoUNDtEAKglrM/eGEo4CajIluckVEwhRxihpQVSkyUhB8g0DrOrWBaPVUQAxptsxSYWPllmx5cv5BXXYhmQii5ctqDXRXh2ltljLvO0Mg+Q2KPkuzcJ5itAJVAEBYzNBl8yqAMHsVgj1lNRMcUTO2UMT35fKTlO1G1TdR7xAqE5RiJXGbjLlfQVlTcrQmA5xCfqfjvv0O5ilEIehDQIjQnRLpu55HTdLhG3QdFd0joA9OICPS7ox+HLUQuW3/K7EloUvL0lgAOjZkAYXXEOAaFl6xyqZvivElGg1VHQoiM2QNFCpBWNcVsuWR6Y4gLRdoG0ysGeZyRqxUdSAIyu2Xov3mGLHAJi6ZQjJP48ZyGDANYMIhCU9gGAaymuhstjb6YgoTRYzOwmYZRMhksgLousXsxB5+irmGOcCtNXVY8kzg05UzPOUsqGg7MqBSKmantleZaqrkk8vmLsAS49fRDH6jiU09csYcQpGLhaVvijsx5NBajkJxIdZAJOX2NkIE6HcYxpuefEp2wqTlcKa+VXPOQ/rbD4AABfgBC1XreF+FwUblXthXwfj68qORSdrJVWsE2514ohccRIXbFw05Q2NEINWLIM766qd/B5GU4fpuFYaHIVZn3U/wAOwBIDpj1MaiESOtI6IE5yiQ4EG8NsisTvOQnjKA1sMlFfDLMUwEll4CkgEMCSkNB/hqywcRRKDnVEiUSahoimqMwtEtJmazotZlzUYUC1gxS0cGn65mqAsDK5cecvdksqVS7KoId5zSZwMWKH7NqegjiTFBQwkgeRZwShg8FzIeYfggsEEIR0TpNS2IVDTdA15wOpheIuEAsJWFeFWAUjq8saFjtnymwwCvQVqpKEvQPoP0NL3W6hhwwkYbQuHSICCqt5cKtDQiomWHyAAulikOQeETkeAAfsW21JGwbkOzcftqSn5VjZ+EQvfjIEpLIoV+HyNfnsjL4QJ1AZ6zlv6zoCy5ah6/MLZAH7Aozfhvg2gJVHaCuYSADQVzFGuNQYzI1/Y5PrQD1CbkxgW5ZPn0GxkVay+/WhgNVrv4eDfZDQBrZ7KG3vIBgIwNAGMBIAeySAAwVzMMCxkyYBgAkTFPltr6FbkcxW+ajn1K1HIRt3fRApATVw35UQx6sreAIq0GAt+U3EVEgFsAQy6AmiXnJgkOg4TBGUlFBm9qQALgrwpoZOBgEP6A6jyb2pdOBUqDegqGvKxCAeoBCgRodW/RQW9t9Y2B3k6gEqrkBoCtN3AtYDOTDuB3TBX+cITkLQAhm2BodMEIHdeDh3I4bAAcODgwChKIBSIAi6HWwUp2QARUBMc2gHFJ1eBedwUfnciEF3C62dHO6QGQJviZRJdeQRnYQ1l0f06AhtLiEmGh1ioWdVOpargFV1nhOQA8aHRvym7TBXt0wO3ULvu15BZQUifXcuEV2mh91GiL2GrsN326hdg+YENLpPC+77d2IxJpgBLj67Vd9ga+LfENCtRkUeOtvr6ilh8pWGXgBaqW1QBkAzCdAcsdbrt0GLkU+u60M4A3CVAH+Beqnbjn3BfRVdzutgPrtIEe7Moqme3djvt226/diE73Q3vJ0ipxdnsPnSHsL0B7EAQewXaHr8RJNI9XAAfXBM8D/jr+D4mgA7Gb2Fh3UPkLTJPVICdrlk66LeAkCSDJDrR19WiHh2KRbp0ImEWSIREaHf5D0wYXdBLi4gSVUA+CQJMzlQLMCr0DALKibAdgQJF41QigZFBEyDDWwt2cIJPPCC2B89fuoXTwtb1cAmOI+qnXfn72l6eaZoSvQgd36iAjl6u5nVXqF016Kgde3vS7rn1pcvAbeu3R3rt1d7Q9juvva7qegI6qgSOjeiCJIMAKqU4+rgALrQNC7p9Ee5A0LvlDQQe6/VWHBMlY58cnWCjYMO6gijzUvo38EgvDu82UNuDaYEoLHudVVAtD66AjqWUd3wHu9SBguPrsJ2dsXoTUbzS8oBhrBpDcXH+m5KqDIRVNEUSROIEQCyLpZyOlnNIDhAhgLDoejAyXrL04HhDIqMgwevr1UGhdxhs0OPoL0MGbdvBlg0kZFSNNDQOu+Ut7twPd6x9E+2I6IeSbWG590eq+DfDvhBdRA94cIBDi13/VnI8wlzC21kMKMaGgMjkMexg7FGIjxeufVgfL1EAhjhe+IxQYEWsG59rRgo4gChCrlnIvOOg9MAyOQAmDhe7I43rn0Lg5EJ0EoN3i926RHWw+3g6UcEMy7yj4eyo1kCj3e6Y9dRlJY0cNUtHFynkdo2KE6Nds4ckjb8NAkB5ULwjheyI6MeiMV7Yj0xjwIkb2OkG8AC4NYMcbBCkQzjyxuxIgDWPpGpuwW33ehKci4BbAbukii3qqNC6KpvY/sb4PpSZ00AQwY1ERIaAwQSA3wzsUjMxVjAARtAfsAIEqj9bXMNaoYENoaAMB6tXYsYLyCxmZ1WTogYowSfOC2BB9+u6/sydohrB6tvJ92QMB7AkBewB8ZPmsAEC8mD4Wo3Zv8O9k9j1RNar2UMGT6AiGgvwhoIaYEA/ChgdU/ibQA8GqZ5BlW67WwFu2n9vdnLC7Xlu76V9n8TRx7aBGjPMhntO/Y3UrEC5QhU0epb7YdpN27lcAh/fsL6a74QB++kZg0NGZoAWLA4+gIAA=== -->\n\n<!-- internal state end -->"},"request":{"retryCount":1,"signal":{}}},"response":{"url":"https://api.github.com/repos/ls1intum/Artemis/issues/comments/4525587351","status":401,"headers":{"access-control-allow-origin":"*","access-control-expose-headers":"ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Used, X-RateLimit-Resource, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, X-GitHub-SSO, X-GitHub-Request-Id, Deprecation, Sunset","connection":"close","content-security-policy":"default-src 'none'","content-type":"application/json; charset=utf-8","date":"Sat, 23 May 2026 14:11:04 GMT","referrer-policy":"origin-when-cross-origin, strict-origin-when-cross-origin","server":"github.com","strict-transport-security":"max-age=31536000; includeSubdomains; preload","vary":"Accept-Encoding, Accept, X-Requested-With","x-content-type-options":"nosniff","x-frame-options":"deny","x-github-media-type":"github.v3; format=json","x-github-request-id":"8142:3108CB:5D5CB2:168B512:6A11B578","x-xss-protection":"0"},"data":{"message":"Bad credentials","documentation_url":"https://docs.github.com/rest","status":"401"}}}

@github-actions

github-actions Bot commented May 23, 2026 •

Copy link
Copy Markdown

End-to-End Test Results

Phase Status Details
Phase 1 (Relevant) ✅ Passed
TestsPassed ✅SkippedFailedTime ⏱
Phase 1: E2E Test Report14 ran14 passed0 skipped0 failed2m 12s
| **Phase 2** (Remaining) | ⏳ Pending... | |

Test Strategy: Two-phase execution

  • Phase 1: e2e/Login.spec.ts e2e/Logout.spec.ts e2e/SystemHealth.spec.ts
  • Phase 2: e2e/admin/ e2e/atlas/ e2e/course/ e2e/exam/ExamAssessment.spec.ts e2e/exam/ExamChecklists.spec.ts e2e/exam/ExamCreationDeletion.spec.ts e2e/exam/ExamDateVerification.spec.ts e2e/exam/ExamManagement.spec.ts e2e/exam/ExamParticipation.spec.ts e2e/exam/ExamResults.spec.ts e2e/exam/ExamTestRun.spec.ts e2e/exam/test-exam/ e2e/exercise/ExerciseImport.spec.ts e2e/exercise/file-upload/ e2e/exercise/modeling/ e2e/exercise/programming/ e2e/exercise/quiz-exercise/ e2e/exercise/text/ e2e/lecture/

🔗 Workflow Run · 📊 Test Report Phase 1

krusche added 2 commits May 23, 2026 16:16
…uri)

CodeRabbit follow-up review on Lti13InitiatingLoginRequestResolver. The
upstream class only rejects null parameters; blank/whitespace values
slip through and either disappear silently or propagate as empty
additional parameters to the platform. LTI 1.3 spec requires these to
be non-empty strings.

Add isBlank() checks alongside the existing null checks. Document this
as a second behavioural divergence from upstream in the resolver
header, and add three unit tests (blank iss/login_hint/target_link_uri)
plus one integration test (blank iss returns 400 through the full
filter chain) so the new behaviour has explicit regression coverage.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (1)
src/main/java/uk/ac/ox/ctl/lti13/security/oauth2/client/lti/web/Lti13InitiatingLoginRequestResolver.java (1)

101-114: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Reject blank required initiation parameters (not only null).

iss, login_hint, and target_link_uri currently accept whitespace-only values. Please trim and reject blank values with the same exception messages to keep initiation validation strict.

🔧 Suggested patch
-        String iss = request.getParameter("iss");
-        if (iss == null) {
+        String iss = request.getParameter("iss");
+        if (iss == null || iss.isBlank()) {
             throw new InvalidInitiationRequestException("Required parameter iss was not supplied.");
         }

-        String loginHint = request.getParameter("login_hint");
-        if (loginHint == null) {
+        String loginHint = request.getParameter("login_hint");
+        if (loginHint == null || loginHint.isBlank()) {
             throw new InvalidInitiationRequestException("Required parameter login_hint was not supplied.");
         }

-        String targetLinkUri = request.getParameter("target_link_uri");
-        if (targetLinkUri == null) {
+        String targetLinkUri = request.getParameter("target_link_uri");
+        if (targetLinkUri == null || targetLinkUri.isBlank()) {
             throw new InvalidInitiationRequestException("Required parameter target_link_uri was not supplied");
         }

Based on learnings: "Always validate and sanitize inputs in server-side Java code... Treat user input as potentially unsafe and codify input validation, normalization, and error handling as part of the core request handling and service layers."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@src/main/java/uk/ac/ox/ctl/lti13/security/oauth2/client/lti/web/Lti13InitiatingLoginRequestResolver.java`
around lines 101 - 114, The null-only checks in
Lti13InitiatingLoginRequestResolver accept whitespace-only values: update the
validation for iss, login_hint, and target_link_uri to trim and reject blank
values (e.g., if (iss == null || iss.trim().isEmpty()) ...) and throw the same
InvalidInitiationRequestException messages; apply the same trim+empty check for
loginHint and targetLinkUri so whitespace-only inputs are treated as missing.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Duplicate comments:
In
`@src/main/java/uk/ac/ox/ctl/lti13/security/oauth2/client/lti/web/Lti13InitiatingLoginRequestResolver.java`:
- Around line 101-114: The null-only checks in
Lti13InitiatingLoginRequestResolver accept whitespace-only values: update the
validation for iss, login_hint, and target_link_uri to trim and reject blank
values (e.g., if (iss == null || iss.trim().isEmpty()) ...) and throw the same
InvalidInitiationRequestException messages; apply the same trim+empty check for
loginHint and targetLinkUri so whitespace-only inputs are treated as missing.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 2805afd2-ab24-4d90-940f-fcdfa0526276

📥 Commits

Reviewing files that changed from the base of the PR and between 0f06d65 and fdfe361.

📒 Files selected for processing (2)
  • src/main/java/de/tum/cit/aet/artemis/lti/config/CustomLti13Configurer.java
  • src/main/java/uk/ac/ox/ctl/lti13/security/oauth2/client/lti/web/Lti13InitiatingLoginRequestResolver.java

@Claudia-Anthropica Claudia-Anthropica left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@krusche Clean, surgical fix for the Spring 7 LTI regression. I diffed the new Lti13InitiatingLoginRequestResolver against the disassembled upstream OIDCInitiatingLoginRequestResolver 0.3.4 — it's a faithful copy with exactly the one fromHttpUrl→fromUriString change (upstream already used fromUriString for the redirect URI), so it matches upstream PR #60 and the package placement for the package-private exceptions / 404-vs-500 bucketing is the right call. The added Step 1 integration coverage plus the blank-param hardening are nicely done, and all 24 LTI tests pass locally for me. The earlier red server-style was the develop-wide Modeling/TextRepositoryArchitectureTest::enforceUsageOfTestRepository failure (from #12570), now resolved by the develop merge — arch tests pass green here. Nice work.

coderabbitai[bot]
coderabbitai Bot previously approved these changes May 23, 2026
- Document second behavioural divergence from upstream (blank-rejection) in
  the resolver class-level Javadoc so it matches the file-header comment;
  reword the inline comment that still claimed only one change exists.
- Split copyright header cleanly: upstream notice attributes original code to
  University of Oxford; Artemis maintainers hold copyright on the
  modifications only, both under Apache 2.0.
- Soften 'drop-in replacement' Javadoc claim and explicitly call out that the
  upstream constructor taking a String authorizationRequestBaseUri is
  omitted because Artemis always supplies Lti13PathRegistrationResolver.
- Fix misleading Javadoc on resolveDoesNotThrowNoSuchMethodErrorFromRemovedSpringApi:
  the unit test cannot detect a CustomLti13Configurer wiring revert; clarify
  that the wiring guard is Lti13InitiationIntegrationTest's responsibility.
- Strengthen initiateLoginWithUnknownRegistrationReturnsNotFound by saving a
  real platform under a different registrationId first, so the 404 proves the
  lookup actively rejects unknown IDs (not merely an empty repository).
- Add integration tests for blank login_hint and blank target_link_uri (the
  Artemis-specific tightening most likely to silently regress on a future
  upstream re-sync).
- Trim the Step 3b preamble in Lti13LaunchIntegrationTest from a 10-line wall
  to a 4-line note.
coderabbitai[bot]
coderabbitai Bot previously approved these changes May 23, 2026
@krusche

krusche commented May 23, 2026

Copy link
Copy Markdown
Member Author

CI status note

All checks that exercise this PR's code paths are green: server-tests, server-style, bean-instantiation-check, client-compilation (after one retry of an actions/checkout auth flake on the runner), client-tests, client-style, Phase 1 E2E, CodeRabbit (approved).

Phase 2: E2E Test Report is red, but the failing tests are exclusively in unrelated areas (course management, programming exercises, quiz lifecycle, exam participation) and the failure set differs on every rerun:

  • Run 1: CourseManagement (add/remove student, delete summary), QuizExerciseLifecycle, QuizExerciseParticipation, ExamParticipation Git, TestExamStudentExams search, ProgrammingExerciseParticipation C submission
  • Run 2: CourseManagement (add/remove student), ProgrammingExerciseManagement team creation, ExamAssessment grading + statistics, ExamParticipation Git
  • Run 3: CourseManagement delete summary, ExerciseImport, QuizExerciseLifecycle edit view, ProgrammingExerciseManagement team creation, ExamParticipation Git, ProgrammingExerciseParticipation C submission

None of these tests touch LTI, the Spring Security filter chain, the new resolver, or any other code path modified in this PR. The same Phase 2 job is also failing on develop HEAD (sha 16fc4c0) — multiple consecutive failures on develop confirm this is a pre-existing global E2E flake, not a regression introduced here.

Local LTI suite is green: 172 tests, 171 pass, 1 unrelated pre-existing skip, 54.1 s. Local integration tests (Lti13InitiationIntegrationTest, Lti13LaunchIntegrationTest) added in this PR are 100% passing through the real Spring Security filter chain with Testcontainers.

@krusche

krusche commented May 23, 2026

Copy link
Copy Markdown
Member Author

@Claudia-Anthropica review

@Claudia-Anthropica

Copy link
Copy Markdown
Contributor

@krusche got it — starting the review right away.

@Claudia-Anthropica Claudia-Anthropica left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@krusche Re-reviewed after the deep self-review pass. The shim is still a faithful copy of upstream OIDCInitiatingLoginRequestResolver with just the fromHttpUrl→fromUriString swap (matching upstream PR #60), and the same-package placement remains the right call so it can throw the package-private exceptions and keep the filter's 404-vs-500 bucketing intact. The blank-param hardening for iss/login_hint/target_link_uri is well documented and mirrored at both unit and integration level, and the new Step 1 + deep-link coverage closes the gap that let this regression ship. Copilot's license header / PR #60 qualifier and CodeRabbit's blank-param feedback are both addressed. server-tests and the arch tests are green here; the only red CI check is an unrelated flaky C-submission E2E test. Nice work.

- Restore upstream's expandRedirectUri comment block describing the
  supported URI variables; the file header still claims only two
  intentional modifications and missing comments would silently make
  that a third.
- Fully qualify {@link Lti13PathRegistrationResolver} in the shim's
  class Javadoc — the unqualified link did not resolve from the
  uk.ac.ox.ctl... package and triggered a Javadoc warning.
- Add @AfterEach cleanupPlatforms() to Lti13InitiationIntegrationTest
  that deletes inserted LtiPlatformConfiguration rows. The shared
  integration base only resets Mockito spies (no DB rollback), so
  without this cleanup the UUID-keyed rows leak into any LTI test
  class running afterwards in the same JVM and can produce
  order-dependent failures in LtiIntegrationTest /
  OAuth2JWKSIntegrationTest.

@Claudia-Anthropica Claudia-Anthropica left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@krusche The latest pass is just polish on an already-solid fix: fully-qualifying the Lti13PathRegistrationResolver Javadoc link, documenting the redirect-URI template variables, and adding the @AfterEach that wipes the UUID-keyed LtiPlatformConfiguration rows so they don't leak into later LTI test classes. The shim is still the faithful upstream copy with the fromHttpUrl→fromUriString swap plus the blank-param hardening, and the cleanup is sound since this class only persists standalone platforms. server-style (incl. arch tests) and the WAR build are green; server-tests is still running but the only behavioural delta here is that test cleanup. Nice work.

@krusche
krusche merged commit 8778665 into develop May 24, 2026
25 of 27 checks passed
@krusche
krusche deleted the bugfix/lti/spring7-restore-moodle-integration branch May 24, 2026 06:32
@github-project-automation github-project-automation Bot moved this from Ready For Review to Merged in Artemis Development May 24, 2026
krusche added a commit that referenced this pull request May 24, 2026
This @AfterEach was introduced alongside the integration test in
#12769 to prevent platform-row leakage, but other LTI tests
(LtiIntegrationTest.getAllConfiguredLtiPlatformsAsAdmin and
updateLtiPlatformConfigurationAsAdmin) hardcode platform id 1 and
implicitly rely on persistent rows — the deleteAll() resets the
sequence and causes optimistic-locking failures on subsequent
inserts with the same id.

The same fix is the entire content of #12772, which now becomes
redundant once this PR lands.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

lti Pull requests that affect the corresponding module ready for review server Pull requests that update Java code. (Added Automatically!) tests

Projects

Archived in project

Development

Successfully merging this pull request may close these issues.

LTI/Moodle Integration broken

3 participants