Generic introspecting token services
parent
59f1b1f05e
commit
358bf7fc1c
|
@ -0,0 +1,83 @@
|
|||
package org.mitre.oauth2.filter;
|
||||
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.oauth2.provider.AuthorizationRequest;
|
||||
|
||||
public class AuthorizationRequestImpl implements AuthorizationRequest {
|
||||
|
||||
private JsonObject token;
|
||||
private String clientId;
|
||||
private Set<String> scopes = null;
|
||||
|
||||
public AuthorizationRequestImpl(JsonObject token) {
|
||||
this.token = token;
|
||||
clientId = token.get("client_id").getAsString();
|
||||
scopes = new HashSet<String>();
|
||||
for (JsonElement e : token.get("scope").getAsJsonArray()) {
|
||||
scopes.add(e.getAsString());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> getAuthorizationParameters() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> getApprovalParameters() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getClientId() {
|
||||
return clientId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getScope() {
|
||||
|
||||
return scopes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getResourceIds() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<GrantedAuthority> getAuthorities() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isApproved() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDenied() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getState() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRedirectUri() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getResponseTypes() {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,201 @@
|
|||
package org.mitre.oauth2.filter;
|
||||
|
||||
import com.google.common.collect.Sets;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParser;
|
||||
import java.text.DateFormat;
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Collection;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.AuthenticationException;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.core.authority.AuthorityUtils;
|
||||
import org.springframework.security.oauth2.common.OAuth2AccessToken;
|
||||
import org.springframework.security.oauth2.common.OAuth2RefreshToken;
|
||||
import org.springframework.security.oauth2.provider.AuthorizationRequest;
|
||||
import org.springframework.security.oauth2.provider.OAuth2Authentication;
|
||||
import org.springframework.security.oauth2.provider.token.ResourceServerTokenServices;
|
||||
import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
import org.springframework.web.client.RestClientException;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
public class IntrospectingTokenService implements ResourceServerTokenServices {
|
||||
|
||||
|
||||
private String clientId;
|
||||
private String clientSecret;
|
||||
private String introspectionUrl;
|
||||
|
||||
// Inner class to store in the hash map
|
||||
private class TokenCacheObject { OAuth2AccessToken token; OAuth2Authentication auth;
|
||||
private TokenCacheObject(OAuth2AccessToken token, OAuth2Authentication auth) {
|
||||
this.token = token;
|
||||
this.auth = auth;
|
||||
}
|
||||
}
|
||||
private Map<String, TokenCacheObject> authCache = new HashMap<String, TokenCacheObject>();
|
||||
|
||||
public String getIntrospectionUrl() {
|
||||
return introspectionUrl;
|
||||
}
|
||||
|
||||
public void setIntrospectionUrl(String introspectionUrl) {
|
||||
this.introspectionUrl = introspectionUrl;
|
||||
}
|
||||
|
||||
public String getClientId() {
|
||||
return clientId;
|
||||
}
|
||||
|
||||
public void setClientId(String clientId) {
|
||||
this.clientId = clientId;
|
||||
}
|
||||
|
||||
public String getClientSecret() {
|
||||
return clientSecret;
|
||||
}
|
||||
|
||||
public void setClientSecret(String clientSecret) {
|
||||
this.clientSecret = clientSecret;
|
||||
}
|
||||
|
||||
// Check if there is a token and authentication in the cache
|
||||
// and check if it is not expired.
|
||||
private TokenCacheObject checkCache(String key) {
|
||||
if(authCache.containsKey(key)) {
|
||||
TokenCacheObject tco = authCache.get(key);
|
||||
if (tco.token.getExpiration().after(new Date())) {
|
||||
return tco;
|
||||
} else {
|
||||
// if the token is expired, don't keep things around.
|
||||
authCache.remove(key);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private AuthorizationRequest createAuthRequest(final JsonObject token) {
|
||||
AuthorizationRequest authReq = new AuthorizationRequestImpl(token);
|
||||
return authReq;
|
||||
}
|
||||
|
||||
// create a default authentication object with authority ROLE_API
|
||||
private Authentication createAuthentication(JsonObject token){
|
||||
// TODO: user_id is going to go away. Will have to fix.
|
||||
return new PreAuthenticatedAuthenticationToken(token.get("user_id").getAsString(), null, AuthorityUtils.createAuthorityList("ROLE_API"));
|
||||
}
|
||||
|
||||
private OAuth2AccessToken createAccessToken(final JsonObject token, final String tokenString){
|
||||
OAuth2AccessToken accessToken = new OAuth2AccessTokenImpl(token, tokenString);
|
||||
return accessToken;
|
||||
}
|
||||
|
||||
// Validate a token string against the introspection endpoint,
|
||||
// then parse it and store it in the local cache. Return true on
|
||||
// sucess, false otherwise.
|
||||
private boolean parseToken(String accessToken) {
|
||||
String validatedToken = null;
|
||||
// Use the SpringFramework RestTemplate to send the request to the endpoint
|
||||
|
||||
RestTemplate restTemplate = new RestTemplate();
|
||||
MultiValueMap<String, String> form = new LinkedMultiValueMap<String, String>();
|
||||
form.add("token",accessToken);
|
||||
form.add("client_id", this.clientId);
|
||||
form.add("client_scret", this.clientSecret);
|
||||
|
||||
try {
|
||||
validatedToken = restTemplate.postForObject(introspectionUrl, form, String.class);
|
||||
} catch (RestClientException rce) {
|
||||
// TODO: LOG THIS!?
|
||||
LoggerFactory.getLogger(IntrospectingTokenService.class).error("validateToken", rce);
|
||||
}
|
||||
if (validatedToken != null) {
|
||||
// parse the json
|
||||
JsonElement jsonRoot = new JsonParser().parse(validatedToken);
|
||||
if (!jsonRoot.isJsonObject()) {
|
||||
return false; // didn't get a proper JSON object
|
||||
}
|
||||
|
||||
JsonObject tokenResponse = jsonRoot.getAsJsonObject();
|
||||
|
||||
if (tokenResponse.get("error") != null) {
|
||||
// report an error?
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!tokenResponse.get("valid").getAsBoolean()){
|
||||
// non-valid token
|
||||
return false;
|
||||
}
|
||||
// create an OAuth2Authentication
|
||||
OAuth2Authentication auth = new OAuth2Authentication(createAuthRequest(tokenResponse), null);
|
||||
// create an OAuth2AccessToken
|
||||
OAuth2AccessToken token = createAccessToken(tokenResponse, accessToken);
|
||||
|
||||
if (token.getExpiration().after(new Date())){
|
||||
// Store them in the cache
|
||||
authCache.put(accessToken, new TokenCacheObject(token,auth));
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// If we never put a token and an authentication in the cache...
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public OAuth2Authentication loadAuthentication(String accessToken) throws AuthenticationException {
|
||||
// First check if the in memory cache has an Authentication object, and that it is still valid
|
||||
// If Valid, return it
|
||||
TokenCacheObject cacheAuth = checkCache(accessToken);
|
||||
if (cacheAuth != null) {
|
||||
return cacheAuth.auth;
|
||||
} else {
|
||||
if (parseToken(accessToken)) {
|
||||
cacheAuth = authCache.get(accessToken);
|
||||
if (cacheAuth != null && (cacheAuth.token.getExpiration().after(new Date()))) {
|
||||
return cacheAuth.auth;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public OAuth2AccessToken readAccessToken(String accessToken) {
|
||||
// First check if the in memory cache has a Token object, and that it is still valid
|
||||
// If Valid, return it
|
||||
TokenCacheObject cacheAuth = checkCache(accessToken);
|
||||
if (cacheAuth != null) {
|
||||
return cacheAuth.token;
|
||||
} else {
|
||||
if (parseToken(accessToken)) {
|
||||
cacheAuth = authCache.get(accessToken);
|
||||
if (cacheAuth != null && (cacheAuth.token.getExpiration().after(new Date()))) {
|
||||
return cacheAuth.token;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,90 @@
|
|||
package org.mitre.oauth2.filter;
|
||||
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import java.text.DateFormat;
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import org.springframework.security.oauth2.common.OAuth2AccessToken;
|
||||
import org.springframework.security.oauth2.common.OAuth2RefreshToken;
|
||||
|
||||
|
||||
public class OAuth2AccessTokenImpl implements OAuth2AccessToken {
|
||||
|
||||
private JsonObject token;
|
||||
private String tokenString;
|
||||
private Set<String> scopes = null;
|
||||
private Date expireDate;
|
||||
|
||||
|
||||
public OAuth2AccessTokenImpl(JsonObject token, String tokenString) {
|
||||
this.token = token;
|
||||
this.tokenString = tokenString;
|
||||
scopes = new HashSet<String>();
|
||||
for (JsonElement e : token.get("scope").getAsJsonArray()) {
|
||||
scopes.add(e.getAsString());
|
||||
}
|
||||
|
||||
DateFormat dateFormater = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ");
|
||||
try {
|
||||
expireDate = dateFormater.parse(token.get("expires_at").getAsString());
|
||||
} catch (ParseException ex) {
|
||||
Logger.getLogger(IntrospectingTokenService.class.getName()).log(Level.SEVERE, null, ex);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Map<String, Object> getAdditionalInformation() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getScope() {
|
||||
return scopes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public OAuth2RefreshToken getRefreshToken() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTokenType() {
|
||||
return BEARER_TYPE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isExpired() {
|
||||
if (expireDate != null && expireDate.before(new Date())) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Date getExpiration() {
|
||||
return expireDate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getExpiresIn() {
|
||||
if (expireDate != null) {
|
||||
return (int)TimeUnit.MILLISECONDS.toSeconds(expireDate.getTime() - (new Date()).getTime());
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getValue() {
|
||||
return tokenString;
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue