2626
2727import edu .umd .cs .findbugs .annotations .CheckForNull ;
2828import edu .umd .cs .findbugs .annotations .NonNull ;
29+ import edu .umd .cs .findbugs .annotations .Nullable ;
2930import hudson .Extension ;
3031import hudson .Util ;
3132import hudson .model .Descriptor .FormException ;
@@ -255,13 +256,20 @@ public static class TokenInfoAndStats {
255256 public final int useCounter ;
256257 public final Date lastUseDate ;
257258 public final long numDaysUse ;
259+ public final String expirationDate ;
258260
259261 public TokenInfoAndStats (@ NonNull ApiTokenStore .HashedToken token , @ NonNull ApiTokenStats .SingleTokenStats stats ) {
260262 this .uuid = token .getUuid ();
261263 this .name = token .getName ();
262264 this .creationDate = token .getCreationDate ();
263265 this .numDaysCreation = token .getNumDaysCreation ();
264266 this .isLegacy = token .isLegacy ();
267+ LocalDate expirationDate = token .getExpirationDate ();
268+ if (expirationDate == null ) {
269+ this .expirationDate = "never" ;
270+ } else {
271+ this .expirationDate = expirationDate .toString ();
272+ }
265273
266274 this .useCounter = stats .getUseCounter ();
267275 this .lastUseDate = stats .getLastUseDate ();
@@ -424,15 +432,27 @@ public ApiTokenStats getTokenStats() {
424432 // essentially meant for scripting
425433 @ Restricted (Beta .class )
426434 public @ NonNull String addFixedNewToken (@ NonNull String name , @ NonNull String tokenPlainValue ) throws IOException {
427- String tokenUuid = this .tokenStore .addFixedNewToken (name , tokenPlainValue );
435+ return addFixedNewToken (name , tokenPlainValue , null );
436+ }
437+
438+ // essentially meant for scripting
439+ @ Restricted (Beta .class )
440+ public @ NonNull String addFixedNewToken (@ NonNull String name , @ NonNull String tokenPlainValue , @ Nullable LocalDate expirationDate ) throws IOException {
441+ String tokenUuid = this .tokenStore .addFixedNewToken (name , tokenPlainValue , expirationDate );
428442 user .save ();
429443 return tokenUuid ;
430444 }
431445
432446 // essentially meant for scripting
433447 @ Restricted (Beta .class )
434448 public @ NonNull TokenUuidAndPlainValue generateNewToken (@ NonNull String name ) throws IOException {
435- TokenUuidAndPlainValue tokenUuidAndPlainValue = tokenStore .generateNewToken (name );
449+ return generateNewToken (name , null );
450+ }
451+
452+ // essentially meant for scripting
453+ @ Restricted (Beta .class )
454+ public @ NonNull TokenUuidAndPlainValue generateNewToken (@ NonNull String name , @ Nullable LocalDate expirationDate ) throws IOException {
455+ TokenUuidAndPlainValue tokenUuidAndPlainValue = tokenStore .generateNewToken (name , expirationDate );
436456 user .save ();
437457 return tokenUuidAndPlainValue ;
438458 }
@@ -535,7 +555,7 @@ public boolean hasCurrentUserRightToGenerateNewToken(User propertyOwner) {
535555 }
536556
537557 /**
538- * @deprecated use {@link #doGenerateNewToken(User, String)} instead
558+ * @deprecated use {@link #doGenerateNewToken(User, String, String, String )} instead
539559 */
540560 @ Deprecated
541561 @ RequirePOST
@@ -567,7 +587,8 @@ public HttpResponse doChangeToken(@AncestorInPath User u, StaplerResponse rsp) t
567587 }
568588
569589 @ RequirePOST
570- public HttpResponse doGenerateNewToken (@ AncestorInPath User u , @ QueryParameter String newTokenName ) throws IOException {
590+ public HttpResponse doGenerateNewToken (@ AncestorInPath User u , @ QueryParameter String newTokenName ,
591+ @ QueryParameter String tokenExpiration , @ QueryParameter String expirationDuration ) throws IOException {
571592 if (!hasCurrentUserRightToGenerateNewToken (u )) {
572593 return HttpResponses .forbidden ();
573594 }
@@ -579,21 +600,49 @@ public HttpResponse doGenerateNewToken(@AncestorInPath User u, @QueryParameter S
579600 tokenName = newTokenName ;
580601 }
581602
603+ LocalDate expirationDate = getExpirationDate (tokenExpiration , expirationDuration );
604+
582605 ApiTokenProperty p = u .getProperty (ApiTokenProperty .class );
583606 if (p == null ) {
584607 p = forceNewInstance (u , false );
585608 u .addProperty (p );
586609 }
587610
588- TokenUuidAndPlainValue tokenUuidAndPlainValue = p .generateNewToken (tokenName );
611+ TokenUuidAndPlainValue tokenUuidAndPlainValue = p .generateNewToken (tokenName , expirationDate );
612+ String expirationDateString = "never" ;
613+ if (expirationDate != null ) {
614+ expirationDateString = expirationDate .toString ();
615+ }
589616
590617 Map <String , String > data = new HashMap <>();
591618 data .put ("tokenUuid" , tokenUuidAndPlainValue .tokenUuid );
592619 data .put ("tokenName" , tokenName );
593620 data .put ("tokenValue" , tokenUuidAndPlainValue .plainValue );
621+ data .put ("expirationDate" , expirationDateString );
594622 return HttpResponses .okJSON (data );
595623 }
596624
625+ private LocalDate getExpirationDate (String tokenExpiration , String expirationDuration ) {
626+ if (expirationDuration == null ) {
627+ expirationDuration = "" ;
628+ }
629+ expirationDuration = expirationDuration .trim ();
630+
631+ return switch (expirationDuration ) {
632+ case "" , "never" -> {
633+ yield null ;
634+ }
635+ case "custom" -> {
636+ yield LocalDate .parse (tokenExpiration );
637+ }
638+ default -> {
639+ LocalDate now = LocalDate .now ();
640+ int days = Integer .parseInt (expirationDuration );
641+ yield now .plusDays (days );
642+ }
643+ };
644+
645+ }
597646 /**
598647 * This method is dangerous and should not be used without caution.
599648 * The token passed here could have been tracked by different network system during its trip.
@@ -603,7 +652,9 @@ public HttpResponse doGenerateNewToken(@AncestorInPath User u, @QueryParameter S
603652 @ Restricted (NoExternalUse .class )
604653 public HttpResponse doAddFixedToken (@ AncestorInPath User u ,
605654 @ QueryParameter String newTokenName ,
606- @ QueryParameter String newTokenPlainValue ) throws IOException {
655+ @ QueryParameter String newTokenPlainValue ,
656+ @ QueryParameter String tokenExpiration ,
657+ @ QueryParameter String expirationDuration ) throws IOException {
607658 if (!hasCurrentUserRightToGenerateNewToken (u )) {
608659 return HttpResponses .forbidden ();
609660 }
@@ -615,13 +666,15 @@ public HttpResponse doAddFixedToken(@AncestorInPath User u,
615666 tokenName = newTokenName ;
616667 }
617668
669+ LocalDate expirationDate = getExpirationDate (tokenExpiration , expirationDuration );
670+
618671 ApiTokenProperty p = u .getProperty (ApiTokenProperty .class );
619672 if (p == null ) {
620673 p = forceNewInstance (u , false );
621674 u .addProperty (p );
622675 }
623676
624- String tokenUuid = p .tokenStore .addFixedNewToken (tokenName , newTokenPlainValue );
677+ String tokenUuid = p .tokenStore .addFixedNewToken (tokenName , newTokenPlainValue , expirationDate );
625678 u .save ();
626679
627680 Map <String , String > data = new HashMap <>();
0 commit comments