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

pull/59/head
Amanda Anganes 2012-01-09 11:26:01 -05:00
commit fe88ea4e7a
41 changed files with 2942 additions and 117 deletions

10
pom.xml
View File

@ -7,10 +7,20 @@
<name>OpenIdConnect</name>
<packaging>pom</packaging>
<version>0.1</version>
<profiles>
<profile>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
<id>default</id>
<modules>
<module>spring-security-oauth/spring-security-oauth2</module>
<module>server</module>
</modules>
</profile>
</profiles>
<properties>
<java-version>1.6</java-version>
<spring.version>3.1.0.RELEASE</spring.version>

View File

@ -0,0 +1,50 @@
package org.mitre.oauth2.exception;
/**
*
*/
/**
* @author aanganes
*
*/
public class ClientNotFoundException extends RuntimeException {
/**
*
*/
private static final Long serialVersionUID = 1L;
/**
*
*/
public ClientNotFoundException() {
// TODO Auto-generated constructor stub
}
/**
* @param message
*/
public ClientNotFoundException(String message) {
super(message);
// TODO Auto-generated constructor stub
}
/**
* @param cause
*/
public ClientNotFoundException(Throwable cause) {
super(cause);
// TODO Auto-generated constructor stub
}
/**
* @param message
* @param cause
*/
public ClientNotFoundException(String message, Throwable cause) {
super(message, cause);
// TODO Auto-generated constructor stub
}
}

View File

@ -0,0 +1,15 @@
package org.mitre.oauth2.exception;
public class DuplicateClientIdException extends RuntimeException {
public DuplicateClientIdException(String clientId) {
super("Duplicate client id: " + clientId);
}
/**
*
*/
private static final long serialVersionUID = 1L;
}

View File

@ -0,0 +1,49 @@
/**
*
*/
package org.mitre.oauth2.exception;
/**
* @author AANGANES
*
*/
public class PermissionDeniedException extends RuntimeException {
/**
*
*/
private static final Long serialVersionUID = 1L;
/**
*
*/
public PermissionDeniedException() {
// TODO Auto-generated constructor stub
}
/**
* @param message
*/
public PermissionDeniedException(String message) {
super(message);
// TODO Auto-generated constructor stub
}
/**
* @param cause
*/
public PermissionDeniedException(Throwable cause) {
super(cause);
// TODO Auto-generated constructor stub
}
/**
* @param message
* @param cause
*/
public PermissionDeniedException(String message, Throwable cause) {
super(message, cause);
// TODO Auto-generated constructor stub
}
}

View File

@ -0,0 +1,473 @@
/**
*
*/
package org.mitre.oauth2.model;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import javax.persistence.Basic;
import javax.persistence.CollectionTable;
import javax.persistence.ElementCollection;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.Table;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.oauth2.provider.ClientDetails;
/**
* @author jricher
*
*/
@Entity
@Table(name="clientdetails")
@NamedQueries({
@NamedQuery(name = "ClientDetailsEntity.findAll", query = "SELECT c FROM ClientDetailsEntity c")
})
public class ClientDetailsEntity implements ClientDetails {
/**
* Create a blank ClientDetailsEntity
*/
public ClientDetailsEntity() {
}
private String clientId;
private String clientSecret;
private Set<String> scope;
private Set<String> authorizedGrantTypes;
private Set<GrantedAuthority> authorities = Collections.emptySet();
private String clientName;
private String clientDescription;
private boolean allowRefresh = false; // do we allow refresh tokens for this client?
private Long accessTokenTimeout; // in seconds
private Long refreshTokenTimeout; // in seconds
private String owner; // userid of who registered it
private String registeredRedirectUri;
private Set<String> resourceIds;
// TODO:
/*
private boolean allowMultipleAccessTokens; // do we allow multiple access tokens, or not?
private boolean reuseRefreshToken; // do we let someone reuse a refresh token?
*/
/**
* @return the clientId
*/
@Id
public String getClientId() {
return clientId;
}
/**
* @param clientId The OAuth2 client_id, must be unique to this client
*/
public void setClientId(String clientId) {
this.clientId = clientId;
}
/**
* @return the clientSecret
*/
@Basic
public String getClientSecret() {
return clientSecret;
}
/**
* @param clientSecret the OAuth2 client_secret (optional)
*/
public void setClientSecret(String clientSecret) {
this.clientSecret = clientSecret;
}
/**
* @return the scope
*/
@ElementCollection(fetch = FetchType.EAGER)
@CollectionTable(
name="scope",
joinColumns=@JoinColumn(name="owner_id")
)
public Set<String> getScope() {
return scope;
}
/**
* @param scope the set of scopes allowed to be issued to this client
*/
public void setScope(Set<String> scope) {
this.scope = scope;
}
/**
* @return the authorizedGrantTypes
*/
@ElementCollection(fetch = FetchType.EAGER)
@CollectionTable(
name="authorizedgranttypes",
joinColumns=@JoinColumn(name="owner_id")
)
public Set<String> getAuthorizedGrantTypes() {
return authorizedGrantTypes;
}
/**
* @param authorizedGrantTypes the OAuth2 grant types that this client is allowed to use
*/
public void setAuthorizedGrantTypes(Set<String> authorizedGrantTypes) {
this.authorizedGrantTypes = authorizedGrantTypes;
}
/**
* @return the authorities
*/
@ElementCollection(fetch = FetchType.EAGER)
@CollectionTable(
name="authorities",
joinColumns=@JoinColumn(name="owner_id")
)
public Set<GrantedAuthority> getAuthorities() {
return authorities;
}
/**
* @param authorities the Spring Security authorities this client is given
*/
public void setAuthorities(Set<GrantedAuthority> authorities) {
this.authorities = authorities;
}
/**
* If the clientSecret is not null, then it is always required.
*/
@Override
public boolean isSecretRequired() {
return getClientSecret() != null;
}
/**
* If the scope list is not null or empty, then this client has been scoped.
*/
@Override
public boolean isScoped() {
return getScope() != null && !getScope().isEmpty();
}
/**
* @return the clientName
*/
@Basic
public String getClientName() {
return clientName;
}
/**
* @param clientName Human-readable name of the client (optional)
*/
public void setClientName(String clientName) {
this.clientName = clientName;
}
/**
* @return the clientDescription
*/
@Basic
public String getClientDescription() {
return clientDescription;
}
/**
* @param clientDescription Human-readable long description of the client (optional)
*/
public void setClientDescription(String clientDescription) {
this.clientDescription = clientDescription;
}
/**
* @return the allowRefresh
*/
@Basic
public boolean isAllowRefresh() {
return allowRefresh;
}
/**
* @param allowRefresh Whether to allow for issuance of refresh tokens or not (defaults to false)
*/
public void setAllowRefresh(boolean allowRefresh) {
this.allowRefresh = allowRefresh;
}
/**
* @param accessTokenTimeout Lifetime of access tokens, in seconds (optional - leave null for no timeout)
*/
@Basic
public Long getAccessTokenTimeout() {
return accessTokenTimeout;
}
/**
* @param accessTokenTimeout the accessTokenTimeout to set
*/
public void setAccessTokenTimeout(Long accessTokenTimeout) {
this.accessTokenTimeout = accessTokenTimeout;
}
/**
* @return the refreshTokenTimeout
*/
@Basic
public Long getRefreshTokenTimeout() {
return refreshTokenTimeout;
}
/**
* @param refreshTokenTimeout Lifetime of refresh tokens, in seconds (optional - leave null for no timeout)
*/
public void setRefreshTokenTimeout(Long refreshTokenTimeout) {
this.refreshTokenTimeout = refreshTokenTimeout;
}
/**
* @return the owner
*/
@Basic
public String getOwner() {
return owner;
}
/**
* @param owner User ID of the person who registered this client (optional)
*/
public void setOwner(String owner) {
this.owner = owner;
}
/**
* @return the registeredRedirectUri
*/
@Basic
public String getRegisteredRedirectUri() {
return registeredRedirectUri;
}
/**
* @param registeredRedirectUri the registeredRedirectUri to set
*/
public void setRegisteredRedirectUri(String registeredRedirectUri) {
this.registeredRedirectUri = registeredRedirectUri;
}
/**
* @return the resourceIds
*/
public Set<String> getResourceIds() {
return resourceIds;
}
/**
* @param resourceIds the resourceIds to set
*/
@ElementCollection(fetch = FetchType.EAGER)
@CollectionTable(
name="resource_ids",
joinColumns=@JoinColumn(name="owner_id")
)
public void setResourceIds(Set<String> resourceIds) {
this.resourceIds = resourceIds;
}
/* (non-Javadoc)
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return "ClientDetailsEntity [" + (clientId != null ? "clientId=" + clientId + ", " : "") + (scope != null ? "scope=" + scope + ", " : "") + (clientName != null ? "clientName=" + clientName + ", " : "") + (owner != null ? "owner=" + owner : "") + "]";
}
/* (non-Javadoc)
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((clientId == null) ? 0 : clientId.hashCode());
return result;
}
/* (non-Javadoc)
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
ClientDetailsEntity other = (ClientDetailsEntity) obj;
if (clientId == null) {
if (other.clientId != null) {
return false;
}
} else if (!clientId.equals(other.clientId)) {
return false;
}
return true;
}
public static ClientDetailsEntityBuilder makeBuilder() {
return new ClientDetailsEntityBuilder();
}
public static class ClientDetailsEntityBuilder {
private ClientDetailsEntity instance;
private ClientDetailsEntityBuilder() {
instance = new ClientDetailsEntity();
}
/**
* @param clientId
* @see org.mitre.oauth2.model.ClientDetailsEntity#setClientId(java.lang.String)
*/
public ClientDetailsEntityBuilder setClientId(String clientId) {
instance.setClientId(clientId);
return this;
}
/**
* @param clientSecret
* @see org.mitre.oauth2.model.ClientDetailsEntity#setClientSecret(java.lang.String)
*/
public ClientDetailsEntityBuilder setClientSecret(String clientSecret) {
instance.setClientSecret(clientSecret);
return this;
}
/**
* @param scope
* @see org.mitre.oauth2.model.ClientDetailsEntity#setScope(java.util.List)
*/
public ClientDetailsEntityBuilder setScope(Set<String> scope) {
instance.setScope(scope);
return this;
}
/**
* @param authorizedGrantTypes
* @see org.mitre.oauth2.model.ClientDetailsEntity#setAuthorizedGrantTypes(java.util.List)
*/
public ClientDetailsEntityBuilder setAuthorizedGrantTypes(Set<String> authorizedGrantTypes) {
instance.setAuthorizedGrantTypes(authorizedGrantTypes);
return this;
}
/**
* @param authorities
* @see org.mitre.oauth2.model.ClientDetailsEntity#setAuthorities(java.util.List)
*/
public ClientDetailsEntityBuilder setAuthorities(Set<GrantedAuthority> authorities) {
instance.setAuthorities(authorities);
return this;
}
/**
* @param clientName
* @see org.mitre.oauth2.model.ClientDetailsEntity#setClientName(java.lang.String)
*/
public ClientDetailsEntityBuilder setClientName(String clientName) {
instance.setClientName(clientName);
return this;
}
/**
* @param clientDescription
* @see org.mitre.oauth2.model.ClientDetailsEntity#setClientDescription(java.lang.String)
*/
public ClientDetailsEntityBuilder setClientDescription(String clientDescription) {
instance.setClientDescription(clientDescription);
return this;
}
/**
* @param allowRefresh
* @see org.mitre.oauth2.model.ClientDetailsEntity#setAllowRefresh(boolean)
*/
public ClientDetailsEntityBuilder setAllowRefresh(boolean allowRefresh) {
instance.setAllowRefresh(allowRefresh);
return this;
}
/**
* @param accessTokenTimeout
* @see org.mitre.oauth2.model.ClientDetailsEntity#setAccessTokenTimeout(java.lang.Long)
*/
public ClientDetailsEntityBuilder setAccessTokenTimeout(Long accessTokenTimeout) {
instance.setAccessTokenTimeout(accessTokenTimeout);
return this;
}
/**
* @param refreshTokenTimeout
* @see org.mitre.oauth2.model.ClientDetailsEntity#setRefreshTokenTimeout(java.lang.Long)
*/
public ClientDetailsEntityBuilder setRefreshTokenTimeout(Long refreshTokenTimeout) {
instance.setRefreshTokenTimeout(refreshTokenTimeout);
return this;
}
/**
* @param owner
* @see org.mitre.oauth2.model.ClientDetailsEntity#setOwner(java.lang.String)
*/
public ClientDetailsEntityBuilder setOwner(String owner) {
instance.setOwner(owner);
return this;
}
/**
* Complete the builder
* @return
*/
public ClientDetailsEntity finish() {
return instance;
}
/**
* @param registeredRedirectUri
* @see org.mitre.oauth2.model.ClientDetailsEntity#setRegisteredRedirectUri(java.lang.String)
*/
public ClientDetailsEntityBuilder setRegisteredRedirectUri(String registeredRedirectUri) {
instance.setRegisteredRedirectUri(registeredRedirectUri);
return this;
}
/**
* @param resourceIds
* @see org.mitre.oauth2.model.ClientDetailsEntity#setResourceIds(java.util.List)
*/
public ClientDetailsEntityBuilder setResourceIds(Set<String> resourceIds) {
instance.setResourceIds(resourceIds);
return this;
}
}
}

View File

@ -0,0 +1,9 @@
package org.mitre.oauth2.model;
import org.mitre.oauth2.model.ClientDetailsEntity.ClientDetailsEntityBuilder;
public interface ClientDetailsEntityFactory {
public ClientDetailsEntity createClient(String clientId, String clientSecret);
}

View File

@ -0,0 +1,32 @@
package org.mitre.oauth2.model;
import java.util.UUID;
import org.apache.commons.codec.binary.Base64;
import org.mitre.oauth2.model.ClientDetailsEntity.ClientDetailsEntityBuilder;
import org.springframework.stereotype.Service;
/**
* A factory for making OAuth2 clients with autogenerated IDs and secrets (as desired)
* @author jricher
*
*/
@Service
public class ClientGeneratorFactory implements ClientDetailsEntityFactory {
@Override
public ClientDetailsEntity createClient(String clientId, String clientSecret) {
ClientDetailsEntityBuilder builder = ClientDetailsEntity.makeBuilder();
if (clientId == null) {
clientId = UUID.randomUUID().toString();
}
builder.setClientId(clientId);
if (clientSecret == null) {
clientSecret = Base64.encodeBase64((UUID.randomUUID().toString() + UUID.randomUUID().toString()).getBytes()).toString();
}
builder.setClientSecret(clientSecret);
return builder.finish();
}
}

View File

@ -0,0 +1,210 @@
/**
*
*/
package org.mitre.oauth2.model;
import java.util.Date;
import java.util.Set;
import javax.persistence.Basic;
import javax.persistence.CollectionTable;
import javax.persistence.Column;
import javax.persistence.ElementCollection;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.Lob;
import javax.persistence.ManyToOne;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.Transient;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.common.OAuth2RefreshToken;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
/**
* @author jricher
*
*/
@Entity
@Table(name="accesstoken")
@NamedQueries({
@NamedQuery(name = "OAuth2AccessTokenEntity.getByRefreshToken", query = "select a from OAuth2AccessTokenEntity a where a.refreshToken = :refreshToken"),
@NamedQuery(name = "OAuth2AccessTokenEntity.getByClient", query = "select a from OAuth2AccessTokenEntity a where a.client = :client"),
@NamedQuery(name = "OAuth2AccessTokenEntity.getExpired", query = "select a from OAuth2AccessTokenEntity a where a.expiration is not null and a.expiration < current_timestamp")
})
public class OAuth2AccessTokenEntity extends OAuth2AccessToken {
private ClientDetailsEntity client;
private OAuth2Authentication authentication; // the authentication that made this access
/**
*
*/
public OAuth2AccessTokenEntity() {
super(null);
}
/**
* @return the authentication
*/
@Lob
@Basic
public OAuth2Authentication getAuthentication() {
return authentication;
}
/**
* @param authentication the authentication to set
*/
public void setAuthentication(OAuth2Authentication authentication) {
this.authentication = authentication;
}
/**
* @return the client
*/
@ManyToOne
@JoinColumn(name = "client_id")
public ClientDetailsEntity getClient() {
return client;
}
/**
* @param client the client to set
*/
public void setClient(ClientDetailsEntity client) {
this.client = client;
}
/* (non-Javadoc)
* @see org.springframework.security.oauth2.common.OAuth2AccessToken#getValue()
*/
@Override
@Id
@Column(name="id")
public String getValue() {
// TODO Auto-generated method stub
return super.getValue();
}
/* (non-Javadoc)
* @see org.springframework.security.oauth2.common.OAuth2AccessToken#setValue(java.lang.String)
*/
@Override
public void setValue(String value) {
// TODO Auto-generated method stub
super.setValue(value);
}
/* (non-Javadoc)
* @see org.springframework.security.oauth2.common.OAuth2AccessToken#getExpiration()
*/
@Override
@Basic
@Temporal(javax.persistence.TemporalType.TIMESTAMP)
public Date getExpiration() {
// TODO Auto-generated method stub
return super.getExpiration();
}
/* (non-Javadoc)
* @see org.springframework.security.oauth2.common.OAuth2AccessToken#setExpiration(java.util.Date)
*/
@Override
public void setExpiration(Date expiration) {
// TODO Auto-generated method stub
super.setExpiration(expiration);
}
/* (non-Javadoc)
* @see org.springframework.security.oauth2.common.OAuth2AccessToken#getTokenType()
*/
@Override
@Basic
public String getTokenType() {
// TODO Auto-generated method stub
return super.getTokenType();
}
/* (non-Javadoc)
* @see org.springframework.security.oauth2.common.OAuth2AccessToken#setTokenType(java.lang.String)
*/
@Override
public void setTokenType(String tokenType) {
// TODO Auto-generated method stub
super.setTokenType(tokenType);
}
/* (non-Javadoc)
* @see org.springframework.security.oauth2.common.OAuth2AccessToken#getRefreshToken()
*/
@Override
@ManyToOne
@JoinColumn(name="refresh_token_id")
public OAuth2RefreshTokenEntity getRefreshToken() {
// TODO Auto-generated method stub
return (OAuth2RefreshTokenEntity) super.getRefreshToken();
}
/* (non-Javadoc)
* @see org.springframework.security.oauth2.common.OAuth2AccessToken#setRefreshToken(org.springframework.security.oauth2.common.OAuth2RefreshToken)
*/
public void setRefreshToken(OAuth2RefreshTokenEntity refreshToken) {
// TODO Auto-generated method stub
super.setRefreshToken(refreshToken);
}
/* (non-Javadoc)
* @see org.springframework.security.oauth2.common.OAuth2AccessToken#setRefreshToken(org.springframework.security.oauth2.common.OAuth2RefreshToken)
*/
@Override
public void setRefreshToken(OAuth2RefreshToken refreshToken) {
if (!(refreshToken instanceof OAuth2RefreshTokenEntity)) {
// TODO: make a copy constructor instead....
throw new IllegalArgumentException("Not a storable refresh token entity!");
}
// force a pass through to the entity version
setRefreshToken((OAuth2RefreshTokenEntity)refreshToken);
}
/* (non-Javadoc)
* @see org.springframework.security.oauth2.common.OAuth2AccessToken#getScope()
*/
@Override
@ElementCollection(fetch=FetchType.EAGER)
@CollectionTable(
joinColumns=@JoinColumn(name="owner_id"),
name="scope"
)
public Set<String> getScope() {
// TODO Auto-generated method stub
return super.getScope();
}
/* (non-Javadoc)
* @see org.springframework.security.oauth2.common.OAuth2AccessToken#setScope(java.util.Set)
*/
@Override
public void setScope(Set<String> scope) {
// TODO Auto-generated method stub
super.setScope(scope);
}
@Transient
public boolean isExpired() {
return getExpiration() == null ? false : System.currentTimeMillis() > getExpiration().getTime();
}
}

View File

@ -0,0 +1,7 @@
package org.mitre.oauth2.model;
public interface OAuth2AccessTokenEntityFactory {
public OAuth2AccessTokenEntity createNewAccessToken();
}

View File

