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