001 //$HeadURL$ 002 /*---------------------------------------------------------------------------- 003 This file is part of deegree, http://deegree.org/ 004 Copyright (C) 2001-2009 by: 005 Department of Geography, University of Bonn 006 and 007 lat/lon GmbH 008 009 This library is free software; you can redistribute it and/or modify it under 010 the terms of the GNU Lesser General Public License as published by the Free 011 Software Foundation; either version 2.1 of the License, or (at your option) 012 any later version. 013 This library is distributed in the hope that it will be useful, but WITHOUT 014 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 015 FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more 016 details. 017 You should have received a copy of the GNU Lesser General Public License 018 along with this library; if not, write to the Free Software Foundation, Inc., 019 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 020 021 Contact information: 022 023 lat/lon GmbH 024 Aennchenstr. 19, 53177 Bonn 025 Germany 026 http://lat-lon.de/ 027 028 Department of Geography, University of Bonn 029 Prof. Dr. Klaus Greve 030 Postfach 1147, 53001 Bonn 031 Germany 032 http://www.geographie.uni-bonn.de/deegree/ 033 034 e-mail: info@deegree.org 035 ----------------------------------------------------------------------------*/ 036 037 package org.deegree.security.owsrequestvalidator.csw; 038 039 import java.io.ByteArrayInputStream; 040 import java.io.IOException; 041 import java.net.URI; 042 import java.net.URISyntaxException; 043 import java.util.ArrayList; 044 import java.util.HashMap; 045 import java.util.List; 046 import java.util.Map; 047 048 import org.deegree.datatypes.QualifiedName; 049 import org.deegree.framework.log.ILogger; 050 import org.deegree.framework.log.LoggerFactory; 051 import org.deegree.framework.util.MimeTypeMapper; 052 import org.deegree.framework.xml.NamespaceContext; 053 import org.deegree.framework.xml.XMLException; 054 import org.deegree.framework.xml.XMLFragment; 055 import org.deegree.framework.xml.XMLParsingException; 056 import org.deegree.framework.xml.XMLTools; 057 import org.deegree.i18n.Messages; 058 import org.deegree.model.filterencoding.ComplexFilter; 059 import org.deegree.model.filterencoding.Filter; 060 import org.deegree.model.filterencoding.FilterConstructionException; 061 import org.deegree.model.filterencoding.Literal; 062 import org.deegree.model.filterencoding.LogicalOperation; 063 import org.deegree.model.filterencoding.Operation; 064 import org.deegree.model.filterencoding.OperationDefines; 065 import org.deegree.model.filterencoding.PropertyIsCOMPOperation; 066 import org.deegree.ogcbase.CommonNamespaces; 067 import org.deegree.ogcwebservices.InvalidParameterValueException; 068 import org.deegree.portal.standard.security.control.ClientHelper; 069 import org.deegree.security.GeneralSecurityException; 070 import org.deegree.security.UnauthorizedException; 071 import org.deegree.security.drm.SecurityAccess; 072 import org.deegree.security.drm.SecurityAccessManager; 073 import org.deegree.security.drm.model.Right; 074 import org.deegree.security.drm.model.RightSet; 075 import org.deegree.security.drm.model.RightType; 076 import org.deegree.security.drm.model.SecuredObject; 077 import org.deegree.security.drm.model.User; 078 import org.deegree.security.owsproxy.Condition; 079 import org.deegree.security.owsproxy.Request; 080 import org.deegree.security.owsrequestvalidator.Policy; 081 import org.w3c.dom.Node; 082 import org.xml.sax.SAXException; 083 084 /** 085 * 086 * 087 * @author <a href="mailto:poth@lat-lon.de">Andreas Poth</a> 088 * @author last edited by: $Author: poth $ 089 * 090 * @version $Revision: 6251 $, $Date: 2007-03-19 16:59:28 +0100 (Mo, 19 Mrz 2007) $ 091 */ 092 public class GetRecordByIdResponseValidator extends AbstractCSWResponseValidator { 093 094 private static final ILogger LOG = LoggerFactory.getLogger( GetRecordByIdResponseValidator.class ); 095 096 private static Map<QualifiedName, Filter> filterMap = new HashMap<QualifiedName, Filter>(); 097 098 /** 099 * 100 * @param policy 101 */ 102 public GetRecordByIdResponseValidator( Policy policy ) { 103 super( policy ); 104 } 105 106 @Override 107 public byte[] validateResponse( String service, byte[] response, String mime, User user ) 108 throws InvalidParameterValueException, UnauthorizedException { 109 110 Request req = policy.getRequest( service, "GetRecordById" ); 111 // request is valid because no restrictions are made 112 if ( req.isAny() || req.getPostConditions().isAny()) { 113 return response; 114 } 115 116 Condition conditions = req.getPostConditions(); 117 118 if ( MimeTypeMapper.isKnownOGCType( mime ) ) { 119 // if the mime-type isn't an image type but a known 120 // OGC mime-type it must be an XML document. 121 // probably it is an exception but it also could be 122 // a GML document 123 try { 124 response = validateXML( response, conditions, user ); 125 } catch ( XMLException e ) { 126 LOG.logError( e.getMessage(), e ); 127 throw new InvalidParameterValueException( e.getMessage(), e ); 128 } catch ( SAXException e ) { 129 LOG.logError( e.getMessage(), e ); 130 throw new InvalidParameterValueException( e.getMessage(), e ); 131 } catch ( IOException e ) { 132 LOG.logError( e.getMessage(), e ); 133 throw new InvalidParameterValueException( e.getMessage(), e ); 134 } catch ( XMLParsingException e ) { 135 LOG.logError( e.getMessage(), e ); 136 throw new InvalidParameterValueException( e.getMessage(), e ); 137 } 138 } else if ( mime.equals( "text/xml" ) ) { 139 // if the mime-type isn't an image type but 'text/xml' 140 // it could be an exception 141 try { 142 response = validateXML( response, conditions, user ); 143 } catch ( XMLException e ) { 144 LOG.logError( e.getMessage(), e ); 145 throw new InvalidParameterValueException( e.getMessage(), e ); 146 } catch ( SAXException e ) { 147 LOG.logError( e.getMessage(), e ); 148 throw new InvalidParameterValueException( e.getMessage(), e ); 149 } catch ( IOException e ) { 150 LOG.logError( e.getMessage(), e ); 151 throw new InvalidParameterValueException( e.getMessage(), e ); 152 } catch ( XMLParsingException e ) { 153 LOG.logError( e.getMessage(), e ); 154 throw new InvalidParameterValueException( e.getMessage(), e ); 155 } 156 } else { 157 throw new InvalidParameterValueException( UNKNOWNMIMETYPE + mime ); 158 } 159 160 return response; 161 } 162 163 private byte[] validateXML( byte[] response, Condition postConditions, User user ) 164 throws XMLException, SAXException, IOException, InvalidParameterValueException, 165 UnauthorizedException, XMLParsingException { 166 167 ByteArrayInputStream bis = new ByteArrayInputStream( response ); 168 XMLFragment xml = new XMLFragment(); 169 xml.load( bis, XMLFragment.DEFAULT_URL ); 170 NamespaceContext nsc = CommonNamespaces.getNamespaceContext(); 171 172 List<Node> nodes = XMLTools.getNodes( xml.getRootElement(), "*", nsc ); 173 for ( Node node : nodes ) { 174 String ln = node.getLocalName(); 175 String nsu = node.getNamespaceURI(); 176 QualifiedName elementName = null; 177 try { 178 if ( nsu != null ) { 179 elementName = new QualifiedName( null, ln, new URI( nsu ) ); 180 } else { 181 elementName = new QualifiedName( ln ); 182 } 183 } catch ( URISyntaxException e ) { 184 // never happens 185 } 186 LOG.logDebug( "checking element: " + elementName.getFormattedString() ); 187 if ( postConditions.getOperationParameter( "instanceFilter" ) != null ) { 188 Map<QualifiedName, Filter> localFilterMap; 189 if ( postConditions.getOperationParameter( "instanceFilter" ).isUserCoupled() ) { 190 // read filterMap from constraints defined in deegree DRM 191 localFilterMap = readFilterFromDRM( user, elementName ); 192 } else { 193 fillFilterMap( postConditions ); 194 // use filterMap read from policy document 195 localFilterMap = filterMap; 196 } 197 ComplexFilter filter = (ComplexFilter) localFilterMap.get( elementName ); 198 if ( filter == null ) { 199 throw new UnauthorizedException( Messages.getMessage( "OWSPROXY_GETRECBYID_NOT_ALLOWED" ) ); 200 } 201 202 // check if returned dataset is valid against the instance filter. If not, 203 // thrown an UnauthorizedException 204 Operation op = filter.getOperation(); 205 if ( op instanceof LogicalOperation ) { 206 LogicalOperation lo = (LogicalOperation) op; 207 if ( lo.getOperatorId() == OperationDefines.AND ) { 208 handleAnd( xml, lo ); 209 } else if ( lo.getOperatorId() == OperationDefines.OR ) { 210 handleOr( xml, lo ); 211 } else { 212 throw new InvalidParameterValueException( 213 Messages.getMessage( "OWSPROXY_GETRECBYID_INVALID_LOGICAL_OPERATOR" ) ); 214 } 215 } else { 216 Literal literal = (Literal) ( (PropertyIsCOMPOperation) op ).getSecondExpression(); 217 String xpath = literal.getValue(); 218 LOG.logDebug( "evaluated xpath expression: " + xpath ); 219 List<Node> list = XMLTools.getNodes( xml.getRootElement(), xpath, nsc ); 220 if ( list == null || list.size() == 0 ) { 221 // if the XPath do not return a result the user is not authorized 222 throw new UnauthorizedException( Messages.getMessage( "OWSPROXY_GETRECBYID_NOT_ALLOWED" ) ); 223 } 224 } 225 226 } 227 } 228 229 return response; 230 } 231 232 /** 233 * checks if a passed XML matches the XPath conditions contained in the passed OR operation. If 234 * at least one XPath matches the user is authoried to see the XML 235 * 236 * @param xml 237 * @param lo 238 * @throws UnauthorizedException 239 * @throws XMLParsingException 240 */ 241 private void handleOr( XMLFragment xml, LogicalOperation lo ) 242 throws UnauthorizedException, XMLParsingException { 243 244 NamespaceContext nsc = CommonNamespaces.getNamespaceContext(); 245 // It is assumed that an XPath is contained within the operations literal (second 246 // expression). 247 List<Operation> ops = lo.getArguments(); 248 for ( Operation operation : ops ) { 249 Literal literal = (Literal) ( (PropertyIsCOMPOperation) operation ).getSecondExpression(); 250 String xpath = literal.getValue(); 251 LOG.logDebug( "evaluated xpath expression: " + xpath ); 252 // check if the XML document matches the XPath. If none of xpath returns a result 253 // the user is not authorized to see this dataset 254 List<Node> list = XMLTools.getNodes( xml.getRootElement(), xpath, nsc ); 255 if ( list != null && list.size() > 0 ) { 256 // at least one xpath returned more than nothing so the user is authorized 257 return; 258 } 259 } 260 throw new UnauthorizedException( Messages.getMessage( "OWSPROXY_GETRECBYID_NOT_ALLOWED" ) ); 261 } 262 263 /** 264 * checks if a passed XML matches the XPath conditions contained in the passed AND operation. 265 * Just If all XPaths matches the user is authoried to see the XML 266 * 267 * @param xml 268 * @param lo 269 * @throws XMLParsingException 270 * @throws UnauthorizedException 271 */ 272 private void handleAnd( XMLFragment xml, LogicalOperation lo ) 273 throws XMLParsingException, UnauthorizedException { 274 275 NamespaceContext nsc = CommonNamespaces.getNamespaceContext(); 276 // It is assumed that an XPath is contained within the operations literal (second 277 // expression). 278 List<Operation> ops = lo.getArguments(); 279 for ( Operation operation : ops ) { 280 Literal literal = (Literal) ( (PropertyIsCOMPOperation) operation ).getSecondExpression(); 281 String xpath = literal.getValue(); 282 LOG.logDebug( "evaluated xpath expression: " + xpath ); 283 // check if the XML document matches the XPath. If at least one of xpath returns no 284 // result 285 // the user is not authorized to see this dataset 286 List<Node> list = XMLTools.getNodes( xml.getRootElement(), xpath, nsc ); 287 if ( list == null || list.size() == 0 ) { 288 // if at least one XPath do not return a result the user is not authorized 289 throw new UnauthorizedException( Messages.getMessage( "OWSPROXY_CSW_GETRECBYID_NOT_ALLOWED" ) ); 290 } 291 } 292 293 } 294 295 /** 296 * 297 * @param postConditions 298 */ 299 private void fillFilterMap( Condition postConditions ) { 300 // TODO Auto-generated method stub 301 } 302 303 private Map<QualifiedName, Filter> readFilterFromDRM( User user, QualifiedName elementName ) 304 throws UnauthorizedException, InvalidParameterValueException { 305 306 Map<QualifiedName, Filter> map = new HashMap<QualifiedName, Filter>(); 307 try { 308 SecurityAccessManager sam = SecurityAccessManager.getInstance(); 309 SecurityAccess access = sam.acquireAccess( user ); 310 String entity = elementName.getFormattedString(); 311 SecuredObject secObj = access.getSecuredObjectByName( entity, ClientHelper.TYPE_METADATASCHEMA ); 312 313 RightSet rs = user.getRights( access, secObj ); 314 Right right = rs.getRight( secObj, RightType.GETRECORDBYID_RESPONSE ); 315 // a constraint - if available - is constructed as a OGC Filter 316 // one of the filter operations may is 'PropertyIsEqualTo' and 317 // defines a ProperyName == 'instanceFilter'. The Literal of this 318 // operation itself is a complete and valid Filter expression. 319 if ( right != null ) { 320 ComplexFilter filter = (ComplexFilter) right.getConstraints(); 321 if ( filter != null ) { 322 List<ComplexFilter> foundFilters = new ArrayList<ComplexFilter>(); 323 // extract filter expression to be used as additional 324 // filter for a GetFeature request 325 extractInstanceFilter( filter.getOperation(), foundFilters ); 326 if ( foundFilters.size() == 1 ) { 327 filter = foundFilters.get( 0 ); 328 } else if ( foundFilters.size() > 1 ) { 329 List<org.deegree.model.filterencoding.Operation> list = new ArrayList<org.deegree.model.filterencoding.Operation>(); 330 for ( ComplexFilter cf : foundFilters ) { 331 list.add( cf.getOperation() ); 332 } 333 LogicalOperation lo = new LogicalOperation( OperationDefines.OR, list ); 334 filter = new ComplexFilter( lo ); 335 } 336 map.put( elementName, filter ); 337 // if ( filter != null ) { 338 // map.put( elementName, filter ); 339 // } 340 } 341 } 342 } catch ( GeneralSecurityException e ) { 343 LOG.logError( e.getMessage(), e ); 344 throw new UnauthorizedException( e.getMessage(), e ); 345 } catch ( FilterConstructionException e ) { 346 LOG.logError( e.getMessage(), e ); 347 throw new InvalidParameterValueException( e.getMessage(), e ); 348 } catch ( SAXException e ) { 349 LOG.logError( e.getMessage(), e ); 350 throw new InvalidParameterValueException( e.getMessage(), e ); 351 } catch ( IOException e ) { 352 LOG.logError( e.getMessage(), e ); 353 throw new InvalidParameterValueException( e.getMessage(), e ); 354 } 355 356 return map; 357 } 358 359 }