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.client.console.panels;
20  
21  import java.io.Serializable;
22  import java.util.ArrayList;
23  import java.util.List;
24  import org.apache.commons.lang3.StringUtils;
25  import org.apache.syncope.client.console.commons.RealmsUtils;
26  import org.apache.syncope.client.console.layout.AnyLayout;
27  import org.apache.syncope.client.console.layout.AnyLayoutUtils;
28  import org.apache.syncope.client.console.panels.search.AbstractSearchPanel;
29  import org.apache.syncope.client.console.panels.search.AnyObjectSearchPanel;
30  import org.apache.syncope.client.console.panels.search.GroupSearchPanel;
31  import org.apache.syncope.client.console.panels.search.SearchClause;
32  import org.apache.syncope.client.console.panels.search.SearchClausePanel;
33  import org.apache.syncope.client.console.panels.search.SearchUtils;
34  import org.apache.syncope.client.console.panels.search.UserSearchPanel;
35  import org.apache.syncope.client.console.rest.AnyObjectRestClient;
36  import org.apache.syncope.client.console.rest.AnyTypeClassRestClient;
37  import org.apache.syncope.client.console.rest.GroupRestClient;
38  import org.apache.syncope.client.console.rest.UserRestClient;
39  import org.apache.syncope.client.console.wicket.markup.html.form.ActionLinksTogglePanel;
40  import org.apache.syncope.client.lib.SyncopeClient;
41  import org.apache.syncope.client.ui.commons.Constants;
42  import org.apache.syncope.client.ui.commons.panels.LabelPanel;
43  import org.apache.syncope.client.ui.commons.panels.ModalPanel;
44  import org.apache.syncope.client.ui.commons.wicket.markup.html.bootstrap.tabs.Accordion;
45  import org.apache.syncope.common.lib.SyncopeConstants;
46  import org.apache.syncope.common.lib.to.AnyObjectTO;
47  import org.apache.syncope.common.lib.to.AnyTypeTO;
48  import org.apache.syncope.common.lib.to.GroupTO;
49  import org.apache.syncope.common.lib.to.RealmTO;
50  import org.apache.syncope.common.lib.to.UserTO;
51  import org.apache.syncope.common.lib.types.AnyEntitlement;
52  import org.apache.syncope.common.lib.types.IdRepoEntitlement;
53  import org.apache.wicket.Component;
54  import org.apache.wicket.PageReference;
55  import org.apache.wicket.ajax.AjaxRequestTarget;
56  import org.apache.wicket.ajax.markup.html.AjaxLink;
57  import org.apache.wicket.authroles.authorization.strategies.role.metadata.MetaDataRoleAuthorizationStrategy;
58  import org.apache.wicket.event.Broadcast;
59  import org.apache.wicket.event.IEvent;
60  import org.apache.wicket.extensions.markup.html.tabs.AbstractTab;
61  import org.apache.wicket.extensions.markup.html.tabs.ITab;
62  import org.apache.wicket.markup.html.WebMarkupContainer;
63  import org.apache.wicket.markup.html.WebPage;
64  import org.apache.wicket.markup.html.panel.Panel;
65  import org.apache.wicket.model.Model;
66  import org.apache.wicket.model.ResourceModel;
67  import org.apache.wicket.model.util.ListModel;
68  import org.apache.wicket.spring.injection.annot.SpringBean;
69  import org.slf4j.Logger;
70  import org.slf4j.LoggerFactory;
71  import org.springframework.util.ClassUtils;
72  
73  public class AnyPanel extends Panel implements ModalPanel {
74  
75      private static final long serialVersionUID = -1100228004207271270L;
76  
77      protected static final Logger LOG = LoggerFactory.getLogger(AnyPanel.class);
78  
79      protected static final String DIRECTORY_PANEL_ID = "searchResult";
80  
81      @FunctionalInterface
82      public interface DirectoryPanelSupplier extends Serializable {
83  
84          Panel supply(
85                  String id,
86                  AnyTypeTO anyTypeTO,
87                  RealmTO realmTO,
88                  AnyLayout anyLayout,
89                  PageReference pageRef);
90      }
91  
92      public static class Builder<AP extends AnyPanel> {
93  
94          private final AP instance;
95  
96          public Builder(
97                  final String panelClass,
98                  final String id,
99                  final AnyTypeTO anyTypeTO,
100                 final RealmTO realmTO,
101                 final AnyLayout anyLayout,
102                 final boolean enableSearch,
103                 final PageReference pageRef) {
104 
105             try {
106                 @SuppressWarnings("unchecked")
107                 Class<AP> clazz = (Class<AP>) ClassUtils.forName(panelClass, ClassUtils.getDefaultClassLoader());
108                 instance = clazz.getDeclaredConstructor(
109                         String.class,
110                         AnyTypeTO.class,
111                         RealmTO.class,
112                         AnyLayout.class,
113                         boolean.class,
114                         PageReference.class).
115                         newInstance(id, anyTypeTO, realmTO, anyLayout, enableSearch, pageRef);
116             } catch (Exception e) {
117                 throw new IllegalArgumentException("Could not instantiate " + panelClass, e);
118             }
119         }
120 
121         public AP build() {
122             return build(instance.getDefaultDirectoryPanelSupplier());
123         }
124 
125         public AP build(final DirectoryPanelSupplier directoryPanelSupplier) {
126             instance.directoryPanel =
127                     instance.createDirectoryPanel(
128                             instance.anyTypeTO,
129                             instance.realmTO,
130                             instance.anyLayout,
131                             directoryPanelSupplier);
132             instance.add(instance.directoryPanel);
133             return instance;
134         }
135     }
136 
137     @SpringBean
138     protected AnyTypeClassRestClient anyTypeClassRestClient;
139 
140     @SpringBean
141     protected UserRestClient userRestClient;
142 
143     @SpringBean
144     protected GroupRestClient groupRestClient;
145 
146     @SpringBean
147     protected AnyObjectRestClient anyObjectRestClient;
148 
149     protected final AnyTypeTO anyTypeTO;
150 
151     protected final RealmTO realmTO;
152 
153     protected final AnyLayout anyLayout;
154 
155     protected final PageReference pageRef;
156 
157     protected AbstractSearchPanel searchPanel;
158 
159     protected Panel directoryPanel;
160 
161     protected AnyPanel(
162             final String id,
163             final AnyTypeTO anyTypeTO,
164             final RealmTO realmTO,
165             final AnyLayout anyLayout,
166             final boolean enableSearch,
167             final PageReference pageRef) {
168 
169         super(id);
170         this.anyTypeTO = anyTypeTO;
171         this.realmTO = realmTO;
172         this.anyLayout = anyLayout;
173         this.pageRef = pageRef;
174 
175         // ------------------------
176         // Accordion
177         // ------------------------
178         Model<Integer> model = Model.of(-1);
179         Accordion accordion = new Accordion("accordionPanel",
180                 List.of(new AbstractTab(new ResourceModel("search.result")) {
181 
182                     protected static final long serialVersionUID = 1037272333056449377L;
183 
184                     @Override
185                     public WebMarkupContainer getPanel(final String panelId) {
186                         searchPanel = getSearchPanel(panelId);
187                         return searchPanel;
188                     }
189 
190                 }), model) {
191 
192             protected static final long serialVersionUID = -3056452800492734900L;
193 
194             @Override
195             protected Component newTitle(final String markupId, final ITab tab, final Accordion.State state) {
196                 return new AjaxLink<Integer>(markupId) {
197 
198                     protected static final long serialVersionUID = 6250423506463465679L;
199 
200                     @Override
201                     public void onClick(final AjaxRequestTarget target) {
202                         model.setObject(model.getObject() == 0 ? -1 : 0);
203                     }
204                 }.setBody(tab.getTitle()).setEscapeModelStrings(false);
205             }
206         };
207         accordion.setOutputMarkupId(true);
208         add(accordion.setEnabled(enableSearch).setVisible(enableSearch));
209         // ------------------------
210     }
211 
212     protected DirectoryPanelSupplier getDefaultDirectoryPanelSupplier() {
213         return (id, anyType, r, layout, pr) -> {
214             Panel panel;
215             String fiql;
216 
217             String realm;
218             String dynRealm;
219             if (StringUtils.startsWith(r.getFullPath(), SyncopeConstants.ROOT_REALM)) {
220                 realm = RealmsUtils.getFullPath(r.getFullPath());
221                 dynRealm = null;
222             } else {
223                 realm = SyncopeConstants.ROOT_REALM;
224                 dynRealm = r.getKey();
225             }
226 
227             switch (anyType.getKind()) {
228                 case USER:
229                     fiql = dynRealm == null
230                             ? SyncopeClient.getUserSearchConditionBuilder().
231                                     is(Constants.KEY_FIELD_NAME).notNullValue().query()
232                             : SyncopeClient.getUserSearchConditionBuilder().
233                                     inDynRealms(dynRealm).query();
234 
235                     UserTO user = new UserTO();
236                     user.setRealm(RealmsUtils.getFullPath(r.getFullPath()));
237                     panel = new UserDirectoryPanel.Builder(
238                             anyTypeClassRestClient.list(anyType.getClasses()),
239                             userRestClient,
240                             anyType.getKey(),
241                             pr).setRealm(realm).setDynRealm(dynRealm).setFiltered(true).
242                             setFiql(fiql).setWizardInModal(true).addNewItemPanelBuilder(
243                             AnyLayoutUtils.newLayoutInfo(
244                                     user, anyType.getClasses(), layout.getUser(), userRestClient, pr)).
245                             build(id);
246                     MetaDataRoleAuthorizationStrategy.authorize(panel, WebPage.RENDER,
247                             IdRepoEntitlement.USER_SEARCH);
248                     break;
249 
250                 case GROUP:
251                     fiql = dynRealm == null
252                             ? SyncopeClient.getGroupSearchConditionBuilder().
253                                     is(Constants.KEY_FIELD_NAME).notNullValue().query()
254                             : SyncopeClient.getGroupSearchConditionBuilder().
255                                     inDynRealms(dynRealm).query();
256 
257                     GroupTO group = new GroupTO();
258                     group.setRealm(RealmsUtils.getFullPath(r.getFullPath()));
259                     panel = new GroupDirectoryPanel.Builder(
260                             anyTypeClassRestClient.list(anyType.getClasses()),
261                             groupRestClient,
262                             anyType.getKey(),
263                             pr).setRealm(realm).setDynRealm(dynRealm).setFiltered(true).
264                             setFiql(fiql).setWizardInModal(true).addNewItemPanelBuilder(
265                             AnyLayoutUtils.newLayoutInfo(
266                                     group, anyType.getClasses(), layout.getGroup(), groupRestClient, pr)).
267                             build(id);
268                     // list of group is available to all authenticated users
269                     break;
270 
271                 case ANY_OBJECT:
272                     fiql = dynRealm == null
273                             ? SyncopeClient.getAnyObjectSearchConditionBuilder(anyType.getKey()).
274                                     is(Constants.KEY_FIELD_NAME).notNullValue().query()
275                             : SyncopeClient.getAnyObjectSearchConditionBuilder(anyType.getKey()).
276                                     inDynRealms(dynRealm).query();
277 
278                     AnyObjectTO anyObject = new AnyObjectTO();
279                     anyObject.setRealm(RealmsUtils.getFullPath(r.getFullPath()));
280                     anyObject.setType(anyType.getKey());
281                     panel = new AnyObjectDirectoryPanel.Builder(
282                             anyTypeClassRestClient.list(anyType.getClasses()),
283                             anyObjectRestClient,
284                             anyType.getKey(),
285                             pr).setRealm(realm).setDynRealm(dynRealm).setFiltered(true).
286                             setFiql(fiql).setWizardInModal(true).addNewItemPanelBuilder(
287                             AnyLayoutUtils.newLayoutInfo(anyObject, anyType.getClasses(),
288                                     layout.getAnyObjects().get(anyType.getKey()),
289                                     anyObjectRestClient, pr)).
290                             build(id);
291                     MetaDataRoleAuthorizationStrategy.authorize(
292                             panel, WebPage.RENDER, AnyEntitlement.SEARCH.getFor(anyType.getKey()));
293                     break;
294 
295                 default:
296                     panel = new LabelPanel(id, null);
297             }
298             return panel;
299         };
300     }
301 
302     protected Panel createDirectoryPanel(
303             final AnyTypeTO anyTypeTO,
304             final RealmTO realmTO,
305             final AnyLayout anyLayout,
306             final DirectoryPanelSupplier directoryPanelSupplier) {
307 
308         return directoryPanelSupplier.supply(DIRECTORY_PANEL_ID, anyTypeTO, realmTO, anyLayout, pageRef);
309     }
310 
311     @Override
312     public void onEvent(final IEvent<?> event) {
313         if (event.getPayload() instanceof SearchClausePanel.SearchEvent) {
314             AjaxRequestTarget target = SearchClausePanel.SearchEvent.class.cast(event.getPayload()).getTarget();
315 
316             send(AnyPanel.this.directoryPanel, Broadcast.BREADTH,
317                     new ActionLinksTogglePanel.ActionLinkToggleCloseEventPayload(target));
318 
319             String precond = realmTO.getFullPath().startsWith(SyncopeConstants.ROOT_REALM)
320                     ? StringUtils.EMPTY
321                     : String.format("$dynRealms=~%s;", realmTO.getKey());
322 
323             switch (anyTypeTO.getKind()) {
324                 case USER:
325                     UserDirectoryPanel.class.cast(AnyPanel.this.directoryPanel).search(
326                             precond + SearchUtils.buildFIQL(
327                                     AnyPanel.this.searchPanel.getModel().getObject(),
328                                     SyncopeClient.getUserSearchConditionBuilder(),
329                                     AnyPanel.this.searchPanel.getAvailableSchemaTypes(),
330                                     SearchUtils.NO_CUSTOM_CONDITION),
331                             target);
332                     break;
333 
334                 case GROUP:
335                     GroupDirectoryPanel.class.cast(AnyPanel.this.directoryPanel).search(
336                             precond + SearchUtils.buildFIQL(
337                                     AnyPanel.this.searchPanel.getModel().getObject(),
338                                     SyncopeClient.getGroupSearchConditionBuilder(),
339                                     AnyPanel.this.searchPanel.getAvailableSchemaTypes(),
340                                     SearchUtils.NO_CUSTOM_CONDITION),
341                             target);
342                     break;
343 
344                 case ANY_OBJECT:
345                     AnyObjectDirectoryPanel.class.cast(AnyPanel.this.directoryPanel).search(
346                             precond + SearchUtils.buildFIQL(
347                                     AnyPanel.this.searchPanel.getModel().getObject(),
348                                     SyncopeClient.getAnyObjectSearchConditionBuilder(anyTypeTO.getKey()),
349                                     AnyPanel.this.searchPanel.getAvailableSchemaTypes(),
350                                     SearchUtils.NO_CUSTOM_CONDITION),
351                             target);
352                     break;
353 
354                 default:
355             }
356         } else {
357             super.onEvent(event);
358         }
359     }
360 
361     protected AbstractSearchPanel getSearchPanel(final String id) {
362         List<SearchClause> clauses = new ArrayList<>();
363         SearchClause clause = new SearchClause();
364         clauses.add(clause);
365 
366         AbstractSearchPanel panel;
367         switch (anyTypeTO.getKind()) {
368             case USER:
369                 clause.setComparator(SearchClause.Comparator.EQUALS);
370                 clause.setType(SearchClause.Type.ATTRIBUTE);
371                 clause.setProperty(Constants.USERNAME_FIELD_NAME);
372 
373                 panel = new UserSearchPanel.Builder(
374                         new ListModel<>(clauses), pageRef).required(true).enableSearch().build(id);
375                 break;
376 
377             case GROUP:
378                 clause.setComparator(SearchClause.Comparator.EQUALS);
379                 clause.setType(SearchClause.Type.ATTRIBUTE);
380                 clause.setProperty(Constants.NAME_FIELD_NAME);
381 
382                 panel = new GroupSearchPanel.Builder(
383                         new ListModel<>(clauses), pageRef).required(true).enableSearch().build(id);
384                 break;
385 
386             case ANY_OBJECT:
387                 clause.setComparator(SearchClause.Comparator.EQUALS);
388                 clause.setType(SearchClause.Type.ATTRIBUTE);
389                 clause.setProperty(Constants.NAME_FIELD_NAME);
390 
391                 panel = new AnyObjectSearchPanel.Builder(anyTypeTO.getKey(),
392                         new ListModel<>(clauses), pageRef).required(true).enableSearch().build(id);
393                 break;
394 
395             default:
396                 panel = null;
397         }
398 
399         return panel;
400     }
401 }