Skip to content

Commit de82740

Browse files
Vzor-tresf
andauthored
Add Timezone to Cert (#1292)
Always show timezone in SiteManager dialog Add toggle for current timezone --------- Co-authored-by: Tres Finocchiaro <tres.finocchiaro@gmail.com>
1 parent 7330141 commit de82740

File tree

2 files changed

+99
-47
lines changed

2 files changed

+99
-47
lines changed

src/qz/auth/Certificate.java

+6-7
Original file line numberDiff line numberDiff line change
@@ -63,9 +63,8 @@ public enum Algorithm {
6363
// Valid date range allows UI to only show "Expired" text for valid certificates
6464
private static final Instant UNKNOWN_MIN = LocalDateTime.MIN.toInstant(ZoneOffset.UTC);
6565
private static final Instant UNKNOWN_MAX = LocalDateTime.MAX.toInstant(ZoneOffset.UTC);
66-
67-
private static DateTimeFormatter dateFormat = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
68-
private static DateTimeFormatter dateParse = DateTimeFormatter.ofPattern("uuuu-MM-dd['T'][ ]HH:mm:ss[.n]['Z']"); //allow parsing of both ISO and custom formatted dates
66+
public static final DateTimeFormatter DATE_FORMAT = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
67+
public static final DateTimeFormatter DATE_PARSE = DateTimeFormatter.ofPattern("uuuu-MM-dd['T'][ ]HH:mm:ss[.n]['Z']"); //allow parsing of both ISO and custom formatted dates
6968

7069
private X509Certificate theCertificate;
7170
private boolean sponsored;
@@ -323,8 +322,8 @@ public static Certificate loadCertificate(HashMap<String,String> data) {
323322
cert.organization = data.get("organization");
324323

325324
try {
326-
cert.validFrom = Instant.from(LocalDateTime.from(dateParse.parse(data.get("validFrom"))).atZone(ZoneOffset.UTC));
327-
cert.validTo = Instant.from(LocalDateTime.from(dateParse.parse(data.get("validTo"))).atZone(ZoneOffset.UTC));
325+
cert.validFrom = Instant.from(LocalDateTime.from(DATE_PARSE.parse(data.get("validFrom"))).atZone(ZoneOffset.UTC));
326+
cert.validTo = Instant.from(LocalDateTime.from(DATE_PARSE.parse(data.get("validTo"))).atZone(ZoneOffset.UTC));
328327
}
329328
catch(DateTimeException e) {
330329
cert.validFrom = UNKNOWN_MIN;
@@ -420,15 +419,15 @@ public String getOrganization() {
420419

421420
public String getValidFrom() {
422421
if (validFrom.isAfter(UNKNOWN_MIN)) {
423-
return dateFormat.format(validFrom.atZone(ZoneOffset.UTC));
422+
return DATE_FORMAT.format(validFrom.atZone(ZoneOffset.UTC));
424423
} else {
425424
return "Not Provided";
426425
}
427426
}
428427

429428
public String getValidTo() {
430429
if (validTo.isBefore(UNKNOWN_MAX)) {
431-
return dateFormat.format(validTo.atZone(ZoneOffset.UTC));
430+
return DATE_FORMAT.format(validTo.atZone(ZoneOffset.UTC));
432431
} else {
433432
return "Not Provided";
434433
}

src/qz/ui/component/CertificateTable.java

+93-40
Original file line numberDiff line numberDiff line change
@@ -1,56 +1,67 @@
11
package qz.ui.component;
22

3-
import org.joor.Reflect;
43
import qz.auth.Certificate;
54
import qz.common.Constants;
65
import qz.ui.Themeable;
76

87
import javax.swing.*;
98
import java.awt.*;
10-
import java.time.Instant;
9+
import java.awt.event.*;
10+
import java.time.*;
1111
import java.time.temporal.ChronoUnit;
12+
import java.util.TimeZone;
13+
import java.util.function.Function;
14+
15+
import static qz.auth.Certificate.*;
1216

1317
/**
1418
* Created by Tres on 2/22/2015.
1519
* Displays Certificate information in a JTable
1620
*/
1721
public class CertificateTable extends DisplayTable implements Themeable {
22+
private Certificate cert;
23+
24+
private static final TimeZone DEFAULT_TIME_ZONE = TimeZone.getTimeZone("UTC");
25+
private static final TimeZone ALTERNATE_TIME_ZONE = TimeZone.getDefault();
26+
private Instant warn;
27+
private Instant now;
1828

19-
/**
20-
* Certificate fields to be displayed (and the corresponding function to Reflect upon)
21-
*/
2229
enum CertificateField {
23-
ORGANIZATION("Organization", "getOrganization"),
24-
COMMON_NAME("Common Name", "getCommonName"),
25-
TRUSTED("Trusted", "isTrusted"),
26-
VALID_FROM("Valid From", "getValidFrom"),
27-
VALID_TO("Valid To", "getValidTo"),
28-
FINGERPRINT("Fingerprint", "getFingerprint");
30+
ORGANIZATION("Organization", (Certificate cert) -> cert.getOrganization()),
31+
COMMON_NAME("Common Name", (Certificate cert) -> cert.getCommonName()),
32+
TRUSTED("Trusted", (Certificate cert) -> cert.isTrusted()),
33+
VALID_FROM("Valid From", (Certificate cert) -> cert.getValidFrom()),
34+
VALID_TO("Valid To", (Certificate cert) -> cert.getValidTo()),
35+
FINGERPRINT("Fingerprint", (Certificate cert) -> cert.getFingerprint());
2936

3037
String description;
31-
String callBack;
38+
Function<Certificate, Object> getter;
39+
TimeZone timeZone = DEFAULT_TIME_ZONE; // Date fields only
3240

33-
CertificateField(String description, String callBack) {
41+
CertificateField(String description, Function<Certificate, Object> getter) {
3442
this.description = description;
35-
this.callBack = callBack;
43+
this.getter = getter;
3644
}
3745

38-
/**
39-
* Returns the <code>String</code> value associated with this certificate field
40-
*
41-
* @return Certificate field such as "commonName"
42-
*/
4346
public String getValue(Certificate cert) {
44-
if (cert == null) {
45-
return "";
46-
}
47-
48-
Reflect reflect = Reflect.on(cert).call(callBack);
49-
Object value = reflect == null? null:reflect.get();
50-
if (value == null) {
51-
return "";
47+
String certFieldValue = getter.apply(cert).toString();
48+
switch(this) {
49+
case VALID_FROM:
50+
case VALID_TO:
51+
if (!certFieldValue.equals("Not Provided")) {
52+
try {
53+
// Parse the date string as UTC (Z/GMT)
54+
ZonedDateTime utcTime = LocalDateTime.from(DATE_PARSE.parse(certFieldValue)).atZone(ZoneOffset.UTC);
55+
// Shift to the new timezone
56+
ZonedDateTime zonedTime = Instant.from(utcTime).atZone(timeZone.toZoneId());
57+
// Append a short timezone name e.g. "EST"
58+
return DATE_PARSE.format(zonedTime) + " " + timeZone.getDisplayName(false, TimeZone.SHORT);
59+
} catch (DateTimeException ignore) {}
60+
}
61+
// fallthrough
62+
default:
63+
return certFieldValue;
5264
}
53-
return value.toString();
5465
}
5566

5667
@Override
@@ -65,16 +76,47 @@ public String getDescription() {
6576
public static int size() {
6677
return values().length;
6778
}
68-
}
6979

70-
private Certificate cert;
71-
72-
private Instant warn;
73-
private Instant now;
80+
public void toggleTimeZone() {
81+
switch(this) {
82+
case VALID_TO:
83+
case VALID_FROM:
84+
this.timeZone = (timeZone == DEFAULT_TIME_ZONE? ALTERNATE_TIME_ZONE:DEFAULT_TIME_ZONE);
85+
break;
86+
default:
87+
throw new UnsupportedOperationException("TimeZone is only supported for date fields");
88+
}
89+
}
90+
}
7491

7592
public CertificateTable(IconCache iconCache) {
7693
super(iconCache);
7794
setDefaultRenderer(Object.class, new CertificateTableCellRenderer());
95+
addMouseListener(new MouseAdapter() {
96+
Point loc = new Point(-1, -1);
97+
98+
@Override
99+
public void mousePressed(MouseEvent e) {
100+
super.mousePressed(e);
101+
JTable target = (JTable)e.getSource();
102+
int x = target.getSelectedColumn();
103+
int y = target.getSelectedRow();
104+
// Only trigger after the cell is click AND highlighted.
105+
if (loc.distance(x, y) == 0) {
106+
CertificateField rowKey = (CertificateField)target.getValueAt(y, 0);
107+
switch(rowKey) {
108+
case VALID_FROM:
109+
case VALID_TO:
110+
rowKey.toggleTimeZone();
111+
refreshComponents();
112+
changeSelection(y, x, false, false);
113+
break;
114+
}
115+
}
116+
loc.setLocation(x, y);
117+
}
118+
});
119+
78120
}
79121

80122
public void setCertificate(Certificate cert) {
@@ -116,7 +158,6 @@ public void autoSize() {
116158
super.autoSize(CertificateField.size(), 2);
117159
}
118160

119-
120161
/** Custom cell renderer for JTable to allow colors and styles not directly available in a JTable */
121162
private class CertificateTableCellRenderer extends StyledTableCellRenderer {
122163

@@ -126,7 +167,22 @@ public Component getTableCellRendererComponent(JTable table, Object value, boole
126167

127168
// First Column
128169
if (value instanceof CertificateField) {
129-
label = stylizeLabel(STATUS_NORMAL, label, isSelected);
170+
switch((CertificateField)value) {
171+
case VALID_FROM:
172+
boolean futureExpiration = cert.getValidFromDate().isAfter(now);
173+
label = stylizeLabel(futureExpiration? STATUS_WARNING:STATUS_NORMAL, label, isSelected, "future inception");
174+
break;
175+
case VALID_TO:
176+
boolean expiresSoon = cert.getValidToDate().isBefore(warn);
177+
boolean expired = cert.getValidToDate().isBefore(now);
178+
String reason = expired? "expired":(expiresSoon? "expires soon":null);
179+
180+
label = stylizeLabel(expiresSoon || expired? STATUS_WARNING:STATUS_NORMAL, label, isSelected, reason);
181+
break;
182+
default:
183+
label = stylizeLabel(STATUS_NORMAL, label, isSelected);
184+
break;
185+
}
130186
if (iconCache != null) {
131187
label.setIcon(iconCache.getIcon(IconCache.Icon.FIELD_ICON));
132188
}
@@ -153,17 +209,14 @@ public Component getTableCellRendererComponent(JTable table, Object value, boole
153209
return stylizeLabel(!cert.isValid()? STATUS_WARNING:STATUS_TRUSTED, label, isSelected);
154210
case VALID_FROM:
155211
boolean futureExpiration = cert.getValidFromDate().isAfter(now);
156-
return stylizeLabel(futureExpiration? STATUS_WARNING:STATUS_NORMAL, label, isSelected, "future inception");
212+
return stylizeLabel(futureExpiration? STATUS_WARNING:STATUS_NORMAL, label, isSelected);
157213
case VALID_TO:
158214
boolean expiresSoon = cert.getValidToDate().isBefore(warn);
159215
boolean expired = cert.getValidToDate().isBefore(now);
160-
String reason = expired? "expired":(expiresSoon? "expires soon":null);
161-
return stylizeLabel(expiresSoon || expired? STATUS_WARNING:STATUS_NORMAL, label, isSelected, reason);
216+
return stylizeLabel(expiresSoon || expired? STATUS_WARNING:STATUS_NORMAL, label, isSelected);
162217
default:
163218
return stylizeLabel(STATUS_NORMAL, label, isSelected);
164219
}
165220
}
166-
167221
}
168-
169222
}

0 commit comments

Comments
 (0)