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}