    001/*
    002 * Licensed to the Apache Software Foundation (ASF) under one or more
    003 * contributor license agreements.  See the NOTICE file distributed with
    004 * this work for additional information regarding copyright ownership.
    005 * The ASF licenses this file to You under the Apache License, Version 2.0
    006 * (the "License"); you may not use this file except in compliance with
    007 * the License.  You may obtain a copy of the License at
    008 *
    009 *      http://www.apache.org/licenses/LICENSE-2.0
    010 *
    011 * Unless required by applicable law or agreed to in writing, software
    012 * distributed under the License is distributed on an "AS IS" BASIS,
    013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    014 * See the License for the specific language governing permissions and
    015 * limitations under the License.
    016 */
    017
    018package org.apache.commons.jexl3.internal.introspection;
    019
    020import java.lang.reflect.Constructor;
    021import java.lang.reflect.Field;
    022import java.lang.reflect.Method;
    023import java.lang.reflect.Modifier;
    024import org.apache.commons.jexl3.annotations.NoJexl;
    025
    026/**
    027 * Checks whether an element (ctor, field or method) is visible by JEXL introspection.
    028 * Default implementation does this by checking if element has been annotated with NoJexl.
    029 */
    030public class Permissions {
    031    /** Allow inheritance. */
    032    protected Permissions() {
    033    }
    034    /**
    035     * The default singleton.
    036     */
    037    public static final Permissions DEFAULT = new Permissions();
    038
    039    /**
    040     * Checks whether a package explicitly disallows JEXL introspection.
    041     * @param pack the package
    042     * @return true if JEXL is allowed to introspect, false otherwise
    043     */
    044    public boolean allow(final Package pack) {
    045        if (pack == null || pack.getAnnotation(NoJexl.class) != null) {
    046            return false;
    047        }
    048        return true;
    049    }
    050
    051    /**
    052     * Checks whether a class or one of its super-classes or implemented interfaces
    053     * explicitly disallows JEXL introspection.
    054     * @param clazz the class to check
    055     * @return true if JEXL is allowed to introspect, false otherwise
    056     */
    057    public boolean allow(final Class<?> clazz) {
    058        return clazz != null && allow(clazz.getPackage()) && allow(clazz, true);
    059    }
    060
    061    /**
    062     * Checks whether a constructor explicitly disallows JEXL introspection.
    063     * @param ctor the constructor to check
    064     * @return true if JEXL is allowed to introspect, false otherwise
    065     */
    066    public boolean allow(final Constructor<?> ctor) {
    067        if (ctor == null) {
    068            return false;
    069        }
    070        if (!Modifier.isPublic(ctor.getModifiers())) {
    071            return false;
    072        }
    073        final Class<?> clazz = ctor.getDeclaringClass();
    074        if (!allow(clazz, false)) {
    075            return false;
    076        }
    077        // is ctor annotated with nojexl ?
    078        final NoJexl nojexl = ctor.getAnnotation(NoJexl.class);
    079        if (nojexl != null) {
    080            return false;
    081        }
    082        return true;
    083    }
    084
    085    /**
    086     * Checks whether a field explicitly disallows JEXL introspection.
    087     * @param field the field to check
    088     * @return true if JEXL is allowed to introspect, false otherwise
    089     */
    090    public boolean allow(final Field field) {
    091        if (field == null) {
    092            return false;
    093        }
    094        if (!Modifier.isPublic(field.getModifiers())) {
    095            return false;
    096        }
    097        final Class<?> clazz = field.getDeclaringClass();
    098        if (!allow(clazz, false)) {
    099            return false;
    100        }
    101        // is field annotated with nojexl ?
    102        final NoJexl nojexl = field.getAnnotation(NoJexl.class);
    103        if (nojexl != null) {
    104            return false;
    105        }
    106        return true;
    107    }
    108
    109    /**
    110     * Checks whether a method explicitly disallows JEXL introspection.
    111     * <p>Since methods can be overridden, this also checks that no superclass or interface
    112     * explicitly disallows this methods.</p>
    113     * @param method the method to check
    114     * @return true if JEXL is allowed to introspect, false otherwise
    115     */
    116    public boolean allow(final Method method) {
    117        if (method == null) {
    118            return false;
    119        }
    120        if (!Modifier.isPublic(method.getModifiers())) {
    121            return false;
    122        }
    123        // is method annotated with nojexl ?
    124        NoJexl nojexl = method.getAnnotation(NoJexl.class);
    125        if (nojexl != null) {
    126            return false;
    127        }
    128        // is the class annotated with nojexl ?
    129        Class<?> clazz = method.getDeclaringClass();
    130        nojexl = clazz.getAnnotation(NoJexl.class);
    131        if (nojexl != null) {
    132            return false;
    133        }
    134        // lets walk all interfaces
    135        for (final Class<?> inter : clazz.getInterfaces()) {
    136            if (!allow(inter, method)) {
    137                return false;
    138            }
    139        }
    140        // lets walk all super classes
    141        clazz = clazz.getSuperclass();
    142        // walk all superclasses
    143        while (clazz != null) {
    144            if (!allow(clazz, method)) {
    145                return false;
    146            }
    147            clazz = clazz.getSuperclass();
    148        }
    149        return true;
    150    }
    151
    152    /**
    153     * Checks whether a class or one of its superclasses or implemented interfaces
    154     * explicitly disallows JEXL introspection.
    155     * @param clazz the class to check
    156     * @param interf whether interfaces should be checked as well
    157     * @return true if JEXL is allowed to introspect, false otherwise
    158     */
    159    protected boolean allow(Class<?> clazz, final boolean interf) {
    160        if (clazz == null) {
    161            return false;
    162        }
    163        if (!Modifier.isPublic(clazz.getModifiers())) {
    164            return false;
    165        }
    166        // lets walk all interfaces
    167        if (interf) {
    168            for (final Class<?> inter : clazz.getInterfaces()) {
    169                // is clazz annotated with nojexl ?
    170                final NoJexl nojexl = inter.getAnnotation(NoJexl.class);
    171                if (nojexl != null) {
    172                    return false;
    173                }
    174            }
    175        }
    176        // lets walk all super classes
    177        clazz = clazz.getSuperclass();
    178        // walk all superclasses
    179        while (clazz != null) {
    180            // is clazz annotated with nojexl ?
    181            final NoJexl nojexl = clazz.getAnnotation(NoJexl.class);
    182            if (nojexl != null) {
    183                return false;
    184            }
    185            clazz = clazz.getSuperclass();
    186        }
    187        return true;
    188    }
    189
    190    /**
    191     * Check whether a method is allowed to be JEXL introspected in all its
    192     * superclasses and interfaces.
    193     * @param clazz the class
    194     * @param method the method
    195     * @return true if JEXL is allowed to introspect, false otherwise
    196     */
    197    protected boolean allow(final Class<?> clazz, final Method method) {
    198        if (clazz != null) {
    199            try {
    200                // check if method in that class is different from the method argument
    201                final Method wmethod = clazz.getMethod(method.getName(), method.getParameterTypes());
    202                if (wmethod != null) {
    203                    NoJexl nojexl = clazz.getAnnotation(NoJexl.class);
    204                    if (nojexl != null) {
    205                        return false;
    206                    }
    207                    // check if parent declaring class said nojexl (transitivity)
    208                    nojexl = wmethod.getAnnotation(NoJexl.class);
    209                    if (nojexl != null) {
    210                        return false;
    211                    }
    212                }
    213            } catch (final NoSuchMethodException ex) {
    214                // unexpected, return no
    215                return true;
    216            } catch (final SecurityException ex) {
    217                // unexpected, can't do much
    218                return false;
    219            }
    220        }
    221        return true;
    222    }
    223}