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