001    //$HeadURL: https://svn.wald.intevation.org/svn/deegree/base/branches/2.3_testing/src/org/deegree/portal/standard/security/control/InitUserEditorListener.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.security.control;
037    
038    import java.io.BufferedReader;
039    import java.io.BufferedWriter;
040    import java.io.File;
041    import java.io.FileReader;
042    import java.io.FileWriter;
043    import java.io.IOException;
044    import java.text.ParseException;
045    import java.util.Collection;
046    import java.util.HashMap;
047    import java.util.Hashtable;
048    import java.util.Iterator;
049    import java.util.List;
050    import java.util.Map;
051    
052    import org.deegree.enterprise.control.AbstractListener;
053    import org.deegree.enterprise.control.FormEvent;
054    import org.deegree.framework.log.ILogger;
055    import org.deegree.framework.log.LoggerFactory;
056    import org.deegree.framework.util.StringTools;
057    import org.deegree.framework.xml.NamespaceContext;
058    import org.deegree.framework.xml.XMLFragment;
059    import org.deegree.framework.xml.XMLParsingException;
060    import org.deegree.framework.xml.XMLTools;
061    import org.deegree.i18n.Messages;
062    import org.deegree.ogcbase.CommonNamespaces;
063    import org.deegree.security.GeneralSecurityException;
064    import org.deegree.security.drm.SecurityAccess;
065    import org.deegree.security.drm.model.User;
066    import org.w3c.dom.DOMException;
067    import org.w3c.dom.Element;
068    import org.w3c.dom.Node;
069    
070    /**
071     * This <code>Listener</code> reacts on 'initUserEditor' events, queries the <code>SecurityManager</code> and passes the
072     * group data on to be displayed by the JSP.
073     * <p>
074     * The internal "SEC_ADMIN" user is sorted out from the USERS parameter.
075     * </p>
076     * <p>
077     * Access constraints:
078     * <ul>
079     * <li>only users that have the 'SEC_ADMIN'-role are allowed</li>
080     * </ul>
081     * </p>
082     *
083     * @author <a href="mschneider@lat-lon.de">Markus Schneider </a>
084     * @author last edited by: $Author: mschneider $
085     *
086     * @version $Revision: 18195 $, $Date: 2009-06-18 17:55:39 +0200 (Do, 18. Jun 2009) $
087     */
088    public class InitUserEditorListener extends AbstractListener {
089    
090        private static final ILogger LOG = LoggerFactory.getLogger( InitUserEditorListener.class );
091    
092        private NamespaceContext cn = CommonNamespaces.getNamespaceContext();
093    
094        private File usersDirectory = null;
095    
096        private final String STARTCONTEXT = "STARTCONTEXT";
097    
098        private final String CONTEXTNAME = "CONTEXT_NAME";
099    
100        private String confPath = null;
101    
102        @Override
103        public void actionPerformed( FormEvent event ) {
104    
105            try {
106                // perform access check
107                SecurityAccess access = SecurityHelper.acquireAccess( this );
108                SecurityHelper.checkForAdminRole( access );
109    
110                getRequest().setAttribute( "ACCESS", access );
111                User[] users = access.getAllUsers();
112                User[] noAdminUsers = new User[users.length - 1];
113                int j = 0;
114                for ( int i = 0; i < users.length; i++ ) {
115                    if ( users[i].getID() != User.ID_SEC_ADMIN ) {
116                        noAdminUsers[j++] = users[i];
117                    }
118                }
119    
120                // read the file name which contains a list of our startContexts
121                String configStartContexts = getInitParameter( "configFile" );
122                // now we know there is a file path in that string
123    
124                // Start sending data to the jsp
125                if ( configStartContexts != null && configStartContexts.length() != 0 ) {
126                    confPath = configStartContexts.substring( 0, configStartContexts.lastIndexOf( "/" ) + 1 );
127                    String confFilePath = getHomePath() + "/" + configStartContexts;
128                    /*
129                     * String confFilePath = null; if ( getHomePath().endsWith( "/" ) ) { confFilePath = getHomePath() +
130                     * configStartContexts; } else { confFilePath = getHomePath() + '/' + configStartContexts; }
131                     */
132    
133                    File absFilePath = new File( confFilePath );
134                    if ( absFilePath.exists() ) {
135                        XMLFragment fragment = new XMLFragment( absFilePath.toURI().toURL() );
136                        Map<String, StartContext> contextsList = parseContextsXml( fragment.getRootElement() );
137                        Map<String, StartContext> usersList = parseUsersXml( fragment.getRootElement(), contextsList );
138    
139                        // sending the data to the jsp page
140                        if ( contextsList == null ) {
141                            LOG.logError( Messages.getMessage( "IGEO_STD_SEC_ERROR_CNXT_LIST_NULL" ) );
142                        }
143                        if ( usersList == null ) {
144                            LOG.logError( Messages.getMessage( "IGEO_STD_SEC_ERROR_USERS_LIST_NULL" ) );
145                        }
146    
147                        String usersDirectory = getHomePath() + confPath
148                                                + getUsersDirectory( fragment.getRootElement() ).toString();
149                        getRequest().setAttribute( "STARTCONTEXTSLIST", contextsList );
150                        getRequest().setAttribute( "USERSCONTEXTLIST", usersList );
151                        getRequest().setAttribute( "USERSDIRECTORY", usersDirectory );
152                    } else {
153                        LOG.logDebug( "The configuration file could not be found: " + absFilePath.getCanonicalFile() );
154                    }
155                } else {
156                    LOG.logInfo( Messages.getMessage( "IGEO_STD_SEC_MISSING_CNTXT_FILE_PATH", "configFile" ) );
157                }
158    
159                getRequest().setAttribute( "USERS", noAdminUsers );
160            } catch ( GeneralSecurityException e ) {
161                getRequest().setAttribute( "SOURCE", this.getClass().getName() );
162                getRequest().setAttribute( "MESSAGE",
163                                           Messages.getMessage( "IGEO_STD_SEC_FAIL_INIT_USER_EDITOR", e.getMessage() ) );
164                setNextPage( "error.jsp" );
165                LOG.logError( e.getMessage(), e );
166            } catch ( Exception e ) {
167                LOG.logError( Messages.getMessage( "IGEO_STD_SEC_ERROR_UNKNOWN", StringTools.stackTraceToString( e ) ) );
168            }
169        }
170    
171        /**
172         * method used for context chooser
173         *
174         * @param rootElem
175         * @return a [2] map Array that contains a list of the read contexts as a first element and a list of the user
176         *         contexts as a second element.the array length should always be 2
177         * @throws XMLParsingException
178         * @throws ClientConfigurationException
179         * @throws IOException
180         * @throws DOMException
181         */
182        private Map<String, StartContext> parseContextsXml( Element rootElem )
183                                throws XMLParsingException, ClientConfigurationException, DOMException, IOException {
184    
185            Hashtable<String, StartContext> contextsListHTable = new Hashtable<String, StartContext>();
186    
187            String xPath = "dgsec:availableWMC/dgsec:WMC";
188    
189            List<Node> list = XMLTools.getRequiredNodes( rootElem, xPath, cn );
190    
191            if ( list.isEmpty() ) {
192                LOG.logError( Messages.getMessage( "IGEO_STD_SEC_MISSING_XML_NO_CONTEXTS" ) );
193                throw new ClientConfigurationException( Messages.getMessage( "IGEO_STD_SEC_MISSING_XML_NO_CONTEXTS" ) );
194            }
195            contextsListHTable = new Hashtable<String, StartContext>( list.size() );
196    
197            Iterator<Node> it = list.iterator();
198            // iterating through the context nodes
199            while ( it.hasNext() ) {
200                Node node = it.next();
201                boolean isDefault = false;
202                if ( node.hasAttributes() ) {
203                    Node defaultSelection = node.getAttributes().getNamedItem( "isDefault" );
204                    // if there exists the attribute "isDefault"
205                    if ( defaultSelection != null && defaultSelection.getNodeValue().compareTo( "1" ) == 0 ) {
206                        isDefault = true;
207                    }
208                }
209    
210                // extracting the name and path of the context
211                Node nameNode = XMLTools.getNode( node, "dgsec:Name", cn );
212                Node pathNode = XMLTools.getNode( node, "dgsec:URL", cn );
213                if ( isDefault ) {
214                    updateDefaultContext( nameNode.getTextContent(), pathNode.getTextContent(), rootElem );
215                }
216                if ( nameNode == null ) {
217                    LOG.logError( Messages.getMessage( "IGEO_STD_SEC_MISSING_XML_ELEMENT", "name" ) );
218                    throw new ClientConfigurationException(
219                                                            Messages.getMessage( "IGEO_STD_SEC_MISSING_XML_ELEMENT", "name" ) );
220                }
221                if ( pathNode == null ) {
222                    LOG.logError( Messages.getMessage( "IGEO_STD_SEC_MISSING_XML_ELEMENT", "path" ) );
223                    throw new ClientConfigurationException(
224                                                            Messages.getMessage( "IGEO_STD_SEC_MISSING_XML_ELEMENT", "path" ) );
225                }
226                File contextPath = new File( getHomePath() + confPath + pathNode.getTextContent() );
227                StartContext context = new StartContext( nameNode.getTextContent(), contextPath.getCanonicalPath(),
228                                                         isDefault );
229                contextsListHTable.put( context.getContextName(), context );
230            }
231    
232            return contextsListHTable;
233        }
234    
235        /**
236         * Used for context chooser
237         *
238         * @param rootElem
239         * @param contextsHT
240         *            its the hashtable containing all the context read from the start_config.xml They will be useful to
241         *            determine the context name
242         * @return list of users contexts
243         * @throws XMLParsingException
244         * @throws ClientConfigurationException
245         */
246        private Map<String, StartContext> parseUsersXml( Element rootElem, Map<String, StartContext> contextsHT )
247                                throws XMLParsingException, ClientConfigurationException {
248    
249            if ( usersDirectory == null ) {
250                usersDirectory = getUsersDirectory( rootElem );
251            }
252            // Will be used to find context names, for these contexts with no names
253            Collection<StartContext> contextsList = contextsHT.values();
254    
255            Map<String, StartContext> usersListHTable = new HashMap<String, StartContext>( 20 );
256    
257            // FileReader reader = null;
258            String[] tempParts = null;
259    
260            // checking for the default context.properties
261            File absUsersDirectory = new File( getHomePath() + confPath + usersDirectory );
262    
263            // the list of folders under the folder "users"
264            String[] usersList = absUsersDirectory.list();
265    
266            try {
267                // iterating through the users folders
268                for ( int i = 0; i < usersList.length; i++ ) {
269                    File userFolder = new File( absUsersDirectory.getAbsoluteFile() + "/" + usersList[i] );
270                    if ( userFolder.isDirectory() ) {
271                        // read the context properties of the user
272                        File contextProps = new File( userFolder.getAbsoluteFile() + "/context.properties" );
273                        if ( contextProps.exists() ) {
274                            // reader = new FileReader( contextProps );
275                            String line = getFirstLineMatch( contextProps.getCanonicalPath(), STARTCONTEXT );
276                            if ( line != null && line.length() != 0 ) {
277                                tempParts = line.split( "=" );
278                                if ( tempParts.length < 2 ) {
279                                    throw new ClientConfigurationException( "the context.properties file in user '"
280                                                                            + userFolder.getName() + "' is bad formatted" );
281                                }
282                                File contextPath = new File( userFolder.getCanonicalPath() + "/" + tempParts[1] );
283                                line = getFirstLineMatch( contextProps.getCanonicalPath(), CONTEXTNAME );
284    
285                                String[] tempParts2 = null;
286                                String contextName = null;
287                                if ( line != null && line.length() > 0 ) {
288                                    tempParts2 = line.split( "=" );
289                                } else {
290                                    contextName = findContextName( usersList[i], tempParts[1], contextsList );
291                                }
292    
293                                if ( tempParts2 != null && tempParts2.length == 2 ) {
294                                    contextName = tempParts2[1];
295                                }
296                                usersListHTable.put( userFolder.getName(),
297                                                     new StartContext( contextName, contextPath.getCanonicalPath(),
298                                                                       new Boolean( false ) ) );
299                                // reader.close();
300                            }
301                        }
302                    }
303                }
304    
305            } catch ( Exception e ) {
306                LOG.logError( Messages.getMessage( "IGEO_STD_SEC_ERROR_UNKNOWN", StringTools.stackTraceToString( e ) ) );
307            }
308            return usersListHTable;
309    
310        }
311    
312        /**
313         * method used for context chooser
314         *
315         * @param rootElem
316         * @return usserDirectory
317         * @throws XMLParsingException
318         * @throws ClientConfigurationException
319         */
320        private File getUsersDirectory( Element rootElem )
321                                throws XMLParsingException, ClientConfigurationException {
322    
323            if ( usersDirectory != null ) {
324                return usersDirectory;
325            }
326    
327            Node userDirectoryNode = XMLTools.getNode( rootElem, "dgsec:UserDirectory", cn );
328            if ( userDirectoryNode == null ) {
329                LOG.logError( Messages.getMessage( "IGEO_STD_SEC_MISSING_XML_ELEMENT", "userdirectory" ) );
330                throw new ClientConfigurationException( Messages.getMessage( "IGEO_STD_SEC_MISSING_XML_ELEMENT",
331                                                                             "userdirectory" ) );
332            }
333    
334            return new File( userDirectoryNode.getTextContent() );
335        }
336    
337        /**
338         * @param contextPath
339         *            The path to search for in the contexts
340         * @param contexts
341         *            The list of contexts to look into
342         * @return The found context name if nothing is found, the file name.xml will be used
343         */
344        private String findContextName( String user, String contextPath, Collection<StartContext> contexts ) {
345    
346            Iterator<StartContext> it = contexts.iterator();
347            String[] delimiters = { "\\", "/" };
348    
349            File absUserDirectory = new File( getHomePath() + confPath + usersDirectory + "/" + user );
350    
351            while ( it.hasNext() ) {
352                StartContext context = it.next();
353                File absContextPath = new File( getHomePath() + confPath + context.getPath() );
354    
355                try {
356                    String mappedPath = RelativePath.mapRelativePath( absUserDirectory.getCanonicalPath(),
357                                                                      absContextPath.getCanonicalPath(), delimiters );
358    
359                    // We return the context name in which its path matches the path written in
360                    // context.properties
361                    if ( mappedPath.compareTo( contextPath ) == 0 ) {
362                        return context.getContextName();
363                    }
364                } catch ( Exception e ) {
365                    LOG.logError( Messages.getMessage( "IGEO_STD_SEC_ERROR_PATH_MAPPING",
366                                                       absUserDirectory.getAbsolutePath(), absContextPath.getAbsolutePath() ) );
367                }
368            }
369    
370            int index = contextPath.lastIndexOf( "/" );
371            String contextName = null;
372            if ( index != -1 ) {
373                contextName = contextPath.substring( index + 1 );
374            }
375            return contextName;
376        }
377    
378        /**
379         * @param target
380         *            The string we are looking for
381         * @return The whole line where our string exists
382         */
383        private String getFirstLineMatch( String filePath, String target ) {
384    
385            try {
386                String line = null;
387                BufferedReader buffer = new BufferedReader( new FileReader( filePath ) );
388                while ( ( line = buffer.readLine() ) != null ) {
389                    if ( line.indexOf( target ) > -1 ) {
390                        return line;
391                    }
392                }
393            } catch ( Exception e ) {
394                LOG.logError( Messages.getMessage( "IGEO_STD_SEC_ERROR_READING_FILE", filePath ) );
395            }
396            return null;
397        }
398    
399        /**
400         * @param contextName
401         * @param contextPath
402         */
403        private void updateDefaultContext( String contextName, String contextPath, Element rootElem ) {
404    
405            File usersFolder = null;
406            File contextFile = null;
407            try {
408                usersFolder = new File( getHomePath() + confPath + getUsersDirectory( rootElem ) );
409                File defaultContextFile = new File( getHomePath() + confPath + getUsersDirectory( rootElem )
410                                                    + "/context.properties" );
411                contextFile = new File( getHomePath() + confPath + contextPath );
412    
413                String[] delimiters = { "\\", "/" };
414                String mappedPath = RelativePath.mapRelativePath( usersFolder.getCanonicalPath(),
415                                                                  contextFile.getCanonicalPath(), delimiters );
416                boolean pathChanged = false;
417                if ( !defaultContextFile.exists() ) {
418                    defaultContextFile.createNewFile();
419                    pathChanged = true;
420                }
421                String line = getFirstLineMatch( defaultContextFile.getCanonicalPath(), "STARTCONTEXT" );
422                if ( line != null && line.length() > 0 ) {
423                    String[] parts = line.split( "=" );
424                    if ( parts.length == 2 ) {
425                        if ( parts[1].compareTo( mappedPath ) != 0 ) {
426                            pathChanged = true;
427                        }
428                    }
429                }
430    
431                if ( pathChanged ) {
432                    BufferedWriter bufferedWriter = new BufferedWriter(
433                                                                        new FileWriter(
434                                                                                        defaultContextFile.getCanonicalPath() ) );
435                    bufferedWriter.flush();
436                    StringBuffer buffer = new StringBuffer();
437                    buffer.append( STARTCONTEXT + "=" + mappedPath + System.getProperty( "line.separator" ) );
438                    buffer.append( CONTEXTNAME + "=" + contextName + System.getProperty( "line.separator" ) );
439                    bufferedWriter.write( buffer.toString() );
440                    bufferedWriter.close();
441                    getRequest().setAttribute(
442                                               "DEFAULTCHANGED",
443                                               contextFile.getCanonicalPath().substring(
444                                                                                         contextFile.getCanonicalPath().indexOf(
445                                                                                                                                 "WEB-INF" ) ) );
446                }
447    
448            } catch ( ParseException e ) {
449                LOG.logError( Messages.getMessage( "IGEO_STD_SEC_ERROR_PATH_MAPPING", usersFolder, contextFile ) );
450            } catch ( IOException e ) {
451                LOG.logError( Messages.getMessage( "IGEO_STD_SEC_ERROR_IO", StringTools.stackTraceToString( e ) ) );
452            } catch ( ClientConfigurationException e ) {
453                LOG.logError( Messages.getMessage( "IGEO_STD_SEC_ERROR_CLIENT_CONFIG", e.getMessage() ) );
454            } catch ( Exception e ) {
455                LOG.logError( Messages.getMessage( "IGEO_STD_SEC_ERROR_UNKNOWN", StringTools.stackTraceToString( e ) ) );
456            }
457        }
458    }