提交 5f7c50cd 编写于 作者: W Wadeck Follonier

[JENKINS-52161] Improve creation-date value determination

- in case of migration we put null instead of now
- improve also the difference in days for the Xxx day(s) ago to take calendar day in difference and not the total duration (<24h = 0 days, which seems wrong in UI)
上级 47bd0da2
......@@ -23,6 +23,7 @@
*/
package hudson;
import com.sun.istack.internal.NotNull;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import hudson.model.TaskListener;
......@@ -74,6 +75,9 @@ import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.text.NumberFormat;
import java.text.ParseException;
import java.time.LocalDate;
import java.time.Period;
import java.time.ZoneId;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
......@@ -1662,7 +1666,27 @@ public class Util {
throw new IOException(e);
}
}
/**
* Compute the number of calendar days elapsed since the given date.
* As it's only the calendar days difference that matter, "11.00pm" to "2.00am the day after" returns 1,
* even if there are only 3 hours between. As well as "10am" to "2pm" both on the same day, returns 0.
*/
@Restricted(NoExternalUse.class)
public static int differenceInCalendarDay(@Nonnull Date a, @Nonnull Date b){
LocalDate aLocal = a.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
LocalDate bLocal = b.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
return Period.between(aLocal, bLocal).getDays();
}
/**
* @see #differenceInCalendarDay(Date, Date)
*/
@Restricted(NoExternalUse.class)
public static int numOfCalendarDayToNow(@Nonnull Date date){
return differenceInCalendarDay(date, new Date());
}
public static final FastDateFormat XS_DATETIME_FORMATTER = FastDateFormat.getInstance("yyyy-MM-dd'T'HH:mm:ss'Z'",new SimpleTimeZone(0,"GMT"));
// Note: RFC822 dates must not be localized!
......
......@@ -24,6 +24,7 @@
package jenkins.security.apitoken;
import hudson.BulkChange;
import hudson.Util;
import hudson.XmlFile;
import hudson.model.Saveable;
import hudson.model.listeners.SaveableListener;
......@@ -33,7 +34,11 @@ import org.kohsuke.accmod.restrictions.NoExternalUse;
import javax.annotation.Nonnull;
import java.io.File;
import java.io.IOException;
import java.time.Duration;
import java.time.Instant;
import java.time.LocalDate;
import java.time.Period;
import java.time.ZoneId;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.Comparator;
......@@ -251,13 +256,7 @@ public class ApiTokenStats implements Saveable {
* Relevant only if the lastUseDate is not null
*/
public long getNumDaysUse() {
return lastUseDate == null ? 0 : computeDeltaDays(lastUseDate.toInstant(), Instant.now());
}
private long computeDeltaDays(Instant a, Instant b) {
long deltaDays = ChronoUnit.DAYS.between(a, b);
deltaDays = Math.max(0, deltaDays);
return deltaDays;
return lastUseDate == null ? 0 : Math.max(0, Util.numOfCalendarDayToNow(lastUseDate));
}
}
}
......@@ -124,7 +124,7 @@ public class ApiTokenStore {
* Remove the legacy token present and generate a new one using the given secret.
*/
public synchronized void regenerateTokenFromLegacy(@Nonnull Secret newLegacyApiToken) {
deleteAllLegacyAndGenerateNewOne(newLegacyApiToken);
deleteAllLegacyAndGenerateNewOne(newLegacyApiToken, false);
}
/**
......@@ -134,13 +134,13 @@ public class ApiTokenStore {
*/
public synchronized void regenerateTokenFromLegacyIfRequired(@Nonnull Secret newLegacyApiToken) {
if(tokenList.stream().noneMatch(HashedToken::isLegacy)){
deleteAllLegacyAndGenerateNewOne(newLegacyApiToken);
deleteAllLegacyAndGenerateNewOne(newLegacyApiToken, true);
}
}
private void deleteAllLegacyAndGenerateNewOne(@Nonnull Secret newLegacyApiToken) {
private void deleteAllLegacyAndGenerateNewOne(@Nonnull Secret newLegacyApiToken, boolean migrationFromExistingLegacy) {
deleteAllLegacyTokens();
addLegacyToken(newLegacyApiToken);
addLegacyToken(newLegacyApiToken, migrationFromExistingLegacy);
}
private void deleteAllLegacyTokens() {
......@@ -148,13 +148,13 @@ public class ApiTokenStore {
tokenList.removeIf(HashedToken::isLegacy);
}
private void addLegacyToken(@Nonnull Secret legacyToken) {
private void addLegacyToken(@Nonnull Secret legacyToken, boolean migrationFromExistingLegacy) {
String tokenUserUseNormally = Util.getDigestOf(legacyToken.getPlainText());
String secretValueHashed = this.plainSecretToHashInHex(tokenUserUseNormally);
HashValue hashValue = new HashValue(LEGACY_VERSION, secretValueHashed);
HashedToken token = HashedToken.buildNew(Messages.ApiTokenProperty_LegacyTokenName(), hashValue);
HashedToken token = HashedToken.buildNewFromLegacy(hashValue, migrationFromExistingLegacy);
this.addToken(token);
}
......@@ -371,6 +371,22 @@ public class ApiTokenStore {
return result;
}
public static @Nonnull HashedToken buildNewFromLegacy(@Nonnull HashValue value, boolean migrationFromExistingLegacy) {
HashedToken result = new HashedToken();
result.name = Messages.ApiTokenProperty_LegacyTokenName();
if(migrationFromExistingLegacy){
// we do not know when the legacy token was created
result.creationDate = null;
}else{
// it comes from a manual action, so we set the creation date to now
result.creationDate = new Date();
}
result.value = value;
return result;
}
public void rename(String newName) {
this.name = newName;
......@@ -404,7 +420,7 @@ public class ApiTokenStore {
* Relevant only if the lastUseDate is not null
*/
public long getNumDaysCreation() {
return creationDate == null ? 0 : computeDeltaDays(creationDate.toInstant(), Instant.now());
return creationDate == null ? 0 : Math.max(0, Util.numOfCalendarDayToNow(creationDate));
}
// used by Jelly view
......@@ -412,12 +428,6 @@ public class ApiTokenStore {
return this.uuid;
}
private long computeDeltaDays(Instant a, Instant b) {
long deltaDays = ChronoUnit.DAYS.between(a, b);
deltaDays = Math.max(0, deltaDays);
return deltaDays;
}
public boolean isLegacy() {
return this.value.version.equals(LEGACY_VERSION);
}
......
......@@ -58,20 +58,26 @@ THE SOFTWARE.
<j:forEach var="user" items="${userList}">
<j:set var="legacyToken" value="${it.getLegacyTokenOf(user)}"/>
<j:set var="legacyStats" value="${it.getLegacyStatsOf(user, legacyToken)}"/>
<j:set var="creationDateFormat" value="${%NoCreationDate}" />
<j:set var="creationDateValue" value="${%NoCreationDateValue}" />
<j:if test="${legacyToken.creationDate != null}">
<i:formatDate var="creationDateFormat" value="${legacyToken.creationDate}" type="both" dateStyle="medium" timeStyle="medium" />
<!--TODO convert to "Today", "Tomorrow", "4 days ago", "2 weeks ago", etc-->
<j:set var="creationDateValue" value="${legacyStats.numDaysCreation}" />
</j:if>
<j:set var="lastUseDateFormat" value="${%NoLastUse}" />
<j:set var="lastUseDateValue" value="${%NoLastUseValue}" />
<j:if test="${legacyStats.lastUseDate != null}">
<i:formatDate var="lastUseDateFormat" value="${legacyStats.lastUseDate}" type="both" dateStyle="medium" timeStyle="medium" />
<!--TODO convert to "Today", "Tomorrow", "4 days ago", "2 weeks ago", etc-->
<j:set var="lastUseDateValue" value="${legacyStats.numDaysUse}" />
</j:if>
<j:set var="hasFreshToken"
value="${it.hasFreshToken(user, legacyStats)}"/>
<j:set var="hasMoreRecentlyUsedToken"
value="${it.hasMoreRecentlyUsedToken(user, legacyStats)}"/>
......@@ -94,13 +100,13 @@ THE SOFTWARE.
${legacyToken.name}
</td>
<td title="${creationDateFormat}">
${legacyStats.numDaysCreation}
${creationDateValue}
</td>
<td>
${legacyStats.useCounter}
</td>
<td title="${lastUseDateFormat}">
${legacyStats.numDaysUse}
${lastUseDateValue}
</td>
<td>
<j:choose>
......@@ -138,14 +144,14 @@ THE SOFTWARE.
</j:otherwise>
</j:choose>
</table>
<div class="selection-panel">
Select:
<a href="#" class="action" onclick="selectAll(this);return false;">all</a>
<a href="#" class="action" onclick="selectFresh(this);return false;">only fresh</a>
<a href="#" class="action" onclick="selectRecent(this);return false;">only recent</a>
</div>
<div class="action-panel">
<span class="yui-button">
<button class="action-revoke-selected" onclick="confirmAndRevokeAllSelected(this);"
......
......@@ -31,7 +31,9 @@ HasMoreRecentlyUsedToken_ok_tooltip=A recent token exist for that user
HasMoreRecentlyUsedToken_warning_tooltip=No recent token exist for that user
NoImpactedUser=There are no users with a legacy token
NoCreationDate=There is no creation date for that token
NoCreationDateValue=Unknown
NoLastUse=There is no last use date for that token
NoLastUseValue=Never used
RevokeAllSelected=Revoke the selected token(s)
RevokeAllSelected_confirm=Are you sure about revoking all %num% selected token(s)
RevokeAllSelected_nothing=No token is selected, please select at least one to revoke
......@@ -24,6 +24,7 @@
*/
package hudson;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.HashMap;
......@@ -781,4 +782,20 @@ public class UtilTest {
assertEquals(0000, Util.permissionsToMode(PosixFilePermissions.fromString("---------")));
}
@Test
public void testDifferenceDays() {
Date day1_10am = new Date(2018, 4, 6, 10, 0);
Date day1_11pm55 = new Date(2018, 4, 6, 23, 55);
Date day2_01am = new Date(2018, 4, 7, 1, 0);
Date day2_11pm = new Date(2018, 4, 7, 1, 0);
Date day3_08am = new Date(2018, 4, 8, 8, 0);
assertEquals(0, Util.differenceInCalendarDay(day1_10am, day1_11pm55));
assertEquals(1, Util.differenceInCalendarDay(day1_10am, day2_01am));
assertEquals(1, Util.differenceInCalendarDay(day1_11pm55, day2_01am));
assertEquals(2, Util.differenceInCalendarDay(day1_10am, day3_08am));
assertEquals(1, Util.differenceInCalendarDay(day2_11pm, day3_08am));
// reverse order
assertEquals(-1, Util.differenceInCalendarDay(day3_08am, day2_11pm));
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册