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