    001    //$HeadURL: https://svn.wald.intevation.org/svn/deegree/base/branches/2.4_testing/src/org/deegree/security/drm/SecurityAccessManager.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.security.drm;
    037
    038    import java.util.Properties;
    039
    040    import org.deegree.framework.log.ILogger;
    041    import org.deegree.framework.log.LoggerFactory;
    042    import org.deegree.security.GeneralSecurityException;
    043    import org.deegree.security.UnauthorizedException;
    044    import org.deegree.security.drm.model.Role;
    045    import org.deegree.security.drm.model.User;
    046
    047    /**
    048     * This singleton manages access to the data stored in an associated <code>SecurityRegistry</code> -instance.
    049     * <p>
    050     * In order to use methods that read from the registry, a <code>SecurityAccess</code> instance has to be acquired first:
    051     * <p>
    052     * <b>Example Code: </b>
    053     *
    054     * <pre>
    055     * SecurityAccess access = SecurityAccessManager.getInstance();
    056     *
    057     * ReadToken accessToken = access.acquireReadToken();
    058     *
    059     * Role role = access.getRoleById( accessToken, 1 );
    060     * </pre>
    061     *
    062     * <p>
    063     * If write access is needed as well, one has to acquire the exclusive <code>SecurityTransaction</code>. This is only
    064     * possible if the <code>User</code> has the "write"-privilege.
    065     * <p>
    066     * <b>Example Code: </b>
    067     *
    068     * <pre>
    069     *   SecurityAccess access = SecurityAccess.getInstance ();
    070     *   SecurityTransaction lock = access.acquireSecurityTransaction (user);
    071     *   access.registerUser (lock, &quot;TESTUSER&quot;);
    072     *   ...
    073     *   access.commitTransaction (lock);
    074     *   // after committing changes are made persistent
    075     * </pre>
    076     *
    077     * @author <a href="mschneider@lat-lon.de">Markus Schneider </a>
    078     * @author last edited by: $Author:wanhoff$
    079     *
    080     * @version $Revision: 23585 $, $Date:26.03.2007$
    081     *
    082     */
    083    public class SecurityAccessManager {
    084
    085        private static final ILogger LOG = LoggerFactory.getLogger( SecurityAccessManager.class );
    086
    087        private static SecurityAccessManager instance = null;
    088
    089        private SecurityRegistry registry = null;
    090
    091        // the currently valid (exclusive) transaction
    092        private SecurityTransaction currentTransaction;
    093
    094        // maximal duration that a transaction lasts (milliseconds)
    095        private long timeout;
    096
    097        // admin user (predefined)
    098        private User adminUser;
    099
    100        // admin group (predefined)
    101        // private Group adminGroup;
    102
    103        // admin role (predefined)
    104        private Role adminRole;
    105
    106        /**
    107         * Initializes the <code>SecurityAccessManager</code> -singleton with the given <code>Registry</code> -instance.
    108         *
    109         * @param registryClassName
    110         * @param registryProperties
    111         * @param timeout
    112         * @throws GeneralSecurityException
    113         */
    114        public static synchronized void initialize( String registryClassName, Properties registryProperties, long timeout )
    115                                throws GeneralSecurityException {
    116            if ( SecurityAccessManager.instance != null ) {
    117                throw new GeneralSecurityException( "SecurityAccessManager may only be initialized once." );
    118            }
    119            SecurityRegistry registry;
    120            try {
    121                registry = (SecurityRegistry) Class.forName( registryClassName ).newInstance();
    122            } catch ( Exception e ) {
    123                throw new GeneralSecurityException( "Unable to instantiate RegistryClass for class name '"
    124                                                    + registryClassName + "': " + e.getMessage() );
    125            }
    126            registry.initialize( registryProperties );
    127            SecurityAccessManager.instance = new SecurityAccessManager( registry, timeout );
    128        }
    129
    130        /**
    131         * @return true if there is an instance
    132         */
    133        public static boolean isInitialized() {
    134            return SecurityAccessManager.instance != null;
    135        }
    136
    137        /**
    138         * Returns the only instance of this class.
    139         *
    140         * @return the only instance of this class.
    141         * @throws GeneralSecurityException
    142         *
    143         */
    144        public static synchronized SecurityAccessManager getInstance()
    145                                throws GeneralSecurityException {
    146            if ( SecurityAccessManager.instance == null ) {
    147                throw new GeneralSecurityException( "SecurityAccessManager has not been initialized yet." );
    148            }
    149            return SecurityAccessManager.instance;
    150        }
    151
    152        /**
    153         * This method is only to be used to get an initial <code>User</code> object. (Otherwise one would need a
    154         * <code>User</code> to perform a <code>User</code> lookup.)
    155         *
    156         * @param name
    157         * @return the user
    158         * @throws GeneralSecurityException
    159         */
    160        public User getUserByName( String name )
    161                                throws GeneralSecurityException {
    162            return registry.getUserByName( null, name );
    163        }
    164
    165        /**
    166         * Tries to acquire a <code>SecurityAccess</code> -instance.
    167         *
    168         * @param user
    169         * @return the instance
    170         *
    171         * @throws GeneralSecurityException
    172         * @throws UnauthorizedException
    173         */
    174        public SecurityAccess acquireAccess( User user )
    175                                throws GeneralSecurityException, UnauthorizedException {
    176
    177            if ( user == null ) {
    178                throw new UnauthorizedException( "Can't acquire security access for anonymous user" );
    179            }
    180            if ( !user.isAuthenticated() ) {
    181                throw new UnauthorizedException( "Can't acquire security access for '" + user.getName()
    182                                                 + "'. User has not been authorized to the system." );
    183            }
    184
    185            return new SecurityAccess( user, registry );
    186        }
    187
    188        /**
    189         * Tries to acquire the <code>SecurityTransaction</code> for the given <code>User</code>. Only possibly for
    190         * <code>User</code> s that have the "modify"-privilege.
    191         * <p>
    192         * NOTE: The implementation checks if the <code>currentTransaction</code> timed out BEFORE it checks if the user is
    193         * allowed to write to the registry at all. This is because some JDBC-drivers (at least the JDBC-ODBC- bridge
    194         * together with Microsoft Access (tm)) have been observed to return strange results sometimes when there's a
    195         * transaction still going on (so that the privileges of the user cannot be retrieved reliably from the registry).
    196         *
    197         * @param user
    198         * @return the transaction
    199         *
    200         * @throws GeneralSecurityException
    201         * @throws UnauthorizedException
    202         */
    203        public SecurityTransaction acquireTransaction( User user )
    204                                throws GeneralSecurityException, UnauthorizedException {
    205            if ( currentTransaction != null ) {
    206                if ( System.currentTimeMillis() < currentTransaction.getTimestamp() + timeout ) {
    207                    throw new ReadWriteLockInUseException( "Can't get ReadWriteLock, because it is currently in use." );
    208                }
    209                try {
    210                    registry.abortTransaction( currentTransaction );
    211                } catch ( GeneralSecurityException e ) {
    212                    e.printStackTrace();
    213                }
    214
    215            }
    216            if ( !user.isAuthenticated() ) {
    217                throw new UnauthorizedException( "Can't acquire ReadWriteLock for '" + user.getName()
    218                                                 + "'. User has not been authorized " + "to the system." );
    219            }
    220            SecurityAccess tempAccess = new SecurityAccess( user, registry );
    221            if ( !user.hasPrivilege( tempAccess, tempAccess.getPrivilegeByName( "write" ) ) ) {
    222                throw new UnauthorizedException( "Can't acquire transaction: " + "User is not allowed to perform changes." );
    223            }
    224            currentTransaction = new SecurityTransaction( user, registry, adminRole );
    225            registry.beginTransaction( currentTransaction );
    226            return currentTransaction;
    227        }
    228
    229        /**
    230         * Private constructor to enforce the singleton pattern.
    231         *
    232         * @param registry
    233         * @param timeout
    234         * @throws GeneralSecurityException
    235         */
    236        private SecurityAccessManager( SecurityRegistry registry, long timeout ) throws GeneralSecurityException {
    237            this.registry = registry;
    238            this.timeout = timeout;
    239
    240            adminUser = getUserByName( "SEC_ADMIN" );
    241            SecurityAccess access = new SecurityAccess( adminUser, registry );
    242            // TODO adminGroup will never been read; can be removed?
    243            // adminGroup = access.getGroupByName( "SEC_ADMIN" );
    244            adminRole = registry.getRoleByName( access, "SEC_ADMIN" );
    245        }
    246
    247        /**
    248         * Verifies that the submitted <code>Transaction</code> is valid. There are two ways for it to become invalid:
    249         * <ul>
    250         * <li>it is too old (and timed out)
    251         * <li>it ended (has been aborted / committed)
    252         * </ul>
    253         *
    254         * @param transaction
    255         * @throws ReadWriteLockInvalidException
    256         * @throws GeneralSecurityException
    257         *             if transaction is invalid
    258         */
    259        void verify( SecurityTransaction transaction )
    260                                throws ReadWriteLockInvalidException {
    261            if ( transaction == null || transaction != currentTransaction ) {
    262                throw new ReadWriteLockInvalidException( "The SecurityTransaction is invalid." );
    263            } else if ( System.currentTimeMillis() > currentTransaction.getTimestamp() + timeout ) {
    264                long ts = currentTransaction.getTimestamp();
    265                currentTransaction = null;
    266                try {
    267                    registry.abortTransaction( transaction );
    268                } catch ( GeneralSecurityException e ) {
    269                    e.printStackTrace();
    270                }
    271                LOG.logInfo( "timeout: " + timeout );
    272                LOG.logInfo( "current: " + System.currentTimeMillis() );
    273                LOG.logInfo( "lock ts: " + ts );
    274
    275                throw new ReadWriteLockInvalidException( "The SecurityTransaction timed out." );
    276            }
    277            currentTransaction.renew();
    278        }
    279
    280        /**
    281         * Ends the current transaction and commits all changes to the <code>Registry</code>.
    282         *
    283         * @param transaction
    284         *
    285         * @throws GeneralSecurityException
    286         */
    287        public void commitTransaction( SecurityTransaction transaction )
    288                                throws GeneralSecurityException {
    289            verify( transaction );
    290            currentTransaction = null;
    291            registry.commitTransaction( transaction );
    292        }
    293
    294        /**
    295         * Aborts the current transaction and undoes all changes made to the <code>Registry</code>.
    296         *
    297         * @param lock
    298         *
    299         * @throws GeneralSecurityException
    300         */
    301        public void abortTransaction( SecurityTransaction lock )
    302                                throws GeneralSecurityException {
    303            verify( lock );
    304            currentTransaction = null;
    305            registry.abortTransaction( lock );
    306        }
    307    }