Merge branch 'master' of github.com:jricher/OpenID-Connect-Java-Spring-Server

pull/59/head
Amanda Anganes 2011-12-27 16:06:20 -05:00
commit 204df8f0bd
27 changed files with 1233 additions and 41 deletions

View File

@ -9,6 +9,7 @@
<classpathentry kind="src" path="spring-security-oauth/spring-security-oauth2/src/test/java"/>
<classpathentry kind="src" path="spring-security-oauth/spring-security-oauth2/src/test/resources"/>
<classpathentry kind="src" path="spring-security-oauth/spring-security-oauth/src/main/java"/>
<classpathentry kind="src" path="mitre-local/src/main/java"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6"/>
<classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER">
<attributes>

View File

@ -9,6 +9,7 @@
<wb-resource deploy-path="/WEB-INF/classes" source-path="/spring-security-oauth/spring-security-oauth2/src/test/java"/>
<wb-resource deploy-path="/WEB-INF/classes" source-path="/spring-security-oauth/spring-security-oauth2/src/test/resources"/>
<wb-resource deploy-path="/WEB-INF/classes" source-path="/spring-security-oauth/spring-security-oauth/src/main/java"/>
<wb-resource deploy-path="/WEB-INF/classes" source-path="/mitre-local/src/main/java"/>
<property name="java-output-path" value="target/classes"/>
<property name="context-root" value="openid"/>
</wb-module>

@ -1 +1 @@
Subproject commit b3dbc687b2922a78cb3879ca16c0ce1bc9a4f657
Subproject commit b81dd3091e53be285bbfd9f0886b5a88b2cab19c

26
pom.xml
View File

@ -194,7 +194,7 @@
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>1.7.1</version>
<version>2.0</version>
</dependency>
<dependency>
<groupId>org.eclipse.persistence</groupId>
@ -214,7 +214,7 @@
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>r09</version>
<version>10.0.1</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
@ -226,25 +226,15 @@
<artifactId>mysql-connector-java</artifactId>
<version>5.1.16</version>
</dependency>
<dependency>
<groupId>com.google.code.maven-replacer-plugin</groupId>
<artifactId>maven-replacer-plugin</artifactId>
<version>1.3.8</version>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-core-asl</artifactId>
<version>1.6.4</version>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
<version>1.6.4</version>
</dependency>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.3</version>
<version>1.6</version>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk16</artifactId>
<version>1.46</version>
</dependency>
</dependencies>
<repositories>

View File

@ -0,0 +1,80 @@
package org.mitre.jwt;
import java.util.List;
import com.google.common.base.Objects;
import com.google.common.base.Splitter;
import com.google.common.base.Strings;
import com.google.common.collect.Lists;
public abstract class AbstractJwtSigner implements JwtSigner {
public static final String PLAINTEXT = "none";
public static final String HS256 = "HS256";
public static final String HS384 = "HS384";
public static final String HS512 = "HS512";
private String algorithm;
public AbstractJwtSigner(String algorithm) {
this.algorithm = algorithm;
}
/**
* @return the algorithm
*/
public String getAlgorithm() {
return algorithm;
}
/**
* @param algorithm the algorithm to set
*/
public void setAlgorithm(String algorithm) {
this.algorithm = algorithm;
}
/**
* Ensures that the 'alg' of the given JWT matches the {@link #algorithm} of this signer
*/
@Override
public void sign(Jwt jwt) {
if (!Objects.equal(algorithm, jwt.getHeader().getAlgorithm())) {
// algorithm type doesn't match
// TODO: should this be an error or should we just fix it in the incoming jwt?
// for now, we fix the Jwt
jwt.getHeader().setAlgorithm(algorithm);
}
String sig = generateSignature(jwt.getSignatureBase());
jwt.setSignature(sig);
}
/* (non-Javadoc)
* @see org.mitre.jwt.JwtSigner#verify(java.lang.String)
*/
@Override
public boolean verify(String jwtString) {
// split on the dots
List<String> parts = Lists.newArrayList(Splitter.on(".").split(jwtString));
if (parts.size() != 3) {
throw new IllegalArgumentException("Invalid JWT format.");
}
String h64 = parts.get(0);
String c64 = parts.get(1);
String s64 = parts.get(2);
String expectedSignature = generateSignature(h64 + "." + c64 + ".");
return Strings.nullToEmpty(s64).equals(Strings.nullToEmpty(expectedSignature));
}
protected abstract String generateSignature(String signatureBase);
}

