001 //$HeadURL: https://svn.wald.intevation.org/svn/deegree/base/branches/2.3_testing/src/org/deegree/security/owsrequestvalidator/csw/GetRecordsRequestValidator.java $ 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 package org.deegree.security.owsrequestvalidator.csw; 037 038 import java.io.IOException; 039 import java.util.ArrayList; 040 import java.util.HashMap; 041 import java.util.List; 042 import java.util.Map; 043 044 import org.deegree.datatypes.QualifiedName; 045 import org.deegree.datatypes.Types; 046 import org.deegree.framework.log.ILogger; 047 import org.deegree.framework.log.LoggerFactory; 048 import org.deegree.model.feature.Feature; 049 import org.deegree.model.feature.FeatureFactory; 050 import org.deegree.model.feature.FeatureProperty; 051 import org.deegree.model.feature.schema.FeatureType; 052 import org.deegree.model.feature.schema.PropertyType; 053 import org.deegree.model.filterencoding.ComplexFilter; 054 import org.deegree.model.filterencoding.FeatureFilter; 055 import org.deegree.model.filterencoding.Filter; 056 import org.deegree.model.filterencoding.FilterConstructionException; 057 import org.deegree.model.filterencoding.LogicalOperation; 058 import org.deegree.model.filterencoding.OperationDefines; 059 import org.deegree.ogcbase.SortProperty; 060 import org.deegree.ogcwebservices.InvalidParameterValueException; 061 import org.deegree.ogcwebservices.OGCWebServiceRequest; 062 import org.deegree.ogcwebservices.csw.discovery.GetRecords; 063 import org.deegree.ogcwebservices.csw.discovery.Query; 064 import org.deegree.ogcwebservices.csw.discovery.XMLFactory; 065 import org.deegree.portal.standard.security.control.ClientHelper; 066 import org.deegree.security.GeneralSecurityException; 067 import org.deegree.security.UnauthorizedException; 068 import org.deegree.security.drm.SecurityAccess; 069 import org.deegree.security.drm.SecurityAccessManager; 070 import org.deegree.security.drm.model.Right; 071 import org.deegree.security.drm.model.RightSet; 072 import org.deegree.security.drm.model.RightType; 073 import org.deegree.security.drm.model.SecuredObject; 074 import org.deegree.security.drm.model.User; 075 import org.deegree.security.owsproxy.Condition; 076 import org.deegree.security.owsproxy.OperationParameter; 077 import org.deegree.security.owsproxy.Request; 078 import org.deegree.security.owsrequestvalidator.Messages; 079 import org.deegree.security.owsrequestvalidator.Policy; 080 import org.xml.sax.SAXException; 081 082 /** 083 * 084 * 085 * @version $Revision: 18195 $ 086 * @author <a href="mailto:poth@lat-lon.de">Andreas Poth</a> 087 * @author last edited by: $Author: mschneider $ 088 * 089 * @version 1.0. $Revision: 18195 $, $Date: 2009-06-18 17:55:39 +0200 (Do, 18. Jun 2009) $ 090 * 091 * @since 2.0 092 */ 093 public class GetRecordsRequestValidator extends AbstractCSWRequestValidator { 094 095 private static final ILogger LOG = LoggerFactory.getLogger( GetRecordsRequestValidator.class ); 096 097 private static final String ELEMENTSETNAME = "elementSetName"; 098 099 private static final String MAXRECORDS = "maxRecords"; 100 101 private static final String OUTPUTFORMAT = "outputFormat"; 102 103 private static final String RESULTTYPE = "resultType"; 104 105 private static final String SORTBY = "sortBy"; 106 107 private static final String TYPENAMES = "typeNames"; 108 109 private static FeatureType grFT = null; 110 111 private static Map<String, Filter> filterMap = new HashMap<String, Filter>(); 112 113 static { 114 if ( grFT == null ) { 115 grFT = GetRecordsRequestValidator.createFeatureType(); 116 } 117 } 118 119 /** 120 * @param policy 121 */ 122 public GetRecordsRequestValidator( Policy policy ) { 123 super( policy ); 124 } 125 126 /** 127 * @param request 128 * @param user 129 */ 130 @Override 131 public void validateRequest( OGCWebServiceRequest request, User user ) 132 throws InvalidParameterValueException, UnauthorizedException { 133 134 userCoupled = false; 135 Request req = policy.getRequest( "CSW", "GetRecords" ); 136 // request is valid because no restrictions are made 137 if ( req.isAny() || req.getPreConditions().isAny() ) { 138 return; 139 } 140 Condition condition = req.getPreConditions(); 141 142 GetRecords casreq = (GetRecords) request; 143 144 validateVersion( condition, casreq.getVersion() ); 145 146 // validateRecordTypes( condition, tn ); 147 validateMaxRecords( condition, casreq.getMaxRecords() ); 148 validateOutputFormat( condition, casreq.getOutputFormat() ); 149 validateResultType( condition, casreq.getResultTypeAsString() ); 150 validateElementSetName( condition, casreq.getQuery().getElementSetName() ); 151 validateSortBy( condition, casreq.getQuery().getSortProperties() ); 152 List<QualifiedName> list = casreq.getQuery().getTypeNamesAsList(); 153 validateTypeNames( condition, list ); 154 155 if ( userCoupled ) { 156 validateAgainstRightsDB( casreq, user ); 157 } 158 159 if ( req.getPostConditions() != null ) { 160 addFilter( casreq, req.getPostConditions(), user ); 161 } 162 163 } 164 165 /** 166 * adds an additional Filter read from parameter 'instanceFilter'to the Filter of the passed 167 * GetFeature request. If parameter 'instanceFilter' is userCoupled the filter will be read from 168 * DRM, if it is not the filter defined within the responsible policy document will be used. 169 * 170 * @param casreq 171 * @param postConditions 172 * @param user 173 * @throws InvalidParameterValueException 174 * @throws UnauthorizedException 175 */ 176 private void addFilter( GetRecords casreq, Condition postConditions, User user ) 177 throws InvalidParameterValueException, UnauthorizedException { 178 if ( postConditions.getOperationParameter( "instanceFilter" ) != null ) { 179 Map<String, Filter> localFilterMap; 180 if ( postConditions.getOperationParameter( "instanceFilter" ).isUserCoupled() ) { 181 // read filterMap from constraints defined in deegree DRM 182 localFilterMap = readFilterFromDRM( casreq, user ); 183 } else { 184 fillFilterMap( postConditions ); 185 // use filterMap read from policy document 186 localFilterMap = filterMap; 187 } 188 Query query = casreq.getQuery(); 189 Filter filter = null; 190 if ( query.getContraint() == null ) { 191 // if query does not define a filter just use the matching 192 // one from the post conditions 193 filter = localFilterMap.get( casreq.getOutputSchema() ); 194 } else if ( query.getContraint() instanceof ComplexFilter ) { 195 // create a new Filter that is a combination of the 196 // original filter and the one defined in the GetFeatures 197 // PostConditions coupled by a logical 'And' 198 ComplexFilter qFilter = (ComplexFilter) query.getContraint(); 199 filter = localFilterMap.get( casreq.getOutputSchema() ); 200 if ( filter == null ) { 201 filter = qFilter; 202 } else { 203 filter = new ComplexFilter( qFilter, (ComplexFilter) filter, OperationDefines.AND ); 204 } 205 } else if ( query.getContraint() instanceof FeatureFilter ) { 206 // just take original filter if it is as feature filter 207 // because feature filter and complex filters can not 208 // be combined 209 filter = query.getContraint(); 210 } 211 // substitue query by a new one using the re-created filter 212 query = new Query( query.getElementSetName(), query.getElementSetNameTypeNamesList(), 213 query.getElementSetNameVariables(), query.getElementNamesAsPropertyPaths(), 214 filter, query.getSortProperties(), query.getTypeNamesAsList(), 215 query.getDeclaredTypeNameVariables() ); 216 217 casreq.setQuery( query ); 218 } 219 if ( LOG.getLevel() != ILogger.LOG_DEBUG ) { 220 try { 221 XMLFactory.export( casreq ).prettyPrint( System.out ); 222 } catch ( Exception e ) { 223 } 224 } 225 } 226 227 private void fillFilterMap( Condition postConditions ) { 228 // List<Element> complexValues = 229 postConditions.getOperationParameter( "instanceFilter" ).getComplexValues(); 230 /* 231 * TODO try { if ( filterMap.size() == 0 ) { for ( int i = 0; i < complexValues.size(); i++ ) { 232 * Query q = Query.create( complexValues.get( 0 ) ); Filter f = q.getFilter(); QualifiedName 233 * qn = q.getTypeNames()[0]; filterMap.put( qn, f ); } } } catch ( XMLParsingException e ) { 234 * LOG.logError( e.getMessage(), e ); throw new InvalidParameterValueException( 235 * this.getClass().getName(), e.getMessage() ); } 236 */ 237 } 238 239 private Map<String, Filter> readFilterFromDRM( GetRecords casreq, User user ) 240 throws UnauthorizedException, InvalidParameterValueException { 241 242 Map<String, Filter> map = new HashMap<String, Filter>(); 243 try { 244 SecurityAccessManager sam = SecurityAccessManager.getInstance(); 245 SecurityAccess access = sam.acquireAccess( user ); 246 String ops = casreq.getOutputSchema(); 247 SecuredObject secObj = access.getSecuredObjectByName( ops, ClientHelper.TYPE_METADATASCHEMA ); 248 RightSet rs = user.getRights( access, secObj ); 249 Right right = rs.getRight( secObj, RightType.GETRECORDS_RESPONSE ); 250 // a constraint - if available - is constructed as a OGC Filter 251 // one of the filter operations may is 'PropertyIsEqualTo' and 252 // defines a ProperyName == 'instanceFilter'. The Literal of this 253 // operation itself is a complete and valid Filter expression. 254 if ( right != null ) { 255 ComplexFilter filter = (ComplexFilter) right.getConstraints(); 256 if ( filter != null ) { 257 List<ComplexFilter> foundFilters = new ArrayList<ComplexFilter>(); 258 // extract filter expression to be used as additional 259 // filter for a GetFeature request 260 extractInstanceFilter( filter.getOperation(), foundFilters ); 261 if ( foundFilters.size() == 1 ) { 262 filter = foundFilters.get( 0 ); 263 } else if ( foundFilters.size() > 1 ) { 264 List<org.deegree.model.filterencoding.Operation> list = new ArrayList<org.deegree.model.filterencoding.Operation>(); 265 for ( ComplexFilter cf : foundFilters ) { 266 list.add( cf.getOperation() ); 267 } 268 LogicalOperation lo = new LogicalOperation( OperationDefines.OR, list ); 269 filter = new ComplexFilter( lo ); 270 } 271 map.put( ops, filter ); 272 } 273 } 274 275 } catch ( GeneralSecurityException e ) { 276 LOG.logError( e.getMessage(), e ); 277 throw new UnauthorizedException( e.getMessage(), e ); 278 } catch ( FilterConstructionException e ) { 279 LOG.logError( e.getMessage(), e ); 280 throw new InvalidParameterValueException( e.getMessage(), e ); 281 } catch ( SAXException e ) { 282 LOG.logError( e.getMessage(), e ); 283 throw new InvalidParameterValueException( e.getMessage(), e ); 284 } catch ( IOException e ) { 285 LOG.logError( e.getMessage(), e ); 286 throw new InvalidParameterValueException( e.getMessage(), e ); 287 } 288 289 return map; 290 } 291 292 /** 293 * validates the passed CSW GetRecords request against a User- and Rights-Management DB. 294 * 295 * @param casreq 296 * @param user 297 */ 298 private void validateAgainstRightsDB( GetRecords casreq, User user ) 299 throws InvalidParameterValueException, UnauthorizedException { 300 301 if ( user == null ) { 302 throw new UnauthorizedException( Messages.getString( "RequestValidator.NOACCESS" ) ); 303 } 304 305 // create a feature instance from the parameters of the GetRecords request 306 // to enable comparsion with a filter encoding expression stored in the 307 // assigned rights management system 308 List<FeatureProperty> fp = new ArrayList<FeatureProperty>(); 309 fp.add( FeatureFactory.createFeatureProperty( new QualifiedName( "version" ), casreq.getVersion() ) ); 310 fp.add( FeatureFactory.createFeatureProperty( new QualifiedName( "maxRecords" ), casreq.getMaxRecords() ) ); 311 fp.add( FeatureFactory.createFeatureProperty( new QualifiedName( "outputFormat" ), casreq.getOutputFormat() ) ); 312 fp.add( FeatureFactory.createFeatureProperty( new QualifiedName( "resultType" ), casreq.getResultTypeAsString() ) ); 313 SortProperty[] sp = casreq.getQuery().getSortProperties(); 314 if ( sp != null ) { 315 for ( int i = 0; i < sp.length; i++ ) { 316 fp.add( FeatureFactory.createFeatureProperty( new QualifiedName( "sortBy" ), 317 sp[i].getSortProperty().getAsString() ) ); 318 } 319 } 320 List<QualifiedName> tp = casreq.getQuery().getTypeNamesAsList(); 321 for ( int i = 0; i < tp.size(); i++ ) { 322 fp.add( FeatureFactory.createFeatureProperty( new QualifiedName( "typeNames" ), 323 tp.get( i ).getPrefixedName() ) ); 324 } 325 fp.add( FeatureFactory.createFeatureProperty( new QualifiedName( "elementSetName" ), 326 casreq.getQuery().getElementSetName() ) ); 327 328 Feature feature = FeatureFactory.createFeature( "id", grFT, fp ); 329 handleUserCoupledRules( user, feature, casreq.getOutputSchema(), ClientHelper.TYPE_METADATASCHEMA, 330 RightType.GETRECORDS ); 331 332 } 333 334 /** 335 * valides if the maxRecords parameter in a GetRecords request is valid against the policy 336 * assigned to Validator. 337 * 338 * @param condition 339 * @param maxRecords 340 * @throws InvalidParameterValueException 341 */ 342 private void validateMaxRecords( Condition condition, int maxRecords ) 343 throws InvalidParameterValueException { 344 OperationParameter op = condition.getOperationParameter( MAXRECORDS ); 345 346 // is valid because no restrictions are made 347 if ( op.isAny() ) 348 return; 349 350 int maxF = op.getFirstAsInt(); 351 352 if ( op.isUserCoupled() ) { 353 userCoupled = true; 354 } else { 355 if ( maxRecords > maxF || maxRecords < 0 ) { 356 String s = Messages.format( "GetRecordsRequestValidator.INVALIDMAXRECORDS", MAXRECORDS ); 357 throw new InvalidParameterValueException( s ); 358 } 359 } 360 361 } 362 363 /** 364 * valides if the elementSetName parameter in a GetRecords request is valid against the policy 365 * assigned to Validator. 366 * 367 * @param condition 368 * @param elementSetName 369 * @throws InvalidParameterValueException 370 */ 371 private void validateElementSetName( Condition condition, String elementSetName ) 372 throws InvalidParameterValueException { 373 OperationParameter op = condition.getOperationParameter( ELEMENTSETNAME ); 374 375 // is valid because no restrictions are made 376 if ( op.isAny() ) 377 return; 378 379 List<String> list = op.getValues(); 380 381 if ( op.isUserCoupled() ) { 382 userCoupled = true; 383 } else { 384 if ( !list.contains( elementSetName ) ) { 385 String s = Messages.format( "GetRecordsRequestValidator.INVALIDELEMENTSETNAME", elementSetName ); 386 throw new InvalidParameterValueException( s ); 387 } 388 } 389 390 } 391 392 /** 393 * valides if the metadataFormat parameter in a GetRecords request is valid against the policy 394 * assigned to Validator. 395 * 396 * @param condition 397 * @param outputFormat 398 * @throws InvalidParameterValueException 399 */ 400 private void validateOutputFormat( Condition condition, String outputFormat ) 401 throws InvalidParameterValueException { 402 OperationParameter op = condition.getOperationParameter( OUTPUTFORMAT ); 403 404 // is valid because no restrictions are made 405 if ( op.isAny() ) 406 return; 407 408 List<String> list = op.getValues(); 409 410 if ( op.isUserCoupled() ) { 411 userCoupled = true; 412 } else { 413 if ( !list.contains( outputFormat ) ) { 414 String s = Messages.format( "GetRecordsRequestValidator.INVALIDOUTPUTFORMAT", outputFormat ); 415 throw new InvalidParameterValueException( s ); 416 } 417 } 418 419 } 420 421 /** 422 * valides if the resultType parameter in a GetRecords request is valid against the policy 423 * assigned to Validator. 424 * 425 * @param condition 426 * @param resultType 427 * @throws InvalidParameterValueException 428 */ 429 private void validateResultType( Condition condition, String resultType ) 430 throws InvalidParameterValueException { 431 OperationParameter op = condition.getOperationParameter( RESULTTYPE ); 432 433 // is valid because no restrictions are made 434 if ( op.isAny() ) 435 return; 436 437 List<String> list = op.getValues(); 438 439 if ( op.isUserCoupled() ) { 440 userCoupled = true; 441 } else { 442 if ( !list.contains( resultType ) ) { 443 String s = Messages.format( "GetRecordsRequestValidator.INVALIDRESULTTYPE", resultType ); 444 throw new InvalidParameterValueException( s ); 445 } 446 } 447 448 } 449 450 /** 451 * valides if the sortBy parameter in a GetRecords request is valid against the policy assigned 452 * to Validator. 453 * 454 * @param condition 455 * @param sortBy 456 * @throws InvalidParameterValueException 457 */ 458 private void validateSortBy( Condition condition, SortProperty[] sortBy ) 459 throws InvalidParameterValueException { 460 OperationParameter op = condition.getOperationParameter( SORTBY ); 461 462 // is valid because no restrictions are made or 463 // nothing to validate 464 if ( op.isAny() || sortBy == null ) 465 return; 466 467 List<String> list = op.getValues(); 468 469 if ( op.isUserCoupled() ) { 470 userCoupled = true; 471 } else { 472 for ( int i = 0; i < sortBy.length; i++ ) { 473 474 if ( !list.contains( sortBy[i].getSortProperty().getAsString() ) ) { 475 String s = Messages.format( "GetRecordsRequestValidator.INVALIDSORTBY", sortBy[i] ); 476 throw new InvalidParameterValueException( s ); 477 } 478 } 479 } 480 481 } 482 483 /** 484 * valides if the sortBy parameter in a GetRecords request is valid against the policy assigned 485 * to Validator. 486 * 487 * @param condition 488 * @param typeNames 489 * @throws InvalidParameterValueException 490 */ 491 private void validateTypeNames( Condition condition, List<QualifiedName> typeNames ) 492 throws InvalidParameterValueException { 493 OperationParameter op = condition.getOperationParameter( TYPENAMES ); 494 495 // is valid because no restrictions are made 496 if ( op.isAny() ) 497 return; 498 499 List<String> list = op.getValues(); 500 501 if ( op.isUserCoupled() ) { 502 userCoupled = true; 503 } else { 504 for ( int i = 0; i < typeNames.size(); i++ ) { 505 if ( !list.contains( typeNames.get( i ).getPrefixedName() ) ) { 506 String s = Messages.format( "GetRecordsRequestValidator.INVALIDTYPENAMES", typeNames.get( i ) ); 507 throw new InvalidParameterValueException( s ); 508 } 509 } 510 } 511 512 } 513 514 /** 515 * creates a feature type that matches the parameters of a GetRecords request 516 * 517 * @return created <tt>FeatureType</tt> 518 */ 519 private static FeatureType createFeatureType() { 520 PropertyType[] ftps = new PropertyType[7]; 521 QualifiedName qn = new QualifiedName( "version" ); 522 ftps[0] = FeatureFactory.createSimplePropertyType( qn, Types.VARCHAR, false ); 523 524 qn = new QualifiedName( "maxRecords" ); 525 ftps[1] = FeatureFactory.createSimplePropertyType( qn, Types.INTEGER, false ); 526 527 qn = new QualifiedName( "outputFormat" ); 528 ftps[2] = FeatureFactory.createSimplePropertyType( qn, Types.VARCHAR, false ); 529 530 qn = new QualifiedName( "resultType" ); 531 ftps[3] = FeatureFactory.createSimplePropertyType( qn, Types.VARCHAR, false ); 532 533 qn = new QualifiedName( "sortBy" ); 534 ftps[4] = FeatureFactory.createSimplePropertyType( qn, Types.VARCHAR, 0, Integer.MAX_VALUE ); 535 536 qn = new QualifiedName( "typeNames" ); 537 ftps[5] = FeatureFactory.createSimplePropertyType( qn, Types.VARCHAR, 0, Integer.MAX_VALUE ); 538 539 qn = new QualifiedName( "elementSetName" ); 540 ftps[6] = FeatureFactory.createSimplePropertyType( qn, Types.VARCHAR, false ); 541 542 return FeatureFactory.createFeatureType( "GetRecords", false, ftps ); 543 } 544 545 }