/** * 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); } } }