1
0
mirror of synced 2026-05-22 13:23:17 +00:00

Null safety via JSpecify spring-security-acl

Closes gh-18401
This commit is contained in:
Robert Winch
2026-01-22 14:26:26 -06:00
parent 42e1e9fb67
commit e7203bf838
18 changed files with 147 additions and 68 deletions
+1
View File
@@ -1,5 +1,6 @@
plugins {
id 'javadoc-warnings-error'
id 'security-nullability'
}
apply plugin: 'io.spring.convention.spring-module'
@@ -18,6 +18,8 @@ package org.springframework.security.acls.domain;
import java.io.Serializable;
import org.jspecify.annotations.Nullable;
import org.springframework.security.acls.model.AccessControlEntry;
import org.springframework.security.acls.model.Acl;
import org.springframework.security.acls.model.AuditableAccessControlEntry;
@@ -36,7 +38,7 @@ public class AccessControlEntryImpl implements AccessControlEntry, AuditableAcce
private Permission permission;
private final Serializable id;
private final @Nullable Serializable id;
private final Sid sid;
@@ -46,7 +48,7 @@ public class AccessControlEntryImpl implements AccessControlEntry, AuditableAcce
private final boolean granting;
public AccessControlEntryImpl(Serializable id, Acl acl, Sid sid, Permission permission, boolean granting,
public AccessControlEntryImpl(@Nullable Serializable id, Acl acl, Sid sid, Permission permission, boolean granting,
boolean auditSuccess, boolean auditFailure) {
Assert.notNull(acl, "Acl required");
Assert.notNull(sid, "Sid required");
@@ -133,7 +135,7 @@ public class AccessControlEntryImpl implements AccessControlEntry, AuditableAcce
}
@Override
public Serializable getId() {
public @Nullable Serializable getId() {
return this.id;
}
@@ -99,7 +99,8 @@ public class AclAuthorizationStrategyImpl implements AclAuthorizationStrategy {
Authentication authentication = context.getAuthentication();
// Check if authorized by virtue of ACL ownership
Sid currentUser = createCurrentUser(authentication);
if (currentUser.equals(acl.getOwner())
Sid owner = acl.getOwner();
if (owner != null && currentUser.equals(owner)
&& ((changeType == CHANGE_GENERAL) || (changeType == CHANGE_OWNERSHIP))) {
return;
}
@@ -108,8 +109,8 @@ public class AclAuthorizationStrategyImpl implements AclAuthorizationStrategy {
Collection<? extends GrantedAuthority> reachableGrantedAuthorities = this.roleHierarchy
.getReachableGrantedAuthorities(authentication.getAuthorities());
Set<String> authorities = AuthorityUtils.authorityListToSet(reachableGrantedAuthorities);
if (acl.getOwner() instanceof GrantedAuthoritySid
&& authorities.contains(((GrantedAuthoritySid) acl.getOwner()).getGrantedAuthority())) {
if (owner instanceof GrantedAuthoritySid
&& authorities.contains(((GrantedAuthoritySid) owner).getGrantedAuthority())) {
return;
}
@@ -20,6 +20,8 @@ import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import org.jspecify.annotations.Nullable;
import org.springframework.security.acls.model.AccessControlEntry;
import org.springframework.security.acls.model.Acl;
import org.springframework.security.acls.model.AuditableAcl;
@@ -41,7 +43,7 @@ import org.springframework.util.ObjectUtils;
*/
public class AclImpl implements Acl, MutableAcl, AuditableAcl, OwnershipAcl {
private Acl parentAcl;
private @Nullable Acl parentAcl;
private transient AclAuthorizationStrategy aclAuthorizationStrategy;
@@ -54,10 +56,10 @@ public class AclImpl implements Acl, MutableAcl, AuditableAcl, OwnershipAcl {
private Serializable id;
// OwnershipAcl
private Sid owner;
private @Nullable Sid owner;
// includes all SIDs the WHERE clause covered, even if there was no ACE for a SID
private List<Sid> loadedSids = null;
private @Nullable List<Sid> loadedSids = null;
private boolean entriesInheriting = true;
@@ -97,8 +99,8 @@ public class AclImpl implements Acl, MutableAcl, AuditableAcl, OwnershipAcl {
* @param owner the owner (required)
*/
public AclImpl(ObjectIdentity objectIdentity, Serializable id, AclAuthorizationStrategy aclAuthorizationStrategy,
PermissionGrantingStrategy grantingStrategy, Acl parentAcl, List<Sid> loadedSids, boolean entriesInheriting,
Sid owner) {
PermissionGrantingStrategy grantingStrategy, @Nullable Acl parentAcl, @Nullable List<Sid> loadedSids,
boolean entriesInheriting, Sid owner) {
Assert.notNull(objectIdentity, "Object Identity required");
Assert.notNull(id, "Id required");
Assert.notNull(aclAuthorizationStrategy, "AclAuthorizationStrategy required");
@@ -117,7 +119,7 @@ public class AclImpl implements Acl, MutableAcl, AuditableAcl, OwnershipAcl {
* Private no-argument constructor for use by reflection-based persistence tools along
* with field-level access.
*/
@SuppressWarnings("unused")
@SuppressWarnings({ "unused", "NullAway.Init" })
private AclImpl() {
}
@@ -199,7 +201,7 @@ public class AclImpl implements Acl, MutableAcl, AuditableAcl, OwnershipAcl {
}
@Override
public boolean isSidLoaded(List<Sid> sids) {
public boolean isSidLoaded(@Nullable List<Sid> sids) {
// If loadedSides is null, this indicates all SIDs were loaded
// Also return true if the caller didn't specify a SID to find
if ((this.loadedSids == null) || (sids == null) || sids.isEmpty()) {
@@ -238,19 +240,19 @@ public class AclImpl implements Acl, MutableAcl, AuditableAcl, OwnershipAcl {
}
@Override
public Sid getOwner() {
public @Nullable Sid getOwner() {
return this.owner;
}
@Override
public void setParent(Acl newParent) {
public void setParent(@Nullable Acl newParent) {
this.aclAuthorizationStrategy.securityCheck(this, AclAuthorizationStrategy.CHANGE_GENERAL);
Assert.isTrue(newParent == null || !newParent.equals(this), "Cannot be the parent of yourself");
this.parentAcl = newParent;
}
@Override
public Acl getParentAcl() {
public @Nullable Acl getParentAcl() {
return this.parentAcl;
}
@@ -18,6 +18,8 @@ package org.springframework.security.acls.domain;
import java.io.Serializable;
import org.jspecify.annotations.Nullable;
import org.springframework.cache.Cache;
import org.springframework.security.acls.model.AclCache;
import org.springframework.security.acls.model.MutableAcl;
@@ -78,13 +80,13 @@ public class SpringCacheBasedAclCache implements AclCache {
}
@Override
public MutableAcl getFromCache(ObjectIdentity objectIdentity) {
public @Nullable MutableAcl getFromCache(ObjectIdentity objectIdentity) {
Assert.notNull(objectIdentity, "ObjectIdentity required");
return getFromCache((Object) objectIdentity);
}
@Override
public MutableAcl getFromCache(Serializable pk) {
public @Nullable MutableAcl getFromCache(Serializable pk) {
Assert.notNull(pk, "Primary key (identifier) required");
return getFromCache((Object) pk);
}
@@ -101,12 +103,16 @@ public class SpringCacheBasedAclCache implements AclCache {
this.cache.put(acl.getId(), acl);
}
private MutableAcl getFromCache(Object key) {
private @Nullable MutableAcl getFromCache(Object key) {
Cache.ValueWrapper element = this.cache.get(key);
if (element == null) {
return null;
}
return initializeTransientFields((MutableAcl) element.get());
Object value = element.get();
if (value == null) {
return null;
}
return initializeTransientFields((MutableAcl) value);
}
private MutableAcl initializeTransientFields(MutableAcl value) {
@@ -17,4 +17,7 @@
/**
* Basic implementation of access control lists (ACLs) interfaces.
*/
@NullMarked
package org.springframework.security.acls.domain;
import org.jspecify.annotations.NullMarked;
@@ -23,6 +23,7 @@ import java.util.UUID;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jspecify.annotations.Nullable;
import org.springframework.core.convert.ConversionFailedException;
import org.springframework.core.convert.ConversionService;
@@ -67,10 +68,10 @@ class AclClassIdUtils {
* @return The identifier in the appropriate target Java type. Typically Long or UUID.
* @throws SQLException
*/
Serializable identifierFrom(Serializable identifier, ResultSet resultSet) throws SQLException {
if (isString(identifier) && hasValidClassIdType(resultSet)
&& canConvertFromStringTo(classIdTypeFrom(resultSet))) {
return convertFromStringTo((String) identifier, classIdTypeFrom(resultSet));
@Nullable Serializable identifierFrom(Serializable identifier, ResultSet resultSet) throws SQLException {
Class<? extends Serializable> classIdType = classIdTypeFrom(resultSet);
if (isString(identifier) && classIdType != null && canConvertFromStringTo(classIdType)) {
return convertFromStringTo((String) identifier, classIdType);
}
// Assume it should be a Long type
return convertToLong(identifier);
@@ -86,11 +87,17 @@ class AclClassIdUtils {
}
}
private <T extends Serializable> Class<T> classIdTypeFrom(ResultSet resultSet) throws SQLException {
return classIdTypeFrom(resultSet.getString(DEFAULT_CLASS_ID_TYPE_COLUMN_NAME));
private <T extends Serializable> @Nullable Class<T> classIdTypeFrom(ResultSet resultSet) throws SQLException {
try {
return classIdTypeFrom(resultSet.getString(DEFAULT_CLASS_ID_TYPE_COLUMN_NAME));
}
catch (SQLException ex) {
log.debug("Unable to obtain the class id type", ex);
return null;
}
}
private <T extends Serializable> Class<T> classIdTypeFrom(String className) {
private <T extends Serializable> @Nullable Class<T> classIdTypeFrom(String className) {
if (className == null) {
return null;
}
@@ -107,7 +114,7 @@ class AclClassIdUtils {
return this.conversionService.canConvert(String.class, targetType);
}
private <T extends Serializable> T convertFromStringTo(String identifier, Class<T> targetType) {
private <T extends Serializable> @Nullable T convertFromStringTo(String identifier, Class<T> targetType) {
return this.conversionService.convert(identifier, targetType);
}
@@ -121,7 +128,7 @@ class AclClassIdUtils {
* exception occurred
* @throws IllegalArgumentException if targetType is null
*/
private Long convertToLong(Serializable identifier) {
private @Nullable Long convertToLong(Serializable identifier) {
if (this.conversionService.canConvert(identifier.getClass(), Long.class)) {
return this.conversionService.convert(identifier, Long.class);
}
@@ -140,10 +147,10 @@ class AclClassIdUtils {
private static class StringToLongConverter implements Converter<String, Long> {
@Override
public Long convert(String identifierAsString) {
public Long convert(@Nullable String identifierAsString) {
if (identifierAsString == null) {
throw new ConversionFailedException(TypeDescriptor.valueOf(String.class),
TypeDescriptor.valueOf(Long.class), null, null);
TypeDescriptor.valueOf(Long.class), identifierAsString, new NullPointerException());
}
return Long.parseLong(identifierAsString);
@@ -154,10 +161,10 @@ class AclClassIdUtils {
private static class StringToUUIDConverter implements Converter<String, UUID> {
@Override
public UUID convert(String identifierAsString) {
public UUID convert(@Nullable String identifierAsString) {
if (identifierAsString == null) {
throw new ConversionFailedException(TypeDescriptor.valueOf(String.class),
TypeDescriptor.valueOf(UUID.class), null, null);
TypeDescriptor.valueOf(UUID.class), identifierAsString, new NullPointerException());
}
return UUID.fromString(identifierAsString);
@@ -31,6 +31,8 @@ import java.util.Set;
import javax.sql.DataSource;
import org.jspecify.annotations.Nullable;
import org.springframework.core.convert.ConversionException;
import org.springframework.core.convert.ConversionService;
import org.springframework.jdbc.core.JdbcTemplate;
@@ -224,7 +226,8 @@ public class BasicLookupStrategy implements LookupStrategy {
* @param findNow Long-based primary keys to retrieve
* @param sids
*/
private void lookupPrimaryKeys(final Map<Serializable, Acl> acls, final Set<Long> findNow, final List<Sid> sids) {
private void lookupPrimaryKeys(final Map<Serializable, Acl> acls, final Set<Long> findNow,
final @Nullable List<Sid> sids) {
Assert.notNull(acls, "ACLs are required");
Assert.notEmpty(findNow, "Items to find now required");
String sql = computeRepeatingSql(this.lookupPrimaryKeysWhereClause, findNow.size());
@@ -264,7 +267,7 @@ public class BasicLookupStrategy implements LookupStrategy {
* automatically create entries if required)
*/
@Override
public final Map<ObjectIdentity, Acl> readAclsById(List<ObjectIdentity> objects, List<Sid> sids) {
public final Map<ObjectIdentity, Acl> readAclsById(List<ObjectIdentity> objects, @Nullable List<Sid> sids) {
Assert.isTrue(this.batchSize >= 1, "BatchSize must be >= 1");
Assert.notEmpty(objects, "Objects to lookup required");
// Map<ObjectIdentity,Acl>
@@ -323,7 +326,7 @@ public class BasicLookupStrategy implements LookupStrategy {
* properly-configured parent ACLs.
*/
private Map<ObjectIdentity, Acl> lookupObjectIdentities(final Collection<ObjectIdentity> objectIdentities,
List<Sid> sids) {
@Nullable List<Sid> sids) {
Assert.notEmpty(objectIdentities, "Must provide identities to lookup");
// contains Acls with StubAclParents
@@ -399,8 +402,10 @@ public class BasicLookupStrategy implements LookupStrategy {
}
// Now we have the parent (if there is one), create the true AclImpl
Sid owner = inputAcl.getOwner();
Assert.isTrue(owner != null, "Owner is required");
AclImpl result = new AclImpl(inputAcl.getObjectIdentity(), inputAcl.getId(), this.aclAuthorizationStrategy,
this.grantingStrategy, parent, null, inputAcl.isEntriesInheriting(), inputAcl.getOwner());
this.grantingStrategy, parent, null, inputAcl.isEntriesInheriting(), owner);
// Copy the "aces" from the input to the destination
@@ -506,9 +511,9 @@ public class BasicLookupStrategy implements LookupStrategy {
private final Map<Serializable, Acl> acls;
private final List<Sid> sids;
private final @Nullable List<Sid> sids;
ProcessResultSet(Map<Serializable, Acl> acls, List<Sid> sids) {
ProcessResultSet(Map<Serializable, Acl> acls, @Nullable List<Sid> sids) {
Assert.notNull(acls, "ACLs cannot be null");
this.acls = acls;
this.sids = sids; // can be null
@@ -579,6 +584,9 @@ public class BasicLookupStrategy implements LookupStrategy {
// target id type, e.g. UUID.
Serializable identifier = (Serializable) rs.getObject("object_id_identity");
identifier = BasicLookupStrategy.this.aclClassIdUtils.identifierFrom(identifier, rs);
if (identifier == null) {
throw new IllegalStateException("Identifier cannot be null");
}
ObjectIdentity objectIdentity = BasicLookupStrategy.this.objectIdentityGenerator
.createObjectIdentity(identifier, rs.getString("class"));
@@ -670,7 +678,7 @@ public class BasicLookupStrategy implements LookupStrategy {
}
@Override
public boolean isSidLoaded(List<Sid> sids) {
public boolean isSidLoaded(@Nullable List<Sid> sids) {
throw new UnsupportedOperationException("Stub only");
}
@@ -27,6 +27,7 @@ import javax.sql.DataSource;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jspecify.annotations.Nullable;
import org.springframework.core.convert.ConversionService;
import org.springframework.jdbc.core.JdbcOperations;
@@ -98,7 +99,7 @@ public class JdbcAclService implements AclService {
}
@Override
public List<ObjectIdentity> findChildren(ObjectIdentity parentIdentity) {
public @Nullable List<ObjectIdentity> findChildren(ObjectIdentity parentIdentity) {
Object[] args = { parentIdentity.getIdentifier().toString(), parentIdentity.getType() };
List<ObjectIdentity> objects = this.jdbcOperations.query(this.findChildrenSql,
(rs, rowNum) -> mapObjectIdentityRow(rs), args);
@@ -109,11 +110,14 @@ public class JdbcAclService implements AclService {
String javaType = rs.getString("class");
Serializable identifier = (Serializable) rs.getObject("obj_id");
identifier = this.aclClassIdUtils.identifierFrom(identifier, rs);
if (identifier == null) {
throw new IllegalStateException("Identifier cannot be null");
}
return this.objectIdentityGenerator.createObjectIdentity(identifier, javaType);
}
@Override
public Acl readAclById(ObjectIdentity object, List<Sid> sids) throws NotFoundException {
public Acl readAclById(ObjectIdentity object, @Nullable List<Sid> sids) throws NotFoundException {
Map<ObjectIdentity, Acl> map = readAclsById(Collections.singletonList(object), sids);
Assert.isTrue(map.containsKey(object),
() -> "There should have been an Acl entry for ObjectIdentity " + object);
@@ -131,7 +135,7 @@ public class JdbcAclService implements AclService {
}
@Override
public Map<ObjectIdentity, Acl> readAclsById(List<ObjectIdentity> objects, List<Sid> sids)
public Map<ObjectIdentity, Acl> readAclsById(List<ObjectIdentity> objects, @Nullable List<Sid> sids)
throws NotFoundException {
Map<ObjectIdentity, Acl> result = this.lookupStrategy.readAclsById(objects, sids);
// Check every requested object identity was found (throw NotFoundException if
@@ -22,6 +22,8 @@ import java.util.List;
import javax.sql.DataSource;
import org.jspecify.annotations.Nullable;
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.BatchPreparedStatementSetter;
import org.springframework.security.acls.domain.AccessControlEntryImpl;
@@ -120,6 +122,7 @@ public class JdbcMutableAclService extends JdbcAclService implements MutableAclS
// Need to retrieve the current principal, in order to know who "owns" this ACL
// (can be changed later on)
Authentication auth = this.securityContextHolderStrategy.getContext().getAuthentication();
Assert.isTrue(auth != null, "Authentication required");
PrincipalSid sid = new PrincipalSid(auth);
// Create the acl_object_identity row
@@ -155,9 +158,12 @@ public class JdbcMutableAclService extends JdbcAclService implements MutableAclS
Assert.isTrue(entry_ instanceof AccessControlEntryImpl, "Unknown ACE class");
AccessControlEntryImpl entry = (AccessControlEntryImpl) entry_;
Assert.state(acl.getId() != null, "ACL ID cannot be null");
stmt.setLong(1, (Long) acl.getId());
stmt.setInt(2, i);
stmt.setLong(3, createOrRetrieveSidPrimaryKey(entry.getSid(), true));
Long sidPrimaryKey = createOrRetrieveSidPrimaryKey(entry.getSid(), true);
Assert.state(sidPrimaryKey != null, "SID primary key cannot be null");
stmt.setLong(3, sidPrimaryKey);
stmt.setInt(4, entry.getPermission().getMask());
stmt.setBoolean(5, entry.isGranting());
stmt.setBoolean(6, entry.isAuditSuccess());
@@ -189,11 +195,14 @@ public class JdbcMutableAclService extends JdbcAclService implements MutableAclS
* @param allowCreate true if creation is permitted if not found
* @return the primary key or null if not found
*/
protected Long createOrRetrieveClassPrimaryKey(String type, boolean allowCreate, Class idType) {
List<Long> classIds = this.jdbcOperations.queryForList(this.selectClassPrimaryKey, Long.class, type);
protected @Nullable Long createOrRetrieveClassPrimaryKey(String type, boolean allowCreate, Class idType) {
List<@Nullable Long> classIds = this.jdbcOperations.queryForList(this.selectClassPrimaryKey, Long.class, type);
if (!classIds.isEmpty()) {
return classIds.get(0);
Long result = classIds.get(0);
if (result != null) {
return result;
}
}
if (allowCreate) {
@@ -204,7 +213,9 @@ public class JdbcMutableAclService extends JdbcAclService implements MutableAclS
this.jdbcOperations.update(this.insertClass, type, idType.getCanonicalName());
}
Assert.isTrue(TransactionSynchronizationManager.isSynchronizationActive(), "Transaction must be running");
return this.jdbcOperations.queryForObject(this.classIdentityQuery, Long.class);
Long result = this.jdbcOperations.queryForObject(this.classIdentityQuery, Long.class);
Assert.state(result != null, "Failed to retrieve class primary key");
return result;
}
return null;
@@ -219,7 +230,7 @@ public class JdbcMutableAclService extends JdbcAclService implements MutableAclS
* @throws IllegalArgumentException if the <tt>Sid</tt> is not a recognized
* implementation.
*/
protected Long createOrRetrieveSidPrimaryKey(Sid sid, boolean allowCreate) {
protected @Nullable Long createOrRetrieveSidPrimaryKey(Sid sid, boolean allowCreate) {
Assert.notNull(sid, "Sid required");
if (sid instanceof PrincipalSid) {
String sidName = ((PrincipalSid) sid).getPrincipal();
@@ -240,16 +251,22 @@ public class JdbcMutableAclService extends JdbcAclService implements MutableAclS
* @param allowCreate true if creation is permitted if not found
* @return the primary key or null if not found
*/
protected Long createOrRetrieveSidPrimaryKey(String sidName, boolean sidIsPrincipal, boolean allowCreate) {
List<Long> sidIds = this.jdbcOperations.queryForList(this.selectSidPrimaryKey, Long.class, sidIsPrincipal,
sidName);
protected @Nullable Long createOrRetrieveSidPrimaryKey(String sidName, boolean sidIsPrincipal,
boolean allowCreate) {
List<@Nullable Long> sidIds = this.jdbcOperations.queryForList(this.selectSidPrimaryKey, Long.class,
sidIsPrincipal, sidName);
if (!sidIds.isEmpty()) {
return sidIds.get(0);
Long result = sidIds.get(0);
if (result != null) {
return result;
}
}
if (allowCreate) {
this.jdbcOperations.update(this.insertSid, sidIsPrincipal, sidName);
Assert.isTrue(TransactionSynchronizationManager.isSynchronizationActive(), "Transaction must be running");
return this.jdbcOperations.queryForObject(this.sidIdentityQuery, Long.class);
Long result = this.jdbcOperations.queryForObject(this.sidIdentityQuery, Long.class);
Assert.state(result != null, "Failed to retrieve sid primary key");
return result;
}
return null;
}
@@ -279,6 +296,9 @@ public class JdbcMutableAclService extends JdbcAclService implements MutableAclS
}
Long oidPrimaryKey = retrieveObjectIdentityPrimaryKey(objectIdentity);
if (oidPrimaryKey == null) {
throw new NotFoundException("Object identity not found: " + objectIdentity);
}
// Delete this ACL's ACEs in the acl_entry table
deleteEntries(oidPrimaryKey);
@@ -319,10 +339,11 @@ public class JdbcMutableAclService extends JdbcAclService implements MutableAclS
* @param oid to find
* @return the object identity or null if not found
*/
protected Long retrieveObjectIdentityPrimaryKey(ObjectIdentity oid) {
protected @Nullable Long retrieveObjectIdentityPrimaryKey(ObjectIdentity oid) {
try {
return this.jdbcOperations.queryForObject(this.selectObjectIdentityPrimaryKey, Long.class, oid.getType(),
oid.getIdentifier().toString());
Long result = this.jdbcOperations.queryForObject(this.selectObjectIdentityPrimaryKey, Long.class,
oid.getType(), oid.getIdentifier().toString());
return result;
}
catch (DataAccessException notFound) {
return null;
@@ -340,7 +361,11 @@ 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()));
Long oidPrimaryKey = retrieveObjectIdentityPrimaryKey(acl.getObjectIdentity());
if (oidPrimaryKey == null) {
throw new NotFoundException("Object identity not found for ACL: " + acl.getObjectIdentity());
}
deleteEntries(oidPrimaryKey);
// Create this ACL's ACEs in the acl_entry table
createEntries(acl);
@@ -19,6 +19,8 @@ package org.springframework.security.acls.jdbc;
import java.util.List;
import java.util.Map;
import org.jspecify.annotations.Nullable;
import org.springframework.security.acls.model.Acl;
import org.springframework.security.acls.model.NotFoundException;
import org.springframework.security.acls.model.ObjectIdentity;
@@ -42,6 +44,6 @@ public interface LookupStrategy {
* {@link NotFoundException}, as a chain of {@link LookupStrategy}s may be used to
* automatically create entries if required)
*/
Map<ObjectIdentity, Acl> readAclsById(List<ObjectIdentity> objects, List<Sid> sids);
Map<ObjectIdentity, Acl> readAclsById(List<ObjectIdentity> objects, @Nullable List<Sid> sids);
}
@@ -17,4 +17,7 @@
/**
* JDBC-based persistence of ACL information
*/
@NullMarked
package org.springframework.security.acls.jdbc;
import org.jspecify.annotations.NullMarked;
@@ -18,6 +18,8 @@ package org.springframework.security.acls.model;
import java.io.Serializable;
import org.jspecify.annotations.Nullable;
/**
* Represents an individual permission assignment within an {@link Acl}.
*
@@ -36,7 +38,7 @@ public interface AccessControlEntry extends Serializable {
* Obtains an identifier that represents this ACE.
* @return the identifier, or <code>null</code> if unsaved
*/
Serializable getId();
@Nullable Serializable getId();
Permission getPermission();
@@ -19,6 +19,8 @@ package org.springframework.security.acls.model;
import java.io.Serializable;
import java.util.List;
import org.jspecify.annotations.Nullable;
/**
* Represents an access control list (ACL) for a domain object.
*
@@ -82,7 +84,7 @@ public interface Acl extends Serializable {
* @return the owner (may be <tt>null</tt> if the implementation does not use
* ownership concepts)
*/
Sid getOwner();
@Nullable Sid getOwner();
/**
* A domain object may have a parent for the purpose of ACL inheritance. If there is a
@@ -103,7 +105,7 @@ public interface Acl extends Serializable {
* @return the parent <tt>Acl</tt> (may be <tt>null</tt> if this <tt>Acl</tt> does not
* have a parent)
*/
Acl getParentAcl();
@Nullable Acl getParentAcl();
/**
* Indicates whether the ACL entries from the {@link #getParentAcl()} should flow down
@@ -189,6 +191,6 @@ public interface Acl extends Serializable {
* @return <tt>true</tt> if every passed <tt>Sid</tt> is represented by this
* <tt>Acl</tt> instance
*/
boolean isSidLoaded(List<Sid> sids);
boolean isSidLoaded(@Nullable List<Sid> sids);
}
@@ -18,6 +18,8 @@ package org.springframework.security.acls.model;
import java.io.Serializable;
import org.jspecify.annotations.Nullable;
import org.springframework.security.acls.jdbc.JdbcAclService;
/**
@@ -31,9 +33,9 @@ public interface AclCache {
void evictFromCache(ObjectIdentity objectIdentity);
MutableAcl getFromCache(ObjectIdentity objectIdentity);
@Nullable MutableAcl getFromCache(ObjectIdentity objectIdentity);
MutableAcl getFromCache(Serializable pk);
@Nullable MutableAcl getFromCache(Serializable pk);
void putInCache(MutableAcl acl);
@@ -19,6 +19,8 @@ package org.springframework.security.acls.model;
import java.util.List;
import java.util.Map;
import org.jspecify.annotations.Nullable;
/**
* Provides retrieval of {@link Acl} instances.
*
@@ -32,7 +34,7 @@ public interface AclService {
* @param parentIdentity to locate children of
* @return the children (or <tt>null</tt> if none were found)
*/
List<ObjectIdentity> findChildren(ObjectIdentity parentIdentity);
@Nullable List<ObjectIdentity> findChildren(ObjectIdentity parentIdentity);
/**
* Same as {@link #readAclsById(List)} except it returns only a single Acl.
@@ -59,7 +61,7 @@ public interface AclService {
* @throws NotFoundException if an {@link Acl} was not found for the requested
* {@link ObjectIdentity}
*/
Acl readAclById(ObjectIdentity object, List<Sid> sids) throws NotFoundException;
Acl readAclById(ObjectIdentity object, @Nullable List<Sid> sids) throws NotFoundException;
/**
* Obtains all the <tt>Acl</tt>s that apply for the passed <tt>Object</tt>s.
@@ -98,6 +100,7 @@ public interface AclService {
* @throws NotFoundException if an {@link Acl} was not found for each requested
* {@link ObjectIdentity}
*/
Map<ObjectIdentity, Acl> readAclsById(List<ObjectIdentity> objects, List<Sid> sids) throws NotFoundException;
Map<ObjectIdentity, Acl> readAclsById(List<ObjectIdentity> objects, @Nullable List<Sid> sids)
throws NotFoundException;
}
@@ -18,4 +18,7 @@
* Interfaces and shared classes to manage access control lists (ACLs) for domain object
* instances.
*/
@NullMarked
package org.springframework.security.acls.model;
import org.jspecify.annotations.NullMarked;
@@ -24,4 +24,7 @@
* older and more verbose attribute/voter/after-invocation approach from versions before
* Spring Security 3.0.
*/
@NullMarked
package org.springframework.security.acls;
import org.jspecify.annotations.NullMarked;