diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index aa98d2a1e..c38571abb 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,8 +1,9 @@ -name: Build repo +name: Build and Test Workflow on: - push: - branches: ["main"] + workflow_dispatch: + # push: + # branches: ["main"] pull_request: branches: ["main"] @@ -10,6 +11,24 @@ permissions: contents: read jobs: + files-changed: + name: detect what files changed + runs-on: ubuntu-latest + timeout-minutes: 3 + # Map a step output to a job output + outputs: + build: ${{ steps.changes.outputs.build }} + app: ${{ steps.changes.outputs.app }} + project: ${{ steps.changes.outputs.project }} + openapi: ${{ steps.changes.outputs.openapi }} + steps: + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + + - name: Check for file changes + uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2 + id: changes + with: + filters: ".github/config/.files.yaml" build: runs-on: ubuntu-latest @@ -25,7 +44,7 @@ jobs: steps: - name: Harden Runner - uses: step-security/harden-runner@6c439dc8bdf85cadbbce9ed30d1c7b959517bc49 # v2.12.2 + uses: step-security/harden-runner@ec9f2d5744a09debf3a187a3f4f675c53b671911 # v2.13.0 with: egress-policy: audit @@ -38,6 +57,11 @@ jobs: java-version: ${{ matrix.jdk-version }} distribution: "temurin" + - name: Setup Gradle + uses: gradle/actions/setup-gradle@ac638b010cf58a27ee6c972d7336334ccaf61c96 # v4.4.1 + with: + gradle-version: 8.14 + - name: Build with Gradle and spring security ${{ matrix.spring-security }} run: ./gradlew clean build env: @@ -88,14 +112,17 @@ jobs: if-no-files-found: warn check-generateOpenApiDocs: + if: needs.files-changed.outputs.openapi == 'true' + needs: [files-changed, build] runs-on: ubuntu-latest steps: - name: Harden Runner - uses: step-security/harden-runner@6c439dc8bdf85cadbbce9ed30d1c7b959517bc49 # v2.12.2 + uses: step-security/harden-runner@ec9f2d5744a09debf3a187a3f4f675c53b671911 # v2.13.0 with: egress-policy: audit - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - name: Checkout repository + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - name: Set up JDK 17 uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 # v4.7.1 @@ -103,7 +130,8 @@ jobs: java-version: "17" distribution: "temurin" - - uses: gradle/actions/setup-gradle@ac638b010cf58a27ee6c972d7336334ccaf61c96 # v4.4.1 + - name: Setup Gradle + uses: gradle/actions/setup-gradle@ac638b010cf58a27ee6c972d7336334ccaf61c96 # v4.4.1 - name: Generate OpenAPI documentation run: ./gradlew :stirling-pdf:generateOpenApiDocs @@ -115,10 +143,12 @@ jobs: path: ./SwaggerDoc.json check-licence: + if: needs.files-changed.outputs.build == 'true' + needs: [files-changed, build] runs-on: ubuntu-latest steps: - name: Harden Runner - uses: step-security/harden-runner@6c439dc8bdf85cadbbce9ed30d1c7b959517bc49 # v2.12.2 + uses: step-security/harden-runner@ec9f2d5744a09debf3a187a3f4f675c53b671911 # v2.13.0 with: egress-policy: audit @@ -129,7 +159,7 @@ jobs: uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 # v4.7.1 with: java-version: "17" - distribution: "adopt" + distribution: "temurin" - name: check the licenses for compatibility run: ./gradlew clean checkLicense @@ -144,6 +174,8 @@ jobs: retention-days: 3 docker-compose-tests: + if: needs.files-changed.outputs.project == 'true' + needs: files-changed # if: github.event_name == 'push' && github.ref == 'refs/heads/main' || # (github.event_name == 'pull_request' && # contains(github.event.pull_request.labels.*.name, 'licenses') == false && @@ -162,7 +194,7 @@ jobs: steps: - name: Harden Runner - uses: step-security/harden-runner@6c439dc8bdf85cadbbce9ed30d1c7b959517bc49 # v2.12.2 + uses: step-security/harden-runner@ec9f2d5744a09debf3a187a3f4f675c53b671911 # v2.13.0 with: egress-policy: audit @@ -173,7 +205,7 @@ jobs: uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 # v4.7.1 with: java-version: "17" - distribution: "adopt" + distribution: "temurin" - name: Set up Docker Buildx uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3.11.1 @@ -188,6 +220,7 @@ jobs: with: python-version: "3.12" cache: 'pip' # caching pip dependencies + cache-dependency-path: ./testing/cucumber/requirements.txt - name: Pip requirements run: | @@ -199,3 +232,69 @@ jobs: chmod +x ./testing/test.sh chmod +x ./testing/test_disabledEndpoints.sh ./testing/test.sh + + test-build-docker-images: + if: github.event_name == 'pull_request' && needs.files-changed.outputs.project == 'true' + needs: [files-changed, build, check-generateOpenApiDocs, check-licence] + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + docker-rev: ["Dockerfile", "Dockerfile.ultra-lite", "Dockerfile.fat"] + steps: + - name: Harden Runner + uses: step-security/harden-runner@ec9f2d5744a09debf3a187a3f4f675c53b671911 # v2.13.0 + with: + egress-policy: audit + + - name: Checkout Repository + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + + - name: Set up JDK 17 + uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 # v4.7.1 + with: + java-version: "17" + distribution: "temurin" + + - name: Set up Gradle + uses: gradle/actions/setup-gradle@ac638b010cf58a27ee6c972d7336334ccaf61c96 # v4.4.1 + with: + gradle-version: 8.14 + + - name: Build application + run: ./gradlew clean build + env: + DISABLE_ADDITIONAL_FEATURES: true + STIRLING_PDF_DESKTOP_UI: false + + - name: Set up QEMU + uses: docker/setup-qemu-action@29109295f81e9208d7d86ff1c6c12d2833863392 # v3.6.0 + + - name: Set up Docker Buildx + id: buildx + uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3.11.1 + + - name: Build ${{ matrix.docker-rev }} + uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6.18.0 + with: + builder: ${{ steps.buildx.outputs.name }} + context: . + file: ./${{ matrix.docker-rev }} + push: false + cache-from: type=gha + cache-to: type=gha,mode=max + platforms: linux/amd64,linux/arm64/v8 + provenance: true + sbom: true + + - name: Upload Reports + if: always() + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 + with: + name: reports-docker-${{ matrix.docker-rev }} + path: | + build/reports/tests/ + build/test-results/ + build/reports/problems/ + retention-days: 3 + if-no-files-found: warn diff --git a/.gitignore b/.gitignore index 945e68b69..105ddec3c 100644 --- a/.gitignore +++ b/.gitignore @@ -200,6 +200,3 @@ id_ed25519.pub # node_modules node_modules/ - -# Claude -CLAUDE.md diff --git a/app/common/src/main/java/stirling/software/common/util/RequestUriUtils.java b/app/common/src/main/java/stirling/software/common/util/RequestUriUtils.java index 654c78fe9..239976b66 100644 --- a/app/common/src/main/java/stirling/software/common/util/RequestUriUtils.java +++ b/app/common/src/main/java/stirling/software/common/util/RequestUriUtils.java @@ -14,8 +14,10 @@ public class RequestUriUtils { || requestURI.startsWith(contextPath + "/images/") || requestURI.startsWith(contextPath + "/public/") || requestURI.startsWith(contextPath + "/pdfjs/") + || requestURI.startsWith(contextPath + "/pdfjs-legacy/") || requestURI.startsWith(contextPath + "/login") || requestURI.startsWith(contextPath + "/error") + || requestURI.startsWith(contextPath + "/favicon") || requestURI.endsWith(".svg") || requestURI.endsWith(".png") || requestURI.endsWith(".ico") diff --git a/app/core/src/main/resources/application.properties b/app/core/src/main/resources/application.properties index e0273eb5a..ec3a0a390 100644 --- a/app/core/src/main/resources/application.properties +++ b/app/core/src/main/resources/application.properties @@ -50,4 +50,4 @@ spring.main.allow-bean-definition-overriding=true java.io.tmpdir=${stirling.tempfiles.directory:${java.io.tmpdir}/stirling-pdf} # V2 features -v2=false +v2=true diff --git a/app/core/src/main/resources/settings.yml.template b/app/core/src/main/resources/settings.yml.template index c7b5724c4..f311dac00 100644 --- a/app/core/src/main/resources/settings.yml.template +++ b/app/core/src/main/resources/settings.yml.template @@ -11,7 +11,7 @@ ############################################################################################################# security: - enableLogin: true # set to 'true' to enable login + enableLogin: false # set to 'true' to enable login csrfDisabled: false # set to 'true' to disable CSRF protection (not recommended for production) loginAttemptCount: 5 # lock user account after 5 tries; when using e.g. Fail2Ban you can deactivate the function with -1 loginResetTimeMinutes: 120 # lock account for 2 hours after x attempts @@ -47,24 +47,23 @@ security: scopes: openid, profile, email # specify the scopes for which the application will request permissions provider: google # set this to your OAuth Provider's name, e.g., 'google' or 'keycloak' saml2: - enabled: true # Only enabled for paid enterprise clients (enterpriseEdition.enabled must be true) - provider: authentik # The name of your Provider + enabled: false # Only enabled for paid enterprise clients (enterpriseEdition.enabled must be true) + provider: '' # The name of your Provider autoCreateUser: true # set to 'true' to allow auto-creation of non-existing users blockRegistration: false # set to 'true' to deny login with SSO without prior registration by an admin registrationId: stirling # The name of your Service Provider (SP) app name. Should match the name in the path for your SSO & SLO URLs idpMetadataUri: https://dev-XXXXXXXX.okta.com/app/externalKey/sso/saml/metadata # The uri for your Provider's metadata idpSingleLoginUrl: https://dev-XXXXXXXX.okta.com/app/dev-XXXXXXXX_stirlingpdf_1/externalKey/sso/saml # The URL for initiating SSO. Provided by your Provider idpSingleLogoutUrl: https://dev-XXXXXXXX.okta.com/app/dev-XXXXXXXX_stirlingpdf_1/externalKey/slo/saml # The URL for initiating SLO. Provided by your Provider - idpIssuer: authentik # The ID of your Provider - idpCert: classpath:authentik-Self_Signed_Certificate.pem # The certificate your Provider will use to authenticate your app's SAML authentication requests. Provided by your Provider - privateKey: classpath:private-key.key # Your private key. Generated from your keypair - spCert: classpath:cert.crt # Your signing certificate. Generated from your keypair + idpIssuer: '' # The ID of your Provider + idpCert: classpath:okta.cert # The certificate your Provider will use to authenticate your app's SAML authentication requests. Provided by your Provider + privateKey: classpath:saml-private-key.key # Your private key. Generated from your keypair + spCert: classpath:saml-public-cert.crt # Your signing certificate. Generated from your keypair premium: key: 00000000-0000-0000-0000-000000000000 - enabled: true # Enable license key checks for pro/enterprise features + enabled: false # Enable license key checks for pro/enterprise features proFeatures: - database: false # Enable database features SSOAutoLogin: false CustomMetadata: autoUpdateMetadata: false @@ -100,7 +99,7 @@ legal: system: defaultLocale: en-US # set the default language (e.g. 'de-DE', 'fr-FR', etc) googlevisibility: false # 'true' to allow Google visibility (via robots.txt), 'false' to disallow - enableAlphaFunctionality: true # set to enable functionality which might need more testing before it fully goes live (this feature might make no changes) + enableAlphaFunctionality: false # set to enable functionality which might need more testing before it fully goes live (this feature might make no changes) showUpdate: false # see when a new update is available showUpdateOnlyAdmin: false # only admins can see when a new update is available, depending on showUpdate it must be set to 'true' customHTMLFiles: false # enable to have files placed in /customFiles/templates override the existing template HTML files diff --git a/app/proprietary/src/main/java/stirling/software/proprietary/security/configuration/SecurityConfiguration.java b/app/proprietary/src/main/java/stirling/software/proprietary/security/configuration/SecurityConfiguration.java index 094a7986b..eeec274bf 100644 --- a/app/proprietary/src/main/java/stirling/software/proprietary/security/configuration/SecurityConfiguration.java +++ b/app/proprietary/src/main/java/stirling/software/proprietary/security/configuration/SecurityConfiguration.java @@ -252,7 +252,9 @@ public class SecurityConfiguration { || trimmedUri.startsWith("/js/") || trimmedUri.startsWith("/favicon") || trimmedUri.startsWith( - "/api/v1/info/status"); + "/api/v1/info/status") + || trimmedUri.startsWith("/v1/api-docs") + || uri.contains("/v1/api-docs"); }) .permitAll() .anyRequest() diff --git a/app/proprietary/src/main/java/stirling/software/proprietary/security/filter/JwtAuthenticationFilter.java b/app/proprietary/src/main/java/stirling/software/proprietary/security/filter/JwtAuthenticationFilter.java index eaa333a76..d3511b08b 100644 --- a/app/proprietary/src/main/java/stirling/software/proprietary/security/filter/JwtAuthenticationFilter.java +++ b/app/proprietary/src/main/java/stirling/software/proprietary/security/filter/JwtAuthenticationFilter.java @@ -1,5 +1,6 @@ package stirling.software.proprietary.security.filter; +import static stirling.software.common.util.RequestUriUtils.isStaticResource; import static stirling.software.proprietary.security.model.AuthenticationType.*; import static stirling.software.proprietary.security.model.AuthenticationType.SAML2; @@ -62,7 +63,7 @@ public class JwtAuthenticationFilter extends OncePerRequestFilter { filterChain.doFilter(request, response); return; } - if (shouldNotFilter(request)) { + if (isStaticResource(request.getContextPath(), request.getRequestURI())) { filterChain.doFilter(request, response); return; } @@ -160,49 +161,6 @@ public class JwtAuthenticationFilter extends OncePerRequestFilter { } } - @Override - protected boolean shouldNotFilter(HttpServletRequest request) { - String uri = request.getRequestURI(); - String method = request.getMethod(); - - // Skip JWT processing for logout requests to prevent token refresh during logout - if ("/logout".equals(uri)) { - return true; - } - - // Allow login POST requests to be processed - if ("/login".equals(uri) && "POST".equalsIgnoreCase(method)) { - return true; - } - - String[] permitAllPatterns = { - "/login", - "/register", - "/error", - "/images/", - "/public/", - "/css/", - "/fonts/", - "/js/", - "/pdfjs/", - "/pdfjs-legacy/", - "/api/v1/info/status", - "/site.webmanifest", - "/favicon" - }; - - for (String pattern : permitAllPatterns) { - if (uri.startsWith(pattern) - || uri.endsWith(".svg") - || uri.endsWith(".png") - || uri.endsWith(".ico")) { - return true; - } - } - - return false; - } - private void handleAuthenticationFailure( HttpServletRequest request, HttpServletResponse response, diff --git a/app/proprietary/src/main/java/stirling/software/proprietary/security/filter/UserAuthenticationFilter.java b/app/proprietary/src/main/java/stirling/software/proprietary/security/filter/UserAuthenticationFilter.java index 8a148e931..bb22f597a 100644 --- a/app/proprietary/src/main/java/stirling/software/proprietary/security/filter/UserAuthenticationFilter.java +++ b/app/proprietary/src/main/java/stirling/software/proprietary/security/filter/UserAuthenticationFilter.java @@ -218,35 +218,4 @@ public class UserAuthenticationFilter extends OncePerRequestFilter { return method; } } - - @Override - protected boolean shouldNotFilter(HttpServletRequest request) throws ServletException { - String uri = request.getRequestURI(); - String contextPath = request.getContextPath(); - String[] permitAllPatterns = { - contextPath + "/login", - contextPath + "/register", - contextPath + "/error", - contextPath + "/images/", - contextPath + "/public/", - contextPath + "/css/", - contextPath + "/fonts/", - contextPath + "/js/", - contextPath + "/pdfjs/", - contextPath + "/pdfjs-legacy/", - contextPath + "/api/v1/info/status", - contextPath + "/site.webmanifest" - }; - - for (String pattern : permitAllPatterns) { - if (uri.startsWith(pattern) - || uri.endsWith(".svg") - || uri.endsWith(".png") - || uri.endsWith(".ico")) { - return true; - } - } - - return false; - } } diff --git a/app/proprietary/src/main/java/stirling/software/proprietary/security/model/AuthenticationType.java b/app/proprietary/src/main/java/stirling/software/proprietary/security/model/AuthenticationType.java index cf9f15e35..0ef0a9235 100644 --- a/app/proprietary/src/main/java/stirling/software/proprietary/security/model/AuthenticationType.java +++ b/app/proprietary/src/main/java/stirling/software/proprietary/security/model/AuthenticationType.java @@ -2,7 +2,7 @@ package stirling.software.proprietary.security.model; public enum AuthenticationType { WEB, - @Deprecated(since = "1.0.2") SSO, + SSO, OAUTH2, SAML2 } diff --git a/app/proprietary/src/main/java/stirling/software/proprietary/security/oauth2/CustomOAuth2AuthenticationSuccessHandler.java b/app/proprietary/src/main/java/stirling/software/proprietary/security/oauth2/CustomOAuth2AuthenticationSuccessHandler.java index 71227b618..dc489ffd2 100644 --- a/app/proprietary/src/main/java/stirling/software/proprietary/security/oauth2/CustomOAuth2AuthenticationSuccessHandler.java +++ b/app/proprietary/src/main/java/stirling/software/proprietary/security/oauth2/CustomOAuth2AuthenticationSuccessHandler.java @@ -85,7 +85,8 @@ public class CustomOAuth2AuthenticationSuccessHandler } if (userService.usernameExistsIgnoreCase(username) && userService.hasPassword(username) - && !userService.isAuthenticationTypeByUsername(username, SSO) + && (!userService.isAuthenticationTypeByUsername(username, SSO) + || !userService.isAuthenticationTypeByUsername(username, OAUTH2)) && oauth2Properties.getAutoCreateUser()) { response.sendRedirect(contextPath + "/logout?oAuth2AuthenticationErrorWeb=true"); return; diff --git a/app/proprietary/src/main/java/stirling/software/proprietary/security/saml2/JwtSaml2AuthenticationRequestRepository.java b/app/proprietary/src/main/java/stirling/software/proprietary/security/saml2/JwtSaml2AuthenticationRequestRepository.java index f1f2da6b1..68c190e64 100644 --- a/app/proprietary/src/main/java/stirling/software/proprietary/security/saml2/JwtSaml2AuthenticationRequestRepository.java +++ b/app/proprietary/src/main/java/stirling/software/proprietary/security/saml2/JwtSaml2AuthenticationRequestRepository.java @@ -39,7 +39,7 @@ public class JwtSaml2AuthenticationRequestRepository HttpServletRequest request, HttpServletResponse response) { if (!jwtService.isJwtEnabled()) { - log.warn("SAML2 v2 is not enabled, skipping saveAuthenticationRequest"); + log.debug("V2 is not enabled, skipping SAMLRequest token storage"); return; } diff --git a/app/proprietary/src/main/java/stirling/software/proprietary/security/service/UserService.java b/app/proprietary/src/main/java/stirling/software/proprietary/security/service/UserService.java index 8cddebcbf..982f551ca 100644 --- a/app/proprietary/src/main/java/stirling/software/proprietary/security/service/UserService.java +++ b/app/proprietary/src/main/java/stirling/software/proprietary/security/service/UserService.java @@ -1,5 +1,8 @@ package stirling.software.proprietary.security.service; +import static stirling.software.proprietary.security.model.AuthenticationType.OAUTH2; +import static stirling.software.proprietary.security.model.AuthenticationType.SSO; + import java.sql.SQLException; import java.util.ArrayList; import java.util.Collection; @@ -63,10 +66,10 @@ public class UserService implements UserServiceInterface { @Transactional public void migrateOauth2ToSSO() { userRepository - .findByAuthenticationTypeIgnoreCase("OAUTH2") + .findByAuthenticationTypeIgnoreCase(OAUTH2.toString()) .forEach( user -> { - user.setAuthenticationType(AuthenticationType.SSO); + user.setAuthenticationType(SSO); userRepository.save(user); }); } diff --git a/app/proprietary/src/test/java/stirling/software/proprietary/security/filter/JwtAuthenticationFilterTest.java b/app/proprietary/src/test/java/stirling/software/proprietary/security/filter/JwtAuthenticationFilterTest.java index ecb84122a..7e67a9106 100644 --- a/app/proprietary/src/test/java/stirling/software/proprietary/security/filter/JwtAuthenticationFilterTest.java +++ b/app/proprietary/src/test/java/stirling/software/proprietary/security/filter/JwtAuthenticationFilterTest.java @@ -88,12 +88,11 @@ class JwtAuthenticationFilterTest { void shouldNotFilterWhenPageIsLogin() throws ServletException, IOException { when(jwtService.isJwtEnabled()).thenReturn(true); when(request.getRequestURI()).thenReturn("/login"); - when(request.getMethod()).thenReturn("POST"); + when(request.getContextPath()).thenReturn("/login"); jwtAuthenticationFilter.doFilterInternal(request, response, filterChain); - verify(filterChain).doFilter(request, response); - verify(jwtService, never()).extractTokenFromRequest(any()); + verify(filterChain, never()).doFilter(request, response); } @Test @@ -104,8 +103,8 @@ class JwtAuthenticationFilterTest { Map claims = Map.of("sub", username, "authType", "WEB"); when(jwtService.isJwtEnabled()).thenReturn(true); + when(request.getContextPath()).thenReturn("/"); when(request.getRequestURI()).thenReturn("/protected"); - when(request.getMethod()).thenReturn("GET"); when(jwtService.extractTokenFromRequest(request)).thenReturn(token); doNothing().when(jwtService).validateToken(token); when(jwtService.extractAllClaims(token)).thenReturn(claims); @@ -151,7 +150,7 @@ class JwtAuthenticationFilterTest { when(jwtService.isJwtEnabled()).thenReturn(true); when(request.getRequestURI()).thenReturn("/protected"); - when(request.getMethod()).thenReturn("GET"); + when(request.getContextPath()).thenReturn("/"); when(jwtService.extractTokenFromRequest(request)).thenReturn(token); doThrow(new AuthenticationFailureException("Invalid token")).when(jwtService).validateToken(token); @@ -168,7 +167,7 @@ class JwtAuthenticationFilterTest { when(jwtService.isJwtEnabled()).thenReturn(true); when(request.getRequestURI()).thenReturn("/protected"); - when(request.getMethod()).thenReturn("GET"); + when(request.getContextPath()).thenReturn("/"); when(jwtService.extractTokenFromRequest(request)).thenReturn(token); doThrow(new AuthenticationFailureException("The token has expired")).when(jwtService).validateToken(token); @@ -187,7 +186,7 @@ class JwtAuthenticationFilterTest { when(jwtService.isJwtEnabled()).thenReturn(true); when(request.getRequestURI()).thenReturn("/protected"); - when(request.getMethod()).thenReturn("GET"); + when(request.getContextPath()).thenReturn("/"); when(jwtService.extractTokenFromRequest(request)).thenReturn(token); doNothing().when(jwtService).validateToken(token); when(jwtService.extractAllClaims(token)).thenReturn(claims); @@ -205,96 +204,11 @@ class JwtAuthenticationFilterTest { } } - @Test - void shouldNotFilterLoginPost() { - when(request.getRequestURI()).thenReturn("/login"); - when(request.getMethod()).thenReturn("POST"); - - assertTrue(jwtAuthenticationFilter.shouldNotFilter(request)); - } - - @Test - void shouldNotFilterLoginGet() { - when(request.getRequestURI()).thenReturn("/login"); - when(request.getMethod()).thenReturn("GET"); - - assertTrue(jwtAuthenticationFilter.shouldNotFilter(request)); - } - - @Test - void shouldNotFilterPublicPaths() { - String[] publicPaths = { - "/register", - "/error", - "/images/logo.png", - "/public/file.txt", - "/css/style.css", - "/fonts/font.ttf", - "/js/script.js", - "/pdfjs/viewer.js", - "/pdfjs-legacy/viewer.js", - "/api/v1/info/status", - "/site.webmanifest", - "/favicon.ico" - }; - - for (String path : publicPaths) { - when(request.getRequestURI()).thenReturn(path); - when(request.getMethod()).thenReturn("GET"); - - assertTrue(jwtAuthenticationFilter.shouldNotFilter(request), - "Should not filter path: " + path); - } - } - - @Test - void shouldNotFilterStaticFiles() { - String[] staticFiles = { - "/some/path/file.svg", - "/another/path/image.png", - "/path/to/icon.ico" - }; - - for (String file : staticFiles) { - when(request.getRequestURI()).thenReturn(file); - when(request.getMethod()).thenReturn("GET"); - - assertTrue(jwtAuthenticationFilter.shouldNotFilter(request), - "Should not filter file: " + file); - } - } - - @Test - void shouldFilterProtectedPaths() { - String[] protectedPaths = { - "/protected", - "/api/v1/user/profile", - "/admin", - "/dashboard" - }; - - for (String path : protectedPaths) { - when(request.getRequestURI()).thenReturn(path); - when(request.getMethod()).thenReturn("GET"); - - assertFalse(jwtAuthenticationFilter.shouldNotFilter(request), - "Should filter path: " + path); - } - } - - @Test - void shouldFilterRootPath() { - when(request.getRequestURI()).thenReturn("/"); - when(request.getMethod()).thenReturn("GET"); - - assertFalse(jwtAuthenticationFilter.shouldNotFilter(request)); - } - @Test void testAuthenticationEntryPointCalledWithCorrectException() throws ServletException, IOException { when(jwtService.isJwtEnabled()).thenReturn(true); when(request.getRequestURI()).thenReturn("/protected"); - when(request.getMethod()).thenReturn("GET"); + when(request.getContextPath()).thenReturn("/"); when(jwtService.extractTokenFromRequest(request)).thenReturn(null); jwtAuthenticationFilter.doFilterInternal(request, response, filterChain);