/*---------------- FILE HEADER ------------------------------------------
This file is part of deegree.
Copyright (C) 2001-2006 by:
EXSE, Department of Geography, University of Bonn
http://www.giub.uni-bonn.de/deegree/
lat/lon GmbH
http://www.lat-lon.de
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Contact:
Andreas Poth
lat/lon GmbH
Aennchenstr. 19
53177 Bonn
Germany
E-Mail: poth@lat-lon.de
Prof. Dr. Klaus Greve
Department of Geography
University of Bonn
Meckenheimer Allee 166
53115 Bonn
Germany
E-Mail: greve@giub.uni-bonn.de
---------------------------------------------------------------------------*/
package org.deegree.portal.standard.security.control;
import java.io.StringReader;
import java.util.HashMap;
import java.util.Map;
import org.deegree.enterprise.control.AbstractListener;
import org.deegree.enterprise.control.FormEvent;
import org.deegree.enterprise.control.RPCException;
import org.deegree.enterprise.control.RPCMember;
import org.deegree.enterprise.control.RPCMethodCall;
import org.deegree.enterprise.control.RPCParameter;
import org.deegree.enterprise.control.RPCStruct;
import org.deegree.enterprise.control.RPCWebEvent;
import org.deegree.framework.log.ILogger;
import org.deegree.framework.log.LoggerFactory;
import org.deegree.framework.xml.XMLTools;
import org.deegree.model.filterencoding.AbstractFilter;
import org.deegree.model.filterencoding.Filter;
import org.deegree.security.GeneralSecurityException;
import org.deegree.security.drm.SecurityAccessManager;
import org.deegree.security.drm.SecurityTransaction;
import org.deegree.security.drm.model.Right;
import org.deegree.security.drm.model.RightType;
import org.deegree.security.drm.model.Role;
import org.deegree.security.drm.model.SecuredObject;
import org.deegree.security.drm.model.User;
import org.w3c.dom.Document;
/**
* This <code>Listener</code> reacts on RPC-StoreRights events.
*
* Access constraints:
* <ul>
* <li>only users that have the 'SEC_ADMIN'-role are allowed
* </ul>
*
* @author <a href="mschneider@lat-lon.de">Markus Schneider </a>
*/
public class StoreRightsListener extends AbstractListener {
private static final ILogger LOG = LoggerFactory.getLogger( StoreRightsListener.class );
private static final String MINX = "-180.0";
private static final String MINY = "-90.0";
private static final String MAXX = "180.0";
private static final String MAXY = "90.0";
public void actionPerformed(FormEvent event) {
// the Role for which the rights are to be set
int roleId = -1;
// array of ints, ids of Layers (SecuredObjects) for which
// the Role has access rights
int[] layers = null;
// corresponding maps of key (PropertyName) / value-pairs that
// constitute access constraints
Map[] layerConstraints = null;
// array of ints, ids of FeatureTypes (SecuredObjects) for which
// the Role has access rights
int[] featureTypes = null;
SecurityAccessManager manager = null;
SecurityTransaction transaction = null;
try {
RPCWebEvent ev = (RPCWebEvent) event;
RPCMethodCall rpcCall = ev.getRPCMethodCall();
RPCParameter[] params = rpcCall.getParameters();
if (params.length != 3) {
throw new RPCException(
"Invalid RPC. Exactly three 'param' elements below 'params' are required.");
}
if (!(params[0].getValue() instanceof String)) {
throw new RPCException(
"Invalid RPC. First 'param' must contain a 'string'-value element.");
}
// extract role-id
try {
roleId = Integer.parseInt((String) params[0].getValue());
} catch (NumberFormatException e) {
throw new RPCException(
"Invalid RPC. Role must be specified by a valid integer.");
}
// extract Layer rights
if (!(params[1].getValue() instanceof RPCParameter[])) {
throw new RPCException(
"Invalid RPC. Second 'param' must contain an 'array' element.");
}
RPCParameter[] layerParams = (RPCParameter[]) params[1].getValue();
layers = new int[layerParams.length];
layerConstraints = new Map[layerParams.length];
for (int i = 0; i < layerParams.length; i++) {
// is the layer access constrained?
if (layerParams[i].getValue() instanceof RPCParameter[]) {
layerConstraints[i] = new HashMap();
RPCParameter[] constrainParams = (RPCParameter[]) layerParams[i]
.getValue();
try {
layers[i] = Integer.parseInt((String) constrainParams[0].getValue());
} catch (NumberFormatException e) {
throw new RPCException(
"Invalid RPC. Layers must be specified by valid integers (their ids).");
}
RPCParameter param = constrainParams[1];
RPCStruct constraints = (RPCStruct) param.getValue();
RPCMember[] members = constraints.getMembers();
for (int j = 0; j < members.length; j++) {
String propertyName = members[j].getName();
Object value = members[j].getValue();
if (value instanceof RPCParameter[]) {
String[] values = new String[((RPCParameter[]) value).length];
for (int k = 0; k < values.length; k++) {
values[k] = (String) ((RPCParameter[]) value)[k]
.getValue();
}
layerConstraints[i].put(propertyName, values);
} else if (value instanceof String) {
layerConstraints[i].put(propertyName, value);
} else {
throw new RPCException(
"Invalid RPC. Access constraints either be specified by string- or by array-values.");
}
}
} else if (layerParams[i].getValue() instanceof String) {
try {
layers[i] = Integer.parseInt((String) layerParams[i]
.getValue());
} catch (NumberFormatException e) {
throw new RPCException(
"Invalid RPC. Rights must be specified by valid integers.");
}
} else {
throw new RPCException(
"Invalid RPC. Rights must be specified using string elements or arrays (if constrains are given).");
}
}
// extract FeatureType rights
if (!(params[2].getValue() instanceof RPCParameter[])) {
throw new RPCException(
"Invalid RPC. Third 'param' must contain an 'array' element.");
}
RPCParameter[] featureTypeParams = (RPCParameter[]) params[2]
.getValue();
featureTypes = new int[featureTypeParams.length];
for (int i = 0; i < featureTypeParams.length; i++) {
if (!(featureTypeParams[i].getValue() instanceof String)) {
throw new RPCException(
"Invalid RPC. Rights must be specified in string elements.");
}
try {
featureTypes[i] = Integer.parseInt((String) featureTypeParams[i].getValue());
} catch (NumberFormatException e) {
throw new RPCException(
"Invalid RPC. Rights must be specified by valid integers.");
}
}
transaction = SecurityHelper.acquireTransaction(this);
SecurityHelper.checkForAdminRole(transaction);
manager = SecurityAccessManager.getInstance();
User user = transaction.getUser();
Role role = transaction.getRoleById(roleId);
// perform access check
if (!user.hasRight(transaction, "update", role)) {
getRequest().setAttribute("SOURCE", this.getClass().getName());
getRequest().setAttribute( "MESSAGE",
"Die Änderungen konnten nicht gespeichert werden, "
+ "da Sie keine Berechtigung zur Modifikation der Rolle '"
+ role.getName() + "' haben.");
setNextPage("error.jsp");
return;
}
// set/delete access rights for Layers
SecuredObject[] presentLayers = transaction.getAllSecuredObjects(ClientHelper.TYPE_LAYER);
for (int i = 0; i < presentLayers.length; i++) {
boolean isAccessible = false;
Map constraintMap = null;
SecuredObject layer = presentLayers[i];
for (int j = 0; j < layers.length; j++) {
if (layer.getID() == layers[j]) {
isAccessible = true;
constraintMap = layerConstraints[j];
break;
}
}
if (isAccessible) {
Filter filter = null;
if (constraintMap != null) {
String xml = buildGetMapFilter(constraintMap);
if (xml != null) {
try {
Document doc = XMLTools.parse(new StringReader(
xml));
filter = AbstractFilter.buildFromDOM(doc
.getDocumentElement());
} catch (Exception e) {
throw new GeneralSecurityException(
"Fehler beim Parsen des generierten Filter Encodings: "
+ e.getMessage());
}
}
if (filter != null) {
LOG.logInfo( "Back to XML: " + filter.toXML() );
}
}
transaction.setRights(layer, role, new Right[] {
new Right(layer, RightType.GETMAP, filter),
new Right(layer, RightType.GETFEATUREINFO),
new Right(layer, RightType.GETLEGENDGRAPHIC) });
} else {
transaction.removeRights(layer, role, new RightType[] {
RightType.GETMAP, RightType.GETFEATUREINFO,
RightType.GETLEGENDGRAPHIC });
}
}
// set/delete access rights for FeatureTypes
SecuredObject[] presentFeatureTypes =
transaction.getAllSecuredObjects(ClientHelper.TYPE_FEATURETYPE);
for (int i = 0; i < presentFeatureTypes.length; i++) {
boolean selected = false;
SecuredObject featureType = presentFeatureTypes[i];
for (int j = 0; j < featureTypes.length; j++) {
if (featureType.getID() == featureTypes[j]) {
selected = true;
break;
}
}
if (selected) {
transaction.addRights(featureType, role, new Right[] {
new Right(featureType, RightType.GETFEATURE),
new Right(featureType,
RightType.DESCRIBEFEATURETYPE) });
} else {
transaction.removeRights(featureType, role,
new RightType[] { RightType.GETFEATURE,
RightType.DESCRIBEFEATURETYPE });
}
}
getRequest().setAttribute("MESSAGE",
"Ihre Änderungen wurden erfolgreich in der Datenbank gespeichert.");
manager.commitTransaction(transaction);
transaction = null;
getRequest().setAttribute( "MESSAGE",
"Ihre Änderungen wurden erfolgreich in der Datenbank gespeichert."
+ "<br/><br/><p><a href='javascript:editRightsRPC("
+ role.getID() + ")'>--> zurück "
+ "zum Rechte-Editor</a></p>");
} catch (RPCException e) {
getRequest().setAttribute("SOURCE", this.getClass().getName());
getRequest().setAttribute( "MESSAGE",
"Ihre Änderungen konnten nicht in der Datenbank gespeichert werden, "
+ "da Ihre Anfrage fehlerhaft war.<br><br>"
+ "Die Fehlermeldung lautet: <code>"
+ e.getMessage() + "</code>");
setNextPage("error.jsp");
} catch (GeneralSecurityException e) {
e.printStackTrace();
getRequest().setAttribute("SOURCE", this.getClass().getName());
getRequest().setAttribute( "MESSAGE",
"Ihre Änderungen konnten nicht in der Datenbank gespeichert werden, "
+ "da ein Fehler aufgetreten ist.<br><br>"
+ "Die Fehlermeldung lautet: <code>"
+ e.getMessage() + "</code>");
setNextPage("error.jsp");
} finally {
if (manager != null && transaction != null) {
try {
manager.abortTransaction(transaction);
} catch (GeneralSecurityException e) {
e.printStackTrace();
}
}
}
}
/**
* Builds a filter encoding-expression as a constraint for GetMap-operations
* from the values stored in the given <code>Map</code>.
*/
String buildGetMapFilter(Map constraintMap) throws RPCException {
int operands = 0;
StringBuffer sb = new StringBuffer(1000);
// bbox
if (constraintMap.get("bbox") != null) {
operands++;
String minx = MINX;
String miny = MINY;
String maxx = MAXX;
String maxy = MAXY;
if (constraintMap.get("bbox") instanceof String) {
if (!constraintMap.get("bbox").equals("wuppertal")) {
throw new RPCException( "Constraint 'bbox' must be specified by a string "
+ "value (\"wuppertal\") or a string array "
+ "containing exactly 4 coordinates!");
}
} else {
String[] bbox = (String[]) constraintMap.get("bbox");
if (bbox.length != 4) {
throw new RPCException( "Constraint 'bbox' must be specified by a string "
+ "value (\"wuppertal\") or a string array "
+ "containing exactly 4 coordinates!");
}
minx = bbox[0];
miny = bbox[1];
maxx = bbox[2];
maxy = bbox[3];
}
sb.append("<ogc:Within>");
sb.append("<ogc:PropertyName>bbox</ogc:PropertyName>");
sb.append("<gml:Box>");
sb.append("<gml:coordinates>");
sb.append(minx).append(",").append(miny).append(" ");
sb.append(maxx).append(",").append(maxy);
sb.append("</gml:coordinates>");
sb.append("</gml:Box></ogc:Within>");
}
// bgcolor
String[] bgcolors = (String[]) constraintMap.get("bgcolor");
if (bgcolors != null && bgcolors.length > 0) {
operands++;
if (bgcolors.length > 1) {
sb.append("<ogc:Or>");
}
for (int i = 0; i < bgcolors.length; i++) {
sb.append("<ogc:PropertyIsEqualTo>");
sb.append("<ogc:PropertyName>bgcolor</ogc:PropertyName>");
sb.append("<ogc:Literal><![CDATA[" + bgcolors[i] + "]]></ogc:Literal>");
sb.append("</ogc:PropertyIsEqualTo>");
}
if (bgcolors.length > 1) {
sb.append("</ogc:Or>");
}
}
// transparent
String transparent = (String) constraintMap.get("transparent");
if (transparent != null) {
operands++;
sb.append("<ogc:PropertyIsEqualTo>");
sb.append("<ogc:PropertyName>transparent</ogc:PropertyName>");
sb.append("<ogc:Literal><![CDATA[" + transparent + "]]></ogc:Literal>");
sb.append("</ogc:PropertyIsEqualTo>");
}
// format
String[] formats = (String[]) constraintMap.get("format");
if (formats != null && formats.length > 0) {
operands++;
if (formats.length > 1) {
sb.append("<ogc:Or>");
}
for (int i = 0; i < formats.length; i++) {
sb.append("<ogc:PropertyIsEqualTo>");
sb.append("<ogc:PropertyName>format</ogc:PropertyName>");
sb.append("<ogc:Literal><![CDATA[" + formats[i] + "]]></ogc:Literal>");
sb.append("</ogc:PropertyIsEqualTo>");
}
if (formats.length > 1) {
sb.append("</ogc:Or>");
}
}
// resolution
String resolution = (String) constraintMap.get("resolution");
if (resolution != null) {
operands++;
sb.append("<ogc:PropertyIsGreaterThanOrEqualTo>");
sb.append("<ogc:PropertyName>resolution</ogc:PropertyName>");
sb.append("<ogc:Literal>" + resolution + "</ogc:Literal>");
sb.append("</ogc:PropertyIsGreaterThanOrEqualTo>");
}
// width
String width = (String) constraintMap.get("width");
if (width != null) {
operands++;
sb.append("<ogc:PropertyIsLessThanOrEqualTo>");
sb.append("<ogc:PropertyName>width</ogc:PropertyName>");
sb.append("<ogc:Literal>" + width + "</ogc:Literal>");
sb.append("</ogc:PropertyIsLessThanOrEqualTo>");
}
// height
String height = (String) constraintMap.get("height");
if (height != null) {
operands++;
sb.append("<ogc:PropertyIsLessThanOrEqualTo>");
sb.append("<ogc:PropertyName>height</ogc:PropertyName>");
sb.append("<ogc:Literal>" + height + "</ogc:Literal>");
sb.append("</ogc:PropertyIsLessThanOrEqualTo>");
}
// exceptions
String[] exceptions = (String[]) constraintMap.get("exceptions");
if (exceptions != null && exceptions.length > 0) {
operands++;
if (exceptions.length > 1) {
sb.append("<ogc:Or>");
}
for (int i = 0; i < exceptions.length; i++) {
sb.append("<ogc:PropertyIsEqualTo>");
sb.append("<ogc:PropertyName>exceptions</ogc:PropertyName>");
sb.append("<ogc:Literal><![CDATA[" + exceptions[i]
+ "]]></ogc:Literal>");
sb.append("</ogc:PropertyIsEqualTo>");
}
if (exceptions.length > 1) {
sb.append("</ogc:Or>");
}
}
if (operands == 0) {
return null;
} else if (operands >= 2) {
sb = new StringBuffer(
"<ogc:Filter xmlns:ogc=\"http://www.opengis.net/ogc\" xmlns:gml=\"http://www.opengis.net/gml\">")
.append("<ogc:And>").append(sb).append( "</ogc:And></ogc:Filter>");
} else {
sb = new StringBuffer(
"<ogc:Filter xmlns:ogc=\"http://www.opengis.net/ogc\" xmlns:gml=\"http://www.opengis.net/gml\">")
.append(sb).append("</ogc:Filter>");
}
return sb.toString();
}
}
/* ********************************************************************
Changes to this class. What the people have been up to:
$Log $
Revision 1.3 2006/07/13 08:10:56 poth
file header added / references to Debug.XXXX removed
Revision 1.2 2006/07/12 14:46:15 poth
comment footer added
********************************************************************** */