mirror of
https://github.com/Frooodle/Stirling-PDF.git
synced 2025-11-16 01:21:16 +01:00
refactor(core,common,proprietary): Replace Date with Instant/modern Date API alternative for improved time handling (#4497)
This commit is contained in:
parent
07392ed25e
commit
133e6d3de6
@ -1,6 +1,6 @@
|
||||
package stirling.software.common.model;
|
||||
|
||||
import java.util.Calendar;
|
||||
import java.time.ZonedDateTime;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
@ -18,6 +18,6 @@ public class PdfMetadata {
|
||||
private String creator;
|
||||
private String subject;
|
||||
private String keywords;
|
||||
private Calendar creationDate;
|
||||
private Calendar modificationDate;
|
||||
private ZonedDateTime creationDate;
|
||||
private ZonedDateTime modificationDate;
|
||||
}
|
||||
|
||||
@ -1,5 +1,9 @@
|
||||
package stirling.software.common.service;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneId;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.Calendar;
|
||||
|
||||
import org.apache.pdfbox.pdmodel.PDDocument;
|
||||
@ -29,17 +33,19 @@ public class PdfMetadataService {
|
||||
this.runningProOrHigher = runningProOrHigher;
|
||||
}
|
||||
|
||||
public PdfMetadata extractMetadataFromPdf(PDDocument pdf) {
|
||||
return PdfMetadata.builder()
|
||||
.author(pdf.getDocumentInformation().getAuthor())
|
||||
.producer(pdf.getDocumentInformation().getProducer())
|
||||
.title(pdf.getDocumentInformation().getTitle())
|
||||
.creator(pdf.getDocumentInformation().getCreator())
|
||||
.subject(pdf.getDocumentInformation().getSubject())
|
||||
.keywords(pdf.getDocumentInformation().getKeywords())
|
||||
.creationDate(pdf.getDocumentInformation().getCreationDate())
|
||||
.modificationDate(pdf.getDocumentInformation().getModificationDate())
|
||||
.build();
|
||||
/**
|
||||
* Converts ZonedDateTime to Calendar for PDFBox compatibility.
|
||||
*
|
||||
* @param zonedDateTime the ZonedDateTime to convert
|
||||
* @return Calendar instance or null if input is null
|
||||
*/
|
||||
public static Calendar toCalendar(ZonedDateTime zonedDateTime) {
|
||||
if (zonedDateTime == null) {
|
||||
return null;
|
||||
}
|
||||
Calendar calendar = Calendar.getInstance();
|
||||
calendar.setTimeInMillis(zonedDateTime.toInstant().toEpochMilli());
|
||||
return calendar;
|
||||
}
|
||||
|
||||
public void setDefaultMetadata(PDDocument pdf) {
|
||||
@ -58,6 +64,52 @@ public class PdfMetadataService {
|
||||
setCommonMetadata(pdf, pdfMetadata);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a date string and converts it to Calendar for PDFBox compatibility.
|
||||
*
|
||||
* @param dateString the date string in "yyyy/MM/dd HH:mm:ss" format
|
||||
* @return Calendar instance or null if parsing fails or input is empty
|
||||
*/
|
||||
public static Calendar parseToCalendar(String dateString) {
|
||||
if (dateString == null || dateString.trim().isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss");
|
||||
ZonedDateTime zonedDateTime =
|
||||
LocalDateTime.parse(dateString, formatter).atZone(ZoneId.systemDefault());
|
||||
return toCalendar(zonedDateTime);
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public PdfMetadata extractMetadataFromPdf(PDDocument pdf) {
|
||||
Calendar creationCal = pdf.getDocumentInformation().getCreationDate();
|
||||
Calendar modificationCal = pdf.getDocumentInformation().getModificationDate();
|
||||
|
||||
ZonedDateTime creationDate =
|
||||
creationCal != null
|
||||
? ZonedDateTime.ofInstant(creationCal.toInstant(), ZoneId.systemDefault())
|
||||
: null;
|
||||
ZonedDateTime modificationDate =
|
||||
modificationCal != null
|
||||
? ZonedDateTime.ofInstant(
|
||||
modificationCal.toInstant(), ZoneId.systemDefault())
|
||||
: null;
|
||||
|
||||
return PdfMetadata.builder()
|
||||
.author(pdf.getDocumentInformation().getAuthor())
|
||||
.producer(pdf.getDocumentInformation().getProducer())
|
||||
.title(pdf.getDocumentInformation().getTitle())
|
||||
.creator(pdf.getDocumentInformation().getCreator())
|
||||
.subject(pdf.getDocumentInformation().getSubject())
|
||||
.keywords(pdf.getDocumentInformation().getKeywords())
|
||||
.creationDate(creationDate)
|
||||
.modificationDate(modificationDate)
|
||||
.build();
|
||||
}
|
||||
|
||||
private void setNewDocumentMetadata(PDDocument pdf, PdfMetadata pdfMetadata) {
|
||||
|
||||
String creator = stirlingPDFLabel;
|
||||
@ -79,7 +131,13 @@ public class PdfMetadataService {
|
||||
}
|
||||
|
||||
pdf.getDocumentInformation().setCreator(creator);
|
||||
pdf.getDocumentInformation().setCreationDate(Calendar.getInstance());
|
||||
|
||||
// Use existing creation date if available, otherwise create new one
|
||||
Calendar creationCal =
|
||||
pdfMetadata.getCreationDate() != null
|
||||
? toCalendar(pdfMetadata.getCreationDate())
|
||||
: Calendar.getInstance();
|
||||
pdf.getDocumentInformation().setCreationDate(creationCal);
|
||||
}
|
||||
|
||||
private void setCommonMetadata(PDDocument pdf, PdfMetadata pdfMetadata) {
|
||||
@ -88,7 +146,13 @@ public class PdfMetadataService {
|
||||
pdf.getDocumentInformation().setProducer(stirlingPDFLabel);
|
||||
pdf.getDocumentInformation().setSubject(pdfMetadata.getSubject());
|
||||
pdf.getDocumentInformation().setKeywords(pdfMetadata.getKeywords());
|
||||
pdf.getDocumentInformation().setModificationDate(Calendar.getInstance());
|
||||
|
||||
// Convert ZonedDateTime to Calendar for PDFBox compatibility
|
||||
Calendar modificationCal =
|
||||
pdfMetadata.getModificationDate() != null
|
||||
? toCalendar(pdfMetadata.getModificationDate())
|
||||
: Calendar.getInstance();
|
||||
pdf.getDocumentInformation().setModificationDate(modificationCal);
|
||||
|
||||
String author = pdfMetadata.getAuthor();
|
||||
if (applicationProperties
|
||||
|
||||
@ -6,6 +6,8 @@ import java.io.InputStream;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Method;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.time.ZoneId;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
@ -145,7 +147,11 @@ public class EmlParser {
|
||||
extractRecipients(message, messageClass, content);
|
||||
|
||||
Method getSentDate = messageClass.getMethod("getSentDate");
|
||||
content.setDate((Date) getSentDate.invoke(message));
|
||||
Date legacyDate = (Date) getSentDate.invoke(message);
|
||||
if (legacyDate != null) {
|
||||
content.setDate(
|
||||
ZonedDateTime.ofInstant(legacyDate.toInstant(), ZoneId.systemDefault()));
|
||||
}
|
||||
|
||||
Method getContent = messageClass.getMethod("getContent");
|
||||
Object messageContent = getContent.invoke(message);
|
||||
@ -616,7 +622,7 @@ public class EmlParser {
|
||||
private String to;
|
||||
private String cc;
|
||||
private String bcc;
|
||||
private Date date;
|
||||
private ZonedDateTime date;
|
||||
private String dateString; // For basic parsing fallback
|
||||
private String htmlBody;
|
||||
private String textBody;
|
||||
|
||||
@ -8,7 +8,9 @@ import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.time.ZoneId;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Base64;
|
||||
import java.util.Date;
|
||||
@ -18,7 +20,6 @@ import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.TimeZone;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
@ -290,11 +291,15 @@ public class PdfAttachmentHandler {
|
||||
|
||||
public static String formatEmailDate(Date date) {
|
||||
if (date == null) return "";
|
||||
return formatEmailDate(ZonedDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault()));
|
||||
}
|
||||
|
||||
SimpleDateFormat formatter =
|
||||
new SimpleDateFormat("EEE, MMM d, yyyy 'at' h:mm a z", Locale.ENGLISH);
|
||||
formatter.setTimeZone(TimeZone.getTimeZone("UTC"));
|
||||
return formatter.format(date);
|
||||
public static String formatEmailDate(ZonedDateTime dateTime) {
|
||||
if (dateTime == null) return "";
|
||||
|
||||
DateTimeFormatter formatter =
|
||||
DateTimeFormatter.ofPattern("EEE, MMM d, yyyy 'at' h:mm a z", Locale.ENGLISH);
|
||||
return dateTime.withZoneSameInstant(ZoneId.of("UTC")).format(formatter);
|
||||
}
|
||||
|
||||
@Data
|
||||
|
||||
@ -7,16 +7,18 @@ import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.time.Instant;
|
||||
import java.time.ZoneId;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Calendar;
|
||||
import java.util.Collections;
|
||||
import java.util.GregorianCalendar;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.TimeZone;
|
||||
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.apache.pdfbox.Loader;
|
||||
@ -564,18 +566,31 @@ public class ConvertPDFToPDFA {
|
||||
adobePdfSchema.setKeywords(keywords);
|
||||
}
|
||||
|
||||
// Set creation and modification dates
|
||||
Calendar now = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
|
||||
Calendar originalCreationDate = docInfo.getCreationDate();
|
||||
if (originalCreationDate == null) {
|
||||
originalCreationDate = now;
|
||||
}
|
||||
docInfo.setCreationDate(originalCreationDate);
|
||||
xmpBasicSchema.setCreateDate(originalCreationDate);
|
||||
// Set creation and modification dates using java.time and convert to GregorianCalendar
|
||||
Instant nowInstant = Instant.now();
|
||||
ZonedDateTime nowZdt = ZonedDateTime.ofInstant(nowInstant, ZoneId.of("UTC"));
|
||||
GregorianCalendar nowCal = GregorianCalendar.from(nowZdt);
|
||||
|
||||
docInfo.setModificationDate(now);
|
||||
xmpBasicSchema.setModifyDate(now);
|
||||
xmpBasicSchema.setMetadataDate(now);
|
||||
java.util.Calendar originalCreationDate = docInfo.getCreationDate();
|
||||
GregorianCalendar creationCal;
|
||||
if (originalCreationDate == null) {
|
||||
creationCal = nowCal;
|
||||
} else if (originalCreationDate instanceof GregorianCalendar) {
|
||||
creationCal = (GregorianCalendar) originalCreationDate;
|
||||
} else {
|
||||
// convert other Calendar implementations to GregorianCalendar preserving instant
|
||||
creationCal =
|
||||
GregorianCalendar.from(
|
||||
ZonedDateTime.ofInstant(
|
||||
originalCreationDate.toInstant(), ZoneId.of("UTC")));
|
||||
}
|
||||
|
||||
docInfo.setCreationDate(creationCal);
|
||||
xmpBasicSchema.setCreateDate(creationCal);
|
||||
|
||||
docInfo.setModificationDate(nowCal);
|
||||
xmpBasicSchema.setModifyDate(nowCal);
|
||||
xmpBasicSchema.setMetadataDate(nowCal);
|
||||
|
||||
// Serialize the created metadata so it can be attached to the existent metadata
|
||||
ByteArrayOutputStream xmpOut = new ByteArrayOutputStream();
|
||||
|
||||
@ -1,8 +1,6 @@
|
||||
package stirling.software.SPDF.controller.api.misc;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Calendar;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
@ -25,6 +23,7 @@ import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import stirling.software.SPDF.model.api.misc.MetadataRequest;
|
||||
import stirling.software.common.service.CustomPDFDocumentFactory;
|
||||
import stirling.software.common.service.PdfMetadataService;
|
||||
import stirling.software.common.util.WebResponseUtils;
|
||||
import stirling.software.common.util.propertyeditor.StringToMapPropertyEditor;
|
||||
|
||||
@ -144,30 +143,13 @@ public class MetadataController {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (creationDate != null && creationDate.length() > 0) {
|
||||
Calendar creationDateCal = Calendar.getInstance();
|
||||
try {
|
||||
creationDateCal.setTime(
|
||||
new SimpleDateFormat("yyyy/MM/dd HH:mm:ss").parse(creationDate));
|
||||
} catch (ParseException e) {
|
||||
log.error("exception", e);
|
||||
}
|
||||
// Set creation date using utility method
|
||||
Calendar creationDateCal = PdfMetadataService.parseToCalendar(creationDate);
|
||||
info.setCreationDate(creationDateCal);
|
||||
} else {
|
||||
info.setCreationDate(null);
|
||||
}
|
||||
if (modificationDate != null && modificationDate.length() > 0) {
|
||||
Calendar modificationDateCal = Calendar.getInstance();
|
||||
try {
|
||||
modificationDateCal.setTime(
|
||||
new SimpleDateFormat("yyyy/MM/dd HH:mm:ss").parse(modificationDate));
|
||||
} catch (ParseException e) {
|
||||
log.error("exception", e);
|
||||
}
|
||||
|
||||
// Set modification date using utility method
|
||||
Calendar modificationDateCal = PdfMetadataService.parseToCalendar(modificationDate);
|
||||
info.setModificationDate(modificationDateCal);
|
||||
} else {
|
||||
info.setModificationDate(null);
|
||||
}
|
||||
info.setCreator(creator);
|
||||
info.setKeywords(keywords);
|
||||
info.setAuthor(author);
|
||||
|
||||
@ -120,7 +120,7 @@ public class CertSignController {
|
||||
signature.setName(name);
|
||||
signature.setLocation(location);
|
||||
signature.setReason(reason);
|
||||
signature.setSignDate(Calendar.getInstance());
|
||||
signature.setSignDate(Calendar.getInstance()); // PDFBox requires Calendar
|
||||
if (Boolean.TRUE.equals(showSignature)) {
|
||||
SignatureOptions signatureOptions = new SignatureOptions();
|
||||
signatureOptions.setVisualSignature(
|
||||
|
||||
@ -3,7 +3,9 @@ package stirling.software.SPDF.controller.api.security;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.time.ZoneId;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.*;
|
||||
|
||||
import org.apache.pdfbox.cos.COSInputStream;
|
||||
@ -817,8 +819,10 @@ public class GetInfoOnPDF {
|
||||
|
||||
private String formatDate(Calendar calendar) {
|
||||
if (calendar != null) {
|
||||
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
|
||||
return sdf.format(calendar.getTime());
|
||||
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
|
||||
ZonedDateTime zonedDateTime =
|
||||
ZonedDateTime.ofInstant(calendar.toInstant(), ZoneId.systemDefault());
|
||||
return zonedDateTime.format(formatter);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -7,8 +7,8 @@ import java.security.cert.CertificateException;
|
||||
import java.security.cert.CertificateFactory;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.security.interfaces.RSAPublicKey;
|
||||
import java.time.Instant;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.pdfbox.pdmodel.PDDocument;
|
||||
@ -134,11 +134,12 @@ public class ValidateSignatureController {
|
||||
: certValidationService.validateTrustStore(cert));
|
||||
|
||||
result.setNotRevoked(!certValidationService.isRevoked(cert));
|
||||
result.setNotExpired(!cert.getNotAfter().before(new Date()));
|
||||
result.setNotExpired(
|
||||
Instant.now().isBefore(cert.getNotAfter().toInstant()));
|
||||
|
||||
// Set basic signature info
|
||||
result.setSignerName(sig.getName());
|
||||
result.setSignatureDate(sig.getSignDate().getTime().toString());
|
||||
result.setSignatureDate(sig.getSignDate().toInstant().toString());
|
||||
result.setReason(sig.getReason());
|
||||
result.setLocation(sig.getLocation());
|
||||
|
||||
|
||||
@ -3,6 +3,9 @@ package stirling.software.SPDF.service;
|
||||
import static stirling.software.common.util.AttachmentUtils.setCatalogViewerPreferences;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.time.Instant;
|
||||
import java.time.ZoneId;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.GregorianCalendar;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
@ -54,8 +57,13 @@ public class AttachmentService implements AttachmentServiceInterface {
|
||||
PDEmbeddedFile embeddedFile =
|
||||
new PDEmbeddedFile(document, attachment.getInputStream());
|
||||
embeddedFile.setSize((int) attachment.getSize());
|
||||
embeddedFile.setCreationDate(new GregorianCalendar());
|
||||
embeddedFile.setModDate(new GregorianCalendar());
|
||||
// use java.time.Instant and convert to GregorianCalendar for PDFBox
|
||||
Instant now = Instant.now();
|
||||
GregorianCalendar nowCal =
|
||||
GregorianCalendar.from(
|
||||
ZonedDateTime.ofInstant(now, ZoneId.systemDefault()));
|
||||
embeddedFile.setCreationDate(nowCal);
|
||||
embeddedFile.setModDate(nowCal);
|
||||
String contentType = attachment.getContentType();
|
||||
if (StringUtils.isNotBlank(contentType)) {
|
||||
embeddedFile.setSubtype(contentType);
|
||||
|
||||
@ -7,6 +7,8 @@ import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.time.ZoneId;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.Calendar;
|
||||
|
||||
import org.apache.pdfbox.pdmodel.PDDocument;
|
||||
@ -81,6 +83,12 @@ class PdfMetadataServiceBasicTest {
|
||||
// Act
|
||||
PdfMetadata metadata = pdfMetadataService.extractMetadataFromPdf(testDocument);
|
||||
|
||||
// Convert Calendar to ZonedDateTime for comparison
|
||||
ZonedDateTime expectedCreationDate =
|
||||
ZonedDateTime.ofInstant(creationDate.toInstant(), ZoneId.systemDefault());
|
||||
ZonedDateTime expectedModificationDate =
|
||||
ZonedDateTime.ofInstant(modificationDate.toInstant(), ZoneId.systemDefault());
|
||||
|
||||
// Assert
|
||||
assertEquals(testAuthor, metadata.getAuthor(), "Author should match");
|
||||
assertEquals(testProducer, metadata.getProducer(), "Producer should match");
|
||||
@ -88,9 +96,12 @@ class PdfMetadataServiceBasicTest {
|
||||
assertEquals(testCreator, metadata.getCreator(), "Creator should match");
|
||||
assertEquals(testSubject, metadata.getSubject(), "Subject should match");
|
||||
assertEquals(testKeywords, metadata.getKeywords(), "Keywords should match");
|
||||
assertEquals(creationDate, metadata.getCreationDate(), "Creation date should match");
|
||||
assertEquals(
|
||||
modificationDate, metadata.getModificationDate(), "Modification date should match");
|
||||
expectedCreationDate, metadata.getCreationDate(), "Creation date should match");
|
||||
assertEquals(
|
||||
expectedModificationDate,
|
||||
metadata.getModificationDate(),
|
||||
"Modification date should match");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@ -7,6 +7,8 @@ import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.time.ZoneId;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.Calendar;
|
||||
|
||||
import org.apache.pdfbox.pdmodel.PDDocument;
|
||||
@ -84,6 +86,12 @@ class PdfMetadataServiceTest {
|
||||
// Act
|
||||
PdfMetadata metadata = pdfMetadataService.extractMetadataFromPdf(testDocument);
|
||||
|
||||
// Convert Calendar to ZonedDateTime for comparison
|
||||
ZonedDateTime expectedCreationDate =
|
||||
ZonedDateTime.ofInstant(creationDate.toInstant(), ZoneId.systemDefault());
|
||||
ZonedDateTime expectedModificationDate =
|
||||
ZonedDateTime.ofInstant(modificationDate.toInstant(), ZoneId.systemDefault());
|
||||
|
||||
// Assert
|
||||
assertEquals(testAuthor, metadata.getAuthor(), "Author should match");
|
||||
assertEquals(testProducer, metadata.getProducer(), "Producer should match");
|
||||
@ -91,9 +99,12 @@ class PdfMetadataServiceTest {
|
||||
assertEquals(testCreator, metadata.getCreator(), "Creator should match");
|
||||
assertEquals(testSubject, metadata.getSubject(), "Subject should match");
|
||||
assertEquals(testKeywords, metadata.getKeywords(), "Keywords should match");
|
||||
assertEquals(creationDate, metadata.getCreationDate(), "Creation date should match");
|
||||
assertEquals(
|
||||
modificationDate, metadata.getModificationDate(), "Modification date should match");
|
||||
expectedCreationDate, metadata.getCreationDate(), "Creation date should match");
|
||||
assertEquals(
|
||||
expectedModificationDate,
|
||||
metadata.getModificationDate(),
|
||||
"Modification date should match");
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -190,6 +201,8 @@ class PdfMetadataServiceTest {
|
||||
// Prepare test metadata with existing creation date
|
||||
Calendar existingCreationDate = Calendar.getInstance();
|
||||
existingCreationDate.add(Calendar.DAY_OF_MONTH, -1); // Yesterday
|
||||
ZonedDateTime existingCreationDateZdt =
|
||||
ZonedDateTime.ofInstant(existingCreationDate.toInstant(), ZoneId.systemDefault());
|
||||
|
||||
PdfMetadata testMetadata =
|
||||
PdfMetadata.builder()
|
||||
@ -197,7 +210,7 @@ class PdfMetadataServiceTest {
|
||||
.title("Test Title")
|
||||
.subject("Test Subject")
|
||||
.keywords("Test Keywords")
|
||||
.creationDate(existingCreationDate)
|
||||
.creationDate(existingCreationDateZdt)
|
||||
.build();
|
||||
|
||||
// Act
|
||||
|
||||
@ -4,7 +4,6 @@ import static stirling.software.common.util.ProviderUtils.validateProvider;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
@ -218,7 +217,7 @@ public class AccountWebController {
|
||||
Map<String, String> roleDetails = Role.getAllRoleDetails();
|
||||
// Map to store session information and user activity status
|
||||
Map<String, Boolean> userSessions = new HashMap<>();
|
||||
Map<String, Date> userLastRequest = new HashMap<>();
|
||||
Map<String, Instant> userLastRequest = new HashMap<>();
|
||||
int activeUsers = 0;
|
||||
int disabledUsers = 0;
|
||||
while (iterator.hasNext()) {
|
||||
@ -249,27 +248,29 @@ public class AccountWebController {
|
||||
// Determine the user's session status and last request time
|
||||
int maxInactiveInterval = sessionPersistentRegistry.getMaxInactiveInterval();
|
||||
boolean hasActiveSession = false;
|
||||
Date lastRequest = null;
|
||||
Instant lastRequest = null;
|
||||
Optional<SessionEntity> latestSession =
|
||||
sessionPersistentRegistry.findLatestSession(user.getUsername());
|
||||
if (latestSession.isPresent()) {
|
||||
SessionEntity sessionEntity = latestSession.get();
|
||||
Date lastAccessedTime = sessionEntity.getLastRequest();
|
||||
// sessionEntity stores Instant directly
|
||||
Instant lastAccessedTime =
|
||||
Optional.ofNullable(sessionEntity.getLastRequest())
|
||||
.orElse(Instant.EPOCH);
|
||||
|
||||
Instant now = Instant.now();
|
||||
// Calculate session expiration and update session status accordingly
|
||||
Instant expirationTime =
|
||||
lastAccessedTime
|
||||
.toInstant()
|
||||
.plus(maxInactiveInterval, ChronoUnit.SECONDS);
|
||||
lastAccessedTime.plus(maxInactiveInterval, ChronoUnit.SECONDS);
|
||||
if (now.isAfter(expirationTime)) {
|
||||
sessionPersistentRegistry.expireSession(sessionEntity.getSessionId());
|
||||
} else {
|
||||
hasActiveSession = !sessionEntity.isExpired();
|
||||
}
|
||||
lastRequest = sessionEntity.getLastRequest();
|
||||
lastRequest = lastAccessedTime;
|
||||
} else {
|
||||
// No session, set default last request time
|
||||
lastRequest = new Date(0);
|
||||
lastRequest = Instant.EPOCH;
|
||||
}
|
||||
userSessions.put(user.getUsername(), hasActiveSession);
|
||||
userLastRequest.put(user.getUsername(), lastRequest);
|
||||
@ -286,19 +287,21 @@ public class AccountWebController {
|
||||
allUsers.stream()
|
||||
.sorted(
|
||||
(u1, u2) -> {
|
||||
boolean u1Active = userSessions.get(u1.getUsername());
|
||||
boolean u2Active = userSessions.get(u2.getUsername());
|
||||
boolean u1Active =
|
||||
userSessions.getOrDefault(u1.getUsername(), false);
|
||||
boolean u2Active =
|
||||
userSessions.getOrDefault(u2.getUsername(), false);
|
||||
if (u1Active && !u2Active) {
|
||||
return -1;
|
||||
} else if (!u1Active && u2Active) {
|
||||
return 1;
|
||||
} else {
|
||||
Date u1LastRequest =
|
||||
Instant u1LastRequest =
|
||||
userLastRequest.getOrDefault(
|
||||
u1.getUsername(), new Date(0));
|
||||
Date u2LastRequest =
|
||||
u1.getUsername(), Instant.EPOCH);
|
||||
Instant u2LastRequest =
|
||||
userLastRequest.getOrDefault(
|
||||
u2.getUsername(), new Date(0));
|
||||
u2.getUsername(), Instant.EPOCH);
|
||||
return u2LastRequest.compareTo(u1LastRequest);
|
||||
}
|
||||
})
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
package stirling.software.proprietary.security.controller.web;
|
||||
|
||||
import java.util.Date;
|
||||
import java.time.Instant;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@ -44,17 +44,17 @@ public class TeamWebController {
|
||||
// Filter out the Internal team
|
||||
List<TeamWithUserCountDTO> teamsWithCounts =
|
||||
allTeamsWithCounts.stream()
|
||||
.filter(team -> !team.getName().equals(TeamService.INTERNAL_TEAM_NAME))
|
||||
.filter(team -> !TeamService.INTERNAL_TEAM_NAME.equals(team.getName()))
|
||||
.toList();
|
||||
|
||||
// Get the latest activity for each team
|
||||
List<Object[]> teamActivities = sessionRepository.findLatestActivityByTeam();
|
||||
|
||||
// Convert the query results to a map for easy access in the view
|
||||
Map<Long, Date> teamLastRequest = new HashMap<>();
|
||||
Map<Long, Instant> teamLastRequest = new HashMap<>();
|
||||
for (Object[] result : teamActivities) {
|
||||
Long teamId = (Long) result[0]; // teamId alias
|
||||
Date lastActivity = (Date) result[1]; // lastActivity alias
|
||||
Instant lastActivity = (Instant) result[1]; // lastActivity alias
|
||||
teamLastRequest.put(teamId, lastActivity);
|
||||
}
|
||||
|
||||
@ -97,7 +97,7 @@ public class TeamWebController {
|
||||
.orElseThrow(() -> new RuntimeException("Team not found"));
|
||||
|
||||
// Prevent access to Internal team
|
||||
if (team.getName().equals(TeamService.INTERNAL_TEAM_NAME)) {
|
||||
if (TeamService.INTERNAL_TEAM_NAME.equals(team.getName())) {
|
||||
return "redirect:/teams?error=internalTeamNotAccessible";
|
||||
}
|
||||
|
||||
@ -114,21 +114,18 @@ public class TeamWebController {
|
||||
(user.getTeam() == null
|
||||
|| !user.getTeam().getId().equals(id))
|
||||
&& (user.getTeam() == null
|
||||
|| !user.getTeam()
|
||||
.getName()
|
||||
.equals(
|
||||
TeamService
|
||||
.INTERNAL_TEAM_NAME)))
|
||||
|| !TeamService.INTERNAL_TEAM_NAME.equals(
|
||||
user.getTeam().getName())))
|
||||
.toList();
|
||||
|
||||
// Get the latest session for each user in the team
|
||||
List<Object[]> userSessions = sessionRepository.findLatestSessionByTeamId(id);
|
||||
|
||||
// Create a map of username to last request date
|
||||
Map<String, Date> userLastRequest = new HashMap<>();
|
||||
Map<String, Instant> userLastRequest = new HashMap<>();
|
||||
for (Object[] result : userSessions) {
|
||||
String username = (String) result[0]; // username alias
|
||||
Date lastRequest = (Date) result[1]; // lastRequest alias
|
||||
Instant lastRequest = (Instant) result[1]; // lastRequest alias
|
||||
userLastRequest.put(username, lastRequest);
|
||||
}
|
||||
|
||||
|
||||
@ -23,7 +23,7 @@ public class JPATokenRepositoryImpl implements PersistentTokenRepository {
|
||||
newToken.setSeries(token.getSeries());
|
||||
newToken.setUsername(token.getUsername());
|
||||
newToken.setToken(token.getTokenValue());
|
||||
newToken.setLastUsed(token.getDate());
|
||||
newToken.setLastUsed(token.getDate().toInstant());
|
||||
persistentLoginRepository.save(newToken);
|
||||
}
|
||||
|
||||
@ -33,7 +33,7 @@ public class JPATokenRepositoryImpl implements PersistentTokenRepository {
|
||||
PersistentLogin existingToken = persistentLoginRepository.findById(series).orElse(null);
|
||||
if (existingToken != null) {
|
||||
existingToken.setToken(tokenValue);
|
||||
existingToken.setLastUsed(lastUsed);
|
||||
existingToken.setLastUsed(lastUsed.toInstant());
|
||||
persistentLoginRepository.save(existingToken);
|
||||
}
|
||||
}
|
||||
@ -43,7 +43,10 @@ public class JPATokenRepositoryImpl implements PersistentTokenRepository {
|
||||
PersistentLogin token = persistentLoginRepository.findById(seriesId).orElse(null);
|
||||
if (token != null) {
|
||||
return new PersistentRememberMeToken(
|
||||
token.getUsername(), token.getSeries(), token.getToken(), token.getLastUsed());
|
||||
token.getUsername(),
|
||||
token.getSeries(),
|
||||
token.getToken(),
|
||||
Date.from(token.getLastUsed()));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
package stirling.software.proprietary.security.database.repository;
|
||||
|
||||
import java.util.Date;
|
||||
import java.time.Instant;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
@ -27,7 +27,7 @@ public interface SessionRepository extends JpaRepository<SessionEntity, String>
|
||||
"UPDATE SessionEntity s SET s.expired = :expired, s.lastRequest = :lastRequest WHERE s.principalName = :principalName")
|
||||
void saveByPrincipalName(
|
||||
@Param("expired") boolean expired,
|
||||
@Param("lastRequest") Date lastRequest,
|
||||
@Param("lastRequest") Instant lastRequest,
|
||||
@Param("principalName") String principalName);
|
||||
|
||||
@Query(
|
||||
|
||||
@ -1,8 +1,9 @@
|
||||
package stirling.software.proprietary.security.filter;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.time.Instant;
|
||||
import java.time.ZoneId;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
@ -60,8 +61,12 @@ public class FirstLoginFilter extends OncePerRequestFilter {
|
||||
}
|
||||
if (log.isDebugEnabled()) {
|
||||
HttpSession session = request.getSession(true);
|
||||
SimpleDateFormat timeFormat = new SimpleDateFormat("HH:mm:ss");
|
||||
String creationTime = timeFormat.format(new Date(session.getCreationTime()));
|
||||
DateTimeFormatter timeFormat = DateTimeFormatter.ofPattern("HH:mm:ss");
|
||||
String creationTime =
|
||||
timeFormat.format(
|
||||
Instant.ofEpochMilli(session.getCreationTime())
|
||||
.atZone(ZoneId.systemDefault())
|
||||
.toLocalTime());
|
||||
log.debug(
|
||||
"Request Info - New: {}, creationTimeSession {}, ID: {}, IP: {}, User-Agent: {}, Referer: {}, Request URL: {}",
|
||||
session.isNew(),
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
package stirling.software.proprietary.security.model;
|
||||
|
||||
import java.util.Date;
|
||||
import java.time.Instant;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
@ -25,5 +25,5 @@ public class PersistentLogin {
|
||||
private String token;
|
||||
|
||||
@Column(name = "last_used", nullable = false)
|
||||
private Date lastUsed;
|
||||
private Instant lastUsed;
|
||||
}
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
package stirling.software.proprietary.security.model;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
import java.time.Instant;
|
||||
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.Id;
|
||||
@ -17,7 +17,7 @@ public class SessionEntity implements Serializable {
|
||||
|
||||
private String principalName;
|
||||
|
||||
private Date lastRequest;
|
||||
private Instant lastRequest;
|
||||
|
||||
private boolean expired;
|
||||
}
|
||||
|
||||
@ -4,6 +4,7 @@ import java.security.KeyPair;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.PublicKey;
|
||||
import java.security.spec.InvalidKeySpecException;
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
@ -92,8 +93,8 @@ public class JwtService implements JwtServiceInterface {
|
||||
.claims(claims)
|
||||
.subject(username)
|
||||
.issuer(ISSUER)
|
||||
.issuedAt(new Date())
|
||||
.expiration(new Date(System.currentTimeMillis() + EXPIRATION))
|
||||
.issuedAt(Date.from(Instant.now()))
|
||||
.expiration(Date.from(Instant.now().plusMillis(EXPIRATION)))
|
||||
.signWith(keyPair.getPrivate(), Jwts.SIG.RS256);
|
||||
|
||||
String keyId = activeKey.getKeyId();
|
||||
@ -129,7 +130,7 @@ public class JwtService implements JwtServiceInterface {
|
||||
|
||||
@Override
|
||||
public boolean isTokenExpired(String token) {
|
||||
return extractExpiration(token).before(new Date());
|
||||
return extractExpiration(token).before(Date.from(Instant.now()));
|
||||
}
|
||||
|
||||
private Date extractExpiration(String token) {
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
package stirling.software.proprietary.security.session;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
@ -67,7 +68,7 @@ public class SessionPersistentRegistry implements SessionRegistry {
|
||||
new SessionInformation(
|
||||
sessionEntity.getPrincipalName(),
|
||||
sessionEntity.getSessionId(),
|
||||
sessionEntity.getLastRequest()));
|
||||
Date.from(sessionEntity.getLastRequest())));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -101,7 +102,7 @@ public class SessionPersistentRegistry implements SessionRegistry {
|
||||
SessionEntity sessionEntity = new SessionEntity();
|
||||
sessionEntity.setSessionId(sessionId);
|
||||
sessionEntity.setPrincipalName(principalName);
|
||||
sessionEntity.setLastRequest(new Date()); // Set lastRequest to the current date
|
||||
sessionEntity.setLastRequest(Instant.now()); // Set lastRequest to the current date
|
||||
sessionEntity.setExpired(false);
|
||||
sessionRepository.save(sessionEntity);
|
||||
}
|
||||
@ -119,7 +120,7 @@ public class SessionPersistentRegistry implements SessionRegistry {
|
||||
Optional<SessionEntity> sessionEntityOpt = sessionRepository.findById(sessionId);
|
||||
if (sessionEntityOpt.isPresent()) {
|
||||
SessionEntity sessionEntity = sessionEntityOpt.get();
|
||||
sessionEntity.setLastRequest(new Date()); // Update lastRequest to the current date
|
||||
sessionEntity.setLastRequest(Instant.now()); // Update lastRequest to the current date
|
||||
sessionRepository.save(sessionEntity);
|
||||
}
|
||||
}
|
||||
@ -132,7 +133,7 @@ public class SessionPersistentRegistry implements SessionRegistry {
|
||||
return new SessionInformation(
|
||||
sessionEntity.getPrincipalName(),
|
||||
sessionEntity.getSessionId(),
|
||||
sessionEntity.getLastRequest());
|
||||
Date.from(sessionEntity.getLastRequest()));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@ -170,7 +171,7 @@ public class SessionPersistentRegistry implements SessionRegistry {
|
||||
// Update session details by principal name
|
||||
public void updateSessionByPrincipalName(
|
||||
String principalName, boolean expired, Date lastRequest) {
|
||||
sessionRepository.saveByPrincipalName(expired, lastRequest, principalName);
|
||||
sessionRepository.saveByPrincipalName(expired, lastRequest.toInstant(), principalName);
|
||||
}
|
||||
|
||||
// Find the latest session for a given principal name
|
||||
|
||||
Loading…
Reference in New Issue
Block a user