/* (c) 2015 - 2016 Open Source Geospatial Foundation - all rights reserved
* This code is licensed under the GPL 2.0 license, available at the root
* application directory.
*/
package org.geoserver.geofence.web;
import java.io.IOException;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.logging.Level;
import com.vividsolutions.jts.geom.Geometry;
import com.vividsolutions.jts.geom.GeometryFactory;
import com.vividsolutions.jts.geom.MultiPolygon;
import com.vividsolutions.jts.geom.Polygon;
import com.vividsolutions.jts.io.WKTReader;
import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.ajax.form.OnChangeAjaxBehavior;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.html.form.*;
import org.apache.wicket.markup.html.link.BookmarkablePageLink;
import org.apache.wicket.model.CompoundPropertyModel;
import org.apache.wicket.model.ResourceModel;
import org.geoserver.catalog.Predicates;
import org.geoserver.catalog.ResourceInfo;
import org.geoserver.catalog.WorkspaceInfo;
import org.geoserver.catalog.util.CloseableIterator;
import org.geoserver.geofence.core.model.RuleLimits;
import org.geoserver.geofence.core.model.enums.GrantType;
import org.geoserver.geofence.services.dto.ShortRule;
import org.geoserver.platform.GeoServerExtensions;
import org.geoserver.platform.Service;
import org.geoserver.platform.exception.GeoServerRuntimException;
import org.geoserver.security.GeoServerRoleService;
import org.geoserver.security.GeoServerSecurityManager;
import org.geoserver.security.impl.GeoServerRole;
import org.geoserver.security.impl.GeoServerUser;
import org.geoserver.web.GeoServerApplication;
import org.geoserver.web.GeoServerSecuredPage;
import org.geoserver.web.wicket.ParamResourceModel;
import org.geotools.factory.CommonFactoryFinder;
import org.opengis.filter.sort.SortOrder;
import org.opengis.filter.FilterFactory2;
import org.springframework.dao.DuplicateKeyException;
/**
*
* Internal Geofence Rule Page
*
* @author Niels Charlier
*
*/
public class GeofenceRulePage extends GeoServerSecuredPage {
private static final long serialVersionUID = -3986495664060319256L;
private class RuleFormData implements Serializable {
private static final long serialVersionUID = 3045099348340468123L;
ShortRule rule;
RuleLimits ruleLimits;
String allowedArea;
}
protected DropDownChoice<String> userChoice, roleChoice, serviceChoice, requestChoice, workspaceChoice, layerChoice, accessChoice;
protected DropDownChoice<GrantType> grantTypeChoice;
protected TextArea<String> allowedArea;
protected Label allowedAreaLabel;
public GeofenceRulePage(final ShortRule rule, final GeofenceRulesModel rules) {
RuleFormData ruleFormData = new RuleFormData();
ruleFormData.rule = rule;
final RuleLimits ruleLimits = rules.getRulesLimits(rule.getId());
if (ruleLimits == null) {
ruleFormData.ruleLimits = new RuleLimits();
} else {
ruleFormData.ruleLimits = ruleLimits;
ruleFormData.allowedArea = getAllowedAreaAsString(ruleLimits);
}
CompoundPropertyModel<RuleFormData> ruleFormModel = new CompoundPropertyModel<RuleFormData>(ruleFormData);
// build the form
final Form<RuleFormData> form = new Form<RuleFormData>("form", ruleFormModel);
add(form);
form.add(new TextField<>("priority", ruleFormModel.bind("rule.priority")).setRequired(true));
form.add(roleChoice = new DropDownChoice<>("roleName", ruleFormModel.bind("rule.roleName"), getRoleNames()));
roleChoice.add(new OnChangeAjaxBehavior() {
private static final long serialVersionUID = -2880886409750911044L;
@Override
protected void onUpdate(AjaxRequestTarget target) {
userChoice.setChoices(getUserNames(roleChoice.getConvertedInput()));
form.getModelObject().rule.setUserName(null);
userChoice.modelChanged();
target.add(userChoice);
}
});
roleChoice.setNullValid(true);
form.add(userChoice = new DropDownChoice<>("userName", ruleFormModel.bind("rule.userName"), getUserNames(rule.getRoleName())));
userChoice.setOutputMarkupId(true);
userChoice.setNullValid(true);
form.add(serviceChoice = new DropDownChoice<>("service", ruleFormModel.bind("rule.service"), getServiceNames()));
serviceChoice.add(new OnChangeAjaxBehavior() {
private static final long serialVersionUID = -5925784823433092831L;
@Override
protected void onUpdate(AjaxRequestTarget target) {
requestChoice.setChoices(getOperationNames(serviceChoice.getConvertedInput()));
form.getModelObject().rule.setRequest(null);
requestChoice.modelChanged();
target.add(requestChoice);
}
});
serviceChoice.setNullValid(true);
form.add(requestChoice = new DropDownChoice<>("request", ruleFormModel.bind("rule.request"),
getOperationNames(rule.getService()), new CaseConversionRenderer()));
requestChoice.setOutputMarkupId(true);
requestChoice.setNullValid(true);
form.add(workspaceChoice = new DropDownChoice<>("workspace", ruleFormModel.bind("rule.workspace"),
getWorkspaceNames()));
workspaceChoice.add(new OnChangeAjaxBehavior() {
private static final long serialVersionUID = 732177308220189475L;
@Override
protected void onUpdate(AjaxRequestTarget target) {
layerChoice.setChoices(getLayerNames(workspaceChoice.getConvertedInput()));
form.getModelObject().rule.setLayer(null);
layerChoice.modelChanged();
target.add(layerChoice);
}
});
workspaceChoice.setNullValid(true);
form.add(layerChoice = new DropDownChoice<>("layer", ruleFormModel.bind("rule.layer"), getLayerNames(rule.getWorkspace())));
layerChoice.setOutputMarkupId(true);
layerChoice.setNullValid(true);
form.add(grantTypeChoice = new DropDownChoice<>("access", ruleFormModel.bind("rule.access"),
Arrays.asList(GrantType.values()), new GrantTypeRenderer()));
grantTypeChoice.setRequired(true);
grantTypeChoice.add(new OnChangeAjaxBehavior() {
private static final long serialVersionUID = -4302901248019983282L;
@Override
protected void onUpdate(AjaxRequestTarget target) {
if (grantTypeChoice.getConvertedInput().equals(GrantType.LIMIT)) {
allowedAreaLabel.setVisible(true);
allowedArea.setVisible(true);
} else {
allowedAreaLabel.setVisible(false);
allowedArea.setVisible(false);
}
target.add(allowedAreaLabel);
target.add(allowedArea);
}
});
form.add(allowedAreaLabel = new Label("allowedAreaLabel", new ResourceModel("allowedArea", "Allow area")));
allowedAreaLabel.setVisible(form.getModelObject().rule.getAccess() != null
&& form.getModelObject().rule.getAccess().equals(GrantType.LIMIT));
allowedAreaLabel.setOutputMarkupId(true);
allowedAreaLabel.setOutputMarkupPlaceholderTag(true);
form.add(allowedArea = new TextArea<>("allowedArea", ruleFormModel.bind("allowedArea")));
allowedArea.setConvertedInput(form.getModelObject().allowedArea);
allowedArea.setVisible(form.getModelObject().rule.getAccess() != null
&& form.getModelObject().rule.getAccess().equals(GrantType.LIMIT));
allowedArea.setOutputMarkupId(true);
allowedArea.setOutputMarkupPlaceholderTag(true);
// build the submit/cancel
form.add(new SubmitLink("save") {
private static final long serialVersionUID = 3735176778941168701L;
@Override
public void onSubmit() {
RuleFormData ruleFormData = (RuleFormData) getForm().getModelObject();
try {
rules.save(ruleFormData.rule);
if (ruleFormData.rule.getAccess().equals(GrantType.LIMIT)) {
rules.save(ruleFormData.rule.getId(), parseAllowedArea(ruleFormData.allowedArea));
}
doReturn(GeofenceServerPage.class);
} catch (DuplicateKeyException e) {
error(new ResourceModel("GeofenceRulePage.duplicate").getObject());
} catch (Exception e){
error(e);
}
}
});
form.add(new BookmarkablePageLink<ShortRule>("cancel", GeofenceServerPage.class));
}
private String getAllowedAreaAsString(RuleLimits ruleLimits) {
if (ruleLimits == null || ruleLimits.getAllowedArea() == null) {
return "";
}
MultiPolygon multiPolygon = ruleLimits.getAllowedArea();
return "SRID=" + multiPolygon.getSRID() + ";" + multiPolygon.toText();
}
private MultiPolygon parseAllowedArea(String allowedArea) {
if (allowedArea == null || allowedArea.isEmpty()) {
return null;
}
String[] allowedAreaParts = allowedArea.split(";");
if (allowedAreaParts.length != 2) {
throw new GeoServerRuntimException(String.format(
"Invalid allowed area '%s' expecting SRID=<CODE>;<WKT>.", allowedArea));
}
Integer srid;
Geometry geometry;
try {
srid = Integer.valueOf(allowedAreaParts[0].split("=")[1]);
geometry = new WKTReader().read(allowedAreaParts[1]);
} catch (Exception exception) {
String message = String.format(
"Error parsing SRID '%s' or WKT geometry '%s' expecting SRID=<CODE>;<WKT>.",
allowedAreaParts[0], allowedAreaParts[1]);
LOGGER.log(Level.WARNING, message, exception);
throw new GeoServerRuntimException(message, exception);
}
MultiPolygon multiPolygon = castToMultiPolygon(geometry);
multiPolygon.setSRID(srid);
return multiPolygon;
}
private MultiPolygon castToMultiPolygon(Geometry geometry) {
if (geometry instanceof MultiPolygon) {
return (MultiPolygon) geometry;
}
if (geometry instanceof Polygon) {
return new MultiPolygon(new Polygon[]{(Polygon) geometry}, new GeometryFactory());
}
throw new GeoServerRuntimException(String.format(
"Invalid geometry of type '%s' expect a Polygon or MultiPolygon.", geometry.getClass().getSimpleName()));
}
/**
* Returns a sorted list of workspace names
*/
protected List<String> getWorkspaceNames() {
SortedSet<String> resultSet = new TreeSet<String>();
for (WorkspaceInfo ws : getCatalog().getFacade().getWorkspaces()) {
resultSet.add(ws.getName());
}
return new ArrayList<String>(resultSet);
}
/**
* Returns a sorted list of layer names in the specified workspace (or * if the workspace is *)
*/
protected List<String> getLayerNames(String workspaceName) {
List<String> resultSet = new ArrayList<String>();
if (workspaceName != null) {
FilterFactory2 ff = CommonFactoryFinder.getFilterFactory2();
try(CloseableIterator<ResourceInfo> it =
getCatalog().getFacade().list(ResourceInfo.class,
Predicates.equal("store.workspace.name", workspaceName),
null, null, ff.sort("name", SortOrder.ASCENDING)))
{
while(it.hasNext()) {
resultSet.add(it.next().getName());
}
}
}
return resultSet;
}
/**
* Returns a sorted list of workspace names
*/
protected List<String> getServiceNames() {
SortedSet<String> resultSet = new TreeSet<String>();
for (Service ows : GeoServerExtensions.extensions(Service.class)) {
resultSet.add(ows.getId().toUpperCase());
}
return new ArrayList<String>(resultSet);
}
/**
* Returns a sorted list of operation names in the specified service (or * if the workspace is *)
*/
protected List<String> getOperationNames(String serviceName) {
SortedSet<String> resultSet = new TreeSet<String>();
boolean flag = true;
if (serviceName != null) {
for (Service ows : GeoServerExtensions.extensions(Service.class)) {
if (serviceName.equalsIgnoreCase(ows.getId()) && flag) {
flag = false;
resultSet.addAll(ows.getOperations());
}
}
}
return new ArrayList<String>(resultSet);
}
protected List<String> getRoleNames() {
SortedSet<String> resultSet = new TreeSet<String>();
try {
for (GeoServerRole role : securityManager().getRolesForAccessControl()) {
resultSet.add(role.getAuthority());
}
} catch (IOException e) {
LOGGER.log(Level.WARNING, e.getLocalizedMessage(), e);
}
return new ArrayList<String>(resultSet);
}
protected List<String> getUserNames(String roleName) {
SortedSet<String> resultSet = new TreeSet<String>();
GeoServerSecurityManager securityManager = securityManager();
try {
if (roleName == null) {
for (String serviceName : securityManager.listUserGroupServices()) {
for (GeoServerUser user : securityManager.loadUserGroupService(serviceName).getUsers()) {
resultSet.add(user.getUsername());
}
}
} else {
for (String serviceName : securityManager.listRoleServices()) {
GeoServerRoleService roleService = securityManager.loadRoleService(serviceName);
GeoServerRole role = roleService.getRoleByName(roleName);
if (role != null) {
resultSet.addAll(roleService.getUserNamesForRole(role));
}
}
}
} catch (IOException e) {
LOGGER.log(Level.WARNING, e.getLocalizedMessage(), e);
}
return new ArrayList<String>(resultSet);
}
/**
* Makes sure we see translated text, by the raw name is used for the model
*/
protected class GrantTypeRenderer extends ChoiceRenderer<GrantType> {
private static final long serialVersionUID = -7478943956804313995L;
public Object getDisplayValue(GrantType object) {
return (String) new ParamResourceModel( object.name(), getPage())
.getObject();
}
public String getIdValue(GrantType object, int index) {
return object.name();
}
}
/**
* Makes sure that while rendered in mixed case, is stored in uppercase
*/
protected class CaseConversionRenderer extends ChoiceRenderer<String> {
private static final long serialVersionUID = 4238195087731806209L;
public Object getDisplayValue(String object) {
return object;
}
public String getIdValue(String object, int index) {
return object.toUpperCase();
}
}
protected GeoServerSecurityManager securityManager() {
return GeoServerApplication.get().getSecurityManager();
}
}