PageRenderTime 50ms CodeModel.GetById 23ms RepoModel.GetById 0ms app.codeStats 0ms

/server/src/io/seldon/api/interceptor/GenericScopedInterceptor.java

https://gitlab.com/varunkothamachu/seldon-server
Java | 183 lines | 131 code | 27 blank | 25 comment | 19 complexity | 985518b95b8cae21ad23bd3542eca39e MD5 | raw file
  1. /*
  2. * Seldon -- open source prediction engine
  3. * =======================================
  4. *
  5. * Copyright 2011-2015 Seldon Technologies Ltd and Rummble Ltd (http://www.seldon.io/)
  6. *
  7. * ********************************************************************************************
  8. *
  9. * Licensed under the Apache License, Version 2.0 (the "License");
  10. * you may not use this file except in compliance with the License.
  11. * You may obtain a copy of the License at
  12. *
  13. * http://www.apache.org/licenses/LICENSE-2.0
  14. *
  15. * Unless required by applicable law or agreed to in writing, software
  16. * distributed under the License is distributed on an "AS IS" BASIS,
  17. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  18. * See the License for the specific language governing permissions and
  19. * limitations under the License.
  20. *
  21. * ********************************************************************************************
  22. */
  23. package io.seldon.api.interceptor;
  24. import io.seldon.api.APIException;
  25. import io.seldon.api.logging.ApiLogger;
  26. import io.seldon.api.logging.MDCKeys;
  27. import io.seldon.api.resource.ConsumerBean;
  28. import io.seldon.api.resource.ErrorBean;
  29. import io.seldon.api.resource.ScopedConsumerBean;
  30. import io.seldon.api.service.AuthorizationServer;
  31. import io.seldon.api.service.TokenScope;
  32. import java.io.IOException;
  33. import java.net.URI;
  34. import java.net.URISyntaxException;
  35. import java.util.Date;
  36. import java.util.regex.Matcher;
  37. import java.util.regex.Pattern;
  38. import javax.servlet.http.HttpServletRequest;
  39. import javax.servlet.http.HttpServletResponse;
  40. import javax.servlet.http.HttpSession;
  41. import org.apache.commons.lang.StringUtils;
  42. import org.apache.log4j.Logger;
  43. import org.springframework.beans.factory.annotation.Autowired;
  44. import org.springframework.web.servlet.ModelAndView;
  45. import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
  46. import com.fasterxml.jackson.databind.ObjectMapper;
  47. /**
  48. * Created by: marc on 13/08/2012 at 15:24
  49. */
  50. abstract public class GenericScopedInterceptor extends HandlerInterceptorAdapter implements ScopedInterceptor {
  51. private static final Logger logger = Logger.getLogger(GenericScopedInterceptor.class);
  52. private static final String START_TIME = "startTime";
  53. @Autowired
  54. private AuthorizationServer authorizationServer;
  55. @Override
  56. public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
  57. final String requestURI = request.getRequestURI();
  58. if (logger.isDebugEnabled())
  59. logger.debug("Scoped interceptor for: " + requestURI);
  60. Date start = new Date();
  61. request.setAttribute("startTime", start);
  62. try {
  63. final ConsumerBean consumerBean = authorise(request);
  64. final HttpSession session = request.getSession();
  65. session.setAttribute("consumer", consumerBean);
  66. return super.preHandle(request, response, handler);
  67. } catch (APIException e) {
  68. exceptionResponse(request, response, e);
  69. String apiKey = request.getServletPath().replaceAll("/", "\\."); //Asumes no user/item ids in path!!!!
  70. ApiLogger.log(apiKey,start, request, null);
  71. } catch (Throwable t) {
  72. logger.error("GenericScopedInterceptor#preHandle: " + t.getMessage(), t);
  73. }
  74. return false;
  75. }
  76. @Override
  77. public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
  78. Date start = (Date)request.getAttribute(START_TIME);
  79. final HttpSession session = request.getSession();
  80. final ConsumerBean consumerBean = (ConsumerBean) session.getAttribute("consumer");
  81. String apiKey = request.getServletPath().replaceAll("/", "\\."); //Asumes no user/item ids in path!!!!
  82. ApiLogger.log(apiKey,start, request, consumerBean);
  83. super.postHandle(request, response, handler, modelAndView);
  84. }
  85. protected void exceptionResponse(HttpServletRequest request, HttpServletResponse response, APIException e) throws IOException {
  86. ErrorBean bean = new ErrorBean(e);
  87. logger.error("GenericScopedInterceptor#exceptionResponse: " + e.getMessage(), e);
  88. final ObjectMapper objectMapper = new ObjectMapper();
  89. response.setContentType("application/json");
  90. objectMapper.writeValue(response.getOutputStream(), bean);
  91. }
  92. private ScopedConsumerBean authorise(HttpServletRequest request) {
  93. final ScopedConsumerBean scopedConsumerBean = authorizationServer.getConsumer(request);
  94. if (scopedConsumerBean != null)
  95. MDCKeys.addKeysConsumer(scopedConsumerBean);
  96. final TokenScope.Scope tokenScope = TokenScope.fromString(scopedConsumerBean.getScope());
  97. final String requestURI = request.getRequestURI();
  98. final String queryString = request.getQueryString();
  99. final String requestInfo = requestURI + ", " + queryString;
  100. if (tokenScope == getScope() && validReferrer(request, scopedConsumerBean)) {
  101. if (logger.isDebugEnabled())
  102. logger.debug("Scope and referrer valid for request: " + requestInfo);
  103. return scopedConsumerBean;
  104. } else {
  105. logger.warn("Incorrect scope of invalid referrer in request for: " + requestInfo);
  106. throw new APIException(APIException.METHOD_NOT_AUTHORIZED); // create additional error code?
  107. }
  108. }
  109. private boolean validReferrer(HttpServletRequest request, ScopedConsumerBean scopedConsumerBean) {
  110. final String referrer = request.getHeader("Referer"); // sic.; incorrect spelling due to spec
  111. String referringDomain = referrerDomain(referrer);
  112. if (logger.isDebugEnabled())
  113. logger.debug("Checking " + referringDomain + " (from " + referrer + ") against authorised domains.");
  114. final String commaSeparatedUrls = scopedConsumerBean.getUrl();
  115. if (commaSeparatedUrls == null) {
  116. if (logger.isDebugEnabled())
  117. logger.debug("No URLs specified for consumer: " + scopedConsumerBean.getShort_name() + "; valid referrer.");
  118. return true;
  119. }
  120. final String[] validDomains = StringUtils.split(commaSeparatedUrls, ",");
  121. for (String validDomain : validDomains) {
  122. if (logger.isDebugEnabled())
  123. logger.debug("-> Comparing against domain: " + validDomain);
  124. if (referringDomain.equals(validDomain)) {
  125. return true;
  126. }
  127. }
  128. logger.warn(referringDomain + " is invalid. Matched against "+commaSeparatedUrls);
  129. return false;
  130. }
  131. private String referrerDomain(String referrer) {
  132. Pattern topLevelDomain = Pattern.compile(".*?([^.]+\\.[^.]+)");
  133. Pattern ipAddress = Pattern.compile("^\\d+\\.\\d+\\.\\d+\\.\\d+");
  134. if ( StringUtils.isBlank(referrer) ) {
  135. return "";
  136. }
  137. try {
  138. final URI referrerUri = new URI(referrer);
  139. final String host = referrerUri.getHost();
  140. if (StringUtils.isBlank(host)){
  141. return "";
  142. }
  143. final Matcher ipMatcher = ipAddress.matcher(host);
  144. if (ipMatcher.matches()) {
  145. return host;
  146. }
  147. final Matcher tldMatcher = topLevelDomain.matcher(host);
  148. if (tldMatcher.matches()) {
  149. return tldMatcher.group(1);
  150. }
  151. return host;
  152. } catch (URISyntaxException e) {
  153. return referrer;
  154. }
  155. }
  156. }