LdapFilter.java

  1. /*
  2.  * GovWay - A customizable API Gateway
  3.  * https://govway.org
  4.  *
  5.  * Copyright (c) 2005-2025 Link.it srl (https://link.it).
  6.  *
  7.  * This program is free software: you can redistribute it and/or modify
  8.  * it under the terms of the GNU General Public License version 3, as published by
  9.  * the Free Software Foundation.
  10.  *
  11.  * This program is distributed in the hope that it will be useful,
  12.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14.  * GNU General Public License for more details.
  15.  *
  16.  * You should have received a copy of the GNU General Public License
  17.  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  18.  *
  19.  */

  20. package org.openspcoop2.utils.transport.ldap;

  21. import java.text.ParseException;
  22. import java.util.ArrayDeque;
  23. import java.util.ArrayList;
  24. import java.util.Deque;
  25. import java.util.List;
  26. import java.util.regex.Matcher;
  27. import java.util.regex.Pattern;

  28. import javax.naming.NamingException;
  29. import javax.naming.directory.Attributes;

  30. /**
  31.  * classe che implementa i filtri di ricerca
  32.  *
  33.  * @author Tommaso Burlon (tommaso.burlon@link.it)
  34.  * @author $Author$
  35.  * @version $Rev$, $Date$
  36.  *
  37.  */
  38. public class LdapFilter {

  39.     private String key = null;
  40.     private String value = null;
  41.     private List<LdapFilter> subFilters = null;
  42.     private FilterType type = null;
  43.    
  44.     private enum FilterType {
  45.         AND("&"), OR("|"), NOT("!"), EQUALS("="), PRESENCE("=*"), GTE(">="), LTE("<="), TRUE(null), FALSE(null);
  46.        
  47.         private String operator;
  48.         private FilterType(String op) {
  49.             this.operator = op;
  50.         }
  51.        
  52.         @Override
  53.         public String toString() {
  54.             return this.operator;
  55.         }
  56.        
  57.         public static FilterType fromString(String str) {
  58.             FilterType[] types = FilterType.values();
  59.             for (FilterType type : types)
  60.                 if (type.toString().equals(str))
  61.                     return type;
  62.             return null;
  63.         }
  64.     }

  65.     private LdapFilter() {}
  66.    
  67.     private LdapFilter(FilterType type) {
  68.         this.type = type;
  69.     }
  70.    
  71.     private LdapFilter(FilterType type, String key, String value) {
  72.         this.key = key;
  73.         this.value = value;
  74.         this.type = type;
  75.     }
  76.    
  77.     private LdapFilter(FilterType type, List<LdapFilter> subFilters) {
  78.         this.subFilters = subFilters;
  79.         this.type = type;
  80.     }
  81.    
  82.     private String getOperator() {
  83.         return this.type.toString();
  84.     }
  85.    
  86.     private StringBuilder buildString(StringBuilder ret) {
  87.         switch (this.type) {
  88.         case AND:
  89.         case OR:
  90.         case NOT:
  91.             ret.append("(").append(this.getOperator());
  92.             for (LdapFilter subFilter : this.subFilters) {
  93.                 subFilter.buildString(ret);
  94.             }
  95.             ret.append(")");
  96.             break;
  97.         case EQUALS:
  98.         case PRESENCE:
  99.         case GTE:
  100.         case LTE:
  101.             ret.append("(").append(this.key).append(this.getOperator()).append(this.value).append(")");
  102.             break;
  103.         case TRUE:
  104.             ret.append("(&)");
  105.             break;
  106.         case FALSE:
  107.             ret.append("(|)");
  108.             break;
  109.         }
  110.         return ret;
  111.     }
  112.    
  113.     @Override
  114.     public String toString() {
  115.         StringBuilder ret = new StringBuilder();
  116.         return this.buildString(ret).toString();
  117.     }
  118.    
  119.     // operatori
  120.     public static LdapFilter and(LdapFilter ...filters) {
  121.         return new LdapFilter(FilterType.AND, List.of(filters));
  122.     }
  123.    
  124.     public boolean isAndFilter() {
  125.         return this.type.equals(FilterType.AND);
  126.     }
  127.    
  128.     public static LdapFilter or(LdapFilter ...filters) {
  129.         return new LdapFilter(FilterType.OR, List.of(filters));
  130.     }
  131.    
  132.     public boolean isOrFilter() {
  133.         return this.type.equals(FilterType.OR);
  134.     }
  135.    
  136.     public static LdapFilter not(LdapFilter filter) {
  137.         return new LdapFilter(FilterType.NOT, List.of(filter));
  138.     }
  139.    
  140.     public boolean isNotFilter() {
  141.         return this.type.equals(FilterType.NOT);
  142.     }
  143.    
  144.     public static LdapFilter absoluteTrue() {
  145.         return new LdapFilter(FilterType.TRUE);
  146.     }
  147.    
  148.     public boolean isAbsoluteTrueFilter() {
  149.         return this.type.equals(FilterType.TRUE);
  150.     }
  151.    
  152.     public static LdapFilter absoluteFalse() {
  153.         return new LdapFilter(FilterType.FALSE);
  154.     }
  155.    
  156.     public boolean isAbsoluteFalseFilter() {
  157.         return this.type.equals(FilterType.FALSE);
  158.     }
  159.    
  160.     // condizioni di base
  161.     public static LdapFilter isPresent(String attribute) {
  162.         return new LdapFilter(FilterType.PRESENCE, attribute, "");
  163.     }

  164.     public static LdapFilter isEqual(String attribute, String value) {
  165.         return new LdapFilter(FilterType.EQUALS, attribute, value);
  166.     }
  167.    
  168.     public static LdapFilter isAbsent(String attribute) {
  169.         return LdapFilter.not(LdapFilter.isPresent(attribute));
  170.     }
  171.    
  172.     public static LdapFilter isGreaterEqual(String attribute, String value) {
  173.         return new LdapFilter(FilterType.GTE, attribute, value);
  174.     }
  175.    
  176.     public static LdapFilter isLessEqual(String attribute, String value) {
  177.         return new LdapFilter(FilterType.LTE, attribute, value);
  178.     }
  179.    
  180.     // parse del filtro da una stringa
  181.     private static final Pattern operatorsPatern = Pattern.compile("(=|<=|>=|\\&|\\|)");
  182.     private static LdapFilter parseCondition(String raw, int start, int end) throws ParseException {
  183.        
  184.         if (raw.charAt(start) != '(')
  185.             throw new ParseException("la condizione non inizia con una parentesi", start);
  186.         if (raw.charAt(end - 1) != ')')
  187.             throw new ParseException("la condizione non finisce con una parentesi", end - 1);
  188.        
  189.         Matcher matcher = operatorsPatern.matcher(raw).region(start, end);
  190.         if (!matcher.find())
  191.             throw new ParseException("operatore non presente o non riconusciuto operatori possibili: =,<=,>=,&,|", start + 1);
  192.         if (matcher.groupCount() != 1)
  193.             throw new ParseException("inseriti piu operatori in una signola condizione", matcher.end());
  194.        
  195.         String key = raw.substring(start + 1, matcher.start(0));
  196.         FilterType op = FilterType.fromString(raw.substring(matcher.start(0), matcher.end(0)));
  197.         String value = raw.substring(matcher.end(0), end - 1);
  198.                
  199.         if (op == null)
  200.             throw new ParseException("operatore non riconusciuto operatori possibili: =,<=,>=,&,|,=*", start + 1);
  201.        
  202.         if (op.equals(FilterType.OR) || op.equals(FilterType.AND)) {
  203.             if (key.isEmpty() && value.isEmpty())
  204.                 return op.equals(FilterType.OR) ? LdapFilter.absoluteFalse() : LdapFilter.absoluteTrue();
  205.             throw new ParseException("condizione non valida, atteso absolute true/false ma chiave valore presenti", start + 1);
  206.         }
  207.        
  208.         if (op.equals(FilterType.EQUALS) && value.equals("*"))
  209.             return LdapFilter.isPresent(key);
  210.         return new LdapFilter(op, key, value);
  211.     }
  212.    
  213.     private static LdapFilter parseOperator(String raw, int start, int end, List<LdapFilter> subFilters) throws ParseException {
  214.         if (raw.length() <= start + 1 || raw.length() < end)
  215.             throw new ParseException("lunghezza stringa operatore tropo breve", start + 1);
  216.        
  217.         char op = raw.charAt(start + 1);
  218.         FilterType type = null;
  219.         if (op == '|') type = FilterType.OR;
  220.         if (op == '&') type = FilterType.AND;
  221.         if (op == '!') type = FilterType.NOT;
  222.         if (type == null) throw new ParseException("tipo operatore di aggregazione ldap non riconosciuto: " + op, start + 1);
  223.        
  224.         return new LdapFilter(type, subFilters);
  225.     }
  226.        
  227.     public static LdapFilter parse(String raw) throws ParseException {
  228.         boolean isCondition = true;
  229.         String in = raw.replace(" ", "");
  230.        
  231.         Deque<List<LdapFilter>> filters = new ArrayDeque<>();
  232.         Deque<Integer> brackets = new ArrayDeque<>();
  233.        
  234.         filters.push(new ArrayList<>());
  235.         for (int i = 0; i < in.length(); i++) {
  236.             if(in.charAt(i) == '(') {
  237.                 brackets.push(i);
  238.                 filters.push(new ArrayList<>());
  239.                 isCondition = true;
  240.             }
  241.             if (in.charAt(i) == ')') {
  242.                 if (brackets.isEmpty())
  243.                     throw new ParseException("parentesi chiusa non accoppiata con una aperta", i);
  244.                 int prev = brackets.pop();
  245.                
  246.                 if (isCondition) {
  247.                     filters.pop();
  248.                     filters.peek().add(parseCondition(in, prev, i + 1));
  249.                     isCondition = false;
  250.                 } else {
  251.                     List<LdapFilter> subFilters = filters.pop();
  252.                     filters.peek().add(parseOperator(in, prev, i + 1, subFilters));
  253.                 }
  254.             }
  255.         }
  256.        
  257.         return filters.peek().get(0);
  258.     }
  259.    
  260.     // controlla se un attributo rispetta il filtro
  261.     public boolean check(Attributes attrs) {
  262.        
  263.         try {
  264.             switch (this.type) {
  265.             case TRUE: return false;
  266.             case FALSE: return true;
  267.             case AND:
  268.                 for (LdapFilter filter : this.subFilters)
  269.                     if (!filter.check(attrs))
  270.                         return false;
  271.                 return true;
  272.             case OR:
  273.                 for (LdapFilter filter : this.subFilters)
  274.                     if (filter.check(attrs))
  275.                         return true;
  276.                 return false;
  277.             case NOT:
  278.                 return !this.subFilters.get(0).check(attrs);
  279.             case EQUALS:
  280.                 if (attrs.get(this.key) == null) return false;
  281.                 Pattern pattern = Pattern.compile(this.value.replace("*", ".*"), Pattern.CASE_INSENSITIVE);
  282.                 Matcher matcher = pattern.matcher((String)attrs.get(this.key).get(0));
  283.                 return matcher.find();
  284.             case PRESENCE:
  285.                 return attrs.get(this.key) != null;
  286.             case GTE: return this.value.compareTo((String)attrs.get(this.key).get(0)) <= 0;
  287.             case LTE: return this.value.compareTo((String)attrs.get(this.key).get(0)) >= 0;
  288.             default: return false;
  289.             }
  290.         } catch(NamingException e) {
  291.             return false;
  292.         }
  293.     }
  294. }