All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.commons.jexl3.internal.introspection.PermissionsParser Maven / Gradle / Ivy

Go to download

The Apache Commons JEXL library is an implementation of the JSTL Expression Language with extensions.

The newest version!
/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF 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
 *
 *      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 org.apache.commons.jexl3.internal.introspection;

import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

/**
 * A crude parser to configure permissions akin to NoJexl annotations.
 * The syntax recognizes 2 types of permissions:
 * 
    *
  • restricting access to packages, classes (and inner classes), methods and fields
  • *
  • allowing access to a wildcard restricted set of packages
  • *
*

* Example: *

*
 *  my.allowed.packages.*
 *  another.allowed.package.*
 *  # nojexl like restrictions
 *  my.package {
 *   class0 {...
 *     class1 {...}
 *     class2 {
 *        ...
 *         class3 {}
 *     }
 *     # and eol comment
 *     class0(); # constructors
 *     method(); # method is not allowed
 *     field; # field
 *   } # end class0
 *   +class1 {
 *     method(); // only allowed method of class1
 *   }
 * } # end package my.package
 * 
*/ public class PermissionsParser { /** The source. */ private String src; /** The source size. */ private int size; /** The @NoJexl execution-time map. */ private Map packages; /** The set of wildcard imports. */ private Set wildcards; /** * Basic ctor. */ public PermissionsParser() { // nothing besides default member initialization } /** * Clears this parser internals. */ private void clear() { src = null; size = 0; packages = null; wildcards = null; } /** * Parses permissions from a source. * @param wildcards the set of allowed packages * @param packages the map of restricted elements * @param srcs the sources * @return the permissions map */ synchronized Permissions parse(final Set wildcards, final Map packages, final String... srcs) { try { if (srcs == null || srcs.length == 0) { return Permissions.UNRESTRICTED; } this.packages = packages; this.wildcards = wildcards; for (final String source : srcs) { this.src = source; this.size = source.length(); readPackages(); } return new Permissions(wildcards, packages); } finally { clear(); } } /** * Parses permissions from a source. * @param srcs the sources * @return the permissions map */ public Permissions parse(final String... srcs) { return parse(new LinkedHashSet<>(), new ConcurrentHashMap<>(), srcs); } /** * Reads a class permission. * @param njpackage the owning package * @param nojexl whether the restriction is explicitly denying (true) or allowing (false) members * @param outer the outer class (if any) * @param inner the inner class name (if any) * @param offset the initial parsing position in the source * @return the new parsing position */ private int readClass(final Permissions.NoJexlPackage njpackage, final boolean nojexl, final String outer, final String inner, final int offset) { final StringBuilder temp = new StringBuilder(); Permissions.NoJexlClass njclass = null; String njname = null; String identifier = inner; boolean deny = nojexl; int i = offset; int j = -1; boolean isMethod = false; while(i < size) { final char c = src.charAt(i); // if no parsing progress can be made, we are in error if (j >= i) { throw new IllegalStateException(unexpected(c, i)); } j = i; // get rid of space if (Character.isWhitespace(c)) { i = readSpaces(i + 1); continue; } // eol comment if (c == '#') { i = readEol(i + 1); continue; } // end of class ? if (njclass != null && c == '}') { i += 1; break; } // read an identifier, the class name if (identifier == null) { // negative or positive set ? if (c == '-') { i += 1; } else if (c == '+') { deny = false; i += 1; } final int next = readIdentifier(temp, i); if (i != next) { identifier = temp.toString(); temp.setLength(0); i = next; continue; } } // parse a class: if (njclass == null) { // we must have read the class ('identifier {'...) if (identifier == null || c != '{') { throw new IllegalStateException(unexpected(c, i)); } // if we have a class, it has a name njclass = deny ? new Permissions.NoJexlClass() : new Permissions.JexlClass(); njname = outer != null ? outer + "$" + identifier : identifier; njpackage.addNoJexl(njname, njclass); identifier = null; } else if (identifier != null) { // class member mode if (c == '{') { // inner class i = readClass(njpackage, deny, njname, identifier, i - 1); identifier = null; continue; } if (c == ';') { // field or method? if (isMethod) { njclass.methodNames.add(identifier); isMethod = false; } else { njclass.fieldNames.add(identifier); } identifier = null; } else if (c == '(' && !isMethod) { // method; only one opening parenthesis allowed isMethod = true; } else if (c != ')' || src.charAt(i - 1) != '(') { // closing parenthesis following opening one was expected throw new IllegalStateException(unexpected(c, i)); } } i += 1; } // empty class means allow or deny all if (njname != null && njclass.isEmpty()) { njpackage.addNoJexl(njname, njclass instanceof Permissions.JexlClass ? Permissions.JEXL_CLASS : Permissions.NOJEXL_CLASS); } return i; } /** * Reads a comment till end-of-line. * @param offset initial position * @return position after comment */ private int readEol(final int offset) { int i = offset; while (i < size) { final char c = src.charAt(i); if (c == '\n') { break; } i += 1; } return i; } /** * Reads an identifier (optionally dot-separated). * @param id the builder to fill the identifier character with * @param offset the initial reading position * @return the position after the identifier */ private int readIdentifier(final StringBuilder id, final int offset) { return readIdentifier(id, offset, false, false); } /** * Reads an identifier (optionally dot-separated). * @param id the builder to fill the identifier character with * @param offset the initial reading position * @param dot whether dots (.) are allowed * @param star whether stars (*) are allowed * @return the position after the identifier */ private int readIdentifier(final StringBuilder id, final int offset, final boolean dot, final boolean star) { int begin = -1; boolean starf = star; int i = offset; char c = 0; while (i < size) { c = src.charAt(i); // accumulate identifier characters if (Character.isJavaIdentifierStart(c) && begin < 0) { begin = i; id.append(c); } else if (Character.isJavaIdentifierPart(c) && begin >= 0) { id.append(c); } else if (dot && c == '.') { if (src.charAt(i - 1) == '.') { throw new IllegalStateException(unexpected(c, i)); } id.append('.'); begin = -1; } else if (starf && c == '*') { id.append('*'); starf = false; // only one star } else { break; } i += 1; } // cant end with a dot if (dot && c == '.') { throw new IllegalStateException(unexpected(c, i)); } return i; } /** * Reads a package permission. */ private void readPackages() { final StringBuilder temp = new StringBuilder(); Permissions.NoJexlPackage njpackage = null; int i = 0; int j = -1; String pname = null; while (i < size) { final char c = src.charAt(i); // if no parsing progress can be made, we are in error if (j >= i) { throw new IllegalStateException(unexpected(c, i)); } j = i; // get rid of space if (Character.isWhitespace(c)) { i = readSpaces(i + 1); continue; } // eol comment if (c == '#') { i = readEol(i + 1); continue; } // read the package qualified name if (pname == null) { final int next = readIdentifier(temp, i, true, true); if (i != next) { pname = temp.toString(); temp.setLength(0); i = next; // consume it if it is a wildcard declaration if (pname.endsWith(".*")) { wildcards.add(pname); pname = null; } continue; } } // package mode if (njpackage == null) { if (c == '{') { njpackage = packages.compute(pname, (n, p) -> new Permissions.NoJexlPackage(p == null? null : p.nojexl) ); i += 1; } } else if (c == '}') { // empty means whole package if (njpackage.isEmpty()) { packages.put(pname, Permissions.NOJEXL_PACKAGE); } njpackage = null; // can restart anew pname = null; i += 1; } else { i = readClass(njpackage, true,null, null, i); } } } /** * Reads spaces. * @param offset initial position * @return position after spaces */ private int readSpaces(final int offset) { int i = offset; while (i < size) { final char c = src.charAt(i); if (!Character.isWhitespace(c)) { break; } i += 1; } return offset; } /** * Compose a parsing error message. * @param c the offending character * @param i the offset position * @return the error message */ private String unexpected(final char c, final int i) { return "unexpected '" + c + "'" + "@" + i; } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy