001: //$HeadURL: https://svn.wald.intevation.org/svn/deegree/base/trunk/src/org/deegree/security/owsrequestvalidator/wfs/GetFeatureRequestValidator.java $
002: /*---------------- FILE HEADER ------------------------------------------
003:
004: This file is part of deegree.
005: Copyright (C) 2001-2008 by:
006: EXSE, Department of Geography, University of Bonn
007: http://www.giub.uni-bonn.de/deegree/
008: lat/lon GmbH
009: http://www.lat-lon.de
010:
011: This library is free software; you can redistribute it and/or
012: modify it under the terms of the GNU Lesser General Public
013: License as published by the Free Software Foundation; either
014: version 2.1 of the License, or (at your option) any later version.
015:
016: This library is distributed in the hope that it will be useful,
017: but WITHOUT ANY WARRANTY; without even the implied warranty of
018: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
019: Lesser General Public License for more details.
020:
021: You should have received a copy of the GNU Lesser General Public
022: License along with this library; if not, write to the Free Software
023: Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
024:
025: Contact:
026:
027: Andreas Poth
028: lat/lon GmbH
029: Aennchenstr. 19
030: 53177 Bonn
031: Germany
032: E-Mail: poth@lat-lon.de
033:
034: Prof. Dr. Klaus Greve
035: Department of Geography
036: University of Bonn
037: Meckenheimer Allee 166
038: 53115 Bonn
039: Germany
040: E-Mail: greve@giub.uni-bonn.de
041:
042: ---------------------------------------------------------------------------*/
043: package org.deegree.security.owsrequestvalidator.wfs;
044:
045: import java.io.IOException;
046: import java.util.HashMap;
047: import java.util.List;
048: import java.util.Map;
049:
050: import org.deegree.datatypes.QualifiedName;
051: import org.deegree.datatypes.Types;
052: import org.deegree.framework.log.ILogger;
053: import org.deegree.framework.log.LoggerFactory;
054: import org.deegree.framework.xml.XMLParsingException;
055: import org.deegree.i18n.Messages;
056: import org.deegree.model.feature.Feature;
057: import org.deegree.model.feature.FeatureFactory;
058: import org.deegree.model.feature.FeatureProperty;
059: import org.deegree.model.feature.schema.FeatureType;
060: import org.deegree.model.feature.schema.PropertyType;
061: import org.deegree.model.filterencoding.ComplexFilter;
062: import org.deegree.model.filterencoding.FeatureFilter;
063: import org.deegree.model.filterencoding.Filter;
064: import org.deegree.model.filterencoding.FilterConstructionException;
065: import org.deegree.model.filterencoding.OperationDefines;
066: import org.deegree.ogcwebservices.InvalidParameterValueException;
067: import org.deegree.ogcwebservices.OGCWebServiceRequest;
068: import org.deegree.ogcwebservices.wfs.XMLFactory;
069: import org.deegree.ogcwebservices.wfs.operation.GetFeature;
070: import org.deegree.ogcwebservices.wfs.operation.Query;
071: import org.deegree.portal.standard.security.control.ClientHelper;
072: import org.deegree.security.GeneralSecurityException;
073: import org.deegree.security.UnauthorizedException;
074: import org.deegree.security.drm.SecurityAccess;
075: import org.deegree.security.drm.SecurityAccessManager;
076: import org.deegree.security.drm.model.Right;
077: import org.deegree.security.drm.model.RightSet;
078: import org.deegree.security.drm.model.RightType;
079: import org.deegree.security.drm.model.SecuredObject;
080: import org.deegree.security.drm.model.User;
081: import org.deegree.security.owsproxy.Condition;
082: import org.deegree.security.owsproxy.OperationParameter;
083: import org.deegree.security.owsproxy.Request;
084: import org.deegree.security.owsrequestvalidator.Policy;
085: import org.w3c.dom.Element;
086: import org.xml.sax.SAXException;
087:
088: /**
089: *
090: *
091: * @author <a href="mailto:poth@lat-lon.de">Andreas Poth </a>
092: * @author last edited by: $Author: rbezema $
093: *
094: * @version 1.1, $Revision: 10573 $, $Date: 2008-03-13 02:44:08 -0700 (Thu, 13 Mar 2008) $
095: *
096: * @since 1.1
097: */
098: class GetFeatureRequestValidator extends AbstractWFSRequestValidator {
099:
100: private static final ILogger LOG = LoggerFactory
101: .getLogger(GetFeatureRequestValidator.class);
102:
103: // known condition parameter
104: private static final String FORMAT = "format";
105:
106: private static final String MAXFEATURES = "maxFeatures";
107:
108: private static Map<QualifiedName, Filter> filterMap = new HashMap<QualifiedName, Filter>();
109:
110: private static FeatureType gfFT = null;
111:
112: static {
113: if (gfFT == null) {
114: gfFT = GetFeatureRequestValidator.createFeatureType();
115: }
116: }
117:
118: /**
119: * @param policy
120: */
121: public GetFeatureRequestValidator(Policy policy) {
122: super (policy);
123: }
124:
125: /**
126: * validates if the passed request is valid against the policy assigned to the validator. If the
127: * passed user is not <tt>null</tt> user coupled parameters will be validated against a users
128: * and rights management system.
129: */
130: @Override
131: public void validateRequest(OGCWebServiceRequest request, User user)
132: throws InvalidParameterValueException,
133: UnauthorizedException {
134:
135: userCoupled = false;
136: Request req = policy.getRequest("WFS", "GetFeature");
137: // request is valid because no restrictions are made
138: if (req.isAny())
139: return;
140: Condition condition = req.getPreConditions();
141:
142: GetFeature wfsreq = (GetFeature) request;
143:
144: validateVersion(condition, wfsreq.getVersion());
145:
146: Query[] queries = wfsreq.getQuery();
147: String[] ft = new String[queries.length];
148: for (int i = 0; i < ft.length; i++) {
149: ft[i] = queries[i].getTypeNames()[0].getFormattedString();
150: }
151:
152: validateFeatureTypes(condition, ft);
153: validateFormat(condition, wfsreq.getOutputFormat());
154: validateMaxFeatures(condition, wfsreq.getMaxFeatures());
155:
156: if (userCoupled) {
157: validateAgainstRightsDB(wfsreq, user);
158: }
159:
160: if (req.getPostConditions() != null) {
161: addFilter(wfsreq, req.getPostConditions(), user);
162: }
163:
164: }
165:
166: /**
167: * adds an additional Filter read from parameter 'instanceFilter'to the Filter of the passed
168: * GetFeature request. If parameter 'instanceFilter' is userCoupled the filter will be read from
169: * DRM, if it is not the filter defined within the responsible policy document will be used.
170: *
171: * @param wfsreq
172: * @param postConditions
173: * @param user
174: * @throws InvalidParameterValueException
175: * @throws UnauthorizedException
176: */
177: private void addFilter(GetFeature wfsreq, Condition postConditions,
178: User user) throws InvalidParameterValueException,
179: UnauthorizedException {
180: if (LOG.isDebug()) {
181: LOG.logDebug("Feature type", wfsreq.getQuery()[0]
182: .getTypeNames()[0]);
183: }
184:
185: if (postConditions.getOperationParameter("instanceFilter") != null
186: && !postConditions.getOperationParameter(
187: "instanceFilter").isAny()) {
188: Map<QualifiedName, Filter> localFilterMap;
189: if (postConditions.getOperationParameter("instanceFilter")
190: .isUserCoupled()) {
191: // read filterMap from constraints defined in deegree DRM
192: localFilterMap = readFilterFromDRM(wfsreq, user);
193: LOG.logDebug("Filter map from DRM", localFilterMap);
194: } else {
195: fillFilterMap(postConditions);
196: // use filterMap read from policy document
197: localFilterMap = filterMap;
198: }
199: Query[] queries = wfsreq.getQuery();
200: for (int i = 0; i < queries.length; i++) {
201: Filter filter = null;
202: if (queries[i].getFilter() == null) {
203: // if query does not define a filter just use the matching
204: // one from the post conditions
205: filter = localFilterMap.get(queries[i]
206: .getTypeNames()[0]);
207: } else if (queries[i].getFilter() instanceof ComplexFilter) {
208: // create a new Filter that is a combination of the
209: // original filter and the one defined in the GetFeatures
210: // PostConditions coupled by a logical 'And'
211: ComplexFilter qFilter = (ComplexFilter) queries[i]
212: .getFilter();
213: filter = localFilterMap.get(queries[i]
214: .getTypeNames()[0]);
215: if (filter == null) {
216: filter = qFilter;
217: } else {
218: filter = new ComplexFilter(qFilter,
219: (ComplexFilter) filter,
220: OperationDefines.AND);
221: }
222: } else if (queries[i].getFilter() instanceof FeatureFilter) {
223: // just take original filter if it is as feature filter
224: // because feature filter and complex filters can not
225: // be combined
226: filter = queries[i].getFilter();
227: }
228:
229: if (LOG.isDebug()) {
230: LOG.logDebug("Filter", filter == null ? " is null"
231: : filter.toXML());
232: }
233:
234: // substitue query by a new one using the re-created filter
235: queries[i] = Query.create(
236: queries[i].getPropertyNames(), queries[i]
237: .getFunctions(), queries[i]
238: .getSortProperties(), queries[i]
239: .getHandle(), queries[i]
240: .getFeatureVersion(), queries[i]
241: .getTypeNames(), queries[i]
242: .getAliases(), queries[i].getSrsName(),
243: filter, queries[i].getMaxFeatures(), queries[i]
244: .getStartPosition(), queries[i]
245: .getResultType());
246: }
247: wfsreq.setQueries(queries);
248: }
249: if (LOG.getLevel() == ILogger.LOG_DEBUG) {
250: try {
251: XMLFactory.export(wfsreq).prettyPrint(System.out);
252: } catch (Exception e) {
253: //nottin
254: }
255: }
256: }
257:
258: /**
259: *
260: * @param wfsreq
261: * @param user
262: * @return a map with the filters
263: * @throws UnauthorizedException
264: * @throws InvalidParameterValueException
265: */
266: private Map<QualifiedName, Filter> readFilterFromDRM(
267: GetFeature wfsreq, User user) throws UnauthorizedException,
268: InvalidParameterValueException {
269:
270: Map<QualifiedName, Filter> map = new HashMap<QualifiedName, Filter>();
271: try {
272: SecurityAccessManager sam = SecurityAccessManager
273: .getInstance();
274: SecurityAccess access = sam.acquireAccess(user);
275: Query[] queries = wfsreq.getQuery();
276: for (int i = 0; i < queries.length; i++) {
277: QualifiedName qn = queries[i].getTypeNames()[0];
278: SecuredObject secObj = access.getSecuredObjectByName(qn
279: .getFormattedString(),
280: ClientHelper.TYPE_FEATURETYPE);
281:
282: RightSet rs = user.getRights(access, secObj);
283: Right right = rs.getRight(secObj,
284: RightType.GETFEATURE_RESPONSE);
285: // a constraint - if available - is constructed as a OGC Filter
286: // one of the filter operations may is 'PropertyIsEqualTo' and
287: // defines a ProperyName == 'instanceFilter'. The Literal of this
288: // operation itself is a complete and valid Filter expression.
289:
290: if (right != null) {
291: ComplexFilter filter = (ComplexFilter) right
292: .getConstraints();
293: if (filter != null) {
294: // extract filter expression to be used as additional
295: // filter for a GetFeature request
296: LOG.logDebug("Filter before extraction", filter
297: .toXML());
298: filter = extractInstanceFilter(filter
299: .getOperation());
300: if (filter != null) {
301: map.put(qn, filter);
302: if (LOG.getLevel() == ILogger.LOG_DEBUG) {
303: LOG
304: .logDebug(
305: "instance filter for right GETFEATURE_RESPONSE",
306: filter.toXML());
307: }
308: } else {
309: LOG
310: .logDebug("no instance filter defined for right GETFEATURE_RESPONSE and feature type: "
311: + qn);
312: }
313: } else {
314: LOG
315: .logDebug("no constraint defined for right GETFEATURE_RESPONSE and feature type: "
316: + qn);
317: }
318: } else {
319: LOG
320: .logDebug("right GETFEATURE_RESPONSE not defined for current user and feature type: "
321: + qn);
322: }
323: }
324: } catch (GeneralSecurityException e) {
325: LOG.logError(e.getMessage(), e);
326: throw new UnauthorizedException(e.getMessage(), e);
327: } catch (FilterConstructionException e) {
328: LOG.logError(e.getMessage(), e);
329: throw new InvalidParameterValueException(e.getMessage(), e);
330: } catch (SAXException e) {
331: LOG.logError(e.getMessage(), e);
332: throw new InvalidParameterValueException(e.getMessage(), e);
333: } catch (IOException e) {
334: LOG.logError(e.getMessage(), e);
335: throw new InvalidParameterValueException(e.getMessage(), e);
336: }
337:
338: return map;
339: }
340:
341: private void fillFilterMap(Condition postConditions)
342: throws InvalidParameterValueException {
343: List<Element> complexValues = postConditions
344: .getOperationParameter("instanceFilter")
345: .getComplexValues();
346: try {
347: if (filterMap.size() == 0) {
348: for (int i = 0; i < complexValues.size(); i++) {
349: Query q = Query.create(complexValues.get(0));
350: Filter f = q.getFilter();
351: QualifiedName qn = q.getTypeNames()[0];
352: filterMap.put(qn, f);
353: }
354: }
355: } catch (XMLParsingException e) {
356: LOG.logError(e.getMessage(), e);
357: throw new InvalidParameterValueException(this .getClass()
358: .getName(), e.getMessage());
359: }
360: }
361:
362: /**
363: * valides if the format you in a GetFeature request is valid against the policy assigned to
364: * Validator. If the passed user is not <tt>null</tt> and the format parameter is user coupled
365: * the format will be validated against a users and rights management system.
366: *
367: * @param condition
368: * @param format
369: * @throws InvalidParameterValueException
370: */
371: private void validateFormat(Condition condition, String format)
372: throws InvalidParameterValueException {
373: OperationParameter op = condition.getOperationParameter(FORMAT);
374:
375: // version is valid because no restrictions are made
376: if (op.isAny())
377: return;
378:
379: List<String> validLayers = op.getValues();
380: if (op.isUserCoupled()) {
381: userCoupled = true;
382: } else {
383: if (!validLayers.contains(format)) {
384: String s = Messages.getMessage(
385: "OWSPROXY_DESCRIBEFEATURETYPE_FORMAT", format);
386: throw new InvalidParameterValueException(s);
387: }
388: }
389:
390: }
391:
392: /**
393: * valides if the format you in a GetFeature request is valid against the policy assigned to
394: * Validator. If the passed user is not <tt>null</tt> and the maxFeatures parameter is user
395: * coupled the maxFeatures will be validated against a users and rights management system.
396: *
397: * @param condition
398: * @param maxFeatures
399: * @throws InvalidParameterValueException
400: */
401: private void validateMaxFeatures(Condition condition,
402: int maxFeatures) throws InvalidParameterValueException {
403: OperationParameter op = condition
404: .getOperationParameter(MAXFEATURES);
405:
406: // version is valid because no restrictions are made
407: if (op.isAny())
408: return;
409:
410: int maxF = Integer.parseInt(op.getValues().get(0));
411:
412: if (op.isUserCoupled()) {
413: userCoupled = true;
414: } else {
415: if (maxFeatures > maxF || maxFeatures < 0) {
416: String s = Messages.getMessage(
417: "OWSPROXY_GETFEATURE_MAXFEATURE", maxFeatures);
418: throw new InvalidParameterValueException(s);
419: }
420: }
421:
422: }
423:
424: /**
425: * validates the passed WMS GetMap request against a User- and Rights-Management DB.
426: *
427: * @param wfsreq
428: * @param user
429: * @throws InvalidParameterValueException
430: */
431: private void validateAgainstRightsDB(GetFeature wfsreq, User user)
432: throws InvalidParameterValueException,
433: UnauthorizedException {
434:
435: if (user == null) {
436: throw new UnauthorizedException(
437: "no access to anonymous user");
438: }
439:
440: // create feature that describes the map request
441: FeatureProperty[] fps = new FeatureProperty[3];
442: fps[0] = FeatureFactory.createFeatureProperty(
443: new QualifiedName("version"), wfsreq.getVersion());
444: Integer mxf = new Integer(wfsreq.getMaxFeatures());
445: // The database can handle "features as a key", this feature is build from the request's
446: // features
447: fps[1] = FeatureFactory.createFeatureProperty(
448: new QualifiedName("maxfeatures"), mxf);
449: fps[2] = FeatureFactory.createFeatureProperty(
450: new QualifiedName("outputformat"), wfsreq
451: .getOutputFormat());
452:
453: Feature feature = FeatureFactory.createFeature("id", gfFT, fps);
454: Query[] queries = wfsreq.getQuery();
455: for (int i = 0; i < queries.length; i++) {
456: StringBuffer sb = new StringBuffer(200);
457: sb.append('{').append(
458: queries[i].getTypeNames()[0].getNamespace()
459: .toASCIIString());
460: sb.append("}:").append(
461: queries[i].getTypeNames()[0].getLocalName());
462: handleUserCoupledRules(user, // the user who posted the request
463: feature, // This is the Database feature
464: sb.toString(), // the Qualified name of the users
465: // Featurerequest
466: ClientHelper.TYPE_FEATURETYPE, // a primary key in the db.
467: RightType.GETFEATURE);// We're requesting a featuretype.
468: }
469:
470: }
471:
472: /**
473: * creates a feature type that matches the parameters of a GetLagendGraphic request
474: *
475: * @return created <tt>FeatureType</tt>
476: */
477: private static FeatureType createFeatureType() {
478: PropertyType[] ftps = new PropertyType[3];
479: ftps[0] = FeatureFactory.createSimplePropertyType(
480: new QualifiedName("version"), Types.VARCHAR, false);
481: ftps[1] = FeatureFactory.createSimplePropertyType(
482: new QualifiedName("maxfeatures"), Types.INTEGER, false);
483: ftps[2] = FeatureFactory
484: .createSimplePropertyType(new QualifiedName(
485: "outputformat"), Types.VARCHAR, false);
486:
487: return FeatureFactory.createFeatureType("GetFeature", false,
488: ftps);
489: }
490: }
|