content: /**
   * Licensed to the Apache Software Foundation (ASF) under one
   * or more contributor license agreements.  See the NOTICE file
   * distributed with this work for additional information
   * regarding copyright ownership.  The ASF licenses this file
   * to you under the Apache License, Version 2.0 (the
   * "License"); you may not use this file except in compliance
   * with the License.  You may obtain a copy of the License at
   * <p>
   * http://www.apache.org/licenses/LICENSE-2.0
   * <p>
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  
  package org.apache.atlas.web.util;
  
  import com.fasterxml.jackson.databind.node.ObjectNode;
  import org.apache.atlas.AtlasClient;
  import org.apache.atlas.AtlasConfiguration;
  import org.apache.atlas.AtlasErrorCode;
  import org.apache.atlas.LocalServletRequest;
  import org.apache.atlas.exception.AtlasBaseException;
  import org.apache.atlas.utils.AtlasJson;
  import org.apache.atlas.utils.ParamChecker;
  import org.apache.commons.collections.MapUtils;
  import org.apache.commons.io.IOUtils;
  import org.apache.commons.lang3.StringEscapeUtils;
  import org.apache.commons.lang3.StringUtils;
  import org.apache.http.NameValuePair;
  import org.apache.http.client.utils.URLEncodedUtils;
  import org.slf4j.Logger;
  import org.slf4j.LoggerFactory;
  import org.springframework.web.util.UriUtils;
  
  import javax.servlet.http.HttpServletRequest;
  import javax.ws.rs.core.MediaType;
  import javax.ws.rs.core.Response;
  
  import java.io.IOException;
  import java.io.StringWriter;
  import java.nio.charset.Charset;
  import java.nio.charset.StandardCharsets;
  import java.util.HashMap;
  import java.util.List;
  import java.util.Map;
  
  /**
   * Utility functions for dealing with servlets.
   */
  public final class Servlets {
      private static final Logger LOG = LoggerFactory.getLogger(Servlets.class);
  
      public static final String JSON_MEDIA_TYPE = MediaType.APPLICATION_JSON + "; charset=UTF-8";
      public static final String BINARY          = MediaType.APPLICATION_OCTET_STREAM;
  
      private static final int     QUERY_PARAM_MAX_LENGTH = AtlasConfiguration.QUERY_PARAM_MAX_LENGTH.getInt();
      private static final Charset UTF8_CHARSET           = StandardCharsets.UTF_8;
      private static final String  DO_AS                  = "doAs";
  
      private Servlets() {
          /* singleton */
      }
  
      /**
       * Returns the user of the given request.
       *
       * @param httpRequest    an HTTP servlet request
       * @return the user
       */
      public static String getUserFromRequest(HttpServletRequest httpRequest) {
          String user = httpRequest.getRemoteUser();
  
          if (!StringUtils.isEmpty(user)) {
              return user;
          }
  
          user = httpRequest.getParameter("user.name"); // available in query-param
  
          if (!StringUtils.isEmpty(user)) {
              return user;
          }
  
          user = httpRequest.getHeader("Remote-User"); // backwards-compatibility
  
          if (!StringUtils.isEmpty(user)) {
              return user;
          }
  
          user = getDoAsUser(httpRequest);
  
          if (!StringUtils.isEmpty(user)) {
              return user;
          }
  
          return null;
      }
  
      public static String getDoAsUser(HttpServletRequest request) {
          if (StringUtils.isNoneEmpty(request.getQueryString())) {
              List<NameValuePair> list = URLEncodedUtils.parse(request.getQueryString(), UTF8_CHARSET);
  
              if (list != null) {
                  for (NameValuePair nv : list) {
                      if (DO_AS.equals(nv.getName())) {
                          return nv.getValue();
                      }
                  }
              }
          }
  
          return null;
      }
  
      /**
       * Returns the URI of the given request.
       *
       * @param httpRequest    an HTTP servlet request
       * @return the URI, including the query string
       */
      public static String getRequestURI(HttpServletRequest httpRequest) {
          final StringBuilder url = new StringBuilder(100).append(httpRequest.getRequestURI());
  
          if (httpRequest.getQueryString() != null) {
              url.append('?').append(httpRequest.getQueryString());
          }
  
          return url.toString();
      }
  
      /**
       * Returns the full URL of the given request.
       *
       * @param httpRequest    an HTTP servlet request
       * @return the full URL, including the query string
       */
      public static String getRequestURL(HttpServletRequest httpRequest) {
          final StringBuilder url = new StringBuilder(100).append(httpRequest.getRequestURL());
  
          if (httpRequest.getQueryString() != null) {
              url.append('?').append(httpRequest.getQueryString());
          }
  
          return url.toString();
      }
  
      public static Response getErrorResponse(AtlasBaseException e) {
          String   message  = e.getMessage() == null ? "Failed with " + e.getClass().getName() : e.getMessage();
  
          return getErrorResponse(message, e.getAtlasErrorCode().getHttpCode());
      }
  
      public static Response getErrorResponse(Throwable e, Response.Status status) {
          String   message  = e.getMessage() == null ? "Failed with " + e.getClass().getName() : e.getMessage();
  
          return getErrorResponse(message, status);
      }
  
      public static Response getErrorResponse(String message, Response.Status status) {
          Object     errorEntity = escapeJsonString(message);
          ObjectNode errorJson   = AtlasJson.createV1ObjectNode(AtlasClient.ERROR, errorEntity);
  
          return Response.status(status).entity(errorJson).type(JSON_MEDIA_TYPE).build();
      }
  
      public static String getRequestPayload(HttpServletRequest request) throws IOException {
          //request is an instance of LocalServletRequest for calls from LocalAtlasClient
          if (request instanceof LocalServletRequest) {
              return ((LocalServletRequest) request).getPayload();
          }
  
          StringWriter writer = new StringWriter();
  
          IOUtils.copy(request.getInputStream(), writer);
  
          return writer.toString();
      }
  
      public static String getRequestId() {
          return Thread.currentThread().getName();
      }
  
      public static String escapeJsonString(String inputStr) {
          ParamChecker.notNull(inputStr, "Input String cannot be null");
  
          return StringEscapeUtils.escapeJson(inputStr);
      }
  
      public static String getHostName(HttpServletRequest httpServletRequest) {
          return httpServletRequest.getLocalName();
      }
  
      public static String getUserName(HttpServletRequest httpServletRequest) {
          return httpServletRequest.getRemoteUser();
      }
  
      public static Map<String, Object> getParameterMap(HttpServletRequest request) {
          Map<String, Object> attributes = new HashMap<>();
  
          if (MapUtils.isNotEmpty(request.getParameterMap())) {
              for (Map.Entry<String, String[]> e : request.getParameterMap().entrySet()) {
                  String key = e.getKey();
  
                  if (key != null) {
                      String[] values = e.getValue();
                      String   value  = values != null && values.length > 0 ? values[0] : null;
  
                      attributes.put(key, value);
                  }
              }
          }
  
          return attributes;
      }
  
      public static void validateQueryParamLength(String paramName, String paramValue) throws AtlasBaseException {
          if (StringUtils.isNotEmpty(paramValue) && paramValue.length() > QUERY_PARAM_MAX_LENGTH) {
              throw new AtlasBaseException(AtlasErrorCode.INVALID_QUERY_PARAM_LENGTH, paramName);
          }
      }
  
      public static String decodeQueryString(String query) throws AtlasBaseException {
          try {
              return UriUtils.decode(query, "UTF-8");
          } catch (Exception e) {
              LOG.error("Error occurred while decoding query: {}{}", e.getMessage(), query);
  
              throw new AtlasBaseException(e.getMessage());
          }
      }
  }
  