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 }