001 //$HeadURL: svn+ssh://jwilden@svn.wald.intevation.org/deegree/base/branches/2.5_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, "TESTUSER");
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 }