/* * Copyright 2011 Google Inc. * * Licensed 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 * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 com.google.gwt.user.server; import java.lang.annotation.Annotation; import java.lang.reflect.Method; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; /** * A bunch of useful methods. */ public class Util { /** * Find an instance of the specified annotation, walking up the inheritance * tree if necessary. Copied from {@link * com.google.gwt.i18n.rebind.AnnotationUtil}. * * <p>The super chain is walked first, so if an ancestor superclass has the * requested annotation, it will be preferred over a directly implemented * interface. * * @param <T> Annotation type to search for * @param clazz root class to search, may be null * @param annotationClass class object of Annotation subclass to search for * @return the requested annotation or null if none */ public static <T extends Annotation> T getClassAnnotation(Class<?> clazz, Class<T> annotationClass) { if (clazz == null) { return null; } T annot = clazz.getAnnotation(annotationClass); if (annot == null) { annot = getClassAnnotation(clazz.getSuperclass(), annotationClass); if (annot != null) { return annot; } for (Class<?> intf : clazz.getInterfaces()) { annot = getClassAnnotation(intf, annotationClass); if (annot != null) { return annot; } } } return annot; } /** * Retrieves named cookie from supplied request. If {@code allowDuplicates} is * set to {@code true}, method will throw {@link IllegalStateException} if * duplicate cookies are found, which can be a sign of a cookie overwrite * attack. * * @param request HTTP request to retrieve cookie from. * @param cookieName Cookie name. * @param allowDuplicates if {@code true} duplicate cookies are allowed, * otherwise {@link IllegalStateException} is thrown if duplicate * cookies are detected. * @return {@link Cookie} if specified cookie is present, {@code null} * otherwise. * @throws IllegalArgumentException if duplicate cookies are detected. */ public static Cookie getCookie(HttpServletRequest request, String cookieName, boolean allowDuplicates) { Cookie cookieToReturn = null; Cookie[] cookies = request.getCookies(); if (cookies != null) { for (Cookie cookie : cookies) { if (cookieName.equals(cookie.getName())) { // ensure that it's the only one cookie, since duplicate cookies // can be a sign of a cookie overriding attempt. if (cookieToReturn == null) { if (allowDuplicates) { // do not attempt to detect duplicate cookies return cookie; } else { cookieToReturn = cookie; } } else { throw new IllegalArgumentException("Duplicate cookie! " + "Cookie override attack?"); } } } } return cookieToReturn; } /** * Checks if specified method is XSRF protected based on the following logic: * * <ul> * <li>Method level annotations override class level annotations. * <li>If method is annotated with {@code xsrfAnnotation} this * method returns {@code true} * <li>If method is annotated with {@code noXsrfAnnotation}, this method * returns {@code false}. * <li>If class is annotated with {@code xsrfAnnotation} and method is not * annotated, this method returns {@code true}. * <li>If class is annotated with {@code noXsrfAnnotation} and method is not * annotated, this method returns {@code false}. * <li>If no annotations are present and class has a method with return value * assignable from {@code xsrfTokenInterface}, this method returns * {@code true}. * <li>If no annotations are present this method returns {@code false}. * </ul> * * @see com.google.gwt.user.server.rpc.AbstractXsrfProtectedServiceServlet */ public static boolean isMethodXsrfProtected(Method method, Class<? extends Annotation> xsrfAnnotation, Class<? extends Annotation> noXsrfAnnotation, Class<?> xsrfTokenInterface) { Class<?> declaringClass = method.getDeclaringClass(); if (method.getAnnotation(noXsrfAnnotation) != null || (Util.getClassAnnotation( declaringClass, noXsrfAnnotation) != null && method.getAnnotation(xsrfAnnotation) == null)) { // XSRF protection is disabled return false; } if (Util.getClassAnnotation(declaringClass, xsrfAnnotation) != null || method.getAnnotation(xsrfAnnotation) != null) { return true; } // if no explicit annotation is given no XSRF token verification is done, // unless there's a method returning RpcToken in which case XSRF token // verification is performed for all methods Method[] classMethods = declaringClass.getMethods(); for (Method classMethod : classMethods) { if (xsrfTokenInterface.isAssignableFrom(classMethod.getReturnType()) && !method.equals(classMethod)) { return true; } } return false; } private Util() { } }