001 //$HeadURL: https://svn.wald.intevation.org/svn/deegree/base/branches/2.4_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 }