    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    }