1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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 }