001    //$HeadURL: svn+ssh://rbezema@svn.wald.intevation.org/deegree/base/branches/2.2_testing/src/org/deegree/security/owsrequestvalidator/csw/TransactionValidator.java $
002    /*----------------    FILE HEADER  ------------------------------------------
003    
004     This file is part of deegree.
005     Copyright (C) 2001-2008 by:
006     EXSE, Department of Geography, University of Bonn
007     http://www.giub.uni-bonn.de/deegree/
008     lat/lon GmbH
009     http://www.lat-lon.de
010    
011     This library is free software; you can redistribute it and/or
012     modify it under the terms of the GNU Lesser General Public
013     License as published by the Free Software Foundation; either
014     version 2.1 of the License, or (at your option) any later version.
015    
016     This library is distributed in the hope that it will be useful,
017     but WITHOUT ANY WARRANTY; without even the implied warranty of
018     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
019     Lesser General Public License for more details.
020    
021     You should have received a copy of the GNU Lesser General Public
022     License along with this library; if not, write to the Free Software
023     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
024    
025     Contact:
026    
027     Andreas Poth
028     lat/lon GmbH
029     Aennchenstr. 19
030     53177 Bonn
031     Germany
032     E-Mail: poth@lat-lon.de
033    
034     Prof. Dr. Klaus Greve
035     Department of Geography
036     University of Bonn
037     Meckenheimer Allee 166
038     53115 Bonn
039     Germany
040     E-Mail: greve@giub.uni-bonn.de
041    
042     ---------------------------------------------------------------------------*/
043    package org.deegree.security.owsrequestvalidator.csw;
044    
045    import java.io.IOException;
046    import java.net.URI;
047    import java.net.URISyntaxException;
048    import java.util.ArrayList;
049    import java.util.HashMap;
050    import java.util.List;
051    import java.util.Map;
052    
053    import org.deegree.datatypes.QualifiedName;
054    import org.deegree.datatypes.Types;
055    import org.deegree.framework.log.ILogger;
056    import org.deegree.framework.log.LoggerFactory;
057    import org.deegree.framework.util.StringTools;
058    import org.deegree.framework.xml.DOMPrinter;
059    import org.deegree.framework.xml.NamespaceContext;
060    import org.deegree.framework.xml.XMLParsingException;
061    import org.deegree.framework.xml.XMLTools;
062    import org.deegree.model.feature.Feature;
063    import org.deegree.model.feature.FeatureFactory;
064    import org.deegree.model.feature.FeatureProperty;
065    import org.deegree.model.feature.schema.FeatureType;
066    import org.deegree.model.feature.schema.PropertyType;
067    import org.deegree.model.filterencoding.ComplexFilter;
068    import org.deegree.model.filterencoding.Expression;
069    import org.deegree.model.filterencoding.FeatureFilter;
070    import org.deegree.model.filterencoding.Filter;
071    import org.deegree.model.filterencoding.FilterConstructionException;
072    import org.deegree.model.filterencoding.Literal;
073    import org.deegree.model.filterencoding.LogicalOperation;
074    import org.deegree.model.filterencoding.OperationDefines;
075    import org.deegree.model.filterencoding.PropertyIsBetweenOperation;
076    import org.deegree.model.filterencoding.PropertyIsCOMPOperation;
077    import org.deegree.model.filterencoding.PropertyIsLikeOperation;
078    import org.deegree.model.filterencoding.PropertyIsNullOperation;
079    import org.deegree.model.filterencoding.PropertyName;
080    import org.deegree.ogcbase.CommonNamespaces;
081    import org.deegree.ogcwebservices.InvalidParameterValueException;
082    import org.deegree.ogcwebservices.OGCWebServiceRequest;
083    import org.deegree.ogcwebservices.csw.manager.Delete;
084    import org.deegree.ogcwebservices.csw.manager.Insert;
085    import org.deegree.ogcwebservices.csw.manager.Operation;
086    import org.deegree.ogcwebservices.csw.manager.Transaction;
087    import org.deegree.ogcwebservices.csw.manager.Update;
088    import org.deegree.ogcwebservices.wfs.operation.Query;
089    import org.deegree.portal.standard.security.control.ClientHelper;
090    import org.deegree.security.GeneralSecurityException;
091    import org.deegree.security.UnauthorizedException;
092    import org.deegree.security.drm.SecurityAccess;
093    import org.deegree.security.drm.SecurityAccessManager;
094    import org.deegree.security.drm.model.Right;
095    import org.deegree.security.drm.model.RightSet;
096    import org.deegree.security.drm.model.RightType;
097    import org.deegree.security.drm.model.SecuredObject;
098    import org.deegree.security.drm.model.User;
099    import org.deegree.security.owsproxy.Condition;
100    import org.deegree.security.owsproxy.OperationParameter;
101    import org.deegree.security.owsproxy.Request;
102    import org.deegree.security.owsrequestvalidator.Messages;
103    import org.deegree.security.owsrequestvalidator.Policy;
104    import org.w3c.dom.Element;
105    import org.xml.sax.SAXException;
106    
107    /**
108     * Validator for OGC CSW Transaction requests. It will validated values of:<br>
109     * <ul>
110     * <li>service version</li>
111     * <li>operation</li>
112     * <li>type names</li>
113     * <li>metadata standard</li>
114     * </ul>
115     * 
116     * @version $Revision: 9346 $
117     * @author <a href="mailto:poth@lat-lon.de">Andreas Poth</a>
118     * @author last edited by: $Author: apoth $
119     * 
120     * @version 1.0. $Revision: 9346 $, $Date: 2007-12-27 17:39:07 +0100 (Do, 27 Dez 2007) $
121     * 
122     * @since 2.0
123     */
124    public class TransactionValidator extends AbstractCSWRequestValidator {
125    
126        private static final ILogger LOG = LoggerFactory.getLogger( TransactionValidator.class );
127    
128        private final static String METADATAFORMAT = "metadataFormat";
129    
130        private final static String TYPENAME = "typeName";
131    
132        private static Map<QualifiedName, Filter> filterMap = new HashMap<QualifiedName, Filter>();
133    
134        private static FeatureType insertFT = null;
135    
136        private static FeatureType updateFT = null;
137    
138        private static FeatureType deleteFT = null;
139    
140        private static NamespaceContext nsc = CommonNamespaces.getNamespaceContext();
141    
142        static {
143            if ( insertFT == null ) {
144                insertFT = TransactionValidator.createInsertFeatureType();
145            }
146            if ( updateFT == null ) {
147                updateFT = TransactionValidator.createUpdateFeatureType();
148            }
149            if ( deleteFT == null ) {
150                deleteFT = TransactionValidator.createDeleteFeatureType();
151            }
152        }
153    
154        /**
155         * 
156         * @param policy
157         */
158        public TransactionValidator( Policy policy ) {
159            super( policy );
160        }
161    
162        @Override
163        public void validateRequest( OGCWebServiceRequest request, User user )
164                                throws InvalidParameterValueException, UnauthorizedException {
165    
166            userCoupled = false;
167    
168            Transaction cswreq = (Transaction) request;
169    
170            List<Operation> ops = cswreq.getOperations();
171            for ( int i = 0; i < ops.size(); i++ ) {
172                userCoupled = false;
173                if ( ops.get( i ) instanceof Insert ) {
174                    Request req = policy.getRequest( "CSW", "CSW_Insert" );
175                    if ( req != null ) {
176                        if ( !req.isAny() ) {
177                            Condition condition = req.getPreConditions();
178                            validateOperation( condition, (Insert) ops.get( i ) );
179                        }
180                        if ( userCoupled ) {
181                            validateAgainstRightsDB( (Insert) ops.get( i ), user );
182                        }
183                        if ( req.getPostConditions() != null ) {
184                            evaluateFilter( ops.get( i ), req.getPostConditions(), user );
185                        }
186                    } else {
187                        throw new UnauthorizedException( "You are not allowed to Insert items from the repository." );
188                    }
189                } else if ( ops.get( i ) instanceof Update ) {
190                    Request req = policy.getRequest( "CSW", "CSW_Update" );
191                    if ( req != null ) {
192                        if ( !req.isAny() ) {
193                            Condition condition = req.getPreConditions();
194                            validateOperation( condition, (Update) ops.get( i ) );
195                        }
196                        if ( userCoupled ) {
197                            validateAgainstRightsDB( (Update) ops.get( i ), user );
198                        }
199                        if ( req.getPostConditions() != null ) {
200                            evaluateFilter( ops.get( i ), req.getPostConditions(), user );
201                        }
202                    } else {
203                        throw new UnauthorizedException( "You are not allowed to update items from the repository." );
204                    }
205                } else if ( ops.get( i ) instanceof Delete ) {
206                    Request req = policy.getRequest( "CSW", "CSW_Delete" );
207                    if ( req != null ) {
208                        if ( !req.isAny() ) {
209                            Condition condition = req.getPreConditions();
210                            validateOperation( condition, (Delete) ops.get( i ) );
211                        }
212                        if ( userCoupled ) {
213                            validateAgainstRightsDB( (Delete) ops.get( i ), user );
214                        }
215                        if ( req.getPostConditions() != null ) {
216                            evaluateFilter( ops.get( i ), req.getPostConditions(), user );
217                        }
218                    } else {
219                        throw new UnauthorizedException( "You are not allowed to delete items from the repository." );
220                    }
221                }
222            }
223    
224        }
225    
226        /**
227         * adds a filter to the passed opertaion. If the condition is userCoupled the filter will be read from the DRM
228         * otherwise it is read from the current WFS policy file
229         * 
230         * @param operation
231         * @param postConditions
232         * @param user
233         * @throws InvalidParameterValueException
234         * @throws UnauthorizedException
235         */
236        private void evaluateFilter( Operation operation, Condition postConditions, User user )
237                                throws InvalidParameterValueException, UnauthorizedException {
238            if ( postConditions.getOperationParameter( "instanceFilter" ) != null ) {
239                if ( postConditions.getOperationParameter( "instanceFilter" ).isAny() ) {
240                    return;
241                }
242    
243                List<QualifiedName> qns = getMetadataTypes( operation );
244                Map<QualifiedName, Filter> filter = null;
245                if ( postConditions.getOperationParameter( "instanceFilter" ).isUserCoupled() ) {
246                    // read filterMap from constraints defined in deegree DRM
247                    filter = readFilterFromDRM( qns, operation, user );
248                } else {
249                    fillFilterMap( postConditions );
250                    filter = filterMap;
251                }
252    
253                if ( operation instanceof Update || operation instanceof Delete ) {
254                    handleUpdateDelete( operation, filter );
255                } else {
256                    handleInsert( (Insert) operation, filter );
257                }
258            } else if ( operation instanceof Insert ) {
259                // because of its pessimistic security concept deegree assumed
260                // that there is a security validation if no filter is available
261                // for a metadata type
262                String msg = org.deegree.i18n.Messages.getMessage( "OWSPROXY_CSW_INSERT_NOT_ALLOWED" );
263                throw new UnauthorizedException( msg );
264            }
265        }
266    
267        /**
268         * validates if the content of an Insert operation matches the defined security conditions
269         * 
270         * @param operation
271         * @param filterMap
272         * @throws InvalidParameterValueException
273         * @throws UnauthorizedException
274         */
275        private void handleInsert( Insert operation, Map<QualifiedName, Filter> filterMap )
276                                throws InvalidParameterValueException, UnauthorizedException {
277    
278            List<Element> records = operation.getRecords();
279    
280            for ( int i = 0; i < records.size(); i++ ) {
281                Element rec = records.get( i );
282                String name = rec.getLocalName();
283                URI uri;
284                try {
285                    uri = new URI( rec.getNamespaceURI() );
286                } catch ( URISyntaxException e ) {
287                    LOG.logError( e.getMessage(), e );
288                    throw new InvalidParameterValueException( e.getMessage(), e );
289                }
290                QualifiedName qn = new QualifiedName( "a", name, uri );
291                
292                ComplexFilter filter = (ComplexFilter) filterMap.get( qn );
293                if ( filter != null ) {
294                    boolean match = false;
295                    // just if a constraint is defined on combination of insert
296                    // operation and the current metadata type it can be evaluated
297                    LogicalOperation lo = (LogicalOperation) filter.getOperation();
298                    if ( lo.getOperatorId() == OperationDefines.AND ) {
299                        // handle conditions connected by logical AND
300                        List<org.deegree.model.filterencoding.Operation> args = lo.getArguments();
301                        match = evaluateLogicalAnd( rec, args );
302                    } else if ( lo.getOperatorId() == OperationDefines.OR ) {
303                        // handle conditions connected by logical OR
304                        List<org.deegree.model.filterencoding.Operation> args = lo.getArguments();
305                        match = evaluateLogicalOr( rec, args );
306                    } else {
307                        // NOT
308                    }
309                    if ( !match ) {
310                        // if loop has been left and 'match' is still false
311                        // no condition has matched
312                        String msg = org.deegree.i18n.Messages.getMessage( "OWSPROXY_CSW_INSERT_NOT_ALLOWED" );
313                        throw new UnauthorizedException( msg );
314                    }
315                } else {
316                    // because of its pessimistic security concept deegree assumed
317                    // that there is a security validation if no filter is available
318                    // for a metadata type
319                    String msg = org.deegree.i18n.Messages.getMessage( "OWSPROXY_CSW_INSERT_NOT_ALLOWED" );
320                    throw new UnauthorizedException( msg );
321                }
322            }
323    
324        }
325    
326        /**
327         * evaluates if operations surrounded by a logical OR. If none of the contained conditions are fullfilled an
328         * {@link UnauthorizedException} will be thrown
329         * 
330         * @param rec
331         * @param args
332         * @return
333         * @throws InvalidParameterValueException
334         */
335        private boolean evaluateLogicalOr( Element rec, List<org.deegree.model.filterencoding.Operation> args )
336                                throws InvalidParameterValueException {
337            boolean match = false;
338            for ( org.deegree.model.filterencoding.Operation op : args ) {
339                try {
340                    match = evaluate( rec, op );
341                    if ( match ) {
342                        // loop can be breaked if at least one condition
343                        // matches
344                        return true;
345                    }
346                } catch ( XMLParsingException e ) {
347                    LOG.logError( e.getMessage(), e );
348                    throw new InvalidParameterValueException( e.getMessage(), e );
349                }
350            }
351            return match;
352        }
353    
354        /**
355         * evaluates if operations surrounded by a logical AND. If at least one of the contained conditions is not
356         * fullfilled an {@link UnauthorizedException} will be thrown
357         * 
358         * @param rec
359         * @param args
360         * @return
361         * @throws InvalidParameterValueException
362         */
363        private boolean evaluateLogicalAnd( Element rec, List<org.deegree.model.filterencoding.Operation> args )
364                                throws InvalidParameterValueException {
365            boolean match = false;
366            for ( org.deegree.model.filterencoding.Operation op : args ) {
367                try {
368                    match = evaluate( rec, op );
369                    if ( !match ) {
370                        break;
371                    }
372                } catch ( XMLParsingException e ) {
373                    LOG.logError( e.getMessage(), e );
374                    throw new InvalidParameterValueException( e.getMessage(), e );
375                }
376            }
377            return match;
378        }
379    
380        /**
381         * evaluates if the passed record matches the passed filter operation
382         * 
383         * @param element
384         * @param op
385         * @return
386         * @throws XMLParsingException
387         * @throws InvalidParameterValueException
388         */
389        private boolean evaluate( Element record, org.deegree.model.filterencoding.Operation op )
390                                throws XMLParsingException, InvalidParameterValueException {
391            if( op == null ){
392                throw new InvalidParameterValueException( "The operation cannot be null" );
393            }
394            boolean matches = false;
395            Expression exp = null;
396            if ( op.getOperatorId() == OperationDefines.PROPERTYISEQUALTO
397                 || op.getOperatorId() == OperationDefines.PROPERTYISGREATERTHAN
398                 || op.getOperatorId() == OperationDefines.PROPERTYISGREATERTHANOREQUALTO
399                 || op.getOperatorId() == OperationDefines.PROPERTYISLESSTHAN
400                 || op.getOperatorId() == OperationDefines.PROPERTYISLESSTHANOREQUALTO ) {
401                matches = evaluateCOMP( record, (PropertyIsCOMPOperation) op );
402            } else if ( op.getOperatorId() == OperationDefines.PROPERTYISNULL ) {
403                PropertyName pn = ( (PropertyIsNullOperation) op ).getPropertyName();
404                String xpath = pn.getValue().getAsString();
405                nsc.addAll( pn.getValue().getNamespaceContext() );
406                matches = XMLTools.getNode( record, xpath, nsc ) == null;
407            } else if ( op.getOperatorId() == OperationDefines.PROPERTYISBETWEEN ) {
408                PropertyName pn = ( (PropertyIsBetweenOperation) op ).getPropertyName();
409                String xpath = pn.getValue().getAsString();
410                nsc.addAll( pn.getValue().getNamespaceContext() );
411                exp = ( (PropertyIsBetweenOperation) op ).getLowerBoundary();
412                String lower = ( (Literal) exp ).getValue();
413                exp = ( (PropertyIsBetweenOperation) op ).getUpperBoundary();
414                String upper = ( (Literal) exp ).getValue();
415                String value = XMLTools.getNodeAsString( record, xpath, nsc, null );
416                matches = lower.compareTo( value ) < 0 && upper.compareTo( value ) > 0;
417            } else if ( op.getOperatorId() == OperationDefines.PROPERTYISLIKE ) {
418                PropertyName pn = ( (PropertyIsLikeOperation) op ).getPropertyName();
419                String xpath = pn.getValue().getAsString();
420                nsc.addAll( pn.getValue().getNamespaceContext() );
421                String value = XMLTools.getNodeAsString( record, xpath, nsc, null );
422                String literal = ( (PropertyIsLikeOperation) op ).getLiteral().getValue();
423                if( literal == null ){
424                    throw new InvalidParameterValueException( "No literal found resulting from the xpath: " + xpath + " therefore you're not authorized." );
425                }
426                matches = ( (PropertyIsLikeOperation) op ).matches( literal, value );
427            } else if ( op.getOperatorId() == OperationDefines.AND ) {
428                List<org.deegree.model.filterencoding.Operation> ops = ( (LogicalOperation) op ).getArguments();
429                return evaluateLogicalAnd( record, ops );
430            } else if ( op.getOperatorId() == OperationDefines.OR ) {
431                List<org.deegree.model.filterencoding.Operation> ops = ( (LogicalOperation) op ).getArguments();
432                return evaluateLogicalOr( record, ops );
433            }
434            return matches;
435        }
436    
437        private boolean evaluateCOMP( Element record, PropertyIsCOMPOperation op )
438                                throws XMLParsingException {
439            boolean matches = false;
440            Expression exp = op.getFirstExpression();
441            PropertyName pn = (PropertyName) exp;
442            String xpath = pn.getValue().getAsString();
443            nsc.addAll( pn.getValue().getNamespaceContext() );
444            String value = XMLTools.getNodeAsString( record, xpath, nsc, null );
445            exp = op.getSecondExpression();
446            Literal literal = (Literal) exp;
447            if ( op.getOperatorId() == OperationDefines.PROPERTYISEQUALTO ) {
448                matches = literal.getValue().equals( value );
449            } else if ( op.getOperatorId() == OperationDefines.PROPERTYISGREATERTHAN ) {
450                matches = value != null && literal.getValue().compareTo( value ) < 0;
451            } else if ( op.getOperatorId() == OperationDefines.PROPERTYISGREATERTHANOREQUALTO ) {
452                matches = value != null && literal.getValue().compareTo( value ) <= 0;
453            } else if ( op.getOperatorId() == OperationDefines.PROPERTYISLESSTHAN ) {
454                matches = value != null && literal.getValue().compareTo( value ) > 0;
455            } else if ( op.getOperatorId() == OperationDefines.PROPERTYISLESSTHANOREQUALTO ) {
456                matches = value != null && literal.getValue().compareTo( value ) >= 0;
457            }
458            return matches;
459        }
460    
461        /**
462         * redefines the constraints of the passed operation if necessary
463         * 
464         * @param operation
465         * @param filter
466         * @return
467         */
468        private Operation handleUpdateDelete( Operation operation, Map<QualifiedName, Filter> filter ) {
469            Filter tmpFilter = null;
470            Filter opFilter = null;
471            if ( operation instanceof Update ) {
472                opFilter = ( (Update) operation ).getConstraint();
473            } else {
474                opFilter = ( (Delete) operation ).getConstraint();
475            }
476            if ( opFilter instanceof ComplexFilter ) {
477                // create a new Filter that is a combination of the
478                // original filter and the one defined in the GetFeatures
479                // PostConditions coupled by a logical 'And'
480                ComplexFilter qFilter = (ComplexFilter) opFilter;
481                if ( filter == null ) {
482                    // not filter defined in security managment
483                    tmpFilter = qFilter;
484                } else {
485                    // merger filter of update/delete operation and filter
486                    // defined in security managment
487                    tmpFilter = filter.values().iterator().next();
488                    tmpFilter = new ComplexFilter( qFilter, (ComplexFilter) tmpFilter, OperationDefines.AND );
489                }
490            } else if ( opFilter instanceof FeatureFilter ) {
491                // just take original filter if it is as feature filter
492                // because feature filter and complex filters can not
493                // be combined
494                tmpFilter = opFilter;
495            }
496            if ( operation instanceof Update ) {
497                ( (Update) operation ).setConstraint( tmpFilter );
498            } else {
499                ( (Delete) operation ).setConstraint( tmpFilter );
500            }
501            return operation;
502        }
503    
504        /**
505         * reads a filter m
506         * 
507         * @param operation
508         * @param user
509         * @return
510         * @throws UnauthorizedException
511         * @throws InvalidParameterValueException
512         */
513        private Map<QualifiedName, Filter> readFilterFromDRM( List<QualifiedName> qns, Operation operation, User user )
514                                throws UnauthorizedException, InvalidParameterValueException {
515            Map<QualifiedName, Filter> f = new HashMap<QualifiedName, Filter>();
516            try {
517                SecurityAccessManager sam = SecurityAccessManager.getInstance();
518                SecurityAccess access = sam.acquireAccess( user );
519    
520                for ( int i = 0; i < qns.size(); i++ ) {
521                    List<ComplexFilter> foundFilters = new ArrayList<ComplexFilter>();
522                    SecuredObject secObj = access.getSecuredObjectByName( qns.get( i ).getFormattedString(),
523                                                                          ClientHelper.TYPE_METADATASCHEMA );
524                    RightSet rs = user.getRights( access, secObj );
525                    Right right = null;
526                    if ( operation instanceof Update ) {
527                        right = rs.getRight( secObj, RightType.UPDATE_RESPONSE );
528                    } else if ( operation instanceof Delete ) {
529                        right = rs.getRight( secObj, RightType.DELETE_RESPONSE );
530                    } else {
531                        right = rs.getRight( secObj, RightType.INSERT_RESPONSE );
532                    }
533                    // a constraint - if available - is constructed as a OGC Filter
534                    // one of the filter operations may is 'PropertyIsEqualTo' and
535                    // defines a ProperyName == 'instanceFilter'. The Literal of this
536                    // operation itself is a complete and valid Filter expression.
537                    if ( right != null ) {
538                        ComplexFilter filter = (ComplexFilter) right.getConstraints();
539                        if ( filter != null ) {
540                            // extract filter expression to be used as additional
541                            // filter for a GetFeature request
542                            extractInstanceFilter( filter.getOperation(), foundFilters );
543                            if ( foundFilters.size() == 1 ) {
544                                filter = foundFilters.get( 0 );
545                            } else if ( foundFilters.size() > 1 ) {
546                                List<org.deegree.model.filterencoding.Operation> list = new ArrayList<org.deegree.model.filterencoding.Operation>();
547                                for ( ComplexFilter cf : foundFilters ) {
548                                    list.add( cf.getOperation() );
549                                }
550                                LogicalOperation lo = new LogicalOperation( OperationDefines.OR, list );
551                                filter = new ComplexFilter( lo );
552                            }
553                            f.put( qns.get( i ), filter );
554                        }
555                    }
556                }
557    
558            } catch ( GeneralSecurityException e ) {
559                LOG.logError( e.getMessage(), e );
560                throw new UnauthorizedException( e.getMessage(), e );
561            } catch ( FilterConstructionException e ) {
562                LOG.logError( e.getMessage(), e );
563                throw new InvalidParameterValueException( e.getMessage(), e );
564            } catch ( SAXException e ) {
565                LOG.logError( e.getMessage(), e );
566                throw new InvalidParameterValueException( e.getMessage(), e );
567            } catch ( IOException e ) {
568                LOG.logError( e.getMessage(), e );
569                throw new InvalidParameterValueException( e.getMessage(), e );
570            }
571            return f;
572        }
573    
574        /**
575         * returns the list a metadata types targeted by an operation
576         * 
577         * @param operation
578         * @return
579         * @throws InvalidParameterValueException
580         */
581        private List<QualifiedName> getMetadataTypes( Operation operation )
582                                throws InvalidParameterValueException {
583            List<QualifiedName> qns = new ArrayList<QualifiedName>();
584            if ( operation instanceof Update ) {
585    
586            } else if ( operation instanceof Delete ) {
587    
588            } else {
589                // get list of all record types to be inserted
590                List<Element> recs = ( (Insert) operation ).getRecords();
591                for ( int i = 0; i < recs.size(); i++ ) {
592                    String name = recs.get( i ).getLocalName();
593                    URI uri;
594                    try {
595                        uri = new URI( recs.get( i ).getNamespaceURI() );
596                    } catch ( URISyntaxException e ) {
597                        LOG.logError( e.getMessage(), e );
598                        throw new InvalidParameterValueException( e.getMessage(), e );
599                    }
600                    QualifiedName qn = new QualifiedName( "a", name, uri );
601                    if ( !qns.contains( qn ) ) {
602                        qns.add( qn );
603                    }
604                }
605            }
606            return qns;
607        }
608    
609        private void fillFilterMap( Condition postConditions )
610                                throws InvalidParameterValueException {
611            List<Element> complexValues = postConditions.getOperationParameter( "instanceFilter" ).getComplexValues();
612            try {
613                if ( filterMap.size() == 0 ) {
614                    for ( int i = 0; i < complexValues.size(); i++ ) {
615                        Query q = Query.create( complexValues.get( 0 ) );
616                        Filter f = q.getFilter();
617                        QualifiedName qn = q.getTypeNames()[0];
618                        filterMap.put( qn, f );
619                    }
620                }
621            } catch ( XMLParsingException e ) {
622                LOG.logError( e.getMessage(), e );
623                throw new InvalidParameterValueException( this.getClass().getName(), e.getMessage() );
624            }
625        }
626    
627        /**
628         * 
629         * @param condition
630         * @param insert
631         * @throws InvalidParameterValueException
632         */
633        private void validateOperation( Condition condition, Insert insert )
634                                throws InvalidParameterValueException {
635    
636            OperationParameter op = condition.getOperationParameter( METADATAFORMAT );
637    
638            // version is valid because no restrictions are made
639            if ( op.isAny() ) {
640                return;
641            }
642    
643            List vals = op.getValues();
644    
645            List<Element> records = insert.getRecords();
646            for ( int i = 0; i < records.size(); i++ ) {
647                String name = records.get( i ).getLocalName();
648                String ns = records.get( i ).getNamespaceURI();
649                String qn = StringTools.concat( 200, '{', ns, "}:", name );
650    
651                if ( !vals.contains( qn ) ) {
652                    if ( !op.isUserCoupled() ) {
653                        String s = Messages.format( "CSWTransactionValidator.INVALIDMETADATAFORMAT", qn );
654                        throw new InvalidParameterValueException( s );
655                    }
656                    userCoupled = true;
657                    break;
658                }
659            }
660    
661        }
662    
663        /**
664         * 
665         * @param condition
666         * @param delete
667         * @throws InvalidParameterValueException
668         */
669        private void validateOperation( Condition condition, Delete delete )
670                                throws InvalidParameterValueException {
671            OperationParameter op = condition.getOperationParameter( TYPENAME );
672    
673            // version is valid because no restrictions are made
674            if ( op.isAny() )
675                return;
676    
677            URI typeName = delete.getTypeName();
678    
679            if ( typeName == null ) {
680                String s = Messages.getString( "CSWTransactionValidator.INVALIDDELETETYPENAME1" );
681                throw new InvalidParameterValueException( s );
682            }
683    
684            List vals = op.getValues();
685            if ( !vals.contains( typeName.toASCIIString() ) ) {
686                if ( !op.isUserCoupled() ) {
687                    String s = Messages.format( "CSWTransactionValidator.INVALIDDELETETYPENAME2", typeName );
688                    throw new InvalidParameterValueException( s );
689                }
690                userCoupled = true;
691            }
692    
693        }
694    
695        /**
696         * 
697         * @param condition
698         * @param update
699         * @throws InvalidParameterValueException
700         */
701        private void validateOperation( Condition condition, Update update )
702                                throws InvalidParameterValueException {
703    
704            URI typeName = update.getTypeName();
705            Element record = update.getRecord();
706    
707            if ( typeName == null && record == null ) {
708                String s = Messages.getString( "CSWTransactionValidator.INVALIDUPDATETYPENAME1" );
709                throw new InvalidParameterValueException( s );
710            }
711    
712            OperationParameter op = condition.getOperationParameter( TYPENAME );
713            List vals = op.getValues();
714    
715            if ( typeName != null && !vals.contains( typeName.toASCIIString() ) ) {
716                // version is valid because no restrictions are made
717                if ( op.isAny() ) {
718                    return;
719                }
720                if ( !op.isUserCoupled() ) {
721                    String s = Messages.format( "CSWTransactionValidator.INVALIDUPDATETYPENAME2", typeName );
722                    throw new InvalidParameterValueException( s );
723                }
724                userCoupled = true;
725            } else {
726                op = condition.getOperationParameter( METADATAFORMAT );
727                // version is valid because no restrictions are made
728                if ( op.isAny() ) {
729                    return;
730                }
731                vals = op.getValues();
732                String name = record.getLocalName();
733                String ns = record.getNamespaceURI();
734                String qn = StringTools.concat( 200, '{', ns, "}:", name );
735                if ( !vals.contains( qn ) ) {
736                    if ( !op.isUserCoupled() ) {
737                        String s = Messages.format( "CSWTransactionValidator.INVALIDMETADATAFORMAT", qn );
738                        throw new InvalidParameterValueException( s );
739                    }
740                    userCoupled = true;
741                }
742            }
743        }
744    
745        /**
746         * validates a Transcation.Delete request against the underlying users and rights management system
747         * 
748         * @param delete
749         * @param version
750         * @param user
751         * @throws InvalidParameterValueException
752         * @throws UnauthorizedException
753         */
754        private void validateAgainstRightsDB( Delete delete, User user )
755                                throws InvalidParameterValueException, UnauthorizedException {
756            if ( user == null ) {
757                throw new UnauthorizedException( Messages.getString( "RequestValidator.NOACCESS" ) );
758            }
759    
760            // create a feature instance from the parameters of the GetRecords request
761            // to enable comparsion with a filter encoding expression stored in the
762            // assigned rights management system
763            List<FeatureProperty> fps = new ArrayList<FeatureProperty>();
764    
765            URI typeName = delete.getTypeName();
766            String tn = null;
767            if ( typeName != null ) {
768                tn = typeName.toASCIIString();
769            }
770            FeatureProperty fp = FeatureFactory.createFeatureProperty( new QualifiedName( "typeName" ), tn );
771            fps.add( fp );
772            Feature feature = FeatureFactory.createFeature( "id", insertFT, fps );
773    
774            handleUserCoupledRules( user, // the user who posted the request
775                                    feature, // This is the Database feature
776                                    // the name the metadataFormat to be deleted
777                                    "{http://www.opengis.net/cat/csw}:profil", ClientHelper.TYPE_METADATASCHEMA, // a
778                                    // primary
779                                    // key
780                                    // in
781                                    // the
782                                    // db.
783                                    RightType.DELETE );// We're requesting a featuretype.
784    
785        }
786    
787        /**
788         * validates a Transcation.Update request against the underlying users and rights management system
789         * 
790         * @param update
791         * @param user
792         */
793        private void validateAgainstRightsDB( Update update, User user ) {
794            throw new NoSuchMethodError( getClass().getName() + ".validateAgainstRightsDB not implemented yet" );
795        }
796    
797        /**
798         * validates the passed insert operation against the deegree user/rights management system
799         * 
800         * @param insert
801         * @param version
802         * @param user
803         * @throws InvalidParameterValueException
804         * @throws UnauthorizedException
805         */
806        private void validateAgainstRightsDB( Insert insert, User user )
807                                throws InvalidParameterValueException, UnauthorizedException {
808    
809            if ( user == null ) {
810                throw new UnauthorizedException( Messages.getString( "RequestValidator.NOACCESS" ) );
811            }
812    
813            // create a feature instance from the parameters of the GetRecords request
814            // to enable comparsion with a filter encoding expression stored in the
815            // assigned rights management system
816            List<FeatureProperty> fps = new ArrayList<FeatureProperty>();
817            FeatureProperty fp = null;
818            fps.add( fp );
819    
820            Feature feature = FeatureFactory.createFeature( "id", insertFT, fps );
821    
822            List<Element> records = insert.getRecords();
823            for ( int i = 0; i < records.size(); i++ ) {
824                String name = records.get( i ).getLocalName();
825                String ns = records.get( i ).getNamespaceURI();
826                String qn = StringTools.concat( 200, '{', ns, "}:", name );
827    
828                handleUserCoupledRules( user, // the user who posted the request
829                                        feature, // This is the Database feature
830                                        qn, // the Qualified name of the users Featurerequest
831                                        ClientHelper.TYPE_METADATASCHEMA, // a primary key in the db.
832                                        RightType.INSERT );// We're requesting a featuretype.
833            }
834    
835        }
836    
837        /**
838         * creates a feature type that matches the parameters of a Insert operation
839         * 
840         * @return created <tt>FeatureType</tt>
841         */
842        private static FeatureType createInsertFeatureType() {
843            PropertyType[] ftps = new PropertyType[1];
844            ftps[0] = FeatureFactory.createSimplePropertyType( new QualifiedName( "metadataFormat" ), Types.VARCHAR, false );
845    
846            return FeatureFactory.createFeatureType( "CSW_Insert", false, ftps );
847        }
848    
849        /**
850         * creates a feature type that matches the parameters of a Update operation
851         * 
852         * @return created <tt>FeatureType</tt>
853         */
854        private static FeatureType createUpdateFeatureType() {
855            PropertyType[] ftps = new PropertyType[2];
856            ftps[0] = FeatureFactory.createSimplePropertyType( new QualifiedName( "metadataFormat" ), Types.VARCHAR, false );
857            ftps[1] = FeatureFactory.createSimplePropertyType( new QualifiedName( "typeName" ), Types.VARCHAR, false );
858    
859            return FeatureFactory.createFeatureType( "CSW_Update", false, ftps );
860        }
861    
862        /**
863         * creates a feature type that matches the parameters of a Delete operation
864         * 
865         * @return created <tt>FeatureType</tt>
866         */
867        private static FeatureType createDeleteFeatureType() {
868            PropertyType[] ftps = new PropertyType[1];
869            ftps[0] = FeatureFactory.createSimplePropertyType( new QualifiedName( "typeName" ), Types.VARCHAR, false );
870    
871            return FeatureFactory.createFeatureType( "CSW_Delete", false, ftps );
872        }
873    
874    }