1
0
mirror of synced 2026-07-16 08:05:13 +00:00

Compare commits

..

3 Commits

Author SHA1 Message Date
Luke Taylor 99e3a5d715 Added log4j dependency 2008-04-01 19:14:34 +00:00
Luke Taylor ea414140e2 Added excludeDependencies to pom.xml to prevent core classes from being bundled into core-tiger jar 2008-04-01 19:02:53 +00:00
Luke Taylor fd04adf2f3 [maven-release-plugin] copy for tag release_2_0_0_RC1 2008-04-01 15:02:44 +00:00
320 changed files with 6249 additions and 13308 deletions
+2 -28
View File
@@ -3,14 +3,13 @@
<parent>
<artifactId>spring-security-parent</artifactId>
<groupId>org.springframework.security</groupId>
<version>2.0.2</version>
<version>2.0.0-RC1</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-acl</artifactId>
<name>Spring Security - ACL module</name>
<version>2.0.2</version>
<packaging>bundle</packaging>
<version>2.0.0-RC1</version>
<dependencies>
<dependency>
@@ -45,29 +44,4 @@
<scope>test</scope>
</dependency>
</dependencies>
<properties>
<spring.osgi.export>
org.springframework.security.*;version=${pom.version.osgi}
</spring.osgi.export>
<spring.osgi.import>
net.sf.ehcache.*;version="[1.4.1, 2.0.0)";resolution:=optional,
org.springframework.security.*;version="[${pom.version.osgi},${pom.version.osgi}]",
org.springframework.context.*;version="${spring.version.osgi}",
org.springframework.dao.*;version="${spring.version.osgi}";resolution:=optional,
org.springframework.jdbc.*;version="${spring.version.osgi}";resolution:=optional,
org.springframework.transaction.support.*;version="${spring.version.osgi}";resolution:=optional,
org.springframework.util.*;version="${spring.version.osgi}",
org.apache.commons.logging.*;version="[1.1.1, 2.0.0)",
javax.sql.*
</spring.osgi.import>
<spring.osgi.private.pkg>
!org.springframework.security.*
</spring.osgi.private.pkg>
<spring.osgi.symbolic.name>org.springframework.security.acls</spring.osgi.symbolic.name>
</properties>
</project>
@@ -31,7 +31,7 @@ import java.io.Serializable;
* @version $Id$
*
*/
public interface AccessControlEntry extends Serializable {
public interface AccessControlEntry {
//~ Methods ========================================================================================================
Acl getAcl();
@@ -24,7 +24,7 @@ import java.io.Serializable;
* Represents an access control list (ACL) for a domain object.
*
* <p>
* An <tt>Acl</tt> represents all ACL entries for a given domain object. In
* An <code>Acl</code> represents all ACL entries for a given domain object. In
* order to avoid needing references to the domain object itself, this
* interface handles indirection between a domain object and an ACL object
* identity via the {@link
@@ -32,139 +32,124 @@ import java.io.Serializable;
* </p>
*
* <p>
* Implementing classes may elect to return instances that represent
* {@link org.springframework.security.acls.Permission} information for either
* some OR all {@link org.springframework.security.acls.sid.Sid}
* instances. Therefore, an instance may NOT necessarily contain ALL <tt>Sid</tt>s
* for a given domain object.
* An implementation represents the {@link org.springframework.security.acls.Permission}
* list applicable for some or all {@link org.springframework.security.acls.sid.Sid}
* instances.
* </p>
*
* @author Ben Alex
* @version $Id$
*/
public interface Acl extends Serializable {
//~ Methods ========================================================================================================
/**
* Returns all of the entries represented by the present <tt>Acl</tt>. Entries associated with
* the <tt>Acl</tt> parents are not returned.
*
* <p>This method is typically used for administrative purposes.</p>
*
* <p>The order that entries appear in the array is important for methods declared in the
* {@link MutableAcl} interface. Furthermore, some implementations MAY use ordering as
* part of advanced permission checking.</p>
*
* <p>Do <em>NOT</em> use this method for making authorization decisions. Instead use {@link
/**
* Returns all of the entries represented by the present <code>Acl</code> (not parents).<p>This method is
* typically used for administrative purposes.</p>
* <p>The order that entries appear in the array is unspecified. However, if implementations use
* particular ordering logic in authorization decisions, the entries returned by this method <em>MUST</em> be
* ordered in that manner.</p>
* <p>Do <em>NOT</em> use this method for making authorization decisions. Instead use {@link
* #isGranted(Permission[], Sid[], boolean)}.</p>
*
* <p>This method must operate correctly even if the <tt>Acl</tt> only represents a subset of
* <tt>Sid</tt>s. The caller is responsible for correctly handling the result if only a subset of
* <tt>Sid</tt>s is represented.</p>
* <p>This method must operate correctly even if the <code>Acl</code> only represents a subset of
* <code>Sid</code>s. The caller is responsible for correctly handling the result if only a subset of
* <code>Sid</code>s is represented.</p>
*
* @return the list of entries represented by the <tt>Acl</tt>, or <tt>null</tt> if there are
* no entries presently associated with this <tt>Acl</tt>.
* @return the list of entries represented by the <code>Acl</code>
*/
AccessControlEntry[] getEntries();
/**
* Obtains the domain object this <tt>Acl</tt> provides entries for. This is immutable once an
* <tt>Acl</tt> is created.
* Obtains the domain object this <code>Acl</code> provides entries for. This is immutable once an
* <code>Acl</code> is created.
*
* @return the object identity (never <tt>null</tt>)
* @return the object identity
*/
ObjectIdentity getObjectIdentity();
/**
* Determines the owner of the <tt>Acl</tt>. The meaning of ownership varies by implementation and is
* Determines the owner of the <code>Acl</code>. The meaning of ownership varies by implementation and is
* unspecified.
*
* @return the owner (may be <tt>null</tt> if the implementation does not use ownership concepts)
* @return the owner (may be null if the implementation does not use ownership concepts)
*/
Sid getOwner();
/**
* A domain object may have a parent for the purpose of ACL inheritance. If there is a parent, its ACL can
* be accessed via this method. In turn, the parent's parent (grandparent) can be accessed and so on.
*
* <p>This method solely represents the presence of a navigation hierarchy between the parent <tt>Acl</tt> and this
* <tt>Acl</tt>. For actual inheritance to take place, the {@link #isEntriesInheriting()} must also be
* <tt>true</tt>.</p>
*
* <p>This method must operate correctly even if the <tt>Acl</tt> only represents a subset of
* <tt>Sid</tt>s. The caller is responsible for correctly handling the result if only a subset of
* <tt>Sid</tt>s is represented.</p>
* be accessed via this method. In turn, the parent's parent (grandparent) can be accessed and so on.<p>This
* method solely represents the presence of a navigation hierarchy between the parent <code>Acl</code> and this
* <code>Acl</code>. For actual inheritance to take place, the {@link #isEntriesInheriting()} must also be
* <code>true</code>.</p>
* <p>This method must operate correctly even if the <code>Acl</code> only represents a subset of
* <code>Sid</code>s. The caller is responsible for correctly handling the result if only a subset of
* <code>Sid</code>s is represented.</p>
*
* @return the parent <tt>Acl</tt> (may be <tt>null</tt> if this <tt>Acl</tt> does not have a parent)
* @return the parent <code>Acl</code>
*/
Acl getParentAcl();
/**
* Indicates whether the ACL entries from the {@link #getParentAcl()} should flow down into the current
* <tt>Acl</tt>.<p>The mere link between an <tt>Acl</tt> and a parent <tt>Acl</tt> on its own
* <code>Acl</code>.<p>The mere link between an <code>Acl</code> and a parent <code>Acl</code> on its own
* is insufficient to cause ACL entries to inherit down. This is because a domain object may wish to have entirely
* independent entries, but maintain the link with the parent for navigation purposes. Thus, this method denotes
* whether or not the navigation relationship also extends to the actual inheritance of entries.</p>
* whether or not the navigation relationship also extends to the actual inheritence of entries.</p>
*
* @return <tt>true</tt> if parent ACL entries inherit into the current <tt>Acl</tt>
* @return <code>true</code> if parent ACL entries inherit into the current <code>Acl</code>
*/
boolean isEntriesInheriting();
/**
* This is the actual authorization logic method, and must be used whenever ACL authorization decisions are
* required.
*
* <p>An array of <tt>Sid</tt>s are presented, representing security identifies of the current
* principal. In addition, an array of <tt>Permission</tt>s is presented which will have one or more bits set
* required.<p>An array of <code>Sid</code>s are presented, representing security identifies of the current
* principal. In addition, an array of <code>Permission</code>s is presented which will have one or more bits set
* in order to indicate the permissions needed for an affirmative authorization decision. An array is presented
* because holding <em>any</em> of the <tt>Permission</tt>s inside the array will be sufficient for an
* because holding <em>any</em> of the <code>Permission</code>s inside the array will be sufficient for an
* affirmative authorization.</p>
*
* <p>The actual approach used to make authorization decisions is left to the implementation and is not
* <p>The actual approach used to make authorization decisions is left to the implementation and is not
* specified by this interface. For example, an implementation <em>MAY</em> search the current ACL in the order
* the ACL entries have been stored. If a single entry is found that has the same active bits as are shown in a
* passed <tt>Permission</tt>, that entry's grant or deny state may determine the authorization decision. If
* the case of a deny state, the deny decision will only be relevant if all other <tt>Permission</tt>s passed
* passed <code>Permission</code>, that entry's grant or deny state may determine the authorization decision. If
* the case of a deny state, the deny decision will only be relevant if all other <code>Permission</code>s passed
* in the array have also been unsuccessfully searched. If no entry is found that match the bits in the current
* ACL, provided that {@link #isEntriesInheriting()} is <tt>true</tt>, the authorization decision may be
* ACL, provided that {@link #isEntriesInheriting()} is <code>true</code>, the authorization decision may be
* passed to the parent ACL. If there is no matching entry, the implementation MAY throw an exception, or make a
* predefined authorization decision.</p>
*
* <p>This method must operate correctly even if the <tt>Acl</tt> only represents a subset of <tt>Sid</tt>s,
* although the implementation is permitted to throw one of the signature-defined exceptions if the method
* is called requesting an authorization decision for a {@link Sid} that was never loaded in this <tt>Acl</tt>.
* </p>
* <p>This method must operate correctly even if the <code>Acl</code> only represents a subset of
* <code>Sid</code>s.</p>
*
* @param permission the permission or permissions required (at least one entry required)
* @param sids the security identities held by the principal (at least one entry required)
* @param administrativeMode if <tt>true</tt> denotes the query is for administrative purposes and no logging
* @param permission the permission or permissions required
* @param sids the security identities held by the principal
* @param administrativeMode if <code>true</code> denotes the query is for administrative purposes and no logging
* or auditing (if supported by the implementation) should be undertaken
*
* @return <tt>true</tt> if authorization is granted
* @return <code>true</code> is authorization is granted
*
* @throws NotFoundException MUST be thrown if an implementation cannot make an authoritative authorization
* decision, usually because there is no ACL information for this particular permission and/or SID
* @throws UnloadedSidException thrown if the <tt>Acl</tt> does not have details for one or more of the
* <tt>Sid</tt>s passed as arguments
* @throws UnloadedSidException thrown if the <code>Acl</code> does not have details for one or more of the
* <code>Sid</code>s passed as arguments
*/
boolean isGranted(Permission[] permission, Sid[] sids, boolean administrativeMode)
throws NotFoundException, UnloadedSidException;
/**
* For efficiency reasons an <tt>Acl</tt> may be loaded and <em>not</em> contain entries for every
* <tt>Sid</tt> in the system. If an <tt>Acl</tt> has been loaded and does not represent every
* <tt>Sid</tt>, all methods of the <tt>Acl</tt> can only be used within the limited scope of the
* <tt>Sid</tt> instances it actually represents.
* For efficiency reasons an <code>Acl</code> may be loaded and <em>not</em> contain entries for every
* <code>Sid</code> in the system. If an <code>Acl</code> has been loaded and does not represent every
* <code>Sid</code>, all methods of the <code>Sid</code> can only be used within the limited scope of the
* <code>Sid</code> instances it actually represents.
* <p>
* It is normal to load an <tt>Acl</tt> for only particular <tt>Sid</tt>s if read-only authorization
* decisions are being made. However, if user interface reporting or modification of <tt>Acl</tt>s are
* desired, an <tt>Acl</tt> should be loaded with all <tt>Sid</tt>s. This method denotes whether or
* not the specified <tt>Sid</tt>s have been loaded or not.
* It is normal to load an <code>Acl</code> for only particular <code>Sid</code>s if read-only authorization
* decisions are being made. However, if user interface reporting or modification of <code>Acl</code>s are
* desired, an <code>Acl</code> should be loaded with all <code>Sid</code>s. This method denotes whether or
* not the specified <code>Sid</code>s have been loaded or not.
* </p>
*
* @param sids one or more security identities the caller is interest in knowing whether this <tt>Sid</tt>
* @param sids one or more security identities the caller is interest in knowing whether this <code>Sid</code>
* supports
*
* @return <tt>true</tt> if every passed <tt>Sid</tt> is represented by this <tt>Acl</tt> instance
* @return <code>true</code> if every passed <code>Sid</code> is represented by this <code>Acl</code> instance
*/
boolean isSidLoaded(Sid[] sids);
}
@@ -23,7 +23,13 @@ import org.springframework.util.Assert;
* @author Ben Alex
* @version $Id$
*/
public abstract class AclFormattingUtils {
public final class AclFormattingUtils {
//~ Constructors ===================================================================================================
private AclFormattingUtils() {
}
//~ Methods ========================================================================================================
public static String demergePatterns(String original, String removeBits) {
Assert.notNull(original, "Original string required");
@@ -34,67 +34,66 @@ public interface AclService {
*
* @param parentIdentity to locate children of
*
* @return the children (or <tt>null</tt> if none were found)
* @return the children (or <code>null</code> if none were found)
*/
ObjectIdentity[] findChildren(ObjectIdentity parentIdentity);
/**
* Same as {@link #readAclsById(ObjectIdentity[])} except it returns only a single Acl.<p>This method
* should not be called as it does not leverage the underlaying implementation's potential ability to filter
* <tt>Acl</tt> entries based on a {@link Sid} parameter.</p>
* <code>Acl</code> entries based on a {@link Sid} parameter.</p>
*
* @param object to locate an {@link Acl} for
* @param object DOCUMENT ME!
*
* @return the {@link Acl} for the requested {@link ObjectIdentity} (never <tt>null</tt>)
* @return DOCUMENT ME!
*
* @throws NotFoundException if an {@link Acl} was not found for the requested {@link ObjectIdentity}
* @throws NotFoundException DOCUMENT ME!
*/
Acl readAclById(ObjectIdentity object) throws NotFoundException;
/**
* Same as {@link #readAclsById(ObjectIdentity[], Sid[])} except it returns only a single Acl.
*
* @param object to locate an {@link Acl} for
* @param sids the security identities for which {@link Acl} information is required
* (may be <tt>null</tt> to denote all entries)
* @param object DOCUMENT ME!
* @param sids DOCUMENT ME!
*
* @return the {@link Acl} for the requested {@link ObjectIdentity} (never <tt>null</tt>)
* @return DOCUMENT ME!
*
* @throws NotFoundException if an {@link Acl} was not found for the requested {@link ObjectIdentity}
* @throws NotFoundException DOCUMENT ME!
*/
Acl readAclById(ObjectIdentity object, Sid[] sids)
throws NotFoundException;
/**
* Obtains all the <tt>Acl</tt>s that apply for the passed <tt>Object</tt>s.<p>The returned map is
* keyed on the passed objects, with the values being the <tt>Acl</tt> instances. Any unknown objects will not
* Obtains all the <code>Acl</code>s that apply for the passed <code>Object</code>s.<p>The returned map is
* keyed on the passed objects, with the values being the <code>Acl</code> instances. Any unknown objects will not
* have a map key.</p>
*
* @param objects the objects to find {@link Acl} information for
* @param objects the objects to find ACL information for
*
* @return a map with exactly one element for each {@link ObjectIdentity} passed as an argument (never <tt>null</tt>)
* @return a map with zero or more elements (never <code>null</code>)
*
* @throws NotFoundException if an {@link Acl} was not found for each requested {@link ObjectIdentity}
* @throws NotFoundException DOCUMENT ME!
*/
Map readAclsById(ObjectIdentity[] objects) throws NotFoundException;
/**
* Obtains all the <tt>Acl</tt>s that apply for the passed <tt>Object</tt>s, but only for the
* Obtains all the <code>Acl</code>s that apply for the passed <code>Object</code>s, but only for the
* security identifies passed.<p>Implementations <em>MAY</em> provide a subset of the ACLs via this method
* although this is NOT a requirement. This is intended to allow performance optimisations within implementations.
* Callers should therefore use this method in preference to the alternative overloaded version which does not
* have performance optimisation opportunities.</p>
* <p>The returned map is keyed on the passed objects, with the values being the <tt>Acl</tt>
* instances. Any unknown objects (or objects for which the interested <tt>Sid</tt>s do not have entries) will
* <p>The returned map is keyed on the passed objects, with the values being the <code>Acl</code>
* instances. Any unknown objects (or objects for which the interested <code>Sid</code>s do not have entries) will
* not have a map key.</p>
*
* @param objects the objects to find {@link Acl} information for
* @param sids the security identities for which {@link Acl} information is required
* (may be <tt>null</tt> to denote all entries)
* @param objects the objects to find ACL information for
* @param sids the security identities for which ACL information is required (may be <code>null</code> to denote
* all entries)
*
* @return a map with exactly one element for each {@link ObjectIdentity} passed as an argument (never <tt>null</tt>)
* @return a map with zero or more elements (never <code>null</code>)
*
* @throws NotFoundException if an {@link Acl} was not found for each requested {@link ObjectIdentity}
* @throws NotFoundException DOCUMENT ME!
*/
Map readAclsById(ObjectIdentity[] objects, Sid[] sids)
throws NotFoundException;
@@ -14,6 +14,7 @@
*/
package org.springframework.security.acls;
import java.io.Serializable;
/**
@@ -26,5 +27,5 @@ package org.springframework.security.acls;
public interface AuditableAcl extends MutableAcl {
//~ Methods ========================================================================================================
void updateAuditing(int aceIndex, boolean auditSuccess, boolean auditFailure);
void updateAuditing(Serializable aceId, boolean auditSuccess, boolean auditFailure);
}
@@ -14,13 +14,13 @@
*/
package org.springframework.security.acls;
import java.io.Serializable;
import org.springframework.security.acls.sid.Sid;
import java.io.Serializable;
/**
* A mutable <tt>Acl</tt>.
* A mutable <code>Acl</code>.
*
* <p>
* A mutable ACL must ensure that appropriate security checks are performed
@@ -33,25 +33,29 @@ import org.springframework.security.acls.sid.Sid;
public interface MutableAcl extends Acl {
//~ Methods ========================================================================================================
void deleteAce(int aceIndex) throws NotFoundException;
void deleteAce(Serializable aceId) throws NotFoundException;
/**
* Obtains an identifier that represents this <tt>MutableAcl</tt>.
* Retrieves all of the non-deleted {@link AccessControlEntry} instances currently stored by the
* <code>MutableAcl</code>. The returned objects should be immutable outside the package, and therefore it is safe
* to return them to the caller for informational purposes. The <code>AccessControlEntry</code> information is
* needed so that invocations of update and delete methods on the <code>MutableAcl</code> can refer to a valid
* {@link AccessControlEntry#getId()}.
*
* @return the identifier, or <tt>null</tt> if unsaved
* @return DOCUMENT ME!
*/
AccessControlEntry[] getEntries();
/**
* Obtains an identifier that represents this <code>MutableAcl</code>.
*
* @return the identifier, or <code>null</code> if unsaved
*/
Serializable getId();
void insertAce(int atIndexLocation, Permission permission, Sid sid, boolean granting)
void insertAce(Serializable afterAceId, Permission permission, Sid sid, boolean granting)
throws NotFoundException;
/**
* Changes the present owner to a different owner.
*
* @param newOwner the new owner (mandatory; cannot be null)
*/
void setOwner(Sid newOwner);
/**
* Change the value returned by {@link Acl#isEntriesInheriting()}.
*
@@ -66,6 +70,6 @@ public interface MutableAcl extends Acl {
*/
void setParent(Acl newParent);
void updateAce(int aceIndex, Permission permission)
void updateAce(Serializable aceId, Permission permission)
throws NotFoundException;
}
@@ -14,15 +14,13 @@
*/
package org.springframework.security.acls;
import java.io.Serializable;
/**
* Represents a permission granted to a {@link org.springframework.security.acls.sid.Sid Sid} for a given domain object.
*
* @author Ben Alex
* @version $Id$
*/
public interface Permission extends Serializable {
public interface Permission {
//~ Static fields/initializers =====================================================================================
char RESERVED_ON = '~';
@@ -67,50 +67,8 @@ public class AccessControlEntryImpl implements AccessControlEntry, AuditableAcce
AccessControlEntryImpl rhs = (AccessControlEntryImpl) arg0;
if (this.acl == null) {
if (rhs.getAcl() != null) {
return false;
}
// Both this.acl and rhs.acl are null and thus equal
} else {
// this.acl is non-null
if (rhs.getAcl() == null) {
return false;
}
// Both this.acl and rhs.acl are non-null, so do a comparison
if (this.acl.getObjectIdentity() == null) {
if (rhs.acl.getObjectIdentity() != null) {
return false;
}
// Both this.acl and rhs.acl are null and thus equal
} else {
// Both this.acl.objectIdentity and rhs.acl.objectIdentity are non-null
if (!this.acl.getObjectIdentity().equals(rhs.getAcl().getObjectIdentity())) {
return false;
}
}
}
if (this.id == null) {
if (rhs.id != null) {
return false;
}
// Both this.id and rhs.id are null and thus equal
} else {
// this.id is non-null
if (rhs.id == null) {
return false;
}
// Both this.id and rhs.id are non-null
if (!this.id.equals(rhs.id)) {
return false;
}
}
if ((this.auditFailure != rhs.isAuditFailure()) || (this.auditSuccess != rhs.isAuditSuccess())
|| (this.granting != rhs.isGranting())
|| (this.granting != rhs.isGranting()) || !this.acl.equals(rhs.getAcl()) || !this.id.equals(rhs.getId())
|| !this.permission.equals(rhs.getPermission()) || !this.sid.equals(rhs.getSid())) {
return false;
}
@@ -14,11 +14,6 @@
*/
package org.springframework.security.acls.domain;
import java.io.Serializable;
import java.util.Iterator;
import java.util.List;
import java.util.Vector;
import org.springframework.security.acls.AccessControlEntry;
import org.springframework.security.acls.Acl;
import org.springframework.security.acls.AuditableAcl;
@@ -29,8 +24,15 @@ import org.springframework.security.acls.Permission;
import org.springframework.security.acls.UnloadedSidException;
import org.springframework.security.acls.objectidentity.ObjectIdentity;
import org.springframework.security.acls.sid.Sid;
import org.springframework.util.Assert;
import java.io.Serializable;
import java.util.Iterator;
import java.util.List;
import java.util.Vector;
/**
* Base implementation of <code>Acl</code>.
@@ -40,10 +42,10 @@ import org.springframework.util.Assert;
*/
public class AclImpl implements Acl, MutableAcl, AuditableAcl, OwnershipAcl {
//~ Instance fields ================================================================================================
private Acl parentAcl;
private transient AclAuthorizationStrategy aclAuthorizationStrategy;
private transient AuditLogger auditLogger;
private AclAuthorizationStrategy aclAuthorizationStrategy;
private AuditLogger auditLogger;
private List aces = new Vector();
private ObjectIdentity objectIdentity;
private Serializable id;
@@ -114,22 +116,34 @@ public class AclImpl implements Acl, MutableAcl, AuditableAcl, OwnershipAcl {
//~ Methods ========================================================================================================
private void verifyAceIndexExists(int aceIndex) {
if (aceIndex < 0) {
throw new NotFoundException("aceIndex must be greater than or equal to zero");
}
if (aceIndex > this.aces.size()) {
throw new NotFoundException("aceIndex must correctly refer to an index of the AccessControlEntry collection");
public void deleteAce(Serializable aceId) throws NotFoundException {
aclAuthorizationStrategy.securityCheck(this, AclAuthorizationStrategy.CHANGE_GENERAL);
synchronized (aces) {
int offset = findAceOffset(aceId);
if (offset == -1) {
throw new NotFoundException("Requested ACE ID not found");
}
this.aces.remove(offset);
}
}
public void deleteAce(int aceIndex) throws NotFoundException {
aclAuthorizationStrategy.securityCheck(this, AclAuthorizationStrategy.CHANGE_GENERAL);
verifyAceIndexExists(aceIndex);
private int findAceOffset(Serializable aceId) {
Assert.notNull(aceId, "ACE ID is required");
synchronized (aces) {
this.aces.remove(aceIndex);
for (int i = 0; i < aces.size(); i++) {
AccessControlEntry ace = (AccessControlEntry) aces.get(i);
if (ace.getId().equals(aceId)) {
return i;
}
}
}
return -1;
}
public AccessControlEntry[] getEntries() {
@@ -153,22 +167,26 @@ public class AclImpl implements Acl, MutableAcl, AuditableAcl, OwnershipAcl {
return parentAcl;
}
public void insertAce(int atIndexLocation, Permission permission, Sid sid, boolean granting)
public void insertAce(Serializable afterAceId, Permission permission, Sid sid, boolean granting)
throws NotFoundException {
aclAuthorizationStrategy.securityCheck(this, AclAuthorizationStrategy.CHANGE_GENERAL);
Assert.notNull(permission, "Permission required");
Assert.notNull(sid, "Sid required");
if (atIndexLocation < 0) {
throw new NotFoundException("atIndexLocation must be greater than or equal to zero");
}
if (atIndexLocation > this.aces.size()) {
throw new NotFoundException("atIndexLocation must be less than or equal to the size of the AccessControlEntry collection");
}
AccessControlEntryImpl ace = new AccessControlEntryImpl(null, this, sid, permission, granting, false, false);
synchronized (aces) {
this.aces.add(atIndexLocation, ace);
if (afterAceId != null) {
int offset = findAceOffset(afterAceId);
if (offset == -1) {
throw new NotFoundException("Requested ACE ID not found");
}
this.aces.add(offset + 1, ace);
} else {
this.aces.add(ace);
}
}
}
@@ -319,7 +337,8 @@ public class AclImpl implements Acl, MutableAcl, AuditableAcl, OwnershipAcl {
public void setParent(Acl newParent) {
aclAuthorizationStrategy.securityCheck(this, AclAuthorizationStrategy.CHANGE_GENERAL);
Assert.isTrue(newParent == null || !newParent.equals(this), "Cannot be the parent of yourself");
Assert.notNull(newParent, "New Parent required");
Assert.isTrue(!newParent.equals(this), "Cannot be the parent of yourself");
this.parentAcl = newParent;
}
@@ -349,63 +368,40 @@ public class AclImpl implements Acl, MutableAcl, AuditableAcl, OwnershipAcl {
sb.append("inheriting: ").append(this.entriesInheriting).append("; ");
sb.append("parent: ").append((this.parentAcl == null) ? "Null" : this.parentAcl.getObjectIdentity().toString());
sb.append("aclAuthorizationStrategy: ").append(this.aclAuthorizationStrategy).append("; ");
sb.append("auditLogger: ").append(this.auditLogger);
sb.append("]");
return sb.toString();
}
public void updateAce(int aceIndex, Permission permission)
public void updateAce(Serializable aceId, Permission permission)
throws NotFoundException {
aclAuthorizationStrategy.securityCheck(this, AclAuthorizationStrategy.CHANGE_GENERAL);
verifyAceIndexExists(aceIndex);
synchronized (aces) {
AccessControlEntryImpl ace = (AccessControlEntryImpl) aces.get(aceIndex);
int offset = findAceOffset(aceId);
if (offset == -1) {
throw new NotFoundException("Requested ACE ID not found");
}
AccessControlEntryImpl ace = (AccessControlEntryImpl) aces.get(offset);
ace.setPermission(permission);
}
}
public void updateAuditing(int aceIndex, boolean auditSuccess, boolean auditFailure) {
public void updateAuditing(Serializable aceId, boolean auditSuccess, boolean auditFailure) {
aclAuthorizationStrategy.securityCheck(this, AclAuthorizationStrategy.CHANGE_AUDITING);
verifyAceIndexExists(aceIndex);
synchronized (aces) {
AccessControlEntryImpl ace = (AccessControlEntryImpl) aces.get(aceIndex);
int offset = findAceOffset(aceId);
if (offset == -1) {
throw new NotFoundException("Requested ACE ID not found");
}
AccessControlEntryImpl ace = (AccessControlEntryImpl) aces.get(offset);
ace.setAuditSuccess(auditSuccess);
ace.setAuditFailure(auditFailure);
}
}
public boolean equals(Object obj) {
if (obj instanceof AclImpl) {
AclImpl rhs = (AclImpl) obj;
if (this.aces.equals(rhs.aces)) {
if ((this.parentAcl == null && rhs.parentAcl == null) || (this.parentAcl.equals(rhs.parentAcl))) {
if ((this.objectIdentity == null && rhs.objectIdentity == null) || (this.objectIdentity.equals(rhs.objectIdentity))) {
if ((this.id == null && rhs.id == null) || (this.id.equals(rhs.id))) {
if ((this.owner == null && rhs.owner == null) || this.owner.equals(rhs.owner)) {
if (this.entriesInheriting == rhs.entriesInheriting) {
if ((this.loadedSids == null && rhs.loadedSids == null)) {
return true;
}
if (this.loadedSids.length == rhs.loadedSids.length) {
for (int i = 0; i < this.loadedSids.length; i++) {
if (!this.loadedSids[i].equals(rhs.loadedSids[i])) {
return false;
}
}
return true;
}
}
}
}
}
}
}
}
return false;
}
}
@@ -14,23 +14,6 @@
*/
package org.springframework.security.acls.jdbc;
import java.lang.reflect.Field;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.sql.DataSource;
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.PreparedStatementSetter;
import org.springframework.jdbc.core.ResultSetExtractor;
import org.springframework.security.acls.AccessControlEntry;
import org.springframework.security.acls.Acl;
import org.springframework.security.acls.MutableAcl;
@@ -47,9 +30,32 @@ import org.springframework.security.acls.objectidentity.ObjectIdentityImpl;
import org.springframework.security.acls.sid.GrantedAuthoritySid;
import org.springframework.security.acls.sid.PrincipalSid;
import org.springframework.security.acls.sid.Sid;
import org.springframework.security.util.FieldUtils;
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.PreparedStatementSetter;
import org.springframework.jdbc.core.ResultSetExtractor;
import org.springframework.util.Assert;
import java.lang.reflect.Field;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.sql.DataSource;
/**
* Performs lookups in a manner that is compatible with ANSI SQL.<p>NB: This implementation does attempt to provide
@@ -73,7 +79,7 @@ public final class BasicLookupStrategy implements LookupStrategy {
//~ Constructors ===================================================================================================
/**
/**
* Constructor accepting mandatory arguments
*
* @param dataSource to access the database
@@ -97,30 +103,19 @@ public final class BasicLookupStrategy implements LookupStrategy {
private static String computeRepeatingSql(String repeatingSql, int requiredRepetitions) {
Assert.isTrue(requiredRepetitions >= 1, "Must be => 1");
String startSql = "select acl_object_identity.object_id_identity, "
+ "acl_entry.ace_order, "
+ "acl_object_identity.id as acl_id, "
+ "acl_object_identity.parent_object, "
+ "acl_object_identity.entries_inheriting, "
+ "acl_entry.id as ace_id, "
+ "acl_entry.mask, "
+ "acl_entry.granting, "
+ "acl_entry.audit_success, "
+ "acl_entry.audit_failure, "
+ "acl_sid.principal as ace_principal, "
+ "acl_sid.sid as ace_sid, "
+ "acli_sid.principal as acl_principal, "
+ "acli_sid.sid as acl_sid, "
+ "acl_class.class "
+ "from acl_object_identity "
+ "left join acl_sid acli_sid on acli_sid.id = acl_object_identity.owner_sid "
+ "left join acl_class on acl_class.id = acl_object_identity.object_id_class "
+ "left join acl_entry on acl_object_identity.id = acl_entry.acl_object_identity "
+ "left join acl_sid on acl_entry.sid = acl_sid.id "
+ "where ( ";
String startSql = "select ACL_OBJECT_IDENTITY.OBJECT_ID_IDENTITY, ACL_ENTRY.ACE_ORDER, "
+ "ACL_OBJECT_IDENTITY.ID as ACL_ID, " + "ACL_OBJECT_IDENTITY.PARENT_OBJECT, "
+ "ACL_OBJECT_IDENTITY,ENTRIES_INHERITING, "
+ "ACL_ENTRY.ID as ACE_ID, ACL_ENTRY.MASK, ACL_ENTRY.GRANTING, "
+ "ACL_ENTRY.AUDIT_SUCCESS, ACL_ENTRY.AUDIT_FAILURE, "
+ "ACL_SID.PRINCIPAL as ACE_PRINCIPAL, ACL_SID.SID as ACE_SID, "
+ "ACLI_SID.PRINCIPAL as ACL_PRINCIPAL, ACLI_SID.SID as ACL_SID, " + "ACL_CLASS.CLASS "
+ "from ACL_OBJECT_IDENTITY, ACL_SID ACLI_SID, ACL_CLASS "
+ "LEFT JOIN ACL_ENTRY ON ACL_OBJECT_IDENTITY.ID = ACL_ENTRY.ACL_OBJECT_IDENTITY "
+ "LEFT JOIN ACL_SID ON ACL_ENTRY.SID = ACL_SID.ID where ACLI_SID.ID = ACL_OBJECT_IDENTITY.OWNER_SID "
+ "and ACL_CLASS.ID = ACL_OBJECT_IDENTITY.OBJECT_ID_CLASS " + "and ( ";
String endSql = ") order by acl_object_identity.object_id_identity"
+ " asc, acl_entry.ace_order asc";
String endSql = ") order by ACL_OBJECT_IDENTITY.OBJECT_ID_IDENTITY asc, ACL_ENTRY.ACE_ORDER asc";
StringBuffer sqlStringBuffer = new StringBuffer();
sqlStringBuffer.append(startSql);
@@ -196,30 +191,30 @@ public final class BasicLookupStrategy implements LookupStrategy {
*/
private void convertCurrentResultIntoObject(Map acls, ResultSet rs)
throws SQLException {
Long id = new Long(rs.getLong("acl_id"));
Long id = new Long(rs.getLong("ACL_ID"));
// If we already have an ACL for this ID, just create the ACE
AclImpl acl = (AclImpl) acls.get(id);
if (acl == null) {
// Make an AclImpl and pop it into the Map
ObjectIdentity objectIdentity = new ObjectIdentityImpl(rs.getString("class"),
new Long(rs.getLong("object_id_identity")));
ObjectIdentity objectIdentity = new ObjectIdentityImpl(rs.getString("CLASS"),
new Long(rs.getLong("OBJECT_ID_IDENTITY")));
Acl parentAcl = null;
long parentAclId = rs.getLong("parent_object");
long parentAclId = rs.getLong("PARENT_OBJECT");
if (parentAclId != 0) {
parentAcl = new StubAclParent(new Long(parentAclId));
}
boolean entriesInheriting = rs.getBoolean("entries_inheriting");
boolean entriesInheriting = rs.getBoolean("ENTRIES_INHERITING");
Sid owner;
if (rs.getBoolean("acl_principal")) {
owner = new PrincipalSid(rs.getString("acl_sid"));
if (rs.getBoolean("ACL_PRINCIPAL")) {
owner = new PrincipalSid(rs.getString("ACL_SID"));
} else {
owner = new GrantedAuthoritySid(rs.getString("acl_sid"));
owner = new GrantedAuthoritySid(rs.getString("ACL_SID"));
}
acl = new AclImpl(objectIdentity, id, aclAuthorizationStrategy, auditLogger, parentAcl, null,
@@ -229,20 +224,20 @@ public final class BasicLookupStrategy implements LookupStrategy {
// Add an extra ACE to the ACL (ORDER BY maintains the ACE list order)
// It is permissable to have no ACEs in an ACL (which is detected by a null ACE_SID)
if (rs.getString("ace_sid") != null) {
Long aceId = new Long(rs.getLong("ace_id"));
if (rs.getString("ACE_SID") != null) {
Long aceId = new Long(rs.getLong("ACE_ID"));
Sid recipient;
if (rs.getBoolean("ace_principal")) {
recipient = new PrincipalSid(rs.getString("ace_sid"));
if (rs.getBoolean("ACE_PRINCIPAL")) {
recipient = new PrincipalSid(rs.getString("ACE_SID"));
} else {
recipient = new GrantedAuthoritySid(rs.getString("ace_sid"));
recipient = new GrantedAuthoritySid(rs.getString("ACE_SID"));
}
Permission permission = BasePermission.buildFromMask(rs.getInt("mask"));
boolean granting = rs.getBoolean("granting");
boolean auditSuccess = rs.getBoolean("audit_success");
boolean auditFailure = rs.getBoolean("audit_failure");
Permission permission = BasePermission.buildFromMask(rs.getInt("MASK"));
boolean granting = rs.getBoolean("GRANTING");
boolean auditSuccess = rs.getBoolean("AUDIT_SUCCESS");
boolean auditFailure = rs.getBoolean("AUDIT_FAILURE");
AccessControlEntryImpl ace = new AccessControlEntryImpl(aceId, acl, recipient, permission, granting,
auditSuccess, auditFailure);
@@ -283,10 +278,10 @@ public final class BasicLookupStrategy implements LookupStrategy {
// Make the "acls" map contain all requested objectIdentities
// (including markers to each parent in the hierarchy)
String sql = computeRepeatingSql("(acl_object_identity.object_id_identity = ? and acl_class.class = ?)",
String sql = computeRepeatingSql("(ACL_OBJECT_IDENTITY.OBJECT_ID_IDENTITY = ? and ACL_CLASS.CLASS = ?)",
objectIdentities.length);
Set parentsToLookup = (Set) jdbcTemplate.query(sql,
jdbcTemplate.query(sql,
new PreparedStatementSetter() {
public void setValues(PreparedStatement ps)
throws SQLException {
@@ -304,11 +299,6 @@ public final class BasicLookupStrategy implements LookupStrategy {
}
}
}, new ProcessResultSet(acls, sids));
// Lookup the parents, now that our JdbcTemplate has released the database connection (SEC-547)
if (parentsToLookup.size() > 0) {
lookupPrimaryKeys(acls, parentsToLookup, sids);
}
// Finally, convert our "acls" containing StubAclParents into true Acls
Map resultMap = new HashMap();
@@ -338,9 +328,9 @@ public final class BasicLookupStrategy implements LookupStrategy {
Assert.notNull(acls, "ACLs are required");
Assert.notEmpty(findNow, "Items to find now required");
String sql = computeRepeatingSql("(acl_object_identity.id = ?)", findNow.size());
String sql = computeRepeatingSql("(ACL_OBJECT_IDENTITY.ID = ?)", findNow.size());
Set parentsToLookup = (Set) jdbcTemplate.query(sql,
jdbcTemplate.query(sql,
new PreparedStatementSetter() {
public void setValues(PreparedStatement ps)
throws SQLException {
@@ -353,29 +343,25 @@ public final class BasicLookupStrategy implements LookupStrategy {
}
}
}, new ProcessResultSet(acls, sids));
// Lookup the parents, now that our JdbcTemplate has released the database connection (SEC-547)
if (parentsToLookup.size() > 0) {
lookupPrimaryKeys(acls, parentsToLookup, sids);
}
}
/**
* The main method.<p>WARNING: This implementation completely disregards the "sids" argument! Every item
* The main method.<p>WARNING: This implementation completely disregards the "sids" parameter! Every item
* in the cache is expected to contain all SIDs. If you have serious performance needs (eg a very large number of
* SIDs per object identity), you'll probably want to develop a custom {@link LookupStrategy} implementation
* instead.</p>
* <p>The implementation works in batch sizes specfied by {@link #batchSize}.</p>
*
* @param objects the identities to lookup (required)
* @param sids the SIDs for which identities are required (ignored by this implementation)
* @param objects DOCUMENT ME!
* @param sids DOCUMENT ME!
*
* @return a <tt>Map</tt> where keys represent the {@link ObjectIdentity} of the located {@link Acl} and values
* are the located {@link Acl} (never <tt>null</tt> although some entries may be missing; this method
* should not throw {@link NotFoundException}, as a chain of {@link LookupStrategy}s may be used
* to automatically create entries if required)
* @return DOCUMENT ME!
*
* @throws NotFoundException DOCUMENT ME!
* @throws IllegalStateException DOCUMENT ME!
*/
public Map readAclsById(ObjectIdentity[] objects, Sid[] sids) {
public Map readAclsById(ObjectIdentity[] objects, Sid[] sids)
throws NotFoundException {
Assert.isTrue(batchSize >= 1, "BatchSize must be >= 1");
Assert.notEmpty(objects, "Objects to lookup required");
@@ -385,53 +371,56 @@ public final class BasicLookupStrategy implements LookupStrategy {
Set currentBatchToLoad = new HashSet(); // contains ObjectIdentitys
for (int i = 0; i < objects.length; i++) {
boolean aclFound = false;
// Check we don't already have this ACL in the results
// Check we don't already have this ACL in the results
if (result.containsKey(objects[i])) {
aclFound = true;
continue; // already in results, so move to next element
}
// Check cache for the present ACL entry
if (!aclFound) {
Acl acl = aclCache.getFromCache(objects[i]);
// Ensure any cached element supports all the requested SIDs
// (they should always, as our base impl doesn't filter on SID)
if (acl != null) {
if (acl.isSidLoaded(sids)) {
result.put(acl.getObjectIdentity(), acl);
aclFound = true;
} else {
throw new IllegalStateException(
"Error: SID-filtered element detected when implementation does not perform SID filtering "
+ "- have you added something to the cache manually?");
}
Acl acl = aclCache.getFromCache(objects[i]);
// Ensure any cached element supports all the requested SIDs
// (they should always, as our base impl doesn't filter on SID)
if (acl != null) {
if (acl.isSidLoaded(sids)) {
result.put(acl.getObjectIdentity(), acl);
continue; // now in results, so move to next element
} else {
throw new IllegalStateException(
"Error: SID-filtered element detected when implementation does not perform SID filtering "
+ "- have you added something to the cache manually?");
}
}
// Load the ACL from the database
if (!aclFound) {
currentBatchToLoad.add(objects[i]);
}
// To get this far, we have no choice but to retrieve it via JDBC
// (although we don't do it until we get a batch of them to load)
currentBatchToLoad.add(objects[i]);
// Is it time to load from JDBC the currentBatchToLoad?
if ((currentBatchToLoad.size() == this.batchSize) || ((i + 1) == objects.length)) {
if (currentBatchToLoad.size() > 0) {
Map loadedBatch = lookupObjectIdentities((ObjectIdentity[]) currentBatchToLoad.toArray(new ObjectIdentity[] {}), sids);
Map loadedBatch = lookupObjectIdentities((ObjectIdentity[]) currentBatchToLoad.toArray(
new ObjectIdentity[] {}), sids);
// Add loaded batch (all elements 100% initialized) to results
result.putAll(loadedBatch);
// Add the loaded batch to the cache
Iterator loadedAclIterator = loadedBatch.values().iterator();
while (loadedAclIterator.hasNext()) {
aclCache.putInCache((AclImpl) loadedAclIterator.next());
}
currentBatchToLoad.clear();
}
// Add loaded batch (all elements 100% initialized) to results
result.putAll(loadedBatch);
// Add the loaded batch to the cache
Iterator loadedAclIterator = loadedBatch.values().iterator();
while (loadedAclIterator.hasNext()) {
aclCache.putInCache((AclImpl) loadedAclIterator.next());
}
currentBatchToLoad.clear();
}
}
// Now we're done, check every requested object identity was found (throw NotFoundException if needed)
for (int i = 0; i < objects.length; i++) {
if (!result.containsKey(objects[i])) {
throw new NotFoundException("Unable to find ACL information for object identity '"
+ objects[i].toString() + "'");
}
}
@@ -454,17 +443,6 @@ public final class BasicLookupStrategy implements LookupStrategy {
this.sids = sids; // can be null
}
/**
* Implementation of {@link ResultSetExtractor#extractData(ResultSet)}.
* Creates an {@link Acl} for each row in the {@link ResultSet} and
* ensures it is in member field <tt>acls</tt>. Any {@link Acl} with
* a parent will have the parents id returned in a set. The returned
* set of ids may requires further processing.
* @param rs The {@link ResultSet} to be processed
* @return a list of parent IDs remaining to be looked up (may be empty, but never <tt>null</tt>)
* @throws SQLException
* @throws DataAccessException
*/
public Object extractData(ResultSet rs) throws SQLException, DataAccessException {
Set parentIdsToLookup = new HashSet(); // Set of parent_id Longs
@@ -473,7 +451,7 @@ public final class BasicLookupStrategy implements LookupStrategy {
convertCurrentResultIntoObject(acls, rs);
// Figure out if this row means we need to lookup another parent
long parentId = rs.getLong("parent_object");
long parentId = rs.getLong("PARENT_OBJECT");
if (parentId != 0) {
// See if it's already in the "acls"
@@ -494,8 +472,13 @@ public final class BasicLookupStrategy implements LookupStrategy {
}
}
// Return the parents left to lookup to the calller
return parentIdsToLookup;
// Lookup parents, adding Acls (with StubAclParents) to "acl" map
if (parentIdsToLookup.size() > 0) {
lookupPrimaryKeys(acls, parentIdsToLookup, sids);
}
// Return null to meet ResultSetExtractor method contract
return null;
}
}
@@ -14,28 +14,20 @@
*/
package org.springframework.security.acls.jdbc;
import java.io.Serializable;
import net.sf.ehcache.CacheException;
import net.sf.ehcache.Ehcache;
import net.sf.ehcache.Element;
import net.sf.ehcache.Ehcache;
import org.springframework.security.acls.MutableAcl;
import org.springframework.security.acls.domain.AclAuthorizationStrategy;
import org.springframework.security.acls.domain.AclImpl;
import org.springframework.security.acls.domain.AuditLogger;
import org.springframework.security.acls.objectidentity.ObjectIdentity;
import org.springframework.security.util.FieldUtils;
import org.springframework.util.Assert;
import java.io.Serializable;
/**
* Simple implementation of {@link AclCache} that delegates to EH-CACHE.
*
* <p>
* Designed to handle the transient fields in {@link AclImpl}. Note that this implementation assumes all
* {@link AclImpl} instances share the same {@link AuditLogger} and {@link AclAuthorizationStrategy} instance.
* </p>
*
* @author Ben Alex
* @version $Id$
@@ -44,8 +36,6 @@ public class EhCacheBasedAclCache implements AclCache {
//~ Instance fields ================================================================================================
private Ehcache cache;
private AuditLogger auditLogger;
private AclAuthorizationStrategy aclAuthorizationStrategy;
//~ Constructors ===================================================================================================
@@ -91,10 +81,10 @@ public class EhCacheBasedAclCache implements AclCache {
return null;
}
return initializeTransientFields((MutableAcl)element.getValue());
return (MutableAcl) element.getValue();
}
public MutableAcl getFromCache(Serializable pk) {
public MutableAcl getFromCache(Serializable pk) {
Assert.notNull(pk, "Primary key (identifier) required");
Element element = null;
@@ -107,7 +97,7 @@ public class EhCacheBasedAclCache implements AclCache {
return null;
}
return initializeTransientFields((MutableAcl) element.getValue());
return (MutableAcl) element.getValue();
}
public void putInCache(MutableAcl acl) {
@@ -115,13 +105,6 @@ public class EhCacheBasedAclCache implements AclCache {
Assert.notNull(acl.getObjectIdentity(), "ObjectIdentity required");
Assert.notNull(acl.getId(), "ID required");
if (this.aclAuthorizationStrategy == null) {
if (acl instanceof AclImpl) {
this.aclAuthorizationStrategy = (AclAuthorizationStrategy) FieldUtils.getProtectedFieldValue("aclAuthorizationStrategy", acl);
this.auditLogger = (AuditLogger) FieldUtils.getProtectedFieldValue("auditLogger", acl);
}
}
if ((acl.getParentAcl() != null) && (acl.getParentAcl() instanceof MutableAcl)) {
putInCache((MutableAcl) acl.getParentAcl());
}
@@ -129,12 +112,4 @@ public class EhCacheBasedAclCache implements AclCache {
cache.put(new Element(acl.getObjectIdentity(), acl));
cache.put(new Element(acl.getId(), acl));
}
private MutableAcl initializeTransientFields(MutableAcl value) {
if (value instanceof AclImpl) {
FieldUtils.setProtectedFieldValue("aclAuthorizationStrategy", value, this.aclAuthorizationStrategy);
FieldUtils.setProtectedFieldValue("auditLogger", value, this.auditLogger);
}
return value;
}
}
@@ -28,7 +28,6 @@ import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import java.sql.ResultSet;
import java.sql.SQLException;
@@ -53,11 +52,11 @@ public class JdbcAclService implements AclService {
//~ Static fields/initializers =====================================================================================
protected static final Log log = LogFactory.getLog(JdbcAclService.class);
private static final String selectAclObjectWithParent = "select obj.object_id_identity obj_id, class.class class "
+ "from acl_object_identity obj, acl_object_identity parent, acl_class class "
+ "where obj.parent_object = parent.id and obj.object_id_class = class.id "
+ "and parent.object_id_identity = ? and parent.object_id_class = ("
+ "select id FROM acl_class where acl_class.class = ?)";
private static final String selectAclObjectWithParent = "SELECT obj.object_id_identity obj_id, class.class class "
+ "FROM acl_object_identity obj, acl_object_identity parent, acl_class class "
+ "WHERE obj.parent_object = parent.id AND obj.object_id_class = class.id "
+ "AND parent.object_id_identity = ? AND parent.object_id_class = ("
+ "SELECT id FROM acl_class WHERE acl_class.class = ?)";
//~ Instance fields ================================================================================================
@@ -82,22 +81,21 @@ public class JdbcAclService implements AclService {
public Object mapRow(ResultSet rs, int rowNum)
throws SQLException {
String javaType = rs.getString("class");
Long identifier = new Long(rs.getLong("obj_id"));
String identifier = rs.getString("obj_id");
return new ObjectIdentityImpl(javaType, identifier);
}
});
if (objects.size() == 0) {
return null;
}
return (ObjectIdentityImpl[]) objects.toArray(new ObjectIdentityImpl[objects.size()]);
return (ObjectIdentityImpl[]) objects.toArray(new ObjectIdentityImpl[] {});
}
public Acl readAclById(ObjectIdentity object, Sid[] sids) throws NotFoundException {
Map map = readAclsById(new ObjectIdentity[] {object}, sids);
Assert.isTrue(map.containsKey(object), "There should have been an Acl entry for ObjectIdentity " + object);
if (map.size() == 0) {
throw new NotFoundException("Could not find ACL");
}
return (Acl) map.get(object);
}
@@ -106,21 +104,11 @@ public class JdbcAclService implements AclService {
return readAclById(object, null);
}
public Map readAclsById(ObjectIdentity[] objects) throws NotFoundException {
public Map readAclsById(ObjectIdentity[] objects) {
return readAclsById(objects, null);
}
public Map readAclsById(ObjectIdentity[] objects, Sid[] sids) throws NotFoundException {
Map result = lookupStrategy.readAclsById(objects, sids);
// Check every requested object identity was found (throw NotFoundException if needed)
for (int i = 0; i < objects.length; i++) {
if (!result.containsKey(objects[i])) {
throw new NotFoundException("Unable to find ACL information for object identity '"
+ objects[i].toString() + "'");
}
}
return result;
return lookupStrategy.readAclsById(objects, sids);
}
}
@@ -60,24 +60,26 @@ import javax.sql.DataSource;
public class JdbcMutableAclService extends JdbcAclService implements MutableAclService {
//~ Instance fields ================================================================================================
private boolean foreignKeysInDatabase = true;
private AclCache aclCache;
private String deleteEntryByObjectIdentityForeignKey = "delete from acl_entry where acl_object_identity=?";
private String deleteObjectIdentityByPrimaryKey = "delete from acl_object_identity where id=?";
private String deleteClassByClassNameString = "DELETE FROM acl_class WHERE class=?";
private String deleteEntryByObjectIdentityForeignKey = "DELETE FROM acl_entry WHERE acl_object_identity=?";
private String deleteObjectIdentityByPrimaryKey = "DELETE FROM acl_object_identity WHERE id=?";
private String identityQuery = "call identity()";
private String insertClass = "insert into acl_class (class) values (?)";
private String insertEntry = "insert into acl_entry "
+ "(acl_object_identity, ace_order, sid, mask, granting, audit_success, audit_failure)"
+ "values (?, ?, ?, ?, ?, ?, ?)";
private String insertObjectIdentity = "insert into acl_object_identity "
+ "(object_id_class, object_id_identity, owner_sid, entries_inheriting) " + "values (?, ?, ?, ?)";
private String insertSid = "insert into acl_sid (principal, sid) values (?, ?)";
private String selectClassPrimaryKey = "select id from acl_class where class=?";
private String selectObjectIdentityPrimaryKey = "select acl_object_identity.id from acl_object_identity, acl_class "
+ "where acl_object_identity.object_id_class = acl_class.id and acl_class.class=? "
private String insertClass = "INSERT INTO acl_class (id, class) VALUES (null, ?)";
private String insertEntry = "INSERT INTO acl_entry "
+ "(id, acl_object_identity, ace_order, sid, mask, granting, audit_success, audit_failure)"
+ "VALUES (null, ?, ?, ?, ?, ?, ?, ?)";
private String insertObjectIdentity = "INSERT INTO acl_object_identity "
+ "(id, object_id_class, object_id_identity, owner_sid, entries_inheriting) " + "VALUES (null, ?, ?, ?, ?)";
private String insertSid = "INSERT INTO acl_sid (id, principal, sid) VALUES (null, ?, ?)";
private String selectClassPrimaryKey = "SELECT id FROM acl_class WHERE class=?";
private String selectCountObjectIdentityRowsForParticularClassNameString = "SELECT COUNT(acl_object_identity.id) "
+ "FROM acl_object_identity, acl_class WHERE acl_class.id = acl_object_identity.object_id_class and class=?";
private String selectObjectIdentityPrimaryKey = "SELECT acl_object_identity.id FROM acl_object_identity, acl_class "
+ "WHERE acl_object_identity.object_id_class = acl_class.id and acl_class.class=? "
+ "and acl_object_identity.object_id_identity = ?";
private String selectSidPrimaryKey = "select id from acl_sid where principal=? and sid=?";
private String updateObjectIdentity = "update acl_object_identity set "
private String selectSidPrimaryKey = "SELECT id FROM acl_sid WHERE principal=? AND sid=?";
private String updateObjectIdentity = "UPDATE acl_object_identity SET "
+ "parent_object = ?, owner_sid = ?, entries_inheriting = ?" + " where id = ?";
//~ Constructors ===================================================================================================
@@ -235,60 +237,58 @@ public class JdbcMutableAclService extends JdbcAclService implements MutableAclS
Assert.notNull(objectIdentity, "Object Identity required");
Assert.notNull(objectIdentity.getIdentifier(), "Object Identity doesn't provide an identifier");
// Recursively call this method for children, or handle children if they don't want automatic recursion
ObjectIdentity[] children = findChildren(objectIdentity);
if (deleteChildren) {
ObjectIdentity[] children = findChildren(objectIdentity);
if (children != null) {
for (int i = 0; i < children.length; i++) {
deleteAcl(children[i], true);
}
}
} else {
if (!foreignKeysInDatabase) {
// We need to perform a manual verification for what a FK would normally do
// We generally don't do this, in the interests of deadlock management
ObjectIdentity[] children = findChildren(objectIdentity);
if (children != null) {
throw new ChildrenExistException("Cannot delete '" + objectIdentity + "' (has " + children.length
+ " children)");
}
}
for (int i = 0; i < children.length; i++) {
deleteAcl(children[i], true);
}
} else if (children.length > 0) {
throw new ChildrenExistException("Cannot delete '" + objectIdentity + "' (has " + children.length
+ " children)");
}
Long oidPrimaryKey = retrieveObjectIdentityPrimaryKey(objectIdentity);
// Delete this ACL's ACEs in the acl_entry table
deleteEntries(oidPrimaryKey);
deleteEntries(objectIdentity);
// Delete this ACL's acl_object_identity row
deleteObjectIdentity(oidPrimaryKey);
deleteObjectIdentityAndOptionallyClass(objectIdentity);
// Clear the cache
aclCache.evictFromCache(objectIdentity);
}
/**
* Deletes all ACEs defined in the acl_entry table belonging to the presented ObjectIdentity primary key.
* Deletes all ACEs defined in the acl_entry table belonging to the presented ObjectIdentity
*
* @param oidPrimaryKey the rows in acl_entry to delete
* @param oid the rows in acl_entry to delete
*/
protected void deleteEntries(Long oidPrimaryKey) {
jdbcTemplate.update(deleteEntryByObjectIdentityForeignKey,
new Object[] {oidPrimaryKey});
protected void deleteEntries(ObjectIdentity oid) {
jdbcTemplate.update(deleteEntryByObjectIdentityForeignKey,
new Object[] {retrieveObjectIdentityPrimaryKey(oid)});
}
/**
* Deletes a single row from acl_object_identity that is associated with the presented ObjectIdentity primary key.
*
* <p>
* We do not delete any entries from acl_class, even if no classes are using that class any longer. This is a
* deadlock avoidance approach.
* </p>
* Deletes a single row from acl_object_identity that is associated with the presented ObjectIdentity. In
* addition, deletes the corresponding row from acl_class if there are no more entries in acl_object_identity that
* use that particular acl_class. This keeps the acl_class table reasonably small.
*
* @param oidPrimaryKey to delete the acl_object_identity
* @param oid to delete the acl_object_identity (and clean up acl_class for that class name if appropriate)
*/
protected void deleteObjectIdentity(Long oidPrimaryKey) {
protected void deleteObjectIdentityAndOptionallyClass(ObjectIdentity oid) {
// Delete the acl_object_identity row
jdbcTemplate.update(deleteObjectIdentityByPrimaryKey, new Object[] {oidPrimaryKey});
jdbcTemplate.update(deleteObjectIdentityByPrimaryKey, new Object[] {retrieveObjectIdentityPrimaryKey(oid)});
// Delete the acl_class row, assuming there are no other references to it in acl_object_identity
Object[] className = {oid.getJavaType().getName()};
long numObjectIdentities = jdbcTemplate.queryForLong(selectCountObjectIdentityRowsForParticularClassNameString,
className);
if (numObjectIdentities == 0) {
// No more rows
jdbcTemplate.update(deleteClassByClassNameString, className);
}
}
/**
@@ -324,7 +324,7 @@ public class JdbcMutableAclService extends JdbcAclService implements MutableAclS
Assert.notNull(acl.getId(), "Object Identity doesn't provide an identifier");
// Delete this ACL's ACEs in the acl_entry table
deleteEntries(retrieveObjectIdentityPrimaryKey(acl.getObjectIdentity()));
deleteEntries(acl.getObjectIdentity());
// Create this ACL's ACEs in the acl_entry table
createEntries(acl);
@@ -332,23 +332,12 @@ public class JdbcMutableAclService extends JdbcAclService implements MutableAclS
// Change the mutable columns in acl_object_identity
updateObjectIdentity(acl);
// Clear the cache, including children
clearCacheIncludingChildren(acl.getObjectIdentity());
// Clear the cache
aclCache.evictFromCache(acl.getObjectIdentity());
// Retrieve the ACL via superclass (ensures cache registration, proper retrieval etc)
return (MutableAcl) super.readAclById(acl.getObjectIdentity());
}
private void clearCacheIncludingChildren(ObjectIdentity objectIdentity) {
Assert.notNull(objectIdentity, "ObjectIdentity required");
ObjectIdentity[] children = findChildren(objectIdentity);
if (children != null) {
for (int i = 0; i < children.length; i++) {
clearCacheIncludingChildren(children[i]);
}
}
aclCache.evictFromCache(objectIdentity);
}
/**
* Updates an existing acl_object_identity row, with new information presented in the passed MutableAcl
@@ -379,17 +368,4 @@ public class JdbcMutableAclService extends JdbcAclService implements MutableAclS
throw new NotFoundException("Unable to locate ACL to update");
}
}
public void setIdentityQuery(String identityQuery) {
Assert.hasText(identityQuery, "New identity query is required");
this.identityQuery = identityQuery;
}
/**
* @param foreignKeysInDatabase if false this class will perform additional FK constrain checking, which may
* cause deadlocks (the default is true, so deadlocks are avoided but the database is expected to enforce FKs)
*/
public void setForeignKeysInDatabase(boolean foreignKeysInDatabase) {
this.foreignKeysInDatabase = foreignKeysInDatabase;
}
}
@@ -14,7 +14,6 @@
*/
package org.springframework.security.acls.jdbc;
import org.springframework.security.acls.NotFoundException;
import org.springframework.security.acls.objectidentity.ObjectIdentity;
import org.springframework.security.acls.sid.Sid;
@@ -23,7 +22,7 @@ import java.util.Map;
/**
* Performs lookups for {@link org.springframework.security.acls.AclService}.
*
*
* @author Ben Alex
* @version $Id$
*/
@@ -34,13 +33,11 @@ public interface LookupStrategy {
* Perform database-specific optimized lookup.
*
* @param objects the identities to lookup (required)
* @param sids the SIDs for which identities are required (may be <tt>null</tt> - implementations may elect not
* @param sids the SIDs for which identities are required (may be <code>null</code> - implementations may elect not
* to provide SID optimisations)
*
* @return a <tt>Map</tt> where keys represent the {@link ObjectIdentity} of the located {@link Acl} and values
* are the located {@link Acl} (never <tt>null</tt> although some entries may be missing; this method
* should not throw {@link NotFoundException}, as a chain of {@link LookupStrategy}s may be used
* to automatically create entries if required)
* @return the <code>Map</code> pursuant to the interface contract for {@link
* org.springframework.security.acls.AclService#readAclsById(ObjectIdentity[], Sid[])}
*/
Map readAclsById(ObjectIdentity[] objects, Sid[] sids);
}
@@ -18,15 +18,15 @@ import java.io.Serializable;
/**
* Represents the identity of an individual domain object instance.
* Interface representing the identity of an individual domain object instance.
*
* <p>
* As implementations of <tt>ObjectIdentity</tt> are used as the key to represent
* domain objects in the ACL subsystem, it is essential that implementations provide
* methods so that object-equality rather than reference-equality can be relied upon
* reliably. In other words, the ACL subsystem can consider two
* <tt>ObjectIdentity</tt>s equal if <tt>identity1.equals(identity2)</tt>, rather than
* reference-equality of <tt>identity1==identity2</tt>.
* <P>
* As implementations are used as the key for caching and lookup, it is
* essential that implementations provide methods so that object-equality
* rather than reference-equality can be relied upon by caches. In other
* words, a cache can consider two <code>ObjectIdentity</code>s equal if
* <code>identity1.equals(identity2)</code>, rather than reference-equality of
* <code>identity1==identity2</code>.
* </p>
*
* @author Ben Alex
@@ -36,37 +36,35 @@ public interface ObjectIdentity extends Serializable {
//~ Methods ========================================================================================================
/**
* Refer to the <code>java.lang.Object</code> documentation for the interface contract.
*
* @param obj to be compared
*
* @return <tt>true</tt> if the objects are equal, <tt>false</tt> otherwise
* @see Object#equals(Object)
* @return <code>true</code> if the objects are equal, <code>false</code> otherwise
*/
boolean equals(Object obj);
/**
* Obtains the actual identifier. This identifier must not be reused to represent other domain objects with
* the same <tt>javaType</tt>.
*
* <p>Because ACLs are largely immutable, it is strongly recommended to use
* the same <code>javaType</code>.<p>Because ACLs are largely immutable, it is strongly recommended to use
* a synthetic identifier (such as a database sequence number for the primary key). Do not use an identifier with
* business meaning, as that business meaning may change in the future such change will cascade to the ACL
* subsystem data.</p>
* business meaning, as that business meaning may change.</p>
*
* @return the identifier (unique within this <tt>javaType</tt>; never <tt>null</tt>)
* @return the identifier (unique within this <code>javaType</code>
*/
Serializable getIdentifier();
/**
* Obtains the Java type represented by the domain object. The Java type can be an interface or a class, but is
* most often the domain object implementation class.
* Obtains the Java type represented by the domain object.
*
* @return the Java type of the domain object (never <tt>null</tt>)
* @return the Java type of the domain object
*/
Class getJavaType();
/**
* @return a hash code representation of the <tt>ObjectIdentity</tt>
* @see Object#hashCode()
* Refer to the <code>java.lang.Object</code> documentation for the interface contract.
*
* @return a hash code representation of this object
*/
int hashCode();
}
@@ -17,7 +17,6 @@ package org.springframework.security.acls.objectidentity;
import org.springframework.security.acls.IdentityUnavailableException;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.ReflectionUtils;
import java.io.Serializable;
@@ -76,7 +75,7 @@ public class ObjectIdentityImpl implements ObjectIdentity {
public ObjectIdentityImpl(Object object) throws IdentityUnavailableException {
Assert.notNull(object, "object cannot be null");
this.javaType = ClassUtils.getUserClass(object.getClass());
this.javaType = object.getClass();
Object result;
@@ -144,7 +143,7 @@ public class ObjectIdentityImpl implements ObjectIdentity {
public String toString() {
StringBuffer sb = new StringBuffer();
sb.append(this.getClass().getName()).append("[");
sb.append("Java Type: ").append(this.javaType.getName());
sb.append("Java Type: ").append(this.javaType);
sb.append("; Identifier: ").append(this.identifier).append("]");
return sb.toString();
@@ -14,8 +14,6 @@
*/
package org.springframework.security.acls.sid;
import java.io.Serializable;
/**
* A security identity recognised by the ACL system.
*
@@ -31,7 +29,7 @@ import java.io.Serializable;
* @author Ben Alex
* @version $Id$
*/
public interface Sid extends Serializable {
public interface Sid {
//~ Methods ========================================================================================================
/**
@@ -63,7 +63,7 @@
</bean>
<bean id="dataSource" class="org.springframework.security.TestDataSource">
<constructor-arg value="acltest" />
<constructor-arg value="test" />
</bean>
</beans>
@@ -86,6 +86,8 @@ public class AccessControlEntryTests extends TestCase {
BasePermission.ADMINISTRATION, true, true, true)));
Assert.assertFalse(ace.equals(new AccessControlEntryImpl(new Long(2), mockAcl, sid,
BasePermission.ADMINISTRATION, true, true, true)));
Assert.assertFalse(ace.equals(new AccessControlEntryImpl(new Long(1), new MockAcl(), sid,
BasePermission.ADMINISTRATION, true, true, true)));
Assert.assertFalse(ace.equals(new AccessControlEntryImpl(new Long(1), mockAcl, new PrincipalSid("scott"),
BasePermission.ADMINISTRATION, true, true, true)));
Assert.assertFalse(ace.equals(new AccessControlEntryImpl(new Long(1), mockAcl, sid, BasePermission.WRITE, true,
@@ -138,14 +138,14 @@ public class AclImplTests extends TestCase {
MutableAcl acl = new AclImpl(identity, new Long(1), strategy, auditLogger, null, null, true, new PrincipalSid(
"johndoe"));
try {
acl.insertAce(0, null, new GrantedAuthoritySid("ROLE_IGNORED"), true);
acl.insertAce(new Long(1), null, new GrantedAuthoritySid("ROLE_IGNORED"), true);
fail("It should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException expected) {
assertTrue(true);
}
try {
acl.insertAce(0, BasePermission.READ, null, true);
acl.insertAce(new Long(1), BasePermission.READ, null, true);
fail("It should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException expected) {
@@ -168,7 +168,7 @@ public class AclImplTests extends TestCase {
MockAclService service = new MockAclService();
// Insert one permission
acl.insertAce(0, BasePermission.READ, new GrantedAuthoritySid("ROLE_TEST1"), true);
acl.insertAce(null, BasePermission.READ, new GrantedAuthoritySid("ROLE_TEST1"), true);
service.updateAcl(acl);
// Check it was successfully added
assertEquals(1, acl.getEntries().length);
@@ -177,7 +177,7 @@ public class AclImplTests extends TestCase {
assertEquals(acl.getEntries()[0].getSid(), new GrantedAuthoritySid("ROLE_TEST1"));
// Add a second permission
acl.insertAce(1, BasePermission.READ, new GrantedAuthoritySid("ROLE_TEST2"), true);
acl.insertAce(null, BasePermission.READ, new GrantedAuthoritySid("ROLE_TEST2"), true);
service.updateAcl(acl);
// Check it was added on the last position
assertEquals(2, acl.getEntries().length);
@@ -186,7 +186,7 @@ public class AclImplTests extends TestCase {
assertEquals(acl.getEntries()[1].getSid(), new GrantedAuthoritySid("ROLE_TEST2"));
// Add a third permission, after the first one
acl.insertAce(1, BasePermission.WRITE, new GrantedAuthoritySid("ROLE_TEST3"), false);
acl.insertAce(acl.getEntries()[0].getId(), BasePermission.WRITE, new GrantedAuthoritySid("ROLE_TEST3"), false);
service.updateAcl(acl);
assertEquals(3, acl.getEntries().length);
// Check the third entry was added between the two existent ones
@@ -213,11 +213,11 @@ public class AclImplTests extends TestCase {
MockAclService service = new MockAclService();
// Insert one permission
acl.insertAce(0, BasePermission.READ, new GrantedAuthoritySid("ROLE_TEST1"), true);
acl.insertAce(null, BasePermission.READ, new GrantedAuthoritySid("ROLE_TEST1"), true);
service.updateAcl(acl);
try {
acl.insertAce(55, BasePermission.READ, new GrantedAuthoritySid("ROLE_TEST2"), true);
acl.insertAce(new Long(5), BasePermission.READ, new GrantedAuthoritySid("ROLE_TEST2"), true);
fail("It should have thrown NotFoundException");
}
catch (NotFoundException expected) {
@@ -240,28 +240,28 @@ public class AclImplTests extends TestCase {
MockAclService service = new MockAclService();
// Add several permissions
acl.insertAce(0, BasePermission.READ, new GrantedAuthoritySid("ROLE_TEST1"), true);
acl.insertAce(1, BasePermission.READ, new GrantedAuthoritySid("ROLE_TEST2"), true);
acl.insertAce(2, BasePermission.READ, new GrantedAuthoritySid("ROLE_TEST3"), true);
acl.insertAce(null, BasePermission.READ, new GrantedAuthoritySid("ROLE_TEST1"), true);
acl.insertAce(null, BasePermission.READ, new GrantedAuthoritySid("ROLE_TEST2"), true);
acl.insertAce(null, BasePermission.READ, new GrantedAuthoritySid("ROLE_TEST3"), true);
service.updateAcl(acl);
// Delete first permission and check the order of the remaining permissions is kept
acl.deleteAce(0);
acl.deleteAce(new Long(1));
assertEquals(2, acl.getEntries().length);
assertEquals(acl.getEntries()[0].getSid(), new GrantedAuthoritySid("ROLE_TEST2"));
assertEquals(acl.getEntries()[1].getSid(), new GrantedAuthoritySid("ROLE_TEST3"));
// Add one more permission and remove the permission in the middle
acl.insertAce(2, BasePermission.READ, new GrantedAuthoritySid("ROLE_TEST4"), true);
acl.insertAce(null, BasePermission.READ, new GrantedAuthoritySid("ROLE_TEST4"), true);
service.updateAcl(acl);
acl.deleteAce(1);
acl.deleteAce(new Long(2));
assertEquals(2, acl.getEntries().length);
assertEquals(acl.getEntries()[0].getSid(), new GrantedAuthoritySid("ROLE_TEST2"));
assertEquals(acl.getEntries()[1].getSid(), new GrantedAuthoritySid("ROLE_TEST4"));
// Remove remaining permissions
acl.deleteAce(1);
acl.deleteAce(0);
acl.deleteAce(new Long(1));
acl.deleteAce(new Long(3));
assertEquals(0, acl.getEntries().length);
}
@@ -278,7 +278,7 @@ public class AclImplTests extends TestCase {
MutableAcl acl = new AclImpl(identity, new Long(1), strategy, auditLogger, null, null, true, new PrincipalSid(
"johndoe"));
try {
acl.deleteAce(99);
acl.deleteAce(new Long(1));
fail("It should have thrown NotFoundException");
}
catch (NotFoundException expected) {
@@ -327,10 +327,10 @@ public class AclImplTests extends TestCase {
"johndoe"));
// Grant some permissions
rootAcl.insertAce(0, BasePermission.READ, new PrincipalSid("ben"), false);
rootAcl.insertAce(1, BasePermission.WRITE, new PrincipalSid("scott"), true);
rootAcl.insertAce(2, BasePermission.WRITE, new PrincipalSid("rod"), false);
rootAcl.insertAce(3, BasePermission.WRITE, new GrantedAuthoritySid("WRITE_ACCESS_ROLE"), true);
rootAcl.insertAce(null, BasePermission.READ, new PrincipalSid("ben"), false);
rootAcl.insertAce(null, BasePermission.WRITE, new PrincipalSid("scott"), true);
rootAcl.insertAce(null, BasePermission.WRITE, new PrincipalSid("rod"), false);
rootAcl.insertAce(null, BasePermission.WRITE, new GrantedAuthoritySid("WRITE_ACCESS_ROLE"), true);
// Check permissions granting
Permission[] permissions = new Permission[] { BasePermission.READ, BasePermission.CREATE };
@@ -394,14 +394,14 @@ public class AclImplTests extends TestCase {
parentAcl1.setParent(grandParentAcl);
// Add some permissions
grandParentAcl.insertAce(0, BasePermission.READ, new GrantedAuthoritySid("ROLE_USER_READ"), true);
grandParentAcl.insertAce(1, BasePermission.WRITE, new PrincipalSid("ben"), true);
grandParentAcl.insertAce(2, BasePermission.DELETE, new PrincipalSid("ben"), false);
grandParentAcl.insertAce(3, BasePermission.DELETE, new PrincipalSid("scott"), true);
parentAcl1.insertAce(0, BasePermission.READ, new PrincipalSid("scott"), true);
parentAcl1.insertAce(1, BasePermission.DELETE, new PrincipalSid("scott"), false);
parentAcl2.insertAce(0, BasePermission.CREATE, new PrincipalSid("ben"), true);
childAcl1.insertAce(0, BasePermission.CREATE, new PrincipalSid("scott"), true);
grandParentAcl.insertAce(null, BasePermission.READ, new GrantedAuthoritySid("ROLE_USER_READ"), true);
grandParentAcl.insertAce(null, BasePermission.WRITE, new PrincipalSid("ben"), true);
grandParentAcl.insertAce(null, BasePermission.DELETE, new PrincipalSid("ben"), false);
grandParentAcl.insertAce(null, BasePermission.DELETE, new PrincipalSid("scott"), true);
parentAcl1.insertAce(null, BasePermission.READ, new PrincipalSid("scott"), true);
parentAcl1.insertAce(null, BasePermission.DELETE, new PrincipalSid("scott"), false);
parentAcl2.insertAce(null, BasePermission.CREATE, new PrincipalSid("ben"), true);
childAcl1.insertAce(null, BasePermission.CREATE, new PrincipalSid("scott"), true);
// Check granting process for parent1
assertTrue(parentAcl1.isGranted(new Permission[] { BasePermission.READ }, new Sid[] { new PrincipalSid("scott") },
@@ -464,9 +464,9 @@ public class AclImplTests extends TestCase {
"johndoe"));
MockAclService service = new MockAclService();
acl.insertAce(0, BasePermission.READ, new GrantedAuthoritySid("ROLE_USER_READ"), true);
acl.insertAce(1, BasePermission.WRITE, new GrantedAuthoritySid("ROLE_USER_READ"), true);
acl.insertAce(2, BasePermission.CREATE, new PrincipalSid("ben"), true);
acl.insertAce(null, BasePermission.READ, new GrantedAuthoritySid("ROLE_USER_READ"), true);
acl.insertAce(null, BasePermission.WRITE, new GrantedAuthoritySid("ROLE_USER_READ"), true);
acl.insertAce(null, BasePermission.CREATE, new PrincipalSid("ben"), true);
service.updateAcl(acl);
assertEquals(acl.getEntries()[0].getPermission(), BasePermission.READ);
@@ -474,9 +474,9 @@ public class AclImplTests extends TestCase {
assertEquals(acl.getEntries()[2].getPermission(), BasePermission.CREATE);
// Change each permission
acl.updateAce(0, BasePermission.CREATE);
acl.updateAce(1, BasePermission.DELETE);
acl.updateAce(2, BasePermission.READ);
acl.updateAce(new Long(1), BasePermission.CREATE);
acl.updateAce(new Long(2), BasePermission.DELETE);
acl.updateAce(new Long(3), BasePermission.READ);
// Check the change was successfuly made
assertEquals(acl.getEntries()[0].getPermission(), BasePermission.CREATE);
@@ -498,8 +498,8 @@ public class AclImplTests extends TestCase {
"johndoe"));
MockAclService service = new MockAclService();
acl.insertAce(0, BasePermission.READ, new GrantedAuthoritySid("ROLE_USER_READ"), true);
acl.insertAce(1, BasePermission.WRITE, new GrantedAuthoritySid("ROLE_USER_READ"), true);
acl.insertAce(null, BasePermission.READ, new GrantedAuthoritySid("ROLE_USER_READ"), true);
acl.insertAce(null, BasePermission.WRITE, new GrantedAuthoritySid("ROLE_USER_READ"), true);
service.updateAcl(acl);
assertFalse(((AuditableAccessControlEntry) acl.getEntries()[0]).isAuditFailure());
@@ -508,8 +508,8 @@ public class AclImplTests extends TestCase {
assertFalse(((AuditableAccessControlEntry) acl.getEntries()[1]).isAuditSuccess());
// Change each permission
((AuditableAcl) acl).updateAuditing(0, true, true);
((AuditableAcl) acl).updateAuditing(1, true, true);
((AuditableAcl) acl).updateAuditing(new Long(1), true, true);
((AuditableAcl) acl).updateAuditing(new Long(2), true, true);
// Check the change was successfuly made
assertTrue(((AuditableAccessControlEntry) acl.getEntries()[0]).isAuditFailure());
@@ -534,8 +534,8 @@ public class AclImplTests extends TestCase {
MutableAcl parentAcl = new AclImpl(identity2, new Long(2), strategy, auditLogger, null, null, true, new PrincipalSid(
"johndoe"));
MockAclService service = new MockAclService();
acl.insertAce(0, BasePermission.READ, new GrantedAuthoritySid("ROLE_USER_READ"), true);
acl.insertAce(1, BasePermission.WRITE, new GrantedAuthoritySid("ROLE_USER_READ"), true);
acl.insertAce(null, BasePermission.READ, new GrantedAuthoritySid("ROLE_USER_READ"), true);
acl.insertAce(null, BasePermission.WRITE, new GrantedAuthoritySid("ROLE_USER_READ"), true);
service.updateAcl(acl);
assertEquals(acl.getId(), new Long(1));
@@ -114,7 +114,7 @@ public class AclImplementationSecurityCheckTests extends TestCase {
// Let's give the principal the ADMINISTRATION permission, without
// granting access
MutableAcl aclFirstDeny = new AclImpl(identity, new Long(1), aclAuthorizationStrategy, new ConsoleAuditLogger());
aclFirstDeny.insertAce(0, BasePermission.ADMINISTRATION, new PrincipalSid(auth), false);
aclFirstDeny.insertAce(null, BasePermission.ADMINISTRATION, new PrincipalSid(auth), false);
// The CHANGE_GENERAL test should pass as the principal has ROLE_GENERAL
try {
@@ -143,7 +143,7 @@ public class AclImplementationSecurityCheckTests extends TestCase {
}
// Add granting access to this principal
aclFirstDeny.insertAce(1, BasePermission.ADMINISTRATION, new PrincipalSid(auth), true);
aclFirstDeny.insertAce(null, BasePermission.ADMINISTRATION, new PrincipalSid(auth), true);
// and try again for CHANGE_AUDITING - the first ACE's granting flag
// (false) will deny this access
try {
@@ -158,7 +158,7 @@ public class AclImplementationSecurityCheckTests extends TestCase {
// permission, with granting access
MutableAcl aclFirstAllow = new AclImpl(identity, new Long(1), aclAuthorizationStrategy,
new ConsoleAuditLogger());
aclFirstAllow.insertAce(0, BasePermission.ADMINISTRATION, new PrincipalSid(auth), true);
aclFirstAllow.insertAce(null, BasePermission.ADMINISTRATION, new PrincipalSid(auth), true);
// The CHANGE_AUDITING test should pass as there is one ACE with
// granting access
@@ -171,7 +171,7 @@ public class AclImplementationSecurityCheckTests extends TestCase {
}
// Add a deny ACE and test again for CHANGE_AUDITING
aclFirstAllow.insertAce(1, BasePermission.ADMINISTRATION, new PrincipalSid(auth), false);
aclFirstAllow.insertAce(null, BasePermission.ADMINISTRATION, new PrincipalSid(auth), false);
try {
aclAuthorizationStrategy.securityCheck(aclFirstAllow, AclAuthorizationStrategy.CHANGE_AUDITING);
Assert.assertTrue(true);
@@ -215,8 +215,8 @@ public class AclImplementationSecurityCheckTests extends TestCase {
// Let's give the principal an ADMINISTRATION permission, with granting
// access
MutableAcl parentAcl = new AclImpl(identity, new Long(1), aclAuthorizationStrategy, new ConsoleAuditLogger());
parentAcl.insertAce(0, BasePermission.ADMINISTRATION, new PrincipalSid(auth), true);
MutableAcl childAcl = new AclImpl(identity, new Long(2), aclAuthorizationStrategy, new ConsoleAuditLogger());
parentAcl.insertAce(null, BasePermission.ADMINISTRATION, new PrincipalSid(auth), true);
MutableAcl childAcl = new AclImpl(identity, new Long(1), aclAuthorizationStrategy, new ConsoleAuditLogger());
// Check against the 'child' acl, which doesn't offer any authorization
// rights on CHANGE_OWNERSHIP
@@ -244,7 +244,7 @@ public class AclImplementationSecurityCheckTests extends TestCase {
MutableAcl rootParentAcl = new AclImpl(identity, new Long(1), aclAuthorizationStrategy,
new ConsoleAuditLogger());
parentAcl = new AclImpl(identity, new Long(1), aclAuthorizationStrategy, new ConsoleAuditLogger());
rootParentAcl.insertAce(0, BasePermission.ADMINISTRATION, new PrincipalSid(auth), true);
rootParentAcl.insertAce(null, BasePermission.ADMINISTRATION, new PrincipalSid(auth), true);
parentAcl.setEntriesInheriting(true);
parentAcl.setParent(rootParentAcl);
childAcl.setParent(parentAcl);
@@ -1,209 +0,0 @@
package org.springframework.security.acls.jdbc;
import java.io.IOException;
import junit.framework.TestCase;
import net.sf.ehcache.CacheManager;
import net.sf.ehcache.Ehcache;
import org.springframework.cache.ehcache.EhCacheFactoryBean;
import org.springframework.cache.ehcache.EhCacheManagerFactoryBean;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import org.springframework.security.Authentication;
import org.springframework.security.GrantedAuthority;
import org.springframework.security.GrantedAuthorityImpl;
import org.springframework.security.acls.MutableAcl;
import org.springframework.security.acls.domain.AclAuthorizationStrategyImpl;
import org.springframework.security.acls.domain.AclImpl;
import org.springframework.security.acls.domain.BasePermission;
import org.springframework.security.acls.domain.ConsoleAuditLogger;
import org.springframework.security.acls.objectidentity.ObjectIdentityImpl;
import org.springframework.security.acls.sid.GrantedAuthoritySid;
import org.springframework.security.acls.sid.PrincipalSid;
import org.springframework.security.context.SecurityContextHolder;
import org.springframework.security.providers.UsernamePasswordAuthenticationToken;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;
public class AclPermissionInheritanceTests extends TestCase {
private JdbcMutableAclService aclService;
private JdbcTemplate jdbcTemplate;
private DriverManagerDataSource dataSource;
private DataSourceTransactionManager txManager;
private TransactionStatus txStatus;
protected void setUp() throws Exception {
dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName("org.hsqldb.jdbcDriver");
dataSource.setUrl("jdbc:hsqldb:mem:permissiontest");
dataSource.setUsername("sa");
dataSource.setPassword("");
jdbcTemplate = new JdbcTemplate(dataSource);
txManager = new DataSourceTransactionManager();
txManager.setDataSource(dataSource);
txStatus = txManager.getTransaction(new DefaultTransactionDefinition());
aclService = createAclService(dataSource);
Authentication auth = new UsernamePasswordAuthenticationToken(
"system", "secret", new GrantedAuthority[] {new GrantedAuthorityImpl("ROLE_IGNORED")});
SecurityContextHolder.getContext().setAuthentication(auth);
}
protected void tearDown() throws Exception {
txManager.rollback(txStatus);
SecurityContextHolder.clearContext();
}
public void test1() throws Exception {
createAclSchema(jdbcTemplate);
ObjectIdentityImpl rootObject =
new ObjectIdentityImpl(TestDomainObject.class, new Long(1));
MutableAcl parent = aclService.createAcl(rootObject);
MutableAcl child = aclService.createAcl(new ObjectIdentityImpl(TestDomainObject.class, new Long(2)));
child.setParent(parent);
aclService.updateAcl(child);
parent = (AclImpl) aclService.readAclById(rootObject);
parent.insertAce(0, BasePermission.READ,
new PrincipalSid("john"), true);
aclService.updateAcl(parent);
parent = (AclImpl) aclService.readAclById(rootObject);
parent.insertAce(1, BasePermission.READ,
new PrincipalSid("joe"), true);
aclService.updateAcl(parent);
child = (MutableAcl) aclService.readAclById(
new ObjectIdentityImpl(TestDomainObject.class, new Long(2)));
parent = (MutableAcl) child.getParentAcl();
assertEquals("Fails because child has a stale reference to its parent",
2, parent.getEntries().length);
assertEquals(1, parent.getEntries()[0].getPermission().getMask());
assertEquals(new PrincipalSid("john"), parent.getEntries()[0].getSid());
assertEquals(1, parent.getEntries()[1].getPermission().getMask());
assertEquals(new PrincipalSid("joe"), parent.getEntries()[1].getSid());
}
public void test2() throws Exception {
createAclSchema(jdbcTemplate);
ObjectIdentityImpl rootObject =
new ObjectIdentityImpl(TestDomainObject.class, new Long(1));
MutableAcl parent = aclService.createAcl(rootObject);
MutableAcl child = aclService.createAcl(new ObjectIdentityImpl(TestDomainObject.class, new Long(2)));
child.setParent(parent);
aclService.updateAcl(child);
parent.insertAce(0, BasePermission.ADMINISTRATION,
new GrantedAuthoritySid("ROLE_ADMINISTRATOR"), true);
aclService.updateAcl(parent);
parent.insertAce(1, BasePermission.DELETE, new PrincipalSid("terry"), true);
aclService.updateAcl(parent);
child = (MutableAcl) aclService.readAclById(
new ObjectIdentityImpl(TestDomainObject.class, new Long(2)));
parent = (MutableAcl) child.getParentAcl();
assertEquals(2, parent.getEntries().length);
assertEquals(16, parent.getEntries()[0].getPermission().getMask());
assertEquals(new GrantedAuthoritySid("ROLE_ADMINISTRATOR"), parent.getEntries()[0].getSid());
assertEquals(8, parent.getEntries()[1].getPermission().getMask());
assertEquals(new PrincipalSid("terry"), parent.getEntries()[1].getSid());
}
private JdbcMutableAclService createAclService(DriverManagerDataSource ds)
throws IOException {
GrantedAuthorityImpl adminAuthority = new GrantedAuthorityImpl("ROLE_ADMINISTRATOR");
AclAuthorizationStrategyImpl authStrategy = new AclAuthorizationStrategyImpl(
new GrantedAuthorityImpl[]{adminAuthority,adminAuthority,adminAuthority});
EhCacheManagerFactoryBean ehCacheManagerFactoryBean = new EhCacheManagerFactoryBean();
ehCacheManagerFactoryBean.afterPropertiesSet();
CacheManager cacheManager = (CacheManager) ehCacheManagerFactoryBean.getObject();
EhCacheFactoryBean ehCacheFactoryBean = new EhCacheFactoryBean();
ehCacheFactoryBean.setCacheName("aclAche");
ehCacheFactoryBean.setCacheManager(cacheManager);
ehCacheFactoryBean.afterPropertiesSet();
Ehcache ehCache = (Ehcache) ehCacheFactoryBean.getObject();
AclCache aclAche = new EhCacheBasedAclCache(ehCache);
BasicLookupStrategy lookupStrategy =
new BasicLookupStrategy(ds, aclAche, authStrategy, new ConsoleAuditLogger());
return new JdbcMutableAclService(ds,lookupStrategy, aclAche);
}
private void createAclSchema(JdbcTemplate jdbcTemplate) {
jdbcTemplate.execute("DROP TABLE ACL_ENTRY IF EXISTS;");
jdbcTemplate.execute("DROP TABLE ACL_OBJECT_IDENTITY IF EXISTS;");
jdbcTemplate.execute("DROP TABLE ACL_CLASS IF EXISTS");
jdbcTemplate.execute("DROP TABLE ACL_SID IF EXISTS");
jdbcTemplate.execute(
"CREATE TABLE ACL_SID(" +
"ID BIGINT GENERATED BY DEFAULT AS IDENTITY(START WITH 100) NOT NULL PRIMARY KEY," +
"PRINCIPAL BOOLEAN NOT NULL," +
"SID VARCHAR_IGNORECASE(100) NOT NULL," +
"CONSTRAINT UNIQUE_UK_1 UNIQUE(SID,PRINCIPAL));");
jdbcTemplate.execute(
"CREATE TABLE ACL_CLASS(" +
"ID BIGINT GENERATED BY DEFAULT AS IDENTITY(START WITH 100) NOT NULL PRIMARY KEY," +
"CLASS VARCHAR_IGNORECASE(100) NOT NULL," +
"CONSTRAINT UNIQUE_UK_2 UNIQUE(CLASS));");
jdbcTemplate.execute(
"CREATE TABLE ACL_OBJECT_IDENTITY(" +
"ID BIGINT GENERATED BY DEFAULT AS IDENTITY(START WITH 100) NOT NULL PRIMARY KEY," +
"OBJECT_ID_CLASS BIGINT NOT NULL," +
"OBJECT_ID_IDENTITY BIGINT NOT NULL," +
"PARENT_OBJECT BIGINT," +
"OWNER_SID BIGINT," +
"ENTRIES_INHERITING BOOLEAN NOT NULL," +
"CONSTRAINT UNIQUE_UK_3 UNIQUE(OBJECT_ID_CLASS,OBJECT_ID_IDENTITY)," +
"CONSTRAINT FOREIGN_FK_1 FOREIGN KEY(PARENT_OBJECT)REFERENCES ACL_OBJECT_IDENTITY(ID)," +
"CONSTRAINT FOREIGN_FK_2 FOREIGN KEY(OBJECT_ID_CLASS)REFERENCES ACL_CLASS(ID)," +
"CONSTRAINT FOREIGN_FK_3 FOREIGN KEY(OWNER_SID)REFERENCES ACL_SID(ID));");
jdbcTemplate.execute(
"CREATE TABLE ACL_ENTRY(" +
"ID BIGINT GENERATED BY DEFAULT AS IDENTITY(START WITH 100) NOT NULL PRIMARY KEY," +
"ACL_OBJECT_IDENTITY BIGINT NOT NULL,ACE_ORDER INT NOT NULL,SID BIGINT NOT NULL," +
"MASK INTEGER NOT NULL,GRANTING BOOLEAN NOT NULL,AUDIT_SUCCESS BOOLEAN NOT NULL," +
"AUDIT_FAILURE BOOLEAN NOT NULL,CONSTRAINT UNIQUE_UK_4 UNIQUE(ACL_OBJECT_IDENTITY,ACE_ORDER)," +
"CONSTRAINT FOREIGN_FK_4 FOREIGN KEY(ACL_OBJECT_IDENTITY) REFERENCES ACL_OBJECT_IDENTITY(ID)," +
"CONSTRAINT FOREIGN_FK_5 FOREIGN KEY(SID) REFERENCES ACL_SID(ID));");
}
public static class TestDomainObject {
private Long id;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
}
}
@@ -3,9 +3,9 @@ package org.springframework.security.acls.jdbc;
import java.util.Map;
import junit.framework.Assert;
import net.sf.ehcache.Cache;
import net.sf.ehcache.CacheManager;
import net.sf.ehcache.Ehcache;
import net.sf.ehcache.CacheManager;
import net.sf.ehcache.Cache;
import org.junit.After;
import org.junit.AfterClass;
@@ -21,8 +21,6 @@ import org.springframework.security.TestDataSource;
import org.springframework.security.acls.Acl;
import org.springframework.security.acls.AuditableAccessControlEntry;
import org.springframework.security.acls.MutableAcl;
import org.springframework.security.acls.NotFoundException;
import org.springframework.security.acls.Permission;
import org.springframework.security.acls.domain.AclAuthorizationStrategy;
import org.springframework.security.acls.domain.AclAuthorizationStrategyImpl;
import org.springframework.security.acls.domain.BasePermission;
@@ -249,7 +247,7 @@ public class BasicLookupStrategyTests {
/**
* Test created from SEC-590.
*/
@Test
/* @Test
public void testReadAllObjectIdentitiesWhenLastElementIsAlreadyCached() throws Exception {
String query = "INSERT INTO acl_object_identity(ID,OBJECT_ID_CLASS,OBJECT_ID_IDENTITY,PARENT_OBJECT,OWNER_SID,ENTRIES_INHERITING) VALUES (4,2,104,null,1,1);"
+ "INSERT INTO acl_object_identity(ID,OBJECT_ID_CLASS,OBJECT_ID_IDENTITY,PARENT_OBJECT,OWNER_SID,ENTRIES_INHERITING) VALUES (5,2,105,4,1,1);"
@@ -288,7 +286,7 @@ public class BasicLookupStrategyTests {
Acl foundParent2Acl = (Acl) foundAcls.get(parent2Oid);
Assert.assertNotNull(foundParent2Acl);
Assert.assertTrue(foundParent2Acl.isGranted(checkPermission, sids, false));
}
}*/
@Test
public void testAclsWithDifferentSerializableTypesAsObjectIdentities() throws Exception {
@@ -1,25 +1,11 @@
package org.springframework.security.acls.jdbc;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import net.sf.ehcache.Cache;
import net.sf.ehcache.CacheManager;
import net.sf.ehcache.Ehcache;
import net.sf.ehcache.CacheManager;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import org.springframework.security.Authentication;
import org.springframework.security.GrantedAuthority;
import org.springframework.security.GrantedAuthorityImpl;
@@ -32,7 +18,12 @@ import org.springframework.security.acls.objectidentity.ObjectIdentity;
import org.springframework.security.acls.objectidentity.ObjectIdentityImpl;
import org.springframework.security.context.SecurityContextHolder;
import org.springframework.security.providers.TestingAuthenticationToken;
import org.springframework.security.util.FieldUtils;
import org.junit.BeforeClass;
import org.junit.AfterClass;
import org.junit.After;
import org.junit.Test;
import static org.junit.Assert.*;
/**
* Tests {@link EhCacheBasedAclCache}
@@ -47,8 +38,7 @@ public class EhCacheBasedAclCacheTests {
@BeforeClass
public static void initCacheManaer() {
cacheManager = new CacheManager();
// Use disk caching immediately (to test for serialization issue reported in SEC-527)
cacheManager.addCache(new Cache("ehcachebasedacltests", 0, true, false, 30, 30));
cacheManager.addCache(new Cache("ehcachebasedacltests", 500, false, false, 30, 30));
}
@AfterClass
@@ -125,36 +115,6 @@ public class EhCacheBasedAclCacheTests {
assertTrue(true);
}
}
// SEC-527
@Test
public void testDiskSerializationOfMutableAclObjectInstance() throws Exception {
ObjectIdentity identity = new ObjectIdentityImpl("org.springframework.security.TargetObject", new Long(100));
AclAuthorizationStrategy aclAuthorizationStrategy = new AclAuthorizationStrategyImpl(new GrantedAuthority[] {
new GrantedAuthorityImpl("ROLE_OWNERSHIP"), new GrantedAuthorityImpl("ROLE_AUDITING"),
new GrantedAuthorityImpl("ROLE_GENERAL") });
MutableAcl acl = new AclImpl(identity, new Long(1), aclAuthorizationStrategy, new ConsoleAuditLogger());
// Serialization test
File file = File.createTempFile("SEC_TEST", ".object");
FileOutputStream fos = new FileOutputStream(file);
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(acl);
oos.close();
FileInputStream fis = new FileInputStream(file);
ObjectInputStream ois = new ObjectInputStream(fis);
MutableAcl retrieved = (MutableAcl) ois.readObject();
ois.close();
assertEquals(acl, retrieved);
Object retrieved1 = FieldUtils.getProtectedFieldValue("aclAuthorizationStrategy", retrieved);
assertEquals(null, retrieved1);
Object retrieved2 = FieldUtils.getProtectedFieldValue("auditLogger", retrieved);
assertEquals(null, retrieved2);
}
@Test
public void cacheOperationsAclWithoutParent() throws Exception {
@@ -167,13 +127,9 @@ public class EhCacheBasedAclCacheTests {
new GrantedAuthorityImpl("ROLE_GENERAL") });
MutableAcl acl = new AclImpl(identity, new Long(1), aclAuthorizationStrategy, new ConsoleAuditLogger());
assertEquals(0, cache.getDiskStoreSize());
myCache.putInCache(acl);
assertEquals(cache.getSize(), 2);
assertEquals(2, cache.getDiskStoreSize());
assertTrue(cache.isElementOnDisk(acl.getObjectIdentity()));
assertFalse(cache.isElementInMemory(acl.getObjectIdentity()));
// Check we can get from cache the same objects we put in
assertEquals(myCache.getFromCache(new Long(1)), acl);
assertEquals(myCache.getFromCache(identity), acl);
@@ -184,17 +140,14 @@ public class EhCacheBasedAclCacheTests {
myCache.putInCache(acl2);
assertEquals(cache.getSize(), 4);
assertEquals(4, cache.getDiskStoreSize());
// Try to evict an entry that doesn't exist
myCache.evictFromCache(new Long(3));
myCache.evictFromCache(new ObjectIdentityImpl("org.springframework.security.TargetObject", new Long(102)));
assertEquals(cache.getSize(), 4);
assertEquals(4, cache.getDiskStoreSize());
myCache.evictFromCache(new Long(1));
assertEquals(cache.getSize(), 2);
assertEquals(2, cache.getDiskStoreSize());
// Check the second object inserted
assertEquals(myCache.getFromCache(new Long(2)), acl2);
@@ -224,12 +177,8 @@ public class EhCacheBasedAclCacheTests {
acl.setParent(parentAcl);
assertEquals(0, cache.getDiskStoreSize());
myCache.putInCache(acl);
assertEquals(cache.getSize(), 4);
assertEquals(4, cache.getDiskStoreSize());
assertTrue(cache.isElementOnDisk(acl.getObjectIdentity()));
assertFalse(cache.isElementInMemory(acl.getObjectIdentity()));
// Check we can get from cache the same objects we put in
assertEquals(myCache.getFromCache(new Long(1)), acl);
@@ -20,7 +20,6 @@ import org.springframework.security.Authentication;
import org.springframework.security.GrantedAuthority;
import org.springframework.security.GrantedAuthorityImpl;
import org.springframework.security.acls.AccessControlEntry;
import org.springframework.security.acls.Acl;
import org.springframework.security.acls.AlreadyExistsException;
import org.springframework.security.acls.ChildrenExistException;
import org.springframework.security.acls.MutableAcl;
@@ -108,10 +107,10 @@ public class JdbcAclServiceTests extends AbstractTransactionalDataSourceSpringCo
child.setParent(middleParent);
// Now let's add a couple of permissions
topParent.insertAce(0, BasePermission.READ, new PrincipalSid(auth), true);
topParent.insertAce(1, BasePermission.WRITE, new PrincipalSid(auth), false);
middleParent.insertAce(0, BasePermission.DELETE, new PrincipalSid(auth), true);
child.insertAce(0, BasePermission.DELETE, new PrincipalSid(auth), false);
topParent.insertAce(null, BasePermission.READ, new PrincipalSid(auth), true);
topParent.insertAce(null, BasePermission.WRITE, new PrincipalSid(auth), false);
middleParent.insertAce(null, BasePermission.DELETE, new PrincipalSid(auth), true);
child.insertAce(null, BasePermission.DELETE, new PrincipalSid(auth), false);
// Explictly save the changed ACL
jdbcMutableAclService.updateAcl(topParent);
@@ -144,8 +143,10 @@ public class JdbcAclServiceTests extends AbstractTransactionalDataSourceSpringCo
// Check the retrieved rights are correct
assertTrue(topParent.isGranted(new Permission[] {BasePermission.READ}, new Sid[] {new PrincipalSid(auth)}, false));
assertFalse(topParent.isGranted(new Permission[] {BasePermission.WRITE}, new Sid[] {new PrincipalSid(auth)}, false));
assertTrue(middleParent.isGranted(new Permission[] {BasePermission.DELETE}, new Sid[] {new PrincipalSid(auth)}, false));
assertFalse(topParent.isGranted(new Permission[] {BasePermission.WRITE}, new Sid[] {new PrincipalSid(auth)},
false));
assertTrue(middleParent.isGranted(new Permission[] {BasePermission.DELETE}, new Sid[] {new PrincipalSid(auth)},
false));
assertFalse(child.isGranted(new Permission[] {BasePermission.DELETE}, new Sid[] {new PrincipalSid(auth)}, false));
try {
@@ -184,10 +185,10 @@ public class JdbcAclServiceTests extends AbstractTransactionalDataSourceSpringCo
}
// Let's add an identical permission to the child, but it'll appear AFTER the current permission, so has no impact
child.insertAce(1, BasePermission.DELETE, new PrincipalSid(auth), true);
child.insertAce(null, BasePermission.DELETE, new PrincipalSid(auth), true);
// Let's also add another permission to the child
child.insertAce(2, BasePermission.CREATE, new PrincipalSid(auth), true);
child.insertAce(null, BasePermission.CREATE, new PrincipalSid(auth), true);
// Save the changed child
jdbcMutableAclService.updateAcl(child);
@@ -211,7 +212,7 @@ public class JdbcAclServiceTests extends AbstractTransactionalDataSourceSpringCo
assertNotNull(entry.getId());
// Now delete that first ACE
child.deleteAce(0);
child.deleteAce(entry.getId());
// Save and check it worked
child = jdbcMutableAclService.updateAcl(child);
@@ -224,15 +225,11 @@ public class JdbcAclServiceTests extends AbstractTransactionalDataSourceSpringCo
/**
* Test method that demonstrates eviction failure from cache - SEC-676
*/
public void testDeleteAclAlsoDeletesChildren() throws Exception {
/* public void testDeleteAclAlsoDeletesChildren() throws Exception {
ObjectIdentity topParentOid = new ObjectIdentityImpl("org.springframework.security.TargetObject", new Long(100));
ObjectIdentity middleParentOid = new ObjectIdentityImpl("org.springframework.security.TargetObject", new Long(101));
ObjectIdentity childOid = new ObjectIdentityImpl("org.springframework.security.TargetObject", new Long(102));
// Check the childOid really is a child of middleParentOid
Acl childAcl = jdbcMutableAclService.readAclById(childOid);
assertEquals(middleParentOid, childAcl.getParentAcl().getObjectIdentity());
// Delete the mid-parent and test if the child was deleted, as well
jdbcMutableAclService.deleteAcl(middleParentOid, true);
@@ -254,7 +251,7 @@ public class JdbcAclServiceTests extends AbstractTransactionalDataSourceSpringCo
Acl acl = jdbcMutableAclService.readAclById(topParentOid);
assertNotNull(acl);
assertEquals(((MutableAcl) acl).getObjectIdentity(), topParentOid);
}
}*/
public void testConstructorRejectsNullParameters() throws Exception {
try {
@@ -318,17 +315,29 @@ public class JdbcAclServiceTests extends AbstractTransactionalDataSourceSpringCo
public void testDeleteAclWithChildrenThrowsException() throws Exception {
try {
ObjectIdentity topParentOid = new ObjectIdentityImpl("org.springframework.security.TargetObject", new Long(100));
jdbcMutableAclService.setForeignKeysInDatabase(false); // switch on FK checking in the class, not database
jdbcMutableAclService.deleteAcl(topParentOid, false);
fail("It should have thrown ChildrenExistException");
}
catch (ChildrenExistException expected) {
assertTrue(true);
} finally {
jdbcMutableAclService.setForeignKeysInDatabase(true); // restore to the default
}
}
public void testDeleteAllAclsRemovesAclClassRecord() throws Exception {
Authentication auth = new TestingAuthenticationToken("ben", "ignored",
new GrantedAuthority[] {new GrantedAuthorityImpl("ROLE_ADMINISTRATOR")});
auth.setAuthenticated(true);
SecurityContextHolder.getContext().setAuthentication(auth);
ObjectIdentity topParentOid = new ObjectIdentityImpl("org.springframework.security.TargetObject", new Long(100));
// Remove all acls associated with a certain class type
jdbcMutableAclService.deleteAcl(topParentOid, true);
// Check the acl_class table is empty
assertEquals(0, getJdbcTemplate().queryForList(SELECT_ALL_CLASSES, new Object[] {"org.springframework.security.TargetObject"} ).size());
}
public void testDeleteAclRemovesRowsFromDatabase() throws Exception {
Authentication auth = new TestingAuthenticationToken("ben", "ignored",
new GrantedAuthority[] {new GrantedAuthorityImpl("ROLE_ADMINISTRATOR")});
+1 -1
View File
@@ -3,7 +3,7 @@
<parent>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-adapters</artifactId>
<version>2.0.2</version>
<version>2.0.0-RC1</version>
</parent>
<artifactId>spring-security-catalina</artifactId>
<name>Spring Security - Catalina adapter</name>
+6 -4
View File
@@ -3,7 +3,7 @@
<parent>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-adapters</artifactId>
<version>2.0.2</version>
<version>2.0.0-RC1</version>
</parent>
<artifactId>spring-security-jboss</artifactId>
<name>Spring Security - JBoss adapter</name>
@@ -14,9 +14,11 @@
<version>3.2.3</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
</dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.3</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
@@ -57,7 +57,7 @@ import javax.security.auth.login.LoginException;
* which is subsequently available from <code>java:comp/env/security/subject</code>.</p>
*
* @author Ben Alex
* @author Sergio Bern
* @author Sergio Bern
* @version $Id:JbossSpringSecurityLoginModule.java 2151 2007-09-22 11:54:13Z luke_t $
*/
public class JbossSpringSecurityLoginModule extends AbstractServerLoginModule {
+1 -1
View File
@@ -3,7 +3,7 @@
<parent>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-adapters</artifactId>
<version>2.0.2</version>
<version>2.0.0-RC1</version>
</parent>
<artifactId>spring-security-jetty</artifactId>
<name>Spring Security - Jetty adapter</name>
+1 -1
View File
@@ -3,7 +3,7 @@
<parent>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-parent</artifactId>
<version>2.0.2</version>
<version>2.0.0-RC1</version>
</parent>
<artifactId>spring-security-adapters</artifactId>
<name>Spring Security - Adapters</name>
+6 -4
View File
@@ -3,7 +3,7 @@
<parent>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-adapters</artifactId>
<version>2.0.2</version>
<version>2.0.0-RC1</version>
</parent>
<artifactId>spring-security-resin</artifactId>
<name>Spring Security - Resin adapter</name>
@@ -14,9 +14,11 @@
<version>3.0.9</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
</dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.3</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
+2 -27
View File
@@ -3,11 +3,11 @@
<parent>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-parent</artifactId>
<version>2.0.2</version>
<version>2.0.0-RC1</version>
</parent>
<artifactId>spring-security-cas-client</artifactId>
<name>Spring Security - CAS support</name>
<packaging>bundle</packaging>
<packaging>jar</packaging>
<dependencies>
<dependency>
@@ -39,29 +39,4 @@
<optional>true</optional>
</dependency>
</dependencies>
<properties>
<spring.osgi.export>
org.springframework.security.*;version=${pom.version.osgi}
</spring.osgi.export>
<spring.osgi.import>
org.springframework.security.*;version="[${pom.version.osgi},${pom.version.osgi}]",
org.springframework.beans.*;version="${spring.version.osgi}",
org.springframework.context.*;version="${spring.version.osgi}",
org.springframework.dao.*;version="${spring.version.osgi}";resolution:=optional,
org.springframework.util.*;version="${spring.version.osgi}",
javax.servlet.*;version="[2.4.0, 3.0.0)";resolution:=optional,
net.sf.ehcache.*;version="[1.4.1, 2.0.0)";resolution:=optional,
org.apache.commons.logging.*;version="[1.1.1, 2.0.0)",
org.jasig.cas.client.*;version="[3.1.1, 4.0.0)"
</spring.osgi.import>
<spring.osgi.private.pkg>
!org.springframework.security.*
</spring.osgi.private.pkg>
<spring.osgi.symbolic.name>org.springframework.security.cas</spring.osgi.symbolic.name>
</properties>
</project>
@@ -30,14 +30,12 @@ import org.springframework.util.Assert;
/**
* Used by the <code>ExceptionTranslationFilter</code> to commence authentication via the JA-SIG Central
* Authentication Service (CAS).
* <p>
* The user's browser will be redirected to the JA-SIG CAS enterprise-wide login page.
* This page is specified by the <code>loginUrl</code> property. Once login is complete, the CAS login page will
* Used by the <code>SecurityEnforcementFilter</code> to commence authentication via the JA-SIG Central
* Authentication Service (CAS).<P>The user's browser will be redirected to the JA-SIG CAS enterprise-wide login
* page. This page is specified by the <code>loginUrl</code> property. Once login is complete, the CAS login page will
* redirect to the page indicated by the <code>service</code> property. The <code>service</code> is a HTTP URL
* belonging to the current application. The <code>service</code> URL is monitored by the {@link CasProcessingFilter},
* which will validate the CAS login was successful.
* which will validate the CAS login was successful.</p>
*
* @author Ben Alex
* @author Scott Battaglia
@@ -67,8 +65,8 @@ public class CasProcessingFilterEntryPoint implements AuthenticationEntryPoint,
}
public void commence(final ServletRequest servletRequest, final ServletResponse servletResponse,
final AuthenticationException authenticationException) throws IOException, ServletException {
final AuthenticationException authenticationException)
throws IOException, ServletException {
final HttpServletResponse response = (HttpServletResponse) servletResponse;
final String urlEncodedService = CommonUtils.constructServiceUrl(null, response, this.serviceProperties.getService(), null, "ticket", this.encodeServiceUrlWithSessionId);
final String redirectUrl = CommonUtils.constructRedirectUrl(this.loginUrl, "service", urlEncodedService, this.serviceProperties.isSendRenew(), false);
@@ -21,10 +21,9 @@ import org.springframework.util.Assert;
/**
* Stores properties related to this CAS service.
* <p>
* Each web application capable of processing CAS tickets is known as a service.
* <p>Each web application capable of processing CAS tickets is known as a service.
* This class stores the properties that are relevant to the local CAS service, being the application
* that is being secured by Spring Security.
* that is being secured by Spring Security.</p>
*
* @author Ben Alex
* @version $Id$
@@ -43,8 +42,7 @@ public class ServiceProperties implements InitializingBean {
/**
* Represents the service the user is authenticating to.
* <p>
* This service is the callback URL belonging to the local Spring Security System for Spring secured application.
* <p>This service is the callback URL belonging to the local Spring Security System for Spring secured application.
* For example,
* <pre>
* https://www.mycompany.com/application/j_spring_cas_security_check
@@ -58,12 +56,10 @@ public class ServiceProperties implements InitializingBean {
/**
* Indicates whether the <code>renew</code> parameter should be sent to the CAS login URL and CAS
* validation URL.
* <p>
* If <code>true</code>, it will force CAS to authenticate the user again (even if the
* validation URL.<p>If <code>true</code>, it will force CAS to authenticate the user again (even if the
* user has previously authenticated). During ticket validation it will require the ticket was generated as a
* consequence of an explicit login. High security applications would probably set this to <code>true</code>.
* Defaults to <code>false</code>, providing automated single sign on.
* Defaults to <code>false</code>, providing automated single sign on.</p>
*
* @return whether to send the <code>renew</code> parameter to CAS
*/
+22 -31
View File
@@ -3,7 +3,7 @@
<parent>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-parent</artifactId>
<version>2.0.2</version>
<version>2.0.0-RC1</version>
</parent>
<packaging>bundle</packaging>
<artifactId>spring-security-core-tiger</artifactId>
@@ -16,14 +16,7 @@
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-core</artifactId>
<version>${project.version}</version>
<classifier>tests</classifier>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<groupId>aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<optional>true</optional>
</dependency>
@@ -68,6 +61,26 @@
<target>1.5</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<version>${felix.version}</version>
<extensions>true</extensions>
<configuration>
<instructions>
<Bundle-SymbolicName>org.springframework.security.core-tiger</Bundle-SymbolicName>
<Export-Package>org.springframework.security.*;version=${pom.version}</Export-Package>
<Private-Package>!org.springframework.security.*</Private-Package>
<Implementation-Title>${pom.name}</Implementation-Title>
<Implementation-Version>${pom.version}</Implementation-Version>
<Import-Package>
org.springframework*;resolution:=optional;version="[2.0,2.5]",
*;resolution:=optional
</Import-Package>
</instructions>
<excludeDependencies>true</excludeDependencies>
</configuration>
</plugin>
</plugins>
</build>
<reporting>
@@ -81,26 +94,4 @@
</plugin>
</plugins>
</reporting>
<properties>
<spring.osgi.export>
org.springframework.security.*;version=${pom.version}
</spring.osgi.export>
<spring.osgi.import>
javax.annotation.*;version="[1.0.0, 2.0.0)",
org.springframework.security.*;version="[${pom.version.osgi},${pom.version.osgi}]",
org.springframework.core.*;version="${spring.version.osgi}"
</spring.osgi.import>
<spring.osgi.private.pkg>
!org.springframework.security.*
</spring.osgi.private.pkg>
<spring.osgi.include.res>
src/main/resources
</spring.osgi.include.res>
<spring.osgi.symbolic.name>org.springframework.security.annotation</spring.osgi.symbolic.name>
</properties>
</project>
@@ -1,12 +1,8 @@
package org.springframework.security.config;
import static org.junit.Assert.assertEquals;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.springframework.beans.factory.parsing.BeanDefinitionParsingException;
import org.springframework.context.support.AbstractXmlApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.security.AccessDeniedException;
import org.springframework.security.AuthenticationCredentialsNotFoundException;
@@ -15,17 +11,17 @@ import org.springframework.security.GrantedAuthorityImpl;
import org.springframework.security.annotation.BusinessService;
import org.springframework.security.context.SecurityContextHolder;
import org.springframework.security.providers.UsernamePasswordAuthenticationToken;
import org.springframework.security.util.InMemoryXmlApplicationContext;
/**
* @author Ben Alex
* @version $Id$
*/
public class GlobalMethodSecurityBeanDefinitionParserTests {
private AbstractXmlApplicationContext appContext;
private ClassPathXmlApplicationContext appContext;
private BusinessService target;
@Before
public void loadContext() {
appContext = new ClassPathXmlApplicationContext("org/springframework/security/config/global-method-security.xml");
target = (BusinessService) appContext.getBean("target");
@@ -41,13 +37,11 @@ public class GlobalMethodSecurityBeanDefinitionParserTests {
@Test(expected=AuthenticationCredentialsNotFoundException.class)
public void targetShouldPreventProtectedMethodInvocationWithNoContext() {
loadContext();
target.someUserMethod1();
}
@Test
public void targetShouldAllowProtectedMethodInvocationWithCorrectRole() {
loadContext();
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken("Test", "Password",
new GrantedAuthority[] {new GrantedAuthorityImpl("ROLE_USER")});
SecurityContextHolder.getContext().setAuthentication(token);
@@ -57,38 +51,10 @@ public class GlobalMethodSecurityBeanDefinitionParserTests {
@Test(expected=AccessDeniedException.class)
public void targetShouldPreventProtectedMethodInvocationWithIncorrectRole() {
loadContext();
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken("Test", "Password",
new GrantedAuthority[] {new GrantedAuthorityImpl("ROLE_SOMEOTHERROLE")});
SecurityContextHolder.getContext().setAuthentication(token);
target.someAdminMethod();
}
@Test
public void doesntInterfereWithBeanPostProcessing() {
setContext(
"<b:bean id='myUserService' class='org.springframework.security.config.PostProcessedMockUserDetailsService'/>" +
"<global-method-security />" +
// "<http auto-config='true'/>" +
"<authentication-provider user-service-ref='myUserService'/>" +
"<b:bean id='beanPostProcessor' class='org.springframework.security.config.MockUserServiceBeanPostProcessor'/>"
);
PostProcessedMockUserDetailsService service = (PostProcessedMockUserDetailsService)appContext.getBean("myUserService");
assertEquals("Hello from the post processor!", service.getPostProcessorWasHere());
}
@Test(expected=BeanDefinitionParsingException.class)
public void duplicateElementCausesError() {
setContext(
"<global-method-security />" +
"<global-method-security />"
);
}
private void setContext(String context) {
appContext = new InMemoryXmlApplicationContext(context);
}
}
@@ -1,12 +0,0 @@
# Logging
#
# $Id: log4j.properties 2385 2007-12-20 20:53:26Z luke_t $
log4j.rootCategory=DEBUG, stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d %p %c - %m%n
log4j.category.org.springframework.security=DEBUG
+30 -54
View File
@@ -3,7 +3,7 @@
<parent>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-parent</artifactId>
<version>2.0.2</version>
<version>2.0.0-RC1</version>
</parent>
<packaging>bundle</packaging>
<artifactId>spring-security-core</artifactId>
@@ -48,7 +48,7 @@
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<groupId>aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<optional>true</optional>
</dependency>
@@ -141,58 +141,34 @@
<optional>true</optional>
</dependency>
</dependencies>
<properties>
<spring.osgi.export>
org.springframework.security.*;version=${pom.version}
</spring.osgi.export>
<spring.osgi.import>
!com.ibm.websphere.security,
javax.servlet.*;version="[2.4.0, 3.0.0)";resolution:=optional,
net.sf.ehcache.*;version="[1.4.1, 2.0.0)";resolution:=optional,
org.aopalliance.*;version="[1.0.0, 2.0.0)",
org.apache.commons.codec.*;version="[1.3.0, 2.0.0)",
org.apache.commons.collections.*;version="[3.2.0, 4.0.0)",
org.apache.commons.lang.*;version="[2.1.0, 3.0.0)",
org.apache.commons.logging.*;version="[1.1.1, 2.0.0)",
org.apache.directory.server.configuration.*;version="[1.0.2, 2.0.0)";resolution:=optional,
org.apache.directory.server.core.*;version="[1.0.2, 2.0.0)";resolution:=optional,
org.apache.directory.server.protocol.*;version="[1.0.2, 2.0.0)";resolution:=optional,
org.aspectj.*;version="[1.5.4, 2.0.0)";resolution:=optional,
org.jaxen.*;version="[1.1.1, 2.0.0)";resolution:=optional,
org.springframework.aop.*;version="${spring.version.osgi}",
org.springframework.beans.*;version="${spring.version.osgi}",
org.springframework.context.*;version="${spring.version.osgi}",
org.springframework.core.*;version="${spring.version.osgi}",
org.springframework.dao.*;version="${spring.version.osgi}";resolution:=optional,
org.springframework.jdbc.*;version="${spring.version.osgi}";resolution:=optional,
org.springframework.ldap.*;version="[1.2.1.A, 2.0.0)";resolution:=optional,
org.springframework.metadata.*;version="${spring.version.osgi}",
org.springframework.mock.*;version="${spring.version.osgi}";resolution:=optional,
org.springframework.remoting.*;version="${spring.version.osgi}";resolution:=optional,
org.springframework.util.*;version="${spring.version.osgi}",
org.springframework.web.*;version="${spring.version.osgi}";resolution:=optional,
javax.crypto.*,
javax.naming.*,
javax.rmi.*,
javax.security.*,
javax.sql.*,
javax.xml.parsers.*,
org.w3c.dom.*,
org.xml.sax.*,
*;resolution:=optional
</spring.osgi.import>
<spring.osgi.private.pkg>
!org.springframework.security.*
</spring.osgi.private.pkg>
<!--
<spring.osgi.include.res>
src/main/resources
</spring.osgi.include.res>
-->
<spring.osgi.symbolic.name>org.springframework.security.core</spring.osgi.symbolic.name>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<version>${felix.version}</version>
<extensions>true</extensions>
<configuration>
<instructions>
<Bundle-SymbolicName>org.springframework.security.core</Bundle-SymbolicName>
<Export-Package>org.springframework.security.*;version=${pom.version}</Export-Package>
<Private-Package>!org.springframework.security.*</Private-Package>
<Implementation-Title>${pom.name}</Implementation-Title>
<Implementation-Version>${pom.version}</Implementation-Version>
<Import-Package>
org.springframework*;resolution:=optional;version="[2.0,2.5]",
*;resolution:=optional
</Import-Package>
<!--
<Embed-Dependency>
*;scope=compile|runtime;inline=true
</Embed-Dependency>
-->
</instructions>
</configuration>
</plugin>
</plugins>
</build>
</project>
@@ -158,7 +158,8 @@ public class ConfigAttributeDefinition implements Serializable {
* Allows <code>AccessDecisionManager</code>s and other classes to loop through every configuration attribute
* associated with a target secure object.
*
* @return the configuration attributes stored in this instance.
* @return all the configuration attributes stored by the instance, or <code>null</code> if an
* <code>Iterator</code> is unavailable
*/
public Collection getConfigAttributes() {
return this.configAttributes;
@@ -1,7 +1,5 @@
package org.springframework.security;
import java.io.Serializable;
/**
* Indicates that a object stores GrantedAuthority objects.
* <p>
@@ -12,6 +10,6 @@ import java.io.Serializable;
* @author Luke Taylor
* @since 2.0
*/
public interface GrantedAuthoritiesContainer extends Serializable {
public interface GrantedAuthoritiesContainer {
GrantedAuthority[] getGrantedAuthorities();
}
@@ -24,6 +24,5 @@ import java.io.Serializable;
*
* @author Ben Alex
* @version $Id$
* @deprecated Use new spring-security-acl module instead
*/
public interface AclEntry extends Serializable {}
@@ -24,7 +24,6 @@ import org.springframework.security.Authentication;
*
* @author Ben Alex
* @version $Id$
* @deprecated Use new spring-security-acl module instead
*/
public interface AclManager {
//~ Methods ========================================================================================================
@@ -28,7 +28,6 @@ import org.springframework.security.Authentication;
*
* @author Ben Alex
* @version $Id$
* @deprecated Use new spring-security-acl module instead
*/
public interface AclProvider {
//~ Methods ========================================================================================================
@@ -35,7 +35,6 @@ import java.util.List;
*
* @author Ben Alex
* @version $Id$
* @deprecated Use new spring-security-acl module instead
*/
public class AclProviderManager implements AclManager, InitializingBean {
//~ Static fields/initializers =====================================================================================
@@ -28,7 +28,6 @@ import java.util.Arrays;
*
* @author Ben Alex
* @version $Id$
* @deprecated Use new spring-security-acl module instead
*/
public abstract class AbstractBasicAclEntry implements BasicAclEntry {
//~ Static fields/initializers =====================================================================================
@@ -42,7 +42,6 @@ import java.io.Serializable;
*
* @author Ben Alex
* @version $Id$
* @deprecated Use new spring-security-acl module instead
*/
public interface AclObjectIdentity extends Serializable {
//~ Methods ========================================================================================================
@@ -28,7 +28,6 @@ package org.springframework.security.acl.basic;
*
* @author Ben Alex
* @version $Id$
* @deprecated Use new spring-security-acl module instead
*/
public interface AclObjectIdentityAware {
//~ Methods ========================================================================================================
@@ -33,7 +33,6 @@ package org.springframework.security.acl.basic;
*
* @author Ben Alex
* @version $Id$
* @deprecated Use new spring-security-acl module instead
*/
public interface BasicAclDao {
//~ Methods ========================================================================================================
@@ -23,7 +23,6 @@ import org.springframework.security.acl.AclEntry;
*
* @author Ben Alex
* @version $Id$
* @deprecated Use new spring-security-acl module instead
*/
public interface BasicAclEntry extends AclEntry {
//~ Methods ========================================================================================================
@@ -29,7 +29,6 @@ package org.springframework.security.acl.basic;
*
* @author Ben Alex
* @version $Id$
* @deprecated Use new spring-security-acl module instead
*/
public interface BasicAclEntryCache {
//~ Methods ========================================================================================================
@@ -29,7 +29,6 @@ import org.springframework.dao.DataAccessException;
*
* @author Ben Alex
* @version $Id$
* @deprecated Use new spring-security-acl module instead
*/
public interface BasicAclExtendedDao extends BasicAclDao {
//~ Methods ========================================================================================================
@@ -59,7 +59,6 @@ import java.util.Map;
*
* @author Ben Alex
* @version $Id$
* @deprecated Use new spring-security-acl module instead
*/
public class BasicAclProvider implements AclProvider, InitializingBean {
//~ Static fields/initializers =====================================================================================
@@ -44,7 +44,6 @@ import org.springframework.security.acl.AclEntry;
*
* @author Ben Alex
* @version $Id$
* @deprecated Use new spring-security-acl module instead
*/
public interface EffectiveAclsResolver {
//~ Methods ========================================================================================================
@@ -44,7 +44,6 @@ import java.util.Vector;
*
* @author Ben Alex
* @version $Id$
* @deprecated Use new spring-security-acl module instead
*/
public class GrantedAuthorityEffectiveAclsResolver implements EffectiveAclsResolver {
//~ Static fields/initializers =====================================================================================
@@ -25,7 +25,6 @@ import java.lang.reflect.Method;
/**
* Simple implementation of {@link AclObjectIdentity}.<P>Uses <code>String</code>s to store the identity of the
* domain object instance. Also offers a constructor that uses reflection to build the identity information.</p>
* @deprecated Use new spring-security-acl module instead
*/
public class NamedEntityObjectIdentity implements AclObjectIdentity {
//~ Instance fields ================================================================================================
@@ -23,7 +23,6 @@ import org.apache.commons.logging.LogFactory;
*
* @author Ben Alex
* @version $Id$
* @deprecated Use new spring-security-acl module instead
*/
public class SimpleAclEntry extends AbstractBasicAclEntry {
//~ Static fields/initializers =====================================================================================
@@ -30,7 +30,6 @@ import java.io.Serializable;
*
* @author Ben Alex
* @version $Id$
* @deprecated Use new spring-security-acl module instead
*/
public class BasicAclEntryHolder implements Serializable {
//~ Instance fields ================================================================================================
@@ -39,7 +39,6 @@ import org.springframework.util.Assert;
*
* @author Ben Alex
* @version $Id$
* @deprecated Use new spring-security-acl module instead
*/
public class EhCacheBasedAclEntryCache implements BasicAclEntryCache, InitializingBean {
//~ Static fields/initializers =====================================================================================
@@ -26,7 +26,6 @@ import org.springframework.security.acl.basic.BasicAclEntryCache;
*
* @author Ben Alex
* @version $Id$
* @deprecated Use new spring-security-acl module instead
*/
public class NullAclEntryCache implements BasicAclEntryCache {
//~ Methods ========================================================================================================
@@ -48,7 +48,6 @@ import javax.sql.DataSource;
* If this does not provide enough flexibility, another strategy would be to subclass this class and override the
* {@link MappingSqlQuery} instance used, via the {@link #initMappingSqlQueries()} extension point.
* </p>
* @deprecated Use new spring-security-acl module instead
*/
public class JdbcDaoImpl extends JdbcDaoSupport implements BasicAclDao {
//~ Static fields/initializers =====================================================================================
@@ -58,7 +58,6 @@ import javax.sql.DataSource;
*
* @author Ben Alex
* @version $Id$
* @deprecated Use new spring-security-acl module instead
*/
public class JdbcExtendedDaoImpl extends JdbcDaoImpl implements BasicAclExtendedDao {
//~ Static fields/initializers =====================================================================================
@@ -65,7 +65,6 @@ import java.util.Iterator;
* @author Ben Alex
* @author Paulo Neves
* @version $Id$
* @deprecated Use new spring-security-acl module instead
*/
public class BasicAclEntryAfterInvocationCollectionFilteringProvider implements AfterInvocationProvider,
InitializingBean {
@@ -61,8 +61,6 @@ import java.util.Iterator;
* <p>If the provided <code>returnObject</code> is <code>null</code>, permission will always be granted and
* <code>null</code> will be returned.</p>
* <p>All comparisons and prefixes are case sensitive.</p>
*
* @deprecated Use new spring-security-acl module instead
*/
public class BasicAclEntryAfterInvocationProvider implements AfterInvocationProvider, InitializingBean,
MessageSourceAware {
@@ -90,7 +90,7 @@ public class SimpleAttributes2GrantedAuthoritiesMapper implements Attributes2Gra
return attributePrefix == null ? "" : attributePrefix;
}
public void setAttributePrefix(String string) {
public void seAttributePrefix(String string) {
attributePrefix = string;
}
@@ -21,7 +21,6 @@ import org.springframework.security.ui.FilterChainOrder;
import org.springframework.security.ui.SpringSecurityFilter;
import org.springframework.security.ui.logout.LogoutHandler;
import org.springframework.security.ui.logout.SecurityContextLogoutHandler;
import org.springframework.security.util.UrlUtils;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.util.Assert;
@@ -61,7 +60,6 @@ public class ConcurrentSessionFilter extends SpringSecurityFilter implements Ini
public void afterPropertiesSet() throws Exception {
Assert.notNull(sessionRegistry, "SessionRegistry required");
Assert.isTrue(UrlUtils.isValidRedirectUrl(expiredUrl), expiredUrl + " isn't a valid redirect URL");
}
public void doFilterHttp(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
@@ -23,12 +23,12 @@ public abstract class AbstractUserDetailsServiceBeanDefinitionParser implements
/** UserDetailsService bean Id. For use in a stateful context (i.e. in AuthenticationProviderBDP) */
private String id;
protected abstract String getBeanClassName(Element element);
protected abstract Class getBeanClass(Element element);
protected abstract void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder);
public BeanDefinition parse(Element element, ParserContext parserContext) {
BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(getBeanClassName(element));
BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(getBeanClass(element));
doParse(element, parserContext, builder);
@@ -3,8 +3,6 @@ package org.springframework.security.config;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.RuntimeBeanReference;
import org.springframework.beans.factory.parsing.BeanComponentDefinition;
import org.springframework.beans.factory.support.ManagedList;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.beans.factory.xml.BeanDefinitionParser;
@@ -63,7 +61,6 @@ public class AnonymousBeanDefinitionParser implements BeanDefinitionParser {
BeanDefinition authManager = ConfigUtils.registerProviderManagerIfNecessary(parserContext);
RootBeanDefinition provider = new RootBeanDefinition(AnonymousAuthenticationProvider.class);
provider.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
provider.setSource(source);
provider.getPropertyValues().addPropertyValue(ATT_KEY, key);
@@ -71,9 +68,7 @@ public class AnonymousBeanDefinitionParser implements BeanDefinitionParser {
authMgrProviderList.add(provider);
parserContext.getRegistry().registerBeanDefinition(BeanIds.ANONYMOUS_PROCESSING_FILTER, filter);
ConfigUtils.addHttpFilter(parserContext, new RuntimeBeanReference(BeanIds.ANONYMOUS_PROCESSING_FILTER));
parserContext.registerComponent(new BeanComponentDefinition(filter, BeanIds.ANONYMOUS_PROCESSING_FILTER));
return null;
}
}
@@ -3,40 +3,27 @@ package org.springframework.security.config;
import org.springframework.beans.factory.xml.BeanDefinitionParser;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.RuntimeBeanReference;
import org.springframework.util.StringUtils;
import org.w3c.dom.Element;
/**
* Registers an alias name for the default ProviderManager used by the namespace
* Just registers an alias name for the default ProviderManager used by the namespace
* configuration, allowing users to reference it in their beans and clearly see where the name is
* coming from. Also allows the ConcurrentSessionController to be set on the ProviderManager.
* coming from.
*
* @author Luke Taylor
* @version $Id$
*/
public class AuthenticationManagerBeanDefinitionParser implements BeanDefinitionParser {
private static final String ATT_SESSION_CONTROLLER_REF = "session-controller-ref";
private static final String ATT_ALIAS = "alias";
private static final String ATT_ALIAS = "alias";
public BeanDefinition parse(Element element, ParserContext parserContext) {
BeanDefinition authManager = ConfigUtils.registerProviderManagerIfNecessary(parserContext);
String alias = element.getAttribute(ATT_ALIAS);
if (!StringUtils.hasText(alias)) {
parserContext.getReaderContext().error(ATT_ALIAS + " is required.", element );
}
String sessionControllerRef = element.getAttribute(ATT_SESSION_CONTROLLER_REF);
if (StringUtils.hasText(sessionControllerRef)) {
ConfigUtils.setSessionControllerOnAuthenticationManager(parserContext,
BeanIds.CONCURRENT_SESSION_CONTROLLER, element);
authManager.getPropertyValues().addPropertyValue("sessionController",
new RuntimeBeanReference(sessionControllerRef));
}
parserContext.getRegistry().registerAlias(BeanIds.AUTHENTICATION_MANAGER, alias);
@@ -6,7 +6,6 @@ import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.config.RuntimeBeanReference;
import org.springframework.beans.factory.parsing.BeanComponentDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.beans.factory.xml.BeanDefinitionParser;
@@ -29,7 +28,6 @@ class AuthenticationProviderBeanDefinitionParser implements BeanDefinitionParser
public BeanDefinition parse(Element element, ParserContext parserContext) {
RootBeanDefinition authProvider = new RootBeanDefinition(DaoAuthenticationProvider.class);
authProvider.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
authProvider.setSource(parserContext.extractSource(element));
Element passwordEncoderElt = DomUtils.getChildElementByTagName(element, Elements.PASSWORD_ENCODER);
@@ -50,8 +48,7 @@ class AuthenticationProviderBeanDefinitionParser implements BeanDefinitionParser
// We need to register the provider to access it in the post processor to check if it has a cache
final String id = parserContext.getReaderContext().generateBeanName(authProvider);
parserContext.getRegistry().registerBeanDefinition(id, authProvider);
parserContext.registerComponent(new BeanComponentDefinition(authProvider, id));
String ref = element.getAttribute(ATT_USER_DETAILS_REF);
if (StringUtils.hasText(ref)) {
@@ -89,11 +86,9 @@ class AuthenticationProviderBeanDefinitionParser implements BeanDefinitionParser
cacheResolverBldr.addConstructorArg(ref);
cacheResolverBldr.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
BeanDefinition cacheResolver = cacheResolverBldr.getBeanDefinition();
String name = parserContext.getReaderContext().generateBeanName(cacheResolver);
parserContext.getRegistry().registerBeanDefinition(name , cacheResolver);
parserContext.registerComponent(new BeanComponentDefinition(cacheResolver, name));
parserContext.getRegistry().registerBeanDefinition(
parserContext.getReaderContext().generateBeanName(cacheResolver), cacheResolver);
ConfigUtils.getRegisteredProviders(parserContext).add(new RuntimeBeanReference(id));
return null;
@@ -2,7 +2,6 @@ package org.springframework.security.config;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.RuntimeBeanReference;
import org.springframework.beans.factory.parsing.BeanComponentDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.beans.factory.xml.BeanDefinitionParser;
@@ -29,9 +28,7 @@ public class BasicAuthenticationBeanDefinitionParser implements BeanDefinitionPa
public BeanDefinition parse(Element elt, ParserContext parserContext) {
BeanDefinitionBuilder filterBuilder = BeanDefinitionBuilder.rootBeanDefinition(BasicProcessingFilter.class);
RootBeanDefinition entryPoint = new RootBeanDefinition(BasicProcessingFilterEntryPoint.class);
entryPoint.setSource(parserContext.extractSource(elt));
entryPoint.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
entryPoint.getPropertyValues().addPropertyValue("realmName", realmName);
parserContext.getRegistry().registerBeanDefinition(BeanIds.BASIC_AUTHENTICATION_ENTRY_POINT, entryPoint);
@@ -41,9 +38,7 @@ public class BasicAuthenticationBeanDefinitionParser implements BeanDefinitionPa
parserContext.getRegistry().registerBeanDefinition(BeanIds.BASIC_AUTHENTICATION_FILTER,
filterBuilder.getBeanDefinition());
ConfigUtils.addHttpFilter(parserContext, new RuntimeBeanReference(BeanIds.BASIC_AUTHENTICATION_FILTER));
parserContext.registerComponent(new BeanComponentDefinition(filterBuilder.getBeanDefinition(),
BeanIds.BASIC_AUTHENTICATION_FILTER));
return null;
}
}
@@ -16,10 +16,7 @@ public abstract class BeanIds {
/** Package protected as end users shouldn't really be using this BFPP directly */
static final String INTERCEPT_METHODS_BEAN_FACTORY_POST_PROCESSOR = "_interceptMethodsBeanfactoryPP";
static final String CONTEXT_SOURCE_SETTING_POST_PROCESSOR = "_contextSettingPostProcessor";
static final String ENTRY_POINT_INJECTION_POST_PROCESSOR = "_entryPointInjectionBeanPostProcessor";
static final String USER_DETAILS_SERVICE_INJECTION_POST_PROCESSOR = "_userServiceInjectionPostProcessor";
static final String FILTER_CHAIN_POST_PROCESSOR = "_filterChainProxyPostProcessor";
static final String FILTER_LIST = "_filterChainList";
static final String HTTP_POST_PROCESSOR = "_httpConfigBeanFactoryPostProcessor";
public static final String JDBC_USER_DETAILS_MANAGER = "_jdbcUserDetailsManager";
public static final String USER_DETAILS_SERVICE = "_userDetailsService";
@@ -32,7 +29,6 @@ public abstract class BeanIds {
public static final String CONCURRENT_SESSION_CONTROLLER = "_concurrentSessionController";
public static final String ACCESS_MANAGER = "_accessManager";
public static final String AUTHENTICATION_MANAGER = "_authenticationManager";
public static final String AFTER_INVOCATION_MANAGER = "_afterInvocationManager";
public static final String FORM_LOGIN_FILTER = "_formLoginFilter";
public static final String FORM_LOGIN_ENTRY_POINT = "_formLoginEntryPoint";
public static final String OPEN_ID_FILTER = "_openIDFilter";
@@ -52,7 +48,6 @@ public abstract class BeanIds {
public static final String SECURITY_CONTEXT_HOLDER_AWARE_REQUEST_FILTER = "_securityContextHolderAwareRequestFilter";
public static final String SESSION_FIXATION_PROTECTION_FILTER = "_sessionFixationProtectionFilter";
public static final String METHOD_SECURITY_INTERCEPTOR = "_methodSecurityInterceptor";
public static final String METHOD_SECURITY_INTERCEPTOR_POST_PROCESSOR = "_methodSecurityInterceptorPostProcessor";
public static final String METHOD_DEFINITION_SOURCE_ADVISOR = "_methodDefinitionSourceAdvisor";
public static final String PROTECT_POINTCUT_POST_PROCESSOR = "_protectPointcutPostProcessor";
public static final String DELEGATING_METHOD_DEFINITION_SOURCE = "_delegatingMethodDefinitionSource";
@@ -62,8 +57,6 @@ public abstract class BeanIds {
public static final String CONTEXT_SOURCE = "_securityContextSource";
public static final String PORT_MAPPER = "_portMapper";
public static final String X509_FILTER = "_x509ProcessingFilter";
public static final String X509_AUTH_PROVIDER = "_x509AuthenticationProvider";
public static final String X509_AUTH_PROVIDER = "_x509AuthenitcationProvider";
public static final String PRE_AUTH_ENTRY_POINT = "_preAuthenticatedProcessingFilterEntryPoint";
public static final String REMEMBER_ME_SERVICES_INJECTION_POST_PROCESSOR = "_rememberMeServicesInjectionBeanPostProcessor";
}
@@ -2,8 +2,6 @@ package org.springframework.security.config;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.RuntimeBeanReference;
import org.springframework.beans.factory.parsing.BeanComponentDefinition;
import org.springframework.beans.factory.parsing.CompositeComponentDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.RootBeanDefinition;
@@ -29,53 +27,28 @@ public class ConcurrentSessionsBeanDefinitionParser implements BeanDefinitionPar
static final String ATT_EXPIRY_URL = "expired-url";
static final String ATT_MAX_SESSIONS = "max-sessions";
static final String ATT_EXCEPTION_IF_MAX_EXCEEDED = "exception-if-maximum-exceeded";
static final String ATT_SESSION_REGISTRY_ALIAS = "session-registry-alias";
static final String ATT_SESSION_REGISTRY_REF = "session-registry-ref";
static final String ATT_SESSION_REGISTRY_ALIAS = "session-registry-alias";
public BeanDefinition parse(Element element, ParserContext parserContext) {
CompositeComponentDefinition compositeDef =
new CompositeComponentDefinition(element.getTagName(), parserContext.extractSource(element));
parserContext.pushContainingComponent(compositeDef);
BeanDefinitionRegistry beanRegistry = parserContext.getRegistry();
String sessionRegistryId = element.getAttribute(ATT_SESSION_REGISTRY_REF);
if (!StringUtils.hasText(sessionRegistryId)) {
RootBeanDefinition sessionRegistry = new RootBeanDefinition(SessionRegistryImpl.class);
beanRegistry.registerBeanDefinition(BeanIds.SESSION_REGISTRY, sessionRegistry);
parserContext.registerComponent(new BeanComponentDefinition(sessionRegistry, BeanIds.SESSION_REGISTRY));
sessionRegistryId = BeanIds.SESSION_REGISTRY;
} else {
// Register the default ID as an alias so that things like session fixation filter can access it
beanRegistry.registerAlias(sessionRegistryId, BeanIds.SESSION_REGISTRY);
}
String registryAlias = element.getAttribute(ATT_SESSION_REGISTRY_ALIAS);
if (StringUtils.hasText(registryAlias)) {
beanRegistry.registerAlias(sessionRegistryId, registryAlias);
}
BeanDefinitionRegistry beanRegistry = parserContext.getRegistry();
RootBeanDefinition sessionRegistry = new RootBeanDefinition(SessionRegistryImpl.class);
BeanDefinitionBuilder filterBuilder =
BeanDefinitionBuilder.rootBeanDefinition(ConcurrentSessionFilter.class);
filterBuilder.addPropertyValue("sessionRegistry", new RuntimeBeanReference(sessionRegistryId));
BeanDefinitionBuilder controllerBuilder
= BeanDefinitionBuilder.rootBeanDefinition(ConcurrentSessionControllerImpl.class);
controllerBuilder.addPropertyValue("sessionRegistry", new RuntimeBeanReference(BeanIds.SESSION_REGISTRY));
filterBuilder.addPropertyValue("sessionRegistry", new RuntimeBeanReference(BeanIds.SESSION_REGISTRY));
Object source = parserContext.extractSource(element);
filterBuilder.setSource(source);
filterBuilder.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
controllerBuilder.setSource(source);
String expiryUrl = element.getAttribute(ATT_EXPIRY_URL);
if (StringUtils.hasText(expiryUrl)) {
ConfigUtils.validateHttpRedirect(expiryUrl, parserContext, source);
filterBuilder.addPropertyValue("expiredUrl", expiryUrl);
}
BeanDefinitionBuilder controllerBuilder
= BeanDefinitionBuilder.rootBeanDefinition(ConcurrentSessionControllerImpl.class);
controllerBuilder.setSource(source);
controllerBuilder.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
controllerBuilder.addPropertyValue("sessionRegistry", new RuntimeBeanReference(sessionRegistryId));
}
String maxSessions = element.getAttribute(ATT_MAX_SESSIONS);
@@ -90,17 +63,20 @@ public class ConcurrentSessionsBeanDefinitionParser implements BeanDefinitionPar
}
BeanDefinition controller = controllerBuilder.getBeanDefinition();
beanRegistry.registerBeanDefinition(BeanIds.SESSION_REGISTRY, sessionRegistry);
String registryAlias = element.getAttribute(ATT_SESSION_REGISTRY_ALIAS);
if (StringUtils.hasText(registryAlias)) {
beanRegistry.registerAlias(BeanIds.SESSION_REGISTRY, registryAlias);
}
beanRegistry.registerBeanDefinition(BeanIds.CONCURRENT_SESSION_CONTROLLER, controller);
parserContext.registerComponent(new BeanComponentDefinition(controller, BeanIds.CONCURRENT_SESSION_CONTROLLER));
beanRegistry.registerBeanDefinition(BeanIds.CONCURRENT_SESSION_FILTER, filterBuilder.getBeanDefinition());
parserContext.registerComponent(new BeanComponentDefinition(filterBuilder.getBeanDefinition(), BeanIds.CONCURRENT_SESSION_FILTER));
ConfigUtils.addHttpFilter(parserContext, new RuntimeBeanReference(BeanIds.CONCURRENT_SESSION_FILTER));
ConfigUtils.setSessionControllerOnAuthenticationManager(parserContext, BeanIds.CONCURRENT_SESSION_CONTROLLER, element);
parserContext.popAndRegisterContainingComponent();
BeanDefinition providerManager = ConfigUtils.registerProviderManagerIfNecessary(parserContext);
providerManager.getPropertyValues().addPropertyValue("sessionController", controller);
return null;
}
}
@@ -1,32 +1,25 @@
package org.springframework.security.config;
import java.util.List;
import java.util.Map;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.BeanMetadataElement;
import org.springframework.beans.MutablePropertyValues;
import org.springframework.beans.PropertyValue;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.config.RuntimeBeanReference;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.ManagedList;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.security.afterinvocation.AfterInvocationProviderManager;
import org.springframework.security.AuthenticationManager;
import org.springframework.security.providers.ProviderManager;
import org.springframework.security.userdetails.UserDetailsService;
import org.springframework.security.util.UrlUtils;
import org.springframework.security.vote.AffirmativeBased;
import org.springframework.security.vote.AuthenticatedVoter;
import org.springframework.security.vote.RoleVoter;
import org.springframework.util.StringUtils;
import org.w3c.dom.Element;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import java.util.Map;
/**
* Utility methods used internally by the Spring Security namespace configuration code.
* Utitily methods used internally by the Spring Security namespace configuration code.
*
* @author Luke Taylor
* @author Ben Alex
@@ -50,18 +43,6 @@ public abstract class ConfigUtils {
parserContext.getRegistry().registerBeanDefinition(BeanIds.ACCESS_MANAGER, accessMgr);
}
}
public static int countNonEmpty(String[] objects) {
int nonNulls = 0;
for (int i = 0; i < objects.length; i++) {
if (StringUtils.hasText(objects[i])) {
nonNulls++;
}
}
return nonNulls;
}
public static void addVoter(BeanDefinition voter, ParserContext parserContext) {
registerDefaultAccessManagerIfNecessary(parserContext);
@@ -92,95 +73,45 @@ public abstract class ConfigUtils {
return authManager;
}
/**
* Obtains a user details service for use in RememberMeServices etc. Will return a caching version
* if available so should not be used for beans which need to separate the two.
*/
static UserDetailsService getUserDetailsService(ConfigurableListableBeanFactory bf) {
Map services = bf.getBeansOfType(CachingUserDetailsService.class);
if (services.size() == 0) {
services = bf.getBeansOfType(UserDetailsService.class);
}
if (services.size() == 0) {
throw new IllegalArgumentException("No UserDetailsService registered.");
} else if (services.size() > 1) {
throw new IllegalArgumentException("More than one UserDetailsService registered. Please" +
"use a specific Id in your configuration");
}
return (UserDetailsService) services.values().toArray()[0];
}
private static AuthenticationManager getAuthenticationManager(ConfigurableListableBeanFactory bf) {
Map authManagers = bf.getBeansOfType(AuthenticationManager.class);
if (authManagers.size() == 0) {
throw new IllegalArgumentException("No AuthenticationManager registered. " +
"Make sure you have configured at least one AuthenticationProvider?");
} else if (authManagers.size() > 1) {
throw new IllegalArgumentException("More than one AuthenticationManager registered.");
}
return (AuthenticationManager) authManagers.values().toArray()[0];
}
static ManagedList getRegisteredProviders(ParserContext parserContext) {
BeanDefinition authManager = registerProviderManagerIfNecessary(parserContext);
return (ManagedList) authManager.getPropertyValues().getPropertyValue("providers").getValue();
}
static ManagedList getRegisteredAfterInvocationProviders(ParserContext parserContext) {
BeanDefinition manager = registerAfterInvocationProviderManagerIfNecessary(parserContext);
return (ManagedList) manager.getPropertyValues().getPropertyValue("providers").getValue();
}
private static BeanDefinition registerAfterInvocationProviderManagerIfNecessary(ParserContext parserContext) {
if(parserContext.getRegistry().containsBeanDefinition(BeanIds.AFTER_INVOCATION_MANAGER)) {
return parserContext.getRegistry().getBeanDefinition(BeanIds.AFTER_INVOCATION_MANAGER);
}
BeanDefinition manager = new RootBeanDefinition(AfterInvocationProviderManager.class);
manager.getPropertyValues().addPropertyValue("providers", new ManagedList());
parserContext.getRegistry().registerBeanDefinition(BeanIds.AFTER_INVOCATION_MANAGER, manager);
return manager;
}
private static void registerFilterChainPostProcessorIfNecessary(ParserContext pc) {
if (pc.getRegistry().containsBeanDefinition(BeanIds.FILTER_CHAIN_POST_PROCESSOR)) {
return;
}
// Post processor specifically to assemble and order the filter chain immediately before the FilterChainProxy is initialized.
RootBeanDefinition filterChainPostProcessor = new RootBeanDefinition(FilterChainProxyPostProcessor.class);
filterChainPostProcessor.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
pc.getRegistry().registerBeanDefinition(BeanIds.FILTER_CHAIN_POST_PROCESSOR, filterChainPostProcessor);
RootBeanDefinition filterList = new RootBeanDefinition(FilterChainList.class);
filterList.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
pc.getRegistry().registerBeanDefinition(BeanIds.FILTER_LIST, filterList);
}
static void addHttpFilter(ParserContext pc, BeanMetadataElement filter) {
registerFilterChainPostProcessorIfNecessary(pc);
RootBeanDefinition filterList = (RootBeanDefinition) pc.getRegistry().getBeanDefinition(BeanIds.FILTER_LIST);
ManagedList filters;
MutablePropertyValues pvs = filterList.getPropertyValues();
if (pvs.contains("filters")) {
filters = (ManagedList) pvs.getPropertyValue("filters").getValue();
} else {
filters = new ManagedList();
pvs.addPropertyValue("filters", filters);
}
filters.add(filter);
}
/**
* Bean which holds the list of filters which are maintained in the context and modified by calls to
* addHttpFilter. The post processor retrieves these before injecting the list into the FilterChainProxy.
*/
public static class FilterChainList {
List filters;
public List getFilters() {
return filters;
}
public void setFilters(List filters) {
this.filters = filters;
}
}
/**
* Checks the value of an XML attribute which represents a redirect URL.
* If not empty or starting with "$" (potential placeholder), "/" or "http" it will raise an error.
*/
static void validateHttpRedirect(String url, ParserContext pc, Object source) {
if (UrlUtils.isValidRedirectUrl(url) || url.startsWith("$")) {
return;
}
pc.getReaderContext().warning(url + " is not a valid redirect URL (must start with '/' or http(s))", source);
}
static void setSessionControllerOnAuthenticationManager(ParserContext pc, String beanName, Element sourceElt) {
BeanDefinition authManager = registerProviderManagerIfNecessary(pc);
PropertyValue pv = authManager.getPropertyValues().getPropertyValue("sessionController");
if (pv != null && pv.getValue() != null) {
pc.getReaderContext().error("A session controller has already been set on the authentication manager. " +
"The <concurrent-session-control> element isn't compatible with a custom session controller",
pc.extractSource(sourceElt));
}
authManager.getPropertyValues().addPropertyValue("sessionController", new RuntimeBeanReference(beanName));
}
}
@@ -1,24 +0,0 @@
package org.springframework.security.config;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.xml.BeanDefinitionDecorator;
import org.springframework.beans.factory.xml.ParserContext;
import org.w3c.dom.Node;
/**
* Adds the decorated {@link org.springframework.security.afterinvocation.AfterInvocationProvider} to the
* AfterInvocationProviderManager's list.
*
* @author Luke Taylor
* @version $Id$
* @since 2.0
*/
public class CustomAfterInvocationProviderBeanDefinitionDecorator implements BeanDefinitionDecorator {
public BeanDefinitionHolder decorate(Node node, BeanDefinitionHolder holder, ParserContext parserContext) {
ConfigUtils.getRegisteredAfterInvocationProviders(parserContext).add(holder.getBeanDefinition());
return holder;
}
}
@@ -3,7 +3,6 @@ package org.springframework.security.config;
import org.springframework.beans.factory.xml.BeanDefinitionDecorator;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.config.RuntimeBeanReference;
import org.w3c.dom.Node;
@@ -17,7 +16,7 @@ import org.w3c.dom.Node;
*/
public class CustomAuthenticationProviderBeanDefinitionDecorator implements BeanDefinitionDecorator {
public BeanDefinitionHolder decorate(Node node, BeanDefinitionHolder holder, ParserContext parserContext) {
ConfigUtils.getRegisteredProviders(parserContext).add(new RuntimeBeanReference(holder.getBeanName()));
ConfigUtils.getRegisteredProviders(parserContext).add(holder.getBeanDefinition());
return holder;
}
@@ -13,7 +13,6 @@ abstract class Elements {
public static final String JDBC_USER_SERVICE = "jdbc-user-service";
public static final String FILTER_CHAIN_MAP = "filter-chain-map";
public static final String INTERCEPT_METHODS = "intercept-methods";
public static final String INTERCEPT_URL = "intercept-url";
public static final String AUTHENTICATION_PROVIDER = "authentication-provider";
public static final String HTTP = "http";
public static final String LDAP_PROVIDER = "ldap-authentication-provider";
@@ -35,8 +34,7 @@ abstract class Elements {
public static final String PORT_MAPPINGS = "port-mappings";
public static final String PORT_MAPPING = "port-mapping";
public static final String CUSTOM_FILTER = "custom-filter";
public static final String CUSTOM_AUTH_PROVIDER = "custom-authentication-provider";
public static final String CUSTOM_AFTER_INVOCATION_PROVIDER = "custom-after-invocation-provider";
public static final String CUSTOM_AUTH_RPOVIDER = "custom-authentication-provider";
public static final String X509 = "x509";
public static final String FILTER_INVOCATION_DEFINITION_SOURCE = "filter-invocation-definition-source";
public static final String LDAP_PASSWORD_COMPARE = "password-compare";
@@ -1,59 +0,0 @@
package org.springframework.security.config;
import java.util.Map;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.security.ui.AuthenticationEntryPoint;
import org.springframework.security.ui.ExceptionTranslationFilter;
import org.springframework.util.Assert;
/**
*
* @author Luke Taylor
* @since 2.0.2
*/
public class EntryPointInjectionBeanPostProcessor implements BeanPostProcessor, BeanFactoryAware {
private final Log logger = LogFactory.getLog(getClass());
private ConfigurableListableBeanFactory beanFactory;
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if (!BeanIds.EXCEPTION_TRANSLATION_FILTER.equals(beanName)) {
return bean;
}
logger.info("Selecting AuthenticationEntryPoint for use in ExceptionTranslationFilter");
ExceptionTranslationFilter etf = (ExceptionTranslationFilter) beanFactory.getBean(BeanIds.EXCEPTION_TRANSLATION_FILTER);
Object entryPoint = null;
if (beanFactory.containsBean(BeanIds.MAIN_ENTRY_POINT)) {
entryPoint = beanFactory.getBean(BeanIds.MAIN_ENTRY_POINT);
logger.info("Using main configured AuthenticationEntryPoint.");
} else {
Map entryPoints = beanFactory.getBeansOfType(AuthenticationEntryPoint.class);
Assert.isTrue(entryPoints.size() != 0, "No AuthenticationEntryPoint instances defined");
Assert.isTrue(entryPoints.size() == 1, "More than one AuthenticationEntryPoint defined in context");
entryPoint = entryPoints.values().toArray()[0];
}
logger.info("Using bean '" + entryPoint + "' as the entry point.");
etf.setAuthenticationEntryPoint((AuthenticationEntryPoint) entryPoint);
return bean;
}
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = (ConfigurableListableBeanFactory) beanFactory;
}
}
@@ -1,201 +0,0 @@
package org.springframework.security.config;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import javax.servlet.Filter;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.core.OrderComparator;
import org.springframework.core.Ordered;
import org.springframework.security.ConfigAttributeDefinition;
import org.springframework.security.config.ConfigUtils.FilterChainList;
import org.springframework.security.context.HttpSessionContextIntegrationFilter;
import org.springframework.security.intercept.web.DefaultFilterInvocationDefinitionSource;
import org.springframework.security.intercept.web.FilterSecurityInterceptor;
import org.springframework.security.providers.anonymous.AnonymousAuthenticationToken;
import org.springframework.security.providers.anonymous.AnonymousProcessingFilter;
import org.springframework.security.ui.ExceptionTranslationFilter;
import org.springframework.security.ui.SessionFixationProtectionFilter;
import org.springframework.security.ui.basicauth.BasicProcessingFilter;
import org.springframework.security.ui.webapp.AuthenticationProcessingFilter;
import org.springframework.security.ui.webapp.AuthenticationProcessingFilterEntryPoint;
import org.springframework.security.ui.webapp.DefaultLoginPageGeneratingFilter;
import org.springframework.security.util.FilterChainProxy;
import org.springframework.security.wrapper.SecurityContextHolderAwareRequestFilter;
/**
*
* @author Luke Taylor
* @version $Id$
* @since 2.0
*/
public class FilterChainProxyPostProcessor implements BeanPostProcessor, BeanFactoryAware {
private Log logger = LogFactory.getLog(getClass());
private ListableBeanFactory beanFactory;
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if(!BeanIds.FILTER_CHAIN_PROXY.equals(beanName)) {
return bean;
}
FilterChainProxy filterChainProxy = (FilterChainProxy) bean;
FilterChainList filterList = (FilterChainList) beanFactory.getBean(BeanIds.FILTER_LIST);
List filters = new ArrayList(filterList.getFilters());
Collections.sort(filters, new OrderComparator());
logger.info("Checking sorted filter chain: " + filters);
for(int i=0; i < filters.size(); i++) {
Ordered filter = (Ordered)filters.get(i);
if (i > 0) {
Ordered previous = (Ordered)filters.get(i-1);
if (filter.getOrder() == previous.getOrder()) {
throw new SecurityConfigurationException("Filters '" + unwrapFilter(filter) + "' and '" +
unwrapFilter(previous) + "' have the same 'order' value. When using custom filters, " +
"please make sure the positions do not conflict with default filters. " +
"Alternatively you can disable the default filters by removing the corresponding " +
"child elements from <http> and not avoiding the use of <http auto-config='true'>.");
}
}
}
logger.info("Filter chain...");
for (int i=0; i < filters.size(); i++) {
// Remove the ordered wrapper from the filter and put it back in the chain at the same position.
Filter filter = unwrapFilter(filters.get(i));
logger.info("[" + i + "] - " + filter);
filters.set(i, filter);
}
checkFilterStack(filters);
// Note that this returns a copy
Map filterMap = filterChainProxy.getFilterChainMap();
filterMap.put(filterChainProxy.getMatcher().getUniversalMatchPattern(), filters);
filterChainProxy.setFilterChainMap(filterMap);
checkLoginPageIsntProtected(filterChainProxy);
logger.info("FilterChainProxy: " + filterChainProxy);
return bean;
}
/**
* Checks the filter list for possible errors and logs them
*/
private void checkFilterStack(List filters) {
checkForDuplicates(HttpSessionContextIntegrationFilter.class, filters);
checkForDuplicates(AuthenticationProcessingFilter.class, filters);
checkForDuplicates(SessionFixationProtectionFilter.class, filters);
checkForDuplicates(BasicProcessingFilter.class, filters);
checkForDuplicates(SecurityContextHolderAwareRequestFilter.class, filters);
checkForDuplicates(ExceptionTranslationFilter.class, filters);
checkForDuplicates(FilterSecurityInterceptor.class, filters);
}
private void checkForDuplicates(Class clazz, List filters) {
for (int i=0; i < filters.size(); i++) {
Filter f1 = (Filter)filters.get(i);
if (clazz.isAssignableFrom(f1.getClass())) {
// Found the first one, check remaining for another
for (int j=i+1; j < filters.size(); j++) {
Filter f2 = (Filter)filters.get(j);
if (clazz.isAssignableFrom(f2.getClass())) {
logger.warn("Possible error: Filters at position " + i + " and " + j + " are both " +
"instances of " + clazz.getName());
return;
}
}
}
}
}
/* Checks for the common error of having a login page URL protected by the security interceptor */
private void checkLoginPageIsntProtected(FilterChainProxy fcp) {
ExceptionTranslationFilter etf = (ExceptionTranslationFilter) beanFactory.getBean(BeanIds.EXCEPTION_TRANSLATION_FILTER);
if (etf.getAuthenticationEntryPoint() instanceof AuthenticationProcessingFilterEntryPoint) {
String loginPage =
((AuthenticationProcessingFilterEntryPoint)etf.getAuthenticationEntryPoint()).getLoginFormUrl();
List filters = fcp.getFilters(loginPage);
logger.info("Checking whether login URL '" + loginPage + "' is accessible with your configuration");
if (filters == null || filters.isEmpty()) {
logger.debug("Filter chain is empty for the login page");
return;
}
if (loginPage.equals(DefaultLoginPageGeneratingFilter.DEFAULT_LOGIN_PAGE_URL) &&
beanFactory.containsBean(BeanIds.DEFAULT_LOGIN_PAGE_GENERATING_FILTER)) {
logger.debug("Default generated login page is in use");
return;
}
FilterSecurityInterceptor fsi =
((FilterSecurityInterceptor)beanFactory.getBean(BeanIds.FILTER_SECURITY_INTERCEPTOR));
DefaultFilterInvocationDefinitionSource fids =
(DefaultFilterInvocationDefinitionSource) fsi.getObjectDefinitionSource();
ConfigAttributeDefinition cad = fids.lookupAttributes(loginPage, "POST");
if (cad == null) {
logger.debug("No access attributes defined for login page URL");
if (fsi.isRejectPublicInvocations()) {
logger.warn("FilterSecurityInterceptor is configured to reject public invocations." +
" Your login page may not be accessible.");
}
return;
}
if (!beanFactory.containsBean(BeanIds.ANONYMOUS_PROCESSING_FILTER)) {
logger.warn("The login page is being protected by the filter chain, but you don't appear to have" +
" anonymous authentication enabled. This is almost certainly an error.");
return;
}
// Simulate an anonymous access with the supplied attributes.
AnonymousProcessingFilter anonPF = (AnonymousProcessingFilter) beanFactory.getBean(BeanIds.ANONYMOUS_PROCESSING_FILTER);
AnonymousAuthenticationToken token =
new AnonymousAuthenticationToken("key", anonPF.getUserAttribute().getPassword(),
anonPF.getUserAttribute().getAuthorities());
try {
fsi.getAccessDecisionManager().decide(token, new Object(), cad);
} catch (Exception e) {
logger.warn("Anonymous access to the login page doesn't appear to be enabled. This is almost certainly " +
"an error. Please check your configuration allows unauthenticated access to the configured " +
"login page. (Simulated access was rejected: " + e + ")");
}
}
}
/**
* Returns the delegate filter of a wrapper, or the unchanged filter if it isn't wrapped.
*/
private Filter unwrapFilter(Object filter) {
if (filter instanceof OrderedFilterBeanDefinitionDecorator.OrderedFilterDecorator) {
return ((OrderedFilterBeanDefinitionDecorator.OrderedFilterDecorator)filter).getDelegate();
}
return (Filter) filter;
}
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = (ListableBeanFactory) beanFactory;
}
}
@@ -44,8 +44,8 @@ public class FilterInvocationDefinitionSourceBeanDefinitionParser extends Abstra
UrlMatcher matcher = HttpSecurityBeanDefinitionParser.createUrlMatcher(element);
boolean convertPathsToLowerCase = (matcher instanceof AntUrlPathMatcher) && matcher.requiresLowerCaseUrl();
LinkedHashMap requestMap =
HttpSecurityBeanDefinitionParser.parseInterceptUrlsForFilterInvocationRequestMap(interceptUrls,
LinkedHashMap requestMap = new LinkedHashMap();
HttpSecurityBeanDefinitionParser.parseInterceptUrlsForFilterInvocationRequestMap(interceptUrls, requestMap,
convertPathsToLowerCase, parserContext);
builder.addConstructorArg(matcher);
@@ -1,6 +1,5 @@
package org.springframework.security.config;
import org.springframework.beans.PropertyValue;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.RuntimeBeanReference;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
@@ -29,7 +28,6 @@ public class FormLoginBeanDefinitionParser implements BeanDefinitionParser {
static final String DEF_LOGIN_PAGE = DefaultLoginPageGeneratingFilter.DEFAULT_LOGIN_PAGE_URL;
static final String ATT_FORM_LOGIN_TARGET_URL = "default-target-url";
static final String ATT_ALWAYS_USE_DEFAULT_TARGET_URL = "always-use-default-target";
static final String DEF_FORM_LOGIN_TARGET_URL = "/";
static final String ATT_FORM_LOGIN_AUTHENTICATION_FAILURE_URL = "authentication-failure-url";
@@ -47,90 +45,53 @@ public class FormLoginBeanDefinitionParser implements BeanDefinitionParser {
this.filterClassName = filterClassName;
}
public BeanDefinition parse(Element elt, ParserContext pc) {
public BeanDefinition parse(Element elt, ParserContext parserContext) {
String loginUrl = null;
String defaultTargetUrl = null;
String authenticationFailureUrl = null;
String alwaysUseDefault = null;
Object source = null;
// Copy values from the session fixation protection filter
final Boolean sessionFixationProtectionEnabled =
new Boolean(pc.getRegistry().containsBeanDefinition(BeanIds.SESSION_FIXATION_PROTECTION_FILTER));
Boolean migrateSessionAttributes = Boolean.FALSE;
if (sessionFixationProtectionEnabled.booleanValue()) {
PropertyValue pv =
pc.getRegistry().getBeanDefinition(BeanIds.SESSION_FIXATION_PROTECTION_FILTER)
.getPropertyValues().getPropertyValue("migrateSessionAttributes");
migrateSessionAttributes = (Boolean)pv.getValue();
}
if (elt != null) {
source = pc.extractSource(elt);
loginUrl = elt.getAttribute(ATT_LOGIN_URL);
ConfigUtils.validateHttpRedirect(loginUrl, pc, source);
defaultTargetUrl = elt.getAttribute(ATT_FORM_LOGIN_TARGET_URL);
ConfigUtils.validateHttpRedirect(defaultTargetUrl, pc, source);
authenticationFailureUrl = elt.getAttribute(ATT_FORM_LOGIN_AUTHENTICATION_FAILURE_URL);
ConfigUtils.validateHttpRedirect(authenticationFailureUrl, pc, source);
alwaysUseDefault = elt.getAttribute(ATT_ALWAYS_USE_DEFAULT_TARGET_URL);
loginPage = elt.getAttribute(ATT_LOGIN_PAGE);
if (!StringUtils.hasText(loginPage)) {
loginPage = null;
}
ConfigUtils.validateHttpRedirect(loginPage, pc, source);
source = parserContext.extractSource(elt);
}
ConfigUtils.registerProviderManagerIfNecessary(pc);
ConfigUtils.registerProviderManagerIfNecessary(parserContext);
filterBean = createFilterBean(loginUrl, defaultTargetUrl, alwaysUseDefault, loginPage, authenticationFailureUrl);
filterBean = createFilterBean(loginUrl, defaultTargetUrl, loginPage, authenticationFailureUrl);
filterBean.setSource(source);
filterBean.getPropertyValues().addPropertyValue("authenticationManager",
new RuntimeBeanReference(BeanIds.AUTHENTICATION_MANAGER));
filterBean.getPropertyValues().addPropertyValue("invalidateSessionOnSuccessfulAuthentication",
sessionFixationProtectionEnabled);
filterBean.getPropertyValues().addPropertyValue("migrateInvalidatedSessionAttributes",
migrateSessionAttributes);
if (pc.getRegistry().containsBeanDefinition(BeanIds.REMEMBER_ME_SERVICES)) {
filterBean.getPropertyValues().addPropertyValue("rememberMeServices",
new RuntimeBeanReference(BeanIds.REMEMBER_ME_SERVICES) );
}
if (pc.getRegistry().containsBeanDefinition(BeanIds.SESSION_REGISTRY)) {
filterBean.getPropertyValues().addPropertyValue("sessionRegistry",
new RuntimeBeanReference(BeanIds.SESSION_REGISTRY));
}
BeanDefinitionBuilder entryPointBuilder =
BeanDefinitionBuilder.rootBeanDefinition(AuthenticationProcessingFilterEntryPoint.class);
entryPointBuilder.setSource(source);
entryPointBuilder.addPropertyValue("loginFormUrl", loginPage != null ? loginPage : DEF_LOGIN_PAGE);
entryPointBean = (RootBeanDefinition) entryPointBuilder.getBeanDefinition();
return null;
}
private RootBeanDefinition createFilterBean(String loginUrl, String defaultTargetUrl, String alwaysUseDefault,
String loginPage, String authenticationFailureUrl) {
private RootBeanDefinition createFilterBean(String loginUrl, String defaultTargetUrl, String loginPage, String authenticationFailureUrl) {
BeanDefinitionBuilder filterBuilder = BeanDefinitionBuilder.rootBeanDefinition(filterClassName);
if (!StringUtils.hasText(loginUrl)) {
loginUrl = defaultLoginProcessingUrl;
}
if ("true".equals(alwaysUseDefault)) {
filterBuilder.addPropertyValue("alwaysUseDefaultTargetUrl", Boolean.TRUE);
}
filterBuilder.addPropertyValue("filterProcessesUrl", loginUrl);
if (!StringUtils.hasText(defaultTargetUrl)) {
defaultTargetUrl = DEF_FORM_LOGIN_TARGET_URL;
}
@@ -8,7 +8,6 @@ import java.util.Map;
import org.springframework.aop.config.AopNamespaceUtils;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.RuntimeBeanReference;
import org.springframework.beans.factory.parsing.BeanComponentDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.ManagedList;
import org.springframework.beans.factory.support.RootBeanDefinition;
@@ -20,6 +19,7 @@ import org.springframework.security.intercept.method.MapBasedMethodDefinitionSou
import org.springframework.security.intercept.method.ProtectPointcutPostProcessor;
import org.springframework.security.intercept.method.aopalliance.MethodDefinitionSourceAdvisor;
import org.springframework.security.intercept.method.aopalliance.MethodSecurityInterceptor;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;
import org.springframework.util.xml.DomUtils;
@@ -34,6 +34,7 @@ import org.w3c.dom.Element;
class GlobalMethodSecurityBeanDefinitionParser implements BeanDefinitionParser {
public static final String SECURED_DEPENDENCY_CLASS = "org.springframework.security.annotation.Secured";
public static final String SECURED_METHOD_DEFINITION_SOURCE_CLASS = "org.springframework.security.annotation.SecuredMethodDefinitionSource";
public static final String JSR_250_DEPENDENCY_CLASS = "javax.annotation.security.DenyAll";
public static final String JSR_250_SECURITY_METHOD_DEFINITION_SOURCE_CLASS = "org.springframework.security.annotation.Jsr250MethodDefinitionSource";
public static final String JSR_250_VOTER_CLASS = "org.springframework.security.annotation.Jsr250Voter";
private static final String ATT_ACCESS = "access";
@@ -42,144 +43,100 @@ class GlobalMethodSecurityBeanDefinitionParser implements BeanDefinitionParser {
private static final String ATT_USE_JSR250 = "jsr250-annotations";
private static final String ATT_USE_SECURED = "secured-annotations";
private void validatePresent(String className, Element element, ParserContext parserContext) {
if (!ClassUtils.isPresent(className, parserContext.getReaderContext().getBeanClassLoader())) {
parserContext.getReaderContext().error("Cannot locate '" + className + "'", element);
}
private void validatePresent(String className) {
Assert.isTrue(ClassUtils.isPresent(className), "Cannot locate '" + className + "'");
}
public BeanDefinition parse(Element element, ParserContext parserContext) {
Object source = parserContext.extractSource(element);
// The list of method metadata delegates
ManagedList delegates = new ManagedList();
boolean jsr250Enabled = registerAnnotationBasedMethodDefinitionSources(element, parserContext, delegates);
MapBasedMethodDefinitionSource mapBasedMethodDefinitionSource = new MapBasedMethodDefinitionSource();
delegates.add(mapBasedMethodDefinitionSource);
// Now create a Map<String, ConfigAttribute> for each <protect-pointcut> sub-element
Map pointcutMap = parseProtectPointcuts(parserContext,
DomUtils.getChildElementsByTagName(element, Elements.PROTECT_POINTCUT));
if (pointcutMap.size() > 0) {
registerProtectPointcutPostProcessor(parserContext, pointcutMap, mapBasedMethodDefinitionSource, source);
boolean useJsr250 = "enabled".equals(element.getAttribute(ATT_USE_JSR250));
boolean useSecured = "enabled".equals(element.getAttribute(ATT_USE_SECURED));
// Check the required classes are present
if (useSecured) {
validatePresent(SECURED_METHOD_DEFINITION_SOURCE_CLASS);
validatePresent(SECURED_DEPENDENCY_CLASS);
}
if (useJsr250) {
validatePresent(JSR_250_SECURITY_METHOD_DEFINITION_SOURCE_CLASS);
validatePresent(JSR_250_VOTER_CLASS);
validatePresent(JSR_250_DEPENDENCY_CLASS);
}
registerDelegatingMethodDefinitionSource(parserContext, delegates, source);
// Now create a Map<String, ConfigAttribute> for each <protect-pointcut> sub-element
Map pointcutMap = new LinkedHashMap();
List protect = DomUtils.getChildElementsByTagName(element, Elements.PROTECT_POINTCUT);
for (Iterator i = protect.iterator(); i.hasNext();) {
Element childElt = (Element) i.next();
String accessConfig = childElt.getAttribute(ATT_ACCESS);
String expression = childElt.getAttribute(ATT_EXPRESSION);
Assert.hasText(accessConfig, "Access configuration required for '" + childElt + "'");
Assert.hasText(expression, "Expression required for '" + childElt + "'");
ConfigAttributeDefinition def = new ConfigAttributeDefinition(StringUtils.commaDelimitedListToStringArray(accessConfig));
pointcutMap.put(expression, def);
}
MapBasedMethodDefinitionSource mapBasedMethodDefinitionSource = new MapBasedMethodDefinitionSource();
// Now create and populate our ProtectPointcutBeanPostProcessor, if needed
if (pointcutMap.size() > 0) {
RootBeanDefinition ppbp = new RootBeanDefinition(ProtectPointcutPostProcessor.class);
ppbp.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
ppbp.getConstructorArgumentValues().addGenericArgumentValue(mapBasedMethodDefinitionSource);
ppbp.getPropertyValues().addPropertyValue("pointcutMap", pointcutMap);
parserContext.getRegistry().registerBeanDefinition(BeanIds.PROTECT_POINTCUT_POST_PROCESSOR, ppbp);
}
// Create our list of method metadata delegates
ManagedList delegates = new ManagedList();
delegates.add(mapBasedMethodDefinitionSource);
if (useSecured) {
delegates.add(BeanDefinitionBuilder.rootBeanDefinition(SECURED_METHOD_DEFINITION_SOURCE_CLASS).getBeanDefinition());
}
if (useJsr250) {
delegates.add(BeanDefinitionBuilder.rootBeanDefinition(JSR_250_SECURITY_METHOD_DEFINITION_SOURCE_CLASS).getBeanDefinition());
}
// Register our DelegatingMethodDefinitionSource
RootBeanDefinition delegatingMethodDefinitionSource = new RootBeanDefinition(DelegatingMethodDefinitionSource.class);
delegatingMethodDefinitionSource.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
delegatingMethodDefinitionSource.getPropertyValues().addPropertyValue("methodDefinitionSources", delegates);
parserContext.getRegistry().registerBeanDefinition(BeanIds.DELEGATING_METHOD_DEFINITION_SOURCE, delegatingMethodDefinitionSource);
// Register the applicable AccessDecisionManager, handling the special JSR 250 voter if being used
String accessManagerId = element.getAttribute(ATT_ACCESS_MGR);
if (!StringUtils.hasText(accessManagerId)) {
ConfigUtils.registerDefaultAccessManagerIfNecessary(parserContext);
if (jsr250Enabled) {
if (useJsr250) {
ConfigUtils.addVoter(new RootBeanDefinition(JSR_250_VOTER_CLASS, null, null), parserContext);
}
accessManagerId = BeanIds.ACCESS_MANAGER;
}
registerMethodSecurityInterceptor(parserContext, accessManagerId, source);
registerAdvisor(parserContext, source);
AopNamespaceUtils.registerAutoProxyCreatorIfNecessary(parserContext, element);
return null;
}
/**
* Checks whether JSR-250 and/or Secured annotations are enabled and adds the appropriate
* MethodDefinitionSource delegates if required.
*/
private boolean registerAnnotationBasedMethodDefinitionSources(Element element, ParserContext pc, ManagedList delegates) {
boolean useJsr250 = "enabled".equals(element.getAttribute(ATT_USE_JSR250));
boolean useSecured = "enabled".equals(element.getAttribute(ATT_USE_SECURED));
// Check the required classes are present
if (useSecured) {
validatePresent(SECURED_METHOD_DEFINITION_SOURCE_CLASS, element, pc);
validatePresent(SECURED_DEPENDENCY_CLASS, element, pc);
delegates.add(BeanDefinitionBuilder.rootBeanDefinition(SECURED_METHOD_DEFINITION_SOURCE_CLASS).getBeanDefinition());
}
if (useJsr250) {
validatePresent(JSR_250_SECURITY_METHOD_DEFINITION_SOURCE_CLASS, element, pc);
validatePresent(JSR_250_VOTER_CLASS, element, pc);
delegates.add(BeanDefinitionBuilder.rootBeanDefinition(JSR_250_SECURITY_METHOD_DEFINITION_SOURCE_CLASS).getBeanDefinition());
}
return useJsr250;
}
private void registerDelegatingMethodDefinitionSource(ParserContext parserContext, ManagedList delegates, Object source) {
if (parserContext.getRegistry().containsBeanDefinition(BeanIds.DELEGATING_METHOD_DEFINITION_SOURCE)) {
parserContext.getReaderContext().error("Duplicate <global-method-security> detected.", source);
}
RootBeanDefinition delegatingMethodDefinitionSource = new RootBeanDefinition(DelegatingMethodDefinitionSource.class);
delegatingMethodDefinitionSource.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
delegatingMethodDefinitionSource.setSource(source);
delegatingMethodDefinitionSource.getPropertyValues().addPropertyValue("methodDefinitionSources", delegates);
parserContext.getRegistry().registerBeanDefinition(BeanIds.DELEGATING_METHOD_DEFINITION_SOURCE, delegatingMethodDefinitionSource);
}
private void registerProtectPointcutPostProcessor(ParserContext parserContext, Map pointcutMap,
MapBasedMethodDefinitionSource mapBasedMethodDefinitionSource, Object source) {
RootBeanDefinition ppbp = new RootBeanDefinition(ProtectPointcutPostProcessor.class);
ppbp.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
ppbp.setSource(source);
ppbp.getConstructorArgumentValues().addGenericArgumentValue(mapBasedMethodDefinitionSource);
ppbp.getPropertyValues().addPropertyValue("pointcutMap", pointcutMap);
parserContext.getRegistry().registerBeanDefinition(BeanIds.PROTECT_POINTCUT_POST_PROCESSOR, ppbp);
}
private Map parseProtectPointcuts(ParserContext parserContext, List protectPointcutElts) {
Map pointcutMap = new LinkedHashMap();
for (Iterator i = protectPointcutElts.iterator(); i.hasNext();) {
Element childElt = (Element) i.next();
String accessConfig = childElt.getAttribute(ATT_ACCESS);
String expression = childElt.getAttribute(ATT_EXPRESSION);
if (!StringUtils.hasText(accessConfig)) {
parserContext.getReaderContext().error("Access configuration required", parserContext.extractSource(childElt));
}
if (!StringUtils.hasText(expression)) {
parserContext.getReaderContext().error("Pointcut expression required", parserContext.extractSource(childElt));
}
ConfigAttributeDefinition def = new ConfigAttributeDefinition(StringUtils.commaDelimitedListToStringArray(accessConfig));
pointcutMap.put(expression, def);
}
return pointcutMap;
}
private void registerMethodSecurityInterceptor(ParserContext parserContext, String accessManagerId, Object source) {
// MethodSecurityInterceptor
RootBeanDefinition interceptor = new RootBeanDefinition(MethodSecurityInterceptor.class);
interceptor.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
interceptor.setSource(source);
interceptor.getPropertyValues().addPropertyValue("accessDecisionManager", new RuntimeBeanReference(accessManagerId));
interceptor.getPropertyValues().addPropertyValue("authenticationManager", new RuntimeBeanReference(BeanIds.AUTHENTICATION_MANAGER));
interceptor.getPropertyValues().addPropertyValue("objectDefinitionSource", new RuntimeBeanReference(BeanIds.DELEGATING_METHOD_DEFINITION_SOURCE));
parserContext.getRegistry().registerBeanDefinition(BeanIds.METHOD_SECURITY_INTERCEPTOR, interceptor);
parserContext.registerComponent(new BeanComponentDefinition(interceptor, BeanIds.METHOD_SECURITY_INTERCEPTOR));
parserContext.getRegistry().registerBeanDefinition(BeanIds.METHOD_SECURITY_INTERCEPTOR_POST_PROCESSOR,
new RootBeanDefinition(MethodSecurityInterceptorPostProcessor.class));
}
private void registerAdvisor(ParserContext parserContext, Object source) {
// MethodDefinitionSourceAdvisor
RootBeanDefinition advisor = new RootBeanDefinition(MethodDefinitionSourceAdvisor.class);
advisor.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
advisor.setSource(source);
advisor.getConstructorArgumentValues().addGenericArgumentValue(BeanIds.METHOD_SECURITY_INTERCEPTOR);
advisor.getConstructorArgumentValues().addGenericArgumentValue(new RuntimeBeanReference(BeanIds.DELEGATING_METHOD_DEFINITION_SOURCE));
advisor.getConstructorArgumentValues().addGenericArgumentValue(interceptor);
parserContext.getRegistry().registerBeanDefinition(BeanIds.METHOD_DEFINITION_SOURCE_ADVISOR, advisor);
parserContext.getRegistry().registerBeanDefinition(BeanIds.METHOD_DEFINITION_SOURCE_ADVISOR, advisor);
}
AopNamespaceUtils.registerAutoProxyCreatorIfNecessary(parserContext, element);
return null;
}
}
@@ -29,7 +29,6 @@ import org.springframework.security.securechannel.InsecureChannelProcessor;
import org.springframework.security.securechannel.SecureChannelProcessor;
import org.springframework.security.securechannel.RetryWithHttpEntryPoint;
import org.springframework.security.securechannel.RetryWithHttpsEntryPoint;
import org.springframework.security.ui.AccessDeniedHandlerImpl;
import org.springframework.security.ui.ExceptionTranslationFilter;
import org.springframework.security.ui.SessionFixationProtectionFilter;
import org.springframework.security.ui.webapp.DefaultLoginPageGeneratingFilter;
@@ -95,59 +94,139 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser {
static final String ATT_USER_SERVICE_REF = "user-service-ref";
static final String ATT_ENTRY_POINT_REF = "entry-point-ref";
static final String ATT_ONCE_PER_REQUEST = "once-per-request";
static final String ATT_ACCESS_DENIED_PAGE = "access-denied-page";
public BeanDefinition parse(Element element, ParserContext parserContext) {
ConfigUtils.registerProviderManagerIfNecessary(parserContext);
final BeanDefinitionRegistry registry = parserContext.getRegistry();
final UrlMatcher matcher = createUrlMatcher(element);
final Object source = parserContext.extractSource(element);
// SEC-501 - should paths stored in request maps be converted to lower case
// true if Ant path and using lower case
final boolean convertPathsToLowerCase = (matcher instanceof AntUrlPathMatcher) && matcher.requiresLowerCaseUrl();
final List interceptUrlElts = DomUtils.getChildElementsByTagName(element, Elements.INTERCEPT_URL);
final Map filterChainMap = new LinkedHashMap();
final LinkedHashMap channelRequestMap = new LinkedHashMap();
BeanDefinitionRegistry registry = parserContext.getRegistry();
RootBeanDefinition filterChainProxy = new RootBeanDefinition(FilterChainProxy.class);
RootBeanDefinition httpScif = new RootBeanDefinition(HttpSessionContextIntegrationFilter.class);
registerFilterChainProxy(parserContext, filterChainMap, matcher, source);
parseInterceptUrlsForChannelSecurityAndFilterChain(interceptUrlElts, filterChainMap, channelRequestMap,
convertPathsToLowerCase, parserContext);
BeanDefinition portMapper = new PortMappingsBeanDefinitionParser().parse(
DomUtils.getChildElementByTagName(element, Elements.PORT_MAPPINGS), parserContext);
registry.registerBeanDefinition(BeanIds.PORT_MAPPER, portMapper);
registerHttpSessionIntegrationFilter(element, parserContext);
RuntimeBeanReference portMapperRef = new RuntimeBeanReference(BeanIds.PORT_MAPPER);
String createSession = element.getAttribute(ATT_CREATE_SESSION);
if (OPT_CREATE_SESSION_ALWAYS.equals(createSession)) {
httpScif.getPropertyValues().addPropertyValue("allowSessionCreation", Boolean.TRUE);
httpScif.getPropertyValues().addPropertyValue("forceEagerSessionCreation", Boolean.TRUE);
} else if (OPT_CREATE_SESSION_NEVER.equals(createSession)) {
httpScif.getPropertyValues().addPropertyValue("allowSessionCreation", Boolean.FALSE);
httpScif.getPropertyValues().addPropertyValue("forceEagerSessionCreation", Boolean.FALSE);
} else {
createSession = DEF_CREATE_SESSION_IF_REQUIRED;
httpScif.getPropertyValues().addPropertyValue("allowSessionCreation", Boolean.TRUE);
httpScif.getPropertyValues().addPropertyValue("forceEagerSessionCreation", Boolean.FALSE);
}
BeanDefinitionBuilder filterSecurityInterceptorBuilder
= BeanDefinitionBuilder.rootBeanDefinition(FilterSecurityInterceptor.class);
BeanDefinitionBuilder exceptionTranslationFilterBuilder
= BeanDefinitionBuilder.rootBeanDefinition(ExceptionTranslationFilter.class);
Map filterChainMap = new LinkedHashMap();
registerServletApiFilter(element, parserContext);
// Set up the access manager reference for http
UrlMatcher matcher = createUrlMatcher(element);
filterChainProxy.getPropertyValues().addPropertyValue("matcher", matcher);
// Add servlet-api integration filter if required
String provideServletApi = element.getAttribute(ATT_SERVLET_API_PROVISION);
if (!StringUtils.hasText(provideServletApi)) {
provideServletApi = DEF_SERVLET_API_PROVISION;
}
if ("true".equals(provideServletApi)) {
parserContext.getRegistry().registerBeanDefinition(BeanIds.SECURITY_CONTEXT_HOLDER_AWARE_REQUEST_FILTER,
new RootBeanDefinition(SecurityContextHolderAwareRequestFilter.class));
}
filterChainProxy.getPropertyValues().addPropertyValue("filterChainMap", filterChainMap);
// Set up the access manager and authentication manager references for http
String accessManagerId = element.getAttribute(ATT_ACCESS_MGR);
if (!StringUtils.hasText(accessManagerId)) {
ConfigUtils.registerDefaultAccessManagerIfNecessary(parserContext);
accessManagerId = BeanIds.ACCESS_MANAGER;
}
filterSecurityInterceptorBuilder.addPropertyValue("accessDecisionManager",
new RuntimeBeanReference(accessManagerId));
filterSecurityInterceptorBuilder.addPropertyValue("authenticationManager",
ConfigUtils.registerProviderManagerIfNecessary(parserContext));
// SEC-501 - should paths stored in request maps be converted to lower case
// true if Ant path and using lower case
boolean convertPathsToLowerCase = (matcher instanceof AntUrlPathMatcher) && matcher.requiresLowerCaseUrl();
// Register the portMapper. A default will always be created, even if no element exists.
BeanDefinition portMapper = new PortMappingsBeanDefinitionParser().parse(
DomUtils.getChildElementByTagName(element, Elements.PORT_MAPPINGS), parserContext);
registry.registerBeanDefinition(BeanIds.PORT_MAPPER, portMapper);
registerExceptionTranslationFilter(element, parserContext);
LinkedHashMap channelRequestMap = new LinkedHashMap();
LinkedHashMap filterInvocationDefinitionMap = new LinkedHashMap();
List interceptUrlElts = DomUtils.getChildElementsByTagName(element, "intercept-url");
parseInterceptUrlsForChannelSecurityAndFilterChain(interceptUrlElts, filterChainMap, channelRequestMap,
convertPathsToLowerCase, parserContext);
parseInterceptUrlsForFilterInvocationRequestMap(interceptUrlElts, filterInvocationDefinitionMap,
convertPathsToLowerCase, parserContext);
DefaultFilterInvocationDefinitionSource interceptorFilterInvDefSource =
new DefaultFilterInvocationDefinitionSource(matcher, filterInvocationDefinitionMap);
DefaultFilterInvocationDefinitionSource channelFilterInvDefSource =
new DefaultFilterInvocationDefinitionSource(matcher, channelRequestMap);
filterSecurityInterceptorBuilder.addPropertyValue("objectDefinitionSource", interceptorFilterInvDefSource);
// Check if we need to register the channel processing beans
if (channelRequestMap.size() > 0) {
// At least one channel requirement has been specified
registerChannelProcessingBeans(parserContext, matcher, channelRequestMap);
}
registerFilterSecurityInterceptor(element, parserContext, matcher, accessManagerId,
parseInterceptUrlsForFilterInvocationRequestMap(interceptUrlElts, convertPathsToLowerCase, parserContext));
RootBeanDefinition channelFilter = new RootBeanDefinition(ChannelProcessingFilter.class);
channelFilter.getPropertyValues().addPropertyValue("channelDecisionManager",
new RuntimeBeanReference(BeanIds.CHANNEL_DECISION_MANAGER));
boolean sessionControlEnabled = registerConcurrentSessionControlBeansIfRequired(element, parserContext);
channelFilter.getPropertyValues().addPropertyValue("filterInvocationDefinitionSource",
channelFilterInvDefSource);
RootBeanDefinition channelDecisionManager = new RootBeanDefinition(ChannelDecisionManagerImpl.class);
ManagedList channelProcessors = new ManagedList(3);
RootBeanDefinition secureChannelProcessor = new RootBeanDefinition(SecureChannelProcessor.class);
RootBeanDefinition retryWithHttp = new RootBeanDefinition(RetryWithHttpEntryPoint.class);
RootBeanDefinition retryWithHttps = new RootBeanDefinition(RetryWithHttpsEntryPoint.class);
retryWithHttp.getPropertyValues().addPropertyValue("portMapper", portMapperRef);
retryWithHttps.getPropertyValues().addPropertyValue("portMapper", portMapperRef);
secureChannelProcessor.getPropertyValues().addPropertyValue("entryPoint", retryWithHttps);
RootBeanDefinition inSecureChannelProcessor = new RootBeanDefinition(InsecureChannelProcessor.class);
inSecureChannelProcessor.getPropertyValues().addPropertyValue("entryPoint", retryWithHttp);
channelProcessors.add(secureChannelProcessor);
channelProcessors.add(inSecureChannelProcessor);
channelDecisionManager.getPropertyValues().addPropertyValue("channelProcessors", channelProcessors);
registry.registerBeanDefinition(BeanIds.CHANNEL_PROCESSING_FILTER, channelFilter);
registry.registerBeanDefinition(BeanIds.CHANNEL_DECISION_MANAGER, channelDecisionManager);
}
Element sessionControlElt = DomUtils.getChildElementByTagName(element, Elements.CONCURRENT_SESSIONS);
if (sessionControlElt != null) {
new ConcurrentSessionsBeanDefinitionParser().parse(sessionControlElt, parserContext);
}
String sessionFixationAttribute = element.getAttribute(ATT_SESSION_FIXATION_PROTECTION);
registerSessionFixationProtectionFilter(parserContext, element.getAttribute(ATT_SESSION_FIXATION_PROTECTION),
sessionControlEnabled);
if(!StringUtils.hasText(sessionFixationAttribute)) {
sessionFixationAttribute = OPT_SESSION_FIXATION_MIGRATE_SESSION;
}
if (!sessionFixationAttribute.equals(OPT_SESSION_FIXATION_NO_PROTECTION)) {
BeanDefinitionBuilder sessionFixationFilter =
BeanDefinitionBuilder.rootBeanDefinition(SessionFixationProtectionFilter.class);
sessionFixationFilter.addPropertyValue("migrateSessionAttributes",
Boolean.valueOf(sessionFixationAttribute.equals(OPT_SESSION_FIXATION_MIGRATE_SESSION)));
if (sessionControlElt != null) {
sessionFixationFilter.addPropertyReference("sessionRegistry", BeanIds.SESSION_REGISTRY);
}
parserContext.getRegistry().registerBeanDefinition(BeanIds.SESSION_FIXATION_PROTECTION_FILTER,
sessionFixationFilter.getBeanDefinition());
}
boolean autoConfig = false;
if ("true".equals(element.getAttribute(ATT_AUTO_CONFIG))) {
@@ -163,10 +242,6 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser {
Element rememberMeElt = DomUtils.getChildElementByTagName(element, Elements.REMEMBER_ME);
if (rememberMeElt != null || autoConfig) {
new RememberMeBeanDefinitionParser().parse(rememberMeElt, parserContext);
// Post processor to inject RememberMeServices into filters which need it
RootBeanDefinition rememberMeInjectionPostProcessor = new RootBeanDefinition(RememberMeServicesInjectionBeanPostProcessor.class);
rememberMeInjectionPostProcessor.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
registry.registerBeanDefinition(BeanIds.REMEMBER_ME_SERVICES_INJECTION_POST_PROCESSOR, rememberMeInjectionPostProcessor);
}
Element logoutElt = DomUtils.getChildElementByTagName(element, Elements.LOGOUT);
@@ -181,164 +256,21 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser {
new X509BeanDefinitionParser().parse(x509Elt, parserContext);
}
// Register the post processors which will tie up the loose ends in the configuration once the app context has been created and all beans are available.
RootBeanDefinition postProcessor = new RootBeanDefinition(EntryPointInjectionBeanPostProcessor.class);
registry.registerBeanDefinition(BeanIds.FILTER_CHAIN_PROXY, filterChainProxy);
registry.registerAlias(BeanIds.FILTER_CHAIN_PROXY, BeanIds.SPRING_SECURITY_FILTER_CHAIN);
registry.registerBeanDefinition(BeanIds.HTTP_SESSION_CONTEXT_INTEGRATION_FILTER, httpScif);
registry.registerBeanDefinition(BeanIds.EXCEPTION_TRANSLATION_FILTER, exceptionTranslationFilterBuilder.getBeanDefinition());
registry.registerBeanDefinition(BeanIds.FILTER_SECURITY_INTERCEPTOR, filterSecurityInterceptorBuilder.getBeanDefinition());
// Register the post processor which will tie up the loose ends in the configuration once the app context has been created and all beans are available.
RootBeanDefinition postProcessor = new RootBeanDefinition(HttpSecurityConfigPostProcessor.class);
postProcessor.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
registry.registerBeanDefinition(BeanIds.ENTRY_POINT_INJECTION_POST_PROCESSOR, postProcessor);
RootBeanDefinition postProcessor2 = new RootBeanDefinition(UserDetailsServiceInjectionBeanPostProcessor.class);
postProcessor2.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
registry.registerBeanDefinition(BeanIds.USER_DETAILS_SERVICE_INJECTION_POST_PROCESSOR, postProcessor2);
registry.registerBeanDefinition(BeanIds.HTTP_POST_PROCESSOR, postProcessor);
return null;
}
private void registerFilterChainProxy(ParserContext pc, Map filterChainMap, UrlMatcher matcher, Object source) {
if (pc.getRegistry().containsBeanDefinition(BeanIds.FILTER_CHAIN_PROXY)) {
pc.getReaderContext().error("Duplicate <http> element detected", source);
}
RootBeanDefinition filterChainProxy = new RootBeanDefinition(FilterChainProxy.class);
filterChainProxy.setSource(source);
filterChainProxy.getPropertyValues().addPropertyValue("matcher", matcher);
filterChainProxy.getPropertyValues().addPropertyValue("filterChainMap", filterChainMap);
pc.getRegistry().registerBeanDefinition(BeanIds.FILTER_CHAIN_PROXY, filterChainProxy);
pc.getRegistry().registerAlias(BeanIds.FILTER_CHAIN_PROXY, BeanIds.SPRING_SECURITY_FILTER_CHAIN);
}
private void registerHttpSessionIntegrationFilter(Element element, ParserContext pc) {
RootBeanDefinition httpScif = new RootBeanDefinition(HttpSessionContextIntegrationFilter.class);
String createSession = element.getAttribute(ATT_CREATE_SESSION);
if (OPT_CREATE_SESSION_ALWAYS.equals(createSession)) {
httpScif.getPropertyValues().addPropertyValue("allowSessionCreation", Boolean.TRUE);
httpScif.getPropertyValues().addPropertyValue("forceEagerSessionCreation", Boolean.TRUE);
} else if (OPT_CREATE_SESSION_NEVER.equals(createSession)) {
httpScif.getPropertyValues().addPropertyValue("allowSessionCreation", Boolean.FALSE);
httpScif.getPropertyValues().addPropertyValue("forceEagerSessionCreation", Boolean.FALSE);
} else {
createSession = DEF_CREATE_SESSION_IF_REQUIRED;
httpScif.getPropertyValues().addPropertyValue("allowSessionCreation", Boolean.TRUE);
httpScif.getPropertyValues().addPropertyValue("forceEagerSessionCreation", Boolean.FALSE);
}
pc.getRegistry().registerBeanDefinition(BeanIds.HTTP_SESSION_CONTEXT_INTEGRATION_FILTER, httpScif);
ConfigUtils.addHttpFilter(pc, new RuntimeBeanReference(BeanIds.HTTP_SESSION_CONTEXT_INTEGRATION_FILTER));
}
// Adds the servlet-api integration filter if required
private void registerServletApiFilter(Element element, ParserContext pc) {
String provideServletApi = element.getAttribute(ATT_SERVLET_API_PROVISION);
if (!StringUtils.hasText(provideServletApi)) {
provideServletApi = DEF_SERVLET_API_PROVISION;
}
if ("true".equals(provideServletApi)) {
pc.getRegistry().registerBeanDefinition(BeanIds.SECURITY_CONTEXT_HOLDER_AWARE_REQUEST_FILTER,
new RootBeanDefinition(SecurityContextHolderAwareRequestFilter.class));
ConfigUtils.addHttpFilter(pc, new RuntimeBeanReference(BeanIds.SECURITY_CONTEXT_HOLDER_AWARE_REQUEST_FILTER));
}
}
private boolean registerConcurrentSessionControlBeansIfRequired(Element element, ParserContext parserContext) {
Element sessionControlElt = DomUtils.getChildElementByTagName(element, Elements.CONCURRENT_SESSIONS);
if (sessionControlElt == null) {
return false;
}
new ConcurrentSessionsBeanDefinitionParser().parse(sessionControlElt, parserContext);
logger.info("Concurrent session filter in use, setting 'forceEagerSessionCreation' to true");
BeanDefinition sessionIntegrationFilter = parserContext.getRegistry().getBeanDefinition(BeanIds.HTTP_SESSION_CONTEXT_INTEGRATION_FILTER);
sessionIntegrationFilter.getPropertyValues().addPropertyValue("forceEagerSessionCreation", Boolean.TRUE);
return true;
}
private void registerExceptionTranslationFilter(Element element, ParserContext pc) {
String accessDeniedPage = element.getAttribute(ATT_ACCESS_DENIED_PAGE);
ConfigUtils.validateHttpRedirect(accessDeniedPage, pc, pc.extractSource(element));
BeanDefinitionBuilder exceptionTranslationFilterBuilder
= BeanDefinitionBuilder.rootBeanDefinition(ExceptionTranslationFilter.class);
if (StringUtils.hasText(accessDeniedPage)) {
AccessDeniedHandlerImpl accessDeniedHandler = new AccessDeniedHandlerImpl();
accessDeniedHandler.setErrorPage(accessDeniedPage);
exceptionTranslationFilterBuilder.addPropertyValue("accessDeniedHandler", accessDeniedHandler);
}
pc.getRegistry().registerBeanDefinition(BeanIds.EXCEPTION_TRANSLATION_FILTER, exceptionTranslationFilterBuilder.getBeanDefinition());
ConfigUtils.addHttpFilter(pc, new RuntimeBeanReference(BeanIds.EXCEPTION_TRANSLATION_FILTER));
}
private void registerFilterSecurityInterceptor(Element element, ParserContext pc, UrlMatcher matcher,
String accessManagerId, LinkedHashMap filterInvocationDefinitionMap) {
BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(FilterSecurityInterceptor.class);
builder.addPropertyReference("accessDecisionManager", accessManagerId);
builder.addPropertyReference("authenticationManager", BeanIds.AUTHENTICATION_MANAGER);
if ("false".equals(element.getAttribute(ATT_ONCE_PER_REQUEST))) {
builder.addPropertyValue("observeOncePerRequest", Boolean.FALSE);
}
DefaultFilterInvocationDefinitionSource fids =
new DefaultFilterInvocationDefinitionSource(matcher, filterInvocationDefinitionMap);
fids.setStripQueryStringFromUrls(matcher instanceof AntUrlPathMatcher);
builder.addPropertyValue("objectDefinitionSource", fids);
pc.getRegistry().registerBeanDefinition(BeanIds.FILTER_SECURITY_INTERCEPTOR, builder.getBeanDefinition());
ConfigUtils.addHttpFilter(pc, new RuntimeBeanReference(BeanIds.FILTER_SECURITY_INTERCEPTOR));
}
private void registerChannelProcessingBeans(ParserContext pc, UrlMatcher matcher, LinkedHashMap channelRequestMap) {
RootBeanDefinition channelFilter = new RootBeanDefinition(ChannelProcessingFilter.class);
channelFilter.getPropertyValues().addPropertyValue("channelDecisionManager",
new RuntimeBeanReference(BeanIds.CHANNEL_DECISION_MANAGER));
DefaultFilterInvocationDefinitionSource channelFilterInvDefSource =
new DefaultFilterInvocationDefinitionSource(matcher, channelRequestMap);
channelFilterInvDefSource.setStripQueryStringFromUrls(matcher instanceof AntUrlPathMatcher);
channelFilter.getPropertyValues().addPropertyValue("filterInvocationDefinitionSource",
channelFilterInvDefSource);
RootBeanDefinition channelDecisionManager = new RootBeanDefinition(ChannelDecisionManagerImpl.class);
ManagedList channelProcessors = new ManagedList(3);
RootBeanDefinition secureChannelProcessor = new RootBeanDefinition(SecureChannelProcessor.class);
RootBeanDefinition retryWithHttp = new RootBeanDefinition(RetryWithHttpEntryPoint.class);
RootBeanDefinition retryWithHttps = new RootBeanDefinition(RetryWithHttpsEntryPoint.class);
RuntimeBeanReference portMapper = new RuntimeBeanReference(BeanIds.PORT_MAPPER);
retryWithHttp.getPropertyValues().addPropertyValue("portMapper", portMapper);
retryWithHttps.getPropertyValues().addPropertyValue("portMapper", portMapper);
secureChannelProcessor.getPropertyValues().addPropertyValue("entryPoint", retryWithHttps);
RootBeanDefinition inSecureChannelProcessor = new RootBeanDefinition(InsecureChannelProcessor.class);
inSecureChannelProcessor.getPropertyValues().addPropertyValue("entryPoint", retryWithHttp);
channelProcessors.add(secureChannelProcessor);
channelProcessors.add(inSecureChannelProcessor);
channelDecisionManager.getPropertyValues().addPropertyValue("channelProcessors", channelProcessors);
pc.getRegistry().registerBeanDefinition(BeanIds.CHANNEL_PROCESSING_FILTER, channelFilter);
ConfigUtils.addHttpFilter(pc, new RuntimeBeanReference(BeanIds.CHANNEL_PROCESSING_FILTER));
pc.getRegistry().registerBeanDefinition(BeanIds.CHANNEL_DECISION_MANAGER, channelDecisionManager);
}
private void registerSessionFixationProtectionFilter(ParserContext pc, String sessionFixationAttribute, boolean sessionControlEnabled) {
if(!StringUtils.hasText(sessionFixationAttribute)) {
sessionFixationAttribute = OPT_SESSION_FIXATION_MIGRATE_SESSION;
}
if (!sessionFixationAttribute.equals(OPT_SESSION_FIXATION_NO_PROTECTION)) {
BeanDefinitionBuilder sessionFixationFilter =
BeanDefinitionBuilder.rootBeanDefinition(SessionFixationProtectionFilter.class);
sessionFixationFilter.addPropertyValue("migrateSessionAttributes",
Boolean.valueOf(sessionFixationAttribute.equals(OPT_SESSION_FIXATION_MIGRATE_SESSION)));
if (sessionControlEnabled) {
sessionFixationFilter.addPropertyReference("sessionRegistry", BeanIds.SESSION_REGISTRY);
}
pc.getRegistry().registerBeanDefinition(BeanIds.SESSION_FIXATION_PROTECTION_FILTER,
sessionFixationFilter.getBeanDefinition());
ConfigUtils.addHttpFilter(pc, new RuntimeBeanReference(BeanIds.SESSION_FIXATION_PROTECTION_FILTER));
}
}
private void parseBasicFormLoginAndOpenID(Element element, ParserContext pc, boolean autoConfig) {
private void parseBasicFormLoginAndOpenID(Element element, ParserContext parserContext, boolean autoConfig) {
RootBeanDefinition formLoginFilter = null;
RootBeanDefinition formLoginEntryPoint = null;
String formLoginPage = null;
@@ -349,12 +281,12 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser {
String realm = element.getAttribute(ATT_REALM);
if (!StringUtils.hasText(realm)) {
realm = DEF_REALM;
}
}
Element basicAuthElt = DomUtils.getChildElementByTagName(element, Elements.BASIC_AUTH);
if (basicAuthElt != null || autoConfig) {
new BasicAuthenticationBeanDefinitionParser(realm).parse(basicAuthElt, pc);
}
new BasicAuthenticationBeanDefinitionParser(realm).parse(basicAuthElt, parserContext);
}
Element formLoginElt = DomUtils.getChildElementByTagName(element, Elements.FORM_LOGIN);
@@ -362,7 +294,7 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser {
FormLoginBeanDefinitionParser parser = new FormLoginBeanDefinitionParser("/j_spring_security_check",
"org.springframework.security.ui.webapp.AuthenticationProcessingFilter");
parser.parse(formLoginElt, pc);
parser.parse(formLoginElt, parserContext);
formLoginFilter = parser.getFilterBean();
formLoginEntryPoint = parser.getEntryPointBean();
formLoginPage = parser.getLoginPage();
@@ -374,7 +306,7 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser {
FormLoginBeanDefinitionParser parser = new FormLoginBeanDefinitionParser("/j_spring_openid_security_check",
"org.springframework.security.ui.openid.OpenIDAuthenticationProcessingFilter");
parser.parse(openIDLoginElt, pc);
parser.parse(openIDLoginElt, parserContext);
openIDFilter = parser.getFilterBean();
openIDEntryPoint = parser.getEntryPointBean();
openIDLoginPage = parser.getLoginPage();
@@ -389,28 +321,27 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser {
}
BeanDefinition openIDProvider = openIDProviderBuilder.getBeanDefinition();
pc.getRegistry().registerBeanDefinition(BeanIds.OPEN_ID_PROVIDER, openIDProvider);
ConfigUtils.getRegisteredProviders(pc).add(new RuntimeBeanReference(BeanIds.OPEN_ID_PROVIDER));
ConfigUtils.getRegisteredProviders(parserContext).add(openIDProvider);
parserContext.getRegistry().registerBeanDefinition(BeanIds.OPEN_ID_PROVIDER, openIDProvider);
}
boolean needLoginPage = false;
if (formLoginFilter == null && openIDFilter == null) {
return;
}
if (formLoginFilter != null) {
needLoginPage = true;
pc.getRegistry().registerBeanDefinition(BeanIds.FORM_LOGIN_FILTER, formLoginFilter);
ConfigUtils.addHttpFilter(pc, new RuntimeBeanReference(BeanIds.FORM_LOGIN_FILTER));
pc.getRegistry().registerBeanDefinition(BeanIds.FORM_LOGIN_ENTRY_POINT, formLoginEntryPoint);
parserContext.getRegistry().registerBeanDefinition(BeanIds.FORM_LOGIN_FILTER, formLoginFilter);
parserContext.getRegistry().registerBeanDefinition(BeanIds.FORM_LOGIN_ENTRY_POINT, formLoginEntryPoint);
}
if (openIDFilter != null) {
needLoginPage = true;
pc.getRegistry().registerBeanDefinition(BeanIds.OPEN_ID_FILTER, openIDFilter);
ConfigUtils.addHttpFilter(pc, new RuntimeBeanReference(BeanIds.OPEN_ID_FILTER));
pc.getRegistry().registerBeanDefinition(BeanIds.OPEN_ID_ENTRY_POINT, openIDEntryPoint);
parserContext.getRegistry().registerBeanDefinition(BeanIds.OPEN_ID_FILTER, openIDFilter);
parserContext.getRegistry().registerBeanDefinition(BeanIds.OPEN_ID_ENTRY_POINT, openIDEntryPoint);
}
// If no login page has been defined, add in the default page generator.
if (needLoginPage && formLoginPage == null && openIDLoginPage == null) {
if (formLoginPage == null && openIDLoginPage == null) {
logger.info("No login page configured. The default internal one will be used. Use the '"
+ FormLoginBeanDefinitionParser.ATT_LOGIN_PAGE + "' attribute to set the URL of the login page.");
BeanDefinitionBuilder loginPageFilter =
@@ -424,9 +355,8 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser {
loginPageFilter.addConstructorArg(new RuntimeBeanReference(BeanIds.OPEN_ID_FILTER));
}
pc.getRegistry().registerBeanDefinition(BeanIds.DEFAULT_LOGIN_PAGE_GENERATING_FILTER,
parserContext.getRegistry().registerBeanDefinition(BeanIds.DEFAULT_LOGIN_PAGE_GENERATING_FILTER,
loginPageFilter.getBeanDefinition());
ConfigUtils.addHttpFilter(pc, new RuntimeBeanReference(BeanIds.DEFAULT_LOGIN_PAGE_GENERATING_FILTER));
}
// We need to establish the main entry point.
@@ -434,39 +364,30 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser {
String customEntryPoint = element.getAttribute(ATT_ENTRY_POINT_REF);
if (StringUtils.hasText(customEntryPoint)) {
pc.getRegistry().registerAlias(customEntryPoint, BeanIds.MAIN_ENTRY_POINT);
parserContext.getRegistry().registerAlias(customEntryPoint, BeanIds.MAIN_ENTRY_POINT);
return;
}
// Basic takes precedence if explicit element is used and no others are configured
if (basicAuthElt != null && formLoginElt == null && openIDLoginElt == null) {
pc.getRegistry().registerAlias(BeanIds.BASIC_AUTHENTICATION_ENTRY_POINT, BeanIds.MAIN_ENTRY_POINT);
parserContext.getRegistry().registerAlias(BeanIds.BASIC_AUTHENTICATION_ENTRY_POINT, BeanIds.MAIN_ENTRY_POINT);
return;
}
// If formLogin has been enabled either through an element or auto-config, then it is used if no openID login page
// has been set
if (formLoginFilter != null && openIDLoginPage == null) {
pc.getRegistry().registerAlias(BeanIds.FORM_LOGIN_ENTRY_POINT, BeanIds.MAIN_ENTRY_POINT);
parserContext.getRegistry().registerAlias(BeanIds.FORM_LOGIN_ENTRY_POINT, BeanIds.MAIN_ENTRY_POINT);
return;
}
// Otherwise use OpenID if enabled
// Otherwise use OpenID
if (openIDFilter != null && formLoginFilter == null) {
pc.getRegistry().registerAlias(BeanIds.OPEN_ID_ENTRY_POINT, BeanIds.MAIN_ENTRY_POINT);
parserContext.getRegistry().registerAlias(BeanIds.OPEN_ID_ENTRY_POINT, BeanIds.MAIN_ENTRY_POINT);
return;
}
// If X.509 has been enabled, use the preauth entry point.
if (DomUtils.getChildElementByTagName(element, Elements.X509) != null) {
pc.getRegistry().registerAlias(BeanIds.PRE_AUTH_ENTRY_POINT, BeanIds.MAIN_ENTRY_POINT);
return;
}
pc.getReaderContext().error("No AuthenticationEntryPoint could be established. Please " +
"make sure you have a login mechanism configured through the namespace (such as form-login) or " +
"specify a custom AuthenticationEntryPoint with the custom-entry-point-ref attribute ",
pc.extractSource(element));
throw new IllegalStateException("Couldn't set entry point");
}
static UrlMatcher createUrlMatcher(Element element) {
@@ -561,9 +482,9 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser {
}
}
static LinkedHashMap parseInterceptUrlsForFilterInvocationRequestMap(List urlElts, boolean useLowerCasePaths, ParserContext parserContext) {
LinkedHashMap filterInvocationDefinitionMap = new LinkedHashMap();
static void parseInterceptUrlsForFilterInvocationRequestMap(List urlElts, Map filterInvocationDefinitionMap,
boolean useLowerCasePaths, ParserContext parserContext) {
Iterator urlEltsIterator = urlElts.iterator();
ConfigAttributeEditor editor = new ConfigAttributeEditor();
@@ -593,8 +514,6 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser {
filterInvocationDefinitionMap.put(new RequestKey(path, method), editor.getValue());
}
}
return filterInvocationDefinitionMap;
}
}
@@ -0,0 +1,288 @@
package org.springframework.security.config;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import javax.servlet.Filter;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.PropertyValue;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.config.RuntimeBeanReference;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.core.OrderComparator;
import org.springframework.core.Ordered;
import org.springframework.security.concurrent.ConcurrentSessionFilter;
import org.springframework.security.context.HttpSessionContextIntegrationFilter;
import org.springframework.security.ui.AbstractProcessingFilter;
import org.springframework.security.ui.AuthenticationEntryPoint;
import org.springframework.security.ui.basicauth.BasicProcessingFilter;
import org.springframework.security.ui.rememberme.RememberMeServices;
import org.springframework.security.userdetails.UserDetailsByNameServiceWrapper;
import org.springframework.security.util.FilterChainProxy;
import org.springframework.util.Assert;
/**
* Responsible for tying up the HTTP security configuration - building ordered filter stack and linking up
* with other beans.
*
* @author Luke Taylor
* @author Ben Alex
* @version $Id$
* @since 2.0
*/
public class HttpSecurityConfigPostProcessor implements BeanFactoryPostProcessor, Ordered {
private Log logger = LogFactory.getLog(getClass());
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
injectUserDetailsServiceIntoRememberMeServices(beanFactory);
injectUserDetailsServiceIntoX509Provider(beanFactory);
injectUserDetailsServiceIntoOpenIDProvider(beanFactory);
injectAuthenticationEntryPointIntoExceptionTranslationFilter(beanFactory);
injectRememberMeServicesIntoFiltersRequiringIt(beanFactory);
configureFilterChain(beanFactory);
}
private void injectUserDetailsServiceIntoRememberMeServices(ConfigurableListableBeanFactory bf) {
try {
BeanDefinition rememberMeServices = bf.getBeanDefinition(BeanIds.REMEMBER_ME_SERVICES);
PropertyValue pv = rememberMeServices.getPropertyValues().getPropertyValue("userDetailsService");
if (pv == null) {
rememberMeServices.getPropertyValues().addPropertyValue("userDetailsService",
ConfigUtils.getUserDetailsService(bf));
} else {
RuntimeBeanReference cachingUserService = getCachingUserService(bf, pv.getValue());
if (cachingUserService != null) {
rememberMeServices.getPropertyValues().addPropertyValue("userDetailsService", cachingUserService);
}
}
} catch (NoSuchBeanDefinitionException e) {
// ignore
}
}
private void injectUserDetailsServiceIntoX509Provider(ConfigurableListableBeanFactory bf) {
try {
BeanDefinition x509AuthProvider = bf.getBeanDefinition(BeanIds.X509_AUTH_PROVIDER);
PropertyValue pv = x509AuthProvider.getPropertyValues().getPropertyValue("preAuthenticatedUserDetailsService");
if (pv == null) {
UserDetailsByNameServiceWrapper preAuthUserService = new UserDetailsByNameServiceWrapper();
preAuthUserService.setUserDetailsService(ConfigUtils.getUserDetailsService(bf));
x509AuthProvider.getPropertyValues().addPropertyValue("preAuthenticatedUserDetailsService",
preAuthUserService);
} else {
RootBeanDefinition preAuthUserService = (RootBeanDefinition) pv.getValue();
Object userService =
preAuthUserService.getPropertyValues().getPropertyValue("userDetailsService").getValue();
RuntimeBeanReference cachingUserService = getCachingUserService(bf, userService);
if (cachingUserService != null) {
preAuthUserService.getPropertyValues().addPropertyValue("userDetailsService", cachingUserService);
}
}
} catch (NoSuchBeanDefinitionException e) {
// ignore
}
}
private void injectUserDetailsServiceIntoOpenIDProvider(ConfigurableListableBeanFactory beanFactory) {
try {
BeanDefinition openIDProvider = beanFactory.getBeanDefinition(BeanIds.OPEN_ID_PROVIDER);
PropertyValue pv = openIDProvider.getPropertyValues().getPropertyValue("userDetailsService");
if (pv == null) {
openIDProvider.getPropertyValues().addPropertyValue("userDetailsService",
ConfigUtils.getUserDetailsService(beanFactory));
}
} catch (NoSuchBeanDefinitionException e) {
// ignore
}
}
private RuntimeBeanReference getCachingUserService(ConfigurableListableBeanFactory bf, Object userServiceRef) {
Assert.isInstanceOf(RuntimeBeanReference.class, userServiceRef,
"userDetailsService property value must be a RuntimeBeanReference");
String id = ((RuntimeBeanReference)userServiceRef).getBeanName();
// Overwrite with the caching version if available
String cachingId = id + AbstractUserDetailsServiceBeanDefinitionParser.CACHING_SUFFIX;
if (bf.containsBeanDefinition(cachingId)) {
return new RuntimeBeanReference(cachingId);
}
return null;
}
/**
* Sets the remember-me services, if required, on any instances of AbstractProcessingFilter and
* BasicProcessingFilter.
*/
private void injectRememberMeServicesIntoFiltersRequiringIt(ConfigurableListableBeanFactory beanFactory) {
Map beans = beanFactory.getBeansOfType(RememberMeServices.class);
RememberMeServices rememberMeServices = null;
if(beans.size() == 0) {
logger.debug("No RememberMeServices configured");
return;
}
if (beans.size() == 1) {
rememberMeServices = (RememberMeServices) beans.values().toArray()[0];
} else {
throw new SecurityConfigurationException("More than one RememberMeServices bean found.");
}
if (rememberMeServices == null) {
return;
}
// Address AbstractProcessingFilter instances
Iterator filters = beanFactory.getBeansOfType(AbstractProcessingFilter.class).values().iterator();
while (filters.hasNext()) {
AbstractProcessingFilter filter = (AbstractProcessingFilter) filters.next();
logger.info("Using RememberMeServices " + rememberMeServices + " with filter " + filter);
filter.setRememberMeServices(rememberMeServices);
}
// Address BasicProcessingFilter instance, if it exists
// NB: For remember-me to be sent back, a user must submit a "_spring_security_remember_me" with their login request.
// Most of the time a user won't present such a parameter with their BASIC authentication request.
// In the future we might support setting the AbstractRememberMeServices.alwaysRemember = true, but I am reluctant to
// do so because it seems likely to lead to lower security for 99.99% of users if they set the property to true.
if (beanFactory.containsBean(BeanIds.BASIC_AUTHENTICATION_FILTER)) {
BasicProcessingFilter filter = (BasicProcessingFilter) beanFactory.getBean(BeanIds.BASIC_AUTHENTICATION_FILTER);
logger.info("Using RememberMeServices " + rememberMeServices + " with filter " + filter);
filter.setRememberMeServices(rememberMeServices);
}
}
/**
* Selects the entry point that should be used in ExceptionTranslationFilter. Strategy is
*
* <ol>
* <li>If only one, use that one.</li>
* <li>If more than one, use the form login entry point (if form login is being used), then try basic</li>
* <li>If still null, throw an exception (for now).</li>
* </ol>
*
*/
private void injectAuthenticationEntryPointIntoExceptionTranslationFilter(ConfigurableListableBeanFactory beanFactory) {
logger.info("Selecting AuthenticationEntryPoint for use in ExceptionTranslationFilter");
BeanDefinition etf =
beanFactory.getBeanDefinition(BeanIds.EXCEPTION_TRANSLATION_FILTER);
Map entryPointMap = beanFactory.getBeansOfType(AuthenticationEntryPoint.class);
List entryPoints = new ArrayList(entryPointMap.values());
Assert.isTrue(entryPoints.size() > 0, "No AuthenticationEntryPoint instances defined");
AuthenticationEntryPoint mainEntryPoint;
if (entryPoints.size() == 1) {
mainEntryPoint = (AuthenticationEntryPoint) entryPoints.get(0);
} else {
mainEntryPoint = (AuthenticationEntryPoint) beanFactory.getBean(BeanIds.MAIN_ENTRY_POINT);
if (mainEntryPoint == null) {
mainEntryPoint = (AuthenticationEntryPoint) entryPointMap.get(BeanIds.FORM_LOGIN_ENTRY_POINT);
}
if (mainEntryPoint == null) {
mainEntryPoint = (AuthenticationEntryPoint) entryPointMap.get(BeanIds.BASIC_AUTHENTICATION_ENTRY_POINT);
if (mainEntryPoint == null) {
throw new SecurityConfigurationException("Failed to resolve authentication entry point");
}
}
}
logger.info("Main AuthenticationEntryPoint set to " + mainEntryPoint);
etf.getPropertyValues().addPropertyValue("authenticationEntryPoint", mainEntryPoint);
}
private void configureFilterChain(ConfigurableListableBeanFactory beanFactory) {
FilterChainProxy filterChainProxy =
(FilterChainProxy) beanFactory.getBean(BeanIds.FILTER_CHAIN_PROXY);
// Set the default match
List defaultFilterChain = orderFilters(beanFactory);
// Note that this returns a copy
Map filterMap = filterChainProxy.getFilterChainMap();
String allUrlsMatch = filterChainProxy.getMatcher().getUniversalMatchPattern();
filterMap.put(allUrlsMatch, defaultFilterChain);
filterChainProxy.setFilterChainMap(filterMap);
Map sessionFilters = beanFactory.getBeansOfType(ConcurrentSessionFilter.class);
if (!sessionFilters.isEmpty()) {
logger.info("Concurrent session filter in use, setting 'forceEagerSessionCreation' to true");
HttpSessionContextIntegrationFilter scif = (HttpSessionContextIntegrationFilter)
beanFactory.getBean(BeanIds.HTTP_SESSION_CONTEXT_INTEGRATION_FILTER);
scif.setForceEagerSessionCreation(true);
}
logger.info("Configured filter chain(s): " + filterChainProxy);
}
private List orderFilters(ConfigurableListableBeanFactory beanFactory) {
Map filters = beanFactory.getBeansOfType(Filter.class);
Assert.notEmpty(filters, "No filters found in app context!");
Iterator ids = filters.keySet().iterator();
List orderedFilters = new ArrayList();
while (ids.hasNext()) {
String id = (String) ids.next();
Filter filter = (Filter) filters.get(id);
if (filter instanceof FilterChainProxy) {
continue;
}
// Filters must be Spring security filters or wrapped using <custom-filter>
if (!filter.getClass().getName().startsWith("org.springframework.security")) {
continue;
}
if (!(filter instanceof Ordered)) {
logger.info("Filter " + id + " doesn't implement the Ordered interface, skipping it.");
continue;
}
orderedFilters.add(filter);
}
Collections.sort(orderedFilters, new OrderComparator());
return orderedFilters;
}
public int getOrder() {
return HIGHEST_PRECEDENCE + 1;
}
}
@@ -1,8 +1,9 @@
package org.springframework.security.config;
import org.springframework.util.StringUtils;
import org.springframework.security.userdetails.jdbc.JdbcUserDetailsManager;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.beans.factory.BeanDefinitionStoreException;
import org.w3c.dom.Element;
@@ -12,45 +13,21 @@ import org.w3c.dom.Element;
*/
public class JdbcUserServiceBeanDefinitionParser extends AbstractUserDetailsServiceBeanDefinitionParser {
static final String ATT_DATA_SOURCE = "data-source-ref";
static final String ATT_USERS_BY_USERNAME_QUERY = "users-by-username-query";
static final String ATT_AUTHORITIES_BY_USERNAME_QUERY = "authorities-by-username-query";
static final String ATT_GROUP_AUTHORITIES_QUERY = "group-authorities-by-username-query";
static final String ATT_ROLE_PREFIX = "role-prefix";
protected String getBeanClassName(Element element) {
return "org.springframework.security.userdetails.jdbc.JdbcUserDetailsManager";
protected Class getBeanClass(Element element) {
return JdbcUserDetailsManager.class;
}
protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
// TODO: Set authenticationManager property
String dataSource = element.getAttribute(ATT_DATA_SOURCE);
// An explicit dataSource was specified, so use it
if (dataSource != null) {
builder.addPropertyReference("dataSource", dataSource);
} else {
parserContext.getReaderContext().error(ATT_DATA_SOURCE + " is required for "
+ Elements.JDBC_USER_SERVICE, parserContext.extractSource(element));
}
String usersQuery = element.getAttribute(ATT_USERS_BY_USERNAME_QUERY);
String authoritiesQuery = element.getAttribute(ATT_AUTHORITIES_BY_USERNAME_QUERY);
String groupAuthoritiesQuery = element.getAttribute(ATT_GROUP_AUTHORITIES_QUERY);
String rolePrefix = element.getAttribute(ATT_ROLE_PREFIX);
if (StringUtils.hasText(rolePrefix)) {
builder.addPropertyValue("rolePrefix", rolePrefix);
}
if (StringUtils.hasText(usersQuery)) {
builder.addPropertyValue("usersByUsernameQuery", usersQuery);
}
if (StringUtils.hasText(authoritiesQuery)) {
builder.addPropertyValue("authoritiesByUsernameQuery", authoritiesQuery);
}
if (StringUtils.hasText(groupAuthoritiesQuery)) {
builder.addPropertyValue("enableGroups", Boolean.TRUE);
builder.addPropertyValue("authoritiesByUsernameQuery", groupAuthoritiesQuery);
// TODO: Have some sensible fallback if dataSource not specified, eg autowire
throw new BeanDefinitionStoreException(ATT_DATA_SOURCE + " is required for "
+ Elements.JDBC_USER_SERVICE );
}
}
}
@@ -1,13 +1,14 @@
package org.springframework.security.config;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.security.ldap.SpringSecurityContextSource;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.beans.BeansException;
import org.springframework.core.Ordered;
import org.springframework.security.ldap.SpringSecurityContextSource;
import java.util.Map;
/**
* @author Luke Taylor
@@ -16,56 +17,35 @@ import org.springframework.security.ldap.SpringSecurityContextSource;
*/
class LdapConfigUtils {
/**
* Checks for the presence of a ContextSource instance. Also supplies the standard reference to any
* unconfigured <ldap-authentication-provider> or <ldap-user-service> beans. This is
* necessary in cases where the user has given the server a specific Id, but hasn't used
* the server-ref attribute to link this to the other ldap definitions. See SEC-799.
*/
/** Checks for the presence of a ContextSource instance */
private static class ContextSourceSettingPostProcessor implements BeanFactoryPostProcessor, Ordered {
/** If set to true, a bean parser has indicated that the default context source name needs to be set */
private boolean defaultNameRequired;
public void postProcessBeanFactory(ConfigurableListableBeanFactory bf) throws BeansException {
String[] sources = bf.getBeanNamesForType(SpringSecurityContextSource.class);
Map beans = bf.getBeansOfType(SpringSecurityContextSource.class);
if (sources.length == 0) {
if (beans.size() == 0) {
throw new SecurityConfigurationException("No SpringSecurityContextSource instances found. Have you " +
"added an <" + Elements.LDAP_SERVER + " /> element to your application context?");
}
if (!bf.containsBean(BeanIds.CONTEXT_SOURCE) && defaultNameRequired) {
if (sources.length > 1) {
throw new SecurityConfigurationException("More than one SpringSecurityContextSource instance found. " +
"Please specify a specific server id using the 'server-ref' attribute when configuring your <" +
Elements.LDAP_PROVIDER + "> " + "or <" + Elements.LDAP_USER_SERVICE + ">.");
}
bf.registerAlias(sources[0], BeanIds.CONTEXT_SOURCE);
}
}
public void setDefaultNameRequired(boolean defaultNameRequired) {
this.defaultNameRequired = defaultNameRequired;
// else if (beans.size() > 1) {
// throw new SecurityConfigurationException("More than one SpringSecurityContextSource instance found. " +
// "Please specify a specific server id when configuring your <" + Elements.LDAP_PROVIDER + "> " +
// "or <" + Elements.LDAP_USER_SERVICE + ">.");
// }
}
public int getOrder() {
return LOWEST_PRECEDENCE;
}
}
static void registerPostProcessorIfNecessary(BeanDefinitionRegistry registry, boolean defaultNameRequired) {
static void registerPostProcessorIfNecessary(BeanDefinitionRegistry registry) {
if (registry.containsBeanDefinition(BeanIds.CONTEXT_SOURCE_SETTING_POST_PROCESSOR)) {
if (defaultNameRequired) {
BeanDefinition bd = registry.getBeanDefinition(BeanIds.CONTEXT_SOURCE_SETTING_POST_PROCESSOR);
bd.getPropertyValues().addPropertyValue("defaultNameRequired", Boolean.valueOf(defaultNameRequired));
}
return;
}
BeanDefinition bd = new RootBeanDefinition(ContextSourceSettingPostProcessor.class);
registry.registerBeanDefinition(BeanIds.CONTEXT_SOURCE_SETTING_POST_PROCESSOR, bd);
bd.getPropertyValues().addPropertyValue("defaultNameRequired", Boolean.valueOf(defaultNameRequired));
registry.registerBeanDefinition(BeanIds.CONTEXT_SOURCE_SETTING_POST_PROCESSOR,
new RootBeanDefinition(ContextSourceSettingPostProcessor.class));
}
}
@@ -1,8 +1,11 @@
package org.springframework.security.config;
import org.springframework.security.ldap.search.FilterBasedLdapUserSearch;
import org.springframework.security.providers.ldap.LdapAuthenticationProvider;
import org.springframework.security.providers.ldap.authenticator.BindAuthenticator;
import org.springframework.security.providers.ldap.authenticator.PasswordComparisonAuthenticator;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.RuntimeBeanReference;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.beans.factory.xml.BeanDefinitionParser;
import org.springframework.beans.factory.xml.ParserContext;
@@ -24,19 +27,14 @@ public class LdapProviderBeanDefinitionParser implements BeanDefinitionParser {
private Log logger = LogFactory.getLog(getClass());
private static final String ATT_USER_DN_PATTERN = "user-dn-pattern";
private static final String ATT_USER_PASSWORD = "password-attribute";
private static final String ATT_HASH = PasswordEncoderParser.ATT_HASH;
private static final String ATT_USER_PASSWORD= "password-attribute";
private static final String DEF_USER_SEARCH_FILTER = "uid={0}";
private static final String PROVIDER_CLASS = "org.springframework.security.providers.ldap.LdapAuthenticationProvider";
private static final String BIND_AUTH_CLASS = "org.springframework.security.providers.ldap.authenticator.BindAuthenticator";
private static final String PASSWD_AUTH_CLASS = "org.springframework.security.providers.ldap.authenticator.PasswordComparisonAuthenticator";
private static final String DEF_USER_SEARCH_FILTER="uid={0}";
public BeanDefinition parse(Element elt, ParserContext parserContext) {
RuntimeBeanReference contextSource = LdapUserServiceBeanDefinitionParser.parseServerReference(elt, parserContext);
BeanDefinition searchBean = LdapUserServiceBeanDefinitionParser.parseSearchBean(elt, parserContext);
RootBeanDefinition searchBean = LdapUserServiceBeanDefinitionParser.parseSearchBean(elt, parserContext);
String userDnPattern = elt.getAttribute(ATT_USER_DN_PATTERN);
String[] userDnPatternArray = new String[0];
@@ -46,62 +44,49 @@ public class LdapProviderBeanDefinitionParser implements BeanDefinitionParser {
// TODO: Validate the pattern and make sure it is a valid DN.
} else if (searchBean == null) {
logger.info("No search information or DN pattern specified. Using default search filter '" + DEF_USER_SEARCH_FILTER + "'");
BeanDefinitionBuilder searchBeanBuilder = BeanDefinitionBuilder.rootBeanDefinition(LdapUserServiceBeanDefinitionParser.LDAP_SEARCH_CLASS);
searchBeanBuilder.setSource(elt);
searchBeanBuilder.addConstructorArg("");
searchBeanBuilder.addConstructorArg(DEF_USER_SEARCH_FILTER);
searchBeanBuilder.addConstructorArg(contextSource);
searchBean = searchBeanBuilder.getBeanDefinition();
searchBean = new RootBeanDefinition(FilterBasedLdapUserSearch.class);
searchBean.setSource(elt);
searchBean.getConstructorArgumentValues().addIndexedArgumentValue(0, "");
searchBean.getConstructorArgumentValues().addIndexedArgumentValue(1, DEF_USER_SEARCH_FILTER);
searchBean.getConstructorArgumentValues().addIndexedArgumentValue(2, contextSource);
}
BeanDefinitionBuilder authenticatorBuilder =
BeanDefinitionBuilder.rootBeanDefinition(BIND_AUTH_CLASS);
RootBeanDefinition authenticator = new RootBeanDefinition(BindAuthenticator.class);
Element passwordCompareElt = DomUtils.getChildElementByTagName(elt, Elements.LDAP_PASSWORD_COMPARE);
if (passwordCompareElt != null) {
authenticatorBuilder =
BeanDefinitionBuilder.rootBeanDefinition(PASSWD_AUTH_CLASS);
authenticator = new RootBeanDefinition(PasswordComparisonAuthenticator.class);
String passwordAttribute = passwordCompareElt.getAttribute(ATT_USER_PASSWORD);
if (StringUtils.hasText(passwordAttribute)) {
authenticatorBuilder.addPropertyValue("passwordAttributeName", passwordAttribute);
authenticator.getPropertyValues().addPropertyValue("passwordAttributeName", passwordAttribute);
}
Element passwordEncoderElement = DomUtils.getChildElementByTagName(passwordCompareElt, Elements.PASSWORD_ENCODER);
String hash = passwordCompareElt.getAttribute(ATT_HASH);
if (passwordEncoderElement != null) {
if (StringUtils.hasText(hash)) {
parserContext.getReaderContext().warning("Attribute 'hash' cannot be used with 'password-encoder' and " +
"will be ignored.", parserContext.extractSource(elt));
}
PasswordEncoderParser pep = new PasswordEncoderParser(passwordEncoderElement, parserContext);
authenticatorBuilder.addPropertyValue("passwordEncoder", pep.getPasswordEncoder());
authenticator.getPropertyValues().addPropertyValue("passwordEncoder", pep.getPasswordEncoder());
if (pep.getSaltSource() != null) {
parserContext.getReaderContext().warning("Salt source information isn't valid when used with LDAP",
passwordEncoderElement);
parserContext.getReaderContext().warning("Salt source information isn't valid when used with LDAP", passwordEncoderElement);
}
} else if (StringUtils.hasText(hash)) {
Class encoderClass = (Class) PasswordEncoderParser.ENCODER_CLASSES.get(hash);
authenticatorBuilder.addPropertyValue("passwordEncoder", new RootBeanDefinition(encoderClass));
}
}
authenticatorBuilder.addConstructorArg(contextSource);
authenticatorBuilder.addPropertyValue("userDnPatterns", userDnPatternArray);
authenticator.getConstructorArgumentValues().addGenericArgumentValue(contextSource);
authenticator.getPropertyValues().addPropertyValue("userDnPatterns", userDnPatternArray);
if (searchBean != null) {
authenticatorBuilder.addPropertyValue("userSearch", searchBean);
authenticator.getPropertyValues().addPropertyValue("userSearch", searchBean);
}
BeanDefinitionBuilder ldapProvider = BeanDefinitionBuilder.rootBeanDefinition(PROVIDER_CLASS);
ldapProvider.addConstructorArg(authenticatorBuilder.getBeanDefinition());
ldapProvider.addConstructorArg(LdapUserServiceBeanDefinitionParser.parseAuthoritiesPopulator(elt, parserContext));
ldapProvider.addPropertyValue("userDetailsContextMapper",
LdapUserServiceBeanDefinitionParser.parseUserDetailsClass(elt, parserContext));
ConfigUtils.getRegisteredProviders(parserContext).add(ldapProvider.getBeanDefinition());
RootBeanDefinition ldapProvider = new RootBeanDefinition(LdapAuthenticationProvider.class);
ldapProvider.getConstructorArgumentValues().addGenericArgumentValue(authenticator);
ldapProvider.getConstructorArgumentValues().addGenericArgumentValue(LdapUserServiceBeanDefinitionParser.parseAuthoritiesPopulator(elt, parserContext));
LdapConfigUtils.registerPostProcessorIfNecessary(parserContext.getRegistry());
ConfigUtils.getRegisteredProviders(parserContext).add(ldapProvider);
return null;
}
@@ -1,10 +1,6 @@
package org.springframework.security.config;
import javax.naming.directory.Attribute;
import javax.naming.directory.Attributes;
import javax.naming.directory.BasicAttribute;
import javax.naming.directory.BasicAttributes;
import org.springframework.security.ldap.DefaultSpringSecurityContextSource;
import org.springframework.beans.factory.xml.BeanDefinitionParser;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.beans.factory.xml.AbstractBeanDefinitionParser;
@@ -12,6 +8,7 @@ import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.ManagedSet;
import org.springframework.ldap.core.DirContextAdapter;
import org.springframework.util.StringUtils;
import org.w3c.dom.Element;
@@ -23,9 +20,7 @@ import org.apache.commons.logging.LogFactory;
* @version $Id$
*/
public class LdapServerBeanDefinitionParser implements BeanDefinitionParser {
private static final String CONTEXT_SOURCE_CLASS="org.springframework.security.ldap.DefaultSpringSecurityContextSource";
private final Log logger = LogFactory.getLog(getClass());
private Log logger = LogFactory.getLog(getClass());
/** Defines the Url of the ldap server to use. If not specified, an embedded apache DS instance will be created */
private static final String ATT_URL = "url";
@@ -58,8 +53,7 @@ public class LdapServerBeanDefinitionParser implements BeanDefinitionParser {
if (!StringUtils.hasText(url)) {
contextSource = createEmbeddedServer(elt, parserContext);
} else {
contextSource = new RootBeanDefinition();
contextSource.setBeanClassName(CONTEXT_SOURCE_CLASS);
contextSource = new RootBeanDefinition(DefaultSpringSecurityContextSource.class);
contextSource.getConstructorArgumentValues().addIndexedArgumentValue(0, url);
}
@@ -98,22 +92,17 @@ public class LdapServerBeanDefinitionParser implements BeanDefinitionParser {
*/
private RootBeanDefinition createEmbeddedServer(Element element, ParserContext parserContext) {
Object source = parserContext.extractSource(element);
BeanDefinitionBuilder configuration =
BeanDefinitionBuilder.rootBeanDefinition("org.apache.directory.server.configuration.MutableServerStartupConfiguration");
BeanDefinitionBuilder partition =
BeanDefinitionBuilder.rootBeanDefinition("org.apache.directory.server.core.partition.impl.btree.MutableBTreePartitionConfiguration");
BeanDefinitionBuilder configuration = BeanDefinitionBuilder.rootBeanDefinition("org.apache.directory.server.configuration.MutableServerStartupConfiguration");
BeanDefinitionBuilder partition = BeanDefinitionBuilder.rootBeanDefinition("org.apache.directory.server.core.partition.impl.btree.MutableBTreePartitionConfiguration");
configuration.setSource(source);
partition.setSource(source);
Attributes rootAttributes = new BasicAttributes("dc", "springsecurity");
Attribute a = new BasicAttribute("objectClass");
a.add("top");
a.add("domain");
a.add("extensibleObject");
rootAttributes.put(a);
DirContextAdapter rootContext = new DirContextAdapter();
rootContext.setAttributeValues("objectClass", new String[] {"top", "domain", "extensibleObject"});
rootContext.setAttributeValue("dc", "springsecurity");
partition.addPropertyValue("name", "springsecurity");
partition.addPropertyValue("contextEntry", rootAttributes);
partition.addPropertyValue("contextEntry", rootContext.getAttributes());
String suffix = element.getAttribute(ATT_ROOT_SUFFIX);
@@ -142,15 +131,15 @@ public class LdapServerBeanDefinitionParser implements BeanDefinitionParser {
String url = "ldap://127.0.0.1:" + port + "/" + suffix;
BeanDefinitionBuilder contextSource = BeanDefinitionBuilder.rootBeanDefinition(CONTEXT_SOURCE_CLASS);
contextSource.addConstructorArg(url);
contextSource.addPropertyValue("userDn", "uid=admin,ou=system");
contextSource.addPropertyValue("password", "secret");
RootBeanDefinition contextSource = new RootBeanDefinition(DefaultSpringSecurityContextSource.class);
contextSource.getConstructorArgumentValues().addIndexedArgumentValue(0, url);
contextSource.getPropertyValues().addPropertyValue("userDn", "uid=admin,ou=system");
contextSource.getPropertyValues().addPropertyValue("password", "secret");
RootBeanDefinition apacheContainer = new RootBeanDefinition("org.springframework.security.config.ApacheDSContainer", null, null);
apacheContainer.setSource(source);
apacheContainer.getConstructorArgumentValues().addGenericArgumentValue(configuration.getBeanDefinition());
apacheContainer.getConstructorArgumentValues().addGenericArgumentValue(contextSource.getBeanDefinition());
apacheContainer.getConstructorArgumentValues().addGenericArgumentValue(contextSource);
String ldifs = element.getAttribute(ATT_LDIF_FILE);
if (!StringUtils.hasText(ldifs)) {
@@ -168,6 +157,6 @@ public class LdapServerBeanDefinitionParser implements BeanDefinitionParser {
parserContext.getRegistry().registerBeanDefinition(BeanIds.EMBEDDED_APACHE_DS, apacheContainer);
return (RootBeanDefinition) contextSource.getBeanDefinition();
return contextSource;
}
}
@@ -1,5 +1,8 @@
package org.springframework.security.config;
import org.springframework.security.userdetails.ldap.LdapUserDetailsService;
import org.springframework.security.ldap.search.FilterBasedLdapUserSearch;
import org.springframework.security.ldap.populator.DefaultLdapAuthoritiesPopulator;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.RootBeanDefinition;
@@ -14,7 +17,7 @@ import org.w3c.dom.Element;
* @since 2.0
*/
public class LdapUserServiceBeanDefinitionParser extends AbstractUserDetailsServiceBeanDefinitionParser {
public static final String ATT_SERVER = "server-ref";
public static final String ATT_SERVER = "server-ref";
public static final String ATT_USER_SEARCH_FILTER = "user-search-filter";
public static final String ATT_USER_SEARCH_BASE = "user-search-base";
public static final String DEF_USER_SEARCH_BASE = "";
@@ -24,20 +27,9 @@ public class LdapUserServiceBeanDefinitionParser extends AbstractUserDetailsServ
public static final String ATT_GROUP_ROLE_ATTRIBUTE = "group-role-attribute";
public static final String DEF_GROUP_SEARCH_FILTER = "(uniqueMember={0})";
public static final String DEF_GROUP_SEARCH_BASE = "ou=groups";
static final String ATT_ROLE_PREFIX = "role-prefix";
static final String ATT_USER_CLASS = "user-details-class";
static final String OPT_PERSON = "person";
static final String OPT_INETORGPERSON = "inetOrgPerson";
public static final String LDAP_SEARCH_CLASS = "org.springframework.security.ldap.search.FilterBasedLdapUserSearch";
public static final String PERSON_MAPPER_CLASS = "org.springframework.security.userdetails.ldap.PersonContextMapper";
public static final String INET_ORG_PERSON_MAPPER_CLASS = "org.springframework.security.userdetails.ldap.InetOrgPersonContextMapper";
public static final String LDAP_USER_MAPPER_CLASS = "org.springframework.security.userdetails.ldap.LdapUserDetailsMapper";
public static final String LDAP_AUTHORITIES_POPULATOR_CLASS = "org.springframework.security.ldap.populator.DefaultLdapAuthoritiesPopulator";
protected String getBeanClassName(Element element) {
return "org.springframework.security.userdetails.ldap.LdapUserDetailsService";
protected Class getBeanClass(Element element) {
return LdapUserDetailsService.class;
}
protected void doParse(Element elt, ParserContext parserContext, BeanDefinitionBuilder builder) {
@@ -48,7 +40,8 @@ public class LdapUserServiceBeanDefinitionParser extends AbstractUserDetailsServ
builder.addConstructorArg(parseSearchBean(elt, parserContext));
builder.addConstructorArg(parseAuthoritiesPopulator(elt, parserContext));
builder.addPropertyValue("userDetailsMapper", parseUserDetailsClass(elt, parserContext));
LdapConfigUtils.registerPostProcessorIfNecessary(parserContext.getRegistry());
}
static RootBeanDefinition parseSearchBean(Element elt, ParserContext parserContext) {
@@ -68,48 +61,33 @@ public class LdapUserServiceBeanDefinitionParser extends AbstractUserDetailsServ
return null;
}
BeanDefinitionBuilder searchBuilder = BeanDefinitionBuilder.rootBeanDefinition(LDAP_SEARCH_CLASS);
searchBuilder.setSource(source);
searchBuilder.addConstructorArg(userSearchBase);
searchBuilder.addConstructorArg(userSearchFilter);
searchBuilder.addConstructorArg(parseServerReference(elt, parserContext));
RootBeanDefinition search = new RootBeanDefinition(FilterBasedLdapUserSearch.class);
search.setSource(source);
search.getConstructorArgumentValues().addIndexedArgumentValue(0, userSearchBase);
search.getConstructorArgumentValues().addIndexedArgumentValue(1, userSearchFilter);
search.getConstructorArgumentValues().addIndexedArgumentValue(2, parseServerReference(elt, parserContext));
return (RootBeanDefinition) searchBuilder.getBeanDefinition();
return search;
}
static RuntimeBeanReference parseServerReference(Element elt, ParserContext parserContext) {
String server = elt.getAttribute(ATT_SERVER);
boolean requiresDefaultName = false;
if (!StringUtils.hasText(server)) {
server = BeanIds.CONTEXT_SOURCE;
requiresDefaultName = true;
}
RuntimeBeanReference contextSource = new RuntimeBeanReference(server);
contextSource.setSource(parserContext.extractSource(elt));
LdapConfigUtils.registerPostProcessorIfNecessary(parserContext.getRegistry(), requiresDefaultName);
return contextSource;
}
static RootBeanDefinition parseUserDetailsClass(Element elt, ParserContext parserContext) {
String userDetailsClass = elt.getAttribute(ATT_USER_CLASS);
if (OPT_PERSON.equals(userDetailsClass)) {
return new RootBeanDefinition(PERSON_MAPPER_CLASS, null, null);
} else if (OPT_INETORGPERSON.equals(userDetailsClass)) {
return new RootBeanDefinition(INET_ORG_PERSON_MAPPER_CLASS, null, null);
}
return new RootBeanDefinition(LDAP_USER_MAPPER_CLASS, null, null);
}
static RootBeanDefinition parseAuthoritiesPopulator(Element elt, ParserContext parserContext) {
String groupSearchFilter = elt.getAttribute(ATT_GROUP_SEARCH_FILTER);
String groupSearchBase = elt.getAttribute(ATT_GROUP_SEARCH_BASE);
String groupRoleAttribute = elt.getAttribute(ATT_GROUP_ROLE_ATTRIBUTE);
String rolePrefix = elt.getAttribute(ATT_ROLE_PREFIX);
if (!StringUtils.hasText(groupSearchFilter)) {
groupSearchFilter = DEF_GROUP_SEARCH_FILTER;
}
@@ -118,24 +96,16 @@ public class LdapUserServiceBeanDefinitionParser extends AbstractUserDetailsServ
groupSearchBase = DEF_GROUP_SEARCH_BASE;
}
BeanDefinitionBuilder populator = BeanDefinitionBuilder.rootBeanDefinition(LDAP_AUTHORITIES_POPULATOR_CLASS);
RootBeanDefinition populator = new RootBeanDefinition(DefaultLdapAuthoritiesPopulator.class);
populator.setSource(parserContext.extractSource(elt));
populator.addConstructorArg(parseServerReference(elt, parserContext));
populator.addConstructorArg(groupSearchBase);
populator.addPropertyValue("groupSearchFilter", groupSearchFilter);
populator.addPropertyValue("searchSubtree", Boolean.TRUE);
if (StringUtils.hasText(rolePrefix)) {
if ("none".equals(rolePrefix)) {
rolePrefix = "";
}
populator.addPropertyValue("rolePrefix", rolePrefix);
}
populator.getConstructorArgumentValues().addIndexedArgumentValue(0, parseServerReference(elt, parserContext));
populator.getConstructorArgumentValues().addIndexedArgumentValue(1, groupSearchBase);
populator.getPropertyValues().addPropertyValue("groupSearchFilter", groupSearchFilter);
if (StringUtils.hasLength(groupRoleAttribute)) {
populator.addPropertyValue("groupRoleAttribute", groupRoleAttribute);
populator.getPropertyValues().addPropertyValue("groupRoleAttribute", groupRoleAttribute);
}
return (RootBeanDefinition) populator.getBeanDefinition();
return populator;
}
}
@@ -31,18 +31,15 @@ public class LogoutBeanDefinitionParser implements BeanDefinitionParser {
String logoutSuccessUrl = null;
String invalidateSession = null;
BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(LogoutFilter.class);
if (element != null) {
Object source = parserContext.extractSource(element);
builder.setSource(source);
logoutUrl = element.getAttribute(ATT_LOGOUT_URL);
ConfigUtils.validateHttpRedirect(logoutUrl, parserContext, source);
logoutSuccessUrl = element.getAttribute(ATT_LOGOUT_SUCCESS_URL);
ConfigUtils.validateHttpRedirect(logoutSuccessUrl, parserContext, source);
invalidateSession = element.getAttribute(ATT_INVALIDATE_SESSION);
}
BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(LogoutFilter.class);
builder.setSource(parserContext.extractSource(element));
if (!StringUtils.hasText(logoutUrl)) {
logoutUrl = DEF_LOGOUT_URL;
}
@@ -73,8 +70,7 @@ public class LogoutBeanDefinitionParser implements BeanDefinitionParser {
builder.addConstructorArg(handlers);
parserContext.getRegistry().registerBeanDefinition(BeanIds.LOGOUT_FILTER, builder.getBeanDefinition());
ConfigUtils.addHttpFilter(parserContext, new RuntimeBeanReference(BeanIds.LOGOUT_FILTER));
return null;
}
}
@@ -1,48 +0,0 @@
package org.springframework.security.config;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.security.AfterInvocationManager;
import org.springframework.security.intercept.method.aopalliance.MethodSecurityInterceptor;
/**
* BeanPostProcessor which sets the AfterInvocationManager on the default MethodSecurityInterceptor,
* if one has been configured.
*
* @author Luke Taylor
* @version $Id$
*
*/
public class MethodSecurityInterceptorPostProcessor implements BeanPostProcessor, BeanFactoryAware{
private Log logger = LogFactory.getLog(getClass());
private BeanFactory beanFactory;
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if(!BeanIds.METHOD_SECURITY_INTERCEPTOR.equals(beanName)) {
return bean;
}
MethodSecurityInterceptor interceptor = (MethodSecurityInterceptor) bean;
if (beanFactory.containsBean(BeanIds.AFTER_INVOCATION_MANAGER)) {
logger.debug("Setting AfterInvocationManaer on MethodSecurityInterceptor");
interceptor.setAfterInvocationManager((AfterInvocationManager)
beanFactory.getBean(BeanIds.AFTER_INVOCATION_MANAGER));
}
return bean;
}
public Object postProcessAfterInitialization(Object bean, String beanName) {
return bean;
}
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = beanFactory;
}
}
@@ -1,29 +1,29 @@
package org.springframework.security.config;
import java.io.IOException;
import org.springframework.security.ui.FilterChainOrder;
import org.springframework.beans.factory.xml.BeanDefinitionDecorator;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.core.Ordered;
import org.springframework.util.StringUtils;
import org.springframework.util.Assert;
import org.w3c.dom.Node;
import org.w3c.dom.Element;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.config.RuntimeBeanReference;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.xml.BeanDefinitionDecorator;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.core.Ordered;
import org.springframework.security.ui.FilterChainOrder;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import javax.servlet.FilterChain;
import java.io.IOException;
/**
* Adds the decorated "Filter" bean into the standard filter chain maintained by the FilterChainProxy.
* This allows user to add their own custom filters to the security chain. If the user's filter
* Replaces a Spring bean of type "Filter" with a wrapper class which implements the <tt>Ordered</tt>
* interface. This allows user to add their own filter to the security chain. If the user's filter
* already implements Ordered, and no "order" attribute is specified, the filter's default order will be used.
*
* @author Luke Taylor
@@ -33,23 +33,21 @@ public class OrderedFilterBeanDefinitionDecorator implements BeanDefinitionDecor
public static final String ATT_AFTER = "after";
public static final String ATT_BEFORE = "before";
public static final String ATT_POSITION = "position";
public BeanDefinitionHolder decorate(Node node, BeanDefinitionHolder holder, ParserContext parserContext) {
Element elt = (Element)node;
String order = getOrder(elt, parserContext);
BeanDefinition filter = holder.getBeanDefinition();
BeanDefinitionBuilder wrapper = BeanDefinitionBuilder.rootBeanDefinition("org.springframework.security.config.OrderedFilterBeanDefinitionDecorator$OrderedFilterDecorator");
wrapper.addConstructorArg(holder.getBeanName());
wrapper.addConstructorArg(new RuntimeBeanReference(holder.getBeanName()));
wrapper.addConstructorArg(filter);
if (StringUtils.hasText(order)) {
wrapper.addPropertyValue("order", order);
}
ConfigUtils.addHttpFilter(parserContext, wrapper.getBeanDefinition());
return holder;
return new BeanDefinitionHolder(wrapper.getBeanDefinition(), holder.getBeanName());
}
/**
@@ -58,17 +56,7 @@ public class OrderedFilterBeanDefinitionDecorator implements BeanDefinitionDecor
private String getOrder(Element elt, ParserContext pc) {
String after = elt.getAttribute(ATT_AFTER);
String before = elt.getAttribute(ATT_BEFORE);
String position = elt.getAttribute(ATT_POSITION);
if(ConfigUtils.countNonEmpty(new String[] {after, before, position}) != 1) {
pc.getReaderContext().error("A single '" + ATT_AFTER + "', '" + ATT_BEFORE + "', or '" +
ATT_POSITION + "' attribute must be supplied", pc.extractSource(elt));
}
if (StringUtils.hasText(position)) {
return Integer.toString(FilterChainOrder.getOrder(position));
}
if (StringUtils.hasText(after)) {
return Integer.toString(FilterChainOrder.getOrder(after) + 1);
}
@@ -120,13 +108,5 @@ public class OrderedFilterBeanDefinitionDecorator implements BeanDefinitionDecor
public String getBeanName() {
return beanName;
}
public String toString() {
return "OrderedFilterDecorator[ delegate=" + delegate + "; order=" + getOrder() + "]";
}
Filter getDelegate() {
return delegate;
}
}
}
@@ -35,7 +35,6 @@ public class PasswordEncoderParser {
static final String ATT_BASE_64 = "base64";
static final String OPT_HASH_PLAINTEXT = "plaintext";
static final String OPT_HASH_SHA = "sha";
static final String OPT_HASH_SHA256 = "sha-256";
static final String OPT_HASH_MD4 = "md4";
static final String OPT_HASH_MD5 = "md5";
static final String OPT_HASH_LDAP_SHA = "{sha}";
@@ -46,7 +45,6 @@ public class PasswordEncoderParser {
ENCODER_CLASSES = new HashMap();
ENCODER_CLASSES.put(OPT_HASH_PLAINTEXT, PlaintextPasswordEncoder.class);
ENCODER_CLASSES.put(OPT_HASH_SHA, ShaPasswordEncoder.class);
ENCODER_CLASSES.put(OPT_HASH_SHA256, ShaPasswordEncoder.class);
ENCODER_CLASSES.put(OPT_HASH_MD4, Md4PasswordEncoder.class);
ENCODER_CLASSES.put(OPT_HASH_MD5, Md5PasswordEncoder.class);
ENCODER_CLASSES.put(OPT_HASH_LDAP_SHA, LdapShaPasswordEncoder.class);
@@ -76,11 +74,6 @@ public class PasswordEncoderParser {
} else {
Class beanClass = (Class) ENCODER_CLASSES.get(hash);
RootBeanDefinition beanDefinition = new RootBeanDefinition(beanClass);
if (OPT_HASH_SHA256.equals(hash)) {
beanDefinition.getConstructorArgumentValues().addIndexedArgumentValue(0, new Integer(256));
}
beanDefinition.setSource(parserContext.extractSource(element));
if (useBase64) {
if (BaseDigestPasswordEncoder.class.isAssignableFrom(beanClass)) {
@@ -23,14 +23,12 @@ import org.w3c.dom.Element;
*/
public class RememberMeBeanDefinitionParser implements BeanDefinitionParser {
static final String ATT_KEY = "key";
static final String DEF_KEY = "SpringSecured";
static final String DEF_KEY = "doesNotMatter";
static final String ATT_DATA_SOURCE = "data-source-ref";
static final String ATT_SERVICES_REF = "services-ref";
static final String ATT_DATA_SOURCE = "data-source";
static final String ATT_TOKEN_REPOSITORY = "token-repository-ref";
static final String ATT_USER_SERVICE_REF = "user-service-ref";
static final String ATT_TOKEN_VALIDITY = "token-validity-seconds";
protected final Log logger = LogFactory.getLog(getClass());
public BeanDefinition parse(Element element, ParserContext parserContext) {
@@ -39,46 +37,32 @@ public class RememberMeBeanDefinitionParser implements BeanDefinitionParser {
String key = null;
Object source = null;
String userServiceRef = null;
String rememberMeServicesRef = null;
String tokenValiditySeconds = null;
if (element != null) {
tokenRepository = element.getAttribute(ATT_TOKEN_REPOSITORY);
dataSource = element.getAttribute(ATT_DATA_SOURCE);
key = element.getAttribute(ATT_KEY);
userServiceRef = element.getAttribute(ATT_USER_SERVICE_REF);
rememberMeServicesRef = element.getAttribute(ATT_SERVICES_REF);
tokenValiditySeconds = element.getAttribute(ATT_TOKEN_VALIDITY);
userServiceRef = element.getAttribute(ATT_USER_SERVICE_REF);
source = parserContext.extractSource(element);
}
if (!StringUtils.hasText(key)) {
key = DEF_KEY;
}
RootBeanDefinition services = null;
RootBeanDefinition filter = new RootBeanDefinition(RememberMeProcessingFilter.class);
RootBeanDefinition services = new RootBeanDefinition(PersistentTokenBasedRememberMeServices.class);
filter.getPropertyValues().addPropertyValue("authenticationManager",
new RuntimeBeanReference(BeanIds.AUTHENTICATION_MANAGER));
boolean dataSourceSet = StringUtils.hasText(dataSource);
boolean tokenRepoSet = StringUtils.hasText(tokenRepository);
boolean servicesRefSet = StringUtils.hasText(rememberMeServicesRef);
boolean userServiceSet = StringUtils.hasText(userServiceRef);
boolean tokenValiditySet = StringUtils.hasText(tokenValiditySeconds);
if (servicesRefSet && (dataSourceSet || tokenRepoSet || userServiceSet || tokenValiditySet)) {
parserContext.getReaderContext().error(ATT_SERVICES_REF + " can't be used in combination with attributes "
+ ATT_TOKEN_REPOSITORY + "," + ATT_DATA_SOURCE + ", " + ATT_USER_SERVICE_REF + " or " + ATT_TOKEN_VALIDITY, source);
}
if (dataSourceSet && tokenRepoSet) {
parserContext.getReaderContext().error("Specify " + ATT_TOKEN_REPOSITORY + " or " +
ATT_DATA_SOURCE +" but not both", source);
parserContext.getReaderContext().error("Specify tokenRepository or dataSource but not both", element);
}
boolean isPersistent = dataSourceSet | tokenRepoSet;
if (isPersistent) {
Object tokenRepo;
services = new RootBeanDefinition(PersistentTokenBasedRememberMeServices.class);
if (tokenRepoSet) {
tokenRepo = new RuntimeBeanReference(tokenRepository);
@@ -88,51 +72,38 @@ public class RememberMeBeanDefinitionParser implements BeanDefinitionParser {
new RuntimeBeanReference(dataSource));
}
services.getPropertyValues().addPropertyValue("tokenRepository", tokenRepo);
} else if (!servicesRefSet) {
} else {
isPersistent = false;
services = new RootBeanDefinition(TokenBasedRememberMeServices.class);
}
if (services != null) {
if (userServiceSet) {
services.getPropertyValues().addPropertyValue("userDetailsService", new RuntimeBeanReference(userServiceRef));
}
if (tokenValiditySet) {
services.getPropertyValues().addPropertyValue("tokenValiditySeconds", new Integer(tokenValiditySeconds));
}
services.setSource(source);
services.getPropertyValues().addPropertyValue(ATT_KEY, key);
parserContext.getRegistry().registerBeanDefinition(BeanIds.REMEMBER_ME_SERVICES, services);
} else {
parserContext.getRegistry().registerAlias(rememberMeServicesRef, BeanIds.REMEMBER_ME_SERVICES);
if (!StringUtils.hasText(key) && !isPersistent) {
key = DEF_KEY;
}
registerProvider(parserContext, source, key);
registerFilter(parserContext, source);
return null;
}
private void registerProvider(ParserContext pc, Object source, String key) {
BeanDefinition authManager = ConfigUtils.registerProviderManagerIfNecessary(pc);
BeanDefinition authManager = ConfigUtils.registerProviderManagerIfNecessary(parserContext);
RootBeanDefinition provider = new RootBeanDefinition(RememberMeAuthenticationProvider.class);
provider.setSource(source);
provider.getPropertyValues().addPropertyValue(ATT_KEY, key);
ManagedList providers = (ManagedList) authManager.getPropertyValues().getPropertyValue("providers").getValue();
providers.add(provider);
}
private void registerFilter(ParserContext pc, Object source) {
RootBeanDefinition filter = new RootBeanDefinition(RememberMeProcessingFilter.class);
filter.setSource(source);
filter.getPropertyValues().addPropertyValue("authenticationManager",
new RuntimeBeanReference(BeanIds.AUTHENTICATION_MANAGER));
services.setSource(source);
provider.setSource(source);
if (StringUtils.hasText(userServiceRef)) {
services.getPropertyValues().addPropertyValue("userDetailsService", new RuntimeBeanReference(userServiceRef));
}
provider.getPropertyValues().addPropertyValue(ATT_KEY, key);
services.getPropertyValues().addPropertyValue(ATT_KEY, key);
ManagedList providers = (ManagedList) authManager.getPropertyValues().getPropertyValue("providers").getValue();
providers.add(provider);
filter.getPropertyValues().addPropertyValue("rememberMeServices",
new RuntimeBeanReference(BeanIds.REMEMBER_ME_SERVICES));
pc.getRegistry().registerBeanDefinition(BeanIds.REMEMBER_ME_FILTER, filter);
ConfigUtils.addHttpFilter(pc, new RuntimeBeanReference(BeanIds.REMEMBER_ME_FILTER));
parserContext.getRegistry().registerBeanDefinition(BeanIds.REMEMBER_ME_SERVICES, services);
parserContext.getRegistry().registerBeanDefinition(BeanIds.REMEMBER_ME_FILTER, filter);
return null;
}
}
@@ -1,66 +0,0 @@
package org.springframework.security.config;
import java.util.Map;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.security.ui.AbstractProcessingFilter;
import org.springframework.security.ui.basicauth.BasicProcessingFilter;
import org.springframework.security.ui.rememberme.RememberMeServices;
import org.springframework.util.Assert;
/**
*
* @author Luke Taylor
* @version $Id$
* @since 2.0
*/
public class RememberMeServicesInjectionBeanPostProcessor implements BeanPostProcessor, BeanFactoryAware {
private Log logger = LogFactory.getLog(getClass());
private ListableBeanFactory beanFactory;
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof AbstractProcessingFilter) {
AbstractProcessingFilter pf = (AbstractProcessingFilter) bean;
if (pf.getRememberMeServices() == null) {
logger.info("Setting RememberMeServices on bean " + beanName);
pf.setRememberMeServices(getRememberMeServices());
}
} else if (BeanIds.BASIC_AUTHENTICATION_FILTER.equals(beanName)) {
// NB: For remember-me to be sent back, a user must submit a "_spring_security_remember_me" with their login request.
// Most of the time a user won't present such a parameter with their BASIC authentication request.
// In the future we might support setting the AbstractRememberMeServices.alwaysRemember = true, but I am reluctant to
// do so because it seems likely to lead to lower security for 99.99% of users if they set the property to true.
BasicProcessingFilter bf = (BasicProcessingFilter) bean;
logger.info("Setting RememberMeServices on bean " + beanName);
bf.setRememberMeServices(getRememberMeServices());
}
return bean;
}
private RememberMeServices getRememberMeServices() {
Map beans = beanFactory.getBeansOfType(RememberMeServices.class);
Assert.isTrue(beans.size() > 0, "No RememberMeServices configured");
Assert.isTrue(beans.size() == 1, "More than one RememberMeServices bean found.");
return (RememberMeServices) beans.values().toArray()[0];
}
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = (ListableBeanFactory) beanFactory;
}
}
@@ -1,12 +1,13 @@
package org.springframework.security.config;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.beans.factory.xml.BeanDefinitionParser;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.security.providers.dao.salt.ReflectionSaltSource;
import org.springframework.security.providers.dao.salt.SystemWideSaltSource;
import org.springframework.beans.factory.xml.BeanDefinitionParser;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.util.StringUtils;
import org.w3c.dom.Element;
/**
@@ -26,8 +27,7 @@ public class SaltSourceBeanDefinitionParser implements BeanDefinitionParser {
saltSource = new RootBeanDefinition(ReflectionSaltSource.class);
saltSource.getPropertyValues().addPropertyValue("userPropertyToUse", userProperty);
saltSource.setSource(parserContext.extractSource(element));
saltSource.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
return saltSource;
}
@@ -37,7 +37,6 @@ public class SaltSourceBeanDefinitionParser implements BeanDefinitionParser {
saltSource = new RootBeanDefinition(SystemWideSaltSource.class);
saltSource.getPropertyValues().addPropertyValue("systemWideSalt", systemWideSalt);
saltSource.setSource(parserContext.extractSource(element));
saltSource.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
return saltSource;
}
@@ -29,7 +29,6 @@ public class SecurityNamespaceHandler extends NamespaceHandlerSupport {
registerBeanDefinitionDecorator(Elements.INTERCEPT_METHODS, new InterceptMethodsBeanDefinitionDecorator());
registerBeanDefinitionDecorator(Elements.FILTER_CHAIN_MAP, new FilterChainMapBeanDefinitionDecorator());
registerBeanDefinitionDecorator(Elements.CUSTOM_FILTER, new OrderedFilterBeanDefinitionDecorator());
registerBeanDefinitionDecorator(Elements.CUSTOM_AUTH_PROVIDER, new CustomAuthenticationProviderBeanDefinitionDecorator());
registerBeanDefinitionDecorator(Elements.CUSTOM_AFTER_INVOCATION_PROVIDER, new CustomAfterInvocationProviderBeanDefinitionDecorator());
registerBeanDefinitionDecorator(Elements.CUSTOM_AUTH_RPOVIDER, new CustomAuthenticationProviderBeanDefinitionDecorator());
}
}
@@ -1,139 +0,0 @@
package org.springframework.security.config;
import java.util.Map;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.BeanWrapperImpl;
import org.springframework.beans.BeansException;
import org.springframework.beans.PropertyValue;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.config.RuntimeBeanReference;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.security.providers.preauth.PreAuthenticatedAuthenticationProvider;
import org.springframework.security.ui.rememberme.AbstractRememberMeServices;
import org.springframework.security.userdetails.UserDetailsByNameServiceWrapper;
import org.springframework.security.userdetails.UserDetailsService;
import org.springframework.util.Assert;
/**
*
* @author Luke Taylor
* @since 2.0.2
*/
public class UserDetailsServiceInjectionBeanPostProcessor implements BeanPostProcessor, BeanFactoryAware {
private final Log logger = LogFactory.getLog(getClass());
private ConfigurableListableBeanFactory beanFactory;
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if (BeanIds.X509_AUTH_PROVIDER.equals(beanName)) {
injectUserDetailsServiceIntoX509Provider((PreAuthenticatedAuthenticationProvider) bean);
} else if (BeanIds.REMEMBER_ME_SERVICES.equals(beanName)) {
injectUserDetailsServiceIntoRememberMeServices((AbstractRememberMeServices)bean);
} else if (BeanIds.OPEN_ID_PROVIDER.equals(beanName)) {
injectUserDetailsServiceIntoOpenIDProvider(bean);
}
return bean;
}
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
private void injectUserDetailsServiceIntoRememberMeServices(AbstractRememberMeServices services) {
BeanDefinition beanDefinition = beanFactory.getBeanDefinition(BeanIds.REMEMBER_ME_SERVICES);
PropertyValue pv = beanDefinition.getPropertyValues().getPropertyValue("userDetailsService");
if (pv == null) {
services.setUserDetailsService(getUserDetailsService());
} else {
UserDetailsService cachingUserService = getCachingUserService(pv.getValue());
if (cachingUserService != null) {
services.setUserDetailsService(cachingUserService);
}
}
}
private void injectUserDetailsServiceIntoX509Provider(PreAuthenticatedAuthenticationProvider provider) {
BeanDefinition beanDefinition = beanFactory.getBeanDefinition(BeanIds.X509_AUTH_PROVIDER);
PropertyValue pv = beanDefinition.getPropertyValues().getPropertyValue("preAuthenticatedUserDetailsService");
UserDetailsByNameServiceWrapper wrapper = new UserDetailsByNameServiceWrapper();
if (pv == null) {
wrapper.setUserDetailsService(getUserDetailsService());
provider.setPreAuthenticatedUserDetailsService(wrapper);
} else {
RootBeanDefinition preAuthUserService = (RootBeanDefinition) pv.getValue();
Object userService =
preAuthUserService.getPropertyValues().getPropertyValue("userDetailsService").getValue();
UserDetailsService cachingUserService = getCachingUserService(userService);
if (cachingUserService != null) {
wrapper.setUserDetailsService(cachingUserService);
provider.setPreAuthenticatedUserDetailsService(wrapper);
}
}
}
private void injectUserDetailsServiceIntoOpenIDProvider(Object bean) {
BeanDefinition beanDefinition = beanFactory.getBeanDefinition(BeanIds.OPEN_ID_PROVIDER);
PropertyValue pv = beanDefinition.getPropertyValues().getPropertyValue("userDetailsService");
if (pv == null) {
BeanWrapperImpl beanWrapper = new BeanWrapperImpl(bean);
beanWrapper.setPropertyValue("userDetailsService", getUserDetailsService());
}
}
/**
* Obtains a user details service for use in RememberMeServices etc. Will return a caching version
* if available so should not be used for beans which need to separate the two.
*/
UserDetailsService getUserDetailsService() {
Map beans = beanFactory.getBeansOfType(CachingUserDetailsService.class);
if (beans.size() == 0) {
beans = beanFactory.getBeansOfType(UserDetailsService.class);
}
if (beans.size() == 0) {
throw new SecurityConfigurationException("No UserDetailsService registered.");
} else if (beans.size() > 1) {
throw new SecurityConfigurationException("More than one UserDetailsService registered. Please " +
"use a specific Id in your configuration");
}
return (UserDetailsService) beans.values().toArray()[0];
}
private UserDetailsService getCachingUserService(Object userServiceRef) {
Assert.isInstanceOf(RuntimeBeanReference.class, userServiceRef,
"userDetailsService property value must be a RuntimeBeanReference");
String id = ((RuntimeBeanReference)userServiceRef).getBeanName();
// Overwrite with the caching version if available
String cachingId = id + AbstractUserDetailsServiceBeanDefinitionParser.CACHING_SUFFIX;
if (beanFactory.containsBeanDefinition(cachingId)) {
return (UserDetailsService) beanFactory.getBean(cachingId);
}
return null;
}
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = (ConfigurableListableBeanFactory) beanFactory;
}
}
@@ -6,6 +6,7 @@ import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.beans.factory.BeanDefinitionStoreException;
import org.springframework.security.userdetails.memory.InMemoryDaoImpl;
import org.springframework.security.userdetails.memory.UserMap;
import org.springframework.security.userdetails.User;
import org.springframework.security.util.AuthorityUtils;
@@ -32,8 +33,8 @@ public class UserServiceBeanDefinitionParser extends AbstractUserDetailsServiceB
static final String ATT_DISABLED = "disabled";
static final String ATT_LOCKED = "locked";
protected String getBeanClassName(Element element) {
return "org.springframework.security.userdetails.memory.InMemoryDaoImpl";
protected Class getBeanClass(Element element) {
return InMemoryDaoImpl.class;
}
protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
@@ -45,8 +45,8 @@ public class X509BeanDefinitionParser implements BeanDefinitionParser {
}
BeanDefinition provider = new RootBeanDefinition(PreAuthenticatedAuthenticationProvider.class);
ConfigUtils.getRegisteredProviders(parserContext).add(provider);
parserContext.getRegistry().registerBeanDefinition(BeanIds.X509_AUTH_PROVIDER, provider);
ConfigUtils.getRegisteredProviders(parserContext).add(new RuntimeBeanReference(BeanIds.X509_AUTH_PROVIDER));
String userServiceRef = element.getAttribute(ATT_USER_SERVICE_REF);
@@ -62,7 +62,6 @@ public class X509BeanDefinitionParser implements BeanDefinitionParser {
filterBuilder.addPropertyValue("authenticationManager", new RuntimeBeanReference(BeanIds.AUTHENTICATION_MANAGER));
parserContext.getRegistry().registerBeanDefinition(BeanIds.X509_FILTER, filterBuilder.getBeanDefinition());
ConfigUtils.addHttpFilter(parserContext, new RuntimeBeanReference(BeanIds.X509_FILTER));
return null;
}

Some files were not shown because too many files have changed in this diff Show More