Skip to content

Introducing a SidFactory #15

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ public class AclAuthorizationStrategyImpl implements AclAuthorizationStrategy {
private final GrantedAuthority gaModifyAuditing;
private final GrantedAuthority gaTakeOwnership;
private SidRetrievalStrategy sidRetrievalStrategy = new SidRetrievalStrategyImpl();
private SidFactory sidFactory = new DefaultSidFactory();

//~ Constructors ===================================================================================================

Expand Down Expand Up @@ -83,7 +84,7 @@ public void securityCheck(Acl acl, int changeType) {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();

// Check if authorized by virtue of ACL ownership
Sid currentUser = new PrincipalSid(authentication);
Sid currentUser = sidFactory.createPrincipal(authentication);

if (currentUser.equals(acl.getOwner())
&& ((changeType == CHANGE_GENERAL) || (changeType == CHANGE_OWNERSHIP))) {
Expand Down Expand Up @@ -123,4 +124,8 @@ public void setSidRetrievalStrategy(SidRetrievalStrategy sidRetrievalStrategy) {
Assert.notNull(sidRetrievalStrategy, "SidRetrievalStrategy required");
this.sidRetrievalStrategy = sidRetrievalStrategy;
}

public void setSidFactory(SidFactory sidFactory) {
this.sidFactory = sidFactory;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package org.springframework.security.acls.domain;

import org.springframework.security.acls.model.Sid;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

/**
* Creates the same SIDs as the SpringSecurity before the {@link SidFactory} was introduced, which means it creates a
* {@link PrincipalSid} and {@link GrantedAuthoritySid}.
*
* @author stanislav bashkirtsev
*/
public class DefaultSidFactory implements SidFactory {
/**
* {@inheritDoc}
*/
@Override
public Sid create(String sidName, boolean principal) {
if (principal) {
return new PrincipalSid(sidName);
} else {
return new GrantedAuthoritySid(sidName);
}
}

/**
* {@inheritDoc}
*/
@Override
public Sid createPrincipal(Authentication authentication) {
return new PrincipalSid(authentication);
}

/**
* {@inheritDoc}
*/
@Override
public List<? extends Sid> createGrantedAuthorities(Collection<? extends GrantedAuthority> grantedAuthorities) {
List<Sid> sids = new ArrayList<Sid>();
for (GrantedAuthority authority : grantedAuthorities) {
sids.add(new GrantedAuthoritySid(authority));
}
return sids;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -54,18 +54,38 @@ public boolean equals(Object object) {
}

// Delegate to getGrantedAuthority() to perform actual comparison (both should be identical)
return ((GrantedAuthoritySid) object).getGrantedAuthority().equals(this.getGrantedAuthority());
return ((GrantedAuthoritySid) object).getSidId().equals(this.getSidId());
}

public int hashCode() {
return this.getGrantedAuthority().hashCode();
return this.getSidId().hashCode();
}

/**
* @deprecated use {@link #getSidId()} instead
*/
@Deprecated
public String getGrantedAuthority() {
return grantedAuthority;
}

public String toString() {
return "GrantedAuthoritySid[" + this.grantedAuthority + "]";
}

/**
* {@inheritDoc}
*/
@Override
public String getSidId() {
return grantedAuthority;
}

/**
* {@inheritDoc}
*/
@Override
public boolean isPrincipal() {
return false;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -60,18 +60,38 @@ public boolean equals(Object object) {
}

// Delegate to getPrincipal() to perform actual comparison (both should be identical)
return ((PrincipalSid) object).getPrincipal().equals(this.getPrincipal());
return ((PrincipalSid) object).getSidId().equals(this.getSidId());
}

public int hashCode() {
return this.getPrincipal().hashCode();
return this.getSidId().hashCode();
}

/**
* @deprecated use {@link #getSidId()} instead.
*/
@Deprecated
public String getPrincipal() {
return principal;
}

public String toString() {
return "PrincipalSid[" + this.principal + "]";
}

/**
* {@inheritDoc}
*/
@Override
public boolean isPrincipal() {
return true;
}

/**
* {@inheritDoc}
*/
@Override
public String getSidId() {
return principal;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package org.springframework.security.acls.domain;

import org.springframework.security.acls.model.Sid;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;

import java.util.Collection;
import java.util.List;

/**
* Typical application that uses Spring ACL won't need anything but {@link PrincipalSid} or {@link GrantedAuthoritySid},
* but sometimes we need to extend this list of implementations or replace it for more complicated scenarios when
* default capabilities of Spring ACL is not enough. You can implement this factory and inject it into different classes
* that work with {@link Sid}s in order them to create <i>your</i> SIDs.
*
* @author stanislav bashkirtsev
*/
public interface SidFactory {
/**
* The Factory Method that creates a particular implementation of {@link Sid} depending on the arguments.
*
* @param sidName the name of the sid representing its unique identifier. In typical ACL database schema it's
* located in table {@code acl_sid} table, {@code sid} column.
* @param principal whether it's a user or granted authority like role
* @return the instance of Sid with the {@code sidName} as an identifier
*/
Sid create(String sidName, boolean principal);

/**
* Creates a principal-like sid from the authentication information.
*
* @param authentication the authentication information that can provide principal and thus the sid's id will be
* dependant on the value inside
* @return a sid with the ID taken from the authentication information
*/
Sid createPrincipal(Authentication authentication);

List<? extends Sid> createGrantedAuthorities(Collection<? extends GrantedAuthority> grantedAuthorities);
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
* @author Ben Alex
*/
public class SidRetrievalStrategyImpl implements SidRetrievalStrategy {

private SidFactory sidFactory = new DefaultSidFactory();
private RoleHierarchy roleHierarchy = new NullRoleHierarchy();

public SidRetrievalStrategyImpl() {
Expand All @@ -53,13 +53,12 @@ public SidRetrievalStrategyImpl(RoleHierarchy roleHierarchy) {
public List<Sid> getSids(Authentication authentication) {
Collection<? extends GrantedAuthority> authorities = roleHierarchy.getReachableGrantedAuthorities(authentication.getAuthorities());
List<Sid> sids = new ArrayList<Sid>(authorities.size() + 1);

sids.add(new PrincipalSid(authentication));

for (GrantedAuthority authority : authorities) {
sids.add(new GrantedAuthoritySid(authority));
}

sids.add(sidFactory.createPrincipal(authentication));
sids.addAll(sidFactory.createGrantedAuthorities(authorities));
return sids;
}

public void setSidFactory(SidFactory sidFactory) {
this.sidFactory = sidFactory;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,16 +32,7 @@
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.PreparedStatementSetter;
import org.springframework.jdbc.core.ResultSetExtractor;
import org.springframework.security.acls.domain.AccessControlEntryImpl;
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.domain.DefaultPermissionFactory;
import org.springframework.security.acls.domain.DefaultPermissionGrantingStrategy;
import org.springframework.security.acls.domain.GrantedAuthoritySid;
import org.springframework.security.acls.domain.ObjectIdentityImpl;
import org.springframework.security.acls.domain.PermissionFactory;
import org.springframework.security.acls.domain.PrincipalSid;
import org.springframework.security.acls.domain.*;
import org.springframework.security.acls.model.AccessControlEntry;
import org.springframework.security.acls.model.Acl;
import org.springframework.security.acls.model.AclCache;
Expand Down Expand Up @@ -122,6 +113,7 @@ public final class BasicLookupStrategy implements LookupStrategy {
private String lookupPrimaryKeysWhereClause = DEFAULT_LOOKUP_KEYS_WHERE_CLAUSE;
private String lookupObjectIdentitiesWhereClause = DEFAULT_LOOKUP_IDENTITIES_WHERE_CLAUSE;
private String orderByClause = DEFAULT_ORDER_BY_CLAUSE;
private SidFactory sidFactory = new DefaultSidFactory();

//~ Constructors ===================================================================================================

Expand Down Expand Up @@ -557,13 +549,7 @@ private void convertCurrentResultIntoObject(Map<Serializable, Acl> acls, ResultS

boolean entriesInheriting = rs.getBoolean("entries_inheriting");
Sid owner;

if (rs.getBoolean("acl_principal")) {
owner = new PrincipalSid(rs.getString("acl_sid"));
} else {
owner = new GrantedAuthoritySid(rs.getString("acl_sid"));
}

owner = sidFactory.create(rs.getString("acl_sid"), rs.getBoolean("acl_principal"));
acl = new AclImpl(objectIdentity, id, aclAuthorizationStrategy, grantingStrategy, parentAcl, null,
entriesInheriting, owner);

Expand All @@ -574,14 +560,7 @@ private void convertCurrentResultIntoObject(Map<Serializable, Acl> acls, ResultS
// It is permissible 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"));
Sid recipient;

if (rs.getBoolean("ace_principal")) {
recipient = new PrincipalSid(rs.getString("ace_sid"));
} else {
recipient = new GrantedAuthoritySid(rs.getString("ace_sid"));
}

Sid recipient = sidFactory.create(rs.getString("ace_sid"), rs.getBoolean("ace_principal"));
int mask = rs.getInt("mask");
Permission permission = permissionFactory.buildFromMask(mask);
boolean granting = rs.getBoolean("granting");
Expand All @@ -602,6 +581,10 @@ private void convertCurrentResultIntoObject(Map<Serializable, Acl> acls, ResultS
}
}

public void setSidFactory(SidFactory sidFactory) {
this.sidFactory = sidFactory;
}

private class StubAclParent implements Acl {
private final Long id;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,7 @@

import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.BatchPreparedStatementSetter;
import org.springframework.security.acls.domain.AccessControlEntryImpl;
import org.springframework.security.acls.domain.GrantedAuthoritySid;
import org.springframework.security.acls.domain.ObjectIdentityImpl;
import org.springframework.security.acls.domain.PrincipalSid;
import org.springframework.security.acls.domain.*;
import org.springframework.security.acls.model.AccessControlEntry;
import org.springframework.security.acls.model.Acl;
import org.springframework.security.acls.model.AclCache;
Expand Down Expand Up @@ -80,6 +77,7 @@ public class JdbcMutableAclService extends JdbcAclService implements MutableAclS
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 = ?";
private SidFactory sidFactory = new DefaultSidFactory();

//~ Constructors ===================================================================================================

Expand All @@ -101,7 +99,7 @@ public MutableAcl createAcl(ObjectIdentity objectIdentity) throws AlreadyExistsE

// Need to retrieve the current principal, in order to know who "owns" this ACL (can be changed later on)
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
PrincipalSid sid = new PrincipalSid(auth);
Sid sid = sidFactory.createPrincipal(auth);

// Create the acl_object_identity row
createObjectIdentity(objectIdentity, sid);
Expand Down Expand Up @@ -195,16 +193,8 @@ protected Long createOrRetrieveSidPrimaryKey(Sid sid, boolean allowCreate) {
Assert.notNull(sid, "Sid required");

String sidName;
boolean sidIsPrincipal = true;

if (sid instanceof PrincipalSid) {
sidName = ((PrincipalSid) sid).getPrincipal();
} else if (sid instanceof GrantedAuthoritySid) {
sidName = ((GrantedAuthoritySid) sid).getGrantedAuthority();
sidIsPrincipal = false;
} else {
throw new IllegalArgumentException("Unsupported implementation of Sid");
}
sidName = sid.getSidId();
boolean sidIsPrincipal = sid.isPrincipal();

List<Long> sidIds = jdbcTemplate.queryForList(selectSidPrimaryKey,
new Object[] {Boolean.valueOf(sidIsPrincipal), sidName}, Long.class);
Expand Down Expand Up @@ -430,4 +420,8 @@ public void setUpdateObjectIdentity(String updateObjectIdentity) {
public void setForeignKeysInDatabase(boolean foreignKeysInDatabase) {
this.foreignKeysInDatabase = foreignKeysInDatabase;
}

public void setSidFactory(SidFactory sidFactory) {
this.sidFactory = sidFactory;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,14 @@
* @author Ben Alex
*/
public interface Sid extends Serializable {
//~ Methods ========================================================================================================
boolean isPrincipal();
/**
* Gets the unique identifier of the SID (usually a database ID of the entity). It is string since the ACL tables
* require this.
*
* @return the unique identifier of the SID (usually a database ID of the entity)
*/
String getSidId();

/**
* Refer to the <code>java.lang.Object</code> documentation for the interface contract.
Expand Down
Loading