8147772: Update KerberosTicket to describe behavior if it has been destroyed and fix NullPointerExceptions
Reviewed-by: mullan
--- a/jdk/src/java.security.jgss/share/classes/javax/security/auth/kerberos/KerberosTicket.java Tue Feb 16 11:06:19 2016 -0800
+++ b/jdk/src/java.security.jgss/share/classes/javax/security/auth/kerberos/KerberosTicket.java Wed Feb 17 11:23:48 2016 +0800
@@ -64,6 +64,9 @@
* other regular service tickets. A ticket granting ticket is just a
* special case of a more generalized service ticket.
*
+ * @implNote The JAAS login module in the JDK reference implementation destroys
+ * all tickets after logout.
+ *
* @see javax.security.auth.Subject
* @see javax.security.auth.PrivateCredentialPermission
* @see javax.security.auth.login.LoginContext
@@ -335,7 +338,7 @@
/**
* Returns the client principal associated with this ticket.
*
- * @return the client principal.
+ * @return the client principal, or {@code null} if destroyed.
*/
public final KerberosPrincipal getClient() {
return client;
@@ -344,7 +347,7 @@
/**
* Returns the service principal associated with this ticket.
*
- * @return the service principal.
+ * @return the service principal, or {@code null} if destroyed.
*/
public final KerberosPrincipal getServer() {
return server;
@@ -355,6 +358,7 @@
* is always a {@link EncryptionKey} object.
*
* @return the session key.
+ * @throws IllegalStateException if this ticket is destroyed
*/
public final SecretKey getSessionKey() {
if (destroyed) {
@@ -370,6 +374,7 @@
*
* @return the key type of the session key associated with this
* ticket.
+ * @throws IllegalStateException if this ticket is destroyed
*
* @see #getSessionKey()
*/
@@ -383,10 +388,11 @@
/**
* Determines if this ticket is forwardable.
*
- * @return true if this ticket is forwardable, false if not.
+ * @return true if this ticket is forwardable, or false if not forwardable
+ * or destroyed.
*/
public final boolean isForwardable() {
- return flags[FORWARDABLE_TICKET_FLAG];
+ return flags == null? false: flags[FORWARDABLE_TICKET_FLAG];
}
/**
@@ -395,38 +401,41 @@
*
* @return true if this ticket had been forwarded or was issued based on
* authentication involving a forwarded ticket-granting ticket,
- * false otherwise.
+ * or false otherwise or destroyed.
*/
public final boolean isForwarded() {
- return flags[FORWARDED_TICKET_FLAG];
+ return flags == null? false: flags[FORWARDED_TICKET_FLAG];
}
/**
* Determines if this ticket is proxiable.
*
- * @return true if this ticket is proxiable, false if not.
+ * @return true if this ticket is proxiable, or false if not proxiable
+ * or destroyed.
*/
public final boolean isProxiable() {
- return flags[PROXIABLE_TICKET_FLAG];
+ return flags == null? false: flags[PROXIABLE_TICKET_FLAG];
}
/**
* Determines is this ticket is a proxy-ticket.
*
- * @return true if this ticket is a proxy-ticket, false if not.
+ * @return true if this ticket is a proxy-ticket, or false if not
+ * a proxy-ticket or destroyed.
*/
public final boolean isProxy() {
- return flags[PROXY_TICKET_FLAG];
+ return flags == null? false: flags[PROXY_TICKET_FLAG];
}
/**
* Determines is this ticket is post-dated.
*
- * @return true if this ticket is post-dated, false if not.
+ * @return true if this ticket is post-dated, or false if not post-dated
+ * or destroyed.
*/
public final boolean isPostdated() {
- return flags[POSTDATED_TICKET_FLAG];
+ return flags == null? false: flags[POSTDATED_TICKET_FLAG];
}
/**
@@ -434,10 +443,11 @@
* refresh} method can be called, assuming the validity period for
* renewing is not already over.
*
- * @return true if this ticket is renewable, false if not.
+ * @return true if this ticket is renewable, or false if not renewable
+ * or destroyed.
*/
public final boolean isRenewable() {
- return flags[RENEWABLE_TICKET_FLAG];
+ return flags == null? false: flags[RENEWABLE_TICKET_FLAG];
}
/**
@@ -445,10 +455,10 @@
* protocol, and not issued based on some ticket-granting ticket.
*
* @return true if this ticket was issued using the Kerberos AS-Exchange
- * protocol, false if not.
+ * protocol, or false if not issued this way or destroyed.
*/
public final boolean isInitial() {
- return flags[INITIAL_TICKET_FLAG];
+ return flags == null? false: flags[INITIAL_TICKET_FLAG];
}
/**
@@ -456,7 +466,8 @@
* returned array indicates the value for the corresponding bit in the
* ASN.1 BitString that represents the ticket flags.
*
- * @return the flags associated with this ticket.
+ * @return the flags associated with this ticket, or {@code null}
+ * if destroyed.
*/
public final boolean[] getFlags() {
return (flags == null? null: flags.clone());
@@ -466,7 +477,8 @@
* Returns the time that the client was authenticated.
*
* @return the time that the client was authenticated
- * or null if not set.
+ * or {@code null} if the field is not set or
+ * this ticket is destroyed.
*/
public final java.util.Date getAuthTime() {
return (authTime == null) ? null : (Date)authTime.clone();
@@ -476,7 +488,8 @@
* Returns the start time for this ticket's validity period.
*
* @return the start time for this ticket's validity period
- * or null if not set.
+ * or {@code null} if the field is not set or
+ * this ticket is destroyed.
*/
public final java.util.Date getStartTime() {
return (startTime == null) ? null : (Date)startTime.clone();
@@ -485,17 +498,19 @@
/**
* Returns the expiration time for this ticket's validity period.
*
- * @return the expiration time for this ticket's validity period.
+ * @return the expiration time for this ticket's validity period,
+ * or {@code null} if destroyed.
*/
public final java.util.Date getEndTime() {
- return (Date) endTime.clone();
+ return (endTime == null) ? null : (Date) endTime.clone();
}
/**
* Returns the latest expiration time for this ticket, including all
* renewals. This will return a null value for non-renewable tickets.
*
- * @return the latest expiration time for this ticket.
+ * @return the latest expiration time for this ticket, or {@code null}
+ * if destroyed.
*/
public final java.util.Date getRenewTill() {
return (renewTill == null) ? null: (Date)renewTill.clone();
@@ -504,8 +519,8 @@
/**
* Returns a list of addresses from where the ticket can be used.
*
- * @return ths list of addresses or null, if the field was not
- * provided.
+ * @return the list of addresses, or {@code null} if the field was not
+ * provided or this ticket is destroyed.
*/
public final java.net.InetAddress[] getClientAddresses() {
return (clientAddresses == null) ? null: clientAddresses.clone();
@@ -514,7 +529,9 @@
/**
* Returns an ASN.1 encoding of the entire ticket.
*
- * @return an ASN.1 encoding of the entire ticket.
+ * @return an ASN.1 encoding of the entire ticket. A new byte
+ * array is returned each time this method is called.
+ * @throws IllegalStateException if this ticket is destroyed
*/
public final byte[] getEncoded() {
if (destroyed) {
@@ -523,9 +540,14 @@
return asn1Encoding.clone();
}
- /** Determines if this ticket is still current. */
+ /**
+ * Determines if this ticket is still current.
+ *
+ * @return true if this ticket is still current, or false if not current
+ * or destroyed.
+ */
public boolean isCurrent() {
- return (System.currentTimeMillis() <= getEndTime().getTime());
+ return endTime == null? false: (System.currentTimeMillis() <= endTime.getTime());
}
/**
@@ -540,6 +562,7 @@
* threads that might access this and try to renew it at the same
* time.
*
+ * @throws IllegalStateException if this ticket is destroyed
* @throws RefreshFailedException if the ticket is not renewable, or
* the latest allowable renew time has passed, or the KDC returns some
* error.
--- a/jdk/test/javax/security/auth/kerberos/KerberosTixDateTest.java Tue Feb 16 11:06:19 2016 -0800
+++ b/jdk/test/javax/security/auth/kerberos/KerberosTixDateTest.java Wed Feb 17 11:23:48 2016 +0800
@@ -23,18 +23,21 @@
/*
* @test
- * @bug 6659990
- * @summary test the immutability of the Date fields in KerberosTicket class.
+ * @bug 6659990 8147772
+ * @summary test the immutability of the Date fields in KerberosTicket class,
+ * serialization, and behavior after being destroyed.
*/
/*
* Must setup KDC and Kerberos configuration file
*/
-import java.net.InetAddress;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
import java.util.Date;
import java.io.*;
-import javax.security.auth.kerberos.KerberosKey;
+import javax.security.auth.RefreshFailedException;
import javax.security.auth.kerberos.KerberosPrincipal;
import javax.security.auth.kerberos.KerberosTicket;
import java.util.Base64;
@@ -78,6 +81,7 @@
testDateImmutability(t, originalTime);
testS11nCompatibility(t); // S11n: Serialization
+ testDestroy(t);
}
private static void checkTime(KerberosTicket kt, long timeValue) {
@@ -140,4 +144,30 @@
System.out.println("S11nCompatibility Test Passed");
}
+
+ private static void testDestroy(KerberosTicket t) throws Exception {
+ t.destroy();
+ if (!t.isDestroyed()) {
+ throw new RuntimeException("ticket should have been destroyed");
+ }
+ // Although these methods are meaningless, they can be called
+ for (Method m: KerberosTicket.class.getDeclaredMethods()) {
+ if (Modifier.isPublic(m.getModifiers())
+ && m.getParameterCount() == 0) {
+ System.out.println("Testing " + m.getName() + "...");
+ try {
+ m.invoke(t);
+ } catch (InvocationTargetException e) {
+ Throwable cause = e.getCause();
+ if (cause instanceof RefreshFailedException ||
+ cause instanceof IllegalStateException) {
+ // this is OK
+ } else {
+ throw e;
+ }
+ }
+ }
+ }
+ System.out.println("Destroy Test Passed");
+ }
}