// --- BEGIN COPYRIGHT BLOCK --- // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; version 2 of the License. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with this program; if not, write to the Free Software Foundation, Inc., // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // (C) 2007 Red Hat, Inc. // All rights reserved. // --- END COPYRIGHT BLOCK --- package com.netscape.cmscore.policy; import java.util.*; import java.io.*; import com.netscape.certsrv.base.*; import com.netscape.certsrv.policy.*; import com.netscape.cmscore.util.*; import com.netscape.certsrv.apps.CMS; /** * Default implementation of predicate parser. * * Limitations: * * 1. Currently parentheses are not suported. * 2. Only ==, != <, >, <= and >= operators are supported. * 3. The only boolean operators supported are AND and OR. AND takes precedence * over OR. Example: a AND b OR e OR c AND d * is treated as (a AND b) OR e OR (c AND d) * 4. If this is n't adequate, roll your own. * * @author kanda * @version $Revision$, $Date$ */ public class PolicyPredicateParser { public static final int OP_AND = 1; public static final int OP_OR = 2; public static final int EXPRESSION = 0; public static final String AND = "AND"; public static final String OR = "OR"; private static final char COMMA = ','; /** * Parse the predicate expression and return a vector of expressions. * * @param predicateExp The predicate expression as read from the config file. * @return expVector The vector of expressions. */ public static IExpression parse(String predicateExpression) throws EPolicyException { if (predicateExpression == null || predicateExpression.length() == 0) return null; PredicateTokenizer pt = new PredicateTokenizer(predicateExpression); if (pt == null || !pt.hasMoreTokens()) return null; // The first token cannot be an operator. We are not dealing with // reverse-polish notation. String token = pt.nextToken(); boolean opANDSeen; boolean opORSeen; if (getOP(token) != EXPRESSION) { if (Debug.ON) Debug.trace("Malformed expression: " + predicateExpression); throw new EPolicyException(CMS.getUserMessage("CMS_POLICY_BAD_POLICY_EXPRESSION", predicateExpression)); } IExpression current = parseExpression(token); boolean malformed = false; Vector expSet = new Vector(); int prevType = EXPRESSION; while (pt.hasMoreTokens()) { token = pt.nextToken(); int curType = getOP(token); if ((prevType != EXPRESSION && curType != EXPRESSION) || (prevType == EXPRESSION && curType == EXPRESSION)) { malformed = true; break; } // If an operator seen skip to the next token if (curType != EXPRESSION) { prevType = curType; continue; } // If the previous type was an OR token, add the current expression to // the expression set; if (prevType == OP_OR) { expSet.addElement(current); current = parseExpression(token); prevType = curType; continue; } // If the previous type was an AND token, make an AND expression if (prevType == OP_AND) { current = new AndExpression(current, parseExpression(token)); prevType = curType; } } if (malformed) { if (Debug.ON) Debug.trace("Malformed expression: " + predicateExpression); throw new EPolicyException( CMS.getUserMessage("CMS_POLICY_BAD_POLICY_EXPRESSION", predicateExpression)); } // Form an ORExpression if (current != null) expSet.addElement(current); int size = expSet.size(); if (size == 0) return null; OrExpression orExp = new OrExpression((IExpression) expSet.elementAt(0), null); for (int i = 1; i < size; i++) orExp = new OrExpression(orExp, (IExpression) expSet.elementAt(i)); return orExp; } private static int getOP(String token) { if (token.equalsIgnoreCase(AND)) return OP_AND; else if (token.equalsIgnoreCase(OR)) return OP_OR; else return EXPRESSION; } private static IExpression parseExpression(String input) throws EPolicyException { // If the expression has multiple parts separated by commas // we need to construct an AND expression. Else we will return a // simple expression. int commaIndex = input.indexOf(COMMA); if (commaIndex < 0) return SimpleExpression.parse(input); int currentIndex = 0; Vector expVector = new Vector(); while (commaIndex > 0) { SimpleExpression exp = (SimpleExpression) SimpleExpression.parse(input.substring(currentIndex, commaIndex)); expVector.addElement(exp); currentIndex = commaIndex + 1; commaIndex = input.indexOf(COMMA, currentIndex); } if (currentIndex < (input.length() - 1)) { SimpleExpression exp = (SimpleExpression) SimpleExpression.parse(input.substring(currentIndex)); expVector.addElement(exp); } int size = expVector.size(); SimpleExpression exp1 = (SimpleExpression) expVector.elementAt(0); SimpleExpression exp2 = (SimpleExpression) expVector.elementAt(1); AndExpression andExp = new AndExpression(exp1, exp2); for (int i = 2; i < size; i++) { andExp = new AndExpression(andExp, (SimpleExpression) expVector.elementAt(i)); } return andExp; } public static void main(String[] args) { /********* IRequest req = new IRequest(); try { req.set("ou", "people"); req.set("cn", "John Doe"); req.set("uid", "jdoes"); req.set("o", "airius.com"); req.set("certtype", "client"); req.set("request", "issuance"); req.set("id", new Integer(10)); req.set("dualcerts", new Boolean(true)); Vector v = new Vector(); v.addElement("one"); v.addElement("two"); v.addElement("three"); req.set("count", v); } catch (Exception e){e.printStackTrace();} String[] array = { "ou == people AND certtype == client", "ou == servergroup AND certtype == server", "uid == jdoes, ou==people, o==airius.com OR ou == people AND certType == client OR certType == server AND cn == needles.mcom.com", }; for (int i = 0; i < array.length; i++) { System.out.println(); System.out.println("String: " + array[i]); IExpression exp = null; try { exp = parse(array[i]); if (exp != null) { System.out.println("Parsed Expression: " + exp); boolean result = exp.evaluate(req); System.out.println("Result: " + result); } } catch (Exception e) {e.printStackTrace(); } } try { BufferedReader rdr = new BufferedReader( new FileReader(args[0])); String line; while((line=rdr.readLine()) != null) { System.out.println(); System.out.println("Line Read: " + line); IExpression exp = null; try { exp = parse(line); if (exp != null) { System.out.println(exp); boolean result = exp.evaluate(req); System.out.println("Result: " + result); } }catch (Exception e){e.printStackTrace();} } } catch (Exception e){e.printStackTrace(); } *******/ } } class PredicateTokenizer { String input; int currentIndex; int endOfString; String nextToken; boolean first; public PredicateTokenizer(String predString) { input = predString; currentIndex = 0; nextToken = null; } public boolean hasMoreTokens() { return (currentIndex != -1); } public String nextToken() { if (nextToken != null) { String toReturn = nextToken; nextToken = null; return toReturn; } int andIndex = input.indexOf(" AND", currentIndex); if (andIndex < 0) andIndex = input.indexOf(" and", currentIndex); int orIndex = input.indexOf(" OR", currentIndex); if (orIndex < 0) orIndex = input.indexOf(" or", currentIndex); String toReturn = null; if (andIndex == -1 && orIndex == -1) { if (currentIndex == 0) { currentIndex = -1; toReturn = input; } else { int temp = currentIndex; currentIndex = -1; toReturn = input.substring(temp); } } else if (andIndex >= 0 && (andIndex < orIndex || orIndex == -1)) { if (currentIndex != andIndex) { toReturn = input.substring(currentIndex, andIndex); nextToken = input.substring(andIndex + 1, andIndex + 4); currentIndex = andIndex + 4; } else { toReturn = "AND"; currentIndex += 4; } } else if (orIndex >= 0 && (orIndex < andIndex || andIndex == -1)) { if (currentIndex != orIndex) { toReturn = input.substring(currentIndex, orIndex); nextToken = input.substring(orIndex + 1, orIndex + 3); currentIndex = orIndex + 3; } else { toReturn = "OR"; currentIndex += 3; } } else { // Cannot happen; Assert here. toReturn = null; System.out.println("We shouldn't be here!"); } if (toReturn == null) return null; else { String trimmed = toReturn.trim(); if (trimmed == null || trimmed.length() == 0) return nextToken(); else return trimmed; } } } class AttributeSet implements IAttrSet { Hashtable ht = new Hashtable(); public AttributeSet() { } public void delete(String name) throws EBaseException { Object ob = ht.get(name); ht.remove(ob); } public Object get(String name) throws EBaseException { return ht.get(name); } public void set(String name, Object ob) throws EBaseException { ht.put(name, ob); } public Enumeration getElements() { return ht.elements(); } }