@ -0,0 +1,136 @@
/**
*
*/
package org.mitre.oauth2.model;
import java.util.Date;
import java.util.Set;
import javax.persistence.Basic;
import javax.persistence.CollectionTable;
import javax.persistence.Column;
import javax.persistence.ElementCollection;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.Transient;
import org.springframework.security.oauth2.common.ExpiringOAuth2RefreshToken;
/**
* @author jricher
*
*/
@Entity
@Table(name="refreshtoken")
@NamedQueries({
@NamedQuery(name = "OAuth2RefreshTokenEntity.getByClient", query = "select r from OAuth2RefreshTokenEntity r where r.client = :client"),
@NamedQuery(name = "OAuth2RefreshTokenEntity.getExpired", query = "select r from OAuth2RefreshTokenEntity r where r.expiration is not null and r.expiration < current_timestamp")
})
public class OAuth2RefreshTokenEntity extends ExpiringOAuth2RefreshToken {
private ClientDetailsEntity client;
private Set<String> scope; // we save the scope issued to the refresh token so that we can reissue a new access token
/**
*
*/
public OAuth2RefreshTokenEntity() {
// TODO Auto-generated constructor stub
}
/* (non-Javadoc)
* @see org.springframework.security.oauth2.common.OAuth2RefreshToken#getValue()
*/
@Override
@Id
@Column(name="id")
public String getValue() {
// TODO Auto-generated method stub
return super.getValue();
}
/* (non-Javadoc)
* @see org.springframework.security.oauth2.common.OAuth2RefreshToken#setValue(java.lang.String)
*/
@Override
public void setValue(String value) {
// TODO Auto-generated method stub
super.setValue(value);
}
/* (non-Javadoc)
* @see org.springframework.security.oauth2.common.ExpiringOAuth2RefreshToken#getExpiration()
*/
@Override
@Basic
@Temporal(javax.persistence.TemporalType.TIMESTAMP)
public Date getExpiration() {
// TODO Auto-generated method stub
return super.getExpiration();
}
/* (non-Javadoc)
* @see org.springframework.security.oauth2.common.ExpiringOAuth2RefreshToken#setExpiration(java.util.Date)
*/
@Override
public void setExpiration(Date expiration) {
// TODO Auto-generated method stub
super.setExpiration(expiration);
}
/**
* Has this token expired?
* @return true if it has a timeout set and the timeout has passed
*/
@Transient
public boolean isExpired() {
return getExpiration() == null ? false : System.currentTimeMillis() > getExpiration().getTime();
}
/**
* @return the client
*/
@ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "client_id")
public ClientDetailsEntity getClient() {
return client;
}
/**
* @param client the client to set
*/
public void setClient(ClientDetailsEntity client) {
this.client = client;
}
/**
* @return the scope
*/
@ElementCollection(fetch = FetchType.EAGER)
@CollectionTable(
joinColumns=@JoinColumn(name="owner_id"),
name="scope"
)
public Set<String> getScope() {
return scope;
}
/**
* @param scope the scope to set
*/
public void setScope(Set<String> scope) {
this.scope = scope;
}
}

View File

@ -0,0 +1,7 @@
package org.mitre.oauth2.model;
public interface OAuth2RefreshTokenEntityFactory {
public OAuth2RefreshTokenEntity createNewRefreshToken();
}

View File

@ -0,0 +1,39 @@
package org.mitre.oauth2.model;
import java.util.UUID;
import org.springframework.stereotype.Service;
@Service
public class UUIDTokenFactory implements OAuth2AccessTokenEntityFactory, OAuth2RefreshTokenEntityFactory {
/**
* Create a new access token and set its value to a random UUID
*/
@Override
public OAuth2AccessTokenEntity createNewAccessToken() {
// create our token container
OAuth2AccessTokenEntity token = new OAuth2AccessTokenEntity();
// set a random value (TODO: support JWT)
String tokenValue = UUID.randomUUID().toString();
token.setValue(tokenValue);
return token;
}
/**
* Create a new refresh token and set its value to a random UUID
*/
@Override
public OAuth2RefreshTokenEntity createNewRefreshToken() {
OAuth2RefreshTokenEntity refreshToken = new OAuth2RefreshTokenEntity();
// set a random value for the refresh
String refreshTokenValue = UUID.randomUUID().toString();
refreshToken.setValue(refreshTokenValue);
return refreshToken;
}
}

View File

@ -0,0 +1,66 @@
package org.mitre.oauth2.model.serializer;
import java.io.Writer;
import java.lang.reflect.Type;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.validation.BeanPropertyBindingResult;
import org.springframework.web.servlet.view.AbstractView;
import com.google.gson.ExclusionStrategy;
import com.google.gson.FieldAttributes;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonElement;
import com.google.gson.JsonPrimitive;
import com.google.gson.JsonSerializationContext;
import com.google.gson.JsonSerializer;
public class JSONOAuthClientView extends AbstractView {
@Override
protected void renderMergedOutputModel(Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
Gson gson = new GsonBuilder().setExclusionStrategies(new ExclusionStrategy() {
@Override
public boolean shouldSkipField(FieldAttributes f) {
return false;
}
@Override
public boolean shouldSkipClass(Class<?> clazz) {
// skip the JPA binding wrapper
if (clazz.equals(BeanPropertyBindingResult.class)) {
return true;
} else {
return false;
}
}
})
.registerTypeAdapter(GrantedAuthority.class, new JsonSerializer<GrantedAuthority>() {
@Override
public JsonElement serialize(GrantedAuthority src, Type typeOfSrc, JsonSerializationContext context) {
return new JsonPrimitive(src.getAuthority());
}
})
.create();
response.setContentType("application/json");
Writer out = response.getWriter();
Object obj = model.get("entity");
if (obj == null) {
obj = model;
}
gson.toJson(obj, out);
}
}

View File

@ -0,0 +1,100 @@
package org.mitre.oauth2.model.serializer;
import java.io.Writer;
import java.lang.reflect.Type;
import java.text.DateFormat;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.mitre.oauth2.model.OAuth2AccessTokenEntity;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.validation.BeanPropertyBindingResult;
import org.springframework.web.servlet.view.AbstractView;
import com.google.gson.ExclusionStrategy;
import com.google.gson.FieldAttributes;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonPrimitive;
import com.google.gson.JsonSerializationContext;
import com.google.gson.JsonSerializer;
public class TokenIntrospection extends AbstractView {
@Override
protected void renderMergedOutputModel(Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
Gson gson = new GsonBuilder().setExclusionStrategies(new ExclusionStrategy() {
@Override
public boolean shouldSkipField(FieldAttributes f) {
/*
if (f.getDeclaringClass().isAssignableFrom(OAuth2AccessTokenEntity.class)) {
// we don't want to serialize the whole object, just the scope and timeout
if (f.getName().equals("scope")) {
return false;
} else if (f.getName().equals("expiration")) {
return false;
} else {
// skip everything else on this class
return true;
}
} else {
// serialize other classes without filter (lists and sets and things)
return false;
}
*/
return false;
}
@Override
public boolean shouldSkipClass(Class<?> clazz) {
// skip the JPA binding wrapper
if (clazz.equals(BeanPropertyBindingResult.class)) {
return true;
} else {
return false;
}
}
})
.registerTypeAdapter(OAuth2AccessTokenEntity.class, new JsonSerializer<OAuth2AccessTokenEntity>() {
public JsonElement serialize(OAuth2AccessTokenEntity src, Type typeOfSrc, JsonSerializationContext context) {
JsonObject token = new JsonObject();
token.addProperty("valid", true);
JsonArray scopes = new JsonArray();
for (String scope : src.getScope()) {
scopes.add(new JsonPrimitive(scope));
}
token.add("scope", scopes);
token.add("expires", context.serialize(src.getExpiration()));
return token;
}
})
.setDateFormat("yyyy-MM-dd'T'HH:mm:ssZ")
.create();
response.setContentType("application/json");
Writer out = response.getWriter();
Object obj = model.get("entity");
if (obj == null) {
obj = model;
}
gson.toJson(obj, out);
}
}

View File

@ -0,0 +1,22 @@
package org.mitre.oauth2.repository;
import java.util.Collection;
import java.util.List;
import org.mitre.oauth2.model.ClientDetailsEntity;
import org.springframework.security.oauth2.provider.ClientDetails;
import org.springframework.stereotype.Repository;
public interface OAuth2ClientRepository {
public ClientDetailsEntity getClientById(String clientId);
public ClientDetailsEntity saveClient(ClientDetailsEntity client);
public void deleteClient(ClientDetailsEntity client);
public ClientDetailsEntity updateClient(String clientId, ClientDetailsEntity client);
public Collection<ClientDetailsEntity> getAllClients();
}

View File

@ -0,0 +1,35 @@
package org.mitre.oauth2.repository;
import java.util.List;
import org.mitre.oauth2.model.ClientDetailsEntity;
import org.mitre.oauth2.model.OAuth2AccessTokenEntity;
import org.mitre.oauth2.model.OAuth2RefreshTokenEntity;
public interface OAuth2TokenRepository {
public OAuth2AccessTokenEntity saveAccessToken(OAuth2AccessTokenEntity token);
public OAuth2RefreshTokenEntity getRefreshTokenByValue(String refreshTokenValue);
public void clearAccessTokensForRefreshToken(OAuth2RefreshTokenEntity refreshToken);
public void removeRefreshToken(OAuth2RefreshTokenEntity refreshToken);
public OAuth2RefreshTokenEntity saveRefreshToken(OAuth2RefreshTokenEntity refreshToken);
public OAuth2AccessTokenEntity getAccessTokenByValue(String accessTokenValue);
public void removeAccessToken(OAuth2AccessTokenEntity accessToken);
public void clearTokensForClient(ClientDetailsEntity client);
public List<OAuth2AccessTokenEntity> getAccessTokensForClient(ClientDetailsEntity client);
public List<OAuth2RefreshTokenEntity> getRefreshTokensForClient(ClientDetailsEntity client);
public List<OAuth2AccessTokenEntity> getExpiredAccessTokens();
public List<OAuth2RefreshTokenEntity> getExpiredRefreshTokens();
}

View File

@ -0,0 +1,74 @@
package org.mitre.oauth2.repository.impl;
import java.util.Collection;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.TypedQuery;
import org.mitre.oauth2.model.ClientDetailsEntity;
import org.mitre.oauth2.repository.OAuth2ClientRepository;
import org.mitre.util.jpa.JpaUtil;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;
/**
* @author jricher
*
*/
@Repository
@Transactional
public class JpaOAuth2ClientRepository implements OAuth2ClientRepository {
@PersistenceContext
private EntityManager manager;
public JpaOAuth2ClientRepository() {
}
public JpaOAuth2ClientRepository(EntityManager manager) {
this.manager = manager;
}
/* (non-Javadoc)
* @see org.mitre.oauth2.repository.OAuth2ClientRepository#getClientById(java.lang.String)
*/
@Override
public ClientDetailsEntity getClientById(String clientId) {
return manager.find(ClientDetailsEntity.class, clientId);
}
/* (non-Javadoc)
* @see org.mitre.oauth2.repository.OAuth2ClientRepository#saveClient(org.mitre.oauth2.model.ClientDetailsEntity)
*/
@Override
public ClientDetailsEntity saveClient(ClientDetailsEntity client) {
return JpaUtil.saveOrUpdate(client.getClientId(), manager, client);
}
/* (non-Javadoc)
* @see org.mitre.oauth2.repository.OAuth2ClientRepository#deleteClient(org.mitre.oauth2.model.ClientDetailsEntity)
*/
@Override
public void deleteClient(ClientDetailsEntity client) {
ClientDetailsEntity found = getClientById(client.getClientId());
if (found != null) {
manager.remove(found);
} else {
throw new IllegalArgumentException("Client not found: " + client);
}
}
@Override
public ClientDetailsEntity updateClient(String clientId, ClientDetailsEntity client) {
return JpaUtil.saveOrUpdate(clientId, manager, client);
}
@Override
public Collection<ClientDetailsEntity> getAllClients() {
TypedQuery<ClientDetailsEntity> query = manager.createNamedQuery("ClientDetailsEntity.findAll", ClientDetailsEntity.class);
return query.getResultList();
}
}

View File

@ -0,0 +1,138 @@
package org.mitre.oauth2.repository.impl;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.TypedQuery;
import org.mitre.oauth2.model.ClientDetailsEntity;
import org.mitre.oauth2.model.OAuth2AccessTokenEntity;
import org.mitre.oauth2.model.OAuth2RefreshTokenEntity;
import org.mitre.oauth2.repository.OAuth2TokenRepository;
import org.mitre.util.jpa.JpaUtil;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;
@Repository
@Transactional
public class JpaOAuth2TokenRepository implements OAuth2TokenRepository {
@PersistenceContext
private EntityManager manager;
@Override
public OAuth2AccessTokenEntity getAccessTokenByValue(String accessTokenValue) {
return manager.find(OAuth2AccessTokenEntity.class, accessTokenValue);
}
@Override
@Transactional
public OAuth2AccessTokenEntity saveAccessToken(OAuth2AccessTokenEntity token) {
return JpaUtil.saveOrUpdate(token.getValue(), manager, token);
}
@Override
@Transactional
public void removeAccessToken(OAuth2AccessTokenEntity accessToken) {
OAuth2AccessTokenEntity found = getAccessTokenByValue(accessToken.getValue());
if (found != null) {
manager.remove(found);
} else {
throw new IllegalArgumentException("Access token not found: " + accessToken);
}
}
@Override
@Transactional
public void clearAccessTokensForRefreshToken(OAuth2RefreshTokenEntity refreshToken) {
TypedQuery<OAuth2AccessTokenEntity> query = manager.createNamedQuery("OAuth2AccessTokenEntity.getByRefreshToken", OAuth2AccessTokenEntity.class);
query.setParameter("refreshToken", refreshToken);
List<OAuth2AccessTokenEntity> accessTokens = query.getResultList();
for (OAuth2AccessTokenEntity accessToken : accessTokens) {
removeAccessToken(accessToken);
}
}
@Override
public OAuth2RefreshTokenEntity getRefreshTokenByValue(String refreshTokenValue) {
return manager.find(OAuth2RefreshTokenEntity.class, refreshTokenValue);
}
@Override
@Transactional
public OAuth2RefreshTokenEntity saveRefreshToken(OAuth2RefreshTokenEntity refreshToken) {
return JpaUtil.saveOrUpdate(refreshToken.getValue(), manager, refreshToken);
}
@Override
@Transactional
public void removeRefreshToken(OAuth2RefreshTokenEntity refreshToken) {
OAuth2RefreshTokenEntity found = getRefreshTokenByValue(refreshToken.getValue());
if (found != null) {
manager.remove(found);
} else {
throw new IllegalArgumentException("Refresh token not found: " + refreshToken);
}
}
@Override
@Transactional
public void clearTokensForClient(ClientDetailsEntity client) {
TypedQuery<OAuth2AccessTokenEntity> queryA = manager.createNamedQuery("OAuth2AccessTokenEntity.getByClient", OAuth2AccessTokenEntity.class);
queryA.setParameter("client", client);
List<OAuth2AccessTokenEntity> accessTokens = queryA.getResultList();
for (OAuth2AccessTokenEntity accessToken : accessTokens) {
removeAccessToken(accessToken);
}
TypedQuery<OAuth2RefreshTokenEntity> queryR = manager.createNamedQuery("OAuth2RefreshTokenEntity.getByClient", OAuth2RefreshTokenEntity.class);
queryR.setParameter("client", client);
List<OAuth2RefreshTokenEntity> refreshTokens = queryR.getResultList();
for (OAuth2RefreshTokenEntity refreshToken : refreshTokens) {
removeRefreshToken(refreshToken);
}
}
/* (non-Javadoc)
* @see org.mitre.oauth2.repository.OAuth2TokenRepository#getAccessTokensForClient(org.mitre.oauth2.model.ClientDetailsEntity)
*/
@Override
public List<OAuth2AccessTokenEntity> getAccessTokensForClient(ClientDetailsEntity client) {
TypedQuery<OAuth2AccessTokenEntity> queryA = manager.createNamedQuery("OAuth2AccessTokenEntity.getByClient", OAuth2AccessTokenEntity.class);
queryA.setParameter("client", client);
List<OAuth2AccessTokenEntity> accessTokens = queryA.getResultList();
return accessTokens;
}
/* (non-Javadoc)
* @see org.mitre.oauth2.repository.OAuth2TokenRepository#getRefreshTokensForClient(org.mitre.oauth2.model.ClientDetailsEntity)
*/
@Override
public List<OAuth2RefreshTokenEntity> getRefreshTokensForClient(ClientDetailsEntity client) {
TypedQuery<OAuth2RefreshTokenEntity> queryR = manager.createNamedQuery("OAuth2RefreshTokenEntity.getByClient", OAuth2RefreshTokenEntity.class);
queryR.setParameter("client", client);
List<OAuth2RefreshTokenEntity> refreshTokens = queryR.getResultList();
return refreshTokens;
}
/* (non-Javadoc)
* @see org.mitre.oauth2.repository.OAuth2TokenRepository#getExpiredAccessTokens()
*/
@Override
public List<OAuth2AccessTokenEntity> getExpiredAccessTokens() {
TypedQuery<OAuth2AccessTokenEntity> queryA = manager.createNamedQuery("OAuth2AccessTokenEntity.getExpired", OAuth2AccessTokenEntity.class);
List<OAuth2AccessTokenEntity> accessTokens = queryA.getResultList();
return accessTokens;
}
/* (non-Javadoc)
* @see org.mitre.oauth2.repository.OAuth2TokenRepository#getExpiredRefreshTokens()
*/
@Override
public List<OAuth2RefreshTokenEntity> getExpiredRefreshTokens() {
TypedQuery<OAuth2RefreshTokenEntity> queryR = manager.createNamedQuery("OAuth2RefreshTokenEntity.getExpired", OAuth2RefreshTokenEntity.class);
List<OAuth2RefreshTokenEntity> refreshTokens = queryR.getResultList();
return refreshTokens;
}
}

View File

@ -0,0 +1,22 @@
package org.mitre.oauth2.service;
import java.util.Collection;
import java.util.Set;
import org.mitre.oauth2.model.ClientDetailsEntity;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.oauth2.common.exceptions.OAuth2Exception;
import org.springframework.security.oauth2.provider.ClientDetailsService;
public interface ClientDetailsEntityService extends ClientDetailsService {
public ClientDetailsEntity loadClientByClientId(String clientId) throws OAuth2Exception;
public ClientDetailsEntity createClient(String clientId, String clientSecret, Set<String> scope, Set<String> grantTypes, String redirectUri, Set<GrantedAuthority> authorities, Set<String> resourceIds, String name, String description, boolean allowRefresh, Long accessTokenTimeout, Long refreshTokenTimeout, String owner);
public void deleteClient(ClientDetailsEntity client);
public ClientDetailsEntity updateClient(ClientDetailsEntity oldClient, ClientDetailsEntity newClient);
public Collection<ClientDetailsEntity> getAllClients();
}

View File

@ -0,0 +1,26 @@
package org.mitre.oauth2.service;
import java.util.List;
import org.mitre.oauth2.model.ClientDetailsEntity;
import org.mitre.oauth2.model.OAuth2AccessTokenEntity;
import org.mitre.oauth2.model.OAuth2RefreshTokenEntity;
import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices;
import org.springframework.security.oauth2.provider.token.ResourceServerTokenServices;
public interface OAuth2TokenEntityService extends AuthorizationServerTokenServices, ResourceServerTokenServices {
public OAuth2AccessTokenEntity getAccessToken(String accessTokenValue);
public OAuth2RefreshTokenEntity getRefreshToken(String refreshTokenValue);
public void revokeRefreshToken(OAuth2RefreshTokenEntity refreshToken);
public void revokeAccessToken(OAuth2AccessTokenEntity accessToken);
public List<OAuth2AccessTokenEntity> getAccessTokensForClient(ClientDetailsEntity client);
public List<OAuth2RefreshTokenEntity> getRefreshTokensForClient(ClientDetailsEntity client);
public void clearExpiredTokens();
}

View File

@ -0,0 +1,130 @@
package org.mitre.oauth2.service.impl;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import org.mitre.oauth2.model.ClientDetailsEntity;
import org.mitre.oauth2.model.ClientDetailsEntityFactory;
import org.mitre.oauth2.repository.OAuth2ClientRepository;
import org.mitre.oauth2.repository.OAuth2TokenRepository;
import org.mitre.oauth2.service.ClientDetailsEntityService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.oauth2.common.exceptions.InvalidClientException;
import org.springframework.security.oauth2.common.exceptions.OAuth2Exception;
import org.springframework.stereotype.Service;
import com.google.common.base.Strings;
@Service
public class DefaultOAuth2ClientDetailsEntityService implements ClientDetailsEntityService {
@Autowired
private OAuth2ClientRepository clientRepository;
@Autowired
private OAuth2TokenRepository tokenRepository;
@Autowired
private ClientDetailsEntityFactory clientFactory;
public DefaultOAuth2ClientDetailsEntityService() {
}
public DefaultOAuth2ClientDetailsEntityService(OAuth2ClientRepository clientRepository,
OAuth2TokenRepository tokenRepository, ClientDetailsEntityFactory clientFactory) {
this.clientRepository = clientRepository;
this.tokenRepository = tokenRepository;
this.clientFactory = clientFactory;
}
/**
* Get the client for the given ID
*/
@Override
public ClientDetailsEntity loadClientByClientId(String clientId) throws OAuth2Exception, InvalidClientException, IllegalArgumentException {
if (!Strings.isNullOrEmpty(clientId)) {
ClientDetailsEntity client = clientRepository.getClientById(clientId);
if (client == null) {
throw new InvalidClientException("Client with id " + clientId + " was not found");
}
else {
return client;
}
}
throw new IllegalArgumentException("Client id must not be empty!");
}
/**
* Create a new client with the appropriate fields filled in
*/
@Override
public ClientDetailsEntity createClient(String clientId, String clientSecret,
Set<String> scope, Set<String> grantTypes, String redirectUri, Set<GrantedAuthority> authorities,
Set<String> resourceIds,
String name, String description, boolean allowRefresh, Long accessTokenTimeout,
Long refreshTokenTimeout, String owner) {
// TODO: check "owner" locally?
ClientDetailsEntity client = clientFactory.createClient(clientId, clientSecret);
client.setScope(scope);
client.setAuthorizedGrantTypes(grantTypes);
client.setRegisteredRedirectUri(redirectUri);
client.setAuthorities(authorities);
client.setClientName(name);
client.setClientDescription(description);
client.setAllowRefresh(allowRefresh);
client.setAccessTokenTimeout(accessTokenTimeout);
client.setRefreshTokenTimeout(refreshTokenTimeout);
client.setResourceIds(resourceIds);
client.setOwner(owner);
clientRepository.saveClient(client);
return client;
}
/**
* Delete a client and all its associated tokens
*/
@Override
public void deleteClient(ClientDetailsEntity client) throws InvalidClientException {
if (clientRepository.getClientById(client.getClientId()) == null) {
throw new InvalidClientException("Client with id " + client.getClientId() + " was not found");
}
// clean out any tokens that this client had issued
tokenRepository.clearTokensForClient(client);
// take care of the client itself
clientRepository.deleteClient(client);
}
/**
* Update the oldClient with information from the newClient. The
* id from oldClient is retained.
*/
@Override
public ClientDetailsEntity updateClient(ClientDetailsEntity oldClient, ClientDetailsEntity newClient) throws IllegalArgumentException {
if (oldClient != null && newClient != null) {
return clientRepository.updateClient(oldClient.getClientId(), newClient);
}
throw new IllegalArgumentException("Neither old client or new client can be null!");
}
/**
* Get all clients in the system
*/
@Override
public Collection<ClientDetailsEntity> getAllClients() {
return clientRepository.getAllClients();
}
}

