All Downloads are FREE. Search and download functionalities are using the official Maven repository.

de.cidaas.oauth.interceptor.TokenHelper Maven / Gradle / Ivy

There is a newer version: 1.0.4
Show newest version
package de.cidaas.oauth.interceptor;

import java.io.IOException;
import java.lang.reflect.Method;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Map;

import javax.annotation.security.RolesAllowed;

import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpException;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.protocol.HTTP;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;

import de.cidaas.jwt.JWT;
import de.cidaas.jwt.Options;
import de.cidaas.oauth.cache.LRUCache;
import de.cidaas.oauth.model.ResolvedUserInfoFromToken;
import de.cidaas.oauth.model.TokenCheckEntity;

public class TokenHelper {
	private static final Logger LOG = LoggerFactory.getLogger(TokenHelper.class);
	LRUCache tokenCache = LRUCache.getInstance();

	public TokenCheckEntity validateAccessToken(TokenCheckEntity tokenInfo, Method method, String access_token) {

		if (StringUtils.isEmpty(access_token)) {
			tokenInfo.setSuccess(false);
			return tokenInfo;
		}

		if (access_token.contains(".")) {
			LOG.info("JWT Token Try to resolve from local");
			String[] pieses = access_token.split("\\.");
			if (pieses.length == 5 && StringUtils.isEmpty(Constants.get_private_key_path())) {
				return validateAccessTokenFromServer(tokenInfo, method, access_token);
			} else {
				return validateAccessTokenLocal(tokenInfo, method, access_token);
			}
		} else {
			return validateAccessTokenFromServer(tokenInfo, method, access_token);
		}
	}

	private TokenCheckEntity validateAccessTokenFromServer(TokenCheckEntity tokenInfo, Method method,
			String accessToken) {
		ResolvedUserInfoFromToken userInfo = null;
		try {

			userInfo = getUserInfoByToken(tokenInfo);

			if (userInfo == null) {
				LOG.info("No userid found for accesstoken, {} ", accessToken);
				tokenInfo.setSuccess(false);
				return tokenInfo;
			}
			tokenInfo.setUserId(userInfo.getUserId());
			tokenInfo.setClientId(userInfo.getClientId());
			tokenCache.put(accessToken.hashCode(), new Date().getTime());

		} catch (HttpException | IOException ex) {
			LOG.error("OAuth-Exception ", ex);
			tokenInfo.setSuccess(false);
			return tokenInfo;
		} catch (Exception ex) {
			LOG.error("Exception ", ex);
			tokenInfo.setSuccess(false);
			return tokenInfo;
		}
		tokenInfo.setSuccess(true);
		tokenInfo.setNeedServerSubmit(false);
		return tokenInfo;
	}

