001: //$HeadURL: https://svn.wald.intevation.org/svn/deegree/base/trunk/src/org/deegree/security/owsrequestvalidator/wfs/TransactionValidator.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.ArrayList;
047: import java.util.HashMap;
048: import java.util.List;
049: import java.util.Map;
050:
051: import org.deegree.datatypes.QualifiedName;
052: import org.deegree.datatypes.Types;
053: import org.deegree.framework.log.ILogger;
054: import org.deegree.framework.log.LoggerFactory;
055: import org.deegree.framework.util.StringTools;
056: import org.deegree.framework.xml.XMLParsingException;
057: import org.deegree.i18n.Messages;
058: import org.deegree.model.feature.Feature;
059: import org.deegree.model.feature.FeatureFactory;
060: import org.deegree.model.feature.FeatureProperty;
061: import org.deegree.model.feature.schema.FeatureType;
062: import org.deegree.model.feature.schema.PropertyType;
063: import org.deegree.model.filterencoding.ComplexFilter;
064: import org.deegree.model.filterencoding.FeatureFilter;
065: import org.deegree.model.filterencoding.Filter;
066: import org.deegree.model.filterencoding.FilterConstructionException;
067: import org.deegree.model.filterencoding.OperationDefines;
068: import org.deegree.ogcwebservices.InvalidParameterValueException;
069: import org.deegree.ogcwebservices.OGCWebServiceRequest;
070: import org.deegree.ogcwebservices.wfs.XMLFactory;
071: import org.deegree.ogcwebservices.wfs.operation.Query;
072: import org.deegree.ogcwebservices.wfs.operation.transaction.Delete;
073: import org.deegree.ogcwebservices.wfs.operation.transaction.Insert;
074: import org.deegree.ogcwebservices.wfs.operation.transaction.Transaction;
075: import org.deegree.ogcwebservices.wfs.operation.transaction.TransactionOperation;
076: import org.deegree.ogcwebservices.wfs.operation.transaction.Update;
077: import org.deegree.portal.standard.security.control.ClientHelper;
078: import org.deegree.security.GeneralSecurityException;
079: import org.deegree.security.UnauthorizedException;
080: import org.deegree.security.drm.SecurityAccess;
081: import org.deegree.security.drm.SecurityAccessManager;
082: import org.deegree.security.drm.model.Right;
083: import org.deegree.security.drm.model.RightSet;
084: import org.deegree.security.drm.model.RightType;
085: import org.deegree.security.drm.model.SecuredObject;
086: import org.deegree.security.drm.model.User;
087: import org.deegree.security.owsproxy.Condition;
088: import org.deegree.security.owsproxy.OperationParameter;
089: import org.deegree.security.owsproxy.Request;
090: import org.deegree.security.owsrequestvalidator.Policy;
091: import org.w3c.dom.Element;
092: import org.xml.sax.SAXException;
093:
094: /**
095: * Validator for OGC CSW Transaction requests. It will validated values of:<br> <ul> <li>service
096: * version</li> <li>operation</li> <li>type names</li> <li>metadata standard</li> </ul>
097: *
098: * @version $Revision: 10573 $
099: * @author <a href="mailto:poth@lat-lon.de">Andreas Poth</a>
100: * @author last edited by: $Author: rbezema $
101: *
102: * @version 1.0. $Revision: 10573 $, $Date: 2008-03-13 02:44:08 -0700 (Thu, 13 Mar 2008) $
103: *
104: * @since 2.0
105: */
106: public class TransactionValidator extends AbstractWFSRequestValidator {
107:
108: private static final ILogger LOG = LoggerFactory
109: .getLogger(TransactionValidator.class);
110:
111: private final static String TYPENAME = "typeName";
112:
113: private static Map<QualifiedName, Filter> filterMap = new HashMap<QualifiedName, Filter>();
114:
115: private static FeatureType insertFT = null;
116:
117: private static FeatureType updateFT = null;
118:
119: private static FeatureType deleteFT = null;
120:
121: static {
122: if (insertFT == null) {
123: insertFT = TransactionValidator.createInsertFeatureType();
124: }
125: if (updateFT == null) {
126: updateFT = TransactionValidator.createUpdateFeatureType();
127: }
128: if (deleteFT == null) {
129: deleteFT = TransactionValidator.createDeleteFeatureType();
130: }
131: }
132:
133: /**
134: *
135: * @param policy
136: */
137: public TransactionValidator(Policy policy) {
138: super (policy);
139: }
140:
141: @Override
142: public void validateRequest(OGCWebServiceRequest request, User user)
143: throws InvalidParameterValueException,
144: UnauthorizedException {
145:
146: userCoupled = false;
147:
148: Transaction wfsreq = (Transaction) request;
149:
150: List<TransactionOperation> ops = wfsreq.getOperations();
151: for (int i = 0; i < ops.size(); i++) {
152: userCoupled = false;
153: if (ops.get(i) instanceof Insert) {
154: Request req = policy.getRequest("WFS", "WFS_Insert");
155: if (!req.isAny()) {
156: Condition condition = req.getPreConditions();
157: validateOperation(condition, (Insert) ops.get(i));
158: }
159: if (userCoupled) {
160: validateAgainstRightsDB((Insert) ops.get(i), user);
161: }
162: } else if (ops.get(i) instanceof Update) {
163: Request req = policy.getRequest("WFS", "WFS_Update");
164: if (!req.isAny()) {
165: Condition condition = req.getPreConditions();
166: validateOperation(condition, (Update) ops.get(i));
167: }
168: if (userCoupled) {
169: validateAgainstRightsDB((Update) ops.get(i), user);
170: }
171: if (req.getPostConditions() != null) {
172: addFilter(ops.get(i), req.getPostConditions(), user);
173: }
174: } else if (ops.get(i) instanceof Delete) {
175: Request req = policy.getRequest("WFS", "WFS_Delete");
176: if (!req.isAny()) {
177: Condition condition = req.getPreConditions();
178: validateOperation(condition, (Delete) ops.get(i));
179: }
180: if (userCoupled) {
181: validateAgainstRightsDB((Delete) ops.get(i), user);
182: }
183: if (req.getPostConditions() != null) {
184: addFilter(ops.get(i), req.getPostConditions(), user);
185: }
186: }
187: }
188:
189: if (LOG.getLevel() == ILogger.LOG_DEBUG) {
190: try {
191: XMLFactory.export(wfsreq).prettyPrint(System.out);
192: } catch (Exception e) {
193: //nottin
194: }
195: }
196:
197: }
198:
199: /**
200: * adds a filter to the passed opertaion. If the condition is userCoupled
201: * the filter will be read from the DRM otherwise it is read from the
202: * current WFS policy file
203: *
204: * @param operation
205: * @param postConditions
206: * @param user
207: * @throws InvalidParameterValueException
208: * @throws UnauthorizedException
209: */
210: private void addFilter(TransactionOperation operation,
211: Condition postConditions, User user)
212: throws InvalidParameterValueException,
213: UnauthorizedException {
214: if (postConditions.getOperationParameter("instanceFilter") != null) {
215: Filter opFilter = null;
216: if (operation instanceof Update) {
217: opFilter = ((Update) operation).getFilter();
218: } else {
219: opFilter = ((Delete) operation).getFilter();
220: }
221: Filter filter = null;
222: if (postConditions.getOperationParameter("instanceFilter")
223: .isUserCoupled()) {
224: // read filterMap from constraints defined in deegree DRM
225: filter = readFilterFromDRM(operation, user);
226: } else {
227: fillFilterMap(postConditions);
228: // use filterMap read from policy document
229: filter = filterMap.get(operation
230: .getAffectedFeatureTypes().get(0));
231: }
232:
233: if (opFilter instanceof ComplexFilter) {
234: // create a new Filter that is a combination of the
235: // original filter and the one defined in the GetFeatures
236: // PostConditions coupled by a logical 'And'
237: ComplexFilter qFilter = (ComplexFilter) opFilter;
238: if (filter == null) {
239: filter = qFilter;
240: } else {
241: filter = new ComplexFilter(qFilter,
242: (ComplexFilter) filter,
243: OperationDefines.AND);
244: }
245: } else if (opFilter instanceof FeatureFilter) {
246: // just take original filter if it is as feature filter
247: // because feature filter and complex filters can not
248: // be combined
249: filter = opFilter;
250: }
251: if (operation instanceof Update) {
252: ((Update) operation).setFilter(filter);
253: } else {
254: ((Delete) operation).setFilter(filter);
255: }
256: }
257: }
258:
259: /**
260: * reads a filter m
261: *
262: * @param operation
263: * @param user
264: * @return the defined filter for the given operation or <code>null</code> if no such filter was found.
265: * @throws UnauthorizedException
266: * @throws InvalidParameterValueException
267: */
268: private Filter readFilterFromDRM(TransactionOperation operation,
269: User user) throws UnauthorizedException,
270: InvalidParameterValueException {
271: Filter f = null;
272: try {
273: SecurityAccessManager sam = SecurityAccessManager
274: .getInstance();
275: SecurityAccess access = sam.acquireAccess(user);
276:
277: QualifiedName qn = operation.getAffectedFeatureTypes().get(
278: 0);
279: SecuredObject secObj = access.getSecuredObjectByName(qn
280: .getFormattedString(),
281: ClientHelper.TYPE_FEATURETYPE);
282:
283: RightSet rs = user.getRights(access, secObj);
284: Right right = null;
285: if (operation instanceof Update) {
286: right = rs.getRight(secObj, RightType.UPDATE_RESPONSE);
287: } else {
288: right = rs.getRight(secObj, RightType.DELETE_RESPONSE);
289: }
290:
291: // a constraint - if available - is constructed as a OGC Filter
292: // one of the filter operations may is 'PropertyIsEqualTo' and
293: // defines a ProperyName == 'instanceFilter'. The Literal of this
294: // operation itself is a complete and valid Filter expression.
295: if (right != null) {
296: ComplexFilter filter = (ComplexFilter) right
297: .getConstraints();
298: if (filter != null) {
299: // extract filter expression to be used as additional
300: // filter for a GetFeature request
301: filter = extractInstanceFilter(filter
302: .getOperation());
303: if (filter != null) {
304: f = filter;
305: }
306: }
307: }
308:
309: } catch (GeneralSecurityException e) {
310: LOG.logError(e.getMessage(), e);
311: throw new UnauthorizedException(e.getMessage(), e);
312: } catch (FilterConstructionException e) {
313: LOG.logError(e.getMessage(), e);
314: throw new InvalidParameterValueException(e.getMessage(), e);
315: } catch (SAXException e) {
316: LOG.logError(e.getMessage(), e);
317: throw new InvalidParameterValueException(e.getMessage(), e);
318: } catch (IOException e) {
319: LOG.logError(e.getMessage(), e);
320: throw new InvalidParameterValueException(e.getMessage(), e);
321: }
322: return f;
323: }
324:
325: private void fillFilterMap(Condition postConditions)
326: throws InvalidParameterValueException {
327: List<Element> complexValues = postConditions
328: .getOperationParameter("instanceFilter")
329: .getComplexValues();
330: try {
331: if (filterMap.size() == 0) {
332: for (int i = 0; i < complexValues.size(); i++) {
333: Query q = Query.create(complexValues.get(0));
334: Filter f = q.getFilter();
335: QualifiedName qn = q.getTypeNames()[0];
336: filterMap.put(qn, f);
337: }
338: }
339: } catch (XMLParsingException e) {
340: LOG.logError(e.getMessage(), e);
341: throw new InvalidParameterValueException(this .getClass()
342: .getName(), e.getMessage());
343: }
344: }
345:
346: /**
347: *
348: * @param condition
349: * @param insert
350: * @throws InvalidParameterValueException
351: */
352: private void validateOperation(Condition condition, Insert insert)
353: throws InvalidParameterValueException {
354:
355: OperationParameter op = condition
356: .getOperationParameter(TYPENAME);
357:
358: // version is valid because no restrictions are made
359: if (op.isAny()) {
360: return;
361: }
362:
363: if (op.isUserCoupled()) {
364: userCoupled = true;
365: } else {
366: List<String> vals = op.getValues();
367: List<QualifiedName> fts = insert.getAffectedFeatureTypes();
368: for (int i = 0; i < fts.size(); i++) {
369: String qn = fts.get(i).getFormattedString();
370: if (!vals.contains(qn)) {
371: String s = Messages.getMessage(
372: "OWSPROXY_NOT_ALLOWED_FEATURETYPE",
373: "insert", qn);
374: throw new InvalidParameterValueException(s);
375: }
376: }
377: }
378: }
379:
380: /**
381: *
382: * @param condition
383: * @param delete
384: * @throws InvalidParameterValueException
385: */
386: private void validateOperation(Condition condition, Delete delete)
387: throws InvalidParameterValueException {
388: OperationParameter op = condition
389: .getOperationParameter(TYPENAME);
390:
391: // version is valid because no restrictions are made
392: if (op.isAny()) {
393: return;
394: }
395:
396: if (op.isUserCoupled()) {
397: userCoupled = true;
398: } else {
399: List<String> vals = op.getValues();
400: List<QualifiedName> fts = delete.getAffectedFeatureTypes();
401: for (int i = 0; i < fts.size(); i++) {
402: String qn = fts.get(i).getFormattedString();
403: if (!vals.contains(qn)) {
404: String s = Messages.getMessage(
405: "OWSPROXY_NOT_ALLOWED_FEATURETYPE",
406: "delete", qn);
407: throw new InvalidParameterValueException(s);
408: }
409: }
410: }
411: }
412:
413: /**
414: *
415: * @param condition
416: * @param update
417: * @throws InvalidParameterValueException
418: */
419: private void validateOperation(Condition condition, Update update)
420: throws InvalidParameterValueException {
421:
422: OperationParameter op = condition
423: .getOperationParameter(TYPENAME);
424:
425: // version is valid because no restrictions are made
426: if (op.isAny()) {
427: return;
428: }
429:
430: if (op.isUserCoupled()) {
431: userCoupled = true;
432: } else {
433: List<String> vals = op.getValues();
434: List<QualifiedName> fts = update.getAffectedFeatureTypes();
435: for (int i = 0; i < fts.size(); i++) {
436: String qn = fts.get(i).getFormattedString();
437: if (!vals.contains(qn)) {
438: String s = Messages.getMessage(
439: "OWSPROXY_NOT_ALLOWED_FEATURETYPE",
440: "update", qn);
441: throw new InvalidParameterValueException(s);
442: }
443: }
444: }
445: }
446:
447: /**
448: * validates a Transcation.Delete request against the underlying users and rights management
449: * system
450: *
451: * @param delete
452: * @param user
453: * @throws InvalidParameterValueException
454: * @throws UnauthorizedException
455: */
456: private void validateAgainstRightsDB(Delete delete, User user)
457: throws InvalidParameterValueException,
458: UnauthorizedException {
459: if (user == null) {
460: throw new UnauthorizedException(Messages
461: .getMessage("OWSPROXY_NO_ANONYMOUS_ACCESS"));
462: }
463:
464: List<QualifiedName> fts = delete.getAffectedFeatureTypes();
465: for (int i = 0; i < fts.size(); i++) {
466: String name = fts.get(i).getLocalName();
467: String ns = fts.get(i).getNamespace().toASCIIString();
468: String qn = StringTools.concat(200, '{', ns, "}:", name);
469:
470: // create a feature instance from the parameters of the GetFeature request
471: // to enable comparsion with a filter encoding expression stored in the
472: // assigned rights management system
473: List<FeatureProperty> fps = new ArrayList<FeatureProperty>();
474: QualifiedName tn = new QualifiedName("typeName");
475: FeatureProperty fp = FeatureFactory.createFeatureProperty(
476: tn, qn);
477: fps.add(fp);
478: Feature feature = FeatureFactory.createFeature("id",
479: deleteFT, fps);
480:
481: handleUserCoupledRules(user, // the user who posted the request
482: feature, // This is the Database feature
483: qn, // the Qualified name of the users Featurerequest
484: ClientHelper.TYPE_FEATURETYPE, // a primary key in the db.
485: RightType.DELETE);// We're requesting a featuretype.
486: }
487:
488: }
489:
490: /**
491: * validates a Transcation.Update request against the underlying users and rights management
492: * system
493: *
494: * @param update
495: * @param user
496: * @throws UnauthorizedException
497: * @throws InvalidParameterValueException
498: */
499: private void validateAgainstRightsDB(Update update, User user)
500: throws InvalidParameterValueException,
501: UnauthorizedException {
502:
503: if (user == null) {
504: throw new UnauthorizedException(Messages
505: .getMessage("OWSPROXY_NO_ANONYMOUS_ACCESS"));
506: }
507:
508: List<QualifiedName> fts = update.getAffectedFeatureTypes();
509: for (int i = 0; i < fts.size(); i++) {
510: String name = fts.get(i).getLocalName();
511: String ns = fts.get(i).getNamespace().toASCIIString();
512: String qn = StringTools.concat(200, '{', ns, "}:", name);
513:
514: // create a feature instance from the parameters of the GetFeature request
515: // to enable comparsion with a filter encoding expression stored in the
516: // assigned rights management system
517: List<FeatureProperty> fps = new ArrayList<FeatureProperty>();
518: QualifiedName tn = new QualifiedName("typeName");
519: FeatureProperty fp = FeatureFactory.createFeatureProperty(
520: tn, qn);
521: fps.add(fp);
522: Feature feature = FeatureFactory.createFeature("id",
523: updateFT, fps);
524:
525: handleUserCoupledRules(user, // the user who posted the request
526: feature, // This is the Database feature
527: qn, // the Qualified name of the users Featurerequest
528: ClientHelper.TYPE_FEATURETYPE, // a primary key in the db.
529: RightType.UPDATE);// We're requesting a featuretype.
530: }
531: }
532:
533: /**
534: * validates the passed insert operation against the deegree user/rights management system
535: *
536: * @param insert
537: * @param user
538: * @throws InvalidParameterValueException
539: * @throws UnauthorizedException
540: */
541: private void validateAgainstRightsDB(Insert insert, User user)
542: throws InvalidParameterValueException,
543: UnauthorizedException {
544:
545: if (user == null) {
546: throw new UnauthorizedException(Messages
547: .getMessage("OWSPROXY_NO_ANONYMOUS_ACCESS"));
548: }
549:
550: List<QualifiedName> fts = insert.getAffectedFeatureTypes();
551: for (int i = 0; i < fts.size(); i++) {
552: String name = fts.get(i).getLocalName();
553: String ns = fts.get(i).getNamespace().toASCIIString();
554: String qn = StringTools.concat(200, '{', ns, "}:", name);
555: // create a feature instance from the parameters of the GetRecords request
556: // to enable comparsion with a filter encoding expression stored in the
557: // assigned rights management system
558: List<FeatureProperty> fps = new ArrayList<FeatureProperty>();
559: QualifiedName tn = new QualifiedName("typeName");
560: FeatureProperty fp = FeatureFactory.createFeatureProperty(
561: tn, qn);
562: fps.add(fp);
563: Feature feature = FeatureFactory.createFeature("id",
564: insertFT, fps);
565:
566: handleUserCoupledRules(user, // the user who posted the request
567: feature, // This is the Database feature
568: qn, // the Qualified name of the users Featurerequest
569: ClientHelper.TYPE_FEATURETYPE, // a primary key in the db.
570: RightType.INSERT);// We're requesting a featuretype.
571: }
572:
573: }
574:
575: /**
576: * creates a feature type that matches the parameters of a Insert operation
577: *
578: * @return created <tt>FeatureType</tt>
579: */
580: private static FeatureType createInsertFeatureType() {
581: PropertyType[] ftps = new PropertyType[1];
582: ftps[0] = FeatureFactory.createSimplePropertyType(
583: new QualifiedName("typeName"), Types.VARCHAR, false);
584:
585: return FeatureFactory.createFeatureType("WFS_Insert", false,
586: ftps);
587: }
588:
589: /**
590: * creates a feature type that matches the parameters of a Update operation
591: *
592: * @return created <tt>FeatureType</tt>
593: */
594: private static FeatureType createUpdateFeatureType() {
595: PropertyType[] ftps = new PropertyType[2];
596: ftps[0] = FeatureFactory.createSimplePropertyType(
597: new QualifiedName("typeName"), Types.VARCHAR, false);
598:
599: return FeatureFactory.createFeatureType("WFS_Update", false,
600: ftps);
601: }
602:
603: /**
604: * creates a feature type that matches the parameters of a Delete operation
605: *
606: * @return created <tt>FeatureType</tt>
607: */
608: private static FeatureType createDeleteFeatureType() {
609: PropertyType[] ftps = new PropertyType[1];
610: ftps[0] = FeatureFactory.createSimplePropertyType(
611: new QualifiedName("typeName"), Types.VARCHAR, false);
612:
613: return FeatureFactory.createFeatureType("WFS_Delete", false,
614: ftps);
615: }
616:
617: }
|