View File

@ -0,0 +1,319 @@
/**
*
*/
package org.mitre.oauth2.service.impl;
import java.util.Date;
import java.util.List;
import java.util.Set;
import org.mitre.oauth2.model.ClientDetailsEntity;
import org.mitre.oauth2.model.OAuth2AccessTokenEntity;
import org.mitre.oauth2.model.OAuth2AccessTokenEntityFactory;
import org.mitre.oauth2.model.OAuth2RefreshTokenEntity;
import org.mitre.oauth2.model.OAuth2RefreshTokenEntityFactory;
import org.mitre.oauth2.repository.OAuth2TokenRepository;
import org.mitre.oauth2.service.ClientDetailsEntityService;
import org.mitre.oauth2.service.OAuth2TokenEntityService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.security.authentication.AuthenticationCredentialsNotFoundException;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.oauth2.common.exceptions.InvalidClientException;
import org.springframework.security.oauth2.common.exceptions.InvalidTokenException;
import org.springframework.security.oauth2.provider.AuthorizationRequest;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.client.ClientAuthenticationToken;
import org.springframework.stereotype.Service;
import com.google.common.collect.Sets;
/**
* @author jricher
*
*/
@Service
public class DefaultOAuth2ProviderTokenService implements OAuth2TokenEntityService {
private static Logger logger = LoggerFactory.getLogger(DefaultOAuth2ProviderTokenService.class);
@Autowired
private OAuth2TokenRepository tokenRepository;
@Autowired
private ClientDetailsEntityService clientDetailsService;
@Autowired
private OAuth2AccessTokenEntityFactory accessTokenFactory;
@Autowired
private OAuth2RefreshTokenEntityFactory refreshTokenFactory;
@Override
public OAuth2AccessTokenEntity createAccessToken(OAuth2Authentication authentication) throws AuthenticationException, InvalidClientException {
if (authentication != null &&
authentication.getAuthorizationRequest() != null) {
// look up our client
AuthorizationRequest clientAuth = authentication.getAuthorizationRequest();
ClientDetailsEntity client = clientDetailsService.loadClientByClientId(clientAuth.getClientId());
if (client == null) {
throw new InvalidClientException("Client not found: " + clientAuth.getClientId());
}
OAuth2AccessTokenEntity token = accessTokenFactory.createNewAccessToken();
// attach the client
token.setClient(client);
// inherit the scope from the auth
// this lets us match which scope is requested
if (client.isScoped()) {
// restrict granted scopes to a valid subset of those
Set<String> validScopes = Sets.newHashSet();
for (String requested : clientAuth.getScope()) {
if (client.getScope().contains(requested)) {
validScopes.add(requested);
} else {
logger.warn("Client " + client.getClientId() + " requested out of permission scope: " + requested);
}
}
token.setScope(validScopes);
}
// make it expire if necessary
if (client.getAccessTokenTimeout() != null) {
Date expiration = new Date(System.currentTimeMillis() + (client.getAccessTokenTimeout() * 1000L));
token.setExpiration(expiration);
}
// attach the authorization so that we can look it up later
token.setAuthentication(authentication);
// attach a refresh token, if this client is allowed to request them
if (client.isAllowRefresh()) {
OAuth2RefreshTokenEntity refreshToken = refreshTokenFactory.createNewRefreshToken();
// make it expire if necessary
if (client.getRefreshTokenTimeout() != null) {
Date expiration = new Date(System.currentTimeMillis() + (client.getRefreshTokenTimeout() * 1000L));
refreshToken.setExpiration(expiration);
}
// save our scopes so that we can reuse them later for more auth tokens
// TODO: save the auth instead of the just the scope?
if (client.isScoped()) {
refreshToken.setScope(clientAuth.getScope());
}
tokenRepository.saveRefreshToken(refreshToken);
token.setRefreshToken(refreshToken);
}
tokenRepository.saveAccessToken(token);
return token;
}
throw new AuthenticationCredentialsNotFoundException("No authentication credentials found");
}
@Override
public OAuth2AccessTokenEntity refreshAccessToken(String refreshTokenValue, Set<String> scope) throws AuthenticationException {
OAuth2RefreshTokenEntity refreshToken = tokenRepository.getRefreshTokenByValue(refreshTokenValue);
if (refreshToken == null) {
throw new InvalidTokenException("Invalid refresh token: " + refreshTokenValue);
}
ClientDetailsEntity client = refreshToken.getClient();
//Make sure this client allows access token refreshing
if (!client.isAllowRefresh()) {
throw new InvalidClientException("Client does not allow refreshing access token!");
}
// clear out any access tokens
// TODO: make this a configurable option
tokenRepository.clearAccessTokensForRefreshToken(refreshToken);
if (refreshToken.isExpired()) {
tokenRepository.removeRefreshToken(refreshToken);
throw new InvalidTokenException("Expired refresh token: " + refreshTokenValue);
}
// TODO: have the option to recycle the refresh token here, too
// for now, we just reuse it as long as it's valid, which is the original intent
OAuth2AccessTokenEntity token = accessTokenFactory.createNewAccessToken();
if (scope != null && !scope.isEmpty()) {
// ensure a proper subset of scopes
if (refreshToken.getScope() != null && refreshToken.getScope().containsAll(scope)) {
// set the scope of the new access token if requested
refreshToken.setScope(scope);
} else {
// up-scoping is not allowed
// (TODO: should this throw InvalidScopeException? For now just pass through)
token.setScope(refreshToken.getScope());
}
} else {
// otherwise inherit the scope of the refresh token (if it's there -- this can return a null scope set)
token.setScope(refreshToken.getScope());
}
token.setClient(client);
if (client.getAccessTokenTimeout() != null) {
Date expiration = new Date(System.currentTimeMillis() + (client.getAccessTokenTimeout() * 1000L));
token.setExpiration(expiration);
}
token.setRefreshToken(refreshToken);
tokenRepository.saveAccessToken(token);
return token;
}
@Override
public OAuth2Authentication loadAuthentication(String accessTokenValue) throws AuthenticationException {
OAuth2AccessTokenEntity accessToken = tokenRepository.getAccessTokenByValue(accessTokenValue);
if (accessToken == null) {
throw new InvalidTokenException("Invalid access token: " + accessTokenValue);
}
if (accessToken.isExpired()) {
tokenRepository.removeAccessToken(accessToken);
throw new InvalidTokenException("Expired access token: " + accessTokenValue);
}
return accessToken.getAuthentication();
}
@Override
public OAuth2AccessTokenEntity getAccessToken(String accessTokenValue) throws AuthenticationException {
OAuth2AccessTokenEntity accessToken = tokenRepository.getAccessTokenByValue(accessTokenValue);
if (accessToken == null) {
throw new InvalidTokenException("Access token for value " + accessTokenValue + " was not found");
}
else {
return accessToken;
}
}
@Override
public OAuth2RefreshTokenEntity getRefreshToken(String refreshTokenValue) throws AuthenticationException {
OAuth2RefreshTokenEntity refreshToken = tokenRepository.getRefreshTokenByValue(refreshTokenValue);
if (refreshToken == null) {
throw new InvalidTokenException("Refresh token for value " + refreshTokenValue + " was not found");
}
else {
return refreshToken;
}
}
@Override
public void revokeRefreshToken(OAuth2RefreshTokenEntity refreshToken) {
tokenRepository.clearAccessTokensForRefreshToken(refreshToken);
tokenRepository.removeRefreshToken(refreshToken);
}
@Override
public void revokeAccessToken(OAuth2AccessTokenEntity accessToken) {
tokenRepository.removeAccessToken(accessToken);
}
/* (non-Javadoc)
* @see org.mitre.oauth2.service.OAuth2TokenEntityService#getAccessTokensForClient(org.mitre.oauth2.model.ClientDetailsEntity)
*/
@Override
public List<OAuth2AccessTokenEntity> getAccessTokensForClient(ClientDetailsEntity client) {
return tokenRepository.getAccessTokensForClient(client);
}
/* (non-Javadoc)
* @see org.mitre.oauth2.service.OAuth2TokenEntityService#getRefreshTokensForClient(org.mitre.oauth2.model.ClientDetailsEntity)
*/
@Override
public List<OAuth2RefreshTokenEntity> getRefreshTokensForClient(ClientDetailsEntity client) {
return tokenRepository.getRefreshTokensForClient(client);
}
@Override
@Scheduled(fixedRate = 5 * 60 * 1000) // schedule this task every five minutes
public void clearExpiredTokens() {
logger.info("Cleaning out all expired tokens");
List<OAuth2AccessTokenEntity> accessTokens = tokenRepository.getExpiredAccessTokens();
logger.info("Found " + accessTokens.size() + " expired access tokens");
for (OAuth2AccessTokenEntity oAuth2AccessTokenEntity : accessTokens) {
revokeAccessToken(oAuth2AccessTokenEntity);
}
List<OAuth2RefreshTokenEntity> refreshTokens = tokenRepository.getExpiredRefreshTokens();
logger.info("Found " + refreshTokens.size() + " expired refresh tokens");
for (OAuth2RefreshTokenEntity oAuth2RefreshTokenEntity : refreshTokens) {
revokeRefreshToken(oAuth2RefreshTokenEntity);
}
}
/**
* Get a builder object for this class (for tests)
* @return
*/
public static DefaultOAuth2ProviderTokenServicesBuilder makeBuilder() {
return new DefaultOAuth2ProviderTokenServicesBuilder();
}
/**
* Builder class for test harnesses.
*/
public static class DefaultOAuth2ProviderTokenServicesBuilder {
private DefaultOAuth2ProviderTokenService instance;
private DefaultOAuth2ProviderTokenServicesBuilder() {
instance = new DefaultOAuth2ProviderTokenService();
}
public DefaultOAuth2ProviderTokenServicesBuilder setTokenRepository(OAuth2TokenRepository tokenRepository) {
instance.tokenRepository = tokenRepository;
return this;
}
public DefaultOAuth2ProviderTokenServicesBuilder setClientDetailsService(ClientDetailsEntityService clientDetailsService) {
instance.clientDetailsService = clientDetailsService;
return this;
}
public DefaultOAuth2ProviderTokenServicesBuilder setAccessTokenFactory(OAuth2AccessTokenEntityFactory accessTokenFactory) {
instance.accessTokenFactory = accessTokenFactory;
return this;
}
public DefaultOAuth2ProviderTokenServicesBuilder setRefreshTokenFactory(OAuth2RefreshTokenEntityFactory refreshTokenFactory) {
instance.refreshTokenFactory = refreshTokenFactory;
return this;
}
public OAuth2TokenEntityService finish() {
return instance;
}
}
}

