diff --git a/pom.xml b/pom.xml index 1a02dfcaf..9b0d9b3ee 100644 --- a/pom.xml +++ b/pom.xml @@ -5,8 +5,11 @@ org.mitre openid OpenIdConnect - war + pom 0.1 + + spring-security-oauth/spring-security-oauth2 + 1.6 3.0.6.RELEASE @@ -304,7 +307,7 @@ - pushEE + openid-connect-server true diff --git a/src/main/java/org/mitre/jwt/model/ClaimSet.java b/src/main/java/org/mitre/jwt/model/ClaimSet.java new file mode 100644 index 000000000..9107afea7 --- /dev/null +++ b/src/main/java/org/mitre/jwt/model/ClaimSet.java @@ -0,0 +1,117 @@ +package org.mitre.jwt.model; + +import java.util.Date; +import java.util.LinkedHashMap; +import java.util.Map; + +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonPrimitive; + +public class ClaimSet { + + private Map claims = new LinkedHashMap(); + + /** + * Get an extension claim + */ + public Object getClaim(String key) { + return claims.get(key); + } + + /** + * Get a claim as a string + */ + public String getClaimAsString(String key) { + Object v = claims.get(key); + if (v != null) { + return v.toString(); + } else { + return null; + } + } + + /** + * Get a claim as a Date + */ + public Date getClaimAsDate(String key) { + Object v = claims.get(key); + if (v != null) { + if (v instanceof Date) { + return (Date) v; + } else if (v instanceof Long) { + return new Date((Long) v); + } else { + return null; + } + } else { + return null; + } + } + + /** + * Set an extension claim + */ + public void setClaim(String key, Object value) { + claims.put(key, value); + } + + /** + * Set a primitive claim + */ + public void setClaim(String key, JsonPrimitive prim) { + if (prim.isBoolean()) { + claims.put(key, prim.getAsBoolean()); + } else if (prim.isNumber()) { + claims.put(key, prim.getAsNumber()); + } else if (prim.isString()) { + claims.put(key, prim.getAsString()); + } + } + + /** + * Remove an extension claim + */ + public Object removeClaim(String key) { + return claims.remove(key); + } + + + /** + * Get a copy of this claim set as a JsonObject. The JsonObject is not + * backed by a live copy of this ClaimSet. + * @return a copy of the data in this header in a JsonObject + */ + public JsonObject getAsJsonObject() { + JsonObject o = new JsonObject(); + + if (this.claims != null) { + for (Map.Entry claim : this.claims.entrySet()) { + if (claim.getValue() instanceof JsonElement) { + o.add(claim.getKey(), (JsonElement)claim.getValue()); + } else if (claim.getValue() instanceof String) { + o.addProperty(claim.getKey(), (String)claim.getValue()); + } else if (claim.getValue() instanceof Number) { + o.addProperty(claim.getKey(), (Number)claim.getValue()); + } else if (claim.getValue() instanceof Boolean) { + o.addProperty(claim.getKey(), (Boolean)claim.getValue()); + } else if (claim.getValue() instanceof Character) { + o.addProperty(claim.getKey(), (Character)claim.getValue()); + } else if (claim.getValue() instanceof Date) { + // dates get serialized out as integers + o.addProperty(claim.getKey(), ((Date)claim.getValue()).getTime() / 1000L); + } else if (claim.getValue() != null) { + // try to put it in as a string + o.addProperty(claim.getKey(), claim.getValue().toString()); + } else { + // otherwise add in as a null + o.add(claim.getKey(), null); + } + } + } + + return o; + } + + +} diff --git a/src/main/java/org/mitre/jwt/model/JwtClaims.java b/src/main/java/org/mitre/jwt/model/JwtClaims.java index ef2136183..c98a5a726 100644 --- a/src/main/java/org/mitre/jwt/model/JwtClaims.java +++ b/src/main/java/org/mitre/jwt/model/JwtClaims.java @@ -12,34 +12,21 @@ import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.google.gson.JsonPrimitive; -public class JwtClaims { +public class JwtClaims extends ClaimSet { + public static final String TYPE = "typ"; + public static final String JWT_ID = "jti"; + public static final String PRINCIPAL = "prn"; + public static final String AUDIENCE = "aud"; + public static final String ISSUER = "iss"; + public static final String ISSUED_AT = "iat"; + public static final String NOT_BEFORE = "nbf"; + public static final String EXPIRATION = "exp"; + /** * ISO8601 / RFC3339 Date Format */ - public static DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssz"); - - /* - * TODO: Should we instead be using a generic claims map with well-named accessor methods? - */ - - private Date expiration; - - private Date notBefore; - - private Date issuedAt; - - private String issuer; - - private String audience; - - private String principal; - - private String jwtId; - - private String type; - - private Map claims = new HashMap(); + //public static DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssz"); public JwtClaims() { @@ -47,36 +34,28 @@ public class JwtClaims { public JwtClaims(JsonObject json) { for (Entry element : json.entrySet()) { - if (element.getKey().equals("exp")) { - expiration = new Date(element.getValue().getAsLong() * 1000L); - } else if (element.getKey().equals("nbf")) { - notBefore = new Date(element.getValue().getAsLong() * 1000L); - } else if (element.getKey().equals("iat")) { - issuedAt = new Date(element.getValue().getAsLong() * 1000L); - } else if (element.getKey().equals("iss")) { - issuer = element.getValue().getAsString(); - } else if (element.getKey().equals("aud")) { - audience = element.getValue().getAsString(); - } else if (element.getKey().equals("prn")) { - principal = element.getValue().getAsString(); - } else if (element.getKey().equals("jti")) { - jwtId = element.getValue().getAsString(); - } else if (element.getKey().equals("typ")) { - type = element.getValue().getAsString(); + if (element.getKey().equals(EXPIRATION)) { + setExpiration(new Date(element.getValue().getAsLong() * 1000L)); + } else if (element.getKey().equals(NOT_BEFORE)) { + setNotBefore(new Date(element.getValue().getAsLong() * 1000L)); + } else if (element.getKey().equals(ISSUED_AT)) { + setIssuedAt(new Date(element.getValue().getAsLong() * 1000L)); + } else if (element.getKey().equals(ISSUER)) { + setIssuer(element.getValue().getAsString()); + } else if (element.getKey().equals(AUDIENCE)) { + setAudience(element.getValue().getAsString()); + } else if (element.getKey().equals(PRINCIPAL)) { + setPrincipal(element.getValue().getAsString()); + } else if (element.getKey().equals(JWT_ID)) { + setJwtId(element.getValue().getAsString()); + } else if (element.getKey().equals(TYPE)) { + setType(element.getValue().getAsString()); } else if (element.getValue().isJsonPrimitive()){ // we handle all primitives in here JsonPrimitive prim = element.getValue().getAsJsonPrimitive(); - - if (prim.isBoolean()) { - claims.put(element.getKey(), prim.getAsBoolean()); - } else if (prim.isNumber()) { - claims.put(element.getKey(), prim.getAsNumber()); - } else if (prim.isString()) { - claims.put(element.getKey(), prim.getAsString()); - } + setClaim(element.getKey(), prim); } else { - // everything else gets handled as a raw JsonElement - claims.put(element.getKey(), element.getValue()); + setClaim(element.getKey(), element.getValue()); } } } @@ -85,208 +64,113 @@ public class JwtClaims { * @return the expiration */ public Date getExpiration() { - return expiration; + return getClaimAsDate(EXPIRATION); } /** * @param expiration the expiration to set */ public void setExpiration(Date expiration) { - this.expiration = expiration; + setClaim(EXPIRATION, expiration); } /** * @return the notBefore */ public Date getNotBefore() { - return notBefore; + return getClaimAsDate(NOT_BEFORE); } /** * @param notBefore the notBefore to set */ public void setNotBefore(Date notBefore) { - this.notBefore = notBefore; + setClaim(NOT_BEFORE, notBefore); } /** * @return the issuedAt */ public Date getIssuedAt() { - return issuedAt; + return getClaimAsDate(ISSUED_AT); } /** * @param issuedAt the issuedAt to set */ public void setIssuedAt(Date issuedAt) { - this.issuedAt = issuedAt; + setClaim(ISSUED_AT, issuedAt); } /** * @return the issuer */ public String getIssuer() { - return issuer; + return getClaimAsString(ISSUER); } /** * @param issuer the issuer to set */ public void setIssuer(String issuer) { - this.issuer = issuer; + setClaim(ISSUER, issuer); } /** * @return the audience */ public String getAudience() { - return audience; + return getClaimAsString(AUDIENCE); } /** * @param audience the audience to set */ public void setAudience(String audience) { - this.audience = audience; + setClaim(AUDIENCE, audience); } /** * @return the principal */ public String getPrincipal() { - return principal; + return getClaimAsString(PRINCIPAL); } /** * @param principal the principal to set */ public void setPrincipal(String principal) { - this.principal = principal; + setClaim(AUDIENCE, principal); } /** * @return the jwtId */ public String getJwtId() { - return jwtId; + return getClaimAsString(JWT_ID); } /** * @param jwtId the jwtId to set */ public void setJwtId(String jwtId) { - this.jwtId = jwtId; + setClaim(JWT_ID, jwtId); } /** * @return the type */ public String getType() { - return type; + return getClaimAsString(TYPE); } /** * @param type the type to set */ public void setType(String type) { - this.type = type; - } - - /** - * Get an extension claim - */ - public Object getClaim(String key) { - return claims.get(key); - } - - /** - * Set an extension claim - */ - public void setClaim(String key, Object value) { - claims.put(key, value); - } - - /** - * Remove an extension claim - */ - public Object removeClaim(String key) { - return claims.remove(key); + setClaim(TYPE, type); } - /** - * Get a copy of this header as a JsonObject. The JsonObject is not - * backed by a live copy of this JwtHeader. - * @return a copy of the data in this header in a JsonObject - */ - public JsonObject getAsJsonObject() { - JsonObject o = new JsonObject(); - - if (this.expiration != null) { - o.addProperty("exp", this.expiration.getTime() / 1000L); - } - - if (this.notBefore != null) { - o.addProperty("nbf", this.notBefore.getTime() / 1000L); - } - - if (this.issuedAt != null) { - o.addProperty("iat", this.issuedAt.getTime() / 1000L); - } - - if (this.issuer != null) { - o.addProperty("iss", this.issuer); - } - - if (this.audience != null) { - o.addProperty("aud", this.audience); - } - - if (this.principal != null) { - o.addProperty("prn", this.principal); - } - - if (this.jwtId != null) { - o.addProperty("jti", this.jwtId); - } - - if (this.type != null) { - o.addProperty("typ", this.type); - } - - if (this.claims != null) { - for (Map.Entry claim : this.claims.entrySet()) { - if (claim.getValue() instanceof JsonElement) { - o.add(claim.getKey(), (JsonElement)claim.getValue()); - } else if (claim.getValue() instanceof String) { - o.addProperty(claim.getKey(), (String)claim.getValue()); - } else if (claim.getValue() instanceof Number) { - o.addProperty(claim.getKey(), (Number)claim.getValue()); - } else if (claim.getValue() instanceof Boolean) { - o.addProperty(claim.getKey(), (Boolean)claim.getValue()); - } else if (claim.getValue() instanceof Character) { - o.addProperty(claim.getKey(), (Character)claim.getValue()); - } else if (claim.getValue() != null) { - // try to put it in as a string - o.addProperty(claim.getKey(), claim.getValue().toString()); - } else { - // otherwise add in as a null - o.add(claim.getKey(), null); - } - } - } - - return o; - } - - /* (non-Javadoc) - * @see java.lang.Object#toString() - */ - @Override - public String toString() { - return "JwtClaims [expiration=" + expiration + ", notBefore=" + notBefore + ", issuedAt=" + issuedAt + ", issuer=" + issuer + ", audience=" + audience + ", principal=" + principal + ", jwtId=" + jwtId + ", type=" + type + ", claims=" + claims + "]"; - } - - } diff --git a/src/main/java/org/mitre/jwt/model/JwtHeader.java b/src/main/java/org/mitre/jwt/model/JwtHeader.java index 099f6e786..5c44d9d3f 100644 --- a/src/main/java/org/mitre/jwt/model/JwtHeader.java +++ b/src/main/java/org/mitre/jwt/model/JwtHeader.java @@ -7,21 +7,12 @@ import java.util.Map.Entry; import com.google.gson.JsonElement; import com.google.gson.JsonObject; -public class JwtHeader { +public class JwtHeader extends ClaimSet { - /* - * TODO: Should we instead be using a generic claims map with well-named accessor methods? - */ - - private String type; - - private String algorithm; - - private String encryptionMethod; - - private Map claims = new HashMap(); + public static final String TYPE = "typ"; + public static final String ALGORITHM = "alg"; + public static final String ENCRYPTION_METHOD = "enc"; - /** * Make an empty header */ @@ -36,15 +27,15 @@ public class JwtHeader { public JwtHeader(JsonObject json) { for (Entry element : json.entrySet()) { - if (element.getKey().equals("typ")) { - this.type = json.get("typ").getAsString(); - } else if (element.getKey().equals("alg")) { - this.algorithm = json.get("alg").getAsString(); - } else if (element.getKey().equals("enc")) { - this.encryptionMethod = json.get("enc").getAsString(); + if (element.getKey().equals(TYPE)) { + this.setType(json.get(TYPE).getAsString()); + } else if (element.getKey().equals(ALGORITHM)) { + this.setAlgorithm(json.get(ALGORITHM).getAsString()); + } else if (element.getKey().equals(ENCRYPTION_METHOD)) { + this.setEncryptionMethod(json.get(ENCRYPTION_METHOD).getAsString()); } else { // TODO: this assumes string encoding for extensions, probably not quite correct - claims.put(element.getKey(), element.getValue().getAsString()); + setClaim(element.getKey(), element.getValue().getAsString()); } } } @@ -53,7 +44,7 @@ public class JwtHeader { * @return the type */ public String getType() { - return type; + return getClaimAsString(TYPE); } @@ -61,7 +52,7 @@ public class JwtHeader { * @param type the type to set */ public void setType(String type) { - this.type = type; + setClaim(TYPE, type); } @@ -69,7 +60,7 @@ public class JwtHeader { * @return the algorithm */ public String getAlgorithm() { - return algorithm; + return getClaimAsString(ALGORITHM); } @@ -77,7 +68,7 @@ public class JwtHeader { * @param algorithm the algorithm to set */ public void setAlgorithm(String algorithm) { - this.algorithm = algorithm; + setClaim(ALGORITHM, algorithm); } @@ -85,7 +76,7 @@ public class JwtHeader { * @return the encryptionMethod */ public String getEncryptionMethod() { - return encryptionMethod; + return getClaimAsString(ENCRYPTION_METHOD); } @@ -93,80 +84,7 @@ public class JwtHeader { * @param encryptionMethod the encryptionMethod to set */ public void setEncryptionMethod(String encryptionMethod) { - this.encryptionMethod = encryptionMethod; - } - - /** - * Get an extension claim - */ - public Object getClaim(String key) { - return claims.get(key); - } - - /** - * Set an extension claim - */ - public void setClaim(String key, Object value) { - claims.put(key, value); + setClaim(ENCRYPTION_METHOD, encryptionMethod); } - /** - * Remove an extension claim - */ - public Object removeClaim(String key) { - return claims.remove(key); - } - - /** - * Get a copy of this header as a JsonObject. The JsonObject is not - * backed by a live copy of this JwtHeader. - * @return a copy of the data in this header in a JsonObject - */ - public JsonObject getAsJsonObject() { - JsonObject o = new JsonObject(); - - if (this.type != null) { - o.addProperty("typ", this.type); - } - if (this.algorithm != null) { - o.addProperty("alg", this.algorithm); - } - - if (this.encryptionMethod != null) { - o.addProperty("enc", this.encryptionMethod); - } - - if (this.claims != null) { - for (Map.Entry claim : this.claims.entrySet()) { - if (claim.getValue() instanceof String) { - o.addProperty(claim.getKey(), (String)claim.getValue()); - } else if (claim.getValue() instanceof Number) { - o.addProperty(claim.getKey(), (Number)claim.getValue()); - } else if (claim.getValue() instanceof Boolean) { - o.addProperty(claim.getKey(), (Boolean)claim.getValue()); - } else if (claim.getValue() instanceof Character) { - o.addProperty(claim.getKey(), (Character)claim.getValue()); - } else if (claim.getValue() != null) { - // try to put it in as a string - o.addProperty(claim.getKey(), claim.getValue().toString()); - } else { - // otherwise add in as a null - o.add(claim.getKey(), null); - } - } - } - - return o; - } - - /* (non-Javadoc) - * @see java.lang.Object#toString() - */ - @Override - public String toString() { - return "JwtHeader [type=" + type + ", algorithm=" + algorithm + ", encryptionMethod=" + encryptionMethod + ", claims=" + claims + "]"; - } - - - }