From d7d9188e3cc71ab6cb38cb79682ee4be2a4233c9 Mon Sep 17 00:00:00 2001 From: Kai Moritz Date: Sun, 12 Jun 2016 12:18:40 +0200 Subject: [PATCH] Refactoring: removed classes, that are not needed for error-handling --- pom.xml | 24 -- .../java/de/juplo/facebook/FacebookUtils.java | 106 -------- .../de/juplo/facebook/aspects/Sanitize.java | 18 -- .../facebook/aspects/SanitizeAspect.java | 93 ------- ...eAuthorizationCodeAccessTokenProvider.java | 248 ------------------ 5 files changed, 489 deletions(-) delete mode 100644 src/main/java/de/juplo/facebook/FacebookUtils.java delete mode 100644 src/main/java/de/juplo/facebook/aspects/Sanitize.java delete mode 100644 src/main/java/de/juplo/facebook/aspects/SanitizeAspect.java delete mode 100644 src/main/java/de/juplo/facebook/token/SignedRequestAwareAuthorizationCodeAccessTokenProvider.java diff --git a/pom.xml b/pom.xml index 2de9e9c..bdce89f 100644 --- a/pom.xml +++ b/pom.xml @@ -58,7 +58,6 @@ 1.7 - 1.8.5 2.6.2 4.12 1.1.3 @@ -183,14 +182,6 @@ - - - org.aspectj - aspectjrt - ${aspectj.version} - provided - - commons-codec @@ -269,21 +260,6 @@ true - - org.codehaus.mojo - aspectj-maven-plugin - 1.8 - - 1.7 - - - - - compile - - - - org.apache.maven.plugins maven-source-plugin diff --git a/src/main/java/de/juplo/facebook/FacebookUtils.java b/src/main/java/de/juplo/facebook/FacebookUtils.java deleted file mode 100644 index f6f6b37..0000000 --- a/src/main/java/de/juplo/facebook/FacebookUtils.java +++ /dev/null @@ -1,106 +0,0 @@ -package de.juplo.facebook; - - -import de.juplo.facebook.errors.GraphApiErrorHandler; -import de.juplo.facebook.token.SignedRequestAwareAuthorizationCodeAccessTokenProvider; -import java.util.Arrays; -import java.util.LinkedList; -import java.util.List; -import javax.annotation.PostConstruct; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.BeansException; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.config.BeanPostProcessor; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.security.oauth2.client.OAuth2RestTemplate; -import org.springframework.security.oauth2.client.http.OAuth2ErrorHandler; -import org.springframework.security.oauth2.client.token.AccessTokenProvider; -import org.springframework.security.oauth2.client.token.AccessTokenProviderChain; -import org.springframework.security.oauth2.client.token.grant.client.ClientCredentialsAccessTokenProvider; -import org.springframework.security.oauth2.client.token.grant.implicit.ImplicitAccessTokenProvider; -import org.springframework.security.oauth2.client.token.grant.password.ResourceOwnerPasswordAccessTokenProvider; - - - -/** - * This class injects the facebook-utils into the spring-oauth2-configuration - * - * @author kai - */ -@Configuration -public class FacebookUtils -{ - private final Logger log = LoggerFactory.getLogger(FacebookUtils.class); - - - @Autowired(required=false) - private List accessTokenProviderChain; - - - @PostConstruct - public void init() - { - if (accessTokenProviderChain == null) - { - log.info("no AccessTokenProviderChain configured, creating default-chain"); - accessTokenProviderChain = - Arrays. asList( - new ImplicitAccessTokenProvider(), - new ResourceOwnerPasswordAccessTokenProvider(), - new ClientCredentialsAccessTokenProvider() - ); - } - } - - - @Bean - public BeanPostProcessor getBeanPostProcessor(final String clientSecret) - { - log.debug("createing new instance of BeanPostProcessor"); - return new BeanPostProcessor() { - - @Override - public Object postProcessBeforeInitialization( - Object bean, - String beanName - ) - throws - BeansException - { - if (bean instanceof OAuth2RestTemplate) - { - log.info("injecting signed_request-aware AccessTokenProviderChain"); - OAuth2RestTemplate template = (OAuth2RestTemplate)bean; - List chain = - new LinkedList<>(accessTokenProviderChain); - SignedRequestAwareAuthorizationCodeAccessTokenProvider provider = - new SignedRequestAwareAuthorizationCodeAccessTokenProvider(); - provider.setSecret(clientSecret); - chain.add(provider); - template.setAccessTokenProvider(new AccessTokenProviderChain(chain)); - log.info("injecting GraphApiErrorHandler"); - template.setErrorHandler( - new GraphApiErrorHandler( - (OAuth2ErrorHandler)template.getErrorHandler() - ) - ); - } - - return bean; - } - - @Override - public Object postProcessAfterInitialization( - Object bean, - String beanName - ) - throws - BeansException - { - return bean; - } - }; - } -} diff --git a/src/main/java/de/juplo/facebook/aspects/Sanitize.java b/src/main/java/de/juplo/facebook/aspects/Sanitize.java deleted file mode 100644 index 0856a04..0000000 --- a/src/main/java/de/juplo/facebook/aspects/Sanitize.java +++ /dev/null @@ -1,18 +0,0 @@ -package de.juplo.facebook.aspects; - - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * @author Kai Moritz - */ -@Target({ ElementType.FIELD }) -@Retention(RetentionPolicy.RUNTIME) -public @interface Sanitize -{ - int length() default 255; // in accordance to @Column(length) - boolean fail() default false; -} diff --git a/src/main/java/de/juplo/facebook/aspects/SanitizeAspect.java b/src/main/java/de/juplo/facebook/aspects/SanitizeAspect.java deleted file mode 100644 index 4bdf908..0000000 --- a/src/main/java/de/juplo/facebook/aspects/SanitizeAspect.java +++ /dev/null @@ -1,93 +0,0 @@ -package de.juplo.facebook.aspects; - - -import org.aspectj.lang.ProceedingJoinPoint; -import org.aspectj.lang.annotation.Around; -import org.aspectj.lang.annotation.Aspect; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - - - -/** - * @author Kai Moritz - */ -@Aspect -public class SanitizeAspect -{ - private static final Logger log = - LoggerFactory.getLogger(SanitizeAspect.class); - - - /** - * This method sanitizes the given string in all means: - *
    - *
  • It removes leading and trailing whitspace.
  • - *
  • It removes characters, that are not allowed in the XML-output
  • - *
  • It checks the allowed length of the string
  • - *
- * - * This method ensures that the output String has only - * valid XML unicode characters as specified by the - * XML 1.0 standard. For reference, please see - * the - * standard. This method will return an empty - * String if the input is null or empty. - * - * @param jp The join-point captured by AspectJ. - * @param in The String whose non-valid characters we want to remove. - * @param sanitize The annotation, the field was marked with. - * @see Invalid XML Characters: when valid UTF8 does not mean valid XML - * @see Ungültige Zeichen in Eingabefeldern abfangen - */ - @Around("set(String *) && args(in) && @annotation(sanitize)") - public void sanitize( - ProceedingJoinPoint jp, - String in, - Sanitize sanitize - ) - throws Throwable - { - if (in == null) - { - jp.proceed(new Object[] { null }); - return; - } - - in = in.trim(); - if ("".equals(in)) - { - jp.proceed(new Object[] { null }); - return; - } - - StringBuilder out = new StringBuilder(); // Used to hold the output. - char current; // Used to reference the current character. - - for (int i = 0; i < in.length(); i++) - { - current = in.charAt(i); // NOTE: No IndexOutOfBoundsException caught here; it should not happen. - if ((current == 0x9) || - (current == 0xA) || - (current == 0xD) || - ((current >= 0x20) && (current <= 0xD7FF)) || - ((current >= 0xE000) && (current <= 0xFFFD)) || - ((current >= 0x10000) && (current <= 0x10FFFF))) - out.append(current); - } - if (out.length() > sanitize.length()) - { - log.error( - "Maximum length for attribute {} exceeded: should={}, was={}", - jp.getSignature().getName(), - sanitize.length(), - out.length() - ); - if (sanitize.fail()) - throw new RuntimeException("String is longer than " + sanitize.length()); - else - out.setLength(sanitize.length()); - } - jp.proceed(new Object[] { out.toString() }); - } -} diff --git a/src/main/java/de/juplo/facebook/token/SignedRequestAwareAuthorizationCodeAccessTokenProvider.java b/src/main/java/de/juplo/facebook/token/SignedRequestAwareAuthorizationCodeAccessTokenProvider.java deleted file mode 100644 index e187542..0000000 --- a/src/main/java/de/juplo/facebook/token/SignedRequestAwareAuthorizationCodeAccessTokenProvider.java +++ /dev/null @@ -1,248 +0,0 @@ -package de.juplo.facebook.token; - - -import java.io.IOException; -import java.io.UnsupportedEncodingException; -import java.security.InvalidKeyException; -import java.security.NoSuchAlgorithmException; -import java.util.Date; -import java.util.HashMap; -import java.util.Map; -import java.util.regex.Matcher; -import java.util.regex.Pattern; -import javax.crypto.Mac; -import javax.crypto.spec.SecretKeySpec; -import org.apache.commons.codec.binary.Base64; -import org.codehaus.jackson.JsonNode; -import org.codehaus.jackson.map.ObjectMapper; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.security.oauth2.client.resource.OAuth2ProtectedResourceDetails; -import org.springframework.security.oauth2.client.resource.UserRedirectRequiredException; -import org.springframework.security.oauth2.client.token.AccessTokenRequest; -import org.springframework.security.oauth2.client.token.grant.code.AuthorizationCodeAccessTokenProvider; -import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken; -import org.springframework.security.oauth2.common.OAuth2AccessToken; - - -/** - * This class extends {@link AuthorizationCodeAccessTokenProvider} and adds - * support for signed requests, which are issued by Facebook, if the Canvas- - * or Tab-Page of a Facebook-App is accessed for the first time. - * - * @author Kai Moritz - */ -public class SignedRequestAwareAuthorizationCodeAccessTokenProvider - extends AuthorizationCodeAccessTokenProvider -{ - private final Logger log = - LoggerFactory.getLogger(SignedRequestAwareAuthorizationCodeAccessTokenProvider.class); - private final static Pattern pattern = - Pattern.compile("([a-zA-Z0-9_-]+)\\.([a-zA-Z0-9_-]+)"); - - public final static String PARAM_SIGNED_REQUEST = "signed_request"; - - - private String secret; - private ObjectMapper objectMapper; - - - @Override - public OAuth2AccessToken obtainAccessToken( - OAuth2ProtectedResourceDetails details, - AccessTokenRequest parameters - ) - { - try - { - return super.obtainAccessToken(details, parameters); - } - catch (UserRedirectRequiredException redirect) - { - log.debug("no valid access-token available: checking for signed request"); - - if (!parameters.containsKey(PARAM_SIGNED_REQUEST)) - { - log.info( - "parameter " + PARAM_SIGNED_REQUEST + " is not present" - ); - throw redirect; - } - - String signed_request = parameters.get(PARAM_SIGNED_REQUEST).get(0); - - Matcher matcher = pattern.matcher(signed_request); - if (!matcher.matches()) - { - log.error("invalid signed_request: {}", signed_request); - throw redirect; - } - - String signature = matcher.group(1); - String rawdata = matcher.group(2); - - String data; - try - { - data = new String(Base64.decodeBase64(rawdata), "UTF-8"); - log.debug("JSON-data: {}", data); - } - catch (UnsupportedEncodingException e) - { - log.error("error while decoding data: {}", e.getMessage()); - throw redirect; - } - - JsonNode json; - try - { - json = objectMapper.readTree(data); - } - catch (IOException e) - { - log.error("error \"{}\" while parsing JSON-data: {}", e, data); - throw redirect; - } - - String algorithm = ""; - try - { - algorithm = json.get("algorithm").asText(); - } - catch (NullPointerException e) {} - if (algorithm.isEmpty()) - { - log.error("field \"algorithm\" is missing: {}", data); - throw redirect; - } - algorithm = algorithm.replaceAll("-", ""); - - String check; - try - { - SecretKeySpec key = new SecretKeySpec(secret.getBytes("UTF-8"), algorithm); - Mac mac = Mac.getInstance(algorithm); - mac.init(key); - byte[] hmacData = mac.doFinal(rawdata.getBytes("UTF-8")); - check = new String(Base64.encodeBase64URLSafe(hmacData), "UTF-8"); - } - catch ( - UnsupportedEncodingException | - NoSuchAlgorithmException | - InvalidKeyException | - IllegalStateException e - ) - { - log.error("signature check failed!", e); - throw redirect; - } - if (!check.equals(signature)) - { - log.error("signature does not match!"); - throw redirect; - } - - /** - * Extract additional information and store it in the token - * See: - * https://developers.facebook.com/docs/reference/login/signed-request/ - * TODO: - * - Attribute "code" - */ - Map additionalInformation = new HashMap<>(); - try - { - additionalInformation.put( - "issued_at", - new Date(json.get("issued_at").getLongValue()*1000L) - ); - Map user = new HashMap<>(); - user.put( - "country", - json.get("user").get("country").asText() - ); - user.put( - "locale", - json.get("user").get("locale").asText() - ); - user.put( - "age_min", - json.get("user").get("age").get("min").getNumberValue() - ); - if (json.get("user") != null && json.get("user").get("max") != null) - user.put( - "age_max", - json.get("user").get("age").get("max").getNumberValue() - ); - additionalInformation.put("user", user); - if (json.get("app_data") != null) - additionalInformation.put("app_data", json.get("app_data").asText()); - if (json.get("page") != null) - { - Map page = new HashMap<>(); - page.put("id", json.get("page").get("id").asText()); - page.put("liked", json.get("page").get("liked").asBoolean()); - page.put("admin", json.get("page").get("admin").asBoolean()); - additionalInformation.put("page", page); - } - } - catch (NullPointerException e) - { - log.warn("expected additional data is missing: {}", data); - } - - DefaultOAuth2AccessToken token = null; - try - { - String value = json.get("oauth_token").asText(); - if (value.isEmpty()) - { - log.error("field \"oauth_token\" is missing: {}", data); - throw redirect; - } - token = new DefaultOAuth2AccessToken(value); - token.setExpiration(new Date(json.get("expires").getLongValue()*1000L)); - - additionalInformation.put( - "user_id", - json.get("user_id").asText() - ); - - token.setAdditionalInformation(additionalInformation); - } - catch (NullPointerException e) - { - if (token == null) - { - log.error("field \"oauth_token\" is missing: {}", data); - throw redirect; - } - else - log.warn("expected additional data is missing: {}", data); - } - - return token; - } - } - - - public String getSecret() - { - return secret; - } - - public void setSecret(String secret) - { - this.secret = secret; - } - - public ObjectMapper getObjectMapper() - { - return objectMapper; - } - - public void setObjectMapper(ObjectMapper objectMapper) - { - this.objectMapper = objectMapper; - } -} -- 2.20.1