View File

@ -0,0 +1,43 @@
package org.mitre.oauth2.web;
import org.mitre.oauth2.model.OAuth2AccessTokenEntity;
import org.mitre.oauth2.service.OAuth2TokenEntityService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.ModelAndView;
@Controller
public class IntrospectionEndpoint {
@Autowired
OAuth2TokenEntityService tokenServices;
public IntrospectionEndpoint() {
}
public IntrospectionEndpoint(OAuth2TokenEntityService tokenServices) {
this.tokenServices = tokenServices;
}
// TODO
@RequestMapping("/oauth/verify")
public ModelAndView verify(@RequestParam("token") String tokenValue,
ModelAndView modelAndView) {
OAuth2AccessTokenEntity token = tokenServices.getAccessToken(tokenValue);
if (token == null) {
// if it's not a valid token, we'll print a 404
modelAndView.setViewName("tokenNotFound");
} else {
// if it's a valid token, we'll print out the scope and expiration
modelAndView.setViewName("tokenIntrospection");
modelAndView.addObject("entity", token);
}
return modelAndView;
}
}

View File

@ -0,0 +1,201 @@
package org.mitre.oauth2.web;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import org.mitre.oauth2.exception.ClientNotFoundException;
import org.mitre.oauth2.exception.DuplicateClientIdException;
import org.mitre.oauth2.model.ClientDetailsEntity;
import org.mitre.oauth2.service.ClientDetailsEntityService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.GrantedAuthorityImpl;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.ModelAndView;
import com.google.common.base.Function;
import com.google.common.base.Splitter;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
@Controller
@RequestMapping("/manager/oauth/clients/api")
public class OAuthClientAPI {
@Autowired
private ClientDetailsEntityService clientService;
private static final Logger logger = LoggerFactory.getLogger(OAuthClientAPI.class);
public OAuthClientAPI() {
}
public OAuthClientAPI(ClientDetailsEntityService clientService) {
this.clientService = clientService;
}
// TODO: i think this needs a fancier binding than just strings on the way in
@PreAuthorize("hasRole('ROLE_ADMIN')")
@RequestMapping("/add")
public ModelAndView apiAddClient(ModelAndView modelAndView,
@RequestParam String clientId, @RequestParam String clientSecret,
@RequestParam String scope, // space delimited
@RequestParam String grantTypes, // space delimited
@RequestParam(required=false) String redirectUri,
@RequestParam String authorities, // space delimited
@RequestParam(required=false) String resourceIds, // space delimited
@RequestParam(required=false) String name,
@RequestParam(required=false) String description,
@RequestParam(required=false, defaultValue="false") boolean allowRefresh,
@RequestParam(required=false) Long accessTokenTimeout,
@RequestParam(required=false) Long refreshTokenTimeout,
@RequestParam(required=false) String owner
) {
logger.info("apiAddClient - start");
ClientDetailsEntity oldClient = clientService.loadClientByClientId(clientId);
if (oldClient != null) {
throw new DuplicateClientIdException(clientId);
}
Splitter spaceDelimited = Splitter.on(" ");
// parse all of our space-delimited lists
Set<String> scopeSet = Sets.newHashSet(spaceDelimited.split(scope));
Set<String> grantTypesSet = Sets.newHashSet(spaceDelimited.split(grantTypes)); // TODO: make a stronger binding to GrantTypes
logger.info("apiAddClient - before creating authorities list");
Set<GrantedAuthority> authoritiesSet = Sets.newHashSet(
Iterables.transform(spaceDelimited.split(authorities), new Function<String, GrantedAuthority>() {
@Override
public GrantedAuthority apply(String auth) {
return new GrantedAuthorityImpl(auth);
}
}));
logger.info("apiAddClient - printing client details");
logger.info("Making call to create client with " + clientId + ", " + clientSecret
+ ", " + scopeSet + ", " + grantTypesSet + ", " + redirectUri + ", "
+ authoritiesSet + ", " + name + ", " + description + ", " + allowRefresh
+ ", " + accessTokenTimeout + ", " + refreshTokenTimeout + ", " + owner);
Set<String> resourceIdSet = Sets.newHashSet(spaceDelimited.split(resourceIds));
ClientDetailsEntity client = clientService.createClient(clientId, clientSecret,
scopeSet, grantTypesSet, redirectUri, authoritiesSet, resourceIdSet, name, description,
allowRefresh, accessTokenTimeout, refreshTokenTimeout, owner);
logger.info("apiAddClient - adding model objects");
modelAndView.addObject("entity", client);
modelAndView.setViewName("jsonOAuthClientView");
logger.info("apiAddClient - end");
return modelAndView;
}
@PreAuthorize("hasRole('ROLE_ADMIN')")
@RequestMapping("/delete")
public ModelAndView apiDeleteClient(ModelAndView modelAndView,
@RequestParam String clientId) {
ClientDetailsEntity client = clientService.loadClientByClientId(clientId);
if (client == null) {
throw new ClientNotFoundException("Client not found: " + clientId);
}
clientService.deleteClient(client);
modelAndView.setViewName("management/successfullyRemoved");
return modelAndView;
}
// TODO: the serializtion of this falls over, don't know why
@PreAuthorize("hasRole('ROLE_ADMIN')")
@RequestMapping("/getAll")
public ModelAndView apiGetAllClients(ModelAndView modelAndView) {
Collection<ClientDetailsEntity> clients = clientService.getAllClients();
modelAndView.addObject("entity", clients);
modelAndView.setViewName("jsonOAuthClientView");
return modelAndView;
}
@PreAuthorize("hasRole('ROLE_ADMIN')")
@RequestMapping("/update")
public ModelAndView apiUpdateClient(ModelAndView modelAndView,
@RequestParam String clientId, @RequestParam String clientSecret,
@RequestParam String scope, // space delimited
@RequestParam String grantTypes, // space delimited
@RequestParam(required=false) String redirectUri,
@RequestParam String authorities, // space delimited
@RequestParam(required=false) String resourceIds, // space delimited
@RequestParam(required=false) String name,
@RequestParam(required=false) String description,
@RequestParam(required=false, defaultValue="false") boolean allowRefresh,
@RequestParam(required=false) Long accessTokenTimeout,
@RequestParam(required=false) Long refreshTokenTimeout,
@RequestParam(required=false) String owner
) {
ClientDetailsEntity client = clientService.loadClientByClientId(clientId);
if (client == null) {
throw new ClientNotFoundException("Client not found: " + clientId);
}
Splitter spaceDelimited = Splitter.on(" ");
// parse all of our space-delimited lists
Set<String> scopeSet = Sets.newHashSet(spaceDelimited.split(scope));
Set<String> grantTypesSet = Sets.newHashSet(spaceDelimited.split(grantTypes)); // TODO: make a stronger binding to GrantTypes
Set<GrantedAuthority> authoritiesSet = Sets.newHashSet(
Iterables.transform(spaceDelimited.split(authorities), new Function<String, GrantedAuthority>() {
@Override
public GrantedAuthority apply(String auth) {
return new GrantedAuthorityImpl(auth);
}
}));
Set<String> resourceIdSet = Sets.newHashSet(spaceDelimited.split(resourceIds));
client.setClientSecret(clientSecret);
client.setScope(scopeSet);
client.setAuthorizedGrantTypes(grantTypesSet);
client.setRegisteredRedirectUri(redirectUri);
client.setAuthorities(authoritiesSet);
client.setResourceIds(resourceIdSet);
client.setClientName(name);
client.setClientDescription(description);
client.setAllowRefresh(allowRefresh);
client.setAccessTokenTimeout(accessTokenTimeout);
client.setRefreshTokenTimeout(refreshTokenTimeout);
client.setOwner(owner);
clientService.updateClient(client, client);
modelAndView.addObject("entity", client);
modelAndView.setViewName("jsonOAuthClientView");
return modelAndView;
}
@PreAuthorize("hasRole('ROLE_ADMIN')")
@RequestMapping("/getById")
public ModelAndView getClientById(ModelAndView modelAndView,
@RequestParam String clientId) {
ClientDetailsEntity client = clientService.loadClientByClientId(clientId);
if (client == null) {
throw new ClientNotFoundException("Client not found: " + clientId);
}
modelAndView.addObject("entity", client);
modelAndView.setViewName("jsonOAuthClientView");
return modelAndView;
}
}

View File

