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 }