Improve Type-Safe Casting with Pattern Matching (#2990)

# Description of Changes

Please provide a summary of the changes, including:

This PR refactors multiple instances of type casting throughout the
codebase by replacing them with Java's pattern matching for
`instanceof`. This approach eliminates redundant type casting, improves
code readability, and reduces the chances of `ClassCastException`. The
changes primarily affect authentication handling, PDF processing, and
certificate validation.

### Key Changes:
- Replaced traditional `instanceof` checks followed by explicit casting
with pattern matching.
- Improved readability and maintainability of type-related operations.
- Applied changes across security modules, PDF utilities, and image
processing functions.

This refactor does not introduce new functionality but enhances the
robustness and clarity of the existing code.

pending until #2818 is published

---

## Checklist

### General

- [ ] I have read the [Contribution
Guidelines](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/CONTRIBUTING.md)
- [ ] I have read the [Stirling-PDF Developer
Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md)
(if applicable)
- [ ] I have read the [How to add new languages to
Stirling-PDF](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/HowToAddNewLanguage.md)
(if applicable)
- [ ] I have performed a self-review of my own code
- [ ] My changes generate no new warnings

### Documentation

- [ ] I have updated relevant docs on [Stirling-PDF's doc
repo](https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/)
(if functionality has heavily changed)
- [ ] I have read the section [Add New Translation
Tags](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/HowToAddNewLanguage.md#add-new-translation-tags)
(for new translation tags only)

### UI Changes (if applicable)

- [ ] Screenshots or videos demonstrating the UI changes are attached
(e.g., as comments or direct attachments in the PR)

### Testing (if applicable)

- [ ] I have tested my changes locally. Refer to the [Testing
Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md#6-testing)
for more details.
This commit is contained in:
Ludy
2025-02-25 22:31:50 +01:00
committed by GitHub
parent a1f7bb3e4a
commit 2ab951e080
27 changed files with 175 additions and 165 deletions

View File

@@ -45,12 +45,12 @@ public class CustomLogoutSuccessHandler extends SimpleUrlLogoutSuccessHandler {
throws IOException {
if (!response.isCommitted()) {
if (authentication != null) {
if (authentication instanceof Saml2Authentication) {
if (authentication instanceof Saml2Authentication samlAuthentication) {
// Handle SAML2 logout redirection
getRedirect_saml2(request, response, authentication);
} else if (authentication instanceof OAuth2AuthenticationToken) {
getRedirect_saml2(request, response, samlAuthentication);
} else if (authentication instanceof OAuth2AuthenticationToken oAuthToken) {
// Handle OAuth2 logout redirection
getRedirect_oauth2(request, response, authentication);
getRedirect_oauth2(request, response, oAuthToken);
} else if (authentication instanceof UsernamePasswordAuthenticationToken) {
// Handle Username/Password logout
getRedirectStrategy().sendRedirect(request, response, LOGOUT_PATH);
@@ -71,13 +71,14 @@ public class CustomLogoutSuccessHandler extends SimpleUrlLogoutSuccessHandler {
// Redirect for SAML2 authentication logout
private void getRedirect_saml2(
HttpServletRequest request, HttpServletResponse response, Authentication authentication)
HttpServletRequest request,
HttpServletResponse response,
Saml2Authentication samlAuthentication)
throws IOException {
SAML2 samlConf = applicationProperties.getSecurity().getSaml2();
String registrationId = samlConf.getRegistrationId();
Saml2Authentication samlAuthentication = (Saml2Authentication) authentication;
CustomSaml2AuthenticatedPrincipal principal =
(CustomSaml2AuthenticatedPrincipal) samlAuthentication.getPrincipal();
@@ -115,32 +116,46 @@ public class CustomLogoutSuccessHandler extends SimpleUrlLogoutSuccessHandler {
// Redirect for OAuth2 authentication logout
private void getRedirect_oauth2(
HttpServletRequest request, HttpServletResponse response, Authentication authentication)
HttpServletRequest request,
HttpServletResponse response,
OAuth2AuthenticationToken oAuthToken)
throws IOException {
String registrationId;
OAUTH2 oauth = applicationProperties.getSecurity().getOauth2();
String path = checkForErrors(request);
if (authentication instanceof OAuth2AuthenticationToken oauthToken) {
registrationId = oauthToken.getAuthorizedClientRegistrationId();
} else {
registrationId = oauth.getProvider() != null ? oauth.getProvider() : "";
}
String redirectUrl = UrlUtils.getOrigin(request) + "/login?" + path;
registrationId = oAuthToken.getAuthorizedClientRegistrationId();
// Redirect based on OAuth2 provider
switch (registrationId.toLowerCase()) {
case "keycloak" -> {
KeycloakProvider keycloak = oauth.getClient().getKeycloak();
String logoutUrl =
keycloak.getIssuer()
+ "/protocol/openid-connect/logout"
+ "?client_id="
+ keycloak.getClientId()
+ "&post_logout_redirect_uri="
+ response.encodeRedirectURL(redirectUrl);
log.info("Redirecting to Keycloak logout URL: {}", logoutUrl);
boolean isKeycloak = !keycloak.getIssuer().isBlank();
boolean isCustomOAuth = !oauth.getIssuer().isBlank();
String logoutUrl = redirectUrl;
if (isKeycloak) {
logoutUrl = keycloak.getIssuer();
} else if (isCustomOAuth) {
logoutUrl = oauth.getIssuer();
}
if (isKeycloak || isCustomOAuth) {
logoutUrl +=
"/protocol/openid-connect/logout"
+ "?client_id="
+ oauth.getClientId()
+ "&post_logout_redirect_uri="
+ response.encodeRedirectURL(redirectUrl);
log.info("Redirecting to Keycloak logout URL: {}", logoutUrl);
} else {
log.info(
"No redirect URL for {} available. Redirecting to default logout URL: {}",
registrationId,
logoutUrl);
}
response.sendRedirect(logoutUrl);
}
case "github", "google" -> {

View File

@@ -25,8 +25,8 @@ public class IPRateLimitingFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
if (request instanceof HttpServletRequest) {
HttpServletRequest httpRequest = (HttpServletRequest) request;
if (request instanceof HttpServletRequest httpServletRequest) {
HttpServletRequest httpRequest = httpServletRequest;
String method = httpRequest.getMethod();
String requestURI = httpRequest.getRequestURI();
// Check if the request is for static resources

View File

@@ -123,9 +123,11 @@ public class UserAuthenticationFilter extends OncePerRequestFilter {
response.setStatus(HttpStatus.UNAUTHORIZED.value());
response.getWriter()
.write(
"Authentication required. Please provide a X-API-KEY in request header.\n"
"Authentication required. Please provide a X-API-KEY in request"
+ " header.\n"
+ "This is found in Settings -> Account Settings -> API Key\n"
+ "Alternatively you can disable authentication if this is unexpected");
+ "Alternatively you can disable authentication if this is"
+ " unexpected");
return;
}
}
@@ -141,21 +143,21 @@ public class UserAuthenticationFilter extends OncePerRequestFilter {
// Extract username and determine the login method
Object principal = authentication.getPrincipal();
String username = null;
if (principal instanceof UserDetails) {
username = ((UserDetails) principal).getUsername();
if (principal instanceof UserDetails detailsUser) {
username = detailsUser.getUsername();
loginMethod = LoginMethod.USERDETAILS;
} else if (principal instanceof OAuth2User) {
username = ((OAuth2User) principal).getName();
} else if (principal instanceof OAuth2User oAuth2User) {
username = oAuth2User.getName();
loginMethod = LoginMethod.OAUTH2USER;
OAUTH2 oAuth = securityProp.getOauth2();
blockRegistration = oAuth != null && oAuth.getBlockRegistration();
} else if (principal instanceof CustomSaml2AuthenticatedPrincipal) {
username = ((CustomSaml2AuthenticatedPrincipal) principal).name();
} else if (principal instanceof CustomSaml2AuthenticatedPrincipal saml2User) {
username = saml2User.name();
loginMethod = LoginMethod.SAML2USER;
SAML2 saml2 = securityProp.getSaml2();
blockRegistration = saml2 != null && saml2.getBlockRegistration();
} else if (principal instanceof String) {
username = (String) principal;
} else if (principal instanceof String stringUser) {
username = stringUser;
loginMethod = LoginMethod.STRINGUSER;
}
@@ -170,8 +172,8 @@ public class UserAuthenticationFilter extends OncePerRequestFilter {
boolean isUserDisabled = userService.isUserDisabled(username);
boolean notSsoLogin =
!loginMethod.equals(LoginMethod.OAUTH2USER)
&& !loginMethod.equals(LoginMethod.SAML2USER);
!LoginMethod.OAUTH2USER.equals(loginMethod)
&& !LoginMethod.SAML2USER.equals(loginMethod);
// Block user registration if not allowed by configuration
if (blockRegistration && !isUserExists) {

View File

@@ -385,14 +385,14 @@ public class UserService implements UserServiceInterface {
for (Object principal : sessionRegistry.getAllPrincipals()) {
for (SessionInformation sessionsInformation :
sessionRegistry.getAllSessions(principal, false)) {
if (principal instanceof UserDetails userDetails) {
usernameP = userDetails.getUsername();
if (principal instanceof UserDetails detailsUser) {
usernameP = detailsUser.getUsername();
} else if (principal instanceof OAuth2User oAuth2User) {
usernameP = oAuth2User.getName();
} else if (principal instanceof CustomSaml2AuthenticatedPrincipal saml2User) {
usernameP = saml2User.name();
} else if (principal instanceof String) {
usernameP = (String) principal;
} else if (principal instanceof String stringUser) {
usernameP = stringUser;
}
if (usernameP.equalsIgnoreCase(username)) {
sessionRegistry.expireSession(sessionsInformation.getSessionId());
@@ -404,17 +404,17 @@ public class UserService implements UserServiceInterface {
public String getCurrentUsername() {
Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
if (principal instanceof UserDetails) {
return ((UserDetails) principal).getUsername();
} else if (principal instanceof OAuth2User) {
return ((OAuth2User) principal)
.getAttribute(
applicationProperties.getSecurity().getOauth2().getUseAsUsername());
} else if (principal instanceof CustomSaml2AuthenticatedPrincipal) {
return ((CustomSaml2AuthenticatedPrincipal) principal).name();
} else {
return principal.toString();
if (principal instanceof UserDetails detailsUser) {
return detailsUser.getUsername();
} else if (principal instanceof OAuth2User oAuth2User) {
return oAuth2User.getAttribute(
applicationProperties.getSecurity().getOauth2().getUseAsUsername());
} else if (principal instanceof CustomSaml2AuthenticatedPrincipal saml2User) {
return saml2User.name();
} else if (principal instanceof String stringUser) {
return stringUser;
}
return null;
}
@Transactional

View File

@@ -42,12 +42,12 @@ public class CustomOAuth2AuthenticationFailureHandler
getRedirectStrategy().sendRedirect(request, response, "/logout?error=locked");
return;
}
if (exception instanceof OAuth2AuthenticationException) {
OAuth2Error error = ((OAuth2AuthenticationException) exception).getError();
if (exception instanceof OAuth2AuthenticationException oAuth2Exception) {
OAuth2Error error = oAuth2Exception.getError();
String errorCode = error.getErrorCode();
if (error.getErrorCode().equals("Password must not be null")) {
if ("Password must not be null".equals(error.getErrorCode())) {
errorCode = "userAlreadyExistsWeb";
}

View File

@@ -47,10 +47,10 @@ public class CustomOAuth2AuthenticationSuccessHandler
Object principal = authentication.getPrincipal();
String username = "";
if (principal instanceof OAuth2User oauthUser) {
username = oauthUser.getName();
} else if (principal instanceof UserDetails oauthUser) {
username = oauthUser.getUsername();
if (principal instanceof OAuth2User oAuth2User) {
username = oAuth2User.getName();
} else if (principal instanceof UserDetails detailsUser) {
username = detailsUser.getUsername();
}
// Get the saved request

View File

@@ -230,7 +230,7 @@ public class OAuth2Configuration {
// Add existing OAUTH2 Authorities
mappedAuthorities.add(new SimpleGrantedAuthority(authority.getAuthority()));
// Add Authorities from database for existing user, if user is present.
if (authority instanceof OAuth2UserAuthority oauth2Auth) {
if (authority instanceof OAuth2UserAuthority oAuth2Auth) {
String useAsUsername =
applicationProperties
.getSecurity()
@@ -238,7 +238,7 @@ public class OAuth2Configuration {
.getUseAsUsername();
Optional<User> userOpt =
userService.findByUsernameIgnoreCase(
(String) oauth2Auth.getAttributes().get(useAsUsername));
(String) oAuth2Auth.getAttributes().get(useAsUsername));
if (userOpt.isPresent()) {
User user = userOpt.get();
mappedAuthorities.add(

View File

@@ -40,13 +40,12 @@ public class CertificateUtils {
Object object = pemParser.readObject();
JcaPEMKeyConverter converter = new JcaPEMKeyConverter();
if (object instanceof PEMKeyPair) {
if (object instanceof PEMKeyPair keypair) {
// Handle traditional RSA private key format
PEMKeyPair keypair = (PEMKeyPair) object;
return (RSAPrivateKey) converter.getPrivateKey(keypair.getPrivateKeyInfo());
} else if (object instanceof PrivateKeyInfo) {
} else if (object instanceof PrivateKeyInfo keyInfo) {
// Handle PKCS#8 format
return (RSAPrivateKey) converter.getPrivateKey((PrivateKeyInfo) object);
return (RSAPrivateKey) converter.getPrivateKey(keyInfo);
} else {
throw new IllegalArgumentException(
"Unsupported key format: "

View File

@@ -8,7 +8,11 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticatedPrincipal;
@ConditionalOnProperty(name = "security.saml2.enabled", havingValue = "true")
public record CustomSaml2AuthenticatedPrincipal(String name, Map<String, List<Object>> attributes, String nameId, List<String> sessionIndexes)
public record CustomSaml2AuthenticatedPrincipal(
String name,
Map<String, List<Object>> attributes,
String nameId,
List<String> sessionIndexes)
implements Saml2AuthenticatedPrincipal, Serializable {
@Override
@@ -20,5 +24,4 @@ public record CustomSaml2AuthenticatedPrincipal(String name, Map<String, List<Ob
public Map<String, List<Object>> getAttributes() {
return this.attributes;
}
}

View File

@@ -41,8 +41,8 @@ public class CustomSaml2AuthenticationSuccessHandler
Object principal = authentication.getPrincipal();
log.debug("Starting SAML2 authentication success handling");
if (principal instanceof CustomSaml2AuthenticatedPrincipal) {
String username = ((CustomSaml2AuthenticatedPrincipal) principal).name();
if (principal instanceof CustomSaml2AuthenticatedPrincipal saml2Principal) {
String username = saml2Principal.name();
log.debug("Authenticated principal found for user: {}", username);
HttpSession session = request.getSession(false);

View File

@@ -43,14 +43,14 @@ public class SessionPersistentRegistry implements SessionRegistry {
List<SessionInformation> sessionInformations = new ArrayList<>();
String principalName = null;
if (principal instanceof UserDetails) {
principalName = ((UserDetails) principal).getUsername();
} else if (principal instanceof OAuth2User) {
principalName = ((OAuth2User) principal).getName();
} else if (principal instanceof CustomSaml2AuthenticatedPrincipal) {
principalName = ((CustomSaml2AuthenticatedPrincipal) principal).name();
} else if (principal instanceof String) {
principalName = (String) principal;
if (principal instanceof UserDetails detailsUser) {
principalName = detailsUser.getUsername();
} else if (principal instanceof OAuth2User oAuth2User) {
principalName = oAuth2User.getName();
} else if (principal instanceof CustomSaml2AuthenticatedPrincipal saml2User) {
principalName = saml2User.name();
} else if (principal instanceof String stringUser) {
principalName = stringUser;
}
if (principalName != null) {
@@ -74,14 +74,14 @@ public class SessionPersistentRegistry implements SessionRegistry {
public void registerNewSession(String sessionId, Object principal) {
String principalName = null;
if (principal instanceof UserDetails) {
principalName = ((UserDetails) principal).getUsername();
} else if (principal instanceof OAuth2User) {
principalName = ((OAuth2User) principal).getName();
} else if (principal instanceof CustomSaml2AuthenticatedPrincipal) {
principalName = ((CustomSaml2AuthenticatedPrincipal) principal).name();
} else if (principal instanceof String) {
principalName = (String) principal;
if (principal instanceof UserDetails detailsUser) {
principalName = detailsUser.getUsername();
} else if (principal instanceof OAuth2User oAuth2User) {
principalName = oAuth2User.getName();
} else if (principal instanceof CustomSaml2AuthenticatedPrincipal saml2User) {
principalName = saml2User.name();
} else if (principal instanceof String stringUser) {
principalName = stringUser;
}
if (principalName != null) {