@ -0,0 +1,166 @@
/**
*
*/
package org.mitre.oauth2.web;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import org.mitre.oauth2.model.ClientDetailsEntity;
import org.mitre.oauth2.model.OAuth2AccessTokenEntity;
import org.mitre.oauth2.model.OAuth2RefreshTokenEntity;
import org.mitre.oauth2.service.ClientDetailsEntityService;
import org.mitre.oauth2.service.OAuth2TokenEntityService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.GrantedAuthorityImpl;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.oauth2.provider.AuthorizationRequest;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
import com.google.common.collect.Sets;
/**
*
* Endpoint for managing OAuth2 clients
*
* @author jricher
*
*/
@Controller
@RequestMapping("/manager/oauth/clients")
public class OAuthClientController {
private final static Set<String> GRANT_TYPES = Sets.newHashSet("authorization_code", "client_credentials", "password", "implicit");
@Autowired
private ClientDetailsEntityService clientService;
@Autowired
private OAuth2TokenEntityService tokenService;
private Logger logger;
public OAuthClientController() {
logger = LoggerFactory.getLogger(this.getClass());
}
public OAuthClientController(ClientDetailsEntityService clientService, OAuth2TokenEntityService tokenService) {
this.clientService = clientService;
this.tokenService = tokenService;
logger = LoggerFactory.getLogger(this.getClass());
}
/**
* Redirect to the "/" version of the root
* @param modelAndView
* @return
*/
@RequestMapping("")
public ModelAndView redirectRoot(ModelAndView modelAndView) {
modelAndView.setViewName("redirect:/manager/oauth/clients/");
return modelAndView;
}
/**
* View all clients
* @param modelAndView
* @return
*/
@PreAuthorize("hasRole('ROLE_ADMIN')")
@RequestMapping("/")
public ModelAndView viewAllClients(ModelAndView modelAndView) {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
//ClientAuthenticationToken clientAuth = (ClientAuthenticationToken) ((OAuth2Authentication) auth).getClientAuthentication();
AuthorizationRequest clientAuth = ((OAuth2Authentication) auth).getAuthorizationRequest();
logger.info("Client auth = " + clientAuth);
logger.info("Granted authorities = " + clientAuth.getAuthorities().toString());
Collection<ClientDetailsEntity> clients = clientService.getAllClients();
modelAndView.addObject("clients", clients);
modelAndView.setViewName("/management/oauth/clientIndex");
return modelAndView;
}
@PreAuthorize("hasRole('ROLE_ADMIN')")
@RequestMapping("/add")
public ModelAndView redirectAdd(ModelAndView modelAndView) {
modelAndView.setViewName("redirect:/manager/oauth/clients/add/");
return modelAndView;
}
@PreAuthorize("hasRole('ROLE_ADMIN')")
@RequestMapping("/add/")
public ModelAndView addClientPage(ModelAndView modelAndView) {
Set<GrantedAuthority> auth = Sets.newHashSet();
auth.add(new GrantedAuthorityImpl("ROLE_CLIENT"));
ClientDetailsEntity client = ClientDetailsEntity.makeBuilder()
.setScope(Sets.newHashSet("scope"))
.setAuthorities(auth) // why do we have to pull this into a separate list?
.setAuthorizedGrantTypes(Sets.newHashSet("authorization_code"))
.finish();
modelAndView.addObject("availableGrantTypes", GRANT_TYPES);
modelAndView.addObject("client", client);
modelAndView.setViewName("/management/oauth/editClient");
return modelAndView;
}
@PreAuthorize("hasRole('ROLE_ADMIN')")
@RequestMapping("/delete/{clientId}")
public ModelAndView deleteClientConfirmation(ModelAndView modelAndView,
@PathVariable String clientId) {
ClientDetailsEntity client = clientService.loadClientByClientId(clientId);
modelAndView.addObject("client", client);
modelAndView.setViewName("/management/oauth/deleteClientConfirm");
return modelAndView;
}
@PreAuthorize("hasRole('ROLE_ADMIN')")
@RequestMapping("/edit/{clientId}")
public ModelAndView editClientPage(ModelAndView modelAndView,
@PathVariable String clientId) {
ClientDetailsEntity client = clientService.loadClientByClientId(clientId);
modelAndView.addObject("availableGrantTypes", GRANT_TYPES);
modelAndView.addObject("client", client);
modelAndView.setViewName("/management/oauth/editClient");
return modelAndView;
}
@PreAuthorize("hasRole('ROLE_ADMIN')")
@RequestMapping("/view/{clientId}")
public ModelAndView viewClientDetails(ModelAndView modelAndView,
@PathVariable String clientId) {
ClientDetailsEntity client = clientService.loadClientByClientId(clientId);
List<OAuth2AccessTokenEntity> accessTokens = tokenService.getAccessTokensForClient(client);
List<OAuth2RefreshTokenEntity> refreshTokens = tokenService.getRefreshTokensForClient(client);
modelAndView.addObject("client", client);
modelAndView.addObject("accessTokens", accessTokens);
modelAndView.addObject("refreshTokens", refreshTokens);
modelAndView.setViewName("/management/oauth/viewClient");
return modelAndView;
}
}

View File

@ -0,0 +1,70 @@
/**
*
*/
package org.mitre.oauth2.web;
import org.mitre.oauth2.exception.ClientNotFoundException;
import org.mitre.oauth2.service.ClientDetailsEntityService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.oauth2.provider.AuthorizationRequest;
import org.springframework.security.oauth2.provider.ClientDetails;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.SessionAttributes;
import org.springframework.web.servlet.ModelAndView;
/**
* @author jricher
*
*/
@Controller
@SessionAttributes(types = AuthorizationRequest.class)
public class OAuthConfirmationController {
private ClientDetailsEntityService clientService;
public OAuthConfirmationController() {
}
public OAuthConfirmationController(ClientDetailsEntityService clientService) {
this.clientService = clientService;
}
@PreAuthorize("hasRole('ROLE_USER')")
@RequestMapping("/oauth/user/approve")
public ModelAndView confimAccess(@ModelAttribute AuthorizationRequest clientAuth,
ModelAndView modelAndView) {
ClientDetails client = clientService.loadClientByClientId(clientAuth.getClientId());
if (client == null) {
throw new ClientNotFoundException("Client not found: " + clientAuth.getClientId());
}
modelAndView.addObject("auth_request", clientAuth);
modelAndView.addObject("client", client);
modelAndView.setViewName("oauth/approve");
return modelAndView;
}
/**
* @return the clientService
*/
public ClientDetailsEntityService getClientService() {
return clientService;
}
/**
* @param clientService the clientService to set
*/
@Autowired
public void setClientService(ClientDetailsEntityService clientService) {
this.clientService = clientService;
}
}

View File

@ -0,0 +1,79 @@
package org.mitre.oauth2.web;
import org.mitre.oauth2.exception.PermissionDeniedException;
import org.mitre.oauth2.model.OAuth2AccessTokenEntity;
import org.mitre.oauth2.model.OAuth2RefreshTokenEntity;
import org.mitre.oauth2.service.OAuth2TokenEntityService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.oauth2.common.exceptions.InvalidTokenException;
import org.springframework.security.oauth2.provider.AuthorizationRequest;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.ModelAndView;
@Controller
public class RevocationEndpoint {
@Autowired
OAuth2TokenEntityService tokenServices;
public RevocationEndpoint() {
}
public RevocationEndpoint(OAuth2TokenEntityService tokenServices) {
this.tokenServices = tokenServices;
}
// TODO
@PreAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_CLIENT')")
@RequestMapping("/oauth/revoke")
public ModelAndView revoke(@RequestParam("token") String tokenValue,
ModelAndView modelAndView) {
OAuth2RefreshTokenEntity refreshToken = tokenServices.getRefreshToken(tokenValue);
OAuth2AccessTokenEntity accessToken = tokenServices.getAccessToken(tokenValue);
if (refreshToken == null && accessToken == null) {
// TODO: this should throw a 400 with a JSON error code
throw new InvalidTokenException("Invalid OAuth token: " + tokenValue);
}
// TODO: there should be a way to do this in SPEL, right?
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
if (auth instanceof OAuth2Authentication) {
// we've got a client acting on its own behalf, not an admin
//ClientAuthentication clientAuth = (ClientAuthenticationToken) ((OAuth2Authentication) auth).getClientAuthentication();
AuthorizationRequest clientAuth = ((OAuth2Authentication) auth).getAuthorizationRequest();
if (refreshToken != null) {
if (!refreshToken.getClient().getClientId().equals(clientAuth.getClientId())) {
// trying to revoke a token we don't own, fail
// TODO: this should throw a 403
throw new PermissionDeniedException("Client tried to revoke a token it doesn't own");
}
} else {
if (!accessToken.getClient().getClientId().equals(clientAuth.getClientId())) {
// trying to revoke a token we don't own, fail
// TODO: this should throw a 403
throw new PermissionDeniedException("Client tried to revoke a token it doesn't own");
}
}
}
// if we got this far, we're allowed to do this
if (refreshToken != null) {
tokenServices.revokeRefreshToken(refreshToken);
} else {
tokenServices.revokeAccessToken(accessToken);
}
// TODO: throw a 200 back (no content?)
return modelAndView;
}
}

View File

