View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.syncope.core.persistence.api.search;
20  
21  import java.net.URLDecoder;
22  import java.nio.charset.StandardCharsets;
23  import java.util.ArrayList;
24  import java.util.HashSet;
25  import java.util.List;
26  import java.util.Optional;
27  import java.util.Set;
28  import org.apache.commons.lang3.StringUtils;
29  import org.apache.cxf.jaxrs.ext.search.ConditionType;
30  import org.apache.cxf.jaxrs.ext.search.SearchBean;
31  import org.apache.cxf.jaxrs.ext.search.SearchCondition;
32  import org.apache.cxf.jaxrs.ext.search.SearchUtils;
33  import org.apache.cxf.jaxrs.ext.search.visitor.AbstractSearchConditionVisitor;
34  import org.apache.syncope.common.lib.search.SpecialAttr;
35  import org.apache.syncope.common.lib.search.SyncopeFiqlParser;
36  import org.apache.syncope.common.lib.search.SyncopeFiqlSearchCondition;
37  import org.identityconnectors.framework.common.objects.Attribute;
38  import org.identityconnectors.framework.common.objects.AttributeBuilder;
39  import org.identityconnectors.framework.common.objects.filter.Filter;
40  import org.identityconnectors.framework.common.objects.filter.FilterBuilder;
41  
42  public class FilterVisitor extends AbstractSearchConditionVisitor<SearchBean, Filter> {
43  
44      private Filter filter;
45  
46      private final Set<String> attrs = new HashSet<>();
47  
48      public FilterVisitor() {
49          super(null);
50      }
51  
52      private Filter visitPrimitive(final SearchCondition<SearchBean> sc) {
53          String name = getRealPropertyName(sc.getStatement().getProperty());
54          Optional<SpecialAttr> specialAttrName = SpecialAttr.fromString(name);
55  
56          String value = SearchUtils.toSqlWildcardString(
57                  URLDecoder.decode(sc.getStatement().getValue().toString(), StandardCharsets.UTF_8), false).
58                  replaceAll("\\\\_", "_");
59          Optional<SpecialAttr> specialAttrValue = SpecialAttr.fromString(value);
60  
61          ConditionType ct = sc.getConditionType();
62          if (sc instanceof SyncopeFiqlSearchCondition && sc.getConditionType() == ConditionType.CUSTOM) {
63              SyncopeFiqlSearchCondition<SearchBean> sfsc = (SyncopeFiqlSearchCondition<SearchBean>) sc;
64              switch (sfsc.getOperator()) {
65                  case SyncopeFiqlParser.IEQ:
66                      ct = ConditionType.EQUALS;
67                      break;
68  
69                  case SyncopeFiqlParser.NIEQ:
70                      ct = ConditionType.NOT_EQUALS;
71                      break;
72  
73                  default:
74                      throw new IllegalArgumentException(
75                              String.format("Condition type %s is not supported", sfsc.getOperator()));
76              }
77          }
78  
79          Attribute attr = AttributeBuilder.build(name, value);
80          attrs.add(name);
81  
82          Filter leaf;
83          switch (ct) {
84              case EQUALS:
85              case NOT_EQUALS:
86                  if (specialAttrName.isEmpty()) {
87                      if (specialAttrValue.isPresent() && specialAttrValue.get() == SpecialAttr.NULL) {
88                          Filter empty = FilterBuilder.startsWith(AttributeBuilder.build(name, StringUtils.EMPTY));
89                          if (ct == ConditionType.NOT_EQUALS) {
90                              leaf = empty;
91                          } else {
92                              leaf = FilterBuilder.not(empty);
93                              attrs.remove(name);
94                          }
95                      } else {
96                          if (value.indexOf('%') == -1) {
97                              leaf = sc.getConditionType() == ConditionType.CUSTOM
98                                      ? FilterBuilder.equalsIgnoreCase(attr)
99                                      : FilterBuilder.equalTo(attr);
100                         } else if (sc.getConditionType() != ConditionType.CUSTOM && value.startsWith("%")) {
101                             leaf = FilterBuilder.endsWith(
102                                     AttributeBuilder.build(name, value.substring(1)));
103                         } else if (sc.getConditionType() != ConditionType.CUSTOM && value.endsWith("%")) {
104                             leaf = FilterBuilder.startsWith(
105                                     AttributeBuilder.build(name, value.substring(0, value.length() - 1)));
106                         } else {
107                             throw new IllegalArgumentException(
108                                     String.format("Unsupported search value %s", value));
109                         }
110 
111                         if (ct == ConditionType.NOT_EQUALS) {
112                             leaf = FilterBuilder.not(leaf);
113                         }
114                     }
115                 } else {
116                     throw new IllegalArgumentException(
117                             String.format("Special attr name %s is not supported", specialAttrName));
118                 }
119                 break;
120 
121             case GREATER_OR_EQUALS:
122                 leaf = FilterBuilder.greaterThanOrEqualTo(attr);
123                 break;
124 
125             case GREATER_THAN:
126                 leaf = FilterBuilder.greaterThan(attr);
127                 break;
128 
129             case LESS_OR_EQUALS:
130                 leaf = FilterBuilder.lessThanOrEqualTo(attr);
131                 break;
132 
133             case LESS_THAN:
134                 leaf = FilterBuilder.lessThan(attr);
135                 break;
136 
137             default:
138                 throw new IllegalArgumentException(String.format("Condition type %s is not supported", ct.name()));
139         }
140 
141         return leaf;
142     }
143 
144     private Filter visitCompount(final SearchCondition<SearchBean> sc) {
145         List<Filter> searchConds = new ArrayList<>();
146         sc.getSearchConditions().forEach(searchCond -> {
147             searchConds.add(searchCond.getStatement() == null
148                     ? visitCompount(searchCond)
149                     : visitPrimitive(searchCond));
150         });
151 
152         Filter compound;
153         switch (sc.getConditionType()) {
154             case AND:
155                 compound = FilterBuilder.and(searchConds);
156                 break;
157 
158             case OR:
159                 compound = FilterBuilder.or(searchConds);
160                 break;
161 
162             default:
163                 throw new IllegalArgumentException(
164                         String.format("Condition type %s is not supported", sc.getConditionType().name()));
165         }
166 
167         return compound;
168     }
169 
170     @Override
171     public void visit(final SearchCondition<SearchBean> sc) {
172         filter = sc.getStatement() == null
173                 ? visitCompount(sc)
174                 : visitPrimitive(sc);
175     }
176 
177     @Override
178     public Filter getQuery() {
179         return filter;
180     }
181 
182     public Set<String> getAttrs() {
183         return attrs;
184     }
185 }