wip - /view-pdf not loading properly

This commit is contained in:
DarioGii 2025-07-25 19:28:14 +01:00
parent 6a7328ff3d
commit 017b737b72
7 changed files with 71 additions and 22 deletions

View File

@ -9,7 +9,9 @@
"Bash(find:*)", "Bash(find:*)",
"Bash(grep:*)", "Bash(grep:*)",
"Bash(rg:*)", "Bash(rg:*)",
"Bash(strings:*)" "Bash(strings:*)",
"Bash(pkill:*)",
"Bash(true)"
], ],
"deny": [] "deny": []
} }

View File

@ -31,7 +31,7 @@ security:
google: google:
clientId: '' # client ID for Google OAuth2 clientId: '' # client ID for Google OAuth2
clientSecret: '' # client secret for Google OAuth2 clientSecret: '' # client secret for Google OAuth2
scopes: email, profile # scopes for Google OAuth2 scopes: https://www.googleapis.com/auth/userinfo.email, https://www.googleapis.com/auth/userinfo.profile # scopes for Google OAuth2
useAsUsername: email # field to use as the username for Google OAuth2. Available options are: [email | name | given_name | family_name] useAsUsername: email # field to use as the username for Google OAuth2. Available options are: [email | name | given_name | family_name]
github: github:
clientId: '' # client ID for GitHub OAuth2 clientId: '' # client ID for GitHub OAuth2
@ -51,20 +51,20 @@ security:
provider: '' # The name of your Provider provider: '' # The name of your Provider
autoCreateUser: true # set to 'true' to allow auto-creation of non-existing users 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 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 registrationId: stirlingpdf-dario-saml # 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 idpMetadataUri: https://authentik.dev.stirlingpdf.com/api/v3/providers/saml/5/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 idpSingleLoginUrl: https://authentik.dev.stirlingpdf.com/application/saml/stirlingpdf-dario-saml/sso/binding/post/ # 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 idpSingleLogoutUrl: https://authentik.dev.stirlingpdf.com/application/saml/stirlingpdf-dario-saml/slo/binding/post/ # The URL for initiating SLO. Provided by your Provider
idpIssuer: '' # The ID of your Provider idpIssuer: authentik # 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 idpCert: classpath:authentik-Self-signed_Certificate_certificate.pem # 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 privateKey: classpath:private_key.key # Your private key. Generated from your keypair
spCert: classpath:saml-public-cert.crt # Your signing certificate. Generated from your keypair spCert: classpath:certificate.crt # Your signing certificate. Generated from your keypair
jwt: jwt:
enableKeyStore: true # Set to 'true' to enable JWT key store enableKeyStore: true # Set to 'true' to enable JWT key store
enableKeyRotation: true # Set to 'true' to enable JWT key rotation enableKeyRotation: true # Set to 'true' to enable JWT key rotation
premium: premium:
key: 00000000-0000-0000-0000-000000000000 key: 3R3T-WFPY-UNRW-LJFA-MMXM-YVJK-WCKY-PCRT # fixme: remove
enabled: false # Enable license key checks for pro/enterprise features enabled: false # Enable license key checks for pro/enterprise features
proFeatures: proFeatures:
SSOAutoLogin: false SSOAutoLogin: false

View File

@ -135,7 +135,7 @@ public class SecurityConfiguration {
boolean v2Enabled = appConfig.v2Enabled(); boolean v2Enabled = appConfig.v2Enabled();
if (v2Enabled) { if (v2Enabled) {
http.addFilterAt( http.addFilterBefore(
jwtAuthenticationFilter(), jwtAuthenticationFilter(),
UsernamePasswordAuthenticationFilter.class) UsernamePasswordAuthenticationFilter.class)
.exceptionHandling( .exceptionHandling(
@ -145,8 +145,8 @@ public class SecurityConfiguration {
} }
http.addFilterBefore( http.addFilterBefore(
userAuthenticationFilter, UsernamePasswordAuthenticationFilter.class) userAuthenticationFilter, UsernamePasswordAuthenticationFilter.class)
.addFilterAfter(rateLimitingFilter(), userAuthenticationFilter.getClass()) .addFilterAfter(rateLimitingFilter(), UserAuthenticationFilter.class)
.addFilterAfter(firstLoginFilter, rateLimitingFilter().getClass()); .addFilterAfter(firstLoginFilter, UsernamePasswordAuthenticationFilter.class);
if (!securityProperties.getCsrfDisabled()) { if (!securityProperties.getCsrfDisabled()) {
CookieCsrfTokenRepository cookieRepo = CookieCsrfTokenRepository cookieRepo =

View File

@ -88,6 +88,8 @@ public class JwtAuthenticationFilter extends OncePerRequestFilter {
try { try {
jwtService.validateToken(jwtToken); jwtService.validateToken(jwtToken);
} catch (AuthenticationFailureException e) { } catch (AuthenticationFailureException e) {
// Clear invalid tokens from response
jwtService.clearTokenFromResponse(response);
handleAuthenticationFailure(request, response, e); handleAuthenticationFailure(request, response, e);
return; return;
} }
@ -129,7 +131,9 @@ public class JwtAuthenticationFilter extends OncePerRequestFilter {
authToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); authToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(authToken); SecurityContextHolder.getContext().setAuthentication(authToken);
log.debug("JWT authentication successful for user: {}", username); log.info(
"JWT authentication successful for user: {} - Authentication set in SecurityContext",
username);
} else { } else {
throw new UsernameNotFoundException("User not found: " + username); throw new UsernameNotFoundException("User not found: " + username);

View File

@ -63,7 +63,15 @@ public class UserAuthenticationFilter extends OncePerRequestFilter {
return; return;
} }
String requestURI = request.getRequestURI(); String requestURI = request.getRequestURI();
Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
log.info(
"UserAuthenticationFilter - Authentication from SecurityContext: {}",
authentication != null
? authentication.getClass().getSimpleName()
+ " for "
+ authentication.getName()
: "null");
// Check for session expiration (unsure if needed) // Check for session expiration (unsure if needed)
// if (authentication != null && authentication.isAuthenticated()) { // if (authentication != null && authentication.isAuthenticated()) {
@ -218,4 +226,37 @@ public class UserAuthenticationFilter extends OncePerRequestFilter {
return method; return method;
} }
} }
@Override
protected boolean shouldNotFilter(HttpServletRequest request) {
String uri = request.getRequestURI();
String contextPath = request.getContextPath();
String[] permitAllPatterns = {
contextPath + "/login",
contextPath + "/signup",
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(".mjs")
|| uri.endsWith(".png")
|| uri.endsWith(".ico")) {
return true;
}
}
return false;
}
} }

View File

@ -37,7 +37,7 @@ public class JwtKeystoreService implements JwtKeystoreServiceInterface {
public static final String KEY_SUFFIX = ".key"; public static final String KEY_SUFFIX = ".key";
private final JwtSigningKeyRepository repository; private final JwtSigningKeyRepository repository;
private final ApplicationProperties.Security.Jwt jwtConfig; private final ApplicationProperties.Security.Jwt jwtProperties;
private final Path privateKeyDirectory; private final Path privateKeyDirectory;
private volatile KeyPair currentKeyPair; private volatile KeyPair currentKeyPair;
@ -47,7 +47,7 @@ public class JwtKeystoreService implements JwtKeystoreServiceInterface {
public JwtKeystoreService( public JwtKeystoreService(
JwtSigningKeyRepository repository, ApplicationProperties applicationProperties) { JwtSigningKeyRepository repository, ApplicationProperties applicationProperties) {
this.repository = repository; this.repository = repository;
this.jwtConfig = applicationProperties.getSecurity().getJwt(); this.jwtProperties = applicationProperties.getSecurity().getJwt();
this.privateKeyDirectory = Paths.get(InstallationPathConfig.getConfigPath(), "jwt-keys"); this.privateKeyDirectory = Paths.get(InstallationPathConfig.getConfigPath(), "jwt-keys");
} }
@ -128,7 +128,7 @@ public class JwtKeystoreService implements JwtKeystoreServiceInterface {
@Override @Override
public boolean isKeystoreEnabled() { public boolean isKeystoreEnabled() {
return jwtConfig.isEnableKeystore(); return jwtProperties.isEnableKeystore();
} }
private void loadOrGenerateKeypair() { private void loadOrGenerateKeypair() {

View File

@ -82,7 +82,6 @@ public class JwtService implements JwtServiceInterface {
.expiration(new Date(System.currentTimeMillis() + EXPIRATION)) .expiration(new Date(System.currentTimeMillis() + EXPIRATION))
.signWith(keyPair.getPrivate(), Jwts.SIG.RS256); .signWith(keyPair.getPrivate(), Jwts.SIG.RS256);
// Add key ID to header if keystore is enabled
String keyId = keystoreService.getActiveKeyId(); String keyId = keystoreService.getActiveKeyId();
if (keyId != null) { if (keyId != null) {
builder.header().keyId(keyId); builder.header().keyId(keyId);
@ -136,8 +135,11 @@ public class JwtService implements JwtServiceInterface {
if (specificKeyPair.isPresent()) { if (specificKeyPair.isPresent()) {
keyPair = specificKeyPair.get(); keyPair = specificKeyPair.get();
} else { } else {
log.warn("Key ID {} not found in keystore, using active keypair", keyId); log.warn(
keyPair = keystoreService.getActiveKeypair(); "Key ID {} not found in keystore, token may have been signed with a rotated key",
keyId);
throw new AuthenticationFailureException(
"JWT token signed with unknown key ID: " + keyId);
} }
} else { } else {
keyPair = keystoreService.getActiveKeypair(); keyPair = keystoreService.getActiveKeypair();
@ -233,7 +235,7 @@ public class JwtService implements JwtServiceInterface {
.getHeader() .getHeader()
.get("kid"); .get("kid");
} catch (Exception e) { } catch (Exception e) {
// Token might not have a key ID or be malformed log.debug("Failed to extract key ID from token header: {}", e.getMessage());
return null; return null;
} }
} }