mirror of
https://github.com/Stirling-Tools/Stirling-PDF.git
synced 2026-09-02 21:03:34 +03:00
refactor(api): standardize syntax and simplify type declarations across security, workflow, and controller modules (#7127)
Co-authored-by: Anthony Stirling <77850077+Frooodle@users.noreply.github.com>
This commit is contained in:
co-authored by
Anthony Stirling
parent
0b7b4e02c2
commit
34694c6f5e
@@ -237,7 +237,7 @@ public class TabulaTableParser implements TableParser {
|
||||
score -= 0.3f;
|
||||
}
|
||||
|
||||
return Math.max(0f, Math.min(1f, score));
|
||||
return Math.clamp(score, 0f, 1f);
|
||||
}
|
||||
|
||||
private Bounds tableBounds(Table table) {
|
||||
|
||||
+2
-1
@@ -15,7 +15,8 @@ public class StringToMapPropertyEditor extends PropertyEditorSupport {
|
||||
@Override
|
||||
public void setAsText(String text) throws IllegalArgumentException {
|
||||
try {
|
||||
TypeReference<HashMap<String, String>> typeRef = new TypeReference<>() {};
|
||||
TypeReference<HashMap<String, String>> typeRef =
|
||||
new TypeReference<HashMap<String, String>>() {};
|
||||
Map<String, String> map = objectMapper.readValue(text, typeRef);
|
||||
setValue(map);
|
||||
} catch (Exception e) {
|
||||
|
||||
+1
-1
@@ -237,7 +237,7 @@ public class EditTextController {
|
||||
|
||||
Matcher matcher = edit.pattern().matcher(joined);
|
||||
List<MatchSpan> spans = new ArrayList<>();
|
||||
StringBuffer interpolation = new StringBuffer();
|
||||
StringBuilder interpolation = new StringBuilder();
|
||||
int previousAppendPosition = 0;
|
||||
while (matcher.find()) {
|
||||
if (matcher.start() == matcher.end()) {
|
||||
|
||||
@@ -95,7 +95,8 @@ public class UIDataController {
|
||||
|
||||
try (InputStream is = resource.getInputStream()) {
|
||||
Map<String, List<Dependency>> licenseData =
|
||||
objectMapper.readValue(is, new TypeReference<>() {});
|
||||
objectMapper.readValue(
|
||||
is, new TypeReference<Map<String, List<Dependency>>>() {});
|
||||
data.setDependencies(licenseData.get("dependencies"));
|
||||
} catch (IOException e) {
|
||||
log.error("Failed to load licenses data", e);
|
||||
|
||||
+6
-3
@@ -25,12 +25,15 @@ final class FormPayloadParser {
|
||||
private static final String KEY_VALUE = "value";
|
||||
private static final String KEY_DEFAULT_VALUE = "defaultValue";
|
||||
|
||||
private static final TypeReference<Map<String, Object>> MAP_TYPE = new TypeReference<>() {};
|
||||
private static final TypeReference<Map<String, Object>> MAP_TYPE =
|
||||
new TypeReference<Map<String, Object>>() {};
|
||||
private static final TypeReference<List<FormUtils.ModifyFormFieldDefinition>>
|
||||
MODIFY_FIELD_LIST_TYPE = new TypeReference<>() {};
|
||||
MODIFY_FIELD_LIST_TYPE =
|
||||
new TypeReference<List<FormUtils.ModifyFormFieldDefinition>>() {};
|
||||
private static final TypeReference<List<FormUtils.NewFormFieldDefinition>> NEW_FIELD_LIST_TYPE =
|
||||
new TypeReference<>() {};
|
||||
private static final TypeReference<List<String>> STRING_LIST_TYPE = new TypeReference<>() {};
|
||||
private static final TypeReference<List<String>> STRING_LIST_TYPE =
|
||||
new TypeReference<List<String>>() {};
|
||||
|
||||
private FormPayloadParser() {}
|
||||
|
||||
|
||||
+3
-1
@@ -96,7 +96,9 @@ public class AddCommentsController {
|
||||
|
||||
List<CommentSpecDto> dtos;
|
||||
try {
|
||||
dtos = objectMapper.readValue(commentsJson, new TypeReference<>() {});
|
||||
dtos =
|
||||
objectMapper.readValue(
|
||||
commentsJson, new TypeReference<List<CommentSpecDto>>() {});
|
||||
} catch (JacksonException e) {
|
||||
throw new ResponseStatusException(
|
||||
HttpStatus.BAD_REQUEST, "comments must be a JSON array of CommentSpec objects");
|
||||
|
||||
@@ -59,7 +59,7 @@ public enum AuditLevel {
|
||||
*/
|
||||
public static AuditLevel fromInt(int level) {
|
||||
// Ensure level is within valid bounds
|
||||
int boundedLevel = Math.min(Math.max(level, 0), 3);
|
||||
int boundedLevel = Math.clamp(level, 0, 3);
|
||||
|
||||
for (AuditLevel auditLevel : values()) {
|
||||
if (auditLevel.level == boundedLevel) {
|
||||
|
||||
+4
-2
@@ -44,8 +44,10 @@ public class ValkeyJobStore implements JobStore {
|
||||
private static final String FILE_INDEX_PREFIX = "stirling:file2job:";
|
||||
|
||||
private static final ObjectMapper MAPPER = new ObjectMapper();
|
||||
private static final TypeReference<List<String>> LIST_STRING = new TypeReference<>() {};
|
||||
private static final TypeReference<Map<String, String>> MAP_STRING = new TypeReference<>() {};
|
||||
private static final TypeReference<List<String>> LIST_STRING =
|
||||
new TypeReference<List<String>>() {};
|
||||
private static final TypeReference<Map<String, String>> MAP_STRING =
|
||||
new TypeReference<Map<String, String>>() {};
|
||||
|
||||
private final StringRedisTemplate template;
|
||||
|
||||
|
||||
+1
-1
@@ -35,7 +35,7 @@ public class AuditConfigurationProperties {
|
||||
|
||||
// Ensure level is within valid bounds (0-3)
|
||||
int configLevel = auditConfig.getLevel();
|
||||
this.level = Math.min(Math.max(configLevel, 0), 3);
|
||||
this.level = Math.clamp(configLevel, 0, 3);
|
||||
|
||||
// Retention days (0 means infinite)
|
||||
this.retentionDays = auditConfig.getRetentionDays();
|
||||
|
||||
+1
-1
@@ -48,7 +48,7 @@ public class UsageRestController {
|
||||
@RequestParam(value = "dataType", defaultValue = "all") String dataType,
|
||||
@RequestParam(value = "days", defaultValue = "30") Integer days) {
|
||||
|
||||
int lookbackDays = Math.max(1, Math.min(days, 365));
|
||||
int lookbackDays = Math.clamp(days, 1, 365);
|
||||
|
||||
// Get audit events filtered by type
|
||||
List<PersistentAuditEvent> events = getEventsByDataType(dataType, lookbackDays);
|
||||
|
||||
+2
-1
@@ -1,5 +1,6 @@
|
||||
package stirling.software.proprietary.model;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
@@ -19,7 +20,7 @@ import lombok.*;
|
||||
@ToString
|
||||
public class UserLicenseSettings implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
@Serial private static final long serialVersionUID = 1L;
|
||||
|
||||
public static final Long SINGLETON_ID = 1L;
|
||||
|
||||
|
||||
+17
-15
@@ -70,21 +70,23 @@ public class CustomLogoutSuccessHandler extends SimpleUrlLogoutSuccessHandler {
|
||||
|
||||
if (!response.isCommitted()) {
|
||||
if (authentication != null) {
|
||||
if (authentication instanceof Saml2Authentication samlAuthentication) {
|
||||
// Handle SAML2 logout redirection
|
||||
getRedirect_saml2(request, response, samlAuthentication);
|
||||
} else if (authentication instanceof OAuth2AuthenticationToken oAuthToken) {
|
||||
// Handle OAuth2 logout redirection
|
||||
getRedirect_oauth2(request, response, oAuthToken);
|
||||
} else if (authentication instanceof UsernamePasswordAuthenticationToken) {
|
||||
// Handle Username/Password logout
|
||||
getRedirectStrategy().sendRedirect(request, response, LOGOUT_PATH);
|
||||
} else {
|
||||
// Handle unknown authentication types
|
||||
log.error(
|
||||
"Authentication class unknown: {}",
|
||||
authentication.getClass().getSimpleName());
|
||||
getRedirectStrategy().sendRedirect(request, response, LOGOUT_PATH);
|
||||
switch (authentication) {
|
||||
case Saml2Authentication samlAuthentication ->
|
||||
// Handle SAML2 logout redirection
|
||||
getRedirect_saml2(request, response, samlAuthentication);
|
||||
case OAuth2AuthenticationToken oAuthToken ->
|
||||
// Handle OAuth2 logout redirection
|
||||
getRedirect_oauth2(request, response, oAuthToken);
|
||||
case UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken ->
|
||||
// Handle Username/Password logout
|
||||
getRedirectStrategy().sendRedirect(request, response, LOGOUT_PATH);
|
||||
default -> {
|
||||
// Handle unknown authentication types
|
||||
log.error(
|
||||
"Authentication class unknown: {}",
|
||||
authentication.getClass().getSimpleName());
|
||||
getRedirectStrategy().sendRedirect(request, response, LOGOUT_PATH);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (jwtService != null) {
|
||||
|
||||
+34
-34
@@ -392,40 +392,40 @@ public class SecurityConfiguration {
|
||||
// Handle OAUTH2 Logins
|
||||
if (securityProperties.isOauth2Active()) {
|
||||
http.oauth2Login(
|
||||
oauth2 -> {
|
||||
oauth2.loginPage("/login")
|
||||
.authorizationEndpoint(
|
||||
authorizationEndpoint -> {
|
||||
if (clientRegistrationRepository != null) {
|
||||
authorizationEndpoint
|
||||
.authorizationRequestResolver(
|
||||
new TauriAuthorizationRequestResolver(
|
||||
clientRegistrationRepository));
|
||||
}
|
||||
})
|
||||
.successHandler(
|
||||
new CustomOAuth2AuthenticationSuccessHandler(
|
||||
loginAttemptService,
|
||||
securityProperties.getOauth2(),
|
||||
userService,
|
||||
jwtService,
|
||||
licenseSettingsService,
|
||||
applicationProperties))
|
||||
.failureHandler(new CustomOAuth2AuthenticationFailureHandler())
|
||||
// Add existing Authorities from the database
|
||||
.userInfoEndpoint(
|
||||
userInfoEndpoint ->
|
||||
userInfoEndpoint
|
||||
.oidcUserService(
|
||||
new CustomOAuth2UserService(
|
||||
securityProperties
|
||||
.getOauth2(),
|
||||
userService,
|
||||
loginAttemptService))
|
||||
.userAuthoritiesMapper(
|
||||
oAuth2userAuthoritiesMapper))
|
||||
.permitAll();
|
||||
});
|
||||
oauth2 ->
|
||||
oauth2.loginPage("/login")
|
||||
.authorizationEndpoint(
|
||||
authorizationEndpoint -> {
|
||||
if (clientRegistrationRepository != null) {
|
||||
authorizationEndpoint
|
||||
.authorizationRequestResolver(
|
||||
new TauriAuthorizationRequestResolver(
|
||||
clientRegistrationRepository));
|
||||
}
|
||||
})
|
||||
.successHandler(
|
||||
new CustomOAuth2AuthenticationSuccessHandler(
|
||||
loginAttemptService,
|
||||
securityProperties.getOauth2(),
|
||||
userService,
|
||||
jwtService,
|
||||
licenseSettingsService,
|
||||
applicationProperties))
|
||||
.failureHandler(
|
||||
new CustomOAuth2AuthenticationFailureHandler())
|
||||
// Add existing Authorities from the database
|
||||
.userInfoEndpoint(
|
||||
userInfoEndpoint ->
|
||||
userInfoEndpoint
|
||||
.oidcUserService(
|
||||
new CustomOAuth2UserService(
|
||||
securityProperties
|
||||
.getOauth2(),
|
||||
userService,
|
||||
loginAttemptService))
|
||||
.userAuthoritiesMapper(
|
||||
oAuth2userAuthoritiesMapper))
|
||||
.permitAll());
|
||||
}
|
||||
// Handle SAML
|
||||
if (securityProperties.isSaml2Active() && runningProOrHigher) {
|
||||
|
||||
+12
-11
@@ -703,17 +703,18 @@ public class AuthController {
|
||||
}
|
||||
|
||||
private long extractEpochMillis(Object claimValue) {
|
||||
if (claimValue == null) {
|
||||
return -1L;
|
||||
}
|
||||
|
||||
if (claimValue instanceof java.util.Date date) {
|
||||
return date.getTime();
|
||||
}
|
||||
|
||||
if (claimValue instanceof Number number) {
|
||||
long epochSeconds = number.longValue();
|
||||
return epochSeconds * 1000L;
|
||||
switch (claimValue) {
|
||||
case null -> {
|
||||
return -1L;
|
||||
}
|
||||
case java.util.Date date -> {
|
||||
return date.getTime();
|
||||
}
|
||||
case Number number -> {
|
||||
long epochSeconds = number.longValue();
|
||||
return epochSeconds * 1000L;
|
||||
}
|
||||
default -> {}
|
||||
}
|
||||
|
||||
return -1L;
|
||||
|
||||
+8
-8
@@ -760,14 +760,14 @@ public class UserController {
|
||||
for (Object principal : principals) {
|
||||
List<SessionInformation> sessionsInformation =
|
||||
sessionRegistry.getAllSessions(principal, false);
|
||||
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 stringUser) {
|
||||
userNameP = stringUser;
|
||||
switch (principal) {
|
||||
case null -> {}
|
||||
case UserDetails detailsUser -> userNameP = detailsUser.getUsername();
|
||||
case OAuth2User oAuth2User -> userNameP = oAuth2User.getName();
|
||||
case CustomSaml2AuthenticatedPrincipal saml2User ->
|
||||
userNameP = saml2User.name();
|
||||
case String stringUser -> userNameP = stringUser;
|
||||
default -> {}
|
||||
}
|
||||
if (userNameP.equalsIgnoreCase(username)) {
|
||||
for (SessionInformation sessionInfo : sessionsInformation) {
|
||||
|
||||
+2
-1
@@ -1,5 +1,6 @@
|
||||
package stirling.software.proprietary.security.model;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
@@ -28,7 +29,7 @@ import lombok.Setter;
|
||||
@Setter
|
||||
public class Authority implements GrantedAuthority, Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
@Serial private static final long serialVersionUID = 1L;
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
|
||||
+2
-1
@@ -1,5 +1,6 @@
|
||||
package stirling.software.proprietary.security.model;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@@ -18,7 +19,7 @@ import lombok.Setter;
|
||||
@Setter
|
||||
public class InviteToken implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
@Serial private static final long serialVersionUID = 1L;
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
|
||||
+52
-47
@@ -36,57 +36,62 @@ public class CustomOAuth2AuthenticationFailureHandler
|
||||
AuthenticationException exception)
|
||||
throws IOException, ServletException {
|
||||
|
||||
if (exception instanceof BadCredentialsException) {
|
||||
log.error("BadCredentialsException", exception);
|
||||
getRedirectStrategy().sendRedirect(request, response, "/login?error=badCredentials");
|
||||
return;
|
||||
}
|
||||
if (exception instanceof DisabledException) {
|
||||
log.error("User is deactivated: ", exception);
|
||||
getRedirectStrategy().sendRedirect(request, response, "/logout?userIsDisabled=true");
|
||||
return;
|
||||
}
|
||||
if (exception instanceof LockedException) {
|
||||
log.error("Account locked: ", exception);
|
||||
getRedirectStrategy().sendRedirect(request, response, "/logout?error=locked");
|
||||
return;
|
||||
}
|
||||
if (exception instanceof OAuth2AuthenticationException oAuth2Exception) {
|
||||
OAuth2Error error = oAuth2Exception.getError();
|
||||
|
||||
String errorCode = error.getErrorCode();
|
||||
|
||||
if ("Password must not be null".equals(error.getErrorCode())) {
|
||||
errorCode = "userAlreadyExistsWeb";
|
||||
switch (exception) {
|
||||
case BadCredentialsException badCredentialsException -> {
|
||||
log.error("BadCredentialsException", exception);
|
||||
getRedirectStrategy()
|
||||
.sendRedirect(request, response, "/login?error=badCredentials");
|
||||
return;
|
||||
}
|
||||
case DisabledException disabledException -> {
|
||||
log.error("User is deactivated: ", exception);
|
||||
getRedirectStrategy()
|
||||
.sendRedirect(request, response, "/logout?userIsDisabled=true");
|
||||
return;
|
||||
}
|
||||
case LockedException lockedException -> {
|
||||
log.error("Account locked: ", exception);
|
||||
getRedirectStrategy().sendRedirect(request, response, "/logout?error=locked");
|
||||
return;
|
||||
}
|
||||
case OAuth2AuthenticationException oAuth2Exception -> {
|
||||
OAuth2Error error = oAuth2Exception.getError();
|
||||
|
||||
log.error(
|
||||
"OAuth2 Authentication error: {}",
|
||||
errorCode != null ? errorCode : exception.getMessage(),
|
||||
exception);
|
||||
String errorValue = errorCode != null ? errorCode : "oauth2AuthenticationError";
|
||||
clearRedirectCookie(response);
|
||||
boolean tauriState = TauriOAuthUtils.isTauriState(request);
|
||||
String redirectUrl;
|
||||
if (tauriState) {
|
||||
String basePath =
|
||||
TauriOAuthUtils.defaultTauriCallbackPath(request.getContextPath());
|
||||
redirectUrl = basePath;
|
||||
String stateParam = request.getParameter("state");
|
||||
if (stateParam != null && !stateParam.isBlank()) {
|
||||
redirectUrl = appendQueryParam(redirectUrl, "state", stateParam);
|
||||
// Extract and pass nonce for CSRF validation
|
||||
String nonce = TauriOAuthUtils.extractNonceFromState(stateParam);
|
||||
if (nonce != null) {
|
||||
redirectUrl = appendQueryParam(redirectUrl, "nonce", nonce);
|
||||
}
|
||||
String errorCode = error.getErrorCode();
|
||||
|
||||
if ("Password must not be null".equals(error.getErrorCode())) {
|
||||
errorCode = "userAlreadyExistsWeb";
|
||||
}
|
||||
redirectUrl = appendQueryParam(redirectUrl, "errorOAuth", errorValue);
|
||||
} else {
|
||||
redirectUrl = buildFailureRedirectUrl(request, errorValue);
|
||||
|
||||
log.error(
|
||||
"OAuth2 Authentication error: {}",
|
||||
errorCode != null ? errorCode : exception.getMessage(),
|
||||
exception);
|
||||
String errorValue = errorCode != null ? errorCode : "oauth2AuthenticationError";
|
||||
clearRedirectCookie(response);
|
||||
boolean tauriState = TauriOAuthUtils.isTauriState(request);
|
||||
String redirectUrl;
|
||||
if (tauriState) {
|
||||
String basePath =
|
||||
TauriOAuthUtils.defaultTauriCallbackPath(request.getContextPath());
|
||||
redirectUrl = basePath;
|
||||
String stateParam = request.getParameter("state");
|
||||
if (stateParam != null && !stateParam.isBlank()) {
|
||||
redirectUrl = appendQueryParam(redirectUrl, "state", stateParam);
|
||||
// Extract and pass nonce for CSRF validation
|
||||
String nonce = TauriOAuthUtils.extractNonceFromState(stateParam);
|
||||
if (nonce != null) {
|
||||
redirectUrl = appendQueryParam(redirectUrl, "nonce", nonce);
|
||||
}
|
||||
}
|
||||
redirectUrl = appendQueryParam(redirectUrl, "errorOAuth", errorValue);
|
||||
} else {
|
||||
redirectUrl = buildFailureRedirectUrl(request, errorValue);
|
||||
}
|
||||
getRedirectStrategy().sendRedirect(request, response, redirectUrl);
|
||||
return;
|
||||
}
|
||||
getRedirectStrategy().sendRedirect(request, response, redirectUrl);
|
||||
return;
|
||||
default -> {}
|
||||
}
|
||||
log.error("Unhandled authentication exception", exception);
|
||||
super.onAuthenticationFailure(request, response, exception);
|
||||
|
||||
+6
-1
@@ -61,7 +61,12 @@ public class CustomSaml2ResponseAuthenticationConverter
|
||||
|
||||
@Override
|
||||
public Saml2Authentication convert(ResponseToken responseToken) {
|
||||
Assertion assertion = responseToken.getResponse().getAssertions().getFirst();
|
||||
List<Assertion> assertions = responseToken.getResponse().getAssertions();
|
||||
if (assertions == null || assertions.isEmpty()) {
|
||||
log.error("SAML response contains no assertions");
|
||||
return null;
|
||||
}
|
||||
Assertion assertion = assertions.getFirst();
|
||||
Map<String, List<Object>> attributes = extractAttributes(assertion);
|
||||
|
||||
// Debug log with actual values
|
||||
|
||||
+5
-2
@@ -213,8 +213,11 @@ public class CustomOAuth2UserService implements OAuth2UserService<OidcUserReques
|
||||
}
|
||||
|
||||
sb.append(
|
||||
"\nWARNING: this block contains PII. Set security.oauth2.debugLogging=false once"
|
||||
+ " troubleshooting is complete.\n");
|
||||
"""
|
||||
|
||||
WARNING: this block contains PII. Set security.oauth2.debugLogging=false once\
|
||||
troubleshooting is complete.
|
||||
""");
|
||||
sb.append("========== [/OAUTH2 DEBUG] ==========");
|
||||
|
||||
if (failure) {
|
||||
|
||||
+3
-1
@@ -132,7 +132,9 @@ public class KeyPersistenceService implements KeyPersistenceServiceInterface {
|
||||
verifyingKeyCache.put(
|
||||
key.getKeyId(), new JwtVerificationKey(key.getKeyId(), key.getVerifyingKey()));
|
||||
}
|
||||
activeKey = new JwtVerificationKey(keys.get(0).getKeyId(), keys.get(0).getVerifyingKey());
|
||||
activeKey =
|
||||
new JwtVerificationKey(
|
||||
keys.getFirst().getKeyId(), keys.getFirst().getVerifyingKey());
|
||||
log.info("Loaded {} JWT key(s) from DB, active key: {}", keys.size(), activeKey.getKeyId());
|
||||
}
|
||||
|
||||
|
||||
+8
-8
@@ -640,14 +640,14 @@ public class UserService implements UserServiceInterface {
|
||||
for (Object principal : sessionRegistry.getAllPrincipals()) {
|
||||
for (SessionInformation sessionsInformation :
|
||||
sessionRegistry.getAllSessions(principal, false)) {
|
||||
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 stringUser) {
|
||||
usernameP = stringUser;
|
||||
switch (principal) {
|
||||
case null -> {}
|
||||
case UserDetails detailsUser -> usernameP = detailsUser.getUsername();
|
||||
case OAuth2User oAuth2User -> usernameP = oAuth2User.getName();
|
||||
case CustomSaml2AuthenticatedPrincipal saml2User ->
|
||||
usernameP = saml2User.name();
|
||||
case String stringUser -> usernameP = stringUser;
|
||||
default -> {}
|
||||
}
|
||||
if (usernameP.equalsIgnoreCase(username)) {
|
||||
sessionRegistry.expireSession(sessionsInformation.getSessionId());
|
||||
|
||||
+14
-16
@@ -47,14 +47,13 @@ public class SessionPersistentRegistry implements SessionRegistry {
|
||||
List<SessionInformation> sessionInformations = new ArrayList<>();
|
||||
String principalName = null;
|
||||
|
||||
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;
|
||||
switch (principal) {
|
||||
case null -> {}
|
||||
case UserDetails detailsUser -> principalName = detailsUser.getUsername();
|
||||
case OAuth2User oAuth2User -> principalName = oAuth2User.getName();
|
||||
case CustomSaml2AuthenticatedPrincipal saml2User -> principalName = saml2User.name();
|
||||
case String stringUser -> principalName = stringUser;
|
||||
default -> {}
|
||||
}
|
||||
|
||||
if (principalName != null) {
|
||||
@@ -78,14 +77,13 @@ public class SessionPersistentRegistry implements SessionRegistry {
|
||||
public void registerNewSession(String sessionId, Object principal) {
|
||||
String principalName = null;
|
||||
|
||||
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;
|
||||
switch (principal) {
|
||||
case null -> {}
|
||||
case UserDetails detailsUser -> principalName = detailsUser.getUsername();
|
||||
case OAuth2User oAuth2User -> principalName = oAuth2User.getName();
|
||||
case CustomSaml2AuthenticatedPrincipal saml2User -> principalName = saml2User.name();
|
||||
case String stringUser -> principalName = stringUser;
|
||||
default -> {}
|
||||
}
|
||||
|
||||
if (principalName != null) {
|
||||
|
||||
+2
-1
@@ -1,5 +1,6 @@
|
||||
package stirling.software.proprietary.storage.model;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@@ -46,7 +47,7 @@ import stirling.software.proprietary.security.model.User;
|
||||
@Setter
|
||||
public class FileShare implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
@Serial private static final long serialVersionUID = 1L;
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
|
||||
+2
-1
@@ -1,5 +1,6 @@
|
||||
package stirling.software.proprietary.storage.model;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@@ -39,7 +40,7 @@ import stirling.software.proprietary.security.model.User;
|
||||
@Setter
|
||||
public class FileShareAccess implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
@Serial private static final long serialVersionUID = 1L;
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
|
||||
+2
-1
@@ -1,5 +1,6 @@
|
||||
package stirling.software.proprietary.storage.model;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@@ -24,7 +25,7 @@ import lombok.Setter;
|
||||
@Setter
|
||||
public class StorageCleanupEntry implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
@Serial private static final long serialVersionUID = 1L;
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
|
||||
+2
-1
@@ -1,5 +1,6 @@
|
||||
package stirling.software.proprietary.storage.model;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.HashSet;
|
||||
@@ -45,7 +46,7 @@ import stirling.software.proprietary.workflow.model.WorkflowSession;
|
||||
@Setter
|
||||
public class StoredFile implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
@Serial private static final long serialVersionUID = 1L;
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
|
||||
+2
-1
@@ -1,5 +1,6 @@
|
||||
package stirling.software.proprietary.storage.model;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
@@ -19,7 +20,7 @@ import lombok.Setter;
|
||||
@Setter
|
||||
public class StoredFileBlob implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
@Serial private static final long serialVersionUID = 1L;
|
||||
|
||||
@Id
|
||||
@Column(name = "storage_key", nullable = false, length = 128)
|
||||
|
||||
@@ -7,6 +7,7 @@ import org.slf4j.MDC;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.filter.OncePerRequestFilter;
|
||||
@@ -64,7 +65,7 @@ public class AuditWebFilter extends OncePerRequestFilter {
|
||||
if (auth != null && auth.getAuthorities() != null) {
|
||||
String roles =
|
||||
auth.getAuthorities().stream()
|
||||
.map(a -> a.getAuthority())
|
||||
.map(GrantedAuthority::getAuthority)
|
||||
.reduce((a, b) -> a + "," + b)
|
||||
.orElse("");
|
||||
MDC.put("userRoles", roles);
|
||||
|
||||
+4
-1
@@ -37,6 +37,7 @@ import stirling.software.proprietary.workflow.dto.CertificateInfo;
|
||||
import stirling.software.proprietary.workflow.dto.CertificateValidationResponse;
|
||||
import stirling.software.proprietary.workflow.dto.ParticipantRequest;
|
||||
import stirling.software.proprietary.workflow.dto.WorkflowCreationRequest;
|
||||
import stirling.software.proprietary.workflow.model.WorkflowParticipant;
|
||||
import stirling.software.proprietary.workflow.model.WorkflowSession;
|
||||
import stirling.software.proprietary.workflow.service.CertificateSubmissionValidator;
|
||||
import stirling.software.proprietary.workflow.service.SigningFinalizationService;
|
||||
@@ -259,7 +260,9 @@ public class SigningSessionController {
|
||||
+ "database until manual cleanup.",
|
||||
sessionId,
|
||||
session.getParticipants() != null
|
||||
? session.getParticipants().stream().map(p -> p.getEmail()).toList()
|
||||
? session.getParticipants().stream()
|
||||
.map(WorkflowParticipant::getEmail)
|
||||
.toList()
|
||||
: "unknown",
|
||||
e);
|
||||
throw new ResponseStatusException(
|
||||
|
||||
+2
-1
@@ -5,6 +5,7 @@ import java.nio.charset.StandardCharsets;
|
||||
import java.time.ZoneOffset;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.http.ContentDisposition;
|
||||
@@ -429,7 +430,7 @@ public class WorkflowParticipantController {
|
||||
java.util.List<Map<String, Object>> wetSigs =
|
||||
objectMapper.readValue(
|
||||
request.getWetSignaturesData(),
|
||||
new TypeReference<java.util.List<Map<String, Object>>>() {});
|
||||
new TypeReference<List<Map<String, Object>>>() {});
|
||||
if (wetSigs.size() > WetSignatureMetadata.MAX_SIGNATURES_PER_PARTICIPANT) {
|
||||
throw new ResponseStatusException(
|
||||
HttpStatus.BAD_REQUEST, "Too many wet signatures submitted");
|
||||
|
||||
+2
-1
@@ -1,5 +1,6 @@
|
||||
package stirling.software.proprietary.workflow.model;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.ArrayList;
|
||||
@@ -51,7 +52,7 @@ import stirling.software.proprietary.storage.model.ShareAccessRole;
|
||||
@Setter
|
||||
public class WorkflowParticipant implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
@Serial private static final long serialVersionUID = 1L;
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
|
||||
+2
-1
@@ -1,5 +1,6 @@
|
||||
package stirling.software.proprietary.workflow.model;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.ArrayList;
|
||||
@@ -53,7 +54,7 @@ import stirling.software.proprietary.storage.model.StoredFile;
|
||||
@Setter
|
||||
public class WorkflowSession implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
@Serial private static final long serialVersionUID = 1L;
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
|
||||
+4
-10
@@ -217,16 +217,13 @@ public class SigningFinalizationService {
|
||||
wetSignatures.size(),
|
||||
session.getSessionId());
|
||||
|
||||
PDDocument document = pdfDocumentFactory.load(new ByteArrayInputStream(pdfBytes));
|
||||
try {
|
||||
try (PDDocument document = pdfDocumentFactory.load(new ByteArrayInputStream(pdfBytes))) {
|
||||
for (WetSignatureMetadata wetSig : wetSignatures) {
|
||||
applyWetSignatureToPage(document, wetSig);
|
||||
}
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
document.save(baos);
|
||||
return baos.toByteArray();
|
||||
} finally {
|
||||
document.close();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -242,11 +239,10 @@ public class SigningFinalizationService {
|
||||
}
|
||||
|
||||
PDPage page = document.getPage(pageIndex);
|
||||
PDPageContentStream contentStream =
|
||||
new PDPageContentStream(
|
||||
document, page, PDPageContentStream.AppendMode.APPEND, true, true);
|
||||
|
||||
try {
|
||||
try (PDPageContentStream contentStream =
|
||||
new PDPageContentStream(
|
||||
document, page, PDPageContentStream.AppendMode.APPEND, true, true)) {
|
||||
// Use WetSignatureMetadata.extractBase64Data() to strip data URL prefix
|
||||
String base64Data = wetSig.extractBase64Data();
|
||||
if (base64Data == null || base64Data.isBlank()) {
|
||||
@@ -279,8 +275,6 @@ public class SigningFinalizationService {
|
||||
pdfY,
|
||||
width,
|
||||
height);
|
||||
} finally {
|
||||
contentStream.close();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+16
-15
@@ -954,21 +954,22 @@ public class WorkflowSessionService {
|
||||
Object pemObject = pemParser.readObject();
|
||||
JcaPEMKeyConverter converter = new JcaPEMKeyConverter().setProvider("BC");
|
||||
PrivateKeyInfo keyInfo;
|
||||
if (pemObject instanceof PKCS8EncryptedPrivateKeyInfo encrypted) {
|
||||
InputDecryptorProvider decryptor =
|
||||
new JceOpenSSLPKCS8DecryptorProviderBuilder().build(password);
|
||||
keyInfo = encrypted.decryptPrivateKeyInfo(decryptor);
|
||||
} else if (pemObject instanceof PEMEncryptedKeyPair encryptedKeyPair) {
|
||||
PEMDecryptorProvider decryptor =
|
||||
new JcePEMDecryptorProviderBuilder().build(password);
|
||||
keyInfo = encryptedKeyPair.decryptKeyPair(decryptor).getPrivateKeyInfo();
|
||||
} else if (pemObject instanceof PEMKeyPair keyPair) {
|
||||
keyInfo = keyPair.getPrivateKeyInfo();
|
||||
} else if (pemObject instanceof PrivateKeyInfo info) {
|
||||
keyInfo = info;
|
||||
} else {
|
||||
throw new ResponseStatusException(
|
||||
HttpStatus.BAD_REQUEST, "Unsupported PEM private key format");
|
||||
switch (pemObject) {
|
||||
case PKCS8EncryptedPrivateKeyInfo encrypted -> {
|
||||
InputDecryptorProvider decryptor =
|
||||
new JceOpenSSLPKCS8DecryptorProviderBuilder().build(password);
|
||||
keyInfo = encrypted.decryptPrivateKeyInfo(decryptor);
|
||||
}
|
||||
case PEMEncryptedKeyPair encryptedKeyPair -> {
|
||||
PEMDecryptorProvider decryptor =
|
||||
new JcePEMDecryptorProviderBuilder().build(password);
|
||||
keyInfo = encryptedKeyPair.decryptKeyPair(decryptor).getPrivateKeyInfo();
|
||||
}
|
||||
case PEMKeyPair keyPair -> keyInfo = keyPair.getPrivateKeyInfo();
|
||||
case PrivateKeyInfo info -> keyInfo = info;
|
||||
case null, default ->
|
||||
throw new ResponseStatusException(
|
||||
HttpStatus.BAD_REQUEST, "Unsupported PEM private key format");
|
||||
}
|
||||
return converter.getPrivateKey(keyInfo);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user