001: //$HeadURL: https://svn.wald.intevation.org/svn/deegree/base/trunk/src/org/deegree/security/drm/SecurityAccessManager.java $
002: /*---------------- FILE HEADER ------------------------------------------
003:
004: This file is part of deegree.
005: Copyright (C) 2001-2008 by:
006: EXSE, Department of Geography, University of Bonn
007: http://www.giub.uni-bonn.de/deegree/
008: lat/lon GmbH
009: http://www.lat-lon.de
010:
011: This library is free software; you can redistribute it and/or
012: modify it under the terms of the GNU Lesser General Public
013: License as published by the Free Software Foundation; either
014: version 2.1 of the License, or (at your option) any later version.
015:
016: This library is distributed in the hope that it will be useful,
017: but WITHOUT ANY WARRANTY; without even the implied warranty of
018: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
019: Lesser General Public License for more details.
020:
021: You should have received a copy of the GNU Lesser General Public
022: License along with this library; if not, write to the Free Software
023: Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
024:
025: Contact:
026:
027: Andreas Poth
028: lat/lon GmbH
029: Aennchenstr. 19
030: 53115 Bonn
031: Germany
032: E-Mail: poth@lat-lon.de
033:
034: Prof. Dr. Klaus Greve
035: Department of Geography
036: University of Bonn
037: Meckenheimer Allee 166
038: 53115 Bonn
039: Germany
040: E-Mail: greve@giub.uni-bonn.de
041:
042: ---------------------------------------------------------------------------*/
043: package org.deegree.security.drm;
044:
045: import java.util.Properties;
046:
047: import org.deegree.framework.log.ILogger;
048: import org.deegree.framework.log.LoggerFactory;
049: import org.deegree.security.GeneralSecurityException;
050: import org.deegree.security.UnauthorizedException;
051: import org.deegree.security.drm.model.Role;
052: import org.deegree.security.drm.model.User;
053:
054: /**
055: * This singleton manages access to the data stored in an associated <code>SecurityRegistry</code> -instance.
056: * <p>
057: * In order to use methods that read from the registry, a <code>SecurityAccess</code> instance has to be acquired
058: * first:
059: * <p>
060: * <b>Example Code: </b>
061: *
062: * <pre>
063: * SecurityAccess access = SecurityAccessManager.getInstance();
064: *
065: * ReadToken accessToken = access.acquireReadToken();
066: *
067: * Role role = access.getRoleById( accessToken, 1 );
068: * </pre>
069: *
070: * <p>
071: * If write access is needed as well, one has to acquire the exclusive <code>SecurityTransaction</code>. This is only
072: * possible if the <code>User</code> has the "write"-privilege.
073: * <p>
074: * <b>Example Code: </b>
075: *
076: * <pre>
077: * SecurityAccess access = SecurityAccess.getInstance ();
078: * SecurityTransaction lock = access.acquireSecurityTransaction (user);
079: * access.registerUser (lock, "TESTUSER");
080: * ...
081: * access.commitTransaction (lock);
082: * // after committing changes are made persistent
083: * </pre>
084: *
085: * @author <a href="mschneider@lat-lon.de">Markus Schneider </a>
086: * @author last edited by: $Author:wanhoff$
087: *
088: * @version $Revision: 10606 $, $Date:26.03.2007$
089: *
090: */
091: public class SecurityAccessManager {
092:
093: private static final ILogger LOG = LoggerFactory
094: .getLogger(SecurityAccessManager.class);
095:
096: private static SecurityAccessManager instance = null;
097:
098: private SecurityRegistry registry = null;
099:
100: // the currently valid (exclusive) transaction
101: private SecurityTransaction currentTransaction;
102:
103: // maximal duration that a transaction lasts (milliseconds)
104: private long timeout;
105:
106: // admin user (predefined)
107: private User adminUser;
108:
109: // admin group (predefined)
110: // private Group adminGroup;
111:
112: // admin role (predefined)
113: private Role adminRole;
114:
115: /**
116: * Initializes the <code>SecurityAccessManager</code> -singleton with the given <code>Registry</code> -instance.
117: *
118: * @param registryClassName
119: * @param registryProperties
120: * @param timeout
121: * @throws GeneralSecurityException
122: */
123: public static synchronized void initialize(
124: String registryClassName, Properties registryProperties,
125: long timeout) throws GeneralSecurityException {
126: if (SecurityAccessManager.instance != null) {
127: throw new GeneralSecurityException(
128: "SecurityAccessManager may only be initialized once.");
129: }
130: SecurityRegistry registry;
131: try {
132: registry = (SecurityRegistry) Class.forName(
133: registryClassName).newInstance();
134: } catch (Exception e) {
135: throw new GeneralSecurityException(
136: "Unable to instantiate RegistryClass for class name '"
137: + registryClassName + "': "
138: + e.getMessage());
139: }
140: registry.initialize(registryProperties);
141: SecurityAccessManager.instance = new SecurityAccessManager(
142: registry, timeout);
143: }
144:
145: /**
146: * @return true if there is an instance
147: */
148: public static boolean isInitialized() {
149: return SecurityAccessManager.instance != null;
150: }
151:
152: /**
153: * Returns the only instance of this class.
154: *
155: * @return the only instance of this class.
156: * @throws GeneralSecurityException
157: *
158: */
159: public static synchronized SecurityAccessManager getInstance()
160: throws GeneralSecurityException {
161: if (SecurityAccessManager.instance == null) {
162: throw new GeneralSecurityException(
163: "SecurityAccessManager has not been initialized yet.");
164: }
165: return SecurityAccessManager.instance;
166: }
167:
168: /**
169: * This method is only to be used to get an initial <code>User</code> object. (Otherwise one would need a
170: * <code>User</code> to perform a <code>User</code> lookup.)
171: *
172: * @param name
173: * @return the user
174: * @throws GeneralSecurityException
175: */
176: public User getUserByName(String name)
177: throws GeneralSecurityException {
178: return registry.getUserByName(null, name);
179: }
180:
181: /**
182: * Tries to acquire a <code>SecurityAccess</code> -instance.
183: *
184: * @param user
185: * @return the instance
186: *
187: * @throws GeneralSecurityException
188: * @throws UnauthorizedException
189: */
190: public SecurityAccess acquireAccess(User user)
191: throws GeneralSecurityException, UnauthorizedException {
192:
193: if (user == null) {
194: throw new UnauthorizedException(
195: "Can't acquire security access for anonymous user");
196: }
197: if (!user.isAuthenticated()) {
198: throw new UnauthorizedException(
199: "Can't acquire security access for '"
200: + user.getName()
201: + "'. User has not been authorized to the system.");
202: }
203:
204: return new SecurityAccess(user, registry);
205: }
206:
207: /**
208: * Tries to acquire the <code>SecurityTransaction</code> for the given <code>User</code>. Only possibly for
209: * <code>User</code> s that have the "modify"-privilege.
210: * <p>
211: * NOTE: The implementation checks if the <code>currentTransaction</code> timed out BEFORE it checks if the user
212: * is allowed to write to the registry at all. This is because some JDBC-drivers (at least the JDBC-ODBC- bridge
213: * together with Microsoft Access (tm)) have been observed to return strange results sometimes when there's a
214: * transaction still going on (so that the privileges of the user cannot be retrieved reliably from the registry).
215: *
216: * @param user
217: * @return the transaction
218: *
219: * @throws GeneralSecurityException
220: * @throws UnauthorizedException
221: */
222: public SecurityTransaction acquireTransaction(User user)
223: throws GeneralSecurityException, UnauthorizedException {
224: if (currentTransaction != null) {
225: if (System.currentTimeMillis() < currentTransaction
226: .getTimestamp()
227: + timeout) {
228: throw new ReadWriteLockInUseException(
229: "Can't get ReadWriteLock, because it is currently in use.");
230: }
231: try {
232: registry.abortTransaction(currentTransaction);
233: } catch (GeneralSecurityException e) {
234: e.printStackTrace();
235: }
236:
237: }
238: if (!user.isAuthenticated()) {
239: throw new UnauthorizedException(
240: "Can't acquire ReadWriteLock for '"
241: + user.getName()
242: + "'. User has not been authorized "
243: + "to the system.");
244: }
245: SecurityAccess tempAccess = new SecurityAccess(user, registry);
246: if (!user.hasPrivilege(tempAccess, tempAccess
247: .getPrivilegeByName("write"))) {
248: throw new UnauthorizedException(
249: "Can't acquire transaction: "
250: + "User is not allowed to perform changes.");
251: }
252: currentTransaction = new SecurityTransaction(user, registry,
253: adminRole);
254: registry.beginTransaction(currentTransaction);
255: return currentTransaction;
256: }
257:
258: /**
259: * Private constructor to enforce the singleton pattern.
260: *
261: * @param registry
262: * @param timeout
263: * @throws GeneralSecurityException
264: */
265: private SecurityAccessManager(SecurityRegistry registry,
266: long timeout) throws GeneralSecurityException {
267: this .registry = registry;
268: this .timeout = timeout;
269:
270: adminUser = getUserByName("SEC_ADMIN");
271: SecurityAccess access = new SecurityAccess(adminUser, registry);
272: // TODO adminGroup will never been read; can be removed?
273: // adminGroup = access.getGroupByName( "SEC_ADMIN" );
274: adminRole = registry.getRoleByName(access, "SEC_ADMIN");
275: }
276:
277: /**
278: * Verifies that the submitted <code>Transaction</code> is valid. There are two ways for it to become invalid:
279: * <ul>
280: * <li>it is too old (and timed out)
281: * <li>it ended (has been aborted / committed)
282: * </ul>
283: *
284: * @param transaction
285: * @throws ReadWriteLockInvalidException
286: * @throws GeneralSecurityException
287: * if transaction is invalid
288: */
289: void verify(SecurityTransaction transaction)
290: throws ReadWriteLockInvalidException {
291: if (transaction == null || transaction != currentTransaction) {
292: throw new ReadWriteLockInvalidException(
293: "The SecurityTransaction is invalid.");
294: } else if (System.currentTimeMillis() > currentTransaction
295: .getTimestamp()
296: + timeout) {
297: currentTransaction = null;
298: try {
299: registry.abortTransaction(transaction);
300: } catch (GeneralSecurityException e) {
301: e.printStackTrace();
302: }
303: LOG.logInfo("timeout: " + timeout);
304: LOG.logInfo("current: " + System.currentTimeMillis());
305: LOG
306: .logInfo("lock ts: "
307: + currentTransaction.getTimestamp());
308:
309: throw new ReadWriteLockInvalidException(
310: "The SecurityTransaction timed out.");
311: }
312: currentTransaction.renew();
313: }
314:
315: /**
316: * Ends the current transaction and commits all changes to the <code>Registry</code>.
317: *
318: * @param transaction
319: *
320: * @throws GeneralSecurityException
321: */
322: public void commitTransaction(SecurityTransaction transaction)
323: throws GeneralSecurityException {
324: verify(transaction);
325: currentTransaction = null;
326: registry.commitTransaction(transaction);
327: }
328:
329: /**
330: * Aborts the current transaction and undoes all changes made to the <code>Registry</code>.
331: *
332: * @param lock
333: *
334: * @throws GeneralSecurityException
335: */
336: public void abortTransaction(SecurityTransaction lock)
337: throws GeneralSecurityException {
338: verify(lock);
339: currentTransaction = null;
340: registry.abortTransaction(lock);
341: }
342: }
|