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.jpa.dao;
20  
21  import static org.assertj.core.api.Assertions.assertThat;
22  import static org.junit.jupiter.api.Assertions.assertEquals;
23  import static org.junit.jupiter.api.Assertions.assertFalse;
24  import static org.mockito.ArgumentMatchers.any;
25  import static org.mockito.ArgumentMatchers.anyString;
26  import static org.mockito.ArgumentMatchers.eq;
27  import static org.mockito.Mockito.doAnswer;
28  import static org.mockito.Mockito.mock;
29  import static org.mockito.Mockito.when;
30  
31  import co.elastic.clients.elasticsearch._types.SearchType;
32  import co.elastic.clients.elasticsearch._types.query_dsl.BoolQuery;
33  import co.elastic.clients.elasticsearch._types.query_dsl.DisMaxQuery;
34  import co.elastic.clients.elasticsearch._types.query_dsl.Query;
35  import co.elastic.clients.elasticsearch._types.query_dsl.QueryBuilders;
36  import co.elastic.clients.elasticsearch.core.SearchRequest;
37  import java.io.IOException;
38  import java.util.List;
39  import java.util.Optional;
40  import java.util.Set;
41  import org.apache.commons.lang3.tuple.Triple;
42  import org.apache.syncope.common.lib.SyncopeConstants;
43  import org.apache.syncope.common.lib.types.AnyTypeKind;
44  import org.apache.syncope.core.persistence.api.attrvalue.validation.PlainAttrValidationManager;
45  import org.apache.syncope.core.persistence.api.dao.DynRealmDAO;
46  import org.apache.syncope.core.persistence.api.dao.GroupDAO;
47  import org.apache.syncope.core.persistence.api.dao.RealmDAO;
48  import org.apache.syncope.core.persistence.api.dao.search.AnyCond;
49  import org.apache.syncope.core.persistence.api.dao.search.AttrCond;
50  import org.apache.syncope.core.persistence.api.dao.search.SearchCond;
51  import org.apache.syncope.core.persistence.api.entity.AnyUtils;
52  import org.apache.syncope.core.persistence.api.entity.AnyUtilsFactory;
53  import org.apache.syncope.core.persistence.api.entity.DynRealm;
54  import org.apache.syncope.core.persistence.api.entity.EntityFactory;
55  import org.apache.syncope.core.persistence.api.entity.PlainAttrValue;
56  import org.apache.syncope.core.persistence.api.entity.PlainSchema;
57  import org.apache.syncope.core.persistence.api.entity.Realm;
58  import org.apache.syncope.core.persistence.jpa.entity.JPAPlainSchema;
59  import org.apache.syncope.core.persistence.jpa.entity.user.JPAUPlainAttrValue;
60  import org.apache.syncope.core.persistence.jpa.entity.user.JPAUser;
61  import org.apache.syncope.core.provisioning.api.utils.RealmUtils;
62  import org.apache.syncope.core.spring.security.AuthContextUtils;
63  import org.apache.syncope.ext.elasticsearch.client.ElasticsearchUtils;
64  import org.junit.jupiter.api.BeforeEach;
65  import org.junit.jupiter.api.Test;
66  import org.junit.jupiter.api.extension.ExtendWith;
67  import org.mockito.Mock;
68  import org.mockito.MockedStatic;
69  import org.mockito.Mockito;
70  import org.mockito.junit.jupiter.MockitoExtension;
71  import org.springframework.util.ReflectionUtils;
72  
73  @ExtendWith(MockitoExtension.class)
74  public class ElasticsearchAnySearchDAOTest {
75  
76      @Mock
77      private RealmDAO realmDAO;
78  
79      @Mock
80      private DynRealmDAO dynRealmDAO;
81  
82      @Mock
83      private GroupDAO groupDAO;
84  
85      @Mock
86      private EntityFactory entityFactory;
87  
88      @Mock
89      private AnyUtilsFactory anyUtilsFactory;
90  
91      @Mock
92      private PlainAttrValidationManager validator;
93  
94      private ElasticsearchAnySearchDAO searchDAO;
95  
96      @BeforeEach
97      protected void setupSearchDAO() {
98          searchDAO = new ElasticsearchAnySearchDAO(
99                  realmDAO,
100                 dynRealmDAO,
101                 null,
102                 groupDAO,
103                 null,
104                 null,
105                 entityFactory,
106                 anyUtilsFactory,
107                 validator,
108                 null,
109                 10000);
110     }
111 
112     @Test
113     public void getAdminRealmsFilter4realm() throws IOException {
114         // 1. mock
115         Realm root = mock(Realm.class);
116         when(root.getFullPath()).thenReturn(SyncopeConstants.ROOT_REALM);
117 
118         when(realmDAO.findByFullPath(SyncopeConstants.ROOT_REALM)).thenReturn(root);
119         when(realmDAO.findDescendants(eq(SyncopeConstants.ROOT_REALM), anyString())).thenReturn(List.of("rootKey"));
120 
121         // 2. test
122         Set<String> adminRealms = Set.of(SyncopeConstants.ROOT_REALM);
123         Triple<Optional<Query>, Set<String>, Set<String>> filter =
124                 searchDAO.getAdminRealmsFilter(root, true, adminRealms, AnyTypeKind.USER);
125 
126         assertThat(new Query.Builder().disMax(QueryBuilders.disMax().queries(
127                 new Query.Builder().term(QueryBuilders.term().field("realm").value("rootKey").build()).
128                         build()).build()).build()).
129                 usingRecursiveComparison().isEqualTo(filter.getLeft().get());
130         assertEquals(Set.of(), filter.getMiddle());
131         assertEquals(Set.of(), filter.getRight());
132     }
133 
134     @Test
135     public void getAdminRealmsFilter4dynRealm() {
136         // 1. mock
137         DynRealm dyn = mock(DynRealm.class);
138         when(dyn.getKey()).thenReturn("dyn");
139 
140         when(dynRealmDAO.find("dyn")).thenReturn(dyn);
141 
142         // 2. test
143         Set<String> adminRealms = Set.of("dyn");
144         Triple<Optional<Query>, Set<String>, Set<String>> filter =
145                 searchDAO.getAdminRealmsFilter(realmDAO.getRoot(), true, adminRealms, AnyTypeKind.USER);
146         assertFalse(filter.getLeft().isPresent());
147         assertEquals(Set.of("dyn"), filter.getMiddle());
148         assertEquals(Set.of(), filter.getRight());
149     }
150 
151     @Test
152     public void getAdminRealmsFilter4groupOwner() {
153         Set<String> adminRealms = Set.of(RealmUtils.getGroupOwnerRealm("/any", "groupKey"));
154         Triple<Optional<Query>, Set<String>, Set<String>> filter =
155                 searchDAO.getAdminRealmsFilter(realmDAO.getRoot(), true, adminRealms, AnyTypeKind.USER);
156         assertFalse(filter.getLeft().isPresent());
157         assertEquals(Set.of(), filter.getMiddle());
158         assertEquals(Set.of("groupKey"), filter.getRight());
159     }
160 
161     @Test
162     public void searchRequest4groupOwner() throws IOException {
163         // 1. mock
164         AnyUtils anyUtils = mock(AnyUtils.class);
165         when(anyUtils.getField("key")).thenReturn(ReflectionUtils.findField(JPAUser.class, "id"));
166         when(anyUtils.newPlainAttrValue()).thenReturn(new JPAUPlainAttrValue());
167 
168         when(anyUtilsFactory.getInstance(AnyTypeKind.USER)).thenReturn(anyUtils);
169 
170         when(entityFactory.newEntity(PlainSchema.class)).thenReturn(new JPAPlainSchema());
171 
172         when(groupDAO.findKey("groupKey")).thenReturn("groupKey");
173 
174         try (MockedStatic<ElasticsearchUtils> utils = Mockito.mockStatic(ElasticsearchUtils.class)) {
175             utils.when(() -> ElasticsearchUtils.getAnyIndex(
176                     SyncopeConstants.MASTER_DOMAIN, AnyTypeKind.USER)).thenReturn("master_user");
177 
178             // 2. test
179             Set<String> adminRealms = Set.of(RealmUtils.getGroupOwnerRealm("/any", "groupKey"));
180 
181             AnyCond anyCond = new AnyCond(AttrCond.Type.ISNOTNULL);
182             anyCond.setSchema("key");
183 
184             SearchRequest request = new SearchRequest.Builder().
185                     index(ElasticsearchUtils.getAnyIndex(AuthContextUtils.getDomain(), AnyTypeKind.USER)).
186                     searchType(SearchType.QueryThenFetch).
187                     query(searchDAO.getQuery(realmDAO.findByFullPath("/any"), true,
188                             adminRealms, SearchCond.getLeaf(anyCond), AnyTypeKind.USER)).
189                     from(1).
190                     size(10).
191                     build();
192 
193             assertThat(
194                     new Query.Builder().bool(QueryBuilders.bool().
195                             must(new Query.Builder().exists(QueryBuilders.exists().field("id").build()).build()).
196                             must(new Query.Builder().term(QueryBuilders.term().field("memberships").value("groupKey").
197                                     build()).build()).build()).build()).
198                     usingRecursiveComparison().
199                     isEqualTo(request.query());
200         }
201     }
202 
203     @Test
204     public void issueSYNCOPE1725() throws IOException {
205         // 1. mock
206         AnyUtils anyUtils = mock(AnyUtils.class);
207         when(anyUtils.getField("key")).thenReturn(ReflectionUtils.findField(JPAUser.class, "id"));
208         JPAUPlainAttrValue value = new JPAUPlainAttrValue();
209         when(anyUtils.newPlainAttrValue()).thenReturn(value);
210 
211         when(anyUtilsFactory.getInstance(AnyTypeKind.USER)).thenReturn(anyUtils);
212 
213         when(entityFactory.newEntity(PlainSchema.class)).thenReturn(new JPAPlainSchema());
214 
215         doAnswer(ic -> {
216             value.setStringValue(ic.getArgument(1));
217             return null;
218         }).when(validator).validate(any(PlainSchema.class), anyString(), any(PlainAttrValue.class));
219 
220         AnyCond cond1 = new AnyCond(AttrCond.Type.EQ);
221         cond1.setSchema("key");
222         cond1.setExpression("1");
223 
224         AnyCond cond2 = new AnyCond(AttrCond.Type.EQ);
225         cond2.setSchema("key");
226         cond2.setExpression("2");
227 
228         AnyCond cond3 = new AnyCond(AttrCond.Type.EQ);
229         cond3.setSchema("key");
230         cond3.setExpression("3");
231 
232         AnyCond cond4 = new AnyCond(AttrCond.Type.EQ);
233         cond4.setSchema("key");
234         cond4.setExpression("4");
235 
236         AnyCond cond5 = new AnyCond(AttrCond.Type.EQ);
237         cond5.setSchema("key");
238         cond5.setExpression("5");
239 
240         AnyCond cond6 = new AnyCond(AttrCond.Type.EQ);
241         cond6.setSchema("key");
242         cond6.setExpression("6");
243 
244         try (MockedStatic<ElasticsearchUtils> utils = Mockito.mockStatic(ElasticsearchUtils.class)) {
245             utils.when(() -> ElasticsearchUtils.getAnyIndex(
246                     SyncopeConstants.MASTER_DOMAIN, AnyTypeKind.USER)).thenReturn("master_user");
247 
248             Query query = searchDAO.getQuery(
249                     SearchCond.getAnd(
250                             List.of(SearchCond.getLeaf(cond1),
251                                     SearchCond.getLeaf(cond2),
252                                     SearchCond.getLeaf(cond3),
253                                     SearchCond.getLeaf(cond4),
254                                     SearchCond.getLeaf(cond5),
255                                     SearchCond.getLeaf(cond6))),
256                     AnyTypeKind.USER);
257             assertEquals(Query.Kind.Bool, query._kind());
258             assertEquals(6, ((BoolQuery) query._get()).must().size());
259             assertThat(
260                     new Query.Builder().bool(QueryBuilders.bool().
261                             must(new Query.Builder().term(
262                                     QueryBuilders.term().field("id").value("1").build()).build()).
263                             must(new Query.Builder().term(
264                                     QueryBuilders.term().field("id").value("2").build()).build()).
265                             must(new Query.Builder().term(
266                                     QueryBuilders.term().field("id").value("3").build()).build()).
267                             must(new Query.Builder().term(
268                                     QueryBuilders.term().field("id").value("4").build()).build()).
269                             must(new Query.Builder().term(
270                                     QueryBuilders.term().field("id").value("5").build()).build()).
271                             must(new Query.Builder().term(
272                                     QueryBuilders.term().field("id").value("6").build()).build()).
273                             build()).build()).
274                     usingRecursiveComparison().isEqualTo(query);
275 
276             query = searchDAO.getQuery(
277                     SearchCond.getOr(
278                             List.of(SearchCond.getLeaf(cond1),
279                                     SearchCond.getLeaf(cond2),
280                                     SearchCond.getLeaf(cond3),
281                                     SearchCond.getLeaf(cond4),
282                                     SearchCond.getLeaf(cond5),
283                                     SearchCond.getLeaf(cond6))),
284                     AnyTypeKind.USER);
285             assertEquals(Query.Kind.DisMax, query._kind());
286             assertEquals(6, ((DisMaxQuery) query._get()).queries().size());
287             assertThat(
288                     new Query.Builder().disMax(QueryBuilders.disMax().
289                             queries(new Query.Builder().term(
290                                     QueryBuilders.term().field("id").value("1").build()).build()).
291                             queries(new Query.Builder().term(
292                                     QueryBuilders.term().field("id").value("2").build()).build()).
293                             queries(new Query.Builder().term(
294                                     QueryBuilders.term().field("id").value("3").build()).build()).
295                             queries(new Query.Builder().term(
296                                     QueryBuilders.term().field("id").value("4").build()).build()).
297                             queries(new Query.Builder().term(
298                                     QueryBuilders.term().field("id").value("5").build()).build()).
299                             queries(new Query.Builder().term(
300                                     QueryBuilders.term().field("id").value("6").build()).build()).
301                             build()).build()).
302                     usingRecursiveComparison().isEqualTo(query);
303 
304             query = searchDAO.getQuery(
305                     SearchCond.getAnd(List.of(
306                             SearchCond.getOr(List.of(
307                                     SearchCond.getLeaf(cond1),
308                                     SearchCond.getLeaf(cond2),
309                                     SearchCond.getLeaf(cond3))),
310                             SearchCond.getOr(List.of(
311                                     SearchCond.getLeaf(cond4),
312                                     SearchCond.getLeaf(cond5),
313                                     SearchCond.getLeaf(cond6))))),
314                     AnyTypeKind.USER);
315             assertEquals(Query.Kind.Bool, query._kind());
316             assertEquals(2, ((BoolQuery) query._get()).must().size());
317             Query left = ((BoolQuery) query._get()).must().get(0);
318             assertEquals(Query.Kind.DisMax, left._kind());
319             assertEquals(3, ((DisMaxQuery) left._get()).queries().size());
320             Query right = ((BoolQuery) query._get()).must().get(1);
321             assertEquals(Query.Kind.DisMax, right._kind());
322             assertEquals(3, ((DisMaxQuery) right._get()).queries().size());
323             assertThat(
324                     new Query.Builder().bool(QueryBuilders.bool().
325                             must(new Query.Builder().disMax(QueryBuilders.disMax().
326                                     queries(new Query.Builder().term(
327                                             QueryBuilders.term().field("id").value("1").build()).build()).
328                                     queries(new Query.Builder().term(
329                                             QueryBuilders.term().field("id").value("2").build()).build()).
330                                     queries(new Query.Builder().term(
331                                             QueryBuilders.term().field("id").value("3").build()).build()).build()).
332                                     build()).
333                             must(new Query.Builder().disMax(QueryBuilders.disMax().
334                                     queries(new Query.Builder().term(
335                                             QueryBuilders.term().field("id").value("4").build()).build()).
336                                     queries(new Query.Builder().term(
337                                             QueryBuilders.term().field("id").value("5").build()).build()).
338                                     queries(new Query.Builder().term(
339                                             QueryBuilders.term().field("id").value("6").build()).build()).build()).
340                                     build()).
341                             build()).build()).
342                     usingRecursiveComparison().isEqualTo(query);
343 
344             query = searchDAO.getQuery(
345                     SearchCond.getOr(List.of(
346                             SearchCond.getAnd(List.of(
347                                     SearchCond.getLeaf(cond1),
348                                     SearchCond.getLeaf(cond2),
349                                     SearchCond.getLeaf(cond3))),
350                             SearchCond.getAnd(List.of(
351                                     SearchCond.getLeaf(cond4),
352                                     SearchCond.getLeaf(cond5),
353                                     SearchCond.getLeaf(cond6))))),
354                     AnyTypeKind.USER);
355             assertEquals(Query.Kind.DisMax, query._kind());
356             assertEquals(2, ((DisMaxQuery) query._get()).queries().size());
357             left = ((DisMaxQuery) query._get()).queries().get(0);
358             assertEquals(Query.Kind.Bool, left._kind());
359             assertEquals(3, ((BoolQuery) left._get()).must().size());
360             right = ((DisMaxQuery) query._get()).queries().get(1);
361             assertEquals(Query.Kind.Bool, right._kind());
362             assertEquals(3, ((BoolQuery) right._get()).must().size());
363             assertThat(
364                     new Query.Builder().disMax(QueryBuilders.disMax().
365                             queries(new Query.Builder().bool(QueryBuilders.bool().
366                                     must(new Query.Builder().term(
367                                             QueryBuilders.term().field("id").value("1").build()).build()).
368                                     must(new Query.Builder().term(
369                                             QueryBuilders.term().field("id").value("2").build()).build()).
370                                     must(new Query.Builder().term(
371                                             QueryBuilders.term().field("id").value("3").build()).build()).build()).
372                                     build()).
373                             queries(new Query.Builder().bool(QueryBuilders.bool().
374                                     must(new Query.Builder().term(
375                                             QueryBuilders.term().field("id").value("4").build()).build()).
376                                     must(new Query.Builder().term(
377                                             QueryBuilders.term().field("id").value("5").build()).build()).
378                                     must(new Query.Builder().term(
379                                             QueryBuilders.term().field("id").value("6").build()).build()).build()).
380                                     build()).
381                             build()).build()).
382                     usingRecursiveComparison().isEqualTo(query);
383         }
384     }
385 }