/* (c) 2014 - 2016 Open Source Geospatial Foundation - all rights reserved * (c) 2001 - 2013 OpenPlans * This code is licensed under the GPL 2.0 license, available at the root * application directory. */ package org.geoserver.security.web; import static org.geoserver.security.web.SecurityNamedServiceProvider.NAME; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import org.apache.wicket.Component; import org.apache.wicket.ajax.AjaxRequestTarget; import org.apache.wicket.ajax.markup.html.AjaxLink; import org.apache.wicket.markup.html.basic.Label; import org.apache.wicket.markup.html.panel.FeedbackPanel; import org.apache.wicket.markup.html.panel.Panel; import org.apache.wicket.model.IModel; import org.apache.wicket.model.ResourceModel; import org.geoserver.security.GeoServerSecurityManager; import org.geoserver.security.config.SecurityNamedServiceConfig; import org.geoserver.security.validation.SecurityConfigException; import org.geoserver.security.web.SecurityNamedServiceProvider.ResourceBeanProperty; import org.geoserver.web.GeoServerApplication; import org.geoserver.web.wicket.GeoServerDataProvider.Property; import org.geoserver.web.wicket.GeoServerDialog; import org.geoserver.web.wicket.GeoServerTablePanel; import org.geoserver.web.wicket.SimpleAjaxLink; /** * Base class for listing all instances of a class of named security service type. * <p> * This base panel provides a table of the configuration instances, along with "add new" and * "remove selected" functionality. Subclasses need to provide the specific type of * {@link SecurityNamedServiceProvider} for the service class, as well as implement some additional * methods for validating the removal of configuration instances. * </p> * @author Justin Deoliveira, OpenGeo * */ public abstract class SecurityNamedServicesPanel<T extends SecurityNamedServiceConfig> extends Panel{ SecurityNamedServiceTablePanel<T> tablePanel; AjaxLink removeLink; FeedbackPanel feedbackPanel; GeoServerDialog dialog; public SecurityNamedServicesPanel(String id, SecurityNamedServiceProvider<T> dataProvider) { super(id); final boolean isAdmin = getSecurityManager().checkAuthenticationForAdminRole(); add(new AjaxLink("add") { @Override public void onClick(AjaxRequestTarget target) { //create a new config class and instantiate the page SecurityNamedServiceNewPage newPage = new SecurityNamedServiceNewPage(getServiceClass()); newPage.setReturnPage(getPage()); setResponsePage(newPage); } }.setEnabled(isAdmin)); add(removeLink = new AjaxLink("remove") { @Override public void onClick(AjaxRequestTarget target) { boolean ok = true; for (T config : tablePanel.getSelection()) { //first determine if the config can be removed try { validateRemoveConfig(config); } catch (Exception e) { handleException(e, null); ok = false; } } if (ok) { //proceed with the removal, confirming first dialog.showOkCancel(target, new GeoServerDialog.DialogDelegate() { @Override protected Component getContents(String id) { return new ConfirmRemovalNamedServicePanel(id, tablePanel.getSelection()); } @Override protected boolean onSubmit(AjaxRequestTarget target, Component contents) { for (T config : tablePanel.getSelection()) { try { removeConfig(config); feedbackPanel.info(config.getName() + " removed"); tablePanel.clearSelection(); } catch (Exception e) { handleException(e, feedbackPanel); } } return true; } @Override public void onClose(AjaxRequestTarget target) { target.add(tablePanel); target.add(feedbackPanel); } }); } //render any feedback target.add(feedbackPanel); } }); removeLink.setEnabled(false); add(tablePanel = new SecurityNamedServiceTablePanel<T>("table", dataProvider) { @Override protected void onSelectionUpdate(AjaxRequestTarget target) { if (isAdmin) { target.add(removeLink.setEnabled(!getSelection().isEmpty())); } } }); tablePanel.setOutputMarkupId(true); tablePanel.getTopPager().setVisible(false); add(tablePanel); add(feedbackPanel = new FeedbackPanel("feedback")); feedbackPanel.setOutputMarkupId(true); add(dialog = new GeoServerDialog("dialog")); } /** * accessors for security manager */ public GeoServerSecurityManager getSecurityManager() { return GeoServerApplication.get().getSecurityManager(); } /* * helper for handling an exception by reporting it as an error on the feedback panel */ void handleException(Exception e, Component target) { (target != null ? target : getPage()).error(e); } /** * Create a new configuration object. */ protected abstract Class getServiceClass(); /** * Do pre validation before a configuration object is removed. */ protected abstract void validateRemoveConfig(T config) throws SecurityConfigException; /** * Remove configuration. */ protected abstract void removeConfig(T config) throws Exception; SecurityNamedServicePanelInfo lookupPageInfo(SecurityNamedServiceConfig config) { Class serviceClass = null; try { serviceClass = Class.forName(config.getClassName()); } catch (ClassNotFoundException e) { throw new RuntimeException(e); } List<SecurityNamedServicePanelInfo> pageInfos = new ArrayList(); for (SecurityNamedServicePanelInfo pageInfo : GeoServerApplication.get().getBeansOfType(SecurityNamedServicePanelInfo.class)) { if (pageInfo.getServiceClass().isAssignableFrom(serviceClass)) { pageInfos.add(pageInfo); } } if (pageInfos.isEmpty()) { throw new RuntimeException("Unable to find page info for service config: " + config + ", service class: " + serviceClass); } if (pageInfos.size() > 1) { //filter by strict equals List<SecurityNamedServicePanelInfo> l = new ArrayList(pageInfos); for (Iterator<SecurityNamedServicePanelInfo> it = l.iterator(); it.hasNext();) { if (!it.next().getServiceClass().equals(serviceClass)) { it.remove(); } } if (l.size() == 1) { //filter down to one match return l.get(0); } throw new RuntimeException("Found multiple page infos for service config: " + config + ", service class: " + serviceClass); } //found just one return pageInfos.get(0); } void goToPage(SecurityNamedServicePanelInfo pageInfo, IModel model) { //instantiate the page try { AbstractSecurityPage editPage = (AbstractSecurityPage) pageInfo .getComponentClass().getConstructor(IModel.class).newInstance(model); editPage.setReturnPage(getPage()); setResponsePage(editPage); } catch (Exception e) { throw new RuntimeException( "Unable to create page for page info: " + pageInfo, e); } } class SecurityNamedServiceTablePanel<T extends SecurityNamedServiceConfig> extends GeoServerTablePanel<T> { public SecurityNamedServiceTablePanel(String id, SecurityNamedServiceProvider<T> dataProvider) { super(id, dataProvider, true); } @Override protected Component getComponentForProperty(String id, IModel<T> itemModel, Property<T> property) { if (property == NAME) { return createEditLink(id, itemModel, property); } if (property instanceof ResourceBeanProperty) { Object val = property.getModel(itemModel).getObject(); if (val != null) { return new Label(id, new ResourceModel(val.toString(), val.toString())); } } //backback to just a label return new Label(id, property.getModel(itemModel)); } Component createEditLink(String id, final IModel model, Property<T> property) { return new SimpleAjaxLink(id, property.getModel(model)) { @Override protected void onClick(AjaxRequestTarget target) { SecurityNamedServiceEditPage<T> editPage = new SecurityNamedServiceEditPage<T>(model); editPage.setReturnPage(getPage()); setResponsePage(editPage); } }; } } protected void onBeforeRender() { tablePanel.clearSelection(); removeLink.setEnabled(false); super.onBeforeRender(); }; }