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.authprofiles;
20  
21  import de.agilecoders.wicket.core.markup.html.bootstrap.dialog.Modal;
22  import java.io.Serializable;
23  import java.util.ArrayList;
24  import java.util.Collection;
25  import java.util.Iterator;
26  import java.util.List;
27  import org.apache.syncope.client.console.SyncopeConsoleSession;
28  import org.apache.syncope.client.console.authprofiles.AuthProfileDirectoryPanel.AuthProfileProvider;
29  import org.apache.syncope.client.console.commons.AMConstants;
30  import org.apache.syncope.client.console.commons.DirectoryDataProvider;
31  import org.apache.syncope.client.console.panels.DirectoryPanel;
32  import org.apache.syncope.client.console.panels.ModalDirectoryPanel;
33  import org.apache.syncope.client.console.rest.AuthProfileRestClient;
34  import org.apache.syncope.client.console.wicket.extensions.markup.html.repeater.data.table.BooleanConditionColumn;
35  import org.apache.syncope.client.console.wicket.extensions.markup.html.repeater.data.table.DatePropertyColumn;
36  import org.apache.syncope.client.console.wicket.extensions.markup.html.repeater.data.table.KeyPropertyColumn;
37  import org.apache.syncope.client.console.wicket.markup.html.bootstrap.dialog.BaseModal;
38  import org.apache.syncope.client.console.wicket.markup.html.form.ActionLink;
39  import org.apache.syncope.client.console.wicket.markup.html.form.ActionsPanel;
40  import org.apache.syncope.client.ui.commons.Constants;
41  import org.apache.syncope.client.ui.commons.markup.html.form.AjaxTextFieldPanel;
42  import org.apache.syncope.client.ui.commons.pages.BaseWebPage;
43  import org.apache.syncope.common.lib.to.AuthProfileTO;
44  import org.apache.syncope.common.lib.types.AMEntitlement;
45  import org.apache.syncope.common.lib.wa.GoogleMfaAuthAccount;
46  import org.apache.syncope.common.lib.wa.GoogleMfaAuthToken;
47  import org.apache.syncope.common.lib.wa.ImpersonationAccount;
48  import org.apache.syncope.common.lib.wa.MfaTrustedDevice;
49  import org.apache.syncope.common.lib.wa.U2FDevice;
50  import org.apache.syncope.common.lib.wa.WebAuthnDeviceCredential;
51  import org.apache.wicket.PageReference;
52  import org.apache.wicket.ajax.AjaxRequestTarget;
53  import org.apache.wicket.authroles.authorization.strategies.role.metadata.MetaDataRoleAuthorizationStrategy;
54  import org.apache.wicket.extensions.markup.html.repeater.data.sort.SortOrder;
55  import org.apache.wicket.extensions.markup.html.repeater.data.table.IColumn;
56  import org.apache.wicket.extensions.markup.html.repeater.data.table.PropertyColumn;
57  import org.apache.wicket.extensions.wizard.WizardModel;
58  import org.apache.wicket.model.CompoundPropertyModel;
59  import org.apache.wicket.model.IModel;
60  import org.apache.wicket.model.Model;
61  import org.apache.wicket.model.PropertyModel;
62  import org.apache.wicket.model.ResourceModel;
63  import org.apache.wicket.model.StringResourceModel;
64  
65  public class AuthProfileDirectoryPanel
66          extends DirectoryPanel<AuthProfileTO, AuthProfileTO, AuthProfileProvider, AuthProfileRestClient> {
67  
68      private static final long serialVersionUID = 2018518567549153364L;
69  
70      private final BaseModal<AuthProfileTO> authProfileModal;
71  
72      public AuthProfileDirectoryPanel(
73              final String id, final AuthProfileRestClient restClient, final PageReference pageRef) {
74  
75          super(id, restClient, pageRef);
76  
77          authProfileModal = new BaseModal<>(Constants.OUTER) {
78  
79              private static final long serialVersionUID = 389935548143327858L;
80  
81              @Override
82              protected void onConfigure() {
83                  super.onConfigure();
84                  setFooterVisible(false);
85              }
86          };
87          authProfileModal.size(Modal.Size.Large);
88          authProfileModal.setWindowClosedCallback(target -> {
89              updateResultTable(target);
90              authProfileModal.show(false);
91          });
92          addOuterObject(authProfileModal);
93  
94          addNewItemPanelBuilder(new CreateAuthProfileWizardBuilder(pageRef), true);
95          MetaDataRoleAuthorizationStrategy.authorize(addAjaxLink, RENDER, AMEntitlement.AUTH_PROFILE_CREATE);
96  
97          disableCheckBoxes();
98          initResultTable();
99      }
100 
101     @Override
102     protected AuthProfileProvider dataProvider() {
103         return new AuthProfileProvider(rows);
104     }
105 
106     @Override
107     protected String paginatorRowsKey() {
108         return AMConstants.PREF_AUTHPROFILE_PAGINATOR_ROWS;
109     }
110 
111     @Override
112     protected Collection<ActionLink.ActionType> getBatches() {
113         return List.of();
114     }
115 
116     @Override
117     protected List<IColumn<AuthProfileTO, String>> getColumns() {
118         List<IColumn<AuthProfileTO, String>> columns = new ArrayList<>();
119 
120         columns.add(new KeyPropertyColumn<>(
121                 new StringResourceModel(Constants.KEY_FIELD_NAME, this), Constants.KEY_FIELD_NAME));
122 
123         columns.add(new PropertyColumn<>(new ResourceModel("owner"), "owner", "owner"));
124 
125         columns.add(new BooleanConditionColumn<>(new StringResourceModel("impersonationAccounts")) {
126 
127             private static final long serialVersionUID = -8236820422411536323L;
128 
129             @Override
130             protected boolean isCondition(final IModel<AuthProfileTO> rowModel) {
131                 return !rowModel.getObject().getImpersonationAccounts().isEmpty();
132             }
133         });
134         columns.add(new BooleanConditionColumn<>(new StringResourceModel("googleMfaAuthTokens")) {
135 
136             private static final long serialVersionUID = -8236820422411536323L;
137 
138             @Override
139             protected boolean isCondition(final IModel<AuthProfileTO> rowModel) {
140                 return !rowModel.getObject().getGoogleMfaAuthTokens().isEmpty();
141             }
142         });
143         columns.add(new BooleanConditionColumn<>(new StringResourceModel("googleMfaAuthAccounts")) {
144 
145             private static final long serialVersionUID = -8236820422411536323L;
146 
147             @Override
148             protected boolean isCondition(final IModel<AuthProfileTO> rowModel) {
149                 return !rowModel.getObject().getGoogleMfaAuthAccounts().isEmpty();
150             }
151         });
152         columns.add(new BooleanConditionColumn<>(new StringResourceModel("u2fRegisteredDevices")) {
153 
154             private static final long serialVersionUID = -8236820422411536323L;
155 
156             @Override
157             protected boolean isCondition(final IModel<AuthProfileTO> rowModel) {
158                 return !rowModel.getObject().getU2FRegisteredDevices().isEmpty();
159             }
160         });
161         columns.add(new BooleanConditionColumn<>(new StringResourceModel("mfaTrustedDevices")) {
162 
163             private static final long serialVersionUID = -8236820422411536323L;
164 
165             @Override
166             protected boolean isCondition(final IModel<AuthProfileTO> rowModel) {
167                 return !rowModel.getObject().getMfaTrustedDevices().isEmpty();
168             }
169         });
170         columns.add(new BooleanConditionColumn<>(new StringResourceModel("webAuthnAccount")) {
171 
172             private static final long serialVersionUID = -8236820422411536323L;
173 
174             @Override
175             protected boolean isCondition(final IModel<AuthProfileTO> rowModel) {
176                 return !rowModel.getObject().getWebAuthnDeviceCredentials().isEmpty();
177             }
178         });
179 
180         return columns;
181     }
182 
183     @Override
184     public ActionsPanel<AuthProfileTO> getActions(final IModel<AuthProfileTO> model) {
185         ActionsPanel<AuthProfileTO> panel = super.getActions(model);
186 
187         panel.add(new ActionLink<>() {
188 
189             private static final long serialVersionUID = -3722207913631435501L;
190 
191             @Override
192             public void onClick(final AjaxRequestTarget target, final AuthProfileTO ignore) {
193                 model.setObject(restClient.read(model.getObject().getKey()));
194                 target.add(authProfileModal.setContent(new ModalDirectoryPanel<>(
195                         authProfileModal,
196                         new AuthProfileItemDirectoryPanel<ImpersonationAccount>(
197                                 "panel", restClient, authProfileModal, model.getObject(), pageRef) {
198 
199                     private static final long serialVersionUID = -5380664539000792237L;
200 
201                     @Override
202                     protected List<ImpersonationAccount> getItems() {
203                         return model.getObject().getImpersonationAccounts();
204                     }
205 
206                     @Override
207                     protected ImpersonationAccount defaultItem() {
208                         return new ImpersonationAccount();
209                     }
210 
211                     @Override
212                     protected String sortProperty() {
213                         return "impersonated";
214                     }
215 
216                     @Override
217                     protected String paginatorRowsKey() {
218                         return AMConstants.PREF_AUTHPROFILE_IMPERSONATED_PAGINATOR_ROWS;
219                     }
220 
221                     @Override
222                     protected List<IColumn<ImpersonationAccount, String>> getColumns() {
223                         List<IColumn<ImpersonationAccount, String>> columns = new ArrayList<>();
224                         columns.add(new PropertyColumn<>(new ResourceModel("impersonated"),
225                                 "impersonated", "impersonated"));
226                         return columns;
227                     }
228                 }, pageRef)));
229                 authProfileModal.header(new Model<>(getString("impersonationAccounts", model)));
230                 authProfileModal.show(true);
231             }
232         }, ActionLink.ActionType.TYPE_EXTENSIONS, AMEntitlement.AUTH_PROFILE_UPDATE);
233 
234         panel.add(new ActionLink<>() {
235 
236             private static final long serialVersionUID = -3722207913631435501L;
237 
238             @Override
239             public void onClick(final AjaxRequestTarget target, final AuthProfileTO ignore) {
240                 model.setObject(restClient.read(model.getObject().getKey()));
241                 target.add(authProfileModal.setContent(new ModalDirectoryPanel<>(
242                         authProfileModal,
243                         new AuthProfileItemDirectoryPanel<GoogleMfaAuthToken>(
244                                 "panel", restClient, authProfileModal, model.getObject(), pageRef) {
245 
246                     private static final long serialVersionUID = 7332357430197837993L;
247 
248                     @Override
249                     protected List<GoogleMfaAuthToken> getItems() {
250                         return model.getObject().getGoogleMfaAuthTokens();
251                     }
252 
253                     @Override
254                     protected GoogleMfaAuthToken defaultItem() {
255                         return new GoogleMfaAuthToken();
256                     }
257 
258                     @Override
259                     protected String sortProperty() {
260                         return "issueDate";
261                     }
262 
263                     @Override
264                     protected String paginatorRowsKey() {
265                         return AMConstants.PREF_AUTHPROFILE_GOOGLEMFAAUTHTOKENS_PAGINATOR_ROWS;
266                     }
267 
268                     @Override
269                     protected List<IColumn<GoogleMfaAuthToken, String>> getColumns() {
270                         List<IColumn<GoogleMfaAuthToken, String>> columns = new ArrayList<>();
271                         columns.add(new DatePropertyColumn<>(
272                                 new ResourceModel("issueDate"), "issueDate", "issueDate"));
273                         columns.add(new PropertyColumn<>(
274                                 new ResourceModel("otp"), "otp", "otp"));
275                         return columns;
276                     }
277                 }, pageRef)));
278                 authProfileModal.header(new Model<>(getString("googleMfaAuthTokens", model)));
279                 authProfileModal.show(true);
280             }
281         }, ActionLink.ActionType.EDIT_APPROVAL, AMEntitlement.AUTH_PROFILE_UPDATE);
282 
283         panel.add(new ActionLink<>() {
284 
285             private static final long serialVersionUID = -3722207913631435501L;
286 
287             @Override
288             public void onClick(final AjaxRequestTarget target, final AuthProfileTO ignore) {
289                 model.setObject(restClient.read(model.getObject().getKey()));
290                 target.add(authProfileModal.setContent(new ModalDirectoryPanel<>(
291                         authProfileModal,
292                         new AuthProfileItemDirectoryPanel<GoogleMfaAuthAccount>(
293                                 "panel", restClient, authProfileModal, model.getObject(), pageRef) {
294 
295                     private static final long serialVersionUID = -670769282358547044L;
296 
297                     @Override
298                     protected List<GoogleMfaAuthAccount> getItems() {
299                         return model.getObject().getGoogleMfaAuthAccounts();
300                     }
301 
302                     @Override
303                     protected GoogleMfaAuthAccount defaultItem() {
304                         return new GoogleMfaAuthAccount();
305                     }
306 
307                     @Override
308                     protected String sortProperty() {
309                         return "id";
310                     }
311 
312                     @Override
313                     protected String paginatorRowsKey() {
314                         return AMConstants.PREF_AUTHPROFILE_GOOGLEMFAAUTHACCOUNTS_PAGINATOR_ROWS;
315                     }
316 
317                     @Override
318                     protected List<IColumn<GoogleMfaAuthAccount, String>> getColumns() {
319                         List<IColumn<GoogleMfaAuthAccount, String>> columns = new ArrayList<>();
320                         columns.add(new PropertyColumn<>(new ResourceModel("id"), "id", "id"));
321                         columns.add(new DatePropertyColumn<>(
322                                 new ResourceModel("registrationDate"), "registrationDate", "registrationDate"));
323                         columns.add(new PropertyColumn<>(new ResourceModel("name"), "name", "name"));
324                         return columns;
325                     }
326                 }, pageRef)));
327                 authProfileModal.header(new Model<>(getString("googleMfaAuthAccounts", model)));
328                 authProfileModal.show(true);
329             }
330         }, ActionLink.ActionType.EXECUTE, AMEntitlement.AUTH_PROFILE_UPDATE);
331 
332         panel.add(new ActionLink<>() {
333 
334             private static final long serialVersionUID = -3722207913631435501L;
335 
336             @Override
337             public void onClick(final AjaxRequestTarget target, final AuthProfileTO ignore) {
338                 model.setObject(restClient.read(model.getObject().getKey()));
339                 target.add(authProfileModal.setContent(new ModalDirectoryPanel<>(
340                         authProfileModal,
341                         new AuthProfileItemDirectoryPanel<U2FDevice>(
342                                 "panel", restClient, authProfileModal, model.getObject(), pageRef) {
343 
344                     private static final long serialVersionUID = 5788448799796630011L;
345 
346                     @Override
347                     protected List<U2FDevice> getItems() {
348                         return model.getObject().getU2FRegisteredDevices();
349                     }
350 
351                     @Override
352                     protected U2FDevice defaultItem() {
353                         return new U2FDevice();
354                     }
355 
356                     @Override
357                     protected String sortProperty() {
358                         return "id";
359                     }
360 
361                     @Override
362                     protected String paginatorRowsKey() {
363                         return AMConstants.PREF_AUTHPROFILE_U2FDEVICES_PAGINATOR_ROWS;
364                     }
365 
366                     @Override
367                     protected List<IColumn<U2FDevice, String>> getColumns() {
368                         List<IColumn<U2FDevice, String>> columns = new ArrayList<>();
369                         columns.add(new PropertyColumn<>(new ResourceModel("id"), "id", "id"));
370                         columns.add(new DatePropertyColumn<>(
371                                 new ResourceModel("issueDate"), "issueDate", "issueDate"));
372                         columns.add(new PropertyColumn<>(new ResourceModel("record"), "record", "record"));
373                         return columns;
374                     }
375                 }, pageRef)));
376                 authProfileModal.header(new Model<>(getString("u2fRegisteredDevices", model)));
377                 authProfileModal.show(true);
378             }
379         }, ActionLink.ActionType.DEPROVISION, AMEntitlement.AUTH_PROFILE_UPDATE);
380 
381         panel.add(new ActionLink<>() {
382 
383             private static final long serialVersionUID = -3722207913631435501L;
384 
385             @Override
386             public void onClick(final AjaxRequestTarget target, final AuthProfileTO ignore) {
387                 model.setObject(restClient.read(model.getObject().getKey()));
388                 target.add(authProfileModal.setContent(new ModalDirectoryPanel<>(
389                         authProfileModal,
390                         new AuthProfileItemDirectoryPanel<MfaTrustedDevice>(
391                                 "panel", restClient, authProfileModal, model.getObject(), pageRef) {
392 
393                     private static final long serialVersionUID = 5788448799796630011L;
394 
395                     @Override
396                     protected List<MfaTrustedDevice> getItems() {
397                         return model.getObject().getMfaTrustedDevices();
398                     }
399 
400                     @Override
401                     protected MfaTrustedDevice defaultItem() {
402                         return new MfaTrustedDevice();
403                     }
404 
405                     @Override
406                     protected String sortProperty() {
407                         return "id";
408                     }
409 
410                     @Override
411                     protected String paginatorRowsKey() {
412                         return AMConstants.PREF_AUTHPROFILE_MFA_TRUSTED_FDEVICES_PAGINATOR_ROWS;
413                     }
414 
415                     @Override
416                     protected List<IColumn<MfaTrustedDevice, String>> getColumns() {
417                         List<IColumn<MfaTrustedDevice, String>> columns = new ArrayList<>();
418                         columns.add(new PropertyColumn<>(new ResourceModel("id"), "id", "id"));
419                         columns.add(new PropertyColumn<>(new ResourceModel("name"), "name", "name"));
420                         columns.add(new DatePropertyColumn<>(
421                                 new ResourceModel("recordDate"), "recordDate", "recordDate"));
422                         columns.add(new DatePropertyColumn<>(
423                                 new ResourceModel("expirationDate"), "expirationDate", "expirationDate"));
424                         return columns;
425                     }
426                 }, pageRef)));
427                 authProfileModal.header(new Model<>(getString("mfaTrustedDevices", model)));
428                 authProfileModal.show(true);
429             }
430         }, ActionLink.ActionType.DOWN, AMEntitlement.AUTH_PROFILE_UPDATE);
431 
432         panel.add(new ActionLink<>() {
433 
434             private static final long serialVersionUID = -3722207913631435501L;
435 
436             @Override
437             public void onClick(final AjaxRequestTarget target, final AuthProfileTO ignore) {
438                 model.setObject(restClient.read(model.getObject().getKey()));
439                 target.add(authProfileModal.setContent(new ModalDirectoryPanel<>(
440                         authProfileModal,
441                         new AuthProfileItemDirectoryPanel<WebAuthnDeviceCredential>(
442                                 "panel", restClient, authProfileModal, model.getObject(), pageRef) {
443 
444                     private static final long serialVersionUID = 6820212423488933184L;
445 
446                     @Override
447                     protected List<WebAuthnDeviceCredential> getItems() {
448                         return model.getObject().getWebAuthnDeviceCredentials();
449                     }
450 
451                     @Override
452                     protected WebAuthnDeviceCredential defaultItem() {
453                         return new WebAuthnDeviceCredential();
454                     }
455 
456                     @Override
457                     protected String sortProperty() {
458                         return "identifier";
459                     }
460 
461                     @Override
462                     protected String paginatorRowsKey() {
463                         return AMConstants.PREF_AUTHPROFILE_WEBAUTHNDEVICECREDENTIALS_PAGINATOR_ROWS;
464                     }
465 
466                     @Override
467                     protected List<IColumn<WebAuthnDeviceCredential, String>> getColumns() {
468                         List<IColumn<WebAuthnDeviceCredential, String>> columns = new ArrayList<>();
469                         columns.add(new PropertyColumn<>(
470                                 new ResourceModel("identifier"), "identifier", "identifier"));
471                         columns.add(new PropertyColumn<>(
472                                 new ResourceModel("json"), "json", "json"));
473                         return columns;
474                     }
475                 }, pageRef)));
476                 authProfileModal.header(new Model<>(getString("webAuthnDeviceCredentials", model)));
477                 authProfileModal.show(true);
478             }
479         }, ActionLink.ActionType.HTML, AMEntitlement.AUTH_PROFILE_UPDATE);
480 
481         panel.add(new ActionLink<>() {
482 
483             private static final long serialVersionUID = -3722207913631435501L;
484 
485             @Override
486             public void onClick(final AjaxRequestTarget target, final AuthProfileTO ignore) {
487                 try {
488                     restClient.delete(model.getObject().getKey());
489 
490                     SyncopeConsoleSession.get().success(getString(Constants.OPERATION_SUCCEEDED));
491                     target.add(container);
492                 } catch (Exception e) {
493                     LOG.error("While deleting {}", model.getObject().getKey(), e);
494                     SyncopeConsoleSession.get().onException(e);
495                 }
496                 ((BaseWebPage) pageRef.getPage()).getNotificationPanel().refresh(target);
497             }
498         }, ActionLink.ActionType.DELETE, AMEntitlement.AUTH_PROFILE_DELETE, true);
499 
500         return panel;
501     }
502 
503     protected final class AuthProfileProvider extends DirectoryDataProvider<AuthProfileTO> {
504 
505         private static final long serialVersionUID = -185944053385660794L;
506 
507         private AuthProfileProvider(final int paginatorRows) {
508             super(paginatorRows);
509             setSort("owner", SortOrder.ASCENDING);
510         }
511 
512         @Override
513         public Iterator<AuthProfileTO> iterator(final long first, final long count) {
514             int page = ((int) first / paginatorRows);
515             return restClient.list((page < 0 ? 0 : page) + 1, paginatorRows).iterator();
516         }
517 
518         @Override
519         public long size() {
520             return restClient.count();
521         }
522 
523         @Override
524         public IModel<AuthProfileTO> model(final AuthProfileTO object) {
525             return new CompoundPropertyModel<>(object);
526         }
527     }
528 
529     private class CreateAuthProfileWizardBuilder extends AuthProfileWizardBuilder<AuthProfileTO> {
530 
531         private static final long serialVersionUID = -2478221092672979490L;
532 
533         private class NewAuthProfileStep extends AuthProfileWizardBuilder<AuthProfileTO>.Step {
534 
535             private static final long serialVersionUID = 6290450377240300418L;
536 
537             NewAuthProfileStep(final AuthProfileTO modelObject) {
538                 super(modelObject);
539 
540                 AjaxTextFieldPanel owner = new AjaxTextFieldPanel(
541                         "bean", "owner", new PropertyModel<>(modelObject, "owner"));
542                 owner.addRequiredLabel();
543                 addOrReplace(owner);
544             }
545         }
546 
547         CreateAuthProfileWizardBuilder(final PageReference pageRef) {
548             super(new AuthProfileTO(), new StepModel<>(), pageRef);
549         }
550 
551         @Override
552         protected WizardModel buildModelSteps(final AuthProfileTO modelObject, final WizardModel wizardModel) {
553             wizardModel.add(new NewAuthProfileStep(modelObject));
554             return wizardModel;
555         }
556 
557         @Override
558         protected Serializable onApplyInternal(final AuthProfileTO modelObject) {
559             restClient.create(modelObject);
560             return modelObject;
561         }
562     }
563 }