@ -1,10 +1,14 @@
package org.mitre.openid.connect.model;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
@Entity
public class Address {
private Long id;
private String formatted;
private String street_address;
private String locality;
@ -12,6 +16,8 @@ public class Address {
private String postal_code;
private String country;
/**
* @return the formatted
*/
@ -85,4 +91,20 @@ public class Address {
this.country = country;
}
/**
* @return the id
*/
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
public Long getId() {
return id;
}
/**
* @param id the id to set
*/
public void setId(Long id) {
this.id = id;
}
}

View File

@ -8,6 +8,8 @@ import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.Temporal;
import org.springframework.security.oauth2.provider.ClientDetails;
@ -48,7 +50,7 @@ public class ApprovedSite {
* @return the id
*/
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@GeneratedValue(strategy = GenerationType.IDENTITY)
public Long getId() {
return id;
}
@ -63,6 +65,7 @@ public class ApprovedSite {
/**
* @return the userInfo
*/
@ManyToOne
public UserInfo getUserInfo() {
return userInfo;
}
@ -123,6 +126,7 @@ public class ApprovedSite {
/**
* @return the allowedScopes
*/
@OneToMany
public Collection<String> getAllowedScopes() {
return allowedScopes;
}

View File

@ -2,7 +2,12 @@ package org.mitre.openid.connect.model;
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;
/**
* Class to contain a logged event in the system.
@ -20,4 +25,45 @@ public class Event {
private EventType type;
private Date timestamp;
/**
* @return the id
*/
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
public Long getId() {
return id;
}
/**
* @param id the id to set
*/
public void setId(Long id) {
this.id = id;
}
/**
* @return the type
*/
public EventType getType() {
return type;
}
/**
* @param type the type to set
*/
public void setType(EventType type) {
this.type = type;
}
/**
* @return the timestamp
*/
@Basic
@Temporal(javax.persistence.TemporalType.TIMESTAMP)
public Date getTimestamp() {
return timestamp;
}
/**
* @param timestamp the timestamp to set
*/
public void setTimestamp(Date timestamp) {
this.timestamp = timestamp;
}
}

View File

@ -1,105 +0,0 @@
package org.mitre.openid.connect.model;
import javax.persistence.Entity;
import org.mitre.jwt.model.Jwt;
/*
* TODO: This class needs to be encoded as a JWT
*/
@Entity
public class IdToken extends Jwt {
private String iss;
private String user_id;
private String aud;
private String exp;
private String iso29115;
private String nonce;
private String auth_time;
/**
* @return the iss
*/
public String getIss() {
return iss;
}
/**
* @param iss the iss to set
*/
public void setIss(String iss) {
this.iss = iss;
}
/**
* @return the user_id
*/
public String getUser_id() {
return user_id;
}
/**
* @param user_id the user_id to set
*/
public void setUser_id(String user_id) {
this.user_id = user_id;
}
/**
* @return the aud
*/
public String getAud() {
return aud;
}
/**
* @param aud the aud to set
*/
public void setAud(String aud) {
this.aud = aud;
}
/**
* @return the exp
*/
public String getExp() {
return exp;
}
/**
* @param exp the exp to set
*/
public void setExp(String exp) {
this.exp = exp;
}
/**
* @return the iso29115
*/
public String getIso29115() {
return iso29115;
}
/**
* @param iso29115 the iso29115 to set
*/
public void setIso29115(String iso29115) {
this.iso29115 = iso29115;
}
/**
* @return the nonce
*/
public String getNonce() {
return nonce;
}
/**
* @param nonce the nonce to set
*/
public void setNonce(String nonce) {
this.nonce = nonce;
}
/**
* @return the auth_time
*/
public String getAuth_time() {
return auth_time;
}
/**
* @param auth_time the auth_time to set
*/
public void setAuth_time(String auth_time) {
this.auth_time = auth_time;
}
}

View File

@ -0,0 +1,77 @@
package org.mitre.openid.connect.model;
import java.util.Date;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import org.mitre.jwt.model.Jwt;
import org.mitre.jwt.model.JwtClaims;
/*
* TODO: This class needs to be encoded as a JWT
*/
@Entity
public class IdTokenClaims extends JwtClaims {
public static final String USER_ID = "user_id";
public static final String AUTHENTICATION_CONTEXT_CLASS_REFERENCE = "acr";
public static final String NONCE = "nonce";
public static final String AUTH_TIME = "auth_time";
private Long id;
/**
* @return the id
*/
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
public Long getId() {
return id;
}
/**
* @param id the id to set
*/
public void setId(Long id) {
this.id = id;
}
public String getUserId() {
return getClaimAsString(USER_ID);
}
public void setUserId(String user_id) {
setClaim(USER_ID, user_id);
}
public String getAuthContext() {
return getClaimAsString(AUTHENTICATION_CONTEXT_CLASS_REFERENCE);
}
public void setAuthContext(String acr) {
setClaim(AUTHENTICATION_CONTEXT_CLASS_REFERENCE, acr);
}
public String getNonce() {
return getClaimAsString(NONCE);
}
public void setNonce(String nonce) {
setClaim(NONCE, nonce);
}
public Date getAuthTime() {
return getClaimAsDate(AUTH_TIME);
}
public void setAuthTime(Date authTime) {
setClaim(AUTH_TIME, authTime);
}
}

View File

@ -1,6 +1,7 @@
package org.mitre.openid.connect.model;
import javax.persistence.Entity;
import javax.persistence.Id;
@Entity
public class UserInfo {
@ -29,6 +30,7 @@ public class UserInfo {
/**
* @return the user_id
*/
@Id
public String getUser_id() {
return user_id;
}

View File

@ -6,6 +6,7 @@ import java.util.Date;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.ManyToOne;
import org.springframework.security.oauth2.provider.ClientDetails;
@ -19,7 +20,7 @@ public class WhitelistedSite {
// unique id
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
// who added this site to the whitelist (should be an admin)
@ -30,5 +31,6 @@ public class WhitelistedSite {
// what scopes be allowed by default
// this should include all information for what data to access
@ManyToOne
private Collection<String> allowedScopes;
}

View File

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

View File

@ -1,6 +1,6 @@
package org.mitre.openid.connect.web;
import org.mitre.openid.connect.model.IdToken;
import org.mitre.openid.connect.model.IdTokenClaims;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
@ -13,7 +13,7 @@ public class CheckIDEndpoint {
@RequestMapping("/")
public ModelAndView checkID(@RequestParam("id_token") String idToken, ModelAndView mav) {
IdToken token = new IdToken();
IdTokenClaims token = new IdTokenClaims();
//TODO: Set claims

View File

@ -0,0 +1,98 @@
package org.mitre.pushee.openid.service.impl;
import java.util.ArrayList;
import java.util.List;
import org.springframework.dao.DataAccessException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.GrantedAuthorityImpl;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import com.google.common.base.Splitter;
import com.google.common.collect.Iterables;
public class OpenIDUserDetailsService implements UserDetailsService {
private String openIdRoot;
private String admins;
private List<String> adminList = new ArrayList<String>();
private GrantedAuthority roleUser = new GrantedAuthorityImpl("ROLE_USER");
private GrantedAuthority roleAdmin = new GrantedAuthorityImpl("ROLE_ADMIN");
/**
* @return the roleUser
*/
public GrantedAuthority getRoleUser() {
return roleUser;
}
/**
* @param roleUser the roleUser to set
*/
public void setRoleUser(GrantedAuthority roleUser) {
this.roleUser = roleUser;
}
/**
* @return the roleAdmin
*/
public GrantedAuthority getRoleAdmin() {
return roleAdmin;
}
/**
* @param roleAdmin the roleAdmin to set
*/
public void setRoleAdmin(GrantedAuthority roleAdmin) {
this.roleAdmin = roleAdmin;
}
public String getOpenIdRoot() {
return openIdRoot;
}
public void setOpenIdRoot(String openIdRoot) {
this.openIdRoot = openIdRoot;
}
public String getAdmins() {
return admins;
}
public void setAdmins(String admins) {
this.admins = admins;
adminList.clear();
Iterables.addAll(adminList, Splitter.on(',').omitEmptyStrings().split(admins));
}
public UserDetails loadUserByUsername(String identifier) throws UsernameNotFoundException, DataAccessException {
if (identifier != null && identifier.startsWith(openIdRoot)) {
String username = identifier;
//String username = identifier.replace(openIdRoot, ""); // strip off the OpenID root
String password = "notused";
boolean enabled = true;
boolean accountNonExpired = true;
boolean credentialsNonExpired = true;
boolean accountNonLocked = true;
List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
authorities.add(roleUser);
// calculate raw user id (SUI)
// TODO: make this more generic, right now only works with postfix names
String userid = identifier.replace(openIdRoot, "");
if (adminList.contains(userid)) {
authorities.add(roleAdmin);
}
return new User(username, password, enabled, accountNonExpired, credentialsNonExpired, accountNonLocked, authorities);
} else {
throw new UsernameNotFoundException("Identifier " + identifier + " did not match OpenID root " + openIdRoot);
}
}
}

View File

@ -0,0 +1,37 @@
package org.mitre.util.jpa;
import org.springframework.dao.IncorrectResultSizeDataAccessException;
import org.springframework.transaction.annotation.Transactional;
import javax.persistence.EntityManager;
import java.util.List;
/**
* @author mfranklin
* Date: 4/28/11
* Time: 2:13 PM
*/
public class JpaUtil {
public static <T> T getSingleResult(List<T> list) {
switch(list.size()) {
case 0:
return null;
case 1:
return list.get(0);
default:
throw new IncorrectResultSizeDataAccessException(1);
}
}
public static <T, I> T saveOrUpdate(I id, EntityManager entityManager, T entity) {
if (id == null) {
entityManager.persist(entity);
entityManager.flush();
return entity;
} else {
T tmp = entityManager.merge(entity);
entityManager.flush();
return tmp;
}
}
}

@ -1 +1 @@
Subproject commit 7a55ca546a3567a8a1833340d0ff9e2b47cf4bdc
Subproject commit 0aa98f09467f90b36ed06ba5cdbb82722f08de26

View File

@ -0,0 +1 @@
This directory contains .patch files which must be applied to the upstream spring-security-oauth2 module in order for the project to build.

View File

@ -0,0 +1,113 @@
diff --git a/spring-security-oauth2/src/main/java/org/springframework/security/oauth2/common/ExpiringOAuth2RefreshToken.java b/spring-security-oauth2/src/main/java/org/springframework/security/oauth2/common/ExpiringOAuth2RefreshToken.java
index 20d2512..a773b29 100644
--- a/spring-security-oauth2/src/main/java/org/springframework/security/oauth2/common/ExpiringOAuth2RefreshToken.java
+++ b/spring-security-oauth2/src/main/java/org/springframework/security/oauth2/common/ExpiringOAuth2RefreshToken.java
@@ -9,9 +9,17 @@ public class ExpiringOAuth2RefreshToken extends OAuth2RefreshToken {
private static final long serialVersionUID = 3449554332764129719L;
- private final Date expiration;
+ private Date expiration;
/**
+ * Create an expiring refresh token with a null value and no expiration
+ * @param expiration
+ */
+ public ExpiringOAuth2RefreshToken() {
+ this(null, null);
+ }
+
+ /**
* @param value
*/
public ExpiringOAuth2RefreshToken(String value, Date expiration) {
@@ -28,4 +36,11 @@ public class ExpiringOAuth2RefreshToken extends OAuth2RefreshToken {
return expiration;
}
+ /**
+ * Set the expiration of this token
+ */
+ public void setExpiration(Date expiration) {
+ this.expiration = expiration;
+ }
+
}
diff --git a/spring-security-oauth2/src/main/java/org/springframework/security/oauth2/common/OAuth2AccessToken.java b/spring-security-oauth2/src/main/java/org/springframework/security/oauth2/common/OAuth2AccessToken.java
index 791780f..25edf77 100644
--- a/spring-security-oauth2/src/main/java/org/springframework/security/oauth2/common/OAuth2AccessToken.java
+++ b/spring-security-oauth2/src/main/java/org/springframework/security/oauth2/common/OAuth2AccessToken.java
@@ -57,7 +57,7 @@ public class OAuth2AccessToken implements Serializable {
*/
public static String SCOPE = "scope";
- private final String value;
+ private String value;
private Date expiration;
@@ -74,8 +74,10 @@ public class OAuth2AccessToken implements Serializable {
this.value = value;
}
- @SuppressWarnings("unused")
- private OAuth2AccessToken() {
+ /**
+ * Create an access token with no value
+ */
+ public OAuth2AccessToken() {
this(null);
}
@@ -88,6 +90,14 @@ public class OAuth2AccessToken implements Serializable {
return value;
}
+ /**
+ * Set the value of the token.
+ * @param value the token value
+ */
+ public void setValue(String value) {
+ this.value = value;
+ }
+
public int getExpiresIn() {
return expiration != null ? Long.valueOf((expiration.getTime() - System.currentTimeMillis()) / 1000L)
.intValue() : 0;
diff --git a/spring-security-oauth2/src/main/java/org/springframework/security/oauth2/common/OAuth2RefreshToken.java b/spring-security-oauth2/src/main/java/org/springframework/security/oauth2/common/OAuth2RefreshToken.java
index 00b002c..96f3f1b 100644
--- a/spring-security-oauth2/src/main/java/org/springframework/security/oauth2/common/OAuth2RefreshToken.java
+++ b/spring-security-oauth2/src/main/java/org/springframework/security/oauth2/common/OAuth2RefreshToken.java
@@ -15,9 +15,16 @@ public class OAuth2RefreshToken implements Serializable {
private static final long serialVersionUID = 8349970621900575838L;
- private final String value;
+ private String value;
/**
+ * Create an empty token with no value
+ */
+ public OAuth2RefreshToken() {
+ this(null);
+ }
+
+ /**
* Create a new refresh token.
*/
@JsonCreator
@@ -35,6 +42,14 @@ public class OAuth2RefreshToken implements Serializable {
return value;
}
+ /**
+ * Set the value of the token
+ * @param value the value of the token
+ */
+ public void setValue(String value) {
+ this.value = value;
+ }
+
@Override
public String toString() {
return getValue();