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