View File

@ -0,0 +1,90 @@
package org.mitre.jwt;
import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.codec.binary.Base64;
public class Hmac256Signer extends AbstractJwtSigner {
private Mac mac;
private byte[] passphrase;
public Hmac256Signer() {
this(null);
}
public Hmac256Signer(byte[] passphrase) {
super(HS256);
//TODO: set up a factory for other signature methods
setPassphrase(passphrase);
try {
mac = Mac.getInstance("HMACSHA256");
} catch (NoSuchAlgorithmException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
@Override
protected String generateSignature(String signatureBase) {
if (passphrase == null) {
return null; // TODO: probably throw some kind of exception
}
try {
mac.init(new SecretKeySpec(getPassphrase(), mac.getAlgorithm()));
} catch (InvalidKeyException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
try {
mac.update(signatureBase.getBytes("UTF-8"));
} catch (IllegalStateException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
byte[] sigBytes = mac.doFinal();
String sig = new String(Base64.encodeBase64URLSafe(sigBytes));
// strip off any padding
sig = sig.replace("=", "");
return sig;
}
/**
* @return the passphrase
*/
public byte[] getPassphrase() {
return passphrase;
}
/**
* @param passphrase the passphrase to set
*/
public void setPassphrase(byte[] passphrase) {
this.passphrase = passphrase;
}
}

View File

@ -0,0 +1,155 @@
package org.mitre.jwt;
import java.io.ByteArrayInputStream;
import java.io.InputStreamReader;
import java.util.List;
import org.apache.commons.codec.binary.Base64;
import com.google.common.base.Splitter;
import com.google.common.base.Strings;
import com.google.common.collect.Lists;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
public class Jwt {
private JwtHeader header;
private JwtClaims claims;
/**
* Base64Url encoded signature string
*/
private String signature;
public Jwt() {
this.header = new JwtHeader();
this.claims = new JwtClaims();
this.signature = null; // unsigned by default
}
/**
* Create a Jwt from existing components
* @param header
* @param claims
* @param signature
*/
public Jwt(JwtHeader header, JwtClaims claims, String signature) {
super();
this.header = header;
this.claims = claims;
this.signature = signature;
}
/**
* @return the header
*/
public JwtHeader getHeader() {
return header;
}
/**
* @param header the header to set
*/
public void setHeader(JwtHeader header) {
this.header = header;
}
/**
* @return the claims
*/
public JwtClaims getClaims() {
return claims;
}
/**
* @param claims the claims to set
*/
public void setClaims(JwtClaims claims) {
this.claims = claims;
}
/**
* @return the signature
*/
public String getSignature() {
return signature;
}
/**
* @param signature the signature to set
*/
public void setSignature(String signature) {
this.signature = signature;
}
/**
* Return the canonical encoded string of this JWT, the header in Base64, a period ".", the claims in Base64, a period ".", and the signature in Base64.
*/
public String toString() {
return getSignatureBase() + "." + Strings.nullToEmpty(this.signature);
}
/**
* The signature base of a JWT is the header in Base64, a period ".", and the claims in Base64.
*/
public String getSignatureBase() {
JsonObject h = header.getAsJsonObject();
JsonObject c = claims.getAsJsonObject();
String h64 = new String(Base64.encodeBase64URLSafe(h.toString().getBytes()));
String c64 = new String(Base64.encodeBase64URLSafe(c.toString().getBytes()));
return h64 + "." + c64;
}
/**
* Parse a wire-encoded JWT
*/
public static Jwt parse(String s) {
// split on the dots
List<String> parts = Lists.newArrayList(Splitter.on(".").split(s));
if (parts.size() != 3) {
throw new IllegalArgumentException("Invalid JWT format.");
}
String h64 = parts.get(0);
String c64 = parts.get(1);
String s64 = parts.get(2);
JsonParser parser = new JsonParser();
JsonObject hjo = parser.parse(new InputStreamReader(new ByteArrayInputStream(Base64.decodeBase64(h64)))).getAsJsonObject();
JsonObject cjo = parser.parse(new InputStreamReader(new ByteArrayInputStream(Base64.decodeBase64(c64)))).getAsJsonObject();
// shuttle for return value
Jwt jwt = new Jwt(new JwtHeader(hjo), new JwtClaims(cjo), s64);
// TODO: save the wire-encoded string in the Jwt object itself?
return jwt;
}
}

View File

@ -0,0 +1,292 @@
package org.mitre.jwt;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonPrimitive;
public class JwtClaims {
/**
* 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<String, Object> claims = new HashMap<String, Object>();
public JwtClaims() {
}
public JwtClaims(JsonObject json) {
for (Entry<String, JsonElement> 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();
} 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());
}
} else {
// everything else gets handled as a raw JsonElement
claims.put(element.getKey(), element.getValue());
}
}
}
/**
* @return the expiration
*/
public Date getExpiration() {
return expiration;
}
/**
* @param expiration the expiration to set
*/
public void setExpiration(Date expiration) {
this.expiration = expiration;
}
/**
* @return the notBefore
*/
public Date getNotBefore() {
return notBefore;
}
/**
* @param notBefore the notBefore to set
*/
public void setNotBefore(Date notBefore) {
this.notBefore = notBefore;
}
/**
* @return the issuedAt
*/
public Date getIssuedAt() {
return issuedAt;
}
/**
* @param issuedAt the issuedAt to set
*/
public void setIssuedAt(Date issuedAt) {
this.issuedAt = issuedAt;
}
/**
* @return the issuer
*/
public String getIssuer() {
return issuer;
}
/**
* @param issuer the issuer to set
*/
public void setIssuer(String issuer) {
this.issuer = issuer;
}
/**
* @return the audience
*/
public String getAudience() {
return audience;
}
/**
* @param audience the audience to set
*/
public void setAudience(String audience) {
this.audience = audience;
}
/**
* @return the principal
*/
public String getPrincipal() {
return principal;
}
/**
* @param principal the principal to set
*/
public void setPrincipal(String principal) {
this.principal = principal;
}
/**
* @return the jwtId
*/
public String getJwtId() {
return jwtId;
}
/**
* @param jwtId the jwtId to set
*/
public void setJwtId(String jwtId) {
this.jwtId = jwtId;
}
/**
* @return the type
*/
public String getType() {
return 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);
}
/**
* 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<String, Object> 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 + "]";
}
}

View File

@ -0,0 +1,172 @@
package org.mitre.jwt;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
public class JwtHeader {
/*
* 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<String, Object> claims = new HashMap<String, Object>();
/**
* Make an empty header
*/
public JwtHeader() {
}
/**
* Build a header from a JSON object
* @param json
*/
public JwtHeader(JsonObject json) {
for (Entry<String, JsonElement> 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();
} else {
// TODO: this assumes string encoding for extensions, probably not quite correct
claims.put(element.getKey(), element.getValue().getAsString());
}
}
}
/**
* @return the type
*/
public String getType() {
return type;
}
/**
* @param type the type to set
*/
public void setType(String type) {
this.type = type;
}
/**
* @return the algorithm
*/
public String getAlgorithm() {
return algorithm;
}
/**
* @param algorithm the algorithm to set
*/
public void setAlgorithm(String algorithm) {
this.algorithm = algorithm;
}
/**
* @return the encryptionMethod
*/
public String getEncryptionMethod() {
return encryptionMethod;
}
/**
* @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);
}
/**
* 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<String, Object> 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 + "]";
}
}

View File

@ -0,0 +1,9 @@
package org.mitre.jwt;
public interface JwtSigner {
public void sign(Jwt jwt);
public boolean verify(String jwtString);
}

View File

@ -0,0 +1,14 @@
package org.mitre.jwt;
public class PlaintextSigner extends AbstractJwtSigner {
public PlaintextSigner() {
super(PLAINTEXT);
}
@Override
protected String generateSignature(String signatureBase) {
return null;
}
}

View File

@ -0,0 +1,155 @@
package org.mitre.openid.connect.model;
import java.util.Collection;
import java.util.Date;
import javax.persistence.Basic;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Temporal;
import org.springframework.security.oauth2.provider.ClientDetails;
@Entity
public class ApprovedSite {
// unique id
private Long id;
// which user made the approval
private UserInfo userInfo;
// which OAuth2 client is this tied to
private ClientDetails clientDetails;
// when was this first approved?
private Date creationDate;
// when was this last accessed?
private Date accessDate;
// if this is a time-limited access, when does it run out?
private Date timeoutDate;
// what scopes have been allowed
// this should include all information for what data to access
private Collection<String> allowedScopes;
// TODO: should we store the OAuth2 tokens and IdTokens here?
public ApprovedSite() {
}
/**
* @return the id
*/
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
public Long getId() {
return id;
}
/**
* @param id the id to set
*/
public void setId(Long id) {
this.id = id;
}
/**
* @return the userInfo
*/
public UserInfo getUserInfo() {
return userInfo;
}
/**
* @param userInfo the userInfo to set
*/
public void setUserInfo(UserInfo userInfo) {
this.userInfo = userInfo;
}
/**
* @return the clientDetails
*/
public ClientDetails getClientDetails() {
return clientDetails;
}
/**
* @param clientDetails the clientDetails to set
*/
public void setClientDetails(ClientDetails clientDetails) {
this.clientDetails = clientDetails;
}
/**
* @return the creationDate
*/
@Basic
@Temporal(javax.persistence.TemporalType.TIMESTAMP)
public Date getCreationDate() {
return creationDate;
}
/**
* @param creationDate the creationDate to set
*/
public void setCreationDate(Date creationDate) {
this.creationDate = creationDate;
}
/**
* @return the accessDate
*/
@Basic
@Temporal(javax.persistence.TemporalType.TIMESTAMP)
public Date getAccessDate() {
return accessDate;
}
/**
* @param accessDate the accessDate to set
*/
public void setAccessDate(Date accessDate) {
this.accessDate = accessDate;
}
/**
* @return the allowedScopes
*/
public Collection<String> getAllowedScopes() {
return allowedScopes;
}
/**
* @param allowedScopes the allowedScopes to set
*/
public void setAllowedScopes(Collection<String> allowedScopes) {
this.allowedScopes = allowedScopes;
}
/**
* @return the timeoutDate
*/
@Basic
@Temporal(javax.persistence.TemporalType.TIMESTAMP)
public Date getTimeoutDate() {
return timeoutDate;
}
/**
* @param timeoutDate the timeoutDate to set
*/
public void setTimeoutDate(Date timeoutDate) {
this.timeoutDate = timeoutDate;
}
}

View File

@ -0,0 +1,23 @@
package org.mitre.openid.connect.model;
import java.util.Date;
import javax.persistence.Entity;
/**
* Class to contain a logged event in the system.
*
* @author jricher
*
*/
@Entity
public class Event {
public static enum EventType { LOGIN, AUTHORIZATION, ACCESS }
private Long id;
private EventType type;
private Date timestamp;
}

View File

@ -2,11 +2,13 @@ package org.mitre.openid.connect.model;
import javax.persistence.Entity;
import org.mitre.jwt.Jwt;
/*
* TODO: This class needs to be encoded as a JWT
*/
@Entity
public class IdToken {
public class IdToken extends Jwt {
private String iss;
private String user_id;

View File

@ -1,8 +0,0 @@
package org.mitre.openid.connect.model;
import javax.persistence.Entity;
@Entity
public class OAuth2AccessToken {
}

View File

@ -1,8 +0,0 @@
package org.mitre.openid.connect.model;
import javax.persistence.Entity;
@Entity
public class OAuth2Client {
}

View File

@ -5,6 +5,8 @@ import javax.persistence.Entity;
@Entity
public class UserInfo {
// TODO: underbars are awkward in java, should we switch all this to camel case and put in underbars in the serialization view?
private String user_id;
private String name;
private String given_name;

View File

@ -0,0 +1,34 @@
package org.mitre.openid.connect.model;
import java.util.Collection;
import java.util.Date;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import org.springframework.security.oauth2.provider.ClientDetails;
/**
* Indicator that login to a site should be automatically granted
* without user interaction.
* @author jricher
*
*/
public class WhitelistedSite {
// unique id
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
// who added this site to the whitelist (should be an admin)
private UserInfo userInfo;
// which OAuth2 client is this tied to
private ClientDetails clientDetails;
// what scopes be allowed by default
// this should include all information for what data to access
private Collection<String> allowedScopes;
}

View File

@ -0,0 +1,19 @@
package org.mitre.openid.connect.repository;
import java.util.Collection;
import java.util.Collections;
import org.mitre.openid.connect.model.ApprovedSite;
import org.mitre.openid.connect.model.UserInfo;
public interface ApprovedSiteRepository {
public ApprovedSite getById(Long id);
public Collection<ApprovedSite> getAllForUser(UserInfo user);
public Collection<ApprovedSite> getAllExpired();
public ApprovedSite save(ApprovedSite site);
}

View File

@ -0,0 +1,11 @@
package org.mitre.openid.connect.repository;
import org.mitre.openid.connect.model.IdToken;
public interface IdTokenRepository {
public IdToken getById(Long id);
public IdToken save(IdToken idToken);
}

View File

@ -0,0 +1,11 @@
package org.mitre.openid.connect.repository;
import org.mitre.openid.connect.model.UserInfo;
public interface UserInfoRepository {
public UserInfo getByUserId(String user_id);
public UserInfo save(UserInfo user);
}

View File

@ -0,0 +1,5 @@
package org.mitre.openid.connect.repository;
public interface WhitelistedSiteRepository {
}

View File

@ -7,4 +7,19 @@ import org.springframework.web.bind.annotation.RequestMapping;
@RequestMapping("/authorize")
public class AuthorizationEndpoint {
/*
* handle "code" flow
*
*/
/*
* handle "idtoken token" flow
*
*/
/*
* Other flows get handled outside of our endpoints by SSOA
*/
}

View File

@ -9,4 +9,8 @@ public class TokenEndpoint {
//Corresponds to spring security Authentication Filter class
// handle sending back a token and an id token for a code
// fall through to SSOA code if no id token?
}

View File

@ -29,11 +29,8 @@
<beans:property name="order" value="1"/>
</beans:bean>
<!-- json view, capable of converting any POJO to json format -->
<beans:bean id="jsonView" class="org.springframework.web.servlet.view.json.MappingJacksonJsonView"/>
<!-- JSON views for each type of model object -->
<beans:bean id="jsonUserInfoView" class="org.mitre.openid.connect.model.serializer.JSONUserInfoView"/>
<beans:bean id="jsonIdTokenView" class="org.mitre.openid.connect.model.serializer.JSONIdTokenView"/>
<!-- <beans:bean id="jsonUserInfoView" class="org.mitre.openid.connect.model.serializer.JSONUserInfoView"/> -->
<!-- <beans:bean id="jsonIdTokenView" class="org.mitre.openid.connect.model.serializer.JSONIdTokenView"/> -->
</beans:beans>

View File

@ -0,0 +1,126 @@
package org.mitre.jwt;
import static org.junit.Assert.*;
import static org.hamcrest.CoreMatchers.*;
import java.io.UnsupportedEncodingException;
import java.util.Date;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
public class JwtTest {
@Test
public void testToStringPlaintext() {
Jwt jwt = new Jwt();
jwt.getHeader().setAlgorithm("none");
jwt.getClaims().setIssuer("joe");
jwt.getClaims().setExpiration(new Date(1300819380L * 1000L));
jwt.getClaims().setClaim("http://example.com/is_root", Boolean.TRUE);
// sign it with a blank signature
JwtSigner signer = new PlaintextSigner();
signer.sign(jwt);
/*
* Expected string based on the following structures, serialized exactly as follows and base64 encoded:
*
* header: {"alg":"none"}
* claims: {"exp":1300819380,"iss":"joe","http://example.com/is_root":true}
*/
String expected = "eyJhbGciOiJub25lIn0.eyJleHAiOjEzMDA4MTkzODAsImlzcyI6ImpvZSIsImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ.";
String actual = jwt.toString();
assertThat(actual, equalTo(expected));
}
@Test
public void testGenerateHmacSignature() {
Jwt jwt = new Jwt();
jwt.getHeader().setType("JWT");
jwt.getHeader().setAlgorithm("HS256");
jwt.getClaims().setIssuer("joe");
jwt.getClaims().setExpiration(new Date(1300819380L * 1000L));
jwt.getClaims().setClaim("http://example.com/is_root", Boolean.TRUE);
// sign it
byte[] key = null;
try {
key = "secret".getBytes("UTF-8");
} catch (UnsupportedEncodingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
JwtSigner signer = new Hmac256Signer(key);
signer.sign(jwt);
/*
* Expected string based on the following strucutres, serialized exactly as follows and base64 encoded:
*
* header: {"typ":"JWT","alg":"HS256"}
* claims: {"exp":1300819380,"iss":"joe","http://example.com/is_root":true}
*
* Expected signature: iGBPJj47S5q_HAhSoQqAdcS6A_1CFj3zrLaImqNbt9E
*
*/
String signature = "p-63Jzz7mgi3H4hvW6MFB7lmPRZjhsL666MYkmpX33Y";
String expected = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjEzMDA4MTkzODAsImlzcyI6ImpvZSIsImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ." + signature;
String actual = jwt.toString();
assertThat(actual, equalTo(expected));
assertThat(jwt.getSignature(), equalTo(signature));
}
@Test
public void testValidateHmacSignature() {
// sign it
byte[] key = null;
try {
key = "secret".getBytes("UTF-8");
} catch (UnsupportedEncodingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
JwtSigner signer = new Hmac256Signer(key);
/*
* Token string based on the following strucutres, serialized exactly as follows and base64 encoded:
*
* header: {"typ":"JWT","alg":"HS256"}
* claims: {"exp":1300819380,"iss":"joe","http://example.com/is_root":true}
*
* Expected signature: iGBPJj47S5q_HAhSoQqAdcS6A_1CFj3zrLaImqNbt9E
*
*/
String jwtString = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjEzMDA4MTkzODAsImlzcyI6ImpvZSIsImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ.iGBPJj47S5q_HAhSoQqAdcS6A_1CFj3zrLaImqNbt9E";
boolean valid = signer.verify(jwtString);
assertThat(valid, equalTo(Boolean.TRUE));
}
@Test
public void testParse() {
String source = "eyJhbGciOiJub25lIn0.eyJleHAiOjEzMDA4MTkzODAsImlzcyI6ImpvZSIsImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ.";
Jwt jwt = Jwt.parse(source);
assertThat(jwt.getHeader().getAlgorithm(), equalTo(AbstractJwtSigner.PLAINTEXT));
assertThat(jwt.getClaims().getIssuer(), equalTo("joe"));
assertThat(jwt.getClaims().getExpiration(), equalTo(new Date(1300819380L * 1000L)));
assertThat((Boolean)jwt.getClaims().getClaim("http://example.com/is_root"), equalTo(Boolean.TRUE));
}
}