001    //$HeadURL: svn+ssh://jwilden@svn.wald.intevation.org/deegree/base/branches/2.5_testing/src/org/deegree/portal/standard/context/control/AbstractContextListener.java $
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    package org.deegree.portal.standard.context.control;
037    
038    import java.io.File;
039    import java.io.IOException;
040    import java.io.InputStream;
041    import java.io.StringWriter;
042    import java.net.MalformedURLException;
043    import java.net.URL;
044    import java.util.ArrayList;
045    import java.util.Arrays;
046    import java.util.List;
047    import java.util.Properties;
048    
049    import javax.servlet.http.HttpServletRequest;
050    import javax.servlet.http.HttpSession;
051    import javax.xml.transform.TransformerException;
052    
053    import org.apache.commons.httpclient.HttpClient;
054    import org.apache.commons.httpclient.methods.GetMethod;
055    import org.deegree.enterprise.WebUtils;
056    import org.deegree.enterprise.control.AbstractListener;
057    import org.deegree.enterprise.control.RPCMember;
058    import org.deegree.enterprise.control.RPCMethodCall;
059    import org.deegree.enterprise.control.RPCParameter;
060    import org.deegree.enterprise.control.RPCStruct;
061    import org.deegree.enterprise.control.RPCUtils;
062    import org.deegree.enterprise.control.RPCWebEvent;
063    import org.deegree.framework.log.ILogger;
064    import org.deegree.framework.log.LoggerFactory;
065    import org.deegree.framework.util.StringTools;
066    import org.deegree.framework.xml.NamespaceContext;
067    import org.deegree.framework.xml.XMLFragment;
068    import org.deegree.framework.xml.XMLParsingException;
069    import org.deegree.framework.xml.XMLTools;
070    import org.deegree.framework.xml.XSLTDocument;
071    import org.deegree.i18n.Messages;
072    import org.deegree.model.crs.CoordinateSystem;
073    import org.deegree.model.spatialschema.Envelope;
074    import org.deegree.model.spatialschema.GeometryFactory;
075    import org.deegree.model.spatialschema.Point;
076    import org.deegree.ogcbase.BaseURL;
077    import org.deegree.ogcbase.CommonNamespaces;
078    import org.deegree.ogcwebservices.OWSUtils;
079    import org.deegree.ogcwebservices.getcapabilities.OGCCapabilities;
080    import org.deegree.ogcwebservices.wms.capabilities.WMSCapabilitiesDocument;
081    import org.deegree.ogcwebservices.wms.capabilities.WMSCapabilitiesDocumentFactory;
082    import org.deegree.portal.Constants;
083    import org.deegree.portal.PortalException;
084    import org.deegree.portal.context.ContextException;
085    import org.deegree.portal.context.General;
086    import org.deegree.portal.context.GeneralExtension;
087    import org.deegree.portal.context.Layer;
088    import org.deegree.portal.context.LayerList;
089    import org.deegree.portal.context.Server;
090    import org.deegree.portal.context.ViewContext;
091    import org.xml.sax.SAXException;
092    
093    /**
094     * This exception shall be thrown when a session(ID) will be used that has been expired.
095     * 
096     * @author <a href="mailto:poth@lat-lon.de">Andreas Poth </a>
097     * @author last edited by: $Author: jmays $
098     * 
099     * @version $Revision: 24549 $, $Date: 2010-05-25 14:29:38 +0200 (Di, 25 Mai 2010) $
100     */
101    abstract public class AbstractContextListener extends AbstractListener {
102    
103        private static ILogger LOG = LoggerFactory.getLogger( AbstractContextListener.class );
104    
105        private static final NamespaceContext nsContext = CommonNamespaces.getNamespaceContext();
106    
107        /**
108         * WEB-INF/conf/igeoportal/users/
109         */
110        protected static final String userDir = "WEB-INF/conf/igeoportal/users/";
111    
112        /**
113         * gets the user name assigned to the passed session ID from an authentication service. If no user is assigned to
114         * the session ID <tt>null</tt> will be returned. If the session is closed or expired an exception will be thrown
115         * 
116         * @param sessionId
117         * @return name of the user assigned to the passed session ID
118         * @throws XMLParsingException
119         * @throws SAXException
120         * @throws IOException
121         */
122        protected String getUserName( String sessionId )
123                                throws XMLParsingException, IOException, SAXException {
124    
125            HttpSession session = ( (HttpServletRequest) getRequest() ).getSession( true );
126            ViewContext vc = (ViewContext) session.getAttribute( Constants.CURRENTMAPCONTEXT );
127            if ( vc == null ) {
128                return null;
129            }
130            GeneralExtension ge = vc.getGeneral().getExtension();
131            String userName = null;
132            if ( sessionId != null && ge.getAuthentificationSettings() != null ) {
133                LOG.logDebug( "try getting user from WAS/sessionID" );
134                BaseURL baseUrl = ge.getAuthentificationSettings().getAuthentificationURL();
135                String url = OWSUtils.validateHTTPGetBaseURL( baseUrl.getOnlineResource().toExternalForm() );
136                StringBuffer sb = new StringBuffer( url );
137                sb.append( "request=DescribeUser&SESSIONID=" ).append( sessionId );
138    
139                XMLFragment xml = new XMLFragment();
140                xml.load( new URL( sb.toString() ) );
141    
142                userName = XMLTools.getRequiredNodeAsString( xml.getRootElement(), "/User/UserName", nsContext );
143            } else {
144                LOG.logDebug( "try getting user from getUserPrincipal()" );
145                if ( ( (HttpServletRequest) getRequest() ).getUserPrincipal() != null ) {
146                    userName = ( (HttpServletRequest) getRequest() ).getUserPrincipal().getName();
147                    if ( userName.indexOf( "\\" ) > 1 ) {
148                        String[] us = StringTools.toArray( userName, "\\", false );
149                        userName = us[us.length - 1];
150                    }
151                }
152            }
153            LOG.logDebug( "userName: " + userName );
154            return userName;
155        }
156    
157        /**
158         * gets the user password assigned to the passed session ID from a authentification service. If no user is assigned
159         * to the session ID <tt>null</tt> will be returned. If the session is closed or expired an exception will be thrown
160         * 
161         * @param sessionId
162         * @return password of the user assigned to the passed session ID
163         */
164        protected String getUserPassword( String sessionId )
165                                throws XMLParsingException, IOException, SAXException, PortalException {
166    
167            HttpSession session = ( (HttpServletRequest) getRequest() ).getSession( true );
168            ViewContext vc = (ViewContext) session.getAttribute( Constants.CURRENTMAPCONTEXT );
169            if ( vc == null ) {
170                return null;
171            }
172            GeneralExtension ge = vc.getGeneral().getExtension();
173            String userPassword = null;
174            if ( sessionId != null && ge.getAuthentificationSettings() != null ) {
175                LOG.logDebug( "try getting user from WAS/sessionID" );
176                BaseURL baseUrl = ge.getAuthentificationSettings().getAuthentificationURL();
177                String url = OWSUtils.validateHTTPGetBaseURL( baseUrl.getOnlineResource().toExternalForm() );
178                StringBuffer sb = new StringBuffer( url );
179                sb.append( "request=DescribeUser&SESSIONID=" ).append( sessionId );
180    
181                XMLFragment xml = new XMLFragment();
182                xml.load( new URL( sb.toString() ) );
183    
184                LOG.logDebug( xml.getAsPrettyString() );
185                userPassword = XMLTools.getRequiredNodeAsString( xml.getRootElement(), "/User/Password", nsContext );
186            } else {
187                throw new PortalException( "The session ID is null and thus the user password can not be extracted" );
188            }
189            LOG.logDebug( "Password: " + userPassword );
190            return userPassword;
191        }
192    
193        /**
194         * reads the users session ID.<br>
195         * first the PRC will be parsed for a 'sessionID' element. If not present the sessionID will be read from the users
196         * session. If even the user's HTTP session does not contain a sessionID, it will be tried to get it from the WAS
197         * registered to the current context. If no WAS available <code>null</code> will be returned.
198         * 
199         * @param struct
200         * @return the users session id
201         * @throws IOException
202         * @throws SAXException
203         * @throws XMLParsingException
204         */
205        protected String readSessionID( RPCStruct struct )
206                                throws XMLParsingException, SAXException, IOException {
207            String sid = RPCUtils.getRpcPropertyAsString( struct, "sessionID" );
208            if ( sid == null ) {
209                LOG.logDebug( "try getting sessionID from HTTP session" );
210                HttpSession session = ( (HttpServletRequest) getRequest() ).getSession();
211                sid = (String) session.getAttribute( "SESSIONID" );
212            }
213            if ( sid == null ) {
214                // try get SessionID from WAS if user name is available
215                // in this case it is assumed that a user's name can be determined
216                // evaluating the requests userPrincipal that will be available if
217                // the user has been logged in to the the server (or network)
218                String userName = getUserName( null );
219                if ( userName != null ) {
220                    LOG.logDebug( "try getting sessionID by authorizing current user: " + userName );
221                    HttpSession session = ( (HttpServletRequest) getRequest() ).getSession( true );
222                    ViewContext vc = (ViewContext) session.getAttribute( Constants.CURRENTMAPCONTEXT );
223                    GeneralExtension ge = vc.getGeneral().getExtension();
224                    if ( ge.getAuthentificationSettings() != null ) {
225                        BaseURL baseUrl = ge.getAuthentificationSettings().getAuthentificationURL();
226                        StringBuffer sb = new StringBuffer( 500 );
227                        String addr = baseUrl.getOnlineResource().toExternalForm();
228                        sb.append( OWSUtils.validateHTTPGetBaseURL( addr ) );
229                        sb.append( "SERVICE=WAS&VERSION=1.0.0&REQUEST=GetSession&" );
230                        sb.append( "AUTHMETHOD=urn:x-gdi-nrw:authnMethod:1.0:password&CREDENTIALS=" );
231                        sb.append( userName );
232                        LOG.logDebug( "authenticat user: ", sb.toString() );
233                        try {
234                            HttpClient client = new HttpClient();
235                            client = WebUtils.enableProxyUsage( client, baseUrl.getOnlineResource() );
236                            GetMethod meth = new GetMethod( sb.toString() );
237                            client.executeMethod( meth );
238                            if ( meth.getStatusCode() == 200 ) {
239                                sid = meth.getResponseBodyAsString();
240                                session.setAttribute( "SESSIONID", sid );
241                            } else {
242                                LOG.logWarning( "Web application at " + addr + " does not exist." );
243                            }
244                        } catch ( Exception e ) {
245                            LOG.logWarning( "WAS at " + addr + " does not exist." );
246                        }
247                    }
248                }
249            }
250            LOG.logDebug( "sessionID: " + sid );
251            return sid;
252        }
253    
254        /**
255         * Convenience method to extract the boundig box from an rpc fragment.
256         * 
257         * @param bboxStruct
258         *            the <code>RPCStruct</code> containing the bounding box. For example,
259         *            <code>&lt;member&gt;&lt;name&gt;boundingBox&lt;/name&gt;etc...</code>.
260         * @param crs
261         *            a coordinate system value, may be null.
262         * @return an envelope with the boundaries defined in the rpc structure
263         */
264        protected Envelope extractBBox( RPCStruct bboxStruct, CoordinateSystem crs ) {
265    
266            Double minx = (Double) bboxStruct.getMember( Constants.RPC_BBOXMINX ).getValue();
267            Double miny = (Double) bboxStruct.getMember( Constants.RPC_BBOXMINY ).getValue();
268            Double maxx = (Double) bboxStruct.getMember( Constants.RPC_BBOXMAXX ).getValue();
269            Double maxy = (Double) bboxStruct.getMember( Constants.RPC_BBOXMAXY ).getValue();
270    
271            Envelope bbox = GeometryFactory.createEnvelope( minx.doubleValue(), miny.doubleValue(), maxx.doubleValue(),
272                                                            maxy.doubleValue(), crs );
273            return bbox;
274        }
275    
276        /**
277         * This method is kept for downward compatibility of the API. Do not use it any more!
278         * 
279         * @deprecated use extractBBox( RPCStruct, CoordinateSystem ) instead.
280         * @param bboxStruct
281         *            the <code>RPCStruct</code> containing the bounding box. For example,
282         *            <code>&lt;member&gt;&lt;name&gt;boundingBox&lt;/name&gt;etc...</code>.
283         * @return an envelope with the boundaries defined in the rpc structure
284         */
285        @Deprecated
286        protected Envelope extractBBox( RPCStruct bboxStruct ) {
287            return extractBBox( bboxStruct, null );
288        }
289    
290        /**
291         * changes the bounding box of a given view context
292         * 
293         * @param vc
294         *            the view context to be changed
295         * @param bbox
296         *            the new bounding box
297         * @throws PortalException
298         */
299        public static final void changeBBox( ViewContext vc, Envelope bbox )
300                                throws PortalException {
301            General gen = vc.getGeneral();
302    
303            CoordinateSystem cs = gen.getBoundingBox()[0].getCoordinateSystem();
304            Point[] p = new Point[] { GeometryFactory.createPoint( bbox.getMin(), cs ),
305                                     GeometryFactory.createPoint( bbox.getMax(), cs ) };
306            try {
307                gen.setBoundingBox( p );
308            } catch ( ContextException e ) {
309                LOG.logError( e.getMessage(), e );
310                throw new PortalException( Messages.getMessage( "IGEO_STD_CNTXT_ERROR_SET_BBOX" ) );
311            }
312        }
313    
314        /**
315         * changes the layer list of the ViewContext vc according to the information contained in the rpcLayerList
316         * 
317         * @param vc
318         *            The original ViewContext where the changes will be applied to
319         * @param rpcLayerList
320         *            the current layerlist
321         * @throws PortalException
322         */
323        protected void changeLayerList( ViewContext vc, RPCMember[] rpcLayerList )
324                                throws PortalException {
325            LayerList layerList = vc.getLayerList();
326            ArrayList<Layer> nLayers = new ArrayList<Layer>( rpcLayerList.length );
327    
328            // this is needed to keep layer order
329            // order is correct in rpc call JavaScript) but get lost in translation...
330            for ( int i = 0; i < rpcLayerList.length; i++ ) {
331                String[] v = StringTools.toArray( (String) rpcLayerList[i].getValue(), "|", false );
332                String n = rpcLayerList[i].getName();
333    
334                String title = n;
335                if ( v.length > 5 ) {
336                    // this check is necessary, in order to not break running iGeoPortal instances
337                    title = v[5];
338                }
339                boolean isQueryable = false;
340                if ( v.length > 6 ) {
341                    // JM: this check is necessary, in order to not break running iGeoPortal instances
342                    isQueryable = v[6].equalsIgnoreCase( "true" );
343                }
344    
345                boolean isVisible = Boolean.valueOf( v[0] ).booleanValue();
346                Layer l = layerList.getLayer( n, null );
347                if ( l != null ) {
348                    // needed to reconstruct new layer order
349                    // otherwise layer order is still from original context
350                    l.setHidden( !isVisible );
351                } else {
352    
353                    if ( layerList.getLayers().length == 0 ) {
354                        // FIXME is this Exception Correct
355                        throw new PortalException( Messages.getMessage( "IGEO_STD_CNTXT_ERROR_EMPTY_LAYERLIST" ) );
356                    }
357    
358                    Layer p = layerList.getLayers()[0];
359                    // a new layer must be created because it is not prsent
360                    // in the current context. This is the case if the client
361                    // has loaded an additional WMS
362                    String[] tmp = StringTools.toArray( v[2], " ", false );
363                    try {
364                        v[4] = OWSUtils.validateHTTPGetBaseURL( v[4] );
365                        String s = v[4] + "request=GetCapabilities&service=WMS";
366                        WMSCapabilitiesDocument doc = WMSCapabilitiesDocumentFactory.getWMSCapabilitiesDocument( new URL( s ) );
367                        OGCCapabilities capa = doc.parseCapabilities();
368                        Server server = new Server( v[3], tmp[1], tmp[0], new URL( v[4] ), capa );
369                        // l = new Layer( server, n, title, "", p.getSrs(), null, null, p.getFormatList(), p.getStyleList(),
370                        // isQueryable, !isVisible, null );
371                        l = new Layer( server, n, title, "", p.getSrs(), p.getDataURL(), p.getMetadataURL(),
372                                       p.getFormatList(), p.getStyleList(), isQueryable, !isVisible, p.getExtension() );
373                    } catch ( Exception e ) {
374                        throw new PortalException( StringTools.stackTraceToString( e ) );
375                    }
376                }
377                nLayers.add( l );
378            }
379            try {
380                nLayers.trimToSize();
381                Layer[] ls = new Layer[nLayers.size()];
382                ls = nLayers.toArray( ls );
383                vc.setLayerList( new LayerList( ls ) );
384            } catch ( ContextException e ) {
385                throw new PortalException( Messages.getMessage( "IGEO_STD_CNTXT_ERROR_SET_LAYERLIST",
386                                                                StringTools.stackTraceToString( e.getStackTrace() ) ) );
387            }
388        }
389    
390        /**
391         * This function takes in a XmlFragment and transforms it to a html map context
392         * 
393         * @param xml
394         *            xmlFragment to transform
395         * @param xsl
396         *            xsl file used to transform
397         * @return html representing the mapContext
398         * @throws TransformerException
399         * @throws SAXException
400         * @throws IOException
401         * @throws MalformedURLException
402         */
403        protected String transformToHtmlMapContext( XMLFragment xml, String xsl )
404                                throws TransformerException, MalformedURLException, IOException, SAXException {
405    
406            XSLTDocument xslt = new XSLTDocument();
407            StringWriter sw = new StringWriter( 30000 );
408    
409            xslt.load( new URL( xsl ) );
410            xml = xslt.transform( xml );
411            xml.write( sw );
412    
413            return sw.toString();
414        }
415    
416        /**
417         * Extracts the parameters from the method call element within the passed rpcEvent.
418         * 
419         * @param rpcEvent
420         * @return Returns the parameters as array of <code>RPCParameter</code>.
421         * @throws PortalException
422         */
423        protected RPCParameter[] extractRPCParameters( RPCWebEvent rpcEvent )
424                                throws PortalException {
425            RPCParameter[] params;
426            try {
427                RPCMethodCall mc = rpcEvent.getRPCMethodCall();
428                params = mc.getParameters();
429            } catch ( Exception e ) {
430                throw new PortalException( Messages.getMessage( "IGEO_STD_CNTXT_ERROR_EXTRACT_PARAM_RPC", e.getMessage() ) );
431            }
432            return params;
433        }
434    
435        /**
436         * Extracts the <code>RPCStruct</code> from the indicated parameter in the params element of the passed
437         * <code>RPCWebEvent</code>.
438         * 
439         * @param rpcEvent
440         *            The RPCWebEvent, that contains the RPCStruct to extract.
441         * @param index
442         *            The index of the parameter from which to extract the RPCStruct (starting with 0).
443         * @return Returns the <code>RPCStruct</code> from the indicated params element.
444         * @throws PortalException
445         */
446        protected RPCStruct extractRPCStruct( RPCWebEvent rpcEvent, int index )
447                                throws PortalException {
448            RPCStruct rpcStruct;
449            try {
450                RPCParameter[] params = extractRPCParameters( rpcEvent );
451                rpcStruct = (RPCStruct) params[index].getValue();
452            } catch ( Exception e ) {
453                throw new PortalException( Messages.getMessage( "IGEO_STD_CNTXT_ERROR_EXTRACT_STRUCT_RPC", e.getMessage() ) );
454            }
455            return rpcStruct;
456        }
457    
458        /**
459         * returns a list of all available context documents assigned to the passed user
460         * 
461         * @param userName
462         * @return the list of available context documents
463         */
464        protected List<String> getContextList( String userName ) {
465    
466            String path2Dir = getHomePath() + userDir + userName;
467            File dir = new File( path2Dir );
468            File[] files = dir.listFiles();
469            List<String> contextList = new ArrayList<String>();
470            if ( files != null ) {
471                for ( int i = 0; i < files.length; i++ ) {
472                    String s = files[i].getName();
473                    if ( files[i].isFile() && s.endsWith( ".xml" ) ) {
474                        contextList.add( files[i].getName() );
475                    }
476                }
477            }
478            String[] list = contextList.toArray( new String[contextList.size()] );
479            Arrays.sort( list );
480            return new ArrayList<String>( Arrays.asList( list ) );
481        }
482    
483        /**
484         * returns the name of the users start context. If the user does not own an individual start context the name of the
485         * default start context for all users will be returned.
486         * 
487         * (copied and adapted from org.deegree.portal.standard.security.control.GetSessionIDListener)
488         * 
489         * @param userName
490         * @return String
491         * @throws IOException
492         */
493        protected String getUsersStartContext( String userName )
494                                throws IOException {
495            StringBuffer dir = new StringBuffer( "users/" );
496            StringBuffer sb = new StringBuffer( 300 );
497            sb.append( getHomePath() ).append( userDir ).append( userName );
498            sb.append( "/context.properties" );
499    
500            File file = new File( sb.toString() );
501            if ( !file.exists() ) {
502                sb.delete( 0, sb.length() );
503                sb.append( getHomePath() ).append( userDir ).append( "context.properties" );
504                file = new File( sb.toString() );
505            } else {
506                dir.append( userName ).append( '/' );
507            }
508    
509            Properties prop = new Properties();
510            InputStream is = file.toURL().openStream();
511            prop.load( is );
512            is.close();
513    
514            return dir.append( prop.getProperty( "STARTCONTEXT" ) ).toString();
515        }
516    
517    }