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 }