	private TokenCheckEntity validateAccessTokenLocal(TokenCheckEntity tokenInfo, Method method, String access_token) {
		Map claims = parseAccessToken(access_token);
		if (claims == null) {
			LOG.info("claims cannot be null ");
			tokenInfo.setSuccess(false);
			tokenInfo.setRequestedScopes(getAnotationRequestedScopeJoined(method));
			tokenInfo.setRequestedRoles(getAnotationRequestedRolesJoined(method));
			return tokenInfo;
		}

		Object exp = null;
		if (claims.containsKey("exp")) {
			exp = claims.get("exp").toString();
		}
		if (exp == null) {
			tokenInfo.setSuccess(false);
			LOG.info("exp cannot be null ");
			return tokenInfo;
		}

		boolean is_jwt_latest = false;
		if (claims.containsKey("is_jwt_latest")) {
			is_jwt_latest = true;
		}

		if (!validateToken(exp, is_jwt_latest)) {
			tokenInfo.setSuccess(false);
			return tokenInfo;
		}

		String userId = null;
		if (claims.containsKey("sub")) {
			userId = claims.get("sub").toString();
		}
		if (StringUtils.isEmpty(userId)) {
			LOG.info("sub cannot be null ");
			tokenInfo.setSuccess(false);
			return tokenInfo;
		}
		tokenInfo.setUserId(userId);

		// check for the cache time , if not resolve it from server.
		if (!tokenCache.isPresent(access_token)) {
			return validateAccessTokenFromServer(tokenInfo, method, access_token);
		}

		String clientId = null;
		if (claims.containsKey("clientid")) {
			clientId = claims.get("clientid").toString();
		}
		if (StringUtils.isEmpty(clientId)) {
			LOG.info("clientid cannot be null ");
			tokenInfo.setSuccess(false);
			return tokenInfo;
		}
		tokenInfo.setClientId(clientId);
		List scopesAllowedInToken = new ArrayList<>();
		String scopes = "";

		String scopeKey = "scope";
		if (claims.containsKey("is_jwt_latest")) {
			scopeKey = "scopes";
		}
		if (claims.containsKey(scopeKey)) {
			Object val = claims.get(scopeKey);
			if (val != null) {
				if (val instanceof List) {
					scopesAllowedInToken = (List) val;
					scopes = String.join(" ", scopesAllowedInToken);
				} else {
					String castedVal = (String) val;
					if (StringUtils.isNotEmpty(castedVal)) {
						String[] scopesArray = castedVal.split(" ");
						scopes = String.join(" ", scopesArray);
						scopesAllowedInToken = Arrays.asList(scopesArray);
					}
				}
			}
		}

		String[] scopesRequired = getAnotationRequestedScope(method);

		if (scopesRequired != null && scopesRequired.length > 0) {
			tokenInfo.setRequestedScopes(getAnotationRequestedScopeJoined(method));
			tokenInfo.setAllowedScopes(scopes);
			if (!validateScope(scopesRequired, scopesAllowedInToken)) {
				LOG.info("Scope validation failed, Requested scopes {}, Allowed Scopes {} ",
						tokenInfo.getRequestedScopes(), tokenInfo.getAllowedScopes());
				tokenInfo.setSuccess(false);
				return tokenInfo;
			}
		}

		List rolesAllowedInToken = new ArrayList<>();
		String roles = "";

		String roleKey = "role";
		if (claims.containsKey("is_jwt_latest")) {
			roleKey = "roles";
		}
		if (claims.containsKey(roleKey)) {
			Object val = claims.get(roleKey);
			if (val != null) {
				if (val instanceof List) {
					rolesAllowedInToken = (List) val;
					roles = String.join(",", rolesAllowedInToken);
				} else {
					String castedVal = (String) val;
					if (StringUtils.isNotEmpty(castedVal)) {
						String[] roleArray = castedVal.split(",");
						roles = String.join(",", roleArray);
						rolesAllowedInToken = Arrays.asList(roleArray);
					}
				}
			}
		}

		String[] rolesRequired = getAnotationRequestedRoles(method);

		if (rolesRequired != null && rolesRequired.length > 0) {
			tokenInfo.setRequestedRoles(getAnotationRequestedRolesJoined(method));
			tokenInfo.setAllowedRoles(roles);
			if (!validateRole(rolesRequired, rolesAllowedInToken)) {
				LOG.info("Role validation failed, Requested Roles {}, Allowed Roles {} ", tokenInfo.getRequestedRoles(),
						tokenInfo.getAllowedRoles());
				tokenInfo.setSuccess(false);
				return tokenInfo;
			}
		}

		tokenInfo.setSuccess(true);
		return tokenInfo;
	}

	protected String getAnotationRequestedScopeJoined(Method method) {
		String[] scopes = getAnotationRequestedScope(method);
		if (scopes != null && scopes.length > 0) {
			return StringUtils.join(getAnotationRequestedScope(method), " ");
		}
		return null;
	}

	protected String[] getAnotationRequestedScope(Method method) {
		if (method.isAnnotationPresent(OAuthScopes.class)) {
			OAuthScopes oAuthScopes = method.getAnnotation(OAuthScopes.class);
			if (oAuthScopes != null && oAuthScopes.scopes().length > 0) {
				return oAuthScopes.scopes();
			}
		}
		return null;
	}

	protected String getAnotationRequestedRolesJoined(Method method) {
		String[] roles = getAnotationRequestedRoles(method);
		if (roles != null && roles.length > 0) {
			return StringUtils.join(roles, ",");
		}
		return null;
	}

	protected String[] getAnotationRequestedRoles(Method method) {
		if (method.isAnnotationPresent(RolesAllowed.class)) {
			RolesAllowed rolesAnnotation = method.getAnnotation(RolesAllowed.class);
			if (rolesAnnotation != null && rolesAnnotation.value().length > 0) {
				return rolesAnnotation.value();
			}
		}
		return null;
	}

	public boolean validateToken(Object exp, boolean jwtlatest) {
		long expireTime = 0;
		if (exp instanceof Date) {
			expireTime = ((Date) exp).getTime();
		} else {
			String _exp = (String) exp;
			try {

				Long expSeconds = Long.parseLong(_exp);
				if (jwtlatest) {
					// hack for to support old token exp time
					try {
						Date tempDate = new Date(expSeconds);
						if (tempDate.getYear() > 2017) {
							expSeconds = expSeconds / 1000l;
						}
					} catch (Exception e) {

					}

					expireTime = expSeconds * 1000;
				} else {
					expireTime = expSeconds;
				}
			} catch (Exception e) {

			}
		}

		if (expireTime == 0) {
			return false;
		}

		long currentMillisec = System.currentTimeMillis();
		if (currentMillisec >= expireTime) {
			return false;
		}
		return true;
	}

