/**
* Licensed to Apereo under one or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information regarding copyright ownership. Apereo
* 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 the
* following location:
*
* <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.apereo.portal.utils.jsp;
import com.fasterxml.jackson.core.JsonGenerationException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Collection;
import java.util.Map;
import org.apereo.portal.spring.beans.factory.ObjectMapperFactoryBean;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.util.UriUtils;
/**
* JSP Static utility functions
*
*/
public class Util {
static final Logger logger = LoggerFactory.getLogger(Util.class);
private static final ObjectMapper OBJECT_MAPPER;
static {
ObjectMapper mapper;
try {
final ObjectMapperFactoryBean omfb = new ObjectMapperFactoryBean();
omfb.afterPropertiesSet();
mapper = omfb.getObject();
} catch (Exception e) {
mapper = new ObjectMapper();
mapper.findAndRegisterModules();
}
OBJECT_MAPPER = mapper;
}
public static boolean contains(Collection<?> coll, Object o) {
return coll != null && coll.contains(o);
}
public static boolean containsKey(Map<?, ?> map, Object o) {
return map != null && map.containsKey(o);
}
public static boolean containsValue(Map<?, ?> map, Object o) {
return map != null && map.containsValue(o);
}
public static boolean instanceOf(Object obj, String className) throws ClassNotFoundException {
final ClassLoader cl = obj.getClass().getClassLoader();
final Class<?> clazz = Class.forName(className, true, cl);
final boolean isInstanceOf = obj.getClass().isAssignableFrom(clazz);
return isInstanceOf;
}
public static String json(Object obj)
throws JsonGenerationException, JsonMappingException, IOException {
return OBJECT_MAPPER.writeValueAsString(obj);
}
/**
* Tests if a string is a valid URL Will open a connection and make sure that it can connect to
* the URL it looks for an HTTP status code of 200 on a GET. This is a valid URL.
*
* @param url - A string representing a url
* @return True if URL is valid according to included definition
*/
public static boolean isValidUrl(String url) {
HttpURLConnection huc = null;
boolean isValid = false;
try {
URL u = new URL(url);
huc = (HttpURLConnection) u.openConnection();
huc.setRequestMethod("GET");
huc.connect();
int response = huc.getResponseCode();
if (response != HttpURLConnection.HTTP_OK) {
throw new IOException(
String.format(
"URL %s did not return a valid response code while attempting to connect. Expected: %d. Received: %d",
url, HttpURLConnection.HTTP_OK, response));
}
isValid = true;
} catch (IOException e) {
logger.warn(
"A problem happened while trying to verify url: {} Error Message: {}",
url,
e.getMessage());
} finally {
if (huc != null) {
huc.disconnect();
}
;
}
return isValid;
}
/**
* URL encode a path segment. This is just a thin wrapper around UriUtils.encodePathSegment. It
* is intended for the case where you are building URLs in JS. c:url + escapeBody doesn't
* correctly escape the contents (especially "</script>"), and fn:escapeXml incorrectly encodes
* the URL (it escapes chars like '<' as < instead of %3C). It should help avoid XSS attacks
* when building RESTful URLS in js. Example:
*
* <p>Given: ${userId} -> "<script>alert('test')</script>
*
* <p>... <script> $.ajax({ url: <c:url value='/users/${up:encodePathSegment(userId)}'/> });
* </script>
*
* <p>Will encode the URL as:
*
* <p>/users/%3Cscript%3Ealert('test%')%3C%2Fscript%3E
*
* <p>IMPORTANT: Note that this encodes the '/' in </script> to %2F. Unfortunately, tomcat still
* does not interpret %2F correctly unless you relax some security settings (@see
* tomcat.apache.org/tomcat-7.0-doc/config/systemprops.html#Security, the ALLOW_ENCODED_SLASH
* property). So, while this method does a better job at avoiding XSS issues than c:url, it's
* still not ideal. Unless the input is whitelisted to avoid invalid input chars, it's still
* possible to end up with REST URLs that won't work correctly (like the one above), but at
* least this will protect you from XSS attacks on the front end.
*
* @param val the path segment to encode
* @return the encoded path segment
*/
public static String encodePathSegment(String val) {
try {
return UriUtils.encodePathSegment(val, "UTF-8");
} catch (UnsupportedEncodingException e) {
// should be unreachable...
throw new RuntimeException(e);
}
}
}