JSEC-37 - architectural refactoring in preparation for 1.0 - simplified implementations and implemented some best practices based on Joshua Bloch's "Effective Java" book.
git-svn-id: https://svn.apache.org/repos/asf/incubator/jsecurity/trunk@735625 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/core/src/org/jsecurity/authc/Authenticator.java b/core/src/org/jsecurity/authc/Authenticator.java
index c98d2ed..ca9e38d 100644
--- a/core/src/org/jsecurity/authc/Authenticator.java
+++ b/core/src/org/jsecurity/authc/Authenticator.java
@@ -21,16 +21,15 @@
/**
* An Authenticator is responsible for authenticating accounts in an application. It
* is one of the primary entry points into the JSecurity API.
- *
- * <p>Although not a requirement, there is usually a single 'master' Authenticator configured for
+ * <p/>
+ * Although not a requirement, there is usually a single 'master' Authenticator configured for
* an application. Enabling Pluggable Authentication Module (PAM) behavior
- * (Two Phase Commit, etc.) is usually achieved by the single <tt>Authenticator</tt> coordinating
- * and interacting with an application-configured set of
- * {@link org.jsecurity.realm.Realm Realm}s.
- *
- * <p>Note that most JSecurity users will not interact with an <tt>Authenticator</tt> instance directly. JSecurity's
- * default architecture is based on an overall <tt>SecurityManager</tt> which typically wraps an
- * <tt>Authenticator</tt> instance.
+ * (Two Phase Commit, etc.) is usually achieved by the single {@code Authenticator} coordinating
+ * and interacting with an application-configured set of {@link org.jsecurity.realm.Realm Realm}s.
+ * <p/>
+ * Note that most JSecurity users will not interact with an {@code Authenticator} instance directly.
+ * JSecurity's default architecture is based on an overall {@code SecurityManager} which typically
+ * wraps an {@code Authenticator} instance.
*
* @author Les Hazlewood
* @author Jeremy Haile
@@ -42,22 +41,22 @@
public interface Authenticator {
/**
- * Authenticates a user based on the submitted <tt>authenticationToken</tt>.
+ * Authenticates a user based on the submitted {@code AuthenticationToken}.
+ * <p/>
+ * If the authentication is successful, an {@link AuthenticationInfo} instance is returned that represents the
+ * user's account data relevant to JSecurity. This returned object is generally used in turn to construct a
+ * {@code Subject} representing a more complete security-specific 'view' of an account that also allows access to
+ * a {@code Session}.
*
- * <p>If the authentication is successful, an {@link AuthenticationInfo}
- * object is returned that represents the user's account data relevant to JSecurity. This returned object is
- * generally used in turn to construct a <tt>Subject</tt> representing that user's identity and
- * access to a <tt>Session</tt>
- *
- * @param authenticationToken any representation of a user's principals and credentials
- * submitted during an authentication attempt.
+ * @param authenticationToken any representation of a user's principals and credentials submitted during an
+ * authentication attempt.
* @return the AuthenticationInfo representing the authenticating user's account data.
* @throws AuthenticationException if there is any problem during the authentication process.
- * See the specific exceptions listed below to as examples of what could happen in order
- * to accurately handle these problems and to notify the user in an appropriate manner why
- * the authentication attempt failed. Realize an implementation of this interface may or may
- * not throw those listed or may throw other AuthenticationExceptions, but the list shows
- * the most common ones.
+ * See the specific exceptions listed below to as examples of what could happen
+ * in order to accurately handle these problems and to notify the user in an
+ * appropriate manner why the authentication attempt failed. Realize an
+ * implementation of this interface may or may not throw those listed or may
+ * throw other AuthenticationExceptions, but the list shows the most common ones.
* @see ExpiredCredentialsException
* @see IncorrectCredentialsException
* @see ExcessiveAttemptsException
diff --git a/core/src/org/jsecurity/crypto/BlowfishCipher.java b/core/src/org/jsecurity/crypto/BlowfishCipher.java
index 38fa956..45c210b 100644
--- a/core/src/org/jsecurity/crypto/BlowfishCipher.java
+++ b/core/src/org/jsecurity/crypto/BlowfishCipher.java
@@ -94,9 +94,10 @@
* Returns the default {@link Key Key} to use for symmetric encryption and decryption if one is not specified during
* encryption/decryption. For truly secure applications,
* you should always specify your own key via the {@link #setKey(java.security.Key) setKey} method.
+ *
* @return the {@link Key Key} to use for symmetric encryption and decryption.
* @see #encrypt(byte[], byte[])
- * @see #decrypt(byte[], byte[])
+ * @see #decrypt(byte[], byte[])
*/
public Key getKey() {
return key;
@@ -106,6 +107,7 @@
* Sets the internal default {@link Key Key} to use for symmetric encryption and decryption if one is not
* specified during encryption/decryption. For truly secure applications, you should always specify your own
* key via this method.
+ *
* @param key the key to use for symmetric encryption and decryption.
* @see #encrypt(byte[], byte[])
* @see #decrypt(byte[], byte[])
@@ -142,9 +144,10 @@
/**
* Returns a new {@link javax.crypto.Cipher Cipher} instance to use for encryption/decryption operations, based on
* the {@link #TRANSFORMATION_STRING TRANSFORMATION_STRING} constant.
+ *
* @return a new Cipher instance.
* @throws IllegalStateException if a new Cipher instance cannot be constructed based on the
- * {@link #TRANSFORMATION_STRING TRANSFORMATION_STRING} constant.
+ * {@link #TRANSFORMATION_STRING TRANSFORMATION_STRING} constant.
*/
protected javax.crypto.Cipher newCipherInstance() throws IllegalStateException {
try {
@@ -163,8 +166,8 @@
* potential {@link InvalidKeyException InvalidKeyException} that might arise.
*
* @param cipher the JDK Cipher to {@link javax.crypto.Cipher#init(int, java.security.Key) init}.
- * @param mode the Cipher mode
- * @param key the Cipher's Key
+ * @param mode the Cipher mode
+ * @param key the Cipher's Key
*/
protected void init(javax.crypto.Cipher cipher, int mode, java.security.Key key) {
try {
@@ -178,8 +181,9 @@
/**
* Calls the {@link javax.crypto.Cipher#doFinal(byte[]) doFinal(bytes)} method, propagating any exception that
* might arise in an {@link IllegalStateException IllegalStateException}
+ *
* @param cipher the JDK Cipher to finalize (perform the actual cryption)
- * @param bytes the bytes to crypt
+ * @param bytes the bytes to crypt
* @return the resulting crypted byte array.
*/
protected byte[] crypt(javax.crypto.Cipher cipher, byte[] bytes) {
@@ -195,9 +199,10 @@
* Calls the {@link #init(javax.crypto.Cipher, int, java.security.Key)} and then
* {@link #crypt(javax.crypto.Cipher, byte[])}. Ensures that the key is never null by using the
* {@link #getKey() default key} if the method argument key is <code>null</code>.
+ *
* @param bytes the bytes to crypt
- * @param mode the JDK Cipher mode
- * @param key the key to use to do the cryption. If <code>null</code> the {@link #getKey() default key} will be used.
+ * @param mode the JDK Cipher mode
+ * @param key the key to use to do the cryption. If <code>null</code> the {@link #getKey() default key} will be used.
* @return the resulting crypted byte array
*/
protected byte[] crypt(byte[] bytes, int mode, byte[] key) {
@@ -216,7 +221,8 @@
/**
* Generates a new {@link Key Key} suitable for this Cipher by calling
- * {@link #generateNewKey() generateNewKey(128)} (uses a 128 bit size by default).
+ * {@link #generateNewKey(int) generateNewKey(128)} (uses a 128 bit size by default).
+ *
* @return a new {@link Key Key}, 128 bits in length.
*/
public static Key generateNewKey() {
@@ -226,6 +232,7 @@
/**
* Generates a new {@link Key Key} of the specified size suitable for this Cipher
* (based on the {@link #ALGORITHM ALGORITHM} using the JDK {@link KeyGenerator KeyGenerator}.
+ *
* @param keyBitSize the bit size of the key to create
* @return the created key suitable for use with this Cipher.
*/
@@ -243,6 +250,7 @@
/**
* Simple test main method to ensure functionality is correct. Should really be moved to a proper test case.
+ *
* @param unused ignored
* @throws Exception if anything unexpected happens.
*/
diff --git a/core/src/org/jsecurity/crypto/Cipher.java b/core/src/org/jsecurity/crypto/Cipher.java
index 96af814..4fb37b8 100644
--- a/core/src/org/jsecurity/crypto/Cipher.java
+++ b/core/src/org/jsecurity/crypto/Cipher.java
@@ -23,10 +23,10 @@
* an uninterpretable format. The resulting encrypted output is only able to be converted back to original form with
* a <tt>Key</tt> as well.
*
- * <p>In what is known as <em>Symmetric</em> <tt>Cipher</tt>s, the <tt>Key</tt> used to encrypt the source is the same
+ * <p>For what is known as <em>Symmetric</em> <tt>Cipher</tt>s, the <tt>Key</tt> used to encrypt the source is the same
* as (or trivially similar to) the <tt>Key</tt> used to decrypt it.
*
- * <p>In <em>Assymetric</em> <tt>Cipher</tt>s, the encryption <tt>Key</tt> is not the same as the decryption <tt>Key</tt>.
+ * <p>For <em>Assymetric</em> <tt>Cipher</tt>s, the encryption <tt>Key</tt> is not the same as the decryption <tt>Key</tt>.
* The most common type of Assymetric Ciphers are based on what is called public/private key pairs:
*
* <p>A <em>private</em> key is known only to a single party, and as its name implies, is supposed be kept very private
diff --git a/core/src/org/jsecurity/subject/AbstractRememberMeManager.java b/core/src/org/jsecurity/mgt/AbstractRememberMeManager.java
similarity index 94%
rename from core/src/org/jsecurity/subject/AbstractRememberMeManager.java
rename to core/src/org/jsecurity/mgt/AbstractRememberMeManager.java
index d417266..2ecdfdc 100644
--- a/core/src/org/jsecurity/subject/AbstractRememberMeManager.java
+++ b/core/src/org/jsecurity/mgt/AbstractRememberMeManager.java
@@ -16,7 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.jsecurity.subject;
+package org.jsecurity.mgt;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -31,6 +31,7 @@
import org.jsecurity.io.DefaultSerializer;
import org.jsecurity.io.SerializationException;
import org.jsecurity.io.Serializer;
+import org.jsecurity.subject.PrincipalCollection;
/**
* Abstract implementation of the <code>RememberMeManager</code> interface that handles
@@ -47,7 +48,9 @@
//TODO - complete JavaDoc
- /** private inner log instance. */
+ /**
+ * private inner log instance.
+ */
private static final Log log = LogFactory.getLog(AbstractRememberMeManager.class);
private Serializer serializer = new DefaultSerializer();
@@ -207,20 +210,22 @@
}
return principals;
- } catch( Exception e ) {
- return onRememberedPrincipalFailure( e );
+ } catch (Exception e) {
+ return onRememberedPrincipalFailure(e);
}
}
/**
* Called when an exception is thrown while trying to retrieve principals. The default implementation logs a
- * warning and forgets the problem identity. This most commonly would occur when an encryption key is
- * updated and old principals are retrieved that have been encrypted with the previous key.
+ * warning and forgets ('unremembers') the problem identity by calling {@link #forgetIdentity() forgetIdentity()}.
+ * This most commonly would occur when an encryption key is updated and old principals are retrieved that have
+ * been encrypted with the previous key.\
+ *
* @param e the exception that was thrown.
- * @return the principal collection to be returned.
+ * @return <code>null</code> in all cases.
*/
protected PrincipalCollection onRememberedPrincipalFailure(Exception e) {
- if(log.isWarnEnabled() ) {
+ if (log.isWarnEnabled()) {
log.warn("There was a failure while trying to retrieve remembered principals. This could be due to a " +
"configuration problem or corrupted principals. This could also be due to a recently " +
"changed encryption key. The remembered identity will be forgotten and not used for this " +
diff --git a/core/src/org/jsecurity/mgt/AuthenticatingSecurityManager.java b/core/src/org/jsecurity/mgt/AuthenticatingSecurityManager.java
index 9182f27..1584ae4 100644
--- a/core/src/org/jsecurity/mgt/AuthenticatingSecurityManager.java
+++ b/core/src/org/jsecurity/mgt/AuthenticatingSecurityManager.java
@@ -51,10 +51,11 @@
/**
* Default no-arg constructor that initializes its internal
- * <code>authenticator</code> instance by just calling {@link #createAuthenticator() createAuthenticator()}.
+ * <code>authenticator</code> instance to a
+ * {@link org.jsecurity.authc.pam.ModularRealmAuthenticator ModularRealmAuthenticator}.
*/
public AuthenticatingSecurityManager() {
- setAuthenticator(createAuthenticator());
+ this.authenticator = new ModularRealmAuthenticator();
}
/**
@@ -88,17 +89,6 @@
}
/**
- * Creates the default <code>Authenticator</code> instance to use at startup. This simple default implementation
- * merely returns <code>new {@link ModularRealmAuthenticator ModularRealmAuthenticator}()</code>.
- *
- * @return the default <code>Authenticator</code> instance to use at startup.
- * @since 1.0
- */
- protected Authenticator createAuthenticator() {
- return new ModularRealmAuthenticator();
- }
-
- /**
* Sets the {@link org.jsecurity.authc.pam.ModularAuthenticationStrategy ModularAuthenticationStrategy} to use
* in multi-realm environments.
*
@@ -149,6 +139,16 @@
((AuthenticationListenerRegistrar) this.authenticator).setAuthenticationListeners(listeners);
}
+ public void add(AuthenticationListener listener) {
+ assertAuthenticatorListenerSupport();
+ ((AuthenticationListenerRegistrar) this.authenticator).add(listener);
+ }
+
+ public boolean remove(AuthenticationListener listener) {
+ return (this.authenticator instanceof AuthenticationListenerRegistrar) &&
+ ((AuthenticationListenerRegistrar) this.authenticator).remove(listener);
+ }
+
/**
* Ensures that <code>this.authenticator</code> implements the
* {@link org.jsecurity.authc.AuthenticationListenerRegistrar AuthenticationListenerRegistrar} interface to ensure
@@ -164,18 +164,6 @@
}
}
- public void add(AuthenticationListener listener) {
- assertAuthenticatorListenerSupport();
- Authenticator authc = getAuthenticator();
- ((AuthenticationListenerRegistrar) authc).add(listener);
- }
-
- public boolean remove(AuthenticationListener listener) {
- Authenticator authc = getAuthenticator();
- return (authc instanceof AuthenticationListenerRegistrar) &&
- ((AuthenticationListenerRegistrar) authc).remove(listener);
- }
-
/**
* Immediately calls {@link RealmSecurityManager#setRealms(java.util.Collection) super.setRealms} and then
* additionally passes on those realms to the internal delegate <code>Authenticator</code> instance so
diff --git a/core/src/org/jsecurity/mgt/AuthorizingSecurityManager.java b/core/src/org/jsecurity/mgt/AuthorizingSecurityManager.java
index 6739979..c755c49 100644
--- a/core/src/org/jsecurity/mgt/AuthorizingSecurityManager.java
+++ b/core/src/org/jsecurity/mgt/AuthorizingSecurityManager.java
@@ -52,14 +52,14 @@
/**
* The wrapped instance to which all of this <tt>SecurityManager</tt> authorization calls are delegated.
*/
- protected Authorizer authorizer;
+ private Authorizer authorizer;
/**
- * Default no-arg constructor that initializes its internal <code>authorizer</code> instance by just
- * calling {@link #createAuthorizer() createAuthorizer()}.
+ * Default no-arg constructor that initializes an internal default
+ * {@link ModularRealmAuthorizer ModularRealmAuthorizer}.
*/
public AuthorizingSecurityManager() {
- setAuthorizer(createAuthorizer());
+ this.authorizer = new ModularRealmAuthorizer();
}
/**
@@ -88,18 +88,6 @@
}
/**
- * Creates a new {@link Authorizer Authorizer} instance to be used by this <code>AuthorizingSecurityManager</code> instance.
- * <p/>
- * This default implementation merely returns
- * <code>new {@link org.jsecurity.authz.ModularRealmAuthorizer ModularRealmAuthorizer}()</code>
- *
- * @return a new {@link Authorizer Authorizer} instance to be used by this <code>AuthorizingSecurityManager</code> instance.
- */
- protected Authorizer createAuthorizer() {
- return new ModularRealmAuthorizer();
- }
-
- /**
* Sets the <tt>PermissionResolver</tt> instance that will be passed on to the underlying default wrapped
* {@link Authorizer Authorizer}.
*
@@ -140,9 +128,8 @@
*/
public void setRealms(Collection<Realm> realms) {
super.setRealms(realms);
- Authorizer authz = getAuthorizer();
- if (authz instanceof ModularRealmAuthorizer) {
- ((ModularRealmAuthorizer) authz).setRealms(realms);
+ if (this.authorizer instanceof ModularRealmAuthorizer) {
+ ((ModularRealmAuthorizer) this.authorizer).setRealms(realms);
}
}
@@ -176,62 +163,62 @@
}
public boolean isPermitted(PrincipalCollection principals, String permissionString) {
- return getAuthorizer().isPermitted(principals, permissionString);
+ return this.authorizer.isPermitted(principals, permissionString);
}
public boolean isPermitted(PrincipalCollection principals, Permission permission) {
- return getAuthorizer().isPermitted(principals, permission);
+ return this.authorizer.isPermitted(principals, permission);
}
public boolean[] isPermitted(PrincipalCollection principals, String... permissions) {
- return getAuthorizer().isPermitted(principals, permissions);
+ return this.authorizer.isPermitted(principals, permissions);
}
public boolean[] isPermitted(PrincipalCollection principals, List<Permission> permissions) {
- return getAuthorizer().isPermitted(principals, permissions);
+ return this.authorizer.isPermitted(principals, permissions);
}
public boolean isPermittedAll(PrincipalCollection principals, String... permissions) {
- return getAuthorizer().isPermittedAll(principals, permissions);
+ return this.authorizer.isPermittedAll(principals, permissions);
}
public boolean isPermittedAll(PrincipalCollection principals, Collection<Permission> permissions) {
- return getAuthorizer().isPermittedAll(principals, permissions);
+ return this.authorizer.isPermittedAll(principals, permissions);
}
public void checkPermission(PrincipalCollection principals, String permission) throws AuthorizationException {
- getAuthorizer().checkPermission(principals, permission);
+ this.authorizer.checkPermission(principals, permission);
}
public void checkPermission(PrincipalCollection principals, Permission permission) throws AuthorizationException {
- getAuthorizer().checkPermission(principals, permission);
+ this.authorizer.checkPermission(principals, permission);
}
public void checkPermissions(PrincipalCollection principals, String... permissions) throws AuthorizationException {
- getAuthorizer().checkPermissions(principals, permissions);
+ this.authorizer.checkPermissions(principals, permissions);
}
public void checkPermissions(PrincipalCollection principals, Collection<Permission> permissions) throws AuthorizationException {
- getAuthorizer().checkPermissions(principals, permissions);
+ this.authorizer.checkPermissions(principals, permissions);
}
public boolean hasRole(PrincipalCollection principals, String roleIdentifier) {
- return getAuthorizer().hasRole(principals, roleIdentifier);
+ return this.authorizer.hasRole(principals, roleIdentifier);
}
public boolean[] hasRoles(PrincipalCollection principals, List<String> roleIdentifiers) {
- return getAuthorizer().hasRoles(principals, roleIdentifiers);
+ return this.authorizer.hasRoles(principals, roleIdentifiers);
}
public boolean hasAllRoles(PrincipalCollection principals, Collection<String> roleIdentifiers) {
- return getAuthorizer().hasAllRoles(principals, roleIdentifiers);
+ return this.authorizer.hasAllRoles(principals, roleIdentifiers);
}
public void checkRole(PrincipalCollection principals, String role) throws AuthorizationException {
- getAuthorizer().checkRole(principals, role);
+ this.authorizer.checkRole(principals, role);
}
public void checkRoles(PrincipalCollection principals, Collection<String> roles) throws AuthorizationException {
- getAuthorizer().checkRoles(principals, roles);
+ this.authorizer.checkRoles(principals, roles);
}
}
diff --git a/core/src/org/jsecurity/mgt/CachingSecurityManager.java b/core/src/org/jsecurity/mgt/CachingSecurityManager.java
index db7f5d8..683baa2 100644
--- a/core/src/org/jsecurity/mgt/CachingSecurityManager.java
+++ b/core/src/org/jsecurity/mgt/CachingSecurityManager.java
@@ -18,8 +18,6 @@
*/
package org.jsecurity.mgt;
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
import org.jsecurity.cache.CacheManager;
import org.jsecurity.cache.CacheManagerAware;
import org.jsecurity.cache.DefaultCacheManager;
@@ -27,13 +25,12 @@
import org.jsecurity.util.LifecycleUtils;
/**
- * A very basic extension point for the SecurityManager interface that merely provides logging and caching
- * support. All <tt>SecurityManager</tt> method implementations are left to subclasses.
+ * A very basic starting point for the SecurityManager interface that merely provides logging and caching
+ * support. All actual {@code SecurityManager} method implementations are left to subclasses.
*
- * <p>Upon instantiation, a sensible default {@link CacheManager CacheManager} will be created automatically via the
- * {@link #createCacheManager() createCacheManager() method. This <code>CacheManager</code>
- * can then be used by subclass implementations and children components for use to achieve better application
- * performance.
+ * <p>Upon instantiation, a sensible default {@link CacheManager CacheManager} will be created automatically. This
+ * {@code CacheManager} can then be used by subclass implementations and children components for use to achieve better
+ * application performance if so desired.
*
* @author Les Hazlewood
* @author Jeremy Haile
@@ -42,20 +39,15 @@
public abstract class CachingSecurityManager implements SecurityManager, Destroyable, CacheManagerAware {
/**
- * Internal private static log instance.
- */
- private static final Log log = LogFactory.getLog(CachingSecurityManager.class);
-
- /**
* The CacheManager to use to perform caching operations to enhance performance. Can be null.
*/
- protected CacheManager cacheManager;
+ private CacheManager cacheManager;
/**
* Default no-arg constructor that will automatically attempt to initialize a default cacheManager
*/
public CachingSecurityManager() {
- setCacheManager(createCacheManager());
+ this.cacheManager = new DefaultCacheManager();
}
/**
@@ -93,24 +85,6 @@
}
/**
- * Creates a {@link CacheManager CacheManager} instance to be used by this <code>SecurityManager</code>
- * and potentially any of its children components.
- * <p/>
- * This default implementation returns a new
- * {@link org.jsecurity.cache.DefaultCacheManager DefaultCacheManager}, which uses in-memory (memory-leak safe)
- * caches which are production safe.
- * <p/>
- * This can be overridden by subclasses for a different implementation, but it is often easier to set a
- * different implementation via the {@link #setCacheManager(org.jsecurity.cache.CacheManager) setCacheManager}
- * method, for example in code or Dependency Injection frameworks (a la Spring or JEE 3).
- *
- * @return a newly created <code>CacheManager</code> instance.
- */
- protected CacheManager createCacheManager() {
- return new DefaultCacheManager();
- }
-
- /**
* First calls {@link #beforeCacheManagerDestroyed() beforeCacheManagerDestroyed()} to allow subclasses to clean up
* first, then calls {@link #destroyCacheManager() destroyCacheManager()} to clean up the internal
* {@link CacheManager CacheManager}.
diff --git a/core/src/org/jsecurity/mgt/DefaultSecurityManager.java b/core/src/org/jsecurity/mgt/DefaultSecurityManager.java
index 5487675..2041a97 100644
--- a/core/src/org/jsecurity/mgt/DefaultSecurityManager.java
+++ b/core/src/org/jsecurity/mgt/DefaultSecurityManager.java
@@ -21,17 +21,19 @@
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jsecurity.authc.*;
+import org.jsecurity.authz.AuthorizationException;
import org.jsecurity.authz.Authorizer;
import org.jsecurity.crypto.Cipher;
import org.jsecurity.realm.Realm;
import org.jsecurity.session.InvalidSessionException;
import org.jsecurity.session.Session;
-import org.jsecurity.subject.AbstractRememberMeManager;
+import org.jsecurity.session.mgt.DelegatingSession;
import org.jsecurity.subject.PrincipalCollection;
-import org.jsecurity.subject.RememberMeManager;
import org.jsecurity.subject.Subject;
import org.jsecurity.util.ThreadContext;
+import java.io.Serializable;
+import java.net.InetAddress;
import java.util.Collection;
/**
@@ -42,11 +44,9 @@
* implementation.</p>
*
* <p>To greatly reduce and simplify configuration, this implementation (and its superclasses) will
- * create suitable defaults for <em>all</em> of its required dependencies. Therefore, you only need to override
- * attributes for custom behavior. But, note the following:</p>
- *
- * <p>Unless you're happy with the default simple {@link org.jsecurity.realm.text.PropertiesRealm properties file}-based realm, which may or
- * may not be flexible enough for enterprise applications, you might want to specify at least one custom
+ * create suitable defaults for all of its required dependencies, <em>except</em> the required one or more
+ * {@link Realm Realm}s. Because <code>Realm</code> implementations usually interact with an application's data model,
+ * they are almost always application specific; you will want to specify at least one custom
* <tt>Realm</tt> implementation that 'knows' about your application's data/security model
* (via {@link #setRealm} or one of the overloaded constructors). All other attributes in this class hierarchy
* will have suitable defaults for most enterprise applications.</p>
@@ -58,7 +58,7 @@
*
* <p>Because RememberMe services are inherently client tier-specific and
* therefore aplication-dependent, if you want <tt>RememberMe</tt> services enabled, you will have to specify an
- * instance yourself via the {@link #setRememberMeManager(org.jsecurity.subject.RememberMeManager) setRememberMeManager}
+ * instance yourself via the {@link #setRememberMeManager(RememberMeManager) setRememberMeManager}
* mutator. However if you're reading this JavaDoc with the
* expectation of operating in a Web environment, take a look at the
* {@link org.jsecurity.web.DefaultWebSecurityManager DefaultWebSecurityManager} implementation, which
@@ -85,8 +85,8 @@
* Default no-arg constructor.
*/
public DefaultSecurityManager() {
+ setSubjectFactory(new DefaultSubjectFactory());
setSubjectBinder(new SessionSubjectBinder());
- setSubjectFactory(new DefaultSubjectFactory(this));
}
/**
@@ -115,6 +115,9 @@
public void setSubjectFactory(SubjectFactory subjectFactory) {
this.subjectFactory = subjectFactory;
+ if (this.subjectFactory instanceof SecurityManagerAware) {
+ ((SecurityManagerAware) this.subjectFactory).setSecurityManager(this);
+ }
}
public SubjectBinder getSubjectBinder() {
@@ -134,14 +137,13 @@
}
private AbstractRememberMeManager getRememberMeManagerForCipherAttributes() {
- RememberMeManager rmm = getRememberMeManager();
- if (!(rmm instanceof AbstractRememberMeManager)) {
+ if (!(this.rememberMeManager instanceof AbstractRememberMeManager)) {
String msg = "The convenience passthrough methods for setting remember me cipher attributes " +
"are only available when the underlying RememberMeManager implementation is a subclass of " +
AbstractRememberMeManager.class.getName() + ".";
throw new IllegalStateException(msg);
}
- return (AbstractRememberMeManager) rmm;
+ return (AbstractRememberMeManager) this.rememberMeManager;
}
public void setRememberMeCipher(Cipher cipher) {
@@ -185,8 +187,32 @@
}
protected Subject createSubject() {
- PrincipalCollection principals = getRememberedIdentity();
- return getSubjectFactory().createSubject(principals, null, false, null);
+ Subject subject = null;
+
+ Serializable sessionId = ThreadContext.getSessionId();
+ if (sessionId != null) {
+ try {
+ subject = getSubjectBySessionId(sessionId);
+ } catch (InvalidSessionException e) {
+ if (log.isDebugEnabled()) {
+ log.debug("Session id referenced on the current thread [" + sessionId + "] is invalid. " +
+ "Ignoring and creating a new Subject instance to continue. This message can be " +
+ "safely ignored.", e);
+ }
+ } catch (AuthorizationException e) {
+ if (log.isWarnEnabled()) {
+ log.warn("Session id referenced on the current thread [" + sessionId + "] is not allowed to be " +
+ "referenced. Ignoring and creating a Subject instance without a session to continue.", e);
+ }
+ }
+ }
+
+ if (subject == null) {
+ PrincipalCollection principals = getRememberedIdentity();
+ return getSubjectFactory().createSubject(principals, null, false, null);
+ }
+
+ return subject;
}
/**
@@ -405,4 +431,62 @@
public Subject getSubject() {
return getSubject(true);
}
+
+ protected PrincipalCollection getPrincipals(Session session) {
+ return (PrincipalCollection) session.getAttribute(SessionSubjectBinder.PRINCIPALS_SESSION_KEY);
+ }
+
+ protected boolean isAuthenticated(Session session, PrincipalCollection principals) {
+ if (principals != null) {
+ Boolean authc = (Boolean) session.getAttribute(SessionSubjectBinder.AUTHENTICATED_SESSION_KEY);
+ return authc != null && authc;
+ }
+ return false;
+ }
+
+ /**
+ * Acquires the {@link Subject Subject} that owns the {@link Session Session} with the specified {@code sessionId}.
+ *
+ * <p><b>Although simple in concept, this method provides incredibly powerful functionality:</b>
+ *
+ * <p>The ability to reference a {@code Subject} and their server-side session
+ * <em>across clients of different mediums</em> such as web applications, Java applets,
+ * standalone C# clients over XMLRPC and/or SOAP, and many others. This is a <em>huge</em>
+ * benefit in heterogeneous enterprise applications.
+ *
+ * <p>To maintain session integrity across client mediums, the {@code sessionId} <b>must</b> be transmitted
+ * to all client mediums securely (e.g. over SSL) to prevent man-in-the-middle attacks. This
+ * is nothing new - all web applications are susceptible to the same problem when transmitting
+ * {@link javax.servlet.http.Cookie Cookie}s or when using URL rewriting. As long as the
+ * {@code sessionId} is transmitted securely, session integrity can be maintained.
+ *
+ * @param sessionId the id of the session that backs the desired Subject being acquired.
+ * @return the {@code Subject} that owns the {@code Session Session} with the specified {@code sessionId}
+ * @throws org.jsecurity.session.InvalidSessionException
+ * if the session identified by <tt>sessionId</tt> has
+ * been stopped, expired, or doesn't exist.
+ * @throws org.jsecurity.authz.AuthorizationException
+ * if the executor of this method is not allowed to acquire the owning {@code Subject}. The reason
+ * for the exception is implementation-specific and could be for any number of reasons. A common
+ * reason in many systems would be if one host tried to acquire a {@code Subject} based on a
+ * {@code Session} that originated on an entirely different host (although it is not a JSecurity
+ * requirement this scenario is disallowed - its just an example that <em>may</em> throw an Exception in
+ * some systems).
+ * @see org.jsecurity.authz.HostUnauthorizedException
+ * @since 1.0
+ */
+ protected Subject getSubjectBySessionId(Serializable sessionId) throws InvalidSessionException, AuthorizationException {
+ if (!isValid(sessionId)) {
+ String msg = "Specified id [" + sessionId + "] does not correspond to a valid Session It either " +
+ "does not exist or the corresponding session has been stopped or expired.";
+ throw new InvalidSessionException(msg, sessionId);
+ }
+
+ Session existing = new DelegatingSession(this, sessionId);
+ PrincipalCollection principals = getPrincipals(existing);
+ boolean authenticated = isAuthenticated(existing, principals);
+ InetAddress host = existing.getHostAddress();
+
+ return getSubjectFactory().createSubject(principals, existing, authenticated, host);
+ }
}
\ No newline at end of file
diff --git a/core/src/org/jsecurity/mgt/DefaultSubjectFactory.java b/core/src/org/jsecurity/mgt/DefaultSubjectFactory.java
index a7f612c..7857bf6 100644
--- a/core/src/org/jsecurity/mgt/DefaultSubjectFactory.java
+++ b/core/src/org/jsecurity/mgt/DefaultSubjectFactory.java
@@ -30,12 +30,13 @@
import java.net.InetAddress;
/**
- * TODO - Class JavaDoc
+ * Default {@link SubjectFactory SubjectFactory} implementation that creates {@link DelegatingSubject DelegatingSubject}
+ * instances.
*
* @author Les Hazlewood
* @since 1.0
*/
-public class DefaultSubjectFactory implements SubjectFactory {
+public class DefaultSubjectFactory implements SubjectFactory, SecurityManagerAware {
private SecurityManager securityManager;
@@ -43,7 +44,7 @@
}
public DefaultSubjectFactory(SecurityManager securityManager) {
- setSecurityManager(securityManager);
+ this.securityManager = securityManager;
}
public SecurityManager getSecurityManager() {
@@ -54,11 +55,6 @@
this.securityManager = securityManager;
}
- public Subject createSubject(PrincipalCollection principals, Session existing,
- boolean authenticated, InetAddress inetAddress) {
- return new DelegatingSubject(principals, authenticated, inetAddress, existing, getSecurityManager());
- }
-
private void assertPrincipals(AuthenticationInfo info) {
PrincipalCollection principals = info.getPrincipals();
if (principals == null || principals.isEmpty()) {
@@ -67,14 +63,6 @@
}
}
- /**
- * Creates a <tt>Subject</tt> instance for the user represented by the given method arguments.
- *
- * @param token the <tt>AuthenticationToken</tt> submitted for the successful authentication.
- * @param info the <tt>AuthenticationInfo</tt> of a newly authenticated user.
- * @return the <tt>Subject</tt> instance that represents the user and session data for the newly
- * authenticated user.
- */
public Subject createSubject(AuthenticationToken token, AuthenticationInfo info, Subject existing) {
assertPrincipals(info);
@@ -95,4 +83,9 @@
return createSubject(info.getPrincipals(), session, true, authcSourceIP);
}
+
+ public Subject createSubject(PrincipalCollection principals, Session existing,
+ boolean authenticated, InetAddress inetAddress) {
+ return new DelegatingSubject(principals, authenticated, inetAddress, existing, getSecurityManager());
+ }
}
diff --git a/core/src/org/jsecurity/mgt/RealmSecurityManager.java b/core/src/org/jsecurity/mgt/RealmSecurityManager.java
index 8f117b5..4c58f85 100644
--- a/core/src/org/jsecurity/mgt/RealmSecurityManager.java
+++ b/core/src/org/jsecurity/mgt/RealmSecurityManager.java
@@ -18,8 +18,6 @@
*/
package org.jsecurity.mgt;
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
import org.jsecurity.cache.CacheManager;
import org.jsecurity.cache.CacheManagerAware;
import org.jsecurity.realm.Realm;
@@ -30,7 +28,7 @@
/**
* JSecurity support of a {@link SecurityManager} class hierarchy based around a collection of
- * {@link org.jsecurity.realm.Realm}s. All actual <tt>SecurityManager</tt> method implementations are left to
+ * {@link org.jsecurity.realm.Realm}s. All actual {@code SecurityManager} method implementations are left to
* subclasses.
*
* @author Les Hazlewood
@@ -39,14 +37,9 @@
public abstract class RealmSecurityManager extends CachingSecurityManager {
/**
- * Internal private log instance.
- */
- private static final Log log = LogFactory.getLog(RealmSecurityManager.class);
-
- /**
* Internal collection of <code>Realm</code>s used for all authentication and authorization operations.
*/
- protected Collection<Realm> realms;
+ private Collection<Realm> realms;
/**
* Default no-arg constructor.
@@ -74,6 +67,7 @@
* Sets the realms managed by this <tt>SecurityManager</tt> instance.
*
* @param realms the realms managed by this <tt>SecurityManager</tt> instance.
+ * @throws IllegalArgumentException if the realms collection is null or empty.
*/
public void setRealms(Collection<Realm> realms) {
if (realms == null) {
@@ -110,7 +104,7 @@
protected void applyCacheManagerToRealms() {
CacheManager cacheManager = getCacheManager();
Collection<Realm> realms = getRealms();
- if (cacheManager != null && realms != null && !realms.isEmpty()) {
+ if (realms != null && !realms.isEmpty()) {
for (Realm realm : realms) {
if (realm instanceof CacheManagerAware) {
((CacheManagerAware) realm).setCacheManager(cacheManager);
diff --git a/core/src/org/jsecurity/subject/RememberMeManager.java b/core/src/org/jsecurity/mgt/RememberMeManager.java
similarity index 94%
rename from core/src/org/jsecurity/subject/RememberMeManager.java
rename to core/src/org/jsecurity/mgt/RememberMeManager.java
index eebba0b..e381a8b 100644
--- a/core/src/org/jsecurity/subject/RememberMeManager.java
+++ b/core/src/org/jsecurity/mgt/RememberMeManager.java
@@ -16,11 +16,12 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.jsecurity.subject;
+package org.jsecurity.mgt;
import org.jsecurity.authc.AuthenticationException;
import org.jsecurity.authc.AuthenticationInfo;
import org.jsecurity.authc.AuthenticationToken;
+import org.jsecurity.subject.PrincipalCollection;
/**
* A RememberMeManager is responsible for remembering a Subject's identity across that Subject's sessions with
diff --git a/core/src/org/jsecurity/mgt/SecurityManager.java b/core/src/org/jsecurity/mgt/SecurityManager.java
index 47dfff2..09510ab 100644
--- a/core/src/org/jsecurity/mgt/SecurityManager.java
+++ b/core/src/org/jsecurity/mgt/SecurityManager.java
@@ -21,17 +21,11 @@
import org.jsecurity.authc.AuthenticationException;
import org.jsecurity.authc.AuthenticationToken;
import org.jsecurity.authc.Authenticator;
-import org.jsecurity.authz.AuthorizationException;
import org.jsecurity.authz.Authorizer;
-import org.jsecurity.authz.HostUnauthorizedException;
-import org.jsecurity.session.InvalidSessionException;
-import org.jsecurity.session.Session;
import org.jsecurity.session.mgt.SessionManager;
import org.jsecurity.subject.PrincipalCollection;
import org.jsecurity.subject.Subject;
-import java.io.Serializable;
-
/**
* A <tt>SecurityManager</tt> executes all security operations for <em>all</em> Subjects (aka users) across a
* single application.
@@ -107,37 +101,4 @@
* @since 0.9
*/
Subject getSubject();
-
- /**
- * Acquires a handle to the session identified by the specified <tt>sessionId</tt>.
- *
- * <p><b>Although simple, this method finally enables behavior absent in Java for years:</b>
- *
- * <p>the
- * ability to participate in a server-side session across clients of different mediums,
- * such as web appliations, Java applets, standalone C# clients over XMLRPC and/or SOAP, and
- * many others. This is a <em>huge</em> benefit in heterogeneous enterprise applications.
- *
- * <p>To maintain session integrity across client mediums, the sessionId must be transmitted
- * to all client mediums securely (e.g. over SSL) to prevent man-in-the-middle attacks. This
- * is nothing new - all web applications are susceptible to the same problem when transmitting
- * {@link javax.servlet.http.Cookie Cookie}s or when using URL rewriting. As long as the
- * <tt>sessionId</tt> is transmitted securely, session integrity can be maintained.
- *
- * @param sessionId the id of the session to acquire.
- * @return a handle to the session identified by <tt>sessionId</tt>
- * @throws org.jsecurity.session.InvalidSessionException
- * if the session identified by <tt>sessionId</tt> has
- * been stopped, expired, or doesn't exist.
- * @throws org.jsecurity.authz.AuthorizationException
- * if the executor of this method is not allowed to acquire
- * (i.e. join) the session identified by <tt>sessionId</tt>. The reason for the exception
- * is implementation specific and could be for any number of reasons. A common reason in many
- * systems would be if one host tried to acquire/join a session that originated on an entirely
- * different host (although it is not a JSecurity requirement this scenario is disallowed -
- * its just an example that <em>may</em> throw an Exception in many systems).
- * @see HostUnauthorizedException
- * @since 1.0
- */
- Session getSession(Serializable sessionId) throws InvalidSessionException, AuthorizationException;
}
\ No newline at end of file
diff --git a/core/src/org/jsecurity/mgt/SecurityManagerAware.java b/core/src/org/jsecurity/mgt/SecurityManagerAware.java
new file mode 100644
index 0000000..c3739a8
--- /dev/null
+++ b/core/src/org/jsecurity/mgt/SecurityManagerAware.java
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jsecurity.mgt;
+
+/**
+ * Interface providing a callback method that allows an implementation of this interface to receive a reference to
+ * the {@link SecurityManager SecurityManager} if they require one. This is mostly used by core implementation classes
+ * for framework code and is rarely necessary for software developers enabling JSecurity in their applications.
+ *
+ * @author Les Hazlewood
+ * @since 1.0
+ */
+public interface SecurityManagerAware {
+
+ /**
+ * Callback method that allows a component to receive the {@link SecurityManager SecurityManager} instance if it
+ * requires one.
+ *
+ * @param securityManager the application's <code>SecurityManager</code>
+ */
+ void setSecurityManager(SecurityManager securityManager);
+}
diff --git a/core/src/org/jsecurity/mgt/SessionSubjectBinder.java b/core/src/org/jsecurity/mgt/SessionSubjectBinder.java
index 0597541..7b96e20 100644
--- a/core/src/org/jsecurity/mgt/SessionSubjectBinder.java
+++ b/core/src/org/jsecurity/mgt/SessionSubjectBinder.java
@@ -23,20 +23,54 @@
import org.jsecurity.subject.Subject;
/**
- * Binds the Subject to the accessible Session in addition to the ThreadContext.
+ * Binds the Subject's state to the accessible {@link Session Session} in addition to the
+ * {@link org.jsecurity.util.ThreadContext ThreadContext}
+ * <p/>
+ * The very notion of this class's existence might sound backwards: typically a {@link Session Session} is something
+ * that is created <em>after</em> a {@link Subject Subject} is acquired - for example by calling
+ * <code>Subject.{@link Subject#getSession getSession()}</code>. This might imply that a <code>Session</code> is also
+ * therefore constrained to the 'owning' <code>Subject</code>'s lifecycle.
+ * <p/>
+ * However, in many environments, the <code>Subject</code> instance in memory is transient and exists only for the
+ * duration of thread execution or during an incoming request in web environments. The <code>Session</code> however
+ * must be persistent over time since that is the very nature of the concept of a <code>Session</code>. So, this
+ * particular <code>SubjectBinder</code> implementation will save the relevant <code>Subject</code> state as
+ * <code>Session</code> attributes to enable the <code>Subject</code> to be constructed on subsequent requests or
+ * method invocations.
+ * <p/>
+ * This paradigm requires some framework code elsewhere to re-create the <code>Subject</code>:
+ * <ol>
+ * <li>A session ID would be acquired based on an incoming request or remote method invocation</li>
+ * <li>The <code>Session</code> would be retrieved from the application's {@link SecurityManager SecurityManager}
+ * (using the {@link org.jsecurity.session.mgt.SessionManager SessionManager} parent methods)</li>
+ * <li>A <code>Subject</code> instance would be created based on the attributes found in that session</code>
+ * <li>The constructed <code>Subject</code> would be 'bound' to the application for use during the request or method
+ * invocation (say, bound to the processing thread)</li>
+ * <li>The subject would then be accessible to the application for the duration of the thread</li>
+ * <li>Any state changed to the subject at the end of the thread execution would be saved to back to the
+ * <code>Session</code></li>
+ * <li>The <code>Subject</code> instance wold be 'unbound' from the application/thread and garbage collected at the
+ * end of request/thread execution, and a new one is created on the next request/method invocation as per step #1.</li>
+ * </ol>
+ * <p/>
+ * Indeed this is exactly how JSecurity's default behavior works in enterprise server and web-based environments. It is
+ * enabled in the <code>JSecurityFilter</code> for web-based environments as well as remote-method-invocation-based
+ * components for non-web environments.
*
* @author Les Hazlewood
* @since 1.0
*/
public class SessionSubjectBinder extends ThreadContextSubjectBinder {
+ //TODO - finish JavaDoc
+
/**
- * The key that is used to store subject principals in the session.
+ * The session key that is used to store subject principals.
*/
public static final String PRINCIPALS_SESSION_KEY = SessionSubjectBinder.class.getName() + "_PRINCIPALS_SESSION_KEY";
/**
- * The key that is used to store whether or not the user is authenticated in the session.
+ * The session key that is used to store whether or not the user is authenticated.
*/
public static final String AUTHENTICATED_SESSION_KEY = SessionSubjectBinder.class.getName() + "_AUTHENTICATED_SESSION_KEY";
diff --git a/core/src/org/jsecurity/mgt/SessionsSecurityManager.java b/core/src/org/jsecurity/mgt/SessionsSecurityManager.java
index 023359b..2453867 100644
--- a/core/src/org/jsecurity/mgt/SessionsSecurityManager.java
+++ b/core/src/org/jsecurity/mgt/SessionsSecurityManager.java
@@ -18,16 +18,13 @@
*/
package org.jsecurity.mgt;
-import org.jsecurity.authz.AuthorizationException;
import org.jsecurity.authz.HostUnauthorizedException;
-import org.jsecurity.cache.CacheManager;
import org.jsecurity.cache.CacheManagerAware;
import org.jsecurity.session.InvalidSessionException;
import org.jsecurity.session.Session;
import org.jsecurity.session.SessionListener;
import org.jsecurity.session.SessionListenerRegistrar;
import org.jsecurity.session.mgt.DefaultSessionManager;
-import org.jsecurity.session.mgt.DelegatingSession;
import org.jsecurity.session.mgt.SessionManager;
import org.jsecurity.util.LifecycleUtils;
@@ -41,12 +38,12 @@
* {@link org.jsecurity.session.Session session} operations to a wrapped {@link SessionManager SessionManager}
* instance. That is, this class implements the methods in the
* {@link SessionManager SessionManager} interface, but in reality, those methods are merely passthrough calls to
- * the underlying 'real' <tt>SessionManager</tt> instance.
- *
- * <p>The remaining <tt>SecurityManager</tt> methods not implemented by this class or its parents are left to be
+ * the underlying 'real' {@code SessionManager} instance.
+ * <p/>
+ * The remaining {@code SecurityManager} methods not implemented by this class or its parents are left to be
* implemented by subclasses.
- *
- * <p>In keeping with the other classes in this hierarchy and JSecurity's desire to minimize configuration whenever
+ * <p/>
+ * In keeping with the other classes in this hierarchy and JSecurity's desire to minimize configuration whenever
* possible, suitable default instances for all dependencies will be created upon instantiation.
*
* @author Les Hazlewood
@@ -58,14 +55,19 @@
* The internal delegate <code>SessionManager</code> used by this security manager that manages all the
* application's {@link Session Session}s.
*/
- protected SessionManager sessionManager;
+ private SessionManager sessionManager;
/**
* Default no-arg constructor, internally creates a suitable default {@link SessionManager SessionManager} delegate
- * instance via the {@link #createSessionManager() createSessionManager()} method.
+ * instance.
*/
public SessionsSecurityManager() {
- setSessionManager(createSessionManager());
+ applySessionManager(new DefaultSessionManager());
+ }
+
+ protected final void applySessionManager(SessionManager sm) {
+ setSessionManager(sm);
+ applyCacheManagerToSessionManager();
}
/**
@@ -96,37 +98,6 @@
}
/**
- * Constructs a new <code>SessionManager</code> instance to be used as the internal delegate for this security
- * manager. After creation via the {@link #newSessionManagerInstance() newSessionManagerInstance()} call, the
- * internal {@link #getCacheManager CacheManager} is set on it if the session manager instance implements the
- * {@link CacheManagerAware CacheManagerAware} interface to allow it to utilize the cache manager for its own
- * internal caching needs.
- *
- * @return a new initialized {@link SessionManager SessionManager} to use as this security manager's internal
- * delegate.
- */
- protected SessionManager createSessionManager() {
- SessionManager sm = newSessionManagerInstance();
- CacheManager cm = getCacheManager();
- if (cm != null) {
- if (sm instanceof CacheManagerAware) {
- ((CacheManagerAware) sm).setCacheManager(cm);
- }
- }
- return sm;
- }
-
- /**
- * Merely instantiates (but does not initalize) the default <code>SessionManager</code> implementation. This method
- * merely returns <code>new {@link DefaultSessionManager DefaultSessionManager}()</code>.
- *
- * @return a new, uninitialized {@link SessionManager SessionManager} instance.
- */
- protected SessionManager newSessionManagerInstance() {
- return new DefaultSessionManager();
- }
-
- /**
* Calls {@link AuthorizingSecurityManager#afterCacheManagerSet() super.afterCacheManagerSet()} and then immediately calls
* {@link #applyCacheManagerToSessionManager() applyCacheManagerToSessionManager()} to ensure the
* <code>CacheManager</code> is applied to the SessionManager as necessary.
@@ -144,9 +115,8 @@
* instance implements the {@link CacheManagerAware CacheManagerAware} interface.
*/
protected void applyCacheManagerToSessionManager() {
- SessionManager sm = getSessionManager();
- if (sm instanceof CacheManagerAware) {
- ((CacheManagerAware) sm).setCacheManager(cacheManager);
+ if (this.sessionManager instanceof CacheManagerAware) {
+ ((CacheManagerAware) this.sessionManager).setCacheManager(getCacheManager());
}
}
@@ -199,8 +169,7 @@
*/
public void add(SessionListener listener) {
assertSessionListenerSupport();
- SessionManager sm = getSessionManager();
- ((SessionListenerRegistrar) sm).add(listener);
+ ((SessionListenerRegistrar) this.sessionManager).add(listener);
}
/**
@@ -212,61 +181,60 @@
* instance, <code>false</code> otherwise.
*/
public boolean remove(SessionListener listener) {
- SessionManager sm = getSessionManager();
- return (sm instanceof SessionListenerRegistrar) &&
- ((SessionListenerRegistrar) sm).remove(listener);
+ return (this.sessionManager instanceof SessionListenerRegistrar) &&
+ ((SessionListenerRegistrar) this.sessionManager).remove(listener);
}
public Serializable start(InetAddress originatingHost) throws HostUnauthorizedException, IllegalArgumentException {
- return getSessionManager().start(originatingHost);
+ return this.sessionManager.start(originatingHost);
}
public Date getStartTimestamp(Serializable sessionId) {
- return getSessionManager().getStartTimestamp(sessionId);
+ return this.sessionManager.getStartTimestamp(sessionId);
}
public Date getLastAccessTime(Serializable sessionId) {
- return getSessionManager().getLastAccessTime(sessionId);
+ return this.sessionManager.getLastAccessTime(sessionId);
}
public boolean isValid(Serializable sessionId) {
- return getSessionManager().isValid(sessionId);
+ return this.sessionManager.isValid(sessionId);
}
public long getTimeout(Serializable sessionId) throws InvalidSessionException {
- return getSessionManager().getTimeout(sessionId);
+ return this.sessionManager.getTimeout(sessionId);
}
public void setTimeout(Serializable sessionId, long maxIdleTimeInMillis) throws InvalidSessionException {
- getSessionManager().setTimeout(sessionId, maxIdleTimeInMillis);
+ this.sessionManager.setTimeout(sessionId, maxIdleTimeInMillis);
}
public void touch(Serializable sessionId) throws InvalidSessionException {
- getSessionManager().touch(sessionId);
+ this.sessionManager.touch(sessionId);
}
public InetAddress getHostAddress(Serializable sessionId) {
- return getSessionManager().getHostAddress(sessionId);
+ return this.sessionManager.getHostAddress(sessionId);
}
public void stop(Serializable sessionId) throws InvalidSessionException {
- getSessionManager().stop(sessionId);
+ this.sessionManager.stop(sessionId);
}
public Collection<Object> getAttributeKeys(Serializable sessionId) {
- return getSessionManager().getAttributeKeys(sessionId);
+ return this.sessionManager.getAttributeKeys(sessionId);
}
public Object getAttribute(Serializable sessionId, Object key) throws InvalidSessionException {
- return getSessionManager().getAttribute(sessionId, key);
+ return this.sessionManager.getAttribute(sessionId, key);
}
public void setAttribute(Serializable sessionId, Object key, Object value) throws InvalidSessionException {
- getSessionManager().setAttribute(sessionId, key, value);
+ this.sessionManager.setAttribute(sessionId, key, value);
}
public Object removeAttribute(Serializable sessionId, Object key) throws InvalidSessionException {
- return getSessionManager().removeAttribute(sessionId, key);
+ return this.sessionManager.removeAttribute(sessionId, key);
}
/**
@@ -277,10 +245,10 @@
/**
* Cleans up ('destroys') the internal delegate <code>SessionManager</code> by calling
- * {@link LifecycleUtils#destroy LifecycleUtils.destroy(getSessionManager())}.
+ * {@link LifecycleUtils#destroy LifecycleUtils.destroy(this.sessionManager)}.
*/
protected void destroySessionManager() {
- LifecycleUtils.destroy(getSessionManager());
+ LifecycleUtils.destroy(this.sessionManager);
}
/**
@@ -293,14 +261,4 @@
destroySessionManager();
}
- public Session getSession(Serializable sessionId) throws InvalidSessionException, AuthorizationException {
- SessionManager sm = getSessionManager();
- if (!sm.isValid(sessionId)) {
- String msg = "Specified id [" + sessionId + "] does not correspond to a valid Session It either " +
- "does not exist or the corresponding session has been stopped or expired.";
- throw new InvalidSessionException(msg, sessionId);
- }
- return new DelegatingSession(sm, sessionId);
- }
-
}
diff --git a/core/src/org/jsecurity/mgt/SubjectFactory.java b/core/src/org/jsecurity/mgt/SubjectFactory.java
index 51c8712..1fe22ba 100644
--- a/core/src/org/jsecurity/mgt/SubjectFactory.java
+++ b/core/src/org/jsecurity/mgt/SubjectFactory.java
@@ -27,16 +27,44 @@
import java.net.InetAddress;
/**
- * TODO - Class JavaDoc
+ * A {@code SubjectFactory} is responsible for returning {@link Subject Subject} instances as needed.
*
* @author Les Hazlewood
* @since 1.0
*/
public interface SubjectFactory {
+ /**
+ * Returns a {@code Subject} instance reflecting the state of a <em>successful</em> authentication attempt.
+ * <p/>
+ * The '{@code existing}' {@code Subject} method argument is the {@code Subject} that executed the
+ * authentication attempt but still reflects an unauthenticated state. The instance returned from this method
+ * is the {code Subject} instance to use for future application use and reflects an authenticated state.
+ *
+ * @param token the {@code AuthenticationToken} submitted during the successful authentication attempt.
+ * @param info the {@code AuthenticationInfo} generated due to the successful authentication attempt.
+ * @param existing the {@code Subject} that executed the attempt, still in an 'unauthenticated' state.
+ * @return the {@code Subject} for the application to use going forward, but in an 'authenticated' state.
+ */
Subject createSubject(AuthenticationToken token, AuthenticationInfo info, Subject existing);
- Subject createSubject(PrincipalCollection principals, Session existing,
- boolean authenticated, InetAddress originatingHost);
+ /**
+ * Returns a {@code Subject} instance reflecting the specified Subject identity (aka 'principals'), any
+ * existing {@code Session} that might be in place for that identity, whether or not the Subject is to be
+ * considered already authenticated, and the originating host from where the Subject instance to be created is
+ * being acquired.
+ *
+ * @param principals the identifying attributes of the Subject instance to be created, or
+ * {@code null} if the Subject's identity is unknown because they haven't logged in yet and are not 'remembered'
+ * from {@code RememberMe} services.
+ * @param existing any {@link Session Session} that might be in place for the specified {@link Subject}, or
+ * {@code null} if there is no session yet created for the specified {@code Subject}. If non-{@code null},
+ * it should be retained and used by the {@code Subject} instance returned from this method call.
+ * @param authenticated whether or not the {@code Subject} instance returned should be considered already
+ * authenticated.
+ * @param originatingHost the host location indicating where the {@code Subject} is located.
+ * @return a {@code Subject} instance representing the aggregate state of the specified method arguments.
+ */
+ Subject createSubject(PrincipalCollection principals, Session existing, boolean authenticated, InetAddress originatingHost);
}
diff --git a/core/src/org/jsecurity/mgt/ThreadContextSubjectBinder.java b/core/src/org/jsecurity/mgt/ThreadContextSubjectBinder.java
index 1fd310a..8af9b00 100644
--- a/core/src/org/jsecurity/mgt/ThreadContextSubjectBinder.java
+++ b/core/src/org/jsecurity/mgt/ThreadContextSubjectBinder.java
@@ -24,19 +24,32 @@
import org.jsecurity.util.ThreadContext;
/**
- * TODO - Class JavaDoc
+ * Associates a {@link Subject Subject} instance to the currently executing thread via the {
*
* @author Les Hazlewood
+ * @link ThreadContext ThreadContext} to ensure that the <code>Subject</code> is accessible to any caller during
+ * thread execution.
+ * @see org.jsecurity.SecurityUtils#getSubject SecurityUtils.getSubject()
* @since 1.0
*/
public class ThreadContextSubjectBinder implements SubjectBinder {
private static final Log log = LogFactory.getLog(ThreadContextSubjectBinder.class);
+ /**
+ * This implementation returns the {@link Subject Subject} from the {@link ThreadContext ThreadContext}.
+ *
+ * @return the {@link Subject Subject} in the {@link ThreadContext ThreadContext}
+ */
public Subject getSubject() {
return ThreadContext.getSubject();
}
+ /**
+ * Associates the specified subject to the currently executing thread via the {@link ThreadContext ThreadContext}.
+ *
+ * @param subject the subject to accosiate to the currently executing thread.
+ */
public void bind(Subject subject) {
if (log.isTraceEnabled()) {
log.trace("Binding Subject [" + subject + "] to a thread local...");
@@ -44,6 +57,12 @@
ThreadContext.bind(subject);
}
+ /**
+ * Removes the specified Subject instance from the currently executing thread by removing it from the
+ * {@link ThreadContext ThreadContext}
+ *
+ * @param subject the <code>Subject</code> instance to unbind from the currently executing thread.
+ */
public void unbind(Subject subject) {
ThreadContext.unbindSubject();
}
diff --git a/core/src/org/jsecurity/session/Session.java b/core/src/org/jsecurity/session/Session.java
index 721899d..11171ba 100644
--- a/core/src/org/jsecurity/session/Session.java
+++ b/core/src/org/jsecurity/session/Session.java
@@ -33,9 +33,6 @@
* {@link javax.servlet.http.HttpSession HttpSession} or Stateful Session EJB's, which many times
* unnecessarily coupled applications to web or ejb technologies.
*
- * <p>See the {@link org.jsecurity.mgt.SecurityManager#getSession(java.io.Serializable) SecurityManager.getSession(Serializable)}
- * JavaDoc for more on the benefits of a POJO-based <tt>Session</tt> framework.
- *
* @author Les Hazlewood
* @since 0.1
*/
diff --git a/core/src/org/jsecurity/session/mgt/DefaultSessionManager.java b/core/src/org/jsecurity/session/mgt/DefaultSessionManager.java
index 42ac514..a4a765a 100644
--- a/core/src/org/jsecurity/session/mgt/DefaultSessionManager.java
+++ b/core/src/org/jsecurity/session/mgt/DefaultSessionManager.java
@@ -45,9 +45,10 @@
private static final Log log = LogFactory.getLog(DefaultSessionManager.class);
- protected SessionDAO sessionDAO = new MemorySessionDAO();
+ protected SessionDAO sessionDAO;
public DefaultSessionManager() {
+ this.sessionDAO = new MemorySessionDAO();
}
public void setSessionDAO(SessionDAO sessionDAO) {
@@ -59,7 +60,9 @@
}
public void setCacheManager(CacheManager cacheManager) {
- ((CacheManagerAware) getSessionDAO()).setCacheManager(cacheManager);
+ if (this.sessionDAO instanceof CacheManagerAware) {
+ ((CacheManagerAware) this.sessionDAO).setCacheManager(cacheManager);
+ }
}
protected Session doCreateSession(InetAddress originatingHost) {
diff --git a/core/src/org/jsecurity/util/ThreadContext.java b/core/src/org/jsecurity/util/ThreadContext.java
index 12b8aa6..c0f46ff 100644
--- a/core/src/org/jsecurity/util/ThreadContext.java
+++ b/core/src/org/jsecurity/util/ThreadContext.java
@@ -23,6 +23,7 @@
import org.jsecurity.mgt.SecurityManager;
import org.jsecurity.subject.Subject;
+import java.io.Serializable;
import java.net.InetAddress;
import java.util.HashMap;
import java.util.Map;
@@ -46,11 +47,14 @@
@SuppressWarnings(value = {"unchecked", "unsafe"})
public abstract class ThreadContext {
- /** Private internal log instance. */
+ /**
+ * Private internal log instance.
+ */
private static final Log log = LogFactory.getLog(ThreadContext.class);
public static final String SECURITY_MANAGER_KEY = ThreadContext.class.getName() + "_SECURITY_MANAGER_KEY";
public static final String SUBJECT_KEY = ThreadContext.class.getName() + "_SUBJECT_KEY";
+ public static final String SESSION_ID_KEY = ThreadContext.class.getName() + "_SESSION_ID_KEY";
public static final String INET_ADDRESS_KEY = ThreadContext.class.getName() + "_INET_ADDRESS_KEY";
protected static ThreadLocal<Map<Object, Object>> resources =
@@ -366,5 +370,22 @@
public static InetAddress unbindInetAddress() {
return (InetAddress) remove(INET_ADDRESS_KEY);
}
+
+ //TODO - complete JavaDoc
+
+ public static Serializable getSessionId() {
+ return (Serializable) get(SESSION_ID_KEY);
+ }
+
+ public static void bindSessionId(Serializable sessionId) {
+ if (sessionId != null) {
+ put(SESSION_ID_KEY, sessionId);
+ }
+ }
+
+ public Serializable unbindSessionId() {
+ return (Serializable) remove(SESSION_ID_KEY);
+
+ }
}
diff --git a/support/spring/src/org/jsecurity/spring/remoting/SecureRemoteInvocationExecutor.java b/support/spring/src/org/jsecurity/spring/remoting/SecureRemoteInvocationExecutor.java
index 0fc4ecf..5afeede 100644
--- a/support/spring/src/org/jsecurity/spring/remoting/SecureRemoteInvocationExecutor.java
+++ b/support/spring/src/org/jsecurity/spring/remoting/SecureRemoteInvocationExecutor.java
@@ -21,24 +21,18 @@
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jsecurity.mgt.SecurityManager;
-import org.jsecurity.session.Session;
-import org.jsecurity.subject.DelegatingSubject;
-import org.jsecurity.subject.PrincipalCollection;
-import org.jsecurity.subject.Subject;
import org.jsecurity.util.ThreadContext;
-import org.jsecurity.web.DefaultWebSecurityManager;
import org.springframework.remoting.support.DefaultRemoteInvocationExecutor;
import org.springframework.remoting.support.RemoteInvocation;
import java.io.Serializable;
import java.lang.reflect.InvocationTargetException;
-import java.net.InetAddress;
-import java.net.UnknownHostException;
/**
* An implementation of the Spring {@link org.springframework.remoting.support.RemoteInvocationExecutor}
- * that binds the correct {@link Session} and {@link org.jsecurity.subject.Subject} to the
- * remote invocation thread during a remote execution.
+ * that binds a {@code sessionId} to the incoming thread to make it available to the {@code SecurityManager}
+ * implementation during the thread execution. The {@code SecurityManager} implementation can use this sessionId
+ * to reconstitute the {@code Subject} instance based on persistent state in the corresponding {@code Session}.
*
* @author Jeremy Haile
* @author Les Hazlewood
@@ -78,59 +72,23 @@
/*--------------------------------------------
| M E T H O D S |
============================================*/
-
- protected InetAddress getInetAddress(RemoteInvocation invocation, Object targetObject) {
- try {
- return InetAddress.getLocalHost();
- } catch (UnknownHostException e) {
- return null;
- }
- }
-
- protected PrincipalCollection getPrincipals(RemoteInvocation invocation, Object targetObject, Session session) {
- return (PrincipalCollection) session.getAttribute(DefaultWebSecurityManager.PRINCIPALS_SESSION_KEY);
- }
-
- protected boolean isAuthenticated(RemoteInvocation invocation, Object targetObject, Session session, PrincipalCollection principals) {
- if (principals != null) {
- Boolean authc = (Boolean) session.getAttribute(DefaultWebSecurityManager.AUTHENTICATED_SESSION_KEY);
- return authc != null && authc;
- }
- return false;
- }
-
@SuppressWarnings({"unchecked"})
public Object invoke(RemoteInvocation invocation, Object targetObject) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
-
try {
- PrincipalCollection principals = null;
- boolean authenticated = false;
- InetAddress inetAddress = getInetAddress(invocation, targetObject);
- Session session = null;
-
Serializable sessionId = invocation.getAttribute(SecureRemoteInvocationFactory.SESSION_ID_KEY);
-
if (sessionId != null) {
-
- session = securityManager.getSession(sessionId);
- principals = getPrincipals(invocation, targetObject, session);
- authenticated = isAuthenticated(invocation, targetObject, session, principals);
+ ThreadContext.bindSessionId(sessionId);
} else {
- if (log.isWarnEnabled()) {
- log.warn("RemoteInvocation object did not contain a JSecurity Session id under " +
- "attribute name [" + SecureRemoteInvocationFactory.SESSION_ID_KEY + "]. A Session will not " +
- "be available to the method. Ensure that clients are using a " +
- "SecureRemoteInvocationFactory to prevent this problem.");
+ if (log.isTraceEnabled()) {
+ log.trace("RemoteInvocation did not contain a JSecurity Session id attribute under " +
+ "key [" + SecureRemoteInvocationFactory.SESSION_ID_KEY + "]. A Subject based " +
+ "on an existing Session will not be available during the method invocatin.");
}
}
-
- Subject subject = new DelegatingSubject(principals, authenticated, inetAddress, session, securityManager);
-
ThreadContext.bind(securityManager);
- ThreadContext.bind(subject);
+ ThreadContext.bind(securityManager.getSubject());
return super.invoke(invocation, targetObject);
-
} catch (NoSuchMethodException nsme) {
throw nsme;
} catch (IllegalAccessException iae) {
diff --git a/web/src/org/jsecurity/web/DefaultWebSecurityManager.java b/web/src/org/jsecurity/web/DefaultWebSecurityManager.java
index f8731b7..a4b98d0 100644
--- a/web/src/org/jsecurity/web/DefaultWebSecurityManager.java
+++ b/web/src/org/jsecurity/web/DefaultWebSecurityManager.java
@@ -59,15 +59,19 @@
private String sessionMode = HTTP_SESSION_MODE; //default
public DefaultWebSecurityManager() {
+ WebSessionManager sm = new DefaultWebSessionManager();
+ applySessionManager(sm);
setRememberMeManager(new WebRememberMeManager());
- setSubjectFactory(new WebSubjectFactory(this, (WebSessionManager) getSessionManager()));
+ setSubjectFactory(new WebSubjectFactory(this, sm));
}
public DefaultWebSecurityManager(Realm singleRealm) {
+ this();
setRealm(singleRealm);
}
public DefaultWebSecurityManager(Collection<Realm> realms) {
+ this();
setRealms(realms);
}
@@ -126,8 +130,12 @@
}
public void setSessionMode(String sessionMode) {
- if (sessionMode == null ||
- (!sessionMode.equals(HTTP_SESSION_MODE) && !sessionMode.equals(JSECURITY_SESSION_MODE))) {
+ String mode = sessionMode;
+ if (mode == null) {
+ throw new IllegalArgumentException("sessionMode argument cannot be null.");
+ }
+ mode = sessionMode.toLowerCase();
+ if (!HTTP_SESSION_MODE.equals(mode) && !JSECURITY_SESSION_MODE.equals(mode)) {
String msg = "Invalid sessionMode [" + sessionMode + "]. Allowed values are " +
"public static final String constants in the " + getClass().getName() + " class: '"
+ HTTP_SESSION_MODE + "' or '" + JSECURITY_SESSION_MODE + "', with '" +
@@ -138,9 +146,9 @@
this.sessionMode = sessionMode;
if (recreate) {
LifecycleUtils.destroy(getSessionManager());
- SessionManager sm = createSessionManager();
- setSessionManager(sm);
- setSubjectFactory(new WebSubjectFactory(this, (WebSessionManager) getSessionManager()));
+ SessionManager sessionManager = newSessionManagerInstance();
+ applySessionManager(sessionManager);
+ setSubjectFactory(new WebSubjectFactory(this, (WebSessionManager) sessionManager));
}
}
diff --git a/web/src/org/jsecurity/web/WebRememberMeManager.java b/web/src/org/jsecurity/web/WebRememberMeManager.java
index f2739ab..ef79b4d 100644
--- a/web/src/org/jsecurity/web/WebRememberMeManager.java
+++ b/web/src/org/jsecurity/web/WebRememberMeManager.java
@@ -21,7 +21,7 @@
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jsecurity.codec.Base64;
-import org.jsecurity.subject.AbstractRememberMeManager;
+import org.jsecurity.mgt.AbstractRememberMeManager;
import org.jsecurity.web.attr.CookieAttribute;
import org.jsecurity.web.attr.WebAttribute;
@@ -57,7 +57,7 @@
//TODO - complete JavaDoc
- private static transient final Log log = LogFactory.getLog( WebRememberMeManager.class );
+ private static transient final Log log = LogFactory.getLog(WebRememberMeManager.class);
/**
* The default name of the underlying rememberMe cookie which is <code>rememberMe</code>.
@@ -207,12 +207,12 @@
ServletResponse response = WebUtils.getRequiredServletResponse();
String base64 = getIdentityAttribute().retrieveValue(request, response);
if (base64 != null) {
- if ( log.isTraceEnabled() ) {
- log.trace( "Acquired Base64 encoded identity [" + base64 + "]" );
+ if (log.isTraceEnabled()) {
+ log.trace("Acquired Base64 encoded identity [" + base64 + "]");
}
byte[] decoded = Base64.decode(base64);
- if ( log.isTraceEnabled() ) {
- log.trace( "Base64 decoded byte array length: " + (decoded != null ? decoded.length : 0) + " bytes." );
+ if (log.isTraceEnabled()) {
+ log.trace("Base64 decoded byte array length: " + (decoded != null ? decoded.length : 0) + " bytes.");
}
return decoded;
} else {