	private Map parseAccessToken(String access_token) {
		String[] pieses = access_token.split("\\.");
		if (pieses.length == 3) {
			// It is JWT or JWS token
			try {
				return JWT.parsePlainJWT(access_token);
			} catch (Exception e) {
				LOG.info("Error while parsing the Plain JWT token, Error : {}", e);
			}
		} else if (pieses.length == 5) {
			// It is JWE token
			try {
				String key_file_path = Constants.get_private_key_path();
				if (StringUtils.isNotEmpty(key_file_path)) {
					Options options = new Options();
					options.setJWEToken(true);
					options.setPrivateKeyPath(key_file_path);
					return JWT.decodeJWT(access_token, options);
				}
			} catch (Exception e) {
				LOG.info("Error while parsing the Plain JWT token, Error : {}", e);
			}
		}

		return null;
	}

	private boolean validateScope(String[] scopesNeededForMethod, List scopesAllowedInToken) {

		// Check if the method need scope, if not allow them for next methods
		if (scopesNeededForMethod == null || scopesNeededForMethod.length == 0) {
			return true;
		}

		// Check if the scopes allowed in the access token if is empty don't
		// allow for next step. since we checked already the above 3 lines for
		// scope required.
		if (scopesAllowedInToken == null || scopesAllowedInToken.size() == 0) {
			return false;
		}

		// Check if the scope allowed in access token contains openid, if yes
		// allow further without checking further scopes
		if (scopesAllowedInToken.contains("openid")) {
			return true;
		}

		// Check if the required scope allowed in the access token
		boolean allowFurtherSteps = false;
		for (String scopeNeeded : scopesNeededForMethod) {
			if (scopesAllowedInToken.stream().filter(c -> c.equalsIgnoreCase(scopeNeeded)).findFirst().isPresent()) {
				allowFurtherSteps = true;
				break;
			}
		}

		return allowFurtherSteps;
	}

	private boolean validateRole(String[] rolesNeededForMethod, List rolesAllowedInToken) {

		// Check if the method need role, if not allow them for next methods
		if (rolesNeededForMethod == null || rolesNeededForMethod.length == 0) {
			return true;
		}

		// Check if the roles allowed in the access token if is empty don't
		// allow for next step. since we checked already the above 3 lines for
		// role required.
		if (rolesAllowedInToken == null || rolesAllowedInToken.size() == 0) {
			return false;
		}

		// Check if the required role allowed in the access token
		boolean allowFurtherSteps = false;
		for (String roleNeeded : rolesNeededForMethod) {
			if (rolesAllowedInToken.stream().filter(c -> c.equalsIgnoreCase(roleNeeded)).findFirst().isPresent()) {
				allowFurtherSteps = true;
				break;
			}
		}

		return allowFurtherSteps;
	}

	private ResolvedUserInfoFromToken getUserInfoByToken(TokenCheckEntity tokenInfo)
			throws HttpException, ClientProtocolException, IOException, URISyntaxException {
		try {
			ObjectMapper om = new ObjectMapper();
			String uri = Constants.get_user_info_by_token();

			HttpPost resetPost = new HttpPost(new URI(uri));

			resetPost.addHeader(Constants.get_tokenKey(), tokenInfo.getAccessToken());

			if (tokenInfo != null) {
				StringEntity se = new StringEntity(om.writeValueAsString(tokenInfo), ContentType.APPLICATION_JSON);
				resetPost.setEntity(se);
				resetPost.addHeader(HTTP.CONTENT_TYPE, "application/json; charset=UTF-8");
			}

			HttpResponse response = HttpClientBuilder.create().build().execute(resetPost);
			int responseStatusCode = response.getStatusLine().getStatusCode();
			LOG.info("User Info By Token Status Code:" + responseStatusCode);

			if (responseStatusCode == HttpStatus.SC_OK) {
				om = om.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
				om = om.configure(DeserializationFeature.FAIL_ON_IGNORED_PROPERTIES, false);
				return om.readValue(response.getEntity().getContent(), ResolvedUserInfoFromToken.class);
			} else {
				LOG.error("User id by token fails. URL : " + uri);
			}
		} catch (Exception ex) {
			LOG.error("Exception at getUserInfoByToken {}", ex);
			throw ex;
		}
		return null;
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy